diff --git a/server/routes/lanes.js b/server/routes/lanes.js index 38ae4a3..861e80a 100644 --- a/server/routes/lanes.js +++ b/server/routes/lanes.js @@ -47,6 +47,7 @@ const { removeLaneData, } = require("../lib/lane-runtime"); const { reapOrphanMcp, capOversizedLogs } = require("../lib/lane-gc"); +const { detectNode, scaffoldProfile } = require("../lib/lane-detect"); const router = Router(); const MAX_WORKTREE_DIRECTORY_ATTEMPTS = 50; @@ -389,6 +390,34 @@ router.post("/:id/mcp/sync", sameOriginGuard, async (req, res) => { } }); +/** + * Detect a Node.js project at this lane's OWN directory and scaffold + * .ccam/profile/ if one is found — the HTTP equivalent of + * `ccam lanes profile init`, always targeting lane.cwd (never an arbitrary + * path; the CLI's argument has no meaning here, this lane's own + * directory is the only sensible target). "No Node.js project detected" is + * a normal 200 outcome, not an error — most lanes won't be auto-detectable + * and that's fine, same as every other optional profile declaration. + */ +router.post("/:id/profile/init", sameOriginGuard, (req, res) => { + const lane = lanesLib.getLane(req.params.id); + if (!lane) return res.status(404).json({ error: { code: "ENOLANE", message: "lane not found" } }); + + const facts = detectNode(lane.cwd); + if (!facts) { + return res.json({ scaffolded: false, reason: "no detectable Node.js project" }); + } + try { + const result = scaffoldProfile(lane.cwd, facts, { force: req.body?.force === true }); + res.json({ scaffolded: true, written: result.written, todos: result.todos }); + } catch (err) { + if (err.code === "EPROFILEEXISTS") { + return res.status(400).json({ error: { code: err.code, message: err.message } }); + } + res.status(500).json({ error: { code: err.code || "ERUNTIME", message: err.message } }); + } +}); + /** * Whether a named integration (tracker, dev_qc, ci_wait, ...) is turned on * for this lane — reads .ccam/profile/integrations.env. Read-only, no