Skip to content

Releases: cortexkit/aft

v0.9.0

05 Apr 17:14

Choose a tag to compare

v0.9.0

🌐 New Language Support

  • C, C++, Zig, C# — full tree-sitter grammar support for outline, zoom, and AST search/replace
  • C++ captures classes, functions, methods, namespaces, and templates
  • Supported extensions: .c, .h, .cc, .cpp, .cxx, .hpp, .hh, .zig, .cs

⚡ Search Performance (2-16x faster)

  • memchr fast path — literal queries skip the regex engine entirely, using SIMD-accelerated byte scanning
  • Parallel file verificationrayon parallelizes candidate file reading when >10 candidates
  • Early termination — stops reading files once max_results is reached
  • Sorted Vec merge — replaces BTreeSet intersection for 2-5x faster candidate narrowing
  • Lazy line_starts — only computes line offsets after first match in a file
  • Shared Arc<PathBuf> — reduces per-match allocation overhead
  • Byte-level file reads — skips binary checks and UTF-8 validation for indexed files
  • Posting list sort optimization — conditional sort only when out-of-order, not after every file

Benchmarks (vs ripgrep): 7-236x faster on indexed projects, with the largest gains on rare queries in large codebases (Chromium/base: 236x for WebContents).

📦 Symbol Table Cache

  • Parsed symbol tables cached by (path, mtime) — repeated outline/zoom calls skip re-parsing
  • Background pre-warm populates the cache during search index build
  • Cache invalidation via mtime checks on watcher events

🔄 Stale Index Recovery

  • On cold start with matching git HEAD, verifies all file mtimes against disk
  • Re-indexes only files that changed (uncommitted edits, stash pop, manual changes while closed)
  • Detects and indexes new files not in the stored cache

🎨 Unified Output Format

  • Removed compress_tool_output config — single optimized format for all users
  • Grep: relative paths, filepath\n65: text, no decorators, 200-char truncation, 5 matches/file cap with overflow
  • Glob: relative paths, directory grouping for >20 files, header with match count

📊 Stats

  • 835 tests (649 Rust + 186 TypeScript)
  • Supported languages: TypeScript, JavaScript, TSX, JSX, Python, Rust, Go, Markdown, C, C++, Zig, C# (12 total)

v0.8.2

05 Apr 12:42

Choose a tag to compare

v0.8.2

Search Index Fixes

  • Fixed trigram false negatives — indexed grep now correctly finds all matches (was silently dropping results due to bloom filter check in postings_for_trigram)
  • Added search_ms — self-reported search engine timing in grep response for benchmarking
  • Absolute paths in grep results — matches OpenCode's output format
  • "Found N matches" header — raw grep output now matches OpenCode's exact format
  • mtime-descending sort — grep and glob results sorted by modification time like OpenCode

Binary Safety

  • Versioned binary cache — never runs from node_modules (prevents corruption when npm updates while running)
  • replaceBinary no longer kills running bridges — just updates path for new sessions
  • Legacy flat cache removed — only versioned paths used

Other

  • Version logged after bridge configure
  • Bash grep/rg hint when agents use shell for search
  • Compressed output default changed to true
  • Search benchmark harness (benchmarks/search-bench-v2.py)

v0.8.1

05 Apr 09:06

Choose a tag to compare

v0.8.1

Bug Fixes

  • Stable search index cache key — Git repos now use sha256(root_commit_hash) as the cache key instead of sha256(directory_path + root_commit). This means /repo, /repo/src, and /repo/crates/aft all share the same index cache entry. Previously, each subdirectory session created a separate cache, causing hundreds of stale entries.
  • Non-git projects use sha256(canonical_path) as before.

v0.8.0

04 Apr 23:42

Choose a tag to compare

v0.8.0

🔍 Indexed Search (Experimental)

  • Trigram-indexed grep and glob — hoists opencode's built-in tools when experimental_search_index: true
  • Background thread builds the index at session start, persists to disk with git HEAD tracking for fast cold starts
  • File watcher keeps the index fresh incrementally
  • Falls back to direct scanning when the index isn't ready
  • Out-of-project paths shell out to ripgrep matching opencode's exact flags
  • Compressed output mode (compress_tool_output: true) — RTK-style grouping, line truncation, overflow summarization for up to 80% token reduction

🎯 Grep/Glob Parity (12 fixes)

  • Hidden files now included (.env, .eslintrc, etc.)
  • Glob returns all matching files, not just text files
  • One match per line (deduplicated, matching ripgrep)
  • Results sorted by modification time (newest first)
  • Default cap at 100 matches/files (matching opencode)
  • Glob returns absolute paths
  • Raw grep lines truncated at 2000 chars
  • total_matches reports true pre-truncation count
  • path parameter scopes search to subdirectory
  • Reliable ripgrep JSON parsing (no more pipe-in-filename bugs)
  • Cross-platform binary resolution (no Unix which dependency)
  • Atomic disk writes for index cache (temp file + rename)

🔒 Security Hardening (25 audit fixes)

  • validate_path added to ALL commands including reads (outline, zoom, grep, glob, callgraph, LSP)
  • Commands use the validated PathBuf return value instead of discarding it
  • Symlink traversal hardened — walks existing ancestors to resolve symlinks
  • Security regression tests in security_test.rs
  • 50MB file size guard in read command
  • Regex DoS prevention via RegexBuilder::size_limit()
  • Dry-run permission bypass removed — dryRun still requires permission
  • Glob edit_match now runs syntax validation
  • restore_latest() writes before popping — backup survives write failure
  • Bridge restart counter resets after 5 minutes of stable operation
  • JSONC trailing-comma stripper is now string-aware
  • prepare_exported_symbol handles export default, export {, export *
  • Transaction edit_match requires explicit replacement field
  • move_symbol UTF-8 boundary check before slicing
  • Dead code cleanup in rewrite_consumer_imports

🛠 Other Improvements

  • Formatter resolution fix — explicit overrides now resolve through node_modules/.bin
  • Bash grep/rg hint — nudges agents toward the built-in grep tool
  • Bridge stderr formatting cleanup
  • Watcher/index drain order fix — prevents lost updates during background rebuild
  • Cache key includes project_root path alongside root commit hash

📊 Test Coverage

  • 814 tests (628 Rust + 186 TypeScript)
  • New: search parity tests, indexed search tests, security regression tests

v0.7.7

02 Apr 09:46

Choose a tag to compare

Bug Fixes

  • Cleaner log formatting — Rust binary stderr now shows as [aft] message instead of [aft-plugin] stderr: [aft] message
  • README typo — Fixed "plugins""plugin" in installation example
  • JSONC parser removed — Self-contained JSONC stripper replaces jsonc-parser dependency (fixes ESM/CJS crash from v0.7.4)

v0.7.6

01 Apr 10:23

Choose a tag to compare

Critical Bug Fix

  • Replaced jsonc-parser with self-contained JSONC stripperjsonc-parser's UMD bundle breaks under both ESM named imports (Node.js strict ESM in OpenCode's runtime) and createRequire (Bun/OpenCode hybrid). Replaced with a zero-dependency comment stripper + trailing comma removal + JSON.parse. This was the root cause of plugin loading failures since v0.7.2.

Full Changelog: v0.7.5...v0.7.6

v0.7.5

01 Apr 09:35

Choose a tag to compare

Bug Fixes

Version Mismatch Loop Prevention

  • Fixed infinite configure loop — When the binary version is older than the plugin, the version mismatch handler now fires only once per binary version instead of re-triggering on every bridge respawn. Prevents the Failed to configure bridge after 3 attempts error users were hitting.
  • Tracker doesn't reset prematurely — Per Oracle audit, the upgrade tracker stays set after replacement so a still-outdated downloaded binary doesn't re-trigger the loop. Resets naturally on plugin reload.

User Experience

  • Log file path in errorsFailed to configure bridge after 3 attempts and Max restarts reached now include the path to $TMPDIR/aft-plugin.log so users can find diagnostic logs.
  • Empty param handlinglsp_diagnostics treats empty string filePath/directory as absent instead of throwing a false mutual-exclusivity error.

Full Changelog: v0.7.4...v0.7.5

v0.7.3

30 Mar 19:59

Choose a tag to compare

Bug Fixes

  • lsp_diagnostics empty string handling — Empty string filePath or directory params are now treated as absent instead of triggering the mutual-exclusivity error. Fixes 'filePath' and 'directory' are mutually exclusive when agents send filePath: "" with a directory value.

Full Changelog: v0.7.2...v0.7.3

v0.7.2

27 Mar 21:42

Choose a tag to compare

Improvements & Bug Fixes (Council Audit Round 2)

⚡ Performance

  • Event-driven LSP diagnostics — Replaced blocking thread::sleep (1.5-10s) with recv_timeout polling loop. Post-edit diagnostics now return immediately when the language server responds instead of waiting the full timeout (#10)
  • Callgraph memory reduction — Reduced .clone() calls from 80+ to 52 in build_reverse_index by using Arc<PathBuf> / Arc<str> for shared caller data. Hot loop no longer clones entire FileCallData structs (#7)

🐛 Bug Fixes

  • Backup error misidentificationrestore_latest now reports the actual I/O error (permission denied, disk full) instead of incorrectly claiming "file not found" via new IoError variant (#12)
  • Bridge crash orphan processes — Crash recovery timer now checks isAlive() before spawning, preventing duplicate child processes when ensureSpawned() already created one (#14)
  • LSP workspace/configuration dispatch — Server requests are now dispatched by method: workspace/configuration returns a correctly-typed array instead of null, so language servers receive proper config responses (#19)

🔒 Security

  • Mandatory checksum verification — Binary auto-downloader now aborts if checksums.sha256 is unavailable or missing an entry, preventing MITM attacks that block the checksum request (#16)

📝 Documentation

  • unwrap/expect audit note — Documented in lib.rs that remaining .unwrap() calls are in tree-sitter query operations (compile-time grammar constants) and test code, not production error paths (#2)
  • TypeScript as assertion note — Documented in bridge.ts that as casts are guarded by success === false error checks on all 16 tool handlers (#6)

Full Changelog: v0.7.1...v0.7.2

v0.7.1

27 Mar 19:59

Choose a tag to compare

Bug Fixes (Council Audit P0/P1)

🔒 Security

  • Path validation coverage — Added validate_path() to 6 write-capable commands that were missing it: transaction, ast_replace, lsp_rename, glob edit_match, checkpoint, and restore_checkpoint
  • Path traversal hardeningvalidate_path() canonicalization fallback now normalizes .. components for non-existent paths, preventing root/../outside/file bypass

🐛 Correctness

  • CRLF byte offset fixline_col_to_byte now scans raw bytes instead of using .lines(), fixing byte offset drift on Windows/CRLF files from line 2 onward
  • Code deduplication — Consolidated 5 duplicate line_col_to_byte implementations into one shared function
  • Transaction rollback visibility — Failed rollback file paths now appear in the error response under rollback_failures instead of being silently logged at debug level
  • move_file false success — Returns moved: false with a warning when source file deletion fails after copy, instead of claiming success

🔧 Robustness

  • LSP stderr deadlock prevention — Language server stderr pipe switched to Stdio::null() to prevent blocking after ~64KB of server logs
  • Bridge recursion guardsend() now has a 3-attempt depth limit to prevent infinite recursion on repeated version mismatches
  • Bridge configured flag race — Flag is now set after configure succeeds (not before the await), and reset on failure
  • Format logging fix — Successful auto-format now logs at info level instead of error

Full Changelog: v0.7.0...v0.7.1