Files
AgentHub/docs/superpowers/specs/2026-08-06-add-lane-auto-setup-design.md
T
nntrivi2001 17d56d0e01 refactor!: rebrand CCAM to AgentHub
Clean break, no compatibility alias. Renames the product name, the CLI
binary (`ccam` -> `agenthub`), the env var prefix (`CCAM_` ->
`AGENTHUB_`), the Prometheus metric namespace (`ccam_*` ->
`agenthub_*`), the tmux session prefix (`ccam-lane-<id>` ->
`agenthub-lane-<id>`), the per-repo runtime profile directory
(`.ccam/profile` -> `.agenthub/profile`), the lanes state root
(`~/.claude/ccam-lanes` -> `~/.claude/agenthub-lanes`), the bundled
plugins (`plugins/ccam*` -> `plugins/agenthub*`) and their slash
commands (`/ccam-doctor` -> `/agenthub-doctor`, `/ccam:*` ->
`/agenthub:*`).

Because there is no alias, an existing install has to be updated by
hand: rename its `.ccam/profile` directory and any `CCAM_*` variables,
and reinstall the plugin under its new name.

Two other name families are deliberately left alone here, since they are
not just strings: the `Claude-Code-Monitor` repository/marketplace name,
and the `agent-dashboard` npm package plus its `~/.claude/agent-dashboard`
runtime directory, which holds the live database and needs a migration
rather than a rename.

Verified: server 1406/1406, client 459/459, `grep -i ccam` clean across
tracked files, `openapi.yaml` regenerated (95 paths, unchanged count).
2026-09-07 12:09:15 +07:00

4.6 KiB

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 <repo> 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:

  • agenthub 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:

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:

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.