TL;DR — Today’s coding agents waste roughly 95% of their context budget on grep-and-read passes that return noisy, redundant, and stale code. Repository-mapping CLIs compute a ranked call graph, blast radius, and test selection up front, replacing megabytes of file bodies with kilobytes of signed summaries. The result is faster agents, cheaper runs, and edits that survive review.
The grep tax on every coding agent
If you have watched a coding agent work on a non-trivial repo, you have probably seen the loop. It runs grep -r across the tree, reads a dozen candidate files in full, quotes large swaths back into its own context, and only then proposes an edit. When the edit fails a build, it does the same thing again — same greps, same reads, same duplicated snippets.
This pattern is not unique to one vendor. Claude Code, the Codex CLI, Cursor’s agent mode, Aider, and Continue all converge on the same default behavior: treat the repo as opaque text, query it liberally, and re-read whatever the model needs. The cost shows up in three places:
- Token spend. A grep-and-read pass on a mid-sized service can return 50–200 KB of source. At Claude Sonnet pricing that is a non-trivial fraction of a single turn, and most agents do several passes per task.
- Latency. Every read is a round trip through the model’s context window. The agent is not slow because the model is slow; it is slow because it keeps re-reading.
- Edit quality. Grep returns lines, not semantics. Two functions that share a name both come back. A deprecated helper and its modern replacement both come back. The agent edits the wrong one often enough that humans have learned to double-check every diff.
This is the problem space that ripwire — the project that inspired this post — is built around. Its README describes it as “the ripgrep of AI context,” and the framing is honest: ripgrep replaced ad-hoc grep pipelines for human readers; ripwire wants to do the same for agents.
What a repository map actually is
A repository map is a structured, ranked summary of a codebase that an agent can consume before it reads a single file body. The minimum useful map contains four things:
- Signatures. Function, method, and class declarations with their parameter types and return types. No bodies, no comments, no whitespace.
- Call graph. Which function calls which, with edges weighted by call frequency or recency.
- Blast radius. For any given symbol, the transitive set of callers and the files they live in. This is what tells the agent what its edit will break.
- Test selection. For any change, the smallest set of tests that exercise the affected code paths.
Optional but valuable: import edges between modules, ownership metadata (who owns this file, which team, which service), build-system targets, and quality deltas (lint findings, coverage gaps, complexity regressions) computed against a baseline.
The numbers in ripwire’s README are striking. Signatures come out at roughly 20% the byte size of source bodies, and a complete map can replace about 95% of the tokens a typical grep-and-read pass consumes. On a real Rust codebase that means the difference between shipping 800 KB of context to the model and shipping 40 KB.
Why signatures alone win
The strongest design choice in any repo-mapping tool is to emit signatures and edges before anything else. There are two reasons.
First, signatures are deterministic. A function parse_config(path: &Path) -> Result<Config> has the same shape regardless of who reads it and when. Bodies change, comments rot, formatting churns — but the typed contract does not. That makes signatures safe to cache, hash, and diff across commits.
Second, signatures are dense. The interesting information in a function (its name, its inputs, its outputs, its errors) fits on one line. The uninteresting information (its implementation, its whitespace, its inline comments) takes the other forty. When you compress for an LLM, you want the line, not the forty.
This is not a new insight. The same principle shows up in cscope (1998), ctags, lsif.dev, and the Salsa incremental computation framework that powers rust-analyzer. What is new is that the consumer is now an LLM, and the unit of cost has shifted from CPU cycles to tokens.
Architecture of a zero-dependency mapper
Ripwire is written in C++23 with zero external dependencies, ships as a single binary plus an MCP server, and clocks in at a few hundred milliseconds on a million-line repo. The architectural choices behind that profile matter, because every one of them is a decision you would make differently if you were building for humans.
Single-binary, zero-dependency
A repo mapper is infrastructure for an agent. The agent is going to invoke it dozens of times per session. Anything that requires a runtime, a package manager, or a network connection is friction. C++23 with std::expected, std::flat_map, and the new ranges adaptors gives you most of what Python or Rust would give you, with a static binary that drops onto any container.
The same logic is why ripgrep itself is written in Rust with no runtime, why fd ships as a single binary, and why the Bun team chose to bundle a JavaScript runtime into one executable. The toolchain for agent infrastructure is converging on “one file you can curl and run.”
Build once, query forever
The map is computed once and stored. Subsequent invocations diff against the cached map and only re-analyze changed files. This is the same idea as Bazel remote caching or Turbo’s task graph, applied to a different artifact. The incremental cost of a second pass is proportional to the diff, not the repo size.
MCP server alongside CLI
The Model Context Protocol has settled into the de facto standard for agent-to-tool wiring. A mapper that exposes itself as both a CLI (for humans and CI) and an MCP server (for agents) hits both audiences without forcing a translation layer. The same binary answers ripwire map src/ for a developer and tools/call map {"path": "src/"} for Claude.
Patterns in production: how a map changes agent behavior
The most interesting question is not how the map is built but how an agent uses it. Three patterns are emerging.
Targeted reads instead of broad greps
Instead of grep -r "parse_config", the agent calls map --symbol parse_config and gets the definition site, the call sites, the tests, and the blast radius in one structured response. The model now reasons about five locations instead of fifty, and it knows which five are real.
Edit-then-verify in one turn
With the blast radius and test selection in hand, the agent can propose an edit, run the affected tests, and report a green build without leaving the conversation. This collapses the edit-test-fix loop from multiple turns into one, which is the single largest latency win in agentic coding.
Reviewer-grade diffs
A signature-level diff tells a human reviewer what changed at the contract level before they read the bodies. This is the same trick code coverage tools use to prioritize which lines to inspect, and it generalizes to AI-generated patches. Reviewers stop rubber-stamping agents and start trusting them on the parts that matter.
The economics shift when context gets cheap
The deeper implication is economic. Today, the marginal cost of asking an agent to “look around the repo” is high enough that product teams ration it. New engineers, support tickets, and incident response all compete for the same budget. When the cost drops by 20x, that rationing disappears.
- Onboarding. A new contributor can ask an agent “walk me through the billing subsystem” and get a structured answer instead of a wiki that is six months out of date.
- Incident response. During an outage, the on-call can ask “what calls
retry_charge?” and get an answer in seconds, with the affected deploy and the relevant dashboards linked. - Code review at scale. Reviewers can ask “what is the blast radius of this PR?” and get a ranked list of files and tests, instead of inferring it from the diff.
- Migration projects. A team porting a service from one framework to another can map the old API surface once and plan the migration as a graph walk.
None of these workflows are new. All of them were previously gated on human time. The map is what unblocks them at machine speed.
Where the map ends and the model begins
A map is not free. It is a lossy projection of the codebase, and lossy projections have failure modes.
Staleness. A map is a snapshot. If the agent trusts it without checking timestamps, it will edit against reality that no longer exists. The fix is the same as it is for any cache: invalidation on commit, with a fast incremental rebuild.
False precision. A ranked call graph implies that rank matters. But a low-ranked edge can be the one that breaks production if it happens to be the call path during a specific failure mode. The agent still needs to read the body when the blast radius is ambiguous.
Language coverage. A mapper that handles C++ but not Python, or Rust but not TypeScript, is a mapper that covers half the repo. Ripwire currently targets C++, C, and a handful of others; production users typically pair it with tree-sitter parsers for the long tail.
Ownership and intent. A map knows who calls whom. It does not know why. The “why” still has to come from the model, from commit messages, from issue trackers, and from humans. The map is a scaffold, not a replacement.
These limits are not arguments against mapping. They are arguments for treating the map as one input among several, the way type checkers treat inference: useful, fast, and worth verifying at the boundaries.
How to evaluate a repo-mapping tool
If you are shopping for one, the criteria matter more than the brand. Five questions separate the good from the demo-ware:
- What is the signature-to-body ratio? Look at a real repo, not a toy. If the tool only gets you to 60% compression, the economics do not move.
- How does it handle incremental updates? A tool that recomputes the whole map on every commit is a tool you will stop using on day three.
- What is the false-negative rate on the blast radius? Miss a caller and the agent will confidently break production. Ask for a benchmark on a real PR.
- Does it integrate with your existing agents? MCP support matters, but so does the quality of the structured output. JSON-Lines that the model can stream is better than a single blob.
- How does it handle polyglot repos? Most production repos are not one language. A tool that only speaks one is a tool that forces you to maintain two.
The wider trend: tools that ship maps
Ripwire is one of several projects pushing in this direction. The category is small but growing fast:
- aider’s repo map — Paul Gauthier’s original implementation of the idea, built around tree-sitter.
- Codex’s codebase indexing — OpenAI’s hosted version, exposed through the Codex CLI.
- Cursor’s codebase context — Cursor’s proprietary variant, tuned for its own agent.
- Sweep’s repo embeddings — earlier work on graph-based retrieval for agents.
What is changing is the threshold. A year ago, a repo map was a nice-to-have for the largest codebases. Today, with token costs still nontrivial and agent runtimes multiplying, it is the default for any team that runs more than a handful of agent sessions per day.
Key Takeaways
- The grep-and-read loop is the single biggest hidden cost in agentic coding today, accounting for most of the context window and most of the latency on non-trivial tasks.
- A repository map — signatures, call graph, blast radius, test selection — replaces megabytes of source with kilobytes of structure, typically at a 20x compression ratio.
- The right tool is a zero-dependency single binary that ships as both a CLI and an MCP server, with incremental updates keyed to git diffs.
- A map is a scaffold, not a source of truth. Staleness, false precision, and language coverage are real failure modes that the agent and the human reviewer still have to manage.
- The economics of AI-assisted coding shift meaningfully when context gets cheap: onboarding, incident response, review, and migration all move from human-gated to machine-paced.