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.
Claude Code Monitor
Internal SmartGift build. Local-first dashboard for Claude Code: hooks POST every tool call to an Express + SQLite server, a React UI updates over WebSocket, and lanes track parallel agent work through a pipeline.
Internal build — all rights reserved.
What it does
- Sessions, agents, events. Everything Claude Code emits, recorded and searchable: tool calls, token usage, cost, subagent trees, transcripts.
- Lanes. One lane per working directory, surviving session restarts. A lane moves through pipeline stages and the dashboard shows where it is.
- Stage detection. The stage is inferred from the tool stream, so a session
that never calls
ccam stagestill shows progress — rendered dashed amber and never as done, because an inference is not evidence. - Run Claude from the browser. Spawn a session in a lane's directory, stream its output, send follow-ups, resume any past session.
- Analytics, alerts, Kanban and a workflow view, plus an MCP server and a CLI.
Requirements
Node >= 20 (engines in package.json). Node 24 is what the test suites
are verified on — node 25 currently breaks 6 server tests through a
better-sqlite3 ABI mismatch and 20 client tests through a global localStorage
change.
Install and run
npm run setup # root, client and vscode-extension dependencies
npm run build # builds the client into client/dist
npm start # serves the built client and the API on :4820
Open http://localhost:4820.
Development, with hot reload:
npm run dev # server on :4820, Vite client on :5173
DASHBOARD_PORT overrides the port. postinstall writes the Claude Code hook
entries that feed the dashboard.
The CLI
ccam is linked by npm run setup; otherwise call node bin/ccam.js.
ccam status # is the dashboard up
ccam start # start it in the background and wait for healthy
ccam sessions # recent sessions
ccam lanes # lanes with stage and progress
ccam stage <name> # declare the current lane's stage
ccam tail # live event feed
ccam --help lists the rest.
Lanes
A lane is a working directory the dashboard watches. Two kinds:
- adopted — a directory you already had. The dashboard only reads it; it is never reset or deleted.
- managed — a git worktree the dashboard created under
LANES_ROOT. It owns the full lifecycle and may reset or remove it, behind a three-check destroy guard and a counted preflight the caller has to echo back.
ccam lanes add --cwd /path/to/repo --title "My feature" # adopt
ccam lanes add --repo /path/to/repo --slug my-feature # managed worktree
The declared stage comes from ccam stage. The inferred stage comes from tool
events and expires after DETECTION_TTL_MS (default 5 minutes), so a lane can
move backwards between work sessions. Detection never writes the declared stage,
and an inferred node never renders as done.
A lane can also run its own application stack, isolated per lane, when its
repository declares a profile at <repo>/.ccam/profile/ — a profile.env of
declarations plus shell hooks the dashboard calls. Each lane gets a slot, and its
ports and per-lane directories derive from it:
ccam lanes up # boot the stack of the lane owning this directory
ccam lanes runtime # slot, ports, service health
ccam lanes logs api # tail a service log
ccam lanes down
Services are fully detached, so restarting the dashboard never stops a running lane. This is resource namespacing on the host, not a container: lanes run as the same user and share the network.
docs/LANES.md has the pipeline model, the destroy guard, the
preflight contract, the Workspace page, GET /api/lanes/:id/git, and the full
runtime/profile contract.
Tests
npm run test:server # node:test
npm run test:client # Vitest
Both must be green before a commit; the pre-commit hook runs them plus Prettier.
Layout
| Path | What |
|---|---|
server/ |
Express API, SQLite schema, hook ingest, lane and worktree libraries |
client/ |
React 18 + Vite + Tailwind dashboard |
bin/ccam.js |
CLI |
mcp/ |
MCP server exposing read-only dashboard tools |
desktop/ |
Electron wrapper that embeds the server |
docs/ |
Architecture, API, lanes, database, deployment |
plugins/ |
Claude Code plugins shipped with the dashboard |
Docs
ARCHITECTURE.md— request flow, schema, WebSocket surfacedocs/LANES.md— lanes, pipelines, stage detectiondocs/API.md— REST endpoints (openapi.yamlis generated)docs/DATABASE.md— tables and migrationsINSTALL.md·DEPLOYMENT.md·DESKTOP.mdCLAUDE.md— the rules an agent working in this repo must follow