@gitawego/pi-lsp

Config-driven Language Server Protocol integration for the pi coding agent. Official LSP servers by default (typescript, kotlin, gopls, rust-analyzer, clangd, pyright, ...), persistent per-project sessions, progressive diagnostics after edits, and rich qu

Packages

Package details

extension

Install @gitawego/pi-lsp from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@gitawego/pi-lsp
Package
@gitawego/pi-lsp
Version
0.1.0
Published
Aug 11, 2026
Downloads
168/mo · 28/wk
Author
gitawego
License
MIT
Types
extension
Size
126.6 KB
Dependencies
3 dependencies · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./extensions/pi-lsp.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

@gitawego/pi-lsp

Config-driven Language Server Protocol integration for the pi coding agent. 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

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

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

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)
{
  "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

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 (persistent clients, root detection, push+pull diagnostic merging). JSON-RPC framing patterns from @narumitw/pi-lsp.

License

MIT — see LICENSE.