Give AI coding agents the code, not the whole file
Watch an AI coding agent fix a small bug. It searches for the function name and gets forty matches across comments, tests and unrelated files. It opens the file with the best-looking match and reads all 900 lines. It searches again for the callers, opens three more files, and reads those whole as well. The fix it finally makes is four lines.
Every one of those reads stays in the conversation, and the conversation is sent again on every turn. The agent was not careless. It used the tools it had, cat, grep and sed, and none of them can answer the question it was really asking: show me this function, and who calls it.
vulkro ctx is a code-reading server for AI coding agents. Each command answers one of those questions with just the code that answers it, as numbered lines, and it works with any agent that can run a shell command.
We have written before about the same problem in security review, where an agent that asks the scanner for findings reads far less than one that searches the code itself (security analysis without wasting tokens). This post is about everyday coding: reading, navigating and changing code, with no scan involved.
Why reading whole files is the expensive habit
An agent's context is its working memory, and it is billed. Two facts make whole-file reads costly.
First, what an agent reads stays. A 900-line file read on turn three is still in the context on turn thirty, unless the session is compacted or cleared, and every turn re-sends it.
Second, text search does not know what code is. grep findById cannot tell the definition from a call, a call from a comment, or invoiceRepo.findById from userRepo.findById. So the agent compensates by reading more, to work out from the surrounding lines which match it actually wanted.
A tool that understands declarations and calls can skip both steps. It can return the 30 lines of one method instead of the 900 lines around it, and it can list the five real call sites instead of forty text matches.
The commands
vulkro ctx read FILE # a small file whole, a large one as an outline
vulkro ctx read FILE:NAME # one declaration (FILE:Type.method works)
vulkro ctx read FILE:120-180 # a line range
vulkro ctx search NAME # every definition, call and use, with its code
vulkro ctx body NAME # one function plus the lines that call it
vulkro ctx impact NAME # everything that depends on NAME, and the tests to run
vulkro ctx map # the most central functions, for orientation
vulkro ctx check # after an edit: what it broke
vulkro ctx init # tell the repository's agents to use these
A few details that matter in practice:
read on a large file returns its outline. Declarations with their line ranges and signatures, within a budget of about 2,000 tokens by default. The agent then reads the one declaration it needs with FILE:NAME, or a range with FILE:START-END. If it really does need the whole file, --full returns it.
search returns code, not just locations. Every definition, call, reference and import of a name, with a line of context, in one answer. --lines gives locations only, for "list every usage" questions.
body answers the most common follow-up. Almost every time an agent reads a function, its next question is who calls it. body returns the function and the calling lines together.
impact is the blast radius. Everything that depends on a symbol, transitively, and the tests that cover it. We describe it as a floor: calls the call graph cannot resolve (dynamic dispatch, reflection) are not counted, so the real set can be larger.
map is orientation. The most central functions of the repository, for an agent that has just arrived and does not know where anything is.
The graph commands share a small background server per repository. It starts on first use, listens only on a local socket, keeps the code graph warm so answers come back in a few tens of milliseconds, and exits after 15 idle minutes. Everything is offline and read-only: ctx never writes to your code.
Any agent that has a shell
We deliberately made the interface the shell. Every AI coding agent can run a command; not every agent speaks the same plugin or tool protocol. So ctx works with Codex, Claude Code, Cursor, GitHub Copilot, Gemini CLI, Aider and others without an adapter.
The remaining problem is telling the agent the commands exist. That is what ctx init does:
vulkro ctx init
It adds a short block to AGENTS.md, and to each agent instruction file the repository already has (CLAUDE.md, GEMINI.md, .github/copilot-instructions.md, a Cursor rule). Running it again updates the block in place instead of duplicating it. --agents limits it to the files you choose.
For MCP clients, the same answers are available as tools: read_code reads FILE, FILE:NAME or FILE:START-END, and check_change checks an edit (MCP reference). For Claude Code there is also an optional read hook, vulkro hook read: the first time a session tries to read a large source file whole, it gets the outline instead, and reading the same file again goes through.
What we measured
On our benchmark of 13 everyday coding tasks, agents using vulkro ctx took 49% fewer turns and read 37% less code than agents searching and reading files themselves, at about 15% lower cost, with the same results.
Two things are worth saying about that number. It is our benchmark, on tasks we chose, so treat it as a direction rather than a promise for your repository. And the cost saving is smaller than the turn and reading savings. Code read is only part of what an agent pays for: its instructions, its own reasoning and the edits it writes are all billed too, and ctx does not change those. What it changes is how much code the agent drags along to get there.
The "same results" part matters as much as the savings. Fewer turns would mean nothing if the agent got the task wrong more often; on this benchmark it did not.
The same for Apex and Lightning
Salesforce projects have the same problem with a twist: Apex classes grow large, and a method's callers are spread across controllers, triggers, batch classes and tests. vulkro-sf ctx brings the same commands to an SFDX project:
vulkro-sf ctx read force-app/main/default/classes/AccountService.cls:closeAccount
vulkro-sf ctx body closeAccount
vulkro-sf ctx impact closeAccount
search, body, impact and map read the Apex call graph, so a call such as AccountService.closeAccount(id) is tied to its method, and test classes are listed as the tests to run. vulkro-sf ctx init adds the instructions to AGENTS.md, and MCP clients get sf_read_code (vulkro-sf ctx reference).
What it does not do
ctx reads; it does not judge. It will show an agent the right 30 lines, but it has no opinion on whether the agent's change to them is correct. For that half there is ctx check, which runs after an edit and reports what the change broke, and which we cover in the next post.
It also does not replace reading when reading is the job. An agent reviewing a whole module, or a person learning a codebase, should read the files. ctx is for the far more common case where the question is narrow and the answer is a few lines.
Where to see it
Install Vulkro, run vulkro ctx init in a repository, and give your agent its next task. Then compare how many whole-file reads it makes. The vulkro ctx reference lists every command and flag, and the token efficiency page shows how this fits with the scanner's own agent tools.
To install, start here.
