Your Coding Agent Doesn't Need More Context. It Needs the Right Kind.
AI coding agents have developed an expensive habit.
When they don't understand a codebase, they read more of it.
That works surprisingly well on a 30-file side project.
On a mature repository, it's the software equivalent of solving a library question by reading every book.
The obvious fix is indexing.
Parse the repository once, keep the index fresh, and give the agent only the context relevant to its current task.
Two open-source projects approaching this problem caught my attention recently: CocoIndex Code and code-review-graph.
At first glance they look remarkably similar.
Both parse code with Tree-sitter.
Both update incrementally.
Both integrate with coding agents through MCP.
Both are trying to stop agents from stuffing unnecessary source files into already-expensive context windows.
But after digging into how they work, I don't think they're really solving the same problem.
My mental model is simpler:
CocoIndex is better positioned to answer “Where is the code related to this concept?”
code-review-graph is designed to answer “What does this change affect?”
That distinction becomes important the moment your coding agent graduates from finding code to modifying it.
Here are the official GitHub repositories for both:
- CocoIndex Code: github.com/cocoindex-io/cocoindex-code — AST-based semantic code search for coding agents. (GitHub)
- code-review-graph: github.com/tirth8205/code-review-graph — structural code intelligence and blast-radius analysis for coding agents. (GitHub)
The Context Window Isn't a Codebase
Suppose I ask an agent:
Find where session expiration is handled.
A naive agent can search filenames, grep for session, inspect likely files and progressively build context.
That's reasonable.
Now ask:
I'm changing
validateSession(). What else could this break?
That is a different problem.
Semantic similarity can help locate authentication-related code.
But the most semantically similar function isn't necessarily the function that calls validateSession(), implements the same interface, depends on its return contract, or contains the test that catches the regression.
This is where code intelligence starts splitting into two different retrieval models.
CODEBASE
│
┌────────┴────────┐
│ │
▼ ▼
SEMANTIC VIEW STRUCTURAL VIEW
│ │
"What looks "What depends
relevant?" on what?"
│ │
▼ ▼
CocoIndex code-review-graph
That diagram is deliberately simplified. CocoIndex is AST-aware, and code-review-graph also has search capabilities.
The difference is what each system makes first-class.
CocoIndex Treats Code Retrieval as an Indexing Problem
CocoIndex itself is an incremental data-processing engine for AI workloads. Its code-indexing pipeline walks a repository, uses Tree-sitter to split source along syntax boundaries, embeds those chunks and maintains the resulting index as files change. The underlying incremental processing engine is implemented in Rust.
The packaged developer tool is CocoIndex Code, exposed through the ccc CLI.
Its core operation looks refreshingly unsurprising:
ccc index
ccc search "where is session expiration handled?"
Through MCP, the primary search operation accepts a natural-language query or code snippet and returns matching chunks with their file paths, languages, line numbers and similarity scores. It can also filter by language and path.
Architecturally, the important path looks roughly like this:
Repository
│
▼
Tree-sitter
│
▼
AST-aware chunks
│
▼
Embedding model
│
▼
Vector index
│
▼
Semantic query
│
▼
Top-K relevant code
│
▼
Coding agent
This is a very useful abstraction because code search is often conceptual.
I may not know whether the repository calls something:
SessionManager
AuthContext
TokenValidator
IdentityService
AccessPolicy
I only know what I'm trying to find:
Where do we decide whether a user is still authenticated?
Semantic retrieval is excellent at bridging that vocabulary gap.
Traditional grep isn't.
CocoIndex Code also includes a structural ccc grep mode that matches syntax-tree patterns without requiring the embedding index, so the tool isn't exclusively vector retrieval.
Still, semantic retrieval is the center of gravity.
And for exploration, that's a very sensible center.
code-review-graph Starts Somewhere Else
code-review-graph parses the repository with Tree-sitter too.
But instead of primarily turning syntax units into searchable embeddings, it persists the repository as a structural graph.
Its nodes represent things such as:
Files
Functions
Classes
Methods
Tests
and its edges capture relationships such as:
imports
calls
inheritance
test coverage
dependencies
At review time, the system can traverse those relationships to calculate the blast radius of a change.
Conceptually:
┌──────────────┐
│ API Handler │
└──────┬───────┘
│ calls
▼
┌────────────────┐
│ validateSession│
└───────┬────────┘
│
┌───────────┼────────────┐
│ │ │
called by tested by imports
│ │ │
▼ ▼ ▼
AuthGuard SessionTests TokenStore
│
▼
Route Group
Now changing validateSession() isn't merely a search query.
It's a graph traversal.
That's a materially different operation.
Similar Code and Dependent Code Are Not the Same Thing
This is the part I think matters most.
Imagine this TypeScript service:
export async function calculateFare(
itinerary: Itinerary,
pricingContext: PricingContext
): Promise<Fare> {
// pricing logic
}
A semantic index queried with:
Find fare calculation logic.
should do very well.
It can retrieve calculateFare, adjacent pricing functions, perhaps tax calculations and discount logic.
Now change the question:
If I modify the return contract of
calculateFare, what needs review?
The answer might include code that isn't semantically about fare calculation at all:
BookingController
CheckoutMapper
PaymentRequestBuilder
AncillaryPricingAdapter
AnalyticsPublisher
FareContractTests
AnalyticsPublisher may have almost no semantic resemblance to calculateFare.
But if it consumes the output, I care.
A lot.
That's why I wouldn't frame this comparison as:
vector database vs. graph database.
The more useful distinction is:
similarity retrieval vs. dependency retrieval.
One answers relevance.
The other answers consequence.
CocoIndex Is the Tool I'd Reach for During Exploration
Suppose I've just opened an unfamiliar repository.
My questions are usually fuzzy:
Where is authorization enforced?
How are payment retries implemented?
Where does this service publish events?
Find the logic responsible for refreshing tokens.
I don't yet know the symbols.
I don't know the architecture.
Sometimes I don't even know which package owns the behavior.
This is exactly where semantic search earns its keep.
CocoIndex Code's index is built from syntax-aware chunks rather than arbitrary windows of text, and incremental updates mean changed files can be reprocessed without rebuilding the whole repository index. CocoIndex's official example describes live filesystem indexing where only changed chunks are re-embedded and upserted.
For an agent doing exploration, that produces a clean loop:
Question
│
▼
Semantic search
│
▼
5 relevant chunks
│
▼
Agent reads them
│
├── enough context? ──► reason
│
└── not enough? ─────► search again
That is much healthier than:
Read src/
Read services/
Read auth/
Read utils/
Read another 14 files because we're here anyway
The CocoIndex Code repository currently advertises a 70% token-saving figure. I would treat that as a project-reported result, not a universal constant: retrieval efficiency depends heavily on repository shape, query quality, embedding model and what the agent would otherwise have read.
The architecture is more interesting than the headline percentage anyway.
code-review-graph Is the Tool I'd Reach for Before Changing Something Dangerous
Now suppose I've found the code.
The ticket says:
Change session validation so revoked device tokens are rejected immediately.
Finding validateSession() isn't the difficult part anymore.
The difficult questions are:
Who calls it?
Which execution flows pass through it?
What tests exercise those callers?
Is another module depending on behavior that isn't obvious from the function signature?
Does this change cross a community or subsystem boundary?
That's where code-review-graph becomes interesting.
Its detect_changes and review workflows map changed code to affected functions, flows and tests. The project also exposes architecture maps, execution-flow tracing, community detection, refactoring tools and risk-scored reviews.
Instead of:
Changed file
│
▼
Search for similar files
the reasoning becomes:
Changed symbol
│
▼
Direct callers
│
▼
Transitive dependents
│
├──────────► Tests
│
├──────────► Entry points
│
└──────────► Cross-module edges
│
▼
Impact radius
│
▼
Agent review context
For code review, I prefer that mental model.
A pull request isn't asking:
What code resembles this diff?
It's asking:
What assumptions did this diff disturb?
The Token Benchmarks Need a Footnote the Size of a Small Service
code-review-graph publishes some dramatic context-reduction numbers.
Its current README reports roughly 82× median per-question reduction across six benchmark repositories when comparing graph query context against reading the entire source corpus, with results ranging from 38× to 528×. The repository itself explicitly warns that the whole-corpus baseline is an upper bound because a competent coding agent wouldn't normally read every source file.
I appreciate that qualification.
The project maintains several benchmarks precisely because “tokens saved” changes meaning depending on what baseline you choose.
Its documentation distinguishes:
- whole-repository reading,
- a more realistic grep-and-read agent,
- changed-file context for reviews,
- and complete MCP workflow cost.
That matters enormously.
If I change three lines in a 90-line file, a graph response containing impact edges, source snippets and test relationships could actually be larger than simply handing the changed file to the model.
The project documents this too: its formal review-context benchmark can produce ratios below 1 for small commits because the structural metadata itself has a cost.
That's exactly the kind of boring benchmark detail I trust more than another giant “500×” badge.
Context optimization isn't:
graph = fewer tokens.
It's:
graph = spend tokens describing relationships when those relationships are worth more than their serialization cost.
At 10× Repository Size, the Difference Gets More Interesting
Take a repository that grows from 300 files to 3,000.
Then 30,000.
Semantic retrieval has a pleasant property: the agent can still ask for the top few relevant chunks.
The corpus becomes larger, but the final context doesn't necessarily grow linearly.
That's good.
A structural graph has a different scaling challenge.
More code creates more:
nodes
edges
communities
call paths
cross-module dependencies
test relationships
But that additional structure is precisely what becomes valuable in large systems.
code-review-graph reports incremental updates based on changed-file hashes and graph relationships. Its current documentation describes a roughly 3,000-file Django repository where a two-file edit re-indexes in about 2.5 seconds on the hook path, with a substantial portion of that being process startup.
CocoIndex attacks the same freshness problem from the indexing side: only changed chunks need to be reprocessed and re-embedded rather than rebuilding the corpus.
So both systems understand the same production truth:
A code index that becomes stale every time somebody presses Save isn't code intelligence. It's documentation with impressive latency.
Incrementality isn't an optimization here.
It's part of correctness.
Freshness Is a Consistency Problem
This deserves more attention.
Imagine an agent retrieves an index saying:
PaymentService → LegacyFraudClient
but three minutes ago another developer changed the code to:
PaymentService → RiskGateway
The index is now lying.
That can be worse than having no index because the agent doesn't know its context is stale.
Both tools address this through incremental updates.
CocoIndex Code can refresh the semantic index and its agent integration can keep indexing current as the repository changes.
code-review-graph offers watch mode and hooks that update the structural graph after file changes or commits.
Architecturally, I think this should be treated like cache consistency:
Source code
│
│ change
▼
Change detector
│
├────────► Semantic index update
│
└────────► Graph update
│
▼
Agent query
The agent should never have to wonder whether yesterday's architecture is answering today's question.
Where CocoIndex Starts Hurting
Semantic search is not dependency analysis.
That's the boundary I'd keep in mind.
If I ask:
What code is related to payment retries?
CocoIndex is a natural fit.
If I ask:
Can changing this retry function alter checkout behavior for callers that never mention retries?
semantic similarity alone isn't enough.
You can compensate by retrieving more context, searching symbols, letting the agent inspect imports and iterating.
But at some point you're asking the LLM to reconstruct a dependency graph on demand.
That's expensive.
And slightly absurd when a parser can build one deterministically.
The other operational cost is embeddings.
CocoIndex Code supports local SentenceTransformers or cloud embedding providers through LiteLLM. Its full local installation brings heavier dependencies, while the slim installation expects a cloud embedding provider.
That's not a criticism.
Semantic retrieval requires a semantic representation somewhere.
But it means your architecture has another dimension:
Local embeddings
├── privacy friendly
├── offline capable
└── model/runtime footprint
Cloud embeddings
├── lighter local setup
├── potentially stronger model choice
└── code/privacy/network boundary
For enterprise repositories, that trust boundary isn't a footnote.
It's architecture.
Where code-review-graph Starts Hurting
Graphs aren't free either.
A static graph can tell me:
A calls B
B imports C
D tests B
But production software contains relationships that static analysis struggles to prove.
Dependency injection.
Runtime reflection.
Dynamic imports.
Framework magic.
Configuration-selected implementations.
Message brokers.
Database-driven workflows.
HTTP calls whose relationship exists in configuration rather than source syntax.
A TypeScript service can publish:
eventBus.publish("payment.completed", payload);
while the consumer lives in another repository.
Your local AST doesn't magically know that relationship.
A graph can only be as correct as the relationships it can observe or infer.
This is why I would never interpret blast-radius analysis as:
These are all the things this change can affect.
I would interpret it as:
These are the dependencies the graph can prove or reasonably model.
That's still extremely useful.
It's just not omniscience.
Security Changes the Recommendation
For sensitive repositories, both tools have an attractive property: they can operate locally.
code-review-graph stores its core graph in a local SQLite file under .code-review-graph/, and its documentation warns that exported graph data can contain absolute paths and structural metadata that should be sanitized before publishing.
CocoIndex Code can use local embeddings, meaning source doesn't need to leave the machine for embedding generation. It also supports cloud providers when teams prefer them.
If I were deploying either in a regulated environment, I'd still review:
Source access
│
├── Parser process
├── Embedding provider
├── Local index
├── MCP transport
├── Agent process
└── Export / telemetry behavior
“Local-first” is a useful property.
It isn't a substitute for a data-flow review.
I Wouldn't Actually Choose One for Every Workflow
After comparing them, I think asking:
CocoIndex or code-review-graph?
is slightly the wrong architecture question.
For an agent that spends most of its time discovering implementations, I'd start with CocoIndex Code.
For an agent heavily involved in PR review, refactoring and impact analysis, I'd start with code-review-graph.
For a serious engineering agent, the architecture I find most interesting is actually:
Developer Question
│
▼
Coding Agent
│
┌─────────────────┴─────────────────┐
│ │
▼ ▼
Semantic Retrieval Structural Retrieval
│ │
CocoIndex Code Graph
│ │
"What code is "What code is
relevant?" connected?"
│ │
└─────────────────┬─────────────────┘
▼
Context Composer
│
▼
Minimal useful context
│
▼
Agent reasoning
Now we have two independent signals.
Semantic similarity gives us meaning.
Graph traversal gives us relationships.
Those signals can disagree.
That's useful.
If semantic search says a function is relevant but the graph shows no dependency path from the changed code, maybe it's conceptual context rather than blast radius.
If the graph says a distant analytics module is affected but semantic similarity ranks it near zero, that's exactly the kind of non-obvious dependency I want surfaced during review.
The disagreement is information.
The Best Retrieval System Might Be a Router
I wouldn't blindly query both systems for every prompt.
That's how an optimization becomes another token bill.
I'd route based on intent.
"Where is X implemented?"
│
▼
Semantic Search
"What calls X?"
│
▼
Graph Traversal
"What breaks if I change X?"
│
▼
Graph + Tests
"How does authentication work?"
│
▼
Semantic + Execution Flow
"Find code similar to this pattern"
│
▼
Semantic / Structural Search
"Review this PR"
│
▼
Diff → Graph Blast Radius
│
▼
Semantic Search for Missing Context
This is the architecture I'd want in an agent.
Not one enormous context engine.
A context router that knows what kind of evidence the current question requires.
Because “relevant” is not one thing.
When I'd Choose Something Simpler
Neither tool is automatically necessary.
If your repository has 80 files, clear module boundaries and predictable naming, rg, language-server references and an agent that knows how to search may already be enough.
Adding embeddings, an indexing daemon, MCP configuration and persistent graph state just to save 900 tokens is infrastructure cosplay.
I'd introduce code intelligence when I can identify an actual retrieval problem:
- agents repeatedly reading irrelevant files,
- large monorepos exhausting context,
- developers struggling to discover implementations,
- PR reviews missing transitive impact,
- cross-module refactors becoming dangerous,
- onboarding requiring archaeological expeditions through the repository.
The architecture should pay rent.
Otherwise, grep remains one of the best pieces of developer infrastructure ever shipped.
What I'd Deploy
If my primary problem were agent exploration, I'd deploy CocoIndex Code first.
Its semantic model maps naturally to the fuzzy questions developers ask when they don't yet understand a repository.
If my primary problem were change safety, I'd deploy code-review-graph first.
Call relationships, tests and blast-radius analysis are closer to the evidence I want before modifying production behavior.
For a mature platform with heavy AI-assisted development, I'd eventually want both capabilities behind one retrieval layer.
Something like:
┌─────────────────────┐
│ Agent Query │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Intent Classifier │
└──────────┬──────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
Semantic Structural Hybrid
Search Graph Query
│ │ │
└─────────────────┼─────────────────┘
▼
┌─────────────────────┐
│ Context Budgeter │
│ rank · dedupe · cap │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Coding Agent │
└─────────────────────┘
The interesting engineering problem then stops being indexing.
It becomes context selection.
Which evidence deserves 500 tokens?
Which dependency edge is important enough to include?
When should semantic similarity override structural distance?
When should the agent expand another hop?
When does the retrieval cost exceed the cost of simply reading the file?
Those are the questions I expect code-agent infrastructure to spend a lot more time solving.
Because a larger context window doesn't eliminate retrieval architecture.
It just makes bad retrieval more expensive before anyone notices.
CocoIndex and code-review-graph approach the problem from different directions.
One asks:
What code means something similar to what you're looking for?
The other asks:
What code is structurally connected to what you're touching?
For exploration, meaning often wins.
For change safety, relationships often win.
And for serious codebase intelligence, I don't think the future is choosing between them.
It's knowing which kind of context to ask for before the agent starts reading.
Top comments (0)