1. Scout
ftree or fls to understand layout. Token-cheap, structural.
If grep floods the context, cat drowns it. The fix wasn’t smaller files — it was reading the structure first, then the symbol.
After Episode 1, the search problem was solved. fsearch and fcontent returned ranked, capped, useful results. But the agent’s next move was always the same: it had a file path, and it needed to look inside it.
So it did what every agent does. It read the file.
cat src/core/handler.ts# 612 lines. 14 functions. 3 classes. ~6,000 tokens.The agent needed one function — doTheThing, line 384. It got 612 lines instead. Twelve more files like this and the context budget was gone again, even though searching was now budget-clean.
The agent wasn’t doing anything wrong. It had no way to ask “show me only the function I want.” Every native tool — Read, cat, head, tail — operated on lines or files. None of them understood symbols.
The leap was realizing that agents need an outline before they need the text. A human reading a 600-line file scans the function names down the left margin first, finds the one they want, then jumps. A human’s eye is doing the structural pass automatically. An agent reading the same file gets the structural pass and the textual pass as one undifferentiated wall.
Split them. One tool that returns structure (fmap). Another tool that reads exactly one symbol from that structure (fread --symbol). The agent does what the human does — scan, then jump — but as two separate, cheap tool calls.
fmap(“src/core/handler.ts”) └─ Symbol skeleton · 14 functions, 3 classes · class Handler L42:198 class RetryHandler L200:256 fn parseRequest L258:301 fn validateInput L303:340 fn doTheThing L384:421 // ← target fn formatResponse L423:458 · fread(“src/core/handler.ts” | symbol: “doTheThing”) └─ 38 lines · L384:421 · ~320 tokens
The full file would have been ~6,000 tokens. The chain that found, located, and read the target function used roughly 320 — a 20× reduction for the same answer.
fmap is the tool that separates fsuite from “ripgrep with a budget.” Every other fsuite tool has a roughly-equivalent native sibling — fsearch ↔ fd, fcontent ↔ ripgrep, fread ↔ cat, fls ↔ ls. Not fmap. There is no native CLI tool that returns a Treesitter-aware symbol skeleton with line ranges in a token-budgeted envelope. fmap exists nowhere else.
That’s why the Mental Model page calls it the keystone. Remove fmap and the whole chain reverts to “ripgrep with vibes.” Keep it and you have a discipline.
After Episode 2, the chain settled into the form it still has today:
1. Scout
ftree or fls to understand layout. Token-cheap, structural.
2. Narrow
fsearch for paths, fcontent for content, fs if you don’t want to choose. Capped, ranked.
3. Map
fmap on the file you found. Returns the skeleton. Don’t skip this.
4. Read
fread --symbol NAME for exactly the symbol the map showed you. Surgical.
The reflex it replaces is cat the_file. Once an agent has used fmap → fread once, it stops reaching for cat. The output is just better — smaller, more relevant, navigable.
| Old reflex | New reflex | Why |
|---|---|---|
cat file.ts | fmap file.ts then fread --symbol X | structure first, content second |
head -50 file.ts | fread file.ts -r 1:50 | line ranges are first-class |
wc -l file.ts | fmap file.ts | symbol count is more useful than line count |
| ”let me just read the whole thing" | "let me just map it” | mapping costs ~5% of reading |
Capping fixed flooding. Symbol-aware reading fixed drowning. But agents still reached for native Read and Grep by reflex even when fsuite was installed — training is sticky. The next problem was enforcement.
That’s Episode 3.
fmap — the keystone drone profilefread — symbol-aware and range-aware reading