import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { loadConfig } from "../src/config.js"; import { LspManager } from "../src/manager.js"; import { ProgressiveSink } from "../src/progressive.js"; import { createCallHierarchyTool, createDefinitionTool, createHoverTool, createImplementationTool, createReferencesTool, createRenameTool, createSymbolsTool, createWorkspaceSymbolTool, } from "../src/queries.js"; import { createDiagnosticsTool, createFixTool, createStatusTool } from "../src/tools.js"; // @gitawego/pi-lsp — pi entry point. // Lifecycle: builds the LspManager + ProgressiveSink at session_start (never in // the factory — pi forbids background resources there), tears them down at // session_shutdown, and reacts to agent turns for progressive diagnostics. export default function piLsp(pi: ExtensionAPI) { let manager: LspManager | undefined; let sink: ProgressiveSink | undefined; const getManager = () => { if (!manager) throw new Error("LSP manager is not initialized (session_start not fired)."); return manager; }; const tools = [ createDiagnosticsTool(getManager), createStatusTool(getManager), createFixTool(getManager), createHoverTool(getManager), createDefinitionTool(getManager), createReferencesTool(getManager), createSymbolsTool(getManager), createRenameTool(getManager), createImplementationTool(getManager), createWorkspaceSymbolTool(getManager), createCallHierarchyTool(getManager), ]; for (const tool of tools) pi.registerTool(tool); pi.registerCommand("lsp", { description: "Show LSP server sessions and configuration status", handler: async (_args, ctx) => { const sessions = manager?.status() ?? []; ctx.ui.notify( sessions.length ? `LSP sessions:\n${sessions.map((s) => `${s.id} @ ${s.root}: ${s.status}`).join("\n")}` : "No LSP sessions started.", "info", ); }, }); pi.on("session_start", (_event, ctx) => { const config = loadConfig({ cwd: ctx.cwd, trusted: ctx.isProjectTrusted() }); manager = new LspManager({ config, cwd: ctx.cwd, trusted: ctx.isProjectTrusted(), signal: ctx.signal, onStatus: (text) => ctx.ui.setStatus("lsp", text), }); sink = new ProgressiveSink({ manager, config: config.progressive, onInjection: (text) => { if (config.progressive.inject === "conversation") { pi.sendMessage({ customType: "lsp-diagnostics", content: text, display: true }); } else { ctx.ui.setStatus("lsp", `lsp: ${text.split("\n")[0]}`); } }, }); }); pi.on("session_shutdown", async () => { await manager?.shutdown(); manager = undefined; sink = undefined; }); pi.on("turn_end", async (event) => { await sink?.handleTurn(event.toolResults ?? []); }); }