A lane on the 16-node `ship-feature` template showed an untouched pipeline
map and never declared a stage. Four independent causes, fixed at their
shared choke points:
1. Codex emits no hooks, so `touchLaneFromHook` — the only caller of
`stage-detect.detect()`, and correctly pinned to `claude` — could never
see a `codex` lane. New `server/lib/codex-ingest.js` tails Codex's own
rollout journals (`$CODEX_HOME/sessions/**/rollout-*.jsonl`), maps its
tool calls onto the hook-shaped `{tool_name, tool_input}` the matcher
already understands, and feeds the existing detect path plus an
idle/running status mirror under the same guard hooks use. Polled every
`DASHBOARD_CODEX_POLL_MS` ms (default 15000, `0` disables).
2. `resolveLaneArg` matched on cwd alone, so with a claude and a codex lane
at one directory (the composite `idx_lanes_cwd_tool`) a `ccam stage`
call could drive the sibling lane's card. It now resolves by
`--lane` → `CCAM_LANE_ID` → `--tool`/harness marker → owning lane, and
errors naming both candidates rather than picking one. Every pane the
dashboard starts carries `CCAM_LANE_ID` (`tmux new-session -e`, or an
assignment prefix when typing into a live shell pane).
3. `recordDetection`'s declared-wins veto had no exit: a lane left at the
pipeline's last node rejected every detection for the rest of its life.
A declaration on the LAST node no longer vetoes — the pipeline is over
there, so a later detection is new work. Nothing else is relaxed, and
detection still never writes `stage` nor renders `done`.
4. The run console pre-filled the pipeline's Claude slash command into
codex panes, which Codex reads as prose — so the skill that would have
called `ccam stage` never ran. Claude lanes only now.
A codex lane's *declared* stage still depends on the session calling
`ccam stage`; `AGENTS.md` is the only lever CCAM has, so it now says so.
Verified: server 1345/1345, client 441/441, and end-to-end against a real
codex lane row (detected_stage/detected_signal set, declared stage
untouched, no node rendered done).
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.
(Tiếng Việt: README.vi.md)
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 Code or Codex from the browser. A real terminal (tmux + a real
PTY, rendered with xterm.js) attached to a lane's directory — the exact TUI
you'd see locally, fully interactive, resumable, and attachable from a real
terminal too via
ccam lanes shell. Which CLI it starts is the lane's own setting. - Analytics (in-dashboard tab), alerts, 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 as a Claude Code plugin
Two commands on a machine that has nothing but Claude Code, no clone and no
npm run setup:
/plugin marketplace add Smartgift-AI/Claude-Code-Monitor
/plugin install ccam@claude-code-agent-monitor-plugins
The first session start installs the hooks, boots the server, puts ccam on
PATH and connects the MCP tools; it runs detached, so the session never waits on
it. /ccam-doctor reports the state, /ccam-open builds the UI and prints the
URL, /ccam-update refreshes after a plugin update. This path needs Node
>= 22.5 (no native better-sqlite3, so the server uses node:sqlite).
Details, including what to delete on uninstall:
docs/PLUGINS.md.
Install from a checkout
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, DASHBOARD_CLIENT_DIST overrides where the
built UI is served from (defaults to client/dist; the plugin install points it
at its own runtime directory). postinstall writes the Claude Code hook entries
that feed the dashboard — do not run it when the ccam plugin is installed, or
every event is counted twice.
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 lanes pipeline # this lane's pipeline template, or switch it
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
Every lane also carries the CLI that drives it — claude (the default) or
codex — picked in the Add-lane modal and fixed for the life of the lane. A
Codex lane's run panel offers Codex's own sandbox, approval and reasoning-effort
choices, and resumes with codex resume --last. Codex emits none of this
dashboard's hooks, so a Codex lane records its runs but stays absent from
Sessions and Analytics. Its stage detection works anyway, from Codex's own
rollout journals on disk ($CODEX_HOME/sessions/**/rollout-*.jsonl, polled every
DASHBOARD_CODEX_POLL_MS ms, default 15000) — what a Codex lane cannot get for
free is a declared stage, since only the session itself can call ccam stage.
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's pipeline template can be switched after creation — ccam lanes pipeline <template> from the terminal, or the pipeline-template picker next to
the lane's title in the Workspace detail panel. Both re-resolve the lane's
current declared stage against the new template's nodes and warn if it no
longer matches one.
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 |
docs/ |
Architecture, API, lanes, database |
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.mdCLAUDE.md— the rules an agent working in this repo must follow