Understand the code first
Squeezy reads your repository on your machine before it spends a model token on it, and builds a map of what your code declares and how it all connects. The model then asks precise questions and gets exact answers, instead of whole files poured into its context.
contents
contents
Why this is the first place savings come from
Most of what a coding agent needs to know about a codebase is bookkeeping: where a function lives, what it takes and returns, who calls it, what a class contains, what you changed since the last commit. An agent that answers those questions by opening files pays for a whole file to learn a few lines of it, and pays again on the next follow-up.
Squeezy does that bookkeeping on your machine instead. Parsing code and linking it together is repetitive work a computer is good at, and it happens once. The model's tokens then go to the part that needs a model: the reasoning. This is the first saving layer because it changes what the model asks for. It stops asking for files and starts asking for named things -- one signature, one caller list, one function body.
Mapping the repository, locally
When Squeezy opens a project it parses every supported source file into a code knowledge database: an index of the files, modules, classes, functions, and methods your code declares, and the links between them. It is built from your code on your machine, and nothing is uploaded to build it. The index is kept on disk, so opening the project again updates what changed rather than starting over.
From that index the model can ask for a repository map: an outline of the hierarchy, a count of what is written in each language, and the files that fall outside the supported set. The outline goes two levels deep by default and can be scoped to one subtree or one language. Instead of reading a dozen files to orient itself, the model reads one small map, and the map is capped, so a large repository still produces an outline rather than a wall of text.
- Kept current as you work: an edit re-reads the file that changed and re-resolves only the files whose answers could depend on it, rather than rescanning the repository.
- Honest about certainty: every relationship carries a label -- parsed exactly, resolved through an import, one candidate among several, defined outside your project, or left uncertain by a macro or a build flag -- so the model knows what to trust instead of guessing.
Finding where something is declared
The most common navigation question is “where is this defined?” Squeezy answers it from the index rather than by scanning text. Results come back ranked, and an exact name match always outranks a near miss, so the answer is the function with that name, not a comment that mentions it.
Each result is a small packet rather than a file: the name, the kind, the signature, the file, the line, and how certain the match is. The model reads the signature first, then decides whether it needs the body at all. One lookup can ask about several names at once, and it narrows by kind, language, path, visibility, or whether the file is a test.
- Ask for a type's members or its base types alongside the result, and the model can classify it without opening the file it lives in.
- Describe behavior instead of a name, “reject an expired token”, and a separate search ranks whole declaration bodies, for when the words are in the code but not in its name.
- A position becomes an anchor: a line, a column, or a byte offset from a compiler error or a search hit resolves to the smallest declaration around it, and with a precise position, to the exact thing referenced under the cursor.
- A capped answer is not a dead end: when results are trimmed to stay small, the reply carries the exact follow-up call that returns the next page.
Finding callers, references, and call chains
“Who calls this?” punishes the file-reading approach worst of all. The usual method is to search for the name, then open every file that matched, in full, to check the hit. On a widely used function most of that reading is code nobody needed.
Squeezy answers it as one query against the index: a list of one-line entries, no files read. The same surface runs the other direction (what does this call?), covers references that are not calls, and answers “does A eventually reach B?” with the chain between them. Traversals are bounded by depth and by page size, and where a call could bind to more than one target the alternatives are listed as candidates and capped, so one heavily used method cannot flood the answer.
- Callers and callees within a chosen number of hops, each entry tagged with its distance from the symbol you asked about.
- References beyond calls: type mentions, identifier uses, imports and re-exports, each carrying its own certainty label.
- Stated limits: the index follows imports and re-exports that a text search misses, but it is not exhaustive text recall, and it says so, so plain search stays the right tool when completeness matters.
Type and dependency hierarchy
Beyond single symbols, the index records what contains what and what derives from what: which class holds which methods, which type extends, implements, or mixes in which other, which file imports which. So “list everything in this module”, “what are all the subtypes of this base type, not only the direct ones”, and “which of those subtypes override this method” become bounded traversals instead of a hunt across the tree.
This is where cross-file work pays off. When the pieces of an answer sit in different files -- the call in one, the definition in another, the type three files away -- the index already links them, so a few queries assemble what would otherwise be a long run of reads.
Reading exact lines, not whole files
When the model does need source, it reads a slice, not a file. The index knows where each declaration's signature ends and its body begins, so the signature -- the name, the parameters, the return type -- can be fetched on its own, and the body only when the implementation matters.
Slices can also be taken by plain line range when the model already knows where to look. A window narrower than about 40 lines is widened toward roughly 48, so a clipped function does not force an immediate second fetch, and every returned line carries its real line number so the next read or edit lands in the right place.
- Signature first: read the small shape, then decide whether the body is worth it.
- The shape of a whole file without the file: a skeleton read keeps signatures and imports and collapses bodies, for when the question is what a file offers rather than how it works.
- Several slices in one call: up to 32 ranges in a single round trip instead of one call each.
- Only what moved: a read can be limited to the ranges that changed against your uncommitted work, the staged copy, or the branch base.
- Graceful fallback: a symbol with no separate body, a constant or an abstract method, returns its whole declaration rather than nothing.
The current diff as first-class context
“What did I change, and what does it touch?” comes up constantly while working, and the usual answer is a diff followed by re-reading files to work out the blast radius. Squeezy returns the change set as structured context instead: the changed files and their hunks, with the symbols those hunks sit inside already identified, so the model can triage what moved without re-fetching the source around it. Scope it against your uncommitted work, the staged copy, or the branch base.
Because those changed symbols are already linked to the rest of the index, the follow-ups start from the same place. One blast-radius query returns the files that import the change, the symbols that depend on it, and the tests that cover them, capped so even a widely imported edit stays a list rather than a re-read of half the repository. Reviewing your own work in progress costs a query, not a rescan.
When a file isn't supported
The code knowledge database covers 40+ languages and formats, and inside that set navigation is indexed and precise. Outside it, Squeezy does not pretend. It falls back to bounded search and targeted reads -- pattern search, path matching, and slices, each capped and honoring the ignore rules your repository already sets -- and it marks those files as unsupported instead of reporting a lookup it never made. The repository map lists them, so what is not indexed stays visible.