Server · #185 trending MCP repo · ↗ +29 stars this week

Ripwire

redhat-et/ripwire

The ripgrep of AI context: a zero-dependency C++23 CLI + MCP server for coding agents. Find what you want without reading the repo, then check you built what you meant — blast radius, tests-to-run, quality deltas. Signatures at 74.7% fewer bytes than bodies; every guess labelled, every loss published. Paddle out with a map.

Stars
2.5k
This week
+29
Forks
172
Open issues
94
Skills inside
19
Star trend · last 13 days+51 ★
Oct 25, 2026: 2,479 starsOct 26, 2026: 2,485 starsOct 27, 2026: 2,489 starsOct 28, 2026: 2,493 starsOct 29, 2026: 2,494 starsOct 30, 2026: 2,495 starsOct 31, 2026: 2,501 starsNov 1, 2026: 2,504 starsNov 2, 2026: 2,506 starsNov 3, 2026: 2,512 starsNov 4, 2026: 2,519 starsNov 5, 2026: 2,525 starsNov 6, 2026: 2,527 starsNov 7, 2026: 2,530 stars

Inside this repo

19 skills

emitting-outputWriting or converting formatted output in ripwire's C++ source — std::print/std::format via rw::emitTo, emitRaw and formatTo, which printf specifier maps to which format spec, and how to prove a conversion moved zero bytes. Use when adding any output call site, converting a printf-family one, or tou.claude/skills/emitting-outputripwire-repo-mapUse ripwire CLI to map a repo before reading it.skills/hermes/ripwire-repo-mapripwire-before-you-buildStarting a FEATURE (multi-symbol work): the plan, the API boundary, the scope — how big does this change get — from the codebase's real structure. Implementing an EXISTING interface? --lego=Iface lists its contract and current implementors. ONE standalone symbol → reuse-first. A small feature with askills/ripwire-before-you-buildripwire-change-checkMerge-SAFETY of an existing diff — yours before you push, or a PR you review: blast radius, tests to run, a broken contract (--edit-check=SYM), branch conflicts and landing order, unmerged work stranded on old branches, which ref defines it. Code QUALITY → quality-bar. Even a one-line leaf fix runs skills/ripwire-change-checkripwire-find-bugYou have a SYMPTOM — crash, exception, wrong output, failing test, error string — and don't know which code is responsible. Ranks candidates; a stack trace or sanitizer output maps onto frames innermost-first; 'it worked yesterday' / 'my last edit broke it' → --situ. One clear hit plus one focused rskills/ripwire-find-bugripwire-fresh-eyesMaintenance risk in code you did NOT write — inheriting a module, planning a refactor, a suspected god object, 'what's gnarly / where's the rot / safe to touch?': hotspots, dead or inactive code, switches/defaults (--flags), clones, a function's real shape, bus factor, co-change. Naming the fix or jskills/ripwire-fresh-eyesripwire-graph-queryA call-graph question the fixed verbs can't phrase — 'which high-complexity functions can reach X?', 'what has 10+ callers in src/?', 'untested symbols within one hop of main'. --graph-query: a small closed expression language — kind, complexity, fan-in, tested filters; a file or cluster; bounded hoskills/ripwire-graph-queryripwire-handoffBrief someone ELSE on a subsystem — 'hand this area off' to a successor, teammate or fresh session: purpose, the 2-3 entry points with bodies, the design docs that say WHY, hotspot/bus-factor risk — a compact pasteable brief, not a wall of source. Understanding it yourself → orient.skills/ripwire-handoffripwire-layersArchitecture HEALTH and ENFORCEMENT — 'is this a dependency mess / does the UI reach into the database / enforce module boundaries in CI?': cycles, the godfile, propagation cost (how far a touch ripples), --arch rules with a baseline gate. Overview without gating → orient. One pass answers it.skills/ripwire-layersripwire-mcpWire ripwire into an agent as an MCP server — `ripwire wrap AGENT` (Claude Code, Cursor, Codex, Gemini…) — and choose the server verb mid-task. Also tool HEALTH: a symbol you expected is missing from the ranked output, the index feels stale after a rebase, 'is my ripwire setup broken?'skills/ripwire-mcpripwire-navigateYou can NAME the symbol: who calls it, what it calls, the path from A to B, its full body, or an exact literal/regex match. 'Safe to change or rename X — what breaks downstream?' = the transitive blast radius, not 1-hop callers. Three or more symbols → --connect. Run the one verb that fits, then stoskills/ripwire-navigateripwire-opt-remarksTriage clang optimization remarks (-Rpass, -Rpass-missed, opt-record YAML) while editing ripwire's OWN C++: 'loop not vectorized', 'will not be inlined' — worth a diff, or is LTO/PGO the real fix? Contributor-only: about compiling this tool, never about running it.skills/ripwire-opt-remarksripwire-orientLanding COLD in an unfamiliar repo or subsystem, or about to open several files for one question: map first, read only what it ranks highest — main subsystems, entry points, 'how does X work / where is Y'. Also recover context after compaction, retrieve saved docs (--recall), divide code for subagenskills/ripwire-orientripwire-perf-targetA MEASURED performance problem: start from a benchmark, flame graph, perf sample or profiler run, locate the hot symbol the profile names, test structural hypotheses (memory-bound → cache-line data layout). Static metrics are change-risk signals, not runtime heat. Inspect only the symbols the profilskills/ripwire-perf-targetripwire-quality-barCode QUALITY of what YOU just wrote, before commit or 'done', or a cleanup: --quality-delta lists what got WORSE in 12 kinds, exit 2 if old code got worse (new-symbol rows never gate, except defect-shape format-arity); the fix a measured shape (humps/deep, tangle) calls for. Merge safety → change-chskills/ripwire-quality-barripwire-reuse-firstAbout to write ONE symbol (even a 'quick' one-liner) or add a dependency: reuse before you reinvent. Finds the building block that already exists, the house pattern to imitate, the duplicate you'd recreate, a vendored dependency. An interface or whole feature → before-you-build. One --exemplar or --skills/ripwire-reuse-firstripwire-routerStart HERE when unsure which ripwire skill fits, or asked 'how do I use ripwire / where do I start'. A moment→skill map, cold start to handoff, plus the two reflexes that leak most: --exemplar before you write, --quality-delta before you call it done. One hop to the right skill.skills/ripwire-routerripwire-security-scanSecurity review: (1) vet an untrusted SKILL.md or .mcp.json BEFORE installing it — the injection/exfiltration scanner; CRITICAL blocks the install; (2) audit code on an untrusted-input path (a deserializer, parser, exec of user data, network endpoint): taint reach, untested seams. One scan pass is tskills/ripwire-security-scanripwire-write-testsWrite tests for EXISTING code that has none — 'this is untested, add coverage', 'add a safety net first'; also verify a new test reaches its intended code. Finds what no test reaches: --seams ranks untested cross-module edges, --callers gives the outside contract. Diff test selection → change-check.skills/ripwire-write-tests

Documentation

README

View on GitHub ↗

CI Release Licence Standard Runtime dependencies Slides

ripwire — the ripgrep of AI context

Rip'n Fast. Fewer Tokens. Better Code.

The ripgrep of AI context. A map before your agent reads the repo — and a check on what it writes.

Trendshift: C++ Repository of the Week badge for redhat-et/ripwire

Ranked, deterministic call graph: what to touch, what it breaks, which tests to run. On the edit: blast radius, tests that reach it, twelve quality kinds reporting only what got worse, forgotten co-changes, fields read and written, names that resolve more than one way.

Just want to use it? Install it with the one line below, then start each coding session by telling your agent to use it, for example: "Use ripwire on this repo." That is all most people need: the install also teaches your agent when to reach for each command.

Want every detail? The reference guide near the bottom covers install, commands, output format, exit codes and limits. You do not need it to get started.

Field report: what ripwire contributed to a large multi-agent coding engagement — written by Claude Fable 5.0, the frontier model orchestrating ~20 coding agents over two days on a ~1,500-file C++/Metal codebase. Click for the full report.

For a single developer, this tool is a good lookup accelerator. For an orchestrated fleet, it's load-bearing: it halved the research spend, twice redirected tasks before wasted work, prevented at least one silent-divergence shipped bug, and turned code-quality hygiene from a hope into a per-task mechanical gate. Whole-workflow ~2×; per-lookup 10–25×; and two moments where one call was worth more than the rest of the session's tooling combined.

— the report's bottom line

The full field report: what ripwire contributed to a large multi-agent coding engagement — headline numbers, where the value concentrated, the honest boundary, and the bottom line

Text version of the report

Field report: what ripwire contributed to a large multi-agent coding engagement

Context, genericized: one orchestrating session directing ~20 sequential/parallel coding agents over a ~1,500-file C++/Metal codebase across two days — a deep architecture audit, then a 14-task feature wave (new subsystems, measurement infrastructure, a search-archive migration), ending in a verified all-on release flip. Every agent was instructed to lead with ripwire for orientation and to close with its quality gates.

The headline numbers

  • Roughly half the total token spend of the audit/research phase, saved. This is the operator's whole-phase estimate — it includes everything the research agents did, ordinary file reads and all, which makes it conservative rather than cherry-picked. The per-call factors underneath are much steeper: a single doc-recall call served the relevant sections of a 164KB planning document in ~6K tokens (~25×), and agents that led with the tool ran ~30–40% leaner on tool-call counts than agents doing raw read fan-outs over comparable questions.
  • Two tasks had their direction changed by a single call. One task was chartered to activate a steering behavior the team believed was in production; one --callers query returned zero production callers, redirecting the task to the real gap (the data provider that behavior needed had never been wired anywhere). Another task refactored a widely-shared computation; --edit-check flagged 5 of 6 call sites as incompatible — sites a text search had missed — that would otherwise have silently diverged from the canonical path the day the feature was enabled, surfacing months later as an unexplainable visual bug.
  • A dozen-plus real code-quality defects fixed, not waived, across ~15 implementing agents — all caught by --quality-delta at each agent's "I think I'm done" moment: a 480-token duplicated routine, a fourth private copy of a shared RNG utility, a hand-duplicated cost function, a pair of near-identical functions with a flipped sign (correct at one boundary, quietly wrong at the other), test-fixture duplication across sibling suites, and functions that had quietly absorbed a second job. None of these would have failed a test; all of them are the sediment that rots a codebase under high-velocity multi-agent development. The gate made removing them routine instead of heroic.

Where the value concentrated

  1. Orientation and recall (the token win). Ranked-signature task orientation and section-granular document recall meant agents started from answers, not file dumps. Recall over planning docs was the orchestrator's single most-used verb — the audit's entire framing came from two calls.
  2. The contract checker (the shipped-bug preventions). Beyond the 5-of-6 catch above, --edit-check gave near-free proof-of-contract on every public-symbol change across the wave — the kind of verification that otherwise simply doesn't happen at agent speed.
  3. The quality gate as a protocol. The measurable effect wasn't any single catch — it was that fifteen different agents, none sharing context, all converged on the same "fix or justify with a written reason" discipline, with an acknowledgments ledger that survived across tasks. Unattended orchestration usually leaks quality; here the leak-check was mechanized.
  4. Persistent notes. Agents left gotcha notes pinned to symbols mid-wave (a caller-wiring law, an arming-rule invariant), which later agents' lookups surfaced automatically — cheap institutional memory between contexts that never met.

The honest boundary

  • The engagement's deepest findings did not come from the tool. A structural capacity ceiling, a mis-derived constant, a floating-point re-association drift of 1 ulp, and a seed-keying bug were all found by bespoke measurement the agents built — baseline worktree diffs over full data fills, multi-arm attribution sweeps, funnels. The tool is a floor for honesty and orientation, not a substitute for measurement design.
  • Known false-positive mode, handled by its own documentation: the contract checker's arity heuristic over-counts on defaulted trailing parameters (one task saw "incompatible=18" that were all fine). The tool's own trust-calibration notes say to verify by compiling in exactly this case, and agents that followed them lost nothing.
  • For broad, common-word conceptual queries, plain grep-and-read still occasionally won — consistent with the tool's own guidance that it shines on specific technical asks.

Bottom line

For a single developer, this tool is a good lookup accelerator. For an orchestrated fleet, it's load-bearing: it halved the research spend, twice redirected tasks before wasted work, prevented at least one silent-divergence shipped bug, and turned code-quality hygiene from a hope into a per-task mechanical gate. Whole-workflow ~2×; per-lookup 10–25×; and two moments where one call was worth more than the rest of the session's tooling combined.

The model's own report of one engagement, on a version before 0.5; not a controlled measurement. Controlled measurements are in docs/EVALS.md.

Fifty years of software-engineering results, and research from last month. 49 repositories and 76 papers folded — McCabe (1976) through to seven published in the last two months — each row in docs/LINEAGE.md naming the lesson taken and the file it lives in

Beside those sits a labelled survey of 237 tools that contributed nothing and says so. The two sets are disjoint by construction, so they add rather than nest — a tool that gave a lesson is never counted twice.

Both halves are load-bearing, and they are doing different jobs. The settled results are what make the quality lens trustworthy: McCabe on complexity (1976), Halstead on volume (1977), Spärck Jones on term specificity (1972), Nagappan & Ball on churn. Fifty years of replication means those are not opinions, and a tool that measures your code should be built on the ones that survived.

The recent work is what makes it current: seventeen of the folded papers are from 2026, seven published in the last two months and three in the last thirty days (dates as of 2026-09-08; every row carries its arXiv id, so the claim is checkable rather than atmospheric). Retrieval for coding agents, context-compression cost, placebo-controlled localization — that literature is months old, not decades, and several rows were folded within weeks of the paper appearing.

Neither half alone would be enough. A tool built only on the classics would not know what an agent needs; one built only on last month's preprints would have nothing underneath it. And the newest row is a result that failed when it was tested here — which is the point of writing them down. All three counts are re-derived from that document's own tables by test/readmedriftcheck.sh on every run, which fails if this page and those tables disagree, so the claim cannot quietly drift. The row-by-row ledger is docs/LINEAGE.md.

Languages: Rust · C++ · Objective-C/C++ · C · Metal · CUDA · Python · Go · Swift · TypeScript · JavaScript · Java · Ruby · PHP · Lua · Elixir · Dart · Kotlin · GDScript · Clojure · Bash · C# · JSON · TOML · YAML · Markdown — see language support and limits.

Latest: 0.6.9 — hedged or declined answers instead of confident wrong ones, --path that prints the bodies it names, --defs and owner-named rows. Release notes · the presentation · the changelog — with thanks to the contributors named there.


No API key. No embeddings. No index server. No daemon.

One process, no server — indexes this repository in 0.25 s using 6.6 MB, against 46.8 s and 391 MB for the graph-database MCP server it was measured against; warm queries answer in 197 ms to its 1,082 ms

Measured on 48 matched questions across django, webpack and this repository. Across all three, ripwire indexes in 0.25–0.45 s and 6.6–16.5 MB against that server's 23–52 s and 391–623 MB. The full method, the wins named one by one and the losses included, is in Against the leading graph-database code-context MCP server and docs/EVALS.md.

One binary, offline — and the same line activates the skills for every agent it finds: Claude Code · Codex · Cursor · Windsurf · Gemini · opencode · aider

One self-contained binary on your own machine, offline, installed in one line — and the same line installs and activates the task-shaped skills that teach your agent when to reach for it, not just how, for every agent it finds on the machine. If your agent can run shell commands — Claude Code, Codex, Cursor, Windsurf, Gemini, opencode, aider — it is set up the moment the install finishes; the MCP server is the optional second interface. Install it and ask it something before you finish reading this page:

RIPWIRE_REPO=redhat-et/ripwire bash -c "$(curl -fsSL https://raw.githubusercontent.com/redhat-et/ripwire/main/scripts/install.sh)"
export PATH="$HOME/.local/bin:$PATH"      # where it installed; the installer prints this line if you need it
cd your-repo
ripwire . --for="<the change you are about to make, in words>"

Every install route (prebuilt, from source, per-agent skills, hooks, the MCP server) is in INSTALL.md.

Reach for the CLI first — it is the cheaper interface. The MCP server is the optional second way in, and its convenience has a cost the shell pipe does not carry: its verb schemas sit in your agent's context every session, whether or not it calls them.

The goal: one question, one complete answer.

Terminality is the objective. Ask the codebase a question and the answer should carry everything you need — no follow-on grep, no three more whole-file reads to fill in what it left out. A call followed by three greps is the same search paid for twice: it does not save you tokens and it does not make the coding faster.

Two jobs, one goal. An agent meeting a codebase does two things, and ripwire is shaped for both:

  1. Orient — ripwire . One call gives the ranked map: the main subsystems, the program entry points, the symbols that matter most, and a count of every file the ranking left out. The most important information about a new tree, in as few calls as possible.
  2. Ask — --for, --expand, --callers, --path, … After that, each question gets a lean answer: as few bytes as it can spend while still answering the question fully, so no follow-up grep is needed.

That is the goal, and not every answer reaches it yet; the stair-steps below are how we get there.

The two stair-steps that make it reachable — honest about what is missing, priced in what it spends

Two things make that reachable in practice, and neither is the destination. Answers are honest about their own limits — a count that cannot be a total is labelled a floor, a zero means "none found" and never "none exists", every truncation is disclosed — so an answer never looks more complete than it is, and the map never degrades the code by guessing. And an answer can be given a token budget, so what one costs is something you ask for rather than discover; where a complete answer will not fit, it says it went over rather than silently dropping the row you needed.

Those two are the stair-steps: honest about what is missing, priced in what it spends. The step they climb toward is a question fully answered in one call, which is not always trivial to reach — and where it is not, the output says so rather than pretending otherwise.

How we measure the climb. A complete answer to every code question is genuinely hard, and it is reached over many releases rather than in one. Until an answer can be complete, it owes you three things: it stays bounded in size, it is fully honest about what it does not know, and it gives you a clue where to look next. So every change is judged on more than "was it right":

  • Complete answers: the share of questions answered so you can act without re-checking.
  • Honest-partial rate: how often an incomplete answer says what it is missing, instead of sounding confident.
  • False-confidence rate: wrong answers that claim to be complete — the worst case, held near zero.
  • Next-clue usefulness: for partial and wrong answers, whether following the answer's first suggested next step reaches what the question needed, within one hop. Graded blind.
  • Recoverability: for every cut answer, whether following its next= returns everything that was cut.
  • Tokens per complete answer: total tokens an agent spends across a scenario, divided by the questions it got fully answered.
  • Cut-row recoveries: how often a needed item arrived only through a continuation.

Answering comes first; efficiency is how we get there. We aim for answers about as lean as the leanest tools, but a byte budget is never allowed to cost an answer. We have gone too far toward short answers before and cut information that was needed, so now every size limit has one of two jobs: a runaway guard far above typical answers (it catches an output bug, and says so when it trips), or a stair-step target where anything over it must be explained. A capped answer is always measured against the same answer uncapped, and the one that answers better wins.

A cut is only honest if you can get the rest back. Disclosing a cut is the floor, not the bar: a capped answer plus the next= it prints must return everything the uncapped answer had. A test that checks this for every budget option on every command is being built, so that "the answer was cut" always comes with "and here is the rest".

What a budget may spend first. When a budget is tight, ripwire drops bytes that carry no answer before it drops answers: repeated legend text, duplicated paths, bodies whose signature already says what you need. Only then the lowest-ranked rows, each counted. A whole section never disappears without a marker.

Budgets are yours to set, never ours to impose. The defaults and the setups ripwire wrap writes carry no budget that cuts a typical answer; a budget is a choice the caller makes, with its cost disclosed.

Efficiency is counted per complete answer, across the whole task. Bytes per answer can fall while the task gets more expensive, if the agent then has to ask again. So the efficiency figure is tokens per complete answer over a whole scenario — orient, then the questions that follow — not bytes per call.

Ranking is the tool's best guess; the next= is the safety net — and we measure how often the net is needed. ripwire cannot always know which row answers the question; it goes by ranking. When a needed item appears only after following an answer's next=, the honesty layer saved the answer but the ranking put that item below the cut. We count those per round:

  • Recovered through next=: the item was cut but recoverable — one extra call. Driving this down is ranking work.
  • Lost: the item was cut with no marker or no working next= — a bug, fixed first.

Same answer, a fraction of the tokens — read this table first if your agent is on a budget

How these ten rows were measured — 2026-08-08, figures in ~tokens (≈ bytes/4), every ratio from a real run reproduced by the command in its row

Ten everyday moments, re-measured on this repository, 2026-08-08. Figures are ~tokens (≈ bytes/4); every ratio comes from a real run, reproduced by the command in its row — raw byte counts and exact commands in docs/EVALS.md §5.

Ordered understand → navigate → review-the-change:

Ask it Command ripwire naive read token savings
"Orient me in this repo" ripwire . ~5.6K tok ~20K–25K tok — read README.md (+docs/ARCHITECTURE.md) 3.6×–4.5×
"Where is X handled?" ripwire . --for="…" ~2.1K tok ~4.9K–20K tok — grep -rn <term> src/, then read the file it points at 2.3×–9.3×
"What do I already know?" ripwire . --recall="…" ~15K tok ~445K tok — read all 119 markdown docs this repo carries 29.2×
"Set me up for this task" ripwire . --pack-task="…" ~2.1K tok ~16K–80K tok — read every relevant file, whole 7.7×–37.7×
"Show me this one function" ripwire . --expand=SYM ~260–16.5K tok body (+~5.7K for the ranked-neighborhood bundle) ~43K–174K tok — read the whole file it lives in 2.6×–670×
"Who calls this function?" ripwire . --callers=SYM ~580 tok ~40K–52K tok — grep -rn SYM src/ (mostly noise), then open 2–3 files to sort real calls from mentions 69.2×–89.1×
"Is it safe to change this?" ripwire . --impact=SYM + --uses=SYM ~1.3K tok ~18K tok — open every direct-use file, whole 14.4×
"I have a stack trace" ripwire . --from-trace=FILE ~1.4K tok ~124K–298K tok — grep all 7 frame names, then open the innermost file(s) 86.9×–208.6×
"I changed these files — tests? blast radius?" ripwire . --situ ~410 tok ~3K–132K tok — git diff + grep -rn <syms> test/, then open the candidates 7.3×–324.2×
"Review this PR/diff" ripwire . --pr-context=REF ~1.9K tok ~4.8K–51K tok — git diff REF, then open the touched files 2.6×–27.5×
Same-correct-answer verification, and the honesty line these ratios come with

These aren't summaries that gamble with information. Each row is scored same-correct-answer-or-it-doesn't-count, and both sides were checked, not assumed: orient surfaces this repo's own pipeline files (ingest.cpp, graph.h, serialize.h) in the first screen, the same three docs/ARCHITECTURE.md names as central; the --for row lands mcpStale (src/mcpindex.h:633), the actual staleness check, 5th-ranked; --recall lands the container-rule doc (AGENTS.md) that states, verbatim, the same "no std::map" rule CONTRIBUTING.md explains in full; --pack-task names the same three touch points a human would — cachelint.h, mergeCachePack (src/main.cpp:1787), the lintrules.h helpers it reuses; --expand hands back the requested function's complete, unmodified body — the ranked-neighborhood addition costs the same ~22.6 KB regardless of which function you ask for, confirmed on two (a fixed floor, not per-function variance); --callers on langOfPath names its 2 real callers, the same ones a grep hit-list buries under 5 files of comment-only mentions; --impact+--uses on coversOrEquals names the same 2 direct call sites --uses alone would, plus (disclosed) a transitive reach --uses doesn't cover at all; --from-trace resolves all 7 frames of a real call chain by name to the same definitions a per-frame grep would eventually find, mixed with call sites and comments; --situ on a 2-file diff names the same 6 real test harnesses, 2 of which a filename grep across test/ cannot find even after opening every one of its 41 candidates — a completeness gap, not just a byte one; --pr-context surfaces co-change partners (test/regression.sh, src/main.cpp) a raw git diff has no way to know were usually touched and weren't this time — Fowler's Shotgun Surgery checked rather than merely named (the backtest is in docs/EVALS.md). The map ranks and discloses — it never paraphrases your code — and every truncation is disclosed in the header.

The honesty line, made concrete: the same auto-selection behind the --expand row also runs the other way. On a small file (pageRankDouble in src/pagerank.cpp, 5,559 B) the ranked bundle would cost 27,916 B — nearly 5× more than the file — so ripwire serves the file itself instead, disclosed as mode="whole-file" on the response, not silently. docs/EVALS.md §7 lists that and the other counterexamples this project publishes against itself.

Where those savings compound: an orchestrator that matches tasks to models — every lane it spawns starts cold on the same tree. The map is the one artifact that does not have to be rediscovered per agent, and the quality verbs hand a verdict back instead of a pile of files to re-read

The shape: plan the work, then run a loop that matches each task to the model that fits it. Every thread it spawns opens with an empty context on a repository it has never seen. Orienting an empty context to a large repository is the most repeated cost in the whole system, and the one this tool was built for — it is also the cost that grows with the size of the tree, which is why the pattern matters more the bigger the repository gets.

--for and --pack-task answer it in a single call, at the per-call rates in the table above, instead of a grep-and-read tour that every lane pays over again from scratch.

The return path matters as much. --quality-delta, --test-gate and --edit-check answer what did I make worse, which tests must run, did I change a contract — quantitative answers a lane can hand back as a verdict, rather than a transcript the orchestrator has to read to find out what happened.

What is and is not claimed here. Every figure on this page is a single-agent measurement. That the saving compounds with the number of cold orientations follows from the fixed-cost mechanism, but it is pre-registered and unrun — the reason the pattern is worth trying, not a result this project has published.

See the map — not just the numbers

ripwire --html on Django's migration autodetector: 120 symbols, 183 call edges, arrows pointing caller to callee, nodes coloured by cyclomatic complexity on a five-stop scale running deep blue, mid blue, amber, orange, pale yellow, module outlines drawn as translucent regions, and low-confidence call edges drawn with dashed shafts

Django's migration autodetector, coloured by complexity. Thresholds are fixed, so the colour means the same thing on every repo you point it at.

ripwire path/to/django/db/migrations --rank-by=rrf --top-k=120 --color-by=cx --html=map.html
The same graph twice: above coloured by cyclomatic complexity, below by git commit count. Most nodes sit in a different colour band between the two. A close crop showing solid and dashed call edges side by side; dashed shafts mark calls the resolver could not pin to a single target
The same graph, re-coloured by git churn. 76% of these nodes move to a different band — structure and history disagree, and one run shows you both. A dashed shaft is a guess. 31 of 183 edges here are one arm of a split the resolver could not choose between. No other tool marks which of its arrows it is unsure about.
How to read these pictures — the five lenses, the fixed thresholds, and what the renderer refuses to draw

One self-contained HTML file (--html[=FILE]), no server, no CDN, no external asset. --color-by=lang|community|cx|churn|tested sets the initial colour; the page embeds all five and keeps a live selector, so switching lens costs no second run.

Read from the figures above, which state their own rules in a sidecar saved beside each image:

  • arrow points caller → callee — the graph is directed, and the page draws it that way.
  • 31 of 183 shafts dashed in this view = the resolver could not choose between same-name definitions and split the call over all of them — per edge, not per symbol. A symbol-level "this function makes some ambiguous calls" would mark every one of its edges, which would be a lie about most of them.
  • labels: top 24 by in-view degree, one per name — one label per distinct name, so a picture of a container class stops crowding out the functions you asked about.
  • shapes: ● fn ■ cls ✚ var — kind is nominal data on a nominal channel; complexity never uses shape.
  • module outlines: 7 of 12 modules with 3+ nodes in view (cap 12; 3 dropped as too thin to read as a region; 2 dropped as enclosing mostly other modules) — three separate truncations, each with its own count and its own reason.

The cx and churn ramps share one five-stop scale, ordered so lightness rises with the value — it survives greyscale printing, and every adjacent pair stays separable under protanopia, deuteranopia and tritanopia. Thresholds are fixed rather than per-corpus quantiles, so a hot node cannot be manufactured by a cold repository.

churn needs real git history: a shallow clone reports every file as one commit, and a directory with no repository says churn unavailable rather than drawing zeros.

One deterministic answer: the relevant symbols, their callers, the change risks, and the tests that reach them. The task is yours to phrase — ask about your code, not ours. Run on this repository (2026-08-30), ripwire . --for="incremental cache invalidation" produced about 4.3K estimated tokens, not an enforced token budget. It includes:

This is what the output looks like, not the token-savings recipe. A bare --for on every question is the most expensive way to use this tool — see Where it pays most and the three controls below it.

What those 4.3K tokens actually contain — ranked symbols with their doc comments quoted in place, risk annotated on every row, one-hop callers, and the answer's own confidence=
  • The ranked symbols, in rank order — the cache-header constant kCacheMagic first (with its doc comment quoted in place and the one next= call that opens it), then spanTierMemoPath (the cache-path composer), ingestCommitTree, … ingest — each row with its file, line, and signature.
  • Risk, annotated in place — complexity, git churn (ingest shows 128 recent edits), change amplification (touch ingest and 266 graph nodes feel it), purity and test coverage. The fragile spots are visible before anything touches them.
  • One-hop call context — spanTierMemoPath calls shaKeyedCachePath, headSnapRepoHex, exclConfigHex; no second query needed to see the neighbourhood.
  • Its own confidence — this answer says confidence="high" with the score margin attached; a flat ranking says low, so it reads as a starting point instead of masquerading as an answer. confidence= measures how clearly the ranking separates its head from the rest, not whether the head is what you meant: ask a repository about a concept it does not contain and the best lexical matches still rank, confidently. Phrase the task in your code's own words.
The actual wire format — what your agent reads (minified XML; trimmed and line-wrapped here)
<ctx task="incremental cache invalidation" confidence="high" margin_pct="20"
     bundle="compact" bodies="0" reason="compact-route" est_tokens="3995">
  <sigs shown="23" total="40" capped="1">
    <d l="106" n="kCacheMagic" p="src/ingest_cache.h" cx="0" in="0" churn="11" amp="71" pure="1" r="1"
       next="--expand=src/ingest_cache.h:kCacheMagic">
      <doc>incremental cache (--cache): per-file content hash + raw facts so a
           re-run re-parses ONLY …</doc>constexpr std::uint32_t kCacheMagic = …</d>
    <d l="1307" n="spanTierMemoPath" p="src/ingest_astquery.h" cx="1" in="2" churn="5" amp="44" r="2"> … </d>
    <d l="247" n="ingestCommitTree" p="src/dmm.h" cx="6" in="1" churn="6" amp="27" r="3"> … </d>
    …
    <d l="191" n="ingest" p="src/ingest.cpp" cx="4" in="14" churn="128" amp="266" tested="1" r="13"> … </d>
    … </sigs>
  <hops shown="2" total="6" capped="1" noedge="2">
    <h l="1307" p="src/ingest_astquery.h" n="spanTierMemoPath">
      <calls total="3"><c n="shaKeyedCachePath" l="1621"/> … </calls></h> … </hops>
</ctx>

cx= complexity, churn= git edit frequency, amp= change amplification, r= rank; <hops> rows carry the one-hop call context, caps disclosed. The one legend at the top of the real output defines them — tersely by default, every reading in full with --legend=full — and the root self-reports the bundle's cost — est_tokens="3995" here.

The agent without a map The agent with ripwire
greps a common word, gets hundreds of hits across dozens of files one ranked answer — est_tokens="3995" on this repository (re-derived 2026-09-05, the run above)
reads whole files to find the symbols that matter those symbols, with complexity, churn and test coverage inline
finds the callers only if it thinks to grep for them too callers, blast radius and the tests to run, in the same bundle
pays for every line it read, right or wrong 5.0% of what that grep-and-read pass spends — on a 12-question set where it strictly satisfied 5 to the naive arm's 11 (re-derived 2026-08-23)

Both halves of that sentence, because one without the other is an overclaim. The 5.0% is context compression, not equal task completion: on the five questions both arms strictly satisfied, ripwire spends 5.2% of what the naive pass spends. Cheap context that answers less is not a saving if the agent then retries. The full adjudication, the four ranking defects behind the eleven-to-five gap, and the run where this number got worse are in Measured.

58.3% of instances with all gold files in the top 10 — the best alternative lands 40.0%, while indexing in 0.31 s

And against five retrieval competitors on a held-out LocBench slice, it finds all gold files in the top 10 on 58.3% of instances — the best alternative lands 40.0% — while indexing in 0.31 s. The full leaderboard, losses included ↓

Against the leading graph-database code-context MCP server

Won 27 · lost 7 · tied 14 on 48 matched questions across django, webpack and this repository, spending ~77K tokens against its ~486K for the whole sweep.

The full result, how it was measured, and where ripwire still loses

The 48 questions span symbol lookup, conceptual search, blast radius, and one-call task orientation. ripwire indexes the same three repositories in 0.25–0.45 s and 6.6–16.5 MB, against that server's 23–52 s and 391–623 MB, and answers a warm query in a median 197 ms against its 1,082 ms. Its seven wins are real and named one by one in the method.

…

Releases

Recent versions

All releases ↗
  1. v0.6.9
  2. v0.6.8
  3. v0.6.7
  4. v0.6.5
  5. v0.6.4
  6. v0.6.3