@mrclrchtr/supi-lsp 1.0.0 → 1.1.2
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.2",
|
|
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,5 @@
|
|
|
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"
|
|
28
|
-
},
|
|
29
25
|
"main": "src/index.ts"
|
|
30
26
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-lsp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
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": "
|
|
24
|
+
"@mrclrchtr/supi-core": "1.1.2"
|
|
25
25
|
},
|
|
26
26
|
"bundledDependencies": [
|
|
27
27
|
"@mrclrchtr/supi-core"
|
|
@@ -32,10 +32,6 @@
|
|
|
32
32
|
"@earendil-works/pi-tui": "*",
|
|
33
33
|
"typebox": "*"
|
|
34
34
|
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"vitest": "4.1.5",
|
|
37
|
-
"@mrclrchtr/supi-test-utils": "workspace:*"
|
|
38
|
-
},
|
|
39
35
|
"pi": {
|
|
40
36
|
"extensions": [
|
|
41
37
|
"./src/lsp.ts"
|
|
@@ -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 (
|
|
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
|
},
|