v0.36.0
v0.36.0: Persisted call graph, leaner bash output, and sharper code search
The call graph now lives in a persisted, incremental store instead of being rebuilt from scratch on demand. That removes the file-count limit that capped dead-code analysis on large repositories, makes aft_callgraph queries resolve from disk, and improves edge accuracy across more languages. This release also stops shell pipes from hiding test failures, steers agents toward aft_search for code discovery, and fixes a handful of crashes and papercuts.
Persisted call graph (and the end of the file-count cap)
aft_callgraph and aft_inspect's dead-code analysis used to build a whole-project call graph in memory, re-parsing every file on demand. That build was capped at lsp.max_callgraph_files (5000) to avoid the multi-core CPU spikes reported in #86, which meant dead-code analysis simply turned off above that size.
The call graph is now a persisted SQLite store, built once in the background and updated incrementally as files change. Queries read from disk, so callers/callees/impact/trace return without rebuilding anything, and the cold build reuses the symbol cache instead of re-parsing. On this repo the cold build is ~14s; on a 5,700-file TypeScript repo ~13s, after which updates are incremental.
The lsp.max_callgraph_files cap is gone for the call-graph store. Dead-code analysis runs on large repositories again. The cold-build's parsing pass is bounded to half your cores so it can't monopolize the machine while it runs.
More accurate call edges
The store resolves method calls more precisely. It infers receiver types (for self/Self, parameters, and common bindings in Rust, Java, Kotlin, and C++) to produce exact method-dispatch edges, falls back to name matching when a type can't be resolved, and applies a standard-library denylist so calls like .expect() or .map() don't generate thousands of spurious edges. Call extraction now also covers C, C++, C#, and Zig.
Bash output stops hiding failures
Piping a test or build command through a filter, like bun test | grep fail or cargo test | grep -A3 FAILED, used to strip the output down before AFT's compressor ran, which often threw away the failure detail and summary you actually wanted. When output compression is on, AFT now recognizes that pattern, runs the command without the trailing filter, and lets the compressor keep the failures and summary. This also works through a leading cd <dir> && ... prefix. Pass compressed: false to keep your pipe verbatim.
Sharper code search steering
Agents reach for grep/rg/find in bash out of habit even when AFT's indexed search is faster and better ranked. Workflow guidance and the per-command hints now steer code discovery to aft_search and the other aft_* tools, run in parallel, and when aft_search is available the grep-rewrite footer points there instead of suggesting the grep tool.
Status bar accuracy
The E/W (errors/warnings) counts in the agent status bar are now filtered to the same build membership aft_inspect and tsc use, so files excluded from your tsconfig (test files, build output) no longer inflate the count with diagnostics that your compiler would never report.
Fixes
foreground_wait_window_mshad no effect (#102): a smalltimeoutpassed to a foregroundbashcall collapsed the wait window and could kill the command almost immediately. A foregroundtimeoutbelow the wait window is now ignored (the command runs to the normal window, then promotes to background), while an explicitbackground: truetask still honors a short timeout as a real kill cap.aft_deleteandaft_safety checkpointcould crash on a mistyped argument: when a host delivered thefilesarray as a bare string or a JSON-encoded string, the tool threwinputs.map is not a functionbefore doing anything. The argument is now coerced at the boundary, so it works or returns a clear error instead of crashing.- Trailing newline is preserved in compressed terminal output instead of being dropped from the last line.
- Call-graph lookups no longer show as tool errors in the UI when a symbol isn't found or the store is still building; they return a normal result.
- Semantic index no longer hammers a slow embedding backend with fixed-interval retries; refresh retries back off and trip a circuit breaker.
Thanks to @de-flandres (#102, pinpointing the wait-window override).