feat: add LSP client library with JSON-RPC stdio + server registry #28
Merged
FO
4mo ago+1028+1028+0123456789012345678901234567890123456789-28-28-01234567890123456789
Summary
Foundation for Wave S6 (IDE Integration) — shared LSP client and registry that all 3 tools (hover, references, diagnostics) will use.
Files Added
-
src/lib/lsp-client.ts— Core LSP Client- JSON-RPC 2.0 over stdin/stdout with Content-Length framing
- Request/response matching via numeric IDs
- Lifecycle: spawn → initialize → initialized → openDocument → shutdown
- API methods: hover, references, diagnostics, documentSymbol
- Timeout handling (30s init, 10s requests), graceful degradation
- Logging via
logDebugEvent() - Diagnotics cache populated by
publishDiagnosticsnotifications
-
src/lib/lsp-registry.ts— Language Server Registry- Maps file extensions to LSP server commands
- Pre-configured for: TypeScript/JS, Go, Python, Rust, PHP
resolveServer(filePath)— auto-selects server by file extensionisInstalled(command)— checks binary availability viawhichshutdownAll()— cleanup- Singleton via
getLspRegistry()
-
tests/lsp-client.test.ts— 39 tests (all passing)- JSON-RPC framing & Content-Length parsing (mock LSP server)
- Request/response matching (concurrent requests)
- Hover, references, documentSymbol, diagnostics
- Timeout handling with graceful degradation
- Shutdown lifecycle
- Registry: file extension resolution for all 5 languages
- Registry: null for unknown extensions, no-extension files
- Registry: case-insensitive matching
- Registry: isInstalled (system binaries + nonexistent)
- Registry: register(), getConfigs(), singleton, shutdownAll
Test Results
39 pass, 0 fail, 79 expect() calls
Full suite: 213 pass, 0 fail across 19 files.
Also Modified
eslint.config.js— added Bun global declarations (clearTimeout,TextDecoder,TextEncoder,Timer,Uint8Array)
Closes #28
Summary by cubic
Adds a shared LSP client and server registry using JSON‑RPC over stdio and ships lsp_hover and lsp_references tools for Wave S6 IDE, addressing #28. Stabilizes the client lifecycle, makes initialization non‑blocking with rootUri, improves framing, and ensures safer shutdown and respawn.
-
New Features
lsp_hover: on‑demand docs; converts 1‑based positions; cleans/truncates markdown; includesfullContents; returns ToolResult JSON; registered infour-opencode-supertools.ts.lsp-client: Content‑Length framed JSON‑RPC; non‑blocking auto‑initialize onspawn()withrootUri; idempotentinitialize(); request timeouts; diagnostics cache; dedupedopenDocument();openDocumentsis now public.lsp-registry: resolves by extension; checks install; respawns dead clients; derivesrootUrifrom the file’s parent directory; returns null if spawn fails;getOpenDocuments()reads from publicopenDocuments.
-
Bug Fixes
- Header parsing now tolerates extra headers and finds CRLFCRLF; lifecycle is cleared when streams close; in‑flight requests are rejected on shutdown.
- Fixed init guard recursion; passed
rootUrithroughspawn()to_startInit()to avoid empty roots and improve handshake reliability. - Tools return ToolResult‑compatible JSON strings; tests parse
.output; improved isolation and timeouts. - Fixed stdio type casts; removed incomplete
lsp_diagnostics; updated ESLint globals (TextEncoder,Uint8Array).
Summary by CodeRabbit
-
New Features
- Added JSON-RPC 2.0 LSP client support for hover, references, document symbols, and diagnostics using framed stdio messaging with safe timeouts.
- Added new LSP tools for hover and symbol references from a file path and cursor position (including formatted/trimmed output).
- Extended the LSP registry to resolve servers by file extension, include PHP/Intelephense configuration, and surface currently open documents.
-
Tests
- Added Bun test coverage for the LSP client, registry, hover tool, and references tool (mock LSP servers, timeouts, framing, truncation, shutdown).
-
Chores
- Updated ESLint globals for additional runtime/browser-like identifiers.