MCPcopy Index your code
hub / github.com/Cranot/roam-code

github.com/Cranot/roam-code @v13.6.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v13.6.1 ↗ · + Follow
31,854 symbols 121,126 edges 1,893 files 20,539 documented · 64%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

roam-code

The local codebase intelligence layer that lets AI coding agents earn the right to change code — and prove they did.

PyPI version GitHub stars CI Python 3.10+ License: Apache 2.0

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

roam terminal demo


Why Roam is different

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:

  • Credential-free. No account, no API key, no cloud login. pip install and run.
  • 100% local by default. Source code never leaves the machine; air-gapped repos work like cloud repos. The single outbound surface (roam metrics-push) is opt-in, summary-only, and prints its exact payload under --dry-run.
  • Tamper-evident 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.
  • MCP runtime security at the wrapper boundary. Every MCP response is scrubbed for secrets on egress, gated against the active mode (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.


Install + first four commands

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.


The Compiler — your agent's first token already knows the answer

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;

Extension points exported contracts — how you extend this code

Flyable (Interface)
Interface implementation
tests/fixtures/languages/kotlin/inheritance.kt
Printer (Interface)
Delegation pattern
tests/fixtures/languages/kotlin/inheritance.kt
Repository (Interface)
Interface
tests/fixtures/languages/kotlin/basic.kt
Swimmable (Interface)
(no doc)
tests/fixtures/languages/kotlin/inheritance.kt

Core symbols most depended-on inside this repo

get
called by 9105
tests/fixtures/languages/kotlin/basic.kt
get
called by 7640
src/roam/evidence/policy.py
keys
called by 971
src/roam/evidence/policy.py
get
called by 953
src/roam/capability.py
open_db
called by 947
src/roam/db/connection.py
json_envelope
called by 759
src/roam/output/formatter.py
to_json
called by 730
src/roam/output/formatter.py
add
called by 655
src/roam/commands/cmd_guard_doctor.py

Shape

Function 18,474
Method 10,350
Class 2,618
Route 408
Interface 4

Languages

Python100%
Kotlin1%

Modules by API surface

src/roam/mcp_server.py483 symbols
tests/test_stale_refs.py280 symbols
src/roam/plan/compiler.py244 symbols
tests/test_smells.py228 symbols
tests/test_comprehensive.py211 symbols
tests/test_mcp_server.py175 symbols
tests/test_languages.py143 symbols
tests/test_math.py126 symbols
tests/test_file_roles.py119 symbols
tests/test_performance.py112 symbols
tests/test_doctor.py99 symbols
tests/test_confidence_triples.py95 symbols

Datastores touched

dbDatabase · 1 repos
myappDatabase · 1 repos
(mongodb)Database · 1 repos
dbDatabase · 1 repos
(mysql)Database · 1 repos
mydbDatabase · 1 repos

For agents

$ claude mcp add roam-code \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact