# @gitawego/pi-lsp

Config-driven [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) integration for the [pi coding agent](https://pi.dev). Official LSP servers by default, persistent per-project sessions, progressive diagnostics after edits, and rich query tools.

**Cross-platform (64-bit only):** Linux, macOS, Windows, and **android-arm64 (Termux)**.

## Install

```bash
pi install git:github.com/gitawego/pi-lsp
```

Remove the older `@narumitw/pi-lsp` if installed:

```bash
pi remove npm:@narumitw/pi-lsp
```

Reload with `/reload` (or restart pi).

## Tools

| Tool | Purpose |
| --- | --- |
| `lsp_diagnostics` | Diagnostics for files/directories via the file's LSP server |
| `lsp_status` | Live LSP server sessions (id, root, status) |
| `lsp_fix` | Apply a source code action (default `source.fixAll`; preview unless `write: true`) |
| `lsp_hover` | Hover documentation at a position |
| `lsp_definition` | Definition locations for the symbol at a position |
| `lsp_references` | All references (including the declaration) |
| `lsp_implementation` | Implementations of the symbol at a position |
| `lsp_symbols` | Symbols declared in a file |
| `lsp_workspace_symbol` | Workspace-wide symbol search by query (up to 10 results) |
| `lsp_call_hierarchy` | Call hierarchy: prepare / incoming / outgoing |
| `lsp_rename` | Workspace rename edits (preview only — never writes) |

Also: `/lsp` command (session status) and a `lsp` statusline entry while servers start.

## Progressive diagnostics

After each agent turn, files the agent edited (`edit`, `write`, `lsp_fix`, bash redirects) are re-synced with the live LSP sessions and a throttled, compact diagnostics summary is surfaced. The surface is config-driven (`progressive.inject`):

- `status` (default) — statusline summary
- `widget` — reserved; TUI widget
- `conversation` — injected into the conversation via a custom message
- `none` — disabled

## Default catalog (official servers)

| Server | Languages | Install strategy |
| --- | --- | --- |
| `typescript` | `.ts .tsx .js .jsx .mjs .cjs .mts .cts` | `typescript-language-server` (npm, auto) |
| `kotlin` | `.kt .kts` | `kotlin-lsp` (GitHub release; needs `java`) |
| `gopls` | `.go` | `gopls` (go-install; needs `go`) |
| `rust-analyzer` | `.rs` | `rust-analyzer` (PATH only) |
| `clangd` | `.c .h .cpp .cc .cxx .hpp .hh .hxx` | `clangd` (PATH; ships with Termux) |
| `pyright` | `.py` | `pyright-langserver` (npm) |
| `ruby-lsp` | `.rb .rake .gemspec .ru` | `ruby-lsp` (PATH) |
| `elixir-ls` | `.ex .exs` | `elixir-ls` (PATH) |
| `zls` | `.zig .zon` | `zls` (PATH) |

The TypeScript server is the official `typescript-language-server` — never a linter standing in for a type-checker.

### Platform matrix (64-bit only: `arm64` / `x64`)

| Strategy | Works on |
| --- | --- |
| npm (typescript, pyright) | everywhere, including android-arm64 (pure JS) |
| github-release (kotlin-lsp) | Linux/macOS/Windows with `java`; refused on android (bionic) |
| go-install (gopls) | anywhere with `go` on PATH |
| PATH-only (rust-analyzer, clangd, ruby-lsp, elixir-ls, zls) | wherever the binary is installed; clangd ships with Termux |

> **Kotlin on android-arm64 (verified):** the official JetBrains `kotlin-server` *does* boot on Termux via `glibc-runner -n <bin> --stdio` (ELF patched to the `$PREFIX/glibc` loader), and the plugin can drive it via a config override. However, its IntelliJ-based analysis did not produce diagnostics within 3–10 minutes on this hardware, so kotlin defaults to PATH-only on android. If you have a JVM-based Kotlin LSP on PATH, it works via config: `"kotlin": { "command": ["java", "-jar", "/path/to/server.jar"] }`.

32-bit architectures (`ia32`, `arm`, …) are unsupported: managed installs are refused and the platform is reported as unsupported — never a crash.

## Configuration (config-driven)

Configuration is resolved from `pi-lsp.json` in this order (each may override the previous):

1. Default official-server catalog
2. User: `~/.pi/agent/pi-lsp.json`
3. Project: `<workspace>/.pi/pi-lsp.json` (only when pi trusts the project)

```jsonc
{
  "timeout": 30000,
  "binDir": "~/.cache/pi-lsp/bin",   // managed installs
  "progressive": {
    "enabled": true,
    "inject": "status",              // status | widget | conversation | none
    "maxDiagnostics": 20,
    "quietMs": 2000
  },
  "servers": {
    "typescript": {
      "command": ["typescript-language-server", "--stdio"],
      "extensions": [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"]
    },
    "kotlin": { "disabled": true },  // drop a default server
    "my-lang": {                     // add a custom server
      "command": ["my-lang-lsp", "--stdio"],
      "extensions": [".mylang"]
    }
  }
}
```

Per-server options: `command`, `extensions`, `languageId`, `rootMarkers`, `env`, `initialization`, `autoDownload`, `disabled`. Invalid or unreadable files fall back to defaults for that source.

## How it works

OpenCode-inspired architecture: one persistent LSP client per (project root, server), discovered lazily with marker-file root detection (`package-lock.json`, `go.mod`, `settings.gradle.kts`, …), kept alive for the session, and shut down at `session_shutdown`. Documents are tracked with versions; `touchFile` sends `didChangeWatchedFiles` + `didOpen`/`didChange`; diagnostics merge push (`publishDiagnostics`) and pull (`textDocument/diagnostic`) results with dedupe. Servers that fail to start are marked broken (no retry storms).

The manager API mirrors OpenCode's LSP service interface (`packages/opencode/src/lsp`): `init`, `status`, `hasClients`, `touchFile`, `diagnostics` (Record), `hover`, `definition`, `references`, `implementation`, `documentSymbol`, `workspaceSymbol` (kind-filtered, 10 max), `prepareCallHierarchy`, `incomingCalls`, `outgoingCalls`.

## Differences from `@narumitw/pi-lsp`

- Persistent sessions instead of spawn-per-call (no re-initialization cost per call)
- Official servers by default (biome was the TS default there; it is not here)
- Multi-language first-class (Kotlin and others in the catalog, not just TS routes)
- Rich query tools (hover/definition/references/symbols/rename)
- Progressive diagnostics after agent edits
- Cross-platform + managed installs for missing official servers

## Development

```bash
npm install
npm test            # vitest, single-process (Termux-safe)
npm run typecheck   # tsc --noEmit
```

Design and test-first process: see `docs/superpowers/plans/2026-08-09-pi-lsp-plugin.md`.

## Credits

Architecture inspired by [OpenCode's LSP implementation](https://github.com/anomalyco/opencode/tree/dev/packages/opencode/src/lsp) (persistent clients, root detection, push+pull diagnostic merging). JSON-RPC framing patterns from `@narumitw/pi-lsp`.

## License

MIT — see [LICENSE](./LICENSE).
