magi-code 0.63.4

Repository-aware CLI coding agent for terminal work
Documentation
# LSP diagnostics

[Feature docs index](README.md) · [Repository README](../../README.md)

## Overview

LSP diagnostics are an opt-in live feedback loop for coding sessions. After successful `write` or `hash_edit`, magi-code can sync the changed file to a language server, wait briefly for fresh diagnostics, and append a capped `DIAGNOSTICS` block to the same tool result.

This creates a faster edit → instant truth loop for syntax, type, and compiler-style feedback. It complements normal project checks; it does not replace `cargo check`, tests, linters, or repository-specific verification.

## Enablement and settings

LSP is disabled by default. Enable it in non-secret `settings.json` only when the needed language servers are installed on `PATH`.

```json
{
  "lsp": {
    "enabled": true,
    "inject_diagnostics_on_edit": true,
    "diagnostics_wait_ms": 2000,
    "idle_shutdown_minutes": 10,
    "servers": {
      "rust-analyzer": { "command": "rust-analyzer", "args": [] },
      "typescript-language-server": { "command": "typescript-language-server", "args": ["--stdio"] }
    }
  }
}
```

Fields:

| Field | Behavior |
| --- | --- |
| `enabled` | Starts LSP support for the session. Default `false`; disabled mode spawns no language servers and injects no diagnostics. |
| `inject_diagnostics_on_edit` | When `true`, successful `write` and `hash_edit` calls can append fresh diagnostics to tool output. Default `true` when LSP is enabled. |
| `diagnostics_wait_ms` | Total edit-injection LSP budget, including cold spawn, initialize, sync, and diagnostic wait. Default `2000`; max `30000`. |
| `idle_shutdown_minutes` | Idle server shutdown window. Default `10`; max `240`. |
| `servers` | Optional command/args overrides for built-in server route keys. Values are direct process args, not shell-expanded. |

## Supported language defaults

Built-in routes:

| Files | Language id | Server key | Default command |
| --- | --- | --- | --- |
| `.rs` | `rust` | `rust-analyzer` | `rust-analyzer` |
| `.ts`, `.tsx` | `typescript` | `typescript-language-server` | `typescript-language-server --stdio` |
| `.js`, `.jsx` | `javascript` | `typescript-language-server` | `typescript-language-server --stdio` |
| `.py` | `python` | `pyright-langserver` | `pyright-langserver --stdio` |
| `.go` | `go` | `gopls` | `gopls` |

First release supports command/args overrides for these route keys. It does not add code actions, rename, formatting, hover, completion, semantic tokens, custom extension routing, or multi-root workspaces.

## Edit injection behavior

Injection runs only after successful `write` or `hash_edit` file mutations. The changed file must be a UTF-8 text file inside the workspace root and match a supported route. Shell/external-editor changes are not watched.

When fresh diagnostics arrive before `diagnostics_wait_ms`, magi-code appends a redacted block shaped like:

```text
DIAGNOSTICS (rust-analyzer, src/example.rs):
  ERROR [E0382] line 42: borrow of moved value: `value`
  WARNING [unused_imports] line 3: unused import: `std::fmt`
  (2 shown, 0 truncated)
```

Injected blocks show errors and warnings, omit information/hints, cap at 20 diagnostics, and cap appended text at about 2 KiB. No fresh diagnostics before the deadline means no block is appended; magi-code does not emit empty placeholders.

## `diagnostics` tool

The `diagnostics` tool reads the latest cached LSP diagnostics.

Modes:

- Path scoped: pass `path` to return latest diagnostics for one safe existing file.
- Workspace summary: omit `path` to return a bounded summary across cached files.

If no cached diagnostics exist for a path, output says the cache is empty and suggests running after an edit or using project checks. It must not be read as proof that the file has no errors.

The tool output is bounded, redacted, and capped at 200 diagnostics.

## `references` tool

The `references` tool queries LSP `textDocument/references` for one safe file position. Inputs use 1-based `line` and `column` values. Output returns bounded locations as `path:line:column`.

Default behavior excludes the declaration site:

```json
{ "path": "src/example.rs", "line": 42, "column": 13 }
```

Set `include_declaration` when declaration locations should be included:

```json
{ "path": "src/example.rs", "line": 42, "column": 13, "include_declaration": true }
```

Use this for dead-code verification when grep-like text search may match comments or strings. Treat results as language-server data, not as a substitute for build/test confirmation before deleting important code.

## Limits and caps

| Surface | Cap |
| --- | --- |
| Edit injection | 20 diagnostics and about 2 KiB appended text. |
| `diagnostics` tool | Up to 200 diagnostics. |
| `references` tool | Bounded reference list with truncation count when capped. |
| Edit wait | `diagnostics_wait_ms`, including spawn/init/sync/wait. |
| Server lifetime | Idle shutdown after `idle_shutdown_minutes`. |

Caps keep provider-visible tool output small and deterministic.

## Degradation/failure behavior

LSP never changes edit success. Missing binaries, spawn failures, crashes, stale diagnostics, indexing delays, timeouts, unsupported file types, and disabled settings degrade by skipping injection.

Server lifecycle behavior:

- Servers spawn lazily on first matching edit or query.
- Warm servers are reused within the session.
- Idle servers shut down after the configured idle window.
- A server respawns at most two times per session, then disables for that session.
- Query tools return concise disabled/no-cache/failure guidance when useful; edit injection stays quiet unless fresh diagnostics are available.

## Privacy/redaction boundaries

LSP output follows normal tool-output privacy rules:

- Diagnostics and references are redacted before provider-visible output or session persistence.
- Raw language-server stdout/stderr is never exposed to provider context, sessions, hooks, or docs.
- Server child processes run with filtered environment handling; provider API keys and token-shaped environment variables are not forwarded.
- Paths pass existing cwd/root validation before query tools run.
- `settings.json` is non-secret; do not place credentials in LSP server command args.

## Troubleshooting

Missing server:

- Confirm `lsp.enabled=true`.
- Install the needed language server and confirm its command is on `PATH`.
- Override `lsp.servers.<server>.command` and `args` only when the default command differs.

Indexing delay:

- Some servers need time to index project metadata before returning useful diagnostics.
- Increase `diagnostics_wait_ms` only if slower edit results are acceptable.
- Run normal project checks when LSP output is incomplete or stale.

Stale diagnostics:

- Edit injection requires fresh diagnostics for the synced file version when the server supplies versions.
- If no block appears, run another edit or use the `diagnostics` tool after the server catches up.
- Cached diagnostics are latest-known language-server state, not final build truth.

## Related docs

- [PRD-0071: LSP-Backed Live Diagnostics]../prd/0071-lsp-backed-live-diagnostics.md
- [ADR-0047: Embedded Blocking LSP Client Runtime]../adr/0047-embedded-blocking-lsp-client-runtime.md
- [Tools and safety model]tools-and-safety.md
- [Configuration]configuration.md

---

[Back to feature docs](README.md) · [Back to repository README](../../README.md)