Real-time visualizer for Claude Code multi-agent and subagent activity.
The name comes from the Korean phrase "뭐하노" (mohanŏ), meaning "what are you doing?" — because that's exactly why this project exists: to see what Claude Code agents are up to.
Mohano connects to Claude Code via its hooks system and streams all agent events to a web dashboard, giving you visibility into what each agent is doing across sessions.
- Timeline View - Swim-lane timeline showing events per agent in real time
- Task Graph - Kanban board of tasks (Pending / In Progress / Completed) with dependency arrows
- Agents View - Per-session agent cards grouped by type:
- Team agents - grouped by team name (spawned with
team_name) - Subagents - individual subagents (Explore, Plan, Bash, etc.)
- Main session - the primary Claude Code session
- Team agents - grouped by team name (spawned with
- Live Event Log - Collapsible table of all events with filtering
- Detail Modal - Click any event to see parsed fields or raw JSON
- Filtering - Filter by session, agent, or event type
- WebSocket - Auto-reconnecting real-time connection with no polling
Claude Code ──(hooks)──> send-event.sh ──(POST)──> Mohano Server ──(WebSocket)──> Browser
- Hooks (
hooks/send-event.sh) - Shell script invoked by Claude Code hooks. Reads event JSON from stdin, adds a UTC timestamp, and POSTs to the server. Runs async and always exits 0 to never block Claude Code. - Server (
server/index.mjs) - Node.js HTTP + WebSocket server on port 7777. Receives events viaPOST /api/events, stores them in a circular buffer (2000 max), and broadcasts to all connected WebSocket clients. Also serves the frontend as static files. - Frontend (
frontend/) - Vanilla HTML/CSS/JS single-page app with a dark theme. Connects via WebSocket for real-time updates.
A public demo server is available at https://mohano.onrender.com. You can try it out by pointing your hooks at it:
./setup.sh --url https://mohano.onrender.comData retention:
- Workspace TTL: 24 hours after last activity (checked hourly)
- Events: Up to 2,000 per workspace (circular buffer — oldest events are overwritten)
- Server restart: All data is lost (memory-only, no disk storage)
The demo runs on Render's free plan, which sleeps after 15 minutes of inactivity. A cold start wipes all data, so in practice data only survives for about 15 minutes without activity.
This is a shared public server — all Claude Code event data (tool calls, file paths, agent activity, etc.) will be transmitted over the network. For anything beyond a quick test, deploy your own instance.
git clone https://github.com/hulryung/mohano.git
cd mohano
./setup.shThis will:
- Install server dependencies (
npm install) - Make hook scripts executable
- Add Mohano hooks to
~/.claude/settings.json(merges with existing config)
Then start the server and open the dashboard:
cd server && npm start
# Open http://localhost:7777In another terminal:
./hooks/test-events.shThis sends 25 simulated events from 4 agents to verify the pipeline works.
If you prefer to configure hooks manually, add the following to ~/.claude/settings.json. Replace /path/to/mohano with your actual path:
Show hooks config
{
"hooks": {
"PreToolUse": [
{
"matcher": "",
"hooks": [{"type": "command", "command": "cat | /path/to/mohano/hooks/send-event.sh", "async": true}]
}
],
"PostToolUse": [
{
"matcher": "",
"hooks": [{"type": "command", "command": "cat | /path/to/mohano/hooks/send-event.sh", "async": true}]
}
],
"SubagentStart": [
{
"matcher": "",
"hooks": [{"type": "command", "command": "cat | /path/to/mohano/hooks/send-event.sh", "async": true}]
}
],
"SubagentStop": [
{
"matcher": "",
"hooks": [{"type": "command", "command": "cat | /path/to/mohano/hooks/send-event.sh", "async": true}]
}
],
"TaskCompleted": [
{
"hooks": [{"type": "command", "command": "cat | /path/to/mohano/hooks/send-event.sh", "async": true}]
}
],
"TeammateIdle": [
{
"hooks": [{"type": "command", "command": "cat | /path/to/mohano/hooks/send-event.sh", "async": true}]
}
],
"Notification": [
{
"matcher": "",
"hooks": [{"type": "command", "command": "cat | /path/to/mohano/hooks/send-event.sh", "async": true}]
}
],
"Stop": [
{
"hooks": [{"type": "command", "command": "cat | /path/to/mohano/hooks/send-event.sh", "async": true}]
}
]
}
}Note:
TaskCompleted,TeammateIdle, andStophooks don't support thematcherfield.
The easiest way to get Mohano running on a remote server.
Or manually:
- Go to render.com and connect your GitHub
- Create a New Web Service from the
mohanorepo - Render auto-detects the config from
render.yaml - An API key (
MOHANO_API_KEY) is generated automatically - Copy the deployed URL (e.g.
https://mohano-xxxx.onrender.com) and the API key from the Environment tab
git clone https://github.com/hulryung/mohano.git
cd mohano
./setup.sh --url https://mohano-xxxx.onrender.com --api-key YOUR_KEYThat's it. Start a Claude Code session and events will appear on the remote dashboard.
Docker (any VPS)
git clone https://github.com/hulryung/mohano.git && cd mohano
MOHANO_API_KEY=your-secret-key docker compose up -dNode.js direct
git clone https://github.com/hulryung/mohano.git && cd mohano/server
npm install
MOHANO_API_KEY=your-secret-key npm startHTTPS with nginx reverse proxy
server {
server_name mohano.example.com;
location / {
proxy_pass http://127.0.0.1:7777;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}The Upgrade/Connection headers are required for WebSocket.
After deploying the server, point your local hooks at it:
./setup.sh --url https://your-server-url --api-key YOUR_KEYThis writes config to ~/.config/mohano/config and sets up Claude Code hooks. No local server needed.
| Variable | Default | Description |
|---|---|---|
PORT |
7777 |
Server listen port (Render sets this automatically) |
MOHANO_API_KEY |
(empty) | API key for authentication. If empty, all access is open |
MAX_EVENTS |
2000 |
Circular buffer capacity |
| Endpoint | Method | Description |
|---|---|---|
/api/events |
POST | Ingest a hook event (JSON body). Requires Authorization: Bearer <key> if API key is set |
/api/events |
GET | Retrieve stored events. Query params: session_id, agent_type, tool_name, hook_event_name, since_seq, limit |
/api/agents |
GET | List tracked agents |
/api/tasks |
GET | Scan ~/.claude/tasks/ for task files |
/ws |
WebSocket | Real-time event stream |
mohano/
├── setup.sh # One-command install + hook config
├── Dockerfile # Container image for remote deployment
├── docker-compose.yml # Docker Compose config
├── frontend/
│ ├── index.html # Main page
│ ├── app.js # Frontend logic (state, rendering, WebSocket)
│ └── style.css # Dark theme styles
├── hooks/
│ ├── send-event.sh # Hook script (stdin JSON -> POST to server)
│ ├── test-events.sh # Sample event generator for testing
│ └── claude-hooks-config.json
└── server/
├── index.mjs # Node.js HTTP + WebSocket server
└── package.json
- Node.js 18+
python3(used bysend-event.shto add timestamps; available by default on macOS)curl(used bysend-event.shto POST events)
MIT

