@mrclrchtr/supi-lsp 1.0.0 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,112 +1,66 @@
1
1
  # @mrclrchtr/supi-lsp
2
2
 
3
- Language Server Protocol integration for the [pi coding agent](https://github.com/earendil-works/pi).
3
+ Language Server Protocol for PI your agent navigates code like an IDE.
4
4
 
5
- ## Install
6
-
7
- ```bash
8
- pi install npm:@mrclrchtr/supi-lsp
9
- ```
10
-
11
- ## What it adds
12
-
13
- - `lsp` tool with `hover`, `definition`, `references`, `diagnostics`, `symbols`, `rename`, `code_actions`, `workspace_symbol`, `search`, `symbol_hover`, and `recover`
14
- - Stable system-prompt guidance that tells the agent to prefer LSP over grep/rg for code navigation
15
- - Proactive project scanning and eager startup of detected language servers
16
- - Automatic stale-diagnostic recovery when workspace sentinels change (`package.json`, root lockfiles, `tsconfig*`, generated `*.d.ts` files`) before the next agent turn, plus immediate recovery after successful `write` or `edit` calls for those paths
17
- - Inline diagnostic surfacing around reads, writes, and edits
18
- - Compact diagnostic context injection when outstanding diagnostics change, with stale-diagnostic warnings when needed
19
- - `/lsp-status` status overlay
5
+ Without LSP, agents grep and guess. With it, they jump to definitions, find every reference, rename across files, and catch type errors inline. The same precision you get from an editor, available to your agent.
20
6
 
21
- ## Public library API
7
+ ## What you get
22
8
 
23
- In addition to the extension entrypoint, `@mrclrchtr/supi-lsp` exports a reusable session-scoped service API for peer extensions:
9
+ ### Navigate with precision
24
10
 
25
- ```ts
26
- import { getSessionLspService, SessionLspService } from "@mrclrchtr/supi-lsp";
27
-
28
- const state = getSessionLspService("/project");
11
+ Go-to-definition, find-references, rename, hover types. The agent stops guessing and starts navigating your codebase with IDE-level accuracy.
29
12
 
30
- if (state.kind === "ready") {
31
- const service = state.service;
32
- const hover = await service.hover("src/index.ts", { line: 5, character: 10 });
33
- const defs = await service.definition("src/index.ts", { line: 5, character: 10 });
34
- const refs = await service.references("src/index.ts", { line: 5, character: 10 });
35
- const impls = await service.implementation("src/index.ts", { line: 5, character: 10 });
36
- const symbols = await service.documentSymbols("src/index.ts");
37
- const projectServers = service.getProjectServers();
38
- }
39
- ```
13
+ ### Catch problems immediately
40
14
 
41
- Peer extensions can import from the package root without reaching into private files.
15
+ Type errors, warnings, and hints surface inline after every edit. The agent sees mistakes as it makes them — not 10 turns later when tests fail.
42
16
 
43
- ## Tool actions
17
+ ### Always ready
44
18
 
45
- The `lsp` tool supports these actions:
19
+ Servers start automatically for your project. The agent gets language-aware guidance at session start and stale diagnostics are refreshed when dependencies change.
46
20
 
47
- - `hover`: type info at a position
48
- - `definition`: go to definition
49
- - `references`: find all references
50
- - `diagnostics`: per-file or project-wide diagnostics
51
- - `symbols`: document symbols
52
- - `rename`: workspace-wide rename
53
- - `code_actions`: quick fixes at a position
54
- - `workspace_symbol`: fuzzy symbol search across the project
55
- - `search`: symbol search with text fallback
56
- - `symbol_hover`: hover by symbol name
57
- - `recover`: refresh diagnostics after workspace-wide dependency, config, or generated-type changes
58
-
59
- Line and character positions are **1-based**.
60
-
61
- Example:
21
+ ## Install
62
22
 
63
- ```json
64
- {
65
- "action": "definition",
66
- "file": "src/index.ts",
67
- "line": 12,
68
- "character": 8
69
- }
23
+ ```bash
24
+ pi install npm:@mrclrchtr/supi-lsp
70
25
  ```
71
26
 
72
- ## Configuration
27
+ ## Quick look
73
28
 
74
- If your install surface includes `/supi-settings` (for example via `@mrclrchtr/supi`), this package contributes an LSP settings section there. Use that panel to:
29
+ The agent gets an `lsp` tool. The most-used actions:
75
30
 
76
- - enable or disable LSP globally
77
- - control the severity threshold
78
- - select active language servers
79
- - configure file exclusion patterns
31
+ | Action | What the agent can do |
32
+ |--------|----------------------|
33
+ | `hover` | See the type of any symbol |
34
+ | `definition` | Jump to where something is defined |
35
+ | `references` | Find every usage across the project |
36
+ | `diagnostics` | See errors, warnings, and hints |
37
+ | `rename` | Rename across the entire project |
80
38
 
81
- ## Commands
39
+ Full action reference: the agent's system prompt includes complete guidelines for all 11 actions (hover, definition, references, diagnostics, symbols, rename, code_actions, workspace_symbol, search, symbol_hover, recover). All positions are 1-based.
82
40
 
83
- `/lsp-status` toggles an overlay showing active language servers and outstanding diagnostics:
41
+ ## Settings
84
42
 
85
- ```text
86
- λ LSP inspector /lsp-status toggles
87
- 3 servers running • 12 open files • 5 errors • 2 warnings
43
+ Configure via `/supi-settings` (LSP panel):
88
44
 
89
- Servers
90
- typescript running 24 open files
91
- python running 8 open files
92
- bash running 0 open files
45
+ - Enable or disable LSP per project
46
+ - Set diagnostic severity threshold (errors only, or include warnings/hints)
47
+ - Choose which language servers to activate
48
+ - Add file exclusion patterns (gitignore-style globs)
93
49
 
94
- Problems
95
- src/lsp.ts:42 Cannot find name 'foo' ts(2304)
96
- src/manager.ts:108 Property 'bar' does not exist ts(2339)
97
- ```
50
+ Settings are stored in `~/.pi/agent/supi/config.json` (global) or `.pi/supi/config.json` (project). The `/lsp-status` command shows active servers and outstanding diagnostics.
98
51
 
99
- When no servers are available, the overlay shows `no LSP servers available for this project`. A compact status summary is always visible in the pi status bar.
52
+ ## For extension developers
100
53
 
101
- ## Requirements
54
+ This package exports a reusable session-scoped LSP service. Peer extensions can query the same LSP runtime without starting duplicate servers:
102
55
 
103
- - `@earendil-works/pi-coding-agent`
104
- - `@earendil-works/pi-tui`
105
- - `typebox`
106
- - relevant language servers installed and available on `PATH`
107
- - `@mrclrchtr/supi-core`
56
+ ```ts
57
+ import { getSessionLspService, SessionLspService } from "@mrclrchtr/supi-lsp";
108
58
 
109
- ## Source
59
+ const state = getSessionLspService("/project");
60
+ if (state.kind === "ready") {
61
+ const defs = await state.service.definition("src/index.ts", { line: 5, character: 10 });
62
+ const refs = await state.service.references("src/index.ts", { line: 5, character: 10 });
63
+ }
64
+ ```
110
65
 
111
- - Extension entrypoint: `lsp.ts`
112
- - Public library surface: `index.ts`
66
+ Import from the package root — no need to reach into internal files.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "0.2.0",
3
+ "version": "1.1.3",
4
4
  "description": "SuPi core — shared infrastructure for SuPi extensions (XML context tags, config system)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,9 +22,13 @@
22
22
  "@earendil-works/pi-coding-agent": "*",
23
23
  "@earendil-works/pi-tui": "*"
24
24
  },
25
- "devDependencies": {
26
- "@types/node": "25.6.2",
27
- "vitest": "4.1.5"
25
+ "peerDependenciesMeta": {
26
+ "@earendil-works/pi-coding-agent": {
27
+ "optional": true
28
+ },
29
+ "@earendil-works/pi-tui": {
30
+ "optional": true
31
+ }
28
32
  },
29
33
  "main": "src/index.ts"
30
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-lsp",
3
- "version": "1.0.0",
3
+ "version": "1.1.3",
4
4
  "description": "SuPi LSP extension — Language Server Protocol integration for pi",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  "!__tests__"
22
22
  ],
23
23
  "dependencies": {
24
- "@mrclrchtr/supi-core": "workspace:*"
24
+ "@mrclrchtr/supi-core": "1.1.3"
25
25
  },
26
26
  "bundledDependencies": [
27
27
  "@mrclrchtr/supi-core"
@@ -32,9 +32,19 @@
32
32
  "@earendil-works/pi-tui": "*",
33
33
  "typebox": "*"
34
34
  },
35
- "devDependencies": {
36
- "vitest": "4.1.5",
37
- "@mrclrchtr/supi-test-utils": "workspace:*"
35
+ "peerDependenciesMeta": {
36
+ "@earendil-works/pi-ai": {
37
+ "optional": true
38
+ },
39
+ "@earendil-works/pi-coding-agent": {
40
+ "optional": true
41
+ },
42
+ "@earendil-works/pi-tui": {
43
+ "optional": true
44
+ },
45
+ "typebox": {
46
+ "optional": true
47
+ }
38
48
  },
39
49
  "pi": {
40
50
  "extensions": [
@@ -31,7 +31,7 @@ export function assessStaleDiagnostics(
31
31
  };
32
32
  }
33
33
 
34
- function isLikelyStaleDiagnostic(diagnostic: Diagnostic): boolean {
34
+ export function isLikelyStaleDiagnostic(diagnostic: Diagnostic): boolean {
35
35
  if (diagnostic.severity === undefined) return false;
36
36
  if (diagnostic.code !== undefined) {
37
37
  if (typeof diagnostic.code === "number" && MODULE_RESOLUTION_CODES.has(diagnostic.code)) {
package/src/lsp.ts CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  removeLspTool,
30
30
  } from "./lsp-state.ts";
31
31
  import { LspManager } from "./manager/manager.ts";
32
+ import { forceResyncStaleModuleFiles } from "./manager/manager-stale-resync.ts";
32
33
  import { registerLspAwareToolOverrides } from "./overrides.ts";
33
34
  import { registerLspMessageRenderer } from "./renderer.ts";
34
35
  import { scanMissingServers, scanProjectCapabilities, startDetectedServers } from "./scanner.ts";
@@ -305,6 +306,16 @@ function registerBehaviorHandlers(pi: ExtensionAPI, state: LspRuntimeState): voi
305
306
  // Refresh failures must not prevent agent startup
306
307
  }
307
308
  state.manager.pruneMissingFiles();
309
+
310
+ // Force re-open files with module-resolution errors to clear stale
311
+ // diagnostics that persist when the TS server caches by content hash.
312
+ // Must run before the diagnostic summary so fresh results are captured.
313
+ try {
314
+ await forceResyncStaleModuleFiles(state.manager, ctx.cwd);
315
+ } catch {
316
+ // Best-effort: don't fail the agent turn
317
+ }
318
+
308
319
  refreshProjectServers(state);
309
320
  updateLspUi(ctx, state.manager, state.inlineSeverity, state.projectServers);
310
321
 
@@ -0,0 +1,47 @@
1
+ import * as path from "node:path";
2
+ import { isLikelyStaleDiagnostic } from "../diagnostics/stale-diagnostics.ts";
3
+ import type { LspManager } from "./manager.ts";
4
+
5
+ /**
6
+ * Force re-open files with module-resolution errors (e.g., "Cannot find module")
7
+ * to trigger fresh analysis by the language server.
8
+ *
9
+ * The TypeScript server caches diagnostics by file content hash. When a new
10
+ * file is created that resolves an existing file's import, the stale diagnostic
11
+ * persists because the importing file's content hasn't changed. Re-opening the
12
+ * file (didClose + didOpen) forces the server to re-resolve all imports.
13
+ *
14
+ * Returns true if any files were re-synced.
15
+ */
16
+ export async function forceResyncStaleModuleFiles(
17
+ manager: LspManager,
18
+ cwd: string,
19
+ ): Promise<boolean> {
20
+ const outstanding = manager.getOutstandingDiagnostics(1);
21
+ const staleFiles: string[] = [];
22
+
23
+ for (const entry of outstanding) {
24
+ if (entry.diagnostics.some((d) => isLikelyStaleDiagnostic(d))) {
25
+ staleFiles.push(entry.file);
26
+ }
27
+ }
28
+
29
+ if (staleFiles.length === 0) return false;
30
+
31
+ for (const file of staleFiles) {
32
+ const filePath = path.resolve(cwd, file);
33
+ // Close the file to clear cached diagnostics and remove from openDocs
34
+ manager.closeFile(filePath);
35
+ // Re-open to force the server to re-resolve imports
36
+ await manager.ensureFileOpen(filePath);
37
+ }
38
+
39
+ // Re-sync and wait for fresh diagnostics after the re-opens
40
+ try {
41
+ await manager.refreshOpenDiagnostics({ quietMs: 300, maxWaitMs: 2000 });
42
+ } catch {
43
+ // Best-effort: don't fail the agent turn if refresh has issues
44
+ }
45
+
46
+ return true;
47
+ }
@@ -151,7 +151,7 @@ function buildLspSettingItems(
151
151
  id: "exclude",
152
152
  label: "Exclude Patterns",
153
153
  description:
154
- "Gitignore patterns to suppress LSP diagnostics (e.g. __tests__/, *.generated.ts)",
154
+ "Gitignore patterns to suppress LSP diagnostics. Edit .pi/supi/config.json → lsp.exclude (or ~/.pi/agent/supi/config.json for global). Patterns like __tests__/ exclude a directory, *.test.ts wildcards match at any depth, /dist anchors to root.",
155
155
  currentValue: settings.exclude.length > 0 ? settings.exclude.join(", ") : "none",
156
156
  submenu: (_currentValue, done) => createExcludeSubmenu(scope, cwd, settings, done),
157
157
  },