The local codebase intelligence layer that lets AI coding agents earn the right to change code — and prove they did.
Credential-free · 100% local by default (opt-in metrics-push is the only outbound surface) · tamper-evident ChangeEvidence packets · Apache 2.0 · runs entirely on your machine
267 commands · 243 MCP tools (16 in the default core preset) · 28 languages

Cursor, Cody, Aider, and Windsurf are human-first IDE surfaces that log a session. Roam is an agent-first CLI surface that gates the change and emits proof. Four properties no competitor combines today:
pip install and run.roam metrics-push) is opt-in, summary-only, and prints its exact payload under --dry-run.ChangeEvidence packets. Each AI-assisted change compiles into one portable packet — HMAC-chained run ledger + signed Code Graph Attestation + signed PR bundle — answering eight questions: who acted, what authority existed, what context was read, what changed, what could break, what policy applied, what verified it, who accepted risk. PR Replay answers 7 of 8 today; the remaining approvals question surfaces as producer_not_available, never silently dropped. Cursor logs the run; Roam proves the change.read_only / safe_edit / migration / autonomous_pr) with a closed-enum policy_decision, and each decision receipt is HMAC-linked into the signed run ledger. Inside-server controls; the gateway layer (Interlock / Lasso / Portkey) composes on top — see dev/MCP-SECURITY-POSTURE.md.Underneath sits a SQLite-backed graph of symbols, calls, imports, layers, git history, runtime traces, smells, clones, security flows, and algorithmic patterns across 28 languages — the same local facts queried before, during, and after a change.
Dependency-aware, not string-based. Roam knows Flask has 47 dependents and 31 affected tests; grep knows it appears 847 times. One command replaces 5-10 tool calls — <0.5s per query, plain-ASCII output, --json and --sarif envelopes for agents and CI.
| Without Roam | With Roam | |
|---|---|---|
| Tool calls | 8 | 1 |
| Wall time | ~11s | <0.5s |
| Tokens consumed | ~15,000 | ~3,000 |
Illustrative — a typical agent workflow on a 200-file Python project (Flask). Reproducible smoke transcript in docs/fresh-install-smoke.md; full indexing-rate harness in benchmarks/. Exact numbers vary with repo size, agent prompt, and model.
Ten minutes from pip install to a verdict on whether your next edit is safe.
pip install "roam-code[mcp]" # 1. install with MCP server for Claude Code / Cursor / Continue
cd /path/to/your/repo
roam init # 2. index the repo into .roam/index.db (one-time, ~30s on most repos)
roam health # 3. composite 0-100 score: complexity, cycles, dark-matter coupling, dead code
roam preflight <symbol> # 4. blast radius + tests + complexity + architecture rules before you edit
Python 3.10+. pipx install roam-code and uv tool install roam-code work too. Drop [mcp] for CLI-only. See docs/fresh-install-smoke.md for a verbatim transcript of these four commands against a clean venv.
Step 4 is the payoff — roam preflight on a hot symbol returns a verdict before you touch it:
$ roam preflight open_db
VERDICT: Significant risk — CRITICAL, 1847 symbols in blast radius
Pre-flight check for `open_db (src/roam/db/connection.py:799)`:
Blast radius: 1847 symbols in 382 files [CRITICAL]
Affected tests: 617 direct, 962 transitive [OK]
Complexity: cc=30, nest=4 [CRITICAL]
Coupling: 2 files often change together [MEDIUM]
Conventions: no violations [OK]
Overall risk: CRITICAL
Risk driver: complexity (cc=30, CRITICAL)
An agent sees the blast radius before it edits — not after the tests fail.
Alternate install methods + Docker
pipx install roam-code # isolated environment (recommended)
uv tool install roam-code # uv-managed tool
pip install git+https://github.com/Cranot/roam-code.git # from source
# Docker (alpine-based)
docker build -t roam-code .
docker run --rm -v "$PWD:/workspace" roam-code index
docker run --rm -v "$PWD:/workspace" roam-code health
Works on Linux, macOS, and Windows. Windows: if roam is not found after installing with uv, run uv tool update-shell and restart your terminal.
You ask your agent "who calls handleSave?" and watch it grep, open
three files, grep again, read a fourth — six turns and $1.30 later you get
the answer the repo's call graph held all along.
Roam ships a task compiler that ends that loop. Before your prompt reaches the model, roam recognizes what kind of question it is, runs the right code-graph lookups locally (~90 ms, zero model calls), and puts the answers into the prompt: the caller list with line numbers, the git history already filtered, the source around the bug line you cited. The agent's first words can be the answer.
For Claude Code it's one command, zero configuration:
pip install "roam-code[mcp]"
cd your-repo && roam init
roam hooks claude --write # compile-before + verify-after, wired into Claude Code
Then use claude exactly as you always do. Undo anytime with
roam hooks claude --uninstall --write. A broken install can never block
your agent — every hook is fail-open.
What that buys you, measured head-to-head on Claude (same prompts, same repo, with and without the compiler — June 2026, 41 cells):
| Median per task | vanilla | compiled | delta |
|---|---|---|---|
| Agent turns (navigation/comprehension) | 6 | 1 | −83% |
| Input tokens | 271K | 53K | −80% |
| Cost | $1.30 | $0.48 | −63% |
| Wall time | — | — | −50% |
The same shape reproduces on Opus (−86% turns). And the compiler knows where it doesn't help: prompts that ask the agent to write code get no envelope at all — injection there was measured as pure overhead, so it spends your tokens only where it wins.
The full data — every bench cell (including the losses), the ground-truth bug bench, and routing stats
| Task | turns | input tokens | cost |
|---|---|---|---|
"where is open_db defined?" |
3 → 1 | 156K → 51K | $0.67 → $0.28 |
"which files depend on cli.py?" |
6 → 1 | 252K → 51K | $1.15 → $0.30 |
| "where is the env var configured?" | 9 → 1 | 497K → 53K | $1.40 → $0.31 |
| "what are the layers of this codebase?" | 5 → 1 | 271K → 50K | $1.42 → $0.41 |
"what changed in cli.py recently?" |
4 → 2 | 186K → 104K | $0.62 → $0.40 |
| "explain the compiler module's architecture" | 13 → 6 | 618K → 240K | $1.85 → $1.01 |
| "trace how a command becomes an MCP tool" | 12 → 8 | 464K → 303K | $1.25 → $1.01 |
| security-hook comprehension (hard, multi-file) | 6 → 2 | 267K → 117K | $1.15 → $0.56 |
| "what are the biggest cycles in this codebase?" (re-measured 06-11) | 6 → 1 | — | $0.65 → $0.07 |
| "where is the CLI entry point?" (trivial, re-measured 06-11) | 1 → 1 | 48K → 50K | $0.21 → $0.22 |
| "write a pytest for X" (generation, re-measured 06-11) | 5 → 7 | 275K → 396K | $0.61 → $0.45 |
The last two rows were the published LOSSES (trivial prompts once paid the envelope for nothing at +$0.20; generation once cost +17%). After the generation-skip lever (write-code prompts get a ~0.6 KB lean envelope or none — measured 3.5% of a 723-prompt real corpus) and the entry-point routing fix, both cells were re-measured at n=3 medians on the same model: generation flipped to a −26% cost / −18% wall win — input tokens rise (cache-read-heavy, cheap) while expensive output tokens drop −29% across more-but-cheaper turns — and the trivial cell is a tie within noise. Losses are findable because we publish them — and fixable because the compiler routes them.
Bug-fixing, ground-truth graded (a failing test must transition to
passing — no LLM judging): 20 cells of planted bugs with real tracebacks —
10/10 fixed in both arms at −13% cost; the envelope ships the
source around the cited path:line, so the typical fix lands within 2
turns.
Routing, replayed on 723 real prompts from live agent sessions: 91% of envelopes ship pre-executed answers (L1 probes) — the envelope already contains the literal answer — at p50 0.45 s cold / p50 92 ms live (warm cache) compile latency, fully local. Zero model calls.
Eval history by version — re-measured on every kernel change; losses are published, attacked, then re-measured (full per-cell history in the repo):
| measured | kernel | what | result |
|---|---|---|---|
| Jun 09 | v13.4 | 41-cell nav/comprehension A/B | turns −83%, tokens −80%, cost −63% |
| Jun 09 | v13.4 | 20-cell ground-truth bugbench | 10/10 both arms, cost −13% |
| Jun 09 | v13.4 | trivial-prompt cell | +80% cost — published loss |
| Jun 09 | v13.4 | generation cell | +17% cost — published loss |
| Jun 11 | v13.6 | trivial-prompt cell, re-measured n=3 | tie ($0.21 → $0.22) |
| Jun 11 | v13.6 | generation cell, re-measured n=3 | −26% cost win |
| Jun 11 | v13.6 | "biggest cycles" cell, re-measured n=3 | −89% cost win ($0.65 → $0.07, 6→1 turns) |
| Jun 11 | v13.6 | 723-prompt routing replay | 91% L1, p50 0.45 s cold |
Caveats that always ship with these numbers: trivial prompts the agent one-shots anyway gain nothing (now a within-noise tie after the lean/skip levers); cells are n=2–3 with medians and ranges.
Benchmark archaeology — runs #1–#4 (May 2026), including the honest negative result that drove the fixes
Two independent A/B runs at different scales — the larger sample inverts the smaller. Reporting both honestly.
Run #1 (n=3 per cell, 27 cells, $16.88): compile appeared to dominate (−29% wall vs static). That static prompt included a "Hard cap: 4 tool calls" line that turned out to act as a quota.
Run #2 (n=3–7 per cell, 78 cells, $54.88, "Hard cap" line removed from static):
| Condition | Mean turns | Mean wall | Mean cost |
|---|---|---|---|
| vanilla | 7.0 | 33.2s | $0.68 |
| static / roam_agent | 5.8 | 25.1s | $0.66 |
| compile | 8.2 | 47.9s | $0.78 |
At scale, static (with the "Hard cap" line removed) is the winner: −17% turns and −24% wall vs vanilla, with cost within 3%. The compile-mode envelope was +91% wall vs static on hard structural tasks — variance probe revealed compile occasionally pushes the agent into over-tool-use (one t1 run hit 41 turns and $2.43). The compile-the-COMMAND itself is robust (250/250 latency cells, 14/15 fuzz, brief mode <300 chars across all 10 procedure families) — the issue is over-direction of the consuming agent, not the compiler.
Private raw cells are retained for audit; the public summary above is the quotable result.
Run #3 (2026-05-31, n=1, 24 cells, $12.78, on 8-task user-shape corpus after W34→W37 fixes):
| Condition | Mean turns | Mean wall | Mean cost |
|---|---|---|---|
| vanilla | 6.00 | 28.6s | $0.58 (1 cell timed out at 240s) |
| static / roam_agent | 5.38 | 39.9s | $0.63 |
| compile | 2.75 | 35.6s | $0.46 |
This run inverts Run #2 on a different corpus. Compile wins 7/8 shapes including stack-trace, "what does X do", "what changed recently", compare files, who calls X, file coupling, and trace-flow. The compiler fix wave between Run #2 and Run #3 added six new probes (stack-trace source slice, body-embed for explain, git-log for history, sibling-test embed, path-comparison diff, symbol-pickaxe) and four real bug fixes (callers-backtick fallback, dead-code wrong CLI, consumer-dict flattening, stack-trace classifier missing PascalCase Errors). Headline win: a "what files are coupled to X" task that took vanilla 20 turns / $1.20 / 64s collapsed to compile's 1 turn / $0.32 / 11s — embedded coupling pairs eliminate 19 turns of exploration. The +24% wall vs vanilla is the envelope cache-creation tax at n=1; expected to amortize at n≥3.
Static remains a non-improvement (0/8 wins vs vanilla, 1/8 marginal vs compile). Caveat: Run #3 is n=1 per cell; n=3 replication ($30-40) is pending.
Private per-task tables and raw cells are retained for audit;
$ claude mcp add roam-code \
-- python -m otcore.mcp_server <graph>