Read Outline
Navigate huge files like an IDE — fold everything to its signatures, unfold the one function that matters.
Overview
Claudin's Read tool can return a large code file as a structural skeleton — every function and class signature with its line range — and then expand a single symbol by name. Instead of choosing between "pay ~25k tokens for the whole file" and "guess offset/limit blindly", the model navigates big files the way a human does in an IDE: fold everything, open the one function that matters.
On by default. No configuration required.
The problem
Coding agents read files through a size-capped tool. On a huge file — a 2 000-line shim, a generated client, a god-module — the classic CLI has only two outcomes, and both are bad:
| Outcome | Cost | Problem |
|---|---|---|
Throw "file too large, use offset/limit" |
~100 bytes | The model is left blind — it picks offset/limit with no idea where anything is, then slice-walks the file in expensive guesses |
| Truncate at the cap | ~25k tokens | Expensive, and the cut lands at an arbitrary line |
This trade-off is measured, not speculated: an upstream experiment that tried truncating instead of throwing was reverted — the tool-error rate dropped but mean tokens rose. Neither branch of the dilemma wins; the missing option is one that is both cheap and informative.
There is a subtler failure below the cap, too: even a full read that fits (say 15k chars) induces a pathology — the model re-reads the same file in slices, because a large literal body in a tool result reliably triggers "I need to see the middle again" loops.
What it does
Two optional Read parameters and one Grep mode, all over a single symbol-scan primitive:
| View | Call | Question it answers | Typical cost |
|---|---|---|---|
| outline | Read(file, view='outline') |
"What's in this file?" | ~5–10% of the full-file tokens |
| unfold | Read(file, symbol='login') |
"Show me this one function" | just that body, real line numbers |
| search | Grep(pattern, output_mode='symbols') |
"Which functions mention X, across the repo?" | matched signatures only |
Precedence inside Read is symbol > view > offset/limit, honoured at any file size.
And three automatic behaviors, so the model benefits even when it never asks:
- Over-cap pivot — a plain
Readthat blows the size caps (256 KB / 2 000 lines / ~25k estimated tokens) on a code file returns the outline instead of an error. The dead end becomes a map. - Auto-outline on large reads — a vanilla full-file
Readof a code file ≥ ~10 KB pivots to the outline with a footer explaining how to get more (view='full'forces the body,symbol/offsettarget a range). The ~10 KB floor is empirical: it is where the slice-walk re-read loop starts, and returning the outline removes the stimulus entirely. - Helpful misses —
symbol='foo'on a file that has nofoodoesn't just fail: the error lists the available symbol names and points atview='outline'. On a name collision, the shallowest (top-level) entry wins.
Why it matters on huge-file projects
The win compounds with file size and with session length:
- Token cost: reading one function from a ~2.3k-line file is ~2k tokens end-to-end (outline ≈ 1.5k + unfold ≈ 0.6k) vs ~26k for the full body.
- No blind slicing: the outline carries real line ranges, so the follow-up read is surgical — one
symbolcall, not a binary search withoffset/limit. - Edits still work: an unfold counts as a partial read at the symbol's real line numbers, so editing that function behaves exactly as it would after any normal read.
- It feeds the cache policy: outlines keep the retained history prefix ~45% smaller, which is part of why the lockstep cache benchmark landed 24% under Claude Code on the identical workload (see Cache policy) — every cached re-read and every eviction re-write is cheaper when what was read was a skeleton.
- Markdown too:
.mdfiles outline by heading, so a 1 000-line design doc collapses to its table of contents.
The advantages of never sending the whole file to the model
The deeper point is that a full-file read is not a one-time cost — and avoiding it pays out on every axis at once:
- A read is billed on every subsequent turn. The agent's history is re-sent with each request, so a 25k-token file read on turn 3 of a 50-turn session is re-billed ~47 more times — as cached input at best (0.1×), as full-price input on providers without cached-read discounts. Reading a 1.5k-token skeleton instead saves not 23.5k tokens but 23.5k × the remaining turns.
- Context window is the scarcest resource, and full files exhaust it fastest. Every full body occupies the window for the rest of the session. Skeletons let a session touch far more files before hitting the ceiling — which is exactly what huge-file projects (monorepos, generated clients, vendored code) need. It is also the first rung of the anti-autocompact ladder (see Cache policy): what never enters the context never has to be stubbed, clipped, or summarized away later.
- Less noise means better answers, not just cheaper ones. Model recall degrades as the context fills with irrelevant content — 2 000 lines of unrelated functions actively compete for attention with the task. An outline keeps the map (what exists, where) while excluding the bodies that don't matter, so the signal-to-noise ratio of the whole session stays high.
- Targeted reads produce targeted edits. When the model unfolds exactly the function it is changing — with its real line numbers — the edit context is precise. Dumping whole files invites edits anchored to the wrong copy of a similar-looking block.
- Smaller prompts are faster prompts. Prompt-processing time scales with input size; on the lockstep benchmark Claudin ran ~2× faster per turn, with the smaller retained prefix as the main driver.
- And it stays safe: the model is never prevented from seeing content —
view='full',symbolandoffset/limitare always one call away, and the outline itself tells the model exactly what exists and where to look. The default just stops paying for bodies nobody asked for.
How it works
One primitive, no new tool, no new dependency — the outline and the unfold both slice the same symbol table, so their boundaries always agree:
- A depth-scanner, not a full parser. Strings, comments and regex literals are masked, then symbol bounds come from brace depth (C-like languages), indentation (Python), or heading level (Markdown). A symbol ends at the next sibling at the same depth or shallower.
- Languages: TypeScript/TSX, JavaScript/JSX, Python, Go, Java, Kotlin, C#, Rust, and Markdown (by extension).
- Fails open. Anything the scanner can't handle — an unknown extension, a file over the scan cap, a scan error — degrades to a normal
Read. The feature can make a read cheaper; it can never block one. - Built into
ReadandGrepinstead of a new tool, deliberately: the model reaches forReadby instinct and hits the size wall — insideRead, that wall becomes a map automatically, with no "know to switch tools" step. The outline renderer is itself capped, so a pathological file can't blow the budget with its own outline.
Configuration
The explicit view/symbol parameters and the over-cap pivot are always on. The auto-pivot on large (but under-cap) reads can be turned off:
| Override | Effect |
|---|---|
CLAUDIN_DISABLE_AUTO_OUTLINE_ON_ELISION=1 | vanilla large reads return the full body again (over-cap pivot and explicit params unaffected) |
Read(file, view='full') | per-call: force the full body of a file that would auto-pivot |