Coding agents don't struggle to write code.
They struggle to find where.

leangraph reads a repository once and builds a graph of what calls what — every function, class, import and reference, each link carrying a confidence score. Describe what's wrong in your own words and it points at the code that has to change.

cargo install --path .  ·  no API key, no services, nothing leaves your machine

SWE-bench Verified · 500 real issues · twelve repositories

Better aim, for a fraction of the reading.

Each issue is pinned to the commit it was filed against. The question asked is the issue exactly as a human wrote it; the right answer is the set of files the accepted fix actually touched, test files excluded.

file recall / tokens per query bench/swebench.py
leangraph 0%
100 nodes of ranked context 15,742 tokens
keyword search, top 10 files 0%
what an agent without an index falls back to 282,720 tokens
keyword search, held to our budget 0%
one Python file usually exceeds the budget on its own 41,290 tokens

Bar length is drawn to the token figures. Better recall than reading ten whole files, for one eighteenth of the input. On Rust, across another 239 issues, 74.2%.

What it prints

A ranked answer, and why each line is in it.

Every entry says how it was reached. A name that resolved is not the same kind of evidence as a word matched in a docstring, and an agent that cannot tell them apart weighs them the same.

leangraph context
$ leangraph context "separability_matrix is wrong for nested CompoundModels"
context  8 nodes · ~2,140 tokens
seed1.00fnseparability_matrix  astropy/modeling/separable.py@2017
prose1.00fn_separable  astropy/modeling/separable.py@8778
callee0.50fn_coord_matrix  astropy/modeling/separable.py@5448
caller0.60fn_cstack  astropy/modeling/separable.py@6916
path0.90fn_compute_n_outputs  astropy/modeling/separable.py@3513
seed1.00method_calculate_separability_matrix  astropy/modeling/core.py@30909
caller0.57fileastropy/modeling/tests/test_separable.py
co-chg0.40fileastropy/modeling/functional_models.py

Illustrative — the shape is exactly what the command prints, the query is a real issue. Add --source and it emits the code behind those lines instead of their names.

Why it is quick

Loading is a memory map, not deserialization.

A graph is a file laid out so that querying it needs no parsing step. Opening one is an mmap and a header check, which is why an agent can afford to ask.

0msto open a graph and answer
0sto index django, all 3,038 files
0sto re-index after a file changes
0languages, eleven checked against an oracle

How it works

Four passes, and everything after the first is an integer.

01 Discover

The repository's own ignore rules decide what counts as source. Nothing else is read.

02 Extract

One tree-sitter pass per file collects definitions, references — and the comments and docstrings, which are the only part of a repository written in the words its users actually use.

03 Resolve

Three tiers: lexical scope, explicit import, then a name match across the repository. Each edge keeps how it was found, so a proven call never weighs the same as a guess.

04 Persist

Adjacency in both directions, as CSR. callers() is two array reads and a slice — no descent, no decode, no allocation.

Where it loses

The part most tools leave out.

Every number here comes from a script in bench/ that you can run yourself. So do these.