diff --git a/bin/ccam.js b/bin/ccam.js index c368976..d3d93e6 100755 --- a/bin/ccam.js +++ b/bin/ccam.js @@ -2192,6 +2192,25 @@ async function cmdLanesMcpSync(args) { } } +/** `ccam lanes integration []` — check whether a named + * integration (tracker, dev_qc, ci_wait) is turned on for this lane, per + * its .ccam/profile/integrations.env. Exit 0 = on, 1 = off. */ +async function cmdLanesIntegration(args) { + const name = args.find((arg) => !arg.startsWith("--")); + if (!name) { + console.error("usage: ccam lanes integration []"); + process.exitCode = 1; + return; + } + const resolved = await resolveLaneArg(args.filter((a) => a !== name)); + if (!resolved) return; + const { enabled } = await get( + `/api/lanes/${resolved.laneId}/integrations/${encodeURIComponent(name)}` + ); + console.log(enabled ? "on" : "off"); + process.exitCode = enabled ? 0 : 1; +} + async function cmdFeatureShow(args) { const slug = args.find((arg) => !arg.startsWith("--")); if (!slug) { @@ -2388,6 +2407,11 @@ const COMMAND_GROUPS = [ "[]", "Relocate the source repo's MCP servers into /.mcp.json and seed Chromium profiles", ], + [ + "lanes integration", + " []", + "Check whether a named integration (tracker, dev_qc, ci_wait) is on for this lane — exit 0/1", + ], [ "lock status|acquire|release", "[] [--holder X] [--timeout N]", @@ -3212,6 +3236,9 @@ async function runCommand(argv) { if (rest[0] === "mcp" && rest[1] === "sync") { return cmdLanesMcpSync(rest.slice(2)); } + if (rest[0] === "integration") { + return cmdLanesIntegration(rest.slice(1)); + } return cmdLanes(); case "lock": { const sub = rest[0]; diff --git a/server/routes/lanes.js b/server/routes/lanes.js index b40cca5..99afd3c 100644 --- a/server/routes/lanes.js +++ b/server/routes/lanes.js @@ -35,7 +35,7 @@ const { withLaneLock } = require("../lib/lane-lock"); const { checkSync, mergeSync, continueSync } = require("../lib/lane-sync"); const { installAgents } = require("../lib/lane-agents"); const { syncMcp } = require("../lib/lane-mcp"); -const { HOOKS, runHook, resolveProfile } = require("../lib/lane-profile"); +const { HOOKS, runHook, resolveProfile, isIntegrationEnabled } = require("../lib/lane-profile"); const { slotDirs } = require("../lib/lane-slots"); const { upLane, @@ -371,6 +371,18 @@ router.post("/:id/mcp/sync", sameOriginGuard, async (req, res) => { } }); +/** + * Whether a named integration (tracker, dev_qc, ci_wait, ...) is turned on + * for this lane — reads .ccam/profile/integrations.env. Read-only, no + * sameOriginGuard needed (same reasoning GET /:id/git already documents: + * that guard exists for destructive actions). + */ +router.get("/:id/integrations/:name", (req, res) => { + const lane = lanesLib.getLane(req.params.id); + if (!lane) return res.status(404).json({ error: { code: "ENOLANE", message: "lane not found" } }); + res.json({ enabled: isIntegrationEnabled(lane, req.params.name) }); +}); + /** * A lane's working-copy facts: branch, short HEAD, that commit's subject, and * the uncommitted counts. Read-only, so no same-origin guard — that guard