@spences10/pi-lsp

Language Server Protocol tools for Pi agents to inspect diagnostics, hovers, definitions, references, and symbols

Packages

Package details

extension

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

$ pi install npm:@spences10/pi-lsp
Package
@spences10/pi-lsp
Version
0.1.0
Published
Nov 6, 2026
Downloads
1,773/mo · 1,189/wk
Author
spences10
License
MIT
Types
extension
Size
178.1 KB
Dependencies
1 dependency · 2 peers
Pi manifest JSON
{
  "image": "https://raw.githubusercontent.com/spences10/my-pi/main/assets/pi-package-preview.png",
  "extensions": [
    "./dist/index.js"
  ]
}

Security note

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

README

@spences10/pi-lsp

built with Vite+ tested with Vitest npm version license

my-pi package preview

Prompt guidance uses Pi's named system-prompt sections. It coexists with user SYSTEM.md and APPEND_SYSTEM.md files and other extensions without replacing the complete prompt.

Give agents precise code intelligence instead of guesswork. pi-lsp exposes language-server diagnostics, hovers, definitions, references, and symbols as Pi tools so models can validate edits and navigate typed codebases accurately.

When directly activated, the hover, definition, and document-symbol tools prefer Pi's strict JSON Schema sampling with closed, fully required schemas. LSP tools with optional arguments use normal tool calling for provider portability.

Installation

pi install npm:@spences10/pi-lsp

Local development from this monorepo:

pnpm --filter @spences10/pi-lsp run build
pi install ./packages/pi-lsp
# or for one run only
pi -e ./packages/pi-lsp

Required language servers

This package talks to language-server binaries installed globally on PATH or locally in a project. Global installation makes one server available across projects:

npm install -g typescript svelte-language-server
# or
pnpm add -g typescript svelte-language-server

Project-local development dependencies let a repository pin and share specific server versions:

npm install -D typescript svelte-language-server
# or
pnpm add -D typescript svelte-language-server

For TypeScript 6 and earlier, add typescript-language-server to the same global or project-local command. Volta users can install global tools with volta install typescript svelte-language-server.

Supported server discovery includes:

  • TypeScript 7 / JavaScript via the project-local native tsc --lsp --stdio server
  • TypeScript 6 and earlier via typescript-language-server --stdio
  • Svelte via svelteserver
  • Python via python-lsp-server, Basedpyright, or Pyright
  • Go via gopls
  • Rust via rust-analyzer
  • Ruby via solargraph
  • Java via jdtls
  • Lua via lua-language-server

The TypeScript backend is selected by capability. A project-local TypeScript installation takes priority. TypeScript 7 without lib/tsserver.js uses its native tsc LSP, while classic project installations use typescript-language-server. When a project does not pin TypeScript, a TypeScript 7 tsc on PATH provides the native LSP. /lsp status reports the selected backend and full command. A TypeScript 7 native server that cannot start reports a specific setup hint.

The classic typescript-language-server backend starts with initializationOptions.tsserver.useSyntaxServer: "never". This keeps navigation requests on the full semantic server instead of accepting partial answers from the lightweight syntax server while the project loads. The first request can take longer. This setting is not sent to TypeScript 7's native server or other language servers.

Python server selection

Python keeps pylsp when it is available, so installing another type checker does not replace an existing pylsp setup or its plugins. When pylsp is missing, project-local Basedpyright or Pyright takes priority over global tools. The nearest project installation wins; within the same directory, Basedpyright takes priority over Pyright. Global fallback uses Basedpyright before Pyright.

Set MY_PI_LSP_PYTHON_SERVER to pylsp, basedpyright, or pyright to select a specific backend. Unset it or use auto for the default selection above. For example:

MY_PI_LSP_PYTHON_SERVER=pyright pi

An explicit selection never switches to another backend. Missing or failed servers report an error and an installation hint. Install with pip install python-lsp-server, pip install basedpyright, or pip install pyright. Pyright-family servers use --stdio.

Python discovery checks executable files in ancestor .venv/bin folders (.venv/Scripts on Windows), node_modules/.bin, and PATH. Directories, broken links, and non-executable files are skipped. On Windows, use native executables such as the .exe launchers from pip; .cmd and .bat wrappers are not supported by the shell-free client. /lsp status shows the selected backend and resolved command.

Server discovery does not select the Python interpreter used for analysis. Configure the server's interpreter or virtual environment settings separately when needed.

Project-local language servers

Project-local binaries in node_modules/.bin and Python .venv folders are selected automatically by the discovery rules above, in both interactive and headless sessions. There is no separate per-binary approval prompt or LSP trust store.

MY_PI_LSP_PROJECT_BINARY is no longer used. /lsp status shows the selected backend and resolved command for running and idle servers.

Language-server environment

LSP filters its language-server environment directly, passing only PATH, PI_CODING_AGENT_DIR, home/user, locale (LANG and LC_*), terminal/color, shell, temporary-directory, and CI variables by default. Provider credentials, Pi session metadata, NODE_OPTIONS, and other ambient variables are not inherited unless explicitly allowlisted.

Use MY_PI_LSP_ENV_ALLOWLIST=NAME,OTHER_NAME to pass toolchain configuration such as RUSTUP_HOME, CARGO_HOME, or JAVA_HOME when needed. MY_PI_CHILD_ENV_ALLOWLIST remains supported; entries from both lists are combined.

This reduces ambient credential exposure, but is not a sandbox: language servers retain the current user's filesystem permissions.

Tools

The extension registers LSP-backed Pi tools for:

  • diagnostics
  • hover
  • definitions
  • references
  • document symbols

LSP tools use Pi's codemode exposure: they are callable from scripts without being declared individually to the model. Enable codemode with pi --tools +codemode or upstream Pi's defaultTools setting. No global tool-exposure settings are changed by this extension.

text(await tools.lsp_diagnostics_many({ files: ['src/index.ts'] }));

Use searchTools('lsp') inside codemode to discover the tools. They can also be activated directly with Pi's tool selection, for example pi --tools +lsp_hover. Automatic diagnostics do not require codemode or direct LSP tool activation.

A diagnostics result is clean only when the language server supplied an empty list for the current document. If the server sends nothing before wait_ms expires, the tool reports validation incomplete, and lsp_diagnostics_many counts the file as incomplete and not as clean. An incomplete result is not a pass: retry with a larger wait_ms or use another check. Versioned diagnostics for a closed document or an earlier document version are ignored. Versions are never reused across reopenings, and a pending push wait or pull request cannot validate a snapshot that changed while it was waiting.

Documents stay open between requests, with later checks rereading the file and sending changes when its contents differ. This avoids versionless empty cleanup diagnostics from a close racing with the next check. Versionless push results still rely on the server's ordering. Open documents and their cached text are released when the server stops, restarts, or the Pi session exits.

Automatic diagnostics

Successful edit and write calls queue supported files for one batch at turn end, including calls made inside codemode. Repeated edits to a file are checked once against its final contents. The extension reuses the diagnostics tool's collection and formatting, checking up to eight files concurrently.

Edits themselves do not wait for LSP. The turn boundary waits for server startup and up to 1500 ms of diagnostics per file before the next model response. A session message records the results, including clean files, errors, warnings, and incomplete checks. The final edit is checked even when it was the last tool call. Cancelled turns and session changes discard pending feedback.

Bash commands, external editors, failed edits, and unsupported files are not tracked. Use explicit diagnostics after Bash or external changes, or when automatic feedback is incomplete. Silence is not proof that a file is clean.

Model reminder

When LSP tools, codemode, or tool search are active, the extension reminds the model to use LSP for code questions and reuse current automatic feedback rather than repeating checks. Explicit diagnostics remain available for missing or incomplete feedback, preferring lsp_diagnostics_many for batches. Passed diagnostics can be reused for review, commit, and push if their inputs are unchanged. Only affected or previously missing diagnostics need to run; a commit or push request alone does not require another run.

Commands

/lsp status
/lsp list
/lsp restart all
/lsp restart <language>

Use /lsp status to inspect active clients and /lsp restart after dependency installs or language-server crashes.

Language servers stop after five minutes without an active LSP request and start again on demand. Set MY_PI_LSP_IDLE_TIMEOUT_MS to a positive timeout in milliseconds, or set it to 0 to keep idle servers running until the Pi session exits.

Using from a custom harness

import lsp from '@spences10/pi-lsp';

// pass `lsp` as an ExtensionFactory to your Pi runtime

For harnesses that need to provide their own language-server client factory, use the named extension factory:

import { create_lsp_extension } from '@spences10/pi-lsp';

const lsp = create_lsp_extension({ create_client });

The package also exports CreateLspExtensionOptions, should_inject_lsp_prompt, and LspClientLike for custom harnesses and tests that need to share the same prompt-gating or client seam.

my-pi includes this package.

Development

Package scripts build transitive workspace dependencies first, then run local tools through Vite+ with vp exec.

pnpm --filter @spences10/pi-lsp run check
pnpm --filter @spences10/pi-lsp run test
pnpm --filter @spences10/pi-lsp run build

License

MIT