Interface
Status: draftThree doors into the same state: the dashboard (for the human watching), the CLI (for the human in a terminal and for scripts), and MCP tools (for agents). They share vocabulary exactly — a claim is a claim everywhere — and every dashboard action has a CLI equivalent.
Shipped today (0.3.0)
This document is the spec; the wireframes below describe where the product is going. What exists now:
- Nav: Fleet / Board / PRs / Timeline / Spend / Stats, plus a Session detail reached from any session row. Every list is a data-grid (sortable, resizable, filterable columns, layouts persisted per table).
- Board = a KPI strip (live / held / worktrees / ready or projects / incidents) + Tasks as a kanban (Ready · In progress · Blocked · Done) + Dispatch + Gates + Processes + Resources + Claims + a Worktree map (one tile per worktree, grouped by project, colored by live / dirty / unpushed / merged) + Incidents. Tasks and Worktrees carry a Cards/Table toggle (persisted). Every row, card and tile has one menu — the hover kebab, right-click, or Enter when focused — instead of inline action links; the menu carries the row's actions (open, diff, PR, run, claim, gates, release, stop, ack, codify, merge, copy …) with destructive ones last and confirmed. "View 4 — Incidents" ships as its own Incidents view (feed, Open/All, ack, ack-all) with a short open-only section on the Board.
- Not yet: the keyboard map, the ⌘K palette, the permission-broker Allow/Deny on Fleet rows, the Settings page. The Session input box exists for spawned runs (M3.3).
- CLI and MCP sections below are split into Today / Planned; section D lists the HTTP routes the daemon actually serves.
A. Dashboard
Local web app served by the daemon at http://localhost:<port> (opened by swarm ui). Dense, monochrome, keyboard-first; it is an ops console, not a marketing page. Light and dark. Everything updates live over SSE; no refresh button anywhere.
Global frame
┌──────────────────────────────────────────────────────────────────────────────────────┐
│ ⌂ Swarm Fleet Incidents ●2 ⌕ search (⌘K) daemon ● 0.3.1 ☾ │
├───────────────┬──────────────────────────────────────────────────────────────────────┤
│ PROJECTS │ │
│ ● web-app │ │
│ 3 live │ <view> │
│ ● api │ │
│ 1 live │ │
│ ○ swarm │ │
│ idle │ │
│ ○ discovered │ │
│ ~/tmp/x │ │
│ │ │
│ + add folder │ │
└───────────────┴──────────────────────────────────────────────────────────────────────┘
- Sidebar: registered projects with live-session count and a coloured dot (● live, ○ idle, ⚠ has incident). "discovered" groups sessions seen in unregistered folders; one click promotes to registered.
- Top bar: Fleet (all projects), Incidents (with unacked count), ⌘K palette (jump to project/session/task, run any CLI command), daemon health + version.
- Keyboard:
g ffleet,g iincidents,1–9project,j/kmove,enteropen,escback,?help.
View 1 — Fleet (default)
Every live session across every project, one row each. Answers "what is happening on my machine right now?"
FLEET 4 live · 2 idle · $3.42 today
┌────┬──────────────┬────────────┬──────────┬────────────────────────────────┬──────┬─────────┬──────┐
│ │ project │ session │ claim │ now │ age │ tokens │ cost │
├────┼──────────────┼────────────┼──────────┼────────────────────────────────┼──────┼─────────┼──────┤
│ ● │ web-app │ owner ⌨ │ — │ Read docs/planning/52-impl… │ 2s │ 184k │ 1.10 │
│ ● │ web-app │ M0.6 ▶ │ M0.6 │ Bash bun run test │ 41s │ 92k │ 0.61 │
│ ● │ web-app │ └ sub #2 │ (M0.6) │ Grep "scan.*report" │ 3s │ 12k │ 0.08 │
│ ● │ api │ owner ⌨ │ 9.13.5 │ waiting for input │ 4m │ 301k │ 1.63 │
│ ◐ │ api │ M0.7 ▶ │ M0.7 │ ⚠ permission: rm -rf dist │ 12s │ 40k │ — │
│ ○ │ web-app │ M0.4 ▶ │ M0.4 │ ended · result ok · 14 turns │ 1h │ 210k │ 1.40 │
└────┴──────────────┴────────────┴──────────┴────────────────────────────────┴──────┴─────────┴──────┘
- Icons:
⌨interactive session,▶spawned by Swarm,└subagent (nested under parent).●active,◐blocked (waiting on permission/human),○ended. nowis the latest event: tool name + compact input, or state text. Truncated; hover/focus shows full input.- A blocked row is actionable inline: Allow / Deny / Open. That is the permission broker surface.
- Row click → Session view. Project cell click → Project view.
- Footer filter chips: project, kind, state, "only mine".
View 2 — Project
Shipped as Board: the CLAIMS, worktrees and RESOURCES blocks plus an Incidents section; TASKS ships when the repo declares
[tasks] source(Ready / Open / All, Claim); PROCESSES lists whatswarm serve/swarm procstarted, with Stop; RECENT GATES lists verification runs (latest per gate decides) and the Tasks grid shows ✓ / ✗ / — per declared gate.
Board for one repository: claims, worktrees, resources, gates, and the task list if a task source is configured.
web-app ~/code/web-app main@a1b2c3 · 3 worktrees · 2 live [▶ Run task] [⋯]
CLAIMS RESOURCES
┌──────┬──────────────┬─────────────┬───────────┬────────┐ ┌────────┬────────────┬───────┐
│ task │ holder │ worktree │ lease │ state │ │ name │ holder │ port │
├──────┼──────────────┼─────────────┼───────────┼────────┤ ├────────┼────────────┼───────┤
│ M0.6 │ M0.6 ▶ │ wt/m0.6 │ 38m left │ held │ │ web │ owner ⌨ │ 3000 │
│ M0.4 │ (ended) │ wt/m0.4 ⚠ │ expired │ orphan │ │ web │ M0.6 ▶ │ 3401 │
│ M0.2 │ owner ⌨ │ wt/m0.2 │ 12m left │ held │ │ worker │ — │ — │
└──────┴──────────────┴─────────────┴───────────┴────────┘ │ db │ owner ⌨ │ 54320 │
⚠ wt/m0.4 has 2 uncommitted files and no holder → [inspect] [adopt] [force release] └────────┴────────────┴───────┘
TASKS (docs/planning/52-implementation-plan.md · parsed 3s ago) filter: ⚪ ready ▾
┌──────┬────────────────────────────────────┬──────────────┬──────────┬───────────────┐
│ id │ task │ depends │ status │ gates │
├──────┼────────────────────────────────────┼──────────────┼──────────┼───────────────┤
│ M0.6 │ web: Fleet + Session views │ M0.3 ✅ │ 🟡 held │ review ✓ sec — │
│ M0.7 │ smoke test + dogfood │ M0.4 M0.5 M0.6│ ⚪ ready │ — │
│ M1.1 │ claims + worktrees │ M0 ✅ │ ⚪ ready │ — │
└──────┴────────────────────────────────────┴──────────────┴──────────┴───────────────┘
row actions: [claim] [▶ run] [gates…]
PROCESSES RECENT GATES
pid 48113 web :3401 M0.6 ▶ up 14m [stop] M0.3 review pass 2026-08-19 14:02 "…"
pid 47990 db :54320 owner ⌨ up 3h M0.3 security fail 2026-08-19 13:40 "open port"
M0.3 security pass 2026-08-19 13:58 "fixed"
- Run task opens a drawer: task id (from task source or free text), prompt (prefilled from the task row), model, permission mode, allowed tools, "open in new worktree" (always on). Submit =
swarm run. - Orphaned worktree banner is the single most important affordance in the product: it is where lost work gets found. adopt hands the claim to the current owner session; inspect shows
git status+ diff; force release needs a typed confirmation. - Gates column shows each declared gate with ✓ / ✗ / — and the latest run on hover. A task cannot be marked done from here while any declared gate lacks a pass (M2).
View 3 — Session
The live stream for one session (interactive, spawned, or subagent). This is the "monitor how it's building" screen.
web-app · M0.6 ▶ · claude-opus-5 · wt/m0.6 · task M0.6 · 14m · 92k tok · $0.61 [⏸ pause] [■ stop] [⋯]
┌─────────────────────────────────────────────────────────────────┬────────────────────────────────┐
│ 14:02:11 ▸ user implement M0.6 per docs/06-roadmap.md … │ CONTEXT │
│ 14:02:13 ▸ assistant I'll start by reading the roadmap… │ claim M0.6 · 38m left │
│ 14:02:14 ▸ Read docs/06-roadmap.md ✓ 12ms │ worktree ~/.swarm/wt/web-app/m0.6│
│ 14:02:19 ▸ Bash bun install ✓ 4.1s │ branch task/m0.6 · 3 commits │
│ 14:02:40 ▸ Write packages/web/src/Fleet.tsx ✓ │ resources web:3401 │
│ 14:03:02 ▸ Bash bun run test ✗ 1.2s │ processes 48113 web │
│ │ FAIL Fleet.test.tsx › renders rows (expand) │ │
│ 14:03:05 ▸ assistant The test fails because… │ TURNS 14 · TOOLS 31 │
│ 14:03:08 ▸ Edit packages/web/src/Fleet.tsx ✓ │ Read 9 Bash 8 Edit 7 … │
│ 14:03:12 ▸ Bash rm -rf dist ⚠ waiting │ │
│ │ rule: none matched · asking human [Allow] [Deny] │ DENIED 1 │
│ 14:03:12 ▸ Agent └ sub #2 "find scan report callers" ● │ 14:01 Edit ../api/… │
│ │ shared-tree-readonly │
│ ▍ streaming… │ │
├─────────────────────────────────────────────────────────────────┴────────────────────────────────┤
│ > say something to this agent… [send ⏎] │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
- Left: event log. One line per tool call with status and duration;
assistanttext collapsed to first line, expand on click; subagents render as nested rows, click to open their own Session view. Follows the tail unless you scroll up (then a "↓ 12 new" pill). - Token deltas stream in place when
--include-partial-messagesis on; off by default to keep the log readable. - Right: what the agent holds and what it has been denied — the same
additionalContextthe agent itself receives onSessionStart. - Bottom input exists only for spawned sessions (writes to the run's stdin as a stream-json user message; Stop closes stdin then signals the pid). Interactive sessions have no box — type in your terminal.
- Pause = stop forwarding permission approvals (agent blocks at next ask). Stop = SIGTERM then SIGKILL, release resources, keep the claim as orphaned if dirty.
View 4 — Incidents
Shipped as the Incidents view: a data-grid feed (when, project, session, rule, asked/denied, command, reason, acked) with Open / All chips, per-row Ack and Ack all; the nav badge is the open count. Kinds: rule hits (
denied/asked),orphaned_claim,gate_failed.
Chronological list of things that went wrong or were prevented. Each is one line + ack.
INCIDENTS [ack all visible]
⚠ 14:01 web-app owner ⌨ denied Edit outside worktree (shared-tree-readonly) [ack]
⚠ 13:55 api M0.7 ▶ claim M0.7 expired with dirty worktree → orphaned [open]
⚠ 13:40 web-app M0.3 ▶ gate security failed: "open port in preview" [open]
⚠ 12:10 api — process 47001 (worker) alive but no session owns it [stop] [ack]
Kinds: denied, orphaned, gate-failed, stray-process, port-conflict, daemon-restart. Acked incidents stay searchable.
Shipped views not in the wireframes
- Spend — cost by project, by model and by agent, today and all-time, with a stacked daily-cost chart (7/14/30/90-day range) and a weekday × hour activity heatmap. Per-project when a project is selected, machine-wide from Fleet.
- Stats — the long-horizon numbers: all-time spend / tokens / turns / streak KPIs, a 52-week activity calendar, tokens per day by class, cumulative spend, turns by hour of day, model mix, token composition, tool leaderboard and record holders. Fetched on open from
GET /v1/stats, not part of the live snapshot.swarm stats --jsonprints the same numbers. - Timeline — one lane per session grouped by project, coloured by agent, over a 3–72 h window; answers "who was working when" across every repo.
- PRs — one queue of open pull/merge requests across every tracked repo, read through the locally-authenticated
gh/glabCLIs (GitHub + GitLab, no tokens stored). Shows checks, review state, draft and mergeability; a green row gets a Merge action (squash via the same CLI). Polled gently per project (about every two minutes).
Settings (a page, not a view)
Hook install status per scope (user / project) with install/uninstall buttons, rule toggles per project with the rule's one-line rationale, lease TTL, worktree root, task-source path, model/permission defaults for run, tested Claude Code version range vs installed.
B. CLI
swarm <noun> <verb> with a few top-level shortcuts. Human-readable by default, --json everywhere, exit codes: 0 ok, 1 refused (fail-closed), 2 error. Project resolved from cwd unless -p <name|path>.
Today — exactly what swarm --help prints:
swarm setup start the daemon, install hooks, open the dashboard (do this first)
swarm start | stop | restart manage the background daemon
swarm status [-p] live sessions (whole machine, or one project)
swarm doctor check everything and print the fix for each gap (incl. per-event hook coverage and org-policy overrides, M8.1)
swarm add <path> [--name n] register (pin) a project
swarm ls list projects
swarm ui open the dashboard
swarm tail [--project p] [--session id] follow the live event stream
swarm install | uninstall add/remove Swarm hooks (and the MCP server) in ~/.claude/settings.json
swarm claim <task> [--owner n] claim a task in a fresh isolated git worktree (fail-closed); runs `[worktree]` copy/setup (M7.1)
swarm renew <task> extend the lease
swarm release <task> [--force] release + remove worktree; refuses if dirty or unpushed
swarm claims list claims
swarm reap release abandoned claims (keeps ones holding work)
swarm wt [ls] every worktree with dirty / unpushed / behind / merged (M7.2)
swarm wt create <name> [--base r] task-less worktree under ~/.swarm/worktrees, branch wt/<name>, bootstrapped
swarm wt open|rm <ref> [--force] open via `[worktree] open` / remove (refuses dirty, unpushed, main, held)
swarm wt gc [--apply] stale worktrees (merged branch / released claim); --apply removes the clean ones
swarm wt diff <ref> [--file f] [--patch] what a worktree changed vs the main branch (M7.3)
swarm pr open <task|ref> [--title] [--body] [--draft] [--dry-run] push + open a PR/MR prefilled from task, handoff, gates, files (M7.3)
swarm questions [--all] [--everywhere] · swarm answer <id> <text…> questions agents parked for a human (M7.7)
swarm dispatch --ready | <task…> [--max N] [--parallel N] claim + run per task, cap per project; status | clear (M7.5)
swarm res ls held singletons (project + machine-global)
swarm res acquire <name> [--owner n] [--pid n] [--port n] e.g. web, worker, db, port:3000
pid → alive while the process is; else a lease (default 30m)
port → auto-added to the protected-ports rule while held
swarm res release <name> [--force] refuses if another owner holds it
swarm serve start [--name web] [--from-port 3400] -- <cmd> port-allocating, pid-tracked, PORT set, logs in ~/.swarm/logs
swarm serve ls | stop [name|pid] only processes this project started; by pid + start time
swarm proc start [--name n] -- <cmd> | ls | stop <name|pid> same, without a port
swarm tasks [--ready] the repo's task source
swarm gate record <task> <gate> pass|fail --rubric "…" [--evidence "…"] rubric required; latest run wins
swarm gate ls [task] required gates + verdicts (+ history for one task)
swarm gate run <task> [gate…] execute [gates.<name>] cmd gates in the held worktree, record verdicts (M7.4)
swarm handoff <task> --done "…" --remaining "…" [--files a,b] [--verify "…"] notes for the next holder
swarm resume <task> latest handoff (also injected on SessionStart)
swarm run --task <id> --prompt "…"|--prompt-file f [--model] [--permission-mode] [--allowed-tools a,b] [--max-turns n]
claim + spawn claude -p in the worktree (stream-json both ways)
swarm run ls | send <task|id> "text" | stop <task|id> steer over stdin / stop by pid
swarm run resume <session-id> [--model] [--permission-mode] spawn a run that picks up where a dead session stopped (handoff + tail)
swarm rules dryrun [--set rule=mode,…] [--limit n] replay this repo's history under rule modes; what would fire + flaky signals
swarm search <query…> [-p] [--kind handoff|incident|gate|session] memory over Swarm's own data (never the codebase)
swarm stats [-p] [--json] the Stats view's numbers (totals, per-day classes, records)
Env: SWARM_URL, SWARM_PORT (default 7777), SWARM_HOME (~/.swarm).
Planned — same grammar, not built:
swarm rm <name> unregister a project
swarm wt ls|path <task>|adopt <task>
swarm run attach <session> (use `swarm tail --session <id>` for now)
swarm status sample:
web-app main@a1b2c3 3 live 1 incident
⌨ owner — Read docs/… 2s
▶ M0.6 M0.6 Bash bun run test 41s wt/m0.6 web:3401
○ M0.4 M0.4 ended ok · orphan wt ⚠ 1h
resources: web→owner:3000 web→M0.6:3401 db→owner:54320
C. MCP tools (what agents see)
Server name swarm (stdio, swarm-mcp, registered user-wide by swarm install); project inferred from the server's cwd. Tool names use underscores, as registered. Every tool returns a short human sentence plus a JSON block, and every failure explains who holds the thing and what to do instead.
Today
| Tool | Input | Returns |
|---|---|---|
swarm_status |
{} |
claims, resources, live sessions for this project; your own claim if any |
swarm_claim |
{task, owner?} |
{worktree, branch} or fail-closed {held_by, since, expires_in} |
swarm_renew |
{task} |
new expiry |
swarm_release |
{task, force?} |
ack or `{refused: "dirty" |
swarm_reap |
{} |
expired leases released; dirty ones marked orphaned |
swarm_acquire_resource |
{name, owner?, pid?, port?, leaseMinutes?} |
resource or fail-closed {held_by} |
swarm_release_resource |
{name, owner?, force?} |
ack; refused if another owner holds it unless force |
swarm_resources |
{} |
held singletons for this project (and machine-global ones) |
swarm_handoff |
{task, done, remaining, files?, verify?} |
records a handoff; needs done + remaining |
swarm_search |
{query, kind?, all_projects?, limit?} |
full-text search over Swarm's memory: handoffs, incidents, gates, what sessions said |
swarm_resume |
{task} |
latest handoff, formatted (auto: handoffs are derived by the daemon at Stop/SessionEnd) |
swarm_gate_record |
{task, gate, verdict, rubric, evidence?} |
records a run; rejects a missing rubric; a fail opens an incident |
swarm_gate_run |
{task, gates?} |
executes the repo's [gates.<name>] cmd gates in the task's held worktree and records them; exit 0 = pass (M7.4) |
swarm_context |
{} |
the SessionStart context on demand (holds, lease, handoff, gates, resources, rules) + undelivered answers + open questions (M7.10) |
swarm_ask |
{question, options?} |
parks a question for a human; the answer returns as hook context / stdin / swarm_inbox (M7.7) |
swarm_inbox |
{} |
undelivered answers to this session's questions (M7.7) |
swarm_dispatch |
{tasks?, ready?, max?} |
claim + spawn a run per ready task up to [dispatch] max_parallel; outcome derived from gates + PR on exit (M7.5) |
swarm_pr_open |
{task, title?, body?, draft?} |
pushes the worktree branch and opens a PR/MR via gh/glab, drafted from task + handoff + gates + files; refuses dirty (M7.3) |
swarm_gates |
{task?} |
required gates (.swarm.toml [gates]) and the latest verdict per gate |
swarm_next_task |
{all?} |
first unclaimed task whose dependencies are done (needs [tasks] source); all lists every ready task |
Planned
| Tool | Input | Returns |
|---|---|---|
swarm_note |
{text} |
attaches a note to the session, visible in the dashboard |
swarm_permission |
(internal, --permission-prompt-tool) |
allow/deny from rules or human |
Context injection (shipped on SessionStart, built by store.sessionContext(cwd); per-prompt injection not built) — the hook returns additionalContext like:
[swarm] project web-app · you hold M0.6 (38m left) in ~/.swarm/wt/web-app/m0.6 · resources: web:3401
[swarm] rules: shared-tree-readonly, no-pattern-kill, claim-required-to-write
[swarm] handoff from previous holder: done=…, remaining=…, verify=…
D. HTTP routes (what the daemon serves today)
Everything above is a thin wrapper over these. Bound to 127.0.0.1 only; port discovered via ~/.swarm/daemon.json (SWARM_URL overrides). Source of truth: packages/daemon/src/app.ts.
| Route | Purpose |
|---|---|
GET /v1/health |
liveness + version |
GET /v1/state |
the dashboard snapshot: projects, sessions, claims, worktrees, resources, incidents, spend rollups |
GET /v1/stats?project= |
Stats view numbers (swarm stats) |
GET /v1/projects · POST /v1/projects · PATCH /v1/projects/:id · DELETE /v1/projects/:id |
register (pin) / unpin a project; PATCH takes {name?, pinned?, icon?, color?} — project settings (sidebar menu → Settings…): icon is an emoji / ≤ 4-char glyph, or a small image as a data:image/png URL (the drawer downsizes any image file to 64 px; ≤ 24 KB), shown instead of the folder icon, color a categorical slot c1…c7 (a design token) tinting the project glyph everywhere; POST with {path} is idempotent and returns the project id |
PUT /v1/projects/order |
{ids} in sidebar order — persists the manual order of pinned projects (order; unordered ones follow alphabetically) |
GET /v1/fs/ls?path= |
directory listing for the dashboard's "add folder" picker (directories only) |
GET /v1/claims · POST /v1/claims · POST /v1/claims/renew · POST /v1/claims/release · POST /v1/claims/reap |
the claim ledger (fail-closed; release refuses dirty/unpushed unless force) |
GET /v1/worktrees?project= · POST /v1/worktrees · POST /v1/worktrees/remove · POST /v1/worktrees/open · POST /v1/worktrees/gc |
first-class worktrees (M7.2): fresh listing with behind/merged; create task-less (name, baseRef, branch); remove (worktree = path, folder name or branch; force); open on the desktop; gc (apply) |
GET /v1/resources?project= · POST /v1/resources · DELETE /v1/resources/:name |
runtime-resource singletons (acquire is 201 or 409 with who holds it) |
GET /v1/incidents?limit= |
recent rule hits (incident.opened) |
GET /v1/budget?project= |
the project's [budget] status (level, deciding ceiling, spent, pct) and config (0.7.0) |
GET /v1/context?cwd=&session= |
the SessionStart context for a cwd, refreshable; delivers pending answers to session (M7.10) |
GET /v1/questions?project=&session=&open=1 · POST /v1/questions · POST /v1/questions/:id/answer · GET /v1/inbox?session=[&peek=1] |
ask-the-human (M7.7): messages table; answer also writes to a live spawned run's stdin; inbox returns undelivered answers and marks them delivered unless peek |
GET /v1/dispatch?project= · POST /v1/dispatch · DELETE /v1/dispatch |
dispatch queue per project (entries with state/outcome/detail/cost + config); POST {projectId, tasks?|ready, max?, maxParallel?, permissionMode?, model?, maxTurns?}; DELETE clears queued/finished (M7.5) |
GET /v1/worktrees/diff?project=&worktree=[&file=|&patch=1] |
commits + files (incl. working tree and untracked) vs the merge-base with the main checkout's branch; file/patch add a unified diff (M7.3) |
GET /v1/prs/draft?project=&worktree=|task= · POST /v1/prs/open |
PR title/body drafted from task, handoff, gates, files; open {projectId, worktree|task, title?, body?, draft?} pushes and runs gh pr create / glab mr create, refuses dirty, reuses an open PR (M7.3) |
GET /v1/gates?project=&task= · POST /v1/gates · POST /v1/gates/run |
gate runs (latest wins) and required/executable gate names; run {projectId, task, gates?, wait?} executes [gates.<name>] cmd gates sequentially in the held worktree via the process registry and records them (M7.4) |
GET /v1/memory?q=&project=&kind=&task=&limit= |
full-text search (FTS5/BM25) over handoffs, incidents, gates and session text; hits carry a snippet with \u0001/\u0002 around matches |
| Auth (M8.2b) | every /v1/* route except health accepts Authorization: Bearer <~/.swarm/token> (or ?token= for SSE); required for non-loopback callers and, with [daemon] auth = "required", for everyone; a wrong token is always 401. swarm ui opens the dashboard with the token; GET /v1/health reports auth |
GET /v1/audit?since=30d|ISO&project=&type=&format=json|jsonl|csv&limit= |
the audit log (M8.2c): ledger changes, rule decisions, human answers, incidents — seq, ts, type, projectId, sessionId, actorKind, actorId, summary, payload; never raw |
GET /v1/policy?project= |
org policy for a project (M8.1): path, locked, provenance (key → default|policy|global|repo), overridden (locked keys a lower layer tried to set) |
GET /v1/rules/dryrun?project=&<rule>=ask|deny|off&limit= |
replay recorded tool calls under (overridden) rule modes: per-rule ask/deny counts, hits, flaky signals; records nothing |
GET /v1/sessions/:id/resume · POST /v1/sessions/:id/resume |
the resume plan for a session (task, owner, prompt built from its latest handoff + last actions) / spawn a run from it (RunInput overrides accepted) |
GET /v1/prs · POST /v1/prs/merge |
the forge queue ({projectId, number} to squash-merge via gh / glab) |
GET /v1/pricing · POST /v1/pricing/refresh |
the price table and its LiteLLM refresh |
GET /v1/sessions/:id/events?after=&afterTs= · POST /v1/sessions/:id/tail |
one session's last 500 events + turns (wire shape; incremental with after/afterTs); force a transcript re-tail |
GET /v1/events/:seq |
one stored event in full: clipped tool I/O in payload, upstream hook input in raw |
GET /v1/spend |
the spend rollup on its own (also inside /v1/state) |
POST /v1/hook/:event |
hook ingestion; on PreToolUse returns the rule decision (permissionDecision ask / deny) |
POST /v1/events · GET /v1/events?since=&full= |
append a normalized event (smoke/tests); SSE stream replayable by seq (wire shape — no raw/tool I/O unless full=1; since=0 replays the last 200) |
GET / · `GET /:file.(js |
css)` |
Not built: a unix socket. Stdin for spawned runs is POST /v1/runs/:id/send.
E. Design constraints shared by all three
- Same nouns, same verbs, same error texts. A denial in the session log, the CLI and the MCP result is the same sentence.
- Nothing destructive without a typed confirmation in the UI or
--forcein the CLI; both are logged as incidents. - Every list is filterable by project and copyable as JSON.
- The dashboard never invents state the CLI can't show: if you can't get it from
swarm status --json, it doesn't belong in the UI.
Source: docs/08-interface.md · v0.8.0.