diff --git a/docs/superpowers/specs/2026-08-06-add-lane-auto-setup-design.md b/docs/superpowers/specs/2026-08-06-add-lane-auto-setup-design.md new file mode 100644 index 0000000..eb9d3b3 --- /dev/null +++ b/docs/superpowers/specs/2026-08-06-add-lane-auto-setup-design.md @@ -0,0 +1,66 @@ +# F5 — Auto-setup after Add Lane + +**Status:** approved 2026-08-06. + +## Problem + +F4 surfaced `agents install`/`mcp sync`/profile-init as per-lane manual buttons, but a user creating a lane through `AddLaneModal` (the `/run` page's "add lane" flow) still has to click each one separately afterward, or use the CLI. The user wants one click — pick a source repo, and setup happens automatically. + +## Scope + +**In scope:** +1. New route `POST /api/lanes/:id/profile/init` — wraps `server/lib/lane-detect.js`'s `detectNode`/`scaffoldProfile` (already built, CLI-only until now — same "extract to a route" shape F4 already established for skills-install). Body `{force?: boolean}`. `lane.cwd` is always the scaffold target (the CLI's `` argument becomes implicit — this is always "this lane's own directory" in the UI flow, never an arbitrary path). `scaffoldProfile(repoPath, facts, {force})` returns `{written: string[], todos: string[]}` (confirmed from its actual implementation, `server/lib/lane-detect.js:229-`) and throws `.code === "EPROFILEEXISTS"` if a profile is already there and `force` wasn't set. Route returns `{scaffolded: true, written: string[], todos: string[]}` on success; `{scaffolded: false, reason: string}` (200, not an error) when `detectNode` finds nothing — matching the CLI's own "not an error, just nothing to scaffold" framing; `400 {error:{code:"EPROFILEEXISTS",...}}` when a profile already exists and `force` is false. +2. `AddLaneModal.tsx`: after `api.lanes.worktree()` succeeds, fire `profileInit`, `agentsInstall`, and `mcpSync` in parallel (`Promise.allSettled` — all three are independent of each other: `agentsInstall`/`mcpSync` only need `.git`, not a profile). Each outcome (success/failure/skipped) is collected and shown as a short per-step result list before the modal closes, instead of closing silently the instant the lane record exists. + +**Explicitly out of scope:** +- `ccam skills install` — machine-wide, one-time-per-machine, not lane-specific. Running it every time someone adds a lane would be redundant (idempotent, but pointless) and conceptually wrong (it has nothing to do with THIS lane). Stays a manual Settings-page action. +- Blocking lane creation on any of the three setup steps failing. A lane with a non-Node.js repo, no MCP servers configured, or a git quirk should still exist and be usable — setup is best-effort enrichment, not a precondition. +- Adopting an EXISTING folder (a second "add lane" mode) — `AddLaneModal` only does managed-worktree creation today; that's unchanged. This auto-setup applies to whatever lane the existing flow just created, regardless of how. + +## Design + +### Route + +`POST /api/lanes/:id/profile/init` in `server/routes/lanes.js`, alongside the other per-lane setup routes (`/agents/install`, `/mcp/sync`) added in E3/F1: + +```js +router.post("/:id/profile/init", sameOriginGuard, (req, res) => { + const lane = laneOr404(req, res); + if (!lane) return; + 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 } }); + } +}); +``` + +### `AddLaneModal.tsx` + +After the existing `api.lanes.worktree()` call succeeds (currently: call `onAdded`, close modal), insert a "setting up…" phase: + +```typescript +const setup = await Promise.allSettled([ + api.lanes.profileInit(result.lane.id), + api.lanes.agentsInstall(result.lane.id), + api.lanes.mcpSync(result.lane.id), +]); +``` + +Show a compact three-line result summary (profile / agents / MCP, each ✓/✗/skipped) for a couple seconds (or until the user dismisses it) before calling `onAdded` + closing — the user should see what happened, not have it vanish silently. A failure here is informational, never blocks proceeding to the new lane. + +### Client API + +`client/src/lib/api.ts`'s `lanes` object gains `profileInit: (id: number, force = false) => request<{scaffolded: boolean, written?: string[], todos?: string[], reason?: string}>(...)`. + +## Verify + +Backend: extend the manual-smoke-check convention (no automated HTTP harness in this repo) for the new route. Frontend: a test on `AddLaneModal` confirming all three setup calls fire after a successful `worktree()` call, and that a failure in one doesn't block the others or prevent `onAdded` from eventually firing.