@getspur/spur-graph 1.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ /node_modules
2
+
package/CHANGELOG.md ADDED
@@ -0,0 +1,260 @@
1
+ ## Unreleased
2
+
3
+ ### Added
4
+ - **`spur-graph` content-hash invalidation substrate (bd-jvers, v2.1).**
5
+ Replaces mtime/size per-file invalidation with a filtered-content hash
6
+ over `(path, content_oid)` pairs, where `content_oid` is the git blob OID
7
+ (from `git ls-files -s` for clean files; locally computed via
8
+ `sha1("blob " || decimal(size) || "\0" || bytes)` for dirty / untracked /
9
+ fs-mode files; `gitlink:<oid>` for submodules). Cache key derivation
10
+ reads only the git index + dirty file bytes — does not depend on `HEAD`.
11
+ - **Canonical artifact** at `<git_common_dir>/spur-graph/artifacts/<manifest_version>/<graph_content_hash>.json`
12
+ (immutable per key, content-addressed).
13
+ - **Per-worktree** `.spur/graph-index.json` retained as a full artifact,
14
+ hardlinked to the canonical (with `fs::copy` fallback across filesystems).
15
+ Existing direct consumers (`crates/spur-tui/src/mentions/code_graph/source.rs`)
16
+ are source-compatible — no API changes.
17
+ - **Optional pointer sidecar** at `.spur/graph-index.pointer.json` carries
18
+ provenance (`indexed_commit_oid`, `source_kind`, `manifest_version`).
19
+ - **Bucket reuse.** Within a worktree, unchanged paths (same `content_oid`
20
+ as the previous artifact) are cloned wholesale — no tree-sitter parsing
21
+ or symbol extraction. This is the per-build extraction-saving mechanism.
22
+ - **Cross-worktree dedup.** Two worktrees with identical filtered content
23
+ derive the same canonical key regardless of commit history, so the
24
+ second writer skips the canonical JSON write and hardlinks the existing
25
+ artifact. (Each worktree still computes its own discovery + hash;
26
+ pre-extraction shortcircuit is tracked as a followup, bd-5yful.)
27
+ - **Dirty == committed (I2).** A worktree's dirty `content_oid` equals
28
+ the blob OID git would assign if the file were committed, so
29
+ `graph_content_hash` before and after committing identical bytes
30
+ matches and every bucket is reused on the next build.
31
+ - `fs2` exclusive lock with 5s timeout + worktree-only fallback on
32
+ contention; atomic write via tmp + rename + best-effort `fsync_dir`.
33
+ - Schema bumped to `spur-graph-schema-v4`: `GraphFileManifestEntry` swaps
34
+ `mtime_nanos`/`size_bytes` for `content_oid`; `GraphIndexArtifact` gains
35
+ `graph_content_hash` and value-level `tombstones` (no commit OID).
36
+ - Design spec: `docs/architecture/spur-graph-git-invalidation.md`.
37
+ - **`spur-graph` benchmark gate.** Criterion bench at
38
+ `crates/spur-graph/benches/incremental.rs` covers four scenarios:
39
+ clean cold (cache miss), clean warm (cache hit via hardlink), 1k-file
40
+ change set over baseline, and dirty unstaged mods overlay. Env-knob
41
+ scaling via `BENCH_FILE_COUNT` / `BENCH_CHANGE_SET` / `BENCH_DIRTY_MODS`.
42
+
43
+ ### Fixed
44
+ - **`spur-graph` cache lock-timeout no longer leaves a ghost pointer**
45
+ (bd-jvers followup). On `fs2` lock contention, `write_with_dedup`
46
+ previously wrote the worktree artifact and ALSO wrote a pointer sidecar
47
+ whose `canonical_artifact_path` referenced a canonical file that was
48
+ never created — any pointer-following reader hit a deterministic
49
+ missing-file failure. Fix: on lock-timeout, write the worktree artifact
50
+ (still authoritative) and remove any pre-existing pointer so it cannot
51
+ be stale. Convergent finding from claude-code and kimi reviews. Test
52
+ `lock_timeout_writes_worktree_only` extended to stage a stale pointer
53
+ before the lock-contended write and assert no pointer exists after.
54
+
55
+ - **Experimental TUI Insights view (`--features analytics`, default OFF).**
56
+ `Alt+a` from any view opens a 4-tab Insights surface (Overview / Timeline /
57
+ Breakdown / Live) backed by `spur-context::AnalyticsEngine`'s DuckDB store.
58
+ - Overview: KPI cards (Today / 7d / 30d / Cache hit), 7d cost sparkline,
59
+ cost-provenance gauge (native / priced / unpriced), top-3 agent / model /
60
+ project lists.
61
+ - Timeline: BarChart with `D`/`W`/`M` granularity toggle (no re-query).
62
+ - Breakdown: Pivot table over Agent / Model / Project (`A` / `M` / `P`).
63
+ - Live: per-session burn rate, hourly projection, refresh interval drops to
64
+ 5s while this tab is active (60s otherwise).
65
+ Refresh is driven by an async `tokio::spawn` task with a signal channel and
66
+ 30s timeout; the view never blocks the UI thread.
67
+ - **Five well-known agents are first-class in analytics**: Claude Code,
68
+ Codex, Gemini, OpenCode, Kimi. Kiro remains a Phase-2 stub
69
+ (no token data via filesystem; ACP `UsageUpdate` capture pending).
70
+ - **`spur-context` Gemini extractor (R4).** Parses `~/.gemini/tmp/<uuid>/chats/session-*.json`
71
+ with `tokens.{input,output,cached,thoughts,tool}` folding. Wires into
72
+ `AnalyticsEngine` alongside existing Claude / Codex / OpenCode / Kimi paths.
73
+ - **OpenCode model-prefix strip (R1).** `anthropic/claude-opus-4-5` is now
74
+ stored as `claude-opus-4-5` so the pricing registry's `LIKE` matcher works.
75
+ Eliminates spurious `cost_source='unpriced'` rows for OpenCode users.
76
+ - **Kimi `kimi-for-coding` pricing (R2).** Registered at $0.60 input /
77
+ $2.50 output / $0.15 cache-read per million tokens. Kimi events now surface
78
+ with correct `cost_source='priced'` instead of `'unpriced'`.
79
+ - **OpenCode SQLite mtime in cache staleness (R3).** `newest_agent_mtime`
80
+ now includes `~/.local/share/opencode/opencode.db`, fixing permanently-stale
81
+ cache for OpenCode-only users.
82
+ - **Dashboard cost-source switch (when feature on).** Dashboard's total cost
83
+ reads from a periodically-refreshed `LiveCostCache` populated via
84
+ `AnalyticsEngine::live_session_snapshot` instead of `ExecutorLineage`. The
85
+ status bar shows a `via analytics` pill while the cache has data for the
86
+ active session. Single source of truth for cost when feature is enabled.
87
+ - **CI matrix for `spur-tui`.** New workflow runs
88
+ `cargo check + cargo test --lib` for `--no-default-features`, default, and
89
+ `--features analytics` configurations on every PR and `main` push.
90
+ - **P0 cost-correctness fixes harvested onto this branch.**
91
+ - P0.1: Claude event dedup on `(sessionId, requestId, message.id)`.
92
+ - P0.2/3/4: `cost_source` column + Codex cache double-count fix.
93
+ - P0.6: `include_str!` per-report SQL with synced cache columns.
94
+ - P0.8: `SessionRow::models` aggregates across mid-session model switches.
95
+
96
+ ### Changed
97
+ - **Read-only future metadata saves now fail visibly.** When
98
+ `.spur/session_metadata.json` was written by a future SPUR version,
99
+ `SessionMetadataStore::save()` now returns `Err(ReadOnlyFutureSchema)`
100
+ instead of silently accepting and discarding in-session edits. App
101
+ save paths route through `App::persist_metadata`, which surfaces the
102
+ refusal as a dismissible warning banner.
103
+ - **Peer mailbox reconciler now emits `WorkerPeerMessageAuditFailed`** on
104
+ non-terminal transition errors during startup reconciliation. The
105
+ `WorkerPeerMailboxReconciled.audit_failed_emitted` counter, which was
106
+ always `0` prior to this release, may now report non-zero values when
107
+ the persistent ledger introduces transition failures. Dashboards and
108
+ alerts that filter `audit_failed_emitted == 0` as a "no anomalies"
109
+ signal should switch to alerting on the
110
+ `WorkerPeerMessageAuditFailed { transition_kind: "reconcile_to_delivered" }`
111
+ event directly. (bd-cpf.5b)
112
+
113
+ ### Added
114
+ - **Tolerant input-history deserialize.** A malformed `ProtectedRange` or
115
+ `InputHistoryEntry` in `.spur/session_metadata.json` no longer aborts
116
+ the entire load: bad rows are skipped with `tracing::warn!`, while
117
+ remaining valid history is preserved.
118
+ - **`schema_version` field on `SessionMetadata`.** Persisted as the
119
+ on-disk JSON key `"version"` for backward compatibility. Files written
120
+ by a future SPUR version are loaded read-only; in-memory mutations are
121
+ not persisted until SPUR understands that schema.
122
+ - **Read-only mode banner.** Future-version metadata now shows a top-row
123
+ warning at first paint and after every refused save attempt. `Esc`
124
+ dismisses the current banner without clearing read-only mode.
125
+ - **`SessionMetadataStore::is_read_only()` getter.** Callers can poll
126
+ whether metadata is in read-only mode before enabling write-oriented UI.
127
+ - **Worker heartbeat watchdog configuration.** New `[worktree]` config keys:
128
+ `worker_heartbeat_watchdog_enabled` (bool, default `false`),
129
+ `worker_heartbeat_timeout_secs` (u64, default `90`),
130
+ `worker_heartbeat_initial_grace_secs` (u64, default `60`). See
131
+ `docs/architecture.md` Risk #23 for operational guidance and the
132
+ no-runtime-toggle rollback constraint. (bd-arch.23)
133
+ - **`DelegationAbortReason` enum** distinguishing `BrainRequested` from
134
+ `WorkerHeartbeatTimeout`. Stage-2 will extend with `ResourceLimitExceeded`
135
+ / `SandboxTerminated` for cgroup-based termination. (bd-arch.23)
136
+ - **Peer mailbox production wire-up (Stage-1).** The peer mailbox subsystem
137
+ (hardened in bd-cpf.1–7) is now constructed and attached when
138
+ `peer_mailbox_enabled = true` is set in config. A long-lived reconciler
139
+ task drains stranded peer messages and emits audit events. Startup
140
+ reconcile runs at brain session boundaries. Default is `false`; no
141
+ behavioral change for existing deployments. Operators who opt in should
142
+ monitor for `WorkerPeerMessageUndeliverable` events and be aware that
143
+ the in-memory ledger does not prune entries (Risk #22). To disable,
144
+ set `peer_mailbox_enabled = false` and restart SPUR — runtime toggle
145
+ is not supported. (bd-arch.21)
146
+ - **Peer mailbox drain lifecycle events.** `WorkerPeerMessageDrainStarted`
147
+ and `WorkerPeerMessageDrainTimedOut` add symmetric observability to the
148
+ post-prompt ack drain. `DrainStarted` carries the candidate-set size
149
+ and the cap/quiet-window limits in effect; `DrainTimedOut` carries the
150
+ same payload shape as `WorkerPeerMessageDrainCappedOut` plus
151
+ `quiet_window_ms`, so dashboards can reuse panel queries across both
152
+ exit events. `DrainTimedOut` is emitted only when the quiet-window
153
+ exit leaves remaining non-terminal messages; clean-exit drains
154
+ (`remaining_messages == 0`) emit no exit event. Diagnostic-only —
155
+ message loss continues to be tracked per-message via
156
+ `WorkerPeerMessageIgnored`. (bd-cpf.7)
157
+ - **`WorkerPeerMailboxReconciled.inflight_already_delivered` counter.**
158
+ Tracks benign idempotent races during startup reconciliation where an
159
+ entry was already in `Delivered` state when the reconciler attempted
160
+ to advance it. Always 0 in Stage-1; becomes non-zero under Stage-2
161
+ crash-loop or concurrent-reconcile scenarios. (bd-cpf.5c)
162
+ - **Spur Way skill bundle.** Six bundled skills harden brain-worker-beads
163
+ collaboration: `spur-way` (beads-first invariant), `beads-lifecycle`
164
+ (status state machine), `worker-signals` (`[[spur-signal v1]]` protocol),
165
+ `brain-review-gate` (beads-aware approval checklist), `plan-task-discipline`
166
+ (DAG integrity rules), and `worker-mention-routing` (user `@`-mention
167
+ overrides algorithmic selection). All compile into `spur-core` via
168
+ `include_str!` and render across the seven agent adapters. See
169
+ `docs/superpowers/specs/2026-04-22-superpower-skill-hardening-spur-way.md`.
170
+ - **Role-gated skill rendering + Kimi adapter.** Skills now carry a `role`
171
+ field (`brain | worker | both`). Brain-only skills (`brain-review-gate`,
172
+ `brain-delegation`) no longer leak into worker agent directories. Worker
173
+ skills (`test-driven-development`, `systematic-debugging`) are tagged
174
+ explicitly. New `Adapter::Kimi` renders to `.kimi/skills/`, closing the
175
+ gap where Kimi workers accidentally relied on `.claude/skills/` fallback.
176
+ See `docs/superpowers/specs/2026-04-22-multi-agent-skill-embedding-research.md`.
177
+
178
+ ### Fixed
179
+ - **Risk #4 (worktree orphaning under unclean shutdown).** New
180
+ `WorktreeAuthority` actor sweeps dead-session worktrees safely under
181
+ multi-process operation. Branch namespace migrated to
182
+ `spur/worker/v2/{agent}/{brain_session_id}/{worker_session_id}` so
183
+ sweep enumeration can be precisely scoped to v2 worktrees. Pre-v2
184
+ branches are NOT auto-cleaned; operators reclaim legacy debt via the
185
+ separate `spur-worktree-gc-legacy.sh` script (deferred). The actor
186
+ uses a `SessionLivenessProbe` over advisory `flock(2)` and a
187
+ `SelfHeldSet` to skip self-owned sessions. See
188
+ `docs/superpowers/specs/2026-04-26-worktree-authority-design.md` for
189
+ the full invariants I-1..I-7.
190
+ - **Worker child processes now die with their orchestrator** via
191
+ `kill_on_drop(true)` on the `tokio::process::Command` spawn paths in
192
+ `crates/spur-acp/src/connection/{native,stdio_adapter,cli_wrap_adapter,stream_json_adapter}.rs`.
193
+ Closes Risk #4's hard prerequisite.
194
+ - **`spur-bot/tests/runtime_flow.rs` compile errors absorbed.** 12
195
+ inline `AgentSessionReady` event initializers were missing the
196
+ `fs_unsafe: bool` field added in the single-attach invariant work
197
+ (commit `84e91895`). Fixed inline so the workspace smoke test gate
198
+ passes. This is cross-stream cleanup — not part of the
199
+ WorktreeAuthority design but required for plan closure.
200
+ - **Architecture Risk #23 (semaphore indefinite wait).** Permit acquire is now
201
+ cancellable: `cancel_delegation` arriving while a task is queued for a
202
+ permit short-circuits immediately without acquiring. A heartbeat-based
203
+ watchdog (default-off) detects silent worker hangs and releases the held
204
+ permit after `worker_heartbeat_timeout_secs` (default 90s, configurable).
205
+ Watchdog is gated behind `worker_heartbeat_watchdog_enabled` (default
206
+ `false`) until a v1 `_spur/heartbeat` emitter lands; operators may opt
207
+ in early if their workers emit heartbeats. Watchdog firings map to
208
+ `DelegationStatus::Timeout`, preserving the `Timeout` (worker-hang)
209
+ vs `TimedOut` (review-gate) semantic split. Brain-initiated cancellations
210
+ continue to map to `DelegationStatus::Cancelled`. (bd-arch.23)
211
+ - **Architecture Risk #21.** The peer mailbox reconciler is now spawned
212
+ at orchestrator boot and aborted on shutdown via `Orchestrator::drop`.
213
+ Previously the receiver was dropped immediately after construction,
214
+ causing stranded messages to be silently lost — but the surrounding
215
+ wire-up was also missing in production, so the entire subsystem (62
216
+ tests, bd-cpf.1–7 hardening) was inert. (bd-arch.21)
217
+
218
+ ## v1.1.8 — 2026-05-13
219
+
220
+ Spur 1.1.8 ships fresh defaults for the agents you actually run and makes two long-standing rough edges disappear: `/model` now feels instant on every agent, and long GitHub fetches stop looking like a hang.
221
+
222
+ - **Fresh out-of-the-box agent versions.** `spur init` now seeds new repos with `claude-agent-acp 0.33.1` (up from 0.26.0) and `codex-acp 0.14.0` (up from 0.11.1). New users get the latest ACP features and fixes on first run — no manual version-pinning, no stale prompts about deprecated flags. Existing `.spur/config.toml` files are preserved as-is; bump the pinned versions there when you're ready.
223
+ - **`/model` feels instant on every agent.** Some agents (including older Claude Code and Kimi builds) don't emit a `config_option_update` after a model switch, so the status bar used to keep showing the previous model until you reconnected. Spur now applies an optimistic override the moment you pick a model — the label flips immediately and reconciles with the agent's confirmation when it arrives.
224
+ - **GitHub ingest shows live progress.** `spur pm` ingesting a large GitHub repo's issues used to look frozen for minutes on the first run. The PM layer now surfaces per-page progress as fetches stream in, so you can see it working and estimate how much longer it'll take instead of wondering whether to kill it.
225
+
226
+ ## v1.1.7 — 2026-05-13
227
+
228
+ Spur 1.1.7 makes everyday agent-switching and navigation feel right. Changing models works everywhere, the picker puts what you actually want at the top, and the code graph is there when you need it without any setup.
229
+
230
+ - **`/model` now works on every supported agent.** Switching models in Gemini CLI and Kimi CLI was broken before — both now behave like Codex, Claude Code, and OpenCode. Pick a model, get that model.
231
+ - **Status bar reflects the model you're actually on.** After a `/model` switch, the label updates live instead of showing the old name until you restart the session.
232
+ - **Smarter `@mention` picker.** Files no longer get buried under issues and workers. Results are grouped under clear section headers and ordered so the thing you're most likely reaching for is at the top.
233
+ - **Code graph just works.** Spur now auto-discovers the project's code graph at the worktree root — no environment variable to set, no "run `spur graph build`" hint when the graph is already there. Rebuilds are also faster and symbol names stay stable across runs, so jumping between files is more reliable.
234
+
235
+ ## v0.4.5 — 2026-04-19
236
+
237
+ Spur 0.4.5 focuses on getting around faster and trusting what you see. A new universal palette (Ctrl+K) jumps you between sessions, workers, traces, and commands from anywhere. The `@mention` and `/slash` pickers now share one consistent interface. Agent output renders markdown and mermaid diagrams inline, and streaming stays smooth under bursty traffic.
238
+
239
+ ### Added
240
+ - **Universal palette (Ctrl+K).** Fuzzy-search and jump to any session, worker, trace entry, or command from anywhere in the TUI. A `[Ctrl+K: go]` badge in the status bar reminds you it's there.
241
+ - **Unified completion for `@mention` and `/slash`.** Both now flow through the same picker — same keys, same preview, same ranking. One interface to learn.
242
+ - **Rerun recent prompts with Ctrl+R.** Session picker surfaces your recent prompts; pick one to rerun against the current session.
243
+ - **Auto-resume landing.** Launching spur drops you straight back on the last session you were working in.
244
+ - **Markdown and mermaid in agent output.** Rich content renders inline in the trace view — no more raw source for formatted replies or diagrams.
245
+ - **Dashboard view.** New top-level view for at-a-glance status across sessions and workers.
246
+ - **Skills installer across adapters.** `.spur/skills/` installs to Cursor, Codex, Kiro, Gemini, and OpenCode in one step, with edits you own preserved on upgrade.
247
+ - **Delegation visibility in the timeline.** Delegation plans now appear in the TUI timeline as they happen; `list_available_workers` returns richer descriptors (tier, cost, suitability hints).
248
+
249
+ ### Improved
250
+ - **Smoother streaming.** A new scroll anchor and per-frame drain cap keep long, bursty outputs readable instead of jittery.
251
+ - **Faster palette and pickers.** Single-pass reranking and cached search patterns.
252
+ - **Session metadata persists across restarts.**
253
+
254
+ ### Preview features (opt-in)
255
+ - **Brain delegation framework.** A new orchestration model for deciding which worker handles a task. Opt in via your config:
256
+ ```toml
257
+ [brain.delegation]
258
+ framework = "v1"
259
+ ```
260
+ Release builds default to `legacy` for 0.4.5; the v1 framework will become default once it stabilizes.
package/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # SPUR
2
+
3
+ **The control tower for your CLI coding agents.**
4
+
5
+ > Issue in, PR out — across every agent, in parallel, with one review surface.
6
+
7
+ SPUR is a Rust-native terminal layer that sits above the AI coding agents you already run — Claude Code, Codex, Gemini, Kimi, OpenCode, or any [ACP](https://github.com/anthropics/agent-client-protocol)-speaking agent. A "brain" reasons about your task and delegates to one or more "workers." Each worker runs in its own isolated git worktree. SPUR coordinates dispatch, human review, cherry-pick, retries, cross-vendor cost, and project-management state in one place.
8
+
9
+ > ⚠️ **Early stage** — APIs and config format may change.
10
+
11
+ ## The Problem
12
+
13
+ Running 2+ AI coding agents today means living with four frustrations:
14
+
15
+ - **Rate-limit ambush.** Claude Code Max users blow through 5-hour windows in under 90 minutes. *"Paying $200 a month, I hit my weekly in 3 days last week"* (esperent, HN 47626833). When the lockout hits, the in-flight context is gone.
16
+ - **Cost opacity.** Tokens accrue across five vendors with no single ledger. *"I'm paying for Max, and when I use the tooling to calculate the spend returned by the API, I can see it's almost $1k!"* (buremba, HN 44598254). Devs only discover the gap by reverse-engineering their own bill.
17
+ - **Multi-agent chaos.** *"I run 5–10 Claude Code agents at a time across different repos. Keeping track of which one is waiting for input, which one is working, and which one broke something was chaos. I needed a control tower"* (Beefin, HN 47104424).
18
+ - **Worktree merge tax.** DIY tmux + worktrees is the workaround. *"I expected this to become less necessary over time as models got faster, but the opposite has happened"* (nojs, HN 47573483).
19
+
20
+ SPUR addresses all four: cross-vendor brain-swap when one vendor rate-limits you, a unified live cost ledger across every CLI you pay for, one review surface for parallel workers, and DAG-ordered cherry-pick of approved diffs onto a staging branch.
21
+
22
+ ## How It Works
23
+
24
+ ```
25
+ ┌─────────────────────┐ ┌──────────────────────────────────┐ ┌─────────────────────┐
26
+ │ Project Management │ │ SPUR TUI │ │ Worker Agents │
27
+ │ │ │ │ │ │
28
+ │ beads (native) │◄───►│ ┌────────────────────────────┐ │ │ Claude Code │
29
+ │ GitHub Issues │ │ │ Brain Agent (orchestrator) │──┼────►│ Codex │
30
+ │ │ │ └────────────────────────────┘ │ │ Gemini │
31
+ │ │ │ ┌─────────┬────────┬─────────┐ │ │ Kimi │
32
+ │ │ │ │Dashboard│Insights│Plan View│ │ │ OpenCode │
33
+ │ │ │ └─────────┴────────┴─────────┘ │ │ Kiro (partial) │
34
+ └─────────────────────┘ └──────────────────────────────────┘ │ Generic ACP │
35
+ ▲ └─────────────────────┘
36
+ │ ACP (JSON-RPC 2.0 / stdio)
37
+
38
+ ┌──────────────────────────────────┐
39
+ │ Git Worktrees per Worker │
40
+ │ DuckDB Analytics Engine │
41
+ │ Beads Plan Store + Reconciler │
42
+ │ MCP Server (delegation tools) │
43
+ └──────────────────────────────────┘
44
+ ```
45
+
46
+ ## Capabilities
47
+
48
+ *Ordered by uniqueness — what no single-vendor or cloud peer can match by design.*
49
+
50
+ - **Unified cost ledger across every vendor.** Five live extractors (Claude, Codex, Gemini, OpenCode, Kimi) feeding a DuckDB analytics engine that reads vendor JSONL/SQLite in place — no ETL. The Insights view (`Alt+a`) shows 7-day cost sparklines, per-session burn rate, and a `cost_source` provenance gauge. Devin, Cosine, Cursor, Aider, and Claude Code each only see their own bill; SPUR sees all of them in one number.
51
+ - **Brain-swap across vendors mid-flow.** Hit a Claude rate limit → keep working on Codex → come back to Claude when the window resets. Per-agent capability negotiation; `/model` and `/effort` synthesized from each agent's `InitializeResponse`. Impossible inside any single-vendor tool.
52
+ - **Session resume via event replay.** Close the laptop, restart SPUR, the brain picks up exactly where it left off. Not a soft-reconnect — a full replay from an event-sourced log.
53
+ - **Local-first durability.** Plans persist as beads epics in SQLite, events as NDJSON, outcomes as content-addressed git blobs. Survives crashes, OS updates, and network outages — resilience cloud-only competitors cannot match without offline sync.
54
+ - **Review loop with Reflexion retry.** Every worker completion produces a review card — Approve / Reject / Modify / Retry. Retries feed prior attempts back as context (max 3, auto-reject on exhaustion). A first-class state machine, not a UI convenience.
55
+ - **Parallel delegation, isolated.** `delegate_to_worker`, `delegate_parallel`, and DAG-ordered `submit_plan` dispatch workers concurrently. Each gets its own git worktree on `spur/worker/v2/{agent}/...`, protected by advisory `flock` liveness probes and an orphan-collector.
56
+ - **Cherry-pick in DAG order.** Approved subtask commits are cherry-picked in topological order onto a staging branch. Collapses the post-worktree merge tax that DIY tmux+worktree users hit every day.
57
+ - **Telegram bot on the same state machine.** `spur-bot` mirrors review cards and session streams into Telegram forum topics. Same review lane, same event bus, same approval semantics as the TUI — review on the train, not just at the desk.
58
+ - **Native ACP + MCP dual channel.** Structured JSON-RPC, not PTY scraping or prompt injection. Any ACP-speaking agent works out of the box.
59
+ - **Durable plan mutations.** `submit_plan_mutation` supports split / replace / amend in-flight; the reconciler ticks adaptively, validates ownership, and previews overlay conflicts.
60
+ - **Multi-brain safety.** Plans are locked to a single active brain via a `spur:plan-owner:<id>` label. Tier-1 mutating tools fail on session mismatch. `force_reclaim_plan` breaks deadlocks when a brain wedges.
61
+ - **Structured delegation reasoning.** Brains pass a `DelegationPlan` (candidates, rationale, decomposition) with every dispatch. SPUR verifies the chosen agent matches the actual payload — preventing the LLM from lying to its own reviewer.
62
+ - **Stable-identity code graph.** Tree-sitter parses Rust / Python / TS / TSX / Markdown into a graph with SHA256 stable symbol IDs and incremental per-file rebuilds. Auto-discovered at the worktree root — no env var setup.
63
+ - **Built for orchestration, not chat.** Dashboard with lineage tree, full-screen ReAct trace with inline markdown + mermaid, grouped `@mention` picker (Workers / Files / Issues / Code Graph), `Ctrl+K` universal palette, Plan Inspector (`Alt+P`), live status bar (running count, pending reviews, total cost, model labels).
64
+
65
+ ## What SPUR is NOT
66
+
67
+ SPUR is built to sit *above* the agents and tools you already use. We don't try to replace them — and several excellent products own jobs SPUR explicitly does not compete on:
68
+
69
+ - **Not a Devin replacement.** Devin owns "autonomous engineer I can assign tickets to in Slack." SPUR keeps the human in the loop on purpose — review is a state machine, not a slogan. If you want a fully autonomous agent, hire Devin.
70
+ - **Not a Cursor competitor.** Cursor owns the AI IDE. SPUR sits *next to* your editor, not inside it. Most SPUR users keep Cursor open in another window.
71
+ - **Not an Aider replacement.** Aider owns the simplest free BYO-key single-agent CLI. SPUR's value materializes at fleet size ≥ 2 — "add SPUR above Aider," not "switch from Aider." Aider-as-a-SPUR-worker is on the roadmap.
72
+ - **Not a Claude Code replacement.** SPUR uses Claude Code as a worker. We don't try to beat the in-session UX; we add what Claude Code doesn't attempt — cross-session durability, cross-vendor failover, and a cost ledger that spans every CLI.
73
+ - **Not autonomous.** Review gate is required by design. If you want a "set and forget" system, this is the wrong tool.
74
+
75
+ And on the boring scope side:
76
+
77
+ - Not a chat UI. Every interaction is task-structured.
78
+ - Not an IDE. No editor, no LSP, no inline diff gutter.
79
+ - Not CI/CD. Plans run locally with local worktrees, not on remote runners.
80
+ - Not a universal PM sync. Only beads (native) and GitHub (via `gh`) are implemented today.
81
+
82
+ ## Crate Structure
83
+
84
+ | Crate | Purpose |
85
+ |---|---|
86
+ | `spur-cli` | Binary entry point and CLI commands |
87
+ | `spur-tui` | `ratatui` terminal interface — views, components, input handling |
88
+ | `spur-core` | Orchestration engine, review loop, lineage, event pipeline |
89
+ | `spur-acp` | ACP client, transports (stdio / native / cli-wrap), capability negotiation |
90
+ | `spur-mcp` | MCP server exposing delegation tools to the brain (delegate, plan, signals) |
91
+ | `spur-context` | DuckDB analytics engine + per-agent log extractors |
92
+ | `spur-cost` | Pricing registry + SQLite session ledger |
93
+ | `spur-graph` | Tree-sitter code graph, stable IDs, incremental rebuild |
94
+ | `spur-pm` | Project management adapters (beads, GitHub) |
95
+ | `spur-worktree` | Git worktree creation, isolation, flock liveness, cleanup |
96
+ | `spur-interactive` | Frontend bridge for non-TUI clients |
97
+ | `spur-bot` | Telegram bot frontend |
98
+ | `spur-blob-store` | Content-addressed delegation outcome artifacts |
99
+ | `spur-license` / `spur-license-admin` | Tier / feature-key registry |
100
+
101
+ ## Quickstart
102
+
103
+ ### Install
104
+
105
+ ```sh
106
+ curl -sSL https://getspur.dev/install.sh | sh
107
+ ```
108
+
109
+ Installs the signed `spur` binary. Free Community tier — no key required. Pro / Team / Enterprise unlock via a signed license file from your account dashboard.
110
+
111
+ > Note: install domain is provisional; final URL will be confirmed at launch.
112
+
113
+ ### Initialize a project
114
+
115
+ ```sh
116
+ cd your-project
117
+ spur init
118
+ ```
119
+
120
+ Creates a `.spur/config.toml` with detected agents and sensible defaults.
121
+
122
+ ### Run
123
+
124
+ ```sh
125
+ spur
126
+ ```
127
+
128
+ The TUI launches with your configured brain and worker agents. Type a task or pick an issue from your PM integration.
129
+
130
+ ### Check config
131
+
132
+ ```sh
133
+ spur config check
134
+ ```
135
+
136
+ Validates your configuration and reports warnings.
137
+
138
+ ## Configuration
139
+
140
+ SPUR is configured via `.spur/config.toml` at your project root:
141
+
142
+ ```toml
143
+ [brain]
144
+ agent = "claude-code-acp"
145
+
146
+ [agents.entries.claude-code-acp]
147
+ role = "brain"
148
+ transport = "native"
149
+
150
+ [agents.entries.codex]
151
+ role = "worker"
152
+ transport = "stdio"
153
+ good_for = ["rust", "tests"]
154
+ tier = 1
155
+
156
+ [agents.entries.kimi]
157
+ role = "worker"
158
+ transport = "stdio"
159
+ good_for = ["long-context-exploration", "research"]
160
+
161
+ [pm.github]
162
+ repo = "owner/repo"
163
+
164
+ [worktree]
165
+ enabled = true
166
+
167
+ [cost]
168
+ db_path = "~/.spur/cost.db"
169
+ ```
170
+
171
+ Each agent entry supports delegation descriptors (`good_for`, `avoid_for`, `tier`, `cost_tier`) used by the brain for routing. Run `spur config check` to lint your configuration.
172
+
173
+ ## Telemetry and Privacy
174
+
175
+ SPUR ships anonymous-identifier-based telemetry: Tier 1 crash diagnostics and performance are default ON, Tier 2 usage is default OFF (opt-in).
176
+ Disable all telemetry in one line: `SPUR_TELEMETRY=0 spur`.
177
+ For persistent disable, run `spur telemetry disable all`.
178
+ Collected fields, retention (90 days), deletion-by-ID steps, and pseudonymity details: [docs/PRIVACY.md](docs/PRIVACY.md).
179
+
180
+ ## A Day in the Life
181
+
182
+ **Parallel refactor.** "Refactor error handling across the codebase." Brain submits a 5-task plan → 5 worktrees, 5 workers run in parallel → review cards appear → approve 4, reject 1 with feedback → rejected worker retries with your note → approved branches cherry-picked to staging. Status bar: total cost $2.40.
183
+
184
+ **Cost burn check.** Hit `Alt+a` for Insights. Live tab shows 3 active sessions — Codex is on the wrong model. Switch to that session, type `/model gpt-4o`, status bar updates instantly. Breakdown tab shows yesterday $18 (60% Claude, 40% Codex).
185
+
186
+ **Stuck plan recovery.** Overnight 12-task plan; one task stuck in `Dispatched` (worker died). `Alt+P` Plan Inspector → `force_reclaim_plan` promotes it to `AwaitingReview` → inspect partial diff → reject and split via `submit_plan_mutation` → plan resumes, 11 completed tasks intact.
187
+
188
+ ## Development
189
+
190
+ ```sh
191
+ # Build all crates
192
+ cargo build --workspace
193
+
194
+ # Run the full test suite
195
+ cargo test --workspace
196
+
197
+ # Run tests for a single crate
198
+ cargo test -p spur-tui
199
+
200
+ # Lint
201
+ cargo clippy --workspace -- -D warnings
202
+
203
+ # Format
204
+ cargo fmt --all
205
+
206
+ # Run locally
207
+ cargo run -p spur-cli -- --help
208
+ ```
209
+
210
+ ### Commit format
211
+
212
+ ```
213
+ <type>(<scope>): <short imperative>
214
+ ```
215
+
216
+ Types: `feat`, `fix`, `test`, `docs`, `refactor`, `chore`. Keep subjects under 72 characters.
217
+
218
+ ## License
219
+
220
+ SPUR is a proprietary product. The free Community tier is available without a license key under the terms of the EULA at [https://getspur.dev/eula](https://getspur.dev/eula). Pro / Team / Enterprise tiers require a signed license file. See [LICENSE](LICENSE) for terms.
221
+
222
+ > The source in this repository is not open source. Issues, feedback, and feature requests are welcome via [https://getspur.dev/feedback](https://getspur.dev/feedback) or your team's support channel.