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
+33 -33
View File
@@ -1,8 +1,8 @@
# E2 — `ccam lanes sync-base` Implementation Plan
# E2 — `agenthub lanes sync-base` Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** `ccam lanes sync-base` exists as a real, working command — the migration-collision preflight (`--check`), the one sanctioned merge (`origin/development` into a feature branch), and its conflict-resolution follow-up (`--continue`) — so `.claude/skills/ship-feature-lane/SKILL.md`'s Stage 2/8/12 references to it stop being "later task" placeholders.
**Goal:** `agenthub lanes sync-base` exists as a real, working command — the migration-collision preflight (`--check`), the one sanctioned merge (`origin/development` into a feature branch), and its conflict-resolution follow-up (`--continue`) — so `.claude/skills/ship-feature-lane/SKILL.md`'s Stage 2/8/12 references to it stop being "later task" placeholders.
**Architecture:** One new core module (`server/lib/lane-sync.js`, pure git operations via `worktree.js`'s existing `git()` helper), two new profile declarations (`MIGRATIONS_DIR`, `GENERATED_MERGE_PATHS`), one new synchronous route (`POST /:id/sync-base`), one new CLI subcommand, and a `SKILL.md` edit that turns three "if it exists yet" conditionals into real instructions.
@@ -145,7 +145,7 @@ const os = require("node:os");
const path = require("node:path");
const { execFileSync } = require("node:child_process");
const ROOT = fs.mkdtempSync(path.join(os.tmpdir(), "ccam-sync-"));
const ROOT = fs.mkdtempSync(path.join(os.tmpdir(), "agenthub-sync-"));
const laneSync = require("../lib/lane-sync");
@@ -488,7 +488,7 @@ describe("lane-sync merge: generated-file merge driver + regen fold-in", () => {
gc(GEN_PUSHER, "push", "-q", "origin", "development");
// A regen hook the fold-in step will run.
const profileDir = path.join(GEN_LANE, ".ccam", "profile");
const profileDir = path.join(GEN_LANE, ".agenthub", "profile");
fs.mkdirSync(path.join(profileDir, "hooks"), { recursive: true });
fs.writeFileSync(path.join(profileDir, "profile.env"), "PORTS=api\n");
fs.writeFileSync(
@@ -503,7 +503,7 @@ describe("lane-sync merge: generated-file merge driver + regen fold-in", () => {
env: { MIGRATIONS_DIR: "", GENERATED_MERGE_PATHS: "api/openapi.json" },
generatedMergePaths: ["api/openapi.json"],
hooks: new Set(["regen"]),
dir: path.join(GEN_LANE, ".ccam", "profile"),
dir: path.join(GEN_LANE, ".agenthub", "profile"),
};
}
@@ -567,10 +567,10 @@ async function unmergedFiles(cwd) {
* already established. */
async function setupMergeDriver(cwd, generatedPaths) {
if (!generatedPaths.length) return;
await git(cwd, ["config", "merge.ccam-generated.driver", "true"]);
await git(cwd, ["config", "merge.agenthub-generated.driver", "true"]);
await git(cwd, [
"config",
"merge.ccam-generated.name",
"merge.agenthub-generated.name",
"keep ours; regenerated post-merge by the profile regen hook",
]);
@@ -581,7 +581,7 @@ async function setupMergeDriver(cwd, generatedPaths) {
const lines = new Set(existing.split("\n").filter(Boolean));
let changed = false;
for (const gp of generatedPaths) {
const line = `${gp} merge=ccam-generated`;
const line = `${gp} merge=agenthub-generated`;
if (!lines.has(line)) {
lines.add(line);
changed = true;
@@ -924,7 +924,7 @@ There's no HTTP-level test harness for lane routes in this repo (per the Global
```bash
npm run dev &
sleep 3
# Replace 1 with a real lane id that has a .ccam/profile and a feature branch.
# Replace 1 with a real lane id that has a .agenthub/profile and a feature branch.
curl -s -X POST http://localhost:4820/api/lanes/1/sync-base \
-H 'Content-Type: application/json' \
-d '{"mode":"check","branch":"feat/some-branch"}' | node -e "process.stdin.pipe(require('node:fs').createWriteStream('/dev/stdout'))"
@@ -948,18 +948,18 @@ git commit -m "feat(lanes): add POST /:id/sync-base route (E2)"
---
### Task 4: `ccam lanes sync-base` CLI
### Task 4: `agenthub lanes sync-base` CLI
**Files:**
- Modify: `bin/ccam.js`
- Modify: `bin/agenthub.js`
**Interfaces:**
- Consumes: `POST /api/lanes/:id/sync-base` (Task 3); `resolveLaneArg(args)`, `post(path, body, options)` (both already defined in `bin/ccam.js`).
- Produces: `ccam lanes sync-base [<id>] [--check|--continue] [branch]` — `process.exitCode` set to `0`/`4`/`5` matching the route's `code`, so `SKILL.md`'s documented exit-code contract holds when scripted.
- Consumes: `POST /api/lanes/:id/sync-base` (Task 3); `resolveLaneArg(args)`, `post(path, body, options)` (both already defined in `bin/agenthub.js`).
- Produces: `agenthub lanes sync-base [<id>] [--check|--continue] [branch]` — `process.exitCode` set to `0`/`4`/`5` matching the route's `code`, so `SKILL.md`'s documented exit-code contract holds when scripted.
- [ ] **Step 1: Add the subcommand to `cmdLanesRuntime`**
In `bin/ccam.js`, extend the `if (sub === "hook") { ... }` block (ends at `bin/ccam.js:1902`) with a new `sync-base` branch, inside the same `cmdLanesRuntime(sub, args)` function:
In `bin/agenthub.js`, extend the `if (sub === "hook") { ... }` block (ends at `bin/agenthub.js:1902`) with a new `sync-base` branch, inside the same `cmdLanesRuntime(sub, args)` function:
```js
if (sub === "sync-base") {
@@ -994,7 +994,7 @@ In `bin/ccam.js`, extend the `if (sub === "hook") { ... }` block (ends at `bin/c
console.error(` conflicted: ${result.conflictedFiles.join(", ")}`);
console.error(" resolve, then: git add <resolved files> && git commit --no-edit");
console.error(
` then: ccam lanes sync-base --continue ${branch ? branch + " " : ""}${laneId}`
` then: agenthub lanes sync-base --continue ${branch ? branch + " " : ""}${laneId}`
);
process.exitCode = 4;
return;
@@ -1028,7 +1028,7 @@ In `bin/ccam.js`, extend the `if (sub === "hook") { ... }` block (ends at `bin/c
- [ ] **Step 2: Wire the subcommand into the dispatcher**
`bin/ccam.js:3088` currently reads:
`bin/agenthub.js:3088` currently reads:
```js
if (["up", "down", "runtime", "logs", "hook"].includes(rest[0])) {
@@ -1046,7 +1046,7 @@ Change to:
- [ ] **Step 3: Add the help-catalog entry**
In the command catalog array (`bin/ccam.js:2268`, right after the `lanes hook` entry), add:
In the command catalog array (`bin/agenthub.js:2268`, right after the `lanes hook` entry), add:
```js
[
@@ -1061,7 +1061,7 @@ In the command catalog array (`bin/ccam.js:2268`, right after the `lanes hook` e
```bash
npm run dev &
sleep 3
node bin/ccam.js lanes sync-base --check feat/some-branch 1
node bin/agenthub.js lanes sync-base --check feat/some-branch 1
echo "exit: $?"
```
@@ -1077,8 +1077,8 @@ npm run test:server
- [ ] **Step 6: Commit**
```bash
git add bin/ccam.js
git commit -m "feat(lanes): add ccam lanes sync-base CLI (E2)"
git add bin/agenthub.js
git commit -m "feat(lanes): add agenthub lanes sync-base CLI (E2)"
```
---
@@ -1097,14 +1097,14 @@ git commit -m "feat(lanes): add ccam lanes sync-base CLI (E2)"
In `.claude/skills/ship-feature-lane/SKILL.md`, find (Stage 2, `### 2 — Pre-push CI gates + dev preflight`):
```
- `ccam lanes sync-base --check feat/<slug>` — the dev preflight: fetches and checks the branch against the CURRENT `origin/development` without merging anything. **This command is a LATER task, not yet built as of this skill's authoring** — until it exists, skip this preflight check and rely on Stage 12's conflict handling to catch a divergence at merge time; note this gap explicitly in your Stage 2 evidence (`ccam stage gates --evidence "sync-base preflight unavailable, skipped"`).
- `agenthub lanes sync-base --check feat/<slug>` — the dev preflight: fetches and checks the branch against the CURRENT `origin/development` without merging anything. **This command is a LATER task, not yet built as of this skill's authoring** — until it exists, skip this preflight check and rely on Stage 12's conflict handling to catch a divergence at merge time; note this gap explicitly in your Stage 2 evidence (`agenthub stage gates --evidence "sync-base preflight unavailable, skipped"`).
- Once built, its contract is: exit 5 on a migration-number collision (print the exact rename, do it on the feature branch, re-run Stage 2); informational `DEV_DELTA:`/`DEV_OVERLAP:` output otherwise (you do NOT sync the branch for it — GitHub merges non-conflicting histories fine).
```
Replace with:
```
- `ccam lanes sync-base --check feat/<slug>` — the dev preflight: fetches and checks the branch against the CURRENT `origin/development` without merging anything. Exit 5 on a migration-number collision: rename the printed file to the suggested number on the feature branch (`git mv`, update any in-file references), then re-run Stage 2. Exit 0 with `DEV_DELTA:`/`DEV_OVERLAP:` output otherwise — informational, you do NOT sync the branch for it (GitHub merges non-conflicting histories fine); a large overlapping delta is a heads-up that post-merge behavior may differ from what you test locally.
- `agenthub lanes sync-base --check feat/<slug>` — the dev preflight: fetches and checks the branch against the CURRENT `origin/development` without merging anything. Exit 5 on a migration-number collision: rename the printed file to the suggested number on the feature branch (`git mv`, update any in-file references), then re-run Stage 2. Exit 0 with `DEV_DELTA:`/`DEV_OVERLAP:` output otherwise — informational, you do NOT sync the branch for it (GitHub merges non-conflicting histories fine); a large overlapping delta is a heads-up that post-merge behavior may differ from what you test locally.
```
- [ ] **Step 2: Stage 8 — make the re-run unconditional**
@@ -1112,13 +1112,13 @@ Replace with:
Find (Stage 8, around `SKILL.md:127`):
```
- Re-run the preflight if `ccam lanes sync-base` exists by the time you read this — development may have moved while you were in QC. If it doesn't exist yet, skip straight to the push (same gap noted in Stage 2).
- Re-run the preflight if `agenthub lanes sync-base` exists by the time you read this — development may have moved while you were in QC. If it doesn't exist yet, skip straight to the push (same gap noted in Stage 2).
```
Replace with:
```
- Re-run the preflight: `ccam lanes sync-base --check feat/<slug>` — development may have moved while you were in QC. A migration collision here (exit 5) sends you back to Stage 2 with the rename; a clean result (exit 0) proceeds to the push.
- Re-run the preflight: `agenthub lanes sync-base --check feat/<slug>` — development may have moved while you were in QC. A migration collision here (exit 5) sends you back to Stage 2 with the rename; a clean result (exit 0) proceeds to the push.
```
- [ ] **Step 3: Stage 12 — unconditional merge + the exit-4/`--continue` mechanics**
@@ -1127,7 +1127,7 @@ Find (Stage 12, around `SKILL.md:155`):
```
- `CONFLICTING` → the feature branch conflicts with `development`. Resolve it as real work:
- If `ccam lanes sync-base` exists by now: `ccam lanes sync-base feat/<slug>` (merges the latest `origin/development` INTO the feature branch — the only sanctioned merge). Resolve every conflict thoughtfully — keep `development`'s behavior for code unrelated to this feature, preserve the feature's intent where they overlap; when genuinely ambiguous, STOP and escalate (`--status blocked`, note the files) rather than guess. `git add` ONLY the conflicted files, `git commit --no-edit`.
- If `agenthub lanes sync-base` exists by now: `agenthub lanes sync-base feat/<slug>` (merges the latest `origin/development` INTO the feature branch — the only sanctioned merge). Resolve every conflict thoughtfully — keep `development`'s behavior for code unrelated to this feature, preserve the feature's intent where they overlap; when genuinely ambiguous, STOP and escalate (`--status blocked`, note the files) rather than guess. `git add` ONLY the conflicted files, `git commit --no-edit`.
- If it doesn't exist yet: `git fetch origin && git merge origin/development` directly on the feature branch, resolve conflicts the same way, commit.
- Re-enter the pipeline **from Stage 2 through Stage 8** (the push updates the PR), then return here and keep watching.
```
@@ -1136,8 +1136,8 @@ Replace with:
```
- `CONFLICTING` → the feature branch conflicts with `development`. Resolve it as real work:
- `ccam lanes sync-base feat/<slug>` (merges the latest `origin/development` INTO the feature branch — the only sanctioned merge). A migration-number collision (exit 5) means nothing was merged — rename the printed file on the feature branch, re-run Stage 2, then retry this step.
- **Exit 4 — merge conflict, left in place on purpose.** Resolve every conflict thoughtfully on the feature branch — keep `development`'s behavior for code unrelated to this feature, preserve the feature's intent where they overlap; when genuinely ambiguous, STOP and escalate (`--status blocked`, note the files) rather than guess. Never hand-merge a generated contract/client file listed in the profile's `GENERATED_MERGE_PATHS` — the keep-ours driver + regen own them. `git add` ONLY the conflicted files, `git commit --no-edit`, then `ccam lanes sync-base --continue feat/<slug>` (folds any regenerated artifacts into a follow-up commit).
- `agenthub lanes sync-base feat/<slug>` (merges the latest `origin/development` INTO the feature branch — the only sanctioned merge). A migration-number collision (exit 5) means nothing was merged — rename the printed file on the feature branch, re-run Stage 2, then retry this step.
- **Exit 4 — merge conflict, left in place on purpose.** Resolve every conflict thoughtfully on the feature branch — keep `development`'s behavior for code unrelated to this feature, preserve the feature's intent where they overlap; when genuinely ambiguous, STOP and escalate (`--status blocked`, note the files) rather than guess. Never hand-merge a generated contract/client file listed in the profile's `GENERATED_MERGE_PATHS` — the keep-ours driver + regen own them. `git add` ONLY the conflicted files, `git commit --no-edit`, then `agenthub lanes sync-base --continue feat/<slug>` (folds any regenerated artifacts into a follow-up commit).
- Re-enter the pipeline **from Stage 2 through Stage 8** (the push updates the PR), then return here and keep watching.
```
@@ -1148,12 +1148,12 @@ In `docs/LANES.md`, under `## The ship-feature-lane skill (E1)` (`docs/LANES.md:
```markdown
### Dev preflight and merge safety: sync-base
`ccam lanes sync-base` is the ONE sanctioned merge in the pipeline — `origin/development` into a feature branch — used at Stages 2, 8, and 12. Three modes:
`agenthub lanes sync-base` is the ONE sanctioned merge in the pipeline — `origin/development` into a feature branch — used at Stages 2, 8, and 12. Three modes:
```bash
ccam lanes sync-base --check feat/<slug> # read-only preflight: fetch + collision check + DEV_DELTA/DEV_OVERLAP
ccam lanes sync-base feat/<slug> # merge origin/development into the feature branch
ccam lanes sync-base --continue feat/<slug> # finish after a manually resolved conflict
agenthub lanes sync-base --check feat/<slug> # read-only preflight: fetch + collision check + DEV_DELTA/DEV_OVERLAP
agenthub lanes sync-base feat/<slug> # merge origin/development into the feature branch
agenthub lanes sync-base --continue feat/<slug> # finish after a manually resolved conflict
```
Exit codes: `0` clean, `4` merge conflict (left in place — resolve, commit, then `--continue`), `5` migration-number collision (nothing merged — rename the printed file, re-run).
@@ -1161,7 +1161,7 @@ Exit codes: `0` clean, `4` merge conflict (left in place — resolve, commit, th
Two profile declarations control it, both empty (off) by default:
```bash
# .ccam/profile/profile.env
# .agenthub/profile/profile.env
MIGRATIONS_DIR="db/migrations"
GENERATED_MERGE_PATHS="api/openapi.json api/client.ts"
```
@@ -1194,5 +1194,5 @@ npm run test:server
```bash
git add .claude/skills/ship-feature-lane/SKILL.md docs/LANES.md docs/superpowers/plans/2026-08-03-shipyard-parity-lanes.md
git commit -m "docs(lanes): document ccam lanes sync-base (E2)"
git commit -m "docs(lanes): document agenthub lanes sync-base (E2)"
```