193 lines
8.1 KiB
Markdown
193 lines
8.1 KiB
Markdown
# pi-lsp-extension
|
|
|
|
A [pi](https://github.com/mariozechner/pi-mono) coding agent extension that integrates Language Server Protocol (LSP) servers, giving the LLM access to the same language intelligence that powers your IDE.
|
|
|
|
## Tools
|
|
|
|
| Tool | Description |
|
|
|------|-------------|
|
|
| `lsp_diagnostics` | Compilation errors and warnings for a file |
|
|
| `lsp_hover` | Type information and documentation at a position |
|
|
| `lsp_definition` | Go to definition of a symbol |
|
|
| `lsp_references` | Find all references to a symbol |
|
|
| `lsp_symbols` | List file symbols or search workspace symbols |
|
|
| `lsp_rename` | Preview rename refactoring (returns planned edits) |
|
|
| `lsp_completions` | Code completion suggestions at a position |
|
|
| `code_overview` | Project structure, key files, and symbols (tree-sitter) |
|
|
| `code_search` | Find code by AST structure with metavariables |
|
|
| `code_rewrite` | Transform code matching structural patterns |
|
|
|
|
LSP servers start lazily — they only spin up when a tool is first used on a file of that language. For slow servers (e.g. jdtls), you can [auto-start them on session launch](#project-config).
|
|
|
|
## Auto-diagnostics
|
|
|
|
After a successful `write` or `edit`, if an LSP server is already running for that file type, the extension automatically appends compilation errors to the tool result. This gives the LLM immediate feedback without requiring a separate `lsp_diagnostics` call.
|
|
|
|
- Scoped to the single changed file (no workspace-wide noise)
|
|
- Only errors, max 10 lines — keeps context lean
|
|
- Only fires when a server is already running (no lazy startup)
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
git clone https://github.com/samfoy/pi-lsp-extension.git
|
|
cd pi-lsp-extension
|
|
npm install
|
|
```
|
|
|
|
Add to your pi `settings.json`:
|
|
|
|
```json
|
|
{
|
|
"extensions": ["/path/to/pi-lsp-extension/src/index.ts"]
|
|
}
|
|
```
|
|
|
|
Or run directly:
|
|
|
|
```bash
|
|
pi -e /path/to/pi-lsp-extension/src/index.ts
|
|
```
|
|
|
|
## Supported Languages
|
|
|
|
Install the language server you need, then it works automatically:
|
|
|
|
| Language | Server | Install |
|
|
|----------|--------|---------|
|
|
| TypeScript/JavaScript | `typescript-language-server` | `npm i -g typescript-language-server typescript` |
|
|
| Python | `pyright-langserver` | `pip install pyright` |
|
|
| Rust | `rust-analyzer` | [rustup](https://rustup.rs/) |
|
|
| Go | `gopls` | `go install golang.org/x/tools/gopls@latest` |
|
|
| Java | `jdtls` | [Eclipse JDT.LS](https://github.com/eclipse-jdtls/eclipse.jdt.ls) |
|
|
|
|
Add more at runtime:
|
|
|
|
```
|
|
/lsp-config ruby solargraph stdio
|
|
/lsp-config lua lua-language-server
|
|
```
|
|
|
|
## Commands
|
|
|
|
| Command | Description |
|
|
|---------|-------------|
|
|
| `/lsp` | Show status of running LSP servers |
|
|
| `/lsp-restart <lang>` | Restart an LSP server (kills daemon, re-initializes) |
|
|
| `/lsp-config <lang> <cmd> [args]` | Configure a language server |
|
|
| `/lsp-lombok [path]` | Set Lombok jar path for Java (or show current) |
|
|
| `/bemol [run\|watch\|stop\|status]` | Manage bemol (Brazil workspaces) |
|
|
|
|
## How it Works
|
|
|
|
1. **Lazy startup** — servers start on first tool use for a file type (or eagerly via [`.pi-lsp.json`](#project-config))
|
|
2. **Tree-sitter fallback** — when no LSP server is running, tools like `lsp_diagnostics`, `lsp_hover`, `lsp_definition`, and `lsp_symbols` fall back to tree-sitter for syntax errors, signatures, and symbol extraction
|
|
3. **File sync** — pi's `read`/`write`/`edit` operations are automatically synced to the LSP via `didOpen`/`didChange`, with LRU eviction (`didClose`) after 100 tracked files
|
|
4. **Diagnostics cache** — the server pushes diagnostics asynchronously; tools read from a local cache
|
|
5. **Auto-diagnostics** — errors are appended to write/edit results when a server is running
|
|
6. **Shared daemons** — in supported workspaces, LSP servers run as background daemons shared across pi sessions
|
|
|
|
## Lombok Support (Java)
|
|
|
|
If your Java project uses [Lombok](https://projectlombok.org/), jdtls needs the Lombok agent jar to understand generated code. The extension resolves the jar in this order:
|
|
|
|
1. **`/lsp-lombok` command** — set the path at runtime:
|
|
```
|
|
/lsp-lombok /path/to/lombok.jar
|
|
```
|
|
|
|
2. **`LOMBOK_JAR` environment variable** — set before starting pi:
|
|
```bash
|
|
export LOMBOK_JAR=/path/to/lombok.jar
|
|
pi
|
|
```
|
|
|
|
3. **Auto-detection** — in Brazil workspaces, the extension searches `env/Lombok-*/runtime/lib/` and `env/gradle-cache-2/` automatically.
|
|
|
|
Run `/lsp-lombok` with no arguments to see which jar is currently configured.
|
|
|
|
## Project Config
|
|
|
|
Create a `.pi-lsp.json` file in your project root to configure LSP behavior per-project:
|
|
|
|
```json
|
|
{
|
|
"autoStart": ["java", "typescript"],
|
|
"lombokJar": "auto",
|
|
"autoInjectDiagnostics": ["typescript"],
|
|
"servers": {
|
|
"python": { "command": "pylsp", "args": [] }
|
|
}
|
|
}
|
|
```
|
|
|
|
| Field | Description |
|
|
|-------|-------------|
|
|
| `autoStart` | Array of language IDs to start eagerly on session launch. Servers begin initializing in the background immediately — no need to wait for the first tool call. Ideal for slow servers like `jdtls`. |
|
|
| `lombokJar` | Path to a Lombok jar (absolute or relative to project root), or `"auto"` to auto-detect in Brazil workspaces. Applied before auto-start so jdtls launches with the correct `-javaagent` flag. |
|
|
| `autoInjectDiagnostics` | Controls whether LSP errors are auto-appended to `write`/`edit` tool results. `true` (default) enables for all languages, `false` disables entirely, or pass an array of language IDs (e.g. `["typescript"]`) to enable selectively. Disable for Java/Brazil workspaces where Lombok and dependency-chain false positives create noise. |
|
|
| `servers` | Custom server configs keyed by language ID. Overrides the built-in defaults. Each entry has `command`, optional `args` (string array), and optional `env` (key-value pairs). |
|
|
|
|
The config file is loaded once at session start. Changes require restarting the pi session.
|
|
|
|
**Example for a Java Brazil workspace:**
|
|
```json
|
|
{
|
|
"autoStart": ["java"],
|
|
"lombokJar": "auto"
|
|
}
|
|
```
|
|
|
|
This triggers bemol + jdtls startup as soon as the session begins, so by the time you need `lsp_diagnostics` or `lsp_hover`, the server is already warm.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
src/
|
|
├── index.ts # Extension entry point, .pi-lsp.json config loader
|
|
├── lsp-client.ts # JSON-RPC client (stdio + socket modes)
|
|
├── lsp-manager.ts # Server lifecycle, per-language instances
|
|
├── file-sync.ts # didOpen/didChange tracking
|
|
├── lsp-daemon.ts # Background daemon for shared servers
|
|
├── lsp-daemon-launcher.cjs
|
|
├── bemol.ts # Brazil workspace support
|
|
├── locks.ts # File-based locking for daemon coordination
|
|
├── resolve-provider.ts # LSP vs tree-sitter provider selection
|
|
├── shared/
|
|
│ ├── constants.ts # Skip dirs, file size limits
|
|
│ ├── debug.ts # Debug logger (PI_LSP_DEBUG=1)
|
|
│ ├── format.ts # Location formatting utilities
|
|
│ ├── language-map.ts # File extension → language ID mapping
|
|
│ └── timing.ts # Timing constants
|
|
├── tree-sitter/
|
|
│ ├── parser-manager.ts # WASM parser loading and caching
|
|
│ ├── pattern-compiler.ts # Metavariable pattern → AST matcher
|
|
│ ├── search-engine.ts # Structural search over files
|
|
│ ├── rewrite-engine.ts # Structural find-and-replace
|
|
│ ├── symbol-extractor.ts # Per-language symbol extraction
|
|
│ └── workspace-index.ts # Project-wide symbol index
|
|
└── tools/
|
|
├── diagnostics.ts
|
|
├── hover.ts
|
|
├── definition.ts
|
|
├── references.ts
|
|
├── symbols.ts
|
|
├── rename.ts
|
|
├── completions.ts
|
|
├── code-overview.ts
|
|
├── code-search.ts
|
|
└── code-rewrite.ts
|
|
```
|
|
|
|
## Tips
|
|
|
|
- Position parameters are 1-indexed (line 1, column 1 = first character)
|
|
- `lsp_rename` returns a preview — the LLM uses `edit`/`write` to apply changes
|
|
- Use `/lsp-restart java` after changing Lombok config — jdtls needs a full restart to pick up `-javaagent` changes
|
|
- Set `PI_LSP_DEBUG=1` to enable debug logging for troubleshooting
|
|
- The extension adds a system prompt guideline nudging the LLM to check diagnostics after edits
|
|
|
|
## License
|
|
|
|
MIT
|