v0.33.0
v0.33.0
This release adds aft_inspect — a single codebase-health view for agents — and reworks how diagnostics reach the agent. It also renames aft_navigate to aft_callgraph and lands a large batch of aft_search correctness fixes.
aft_inspect — codebase health at a glance
aft_inspect gives an agent the "open the project and see what's wrong" view in one call, instead of stitching together greps and separate tools. One call returns a summary across categories; pass sections to drill into any of them.
Categories:
- diagnostics — compile/type errors from language servers
- metrics — file count, lines of code, symbol count
- todos — TODO/FIXME/HACK/XXX/BUG markers with file, line, and author
- dead_code — exported symbols with no reachable caller
- unused_exports — exported symbols nothing imports
- duplicates — near-identical code blocks across files
Treat dead_code as a hint, not an authority: reachability is call-based, so symbols reached only through method dispatch (receiver.method()) or referenced only in type position can still appear as false positives. Verify before deleting.
It runs in tiers. Metrics and todos return synchronously from cache. dead_code, unused_exports, and duplicates run as background scans on session idle; calls return cached results immediately (marked stale when a refresh is pending) rather than blocking. Results persist to disk per project, so they survive restarts.
Options:
sections— one category or an array;allfor everything; omit for summary-onlyscope— restrict to a file or directorytopK— cap drill-down items per category
Scan results persist across restarts
Tier-2 scan results are stored on disk. When you reopen a project after editing files, aft_inspect serves the last completed scan immediately (flagged stale) while a fresh scan runs in the background, instead of showing nothing until the rescan finishes.
Diagnostics now flow through aft_inspect
Diagnostics no longer arrive automatically after every edit. Previously edit, write, and apply_patch waited on language-server diagnostics inline on each call. That work moved into aft_inspect, which an agent runs after a batch of edits and before tests or a commit. The result is faster edits and a single deliberate point to check the codebase.
- The standalone
lsp_diagnosticstool is removed; its capability is part ofaft_inspect'sdiagnosticscategory. - A new
lsp.diagnostics_on_editconfig option (default off) restores per-edit diagnostics for users who want them. aft_inspectdiagnostics are build-authoritative: TypeScript/JavaScript files excluded bytsconfig.json(files/include/exclude, followingextends) are skipped, so results match a realtscrun instead of reporting phantom errors on excluded files.- Scoped diagnostics on a single file wait briefly for a cold language server to start and publish, so the result arrives in one call rather than requiring a re-run. Scopes whose file types have no registered or applicable server report a terminal
no_serverstatus instead of waiting forever.
aft_inspect diagnostics are a fast checkpoint, not the project-wide authority — a clean tsc / cargo check / pyright run remains the real gate.
aft_navigate is now aft_callgraph
The call-graph tool is renamed from aft_navigate to aft_callgraph. The new name matches what the tool does — answer code-relationship questions (callers, call tree, impact/blast radius, execution paths, data flow) from a real call graph — and is the term models reliably reach for. All operations (callers, impact, call_tree, trace_to, trace_to_symbol, trace_data) are unchanged.
The Rust call graph now resolves cross-file callers. Calls through crate::, super::, self::, a workspace crate name (e.g. a binary's mycrate::module::func(...)), and use-imported short names now resolve to the right definition. Previously only same-file callers were found, so callers/impact on a Rust symbol returned far too few results and dead_code over-reported. Liveness also now flows through private intermediary functions, so handlers reached only via a private router are no longer flagged dead.
aft_search correctness
A batch of fixes to the unified aft_search shipped in v0.32:
- Disabled vs. unavailable. With
semantic_search: false, a natural-language query now returns a clear "not enabled" result instead of silently degrading to a lexical scan. The degraded lexical fallback is reserved for when semantic is enabled but temporarily building or unavailable. - Truthful routing.
interpreted_asreports the lane that actually ran (lexicalwhen the semantic lane was skipped), not the lane that was requested. - Result provenance. Each result carries the lane that produced it plus a
hybrid_boostedflag, andmore_availablenow accounts for engine caps and semantic overflow so "more results exist" is accurate. - Quoted phrases. Surrounding quotes on a literal query (
"was not found on PATH") are stripped before matching, matchingrg -F/ code-search behavior, instead of searching for the quote characters. - Classifier routing. A sentence-final
?no longer forces regex mode; a query mixing natural language with an error code routes correctly; bare escape sequences route to regex while Windows-path-shaped queries stay exempt. - No hidden truncation. Lexical candidates beyond the old internal cap are no longer dropped silently before fusion.
Edits no longer echo back to the agent
edit, write, and apply_patch were returning unnecessary diff bytes back to LLM. The agent-facing result is now minimal (success plus added/removed line counts).
Bug fixes
Editing and refactoring:
aft_safetyundo history no longer lost on restart. The first edit to a previously-backed-up file after a restart could overwrite that file's entire on-disk undo history (and reuse a backup id). Prior history is now hydrated before the new snapshot is appended.apply_patchpure-insertion hunks insert at the resolved context line instead of end-of-file.aft_transform add_memberon a genericimpl<T> Foo<T>block now inserts methods into the impl block, not the struct body.aft_refactormove no longer adds a spurious import when a parameter or local variable shadows the moved symbol's name.aft_import organizecorrectly handles Rust nestedusetrees (a::{b::{c, d}, e}) without corrupting them.aft_zoomno longer raises spurious mutual-exclusion errors when a model sends empty or empty-contenttargets/symbolsparameters.
Search and indexing:
- The semantic index verifies per-file freshness strictly and uses canonical paths for watcher invalidation, so edits refresh the right entries and large files aren't downgraded to a weaker freshness check.
aft_outlineaccepts JSON registry/package URLs and outlines their top-level keys.- URL fetching for
aft_outline/aft_zoomretries transient connect/transport failures and now runs entirely in Rust.
Reliability and security:
- The worker bridge stays alive through transient starvation timeouts and only escalates to a kill on a genuine hang, so a busy bridge no longer drops unrelated requests.
- SSRF protection on URL fetches now blocks RFC 6598 (CGNAT,
100.64.0.0/10) and RFC 2544 (198.18.0.0/15) ranges, including their IPv4-mapped IPv6 forms. - Windows hardening: validated
LoadLibraryExWONNX Runtime loading, NuGet cache scanning, storage directory creation, and a binary-replacement retry (contributed in #66/#69). - Killing a PTY-mode background task on Windows now finalizes reliably instead of hanging, so
bash_killand the polling that waits on it always return. - LSP and formatter "not installed" warnings no longer re-fire on every session, and synthetic notifications preserve the active model and variant.
Join us on Discord: https://discord.gg/F2uWxjGnU