# evan/pi-lsp LSP extension for pi coding agent. Provides LSP tools that the LLM can use to query language servers, plus automatic diagnostics after edit/write operations. ## Features ### LSP Tools (callable by LLM) - `lsp_hover` - Get hover documentation for a symbol - `lsp_definition` - Find the definition of a symbol - `lsp_references` - Find all references to a symbol - `lsp_completion` - Get completion suggestions - `lsp_documentSymbol` - Get the symbol outline of a file - `lsp_diagnostics` - Get lint/type-check diagnostics ### Auto-Check Automatically runs LSP diagnostics after `edit` or `write` tool calls. If issues are found, sends a message with the diagnostics to the LLM. **Enable/disable:** ```bash pi --lsp-auto-check=false # Disable auto-check pi --lsp-auto-check=true # Enable (default) ``` ### Manual Check Command Run diagnostics manually on specific files: ```bash /lsp-check main.go utils.go ``` ### Server Control Commands Disable a server so this pi instance won't use it (the shared daemon and other instances are unaffected). When all servers are disabled, LSP tools are removed from the active tool set. | Command | Args | Behavior | |---------|------|----------| | `/lsp-servers` | none | List running servers and disabled state | | `/lsp-disable` | `[]` | Disable all (no arg) or specific server. Bare command disables all. | | `/lsp-enable` | `[]` | Enable all (no arg) or specific server. Restores tools when any is enabled. | | `/lsp-destroy` | `[]` | Kill running daemon entries for all (no arg) or specific server. Explicitly destructive. | ```bash /lsp-disable gopls # Disable just gopls; other LSP tools still work /lsp-disable # Disable all — removes LSP tools from active set /lsp-enable gopls # Re-enable gopls; restores tools /lsp-enable # Re-enable all /lsp-destroy gopls # Kill running gopls process(es) in the daemon /lsp-destroy # Kill all running server processes ``` ## Install ```bash cd ~/.pi/extensions/lsp npm install ``` ## CLI Usage (for development/testing) ``` tsx ./cli.ts [--no-daemon] tsx ./cli.ts daemon ``` Requests use a long-lived background daemon by default. The daemon is autospawned on first use, keeps one language server alive per `(server.id, project root)`, and evicts idle servers after `ServerConfig.idleTtlMs` (default: 5 minutes). Pass `--no-daemon` to use the legacy one-shot path for debugging. `req_data_json` is the raw LSP params for the command, minus `textDocument.uri` (we inject that from ``). ### Commands - `hover` - `definition` - `references` - `completion` - `documentSymbol` - `diagnostics` (waits briefly for the first `publishDiagnostics`) ### Examples ```bash # Hover at line 224, col 23 (LSP is 0-indexed, so subtract 1) npm run lsp -- backend/api/server.go hover \ '{"position":{"line":223,"character":22}}' # Go to definition npm run lsp -- backend/api/server.go definition \ '{"position":{"line":223,"character":22}}' # Document symbols (no params needed) npm run lsp -- backend/api/server.go documentSymbol '{}' # Diagnostics npm run lsp -- backend/api/server.go diagnostics '{}' # Inspect/stop the background daemon npm run lsp -- daemon status npm run lsp -- daemon stop ``` Set `LSP_DEBUG=1` to forward server stderr to the daemon log. The daemon socket is `$XDG_RUNTIME_DIR/pi-lsp-$UID.sock` (tmpdir fallback); logs are in `/tmp/pi-lsp-daemon.log`. ## Adding A Server Edit `server.ts`: ```ts { id: "rust-analyzer", match: ["rs"], command: "rust-analyzer", args: [], rootMarkers: ["Cargo.toml"], languageId: "rust", } ``` ## Adding A Command 1. Add to the `LspCommand` union in `src/types.ts`. 2. Add a handler in `src/commands.ts`. ## Future - **Daemon hardening** - persistent metrics, health checks, and richer status output. - **Build output** - ship compiled JS entrypoints instead of relying on tsx for development.