# LSP Extension Language Server Protocol integration for pi-coding-agent. ## Highlights - **Hook** (`lsp.ts`): Auto-diagnostics (default at agent end; optional per `write`/`edit`) - **Tool** (`lsp-tool.ts`): On-demand LSP queries (definitions, references, hover, symbols, diagnostics, signatures) - Manages one LSP server per project root and reuses them across turns - **Efficient**: Bounded memory usage via LRU cache and idle file cleanup - Supports TypeScript/JavaScript, Vue, Svelte, Dart/Flutter, Python, Go, Kotlin, Swift, and Rust ## Supported Languages | Language | Server | Detection | |----------|--------|-----------| | TypeScript/JavaScript | `typescript-language-server` | `package.json`, `tsconfig.json` | | Vue | `vue-language-server` | `package.json`, `vite.config.ts` | | Svelte | `svelteserver` | `svelte.config.js` | | Dart/Flutter | `dart language-server` | `pubspec.yaml` | | Python | `pyright-langserver` | `pyproject.toml`, `requirements.txt` | | Go | `gopls` | `go.mod` | | Kotlin | `kotlin-ls` | `settings.gradle(.kts)`, `build.gradle(.kts)`, `pom.xml` | | Swift | `sourcekit-lsp` | `Package.swift`, Xcode (`*.xcodeproj` / `*.xcworkspace`) | | Rust | `rust-analyzer` | `Cargo.toml` | ### Known Limitations **rust-analyzer**: Very slow to initialize (30-60+ seconds) because it compiles the entire Rust project before returning diagnostics. This is a known rust-analyzer behavior, not a bug in this extension. For quick feedback, consider using `cargo check` directly. ## Usage ### Installation Install the package and enable extensions: ```bash pi install npm:lsp-pi pi config ``` Dependencies are installed automatically during `pi install`. ### Prerequisites Install the language servers you need: ```bash # TypeScript/JavaScript npm i -g typescript-language-server typescript # Vue npm i -g @vue/language-server # Svelte npm i -g svelte-language-server # Python npm i -g pyright # Go (install gopls via go install) go install golang.org/x/tools/gopls@latest # Kotlin (kotlin-ls) brew install JetBrains/utils/kotlin-lsp # Swift (sourcekit-lsp; macOS) # Usually available via Xcode / Command Line Tools xcrun sourcekit-lsp --help # Rust (install via rustup) rustup component add rust-analyzer ``` The extension spawns binaries from your PATH. ## How It Works ### Hook (auto-diagnostics) 1. On `session_start`, warms up LSP for detected project type 2. Tracks files touched by `write`/`edit` 3. Default (`agent_end`): at agent end, sends touched files to LSP and posts a diagnostics message 4. Optional (`edit_write`): per `write`/`edit`, appends diagnostics to the tool result 5. Shows notification with diagnostic summary 6. **Memory Management**: Keeps up to 30 files open per LSP server (LRU eviction), automatically closes idle files (> 60s), and shuts down all LSP servers after 2 minutes of post-agent inactivity (servers restart lazily when files are read again). 7. **Robustness**: Reuses cached diagnostics if a server doesn't re-publish them for unchanged files, avoiding false timeouts on re-analysis. ### Tool (on-demand queries) The `lsp` tool provides these actions: | Action | Description | Requires | |--------|-------------|----------| | `definition` | Jump to definition | `file` + (`line`/`column` or `query`) | | `references` | Find all references | `file` + (`line`/`column` or `query`) | | `hover` | Get type/docs info | `file` + (`line`/`column` or `query`) | | `symbols` | List symbols in file | `file`, optional `query` filter | | `diagnostics` | Get single file diagnostics | `file`, optional `severity` filter | | `workspace-diagnostics` | Get diagnostics for multiple files | `files` array, optional `severity` filter | | `signature` | Get function signature | `file` + (`line`/`column` or `query`) | | `rename` | Rename symbol across files | `file` + (`line`/`column` or `query`) + `newName` | | `codeAction` | Get available quick fixes/refactors | `file` + `line`/`column`, optional `endLine`/`endColumn` | **Query resolution**: For position-based actions, you can provide a `query` (symbol name) instead of `line`/`column`. The tool will find the symbol in the file and use its position. **Severity filtering**: For `diagnostics` and `workspace-diagnostics` actions, use the `severity` parameter to filter results: - `all` (default): Show all diagnostics - `error`: Only errors - `warning`: Errors and warnings - `info`: Errors, warnings, and info - `hint`: All including hints **Workspace diagnostics**: The `workspace-diagnostics` action analyzes multiple files at once. Pass an array of file paths in the `files` parameter. Each file will be opened, analyzed by the appropriate LSP server, and diagnostics returned. Files are cleaned up after analysis to prevent memory bloat. ```bash # Find all TypeScript files and check for errors find src -name "*.ts" -type f | xargs ... # Example tool call lsp action=workspace-diagnostics files=["src/index.ts", "src/utils.ts"] severity=error ``` Example questions the LLM can answer using this tool: - "Where is `handleSessionStart` defined in `lsp-hook.ts`?" - "Find all references to `getManager`" - "What type does `getDefinition` return?" - "List symbols in `lsp-core.ts`" - "Check all TypeScript files in src/ for errors" - "Get only errors from `index.ts`" - "Rename `oldFunction` to `newFunction`" - "What quick fixes are available at line 10?" ## Settings Use `/lsp` to configure the auto diagnostics hook: - Mode: default at agent end; can run after each edit/write or be disabled - Scope: session-only or global (`~/.pi/agent/settings.json`) To disable auto diagnostics, choose "Disabled" in `/lsp` or set in `~/.pi/agent/settings.json`: ```json { "lsp": { "hookMode": "disabled" } } ``` Other values: `"agent_end"` (default) and `"edit_write"`. Agent-end mode analyzes files touched during the full agent response (after all tool calls complete) and posts a diagnostics message only once. Disabling the hook does not disable the `/lsp` tool. ## File Structure | File | Purpose | |------|---------| | `lsp.ts` | Hook extension (auto-diagnostics; default at agent end) | | `lsp-tool.ts` | Tool extension (on-demand LSP queries) | | `lsp-core.ts` | LSPManager class, server configs, singleton manager | | `package.json` | Declares both extensions via "pi" field | ## Testing ```bash # Unit tests (root detection, configuration) npm test # Tool tests npm run test:tool # Integration tests (spawns real language servers) npm run test:integration # Run rust-analyzer tests (slow, disabled by default) RUST_LSP_TEST=1 npm run test:integration ``` ## License MIT