Attention Router
A local-only dashboard for supervising a running Claude Code session — /loop
runs, long tasks, agent sessions — from a browser tab or your phone, while
Claude Code itself stays entirely in the terminal.
State, not log. The terminal is a time-ordered stream of everything that
happened. This dashboard is a materialized view that answers, in under three
seconds: where are things, what changed since I last looked, and does
anything need me?
| Light | Dark |
|---|
 |  |
Tapping a health chip opens a drill-down sheet — what it is, why, and what you
can do about it, without leaving the browser:

How it works
attention-router/
├── hooks/ # Node scripts wired into Claude Code hook events
├── server/ # single Node process, no framework, no database
├── ui/ # static vanilla-JS SPA, served by server/
├── .claude-plugin/ # plugin manifest + hooks.json
└── commands/ # the /attention-router:open slash command
- Hooks fire on Claude Code lifecycle events (
SessionStart,
PostToolUse, Stop, Notification, SessionEnd, SubagentStop, plus
UserPromptSubmit — see Deviations from the spec).
Each hook wraps its payload in {id, ts, event, payload}, appends it to
.claude/attention/events.jsonl in the supervised project, and
fire-and-forget POSTs it to the local server. Hooks always exit 0, use a
500ms POST timeout, and never block Claude Code — even if the dashboard is
closed, unreachable, or crashed.
- The server is a single Node process (built-ins only — no npm install
needed). It reduces
events.jsonl into state.json on every event,
broadcasts the new state over Server-Sent Events, and serves the UI. It's
a self-healing lazy daemon: the first hook event of any session spawns
it automatically if it isn't already running.
- The UI is a static page that renders
state.json live via SSE — no
build step, no framework.
Everything persists as plain files under .claude/attention/ in the
supervised project:
| File | Purpose |
|---|
events.jsonl | append-only log — the durable source of truth |
state.json | last reducer output (what the UI renders) |
inbox.jsonl | directives you send from the browser, waiting for delivery into the session |
cursor.json | last-acknowledged event, drives the "N new" counter |
plan.md | optional hand-authored plan (- [ ] task, one per line), merged with Claude's live TodoWrite state |
config.json | optional {"enabled": false} per-project kill switch |
Install as a plugin
This repo is its own marketplace (.claude-plugin/marketplace.json), so a
persistent install just needs its two commands — no separate marketplace
repo, no approval, no npm install:
/plugin marketplace add robertcdawson/claude-code-dashboard
/plugin install attention-router@attention-router-marketplace
For a one-off, session-only load instead (nothing persisted, gone once you
exit claude):
claude --plugin-url https://github.com/robertcdawson/claude-code-dashboard/archive/refs/heads/main.zip
# or, from a local clone:
claude --plugin-dir ./claude-code-dashboard
Either way, once enabled, hooks fire automatically — there's no separate
"start the server" step.
To open the dashboard from inside a session:
/attention-router:open
This makes sure the daemon is running and opens http://localhost:4123 with
your platform's default opener (falls back to printing the URL if none is
found, e.g. in a headless environment).
Running it standalone (development)
npm start # node server/index.js — listens on :4123
npm test # node --test — reducer + sun-scheduler unit tests
No dependencies to install — everything is Node built-ins. Requires Node
>=20.
GET /health, GET /state?cwd=..., GET /stream?cwd=... (SSE),
GET /api/projects, POST /event, and POST /action are the whole server
API. With exactly one active project, ?cwd= can be omitted from /state
and /stream; with more than one, / shows a session picker.
Configuration