π A real-time monitoring dashboard for Claude Code & Codex, built with SQLite3, Node.js, Express, React, Vite, TailwindCSS, & WebSockets. It tracks sessions, agent activity, tool usage, and subagent orchestration, providing live analytics, a Kanban status board, status notifications, a cute buddy, & an interactive web UI/MacOS/Windows native app.
FAQ
claude-code-agent-monitor is a Claude Code plugin with 59 hand-picked skills for development work, indexed on Flowy. Install it with the command on its page. It includes debug-live-issue, file-headers, mcp-operations. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.
> /plugin marketplace add hoangsonww/Claude-Code-Agent-Monitor
A professional dashboard to track and visualize your Claude Code and Codex agent sessions, tool usage, and subagent orchestration in real-time. Built with Node.js, Express, React, and SQLite, it integrates directly with Claude Code and Codex via their native hook systems for seamless session tracking and analytics.
[!TIP] See also: README-CN.md (δΈζηζ¬), README-VN.md (PhiΓͺn bαΊ£n tiαΊΏng Viα»t), README-KO.md (νκ΅μ΄ λ²μ ), and README-ES.md (versiΓ³n en espaΓ±ol) for localized documentation with region-specific tips and best practices.
Track sessions, monitor agents in real-time, visualize tool usage, and observe subagent orchestration through a professional dark-themed web interface. Integrates directly with Claude Code & Codex via their native hook systems.
graph LR
A["Claude Code<br/>Session"] -->|hooks fire on<br/>tool use / stop| B["Hook Handler<br/>(Node.js script)"]
B -->|HTTP POST| C["Dashboard Server<br/>(Express + SQLite)"]
C -->|WebSocket<br/>broadcast| D["Dashboard UI<br/>(React + Tailwind)"]
style A fill:#6366f1,stroke:#818cf8,color:#fff
style B fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style C fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style D fill:#10b981,stroke:#34d399,color:#fff
In addition to the real-time monitoring dashboard, it also includes a local MCP server implementation in mcp/ that exposes a catalog of tools for introspecting and managing the dashboard itself, making it easy to integrate dashboard operations directly into your Claude Code & Codex workflows. There is also an agent extension layer, which provides Claude Code & Codex plugins, skills, and subagents for dashboard interaction, analytics, and workflow intelligence.
The UI ships with built-in locale switching for English (en), Chinese (zh), Vietnamese (vi), Korean (ko), and Spanish (es). A custom language dropdown prevents the selector from crowding the sidebar as more locales are added. Language resources are loaded by namespace and persisted through browser storage for stable user preference across refreshes.
flowchart LR
A["User selects language<br/>en / zh / vi / ko / es"] --> B["i18next detector<br/>localStorage -> navigator"]
B --> C["Resolved language<br/>fallback: en"]
C --> D["Namespace resources<br/>common/nav/dashboard/..."]
D --> E["React useTranslation hooks"]
E --> F["Localized UI + accessibility labels"]
For full architecture and operational guidance, see docs/I18N.md.
Comes with a sleek dark theme, responsive design, and intuitive navigation to explore your agent activity:
The sidebar provides quick access to the Dashboard, Kanban Board, Sessions list, Activity Feed, Analytics, Workflows, and Settings. Each page is designed to give you deep insights into your Claude Code agent activity with real-time updates and rich visualizations.
The dashboard offers a comprehensive set of features to monitor and analyze your Claude Code sessions and agents:
Cursor sessions too (informational): CCAM ingests whatever agent transcripts land under
~/.claudeβ on this machine and on synced remotes. Cursor usage counts the same way: Cursor happens to store its agent sessions in those paths alongside Claude Code. CCAM does not distinguish which app wrote a file.
| Feature | Description |
|---|---|
| Dashboard | Two tabs persisted in localStorage: Monitor β overview stats (6 stat cards), active agent cards with collapsible subagent hierarchy, and recent activity feed with dynamic item counts that fill available viewport height via ResizeObserver. Health β composite system health score ring (weighted: 0.4 Γ success rate + 0.25 Γ cache hit rate + 0.25 Γ (100 β error rate) + 0.1 Γ (100 β heap %)), storage engine donut chart with record distribution, cache performance / error rate / success rate gauges, tool invocation horizontal bar chart (top 8), subagent effectiveness bars, model token distribution, and compaction impact stats. All health metrics auto-refresh every 5 s from /api/settings/info and /api/workflows. Cursor-following tooltips with viewport edge detection on every chart |
| Kanban Board | Two views with a header toggle (persisted in localStorage): Agents β 4 columns (Working / Waiting / Completed / Error) β and Sessions β 5 columns (Active / Waiting / Completed / Error / Abandoned). The Waiting column maps directly to the persisted waiting status on agents β set when Claude Code is sitting at a prompt (fresh session, between turns, or blocked on a permission Notification) and transitions to working the moment the user resumes (UserPromptSubmit / PreToolUse). Each column header shows a ? tooltip explaining lifecycle transitions. Cards fetch by persisted status from the server (effectively unlimited per status), then paginate client-side at 10 cards per column with a "Show more" affordance. WS subscription scopes to the active view (agent_* vs session_* frames) so off-view updates don't trigger refetches. Waiting badges expose the row's awaiting_reason as a hover tooltip β Needs input (notification), Turn done (stop), At prompt (session_start), Interrupted (interrupted) β kept tooltip-only on the compact cards so titles keep their space; wider surfaces (Sessions table, session-detail header) additionally show the reason inline as a nested chip, with urgent reasons (permission prompts, interruptions) in a hotter amber |
| Sessions | Searchable, filterable, server-paginated table of every recorded session. Each page click hits , so cost computation runs only over the visible page β independent of how many sessions exist in the database. The search box () does case-insensitive matching across / / on the server with a 300 ms debounce, and the response carries a count for the paginator UI. Status filter, search, and pagination compose. Each session's human-readable is read from the transcript and kept in sync in real time β an explicit title from , , or the picker's (the JSONL line) always wins, otherwise the auto-generated fills in, otherwise the session's (truncated, with tool-result / slash-command noise skipped) fills the placeholder name and the main agent's placeholder name/task β so sessions that never get a title (including imported ones) still say what they're doing; the dashboard surfaces that name (falling back to the short ID) on cards, the Dashboard, the Activity Feed, and the Run resume picker. |
Provider scope and homes: Settings keeps the Claude Code / Codex / Both choice globally consistent, and lets you change either session-data home without restarting the dashboard.
| Run Agent + Agent Config | /run begins with a Claude Code / Codex choice and keeps the provider toggle beside its Live status. Claude runs retain their headless and stream-json conversation modes; Codex runs use the CLI's local app-server protocol for a real interactive thread, native approval/sandbox policy, resume, stop, live output, and re-attach. Codex model choices come directly from the signed-in CLI, so model releases need no dashboard update; Claude shows its durable aliases plus locally observed models because its CLI has no model-list command. /cc-config pairs the established editable Claude Code explorer with a Codex workspace for config defaults, model cache, profiles, MCP, projects, skills, rules, hooks, installed plugins, and instruction files. Its normal previews redact secrets; the explicit local editor supports config.toml, hooks.json, user rules, skills, and instructions with atomic saves and mandatory timestamped backups, while warning that it cannot validate syntax. Codex profile commands and managed artifact paths copy in one click, and plugin cards use Codex's installed-plugin registry rather than showing cache folders. Both explorers refresh through their provider-specific filesystem watcher. |
| Codex Agent Config | The Codex half of Agent Config reads the full local account model catalog without the generic preview limit that could falsely show zero models, and always includes base/profile overrides. Create standard Codex <name>.config.toml overlays directly in the app; each card copies its exact codex --profile <name> command in one click and opens a guarded editor. Profiles, hooks, rules, skills, and instructions share Claude-style View source / Copy path / Edit / Delete actions. Every allowed deletion is confirmed and backed up first (a skill keeps its entire directory); config.toml is permanently edit-only. |
| MCP Server (Local) | Enterprise-grade local MCP server in mcp/ with three transport modes (stdio, HTTP+SSE, interactive REPL), 25 typed tools across 6 domains, strict input schemas, retry/backoff, localhost-only API enforcement, and tiered mutation/destructive safety gates. HTTP mode serves Streamable HTTP (2025-11-25) and legacy SSE (2024-11-05) on configurable port. REPL mode provides tab-completed interactive tool invocation with colored output |
| Workflows | D3.js-powered visualization page with 11 interactive sections: agent orchestration DAG, tool execution Sankey diagram, collaboration network, subagent effectiveness (day-of-week sparklines with portal-rendered tooltips that escape the card's overflow:hidden and clamp to the viewport so they never get clipped), detected workflow patterns, model delegation flow, error propagation map (horizontal bars with rate badges, agent type breakdown, API/session error cards), concurrency timeline, session complexity scatter, compaction impact analysis (redesigned as a clear "sessions by compaction count" histogram with axis titles, stat tiles β total / sessions affected / avg / peak β an explanatory help line, and per-bar hover tooltips), and per-session drill-in. Each section's right-aligned subtitle clamps to a single line (ellipsis + hover title) so a long translation never wraps the header. Rich, i18n-aware tooltips throughout: every chart's section title carries an i icon that opens a structured "What this shows / How to read it / Why it matters" popover; hovering nodes, edges, bars, and bubbles surfaces multi-section tooltips with deterministic, value-dependent interpretations (e.g. share-of-source / share-of-target percentages, success-rate health buckets, family descriptions for Opus / Sonnet / Haiku, timing patterns like front-loaded / mid-session / back-loaded). Each of the six headline stat cards has a bottom-right info popover explaining how the metric is calculated and what its current value means in plain language. Tooltips are DOM-mutated through a single ref per chart with container-level mouseleave fallbacks, so they never lag behind the cursor or stick after re-render. Clicking a row in expands an in-place detail panel with the full step sequence, stats grid, a deterministic narrative (loop detection, frequency bucket), and a practical suggestion. Status filter tabs (Active Only / Completed / All) filter all 11 sections. Cross-filtering, JSON export, and real-time WebSocket auto-refresh with 3-second debounce. A panel surfaces "dynamic workflows" β the fleets of sub-agents spawned by the tool (and self-paced ) β which emit no hooks and are instead reconstructed from on-disk run journals (): each run shows its phases and a per-agent token / tool-call / duration breakdown, with live detection before the journal is written and a linked subsection on each Session Detail page |
| | Detects events from JSONL transcripts, creates compaction agents and events. Backfills legacy compactions on startup. A periodic scanner (cadence derived from ) catches compactions even when no hooks fire. Reads each active session's transcript path directly from (populated by the hook handler on the first event that carries it, plus a one-time backfill from ) instead of doing a over the entire events table β so the sweep is O(active sessions) and stays cheap on a mature database. Shares the transcript cache so no duplicate file reads occur. Synthetic compaction rows are stamped with the transcript timestamp on both and so duration is exactly 0 (compaction is instantaneous); a startup repair migration also heals any pre-existing rows where (issue #156) |
| | Automatically reactivates sessions when new events arrive, correctly handles and orphaned sessions. Periodic sweep (every ΒΌ of , clamped to 60 s β 5 min) marks abandoned sessions that slip past event-based detection |
| | Sessions already running when the server starts are imported as "active" (based on recent JSONL file modification). Stop events also reactivate imported completed/abandoned sessions, so the first hook from an in-progress session always surfaces it on the dashboard |
| | The startup auto-import of is one-time (marker-gated), so a project folder created first launch β whose sessions never flow through hooks (e.g. host-only hooks disabled) β would stay invisible until a manual rescan. A background sync () closes that gap via three triggers sharing one mtime cache + a single coalesced sweep: an sweep at startup, a debounced that fires the instant a new session file/project folder appears (recursive on macOS/Windows; root + immediate children on Linux to avoid the userland recursive-watcher hazard), and a (, default 30 s). Each sweep re-parses only files whose mtime advanced and broadcasts / (plus the main agent) so the UI refreshes live; an unchanged session already in the DB is skipped without re-parsing, so restart cost stays O(new/changed files) |
| | Live remote / multi-machine Claude Code and Codex collection over SSH. One source independently mirrors and (plus Codex's lightweight for native renamed titles) through , or + for WSL-hosted CLIs on a Windows SSH host. Each isolated stage goes through its provider's normal importer and tags rows with ; a source is healthy when either provider is available, so Claude-only, Codex-only, and mixed machines all work. The 15 s poller and immediate add/re-enable pulls broadcast provider-aware , , and per-session updates. Remote lifecycle is reconciled from each mirrored transcript. If a provider is unavailable, errors, or is stuck syncing beyond , only that provider's old remote sessions fall back to the ordinary stale sweep; a healthy sibling provider remains mirror-owned. Configure optional independent and paths in or use ; SSH authentication remains entirely on the host (no passwords/secrets stored). |
| | Mobile-friendly layouts with stacking grids, scrollable tables, and collapsible sidebar |
| | Built-in custom-dropdown language switching with translated UI copy and accessibility labels for English (), Chinese (), Vietnamese (), Korean (), and Spanish (). Coverage extends end-to-end through the Workflows tooltips: stat-card calculations and value-bucket interpretations, per-chart "What / How to read / Why" popovers, every graph's hover tooltip (orchestration, tool flow, pipeline, model delegation, concurrency), the Workflow Patterns detail-panel narratives and suggestions, the Settings β Model Pricing info popover, the CLAUDE_HOME panel, and the entire Import History flow. |
| | Built-in seed script for demos and development |
| | Color-coded CLI statusline showing model, context usage, git branch, per-direction tokens, and session cost (USD) |
| | Human-friendly model names throughout the UI: raw identifiers like or display as "Claude Opus 4.7" or "Claude Opus 4.7 (1M)". Handles Claude, GPT, and Gemini families with automatic version dot-joining, date/latest suffix stripping, provider prefix removal, and context-window tag formatting. Settings page retains raw names for pricing rule configuration |
| | Official Claude Code plugin marketplace with 10 plugins (ccam-analytics, ccam-productivity, ccam-devtools, ccam-insights, ccam-dashboard, ccam-cost-guard, ccam-sessions, ccam-workflows, ccam-quality, ccam-config). 53 skills, 14 agents, 30 slash commands, 3 CLI tools, 3 hook configs. All grounded in actual data model β token baselines, pricing engine, workflow intelligence (11 datasets), session metadata. Install via |
| | Spawn subprocesses directly from the dashboard with a chat-style streaming UI. Two modes: (multi-turn β stdin stays open, follow-up turns are piped as stream-json envelopes) and (headless, single prompt β single response). Conversation mode also supports via β pick from your full sessions history with a searchable picker. The unified active-runs / history modal also offers two zero-config jump buttons: on any past conversation row spawns immediately and seeds the chat with the prior transcript so you land in the live view with full context (no need to retype a prompt β the spawn idles on stdin until you send a follow-up); on any past one-shot row loads the captured transcript inline into the run viewer as read-only (no spawn β same panel, no Stop/follow-up controls). Active runs switcher in the header lets you leave a run in the background, start another, and re-attach later. Re-attach is durable: the client reconciles the spawner's in-memory envelope log () with the session's on-disk JSONL transcript and prefers whichever has more user/assistant messages, so navigating away from a resumed run and coming back keeps the full prior history visible (the spawner only sees post-spawn turns; the transcript file has prior + current). Model dropdown (Opus 4.7 / 1M / Sonnet 4.6 / Haiku 4.5 / custom), permission-mode picker with explicit warning, field (low / medium / high β wired to ), cwd autocomplete pre-filled with the user's β a neutral spawn location that doesn't inherit the dashboard repo's own project context (agents, skills, rules, , ); falls back to the dashboard cwd if no home suggestion is available, with home listed first in the suggestion groups (home β dashboard β recent). Real character-by-character streaming via , plus a client-side that drips each / through so even short replies (where claude bundles the whole answer into one or two chunks) appear to type in. The merge code keeps the flag and the delta-accumulated array intact when claude's canonical envelope arrives mid-stream, so thinking blocks aren't dropped at completion. WebSocket dispatch wraps each envelope in so React 18's auto-batching doesn't collapse bursts of deltas into a single render. : a collapsible that minimizes to a slim pill (never disappears) explaining what stream-json mode can and can't do vs. the terminal TUI; a with tiered scoring (exact name β starts-with β word-boundary β contains β subsequence β description-contains) that lists user / project / plugin commands (executed client-side via template expansion before send) and surfaces built-in CLI commands like , , with a "CLI only β won't run from here" badge; with debounced fuzzy-search across the run's cwd (skipping , , , , etc.); a showing input + output + cache-read tokens and running cost, computed from and envelopes during live streaming and from finalized assistant blocks (input / output / cache-read / cache-creation) when seeded from a transcript on resume / view / re-attach, so the meter populates immediately instead of sitting at 0/200k. Progress bar goes indigo β amber β red at 80% / 95% of the model's context cap; a with the active model, effort, permission mode, cwd, session ID, envelope count, and elapsed time. Autocomplete dropdowns open upward so they don't collide with the cwd picker below. Live / Offline indicator next to the title. Same-origin guard on the route prevents browser drive-by spawning. Concurrency is effectively uncapped by default (sanity ceiling of 10000 to prevent fork-bomb footguns from a buggy client; the terminal TUI has no cap and neither do we). Set if you want a real ceiling. Spawned sessions fire the same hooks any process does, so they show up automatically in Sessions / Analytics / Kanban / Workflows β and Sessions / SessionDetail surface a green badge / banner that links back to the Run page for any session that's currently being driven from there |
| | A 12-tab inspector at for everything Claude Code knows about: skills, subagents, slash commands, output styles, plugins (with per-plugin contributions count + author/license/homepage from ), marketplaces (with plugin counts read from each ), MCP servers, hooks (with script listing), settings (an at-a-glance summary of the options controls β model, verbose, theme, output style, effort, auto-compact, notifications, β¦ β resolved across user/project/project-local scopes with unset options shown as defaults, plus the per-file structured key-value view + raw JSON toggle, secret-key redaction), memory (the user + project files the per-project file-based memory store β every under , i.e. a index plus one file per remembered fact, often 100+; grouped by project in collapsible sections that split index files from per-fact files, with a search box and clickable index links that jump to β scroll to + highlight β the matching fact file), keybindings (grouped by context with chips), and statusline (config + script content). For low-risk text-file surfaces (skills / agents / commands / output styles / memory β including the per-project auto-memory files) the page supports atomically written outside the directories Claude Code scans, plus a Backups modal with auto-built restore commands. Plugins, MCP, hooks-in-settings, and files stay read-only with explainer banners + copy-able CLI commands so the user knows the exact command to run themselves. : a running on the server uses on (recursive where the platform supports it) plus , debounced at 500 ms, to broadcast a WebSocket message whenever Claude Code config changes β either via dashboard mutations or external tools (CLI installing a plugin, manually editing , dropping a new skill). The page subscribes and refetches automatically; a Live / Offline pill next to the title shows WebSocket status |
| | A floating cat companion pinned to the bottom-right corner of every page. Built entirely on the existing WebSocket β . A reactive SVG mascot with cursor-tracking eyes and derived from the live session stream (, , , , , , , ), each with its own animation (tail flick, ear perk, head bob, shake, sparkle, zzz, alert "!"). post short, throttled, coalesced quips on notable events (session started/finished, errors, run completed) and can be muted. Click the cat or press (Esc closes) to open a with a live status line (), quick actions (jump to Run Claude / Activity / Sessions / errored sessions, mute bubbles, clear alerts), and an box: simple status questions ("what's running", "any errors", "status") are answered locally from cached data, while any other question hands off to the page (deep-links to ) to spawn a real Claude Code session. Accessible (keyboard-operable, bubbles, honors ), degrades safe to a calm state if the socket is down, toggleable in (localized in en/zh/vi/ko/es). Implementation lives in |
| | Three independent PWAs β dashboard, landing page, and wiki β each with its own Web App Manifest and Service Worker. Install any of them to your home screen / dock for a standalone, chrome-less experience. The dashboard SW serves Vite's content-hashed bundles under cache-first (URLs are immutable per build, so cache hits are always correct) and treats everything else β navigations, the SW itself, , icons, root β as network-first with cache fallback. Combined with explicit headers on the production Express static middleware ( for , for , , ), a rebuild always replaces the in-browser bundle without a hard refresh; a listener in the client reloads exactly once when a new SW takes over an already-controlled page. The VAPID push-notification pipeline is preserved. The landing-page and wiki SWs precache their respective shells and lazy-cache images on first visit, enabling offline access after a single load. All manifests use SVG icons () with for modern browsers, and include + meta tags for iOS standalone mode |
| | Optional native desktop app built with Electron 35, living in the workspace alongside , , , and . Ships as a macOS () a Windows (NSIS installer + no-install portable). It embeds the existing Express server (s β no child process, no IPC) and renders the built React client in a . Adds a native title bar, a menu-bar / notification-area (tray) icon whose single-click dropdown shows a (sessions, agents, events today) pulled from SQLite at click time, a native application menu, auto-start at login (macOS Login Items via ; Windows per-user ), a (second press bypasses), window-close-hides-but-server-keeps-running, a single-instance lock, and tray actions for , , and . Prefers port 4820 (falls back to 4821β4829 then a random high port), adopts a healthy dashboard already running on 4820 instead of double-binding, and β both and the desktop app can run together with hooks fanning out to both. Notifications fire as native OS toasts (Web Push doesn't work reliably inside Electron). On first owned-server boot it auto-installs Claude Code hooks and starts the background services, so an install-only user gets events flowing with zero manual setup. See and |
| | Every font and script is served locally, so the dashboard and docs make β they render fully offline and leak nothing to external hosts. The React app bundles Inter + JetBrains Mono via (latin subset; Vite emits content-hashed WOFF2 into at build time, no to Google Fonts). The landing page and wiki load a self-hosted sheet from the repo-root directory. The wiki's Mermaid is vendored locally as (the genuine minified ) instead of jsDelivr, and the VS Code extension's error page falls back to a system font stack. No , , or calls remain anywhere |
| | A brief branding splash on app load (once per browser session): a (Good morning / afternoon / evening / Working late), a bold tagline, two subtexts, and an animated node-graph brand mark over a dark atmospheric backdrop (radial glow + drifting constellation + grain). Fully localized (en/zh/vi/ko/es). The overlay is so the app never flashes through, holds ~2.5 s, then fades out; click anywhere to skip, and it honors . CSS-only animations, no added dependencies |
git clone https://github.com/hoangsonww/Claude-Code-Agent-Monitor.git
cd Claude-Code-Agent-Monitor
npm run setup
npm run install-hooks
The installer opens an interactive multi-select: use arrow keys, Space, and Enter to choose Claude Code, Codex (beta), or both (Claude Code is preselected). Claude Code entries live in ~/.claude/settings.json; Codex entries live in ~/.codex/hooks.json. If a selected dashboard hook set already exists, it warns before replacing only this dashboard's entries β unrelated hooks are preserved. You can make the same selection later in Settings β Hook Configuration β Install hooks.
On first dashboard entry, choose the data source and the app opens a provider-aware live-monitoring setup gate. It can install the selected hooks in place with overwrite warnings and command output, or you can explicitly confirm they are already installed before entering the dashboard.
Codex rollouts in ~/.codex/sessions are also discovered continuously. The dashboard reads their append-only JSONL incrementally, prioritizes the newest rollouts, and isolates a bad historical file for retry, so sessions, tokens, costs, conversation rows, and WebSocket updates stay current even if a hook notification is missed.
Codex rollout lifecycle records drive the same live card states as Claude Code: user_message and task_started mark the main agent Working; task_complete leaves the session active but shows Waiting; and turn_aborted shows Waiting with an interrupted reason. A new rollout record self-heals an erroneously completed session, while the process-liveness reap only completes a local Codex session after its matching CLI has gone away.
Codex /rename titles are read from its native session index and update session and agent cards in real time. Its conversation replay includes human turns plus exec custom-tool calls and outputs, with cursor pagination that loads older messages at the top of the transcript.
Claude Code and Codex cards show a compact, two-row history of their latest distinct human prompts beneath the provider-native title, so a short friendly name or terse follow-up never hides the active task. Claude refreshes this context from its local transcript cache during live hooks, imports, and watchdog sweeps; Codex refreshes it from rollout records and falls back to persisted user_message events for older imports. The transcript renders persisted PNG/JPEG/GIF/WebP attachments for Claude Code and Codex when available, while duplicate Codex response/event copies collapse into one human turn.
Codex response_item tool invocations are indexed once through a dedicated rollout cursor, so its Workflows tool flow, session drill-in, model/token totals, and context_compacted counts reflect the recorded Codex data without replaying lifecycle or token counters. In a Codex-only dashboard scope, the Claude Code-only Dynamic Workflows journal panel is hidden rather than presented as empty Codex data.
# Development (hot reload on both server and client)
npm run dev
# Production (single process, built client)
npm run build && npm start
[!TIP] Makefile alternative β all commands are also available via
makeif you have it installed on your system. Runmake helpto see every target, or use shortcuts likemake dev,make build,make test, etc.
| Mode | URL |
|---|---|
| Development | http://localhost:5173 |
| Production | http://localhost:4820 |
npm run mcp:install
npm run mcp:build
npm run mcp:start # stdio (default β for MCP host integration)
npm run mcp:start:http # HTTP + SSE server on port 8819
npm run mcp:start:repl # interactive CLI with tab completion
For stdio mode, configure your MCP host (Claude Code / Claude Desktop / other MCP clients):
node["<ABSOLUTE_PATH>/mcp/build/index.js"]For HTTP mode, point remote MCP clients at http://127.0.0.1:8819/mcp (Streamable HTTP) or http://127.0.0.1:8819/sse (legacy SSE).
See mcp/README.md for full host configuration, transport details, safety flags, and tool catalog.
npm run seed
Creates 8 sample sessions, 23 agents, and 106 events so you can explore the UI immediately.
If you'd rather not keep a terminal open, install the optional native desktop app. It embeds the server in-process, adds a menu-bar / notification-area (tray) icon, and supports auto-start at login (macOS Login Items / Windows startup).
The fastest path is to download a pre-built installer from the latest GitHub Release (CI auto-publishes a vX.Y.Z whenever package.json is bumped on master):
ClaudeCodeMonitor-<version>-arm64.dmg (Apple Silicon) or -x64.dmg (Intel) and drag Claude Code Monitor.app into /Applications.ClaudeCodeMonitor-Setup-<version>-x64.exe (installer) or ClaudeCodeMonitor-<version>-x64-portable.exe (no-install) and run it.To build it yourself instead:
npm run desktop:install # install Electron + electron-builder into desktop/ (preflights native deps; prints setup help on failure)
npm run desktop:dmg:arm64 # macOS: fast single-arch DMG (Apple Silicon)
npm run desktop:win # Windows: NSIS installer .exe (run on Windows)
Full coverage of the desktop app β download, install, tray/menu features, build commands, and signing β is in the Desktop App (macOS & Windows) section below. See also DESKTOP.md (user guide) and desktop/README.md (architecture).
A Dockerfile and docker-compose.yml are included. Both Docker and Podman are supported.
With Docker Compose:
docker compose up -d --build
With Podman Compose:
CLAUDE_HOME="$HOME/.claude" podman compose up -d --build
With plain Docker or Podman (no Compose):
# Docker
docker build -t agent-monitor .
docker run -d --name agent-monitor \
-p 127.0.0.1:4820:4820 \
-v "$HOME/.claude:/root/.claude:ro" \
-v "$HOME/.claude/agent-dashboard:/app/data" \
agent-monitor
# Podman
podman build -t agent-monitor .
podman run -d --name agent-monitor \
-p 127.0.0.1:4820:4820 \
-v "$HOME/.claude:/root/.claude:ro" \
-v "$HOME/.claude/agent-dashboard:/app/data" \
agent-monitor
The dashboard is then available at http://localhost:4820. The image binds 0.0.0.0 inside the container (DASHBOARD_HOST) and writes SQLite to /app/data (DASHBOARD_DATA_DIR) β both baked into the Dockerfile so Compose and plain docker run work as-is. Compose and the examples below bind-mount the same host directory as npm start / npm run dev (~/.claude/agent-dashboard), so Docker, native, and dev all share one database. The trust boundary is the host port publish: the examples publish on 127.0.0.1 only, so the dashboard is not LAN-reachable out of the box. To expose it on a LAN, publish on 0.0.0.0 (drop the 127.0.0.1: prefix, e.g. -p 4820:4820) and set DASHBOARD_TOKEN (see Configuration and .github/SECURITY.md).
Volume mounts:
| Mount | Purpose |
|---|---|
~/.claude:/root/.claude:ro | Read legacy session history for import |
~/.claude/agent-dashboard:/app/data | Canonical SQLite database (shared with native installs) |
[!NOTE] If you previously used the old Compose named volume (
dashboard-data), copy any data you need into~/.claude/agent-dashboard/once, then remove the unused volume:docker volume rm claude-code-agent-monitor_dashboard-data(name may vary).
[!IMPORTANT] Note: Claude Code hooks must still point to a running hook-handler process on the host. The container itself does not receive hooks β run
npm run install-hookson the host to configure hooks that POST tohttp://localhost:4820. Running the installer inside a container is refused (issue #193) so it can't write a container-internal handler path into a bind-mounted host~/.claude; override withCCAM_ALLOW_CONTAINER_HOOKS=1only if you actually run Claude Code inside the same container.
The dashboard integrates with Claude Code via its native hook system to provide real-time monitoring of agent activity. Here's an overview of the architecture and data flow:
sequenceDiagram
participant CC as Claude Code
participant HH as Hook Handler
participant API as Express Server
participant DB as SQLite
participant WS as WebSocket
participant UI as React Client
CC->>HH: stdin (JSON event)
HH->>API: POST /api/hooks/event
API->>DB: Insert/update records
API->>WS: Broadcast update
WS->>UI: Push message
UI->>UI: Re-render component
Note over CC,HH: Hooks fire on SessionStart,<br/>PreToolUse, PostToolUse,<br/>Stop, SubagentStop,<br/>SessionEnd, Notification.<br/>Compaction detected from JSONL
Note over API,DB: Transactional writes<br/>with auto session/agent creation
Note over WS,UI: ~0ms latency,<br/>no polling
[!IMPORTANT] See ARCHITECTURE.md for a deep dive into the server architecture, database schema, API routes, WebSocket design, client routing, hook handler flow, deployment modes, and detailed lifecycle diagrams for sessions and agents.
scripts/hook-handler.js) reads the JSON event from stdin, resolves live dashboards via ~/.claude/.agent-dashboard.json (or CLAUDE_DASHBOARD_PORT if set), and POSTs the same payload to one ingest target per unique SQLite data directory (lowest port wins when Docker and npm run dev share ~/.claude/agent-dashboard, so events are never double-ingested). Servers with different databases (e.g. the desktop app using its own Application Support dir alongside npm run dev) still each receive hooks. Fails silently with a 5 s safety-net timeout so it never blocks Claude Code, and per-target promises never reject so a single dead listener can't starve the others.Agent tool calls to track subagent creationSessionStart, stamps the session and main agent's awaiting_input_since so a fresh CLI sitting at the prompt lands in Waiting immediatelyUserPromptSubmit (user hits enter), clears the waiting flag and promotes the main agent to working β the only reliable signal that text-only assistant turns have started, since they emit no PreToolUsePreToolUse (also clears the waiting flag), keeps it working through PostToolUseStop (Claude finishes responding), main agent goes to "waiting" β Claude finished its turn, ball is in the user's court. Background subagents continue running. Session stays active. Stop with stop_reason=error marks the agent error and the session errorNotification (matched by message pattern: permission, waiting for input, needs your approval, β¦), sets the agent to waiting and stamps awaiting_input_sinceSubagentStop deliberately does NOT clear the waiting flag (a backgrounded subagent finishing tells us nothing about the human)SubagentStop. After res.json() returns, fires a fire-and-forget scanAndImportSubagents pass that walks the session's files, pairs β blocks by , and emits + events under each subagent's own β closing the gap where subagent-internal tool calls would otherwise be invisible to the dashboardPersisted statuses: working | waiting | completed | error. The
awaiting_input_since column is supplementary β it tracks when the agent
started waiting and is used for duration display, but waiting is now a
real persisted status.
stateDiagram-v2
[*] --> waiting: ensureSession (first hook)
waiting --> working: PreToolUse / UserPromptSubmit / Codex task_started / user_message
working --> working: PostToolUse (tool completed)
working --> waiting: Stop, non-error / Codex task_complete
working --> waiting: Codex turn_aborted (interrupted)
working --> waiting: Notification (input prompt)
working --> waiting: Esc cancel (watchdog marker or idle timeout)
waiting --> error: Stop with error
working --> error: Stop with error
waiting --> error: API error detected (watchdog)
working --> error: API error detected (watchdog)
error --> working: UserPromptSubmit / PreToolUse (recovery)
working --> completed: SessionEnd
waiting --> completed: SessionEnd
note right of waiting
Agent is between turns or
awaiting user input
end note
Persisted statuses: active | completed | error | abandoned. The
Waiting session state is a UI overlay (status=active with
awaiting_input_since set).
stateDiagram-v2
[*] --> waiting: SessionStart startup/resume/clear (status=active + flag)
active --> active: SessionStart compact (mid-turn β state preserved, no flag)
waiting --> active: UserPromptSubmit / PreToolUse / PostToolUse / Codex task_started / user_message
active --> waiting: Stop, non-error / Codex task_complete (flag re-stamped)
active --> waiting: Codex turn_aborted (interrupted)
active --> waiting: Permission Notification (agent β waiting)
active --> waiting: Esc cancel (watchdog marker or idle timeout)
active --> error: Stop, stop_reason=error
active --> error: API error detected (watchdog)
waiting --> error: API error detected (watchdog)
error --> active: UserPromptSubmit / PreToolUse (recovery)
waiting --> completed: SessionEnd (CLI exited)
active --> completed: SessionEnd (CLI exited)
error --> error: SessionEnd (preserves error)
waiting --> abandoned: Stale > DASHBOARD_STALE_MINUTES (default 180)
active --> abandoned: Stale > DASHBOARD_STALE_MINUTES
completed --> active: Session resumed (new work event)
error --> active: Session resumed (new work event)
abandoned --> active: Session resumed (new work event)
completed --> [*]
error --> [*]
abandoned --> [*]
flowchart LR
TU["token_usage rows<br/>(per session Γ model)"] --> GROUP["Group by model"]
PR["model_pricing rules<br/>(pattern-based)"] --> SORT["Sort by specificity<br/>(longest pattern first)"]
GROUP --> MATCH{"Match model<br/>to pricing rule"}
SORT --> MATCH
MATCH --> CALC["cost = Ξ£ (tokens / 1M) Γ rate<br/>for input, output, cache_read, cache_write"]
CALC --> RESULT["{ total_cost, breakdown[] }"]
style TU fill:#003B57,stroke:#005f8a,color:#fff
style PR fill:#6366f1,stroke:#818cf8,color:#fff
style RESULT fill:#10b981,stroke:#34d399,color:#fff
[!IMPORTANT] The cost calculation flow is based on token usage and model pricing rules. Ensure your pricing rules are up-to-date to reflect accurate costs. Update the model pricing table via the Settings page to maintain accurate cost tracking - the dashboard does not automatically fetch pricing updates from external sources. Once you set the pricing rules, the dashboard applies them retroactively to all sessions for consistent cost reporting.
| Environment Variable | Default | Description |
|---|---|---|
DASHBOARD_PORT | 4820 | Port for the Express server |
CLAUDE_DASHBOARD_PORT | 4820 | Port used by hook handler to reach the server |
NODE_ENV | development | Set to production to serve the built client |
DASHBOARD_UPDATE_CHECK | (enabled) | Set to 0 / false / off to disable periodic git upstream checks |
DASHBOARD_UPDATE_CHECK_INTERVAL_MS | 300000 (5 min) | Interval between automatic checks; floor 60 000 ms. Users can also click Check now in the update modal or in the sidebar to run one on demand. |
DASHBOARD_STALE_MINUTES | 180 (3 h) | Minutes of inactivity before a still-active session (including one sitting in Waiting on user input β "Waiting" is a UI overlay on an active row, not a stored status) is auto-marked abandoned and drops off the active list. Enforced by the 15 s watchdog and the periodic maintenance sweep (which runs every ΒΌ of this value, clamped to 60 s β 5 min). Lower it (e.g. 60) for a shorter idle timeout |
DASHBOARD_WORKING_IDLE_SECONDS | 120 | Idle-working timeout for recovering a turn cancelled with Esc before any output (which leaves no transcript marker). When the main agent has been working with no tool in flight and neither a hook event nor the transcript has advanced for this long, the watchdog moves the session to Waiting. Lower it for snappier recovery at the cost of occasional false flips on long silent-thinking turns (which self-heal) |
DASHBOARD_LIVENESS_PROBE | 1 (on) | Set to 0 to disable the watchdog's dead-session liveness reap (the ps/lsof-based probe that completes active local Claude Code or Codex sessions whose matching CLI process no longer exists β recovering a SessionEnd lost while the dashboard was down). Sessions forwarded from (household hooks) report a non-POSIX and are auto-skipped by the reap, so a mixed local + forwarded deployment no longer needs this off; disable it only for a purely-remote setup where local processes prove nothing. Auto-disabled on Windows and inside containers |
[!IMPORTANT] Secure by default. The server binds
127.0.0.1and is not reachable from the network out of the box (GHSA-gr74-4xfh-6jw9). To expose it on a LAN, set bothDASHBOARD_HOST(e.g.0.0.0.0) andDASHBOARD_TOKEN(which then gates/api/*and the WebSocket), and list your LAN hostnames inDASHBOARD_ALLOWED_HOSTS. See.env.exampleand.github/SECURITY.mdfor details.
For git clones, the server periodically git fetches origin and compares your checkout to origin/master, origin/main, or origin/HEAD. When you are behind, a message appears in the server terminal and a modal appears in the UI with the exact command to run. The dashboard never pulls or restarts itself β you copy the command, run it in a terminal, then restart the server the same way you started it.
ccam CLIThe dashboard's full feature surface is also available from any terminal via the dependency-free ccam CLI (bin/ccam.js). It is linked automatically by npm run setup (via npm link), after which ccam <command> works from any directory. It discovers the running dashboard through ~/.claude/.agent-dashboard.json (the same live-server registry the hook handler uses), with CLAUDE_DASHBOARD_PORT / DASHBOARD_PORT env overrides, falling back to http://127.0.0.1:4820.
# Server
ccam status # β running / β not running indicator
ccam start [--port N] # start the server in the background (detached)
ccam repl # interactive shell (also: shell, i)
# Monitoring
ccam health # is the dashboard up?
ccam stats # totals, today's events, status distributions
ccam kanban # sessions + agents grouped by status columns
ccam tail [--session <id>] # live event feed in the terminal (Ctrl+C stops)
# Data
ccam sessions [--status s] [--q text] [--limit n]
ccam session <id> # detail: agent tree, cost, recent events
ccam agents [--status s] [--session id]
ccam events [--session id] [--limit n]
# Insights
ccam analytics # token totals, top tools, agent types
ccam workflows [--session id] # workflow intelligence stats and patterns
ccam runs [--session id] # dynamic Workflow-tool runs
ccam cost [--session <id>] # total estimated cost with per-model breakdown
# (--session scopes to one; shows tool surcharges;
# warns about models with usage but no pricing rule)
# Alerts & webhooks
ccam alerts [--unacked] # fired-alert feed
ccam alerts ack <id> | ack-all # acknowledge alerts
ccam rules # list alert rules
ccam webhooks # list webhook targets
ccam webhooks test <id> # send a synthetic test alert
# Pricing
ccam pricing # list model pricing rules (incl. fast-mode & intro columns)
ccam pricing set <pattern> --input N --output N [--cache-read N --cache-write N]
[--cache-write-1h N] [--fast-input N --fast-output N]
[--intro-input N --intro-output N β¦ --intro-until YYYY-MM-DD]
ccam pricing delete <pattern>
ccam pricing reset
# Import
ccam import rescan # re-scan ~/.claude/projects
ccam import path <dir> # import every .jsonl under a directory
# Administration
ccam doctor # connectivity, hooks, and database diagnosis
ccam info # raw system info JSON
ccam export [file.json] # full JSON data export
ccam import-data <file.json> # restore an export (idempotent, non-destructive)
ccam cleanup --hours N --days M # abandon stale / purge old sessions
ccam reinstall-hooks # reinstall Claude Code hooks
ccam update-check # is the checkout behind upstream? (prints the update command)
ccam clear-data --yes # delete ALL data (requires --yes)
ccam open # open the dashboard in your browser
ccam version # print the CLI version (also --version / -v)
API-backed commands need the server running β when it isn't, read-only commands fall back to reading data/dashboard.db directly (with an explicit β Offline mode banner, and stored-but-dead active sessions corrected display-side by the same process-liveness probe the server's watchdog uses), while commands that can't run correctly without the server (live tail, analytics/cost math, mutations) print the β Dashboard server is NOT running indicator with the specific reason and the start commands; ccam start brings a production server up in the background. Read commands are always safe; the one destructive command (clear-data) refuses to run without an explicit --yes. Output is a full terminal UI β box-drawn tables with right-aligned numeric columns, status icons (β active, β waiting, β completed, β error), inline bar charts for stats/analytics/cost, and real ββ/ββ agent trees β with ANSI colors auto-enabled on a TTY, off when piped, and controllable via --no-color / NO_COLOR / FORCE_COLOR. For a live monitoring session, ccam repl (aliases shell / i) opens an interactive shell where you type commands without the ccam prefix β with a CCAM welcome banner, tab-completion, persisted arrow-key history, a live server-status prompt (β host up / β offline down), a grouped help / help <cmd> menu, and a watch [secs] <cmd> built-in that auto-refreshes any command (e.g. watch 5 kanban); each line runs as an isolated child process, so an offline refusal or a blocking tail never takes the shell down. If ccam is not on your PATH (e.g. npm link needed elevated permissions), run npm link once from the repo root. Full reference β flags, discovery order, the REPL, safety model, scripting/exit codes, troubleshooting β in docs/CLI.md.
| Command | Description |
|---|---|
npm run setup | Install server and client dependencies |
npm run update:pull-setup | git pull --ff-only then npm run setup (manual upgrade) |
npm run dev | Start server (watch mode) + client (Vite HMR) concurrently |
npm run dev:server | Start only the Express server with --watch |
npm run dev:client | Start only the Vite dev server |
npm run build | Build the React client to client/dist/ |
npm start | Start production server (serves built client) |
npm test | Run the full suite (server node --test + client Vitest) |
npm run test:server | Run backend tests (node --test server/__tests__/) |
npm run test:client | Run frontend Vitest tests, including render snapshots for every screen (client/src/pages/__tests__/screens.snapshot.test.tsx); regenerate baselines after intentional UI changes with cd client && npx vitest run -u |
npm run install-hooks | Configure Claude Code hooks in ~/.claude/settings.json |
npm run seed | Populate database with sample data |
npm run import-history | Import legacy sessions from ~/.claude/ (also runs on startup) |
npm run clear-data | Delete all sessions, agents, events, and token usage |
npm run mcp:install | Install dependencies for local MCP package (mcp/) |
npm run mcp:build | Build MCP server TypeScript into mcp/build/ |
npm run mcp:start | Start MCP server (stdio transport β for MCP hosts) |
npm run mcp:start:http | Start MCP server (HTTP + SSE transport on port 8819) |
This repository includes a comprehensive extension layer for both Claude Code and Codex:
CLAUDE.md, .claude/rules/, .claude/skills/.claude/agents/AGENTS.md, .codex/rules/, .codex/agents/, .codex/skills/graph TD
USER["Developer"]
CLAUDE["Claude Code"]
CODEX["Codex"]
MEMORY["CLAUDE.md + .claude/rules/*"]
C_SKILLS[".claude/skills/*"]
AGENTS_MD["AGENTS.md"]
X_RULES[".codex/rules/*.rules"]
X_AGENTS[".codex/agents/*.toml"]
X_SKILLS[".codex/skills/*"]
USER --> CLAUDE
USER --> CODEX
CLAUDE --> MEMORY
CLAUDE --> C_SKILLS
CODEX --> AGENTS_MD
CODEX --> X_RULES
CODEX --> X_AGENTS
CODEX --> X_SKILLS
repo-onboardingship-featuremcp-operationsdebug-live-issuebackend-reviewerfrontend-reviewermcp-reviewerThis project includes a local, production-grade MCP server at mcp/ that exposes dashboard operations as tools for AI agents. It supports three transport modes to suit different integration scenarios.
flowchart LR
subgraph Transports["Transport Modes"]
STDIO["stdio\n(default)"]
HTTP["HTTP + SSE\n(port 8819)"]
REPL["Interactive REPL\n(terminal CLI)"]
end
subgraph Protocols["Wire Protocols"]
P1["JSON-RPC\nstdin/stdout"]
P2["Streamable HTTP (2025-11-25)\nLegacy SSE (2024-11-05)"]
P3["Direct invocation\ntab completion + colored output"]
end
STDIO --> P1
HTTP --> P2
REPL --> P3
style STDIO fill:#6366f1,stroke:#818cf8,color:#fff
style HTTP fill:#f59e0b,stroke:#fbbf24,color:#000
style REPL fill:#a855f7,stroke:#c084fc,color:#fff
| Mode | Command | Use Case |
|---|---|---|
| stdio | npm run mcp:start | Claude Code, Claude Desktop, IDE MCP hosts |
| HTTP | npm run mcp:start:http | Remote MCP clients, web integrations, multi-session |
| REPL | npm run mcp:start:repl | Ops debugging, manual tool invocation, local admin |
graph LR
HOST["MCP Host<br/>(Claude Code / Claude Desktop)"]
HTTP_CLIENT["Remote MCP Client"]
OPERATOR["Operator CLI"]
MCP_STDIO["MCP Server<br/>stdio"]
MCP_HTTP["MCP Server<br/>HTTP :8819"]
MCP_REPL["MCP Server<br/>REPL"]
API["Dashboard API<br/>Express /api/*"]
DB["SQLite<br/>data/dashboard.db"]
HOST -->|"stdin/stdout"| MCP_STDIO
HTTP_CLIENT -->|"POST /mcp Β· GET /sse"| MCP_HTTP
OPERATOR -->|"interactive CLI"| MCP_REPL
MCP_STDIO --> API
MCP_HTTP --> API
MCP_REPL --> API
API --> DB
style HOST fill:#6366f1,stroke:#818cf8,color:#fff
style HTTP_CLIENT fill:#f59e0b,stroke:#fbbf24,color:#000
style OPERATOR fill:#a855f7,stroke:#c084fc,color:#fff
style MCP_STDIO fill:#0f766e,stroke:#14b8a6,color:#fff
style MCP_HTTP fill:#0f766e,stroke:#14b8a6,color:#fff
style MCP_REPL fill:#0f766e,stroke:#14b8a6,color:#fff
style API fill:#339933,stroke:#5cb85c,color:#fff
style DB fill:#003B57,stroke:#005f8a,color:#fff
graph TD
ROOT["MCP Tools"]
OBS["Observability<br/>health, stats, analytics,<br/>system info, export, snapshot"]
SES["Sessions<br/>list/get/create/update"]
AGT["Agents<br/>list/get/create/update"]
EVT["Events & Hooks<br/>list events, ingest hook events"]
PRC["Pricing & Cost<br/>rules CRUD, total/session cost, reset defaults"]
MNT["Maintenance<br/>cleanup, reimport, reinstall hooks, clear-all (guarded)"]
ROOT --> OBS
ROOT --> SES
ROOT --> AGT
ROOT --> EVT
ROOT --> PRC
ROOT --> MNT
flowchart TD
CALL["tools/call"] --> VALIDATE["zod input validation"]
VALIDATE --> TYPE{"Tool type?"}
TYPE -->|Read-only| EXEC["Execute"]
TYPE -->|Mutation| M_FLAG{"ALLOW_MUTATIONS?"}
M_FLAG -->|No| DENY1["β Reject"]
M_FLAG -->|Yes| DEST{"Destructive?"}
DEST -->|No| EXEC
DEST -->|Yes| D_FLAG{"ALLOW_DESTRUCTIVE?"}
D_FLAG -->|No| DENY2["β Reject"]
D_FLAG -->|Yes| TOKEN{"confirmation_token?"}
TOKEN -->|Invalid| DENY3["β Reject"]
TOKEN -->|Valid| EXEC
EXEC --> RESULT["Return tool result"]
style EXEC fill:#339933,stroke:#5cb85c,color:#fff
style DENY1 fill:#dc2626,stroke:#f87171,color:#fff
style DENY2 fill:#dc2626,stroke:#f87171,color:#fff
style DENY3 fill:#dc2626,stroke:#f87171,color:#fff
MCP_DASHBOARD_ALLOW_MUTATIONS=falseMCP_DASHBOARD_ALLOW_MUTATIONS=trueMCP_DASHBOARD_ALLOW_MUTATIONS=trueMCP_DASHBOARD_ALLOW_DESTRUCTIVE=trueconfirmation_token: "CLEAR_ALL_DATA"Full details: mcp/README.md
All endpoints return JSON. Error responses follow the shape { error: { code, message } }.
There are three ways to explore the HTTP API, all driven by a single OpenAPI 3.0.3 spec (default server port 4820):
| Method | Path | Description |
|---|---|---|
GET | /api/openapi.json | Raw OpenAPI 3.0.3 JSON spec |
GET | /api/docs | Interactive Swagger UI β try-it-out request execution |
GET | /api/redoc | ReDoc reference β a clean, read-optimized three-panel rendering of the same spec |
GET | /api/redoc/redoc.standalone.js | Self-hosted ReDoc bundle (served locally via the redoc dependency, never a CDN β works offline) |
The OpenAPI document is generated from server/openapi.js (createOpenApiSpec()), merged with supplementary fragments under server/openapi-extra/. Swagger UI and ReDoc are both served directly by the backend; the ReDoc bundle is served locally (GET /api/redoc/redoc.standalone.js) so the reference works fully offline / air-gapped, consistent with the project's no-external-assets policy.
Coverage is comprehensive: every backend route is documented (82 path entries) across these tags β Health, Sessions, Agents, Events, Stats, Metrics, Analytics, Hooks, Pricing, Workflows, Settings, Updates, Alerts, Webhooks, Push, CcConfig (Claude Code config explorer), Run (dashboard-spawned runs), and Documentation β each with parameters, request/response schemas, field-level descriptions, and realistic examples.
A committed openapi.yaml at the repo root mirrors the live spec. It is generated from server/openapi.js (never hand-edited) β regenerate it after API changes with:
npm run openapi:yaml
GET /api/metrics exposes the dashboard's live counters β sessions/agents by status, event and token totals, connected realtime clients, configured remote sources, process uptime/memory, and build version β in the Prometheus text-exposition format, so CCAM can be scraped into your own observability stack. A turnkey Prometheus + Grafana stack with four auto-provisioned dashboards (default home: CCAM β Overview) lives in monitoring/.
npm (no Docker β macOS, Linux, or Windows):
npm start # dashboard on :4820
npm run monitoring:install # one-time: npm postinstall pulls binaries
npm run monitoring:up # Grafana on :3000 (login: admin / admin), auto-provisioned
Docker / Podman (when the dashboard runs in a container or you prefer Compose):
# Dashboard only
npm run docker:up
# Dashboard + Prometheus + Grafana (one command)
npm run docker:full:up
# Or mix: native/docker dashboard + docker monitoring
DASHBOARD_ALLOWED_HOSTS=host.docker.internal npm start # or docker:up with same env
npm run monitoring:docker:up
npm run monitoring:verify
See docs/API.md β Metrics for the full metric list and scrape/auth details.
| Method | Path | Description |
|---|---|---|
GET | /api/health | Returns { status: "ok", timestamp } |
| Method | Path | Query Params | Description |
|---|---|---|---|
GET | /api/sessions | status, q, limit, offset | List sessions with agent counts and per-session cost. q does case-insensitive search across id / name / cwd. limit defaults to 50, max 10000. Response includes total for paginators. |
GET | /api/sessions/:id | -- | Session detail with agents and events |
GET | /api/sessions/:id/stats | -- | Aggregated counts powering the Session Detail overview panel: events, events-by-type, top tool usage, error count, agent type/status counts, subagent type breakdown, token totals, time range |
GET | /api/sessions/:id/transcripts | -- | List available JSONL transcripts for the session (main + subagents + compactions) |
GET | /api/sessions/:id/transcript | agent_id, limit, offset, after, before | Stream messages from a specific transcript with cursor-based pagination. Assistant usage includes input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens so the Run page meter can hydrate fully on resume / re-attach |
POST | /api/sessions | -- | Create session (idempotent on id) |
PATCH | /api/sessions/:id | -- | Update session status/metadata |
| Method | Path | Query Params | Description |
|---|---|---|---|
GET | /api/agents | status, session_id, limit, offset | List agents with filters |
GET | /api/agents/:id | -- | Single agent detail |
POST | /api/agents | -- | Create agent |
PATCH | /api/agents/:id | -- | Update agent status/task/tool |
| Method | Path | Query Params | Description |
|---|---|---|---|
GET | /api/events | session_id, limit, offset | List events (newest first) |
| Method | Path | Description |
|---|---|---|
GET | /api/stats | Aggregate counts, status distributions, WS connections |
| Method | Path | Description |
|---|---|---|
GET | /api/analytics | Token/tool/session aggregates for charts and trend views |
| Method | Path | Query Params | Description |
|---|---|---|---|
GET | /api/remote-sources | -- | List configured remote sources with status, last error, and last-sync counts |
POST | /api/remote-sources | -- | Create a source. Body: { label, host, ssh_port?, identity_file?, remote_home?, remote_codex_home?, enabled? } |
PATCH | /api/remote-sources/:id | -- | Update a source (label, connection fields, enabled) |
DELETE | /api/remote-sources/:id | purge | Delete a source; ?purge=true also deletes that source's imported sessions |
POST | /api/remote-sources/:id/test | -- | Probe SSH connectivity to the source |
POST | /api/remote-sources/:id/sync | -- | Pull and re-import from the source now |
GET /api/sessions, /api/events, /api/agents, /api/stats, and /api/analytics also accept an optional sources query param (comma-separated source ids; omit for all) to scope results by origin, and GET /api/sessions/facets returns a sources array for the data-scope selector.
| Method | Path | Description |
|---|---|---|
POST | /api/hooks/event | Receive and process a Claude Code hook event |
Hook event payload:
{
"hook_type": "PreToolUse",
"data": {
"session_id": "abc-123",
"tool_name": "Bash",
"tool_input": { "command": "ls -la" }
}
}
| Method | Path | Description |
|---|---|---|
GET | /api/pricing | List all pricing rules |
PUT | /api/pricing | Create or update a pricing rule |
DELETE | /api/pricing/:pattern | Delete a pricing rule |
GET | /api/pricing/cost | Total cost across all sessions |
GET | /api/pricing/cost/:id | Cost breakdown for a specific session |
| Method | Path | Description |
|---|---|---|
GET | /api/workflows | Provider/source-scoped workflow data (orchestration, recorded tools, patterns, Codex compactions). Optional ?status=active|completed, ?sources=..., and ?providers=claude|codex filters apply to all 11 data sections |
GET | /api/workflows/session/:id | Provider/source-scoped per-session drill-in (agent tree, recorded tool timeline, events) |
| Method | Path | Description |
|---|---|---|
GET | /api/alerts | Fired-alert feed, newest first (?unacked=true, limit, offset) |
POST | /api/alerts/:id/ack | Acknowledge one alert |
POST | /api/alerts/ack-all | Acknowledge every unacked alert |
GET | /api/alerts/rules | List alert rules |
POST | /api/alerts/rules | Create a rule (event_pattern | inactivity | status_duration | token_threshold) |
PATCH | /api/alerts/rules/:id | Update name / config / enabled / cooldown (rule type is immutable) |
DELETE | /api/alerts/rules/:id | Delete a rule and its fired-alert history |
| Method | Path | Description |
|---|---|---|
GET | /api/webhooks/providers | Supported providers + their config fields (drives the UI form) |
GET | /api/webhooks | List webhook targets (URLs masked, secrets redacted) |
POST | /api/webhooks | Create a target (14 first-class providers + generic) |
PATCH | /api/webhooks/:id | Update name / url / enabled / secret / headers / rule scope (type is immutable) |
DELETE | /api/webhooks/:id | Delete a target and its delivery log |
POST | /api/webhooks/:id/test | Send a synthetic test alert and report the delivery result |
GET | /api/webhooks/:id/deliveries | Recent delivery log for a target (limit, offset) |
| Method | Path | Description |
|---|---|---|
GET | /api/settings/info | System info, DB stats, hook status |
POST | /api/settings/clear-data | Delete all sessions, agents, events, token usage |
POST | /api/settings/reimport | Re-import legacy sessions from ~/.claude/ |
POST | /api/settings/reinstall-hooks | Reinstall Claude Code hooks |
POST | /api/settings/reset-pricing | Reset pricing to defaults |
GET | /api/settings/export | Export all data (sessions, agents, events, token_usage, workflows, dashboard_runs, alert_rules, model_pricing, gpt_model_pricing) as one versioned JSON download |
POST | /api/settings/import | Restore a bundle from /export (multipart file or JSON { path }). Idempotent + non-destructive β existing sessions are skipped whole |
POST | /api/settings/cleanup | Abandon stale sessions, purge old data |
/api/cc-config)Read-only inspection of every Claude Code configuration surface, plus carefully-gated mutations for low-risk text-file artifacts. All write paths create timestamped backups under <root>/cc-config-backups/<type>/ before mutating.
| Method | Path | Description |
|---|---|---|
GET | /api/cc-config/overview | Roots (claude home, project .claude, project root, ~/.claude.json) + counts for every surface |
GET | /api/cc-config/skills | Skills under <scope>/.claude/skills/<name>/SKILL.md with parsed frontmatter; ?scope=user|project|all |
GET | /api/cc-config/agents | Subagents <scope>/.claude/agents/*.md |
GET | /api/cc-config/commands | Slash commands <scope>/.claude/commands/*.md |
GET | /api/cc-config/output-styles | Output styles <scope>/.claude/output-styles/*.md |
GET | /api/cc-config/plugins | Installed plugins from ~/.claude/plugins/installed_plugins.json, joined with enabledPlugins from settings; each entry includes contributes (count of skills/agents/commands/hooks/output-styles inside the plugin's install dir) plus plugin.json metadata |
GET | /api/cc-config/marketplaces | Registered marketplaces from known_marketplaces.json, enriched with each marketplace's own marketplace.json (plugin count, owner, description) |
GET | /api/cc-config/mcp | MCP servers from ~/.claude.json (top-level + per-project) and settings.json |
GET | /api/cc-config/hooks | Hooks aggregated across user / project / project-local settings.json files |
GET | /api/cc-config/hook-scripts | Files in ~/.claude/hooks/ (the helper scripts referenced by hooks.<event>.command) |
GET | /api/cc-config/keybindings | ~/.claude/keybindings.json parsed into context-grouped key/action pairs |
/api/run)HTTP surface for spawning and supervising claude subprocesses from the dashboard. Same-origin guard on every route β browser requests must come from a localhost origin; missing-Origin (CLI/curl) requests pass.
| Method | Path | Description |
|---|---|---|
GET | /api/run | List all in-memory run handles (live + recently finished); also returns maxConcurrent and activeCount |
GET | /api/run/binary | Probe whether claude is on PATH and where it lives β used by the UI to surface a clear error before spawning |
GET | /api/run/cwds | Suggested working directories: dashboard server cwd, $HOME, and recent cwds from the sessions table |
GET | /api/run/files?cwd=β¦&q=β¦ | Fuzzy file search inside cwd for the Run page's @-file autocomplete. Skips node_modules, .git, dist, build, .next, .cache, coverage, etc. Cwd is required and must exist; results are capped and ranked by basename match |
POST | /api/run | Spawn a new run. Body: { prompt, mode: "headless"|"conversation", cwd?, model?, permissionMode?, resumeSessionId?, effort? }. Headless puts prompt in argv via -p and closes stdin. Conversation pipes the prompt over stdin as a stream-json envelope and keeps stdin open for follow-ups. resumeSessionId (conversation only) adds --resume <id>; when set, prompt may be empty β the spawner skips the initial stdin write and claude idles on the resumed conversation until the user posts a follow-up via POST /api/run/:id/message. effort (low / medium / high) maps to --effort. The spawner always passes --output-format stream-json --verbose --include-partial-messages so the UI can render character-by-character deltas. Concurrency is effectively uncapped (default ceiling 10000 β override with RUN_MAX_CONCURRENT) |
GET | /api/run/:id | Current handle state. ?envelopes=1 includes the in-memory envelope log so the UI can replay history when re-attaching |
Output streams over the existing dashboard WebSocket as three message types: run_stream (parsed stream-json envelope, including stream_event deltas from --include-partial-messages), run_status (status transitions), run_input_ack (stdin write confirmed). The Config Explorer page subscribes to a fourth message β cc_config_changed β broadcast by server/lib/cc-watcher.js (via fs.watch on ~/.claude/) and by routes/cc-config.js after every successful PUT/DELETE, with payload { source: "dashboard"|"fs", action?, scope?, type?, name?, paths? }. The Sessions list and SessionDetail page poll /api/run (and listen for run_status) to badge any session currently being driven by an in-flight Run with a clickable βΆ Run indicator that links back to /run.
Import existing Claude Code or Codex history through the provider
tabs in Settings β Import History. Claude Code uses its shared JSONL
parser for ~/.claude/projects; Codex uses the same append-only rollout
ingestor as real-time monitoring for ~/.codex/sessions, including token
snapshots, response-item tool calls, lifecycle state, and native /rename
titles when session_index.jsonl is included. Re-imports are idempotent:
Claude preserves compaction baselines and Codex retains byte cursors, so
neither provider double-counts usage or cost. Folder and browser-uploaded
Codex history is copied into dashboard-owned storage before temporary files
are cleaned up, keeping the conversation view available later.
A fourth mode β Restore backup β imports a full dashboard export
.json (produced by the Export data button, ccam export, or
GET /api/settings/export) rather than raw Claude transcripts. This is
the round-trip counterpart to Export: it restores every table (sessions,
agents, events, token_usage, workflows, dashboard_runs, alert_rules,
model_pricing, gpt_model_pricing) and is idempotent + non-destructive β a session already
present is skipped whole, so you can safely consolidate several
machines into one dashboard without duplicating or overwriting
anything. Backed by server/lib/data-transfer.js and
POST /api/settings/import (also ccam import-data <file>).
flowchart LR
subgraph Sources
A1["Default folder<br/>~/.claude/projects"]
A2["Custom folder<br/>any absolute path"]
A3["Uploaded files<br/>.jsonl / .meta.json /<br/>.zip / .tar(.gz) / .gz"]
end
A1 -->|POST /api/import/rescan| R["server/routes/import.js"]
A2 -->|POST /api/import/scan-path| R
A3 -->|POST /api/import/upload<br/>multipart| R
R -->|archive extract<br/>+ path-traversal guard<br/>+ zip-bomb cap| X["server/lib/archive.js"]
R -->|walks recursively| I["importFromDirectory<br/>(scripts/import-history.js)"]
X --> I
I -->|same pipeline as live<br/>hook ingestion| P["parseSessionFile +<br/>importSession"]
P -->|prepared statements,<br/>in one transaction| D[("SQLite<br/>sessions / agents / events /<br/>token_usage")]
I -.->|import.progress<br/>throttled| W["WebSocket /ws"]
W -.-> U["Settings β Import History<br/>progress bar + result card"]
style A1 fill:#6366f1,stroke:#818cf8,color:#fff
style A2 fill:#6366f1,stroke:#818cf8,color:#fff
style A3 fill:#6366f1,stroke:#818cf8,color:#fff
style R fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style X fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style I fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style P fill:#f59e0b,stroke:#fbbf24,color:#000
style D fill:#10b981,stroke:#34d399,color:#fff
style U fill:#a855f7,stroke:#c084fc,color:#fff
Routes
| Method | Path | Description |
|---|---|---|
GET | /api/import/guide | Provider-aware OS paths, archive command, extensions, and instructions (?provider=claude|codex) |
POST | /api/import/rescan | Rescan the selected default: ~/.claude/projects or ~/.codex/sessions ({ provider }) |
POST | /api/import/scan-path | Scan an absolute directory with { path, provider }; walks recursively |
POST | /api/import/upload | Multipart upload of .jsonl, .meta.json, .zip, .tar(.gz), .gz with provider |
Supported inputs. Loose JSONL (.jsonl) session transcripts, their
companion .meta.json sidecars, and archives (.zip, .tar,
.tar.gz/.tgz, plain .gz) containing any nested directory layout.
Both canonical Claude Code layouts are recognized automatically:
<project>/<sessionId>/subagents/agent-*.jsonl (default) and
<project>/subagents/<sessionId>/agent-*.jsonl (alternative).
For Codex, the importer recognises recursive rollout-*.jsonl session files
(including loose JSONL files with session_meta) and an optional
session_index.jsonl for native session names.
Accuracy guarantees. Sessions are deduplicated by UUID; re-running
the importer is always safe. The compaction baseline_input /
baseline_output / baseline_cache_read / baseline_cache_write
columns preserve token counts from before a transcript was compacted,
so re-ingesting a post-compaction JSONL never erases historical cost.
Event-level dedup uses a per-event-type high-water mark
(MAX(created_at) GROUP BY event_type for the session): on every
re-import only JSONL entries with ts > cutoff[type] are inserted, so
long-running sessions whose transcripts grow across multiple days
continue to receive Stop / PostToolUse / TurnDuration / ToolError
events without duplicating earlier work. sessions.ended_at is rolled
forward to the JSONL's last activity when it surpasses the stored
value, and message-count metadata is refreshed on every pass.
Huge-transcript safety. The shared transcript cache
(server/lib/transcript-cache.js) reads JSONL files in 4 MiB chunks
and decodes only one line at a time, so transcripts larger than V8's
max JS string length (~512 MiB on 64-bit Node 20) parse without
aborting the process with FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal. The same chunked path is used by hook ingestion, the
periodic compaction sweep, and the history importer β none of them
materialize the full file as a single JS string. Per-entry growable
arrays (turnDurations, errors, compaction.entries,
usageExtras.*) are tail-capped at TRANSCRIPT_CACHE_MAX_ARRAY_LEN
(default 1000), with trimming applied during parse at a 2 Γ cap
watermark so a fresh full-file parse on a multi-day session can't
build an unbounded transient before finalization.
Safety. Archive extraction validates every entry against path
traversal (absolute paths and .. segments are rejected). A
configurable extraction cap (CCAM_IMPORT_MAX_EXTRACT_BYTES, default
4 GB) stops zip/tar/gzip bombs. Upload size is capped per file
(CCAM_IMPORT_MAX_BYTES, default 1 GB) and per request
(CCAM_IMPORT_MAX_FILES, default 2000). All staging directories are
per-request and reclaimed in finally, including when multer rejects
all files up front.
Progress. Import activity is broadcast over the existing WebSocket
as import.progress messages (phase: start / scan / extract /
parse / complete / error), throttled to avoid flooding the
channel on large imports.
UI. Use the Settings β Import History panel for a guided, drag-and-drop experience with step-by-step instructions, live progress, and a post-import summary showing imported / enriched / skipped / error counts.
Connect to ws://localhost:4820/ws to receive real-time push messages:
{
"type": "agent_updated",
"data": { "id": "...", "status": "working", "current_tool": "Edit" },
"timestamp": "2026-03-05T15:43:01.800Z"
}
Message types: session_created, session_updated, agent_created, agent_updated, new_event, alert_triggered, alert_updated, remote_source.status (data { id, status: "idle"|"syncing"|"ok"|"error"|"deleted", error?, last_sync_at? })
stateDiagram-v2
[*] --> Connecting: Component mounts
Connecting --> Connected: onopen
Connected --> Closed: onclose / onerror
Closed --> Connecting: setTimeout(2000ms)
Connected --> [*]: Component unmounts
Closed --> [*]: Component unmounts
The dashboard processes these Claude Code hook types:
| Hook Type | Trigger | Dashboard Action |
|---|---|---|
SessionStart | Claude Code session begins | Creates session and main agent. Stamps awaiting_input_since (with awaiting_reason=session_start) so a fresh session lands in Waiting β except a compact-source SessionStart (mid-turn auto-compaction), which leaves the flag untouched so a working session stays Active. Reactivates resumed sessions. Abandons orphaned sessions with no activity for DASHBOARD_STALE_MINUTES (default 180) |
UserPromptSubmit | User hits enter on a prompt | Clears the waiting flag and promotes the main agent to working β the only signal that text-only assistant turns have started, since they emit no PreToolUse |
PreToolUse | Agent starts using a tool | Clears the waiting flag, sets agent to working, sets current_tool. If tool is Agent, creates a subagent record |
PostToolUse | Tool execution completed | Clears the waiting flag (handles permission-prompt approvals where the Notification stamped it mid-tool). Clears current_tool. Agent stays working |
Stop | Claude finishes responding | Non-error: main agent β waiting β Claude finished its turn, ball is in the user's court. stop_reason=error: marks the agent and session error. Background subagents keep running |
SubagentStop | Background agent finished | Matches and completes the subagent by description, type, or task. Deliberately does NOT clear the waiting flag β a subagent finishing tells us nothing about the human. Triggers a fire-and-forget JSONL scan (scanAndImportSubagents) that emits per-tool PreToolUse + PostToolUse events under the subagent's own agent_id so the Timeline shows every tool the subagent ran, not just the spawn marker |
Notification | Agent notification | Logs event. Permission/input-prompt messages set the agent to waiting and stamp awaiting_input_since (with awaiting_reason=notification, matched by pattern: , , , β¦). Compaction notifications are tagged as events. Triggers a browser notification if enabled |
The dashboard supports persistent browser notifications via Web Push (VAPID) for real-time alerts even when the dashboard tab is not focused or the browser is backgrounded.
| Event | Default | Description |
|---|---|---|
| New session starts | On | Fires when a new Claude Code session is created |
| Claude finished responding | Off | Fires on Stop events when Claude finishes a response turn |
| Session closed | Off | Fires on SessionEnd when the CLI process exits |
| Session errors | On | Fires when a session ends with an error |
| Subagent spawned | Off | Fires when a background subagent is created |
Additionally, any Notification hook event from Claude Code triggers a browser notification regardless of the per-event toggles (as long as the master toggle is enabled).
web-push on the server for secure message delivery. VAPID keys are auto-generated and stored in data/vapid-keys.json.client/public/sw.js) handles incoming push events and displays notifications with silent: false to ensure audio playback on macOS.push_subscriptions table in SQLite.The project ships three independent Progressive Web Apps β one each for the dashboard, landing page, and wiki. Each has its own manifest.json and Service Worker so the browser treats them as separate installable applications.
| Surface | Manifest | Service Worker | Caching Strategy |
|---|---|---|---|
Dashboard (client/) | client/public/manifest.json | client/public/sw.js | Vite's content-hashed bundles under /assets/* are served cache-first (URLs are immutable per build). Everything else β navigations, the SW itself, manifest.json, icons, root / β is network-first with cache fallback, so a rebuild always shows the freshest UI without a hard refresh. API (/api/*), WebSocket (/ws), and Vite HMR requests are never cached. Push notification handlers are preserved alongside the caching logic. The Express static middleware (server/index.js) reinforces this by sending Cache-Control: public, max-age=31536000, immutable for /assets/* and Cache-Control: no-cache, must-revalidate for index.html, sw.js, and manifest.json. client/src/main.tsx listens for controllerchange: when a new SW activates on an already-controlled page it reloads exactly once (first installs do not). |
| Landing page (root) | manifest.json | sw.js | Precaches the HTML shell, favicon, and OG image. Screenshot PNGs are lazy-cached on first view (cache-first) to avoid a heavy initial precache. Navigation is network-first with offline fallback. |
Wiki (wiki/) | wiki/manifest.json | wiki/sw.js | Precaches index.html, style.css, script.js, manifest, and favicon. Fully offline-capable after one visit. Network-first HTML, cache-first for CSS/JS. |
Cache lifecycle: All three SWs call skipWaiting() on install and delete stale caches on activate (keyed by version strings like dashboard-v2, landing-v1, wiki-v1). Bumping the version constant forces a clean refresh.
iOS support: All three HTML files include <meta name="apple-mobile-web-app-capable" content="yes"> and <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> for standalone home-screen mode on Safari.
Icons: Manifests reference favicon.svg with sizes="any" and type="image/svg+xml" β supported in Chrome 107+, Firefox 110+, Edge 107+. Apple touch icons also use the SVG favicon.
The dashboard watches its own git checkout and surfaces a modal whenever the canonical default branch has commits ahead of HEAD. Branch- and fork-aware: if you have an upstream remote (the standard convention for forks), it's preferred over origin; the chosen remote's master/main/HEAD is the comparison ref. The manual_command adapts to your situation β git pull --ff-only only when your branch actually tracks the canonical ref, otherwise a git fetch (and a fast-forward merge in the fork case) so the command never lies. Users get the exact command to run in a terminal β the server never pulls or restarts itself, which keeps the mechanism portable across dev sessions, pm2/systemd/launchd/Docker supervision, and remote deployments.
flowchart LR
S["Server startup"] --> SCHED["Update scheduler<br/>poll every 5 min"]
SCHED --> PICK["Pick canonical remote<br/>upstream then origin"]
PICK --> FETCH["git fetch remote prune<br/>execFile 120s timeout"]
FETCH --> CMP["rev-list HEAD vs<br/>remote master main HEAD"]
CMP --> FP["Fingerprint changed?"]
FP -->|yes| WS["broadcast<br/>update_status"]
FP -->|no| IDLE["skip broadcast"]
WS --> CLIENT["UpdateNotifier<br/>+ Sidebar badge"]
CHECK["POST updates check"] --> FETCH
STATUS["GET updates status"] -.-> CMP
style WS fill:#6366f1,stroke:#818cf8,color:#fff
style CLIENT fill:#10b981,stroke:#34d399,color:#fff
A single check is cheap (git fetch <remote> --prune against the canonical remote β upstream if configured, else origin), wrapped with execFile (no shell) and a 120s timeout. Failures β offline network, non-git install, no remotes configured, unresolvable upstream ref β all return soft payloads (e.g. fetch_error: "...") rather than throwing, so a flaky remote never blocks the dashboard.
| Surface | Behavior |
|---|---|
Modal (client/src/components/UpdateNotifier.tsx) | Appears when update_available === true and the user hasn't already dismissed this specific remote_sha. Shows commits-behind, the tracked ref, an optional situation_note (when on a feature branch / fork the note explains why the command differs), the copy-pastable command, and three buttons: Copy command (primary), Check now, Dismiss. ESC and backdrop clicks dismiss. Keyed by remote_sha in localStorage, so a newer upstream commit re-opens the modal automatically. |
Sidebar button (client/src/components/Sidebar.tsx) | Always-visible "Check for updates" button in the footer. Emerald border + green badge dot when behind, amber when the last check hit a fetch error. Clicking it clears any prior dismissal, then fires POST /api/updates/check. |
| Server terminal | When the scheduler transitions from "up to date" to "behind," it prints a framed block to stdout with the command so users running headless still see it. |
| Endpoint | Purpose |
|---|---|
GET /api/updates/status | Read-only check: runs git fetch against the canonical remote, compares HEAD to its default branch, returns the payload. |
POST /api/updates/check | Same check, but also broadcasts update_status over WebSocket so all connected clients update at once. |
Both endpoints return the same payload shape:
{
"git_repo": true,
"update_available": true,
"repo_root": "/Users/you/Claude-Code-Agent-Monitor",
"remote_ref": "upstream/master",
"canonical_remote": "upstream",
"current_branch": "master",
"tracking_upstream": "origin/master",
"tracks_canonical": false,
"situation": "fork_or_diverged_tracking",
"local_sha": "abc1234...",
"remote_sha": "def5678...",
"commits_behind": 3,
"manual_command": "cd \"/...\" && git fetch upstream && git merge --ff-only upstream/master && npm run setup",
"situation_note": "You're on 'master' tracking 'origin/master'. This command fast-forwards your branch from upstream/master (the canonical default).",
"message": "3 commit(s) on upstream/master not in your checkout."
}
situation is one of tracking_canonical (typical clone on the default branch β git pull --ff-only works), fork_or_diverged_tracking (local branch name matches canonical, but tracks a different remote β git fetch <remote> && git merge --ff-only <ref>), feature_branch (off the default branch β fetch only, integration left to the user), or detached_head.
There is no POST /api/updates/apply and no self-restart helper, by design. Self-updating a process from inside itself is unreliable without an external supervisor β npm run dev (concurrently), npm start, pm2, systemd, launchd, and Docker each need different restart logic, and git pull / npm install failures on a dying server have no clean rollback path. Detection-only keeps behaviour predictable across every supervisor, every OS, and every branch state, while still closing the "when do I need to pull?" information gap; the user owns the actual update in their own shell.
| Env Var | Default | Notes |
|---|---|---|
DASHBOARD_UPDATE_CHECK | enabled | Set to 0 / false / off to disable the scheduler entirely. |
DASHBOARD_UPDATE_CHECK_INTERVAL_MS | 300000 (5 min) | Interval between automatic checks. Floor is 60 000 ms β values below are clamped. |
Tabby is a cute floating cat companion pinned to the bottom-right corner of every page in the dashboard. Always present, it turns the live session stream into an at-a-glance, reactive mascot you can also talk to.
Tabby is a cursor-tracking SVG cat with eight moods derived from the live session stream, each with its own animation:
| Mood | When | Animation |
|---|---|---|
idle | Nothing notable happening | Resting tail flick |
watching | Sessions are active | Ear perk, eyes follow the cursor |
happy | A session or run finished cleanly | Head bob + sparkle |
worried | Something looks off | Subtle shake |
stuck | A session appears blocked | Alert "!" |
thinking | An agent is mid-work | Slow head bob |
sleeping | Quiet for a while | zzz |
disconnected | WebSocket is down | Calm, still pose |
Tabby auto-surfaces short quips on notable events (session started/finished, errors, run completed). Bubbles are throttled and coalesced so a burst of events never spams the screen, they use aria-live for screen readers, and they can be muted from the panel.
Open the panel by clicking the cat or pressing βB / Ctrl+B (Esc closes). It shows:
N live Β· M errored Β· connection state.The Ask box answers simple status questions locally from cached data β for example "what's running", "any errors", or "status". Any other question is handed off to the existing Run Claude page (it deep-links to /run?prompt=β¦) to spawn a real Claude Code session. Tabby never calls an LLM itself; it simply reuses the Run Claude page for anything beyond a quick status lookup.
Tabby is keyboard-operable, uses aria-live for its bubbles, and honors prefers-reduced-motion. If the WebSocket is down it degrades safely to a calm disconnected state rather than erroring. You can toggle Tabby on or off in Settings (localized in English, Chinese, Vietnamese, Korean, and Spanish), and the implementation lives in client/src/components/Tabby/.
Click the Live / Disconnected pill in the sidebar footer to open a small details panel about the dashboard's WebSocket transport. It shows the active ws:// endpoint, how long the current socket has been up, total events received, top event types as a horizontal bar chart, a 60-second throughput sparkline, and the last 8 events as a recent-activity list. Cumulative stats (totals, type breakdown, recent list) persist across reloads via localStorage under sidebar-connection-stats; the rolling sparkline and "connected since" timer are intentionally ephemeral. A Reset button in the footer clears everything on demand.
The Claude Code Agent Monitor is available as a first-class VS Code extension, allowing you to monitor your AI agents without leaving your editor.
vsce package.npm run dev).For detailed developer configuration, see the .vscode and vscode-extension directories.
[!TIP] Extension on VS Code Marketplace: Claude Code Agent Monitor
The dashboard also ships as an optional native desktop application you install once and forget β a macOS .app (distributed as a .dmg) and a Windows .exe (an NSIS installer plus a no-install portable build). It lives in the desktop/ workspace, a sibling of client/, server/, mcp/, and vscode-extension/, and is built with Electron 35.
Everything you see in the browser at localhost:4820 lives inside this window, with native OS lifecycle on top: a tray icon, a native application menu, auto-start integration, and a single quit button that cleanly shuts the server down.
Unlike running the dashboard from a terminal, the desktop app needs no npm start, no open shell, and no second copy of the server. The Electron main process hosts the Express server in-process β it require()s server/index.js directly in the same Node runtime, with no child process and no IPC β and points a Chromium BrowserWindow at the built React client.
flowchart LR
subgraph electron["Claude Code Monitor.app β one Electron process"]
main["Electron Main Process<br/>Node 22 / Electron 35"]
host["server-host.ts<br/>port discovery Β· adoption Β· ABI patch"]
express["server/index.js<br/>Express API Β· SQLite Β· WebSocket"]
win["BrowserWindow<br/>built React client (client/dist)"]
tray["tray.ts + menu.ts<br/>menu-bar icon Β· native app menu"]
login["login-item.ts<br/>auto-start via SMAppService"]
main -->|"startEmbeddedServer()"| host
host -->|"require() in-process β no child process, no IPC"| express
main --> tray
main --> login
express -->|"http + ws on 127.0.0.1:<port>"| win
end
hooks["Claude Code hooks<br/>(separate node processes)"] -->|"POST /api/hooks/event"| express
sqlite[("data/dashboard.db<br/>SQLite β closed cleanly on quit")] <--> express
style main fill:#47848F,stroke:#2f5a62,color:#fff
style express fill:#339933,stroke:#5cb85c,color:#fff
style win fill:#61DAFB,stroke:#3aa9c9,color:#000
style host fill:#1f6feb,stroke:#1158c7,color:#fff
On launch the app:
/api/health on 4820 (e.g. you ran npm start in a terminal), it adopts that server instead of double-binding β no port collision, no SQLite contention. An adopted server keeps running after you quit the app.cc-watcher, orphaned-run reconciliation). A DMG-only user therefore gets events flowing with zero manual setup β no checkout, no npm run install-hooks.PATH so the Run Claude feature can find and spawn the claude CLI β a Finder/Dock-launched app otherwise inherits only launchd's minimal PATH and would miss CLIs in ~/.local/bin, /opt/homebrew/bin, version-manager bins, etc. (On Windows the process already inherits the user PATH.)HKCU\β¦\Run entry), in which case it stays tray-only.icon.ico (a black template would vanish on the dark taskbar).BrowserWindow is wired to the colored app logo (icon.ico on Windows, icon.png elsewhere), so the title bar / taskbar show the real Claude Code Monitor icon β even an unpackaged npm run desktop:dev run no longer shows the generic Electron icon.About / File / Edit / View / Window / Help menu with β / Ctrl shortcuts. The File β Open Dashboard item (β1) is macOS-only: macOS keeps a global menu bar after the window hides, so it can reopen the window β on Windows/Linux the menu is attached to the window and can't fire while it's hidden, so reopen from the tray's Open Dashboard instead (which reliably raises the window even when minimized or behind other windows).SMAppService API, so the entry appears under System Settings β General β Login Items; on Windows it writes a per-user HKCU\Software\Microsoft\Windows\CurrentVersion\Run entry, visible in Task Manager β Startup.~/Library/Application Support/Claude Code Monitor/data/ on macOS, %APPDATA%\Claude Code Monitor\data\ on Windows. A packaged bundle is read-only, so writing the database inside it would break History Import and event persistence; keeping it in app-data fixes that and means your imported history is untouched when you replace or upgrade the app. (The Windows NSIS uninstaller keeps this data by default.)Option A β download a pre-built installer (recommended). From Releases β latest (public, no GitHub sign-in). CI auto-publishes a new vX.Y.Z release whenever the version in package.json is bumped on master, so this link always serves the current build:
| Platform | Asset | Notes |
|---|---|---|
| macOS (Apple Silicon) | ClaudeCodeMonitor-<ver>-arm64.dmg | drag into /Applications |
| macOS (Intel) | ClaudeCodeMonitor-<ver>-x64.dmg | drag into /Applications |
| Windows (installer) | ClaudeCodeMonitor-Setup-<ver>-x64.exe | per-user install, no admin |
| Windows (portable) | ClaudeCodeMonitor-<ver>-x64-portable.exe | run without installing |
Per-commit fresh builds also live as CI artifacts (sign-in required, 14-day retention): ClaudeCodeMonitor-dmg from the π macOS Desktop (DMG) job and ClaudeCodeMonitor-win from the πͺ Windows Desktop (EXE) job β useful for testing master before the next release tag.
Option B β build it yourself. From the repo root:
npm run setup # install root + client deps, build client, install hooks
npm run build # build the React client (client/dist)
npm run desktop:install # install Electron + electron-builder into desktop/ (preflights native deps; prints setup help on failure)
npm run desktop:dmg:arm64 # macOS: fast single-arch DMG β desktop/release/ClaudeCodeMonitor-<ver>-arm64.dmg
npm run desktop:win # Windows: NSIS installer β desktop/release/ClaudeCodeMonitor-Setup-<ver>-x64.exe
[!NOTE] DMGs build on macOS; Windows
.exes build on Windows β electron-builder packages for the host OS. The macOSnpm run desktop:dmgbuild packages the app twice (once per architecture) and emits both per-arch DMGs (arm64+x64) β the release build; it does not merge them into one universal binary. For your own Mac use the single-archdesktop:dmg:arm64/desktop:dmg:x64. On Windows,better-sqlite3is fetched as a prebuilt Electron binary bynpm run desktop:install, so no Visual Studio C++ toolchain is needed in the common case. If the build does fail (no prebuilt binary, or a missing C++ toolchain),desktop:installprints the exact per-OS fix plus a no-toolchain alternative and fails loudly instead of leaving a broken install.
macOS:
Double-click the .dmg to mount it.
Drag Claude Code Monitor.app into your /Applications folder.
The DMG is ad-hoc signed by default, so macOS Gatekeeper warns on first launch ("Apple could not verifyβ¦"). Clear the quarantine attribute:
xattr -cr "/Applications/Claude Code Monitor.app"
Or open System Settings β Privacy & Security and click Open Anyway.
Launch the app. The tray icon appears and the dashboard window opens.
Windows:
ClaudeCodeMonitor-Setup-<ver>-x64.exe. It installs per-user under %LOCALAPPDATA%\Programs\Claude Code Monitor (no administrator elevation) and lets you pick the install directory; or run the *-portable.exe to launch without installing.All commands run from the repo root:
| Command | What it does |
|---|---|
npm run desktop:install | Install Electron + electron-builder into desktop/; rebuild better-sqlite3 for Electron's ABI; preflights the native better-sqlite3 build and prints actionable setup help (incl. a no-toolchain alternative) on failure |
npm run desktop:build | Compile the desktop TypeScript sources into desktop/out/ |
npm run desktop:dev | Build and launch the Electron app for local iteration |
npm run desktop:test | Run the smoke test (spawn Electron, probe /api/health, shut down) |
npm run desktop:dmg | macOS: build both DMGs (arm64 + x64) β correct for release, slower (packages each arch) |
npm run desktop:dmg:arm64 | macOS: build an Apple-Silicon-only DMG β fast (~1 min), recommended for your own Mac |
npm run desktop:dmg:x64 | macOS: build an Intel-only DMG β fast (~1 min) |
npm run desktop:dmg:universal | macOS: build one merged universal DMG (arm64 + x86_64) β optional, slowest, not what the release ships |
npm run desktop:win | Windows: build the NSIS installer .exe (x64) |
npm run desktop:win:portable | Windows: build the no-install portable .exe (x64) |
The resulting macOS DMG is ~80 MB (β 250 MB on disk once installed) and the Windows installer is comparable β the standard Electron bundle tax.
The macOS DMG is ad-hoc signed by default so anyone can build a working .app without a paid Apple Developer account β the package script sets CSC_IDENTITY_AUTO_DISCOVERY=false so a code-signing certificate already in the contributor's keychain is never auto-picked. Real Developer ID signing is opt-in via CSC_LINK (a base64-encoded .p12) and CSC_KEY_PASSWORD; Apple notarization is opt-in via APPLE_ID, APPLE_TEAM_ID, and APPLE_APP_SPECIFIC_PASSWORD. The Windows build is unsigned by default (SmartScreen may prompt on first launch β More info β Run anyway); Authenticode signing activates only when an explicit certificate is provided via CSC_LINK + CSC_KEY_PASSWORD. CI picks all of these up automatically when provided β no code change required.
better-sqlite3 is the only native module in the dependency tree, and a native module must be compiled against the exact Node ABI it runs on. The desktop/ workspace ships its own copy of better-sqlite3 rebuilt for Electron's ABI and uses a process-local require redirect to point server/db.js at it; the repo-root copy stays built for system Node (so npm run test:server keeps working).better-sqlite3 for the target architecture, which can leave the desktop copy built for the other CPU arch and break npm run desktop:dev / npm run desktop:test with ERR_DLOPEN_FAILED. The desktop prebuild step now auto-heals the native module for the local machine on the next build, so the dev and smoke-test flows keep working after an arch-specific DMG build. The prebuild step also fails fast with setup help when the better-sqlite3 native binary is missing entirely, turning a runtime crash into a copy-pasteable build-time error.desktop/ is a behavior-preserving refactor of server/index.js: its post-listen bootstrap (update scheduler, cc-watcher, orphaned-run reconciliation) was extracted into an exported startBackgroundServices() so the embedded server runs exactly what node server/index.js runs. The standalone node server/index.js path is functionally unchanged; client/, scripts/, mcp/, and vscode-extension/ are untouched.π macOS Desktop (DMG) on macos-latest (uploads the ClaudeCodeMonitor-dmg artifact β two single-arch DMGs) and πͺ Windows Desktop (EXE) on windows-latest (uploads the ClaudeCodeMonitor-win artifact β NSIS installer + portable). On a version-bump push to master, the release job attaches both the macOS DMGs and the Windows .exes to the published vX.Y.Z GitHub Release. The Windows icon (desktop/assets/icon.ico) is committed to the repo (regenerate it from icon.png with npm run build:win-icon, PowerShell + .NET, no extra tooling).For the full user guide (download, install, Gatekeeper / SmartScreen, tray menu, auto-start) see DESKTOP.md; for the contributor / architecture reference (process model, boot lifecycle, port discovery, build pipeline β with Mermaid diagrams) see desktop/README.md.
better-sqlite3 (optional) or Node.js built-in node:sqlitedata/dashboard.dbdata/dashboard.db to clear all dataerDiagram
sessions ||--o{ agents : has
sessions ||--o{ events : has
sessions ||--o{ token_usage : tracks
agents ||--o{ events : generates
agents ||--o{ agents : spawns
sessions {
TEXT id PK "UUID"
TEXT name "Human-readable label"
TEXT status "active|completed|error|abandoned"
TEXT cwd "Working directory"
TEXT model "Claude model ID"
TEXT started_at "ISO 8601"
TEXT ended_at "ISO 8601 or NULL"
TEXT metadata "JSON blob"
TEXT awaiting_input_since "ISO 8601 or NULL β set when Waiting"
TEXT awaiting_reason "notification|stop|session_start|interrupted or NULL"
}
agents {
TEXT id PK "UUID or session_id-main"
TEXT session_id FK
TEXT name "Main Agent β {session name} or subagent description"
TEXT type "main|subagent"
TEXT status "working|waiting|completed|error"
TEXT current_tool "Active tool or NULL"
TEXT awaiting_input_since "ISO 8601 or NULL β supplementary wait timestamp"
TEXT awaiting_reason "notification|stop|session_start|interrupted or NULL"
}
events {
INTEGER id PK "Auto-increment"
TEXT session_id FK
TEXT agent_id FK
TEXT event_type "PreToolUse|PostToolUse|Stop|etc"
TEXT tool_name "Tool that fired the event"
TEXT created_at "ISO 8601"
}
token_usage {
TEXT session_id PK "Composite PK with model"
TEXT model PK "Model identifier"
INTEGER input_tokens
INTEGER output_tokens
INTEGER cache_read_tokens
INTEGER cache_write_tokens
}
model_pricing {
TEXT model_pattern PK "SQL LIKE pattern"
TEXT display_name "Human-readable name"
REAL input_per_mtok "USD per M input tokens"
REAL output_per_mtok "USD per M output tokens"
REAL cache_read_per_mtok "USD per M cache reads"
REAL cache_write_per_mtok "USD per M cache writes"
}
Extend Claude Code with official Agent Monitor plugins β analytics, productivity tools, developer utilities, AI-powered insights, dashboard connectivity, cost guardrails, session forensics, workflow orchestration, reliability/SLOs, and config governance. 10 plugins, 53 skills, 14 agents, 30 slash commands, 3 CLI tools, 3 hook configs, 1 MCP server.
claude plugin marketplace add hoangsonww/Claude-Code-Agent-Monitor
| Plugin | Install command | Skills |
|---|---|---|
| ccam-analytics | claude plugin install ccam-analytics@hoangsonww-claude-code-agent-monitor | session-report, cost-breakdown, usage-trends, productivity-score |
| ccam-cost-guard | claude plugin install ccam-cost-guard@hoangsonww-claude-code-agent-monitor | budget-set, spend-forecast, cost-alert, model-savings, daily-budget-check |
| ccam-productivity | claude plugin install ccam-productivity@hoangsonww-claude-code-agent-monitor | daily-standup, weekly-report, sprint-summary, workflow-optimizer |
| ccam-devtools | claude plugin install ccam-devtools@hoangsonww-claude-code-agent-monitor | session-debug, hook-diagnostics, data-export, health-check |
| ccam-insights | claude plugin install ccam-insights@hoangsonww-claude-code-agent-monitor | pattern-detect, anomaly-alert, optimization-suggest, session-compare |
| ccam-sessions | claude plugin install ccam-sessions@hoangsonww-claude-code-agent-monitor | session-search, session-timeline, transcript-replay, cwd-rollup, session-cleanup |
| ccam-workflows | claude plugin install ccam-workflows@hoangsonww-claude-code-agent-monitor | dag-map, delegation-audit, concurrency-report, error-propagation, fleet-runs |
| ccam-quality | claude plugin install ccam-quality@hoangsonww-claude-code-agent-monitor | error-scan, api-error-report, hook-failure-audit, , |
ccam-stats β Terminal dashboard (sessions, costs, tokens with compaction baselines)ccam-doctor β System diagnostics (API, database, hooks, data freshness)ccam-export β Data export (JSON, CSV) for sessions, events, analytics, costs# In Claude Code, after installing a plugin:
/ccam-analytics:session-report latest
/ccam-analytics:cost-breakdown this week
/ccam-productivity:daily-standup today
/ccam-insights:pattern-detect tools
/ccam-dashboard:quick-stats
A server test (
server/__tests__/plugins-marketplace.test.js) validates the marketplace βplugins/directory bijection and every plugin'splugin.json, agents, skills, and commands.
π Full documentation: docs/plugins.md
A standalone CLI statusline utility for Claude Code that displays model name, user, working directory, git branch, context window usage bar, per-direction token counts, and session cost -- all color-coded with ANSI escape sequences.
nguyens6@host ~/agent-dashboard/client | Sonnet 4.6 | main | ββββββββββ 79% | 3β 2β 156586c | $0.4231
| Segment | Color | Example |
|---|---|---|
| Model | Cyan | Sonnet 4.6 |
| User | Green | nguyens6 |
| CWD | Yellow | ~/agent-dashboard |
| Git branch | Magenta | main |
| Context bar | Green / Yellow / Red | ββββββββββ 79% |
| Tokens | Green / Cyan / Dim | 3β 2β 156586c (green β in, cyan β out, dim c cache) |
| Cost (USD) | Green / Yellow / Red | $0.4231 (session total β shown on API and subscription plans) |
Cost color thresholds: green under $5, yellow $5β$20, red $20+.
See statusline/README.md for installation instructions.
graph TD
INDEX["server/index.js<br/>Express app + HTTP server"]
DB["server/db.js<br/>SQLite + prepared statements"]
WS["server/websocket.js<br/>WS server + broadcast"]
HOOKS["routes/hooks.js<br/>Hook event processing"]
SESSIONS["routes/sessions.js"]
AGENTS["routes/agents.js"]
EVENTS["routes/events.js"]
STATS["routes/stats.js"]
ANALYTICS["routes/analytics.js"]
PRICING["routes/pricing.js<br/>Cost calculation"]
SETTINGS["routes/settings.js<br/>System management"]
WORKFLOWS["routes/workflows.js<br/>Workflow visualizations"]
INDEX --> DB & WS
INDEX --> HOOKS & SESSIONS & AGENTS & EVENTS & STATS & ANALYTICS & PRICING & SETTINGS & WORKFLOWS
HOOKS --> DB & WS
SESSIONS --> DB & WS
AGENTS --> DB & WS
EVENTS --> DB
STATS --> DB
ANALYTICS --> DB
PRICING --> DB
SETTINGS --> DB
WORKFLOWS --> DB
style INDEX fill:#6366f1,stroke:#818cf8,color:#fff
style DB fill:#003B57,stroke:#005f8a,color:#fff
style WS fill:#10b981,stroke:#34d399,color:#fff
graph LR
ROOT["/ (index)"] --> DASH["Dashboard<br/>stats + agents + events"]
K["/kanban"] --> KANBAN["KanbanBoard<br/>agents/sessions toggle"]
S["/sessions"] --> SESS["Sessions<br/>server-paginated table"]
D["/sessions/:id"] --> DETAIL["SessionDetail<br/>agents + timeline + cost"]
A["/activity"] --> ACT["ActivityFeed<br/>streaming event log"]
AN["/analytics"] --> ANALYTICS["Analytics<br/>tokens + heatmap + trends"]
WF["/workflows"] --> WORKFLOWS["Workflows<br/>D3 visualizations + drill-in"]
CC["/cc-config"] --> CCCONFIG["CcConfig<br/>12-tab Claude Code config inspector + editor"]
RUN["/run"] --> RUNPAGE["Run<br/>spawn / resume / stream Claude subprocess"]
ST["/settings"] --> SETTINGS["Settings<br/>pricing + notifications + hooks + export"]
NF["/*"] --> NOTFOUND["NotFound<br/>404 catch-all"]
ALL["All routes"] --> LAYOUT["Layout wrapper<br/>(Sidebar + Outlet)"]
style ALL fill:#6366f1,stroke:#818cf8,color:#fff
style LAYOUT fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
flowchart TD
START["Claude Code fires hook"] --> STDIN["Read stdin to EOF"]
STDIN --> PARSE{"Parse JSON?"}
PARSE -->|Success| POST["POST to 127.0.0.1:4820<br/>/api/hooks/event"]
PARSE -->|Failure| WRAP["Wrap raw input as JSON"]
WRAP --> POST
POST --> RESP{"Response?"}
RESP -->|200 OK| EXIT0["exit(0)"]
RESP -->|Error| EXIT0
RESP -->|Timeout 3s| DESTROY["Destroy request"] --> EXIT0
SAFETY["Safety net: setTimeout 5s"] --> EXIT0
style EXIT0 fill:#10b981,stroke:#34d399,color:#fff
style START fill:#6366f1,stroke:#818cf8,color:#fff
We support both development and production deployment modes with different process architectures:
graph LR
subgraph dev["Development β 2 processes"]
D_CMD["npm run dev"] --> D_SRV["Express :4820<br/>node --watch"]
D_CMD --> D_VITE["Vite :5173<br/>HMR"]
D_BROWSER["Browser"] --> D_VITE
D_VITE -->|"proxy /api + /ws"| D_SRV
end
subgraph prod["Production β 1 process"]
P_BUILD["npm run build"] --> P_DIST["client/dist/"]
P_START["npm start"] --> P_SRV["Express :4820<br/>serves static + API"]
P_BROWSER["Browser"] --> P_SRV
end
style D_VITE fill:#646CFF,stroke:#818cf8,color:#fff
style D_SRV fill:#339933,stroke:#5cb85c,color:#fff
style P_SRV fill:#339933,stroke:#5cb85c,color:#fff
style P_DIST fill:#646CFF,stroke:#818cf8,color:#fff
Optional local MCP sidecar (supports stdio, HTTP+SSE, and REPL transports):
graph LR
subgraph "MCP Transport Options"
M_STDIO["MCP Server (stdio)<br/>npm run mcp:start"]
M_HTTP["MCP Server (HTTP)<br/>npm run mcp:start:http<br/>:8819"]
M_REPL["MCP Server (REPL)<br/>npm run mcp:start:repl"]
end
H["MCP Host"] -->|"stdin/stdout"| M_STDIO
RC["Remote Client"] -->|"POST /mcp Β· GET /sse"| M_HTTP
OP["Operator"] -->|"interactive CLI"| M_REPL
M_STDIO --> D["Dashboard Server<br/>:4820"]
M_HTTP --> D
M_REPL --> D
style M_STDIO fill:#0f766e,stroke:#14b8a6,color:#fff
style M_HTTP fill:#0f766e,stroke:#14b8a6,color:#fff
style M_REPL fill:#0f766e,stroke:#14b8a6,color:#fff
Optional desktop app (macOS & Windows) β a single Electron process that hosts the Express server in-process (no terminal, no child process):
flowchart LR
subgraph desktop["Desktop App (macOS & Windows) β 1 Electron process"]
E_MAIN["Electron Main Process<br/>(Node 22 / Electron 35)"]
E_HOST["server-host.ts<br/>require() server/index.js"]
E_SRV["Embedded Express :4820<br/>API Β· SQLite Β· WebSocket"]
E_WIN["BrowserWindow<br/>built React client"]
E_TRAY["Menu-bar (tray) icon<br/>+ native app menu"]
E_MAIN --> E_HOST
E_HOST -->|"in-process require()"| E_SRV
E_MAIN --> E_TRAY
E_SRV -->|"http + ws on 127.0.0.1"| E_WIN
end
E_HOOKS["Claude Code hooks"] -->|"POST /api/hooks/event"| E_SRV
style E_MAIN fill:#47848F,stroke:#2f5a62,color:#fff
style E_SRV fill:#339933,stroke:#5cb85c,color:#fff
style E_WIN fill:#61DAFB,stroke:#3aa9c9,color:#000
The deployments/ directory provides cloud-agnostic, enterprise-grade infrastructure for deploying the dashboard to production. Supports Helm, Kustomize, and Terraform across AWS, GCP, Azure, and OCI with blue-green, canary, and rolling release strategies.
graph TB
subgraph "Deployment Methods"
HELM["β Helm Chart<br/>Parameterized installs"]
KUST["π¦ Kustomize<br/>Overlay-based patching"]
TF["ποΈ Terraform<br/>Full cloud provisioning"]
end
subgraph "Cloud Providers"
AWS["βοΈ AWS<br/>ECS Fargate + ALB"]
GCP["βοΈ GCP<br/>Cloud Run + GCLB"]
AZ["βοΈ Azure<br/>ACI + App Gateway"]
OCI["βοΈ OCI<br/>OKE + LBaaS"]
end
subgraph "Release Strategies"
ROLL["Rolling Update"]
BG["Blue-Green"]
CAN["Canary + Analysis"]
end
subgraph "Observability"
PROM["π Prometheus + Grafana"]
CX["π‘ Coralogix<br/>Logs Β· Metrics Β· Traces Β· SLOs"]
end
HELM & KUST --> ROLL & BG & CAN
TF --> AWS & GCP & AZ & OCI
ROLL & BG & CAN --> PROM & CX
style HELM fill:#0f1689,color:#fff
style KUST fill:#326ce5,color:#fff
style TF fill:#7b42bc,color:#fff
style AWS fill:#ff9900,color:#fff
style GCP fill:#4285f4,color:#fff
style AZ fill:#0078d4,color:#fff
style OCI fill:#f80000,color:#fff
style PROM fill:#e6522c,color:#fff
style CX fill:#1a1a2e,color:#fff
# Helm (recommended for Kubernetes)
helm install agent-monitor deployments/helm/agent-monitor \
-f deployments/helm/agent-monitor/values-production.yaml \
-n agent-monitor --create-namespace
# Kustomize
kubectl apply -k deployments/kubernetes/overlays/production
# Terraform (full infra + app)
cd deployments/terraform/providers/aws
terraform init && terraform apply -var-file=../../environments/production/terraform.tfvars
# Script orchestrator
./deployments/scripts/deploy.sh --env production --method helm --strategy blue-green
The deployment stack includes CI/CD pipelines (GitHub Actions + GitLab CI), comprehensive monitoring (Prometheus, Grafana, Alertmanager with 13 alert rules, Coralogix full-stack observability with OpenTelemetry Collector for logs, metrics, traces, and SLO tracking), operational scripts (deploy, rollback, blue-green switch, backup/restore, teardown), and a full security posture (Restricted Pod Security Standard, TLS 1.3, network policies, Trivy scanning).
[!NOTE] π Full deployment guide: See DEPLOYMENT.md for step-by-step instructions, architecture diagrams, and operational workflows.
agent-dashboard/
|-- CLAUDE.md # Claude Code project memory and working agreements
|-- AGENTS.md # Codex project instructions
|-- package.json # Root scripts (dashboard + MCP helpers) + server dependencies
|-- .claude/
| +-- rules/ # Path-scoped Claude rules
| +-- skills/ # Claude reusable project skills
| +-- agents/ # Claude custom subagents
|-- .claude-plugin/
| +-- marketplace.json # Plugin marketplace manifest (10 plugins)
|-- plugins/
| |-- ccam-analytics/ # Analytics: session reports, cost breakdown, usage trends, productivity score
| | |-- .claude-plugin/plugin.json
| | |-- skills/ (4) # session-report, cost-breakdown, usage-trends, productivity-score
| | |-- agents/ # analytics-advisor (Sonnet model)
| | |-- hooks/hooks.json # Stop + SubagentStop event logging
| | +-- bin/ccam-stats # Terminal dashboard CLI
| |-- ccam-productivity/ # Productivity: standups, reports, sprints, workflow optimizer
| |-- ccam-devtools/ # DevTools: debug, diagnostics, export, health checks
| | +-- bin/ # ccam-doctor + ccam-export CLIs
| |-- ccam-insights/ # Insights: patterns, anomalies, optimization, comparison
| |-- ccam-cost-guard/ # Cost guardrails: budgets, spend forecast, cost alerts, model savings
| |-- ccam-sessions/ # Session forensics: search, timeline, transcript replay, cwd rollup, cleanup
| |-- ccam-workflows/ # Workflow orchestration: DAG map, delegation audit, concurrency, fleet runs
| |-- ccam-quality/ # Reliability & SLOs: error scan, API-error report, hook-failure audit, SLO check
| |-- ccam-config/ # Config & memory governance: config audit, memory review, skill/MCP/hook inventory
| +-- ccam-dashboard/ # Dashboard connector: status, quick stats, MCP integration
| +-- .mcp.json # MCP server configuration
|-- server/
| |-- index.js # Express app, HTTP server, static serving
| |-- db.js # SQLite schema, migrations, prepared statements
| |-- websocket.js # WebSocket server with heartbeat
| +-- routes/
| |-- hooks.js # Hook event processing (transactional)
| |-- sessions.js # Session CRUD
| |-- agents.js # Agent CRUD
| |-- events.js # Event listing
| |-- stats.js # Aggregate statistics
| |-- analytics.js # Token, tool, and trend analytics
| |-- workflows.js # Aggregate workflow data and per-session drill-in
| |-- pricing.js # Model pricing CRUD and cost calculation
| +-- settings.js # System info, data management, export, cleanup
| +-- lib/
| +-- transcript-cache.js # Stat-based JSONL transcript cache with chunked sync byte-stream reader (4 MiB chunks, line-by-line UTF-8 decode) so files larger than V8's max string length (~512 MiB) parse without aborting Node with "FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal". Extracts tokens, compactions, API errors, turn durations, thinking blocks, and usage extras (service_tier, speed, inference_geo)
| +-- compat-sqlite.js # node:sqlite compatibility wrapper (fallback for better-sqlite3)
|-- client/
| |-- package.json # Client dependencies
| |-- index.html # HTML entry point
| |-- vite.config.ts # Vite + proxy config
| |-- tailwind.config.js # Custom dark theme
| |-- tsconfig.json # Strict TypeScript
| +-- src/
| |-- main.tsx # React entry
| |-- App.tsx # Router + WebSocket provider
| |-- index.css # Tailwind + custom utilities
| |-- lib/
| | |-- types.ts # Shared TypeScript interfaces
| | |-- api.ts # Typed fetch client
| | |-- format.ts # Date/time formatting utilities
| | +-- eventBus.ts # Pub/sub for WebSocket distribution
| |-- hooks/
| | |-- useWebSocket.ts # Auto-reconnecting WebSocket hook
| | +-- useNotifications.ts # Browser notification triggers from WebSocket events
| |-- components/
| | |-- Layout.tsx # Shell with sidebar + outlet
| | |-- Sidebar.tsx # Navigation + connection indicator
| | |-- AgentCard.tsx # Agent info card with status
| | |-- StatCard.tsx # Metric card
| | |-- StatusBadge.tsx # Color-coded status pills
| | |-- EmptyState.tsx # Placeholder for empty lists
| | +-- workflows/ # D3.js workflow visualization components
| | |-- OrchestrationDAG.tsx # Horizontal DAG of agent spawning patterns
| | |-- ToolExecutionFlow.tsx # d3-sankey diagram of tool-to-tool transitions
| | |-- AgentCollaborationNetwork.tsx # Force-directed agent pipeline graph
| | |-- SubagentEffectiveness.tsx # Scorecard grid with SVG success rings
| | |-- WorkflowPatterns.tsx # Auto-detected orchestration sequences
| | |-- ModelDelegationFlow.tsx # Model routing through agent hierarchies
| | |-- ErrorPropagationMap.tsx # Error clustering by hierarchy depth
| | |-- ConcurrencyTimeline.tsx # Swim-lane parallel agent execution
| | |-- SessionComplexityScatter.tsx # D3 bubble chart (duration vs agents vs tokens)
| | |-- CompactionImpact.tsx # Token compression events and recovery
| | |-- WorkflowStats.tsx # Aggregate workflow statistics
| | +-- SessionDrillIn.tsx # Per-session agent tree, tool timeline, events
| +-- pages/
| |-- Dashboard.tsx # Overview page
| |-- KanbanBoard.tsx # Agents/Sessions toggle, status columns
| |-- Sessions.tsx # Server-paginated sessions table
| |-- SessionDetail.tsx # Single session deep dive
| |-- ActivityFeed.tsx # Real-time event stream
| |-- Analytics.tsx # Token usage, heatmap, trends
| |-- Workflows.tsx # D3.js workflow visualizations and session drill-in
| |-- Settings.tsx # Model pricing, notifications, hooks, export, cleanup
| +-- NotFound.tsx # 404 catch-all page
|-- scripts/
| |-- hook-handler.js # Lightweight stdin-to-HTTP forwarder
| |-- install-hooks.js # Auto-configures ~/.claude/settings.json
| |-- import-history.js # Imports sessions from ~/.claude/ with enhanced JSONL extraction (API errors, turn durations, entrypoint, permission modes, thinking blocks, usage extras, tool errors, subagent JSONL files). Re-import is fully incremental: a per-event-type high-water mark (`MAX(created_at) GROUP BY event_type` per session) is computed up-front and only JSONL entries with `ts > cutoff[type]` are inserted, so long-running sessions whose transcripts grow across multiple days continue to receive Stop / PostToolUse / TurnDuration / ToolError events on every re-run. Also rolls `sessions.ended_at` forward when the JSONL advances past the stored value and refreshes message-count metadata on every pass
| +-- seed.js # Sample data generator
|-- mcp/
| |-- package.json # MCP package scripts + dependencies
| |-- README.md # MCP setup, host config, tool catalog, safety model
| |-- src/
| | |-- index.ts # MCP runtime entrypoint (transport router)
| | |-- server.ts # MCP server assembly
| | |-- clients/ # Dashboard API client with retry/backoff
| | |-- config/ # Environment/CLI config parsing
| | |-- core/ # Logger, tool registry, result helpers
| | |-- policy/ # Mutation/destructive guards
| | |-- tools/ # Domain-specific tool modules (6 domains)
| | |-- transports/ # HTTP+SSE server, REPL, tool collector
| | |-- ui/ # ANSI banner, colors, formatter, tables
| | +-- types/ # Shared MCP type definitions
| +-- build/ # Built MCP runtime output
|-- desktop/
| |-- package.json # Electron + electron-builder dependencies and scripts
| |-- electron-builder.yml # DMG packaging config; signing/notarization hooks
| |-- tsconfig.json # Strict TypeScript (src/ -> out/)
| |-- README.md # Desktop app architecture reference (contributor docs)
| |-- assets/ # icon.svg + generated icon.icns + tray PNGs
| |-- src/
| | |-- main.ts # Electron main process entry β lifecycle, wiring
| | |-- server-host.ts # In-process Express boot, port discovery, adoption, DB close
| | |-- window.ts # BrowserWindow + persisted window geometry
| | |-- tray.ts # Menu-bar (tray) icon + context menu
| | |-- menu.ts # Native application menu (File βΈ Open Dashboard is macOS-only)
| | |-- login-item.ts # macOS Login Items auto-start toggle (SMAppService)
| | |-- logger.ts # File logger -> ~/Library/Logs/Claude Code Monitor/desktop.log
| | |-- constants.ts # App name, ports, timeouts, window size
| | +-- preload.ts # Intentionally empty (zero renderer privilege)
| |-- scripts/
| | |-- install.js # Preflights native deps for desktop:install; prints setup help + exits non-zero on failure
| | |-- preflight.js # Shared better-sqlite3 binary check + actionable per-OS setup help (incl. no-toolchain alternative)
| | |-- prebuild.js # Ensures root + client are built before tsc; fails fast with setup help if native binary missing
| | |-- build-icons.sh # SVG -> PNG/ICNS via qlmanage/sips/iconutil
| | +-- notarize.js # electron-builder afterSign hook (opt-in notarization)
| +-- tests/
| +-- smoke.test.mjs # Spawn Electron + probe /api/health
|-- deployments/
| |-- README.md # Deployment infrastructure reference
| |-- terraform/ # Cloud provisioning (AWS, GCP, Azure, OCI)
| | |-- modules/ # Reusable modules (networking, compute, db, lb, monitoring)
| | |-- providers/ # Cloud-specific implementations
| | +-- environments/ # Per-env tfvars (dev, staging, production)
| |-- kubernetes/ # Kustomize manifests
| | |-- base/ # 11 base resources (deployment, service, ingress, hpa, etc.)
| | |-- overlays/ # Environment overlays (dev, staging, production)
| | |-- components/ # Optional add-ons (mcp-sidecar, monitoring)
| | +-- strategies/ # Blue-green and canary deployment strategies
| |-- helm/agent-monitor/ # Helm chart with 12 templates and 4 value sets
| |-- scripts/ # Operational scripts (deploy, rollback, backup, teardown)
| |-- monitoring/ # Prometheus, Grafana, Alertmanager, Coralogix (OTel Collector)
| +-- ci/ # CI/CD pipelines (GitHub Actions, GitLab CI)
|-- .codex/
| |-- config.toml # Code
.agents/
skills/
mcp-maintainer/
agents/
openai.yaml
references/
operations-runbook.md
tool-domain-map.md
SKILL.md
release-guard/
agents/
openai.yaml
references/
release-checklist.md
SKILL.md
repo-onboarding/
agents/
openai.yaml
references/
module-map.md
verification-map.md
SKILL.md
.claude/
.claude-plugin/
marketplace.json
agents/
backend-reviewer.md
frontend-reviewer.md
mcp-reviewer.md
rules/
backend-node.md
docs-markdown.md
file-headers.md
frontend-react.md
mcp-typescript.md
wiki-i18n.md
settings.json
settings.local.json
skills/
debug-live-issue/
references/
investigation-template.md
SKILL.md
file-headers/
scripts/
check-headers-pr.sh
check-headers.sh
SKILL.md
mcp-operations/
references/
host-config.md
runbook.md
SKILL.md
repo-onboarding/
references/
command-map.md
module-map.md
SKILL.md
ship-feature/
references/
feature-checklist.md
SKILL.md
update-project-docs/
references/
doc-map.md
scripts/
doc-coverage.sh
SKILL.md
.codex/
agents/
implementer.toml
release-auditor.toml
reviewer.toml
config.toml
README.md
rules/
default.rules
skills/
mcp-maintainer/
agents/
openai.yaml
references/
operations-runbook.md
tool-domain-map.md
SKILL.md
release-guard/
agents/
openai.yaml
references/
release-checklist.md
SKILL.md
repo-onboarding/
agents/
openai.yaml
references/
module-map.md
verification-map.md
SKILL.md
.devcontainer/
devcontainer.json
Dockerfile
post-create.sh
README.md
.dockerignore
.env.example
.gitattributes
.githooks/
commit-msg
.github/
CODE_OF_CONDUCT.md
CODEOWNERS
CONTRIBUTING.md
FUNDING.yml
ISSUE_TEMPLATE/
bug_report.yml
feature_request.yml
question.yml
PULL_REQUEST_TEMPLATE.md
SECURITY.md
workflows/
ci.yml
cla.yml
file-headers.yml
triage.yml
.gitignore
.husky/
commit-msg
pre-commit
.idea/
.gitignore
Claude-Code-Agent-Monitor.iml
codeStyles/
codeStyleConfig.xml
Project.xml
copilot.data.migration.ask2agent.xml
modules.xml
prettier.xml
vcs.xml
.prettierignore
.prettierrc
.superpowers/
brainstorm/
1542-1774533799/
content/
workflows-page-wireframe.html
state/
server-stopped
server.pid
README.md
.vscode/
extensions.json
launch.json
settings.json
tasks.json
AGENTS.md
ARCHITECTURE.md
bin/
ccam.js
CITATION.cff
CLA.md
CLAUDE.md
client/
index.html
package-lock.json
package.json
postcss.config.js
public/
favicon.svg
manifest.json
og-image.svg
sw.js
README.md
src/
__tests__/
App.test.ts
App.tsx
components/
__tests__/
AgentCard.test.tsx
EmptyState.test.tsx
EventDetail.test.tsx
EventFilters.test.tsx
MultiSelect.test.tsx
Select.test.tsx
SessionCard.test.tsx
Sidebar.test.tsx
SplashScreen.test.tsx
StatCard.test.tsx
StatusBadge.test.tsx
AgentCard.tsx
AlertsNotifications.tsx
Checkbox.tsx
CodexConfigExplorer.tsx
ConfirmModal.tsx
conversation/
__tests__/
ConversationView.pagination.test.tsx
MarkdownContent.test.tsx
MessageList.sender.test.tsx
CodeBlock.tsx
ConversationView.tsx
MarkdownContent.tsx
MessageList.tsx
ToolCallBlock.tsx
toolStyle.ts
tuiSegments.ts
DateTimePicker.tsx
DocumentTitle.tsx
EmptyState.tsx
event-views/
primitives.tsx
tool-views.tsx
EventDetail.tsx
EventFilters.tsx
EventFiltersInfo.tsx
FieldHelp.tsx
ImportHistory.tsx
Layout.tsx
MultiSelect.tsx
RemoteSources.tsx
Select.tsx
SessionCard.tsx
SessionOverview.tsx
Sidebar.tsx
Skeleton.tsx
SplashScreen.tsx
StatCard.tsx
StatusBadge.tsx
Tabby/
__tests__/
brain.test.ts
intents.test.ts
quips.test.ts
Tabby.test.tsx
brain.ts
CatAvatar.tsx
intents.ts
prefs.ts
quips.ts
SpeechBubble.tsx
tabby.css
Tabby.tsx
TabbyPanel.tsx
useTabbyBrain.ts
useTabbyPosition.ts
Tip.tsx
UpdateNotifier.tsx
webhookGuides.ts
WebhookSettings.tsx
workflows/
__tests__/
friendlyPreview.test.ts
WorkflowRunsPanel.test.tsx
WorkflowRunsPanel.transcript.test.tsx
AgentCollaborationNetwork.tsx
CompactionImpact.tsx
ConcurrencyTimeline.tsx
ErrorPropagationMap.tsx
ModelDelegationFlow.tsx
OrchestrationDAG.tsx
SessionComplexityScatter.tsx
SessionDrillIn.tsx
SubagentEffectiveness.tsx
ToolExecutionFlow.tsx
WorkflowPatterns.tsx
WorkflowRunsPanel.tsx
WorkflowStats.tsx
hooks/
useDocumentTitle.ts
useNotifications.ts
useWebSocket.ts
i18n/
__tests__/
i18n.test.ts
index.ts
locales/
en/
activity.json
alerts.json
analytics.json
ccConfig.json
common.json
dashboard.json
errors.json
kanban.json
nav.json
run.json
sessions.json
settings.json
splash.json
updates.json
workflows.json
es/
activity.json
alerts.json
analytics.json
ccConfig.json
common.json
dashboard.json
errors.json
kanban.json
nav.json
run.json
sessions.json
settings.json
splash.json
updates.json
workflows.json
ko/
activity.json
alerts.json
analytics.json
ccConfig.json
common.json
dashboard.json
errors.json
kanban.json
nav.json
run.json
sessions.json
settings.json
splash.json
updates.json
workflows.json
vi/
activity.json
alerts.json
analytics.json
ccConfig.json
common.json
dashboard.json
errors.json
kanban.json
nav.json
run.json
sessions.json
settings.json
splash.json
updates.json
workflows.json
zh/
activity.json
alerts.json
analytics.json
ccConfig.json
common.json
dashboard.json
errors.json
kanban.json
nav.json
run.json
sessions.json
settings.json
splash.json
updates.json
workflows.json
index.css
lib/
__tests__/
agentOriginLabel.test.ts
dataScope.test.ts
eventBus.test.ts
format.test.ts
highlight.test.ts
remoteDataEvents.test.ts
api.ts
dataScope.ts
event-grouping.ts
event-summary.ts
eventBus.ts
format.ts
highlight.ts
push.ts
remoteDataEvents.ts
types.ts
main.tsx
pages/
__tests__/
__snapshots__/
screens.snapshot.test.tsx.snap
Run.defaultCwd.test.tsx
screens.snapshot.test.tsx
SessionDetail.nestedAgents.test.tsx
Workflows.codex.test.tsx
ActivityFeed.tsx
Analytics.tsx
CcConfig.tsx
Dashboard.tsx
KanbanBoard.tsx
NotFound.tsx
Run.tsx
SessionDetail.tsx
Sessions.tsx
Settings.tsx
Workflows.tsx
test-setup.ts
vite-env.d.ts
tailwind.config.js
tsconfig.json
vite.config.ts
... 774 moreΒ© 2026 Flowy Β· Free and open source
Built for Claude Code Β· Not affiliated with Anthropic
/api/sessions?status=&q=&limit=10&offset=β¦q=idnamecwdtotal/renameclaude -nCtrl+Rcustom-titleai-title| Session Detail | Per-session real-time overview panel with active-agent banner (current tool + task), six tile counters (events with events/min rate, tool calls, subagents, compactions, errors, ticking duration), top-tool usage bars, subagent type breakdown, stacked token-flow strip, and event-type pill cloud β all live-refreshed on hook events. Below it: agent hierarchy tree, full event timeline with multi-dimension filters (status, event type, tool, agent, text search, date range), Pre/Post grouping by tool_use_id, human-readable summary block, tool-aware input/response renderers (terminal for Bash, unified diff for Edit, line-numbered code for Read/Write, match list for Grep, key/value card for MCP tools), and a Conversation tab that renders transcripts β including messages typed mid-turn (queued while Claude was still working), placed where Claude actually received them, with harness notifications attributed to System β with markdown (headings, lists, blockquotes, tables, task lists), syntax-highlighted code blocks (js/ts, python, json, bash, html, css, sql, yaml, diff) with line numbers and copy-to-clipboard, and per-tool styled tool calls (Bash β terminal, Edit β side-by-side old/new, Write β file label, Read β path chip, Grep β pattern card). When the session is blocked on the human, a yellow waiting-for-input banner under the header names the awaiting_reason, its explanation, and how long the session has been waiting (pulsing dot + relative time); the header's Waiting badge carries the same reason as a nested chip |
| Activity Feed | Real-time streaming event log with pause/resume, multi-dimension filters (same toolbar as Session Detail plus a Session filter), server-driven "Load more" pagination, debounced filter-aware live refresh preserving the loaded page size, grouping toggle, origin prefix showing project βΊ session βΊ subagent, and a "Session β" button per row |
| Analytics | Token usage, tool frequency, activity heatmap (centered, day-of-week aligned starting Sunday, day-name tooltips), session trends, live/offline connection indicator. While the analytics payload loads, the chart region (not just the stat tiles) shows pulsing skeleton placeholders that mirror the chart layout, so the page never flashes empty/zero charts |
| Live Updates | WebSocket push -- no polling, instant UI updates |
| Auto-Discovery | Sessions and agents are created automatically from hook events |
| History Import | Provider-aware Import History brings in Claude Code transcripts from ~/.claude/ and Codex rollout JSONL from ~/.codex/sessions. Each tab has its own default path, instructions, folder scan, and upload flow; both reuse their live ingestion logic, preserve token/cost/tool accounting, and are idempotent. External Codex rollouts are snapshotted into dashboard storage so their conversation remains available after the archive or source folder is removed. |
| Subagent Hierarchy | Collapsible parent-child agent tree on Dashboard and Session Detail. Agents with subagents show expand/collapse chevrons; leaf agents show a dot indicator. Auto-expands when subagents are active |
| Background Agents | Correctly tracks backgrounded subagents without premature completion |
| Subagent Tool Attribution | Subagent-internal tool calls (Read, Bash, Edit, Grep, β¦) live only in per-subagent JSONL files β Claude Code emits no hooks for them. On every SubagentStop the dashboard fires a fire-and-forget scanAndImportSubagents pass that parses each subagents/agent-*.jsonl, pairs tool_use blocks with their matching tool_result by tool_use_id, and emits PreToolUse + PostToolUse events under the subagent's own agent_id. Idempotent (data LIKE '%"tool_use_id":"X"%' dedup) and merges into a live hook-created subagent row when one matches by type + start-time within 30 s, so no parallel <sid>-jsonl-* rows are created. The same path runs on npm run setup startup import for full historical backfill β sessions that pre-date the dashboard get full per-subagent tool timelines. Activity Feed and Session Detail render the parent chain as main βΊ coder βΊ explorer for nested subagents. That chain is reconstructed authoritatively by reconcileSubagentParents: a subagent row is first inserted flat under the main agent (a single hook event or JSONL file carries no spawner identity), then the spawner is recovered from each subagent transcript's Task tool result (toolUseResult.agentId, captured as spawnedChildren) so a subagent that spawns its own subagents nests under its true spawner instead of collapsing to one level under main. Idempotent and additive β it only repoints parent_agent_id, never inserts or deletes rows β and runs on the same SubagentStop scan, which returns a reparented count so the dashboard refetches even when re-parenting alone changed the tree shape |
| Cost Tracking | Per-model cost estimation with configurable pricing rules and per-session breakdowns. Supports time-limited introductory rates (intro_* + intro_until on a pricing rule): usage on/before the cutoff date is priced at the intro rate and usage after it at the standard rate, so a promo like Claude Sonnet 5's launch discount (through 2026-08-31) stays correct for historical and future usage β the cost endpoint prices each day's usage at the rate effective on that date. Introductory rates are fully editable in Settings β the Model Pricing editor exposes a promo cutoff date plus per-category intro prices (input / output / cache-read / cache-write 5m & 1h), so a future model launch promo needs no code change, just an edit. Subagent cards show each subagent's OWN cost (derived from that subagent's transcript token usage and priced at the current rates), not the session total β a main-agent card stands in for the whole session and shows the session total, while a subagent card shows only what that subagent spent, so a subagent card no longer misleadingly reads as if it cost the entire session. Compaction-aware token accounting preserves totals across context compressions. Transcript reads are cached with incremental byte-offset updates for efficient token extraction |
| Transcript Cache | Real-time extraction from JSONL transcripts: tokens, compactions, API errors (isApiErrorMessage entries stored as APIError events), turn durations (stored as TurnDuration events), thinking block counts, and usage extras (service_tier, speed, inference_geo). Per-entry growable arrays are tail-capped at TRANSCRIPT_CACHE_MAX_ARRAY_LEN (default 1000, configurable) β both during parse and at finalize β so even a session that runs for days cannot grow a single cache entry without bound. Each entry stores only {mtimeMs, size, bytesRead, result}, so there's no shadow copy of the same data at both the top level and inside result. Session metadata is enriched with these fields in real-time |
| Notifications | Full Web Push (VAPID) pipeline for reliable delivery. Arrive even when the tab is backgrounded or the browser is closed. Explicitly configured for macOS audio support. Configurable per-event toggles with subscription management |
| Alerts | Rules-based alerting engine β configured entirely in Settings β Alerts & Notifications, a tabbed Rules / Channels / Activity control center (no separate page). Define alert rules with four condition types β event pattern (match event type / tool name / summary text, optionally requiring N matching events inside a time window, e.g. "more than 5 errors in 2 minutes"), inactivity (active session with no events for N minutes), stuck agent (agent sitting in working/waiting with no activity for N minutes), and token threshold (session total tokens past a limit). Event-driven rules evaluate server-side on every hook ingest (after the ingest transaction β alerting can never slow down or fail hook delivery); time-based rules run on a 60 s sweep. Fired alerts are persisted to alert_events with per-rule + per-session cooldown dedup (default 300 s), broadcast as alert_triggered WebSocket messages, and surface in the Activity tab's live feed with acknowledge / acknowledge-all, an unacknowledged-only filter, and per-alert "View session" links. Rules support enable/disable toggling and cascade their history on delete. Fired alerts also fan out to universal webhook targets configured in the Channels tab β 14 first-class providers plus a generic endpoint: Slack, Discord, Microsoft Teams, Google Chat, Mattermost, Rocket.Chat (native chat payloads); Telegram (Bot API), PagerDuty (Events API v2), Opsgenie (Alert API + GenieKey auth), Splunk On-Call (VictorOps REST); and Zapier, Make, n8n, Pipedream, or any generic endpoint (clean JSON envelope with optional HMAC-SHA256 signing and custom headers). Each provider is described by a server-side registry that declares its payload formatter, how its URL is resolved (some derive it from credentials β e.g. Telegram from the bot token, Opsgenie from the region β others default it), and which credential fields the UI renders. Targets support optional per-rule scoping, a synchronous "Send test" probe, and a recorded delivery log. Delivery runs detached from the alert path with a request timeout and bounded retry/backoff, so it can never slow or block monitoring; target URLs, secrets, and credential fields are stored server-side and never returned by the API (masked/redacted in every response) |
| Update Notifier | Server periodically runs a non-blocking git fetch and compares the local checkout to origin/master/origin/main/origin/HEAD. When upstream is ahead, the UI surfaces a modal with the exact git pull && npm run setup command and a one-click Copy button; the Sidebar gets a persistent "Check for updates" button with live badge. The dashboard never pulls or restarts itself β the user runs the command in a terminal β so the mechanism cannot break dev sessions, pm2/systemd/Docker supervision, or leave orphaned processes |
| Settings | System info, hook status, model pricing management, notification preferences, data export and restore (the Import History panel's Restore backup mode re-imports a full export .json β idempotent and non-destructive, so you can consolidate several machines' history into one dashboard), session cleanup. The Model Pricing section separates Anthropic Claude Model Pricing from OpenAI GPT Model Pricing and exposes an info popover (the i icon next to the title) explaining rule lookup (first matching pattern wins), SQL-style % wildcard syntax, and manual price updates; the GPT editor displays published USD-per-million-token short, long, and Fast tiers, with unavailable tiers clearly unpriced. The Dashboard Data control immediately re-fetches sessions, agents, events, tokens, workflows, analytics, and costs for Claude Code, Codex, or both. Separate Claude Code and Codex home inputs are fully i18n-driven and save at runtime; a Codex save re-arms live rollout watching and scans its new tree. |
Workflow/loopworkflows/wf_<runId>.jsonrunning/compactDASHBOARD_STALE_MINUTESsessions.transcript_patheventsSELECT DISTINCT json_extract(events.data, '$.transcript_path')started_atended_atended_at < started_at/resumeDASHBOARD_STALE_MINUTES~/.claude/projectsstartSessionSyncfs.watchDASHBOARD_SESSION_SYNC_MSsession_createdsession_updated~/.claude/projects~/.codex/sessionssession_index.jsonlwsl.exetarsessions.sourceDASHBOARD_REMOTE_SYNC_MSremote_source.statusremote_data.updatedDASHBOARD_STALE_MINUTESccam remote-sourcesenzhvikoesclaude-opus-4-7-20260101claude-opus-4-7[1m]claude plugin marketplace addclaudeclaude --resume <id>claude --resume <id>?envelopes=1bypassPermissions--effort.claudeCLAUDE.md.mcp.json--include-partial-messagestext_deltathinking_deltarequestAnimationFrame_streamingcontentassistantflushSync/clear/model/config@-file referencesnode_modules.gitdistbuildstream_eventresult.usageusageRUN_MAX_CONCURRENTclaude/cc-configplugin.jsonmarketplace.json~/.claude/hooks//configCLAUDE.md*.md~/.claude/projects/<slug>/memory/MEMORY.mdMEMORY.md<kbd>mvsettings.jsoncc-watcherfs.watch~/.claude/~/.claude.jsoncc_config_changedsettings.jsoneventBusidlewatchinghappyworriedstuckthinkingsleepingdisconnectedN live Β· M errored Β· connection state/run?prompt=β¦aria-liveprefers-reduced-motiondisconnectedclient/src/components/Tabby//assets/manifest.json/Cache-Controlimmutable/assets/*no-cache, must-revalidateindex.htmlsw.jsmanifest.jsoncontrollerchangefavicon.svgsizes="any"apple-mobile-web-app-capableapple-touch-icondesktop/client/server/mcp/vscode-extension/.app.dmg.exerequire()server/index.jsBrowserWindowSMAppServiceHKCU\β¦\Runnpm run devdist/assets/<link>fonts/fonts.css@font-facefonts/wiki/mermaid.min.jsmermaid@10.9.6fonts.googleapis.comfonts.gstatic.comcdn.jsdelivr.netprefers-reduced-motionsubagents/agent-*.jsonltool_usetool_resulttool_use_idPreToolUsePostToolUseagent_idSessionEnd (CLI process exits), drops the waiting flag. If the session is in error, the error state is preserved; otherwise marks all agents + the session as completedSessionStart, any other active session with no activity for DASHBOARD_STALE_MINUTES (default 180 = 3 h, env-overridable) is automatically marked "abandoned" with its agents completed. This handles /resume inside a session, Ctrl+C, and other scenarios where a session is orphaned without a clean SessionEndUserPromptSubmit and PreToolUse can recover a session from error back to active β indicating the user actively retriedisCompactSummary entries in the JSONL transcript) and creates Compaction agents + events. Token baselines are preserved across compactions so no usage is lost. Transcript reads use a shared stat-based cache with incremental byte-offset reads β only new bytes appended since the last read are parsed, giving ~50x speedup for long sessionsisApiErrorMessage entries: quota limits, rate limits, invalid_request) and raw type: "error" responses from JSONL transcripts, stored as APIError events. Turn durations (system subtype turn_duration) are stored as TurnDuration events. Tool result errors (toolUseResult.is_error) are tracked as ToolError eventscwd for imported sessions without transcript_path in event data, and marks sessions/agents as error when API errors are found. This catches cases where the Claude CLI does not fire a hook after an API error (e.g., 401 auth failures where the CLI shows the error and waits)Esc fires no hook (a documented Claude Code limitation), so without intervention the main agent would stay stuck in working forever. The same 15 s watchdog recovers these two ways: (1) when the cancel leaves a [Request interrupted by user] marker in the transcript (Esc after some output), the transcript cache flags it via pendingInterrupt β derived purely from transcript ordering (latest interrupt vs latest real turn activity, same clock, so it works even for a sub-second cancel) β and the session moves to Waiting within ~15 s; (2) when Esc is pressed before any output, Claude Code writes no marker at all, so an idle-timeout fallback applies β if the main agent has been working with no tool in flight and neither a hook event nor the transcript has advanced for DASHBOARD_WORKING_IDLE_SECONDS (default 120), the turn is treated as dead and the session moves to Waiting. Both paths log an Interrupted event and land the session in the same Waiting state a normal Stop produces. Streaming output (transcript still growing) and in-flight tool calls (current_tool set) are exempt; a rare false flip self-heals on the next real hookSessionEnd hook, but if the dashboard isn't running at that moment the event is lost forever and the session would sit in Waiting until the stale sweep (3 h by default). The same 15 s watchdog closes the gap with a process-liveness probe: it lists running claude CLI processes (ps + lsof on macOS, /proc on Linux) and completes any active session whose cwd has no live claude process β landing it in the same completed state a real SessionEnd produces, with a synthetic SessionEnd event on the timeline. Guards: on watchdog ticks the session's transcript must not have been written for at least DASHBOARD_LIVENESS_IDLE_SECONDS (default 60; the last hook write is the fallback clock when no transcript exists on disk) β the boot passes skip this gate entirely, so a session quit even one second before launch clears immediately β and the probe reports "no answer" (changing nothing) on Windows, inside containers (host processes are invisible there), when ps/lsof fail, or when explicitly disabled via DASHBOARD_LIVENESS_PROBE=0. In a mixed deployment the reap also auto-skips any session whose cwd is not POSIX-absolute β a session forwarded from another machine via household hooks reports the origin's own path (e.g. a Windows D:\Git\ai-deck) that a local ps/lsof//proc scan can never match, so remote sessions are protected without disabling the probe for genuinely-local ones. Remote Data Source sessions (sessions.source β local) are always skipped too β their cwd is legitimately POSIX-absolute on another machine, so the local process probe says nothing about them; their lifecycle is owned entirely by the remote-sync reconciliation described above. A false completion self-heals: the next hook event reactivates the session. Besides the 15 s watchdog cadence, the reap runs immediately at startup (clearing dead sessions already in the DB from a previous run before they ever render) and again ~5 s later (covering sessions the startup sync just imported), so a session that died while the dashboard was down never shows as Waiting/compact fires no hook, /resume within seconds of session creation). Cadence is derived from DASHBOARD_STALE_MINUTES (ΒΌ of the threshold, clamped to 60 s β 5 min). The sweep reads transcript_path directly off each active session row (a small index lookup) instead of scanning the events table for it; the column is populated by the hook handler the first time it sees a transcript path and is one-time-backfilled from existing events by the db.js migration, with a partial index idx_sessions_active_tp covering exactly the rows the sweep reads. The sweep shares the transcript cache with the hook handler, avoiding duplicate I/O. Abandoned session cleanup also evicts the transcript cache entry to bound memory. Remote Data Source sessions (source β local) remain mirror-owned only while their matching Claude Code or Codex provider is healthy; if that provider is unavailable, errors, or is stranded in syncing, the periodic and startup cleanup use the normal stale rule so an old Waiting card cannot persist foreverstartSessionSync) keeps ~/.claude/projects discoverable beyond the one-time, marker-gated startup backfill: a project added later whose sessions never flow through hooks would otherwise stay invisible until a manual rescan. An immediate startup sweep, a debounced fs.watch (recursive on macOS/Windows; root + immediate children on Linux), and a DASHBOARD_SESSION_SYNC_MS poll (default 30 s; 0 disables the poll, watcher stays) share one mtime cache and a coalesced sweep that re-parses only files whose mtime advanced β and skips an already-imported, unchanged session without re-parsing, so restart cost stays O(new/changed files). Each newly discovered/grown session broadcasts session_created/session_updated plus its main agent, the same frames hooks emitcwdDASHBOARD_LIVENESS_IDLE_SECONDS | 60 | Idle gate for the watchdog-tick liveness reap: a session is only completed when its transcript hasn't been written for at least this long (the last hook write is the fallback clock when no transcript exists on disk), so a mid-turn or just-resumed session never flickers out on a transient probe miss. The startup passes ignore this gate β at boot the probe alone decides, so sessions quit moments before launch clear immediately |
DASHBOARD_SESSION_SYNC_MS | 30000 | Poll interval (ms) for the continuous ~/.claude/projects background sync that surfaces projects added after startup whose sessions never flow through hooks. The fs.watch watcher fires near-instantly regardless; this poll is the safety net (watchers can miss events / not fire on network filesystems). Set to 0 to disable the poll while leaving the watcher running |
DASHBOARD_CODEX_HOME | CODEX_HOME or ~/.codex | Optional local Codex state directory. Rollouts are read only from its sessions/ tree; saving a new location in Settings persists this dashboard-only override, re-arms live watching, and immediately scans the new tree. |
DASHBOARD_CODEX_SYNC_MS | 4000 | Safety-net poll interval (ms) for append-only Codex rollouts. Codex hooks trigger the same incremental ingest immediately; set to 0 to disable only the poll while retaining the filesystem watcher when available. |
DASHBOARD_REMOTE_SYNC_MS | 15000 (15 s) | Poll interval (ms) for the Remote Data Sources background sync that independently pulls each enabled remote's ~/.claude/projects and ~/.codex/sessions (plus Codex's lightweight session_index.jsonl title index) over SSH, then re-imports each through its local importer. New/enabled sources also sync immediately. Set to 0 to disable the poller (manual / on-demand syncs still work) |
DASHBOARD_REMOTE_ACTIVE_WINDOW_MS | 600000 (10 min) | Freshness window for a Remote Data Source session's live status. On each sync, a remote Claude Code or Codex session whose matching mirrored transcript has a last JSONL event within this window is treated as still running (active); once the mirror stops advancing for longer than this, the session is reconciled to completed. Remote sessions receive no live hooks, so provider-aware mirror reconciliation replaces local liveness; failed, unavailable, or stuck provider mirrors fall back to the normal stale sweep. Raise it for slow links or very long idle turns |
DASHBOARD_REMOTE_SYNC_TIMEOUT_MS | 600000 (10 min) | Per-source timeout (ms) for a single remote sync (scp pull + import) before it is aborted |
DASHBOARD_REMOTE_TEST_TIMEOUT_MS | 15000 (15 s) | Timeout (ms) for the Test SSH probe (POST /api/remote-sources/:id/test) that verifies a remote source is reachable |
DASHBOARD_HOST | 127.0.0.1 | Interface the server binds to. Loopback by default (not network-reachable). Set to 0.0.0.0 to expose on a LAN (logs a startup warning) |
DASHBOARD_TOKEN | (unset) | When set, every /api/* request and the WebSocket must present the token (Authorization: Bearer <token>, x-dashboard-token header, or ?token=). Off by default β loopback bind is the trust boundary |
DASHBOARD_ALLOWED_HOSTS | (loopback) | Comma-separated extra Host values allowed on HTTP + WebSocket upgrades (DNS-rebinding guard). Add your LAN hostnames here when binding beyond loopback |
npm run mcp:start:repl | Start MCP server (interactive REPL with tab completion) |
npm run mcp:dev | Run MCP server in dev mode (tsx, stdio) |
npm run mcp:dev:http | Run MCP server in dev mode (tsx, HTTP + SSE) |
npm run mcp:dev:repl | Run MCP server in dev mode (tsx, interactive REPL) |
npm run mcp:typecheck | Type-check MCP source without emitting build output |
npm run mcp:docker:build | Build MCP container image with Docker (agent-dashboard-mcp:local) |
npm run mcp:podman:build | Build MCP container image with Podman (localhost/agent-dashboard-mcp:local) |
npm run desktop:install | Install Electron + electron-builder into the desktop/ workspace (rebuilds better-sqlite3 for Electron's ABI); preflights the native better-sqlite3 build and prints actionable setup help (incl. a no-toolchain alternative) on failure |
npm run desktop:dev | Build and launch the Electron desktop app for local iteration |
npm run desktop:build | Compile the desktop TypeScript sources into desktop/out/ |
npm run desktop:test | Run the desktop smoke test (spawn Electron, probe /api/health) |
npm run desktop:dmg | Build both macOS DMGs (arm64 + x64) β correct for release, slower (packages each arch) |
npm run desktop:dmg:arm64 | Build an Apple-Silicon-only DMG β fast, recommended for your own Mac |
npm run desktop:dmg:x64 | Build an Intel-only DMG β fast |
npm run desktop:dmg:universal | Build one merged universal DMG (arm64 + x86_64) β optional, slowest, not what the release ships |
npm run desktop:win | Build a Windows NSIS installer .exe (x64) β run on Windows |
npm run desktop:win:portable | Build a Windows portable (no-install) .exe (x64) β run on Windows |
npm run monitoring:install | Run npm install in monitoring/ β downloads Prometheus + Grafana via postinstall |
npm run monitoring:setup | Alias for monitoring:install |
npm run monitoring:up | Start Prometheus (:9090) + Grafana (:3000) in the background (no Docker) |
npm run monitoring:down | Stop the npm-managed monitoring stack |
npm run monitoring:start | Foreground monitoring stack (Ctrl+C stops both) |
npm run monitoring:docker:up | Start Prometheus + Grafana via Docker Compose |
npm run monitoring:docker:down | Tear down the Docker monitoring stack |
npm run monitoring:verify | Health-check dashboard, Prometheus, Grafana, and scrape target |
npm run docker:up | Start the dashboard in Docker (docker compose up -d --build) |
npm run docker:down | Stop the dashboard container |
npm run docker:full:up | Dashboard + Prometheus + Grafana β all in Docker |
npm run docker:full:down | Tear down the full Docker stack |
PUT | /api/cc-config/keybindings | Overwrite keybindings.json from { groups: [{ context, bindings: [{ key, action }] }] }. Backs up first, preserves top-level metadata ($schema/$docs), rejects duplicate contexts/keys. Safe to edit (unlike settings.json, the CLI never rewrites it mid-session) |
GET | /api/cc-config/statusline | settings.json.statusLine config + the actual statusline.py / statusline-command.sh content if present |
GET | /api/cc-config/settings | User / project / project-local settings JSON, with secret-like keys (matching /token|secret|password|api[_-]?key|auth/i) replaced by "<redacted>" |
GET | /api/cc-config/memory | CLAUDE.md files at user + project scope, plus per-project file-based memory: scope:"auto-memory" items (each carrying project, name, isIndex, and parsed frontmatter) for every *.md under ~/.claude/projects/<slug>/memory/. Mutate via PUT/DELETE /api/cc-config/file with { scope: "auto-memory", type: "auto-memory", project, name } (backups land in <memory-dir>/.cc-config-backups/auto-memory/) |
GET | /api/cc-config/file?path=β¦ | Body of a single file (path-contained to CLAUDE_HOME / project .claude / project CLAUDE.md) |
GET | /api/cc-config/backups | Listing of all timestamped backups, optionally filtered ?scope=&type= |
PUT | /api/cc-config/file | Create or overwrite a text-file artifact. Body: { scope, type, name?, content }. Auto-backs-up if file exists. Atomic temp + rename. 256 KB content cap, strict name regex |
DELETE | /api/cc-config/file | Backup-then-delete a text-file artifact. Skill dirs are backed up whole (preserving bundled assets) before recursive removal |
POST/api/run/:id/message |
Send a follow-up turn to a running conversation (conversation mode only). Body: { text } |
DELETE | /api/run/:id | Stop a run. SIGTERM, escalating to SIGKILL after 5 s |
permissionwaiting for inputneeds your approvalCompactionSessionEnd | Claude Code CLI process exits | Drops the waiting flag. If the session is already in error, the error state is preserved; otherwise marks all agents and the session as completed |
Compaction | /compact detected in JSONL | Creates a compaction subagent (type compaction) and Compaction event. Detected via isCompactSummary entries in the transcript JSONL. Also detected by periodic scanner for active sessions |
APIError | API error in JSONL transcript | Extracted from isApiErrorMessage entries (quota, rate limit, invalid_request) and raw type: "error" responses. Now immediately marks the session and agent as error β previously recorded as events without changing status. Stored as event with error details |
TurnDuration | Turn timing in JSONL transcript | Extracted from system subtype turn_duration messages with durationMs. Stored as event for turn-level timing analysis |
ToolError | Tool result error in JSONL | Extracted from toolUseResult.is_error entries. Tracks tool-level failures for error propagation analysis |
Interrupted | Turn cancelled by the user (Esc) | Synthesized by the watchdog β Esc fires no hook, so a stuck working session is detected from the transcript's [Request interrupted by user] marker or, when Esc preceded any output, from the idle-working timeout (DASHBOARD_WORKING_IDLE_SECONDS). The session moves to Waiting (same as a normal Stop) |
claude CLI on PATH β on macOS the app recovers your login-shell PATH at startup, so the Run Claude feature works even though a Finder/Dock-launched app would otherwise only inherit launchd's minimal PATH. (On Windows the inherited user PATH already includes it.)~/Library/Logs/Claude Code Monitor/desktop.log (macOS) or %APPDATA%\Claude Code Monitor\logs\desktop.log (Windows); reach it from the tray menu's Show Logs.slo-checkregression-alert| ccam-config | claude plugin install ccam-config@hoangsonww-claude-code-agent-monitor | config-audit, memory-review, skill-inventory, mcp-audit, hook-inventory |
| ccam-dashboard | claude plugin install ccam-dashboard@hoangsonww-claude-code-agent-monitor | dashboard-status, quick-stats + MCP server |