Skip to content

Releases: cortexkit/aft

v0.48.1

Choose a tag to compare

@github-actions github-actions released this 22 Jul 16:17

v0.48.1

Patch release: correctness and performance fixes. No new features, no config changes.

Sandbox (beta)

  • Fixed the Linux sandbox breaking exit-code recording for every sandboxed bash command. The Landlock launcher's descriptor-hygiene step closed the two file descriptors AFT uses to record a command's exit status, so sandboxed commands finished with no exit code (and a command interrupted mid-git could leave a stale .git/index.lock behind). The hygiene step now preserves those descriptors, and a Linux CI test runs a real sandboxed command end-to-end to verify exit codes are recorded. macOS was unaffected. (#167)

Search

  • Cross-project aft_search on a repository that has no AFT index now returns a bounded lexical scan with a disclosure instead of failing with not_indexed. The error dead-ended agents into shell fallbacks; the same situation in grep already degraded gracefully, and the two tools now behave consistently. Semantic ranking still requires an index built by a session in that project, and the disclosure says so. (#166)

Import management

Three silent-mutation bugs in aft_import, all in the same defect class (import statement shapes modeled too coarsely):

  • Solidity: single-quoted import paths (import './X.sol';) were stored with their quotes, so remove reported module_not_found while the import remained, and add inserted a duplicate with the other quote style. Both quote styles now parse identically across all four Solidity import forms.
  • Scala 2: add in a Scala 2 file never recognized an already-present import (an internal dialect marker made the comparison always fail), so repeated adds accumulated duplicate lines. Deduplication now works; Scala 2 syntax generation is unchanged.
  • Lua: removing an import from a mixed declaration like local x = require("x"), sideEffect() deleted the entire line including the unrelated call, while reporting success. Mixed declarations are no longer treated as imports: remove leaves them byte-identical and reports module_not_found; plain local x = require("x") declarations behave as before.

Performance

  • Call graph extraction no longer rescans the file's AST once per symbol. Cold builds and watcher refreshes of symbol-heavy files scale linearly instead of quadratically (measured: 1,000-symbol file 102ms → 23ms; output byte-identical).
  • Code-health scans (aft_inspect metrics and TODO categories) previously thrashed a fixed-size cache on projects over ~4,000 files, re-reading every file on every scan. The cache now sizes to the project, and unchanged warm scans read nothing (measured: 12,000-file warm scan 101ms → 45ms for metrics, ~90ms → ~40ms for TODOs).
  • Outline generation (also used by aft_zoom container menus and top-ranked search snippets) replaced a quadratic insertion scan with an indexed one. Files with thousands of one-method containers render ~11x faster (measured: 16,000 symbols 65ms → 5.7ms; output byte-identical).

Build

  • Fixed cargo test --release compilation of the library test suite (debug-only test helpers were referenced from tests that compile in release mode).

v0.48.0

Choose a tag to compare

@github-actions github-actions released this 21 Jul 21:29

v0.48.0

Introduces an optional native command sandbox for hoisted bash (beta, off by default), plus a Windows background-bash correctness fix and a search cache-key fix. Safe drop-in upgrade from v0.47.x — no behavior changes unless you explicitly opt into the sandbox.

Native command sandbox (beta, opt-in)

AFT can now confine the commands it runs through hoisted bash using the operating system's own sandbox, so a command cannot write outside your project or read your credentials even if it tries. It is off by default; enable it per-user or per-project with "sandbox": { "enabled": true } in aft.jsonc. This is a beta: we want real-world feedback before considering it a default.

What it enforces when enabled:

  • Writes are confined to your project, the per-task temp directory, and toolchain caches; everything else on disk is read-only. rm -rf outside the project is refused by the kernel, not by a heuristic.
  • A credential floor is never readable or writable~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gcloud, ~/.azure, and ~/.config/cortexkit — and on Linux this floor extends to systemd and container secret directories (/run/credentials, /run/secrets).
  • Git metadata is protected: .git/hooks cannot be written (so a command cannot plant a hook), while ordinary git operations keep working.
  • A first-party command can request a one-time escape with sandbox: "host", which prompts you for approval and runs that single command unconfined.

Platform coverage is stated honestly rather than overpromised:

  • macOS (Seatbelt): full profile — read floor, write confinement, and the credential/hooks denials above.
  • Linux (Landlock, kernel 5.13+ with ABI ≥ V3): read floor and write confinement enforced from held file descriptors, with the secret floor denied by omission. Documented limits, closed later by a container tier: nested write-denies inside a writable tree, AF_UNIX socket connects, and pre-existing hard-link/bind-mount aliases are not fully constrained; /proc is readable (same-UID process visibility depends on your ptrace_scope). If the kernel cannot enforce the required rights, execution fails closed rather than running unconfined.
  • Windows: not supported; enabling the sandbox there fails closed instead of running unconfined.

Full details and the platform matrix are in the sandbox section of docs/config.md.

Correctness fixes

  • Windows background bash tasks now record completion after a detach/rebind. A detached task whose output was correct could report failed with no exit code, because the exit marker was only written by the still-attached daemon; the command now records its own exit code, so detached completion survives losing the daemon handle. (Unix was unaffected.)
  • Search cache keys are stable for repositories with grafted history. Repos with multiple root commits could re-derive a different artifact cache key across runs (triggering a full index rebuild) because the root-commit set was hashed in git's traversal order; it is now canonicalized before hashing.

Under the hood

  • The macOS local test gate signs and warms every test binary up front to avoid the first-exec assessment tax, instead of relying on an ineffective scanner kill.

v0.47.3

Choose a tag to compare

@github-actions github-actions released this 20 Jul 16:45

A correctness-and-performance patch. Every change is a fix, a speedup, or a stability improvement to existing behavior — no new features, no config changes. Safe drop-in upgrade from v0.47.x.

Correctness fixes (tools returning wrong results)

  • aft_search could report zero results when the match existed. With the default includeTests: false, hidden test-file matches consumed the result cap before production files were reached, so a real match in source could come back empty. Test files are now excluded during candidate selection, before the cap.
  • Bash ls no longer exposes hidden dotfiles that a plain ls would hide — ls DIR invocations rewritten to the internal reader now preserve native hidden-file visibility.
  • aft_conflicts kept conflicts with non-ASCII or unusual filenames. Filenames with accents, CJK characters, or control bytes were silently dropped from the conflict list; they are now parsed from raw NUL-delimited git output.
  • aft_import no longer deletes sibling imports when removing one name from a comma-separated PHP use A, B; declaration (removing A used to take B with it). Java static-member import removal is fixed the same way.
  • aft_safety restore preserves Unix permission bits (including the executable bit) when restoring from the durable backup store, even if the metadata sidecar is unavailable.
  • aft_callgraph trace_data no longer reports a flow after the variable was overwritten. let x = raw; x = "other"; sink(x) no longer claims raw reaches sink; conditional overwrites are marked approximate rather than dropped.
  • aft_inspect reports self-import cycles (a file importing itself) that were previously omitted from the cycle count and drill-down.
  • aft_inspect no longer counts TODO/FIXME markers inside string literals — markers are read from real comments (via the syntax tree), not from comment-looking text in strings.
  • Bash cat/redirect rewrites read and write the correct file when a double-quoted path contains a backslash before a non-special character.
  • aft_zoom finds HTML headings by their id= anchor. Real anchors set via id attributes previously returned symbol_not_found.
  • aft_refactor move/inline preserve namespace-consumer imports on moves and reject ambiguous qualified inline call sites instead of rewriting the wrong one.

Performance

  • Faster indexed search: postings decode directly into the filtered result without a throwaway allocation per query trigram.
  • Faster tool dispatch: tool-call arguments are moved through translation rather than deep-cloned, and the request is built without re-materializing the parameter tree.
  • Faster aft_callgraph call_tree: converging call graphs no longer re-query the same node's edges per path.
  • Faster aft_inspect duplicate rollups: cached contributions are read by reference instead of deep-cloned.
  • Lower allocation on the hot path: per-call path resolution borrows ordinary paths, and background completion pushes share one serialized body across routes.
  • Faster background bash status lookups and lower unbounded growth: the task table is indexed and old delivered tasks are reclaimed.

Stability and resource use

  • Closed a route leak that kept dead projects bound. Failed requests could leave route handles open, so long-lived daemons accumulated actors (and their memory) for worktrees deleted long ago; routes are now closed on discard and a connection exit releases its projects.
  • Daemon restarts no longer trigger a re-embedding storm — post-restart semantic refreshes are capped through the shared concurrency limiter instead of all projects re-checking at once.
  • Health reporting now exposes live watcher/actor/route counters and a compact per-root memory rollup, and borrow-only worktrees report an honest ready/disabled status instead of a permanent "building".

Language support

  • Perl now uses the ts-parser-perl grammar (contributor PR #126, thanks @M0Rf30).

v0.47.2

Choose a tag to compare

@github-actions github-actions released this 18 Jul 18:03

v0.47.2

Patch release: a wave of correctness and hot-path fixes from an automated
verify-fix loop, plus URL-reading and PATH improvements.

Data-integrity fixes

  • apply_patch no longer corrupts CRLF files. Updating a file with Windows
    (CRLF) line endings previously wrote mixed endings (the changed line came
    back LF among CRLF lines). Patches now preserve the file's dominant line
    ending.
  • inline refactor preserves operator precedence. Inlining twice(1 + 2)
    where twice(x) => x * 2 produced 1 + 2 * 2 (wrong: 5 instead of 6).
    Non-atomic arguments are now parenthesized.
  • Python import aliases are no longer dropped. Removing one name from
    from m import a, b as c (or import a, b as c) could delete the whole
    statement including the aliased sibling. Aliases are now modeled and
    preserved.

Reliability fixes

  • Fixed a health-probe deadlock under the daemon. A health check could
    deadlock against a starting background worker (lock-order cycle), stalling
    the probe until the supervisor restarted the module. Health checks now
    never wait on worker startup.

  • extract_function no longer drops variables. Extracting a range that
    declared multiple variables used later kept only the first and silently
    deleted the rest (code that crashed at runtime). Multiple live-out
    variables now return together (object destructuring in TS/JS, tuples in
    Python), and unsupported shapes are rejected with a clear error instead of
    emitting broken code.

  • File edits no longer rewrite the whole undo history in the database.
    Each edit re-wrote every retained undo entry (~41 SQL statements at default
    depth); appends now write only the delta.

  • bash_watch no longer reports fabricated matches. stdout and stderr were
    concatenated before pattern scanning, so a pattern could "match" across the
    seam (stdout ERR + stderr OR = ERROR). Streams are now scanned
    independently, and matches report which stream they came from.

  • bash wait: true polling no longer re-reads output 10×/second. Long
    foreground waits stopped doing redundant per-poll output reads under the task
    lock.

  • Orphaned auto-update locks self-heal. A crashed process could leave a
    stale lock that permanently disabled update checks; dead/stale locks are now
    reclaimed.

  • Warm symbol lookups skip re-hashing unchanged files (zoom/outline/rank-0
    search), and the LSP diagnostics store is indexed by file so watcher
    invalidation no longer scans every entry.

URL reading

  • GitHub file links just work. aft_outline / aft_zoom on a
    github.com/…/blob/… URL now fetch the raw file content instead of the web
    page shell (GitLab -/blob/ too).
  • Heading lookups tolerate real-world headings. Zooming a documentation
    heading now matches when you pass the human text, even if the outline shows a
    numeric prefix (12.1.5 …), a markdown link label, an emoji, or an anchor
    slug (#http_flow / http-flow).
  • Paths and targets can be given as file:// URLs.

Shell PATH

  • bash finds tools installed via your shell rc. The login-shell PATH probe
    now sources interactive rc files (zsh .zshrc), so binaries on a PATH you
    export there (e.g. ~/.opencode/bin) resolve in bash tasks.

v0.47.1

Choose a tag to compare

@github-actions github-actions released this 17 Jul 16:03

AFT v0.47.1

Patch release: three data-integrity fixes, much better output for MCP hosts, two requested features, and a batch of hot-path performance work.

Fixes

apply_patch could delete a file when a patch moved it to its own path. An update hunk whose *** Move to: target resolved to the source file wrote the new content and then removed the "source" — deleting the file while reporting success. A same-path move is now treated as what it means: an in-place update. (Affects GPT-family sessions in particular, where apply_patch is the primary edit tool.)

Cross-filesystem moves destroyed symlinks. When aft_move fell back to copy+delete (source and destination on different filesystems), the copy followed the symlink and produced a regular file with the target's contents. The fallback now recreates the link itself, preserving the link text exactly — relative targets stay relative, and moving a dangling link works.

Failed deletes created phantom undo operations. A delete that failed (for example, a read-only parent directory) left its pre-delete backup in the undo history; the next undo "restored" the unchanged file instead of reverting your previous real operation. Failed deletes now leave no trace, including partial failures inside recursive directory deletes.

Auto-update could permanently disable itself. If the process holding the update lock crashed or was killed, the leftover lock file blocked every future update check on that machine. Orphaned locks (dead owner, or older than an hour) are now reclaimed safely.

MCP hosts got raw JSON instead of readable output. Tool replies to MCP clients (for example Claude Code via the CortexKit gateway) carried a structured payload those hosts fed to the model as a JSON dump — with the rendered text buried inside it at several times the token cost. MCP replies are now text-only, and status renders a compact summary instead of a full JSON snapshot.

Pi: file: URLs and batch edits. Pi's path handling now decodes file: URLs at the same choke point as OpenCode's, and both plugins share one decoder implementation matched byte-for-byte with the server's.

Features

  • file: URL paths (#162): tools accept RFC 8089 file: URLs (file:///path, file:/path, file://localhost/path) anywhere a path is expected.
  • No prompt for your own bash artifacts (#161): reading a background task's own output files (the paths bash results point at) no longer triggers an external-directory prompt; the server already restricts these files to the owning session.

Performance

  • Warm symbol lookups (zoom, outline, top search hit rendering) no longer re-read and re-hash the file on every call when size and mtime are unchanged.
  • Hybrid search no longer re-reads the same posting lists once per candidate — each query trigram's postings are read once per query.
  • Semantic/hybrid search snippets stream only the needed portion of hit files instead of reading whole files per query.
  • Edits, writes, and patches no longer deep-copy the file's entire undo history twice per mutation.
  • Semantic search caches embedding norms instead of recomputing them for every entry on every query.

Internal

  • Symbol-cache freshness formally adopts stat-first trust (matching the search index): size+mtime match serves the cache; content hashing runs only when mtime moved; the file watcher remains the invalidation path.
  • CI: unit and e2e stages run in parallel, PR e2e uses a faster build profile (releases still test the exact release profile), and the release pipeline builds binaries alongside tests instead of after them — publishing still gates on everything being green.
  • E2E harnesses no longer allow the auto-updater to swap the locally built plugin under test for the published one.

v0.47.0

Choose a tag to compare

@github-actions github-actions released this 16 Jul 19:37

AFT v0.47.0

128 commits since v0.46.0 — 250 files, +48k/−8k, the largest release in the project's history. Three arcs converge: the daemon transport moves to wire v2, project roots survive restarts warm, and a long list of CPU, memory, and latency pathologies found by running AFT as a fleet-wide daemon are fixed at the mechanism level.

Subc wire v2

The daemon transport is cut over to subc protocol 0.9 (wire v2): typed route channels carry an epoch, and both endpoints validate direction-agnostically, so stale channel state can never route a reply into a newer binding. Around the cutover, the recovery paths hardened end to end:

  • Reconnects, stale route handles (stale_route_handle, unknown_channel), and transient initial-attach failures retry with bounded backoff instead of failing the tool call.
  • A same-root bind with a higher epoch implicitly replaces the older route when the daemon's best-effort Goodbye did not arrive.
  • The connection file's wire version is validated before TCP attach: a v1/v2 mismatch fails loud instead of producing framing timeouts.
  • Tool-call phase traces (queue → translate → execute → format → egress) make slow daemon calls diagnosable from logs.

Operator note: upgrade daemon and module together; the wire versions do not mix (by design, and now by explicit error).

Restarts keep roots warm

Previously, every host restart tore down each project root's warm state: on rebind the module re-verified whole corpora by content hash and force-rebuilt callgraph stores — with many roots restarting at once, this was the "fans spin up after every restart" storm.

Now a transient unbind is cheap by design. Route teardown stops all background work for the root (configure tails, queued maintenance, in-flight index builds — via a new executor-level job cancellation with cooperative checkpoints) but keeps the watcher running and the artifacts resident. Watcher events accumulate while unbound and replay after rebind, so nothing is missed and nothing is rebuilt. The heavy teardown (watcher stop, strict re-verification, forced callgraph reconcile) now happens only when a root stays unbound past a 30-minute idle TTL, or when its directory is deleted.

Measured in fleet use: rebind after a module bounce went from seconds of hashing plus a full callgraph rebuild per root to microseconds, with AFT tool latency staying flat across 10+ consecutive daemon deploys.

Underneath this sits a publication-safety layer for background index builds: receiver generations and epochs gate which completed build may install or persist, callgraph pointer publication is fenced at the actual generation swap, forced-rebuild demand uses monotonic tokens, and freshness memos are ticket-fenced against watcher events that arrive mid-verification. A stale worker from a previous configure can no longer overwrite a newer one's state, in RAM or on disk.

Performance

  • Indexed grep/glob 2-4x faster. Both paid a full ignore-aware filesystem walk per query just to report scope emptiness; scope is now derived from the index snapshot (grep p50 1.6-4.8ms, glob 3.3-3.9ms).
  • Cross-project (borrowed) search ~40x faster. Every borrowed query re-hashed the external corpus (Blake3 over thousands of files) for a freshness census; the parsed borrow index is now cached by artifact identity (medians ~470ms → 1-12ms).
  • Semantic re-embeds coalesce under a 15-second quiet window instead of firing per edit; changed files stay masked from semantic results until re-indexed. Interactive query embeddings get a dedicated budget (semantic.query_timeout_ms, default 3000ms, clamped 500-15000) with lexical fallback, so a busy embedding backend can't stall search.
  • Binds acknowledge before heavy work. Index deserialization moved off the bind-ack path to post-ack maintenance; warm rebinds skip redundant re-verification via a stat-first memo (rebind cost 574ms → 19µs on a warm root).
  • Idle roots release memory. Root-scoped artifacts are attributed, evicted after the idle TTL, and reloaded on demand; on macOS the allocator is asked to release pages after sweeps. Fleet idle CPU went from ~180% to under 4%, RSS from multi-GB to a few hundred MB.
  • Re-export resolution can no longer explode. Cyclic wildcard re-exports (barrel files) made missing-symbol resolution explore branching^depth paths — one bad resolution pinned a worker at 100% CPU indefinitely. The resolver now memoizes with depth dominance, making it O(files) while provably preserving every resolution the capped walk finds.
  • Watcher drains are sliced and budgeted; callgraph store writes run on a dedicated worker off the interactive lanes; incremental refresh no longer reloads the module-resolution index per file (156s → 0.3s for a single-file refresh); duplicate lexical ranking in external hybrid-search fallbacks eliminated.

LSP

  • Scoped aft_inspect diagnostics no longer leak: the per-file loop drains server events as it goes, and every document opened for the inspection is closed on exit (RAII), fixing unbounded LSP memory growth on pull-capable servers (#160).
  • Freshly auto-installed language servers are picked up without a restart (#153).
  • Windows: an unversioned System32 onnxruntime.dll is no longer mistaken for a compatible runtime, and doctor --fix downloads the managed one (#157).

Bash

  • bash with wait: true detaches to a background task the moment a new user message arrives, instead of blocking the conversation until the command ends — on both the standalone and daemon paths.
  • Session-owned bash artifacts (stdout/stderr/exit files) are readable under restrict_to_project_root (#159); mutations remain strictly root-bound.
  • Detached background tasks survive daemon module restarts; the module detaches its registry and leaves children running on SIGTERM.
  • Under the daemon, bash children inherit a login-shell-enriched PATH (with fish support and standard tool directories appended), so bun, cargo, and friends resolve without shell profiles.

Tool correctness

  • delete is honest: deleting a symlink to a directory is no longer a silent no-op, and an all-failed batch returns success: false with per-path reasons.
  • apply_patch failures render the nearest-miss candidate (the actual file lines at the best-scoring location), turning a fail-read-retry loop into a single-shot fix.
  • Batch edits[] items support replaceAll; out-of-range occurrence errors teach the 0-indexing at the point of failure.
  • Relative aft_safety checkpoint paths resolve against the project root, not the daemon's working directory.
  • Pi tool schemas advertise bounded integers as integers (spec-compliant, better tool-call generation on strict providers).
  • Image reads no longer time out from queueing behind other image work under load.

Storage

Callgraph and inspect caches collapse from harness-partitioned to root-keyed storage: one writer per artifact (fenced file leases with heartbeats), any number of read-only sessions (marker files with generation GC), and a copy-based migration that preserves the existing store — a 19 GB callgraph universe migrates without a rebuild, with a legacy read fallback if migration cannot complete. Plugin-less invocations (daemon module, aft warmup, bare CLI) now default to the shared CortexKit data root, ending split-brain caches. Borrowed semantic artifacts from a different embedding backend are rejected rather than served wrong.

Languages

Groovy, Gradle build scripts, and Jenkinsfiles get outline-tier support: aft_outline, aft_zoom, semantic indexing, and AST search (27 supported languages).

Observability

Durable logs land under <storage_root>/logs/ with 20 MB × 5 rotation and dead-process cleanup after 7 days. Health probes answer from a dedicated control lane with try-lock-only reads, so a busy module is never misreported as dead. Status reports carry per-root memory snapshots including what an idle eviction actually released.

Verification

Beyond the standard gates (full serial Rust suite, release-profile storm tests, TS suites across both plugins, Windows/macOS/Linux CI), this release's lifecycle and publication-safety work went through seven adversarial review rounds and a multi-model panel audit, each round closing verified counterexamples down to zero open findings — plus a full day of fleet dogfooding on the release candidates, which is where the warm-restart and CPU-spin fixes were validated live.

v0.46.0

Choose a tag to compare

@github-actions github-actions released this 08 Jul 11:50

AFT v0.46.0

96 commits since v0.45.1. This release hardens the daemon (subc) execution path end to end, fixes a broken TUI sidebar on current OpenCode versions, and unifies storage resolution across all entry points.

OpenCode TUI sidebar fix

OpenCode 1.17.x stopped applying its Solid compile-time transform to plugins loaded from the package cache, which broke the AFT sidebar in two ways: installs with a stale plugin cache failed at import, and fresh installs rendered once and froze (values never updated). The TUI is now precompiled at build time against the host's runtime modules, with a loader that falls back to raw TSX for older hosts and development checkouts. The release pipeline gained a packed-install smoke test that verifies the compiled path is reactive, so this class of breakage cannot ship silently again.

Daemon path hardening

The subc transport arc that started in v0.45 is now production-shaped:

  • Typed elicitation capability: bash permission prompts over MCP hosts (e.g. Claude Code) activate from the consumer_capabilities field introduced in subc-protocol 0.8. Hosts that do not declare the capability fail closed.
  • Bind responsiveness under load: artifact-owner heartbeats moved off the per-root mutating lane to a dedicated timer thread; maintenance drains are decomposed into bounded jobs with batch caps; a pending bind now defers maintenance for its root instead of queueing behind it; and overdue binds receive an explicit deadline error instead of hanging.
  • A production-shaped rebind storm test rig (multiple roots and sessions rebinding over a slow embedding backend) now gates releases, asserting bind acknowledgment, tool, health, and completion push latencies.
  • Killed daemons on Windows surface as connection resets rather than clean EOF; the module now treats both as a clean close, matching Unix behavior.

One storage universe

Plugin-less invocations (the daemon-supervised module, aft warmup, bare CLI) resolved storage to the legacy ~/.cache/aft fallback instead of the CortexKit data root the plugins use. Each front paid its own cold indexes, and artifact-owner leases could not see each other across the split. All fallback sites now resolve to the same CortexKit data root, locked by a regression test.

Search and inspect

  • Cross-root search (multi-project sessions) now serves borrowed indexes with drift warnings instead of failing when checkouts differ in untracked ignore files; nonexistent paths return path_not_found.
  • Duplicate detection has a 10-line minimum span, ending flags on 3-4 line idiomatic blocks. The dead inspect.duplicates config knobs were removed.
  • Cheaper directory outlines (top-level symbols only) and callgraph summaries (top-15 with a summary line past 20 entries), with tests hidden by default across both.

Fixes

  • Search index changes now persist on clean shutdown. The in-memory trigram delta is flushed to the shared cache when a bridge or daemon module exits cleanly, so restarts and cross-worktree searches see current results instead of the state at the last background compaction.
  • TUI sidebar now works when the TUI is attached to a serve/Desktop host from a different directory. The sidebar's status lookup and push subscription follow the session's verified project directory to the process that actually hosts the bridge, instead of staying stuck on the lazy-spawn placeholder.
  • aft_delete accepts stringified booleans for recursive (and all boolean tool parameters coerce consistently).
  • Batch edits[] support on Pi's edit tool, matching OpenCode.
  • aft_outline accepts array-coerced target parameters.
  • Bash pipelines route to raw generic output instead of risking failure-hiding head-token compression; empty command output is never fabricated into non-empty summaries.
  • Grep-nudge no longer fires on suppressed-output commands.
  • The /aft-status dialog no longer resets the session's selected model on OpenCode Desktop.
  • Windows: fixed three CI-only test failures (verbatim path as git clone source, a process-global env-var race between parallel tests, and a resulting poison cascade).

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).