AFT v0.28.1 fixes a CLI install break, a TUI plugin-load crash, and the duplicate background-bash completion-message bug, plus several quality-of-life improvements for the new TUI sidebar and LSP notifications.

## `npx @cortexkit/aft` fixed

The v0.28.0 release of `@cortexkit/aft` (the unified CLI behind `aft setup` and `aft doctor`) shipped with a literal `"workspace:*"` reference to `@cortexkit/aft-bridge` in its `package.json` dependencies. npm rejects that protocol on install with:

```
npm error code EUNSUPPORTEDPROTOCOL
npm error Unsupported URL Type "workspace:": workspace:*
```

The leak only affected `@cortexkit/aft@0.28.0`; the other npm packages were unaffected. Running `npx @cortexkit/aft@latest setup` or `aft doctor` from a clean machine was broken throughout v0.28.0.

v0.28.1 fixes `scripts/version-sync.mjs` so the CLI's `aft-bridge` dependency is rewritten to a real semver at publish time, matching how the plugin packages already work. `@cortexkit/aft@0.28.1` and onward install cleanly under both npm and Bun.

If you saw `EUNSUPPORTEDPROTOCOL` after running setup or doctor with v0.28.0, just run `npx @cortexkit/aft@latest doctor` again — it will pull v0.28.1 and work normally.

## OpenCode TUI plugin no longer crashes on load

OpenCode TUI runs the plugin under Bun. v0.28.0 transitively pulled in `undici@8`, which calls `webidl.util.markAsUncloneable` at module-load time — a Web IDL helper Bun does not currently polyfill. The result was an immediate plugin-load crash:

```
error=__require is not a function failed to load plugin
```

Users on OpenCode Desktop (Node runtime) were unaffected; users on the TUI silently lost all AFT tools.

v0.28.1 pins `undici@7`, which calls the missing helper lazily and never triggers the crash. AFT loads correctly under Bun TUI again.

## Background bash completion duplicate-message bug worked around

The most disruptive bug since v0.27: every background-bash completion reminder could spawn multiple final assistant `stop` messages in OpenCode, wasting tokens and confusing the conversation. Traced to [anomalyco/opencode#28202](https://github.com/anomalyco/opencode/issues/28202) — OpenCode's in-process plugin client uses a separate Effect memoMap from the live request listener, so `SessionRunState.ensureRunning` cannot coalesce AFT-initiated wake requests against the live session.

v0.28.1 routes background-bash wake reminders through a separate `createOpencodeClient` pointed at the live HTTP server URL. The wake hits the same Effect runtime as user-driven turns, so OpenCode's coalescing fires correctly and exactly one assistant child responds.

The workaround works in both runtimes:

- **OpenCode Desktop**: works automatically with no user action.
- **OpenCode TUI**: requires launching with `opencode --port 0` so the in-process server is reachable over loopback. If you start TUI without `--port 0`, AFT shows a one-time ignored message explaining the situation and linking to the upstream issue.

Wake reminders still preserve the assistant's model and variant on synthetic prompts, so provider prefix caches stay warm.

## TUI sidebar populates correctly with `--port 0`

A side effect of OpenCode TUI under `--port 0` is that it loads the AFT plugin twice in the same process (separate ESM module graphs, separate `BridgePool`, two RPC servers). Pre-v0.28.1 the two plugin instances overwrote each other's RPC port file last-write-wins, so the `/aft-status` sidebar could end up polling the plugin instance whose bridge was cold and just kept showing the lazy-spawn placeholder.

v0.28.1 changes the RPC port layout from a single `port` file to a `ports/<instance-id>.json` directory, with one file per plugin instance. The TUI sidebar reads every entry, probes each port, and prefers whichever instance's bridge has warmed up. The old `port` file is still read as a fallback for older plugin versions in mixed deployments.

After this fix, the sidebar populates within ~1.5s of the first AFT tool call, regardless of which plugin instance the tool was routed through.

## `lsp.auto_install: false` actually silences missing-binary warnings

Users with `lsp.auto_install: false` in their `aft.jsonc` were still receiving repeated `lsp_binary_missing` warnings on every configure — both as TUI toasts and Desktop ignored-message notifications — for built-in LSP servers AFT would have installed if the flag had been left on.

The root cause was in the plugin layer: both OpenCode and Pi sent Rust the full list of auto-installable binaries via `lsp_auto_install_binaries` regardless of the user's preference. The Rust-side `detect_missing_lsp_binaries` walk then warned for any of those that weren't on disk.

v0.28.1 makes both plugins send an empty `lsp_auto_install_binaries` list when `lsp.auto_install: false`. The Rust walk short-circuits and emits no built-in warnings. Explicit `lsp.servers` entries you've configured yourself are unaffected and continue to warn when their binary is missing — that's a user-managed surface, not auto.
