Real-time monitoring and visualization for Claude Code agents through comprehensive hook event tracking. You can watch the full breakdown here and watch the latest enhancement where we compare Haiku 4.5 and Sonnet 4.5 here.
This system provides complete observability into Claude Code agent behavior by capturing, storing, and visualizing Claude Code Hook events in real-time. It enables monitoring of multiple concurrent agents with session tracking, event filtering, and live updates.
Claude Agents โ Hook Scripts โ HTTP POST โ Bun Server โ SQLite โ WebSocket โ Vue Client
Before getting started, ensure you have the following installed:
- Claude Code - Anthropic's official CLI for Claude
- Astral uv - Fast Python package manager (required for hook scripts)
- Bun, npm, or yarn - For running the server and client
- Anthropic API Key - Set as
ANTHROPIC_API_KEYenvironment variable - OpenAI API Key (optional) - For multi-model support with just-prompt MCP tool
- ElevenLabs API Key (optional) - For audio features
To setup observability in your repo,we need to copy the .claude directory to your project root.
To integrate the observability hooks into your projects:
-
Copy the entire
.claudedirectory to your project root:cp -R .claude /path/to/your/project/
-
Update the
settings.jsonconfiguration:Open
.claude/settings.jsonin your project and modify thesource-appparameter to identify your project:{ "hooks": { "PreToolUse": [{ "matcher": "", "hooks": [ { "type": "command", "command": "uv run .claude/hooks/pre_tool_use.py" }, { "type": "command", "command": "uv run .claude/hooks/send_event.py --source-app YOUR_PROJECT_NAME --event-type PreToolUse --summarize" } ] }], "PostToolUse": [{ "matcher": "", "hooks": [ { "type": "command", "command": "uv run .claude/hooks/post_tool_use.py" }, { "type": "command", "command": "uv run .claude/hooks/send_event.py --source-app YOUR_PROJECT_NAME --event-type PostToolUse --summarize" } ] }], "UserPromptSubmit": [{ "hooks": [ { "type": "command", "command": "uv run .claude/hooks/user_prompt_submit.py --log-only" }, { "type": "command", "command": "uv run .claude/hooks/send_event.py --source-app YOUR_PROJECT_NAME --event-type UserPromptSubmit --summarize" } ] }] // ... (similar patterns for Notification, Stop, SubagentStop, PreCompact, SessionStart, SessionEnd) } }Replace
YOUR_PROJECT_NAMEwith a unique identifier for your project (e.g.,my-api-server,react-app, etc.). -
Ensure the observability server is running:
# From the observability project directory (this codebase) ./scripts/start-system.sh
Now your project will send events to the observability system whenever Claude Code performs actions.
You can quickly view how this works by running this repositories .claude setup.
# 1. Start both server and client
./scripts/start-system.sh
# 2. Open http://localhost:5173 in your browser
# 3. Open Claude Code and run the following command:
Run git ls-files to understand the codebase.
# 4. Watch events stream in the client
# 5. Copy the .claude folder to other projects you want to emit events from.
cp -R .claude <directory of your codebase you want to emit events from>claude-code-hooks-multi-agent-observability/
โ
โโโ apps/ # Application components
โ โโโ server/ # Bun TypeScript server
โ โ โโโ src/
โ โ โ โโโ index.ts # Main server with HTTP/WebSocket endpoints
โ โ โ โโโ db.ts # SQLite database management & migrations
โ โ โ โโโ types.ts # TypeScript interfaces
โ โ โโโ package.json
โ โ โโโ events.db # SQLite database (gitignored)
โ โ
โ โโโ client/ # Vue 3 TypeScript client
โ โโโ src/
โ โ โโโ App.vue # Main app with theme & WebSocket management
โ โ โโโ components/
โ โ โ โโโ EventTimeline.vue # Event list with auto-scroll
โ โ โ โโโ EventRow.vue # Individual event display
โ โ โ โโโ FilterPanel.vue # Multi-select filters
โ โ โ โโโ ChatTranscriptModal.vue # Chat history viewer
โ โ โ โโโ StickScrollButton.vue # Scroll control
โ โ โ โโโ LivePulseChart.vue # Real-time activity chart
โ โ โโโ composables/
โ โ โ โโโ useWebSocket.ts # WebSocket connection logic
โ โ โ โโโ useEventColors.ts # Color assignment system
โ โ โ โโโ useChartData.ts # Chart data aggregation
โ โ โ โโโ useEventEmojis.ts # Event type emoji mapping
โ โ โโโ utils/
โ โ โ โโโ chartRenderer.ts # Canvas chart rendering
โ โ โโโ types.ts # TypeScript interfaces
โ โโโ .env.sample # Environment configuration template
โ โโโ package.json
โ
โโโ .claude/ # Claude Code integration
โ โโโ hooks/ # Hook scripts (Python with uv)
โ โ โโโ send_event.py # Universal event sender
โ โ โโโ pre_tool_use.py # Tool validation & blocking
โ โ โโโ post_tool_use.py # Result logging
โ โ โโโ notification.py # User interaction events
โ โ โโโ user_prompt_submit.py # User prompt logging & validation
โ โ โโโ stop.py # Session completion
โ โ โโโ subagent_stop.py # Subagent completion
โ โ
โ โโโ settings.json # Hook configuration
โ
โโโ scripts/ # Utility scripts
โ โโโ start-system.sh # Launch server & client
โ โโโ reset-system.sh # Stop all processes
โ โโโ test-system.sh # System validation
โ
โโโ logs/ # Application logs (gitignored)
If you want to master claude code hooks watch this video
The hook system intercepts Claude Code lifecycle events:
-
send_event.py: Core script that sends event data to the observability server- Supports
--add-chatflag for including conversation history - Validates server connectivity before sending
- Handles all event types with proper error handling
- Supports
-
Event-specific hooks: Each implements validation and data extraction
pre_tool_use.py: Blocks dangerous commands, validates tool usagepost_tool_use.py: Captures execution results and outputsnotification.py: Tracks user interaction pointsuser_prompt_submit.py: Logs user prompts, supports validation (v1.0.54+)stop.py: Records session completion with optional chat historysubagent_stop.py: Monitors subagent task completionpre_compact.py: Tracks context compaction operations (manual/auto)session_start.py: Logs session start, can load development contextsession_end.py: Logs session end, saves session statistics
Bun-powered TypeScript server with real-time capabilities:
- Database: SQLite with WAL mode for concurrent access
- Endpoints:
POST /events- Receive events from agentsGET /events/recent- Paginated event retrieval with filteringGET /events/filter-options- Available filter valuesWS /stream- Real-time event broadcasting
- Features:
- Automatic schema migrations
- Event validation
- WebSocket broadcast to all clients
- Chat transcript storage
Vue 3 application with real-time visualization:
-
Visual Design:
- Dual-color system: App colors (left border) + Session colors (second border)
- Gradient indicators for visual distinction
- Dark/light theme support
- Responsive layout with smooth animations
-
Features:
- Real-time WebSocket updates
- Multi-criteria filtering (app, session, event type)
- Live pulse chart with session-colored bars and event type indicators
- Time range selection (1m, 3m, 5m) with appropriate data aggregation
- Chat transcript viewer with syntax highlighting
- Auto-scroll with manual override
- Event limiting (configurable via
VITE_MAX_EVENTS_TO_DISPLAY)
-
Live Pulse Chart:
- Canvas-based real-time visualization
- Session-specific colors for each bar
- Event type emojis displayed on bars
- Smooth animations and glow effects
- Responsive to filter changes
- Event Generation: Claude Code executes an action (tool use, notification, etc.)
- Hook Activation: Corresponding hook script runs based on
settings.jsonconfiguration - Data Collection: Hook script gathers context (tool name, inputs, outputs, session ID)
- Transmission:
send_event.pysends JSON payload to server via HTTP POST - Server Processing:
- Validates event structure
- Stores in SQLite with timestamp
- Broadcasts to WebSocket clients
- Client Update: Vue app receives event and updates timeline in real-time
| Event Type | Emoji | Purpose | Color Coding | Special Display |
|---|---|---|---|---|
| PreToolUse | ๐ง | Before tool execution | Session-based | Tool name & details |
| PostToolUse | โ | After tool completion | Session-based | Tool name & results |
| Notification | ๐ | User interactions | Session-based | Notification message |
| Stop | ๐ | Response completion | Session-based | Summary & chat transcript |
| SubagentStop | ๐ฅ | Subagent finished | Session-based | Subagent details |
| PreCompact | ๐ฆ | Context compaction | Session-based | Compaction details |
| UserPromptSubmit | ๐ฌ | User prompt submission | Session-based | Prompt: "user message" (italic) |
| SessionStart | ๐ | Session started | Session-based | Session source (startup/resume/clear) |
| SessionEnd | ๐ | Session ended | Session-based | End reason (clear/logout/exit/other) |
The UserPromptSubmit hook captures every user prompt before Claude processes it. In the UI:
- Displays as
Prompt: "user's message"in italic text - Shows the actual prompt content inline (truncated to 100 chars)
- Summary appears on the right side when AI summarization is enabled
- Useful for tracking user intentions and conversation flow
-
Copy the event sender:
cp .claude/hooks/send_event.py YOUR_PROJECT/.claude/hooks/
-
Add to your
.claude/settings.json:{ "hooks": { "PreToolUse": [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "uv run .claude/hooks/send_event.py --source-app YOUR_APP --event-type PreToolUse" }] }] } }
Already integrated! Hooks run both validation and observability:
{
"type": "command",
"command": "uv run .claude/hooks/pre_tool_use.py"
},
{
"type": "command",
"command": "uv run .claude/hooks/send_event.py --source-app cc-hooks-observability --event-type PreToolUse"
}# System validation
./scripts/test-system.sh
# Manual event test
curl -X POST http://localhost:4000/events \
-H "Content-Type: application/json" \
-d '{
"source_app": "test",
"session_id": "test-123",
"hook_event_type": "PreToolUse",
"payload": {"tool_name": "Bash", "tool_input": {"command": "ls"}}
}'Copy .env.sample to .env in the project root and fill in your API keys:
Application Root (.env file):
ANTHROPIC_API_KEYโ Anthropic Claude API key (required)ENGINEER_NAMEโ Your name (for logging/identification)GEMINI_API_KEYโ Google Gemini API key (optional)OPENAI_API_KEYโ OpenAI API key (optional)ELEVEN_API_KEYโ ElevenLabs API key (optional)
Client (.env file in apps/client/.env):
VITE_MAX_EVENTS_TO_DISPLAY=100โ Maximum events to show (removes oldest when exceeded)
- Server:
4000(HTTP/WebSocket) - Client:
5173(Vite dev server)
- Blocks dangerous commands (
rm -rf, etc.) - Prevents access to sensitive files (
.env, private keys) - Validates all inputs before execution
- No external dependencies for core functionality
- Server: Bun, TypeScript, SQLite
- Client: Vue 3, TypeScript, Vite, Tailwind CSS
- Hooks: Python 3.8+, Astral uv, TTS (ElevenLabs or OpenAI), LLMs (Claude or OpenAI)
- Communication: HTTP REST, WebSocket
If your hook scripts aren't executing properly, it might be due to relative paths in your .claude/settings.json. Claude Code documentation recommends using absolute paths for command scripts.
Solution: Use the custom Claude Code slash command to automatically convert all relative paths to absolute paths:
# In Claude Code, simply run:
/convert_paths_absoluteThis command will:
- Find all relative paths in your hook command scripts
- Convert them to absolute paths based on your current working directory
- Create a backup of your original settings.json
- Show you exactly what changes were made
This ensures your hooks work correctly regardless of where Claude Code is executed from.
And prepare for the future of software engineering
Learn tactical agentic coding patterns with Tactical Agentic Coding
Follow the IndyDevDan YouTube channel to improve your agentic coding advantage.

