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).
This commit is contained in:
2026-09-07 12:09:15 +07:00
parent ae9b3dbcd5
commit 17d56d0e01
345 changed files with 2705 additions and 2727 deletions
@@ -4,30 +4,30 @@
## Problem
E1 through F3c (this session) built six CLI-only primitives — `ccam lanes sync-base`, `ccam lanes agents install`, `ccam lanes mcp sync`, `ccam lanes integration <name>`, `ccam lanes gc`, `ccam skills install` — with no UI surface. The user wants the lane-scoped, day-to-day-useful ones clickable from the dashboard instead of terminal-only.
E1 through F3c (this session) built six CLI-only primitives — `agenthub lanes sync-base`, `agenthub lanes agents install`, `agenthub lanes mcp sync`, `agenthub lanes integration <name>`, `agenthub lanes gc`, `agenthub skills install` — with no UI surface. The user wants the lane-scoped, day-to-day-useful ones clickable from the dashboard instead of terminal-only.
## Scope
**In scope — LaneCard.tsx (per-lane, gated on `runtime?.available`, same condition the existing up/down button already uses since all four need a `.ccam/profile`):**
**In scope — LaneCard.tsx (per-lane, gated on `runtime?.available`, same condition the existing up/down button already uses since all four need a `.agenthub/profile`):**
1. **"Install agents" button** → `POST /lanes/:id/agents/install` (exists). Shows the installed file list on success.
2. **"Sync MCP" button** → `POST /lanes/:id/mcp/sync` (exists). Shows synced servers + seeded profiles.
3. **Integration status badges** (tracker / dev_qc / ci_wait), read-only, on/off — reads `GET /lanes/:id/integrations/:name` (exists) for each of the three fixed names.
4. **"Check dev sync" button** → `POST /lanes/:id/sync-base` with `{mode: "check"}` (exists — the route already supports `mode`). Shows `DEV_DELTA`/`DEV_OVERLAP` or a collision list. **Read-only preflight only — no merge button.** Merging is a deliberate, evidence-recording action the driving skill/session takes (`ccam lanes sync-base` bare, or `--continue`); a UI click is the wrong place for a git merge with fix-loop implications. This matches the project's own boundary: "the console never writes a lane's stage" — extending that spirit, the console doesn't perform git merges either.
4. **"Check dev sync" button** → `POST /lanes/:id/sync-base` with `{mode: "check"}` (exists — the route already supports `mode`). Shows `DEV_DELTA`/`DEV_OVERLAP` or a collision list. **Read-only preflight only — no merge button.** Merging is a deliberate, evidence-recording action the driving skill/session takes (`agenthub lanes sync-base` bare, or `--continue`); a UI click is the wrong place for a git merge with fix-loop implications. This matches the project's own boundary: "the console never writes a lane's stage" — extending that spirit, the console doesn't perform git merges either.
**In scope — Settings.tsx (machine-wide, new "Lanes" section near the existing Hooks section — modeled on "Reinstall Hooks"/"Cleanup"):**
5. **"Install ship-feature-lane skill" button** → new route `POST /api/skills/install` (doesn't exist yet — needs building; wraps the same `fs.cpSync` logic `ccam skills install`'s CLI already has, factored into `server/lib/lane-agents.js` or a new tiny `server/lib/skills-install.js` so the CLI and route share one implementation instead of duplicating the copy logic).
5. **"Install ship-feature-lane skill" button** → new route `POST /api/skills/install` (doesn't exist yet — needs building; wraps the same `fs.cpSync` logic `agenthub skills install`'s CLI already has, factored into `server/lib/lane-agents.js` or a new tiny `server/lib/skills-install.js` so the CLI and route share one implementation instead of duplicating the copy logic).
6. **"Run housekeeping" button, with a "dry run" checkbox** → new route `POST /api/lanes/gc` (doesn't exist yet — wraps `server/lib/lane-gc.js`'s `reapOrphanMcp`/`capOversizedLogs`, already built and tested in F3c). Shows reaped pids + capped log paths.
**Explicitly out of scope:**
- A merge button for `sync-base` (see #4's reasoning).
- Any UI for `ccam lanes sync-base --continue` (conflict resolution is a session/skill action with real judgment calls about which side to keep — not a button).
- Refactoring the CLI (`bin/ccam.js`) to call the new HTTP routes instead of its current direct `require()` of the lib modules — the CLI's local-only shape (no server round-trip needed when you're already on the machine) stays as-is; only the browser needs an HTTP path, so the routes are additive, not a replacement for the CLI's existing implementation.
- Any UI for `agenthub lanes sync-base --continue` (conflict resolution is a session/skill action with real judgment calls about which side to keep — not a button).
- Refactoring the CLI (`bin/agenthub.js`) to call the new HTTP routes instead of its current direct `require()` of the lib modules — the CLI's local-only shape (no server round-trip needed when you're already on the machine) stays as-is; only the browser needs an HTTP path, so the routes are additive, not a replacement for the CLI's existing implementation.
## Design
### Two new backend routes
- `POST /api/skills/install` — same `sameOriginGuard` as every other mutating route. Extracts the copy logic (`REPO_ROOT/.claude/skills/ship-feature-lane` → `~/.claude/skills/ship-feature-lane`, `fs.cpSync` with `force: true`) out of `bin/ccam.js`'s `cmdSkillsInstall` into a small exported function in `server/lib/skills-install.js`, so `bin/ccam.js` and the route both call the same code (no duplication). Returns `{installed: true, path: string}`.
- `POST /api/skills/install` — same `sameOriginGuard` as every other mutating route. Extracts the copy logic (`REPO_ROOT/.claude/skills/ship-feature-lane` → `~/.claude/skills/ship-feature-lane`, `fs.cpSync` with `force: true`) out of `bin/agenthub.js`'s `cmdSkillsInstall` into a small exported function in `server/lib/skills-install.js`, so `bin/agenthub.js` and the route both call the same code (no duplication). Returns `{installed: true, path: string}`.
- `POST /api/lanes/gc` — same guard. Body `{dryRun?: boolean}`. Calls `reapOrphanMcp`/`capOversizedLogs` from the already-built `server/lib/lane-gc.js` (no changes needed there — Task 2 of F3c already exports both). Returns `{reaped: number[], capped: {path: string, sizeBefore: number}[]}`.
Both routes are machine-wide (no `:id`), registered at the router's top level (not under `/lanes/:id/...`) — `/api/skills/install` sits alongside other non-lane-scoped routes; `/api/lanes/gc` sits in the lanes router but before any `:id` param route (order matters: Express would otherwise try to resolve `gc` as a lane id on a route like `/lanes/:id`).
@@ -60,7 +60,7 @@ gc: (body: { dryRun?: boolean } = {}) =>
### LaneCard.tsx
Four additions, all gated on `runtime?.available` (same condition the up/down button already uses — every one of these four needs a `.ccam/profile`):
Four additions, all gated on `runtime?.available` (same condition the up/down button already uses — every one of these four needs a `.agenthub/profile`):
- **Install agents / Sync MCP buttons**: same visual pattern as the up/down toggle (`rounded px-2 py-1 text-fg-secondary transition-colors hover:bg-surface-2 disabled:opacity-50`), a `useState<"agents" | "mcp" | "sync-check" | null>` busy flag (one shared state covering all three new button actions plus check, mirroring `runtimeBusy`'s shape), result shown as a transient inline line below the button (reuse the `laneActionError`-style banner pattern, but for success text too — a new `actionResult: {label: string; message: string} | null` state scoped to LaneCard).
- **Integration badges**: a small row of three pill-shaped badges (`tracker` / `dev_qc` / `ci_wait`), fetched once when the card mounts and `runtime.available` is true (parallel `Promise.all` of the three `api.lanes.integration` calls, same effect-on-mount shape the existing `git`/`runtime` fetches already use), green/gray styling for on/off, no click handler — read-only.