a deterministic code knowledge graph for coding agents

Your agent is burning its context window on grep.
Stop paying for reads it never needed.

ctx-optimize turns any repo into a queryable knowledge graph — 12,000 files in 0.67 seconds — and answers your agent's questions in ~100ms, with citations. One static Go binary. No LLM at query time, no database, no server.

npm install -g @muthuishere/ctx-optimize
ctx-optimize install --skills            # one-time: teach your agent (global)
cd your-repo && ctx-optimize init && ctx-optimize add .   # gather it — then ask anything
watch it work · 34 seconds

A monorepo — one graph per module. Then see it all.

One real session, unedited: scan finds every module, add builds a knowledge graph for each with a navigator on top, and a symbol card proves that a module whose source and tests live in separate folders is still one graph — the test's call resolves straight to the source. Then the dashboard: every repo, every module, and the tokens it's saved.

Narrated · real, unedited output · a monorepo with a module split across src/ and tests/

get started

Three commands to a smarter agent.

npm install -g @muthuishere/ctx-optimize   # one static binary per platform, no postinstall downloads
ctx-optimize install --skills               # one-time: teach your coding agent to answer from the store
cd your-repo && ctx-optimize init && ctx-optimize add .   # gather this repo — about a second

Monorepo? One more command finds every project first:

ctx-optimize scan                           # lists every project it found (read-only)
ctx-optimize init --scan --yes && ctx-optimize add .   # one store per module + a navigator

Source and tests in separate folders (the .NET / Gradle case)? Declare one module across both — they share a graph, and test→source calls resolve across the split:

# .ctxoptimize/config.json
"modules": [{ "name": "Billing", "paths": ["src/Billing", "tests/Billing.Tests"] }]

A teammate cloning your repo runs one command — init reads the committed remote and fetches your prebuilt graph instead of rebuilding.

v0.3.5 · macOS / Linux / Windows · works with Claude Code, Codex, Copilot & Devin · framework routes, build-tool dependencies, k8s topology, subsystems, multi-path modules & a first-class dashboard · source on GitHub.

agent skills — and everything else your repo knows

Docs, your database schema, any data — one graph, one question.

The installed skill makes your agent query the store before it greps. Markdown docs are gathered natively. Your Postgres schema arrives via a drop-in adapter script — tables become nodes, foreign keys become edges. Anything else goes through the validated JSON door. Then one question cites the table, the code, and the doc together:

Unedited recording · a repo with .ctxoptimize/adapters/postgres-schema.py against a live Postgres

Same corpus. Same machine. 13× faster.

Cold gather of a 12,484-file repository — and ctx-optimize's run includes building the full markdown wiki.

ctx-optimize
0.67s
graphify 0.9.12
8.88s  (its fastest path — no clustering, no LLM)
query, median
96ms
graphify query
367ms

Apple M5 Pro · 2026-07-11 · methodology and every corpus below · raw results.json

the problem

The most expensive thing your agent does is look for things.

without a graph

Grep. Open the file. Wrong one. Grep again. Open three more files just to see one signature. Every hop is tokens, latency, and context that's gone for the rest of the session.

> grep -rn "Merge" .        — 214 matches
> read store.go             — 9.4k tokens
> read store_test.go        — 11k tokens
> read app.go               — 14k tokens
… and the window is half gone
with the store

One query. Complete, citable hits — id, kind, file, line range, signature, doc, callers, callees — under a hard token budget. The agent reads a file only when it truly must.

> ctx-optimize card "Store.Merge"   — 96ms
sig: func (s *Store) Merge(b *schema.Batch) …
doc: Merge upserts a validated batch …
called by (12): cmdAdd, cmdMerge, …
calls (6): Batch.Validate, writeNDJSON, …

the demo that matters

A symbol card answers what used to take four file reads.

Signature, doc comment, location, callers, callees — in one shot, from the graph. This is the read your agent no longer makes.

Unedited recording · ctx-optimize card "Store.Merge" on this project's own source

remember three things

Fast. Honest. Deterministic.

01

Fast

Tree-sitter grammars compiled to WASM, hosted by wazero, fanned across every core. 12 languages embedded in one static binary; any other language is a one-command grammar pack. Re-gather after a change in under a second.

02

Honest

Every edge carries confidence: EXTRACTED is parsed fact, INFERRED is name-matched. Ambiguous calls are dropped, never guessed. The benchmarks on this page show where the other tool wins, too.

03

Deterministic

Same input, same graph, byte for byte — git-diffable NDJSON, a markdown wiki regenerated on every add, atomic writes. The binary never calls a model. Your agent supplies the intelligence; the store supplies precise recall.


all the numbers

Benchmarks, with the parts that don't flatter us.

Head-to-head vs graphify — the one rival we've run the full measured harness against. Measured 2026-07-11 · Apple M5 Pro (18 cores, 48 GB) · graphify 0.9.12 vs a ctx-optimize dev build · raw data. How we stack up against CodeGraph, GitNexus, potpie & Serena — on architecture, setup, and their own published numbers — is on the comparison.

cold gather — full extraction, bars scaled per corpus

graphify's own source

12,484 files13.3× faster
ctx-optimize
0.67s
graphify
8.88s

nodes 11,056 vs 10,996 — within 1% · store 8.3 MB vs 30.1 MB — 3.6× smaller

flask

265 files1.3× faster
ctx-optimize
0.79s
graphify
1.06s

nodes 1,978 vs 1,473 — we also graph docs & import modules · store 1.3 MB vs 4.2 MB

gin

159 files2.2× faster
ctx-optimize
0.41s
graphify
0.90s

nodes 1,922 vs 1,975 — graphify finds more here, shown as-is · store 1.4 MB vs 6.2 MB

ctx-optimize source

75 files1.4× faster
ctx-optimize
0.37s
graphify
0.51s

nodes 572 vs 599 — graphify finds more here too · store 0.4 MB vs 2.2 MB

query latency — 12k-file corpus, median of 5 runs, 2,000-token budget

"how does clustering work"

4.1× faster
ctx-optimize
89 ms
graphify
367 ms

"remote push pull hooks"

3.8× faster
ctx-optimize
96 ms
graphify
364 ms

"community labeling"

3.9× faster
ctx-optimize
96 ms
graphify
370 ms
▸ raw table — every number in one place
corpusfilesctx coldgraphify coldctx nodesgraphify nodesctx sizegraphify size
graphify's own source12,4840.67s8.88s11,05610,9968.3 MB30.1 MB
flask2650.79s1.06s1,9781,4731.3 MB4.2 MB
gin1590.41s0.90s1,9221,9751.4 MB6.2 MB
ctx-optimize source750.37s0.51s5725990.4 MB2.2 MB
query "how does clustering work"89 ms367 ms
query "remote push pull hooks"96 ms364 ms
query "community labeling"96 ms370 ms
Methodology — read this before quoting the numbers. graphify was timed on its fastest deterministic path (update --no-cluster: extraction only, no clustering, no LLM). ctx-optimize was timed on add, which does more work per run: extraction, graph build, stale-node pruning, and full wiki regeneration. Query latency is the median of 5 runs, same question, same 2,000-token budget, both CLIs invoked cold from the shell. Node counts are shown for scale, not equivalence — the tools disagree about what counts as a node (ctx-optimize graphs import modules and markdown documents as first-class nodes; graphify includes some file types we skip), which is why ctx-optimize shows ~34% more nodes on flask while the 12k-file totals land within 1%. On gin and on its own source graphify finds slightly more nodes and edges than we do, and we show that as-is. graphify also does things ctx-optimize deliberately does not: LLM-assisted community labeling and semantic clustering. If you want those, use graphify — the comparison here is the deterministic core both tools share.

the kill test

We tried to kill this product. Published, either way.

Before launch we pre-committed a kill bar: if the store can't beat a plain grep-and-read agent by 25% on hostile terrain, stop. Then we ran it — 12 Linux-kernel questions, answered twice by the same agent (grep-and-read vs store-first — a real agent always reads, never just greps), every answer judged against source-derived ground truth before tokens counted. Measured 2026-07-12 · full write-up · questions + keys (raw json) · raw runs ship in the repo's proof/.

graphify headlines 71.5× — against pasting your entire repo into every prompt. No agent does that. And graphify's output is a static graphify-out/ dump; ours is extensible by design — drop-in adapters, grammar packs, skills installed by default. We threw the strawman out and tested against a real grep-and-read agent. A smaller honest number beats a big fake one.
−43%
tokens to answer
−32%
cost per session
96ms
per query · ~4× graphify

Tokens & cost: natural usage on Claude Code — plain questions, real dollars, no store vs store. Query latency: 12k-file benchmark. Honest caveat: total session wall runs +31% today (the store trades wall-time for tokens and dollars — more tool calls); we just cut query latency 40% and that gap is closing.

Don't take our word — run it yourself. Same model, same questions, answered three ways over OpenRouter — shell, ctx-optimize, graphify. Last CI run on gorilla/mux:
vs plain shell
−31% cost
−64% tool calls · −36% tokens
vs graphify
~½ the tokens
half the tool calls, lower cost
graphify vs shell
+22% tokens
its node-dump query costs more than grep

See the full table, the harness, and how to run it yourself →

the result that clears the bar — natural usage, plain questions, real dollars billed

Natural usage · Claude Code · zero steering

12 plain questions · real $ billed−31.6% cost
with store
$2.16
no store
$3.16

the fix: cards now carry the function body head (first ~30 lines) — the follow-up read they used to force is gone · turns 70→53 (−24%) · 56 unprompted store calls · before the fix the same harness measured +6% worse (published, below) · quality spot-verified: the $0.05 answers are complete and correct

▸ the full autopsy — the misses, every tier, the mechanism (skeptics, open this)
what died

"Cuts raw input+output tokens on a cached frontier agent" — dead. Claude Code and Codex grep well, and prompt caching absorbs every extra hop, so on that one narrow metric the store is parity on locate/mechanism questions. We don't claim otherwise anywhere on this page. But raw tokens aren't the bill →

what survived

The real bill is cheaper. Cache reads still bill (~10%), and fewer steps means fewer of them — so on each harness's own dollar report the store wins every Claude tier: −9% to −16%. Plus onboarding −34% to −49% tokens, correctness (grep arms of Codex and Devin named the wrong gatekeeper; the store arms didn't), and −13% to −29% steps everywhere.

fresh tokens per tier — where even a hand-tuned grep baseline shows parity (the adversarial version of the test)

Claude Code · default

linux/block · 98 filestoken parity
with store
370k
grep+read
370k

steps ~flat · wall −14% (347→298s) · quality 12/12 vs 11.5/12 — the best grep agent on earth doesn't need us for tokens

Claude Code · Sonnet

linux slice · 3,987 filesgrep wins by 5%
with store
390k
grep+read
370k

steps −13% · wall +3% (386→397s) · quality 11.5/12 vs 12/12 — the store arm was the only one to catch a comment posing as a real caller

Claude Code · Haiku

linux slice · 3,987 filestoken parity
with store
284k
grep+read
287k

steps −29% · wall +9% (641→698s) · quality 11/12 vs 11/12 — a third fewer hops, but caching makes hops free

Codex CLI · default

linux/block · 98 filestoken parity
with store
397k
grep+read
386k

steps ~flat · wall ±0% (683→680s) · quality 11.5/12 vs 11.5/12 — its grep arm named the wrong gatekeeper; the store arm didn't

Devin CLI · SWE-1.6

linux/block · 98 files · n=6−42% tokens
with store
2.30M
grep+read
4.00M

steps −24% · wall −34% (294→195s) · quality 5/6 vs 6/6 · no prompt caching — every step re-pays full context, so saved steps become saved money · daily quota cut the run at 6 pairs

▸ raw table — every number in one place
harness · modelcorpustokens, grep→storestepswall timeanswer quality (grep vs store)
Claude Code · defaultlinux/block · 98 files370,398 → 369,673 (±0%)~flat347→298s (−14%)12/12 vs 11.5/12
Claude Code · Sonnetlinux slice · 3,987 files370,308 → 389,647 (+5%)89→77 (−13%)386→397s (+3%)11.5/12 vs 12/12
Claude Code · Haikulinux slice · 3,987 files286,943 → 283,669 (−1%)174→124 (−29%)641→698s (+9%)11/12 vs 11/12
Codex CLI · defaultlinux/block · 98 files385,794 → 397,323 (+3%)~flat683→680s (±0%)11.5/12 vs 11.5/12
Devin CLI · SWE-1.6linux/block · 98 files (n=6)4,004,232 → 2,300,736 (−42%)−24%294→195s (−34%)5/6 vs 6/6
The mechanism: step-cost economics. The store cuts agent steps at every tier (Haiku −29%, Devin −24%, Sonnet −13%). Whether that becomes fewer tokens depends on what your harness pays per step. Devin re-pays the whole context every step — zero cached tokens — so −24% steps became −42% tokens. Claude Code caches, so extra hops are nearly free: token parity, −14% wall.
The correction that helps us. "Parity" prices cache reads at zero — they bill at ~10%. On each harness's own dollar report the store is cheaper at every Claude tier: −9.2% default · −5.6% Sonnet · −16.2% Haiku, from ~26% fewer cache re-reads. Fewer steps is a real bill cut wherever a step costs money.
What we don't hide. Skill-only, plain questions, before the card fix: +6% worse — agents used the store (57 calls) but re-verified in source. Total session wall is +31% today (more tool calls). The test surfaced three defects in our own ranking/card output, now fixed; the after-number re-run is pending. Scale: 3,987 files → a 274,812-node store + 3,989 wiki pages in 5.5s. Everything, unrounded, is in the write-up.

the design contract

What's in the binary — and what never will be.

In
  • tree-sitter → WASM, hosted by wazero — pure Go, CGO_ENABLED=0
  • one committable .ctxoptimize/ per repo: config + drop-in adapter scripts
  • central store at ~/ctxoptimize/<repo>/ — multi-module, git-diffable NDJSON
  • team sync without a server — push/pull to any S3-compatible bucket, credentials as ${VAR} names, values never written
  • query · card · explain · path · affected · hubs · wiki · export · serve
Out — forever
  • No LLM API. The host agent answers; we serve context.
  • No database. Files, sorted, atomic writes.
  • No embeddings. Lexical + graph retrieval, deterministic.
  • No server. A static binary and your filesystem.
  • No telemetry. Nothing leaves your machine unless you push it.

different by design

Not a clone of any of them. A different bet.

The lane has graph-DB tools (CodeGraph on SQLite, GitNexus on an embedded DB, potpie on Neo4j), a Python central-store (graphify), and a live language-server (Serena). We're the one that's a single static binary you extend — here's what that changes versus the whole field.

One binary — no store to run. CGO-free Go, tree-sitter compiled to WASM. Nothing to stand up: no SQLite file, no LadybugDB, no Neo4j service, no Python venv, no language server per language. npm i -g and it runs, offline.
Deterministic — no model, ever. CodeGraph and GitNexus are structural like us; graphify reaches for an LLM to label communities and potpie keeps one in the loop. We hand all semantics to the agent you already run — zero API keys, reproducible every time.
Extensible without a fork. A new language is a drop-in grammar pack; a framework's routes, a build tool's deps, Postgres, Kafka, logs — drop-in route/manifest packs and adapter scripts through one validated door. No other tool here lets you add a recognizer as a committed JSON file.
More than code, in one graph. Routes→handlers, dependencies that federate across build tools, k8s topology, git co-change, subsystems. Serena has no persistent graph; the graph-DB tools mostly stop at code. And team sync is push/pull to any S3 bucket — no server.

The full board — where every rival wins and where we lose, with numbers — is on the honest comparison.


install

Convinced? It's still three commands.

npm install -g @muthuishere/ctx-optimize
ctx-optimize install --skills   # one-time: teaches your coding agent to use the store
cd your-repo && ctx-optimize init && ctx-optimize add .

Every command and every config key — repo config, machine-global config, monorepos, remotes, adapters, route/manifest/grammar packs — on one page: the docs. See where we stand against the field, honestly: the comparison.