| name | ast-bro |
| description | Fast code-navigation toolkit for LLM agents. Use to explore codebases without reading whole files — get file shapes, public APIs, dependency graphs, call graphs, blast-radius analysis, and token-budgeted context. |
| user-invocable | true |
Use sb (the ast-bro toolkit) to explore the code
sb is the short alias for the ast-bro binary. The legacy ast-outline command still works as a thin proxy.
Each command accepts --json for a stable, versioned schema (e.g. ast-bro.map.v1) and --compact to emit single-line JSON instead of pretty-printed.
Error contract (uniform across every subcommand): stdout carries results only; every note, hint, and error goes to stderr. Exit 0 = the query ran (even when the answer is legitimately empty), exit 2 = the query could not run as asked (no such path/symbol, unknown flag, missing argument, empty argument list — e.g. a $(...) substitution that produced nothing), exit 1 = internal failure. Two deliberate additions: cycles exits 3 when cycles exist, and run exits 1 when a valid pattern matched nothing (grep convention) — a rejected run still exits 2 with empty stdout. With --json, a rejected call also emits a machine-readable ast-bro.error.v1 object on stderr. Recovery rule: stdout empty or exit non-zero → the call was wrong; read stderr; fix the call. Never silence stderr with 2>/dev/null — for some commands it carries the only copy of the diagnostic.
Result caps (defaults; truncation is always reported, never silent): --limit 200 on callers / callees / impact / reverse-deps, --limit 20 on a multi-file show (a single explicit file is never capped), --max-members 50 under the digest preset, --budget 8000 on context, -k 10 on search; --depth defaults are 1 (callers/callees), 2 (impact), 3 (deps), 12 (trace); context exposes no --depth — it walks to depth 2 internally under its --budget. When a cap is hit the header on stdout carries the true total (# 113 caller(s) … (showing 3; raise --limit to see the rest)) — for every section, including callees ancestor groups and callers type groups — and JSON carries total / truncated. --limit caps the display, not the walk: callers / impact / reverse-deps traverse the full cone to make the total exact, so --depth is what costs time.
--depth truncates too, and differently. A walk that ran out of depth and one that ran out of graph both end quietly, so every depth-bounded command reports which happened: JSON carries frontier_truncated — on callers, callees, deps, reverse-deps, and trace at the top level, and on each impact report — and text mode says so on stderr (# note: --depth 2 reached with unexplored edges beyond it). Read it before treating a result as exhaustive, because the --limit fields will not tell you: truncated: false with frontier_truncated: true means nothing was cut from the display and total itself counts only the part of the cone inside --depth. callers / callees / impact print no note at --depth 1, where "the callers have callers" is the norm rather than a qualification — the JSON flag is set regardless. On trace, found: false with frontier_truncated: true means "no path within --depth", not "no path".
Read structure with sb before opening full contents. Pull method bodies only once you know which ones you need.
A typical task done two ways:
Read Player.cs
Read DamageSystem.cs
grep "TakeDamage" src/
sb callers Player.TakeDamage
Stop at the step that answers the question:
-
Unfamiliar directory — sb digest <dir>: one-page map of every file's types and public methods. (digest is an alias for sb map --preset digest; it accepts every map flag.)
sb digest src/
sb digest src/ --glob '*.java' --max-members 8
-
One file's shape — sb map <file>: signatures with line ranges, no bodies (5–10× smaller than a full read). Three orthogonal axes: detail (--detail names|signatures|full), visibility (--no-private, --no-fields, --no-docs, …), and scope (--glob, --max-members). --detail signatures --max-members N is the middle ground when a directory-wide map is too big and digest's bare names are too little.
sb map src/file_filter.rs
sb map src/ --detail signatures --max-members 8
-
One symbol's source — sb show <target>... <Symbol>...: suffix matching, multiple symbols at once. A target is a file, a directory, or a quoted glob — pass a directory when you know the symbol but not the file, and skip the search-then-read round trip. Multi-file answers carry a coverage header (# 3 match(es) for 'greet' in 2 of 47 file(s) searched) and cap rendered bodies at --limit 20 with the true total still reported. An unquoted glob is recovered rather than misread: the shell expands sb show src/*.cs Widget before sb sees it, and the extra files are taken as targets, not as symbol names. An argument that reads as a path but isn't there (src/typo.rs) is rejected as a path, never searched for as a symbol; if a symbol genuinely shares its name with a parseable file or a directory (tests, core, utils), qualify it (Type.method) or put it last — the final argument stays a symbol, so only a colliding name in the middle of the list is read as a target. Explicitly-passed extensionless files fall back to shebang detection (#!/usr/bin/env python3 → Python, #!/usr/bin/env node → TypeScript, etc.) — useful for CLI scripts in bin/ or ; directory walks skip extensionless files to keep the walk fast. For markdown the symbol is a heading or (a leading YAML block; TOML is not surfaced). Schema: .
Wrong-path recovery: a path that isn't where you said it was is rejected with exit 2, and the stderr hint offers a repair when one is verifiable on disk — the same file name found elsewhere in the tree, or the quoted form when an unquoted path with spaces got split into several arguments. No hint means nothing on disk corroborated a guess, so re-read the path rather than retrying blind.
Path / argument expectations:
show → expects a file, directory, or quoted glob, then one or more symbol names
deps, reverse-deps → expect a file path
graph, cycles → expect a directory (repo root)
callers, callees, impact, context → expect a symbol name (function or type), not a path
trace → expects two symbol names (FROM then TO), optional repo root
run → expects a -p <pattern> flag, optionally -r <rewrite> and --write
squeeze → expects a file path, optionally a from:to line range
Maintenance commands (not usually called directly — use sb install once and rely on sb prompt / sb mcp for ongoing integration):
sb index Build, refresh, or inspect the per-repo search index
sb prompt Print the agent prompt snippet (for hand-copying into AGENTS.md)
sb install Install ast-bro into a coding-agent CLI
sb uninstall Remove ast-bro from a coding-agent CLI
sb status Report what's installed where
sb mcp Run as an MCP (Model Context Protocol) server over stdio
sb hook Internal: read a tool-call event from stdin and respond