Commit Graph

124 Commits

Author SHA1 Message Date
nntrivi2001 31af7aefbf docs: update Run feature docs for the tmux+PTY terminal (was stream-json)
- README.md: replace stream-json bullet with tmux+PTY description
- ARCHITECTURE.md: update /run Workspace page section to reflect PTY transport, xterm.js, and ccam lanes shell
- docs/API.md: replace stream-json endpoint docs with /api/run/*+/ws-pty/:runId PTY endpoint docs
- docs/LANES.md: add "Attaching a real terminal to a lane" section for ccam lanes shell; update lane "start" action docs and /run Workspace description
- Remove stale references: run_stream, run_input_ack, conversation/headless modes, RunConsole components
2026-08-12 15:35:57 +07:00
nntrivi2001 6709f9a192 feat(cli): add 'ccam lanes shell' to attach a real terminal to a lane's tmux session 2026-08-12 14:15:37 +07:00
nntrivi2001 b951f64321 fix(run): plumb the recorded prompt back into live runs, fix lane routing when the cwd doesn't match the selected lane
- pty-run.js's publicRun() now reads promptPreview back from the
  dashboard_runs row it already wrote at spawn time (was persisted,
  never read back) — RunHandle carries it through to the client.
- Workspace.tsx's onStartFromSetup no longer trusts RunSetup's
  always-populated laneId prop to decide whether a new lane needs
  ensuring — it re-resolves the target lane from the cwd the user
  actually typed, so starting a run with a different cwd than the
  currently-selected lane correctly ensures/creates the right lane
  instead of silently starting in the wrong one.

Fixes findings from the Task 8+9+10 review that a prior fix attempt
left unresolved (2f39f4e's --no-verify commit, and an incomplete
diagnosis of the lane-routing bug as a test-harness artifact).
2026-08-12 13:18:04 +07:00
nntrivi2001 2f39f4ec98 feat(run): wire Workspace to TerminalView, delete the stream-json Run feature
Combines three tasks that couldn't land as separate commits: the
pre-commit hook's full test run crashes on any intermediate state
where Workspace.tsx still imports the files being deleted, so the
deletion (old RunConsole/useRunStream/run-spawner/stream-json-parser),
the RunSetup/RunHistory type adjustments, and this file's own
TerminalView wiring had to be staged together and committed as one
hook-passable unit.

- Delete RunConsole.tsx, useRunStream.ts, server/lib/run-spawner.js,
  server/lib/stream-json-parser.js and their tests (Task 8).
- Adjust RunSetup.tsx/RunHistory.tsx to the tmux-backed RunHandle/
  RunStartArgs/DashboardRunHistoryItem shapes, remove mode selection
  UI (Task 9).
- Swap Workspace.tsx's chat-bubble run console for TerminalView
  (xterm.js over /ws-pty/:runId), drop the stream-json envelope
  plumbing, update Start/Resume to the new RunStartArgs payload.
  Create onStartFromSetup handler to work with RunSetup's new callback
  shape. Remove mode state and related plumbing. Remove send/followUp
  state (no longer using old RunConsole chat interface).
- Add promptPlaceholderTerminal i18n key to support RunSetup's new
  placeholder text (Task 10).
- Update Workspace.test.tsx to mock TerminalView component.
- Regenerate screens.snapshot.test.tsx snapshot (only Workspace run
  panel changes: terminal container instead of chat bubbles).
2026-08-12 11:58:38 +07:00
nntrivi2001 f1e7d4245a test(lanes): add test for stale run_id clearing during healing
Add missing test coverage for healRunId's core behavior: that a STALE run_id
(tmux session gone) gets CLEARED to null with status: idle when read via GET.
The existing test only verified the LIVE case (session still running). This test
proves the release-on-gone path, simulating a session death via tmux mock.
2026-08-12 11:08:21 +07:00
nntrivi2001 82bf803c2e fix(lanes): bridge routes/lanes.js to pty-run.js
Replace run-spawner imports and APIs with pty-run:
- Import pty-run instead of run-spawner
- Delete setRunExitHandler registration, replace with read-time self-heal in payload()
- Remove mode validation (mode no longer exists in pty-run)
- Update spawnRun call to use new parameter names (initialPrompt, not prompt/mode)
- Replace "message" action with explicit 400 EUNSUPPORTED response
- Fix stopLaneRun to poll on status !== "gone" instead of !actualExitedAt

Adapt tests to tmux-based run model:
- Delete tests about mode-specific behavior (removed feature)
- Rewrite lane release tests using tmux.__setExecImpl mocks instead of withFakeClaude
- Update assertions to check status === "gone" instead of specific exit codes
- Update ERUNTIMEOUT test to mock tmux sessions instead of child processes

All lane-related tests pass; only pre-existing port conflicts in lane-detect.test.js remain.
2026-08-12 10:53:23 +07:00
nntrivi2001 24f13911fe feat(run): replace RunHandle/RunStartArgs types and api.run for the tmux backend 2026-08-12 10:24:45 +07:00
nntrivi2001 9b8d9bbe39 feat(run): add TerminalView xterm.js component for the PTY transport
- TerminalView.tsx: xterm.js component with WebSocket attachment to /ws-pty/:runId
- Test: validates WS connection URL, incoming terminal data, and outgoing keystrokes
- Added ResizeObserver stub to test-setup.ts for jsdom environment
2026-08-12 10:19:10 +07:00
nntrivi2001 872c698132 feat(run): add /ws-pty/:runId PTY transport bridging WS to tmux attach 2026-08-12 10:13:14 +07:00
nntrivi2001 1bc237198c feat(run): rewrite routes for the tmux backend, drop stdin-message endpoint 2026-08-12 09:46:55 +07:00
nntrivi2001 56744b360d feat(run): add tmux-backed run lifecycle (spawn/kill/list computed from tmux state) 2026-08-12 09:38:25 +07:00
nntrivi2001 1dd18fe98c feat(run): add tmux command wrapper with an injectable exec seam 2026-08-12 09:34:18 +07:00
nntrivi2001 d96d552428 chore: add node-pty/xterm deps, tmux in Docker, dashboard_runs.tmux_session column 2026-08-12 09:29:44 +07:00
nntrivi2001 00f6338d4c docs: bring plan and spec into the tmux-terminal-run worktree
These were committed on main's local history but this worktree
branched from origin/main, which doesn't have them yet — copying the
files in so subagent-driven-development has a plan to read from this
branch.
2026-08-12 09:25:34 +07:00
nntrivi2001 dfea1a99d6 fix(tests): scrub GIT_* env vars leaking from the pre-commit hook into git-fixture tests
server/lib/update-check.js's execGit() and two test helpers
(lanes-cli.test.js, update-check.test.js) shelled out to git with an
explicit `cwd` but no `env` override. A parent git hook process (this
repo's own .husky/pre-commit, which runs `npm run test:server`) sets
GIT_DIR/GIT_INDEX_FILE in its own environment; those leak to every
child process and take precedence over `cwd` for repo discovery, so
every git command these tests ran against their throwaway tmp repos
was silently redirected at the real repo running the hook instead —
reproduced firsthand as four foreign "init"/"fixture" commits
overwriting a worktree branch mid pre-commit run. Fixes it the same
way server/lib/worktree.js already documented and did for its own git
calls: strip the GIT_* vars before exec.
2026-08-12 09:24:03 +07:00
nntrivi2001 7357070fb9 chore: remove unused desktop app, cloud deployment infra, and monitoring stack
Deletes desktop/ (Electron wrapper), deployments/ (Helm/Kustomize/
Terraform/CI for cloud deploy), and monitoring/ (Prometheus + Grafana
stack) along with DESKTOP.md, DEPLOYMENT.md, docker-compose.full.yml,
their npm scripts, and every dangling reference across README,
ARCHITECTURE, INSTALL, SETUP, docs/, and the repeated per-file
MODULE_GUIDE "Observability" boilerplate comment. The GET /api/metrics
endpoint itself is untouched — it's the dashboard's own route, not
part of the removed monitoring stack.
2026-08-11 12:16:54 +07:00
nntrivi2001 f0ae876472 feat(lanes): add pipeline-template picker to the Workspace lane header
Lets a lane's pipeline template be switched live from the dashboard
(the same PATCH /api/lanes/:id the CLI's `ccam lanes pipeline` uses),
so lanes created before the picker shipped don't need the terminal.
Both ship-feature skills now force their own template before their
first `ccam stage` call, so the human never has to pick correctly at
lane creation.
2026-08-11 12:15:16 +07:00
nntrivi2001 f6946d72f4 docs(plugins): fix inaccurate uninstall cleanup instructions
Verified against a real claude plugin uninstall: it only drops the plugin
from the enabled list. The server keeps running, the cached source stays on
disk, and the hook entries claude plugin install wrote into settings.json
are left behind pointing at the now-uninstalled cache dir — silently fails
once Claude Code eventually GCs it. The previous instructions ("uninstall
removes the hooks and the cached source") were untested assumptions; this
adds the missing settings.json cleanup step.
2026-08-10 16:56:40 +07:00
nntrivi2001 205f40c29c fix(plugins): stop stripLegacyHooks from deleting the plugin's own hooks
claude plugin install materializes the plugin's inline hooks into
~/.claude/settings.json itself, with \${CLAUDE_PLUGIN_ROOT} resolved to the
actual cache path — confirmed by installing the plugin for real and
inspecting the file. Those entries also contain "hook-handler.js", so
isOurEntry()'s plain substring match could not tell a legitimate
plugin-installed hook from a leftover npm run install-hooks entry: every
SessionStart would have stripped the plugin's own working hooks right back
out. isCheckoutHookEntry() only removes entries whose command does NOT
resolve under ~/.claude/plugins/cache/. plugin-doctor.js's duplicate-hook
count uses the same predicate.
2026-08-10 16:28:59 +07:00
nntrivi2001 a65ee1512e fix(plugins): serverIsLive() falsely reported a server as running
resolveAllDashboardPorts() falls back to [DEFAULT_PORT] when the discovery
file has no live entry — a reasonable guess for the CLI/hook handler, but
wrong for the bootstrap's own liveness check: with no server running at all,
the bootstrap believed one was already up and never called startDashboard(),
confirmed against a real plugin install where the dashboard never started.
plugin-doctor.js's "Server" row had the same bug. Both now read the discovery
file directly and check PID liveness via the new liveServers() (livePids()
reused it instead of duplicating the read).
2026-08-10 16:22:13 +07:00
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