Skip to content

Releases: cortexkit/aft

v0.39.2

15 Jun 14:30

Choose a tag to compare

v0.39.2: no more "bridge keeps getting killed", configurable timeouts, quieter sandboxes

A patch centered on bridge stability: a passive status poll can no longer kill the bridge out from under your work. Plus the bridge timeout and restart threshold are now tunable for slow filesystems and multi-window setups, and AFT no longer looks broken in restricted sandbox environments.

The sidebar status poll no longer kills a busy bridge (#117)

The TUI sidebar polls the bridge for status roughly every 1.5 seconds. When the bridge was busy with real work — say a background Code Health scan kicked off by your edits — those polls queued behind it and timed out. Two timed-out polls tripped the "bridge looks hung, restart it" rule, which killed and respawned the bridge — aborting the edit or read you were actually waiting on. The agent then saw "the bridge keeps getting killed" and retried straight back into the same trap.

A passive status poll timing out means the bridge is busy, not hung, so it no longer counts toward the restart rule and now falls back to the last cached status immediately instead of blocking. Your real work is never interrupted by a health check again. (Most visible on Windows and large repos, where the background scan runs longer, but it was cross-platform.)

Configurable bridge timeout and hang threshold

Related to the above: on slow filesystems (WSL/DrvFs, NFS, network mounts) the aft binary's cold start and file walks can legitimately exceed the built-in 30-second request timeout, and combined with the fixed "restart after 2 consecutive timeouts" rule this produced a restart loop. The same fixed threshold also bit setups where several editor windows share one bridge process. The #117 fix removes the most common trigger (passive polls); these knobs let you tune the remaining behavior for genuinely slow environments.

Two settings are now configurable in aft.jsonc (defaults unchanged, so nothing changes unless you opt in):

{
  "bridge": {
    "request_timeout_ms": 30000,  // raise on slow filesystems
    "hang_threshold": 2           // raise when many windows share one bridge
  }
}

These are user-level settings (a project config can't change them, since they govern the bridge's restart safety and your machine's transport budget).

Graceful degradation in restricted sandboxes

  • cpuinfo noise (#97): in some sandboxes, ONNX Runtime's bundled CPU-detection library logs Error in cpuinfo: failed to parse processor information from /proc/cpuinfo. That's a benign third-party message, not an AFT failure — AFT never depended on /proc/cpuinfo. It's now filtered so it doesn't surface as an AFT error; genuine errors still show.
  • watcher unavailable (#111): when the filesystem watcher can't attach (often an inotify limit in sandboxes), AFT already kept working — it just lost live detection of external file changes. It was presented as an alarming error; it now reads as an honest soft degradation: "file watcher unavailable; continuing without live external-change invalidation."

Thanks to @herjarsa for the bridge-timeout investigation and the original implementation it grew from.

Questions or feedback? Join us on Discord.

v0.39.1

14 Jun 15:09

Choose a tag to compare

v0.39.1: lighter background CPU, sharper Code Health, and a calmer bash watch

A patch focused on what AFT does while you work: the background scans cost far less CPU per edit, Code Health gets more accurate, and a couple of sharp edges are filed off.

Background scans barely touch your CPU now

Tier-2 (dead code, unused exports, duplicates) re-checked file freshness by re-hashing every file in the project on each change. It now compares file stat metadata and only re-reads the file that actually changed — about 10x less work per edit, and the gain grows with repository size. This is the background-CPU cost that large-repo users felt after every save.

A companion fix makes the watcher recover correctly when the OS drops filesystem events under load (a large checkout or branch switch): instead of silently missing changes, it falls back to a single project rescan.

Code Health accuracy

  • Modules imported only for their side effects (import "./setup") no longer make their exports look dead, and reachable code behind them is counted.
  • After a failed incremental refresh, stale call-graph rows are no longer projected as if fresh — dead-code results report honestly instead of showing wrong counts.
  • Editing a tsconfig/jsconfig (including extends chains and a package's own tsconfig field) now correctly invalidates dead-code and unused-export results that depend on path resolution.
  • aft_search reports files it couldn't read instead of silently counting them as zero matches.

Code Health speed

The call-graph cold build is about 13% faster, and the incremental call-graph reads, oxc reachability pass, and search-index cold build all do less redundant work. Results are unchanged.

Fixes

  • bash watch could freeze the host. A sync bash_watch with a regex pattern compiled and ran that regex on the host event loop; a pathological pattern over large output could hang the UI. Regex matching now runs in the Rust engine (linear, no catastrophic backtracking), and sync/async watches now share the same line-anchor semantics.
  • apply_patch checkpoints and diffs. A multi-hunk patch that touched the same file twice could lose the original from the undo checkpoint or report the wrong per-file diff. Checkpoints now snapshot every touched path up front, and metadata is tracked per hunk.

Quality of life

  • aft_zoom accepts space-separated symbol names in the symbols string for code files (e.g. "foo bar baz"), splitting them into separate lookups.
  • The "use aft_search" nudge on bash grep no longer fires when the search targets a path outside the project, where aft_search wouldn't help.
  • The sidebar remembers its collapsed state across restarts, sorts predictably alongside other CortexKit sidebars, and lets you hide individual sections via tui-preferences.jsonc.

Questions or feedback? Join us on Discord.

v0.39.0

13 Jun 22:29

Choose a tag to compare

v0.39.0: Code Health that's accurate, incremental, and barely touches your CPU

This release makes Code Health trustworthy and cheap: the dead-code numbers are correct, and the background scans now reparse only the file you changed instead of the whole project on every edit. Plus R language support and pending diffs in edit approval prompts.

Code Health: accurate Rust dead code

The dead-code analyzer was flagging constructors and associated functions as dead even when they had real callers. Type::new() calls resolved through receiver-type inference were being discarded, so anything reached only that way looked unused. On AFT's own source this over-reported by ~80 symbols — the entire ::new false-positive cluster. Those calls now count toward liveness, while looser name-only matches stay excluded to avoid the opposite error.

Code Health: scans no longer rescan the whole project on every edit

Tier-2 scans (dead code, unused exports, duplicates) used to re-parse every file in the project after each change — a CPU burst of several seconds on a mid-sized repo, far worse on large ones, every time you saved. This was the background-CPU complaint behind several reports.

The scan now caches each file's local facts and reparses only the file that actually changed; the cross-file liveness pass runs over cached facts with no re-parsing. On AFT's repo a single-file edit dropped the dead-code parse from re-reading ~1,800 files to exactly one — measured live at ~130ms of scan work where it used to be ~3 seconds. Results are byte-identical to a full cold scan, verified across importer-removal, file-deletion, rename, barrel/pub use re-exports, tsconfig alias changes, cross-file type references, and manifest entry-point edits.

The two earlier per-scan optimizations are still in here too: dead-code edge lookups went from quadratic to grouped (a ~5x cold-scan speedup on this repo), and the call-graph cold build got ~14% faster.

R language support

.R/.r files now work in aft_outline, aft_zoom, and the AST tools (ast_grep_search/ast_grep_replace). Functions (including the name <- function(...) assignment form), top-level assignments, and calls extract with correct ranges. (#112)

Pending diff in edit approval prompts

When OpenCode asked for approval on an AFT edit, the prompt showed No diff provided instead of the pending change — you couldn't review an edit before approving it. AFT now computes the diff before asking: write diffs the new content client-side, edit previews its transform without committing, and apply_patch previews through the same applier it commits with, so the diff you approve is exactly what gets written. (#115)

Fixes

  • Code-search guidance now appears for grep/rg anywhere in a command chain, not only when it leads — multi-statement scripts were slipping through.
  • The Tier-2 staleness ceiling that forces a refresh during continuous editing was raised from 10 to 30 minutes, so a long refactor no longer triggers a mid-session scan of half-applied changes.
  • The bundled tokenizer data is regenerated atomically, so a concurrent build can't observe a half-written file.

Questions or feedback? Join us on Discord.

v0.38.0

13 Jun 01:12

Choose a tag to compare

v0.38.0: Code Health you can trust

Dead code and unused exports are back on the dashboard, rebuilt on a real analysis engine. Plus: tools stay responsive while your project compiles, trace_data moves to the persisted call-graph store, and Pascal joins the language table.

Dead code and unused exports, rebuilt on oxc

In v0.35.3 we pulled dead-code and unused-export numbers from the sidebar and status displays because they over-reported — barrel re-exports, package entry points, and dynamic imports produced false positives we weren't willing to show. This release replaces the TypeScript/JavaScript analysis with a real module-graph engine built on oxc: scope-aware export liveness, barrel and re-export chains, package.json entry-point resolution, tsconfig-aware module resolution, and dynamic-import handling.

On AFT's own repository the unused-export count dropped 62% — and the survivors check out as genuinely dead when inspected by hand. Both categories are live again in the TUI sidebar, /aft-status, aft_inspect, and the agent status bar.

Honest framing, per language: TS/JS findings are import-graph verified by the new engine. Findings for other languages still come from the call-graph analyzer, which can over-report dynamic dispatch targets (trait methods, interface implementations) — those upgrades are next on the roadmap, and results remain labeled by language so you can weigh them accordingly.

The engine also reports its own gaps: files it could not parse and files outside the analysis root are listed in aft_inspect output instead of being silently skipped.

Tools stay responsive during builds

Filesystem event processing moved off the bridge's request thread. Previously, a compile writing thousands of files (cargo's target/, webpack output) could queue tool calls behind a flood of watcher events — requests timed out at 30 seconds while the bridge chewed through events. Events are now received, filtered, and coalesced on a dedicated thread; the request loop only ever sees the handful of source files that actually changed. Tool calls answer in milliseconds mid-build.

trace_data on the call-graph store

aft_callgraph op=trace_data now projects from the persisted call-graph store like the other five ops, instead of re-walking files with the legacy engine. Cross-file value flows resolve through the store's type-aware edges, recursive functions trace correctly, and results come back from an index instead of a parse.

Pascal support

.pas/.pp/.dpr files now work in aft_outline, aft_zoom, and the AST tools (ast_grep_search/ast_grep_replace) — programs, units, classes, procedures, and functions extract with correct ranges. (#109)

Ctrl-C and shutdown behavior

AFT's signal handling now follows a strict contract: if AFT is the only SIGINT/SIGTERM listener in the host process, it owns termination — cleanup runs with a 5-second cap and the process exits with the conventional signal code. If other plugins or the host also listen, AFT cleans up and defers termination to them — and logs which listeners it deferred to, by name, so a hung Ctrl-C is attributable from the log instead of requiring a debugging session.

Status panel reliability

  • The TUI sidebar no longer loses its connection permanently after heavy host load: clients only remove a server's port file when its process is provably dead, and the server re-publishes its port file every 15 seconds if it goes missing. (#110)
  • Transient environment problems (a JSON schema fetch timing out, a registry being unreachable) no longer count as code errors in the status bar or aft_inspect summaries. They remain visible in detail rows, tagged [environmental].

Bash output protection: chained commands

The pipe protection that keeps bun test | grep fail from hiding failures now handles command chains: cd pkg && bun test | tail -3 && bun run typecheck | tail -1 gets each segment's noise filter stripped independently, with the same fail-closed rules — comments, redirections, substitutions, and side-effectful filters leave the command untouched.

Fixes

  • edit honors occurrence: 0 — the first-match selector was treated as an empty parameter and re-prompted with ambiguous_match.
  • Error responses no longer leak status-bar internals — structured tool errors carried a status_bar data block meant for transport, not for the agent.
  • Cloning a repository you already have no longer corrupts the original's call-graph cache — two checkouts of the same repo now build per-clone stores instead of re-rooting each other's metadata back and forth.
  • Analysis caches distinguish .ts from .tsx — identical content parsed under different grammars no longer shares a cache entry.
  • aft_inspect scoped to a directory reports scope-accurate language breakdowns instead of project-wide ones.

Questions or feedback? Join us on Discord.

v0.37.2

12 Jun 12:12

Choose a tag to compare

v0.37.2: Background code-health scans stop rebuilding the call graph

This patch removes the biggest background CPU sink in AFT. If you've seen sustained multi-core usage attributed to aft processes (#108), this release is for you.

Dead-code scans read the persisted call-graph store

Background code-health scans (the dead_code category behind aft_inspect and the status bar) used to rebuild a full call graph from scratch on every refresh — parsing every project file each time. On a ~3,000-file repo that meant 60+ seconds of snapshot building plus minutes of scanning, re-triggered by ordinary file activity (saving in your editor was enough).

The scan now projects from the persisted call-graph store that AFT already maintains incrementally: one cold build per project (the same store that powers aft_callgraph), then millisecond updates per file change. Measured on a 903-file repo: snapshot assembly went from ~93 seconds to ~250 milliseconds, and a full dead_code pass from over 3 minutes to well under a second.

While the store is still building (first open of a project), dead code reports unavailable (call graph building/retrying) instead of a wrong number, and fills in automatically once the build lands.

Remote source files in aft_outline and aft_zoom

URLs pointing at source files (unpkg, raw.githubusercontent, any CDN) now work: the file extension in the URL path decides how the content is parsed, so aft_outline https://unpkg.com/pkg/dist/index.mjs returns real symbols instead of an unsupported-content-type error. This also fixes a silent bug where raw.githubusercontent files (served as text/plain) were cached as markdown and outlined as headings.

Fixes

  • Call-graph store survives project moves and renames. Moving a repository used to leave the store referencing the old path, permanently disabling dead-code analysis. The store now re-roots itself on open (or rebuilds once when necessary).
  • A single stale row in the call-graph store no longer disables dead-code analysis. Orphaned call references now fall back to the file's top-level scope instead of failing the whole projection, and incremental refreshes keep symbol and reference rows coherent so the orphans stop appearing.
  • The status bar no longer shows D0 when dead code is unavailable — it keeps the last known count, and the aft_inspect body says unavailable explicitly.

Join our Discord

v0.37.1

11 Jun 14:46

Choose a tag to compare

v0.37.1: Responsive waits and bridge polish

A patch release focused on staying responsive: sending a message now interrupts a blocked watch, slow embedding backends get their configured headroom, and the bridge keeps its dispatch thread free of background work.

Your message wins over a blocked watch

If the agent is blocked in a synchronous bash_watch wait when you send a message, the wait now aborts immediately and converts itself to a background watch — the agent processes your message right away, and the original notification still arrives when the task finishes or the pattern matches.

Readable web pages in aft_outline and aft_zoom

HTML-only pages (Docusaurus blogs, docs sites) are now converted to Markdown at fetch time. Previously these pages dumped raw HTML into context, and zoom-by-heading failed because headings absorbed invisible permalink anchors. Outlines now come out clean, sections zoom by their visible heading text, and code blocks render as language-tagged fences. Markdown-native URLs are unchanged.

Semantic index responsiveness

  • semantic.timeout_ms is honored up to the real transport budget (55s) instead of being silently clamped to 25s — slow local backends (LMStudio cold-loads) keep their configured headroom, and background refresh batches stop getting aborted mid-embed.
  • Full-corpus refreshes (after .gitignore/.aftignore changes) no longer walk the project on the bridge's request thread — the walk happens on the refresh worker, so other tool calls aren't blocked behind it.
  • Loading a large index verifies file freshness in parallel on a bounded pool, shaving startup time.

Bash output protection covers more commands

The pipe protection that keeps bun test | grep fail from hiding failures now also recognizes env-prefixed runners (CI=1 bun test), bun --cwd <pkg> test, mvn clean test, and multi-task rake invocations (rake db:setup test) — with the same fail-closed safety rules for redirections, substitutions, and side-effectful filters.

Fixes

  • Background bash multi-command scripts now execute from a script file instead of shell argv, removing quoting and length edge cases, with a regression test asserting every command's output lands in the raw capture file.
  • Bridges with a crashed or timed-out process no longer pin themselves against idle eviction through leftover background-task bookkeeping.
  • /dev/fd/ paths in bash permission checks accept only numeric descriptors (/dev/fd/2) — path-traversal shapes like /dev/fd/../sda still ask.
  • The JSONC config schema now includes the inspect block and lsp.diagnostics_on_edit, so editors stop flagging valid config.
  • Two C libraries dropped from the dependency tree (oniguruma regex, and the C++ half of esaxx): the tokenizer now uses pure-Rust backends, trimming build complexity with byte-identical tokenization (regression-tested).

Join our Discord

v0.37.0

10 Jun 21:33

Choose a tag to compare

v0.37.0: Lower CPU, leaner tools

This release attacks the two complaints we heard most: AFT processes burning CPU in the background, and tool output/descriptions eating more of the agent's context than they should. It also removes a tool, trims several others, and lands a batch of correctness fixes across edits, imports, and search.

Much lower background CPU

A set of independent fixes targeting sustained CPU usage and spikes:

  • Idle bridges shut down after 30 minutes. Previously a bridge process stayed alive for every project you touched until the host exited. Bridges with running background tasks are never evicted, and a fresh bridge restores them on the next call.
  • Deleted project roots no longer spin the watcher. Removing a directory a bridge was watching (a worktree, a temp checkout) flooded it with delete events and pinned a core; the bridge now detects the deleted root and stops watching.
  • Background code-health scans reuse warm caches instead of re-parsing the project from scratch on every debounce, and their parsing pool is bounded to half your cores.
  • Semantic index loads and saves stream to disk instead of materializing multi-hundred-MB buffers, and the embedding pipeline no longer clones every batch.
  • Cold symbol scans read each file once instead of twice, and cold search-index builds parse in parallel.

aft_transform removed

aft_transform (add_member, add_derive, wrap_try_catch, add_decorator, add_struct_tags) is gone. Usage data showed agents essentially never called it — everything it did is covered by edit's symbol and find/replace modes, which agents already use fluently. Removing it shrinks every request's tool surface. If you listed it in disabled_tools, the entry is ignored harmlessly.

Leaner tool descriptions and smarter guidance

  • The bash, write, edit, apply_patch, aft_search, and aft_refactor descriptions were rewritten to be shorter and to only describe behavior that is actually enabled in your config — e.g. bash no longer advertises background: true when background execution is disabled.
  • The per-call diagnostics parameter on write/edit/apply_patch is gone; diagnostics flow through the status bar and aft_inspect, with lsp.diagnostics_on_edit as the config knob.
  • The system-prompt guidance was rewritten around what agents should do rather than what they shouldn't: concrete translations from bash habits to the right tool, and a clear protocol for waiting on background commands instead of polling them.

Background bash: right-sized completion reminders

Completion reminders for background tasks now size their output by how the task ended: a successful task shows a short tail, a failed task keeps the head plus a generous tail so the error context survives. Killed or timed-out tasks are treated as failures. Compressors also never render a success summary (N pass, 0 fail) for a command that exited non-zero.

Fixes

  • Failed refactors report failure: aft_refactor move/extract/inline now return an error when a syntax-validation rollback occurs, and a rolled-back destination write can no longer lose the moved symbol. write and apply_patch report rollbacks honestly too.
  • aft_import no longer writes a bare side-effect import when a model sends names as a JSON-stringified array; stringified arrays and bare strings are accepted, wrong types are rejected.
  • Organize imports preserves pub(crate)/pub(super) visibility in Rust and no longer drops modules from Python import os, sys statements.
  • ast_grep_replace rejects rewrite meta-variables the pattern never captures (e.g. a $MGS typo), instead of writing them as literal text.
  • Fuzzy edits preserve the trailing newline of the replaced region in batch mode (the v0.36.1 fix now covers all edit paths).
  • Outline/zoom symbol ranges are correct for Rust functions inside mod blocks, multi-declarator statements (const a = 1, b = 2), and Markdown/HTML heading sections; JavaScript files now extract top-level const/let symbols like TypeScript.
  • The sidebar no longer shows another project's data in multi-window or opencode -s setups, and reconnects more reliably.
  • LSP: a server installed mid-session is picked up on the next configure instead of requiring a restart; per-file diagnostic pulls honor their 10s cap; tsconfig with only a files list no longer treats the whole tree as members.
  • semantic.timeout_ms is honored up to the real transport budget (55s) instead of being silently clamped to 25s — slow local embedding backends (LMStudio cold-loads) no longer have their configured headroom cut, and background refresh batches stop getting aborted mid-embed.
  • Search: capped results are signaled in the output footer, and an uncapped parallel grep no longer falsely reports itself as capped.
  • Bash safety: commands with an explicit workdir outside the project root are never rewritten to AFT tools, and process-group kill works even after the leader exits.
  • Redirects to /dev/null and friends no longer trigger permission prompts (#106): device-file paths (/dev/null, /dev/stderr, numeric /dev/fd/N, ...) are skipped by the bash permission scanner, matching native shell behavior.
  • Windows: diagnostic reports redact user paths on all drives and slash styles.

Thanks to @jonmast (#107) for contributing to this release.

Join our Discord

v0.36.1

08 Jun 15:23

Choose a tag to compare

v0.36.1: Honest edits, compact inspect output, and safer pipes

A correctness-focused patch. Failed edits and patches now report failure instead of a misleading success, aft_inspect returns a compact text summary instead of a JSON dump, and the bash pipe handling added in v0.36.0 is now careful about commands where stripping a pipe would change what you asked for.

Edits and patches report failure honestly

A failed edit (no match, ambiguous match, syntax rollback) or a failed apply_patch hunk could come back looking like it succeeded — edit returned Edited (+0/-0) and apply_patch reported the file as written. Both now surface the underlying error. For apply_patch specifically, a move hunk now confirms the destination write succeeded before removing the source, so a failed write can never delete the original, and a partially-applied patch is reported as "Partially applied (N of M)" listing only the hunks that actually landed.

edit with replaceAll now refuses to run when its matches overlap (which could happen on repeating multi-line patterns), instead of applying them and corrupting the file.

Compact aft_inspect output

aft_inspect returned a full JSON payload that the agent had to parse. It now returns a compact, line-oriented summary of the actionable findings — duplicates, dead code, unused exports, todos, and diagnostics — cutting the per-call output roughly in half. When a category's background scan is stale, the last-known counts are shown (flagged stale) instead of being dropped, so the summary stays consistent with the status bar.

Safer bash pipe handling

v0.36.0 started running a test/build runner without its trailing filter (so bun test | grep fail doesn't hide failures). This release makes that careful: it no longer strips when doing so would lose a redirected file (... | grep FAIL > out.txt), change backgrounding, or misread a subshell or command substitution, and it only applies to read-only test/build invocations — never a stateful command like make deploy test. It now also recognizes more runners (gradle, maven, dotnet, rspec, phpunit, swift, deno, and others), and tells you on every path when a pipe was dropped.

Fixes

  • move_symbol no longer scans or rewrites build artifacts: the consumer search now honors .gitignore/.aftignore and skips node_modules, target, dist, build, .venv, and the like, so it won't rewrite imports inside compiled output or crawl huge irrelevant trees.
  • Multi-name Java static imports generate one valid import static Class.member; per name instead of a single invalid statement that dropped the members.
  • Glob edit reports its real counts (Edited N files (M replacements)) instead of a misleading +0/-0.
  • Edit diffs render reliably in the UI again after a metadata-handling change (#96).
  • The status sidebar reconnects more reliably and no longer briefly shows another project's data in multi-window setups.
  • Leaner output: grep result footers and bun test banners are trimmed of low-value noise.

Join our Discord

v0.36.0

07 Jun 18:35

Choose a tag to compare

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_ms had no effect (#102): a small timeout passed to a foreground bash call collapsed the wait window and could kill the command almost immediately. A foreground timeout below the wait window is now ignored (the command runs to the normal window, then promotes to background), while an explicit background: true task still honors a short timeout as a real kill cap.
  • aft_delete and aft_safety checkpoint could crash on a mistyped argument: when a host delivered the files array as a bare string or a JSON-encoded string, the tool threw inputs.map is not a function before 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).

Join our Discord

v0.35.4

05 Jun 09:05

Choose a tag to compare

v0.35.4: Large-repo CPU relief and concurrent-work stability

A patch release that stops two high-CPU situations on large repositories and under concurrent activity, fixes a crash on deeply-nested files, and hardens the semantic index, watcher, and background bash against transient failures.

High CPU on large repositories (#86)

On large repositories, aft_inspect's dead-code analysis built a whole-project call graph by re-parsing every file and resolving cross-file edges with no size limit, and it did so proactively shortly after the bridge started. On a repository with tens of thousands of files this pegged most CPU cores for a long time, even when no one asked for an inspection. Reported in #86.

The dead-code call-graph build now respects the same lsp.max_callgraph_files limit (default 5000) that the interactive aft_callgraph tools already use. Above that limit, dead-code analysis reports as unavailable instead of running the unbounded build. Duplicate detection and unused-export analysis are unaffected. Raise lsp.max_callgraph_files if you want dead-code analysis to run on a larger repository. A persisted, incremental call graph that lifts this limit entirely is in progress.

Embedding storm under concurrent activity

When the embedding backend returned a transient error during an incremental index refresh (for example a local LM Studio or Ollama server briefly overloaded by several projects indexing at once), AFT treated it as fatal and fell back to re-embedding the entire corpus. That full rebuild hit the already-busy backend, failed again, and cascaded into a sustained multi-core (and, with a GPU backend, multi-bridge) storm.

A transient refresh failure now keeps the existing index and retries only the changed files later, instead of dropping the cache and rebuilding everything. Only a genuine permanent error (such as an embedding-dimension change) triggers a rebuild.

Crash on deeply-nested files

A follow-on to the v0.35.3 crash fix: the call-extraction walkers used during inspection could still overflow the worker stack on a pathologically deep file (minified bundles, generated code, very long chained expressions), aborting the AFT process. Those walkers are now depth-bounded, so deep files are handled safely.

Resilience fixes

  • The semantic index recovers from a brief embedding-backend outage instead of parking in a failed state, and detects the local runtime version more reliably.
  • The watcher no longer leaves the index status stuck on "refreshing" after a transient hiccup, and sidebar status updates are pushed promptly.
  • Background bash keeps its worker bridge alive across a transport timeout rather than tearing it down, so long-running tasks survive.

Join our Discord