@karutoil/pi-lsp

Instant tree-sitter syntax diagnostics for Pi coding agent — 80+ languages, sub-ms parses, auto-checks after every edit

Packages

Package details

extension

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

$ pi install npm:@karutoil/pi-lsp
Package
@karutoil/pi-lsp
Version
2.0.0
Published
Jun 11, 2026
Downloads
not available
Author
karutoil
License
MIT
Types
extension
Size
69.8 KB
Dependencies
2 dependencies · 4 peers
Pi manifest JSON
{
  "extensions": [
    "./extensions"
  ],
  "image": "https://raw.githubusercontent.com/karutoil/pi-lsp/main/pi-lsp.png"
}

Security note

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

README

pi-lsp

Instant tree-sitter LSP emulation + compiler-backed type checking for Pi coding agent.

  • 🚀 Sub-millisecond — in-process WASM parser, zero daemon, zero child processes
  • 🌐 80+ languages — TypeScript, Python, Rust, Go, C/C++, Java, Bash, CSS, JSON, YAML, and more
  • 🔍 Auto-checks after every edit — re-parses changed files, injects diagnostics, updates symbol index
  • 🧭 LSP-style navigation — go to definition, find references, hover, document symbols
  • Two-tier validation — tree-sitter catches syntax instantly; real compiler catches types before done
  • 📦 Zero setup — npm deps auto-installed, grammars bundled, no external binaries needed

Install

pi install npm:pi-lsp

Or try without installing:

pi -e npm:pi-lsp

What It Does

Automatic (no action needed)

After every edit, write, or bash file redirect (> file), pi-lsp instantly re-parses the file. If syntax errors are found, they're injected into the agent context automatically — the LLM sees the errors and can fix them.

Two-Tier Validation

Tier 1 — Syntax (tree-sitter, instant, sub-ms) Catches missing brackets, unclosed blocks, invalid tokens across 80+ languages. Runs automatically after every edit.

Tier 2 — Semantics (real compiler, before task complete) Runs the actual compiler/type checker (tsc, cargo check, go build, mypy, etc.) to catch type mismatches, missing imports, and logic errors that tree-sitter can't see. Auto-detects project type. Agent is instructed to run this before declaring work done.

Tools (agent can call these)

Tool Description
lsp_diagnostics Check a single file for syntax errors
lsp_project_diagnostics Scan the entire project for syntax errors
lsp_symbols List all symbols (functions, classes, variables) in a file
lsp_definition Go to definition of a symbol at line/column
lsp_references Find all references to a symbol in a file
lsp_hover Get node type and info at a position
run_type_check Run the project's compiler/type checker

Commands (you can type these)

Command Description
/lsp-check [path] Check a file or directory for syntax errors
/lsp-project Scan the entire project for syntax errors
/lsp-status Show parser status, loaded grammars, symbol index stats
/symbols Show symbol index statistics
/check Run the language type checker on the project

Supported Languages

TypeScript, TSX, JavaScript, JSX, Python, Rust, Go, C, C++, Java, C#, Swift, Kotlin, Scala, Ruby, Lua, Bash, Zsh, CSS, SCSS, HTML, JSON, YAML, TOML, Markdown, SQL, PHP, Elixir, Erlang, Haskell, OCaml, Nim, Zig, Dart, R, Julia, Perl, Vue, Svelte, Astro, GraphQL, Prisma, Dockerfile, CMake, Make, Nix, Fish, PowerShell, Protobuf, Terraform, HCL, Gleam, Solidity, Typst, LaTeX, and more — 80+ total.

How It Works

Syntax layer uses tree-sitter compiled to WebAssembly via web-tree-sitter, with pre-built grammar WASMs from tree-sitter-wasm. Everything runs in-process — no daemon, no child processes, no network calls.

Symbol index extracts definitions using generic tree-sitter heuristics: any node with a name field whose type matches a definition pattern (*_definition, *_declaration, function_, class_, etc.). Works for all 80+ languages without per-language query files. Parsed trees are cached and incrementally updated on every edit.

Type check layer auto-detects the project type from config files (tsconfig.json, Cargo.toml, go.mod, pyproject.toml, etc.) and runs the appropriate compiler/linter. Falls back through multiple commands if the primary tool isn't installed. Parses compiler output into structured diagnostics.

Example Output

Syntax diagnostics (instant):

🔍 src/main.ts (typescript): 1 syntax error
  ❌ L17:18 — Missing `}` in statement_block

Document symbols:

📋 8 symbols in src/main.ts:

  **function** (1):
    L7:10 — `broken`
  **import** (3):
    L1:10 — `add`
    L1:15 — `greet`
    L1:22 — `Calculator`
  **variable** (4):
    L4:7 — `result`
    L8:9 — `msg` (in broken)
    L15:7 — `calc`
    L17:7 — `val`

Go to definition:

✅ `add` (import) → L1:10

Find references:

📌 2 reference(s) to `greet`:
     L1:15 — in import_specifier
     L8:15 — in call_expression

Hover:

**class**
\`\`\`
class
\`\`\`

Type check (real compiler):

🔍 1 issue(s) found by npx tsc --noEmit:
  ❌ L4:20 — Argument of type 'string' is not assignable to parameter of type 'number'.

All clean:

✅ Project clean — no syntax errors in 363/363 files
✅ Type check passed — detected typescript, npx tsc --noEmit

Architecture

Agent edits file
    ↓
tree-sitter re-parses instantly (<1ms)
    ↓
Auto-updates symbol index (cached trees)
    ↓
Syntax errors? → injected into agent context
    ↓
Agent uses lsp_symbols / lsp_definition / lsp_references as needed
    ↓
Agent calls run_type_check before declaring done
    ↓
Real compiler catches type/semantic errors
    ↓
Agent fixes, re-runs check → clean ✅

Symbol extraction

Generic heuristic walks any tree-sitter tree. Classifies nodes by type name pattern:

Node type pattern Kind Languages
function_declaration, function_definition, function_item function TS, Python, Rust, Go
class_definition, class_declaration, struct_item class TS, Python, Rust
method_definition, method_declaration method TS, Go
variable_declarator, assignment, let_declaration variable TS, Python, Rust
interface_declaration, trait_item interface TS, Rust
enum_declaration, enum_item enum TS, Rust
import_specifier, use_declaration import TS, Rust

A node must also have a name field (via childForFieldName("name")) to be extracted. This filters out false positives like return_statement or class_body that match type patterns but aren't definitions.

Type check commands

Project type Detected by Command
TypeScript tsconfig.json npx tsc --noEmit
JavaScript package.json npx eslint . --format unix
Rust Cargo.toml cargo check --message-format short
Go go.mod go build ./...
Python pyproject.toml, setup.py ruff check, mypy .
Java (Maven) pom.xml mvn compile -q
Java (Gradle) build.gradle gradle compileJava -q
C/C++ (CMake) CMakeLists.txt cmake --build build
C/C++ (Make) Makefile make -k
Ruby Gemfile ruby -c
PHP composer.json php -l
Elixir mix.exs mix compile --no-deps-check
Haskell stack.yaml, cabal.project stack build, cabal build
Scala build.sbt sbt compile
Swift Package.swift swift build
Zig build.zig zig build
Dart dub.json, dub.sdl dart analyze

License

MIT