Claude Code Agent Monitor
A professional monitoring platform for Claude Code agent activity. Captures sessions, agents, and tool events via native hooks, persists them in SQLite, and streams updates to a React UI over WebSocket — with no external services required.
System Overview
Claude Code Agent Monitor integrates with Claude Code through its native hook system. When Claude Code performs any action — tool use, session start, subagent orchestration, session end — it fires a hook that calls a small Node.js script bundled with this project. That script forwards the event over HTTP to the dashboard server, which stores it in SQLite and broadcasts it to the browser over WebSocket.
End-to-end data pipeline from Claude Code to the browser
The server binds to 0.0.0.0 but everything runs on your machine. No
data leaves your system. No API keys. No external services.
What's Included
Every feature is driven by real hook events — nothing is hardcoded or simulated in production mode.
Screenshots
GitHub Star History
This chart tracks how interest in Claude Code Agent Monitor has grown over time. The curve keeps climbing as more developers discover the project, share it, and use it in real workflows. Each new star is a small vote of confidence from the community.
Hook Events Captured
| Hook Type | Trigger | Dashboard Action |
|---|---|---|
SessionStart |
Claude Code session begins | Creates session and main agent. Reactivates resumed sessions. Abandons orphaned sessions with no activity for 5+ minutes. |
PreToolUse |
Agent begins using a tool |
Agent →
Working, current_tool set. If tool is Agent, subagent record
created.
|
PostToolUse |
Tool execution completes |
current_tool cleared. Agent stays
Working (no status change).
|
Stop |
Session/turn ended | Main agent → Idle if subagents still running, else Completed. Running subagents are preserved. Token usage recorded. |
SubagentStop |
Background agent finished | Matched subagent → Completed. Auto-completes session when the last subagent finishes. |
Notification |
Agent sends notification | Event logged to activity feed. Compaction-related notifications tagged as Compaction events. Triggers persistent browser notification if enabled. |
Compaction |
/compact detected in JSONL |
Creates a compaction subagent →
Completed. Detected via isCompactSummary entries in the transcript. Token baselines preserve pre-compaction totals. Periodic scanner (every 2 min) catches compactions when no hooks fire.
|
APIError |
API error detected in transcript | Extracted from JSONL during history import or real-time transcript scanning. Captures quota limits, rate limits, and other API failures. |
TurnDuration |
Per-turn timing recorded | Extracted from JSONL turn boundaries. Records the duration of each assistant turn for latency analysis. |
SessionEnd |
Claude Code CLI process exits | Marks all agents and the session as Completed. |
Quick Start
Clone
Clone the repository to your machine
Install
Run npm run setup to install all dependencies
Start
Run npm run dev — server + client launch automatically
Use Claude
Start a new Claude Code session — events appear in real-time
# 1. Clone
git clone https://github.com/hoangsonww/Claude-Code-Agent-Monitor.git
cd Claude-Code-Agent-Monitor
# 2. Install all dependencies (server + client)
npm run setup
# 3. Start in development mode
npm run dev
# → Express server on http://localhost:4820
# → Vite dev server on http://localhost:5173
# 4. (Optional) Build and run the local MCP server
npm run mcp:install
npm run mcp:build
npm run mcp:start # stdio (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
# 5. Open the dashboard
# http://localhost:5173 (dev)
# http://localhost:4820 (prod after npm run build && npm start)
Alternative: Docker / Podman
A multi-stage Dockerfile and docker-compose.yml are included.
You can run the monitor with either Docker or Podman and keep the SQLite database in a
named volume.
# Docker Compose
docker compose up -d --build
# Podman Compose
CLAUDE_HOME="$HOME/.claude" podman compose up -d --build
# Plain Docker / Podman
docker build -t agent-monitor .
docker run -d --name agent-monitor \
-p 4820:4820 \
-v "$HOME/.claude:/root/.claude:ro" \
-v agent-monitor-data:/app/data \
agent-monitor
When you run the server directly on the host with npm run dev or
npm start, it automatically writes Claude Code hook entries to
~/.claude/settings.json. If you run the dashboard in Docker or Podman,
install hooks from the host with npm run install-hooks after the
container is up, then restart Claude Code.
Optional: Enable MCP and Agent Extensions
This repository also ships a local MCP server under mcp/ and extension
scaffolding for both Claude Code and Codex. These are optional for the dashboard UI, but
recommended for complete local-agent workflows. The MCP server supports stdio (for host
integration), HTTP+SSE (for remote clients), and an interactive REPL (for operator
debugging).
# MCP lifecycle
npm run mcp:install
npm run mcp:build
npm run mcp:start # stdio (default — for MCP hosts)
npm run mcp:start:http # HTTP + SSE server on port 8819
npm run mcp:start:repl # interactive CLI with tab completion
Verification
After starting a Claude Code session, you should see:
| Page | Expected |
|---|---|
| Sessions | Your session listed with status Active |
| Kanban Board | A Main Agent card in the Connected column |
| Activity Feed | Events streaming in as Claude Code uses tools |
| Dashboard | Stats updating in real-time |
Hooks only fire to a running server. If Claude Code was already running when you started the dashboard, restart the Claude Code session.
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
DASHBOARD_PORT |
4820 |
Port the Express server listens on |
CLAUDE_DASHBOARD_PORT |
4820 |
Port used by the hook handler to reach the server (for custom port setups) |
MCP_DASHBOARD_BASE_URL |
http://127.0.0.1:4820 |
Base URL used by the local MCP server when calling dashboard APIs |
MCP_TRANSPORT |
stdio |
MCP transport mode: stdio, http, repl |
MCP_HTTP_PORT |
8819 |
Port for the MCP HTTP+SSE server (only when MCP_TRANSPORT=http) |
MCP_HTTP_HOST |
127.0.0.1 |
Bind address for the MCP HTTP server |
DASHBOARD_DB_PATH |
data/dashboard.db |
Path to the SQLite database file |
NODE_ENV |
development |
Set to production to serve built client from
client/dist/
|
Hook Configuration
The server writes the following to ~/.claude/settings.json on every
startup:
{
"hooks": {
"SessionStart": [
{ "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" SessionStart" }] }
],
"PreToolUse": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" PreToolUse" }] }
],
"PostToolUse": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" PostToolUse" }] }
],
"Stop": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" Stop" }] }],
"SubagentStop": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" SubagentStop" }] }],
"Notification": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" Notification" }] }],
"SessionEnd": [
{ "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" SessionEnd" }] }
]
}
}
Existing hooks are preserved. The installer only adds or updates entries containing
hook-handler.js.
Scripts Reference
| Script | Command | Description |
|---|---|---|
setup |
npm run setup |
Install all dependencies (server + client) |
dev |
npm run dev |
Start server + client in development mode with hot reload |
dev:server |
npm run dev:server |
Start only the Express server with --watch |
dev:client |
npm run dev:client |
Start only the Vite dev server |
build |
npm run build |
TypeScript check + Vite production build to client/dist/ |
start |
npm start |
Start Express in production mode serving built client |
install-hooks |
npm run install-hooks |
Manually write Claude Code hooks to ~/.claude/settings.json |
seed |
npm run seed |
Insert demo sessions, agents, and events (8 sessions / 23 agents / 106 events) |
import-history |
npm run import-history |
Import historical Claude Code sessions from ~/.claude with deep JSONL extraction (API errors, turn durations, thinking blocks, subagent data) |
clear-data |
npm run clear-data |
Delete all data from the database (keeps schema) |
test |
npm test |
Run all server and client tests |
test:server |
npm run test:server |
Server integration tests only (Node built-in test runner) |
test:client |
npm run test:client |
Client unit tests only (Vitest + Testing Library) |
mcp:install |
npm run mcp:install |
Install dependencies for the local MCP package under mcp/ |
mcp:typecheck |
npm run mcp:typecheck |
Type-check MCP source without emitting build output |
mcp:build |
npm run mcp:build |
Compile MCP server into mcp/build/ |
mcp:start |
npm run mcp:start |
Start MCP server (stdio transport — for MCP hosts) |
mcp:start:http |
npm run mcp:start:http |
Start MCP HTTP+SSE server on port 8819 (Streamable HTTP + legacy SSE) |
mcp:start:repl |
npm run mcp:start:repl |
Start interactive MCP REPL with tab completion and colored output |
mcp:dev |
npm run mcp:dev |
Run MCP server in dev mode with tsx (stdio) |
mcp:dev:http |
npm run mcp:dev:http |
Run MCP HTTP server in dev mode with tsx |
mcp:dev:repl |
npm run mcp:dev:repl |
Run MCP REPL in dev mode with tsx |
mcp:docker:build |
npm run mcp:docker:build |
Build MCP container image with Docker (agent-dashboard-mcp:local) |
mcp:podman:build |
npm run mcp:podman:build |
Build MCP container image with Podman
(localhost/agent-dashboard-mcp:local)
|
test:mcp |
npm run test:mcp |
Run MCP server unit tests |
format |
npm run format |
Format all files with Prettier |
format:check |
npm run format:check |
Check formatting without writing |
System Architecture
Core dashboard telemetry is composed of three processes (Claude hook source, dashboard server, browser UI). When the local MCP sidecar is enabled, it integrates with the same dashboard API via stdio, HTTP+SSE, or interactive REPL transport.
Full system architecture — Claude Code process → Hook Layer → Server → Browser
Agent State Machine
Agent status transitions driven by hook events
Session State Machine
Session status lifecycle
Data Flow
Event Ingestion Pipeline
Parses JSON and adds hook_type HH->>API: POST {"hook_type":"PreToolUse","data":{...}} API->>TX: BEGIN TRANSACTION TX->>TX: ensureSession(session_id) Note over TX: Creates session + main agent
on first contact TX->>TX: process by hook_type Note over TX: PreToolUse -> agent working
PostToolUse -> clear current_tool
Stop -> idle if subagents, else completed
SubagentStop -> complete matched subagent TX->>TX: insertEvent(...) TX->>TX: COMMIT API->>WS: broadcast("agent_updated", agent) API->>WS: broadcast("new_event", event) WS->>UI: {"type":"agent_updated","data":{...}} UI->>UI: eventBus.publish -> page re-renders
Complete event ingestion from hook fire to browser re-render
Client Data Loading Pattern
Initial load + WebSocket subscription lifecycle
Server Architecture
Express app + HTTP server"] DB["server/db.js
SQLite + prepared statements
better-sqlite3 / node:sqlite fallback"] WS["server/websocket.js
WS server + heartbeat"] HOOKS["routes/hooks.js
Hook event processing"] SESSIONS["routes/sessions.js
Session CRUD"] AGENTS["routes/agents.js
Agent CRUD"] EVENTS["routes/events.js
Event listing"] STATS["routes/stats.js
Aggregate queries"] ANALYTICS["routes/analytics.js
Analytics metrics"] PRICING["routes/pricing.js
Pricing CRUD + cost calc"] SETTINGS_R["routes/settings.js
System info + data mgmt"] WORKFLOWS_R["routes/workflows.js
Workflow visualizations"] INDEX --> DB INDEX --> WS INDEX --> HOOKS INDEX --> SESSIONS INDEX --> AGENTS INDEX --> EVENTS INDEX --> STATS INDEX --> ANALYTICS INDEX --> PRICING INDEX --> SETTINGS_R INDEX --> WORKFLOWS_R HOOKS --> DB HOOKS --> WS SESSIONS --> DB SESSIONS --> WS AGENTS --> DB AGENTS --> WS EVENTS --> DB STATS --> DB ANALYTICS --> DB PRICING --> DB SETTINGS_R --> DB WORKFLOWS_R --> DB
Server module dependency graph
Server Modules
| Module | Responsibility |
|---|---|
server/index.js |
Express app setup, middleware (CORS, JSON 1MB limit), route mounting, static serving in production, HTTP server, auto-hook installation on startup |
server/db.js |
SQLite connection, WAL/FK pragmas, schema migrations (CREATE TABLE IF NOT EXISTS), all prepared statements as a reusable stmts object. Tries
better-sqlite3 first, falls back to built-in
node:sqlite via compat-sqlite.js
|
server/compat-sqlite.js |
Compatibility wrapper giving Node.js built-in node:sqlite
(DatabaseSync) the same API as better-sqlite3 —
pragma, transaction, prepare. Used as automatic fallback on Node 22+
|
server/websocket.js |
WebSocket server on /ws path, 30s ping/pong heartbeat, typed
broadcast(type, data) function
|
routes/hooks.js |
Core event processing inside SQLite transactions. Auto-creates sessions/agents. Switch-case dispatch by hook type. Extracts token usage from Stop events. |
routes/sessions.js |
CRUD with pagination. GET includes agent count via LEFT JOIN. POST is idempotent on session ID. |
routes/agents.js |
CRUD with status/session_id filtering. PATCH broadcasts
agent_updated.
|
routes/events.js |
Read-only event listing with session_id filter and pagination. |
routes/stats.js |
Single aggregate query — total/active counts, status distributions, WS connection count. |
routes/analytics.js |
Extended analytics — token totals, tool usage counts, daily event/session trends, agent type distribution, event type breakdown, average events per session. |
routes/pricing.js |
Model pricing CRUD (list, upsert, delete). Per-session and global cost calculation with pattern-based model matching and specificity sorting. |
routes/settings.js |
System info (DB size, row counts, hook status, server uptime). Data export as JSON. Session cleanup (abandon stale active sessions, purge old completed sessions). Clear all data. Reset pricing to defaults. Reinstall hooks. |
routes/workflows.js |
Aggregate workflow visualization data (agent orchestration, tool transitions,
collaboration networks, workflow patterns, model delegation, error propagation,
concurrency, session complexity, compaction impact). Accepts
?status=active|completed filter. Per-session drill-in with
agent tree, tool timeline, and events.
|
Client Architecture
React component tree
Client Routes
Key Client Modules
| Module | Purpose |
|---|---|
lib/api.ts |
Typed fetch wrapper — one method per REST endpoint. All return typed promises. |
lib/types.ts |
TypeScript interfaces: Session, Agent,
DashboardEvent, Stats, Analytics,
WSMessage, plus all workflow-related types
(WorkflowData, SessionDrillIn, etc). Status config
maps.
|
lib/eventBus.ts |
Set-based pub/sub. subscribe(fn) returns an unsubscribe function
for clean useEffect teardown.
|
lib/format.ts |
Date/time formatting helpers — relative time, duration, ISO display. |
hooks/useWebSocket.ts |
Auto-reconnecting WebSocket React hook. 2-second reconnect interval. Publishes messages to eventBus. |
State Management
The client deliberately avoids Redux / Zustand / Context. Each page owns its data and lifecycle. WebSocket events trigger a reload or append — no complex state merging.
Each page pulls initial data from REST then subscribes to eventBus for live updates
There is no cross-page shared state. Each page fetches and owns exactly the data it displays. This simplifies debugging and avoids stale-closure hazards that are common with global stores in long-running WebSocket apps.
Database Design
Entity Relationship Diagram — SQLite schema
Indexes
| Index | Table | Column(s) | Purpose |
|---|---|---|---|
idx_agents_session |
agents | session_id |
Fast agent lookup by session |
idx_agents_status |
agents | status |
Kanban column queries |
idx_events_session |
events | session_id |
Session detail event list |
idx_events_type |
events | event_type |
Filter events by type |
idx_events_created |
events | created_at DESC |
Activity feed ordering |
idx_sessions_status |
sessions | status |
Status filter on sessions page |
idx_sessions_started |
sessions | started_at DESC |
Default sort order |
SQLite Configuration
| Pragma | Value | Rationale |
|---|---|---|
journal_mode |
WAL |
Concurrent reads during writes. Far better for read-heavy dashboards. |
foreign_keys |
ON |
Referential integrity — prevents orphaned agents/events. |
busy_timeout |
5000 |
Wait up to 5s for write lock instead of failing immediately under load. |
API Reference
All endpoints return JSON. Errors follow
{ "error": { "code", "message" } }.
Health
{ "status": "ok", "timestamp": "..." }
Sessions
status, limit,
offset
id)
Agents
status, session_id,
limit, offset
Events, Stats, Analytics
session_id, limit,
offset
Hooks Ingestion
Pricing
Settings
Workflows
?status=active|completed query param
to filter by workflow status
{
"hook_type": "PreToolUse",
"data": {
"session_id": "abc-123",
"tool_name": "Bash",
"tool_input": { "command": "ls -la" }
}
}
WebSocket Protocol
| Property | Value |
|---|---|
| Path | ws://localhost:4820/ws |
| Protocol | Standard WebSocket (RFC 6455) |
| Heartbeat | Server pings every 30s — clients that don't pong are terminated |
| Reconnect | Client retries every 2 seconds on disconnect |
Message Envelope
// All messages use this shape
{
type: "session_created" | "session_updated" |
"agent_created" | "agent_updated" | "new_event";
data: Session | Agent | DashboardEvent;
timestamp: string; // ISO 8601
}
Client WebSocket auto-reconnect state machine
Hook Integration
Hook Handler Design
scripts/hook-handler.js is a minimal, fail-safe forwarder. It always exits
0 so it can never block Claude Code regardless of server state.
hook-handler.js flow — always exits 0, never blocks Claude Code
Hook Installation Flow
Hook installation is idempotent — safe to run multiple times
MCP & Agent Extensions
In addition to dashboard telemetry, this project includes a production-grade local MCP server and complete extension scaffolding for both Claude Code and Codex. This gives agents a richer local tool surface while keeping all execution local-first. The MCP server supports three transport modes: stdio for host integration, HTTP+SSE for remote clients, and an interactive REPL for operator debugging.
Local extension architecture: host instructions + skills + multi-transport MCP sidecar
Local MCP Server Runtime
The mcp/ package exposes dashboard-oriented tools for AI agents across three
transport modes. Mutation and destructive operations are policy-gated by environment
variables and disabled by default. HTTP mode serves both Streamable HTTP (protocol
2025-11-25) and legacy SSE (protocol 2024-11-05). REPL mode provides tab-completed
interactive tool invocation with colored output and JSON syntax highlighting.
| Component | Location | Notes |
|---|---|---|
| MCP source | mcp/src/ |
TypeScript server, tools, policy guards, transport layer, CLI UI |
| MCP build output | mcp/build/ |
Compiled JavaScript runtime for all transport modes |
| MCP docs | mcp/README.md |
Tool catalog, architecture diagrams, host integration examples, REPL guide |
| Transport layer | mcp/src/transports/ |
HTTP+SSE server, interactive REPL, tool handler collector |
| CLI UI | mcp/src/ui/ |
ANSI banner, colors, formatter with tables, boxes, JSON highlighting |
| Runtime commands | npm run mcp:start|start:http|start:repl|dev|dev:http|dev:repl |
Start MCP in stdio, HTTP+SSE, or REPL mode (production or dev) |
Agent Extension Layout
| Target | Files | Purpose |
|---|---|---|
| Claude Code | CLAUDE.md, .claude/rules/ |
Persistent project instructions + path-scoped coding rules |
| Claude Code Skills | .claude/skills/ |
Reusable workflows (onboarding, shipping, MCP ops, live debugging) |
| Claude Code Subagents | .claude/agents/ |
Specialized reviewers for backend, frontend, and MCP code paths |
| Codex Base Instructions | AGENTS.md, .codex/rules/default.rules |
Project-wide guidance + execution policy defaults |
| Codex Skills | .codex/skills/ |
Task-specific skills aligned to this repository |
| Codex Agents | .codex/agents/ |
Reusable custom-agent templates for implementation and review |
Root Helper Scripts
| Script | Role |
|---|---|
scripts/hook-handler.js |
Receives Claude hook payloads over stdin and forwards them to dashboard API |
scripts/install-hooks.js |
Writes/updates hook registration in ~/.claude/settings.json |
scripts/import-history.js |
Imports historical session data into local database with deep JSONL extraction (API errors, turn durations, thinking blocks, usage extras, subagent breakdowns) |
scripts/seed.js |
Loads deterministic demo data for testing and demos |
scripts/clear-data.js |
Removes persisted rows while preserving schema |
Plugin Marketplace
The Agent Monitor ships with an official Claude Code plugin marketplace containing five production-ready plugins. These extend Claude Code with skills, agents, hooks, CLI tools, and MCP integration — all grounded in the real data model (token tracking with compaction baselines, cost calculation via pattern-matched pricing rules, workflow intelligence with 11 datasets per session, and session metadata including thinking blocks, turn counts, and inference geography).
Installation
# Add the marketplace
claude plugin marketplace add hoangsonww/Claude-Code-Agent-Monitor
# Install individual plugins
claude plugin install ccam-analytics@hoangsonww-claude-code-agent-monitor
claude plugin install ccam-productivity@hoangsonww-claude-code-agent-monitor
claude plugin install ccam-devtools@hoangsonww-claude-code-agent-monitor
claude plugin install ccam-insights@hoangsonww-claude-code-agent-monitor
claude plugin install ccam-dashboard@hoangsonww-claude-code-agent-monitor
Available Plugins
| Plugin | Skills | Agent | CLI Tools | Focus |
|---|---|---|---|---|
| ccam-analytics | session-report, cost-breakdown, usage-trends, productivity-score | analytics-advisor | ccam-stats |
Token usage (4 types + baselines), cost via pricing engine, daily trends, productivity scoring |
| ccam-productivity | daily-standup, weekly-report, sprint-summary, workflow-optimizer | productivity-coach | — | Standup reports, sprint tracking, workflow optimization via 11 workflow intelligence datasets |
| ccam-devtools | session-debug, hook-diagnostics, data-export, health-check | issue-triager | ccam-doctor, ccam-export |
Session debugging, hook diagnostics, data export (JSON/CSV), system health |
| ccam-insights | pattern-detect, anomaly-alert, optimization-suggest, session-compare | insights-advisor | — | Pattern detection via tool flow transitions, anomaly alerting, optimization, session comparison |
| ccam-dashboard | dashboard-status, quick-stats | — | — | Dashboard connector with MCP integration and one-line metric summaries |
Skill Usage Examples
# Analytics — session report with per-model token breakdown + cost
/ccam-analytics:session-report latest
# Analytics — cost breakdown with cache efficiency analysis
/ccam-analytics:cost-breakdown this week
# Productivity — daily standup grouped by project
/ccam-productivity:daily-standup today
# Productivity — workflow optimization using workflow intelligence API
/ccam-productivity:workflow-optimizer analyze
# DevTools — debug a session's full event chain
/ccam-devtools:session-debug errors
# Insights — detect tool flow patterns and anti-patterns
/ccam-insights:pattern-detect tools
# Dashboard — one-line metrics summary
/ccam-dashboard:quick-stats
CLI Tools
# Quick terminal dashboard
ccam-stats # Sessions, costs (per-model), tokens (with baselines)
ccam-stats --cost # Cost summary with matched pricing rules
ccam-stats --tokens # Token usage including compaction baselines
ccam-stats --json # Raw JSON output
# System diagnostics
ccam-doctor # Full diagnostic: API, endpoints, database, hooks, freshness
ccam-doctor --quick # Basic connectivity check
# Data export
ccam-export sessions --format csv --limit 500
ccam-export all --output backup.json
Plugin Architecture
Each plugin follows the official Claude Code plugin specification. The marketplace manifest
at .claude-plugin/marketplace.json catalogs all five plugins. Each plugin
directory contains:
plugins/ccam-{name}/
├── .claude-plugin/plugin.json # Plugin manifest (name, version, description)
├── skills/{skill-name}/SKILL.md # Skill definitions with $ARGUMENTS
├── agents/{agent-name}.md # Agent definitions (model, tools, instructions)
├── hooks/hooks.json # Event hooks (fail-safe, non-blocking)
├── bin/{cli-tool} # CLI scripts (added to PATH)
├── .mcp.json # MCP server configuration (dashboard plugin)
└── settings.json # Plugin settings (dashboard plugin)
Data Model Reference
All plugins query the Agent Monitor API at http://localhost:4820. Key
capabilities they leverage:
| Capability | Details |
|---|---|
| Token tracking | 4 types (input, output, cache_read, cache_write) + 4 compaction baselines per model per session |
| Cost calculation | (tokens / 1M) × rate_per_mtok for each type; longest pattern match wins |
| Session metadata | thinking_blocks, turn_count, total_turn_duration_ms, usage_extras (service_tier, speed, inference_geo) |
| Event types | PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, Notification, Compaction, APIError, TurnDuration |
| Workflow intelligence | 11 datasets: stats, orchestration (DAG), toolFlow, effectiveness, patterns, modelDelegation, errorPropagation, concurrency, complexity, compaction, cooccurrence |
| Agent hierarchy | Recursive parent/child tree with subagent_type, depth tracking via recursive CTE |
📖 Full documentation: docs/plugins.md
Statusline Utility
The statusline/ directory contains a standalone CLI statusline for Claude
Code — completely independent of the web dashboard. It renders a color-coded bar at the
bottom of the Claude Code terminal showing context window usage, token counts, and git
branch.
Sonnet 4.6 | nguyens6 | ~/agent-dashboard/client | main | ████████░░ 79% | 3↑ 2↓ 156586c
| Segment | Source | Color Logic |
|---|---|---|
| Model | data.model.display_name |
Always cyan |
| User | $USERNAME / $USER |
Always green |
| Working Dir | data.workspace.current_dir |
Always yellow, ~ prefix for home |
| Git Branch | git symbolic-ref --short HEAD |
Always magenta, hidden outside git repos |
| Context Bar | data.context_window.used_percentage |
Green < 50%, Yellow 50–79%, Red ≥ 80% |
| Token Counts | data.context_window.current_usage |
Dim — ↑ input, ↓ output, c cache reads
|
Statusline rendering pipeline — invoked on each Claude Code update
Installation
Add this to ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "bash \"/absolute/path/to/statusline/statusline-command.sh\""
}
}
The statusline uses only Python 3.6+ stdlib (sys, json,
os, subprocess). It fails silently on empty input or JSON
errors and never blocks Claude Code.
Settings Page
The /settings route provides a comprehensive management interface with
six sections:
Model Pricing
Editable table of per-model pricing rules. Each Claude model variant has its own
explicit pattern (e.g., claude-opus-4-6%). Rates cover input, output,
cache read, and cache write tokens. Reset to defaults or add custom models.
Hook Configuration
Shows per-hook installation status (SessionStart, PreToolUse, PostToolUse, Stop, SubagentStop, Notification, SessionEnd). One-click reinstall if hooks are missing or outdated. Validates paths and permissions automatically.
Data Management
View database row counts and size. Session cleanup: abandon stale active sessions after N hours, purge old completed sessions after N days. Danger zone: clear all data with confirmation dialog to prevent accidental loss.
Data Export
Download all sessions, agents, events, token usage, and pricing rules as a single JSON file for backup or analysis. Includes full event history, model metadata, and cost breakdowns in one portable archive.
System Info
Server uptime, Node.js version, platform, and active WebSocket connections. At-a-glance health monitoring for the dashboard server itself, with database statistics and memory usage displayed in real-time.
Notification Preferences
Configure native browser notifications with per-event toggles for session starts, completions, errors, and subagent spawns. Automatic permission management with test-send button and graceful fallback when denied.
Each Claude model variant (e.g., Opus 4.6 vs Opus 4.1) has its own explicit pricing pattern because different model versions have different rates. The cost engine uses specificity sorting — longer patterns match before shorter ones.
Deployment Modes
Development vs production deployment topology
| Aspect | Development | Production |
|---|---|---|
| Processes | 2 (Express + Vite) | 1 (Express only) |
| Client URL | http://localhost:5173 |
http://localhost:4820 |
| API proxy | Vite proxies /api + /ws to :4820 |
Same origin, no proxy |
| File watching | node --watch + Vite HMR |
None |
| Source maps | Inline | External files |
Container Runtime (Docker / Podman)
The production image is OCI-compatible and works with both Docker and Podman. The
server listens on 4820, reads legacy Claude history from a read-only mount,
and persists SQLite data under /app/data.
Container image build and runtime mounts
# Docker Compose
docker compose up -d --build
# Podman Compose
CLAUDE_HOME="$HOME/.claude" podman compose up -d --build
# Stop the stack
docker compose down
# or
podman compose down
| Mount | Purpose |
|---|---|
~/.claude:/root/.claude:ro |
Read historical Claude session files for import without modifying them |
agent-monitor-data:/app/data |
Persist the SQLite database across rebuilds and container restarts |
Claude Code fires hooks from the host machine, not from inside the container. After
the container is healthy on http://localhost:4820, run
npm run install-hooks on the host so hook events post back to the
containerized server.
Docker / Podman
A multi-stage Dockerfile and docker-compose.yml are included.
Both Docker and Podman are fully supported — the image is OCI-compliant.
Quick Start
# Docker Compose
docker compose up -d --build
# Podman Compose
CLAUDE_HOME="$HOME/.claude" podman compose up -d --build
Plain Docker / Podman (no Compose)
# Build the image
docker build -t agent-monitor .
# — or —
podman build -t agent-monitor .
# Run the container
docker run -d --name agent-monitor \
-p 4820:4820 \
-v "$HOME/.claude:/root/.claude:ro" \
-v agent-monitor-data:/app/data \
agent-monitor
Volume Mounts
| Mount | Purpose |
|---|---|
~/.claude:/root/.claude:ro |
Read-only access to legacy session history for automatic import on startup |
agent-monitor-data:/app/data |
Persists the SQLite database across container restarts |
Multi-Stage Build
The Dockerfile uses three stages to minimize the final image size:
| Stage | Purpose |
|---|---|
| server-deps | Installs production node_modules on node:22-alpine. better-sqlite3 is optional — if prebuilds are unavailable, the server falls back to built-in node:sqlite |
| client-build | Runs npm ci + vite build to produce optimized static assets |
| runtime | Clean node:22-alpine with only node_modules, server code, and client/dist |
Claude Code hooks run on the host, not inside the container. The containerized server
receives hook events via HTTP on localhost:4820. Run
npm run install-hooks on the host after starting the container.
Performance Characteristics
| Metric | Value | Notes |
|---|---|---|
| Server startup | < 200ms | SQLite opens instantly; schema migration is idempotent |
| Hook latency | < 50ms | Transaction + broadcast, no async I/O beyond SQLite |
| Client JS bundle | 200 KB / 63 KB gzip | CSS: 17 KB / 4 KB gzip |
| WebSocket latency | < 5ms | Local loopback, JSON serialization only |
| SQLite write throughput | ~50,000 inserts/sec | WAL mode on SSD; far exceeds any hook event rate |
| Max events before slowdown | ~1M rows | Pagination prevents full-table scans |
| Server memory | ~30 MB | SQLite in-process, no ORM overhead |
| Client memory | ~15 MB | React + Tailwind, minimal runtime deps |
Security Considerations
| Area | Approach |
|---|---|
| SQL injection | All queries use prepared statements with parameterized values — no string interpolation |
| Request size | Express JSON body parser limited to 1MB |
| Input validation | Required fields checked before DB operations; CHECK constraints on status enums |
| Hook safety |
Hook handler always exits 0; 5s max lifetime; uses 127.0.0.1 not
external hosts
|
| CORS | Enabled for development; in production same-origin (Express serves the client) |
| Authentication | Intentionally none — local dev tool. Restrict via firewall if exposing on LAN. |
| Secrets | No API keys, tokens, or credentials stored or transmitted anywhere |
| Dependency surface | 5 runtime server deps, 6 runtime client deps (includes D3.js for Workflows) — minimal attack surface |
Troubleshooting
No sessions appearing after starting Claude Code
Check 1 — Is the server running?
curl http://localhost:4820/api/health
# Expected: {"status":"ok","timestamp":"..."}
Check 2 — Are hooks installed?
# Open ~/.claude/settings.json and confirm it contains "hook-handler.js"
# If not, re-run:
npm run install-hooks
Check 3 — Start a new Claude Code session
Hooks only apply to sessions started after installation. Restart Claude Code after starting the dashboard.
Check 4 — Is Node.js in PATH?
On some systems the shell environment when Claude Code fires hooks may not include the
full PATH. Test with node --version. If not found, use the absolute path to
node in the hook command.
Common Issues
| Problem | Solution |
|---|---|
better-sqlite3 errors during install |
This is non-fatal — better-sqlite3 is an optional dependency. On
Node 22+ the server automatically falls back to built-in
node:sqlite. On older Node versions, install Python 3 + C++ build
tools, then run npm rebuild better-sqlite3.
|
| Dashboard shows "Disconnected" |
Server is not running. Start it with npm run dev. Client
auto-reconnects every 2s.
|
| Events Today shows 0 | Ensure you are on the latest version (timezone bug was fixed). Restart the server. |
| Port 4820 already in use |
Run DASHBOARD_PORT=4821 npm run dev, update Vite proxy in
client/vite.config.ts, and re-run
npm run install-hooks.
|
| Stale seed data shown | Run npm run clear-data to wipe all rows, then restart. |
| Hooks show validation error about matcher |
Ensure you're on the latest version — the hook format was updated to use
matcher: "*" string (not object).
|
| "SQLite backend not available" on startup |
Neither better-sqlite3 nor node:sqlite could load.
Upgrade to Node.js 22+ (recommended), or install Python 3 + C++ build tools
and run npm rebuild better-sqlite3.
|
| Docker container runs but no sessions appear |
Hooks run on the host, not inside the container. Run
npm run install-hooks on the host after the container starts.
Verify hooks in ~/.claude/settings.json point to
localhost:4820.
|
Technology Choices
| Technology | Why This Over Alternatives |
|---|---|
| SQLite (better-sqlite3 / node:sqlite) |
Zero-config, embedded, no server process. WAL mode gives concurrent reads.
Synchronous API is simpler than async for this use case.
better-sqlite3 is preferred when prebuilds are available; falls back
to Node.js built-in node:sqlite on Node 22+ when the native module
cannot be compiled.
|
| Express |
Battle-tested, minimal, well-understood. Fastify would be overkill; raw
http module would require too much boilerplate for routing.
|
| ws | Fastest, most lightweight WebSocket library for Node. No Socket.IO overhead needed — we only push typed JSON messages one-way. |
| React 18 | Stable, widely known, strong TypeScript support. No Server Components or RSC needed for a client-rendered local SPA. |
| Vite 6 | Fast builds, native ESM, excellent dev experience. Proxy config handles the dev server split cleanly with no ejection. |
| Tailwind CSS | Utility-first approach keeps styles colocated with markup. No CSS module boilerplate. Custom dark theme config for the dark UI. |
| React Router 6 |
Standard routing for React SPAs. Layout routes with
<Outlet> give clean shell composition without prop drilling.
|
| Lucide React | Tree-shakeable icon library — only imports what's used (~20 icons). No heavy icon font. |
| TypeScript Strict |
Catches null/undefined bugs at compile time.
noUncheckedIndexedAccess prevents array bounds issues in analytics
aggregations.
|
| D3.js + d3-sankey | Industry-standard data visualization library. Powers the Workflows page's 11 interactive sections — DAG layouts, Sankey diagrams, force-directed graphs, bubble charts, and swim-lane timelines. No wrapper libraries needed; direct SVG rendering keeps bundle impact minimal. |
| Python (statusline) | Available on virtually all systems. Handles ANSI and JSON natively with stdlib only. No install step required. |