## Highlights

### Tiered bash output compression with TOML filters + new Rust modules

v0.21 ships the long-pending compression mechanism. Hoisted bash output now flows through a three-tier dispatch (with `experimental.bash.compress=true`):

1. **Rust modules** — hand-written parsers for high-traffic tools. v0.21 adds three new ones (`eslint`, `vitest`/`jest` sharing a parser, `biome`) plus six new `git` subcommand compressors (`add`, `commit`, `push`, `pull`, `fetch`, `stash`) on top of the existing `git status`/`log`/`diff`/`blame`. JSON output is parsed where the tool offers it.
2. **TOML filters** — declarative `strip` + `truncate` + `cap` + `shortcircuit` rules. v0.21 ships **15 builtin filters**: `make`, `ls`, `tree`, `df`, `du`, `find`, `wc`, `gradle`, `xcodebuild`, `terraform`, `helm`, `docker`, `kubectl`, `gh`, `ansible-playbook`. Filters can also be added by users under `<storage_dir>/filters/*.toml` (always loaded) or by projects under `<project>/.aft/filters/*.toml` (trust-gated).
3. **Generic fallback** — ANSI strip + consecutive-line dedup + middle-truncate, always applied when no module or filter matches.

Per-call opt-out via `compressed: false` on the bash tool — preserves raw output for that specific call while keeping the global default on.

**Trust model for project filters.** Project-supplied filters are an attack vector (a malicious repo could ship a `cargo.toml` filter that strips real failures and replaces them with `tests: ok`). They are off by default. Use the new shared CLI to opt in:

```
npx --bun @cortexkit/aft doctor filters         # list builtin + user + project filters
npx --bun @cortexkit/aft doctor filters trust   # interactive trust prompt for current project
npx --bun @cortexkit/aft doctor filters --show <name>
```

Trust state lives in `<storage_dir>/trusted-filter-projects.json` keyed by canonicalized project root.

### Issue #33 — TUI plugin loads on OpenCode 1.14.42-43

`api.command.register` was removed in OpenCode 1.14.42 and reinstated as a deprecated shim in 1.14.44+. The TUI plugin was crashing on the 1.14.42-43 range with `api.command is undefined`. Migrated to `api.tools.toolDefinition` + `api.keymap.registerLayer`, with a backward-compat fallback when those aren't present. `/aft-status` and the `Ctrl+a, ?` keybind now work across <=1.14.41, the broken 1.14.42-43 range, and 1.14.44+.

### Issue #32 — grep brace-glob splitting at the Rust boundary

The plugin layer already brace-aware-splits `**/*.{ts,tsx},**/*.{js,jsx}` correctly, but direct binary callers (bash rewrite, CLI users) hit `grep: invalid include/exclude glob: unclosed alternate group` because the Rust `string_array_param` only accepted arrays. Now accepts both strings and arrays, and runs every input through a brace-aware splitter that treats `,` as a separator only when `{`/`}` depth is zero. Same robustness across all caller paths.

## Other

- Compression config (`experimental.bash.compress`, project filter trust state, storage dir, project root) now re-picks up on the next `configure` without restart — change a setting and the next bash call honors it.
- Empty-body `[BACKGROUND BASH STILL RUNNING]` reminders are gone. A plugin-side race between the in-turn drain and the wake debouncer could fire a reminder shell with no pending tasks attached; both OpenCode and Pi now cancel the debounce timer when the drain absorbs the pending list, and the timer itself short-circuits if there's nothing to report.
- README compression section was rewritten around the three-tier dispatch with a TOML filter authoring guide. `ARCHITECTURE.md` gained a dedicated "Bash Output Compression" section.

**Full Changelog:** https://github.com/cortexkit/aft/compare/v0.20.1...v0.21.0
