154 Commits

Author SHA1 Message Date
nntrivi2001 022b2384ac fix(plugins): plugin.json repository field must be a string
Claude Code's manifest schema requires repository as a URL string, not the
{type,url} object form — the marketplace install failed validation
("expected string, received object") the first time it was tried against a
freshly pushed marketplace.
2026-08-10 16:15:06 +07:00
nntrivi2001 8a82895c65 feat(plugins): make CCAM installable straight from a Claude Code plugin
Adds a root `ccam` plugin (`.claude-plugin/plugin.json`, `"source": "./"`) so
`/plugin marketplace add` + `/plugin install ccam@...` is enough on a machine
with nothing but Claude Code: no clone, no npm run setup, no manual npm start.

- scripts/plugin-bootstrap.js: SessionStart hook. Fast-path exit, Node >=22.5
  gate (node:sqlite), mkdir lock with stale reclaim, deps installed into
  ~/.claude/agent-dashboard/runtime/ (never the plugin cache), legacy
  checkout-hook cleanup (backed up), ~/.local/bin/ccam launcher, eager UI
  build so client routes like /run work immediately, detached server spawn.
- scripts/plugin-open.js, scripts/plugin-doctor.js: /ccam-open, /ccam-doctor.
- server/index.js: DASHBOARD_CLIENT_DIST override (plugin cache is read-only).
- mcp/build/ is committed (plugin MCP servers start before any bootstrap could
  build them) and kept honest by scripts/check-mcp-build.js (content hash,
  not mtime), enforced by pre-commit when mcp/src changes.
- plugins/ccam-dashboard/.mcp.json moved under plugins/ccam/ with a working
  ${CLAUDE_PLUGIN_ROOT} path (the old relative path never resolved from a
  marketplace-cached subdir).
- Docs: README, INSTALL, SETUP, ARCHITECTURE, CLAUDE.md, docs/PLUGINS.md,
  docs/MCP.md, docs/CLI.md, docs/HOOKS.md.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-10 16:05:37 +07:00
nntrivi2001 5a793e70cc fix(test): stop lane-runtime leaking a live server on every run
The two `upLane qc option` tests booted a real stack and then only released
the slot. `downLane` locates a service's pid file through the lane's slot
directory, so releasing the slot first orphaned the child with nothing left
able to reach it — one `python3 -m http.server` survived every run, holding
a port from a pool that is only ten wide. Thirteen had accumulated; the
eleventh run onwards fails with EPORTBUSY in whichever test boots next,
which reads as an unrelated flake.

Both tests now stop the stack before releasing the slot, and assert the port
went quiet — so a teardown that breaks again fails here rather than leaking
into the next run. A suite-level `after` covers the case a test throws
before its own teardown; it runs before SUITE_ROOT is removed, since the pid
files it needs live inside it.
2026-08-07 10:01:01 +07:00
nntrivi2001 201eae68bb fix(client): repair the TypeScript build
`npm run build` runs `tsc -b` first and it has been failing: `api.ts` used
`NamedLock` without importing it, and four lane test fixtures predate
`Lane.active_feature_id` / the widened `LaneRuntime`, so spreading a
`Partial<Lane>` over them no longer satisfied the required fields.

Nothing shipped could be rebuilt while this was red, which is how a client
change reaches a dashboard running in production mode. The fixture fixes are
casts with a note, not type relaxations — the base literals still list every
required field, so the assertion states what they already prove.
2026-08-07 09:52:13 +07:00
nntrivi2001 8fcef5a10b feat(lanes): let Add Lane choose the pipeline template
Creation is the only point the UI could ever set a lane's template, and it
never offered the choice — so every lane added from "+ Add lane" was born
on `default` and rendered an 8-node map for a 16-node workflow, with no
screen able to change it afterwards. That is the defect that made the
ship-feature template unreachable from the browser.

The modal now shows a *Pipeline template* select fed by
`GET /api/lanes/pipelines`, labelled with each template's node count so the
consequence of the choice is visible. A failed fetch degrades to a `default`
option rather than blocking lane creation.

`pipeline` was already accepted by `POST /api/lanes` but silently dropped by
`/ensure` and `/worktree`, which build their own createLane payloads; both
now pass it through, and both map `EBADPIPELINE` to 400 like `EBADCWD`.
2026-08-07 09:44:48 +07:00
nntrivi2001 67edda77eb feat(lanes): make the pipeline map track a skill's real progress
A lane's pipeline map only ever moved when a skill remembered to call
`ccam stage`, and the ship-feature template shipped with no detection rules
at all — so a lane driven by Superpowers skills sat at whatever stage it
last declared, and the `gates` node was never declared by anything.

Detection (`detect` rules on each node) now covers the Superpowers skill
invocations and the `ccam`/`gh` commands the ship-feature-lane skill
actually runs. It stays a safety net, not the mechanism: forward-only,
never `done`, never overriding a declaration. Two rules were deliberately
left out — `git diff` on `review` (this repo's own tests record it pinning
a lane at `review` on a real session) and anything on `merged`/`done`.

Stage vocabulary grows to 50 names over the same 16 nodes, following
Shipyard's PHASES shape: sub-states like `migration-collision`,
`e2e-scoped` and `gate-blocked` say WHY a lane sits on a node without the
map growing a node per reason. Every alias has a source — the skill
declares it, `default.json` uses it, or Shipyard's PHASES lists it.

Two silent failures fixed along the way:

- `lane.stages` is keyed by the raw declared string, so a stage declared
  under an alias lost its `--evidence` and rendered amber instead of
  green. `stageRecords` resolves each key onto its node.
- `ccam stage <typo>` stored fine and then rendered nowhere. It now warns
  on stderr while still exiting 0.

`ccam lanes pipeline` closes the gap that made all of this invisible: a
lane could only be assigned a template at creation, and no screen in the
web UI offers the choice, so every lane added from "+ Add lane" was stuck
on `default`'s 8 nodes. An unknown template id is now refused rather than
silently falling back to `default` on read.

Also merges the repo's own `ship-feature` skill into the Superpowers
workflow: it delegates planning/TDD/review/verification instead of
restating them, and declares a stage at each phase.
2026-08-07 09:34:20 +07:00
nntrivi2001 87b5e1c3db fix(lanes): make Add Lane mode segments fill their row and read as selected
The two-way segment used a translucent accent wash for the active state,
which at this size read as a hover tint rather than a selection. Solid
accent plus a shadow makes the choice unambiguous, and `flex-1` stops the
two segments from sizing to their label text.
2026-08-07 09:29:59 +07:00
nntrivi2001 61443f4814 chore(deps): resync package-lock license and funding with package.json
The lockfile still carried `MIT` and a `funding` URL pointing at an
unrelated sponsors page, both left over from the template this project was
scaffolded from. package.json declares `UNLICENSED` and no funding.
2026-08-07 09:28:28 +07:00
nntrivi2001 bd829ba1c3 fix(lanes): capitalize Vietnamese action-button labels, gitignore .ccam/
action.start/stop/clear/forget/purge/remove/reset in lanes.json (vi)
were lowercase (bắt đầu, dừng...) while every other button label in the
app capitalizes its first letter. Also gitignore /.ccam/ - the local
lane profile that appears in a repo's own working tree only when that
repo is adopted as its own lane (machine-specific runtime config, not
source).
2026-08-06 16:14:55 +07:00
nntrivi2001 78f6e1be8e feat(lanes): Add Lane repo/worktree mode toggle, manual branch, folder browse
- Repo mode adopts a directory as-is via /lanes/ensure (no worktree, no
  branch fields) - the right choice for a main repo you want stage
  detection on. Worktree mode (default) keeps the existing provisioning
  flow but now requires a manually-typed branch name instead of deriving
  one from the title.
- POST /lanes/worktree accepts an optional `branch`, validated via
  `git check-ref-format --branch`; omitting it preserves the CLI's
  existing auto-derived-branch behavior.
- New GET /lanes/browse lists a directory's immediate subdirectories,
  backing a small folder-browse modal on both path fields - browsers
  cannot expose an absolute path from a native picker, so this is
  server-backed instead, consistent with the tool's local-first model.
2026-08-06 16:03:49 +07:00
nntrivi2001 9f13769fb4 feat(lanes): surface child worktrees on a lane card + detect Superpowers skills
Adopting the main repo as its own lane now gets stage detection
(cwd matches, same as any other lane), and its card lists every
managed-worktree lane provisioned from it with a jump-to link.
Also add the two missing Skill-tool detect rules (implement, ship)
so detection covers all four Superpowers workflow phases, not just
plan/review.
2026-08-06 15:33:07 +07:00
nntrivi2001 54299f119e fix(lanes): wait for worktree provisioning before auto-setup
POST /worktree answers with 202 before the background git worktree
add finishes, so profileInit/agentsInstall/mcpSync were racing the
lane's own directory into existence and mostly failing. Poll
GET /api/lanes/:id until provisioning leaves "provisioning" first.
2026-08-06 14:28:21 +07:00
nntrivi2001 e31d261fd7 fix(lanes): don't auto-close Add Lane modal after setup summary
The 3s auto-close timer closed before a user reasonably had time to
look at the setup results, making the feature appear to do nothing.
Require an explicit dismiss (Cancel/X) instead.
2026-08-06 13:55:43 +07:00
nntrivi2001 7fa39687fe feat(lanes): show Add Lane auto-setup summary before closing modal
The three setup calls (profile/agents/mcp) fired after worktree
creation but their outcome was only logged to the console. Keep the
modal open with a ✓/✗ summary for a few seconds (or until dismissed)
so the user actually sees what happened, matching the original F5 design.
2026-08-06 13:31:56 +07:00
nntrivi2001 77f3805083 docs(lanes): document Add Lane auto-setup (F5) 2026-08-06 12:00:14 +07:00
nntrivi2001 dd0cb4ad7c feat(lanes): auto-setup (profile/agents/mcp) after Add Lane (F5) 2026-08-06 11:49:11 +07:00
nntrivi2001 5dd4793b88 feat(lanes): add POST /:id/profile/init route (F5) 2026-08-06 11:42:09 +07:00
nntrivi2001 64c00dea85 docs(lanes): plan F5 — auto-setup after Add Lane (E)
3 tasks: POST /:id/profile/init route (wraps already-built
lane-detect.js), AddLaneModal.tsx wiring (Promise.allSettled over
profile-init + agents-install + mcp-sync, none blocking lane creation
or each other) + client API method + tests, and docs.
2026-08-06 11:35:36 +07:00
nntrivi2001 ab6005afdc docs(lanes): design F5 — auto-setup after Add Lane (E)
One click instead of four: after AddLaneModal creates a worktree lane,
fire profile-init + agents-install + mcp-sync in parallel
(Promise.allSettled, best-effort — none of the three blocks lane
creation or each other). Needs one new route (POST
/:id/profile/init, wrapping already-built lane-detect.js) since
profile-init was CLI-only until now.
2026-08-06 11:31:29 +07:00
nntrivi2001 14dbfa73e5 test(lanes): cover F4's LaneCard additions + regenerate screens snapshot 2026-08-06 10:30:17 +07:00
nntrivi2001 57dd4ffcb7 feat(lanes): add Lanes section (skills install, housekeeping) to Settings (F4) 2026-08-06 10:22:58 +07:00
nntrivi2001 6d8c5399ea feat(lanes): add agents-install/mcp-sync/integration/sync-check to LaneCard (F4) 2026-08-06 09:59:33 +07:00
nntrivi2001 bc7c5e5431 feat(lanes): add client API methods for F4 lane actions (F4) 2026-08-06 09:47:10 +07:00
nntrivi2001 e32c684bf0 feat(lanes): add POST /api/lanes/gc route (F4) 2026-08-06 09:31:42 +07:00
nntrivi2001 e7ef7bcef9 feat(lanes): extract skills-install lib, add POST /api/skills/install (F4) 2026-08-06 09:26:43 +07:00
nntrivi2001 99465d2095 docs(lanes): plan F4 — lane actions UI (LaneCard + Settings)
6 tasks: skills-install lib extraction + route, gc route, client API
methods, LaneCard additions (agents install/mcp sync/integration
badges/sync-base check — read-only, no merge button), Settings
additions (skills install + housekeeping), and test coverage
(LaneCard.test.tsx cases + screens snapshot regen).
2026-08-06 09:17:37 +07:00
nntrivi2001 5f114d7c6f docs(lanes): design F4 — surface E1-F3c lane actions in the UI
LaneCard gets agents-install/mcp-sync buttons, integration status
badges, and a sync-base --check button (read-only preflight only — no
merge button, that stays a session/skill action). Settings gets
skills-install and housekeeping (gc) buttons, machine-wide. Two new
routes needed (POST /api/skills/install, POST /api/lanes/gc) since
those two primitives were CLI-only until now.
2026-08-06 09:09:30 +07:00
nntrivi2001 d0f42254ee docs(lanes): document ccam lanes gc (E, F3c) 2026-08-05 17:46:42 +07:00
nntrivi2001 5e620bd8ab feat(lanes): add ccam lanes gc — orphan MCP reap + log capping (E, F3c)
Ports the two pieces of Shipyard's lane-gc.sh that match CCAM's actual
architecture: kill Playwright MCP processes reparented to pid 1 (owning
session died), cap hook logs over 10MB back to their last 2MB in place.
Drops auto-removing stale worktrees by age (conflicts with the
never-automatic-destroy rule), state archiving, and scratch-debris
sweep (different storage architecture / files CCAM doesn't generate) —
see docs/superpowers/specs/2026-08-05-lane-gc-design.md.
2026-08-05 17:44:46 +07:00
nntrivi2001 e2d516f199 docs(lanes): design F3c — ccam lanes gc (E)
Scopes lane-gc.sh down to the two pieces that match CCAM's actual
architecture (orphan Playwright MCP reap, oversized-log capping).
Drops auto-removing stale worktrees by age — that's exactly the kind
of automatic destructive action this repo's own CLAUDE.md forbids
(destroy always goes through the three-check guard, never automatic).
State archiving and scratch-debris sweep don't apply either (different
storage architecture; CCAM doesn't generate those files).
2026-08-05 17:38:04 +07:00
nntrivi2001 f157977783 docs(lanes): document ccam skills install (E, F3b) 2026-08-05 17:19:11 +07:00
nntrivi2001 6d24f9d7ad feat(lanes): add ccam skills install (E, F3b)
Copies .claude/skills/ship-feature-lane/ into ~/.claude/skills/, so
/ship-feature-lane is discoverable from a session running inside any
lane's own working directory — not just inside this repo, which is
where it lived until now (Claude Code only auto-discovers .claude/
directories from the repo that owns them). Pure filesystem action,
same self-location REPO_ROOT already gives lanes profile init.
2026-08-05 17:16:38 +07:00
nntrivi2001 c781fcdb45 docs(lanes): design F3b — ccam skills install (E)
Found auditing E1-F3a for Shipyard parity: .claude/skills/ship-feature-lane/
only exists inside ccam-lanes' own repo, never installed globally, so
/ship-feature-lane is unreachable from its actual intended invocation
context (a session inside some OTHER lane's repo). Shipyard's own
install-claude-assets.sh solves this for its bundled skills the same way.
2026-08-05 17:12:37 +07:00
nntrivi2001 0006fabb4c docs(lanes): wire ship-feature-lane to the real integration toggle check (F3a) 2026-08-05 16:37:28 +07:00
nntrivi2001 32dc4e432a feat(lanes): add ccam lanes integration CLI + route (F3a) 2026-08-05 16:30:23 +07:00
nntrivi2001 8fc7a32490 feat(lanes): add isIntegrationEnabled toggle check (F3a) 2026-08-05 16:12:58 +07:00
nntrivi2001 36d66cda07 docs(lanes): plan F3a — ccam lanes integration toggle reader (F)
3 tasks: isIntegrationEnabled in lane-profile.js (reuses parseEnvFile),
the route + CLI, and wiring SKILL.md's hardcoded-off Setup text to the
real check (still no-op behaviorally — no ticketer/dev-qc agent exists
to act on an enabled toggle yet, but the check itself is now honest).
2026-08-05 16:07:33 +07:00
nntrivi2001 4b3a1e3d25 docs(lanes): design F3a — ccam lanes integration toggle reader (F)
Scopes down F3 to just the generic <name>_ENABLED toggle-check primitive
(ccam lanes integration <name>) — ticketer/dev-qc agents stay deferred,
same reasoning E3 already used for qc-local's credential gap: no tracker/
dev-QC MCP configured anywhere, no per-lane credential source to consume.
2026-08-05 16:04:39 +07:00
nntrivi2001 ef686454b9 docs(lanes): document ccam lanes mcp sync (F1) 2026-08-05 15:39:04 +07:00
nntrivi2001 60fb7cbaeb feat(lanes): add ccam lanes mcp sync CLI (F1) 2026-08-05 15:34:15 +07:00
nntrivi2001 8471b3a757 feat(lanes): add POST /:id/mcp/sync route (F1) 2026-08-05 15:28:31 +07:00
nntrivi2001 2731b7c8ce feat(lanes): add lane-mcp sync core (F1) 2026-08-05 15:20:25 +07:00
nntrivi2001 d25a20089a docs(lanes): plan F1 — ccam lanes mcp sync (F)
4 tasks: the lane-mcp.js sync core (relocate + pin + seed profiles, no
permission/settings writes per the design spec's scope decision), the
route, the CLI, and docs.
2026-08-05 15:14:31 +07:00
nntrivi2001 4da7ef0453 docs(lanes): design F1 — ccam lanes mcp sync (F)
Scopes the first of F's four pieces to .mcp.json relocation + Chromium
profile seeding only. Drops Shipyard's permission/auto-approval writes
(no existing CCAM pattern for a backend command granting permissions,
and the rules named agents not in scope: pr-reviewer has no driving
skill to port, ticketer is a later task) and the node-version wrapper
(environment-specific workaround, no evidence this repo needs it).
2026-08-05 15:10:42 +07:00
nntrivi2001 f6fa1ea42d fix(lanes): SKILL.md — unblock Stage 6/7, add PR-base guard at Stage 12 (E1)
Two gaps found via audit against Shipyard's source:

1. Stage 6/7 still said "this agent does not exist yet" and told a
   driving session to treat any lane reaching those stages as blocked —
   stale since E3 shipped qc-local + senior-gate-reviewer. Replaced with
   an unconditional launch plus a one-time "ccam lanes agents install"
   preflight note in Setup.

2. Stage 12's conflict-resolution path called `ccam lanes sync-base`
   directly on any CONFLICTING PR, with no check that the PR's base is
   actually `development` first. Shipyard's original has this guard
   (its own "legacy main-based PR" case) — ported here in general form:
   never auto-merge into a PR whose base drifted from `development`.
2026-08-05 14:40:57 +07:00
nntrivi2001 d2327408fb fix(lanes): mergeSync commits a fully rerere-auto-resolved merge instead of rethrowing (E2)
Root cause: the catch block only fell through to the auto-commit path when
`unmergedFiles().length && MERGE_HEAD exists` — but a merge rerere resolved
completely has ZERO unmerged files (git already staged the resolution), so
that guard was always false and the raw git error was rethrown instead.
Found via an audit against the Shipyard source this was ported from.

Fixed by checking MERGE_HEAD first (unconditionally — its absence means the
merge never started, a real failure), then branching on whether any files
are still unmerged. Added a real rerere fixture test (teach a resolution,
recreate the identical conflict, confirm mergeSync auto-commits) — the
existing test suite had no coverage for this path.
2026-08-05 14:39:02 +07:00
nntrivi2001 02b90292c0 docs(lanes): document ccam lanes agents install (E3) 2026-08-05 12:52:42 +07:00
nntrivi2001 727823d75e feat(lanes): add ccam lanes agents install CLI (E3) 2026-08-05 12:46:56 +07:00
nntrivi2001 3fb70a3fc1 feat(lanes): add POST /:id/agents/install route (E3) 2026-08-05 12:42:27 +07:00
nntrivi2001 4134b3d307 feat(lanes): add lane-agents install core (E3) 2026-08-05 12:37:40 +07:00