Skip to Content

Finding code without reading everything

The tools zerd reaches for to locate code, cheapest first, from ranked search to symbol reads.

In the order to reach for them, cheapest first:

  1. search: BM25-ranked files for a plain-words query, camelCase-aware, with co-occurrence rerank and noise down-weighting. Files that fixed similar requests before get a boost.
  2. outline: symbols and JSX section markers with line ranges (bracket-balanced, so huge components don't blow up).
  3. read_file(path, symbol=...): one function, not the file.
  4. git: read-only blame, log, and find_change (git log -S) to answer "when did this break".
  5. graph: structure questions (callers, callees, path, community) from a graphify graph. Call direction is read from source because graph edge order is unreliable, and results say how stale the graph is. An import graph (tsconfig paths, relative and Python imports) is read straight from the tree, so it never goes stale. So is the symbol graph (TS/JS, Python, Rust, Go, Java, C#, PHP, Ruby): find, symbol, callers, callees and impact need no snapshot. A use is a call, a type mention, a rendered JSX component or a function passed by name; in Rust and Go a call goes to the definition it names (symgraph::load, Config::new, a use line). Check what the model would see with zerd graph callers parse_invoice. trace follows callers (or callees) several hops in one call and path finds the chain of calls between two functions. An edit that changes or removes a function's signature comes back with the places that use it, and note pins a fact to a symbol in .zerd/graph.toml: it is shown whenever that symbol comes up, and marked once the code has changed.
  6. grep / glob: exact strings. They respect .gitignore, find dotfiles, never touch secrets, and use smart-case.

With --no-pi, a non-trivial request starts with a scout, a read-only investigator on a cheap model, so the main context begins from findings instead of searches. In pi mode the same investigator is available as a sub-agent the model can call, so wide searches run in their own context instead of filling this one.