Skip to content

Releases: cortexkit/aft

v0.45.1

Choose a tag to compare

@github-actions github-actions released this 04 Jul 15:03

v0.45.1

Patch release.

Fixes

  • Pi: read/write/edit schemas rejected by strict providers (#151). The v0.43.0 filePath alias fix serialized these tool schemas as a root-level anyOf, which OpenAI-compatible function calling rejects (DeepSeek returned HTTP 400; GLM sent empty arguments). The schemas are flat type: "object" again, with the path/filePath alias pair enforced at runtime. A schema-shape contract test in both plugins now rejects any hoisted tool schema without a plain object root.
  • Rust dead code: functions referenced only inside macro bodies (tokio::join!, json!, component DSLs like element!) are no longer flagged dead. Resolution is scoped and fail-closed: bare identifiers, string contents, and receiver methods never rescue anything, and an ambiguous name resolves to nothing.
  • Removed ~300 lines of dead exports and duplicated JSONC parsing code found by aft_inspect on this repository.

Internal

  • The Rust test suite runs integration tests under cargo-nextest locally and in CI (unit tests stay on libtest, watcher tests stay isolated), cutting the loaded-machine test gate from ~25-45 minutes to ~2 minutes.
  • Fixed a latent test-teardown deadlock where in-process test configures installed real FSEvents watchers that could wedge forever when macOS fseventsd was saturated.
  • macOS CI runners pinned to macos-26.

v0.45.0

Choose a tag to compare

@github-actions github-actions released this 03 Jul 14:44

v0.45.0

Code Health accuracy release. A 12-repository field audit (Next.js, NestJS, SvelteKit, Vite, Express, Rust workspaces, Tauri, Go, Python, Java/Kotlin, mixed monorepos, codegen-heavy) drove a set of dead-code and unused-export correctness fixes, re-verified against the same repositories after the fixes.

aft_inspect correctness

  • No fabricated results for outline-tier languages. Dead code is now reported only for languages with a real liveness substrate (TS/JS via oxc; Rust, Go, C, C++, C#, Zig via the call graph). Java, Kotlin, and other outline-tier languages are excluded and listed as not analyzed instead of producing false findings. On OkHttp this removes 8,122 spurious dead-code items.
  • Dead code no longer sticks at unavailable or serves stale counts. Fixed a harness-storage mismatch that left dead code permanently unavailable on some large repos even after warmup (Biome: unavailable → available), and a cache-reuse gap where watcher-driven file changes and deletions didn't refresh the call-graph projection, pinning stale counts.
  • Same-file references keep exports alive. Exported functions called earlier in the same file (or captured via object shorthand) were flagged dead under entry-point reachability. Found live in Vite (createParseErrorInfo, updateStyle).
  • Convention entry points. Tool config defaults (*.config.ts for Playwright, ESLint, tsup, tsdown, oxlint, Vitest), Storybook CSF story exports, Mocha root hooks referenced from package.json, and SvelteKit hooks.server/client special exports are now framework-called liveness roots. SvelteKit $lib/* imports resolve without a generated tsconfig, ambient .d.ts declaration-module exports are no longer dead-code candidates, and package.json exports subpaths (including conditional and build-output targets) propagate public-API liveness through barrels.
  • Rust call resolution. Function-scoped use ... as module aliases, module-qualified calls to inline sibling modules, cross-crate pub use re-exports, and turbofish method calls now resolve to their targets. Calls inside macro token trees (json!, custom UI macros) remain a known limitation.
  • Go interface dispatch. Methods invoked only through interfaces (sort.Interface's Len/Less/Swap, Bubble Tea's FilterValue) are no longer flagged dead. The new method-only gate also stops dispatch names from accidentally rescuing same-named plain functions, so some Go repos report a more honest, higher count.
  • Generated code is bucketed, not mixed in. Dead code, unused exports, and duplicates split generated files (gen/ trees, *_pb.ts, @generated/DO NOT EDIT banners) out of the headline count into a generated: N bucket. On protobuf-es the dead-code headline drops from 5,091 to 52 with 2,283 bucketed as generated. Totals always add up; nothing is hidden.

CLI

  • Fixed npx @cortexkit/aft doctor --fix and setup failing to download the binary ("Stripping types is currently unsupported for files under node_modules"). The published CLI resolved an installed dependency at runtime instead of bundling it; the bundle is now fully self-contained, with a regression guard.
  • wait-release-style release tooling: run discovery now attaches to already-running workflows.

Reliability

  • Bridges detect when the on-disk aft binary changed (e.g. after an update) and gracefully respawn onto the new binary at the next idle window instead of running the old one until restart.
  • Bumped @cortexkit/subc-client to 0.3.0 (timeout arbitration recovers replies raced by request deadlines under heavy load).

Field audit harness

  • New benchmarks/inspect-field-audit/ harness: clones 12 repository categories, runs warmup + aft_inspect, and produces per-repo verification reports. Used as the acceptance gate for this release and rerunnable for future correctness work.

v0.44.0

Choose a tag to compare

@github-actions github-actions released this 03 Jul 08:21

v0.44.0

Code Health accuracy release: aft_inspect's dead-code and unused-exports analysis was
field-tested against a real production monorepo and every false-positive class found was fixed.
On that repo, reported dead code dropped from 951 items to 209 real ones. Plus a new import-cycle
detector, Objective-C support, and a bash wait parameter.

aft_inspect: Code Health accuracy

  • Framework entry points. Functions invoked by frameworks rather than by code are no longer
    reported dead: Rust attribute roots (#[tauri::command], wasm-bindgen, FFI exports), file-based
    routes (Next.js, Nuxt, SvelteKit, Remix/React Router, Astro), and decorator-based entry points
    (NestJS controllers, providers, GraphQL resolvers — import-binding aware, so a same-named
    decorator from a local module doesn't count). Route files seed only their framework-called
    exports, so dead helpers inside a route file are still caught.
  • Barrel re-exports collapsed. Re-export bindings in barrel files no longer count as separate
    dead exports; references through any import path attribute to the canonical definition, and dead
    canonicals show where they're re-exported. This also fixed a hidden inverse bug where an unused
    barrel could keep dead code alive.
  • Test-only usage is its own tier. Symbols referenced only from test files no longer inflate
    the headline counts; they're reported separately ("test-only usage: N, used by x.test.ts") so
    the headline means "deletable" again.
  • Import cycle detection (new). TS/JS import cycles are detected via strongly-connected
    components over resolved (non-type-only) imports, with per-edge drill-down chains. Drill down
    with sections: ["cycles"].
  • Duplicates reframed. Duplicate reporting now leads with duplicated lines, percentage of
    analyzed code, and file count instead of a bare group tally, and suggests extraction targets.
    New inspect.duplicates.expected_mirrors config suppresses intentional mirror pairs (e.g.
    plugin/pi-plugin parity trees) with honest "N mirror groups suppressed" accounting; inline
    escape hatch: a comment containing aft:expected-duplicate.
  • Stale diagnostics fixed. Files changed outside AFT's own editing tools (scripts, git
    operations, other programs) no longer serve stale LSP diagnostics in aft_inspect and the
    status bar: the watcher now marks cached diagnostics stale, hides them from counts, and resyncs
    the file with the language server.
  • Windows: fixed a path-normalization asymmetry that could silently drop entry-point liveness on
    Windows only.

Languages

  • Objective-C (.m, .mm): outline, zoom, AST search/replace, and semantic indexing.
    .h headers stay mapped to C in v1.

Bash

  • wait: true: run a command in the foreground and block until it finishes instead of
    auto-promoting to background at the wait window. For commands you know are long where you need
    the result before doing anything else. Explicit timeout is honored as the bound;
    wait + background (or pty) is rejected as contradictory.

Fixes

  • Pi: edit and write no longer show a confirmation prompt on every mutation (regression in
    v0.43.0). Project-internal edits apply directly again, matching native Pi tools.
  • Pyright now honors pyrightconfig.json (venvPath, extraPaths): config files take priority
    over nearer fallback markers (like requirements.txt) when picking the LSP workspace root, so
    venv-only imports stop producing false errors (#144).
  • TUI sidebar badges stay readable on transparent-background themes (luminance-based text color
    fallback) (#142).
  • grep results are deterministic when files share an mtime (path tiebreak after recency).
  • trace_to recognizes attribute-rooted functions (e.g. Tauri commands) as entry points.

Notes

  • First aft_inspect after upgrading re-scans the project (analysis caches rebuilt for the new
    verdict semantics).

v0.43.1

Choose a tag to compare

@github-actions github-actions released this 02 Jul 22:12

v0.43.1

Patch release fixing the TUI sidebar in v0.43.0.

Fixed

  • TUI sidebar failed to load silently in v0.43.0. The published @cortexkit/aft-bridge
    package declared @cortexkit/subc-client as a devDependency while its compiled output imports
    it directly, so the sidebar (which resolves the published package under the OpenCode host) hit
    an unresolvable module and never rendered. It is now a regular dependency, and the package
    validation gate enforces the correct direction so this cannot regress. Thanks to vizid on
    Discord for the precise diagnosis.

No other changes: this release is v0.43.0 plus the dependency fix.

v0.43.0

Choose a tag to compare

@github-actions github-actions released this 02 Jul 19:24

v0.43.0

Highlights

Fixes you asked for

  • Bash compression no longer fabricates output: seven builtin filters (kubectl, helm, gradle, make, ansible-playbook, xcodebuild, docker) replaced empty successful output with synthesized text like kubectl: no resources found, misleading agents about what actually happened. Empty output now stays empty, and a guard test ensures no filter can ever synthesize text from empty input again. (Discord report)
  • aft_search is no longer blocked by a grep deny rule (#147): it now asks under its own aft_search permission id, so a rule steering agents away from grep no longer kills the tool it steers them toward.
  • No more repeated /tmp permission prompts (#145): system temp directories (/tmp, /var/tmp, $TMPDIR, and macOS /private aliases) no longer trigger external-directory asks. Prompting for world-writable scratch space protected nothing, the host's "Always Allow" doesn't persist across restarts, and macOS's per-boot-randomized temp paths made persistent rules impossible to write. Hard path restriction (restrict_to_project_root) is unchanged.
  • enabled config toggle (#149): set "enabled": false in <root>/.cortexkit/aft.jsonc to disable AFT for one project, or in ~/.config/cortexkit/aft.jsonc to disable it everywhere (a project can re-enable). Disabled projects register no tools, spawn no processes, and aft doctor reports the state.
  • Pi: filePath/path argument aliases (#148): models that mix up the two parameter names (common with GLM) now work on read/write/edit instead of failing validation.

TUI

  • WebSocket push replaces sidebar polling: the sidebar now updates when state actually changes instead of polling every 1.5s, cutting idle CPU.
  • AFT no longer re-adds itself to tui.json(c): if you remove the sidebar plugin entry, it stays removed. Registration is owned by aft setup / aft doctor --fix.

Under the hood: Subconscious daemon readiness

This release completes AFT's daemon-mode plumbing (dormant by default, nothing changes for standalone users):

  • All 21 tools callable through the subc daemon transport, with server-side rendering, deferred bash orchestration, PTY screens rendered in Rust (vt100), and reliable background-completion delivery with replay.
  • Principal-based trust enforcement: MCP-facade connections get forced path restriction, no shell access, and project-tier config caps: verified end-to-end with a real MCP client (all trust checks held on the wire).
  • A user-tier subc.connection_file key selects the daemon transport; when set, the standalone spawn path is structurally disabled (fail-loud if the daemon is unreachable, never a silent fallback).

Other fixes

  • Semantic search e2e now exercises the real ready path when a local ONNX Runtime is present.
  • glob results are sorted deterministically before truncation.
  • Windows: display paths in apply_patch normalize to forward slashes; PTY golden fixtures checkout with LF; assorted CI-contention deflakes.
  • Background bash task ids widened to 16-hex slugs (collision hardening).

Upgrade notes

  • No breaking changes. Standalone mode (the default) is byte-identical in behavior aside from the fixes above.
  • If you previously removed the AFT TUI entry and it kept coming back: it won't anymore. Run npx @cortexkit/aft setup if you ever want it re-registered.

v0.42.0

Choose a tag to compare

@github-actions github-actions released this 26 Jun 22:08

v0.42.0: leaner tool output and configurable backups

This release focuses on the cost of every tool call — how much context AFT's navigation and search tools spend to answer a question — and adds configurable file backups behind undo. The output changes were measured against an external head-to-head benchmark suite and cut AFT's per-task tool-context cost substantially at unchanged answer quality.

Leaner navigation and search output

The navigation and search tools were dumping more than the agent needed: a whole class body when only a menu of its methods was wanted, every standard-library call in a call tree, the same callee repeated once per call site. This release trims each of these to what's actually useful, with no loss of information.

  • aft_zoom on a large container returns a member menu, not the whole body. Zooming a class, struct, impl, trait, or enum larger than ~150 lines now returns its members listed with their signatures and line ranges — the same shape aft_outline gives a file — instead of the entire body. Zooming a specific method still returns its full body; a small container is still returned whole. This stops one zoom of a large class from spending tens of thousands of characters the agent then bypasses to zoom the method it actually wanted.
  • aft_search expands only the top match, within a budget. The top result is shown as full symbol source (so it can be edited without a re-read); ranks 2-3 are short previews; the rest are headers only. If the top match is itself a large symbol, it is rendered within a budget — a member menu for a big container, a head slice for a big function — rather than dumped whole.
  • aft_callgraph collapses standard-library noise. In call_tree, unresolved external/stdlib calls (len, Some, clone, …) are folded into one summary line per caller instead of one line each. These made up over half of call-graph output and can't be navigated anyway. Resolved, project-internal edges are unchanged. Pass includeUnresolved=true to see every edge individually.
  • aft_zoom call lists are deduplicated. calls_out / called_by now list each callee once with a count, instead of repeating a line per call site.
  • Ambiguous name zooms return candidate signatures to choose from, instead of an error.
  • Generated documentation trees are down-ranked in search. Committed jazzy/javadoc/sphinx output (.html/.css/.json artifacts) no longer crowds real source out of aft_search's top results. grep stays exhaustive and unchanged; hand-written docs are unaffected.
  • aft_search invites natural-language questions for conceptual queries (its semantic lane is tuned for intent), and hides test files by default.

Configurable backups behind undo

aft_safety's undo is backed by per-file snapshots on disk. This release makes that configurable and removes write amplification from the edit hot path:

  • New settings: backup.enabled (default true), backup.max_depth (default 20), and backup.max_file_size — control whether backups are kept, how deep the per-file history goes, and a size ceiling above which a file isn't snapshotted.
  • Faster edits. Each edit now appends a single backup file instead of rewriting the entire backup stack, so backup cost no longer grows with history depth.
  • Rollback safety is preserved. Internal transactional rollback for failed multi-file refactors and renames stays active regardless of the backup.enabled setting.

Search ranking

Semantic search adds ranking priors that lift exact-name definitions and "type + concept" matches, improving retrieval on identifier and concept queries without changing the index format or requiring a re-index.

Fixes

  • Editing Rust code containing &raw (a variable named raw passed by reference, e.g. handle(&raw)) no longer fails validation and silently rolls back. Bumped tree-sitter-rust and several other grammars to current releases.
  • Editing files whose paths contain [brackets] or {braces} — e.g. Next.js dynamic routes like app/[id]/page.tsx — now resolves the literal path first instead of treating it as a glob pattern. Thanks to @tobwen.
  • Dependency bumps: zod, @opencode-ai/*, the Pi SDK, and @clack/prompts.

Under the hood

  • Lower peak memory and CPU during indexing: cold semantic builds and the Tier-2 code-health scan are serialized so they don't pile onto the same cores at once.

v0.41.0

Choose a tag to compare

@github-actions github-actions released this 25 Jun 22:33

v0.41.0: search and navigation at any repository size

This release removes the size limits that used to disable AFT's search and call-graph features on large codebases, and extends semantic search to eight more languages. The headline change is structural: the trigram index that powers grep and aft_search is now disk-backed, so it holds a fixed, small amount of memory no matter how large the repository is. On Chromium (176,000 source files) the index dropped from ~14 GB of RAM to under 800 MB — roughly 18x less — with no change to results and no measurable slowdown on normal repositories.

Because memory is no longer the constraint, the file-count caps that existed only to bound it are gone. Search and call-graph navigation now work on repositories of any size, where before they silently switched off above a threshold.

Search works on repositories of any size

The trigram index used to be held entirely in memory, which forced a 20,000-file ceiling: above it, AFT disabled the index and grep/aft_search fell back to a slower full scan. That ceiling is removed.

  • The index is now disk-backed. Posting lists live on disk and are read on demand per query; only a small directory and the per-session edits stay in memory. Peak memory is now bounded by the index structure, not the repository size.
  • No 20,000-file cap. Monorepo-scale repositories (Chromium, large Java estates, and similar) keep full indexed grep/aft_search instead of dropping to fallback.
  • Results are unchanged. Output is byte-identical to the previous in-memory index, verified against the full search test suite. On a 1,200-file repository query latency is identical; at 160,000 files it is within single-digit percent — a small cost for working at a scale that previously had no index at all.
  • Builds stay incremental. The index builds in the background and updates only changed files, as before.

Call-graph navigation has no file-count limit

aft_callgraph operations (callers, call_tree, impact, trace_to, trace_to_symbol, trace_data) and cross-file symbol moves (aft_refactor move) were capped at 5,000 source files; above that they returned a "project too large" error. That cap is removed. These operations are served from the persisted call-graph store, which scales to large repositories, so they now work regardless of project size.

Semantic search covers more languages

aft_search's semantic lane now indexes eight additional languages that AFT already parsed for outlines and navigation but did not embed for meaning-based search: Java, Kotlin, Ruby, Swift, Scala, Lua, Perl, and R. (PHP was already covered.) Semantic search now spans every language AFT extracts code symbols for; only plain-document formats (Markdown, HTML, JSON) remain text-only. Existing indexes are not rebuilt — the new languages are picked up as files are indexed.

v0.40.3

Choose a tag to compare

@github-actions github-actions released this 25 Jun 16:07

v0.40.3

read now hands images and PDFs to the model.

Images and PDFs are read as attachments again

When you read an image (PNG, JPEG, GIF, WebP), AFT now passes it to the model as a real visual attachment instead of a "binary file" placeholder — so a vision-capable model actually sees the picture. Images are decoded and downsized (longest side capped at 1024px) so they stay light in context. On OpenCode, read also hands PDFs to the model as attachments.

Non-vision models are handled gracefully: the image is omitted with a short note rather than breaking the read. Text files, directories, and non-media binaries behave exactly as before.

This restores behavior the host's built-in read provides; AFT's read had been returning a placeholder instead.

No action needed.

v0.40.2

Choose a tag to compare

@github-actions github-actions released this 25 Jun 07:55

v0.40.2

A patch release that finishes the v0.40.0 config consolidation for the CLI.

aft doctor / setup now read config from the right place

v0.40.0 moved AFT config to the shared CortexKit location (~/.config/cortexkit/aft.jsonc), but the aft CLI (doctor, setup, doctor lsp) was still looking in the old per-harness directories (~/.config/opencode, ~/.pi/agent). Since the migration renames the legacy file aside, aft doctor reported your config as "(not set)" even when you had a valid one, and aft setup wrote $schema to the dead path.

The CLI now resolves AFT config from the same canonical path the plugin uses, so the two always agree. This also fixes a fresh-install case where aft setup could create aft.json (which the plugin doesn't read) instead of aft.jsonc.

No action needed. Your harness config (opencode.jsonc) and tui.jsonc are unaffected.

v0.40.1

Choose a tag to compare

@github-actions github-actions released this 24 Jun 23:10

v0.40.1: TUI loads on OpenCode 1.17.10

A patch fix for the AFT sidebar and /aft-status failing to load on OpenCode 1.17.10.

Fix

OpenCode 1.17.10 bumped its bundled OpenTUI to 0.4.2. AFT's TUI entry is raw TSX that needs @opentui/solid and solid-js at import, and the plugin had been relying on the host providing them. On 1.17.10 that copy became unreachable from the plugin, so the sidebar and /aft-status failed to load with Cannot find module '@opentui/solid/jsx-dev-runtime'.

The plugin now declares @opentui/core, @opentui/solid, and solid-js as its own dependencies, matching the host's pinned versions, so the TUI resolves its rendering runtime regardless of how the host bundles it. A new test imports the TUI entry the way OpenCode loads it, so this class of breakage is caught in CI going forward.

If you updated to OpenCode 1.17.10 and lost the AFT sidebar or /aft-status, this restores them.


Questions or feedback? Join us on Discord.