AFT v0.28.2 is a community contribution release: four PRs from [Chris Olszewski](https://github.com/chrisolszewski) ship together — oxfmt formatter, tsgo type checker, high-dimensional embeddings (closes #49), and dotted custom LSP root markers — plus a fix for repeated LSP servers from `lsp.servers` overrides (closes #56), a Windows LSP auto-install fix, and a new `bun test` output compressor.

## New formatter: oxfmt

A new `formatter: "oxfmt"` option enables [oxfmt](https://www.npmjs.com/package/oxfmt) (by the [oxc-project](https://github.com/oxc-project) — same family as oxlint AFT already supports). AFT auto-detects oxfmt projects via `.oxfmtrc.json` and routes JavaScript / TypeScript edits through it.

Priority order when multiple formatters are detected:

1. **biome** (when `biome.json` exists)
2. **oxfmt** (when `.oxfmtrc.json` exists and no biome config)
3. **prettier** (when `.prettierrc*` exists)
4. **deno fmt** (Deno projects)

Set `format.formatter: "oxfmt"` explicitly to force it.

Contributed in [PR #52](https://github.com/cortexkit/aft/pull/52).

## New type checker: tsgo

A new `checker: "tsgo"` option enables [tsgo](https://www.npmjs.com/package/@typescript/native-preview) — the experimental Go-based TypeScript compiler — as an opt-in replacement for `tsc`. Output is tsc-compatible, so AFT's existing diagnostic parser works unchanged.

To use it, install `@typescript/native-preview` in your project and set `format.checker: "tsgo"`. AFT will run `tsgo --noEmit --pretty false` instead of `tsc`. The default remains `tsc`.

Contributed in [PR #53](https://github.com/cortexkit/aft/pull/53).

## High-dimensional semantic embeddings (closes #49)

The semantic search embedding-dimension cap is raised from **1024 → 4096**, with comprehensive validation. This unblocks:

- OpenAI `text-embedding-3-large` at the maximum 3072 dimensions
- Common open-weight 4096-dimensional embedding models for local hosting

Dimension validation is now centralized in `validate_embedding_dimension()`, with consistent rejection of zero / over-cap values at both index build and disk-cache deserialization. Reported and fixed by the same contributor in [#49](https://github.com/cortexkit/aft/issues/49) and [PR #55](https://github.com/cortexkit/aft/pull/55).

## Dotted custom LSP root markers (PR #51)

`lsp.servers` configuration entries with dotted root markers like `".clangd"`, `".oxlintrc.json"`, or `".env"` were having the leading dot silently stripped by config parsing — so root-marker matching failed to find dot-prefixed configs and any custom server with a `.config`-style file was effectively unconfigurable.

The fix scopes the leading-dot strip to `extensions` only (where it's correct, because `Path::extension()` returns extensions without the dot). `args` and `root_markers` are now passed through verbatim. Contributed in [PR #51](https://github.com/cortexkit/aft/pull/51).

## `lsp.servers` overrides now merge with built-ins (closes #56)

Configuring a built-in server through `lsp.servers` — for example, `lsp.servers.clangd: { args: ["--query-driver=...", ...] }` — would previously spawn **two** clangd servers per `.cpp` file: the original built-in and a parallel `Custom("clangd")` instance from the user config. Each emitted diagnostics independently and competed for the same project.

v0.28.2 detects when a user's `lsp.servers` entry has an `id` matching a built-in (`clangd`, `pyright`, `rust_analyzer`, etc.) and merges them: the user's fields take precedence, and any field the user left empty inherits from the built-in. The result is exactly one spawned server per file with the configuration the user asked for.

This means common workflows like "I want clangd but with extra args" or "I want pyright but with my own initialization options" now work the way they read. Reported in [#56](https://github.com/cortexkit/aft/issues/56).

## Windows: LSP auto-install no longer fails with ENOENT

LSP auto-install on Windows was failing with:

```
ENOENT: no such file or directory, uv_spawn 'npm'
```

The cause: Node's `child_process.spawn` on Windows does not auto-resolve `.cmd` shim extensions, and npm is shipped as `npm.cmd` on Windows GitHub runners and most user machines. AFT was passing the bare `"npm"` string to spawn, which only resolves a binary literally named `npm` (no extension).

v0.28.2 selects `npm.cmd` on `win32` and `npm` everywhere else. LSP auto-install on Windows now works.

## `bun test` output now keeps failure details

Until v0.28.2, `bun test` output fell through to the generic output compressor, which middle-truncates large captures. Bun writes the failure block — error message, source pointer, expected/received diff, stack trace, and the explicit `(fail) ...` marker — **between** the header and the final summary, so a real test run could routinely lose the only thing the agent needs to debug.

A new compressor preserves failure blocks verbatim while dropping per-file pass-only section headers, regardless of how big the run is. Catastrophic failure counts (1000+ failed tests) are capped at 25 preserved blocks with a `+N more failures` trailer so the inline output cap stays honest.
