mri-codeintel
v0.1.0
Published
Code intelligence engine: extracts symbols, calls and risk from JavaScript/TypeScript, Python, Go, and Rust repos into a queryable graph.
Maintainers
Readme
mri
An MRI scan for your codebase — structural code intelligence you can trust, because every fact in the graph is either proven or explicitly marked unknown.
mri extracts symbols and structure from JavaScript/TypeScript, Python, Go, and Rust repositories into a queryable graph, then answers hard structural questions: what calls this, what depends on it, what looks dead, where risk concentrates. It is not an LLM guessing about your code — resolution is deterministic, and when a reference cannot be proven, the graph says so instead of inventing an edge.
Watch it work
Real terminal recordings (captured with
examples/recordings/capture.mjs; replay
with asciinema play <file> or read the .txt transcript alongside):
- analyze-got.cast —
mri analyzeagainst sindresorhus/got: architecture, dead-code candidates, churn-based risk, import-based coverage — one command, ~3 s runtime (the recording itself runs longer; it includes typing and scrolling). - ask-got.cast —
mri askrefusing to guess on an ambiguous name ("noop" matches two symbols), then answering "who calls calculateRetryDelay" and "what is the riskiest file" strictly from graph facts. - containment.cast —
mri guard: the allowlist receipt for a billing-module scope, in-scope code executing cleanly, then an attempted.envread blocked pre-execution with the exact violated rule (resources.filesystem, zero grants).
Every recording is a real run against real repositories — no staged output. The fixture used by the containment recording ships in examples/recordings/fixture.
The core idea
Most code-intelligence tools optimize for plausible-looking answers. mri optimizes for trustworthy ones:
- Every cross-symbol reference is stored as either
resolved(points at a concrete destination via a deterministic proof) orambiguous(kept with no destination and its original source text preserved). - The database schema itself forbids lying about which is which.
- Downstream analysis honors the distinction end-to-end: blast radius keeps confirmed reachability separate from ambiguous-only dependents; dead-code findings refuse to declare death under uncertainty.
Ambiguous edges fail closed, never guessed.
Details: docs/ARCHITECTURE.md
Installation
Requires Node.js ≥ 22.5 (mri stores its code graph in node:sqlite,
which ships with Node from 22.5). The installer verifies Node, SQLite and the
tree-sitter native bindings up front and prints actionable instructions if
anything is missing.
From source
The npm package (mri-codeintel) is not yet published, so installing
from source is the supported path right now. When it ships, the installed
command will be mri, and the package will bundle its tree-sitter native
dependencies so no compiler toolchain is needed on platforms with prebuilt
binaries (win32/x64, darwin arm64+x64, linux arm64+x64).
git clone https://github.com/zaydmulani09/mri
cd mri
npm install
npm run build
node dist/cli/index.js --helpIf tree-sitter native bindings ever break after a Node upgrade:
npm rebuild tree-sitter tree-sitter-javascript tree-sitter-typescript tree-sitter-python tree-sitter-go tree-sitter-rustUsage
The CLI ships eight commands: extract, build, blast-radius, analyze,
ask, guard, serve, and mcp.
node dist/cli/index.js analyze /path/to/repoARCHITECTURE
files 16 (javascript 12, python 4)
symbols functions 19 | classes 5 | methods 4
edges defines 27 | imports 7 | calls 14 (8 resolved / 6 ambiguous) | inherits 3
external modules 1 [extlib]
TECH DEBT
dead code candidates 7 (detail under DEAD CODE)
risk scores (top 5 of 14 files, window 90d)
1. py/klass.py score 39 [churn 1 commits (+7pts) | no tests found (+30pts) | max CC 1 (+2pts) | last modified 2026-08-24]
2. src/callbacks.js score 39 [churn 1 commits (+7pts) | no tests found (+30pts) | max CC 1 (+2pts) | last modified 2026-08-24]
…
highest-complexity functions (top 10 of 22)
CC 1 py/helpers.py:1 shrink
CC 1 py/klass.py:2 speak
…
SECURITY-RELEVANT SIGNALS (gaps in knowledge, not findings)
unresolved references 6 "registry.on" x2, "process" x1, ...
DEAD CODE
candidates 7: 4 confirmed-unreferenced, 2 referenced-but-uncalled, 1 no-resolved-references
TEST COVERAGE
estimated coverage 35.7% (5/14 source files, import-based approximation)(Output abridged; captured from a real run against
tests/fixtures/analysis_repo.)
node dist/cli/index.js blast-radius fn:src/api.js#fetchUser --format treefn:src/format.js#pad (function)
├─ ✓ fn:src/api.js#fetchUser d1 · calls
│ ├─ ✓ fn:src/index.js#main d2 · calls
└─ ✓ fn:src/format.js#money d1 · callsmri ask maps a natural-language question onto one of the supported graph
queries, executes it against the real graph, and narrates only that result;
unmappable questions are rejected rather than guessed at. When a local Ollama
server is reachable (MRI_OLLAMA_URL, MRI_OLLAMA_MODEL) the answer is
narrated by the model; otherwise the structured result is printed as-is.
mri guard checks a code snippet against the fail-closed allowlist generated
for any scope in the graph. Ungranted resources, unknown references, or
imports outside the scope's proven reachability block execution before it
starts. Code that passes the gate runs inside an isolated-vm V8 isolate —
a separate realm and heap with no host objects injected — so cross-realm
escape attempts (the .constructor.constructor class from the adversarial
benchmark) are structurally impossible rather than merely discouraged. The
adversarial benchmark behind that claim, re-run after the isolate rewrite:
18/19 legitimate tasks execute (the last is denied by design pending a real
filesystem bridge) and every escape category fails closed, including the two
cross-realm escapes that defeated the earlier node:vm backend —
examples/benchmark/ADVERSARIAL_REPORT.md.
node dist/cli/index.js guard fn:src/api.js#fetchUser snippet.js --path /path/to/repoBLOCKED — code refused for scope fn:src/api.js#fetchUser
1 containment breach(es):
line 1 · ungranted-resource
attempted: process.env.TOKEN
rule: resources.environment -> expected TOKEN
reason: read access to environment variable 'TOKEN' is not granted (granted variables: none)
nothing was executed (fail closed). allowlist: 4 symbol(s), 3 file(s); 2 unresolved reference(s) excludedCommand reference: run mri --help.
To run these checks inside another repository's CI, see
docs/GITHUB_ACTION.md — mri ships composite GitHub
Actions for scoped containment (mri guard) and PR analysis summaries
(mri analyze), and runs them against this repository on every PR.
Against a real codebase
The snippets above use a tiny fixture for readability. A full validation run
against sindresorhus/got — including a
QA pass that found and fixed three real correctness bugs — is captured
verbatim in examples/reports/got-analysis.md
(mri 40ba1d8). Two highlights from it:
$ mri build <got>
85 files parsed
nodes: 85 files, 311 functions, 24 classes, 189 methods, 57 external modules
edges: 495 defines, 525 imports, 2334 calls (712 resolved / 1622 ambiguous),
16 inherits (14 resolved / 2 ambiguous)$ mri blast-radius "fn:source/core/options.ts#assertAny" --format tree
fn:source/core/options.ts#assertAny (function)
├─ ✓ cls:source/core/options.ts#Options d1 · calls
│ ├─ ✓ cls:source/core/index.ts#Request d2 · calls
│ │ ├─ ✓ fn:source/as-promise/index.ts#asPromise d3 · calls
│ ├─ ✓ fn:benchmark/index.ts#internalBenchmark d2 · calls
│ ├─ ✓ fn:source/create.ts#create d2 · calls
│ ├─ ✓ m:source/core/index.ts#Request._onResponseBase d2 · calls
├─ ✓ fn:source/core/options.ts#validateSearchParameters d1 · calls
… (21 more Options.* accessor methods, all ✓ confirmed, d1)Note the build line: 1622 call edges stayed ambiguous rather than being
guessed into destinations — mostly JS globals and dynamic dispatch. That
ratio is the fail-closed contract doing its job on real code.
What's inside
- Extraction — gitignore-aware walker plus tree-sitter parsers for JavaScript (.js/.jsx/.mjs/.cjs), TypeScript (.ts/.tsx), Python, Go, and Rust.
- Graph — SQLite store of files, functions, classes, methods and their
defines/imports/calls/inheritsrelations, built in one atomic transaction per repository. - Analysis — dead-code candidates with two-level confidence labels, import-based test-coverage estimates, cyclomatic-complexity scoring, git-churn risk scoring with printed components, reverse-dependency blast radius by depth.
See ARCHITECTURE.md for the full design and the resolution/confidence contract.
Roadmap
Built and working today:
- Extraction layer for JS/TS, Python, Go, and Rust (
mri extract) - Graph construction with resolved-vs-ambiguous tracking (
mri build), including incremental rebuilds (--incremental) and watch mode (--watch) - Dead-code, coverage, complexity, churn-risk, and blast-radius passes
(
mri analyze,mri blast-radius) - Reasoning v0: deterministic question intents over the graph, grounded
narration via a local Ollama model when available (
mri ask) - Guardrail enforcement: fail-closed allowlists per graph scope plus a
sandboxed checker (
mri guard) - Local dashboard (mri serve): force-graph viewer with live blast-radius highlighting (confirmed vs ambiguous kept visually separate), dead-code and risk tables, fully offline
- Showcase validation run against sindresorhus/got, demo-ready (examples/reports/got-analysis.md)
In flight / planned:
- Public containment demo per
docs/CONTAINMENT_DEMO_SCRIPT.md
Full phase breakdown by dependency order: docs/ROADMAP.md
Status
Pre-alpha under active development. The graph schema and CLI surface may
still change. Nothing in this README describes capabilities that don't
already exist in src/.
