view-only · live · local-first
See what your agents are doing, as it happens.
workstate is a live dashboard for agent work. Projects hold tasks, tasks hold steps, steps hold subtasks. Every node carries a state and a color, and every change is pushed to the page over server-sent events. Agents write; people watch. There is nothing to click.
- ✓ schema Completed
- ✓ api Completed
- ◑ dashboard In work
- ◔ dashboard/legend Planning
- ○ e2e Not started
01Install
git clone https://github.com/nvanderploeg/work-state
cd work-state && scripts/install.sh The installer adds the workstate skill and MCP server to your agent setup. Pass flags
after the script:
--with-server- Start a local tracker with docker compose on :3280.
--replace- Back up and replace existing skills and MCP entries.
--allow-tools- Allowlist the workstate MCP tools in Claude Code.
Dashboards live at /p/<projectId>. Anyone with the link can watch; only a
write key can change anything.
02How agents connect
The workstate skill tells an agent when to open a task and report progress. It does
that through five MCP tools:
start_taskOpen a task on this repo’s project, with optional steps. Returns the task and dashboard URL.update_stepUpsert a step or subtask by path (chunk-2, chunk-2/tests): state, note, tags, position.update_taskChange the task’s title, summary, state or note.get_taskRead a task and its nodes back.configureRead, replace or patch the project’s states, colors and theme.
On each task start the client looks for a server in this order and uses the first that answers:
WORKSTATE_URLwhen set, only this server is triedlocalhost:3280the local docker compose trackerworkstate.nanzero.comthe hosted tracker
If none responds, the agent continues untracked. A missing tracker never interrupts the work.
03API reference
JSON in and out. Errors are { "error": "…" }. Rows marked with a key need Authorization: Bearer <writeKey>.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/health | Service status and version. |
| GET | /api/presets | Available state presets. |
| POST | /api/projects | Create a project. Returns id, write key (once), url. |
| GET | /api/projects/:id | Snapshot: project, tasks, nodes, lastSeq. |
| PUT | /api/projects/:id/configkey | Replace the project config. |
| PATCH | /api/projects/:id/configkey | Merge states by key; replace other fields. |
| POST | /api/projects/:id/taskskey | Create a task, optionally with steps. |
| GET | /api/tasks/:taskId | A task and its nodes. |
| GET | /api/tasks/:taskId/events | Event history, optionally for one node path. |
| PATCH | /api/tasks/:taskIdkey | Update title, summary, state, note. |
| PUT | /api/tasks/:taskId/nodes/<path>key | Upsert a step or subtask. |
| GET | /api/projects/:id/stream | Server-sent events; resume with Last-Event-ID or ?since. |
Stream
GET /api/projects/:id/stream sends frames as id: <seq>, event: config | task | node, and a JSON data line. Reconnect with Last-Event-ID to replay missed events. A : ping comment keeps the connection warm; an idle event ends it after a quiet period.
Default states
The build-it preset. A project starts in queued.
| State | Key | Color | Kind | Motion |
|---|---|---|---|---|
| ○ Not started | queued | #9ca3af | pending | none |
| ◔ Planning | planning | #f97316 | active | shimmer |
| ◑ In work | in_work | #eab308 | active | pulse |
| ◕ Testing | testing | #a855f7 | active | pulse |
| ! Needs input | needs_input | #ef4444 | blocked | blink |
| ✕ Failed | failed | #ef4444 | failed | none |
| ✓ Completed | done | #22c55e | done | none |