pi-lens 3.1.2 → 3.2.0
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/CHANGELOG.md +55 -0
- package/README.md +16 -12
- package/clients/ast-grep-client.js +8 -1
- package/clients/ast-grep-client.ts +9 -1
- package/clients/biome-client.js +51 -38
- package/clients/biome-client.ts +60 -58
- package/clients/dependency-checker.js +30 -1
- package/clients/dependency-checker.ts +35 -1
- package/clients/dispatch/__tests__/runner-registration.test.ts +286 -282
- package/clients/dispatch/bus-dispatcher.js +15 -14
- package/clients/dispatch/bus-dispatcher.ts +32 -25
- package/clients/dispatch/dispatcher.js +18 -25
- package/clients/dispatch/dispatcher.test.ts +2 -1
- package/clients/dispatch/dispatcher.ts +17 -28
- package/clients/dispatch/plan.js +77 -32
- package/clients/dispatch/plan.ts +78 -32
- package/clients/dispatch/runners/ast-grep-napi.js +36 -376
- package/clients/dispatch/runners/ast-grep-napi.ts +60 -433
- package/clients/dispatch/runners/index.js +8 -4
- package/clients/dispatch/runners/index.ts +8 -4
- package/clients/dispatch/runners/lsp.js +65 -0
- package/clients/dispatch/runners/lsp.ts +125 -0
- package/clients/dispatch/runners/oxlint.js +2 -2
- package/clients/dispatch/runners/oxlint.ts +2 -2
- package/clients/dispatch/runners/pyright.js +24 -8
- package/clients/dispatch/runners/pyright.ts +28 -14
- package/clients/dispatch/runners/rust-clippy.js +2 -2
- package/clients/dispatch/runners/rust-clippy.ts +2 -4
- package/clients/dispatch/runners/tree-sitter.js +14 -2
- package/clients/dispatch/runners/tree-sitter.ts +15 -2
- package/clients/dispatch/runners/ts-lsp.js +3 -3
- package/clients/dispatch/runners/ts-lsp.ts +8 -5
- package/clients/dispatch/runners/yaml-rule-parser.js +292 -0
- package/clients/dispatch/runners/yaml-rule-parser.ts +338 -0
- package/clients/dispatch/types.js +3 -0
- package/clients/dispatch/types.ts +3 -0
- package/clients/formatters.js +67 -14
- package/clients/formatters.ts +68 -15
- package/clients/installer/index.js +78 -10
- package/clients/installer/index.ts +519 -426
- package/clients/jscpd-client.js +28 -0
- package/clients/jscpd-client.ts +41 -3
- package/clients/knip-client.js +30 -1
- package/clients/knip-client.ts +34 -2
- package/clients/lsp/__tests__/client.test.ts +64 -41
- package/clients/lsp/__tests__/config.test.ts +25 -17
- package/clients/lsp/__tests__/launch.test.ts +108 -43
- package/clients/lsp/__tests__/service.test.ts +76 -48
- package/clients/lsp/client.js +87 -2
- package/clients/lsp/client.ts +150 -6
- package/clients/lsp/config.js +8 -11
- package/clients/lsp/config.ts +24 -21
- package/clients/lsp/index.js +69 -0
- package/clients/lsp/index.ts +82 -0
- package/clients/lsp/interactive-install.js +19 -8
- package/clients/lsp/interactive-install.ts +52 -27
- package/clients/lsp/launch.js +182 -32
- package/clients/lsp/launch.ts +241 -38
- package/clients/lsp/path-utils.js +3 -46
- package/clients/lsp/path-utils.ts +11 -51
- package/clients/lsp/server.js +93 -71
- package/clients/lsp/server.ts +173 -131
- package/clients/path-utils.js +142 -0
- package/clients/path-utils.ts +153 -0
- package/clients/ruff-client.js +33 -4
- package/clients/ruff-client.ts +44 -13
- package/clients/safe-spawn.js +3 -1
- package/clients/safe-spawn.ts +3 -1
- package/clients/services/effect-integration.js +11 -7
- package/clients/services/effect-integration.ts +34 -26
- package/clients/sg-runner.js +51 -9
- package/clients/sg-runner.ts +58 -15
- package/clients/tree-sitter-client.js +12 -0
- package/clients/tree-sitter-client.ts +12 -0
- package/clients/typescript-client.js +6 -2
- package/clients/typescript-client.ts +9 -2
- package/commands/booboo.js +2 -4
- package/commands/booboo.ts +2 -4
- package/index.ts +377 -93
- package/package.json +2 -1
- package/rules/tree-sitter-queries/tsx/no-nested-links.yml +45 -0
- package/rules/tree-sitter-queries/typescript/constructor-super.yml +55 -0
- package/rules/tree-sitter-queries/typescript/debugger.yml +1 -1
- package/rules/tree-sitter-queries/typescript/no-dupe-class-members.yml +47 -0
- package/tsconfig.json +1 -1
- package/clients/__tests__/file-time.test.js +0 -216
- package/clients/__tests__/format-service.test.js +0 -245
- package/clients/__tests__/formatters.test.js +0 -271
- package/clients/agent-behavior-client.test.js +0 -94
- package/clients/ast-grep-client.test.js +0 -129
- package/clients/ast-grep-client.test.ts +0 -155
- package/clients/biome-client.test.js +0 -144
- package/clients/cache-manager.test.js +0 -197
- package/clients/complexity-client.test.js +0 -234
- package/clients/dependency-checker.test.js +0 -60
- package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
- package/clients/dispatch/__tests__/runner-registration.test.js +0 -236
- package/clients/dispatch/dispatcher.edge.test.js +0 -82
- package/clients/dispatch/dispatcher.format.test.js +0 -46
- package/clients/dispatch/dispatcher.inline.test.js +0 -74
- package/clients/dispatch/dispatcher.test.js +0 -115
- package/clients/dispatch/runners/architect.test.js +0 -138
- package/clients/dispatch/runners/ast-grep-napi.test.js +0 -106
- package/clients/dispatch/runners/oxlint.test.js +0 -230
- package/clients/dispatch/runners/pyright.test.js +0 -98
- package/clients/dispatch/runners/python-slop.test.js +0 -203
- package/clients/dispatch/runners/scan_codebase.test.js +0 -89
- package/clients/dispatch/runners/shellcheck.test.js +0 -98
- package/clients/dispatch/runners/spellcheck.test.js +0 -158
- package/clients/dispatch/runners/ts-slop.test.js +0 -180
- package/clients/dispatch/runners/ts-slop.test.ts +0 -230
- package/clients/dogfood.test.js +0 -201
- package/clients/file-kinds.test.js +0 -169
- package/clients/go-client.test.js +0 -127
- package/clients/jscpd-client.test.js +0 -127
- package/clients/knip-client.test.js +0 -112
- package/clients/lsp/__tests__/client.test.js +0 -325
- package/clients/lsp/__tests__/config.test.js +0 -166
- package/clients/lsp/__tests__/error-recovery.test.js +0 -213
- package/clients/lsp/__tests__/integration.test.js +0 -127
- package/clients/lsp/__tests__/launch.test.js +0 -260
- package/clients/lsp/__tests__/server.test.js +0 -259
- package/clients/lsp/__tests__/service.test.js +0 -417
- package/clients/metrics-client.test.js +0 -141
- package/clients/ruff-client.test.js +0 -132
- package/clients/rust-client.test.js +0 -108
- package/clients/sanitize.test.js +0 -177
- package/clients/secrets-scanner.test.js +0 -100
- package/clients/services/__tests__/effect-integration.test.js +0 -86
- package/clients/test-runner-client.test.js +0 -192
- package/clients/todo-scanner.test.js +0 -301
- package/clients/type-coverage-client.test.js +0 -105
- package/clients/typescript-client.codefix.test.js +0 -157
- package/clients/typescript-client.test.js +0 -105
- package/commands/clients/ast-grep-client.js +0 -250
- package/commands/clients/ast-grep-parser.js +0 -86
- package/commands/clients/ast-grep-rule-manager.js +0 -91
- package/commands/clients/ast-grep-types.js +0 -9
- package/commands/clients/biome-client.js +0 -380
- package/commands/clients/complexity-client.js +0 -667
- package/commands/clients/file-kinds.js +0 -177
- package/commands/clients/file-utils.js +0 -40
- package/commands/clients/jscpd-client.js +0 -169
- package/commands/clients/knip-client.js +0 -211
- package/commands/clients/ruff-client.js +0 -297
- package/commands/clients/safe-spawn.js +0 -88
- package/commands/clients/scan-utils.js +0 -83
- package/commands/clients/sg-runner.js +0 -190
- package/commands/clients/types.js +0 -11
- package/commands/clients/typescript-client.js +0 -505
- package/commands/rate.test.js +0 -119
- package/rules/ast-grep-rules/rules/no-dangerously-set-inner-html.yml +0 -13
- package/rules/ast-grep-rules/rules/no-debugger.yml +0 -12
- package/rules/ast-grep-rules/rules/no-eval.yml +0 -13
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { registerRunner } from "../dispatcher.js";
|
|
5
5
|
import architectRunner from "./architect.js";
|
|
6
|
+
import astGrepNapiRunner from "./ast-grep-napi.js";
|
|
6
7
|
import biomeRunner from "./biome.js";
|
|
7
8
|
import configValidationRunner from "./config-validation.js";
|
|
8
9
|
import goVetRunner from "./go-vet.js";
|
|
10
|
+
import lspRunner from "./lsp.js";
|
|
9
11
|
import oxlintRunner from "./oxlint.js";
|
|
10
12
|
import pyrightRunner from "./pyright.js";
|
|
11
13
|
import pythonSlopRunner from "./python-slop.js";
|
|
@@ -21,11 +23,13 @@ import tsLspRunner from "./ts-lsp.js";
|
|
|
21
23
|
import tsSlopRunner from "./ts-slop.js";
|
|
22
24
|
import typeSafetyRunner from "./type-safety.js";
|
|
23
25
|
// Register all runners (ordered by priority)
|
|
24
|
-
|
|
25
|
-
registerRunner(
|
|
26
|
+
// Unified LSP runner for all languages (TypeScript, Python, Go, Rust, etc.) - priority 4
|
|
27
|
+
registerRunner(lspRunner); // Unified LSP type-checking for all languages (priority 4)
|
|
28
|
+
registerRunner(tsLspRunner); // TypeScript type-checking (priority 5) - fallback when --lens-lsp disabled
|
|
29
|
+
registerRunner(pyrightRunner); // Python type-checking (priority 5) - fallback when --lens-lsp disabled
|
|
26
30
|
registerRunner(configValidationRunner); // Config/env validation (priority 8)
|
|
27
|
-
// DISABLED
|
|
28
|
-
|
|
31
|
+
// DISABLED in post-write dispatch - ast-grep-napi can crash. Enabled via /lens-booboo plan only.
|
|
32
|
+
registerRunner(astGrepNapiRunner); // TS/JS structural analysis via NAPI (priority 15, post-write disabled)
|
|
29
33
|
registerRunner(biomeRunner); // Biome formatting/linting (priority 10)
|
|
30
34
|
registerRunner(oxlintRunner); // Oxlint fast JS/TS linter (priority 12)
|
|
31
35
|
registerRunner(treeSitterRunner); // Tree-sitter structural analysis (priority 14)
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import { registerRunner } from "../dispatcher.js";
|
|
6
6
|
import architectRunner from "./architect.js";
|
|
7
|
+
import astGrepNapiRunner from "./ast-grep-napi.js";
|
|
7
8
|
import biomeRunner from "./biome.js";
|
|
8
9
|
import configValidationRunner from "./config-validation.js";
|
|
9
10
|
import goVetRunner from "./go-vet.js";
|
|
11
|
+
import lspRunner from "./lsp.js";
|
|
10
12
|
import oxlintRunner from "./oxlint.js";
|
|
11
13
|
import pyrightRunner from "./pyright.js";
|
|
12
14
|
import pythonSlopRunner from "./python-slop.js";
|
|
@@ -23,11 +25,13 @@ import tsSlopRunner from "./ts-slop.js";
|
|
|
23
25
|
import typeSafetyRunner from "./type-safety.js";
|
|
24
26
|
|
|
25
27
|
// Register all runners (ordered by priority)
|
|
26
|
-
|
|
27
|
-
registerRunner(
|
|
28
|
+
// Unified LSP runner for all languages (TypeScript, Python, Go, Rust, etc.) - priority 4
|
|
29
|
+
registerRunner(lspRunner); // Unified LSP type-checking for all languages (priority 4)
|
|
30
|
+
registerRunner(tsLspRunner); // TypeScript type-checking (priority 5) - fallback when --lens-lsp disabled
|
|
31
|
+
registerRunner(pyrightRunner); // Python type-checking (priority 5) - fallback when --lens-lsp disabled
|
|
28
32
|
registerRunner(configValidationRunner); // Config/env validation (priority 8)
|
|
29
|
-
// DISABLED
|
|
30
|
-
|
|
33
|
+
// DISABLED in post-write dispatch - ast-grep-napi can crash. Enabled via /lens-booboo plan only.
|
|
34
|
+
registerRunner(astGrepNapiRunner); // TS/JS structural analysis via NAPI (priority 15, post-write disabled)
|
|
31
35
|
registerRunner(biomeRunner); // Biome formatting/linting (priority 10)
|
|
32
36
|
registerRunner(oxlintRunner); // Oxlint fast JS/TS linter (priority 12)
|
|
33
37
|
registerRunner(treeSitterRunner); // Tree-sitter structural analysis (priority 14)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified LSP Runner for pi-lens
|
|
3
|
+
*
|
|
4
|
+
* Handles type checking for ALL LSP-supported languages:
|
|
5
|
+
* - TypeScript/JavaScript (typescript-language-server)
|
|
6
|
+
* - Python (pyright/pylsp)
|
|
7
|
+
* - Go (gopls)
|
|
8
|
+
* - Rust (rust-analyzer)
|
|
9
|
+
* - Ruby, PHP, C#, Java, Kotlin, Swift, Dart, etc.
|
|
10
|
+
*
|
|
11
|
+
* Replaces language-specific runners (ts-lsp, pyright) with a single
|
|
12
|
+
* unified runner that delegates to the LSP service.
|
|
13
|
+
*/
|
|
14
|
+
import { getLSPService } from "../../lsp/index.js";
|
|
15
|
+
import { readFileContent } from "./utils.js";
|
|
16
|
+
const lspRunner = {
|
|
17
|
+
id: "lsp",
|
|
18
|
+
appliesTo: ["jsts", "python", "go", "rust"], // Core LSP languages
|
|
19
|
+
priority: 4, // Run before everything (even ts-lsp was priority 5)
|
|
20
|
+
enabledByDefault: true,
|
|
21
|
+
async run(ctx) {
|
|
22
|
+
// Only run if --lens-lsp flag is enabled
|
|
23
|
+
if (!ctx.pi.getFlag("lens-lsp")) {
|
|
24
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
25
|
+
}
|
|
26
|
+
const lspService = getLSPService();
|
|
27
|
+
// Check if we have LSP available for this file
|
|
28
|
+
const hasLSP = await lspService.hasLSP(ctx.filePath);
|
|
29
|
+
if (!hasLSP) {
|
|
30
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
31
|
+
}
|
|
32
|
+
// Read file content
|
|
33
|
+
const content = readFileContent(ctx.filePath);
|
|
34
|
+
if (!content) {
|
|
35
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
36
|
+
}
|
|
37
|
+
// Open file in LSP and get diagnostics
|
|
38
|
+
await lspService.openFile(ctx.filePath, content);
|
|
39
|
+
// getDiagnostics() internally calls waitForDiagnostics() with bus
|
|
40
|
+
// subscription + 150ms debounce + 3s timeout
|
|
41
|
+
const lspDiags = await lspService.getDiagnostics(ctx.filePath);
|
|
42
|
+
// Convert LSP diagnostics to our format
|
|
43
|
+
// Defensive: filter out malformed diagnostics that may lack range
|
|
44
|
+
const diagnostics = lspDiags
|
|
45
|
+
.filter((d) => d.range?.start?.line !== undefined)
|
|
46
|
+
.map((d) => ({
|
|
47
|
+
id: `lsp:${d.code ?? "unknown"}:${d.range.start.line}`,
|
|
48
|
+
message: d.message,
|
|
49
|
+
filePath: ctx.filePath,
|
|
50
|
+
line: d.range.start.line + 1,
|
|
51
|
+
column: d.range.start.character + 1,
|
|
52
|
+
severity: d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info",
|
|
53
|
+
semantic: d.severity === 1 ? "blocking" : "warning",
|
|
54
|
+
tool: "lsp",
|
|
55
|
+
code: String(d.code ?? ""),
|
|
56
|
+
}));
|
|
57
|
+
const hasErrors = diagnostics.some((d) => d.semantic === "blocking");
|
|
58
|
+
return {
|
|
59
|
+
status: hasErrors ? "failed" : "succeeded",
|
|
60
|
+
diagnostics,
|
|
61
|
+
semantic: hasErrors ? "blocking" : "warning",
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
export default lspRunner;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified LSP Runner for pi-lens
|
|
3
|
+
*
|
|
4
|
+
* Handles type checking for ALL LSP-supported languages:
|
|
5
|
+
* - TypeScript/JavaScript (typescript-language-server)
|
|
6
|
+
* - Python (pyright/pylsp)
|
|
7
|
+
* - Go (gopls)
|
|
8
|
+
* - Rust (rust-analyzer)
|
|
9
|
+
* - Ruby, PHP, C#, Java, Kotlin, Swift, Dart, etc.
|
|
10
|
+
*
|
|
11
|
+
* Replaces language-specific runners (ts-lsp, pyright) with a single
|
|
12
|
+
* unified runner that delegates to the LSP service.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { getLSPService } from "../../lsp/index.js";
|
|
16
|
+
import type {
|
|
17
|
+
Diagnostic,
|
|
18
|
+
DispatchContext,
|
|
19
|
+
RunnerDefinition,
|
|
20
|
+
RunnerResult,
|
|
21
|
+
} from "../types.js";
|
|
22
|
+
import { readFileContent } from "./utils.js";
|
|
23
|
+
|
|
24
|
+
const lspRunner: RunnerDefinition = {
|
|
25
|
+
id: "lsp",
|
|
26
|
+
appliesTo: ["jsts", "python", "go", "rust"], // Core LSP languages
|
|
27
|
+
priority: 4, // Run before everything (even ts-lsp was priority 5)
|
|
28
|
+
enabledByDefault: true,
|
|
29
|
+
|
|
30
|
+
async run(ctx: DispatchContext): Promise<RunnerResult> {
|
|
31
|
+
// Only run if --lens-lsp flag is enabled
|
|
32
|
+
if (!ctx.pi.getFlag("lens-lsp")) {
|
|
33
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const lspService = getLSPService();
|
|
37
|
+
|
|
38
|
+
// Check if we have LSP available for this file
|
|
39
|
+
const hasLSP = await lspService.hasLSP(ctx.filePath);
|
|
40
|
+
if (!hasLSP) {
|
|
41
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Read file content
|
|
45
|
+
const content = readFileContent(ctx.filePath);
|
|
46
|
+
if (!content) {
|
|
47
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Try to open file in LSP and get diagnostics
|
|
51
|
+
// If the server fails to spawn or crashes, this will be caught
|
|
52
|
+
let lspDiags: import("../../lsp/client.js").LSPDiagnostic[] = [];
|
|
53
|
+
let serverFailed = false;
|
|
54
|
+
let failureReason = "";
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
await lspService.openFile(ctx.filePath, content);
|
|
58
|
+
// getDiagnostics() internally calls waitForDiagnostics() with bus
|
|
59
|
+
// subscription + 150ms debounce + 3s timeout
|
|
60
|
+
lspDiags = await lspService.getDiagnostics(ctx.filePath);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
serverFailed = true;
|
|
63
|
+
failureReason = err instanceof Error ? err.message : String(err);
|
|
64
|
+
// Check if this is a server spawn/connection error
|
|
65
|
+
if (
|
|
66
|
+
failureReason.includes("spawn") ||
|
|
67
|
+
failureReason.includes("exited") ||
|
|
68
|
+
failureReason.includes("connection") ||
|
|
69
|
+
failureReason.includes("JSON RPC")
|
|
70
|
+
) {
|
|
71
|
+
// Mark this server as broken so we don't keep trying
|
|
72
|
+
console.error(
|
|
73
|
+
`[lsp-runner] LSP server failed for ${ctx.filePath}: ${failureReason}`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// If server failed to provide diagnostics, report as failed status
|
|
79
|
+
if (serverFailed) {
|
|
80
|
+
return {
|
|
81
|
+
status: "failed",
|
|
82
|
+
diagnostics: [
|
|
83
|
+
{
|
|
84
|
+
id: `lsp:server-error:0`,
|
|
85
|
+
message: `LSP server failed: ${failureReason}`,
|
|
86
|
+
filePath: ctx.filePath,
|
|
87
|
+
line: 1,
|
|
88
|
+
column: 1,
|
|
89
|
+
severity: "error",
|
|
90
|
+
semantic: "warning", // Don't block - fallback to other runners
|
|
91
|
+
tool: "lsp",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
semantic: "warning",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Convert LSP diagnostics to our format
|
|
99
|
+
// Defensive: filter out malformed diagnostics that may lack range
|
|
100
|
+
const diagnostics: Diagnostic[] = lspDiags
|
|
101
|
+
.filter((d) => d.range?.start?.line !== undefined)
|
|
102
|
+
.map((d) => ({
|
|
103
|
+
id: `lsp:${d.code ?? "unknown"}:${d.range.start.line}`,
|
|
104
|
+
message: d.message,
|
|
105
|
+
filePath: ctx.filePath,
|
|
106
|
+
line: d.range.start.line + 1,
|
|
107
|
+
column: d.range.start.character + 1,
|
|
108
|
+
severity:
|
|
109
|
+
d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info",
|
|
110
|
+
semantic: d.severity === 1 ? "blocking" : "warning",
|
|
111
|
+
tool: "lsp",
|
|
112
|
+
code: String(d.code ?? ""),
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
const hasErrors = diagnostics.some((d) => d.semantic === "blocking");
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
status: hasErrors ? "failed" : "succeeded",
|
|
119
|
+
diagnostics,
|
|
120
|
+
semantic: hasErrors ? "blocking" : "warning",
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export default lspRunner;
|
|
@@ -13,8 +13,8 @@ const oxlintRunner = {
|
|
|
13
13
|
id: "oxlint",
|
|
14
14
|
appliesTo: ["jsts"],
|
|
15
15
|
priority: 12,
|
|
16
|
-
enabledByDefault:
|
|
17
|
-
skipTestFiles:
|
|
16
|
+
enabledByDefault: false, // Opt-in: may conflict with ESLint in existing projects
|
|
17
|
+
skipTestFiles: true,
|
|
18
18
|
async run(ctx) {
|
|
19
19
|
const cwd = ctx.cwd || process.cwd();
|
|
20
20
|
// Check if oxlint is available
|
|
@@ -22,8 +22,8 @@ const oxlintRunner: RunnerDefinition = {
|
|
|
22
22
|
id: "oxlint",
|
|
23
23
|
appliesTo: ["jsts"],
|
|
24
24
|
priority: 12,
|
|
25
|
-
enabledByDefault:
|
|
26
|
-
skipTestFiles:
|
|
25
|
+
enabledByDefault: false, // Opt-in: may conflict with ESLint in existing projects
|
|
26
|
+
skipTestFiles: true,
|
|
27
27
|
|
|
28
28
|
async run(ctx: DispatchContext): Promise<RunnerResult> {
|
|
29
29
|
const cwd = ctx.cwd || process.cwd();
|
|
@@ -17,15 +17,31 @@ const pyrightRunner = {
|
|
|
17
17
|
enabledByDefault: true,
|
|
18
18
|
async run(ctx) {
|
|
19
19
|
const cwd = ctx.cwd || process.cwd();
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
// Get pyright command - try multiple strategies
|
|
21
|
+
let cmd = null;
|
|
22
|
+
// Strategy 1: Check cached availability (fast path)
|
|
23
|
+
if (pyright.isAvailable(cwd)) {
|
|
24
|
+
cmd = pyright.getCommand();
|
|
25
|
+
}
|
|
26
|
+
// Strategy 2: Try to find pyright via ensureTool (installs if needed)
|
|
27
|
+
if (!cmd) {
|
|
28
|
+
const installedPath = await ensureTool("pyright");
|
|
29
|
+
if (installedPath)
|
|
30
|
+
cmd = installedPath;
|
|
31
|
+
}
|
|
32
|
+
// Strategy 3: Direct PATH check (handles module cache staleness)
|
|
33
|
+
if (!cmd) {
|
|
34
|
+
const { findCommandAsync } = await import("../../safe-spawn.js");
|
|
35
|
+
const foundCmd = await findCommandAsync("pyright");
|
|
36
|
+
if (foundCmd)
|
|
37
|
+
cmd = foundCmd;
|
|
38
|
+
}
|
|
39
|
+
// If still no pyright, skip this runner
|
|
40
|
+
if (!cmd) {
|
|
41
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
26
42
|
}
|
|
27
|
-
// Run pyright with JSON output
|
|
28
|
-
const result = await safeSpawnAsync(
|
|
43
|
+
// Run pyright with JSON output
|
|
44
|
+
const result = await safeSpawnAsync(cmd, ["--outputjson", ctx.filePath], {
|
|
29
45
|
timeout: 60000,
|
|
30
46
|
});
|
|
31
47
|
// Pyright returns non-zero when errors found, that's OK
|
|
@@ -28,22 +28,36 @@ const pyrightRunner: RunnerDefinition = {
|
|
|
28
28
|
async run(ctx: DispatchContext): Promise<RunnerResult> {
|
|
29
29
|
const cwd = ctx.cwd || process.cwd();
|
|
30
30
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
// Get pyright command - try multiple strategies
|
|
32
|
+
let cmd: string | null = null;
|
|
33
|
+
|
|
34
|
+
// Strategy 1: Check cached availability (fast path)
|
|
35
|
+
if (pyright.isAvailable(cwd)) {
|
|
36
|
+
cmd = pyright.getCommand();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Strategy 2: Try to find pyright via ensureTool (installs if needed)
|
|
40
|
+
if (!cmd) {
|
|
41
|
+
const installedPath = await ensureTool("pyright");
|
|
42
|
+
if (installedPath) cmd = installedPath;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Strategy 3: Direct PATH check (handles module cache staleness)
|
|
46
|
+
if (!cmd) {
|
|
47
|
+
const { findCommandAsync } = await import("../../safe-spawn.js");
|
|
48
|
+
const foundCmd: string | null = await findCommandAsync("pyright");
|
|
49
|
+
if (foundCmd) cmd = foundCmd;
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
// If still no pyright, skip this runner
|
|
53
|
+
if (!cmd) {
|
|
54
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Run pyright with JSON output
|
|
58
|
+
const result = await safeSpawnAsync(cmd, ["--outputjson", ctx.filePath], {
|
|
59
|
+
timeout: 60000,
|
|
60
|
+
});
|
|
47
61
|
|
|
48
62
|
// Pyright returns non-zero when errors found, that's OK
|
|
49
63
|
if (result.error) {
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Runs `cargo clippy` for Rust files to catch common mistakes.
|
|
5
5
|
*/
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
6
8
|
import { safeSpawn } from "../../safe-spawn.js";
|
|
7
9
|
import { stripAnsi } from "../../sanitize.js";
|
|
8
10
|
const rustClippyRunner = {
|
|
@@ -51,8 +53,6 @@ const rustClippyRunner = {
|
|
|
51
53
|
},
|
|
52
54
|
};
|
|
53
55
|
function findCargoToml(filePath) {
|
|
54
|
-
const { dirname, join } = require("node:path");
|
|
55
|
-
const { existsSync } = require("node:fs");
|
|
56
56
|
let dir = dirname(filePath);
|
|
57
57
|
while (dir !== "/" && dir !== ".") {
|
|
58
58
|
const cargoPath = join(dir, "Cargo.toml");
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Runs `cargo clippy` for Rust files to catch common mistakes.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
8
9
|
import { safeSpawn } from "../../safe-spawn.js";
|
|
9
10
|
import { stripAnsi } from "../../sanitize.js";
|
|
10
11
|
import type {
|
|
@@ -74,9 +75,6 @@ const rustClippyRunner: RunnerDefinition = {
|
|
|
74
75
|
};
|
|
75
76
|
|
|
76
77
|
function findCargoToml(filePath: string): string | undefined {
|
|
77
|
-
const { dirname, join } = require("node:path");
|
|
78
|
-
const { existsSync } = require("node:fs");
|
|
79
|
-
|
|
80
78
|
let dir = dirname(filePath);
|
|
81
79
|
while (dir !== "/" && dir !== ".") {
|
|
82
80
|
const cargoPath = join(dir, "Cargo.toml");
|
|
@@ -3,10 +3,21 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Executes all loaded tree-sitter query files from rules/tree-sitter-queries/
|
|
5
5
|
* for fast AST-based pattern matching.
|
|
6
|
+
* Updated: ast-grep-napi test
|
|
6
7
|
*/
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
import { TreeSitterClient } from "../../tree-sitter-client.js";
|
|
9
10
|
import { queryLoader } from "../../tree-sitter-query-loader.js";
|
|
11
|
+
// Module-level singleton: web-tree-sitter WASM must only be initialized once per process.
|
|
12
|
+
// Creating a new TreeSitterClient() on every write resets TRANSFER_BUFFER (a module-level
|
|
13
|
+
// WASM pointer) — concurrent writes race on _ts_init() and corrupt shared WASM state → crash.
|
|
14
|
+
let _sharedClient = null;
|
|
15
|
+
function getSharedClient() {
|
|
16
|
+
if (!_sharedClient) {
|
|
17
|
+
_sharedClient = new TreeSitterClient();
|
|
18
|
+
}
|
|
19
|
+
return _sharedClient;
|
|
20
|
+
}
|
|
10
21
|
const treeSitterRunner = {
|
|
11
22
|
id: "tree-sitter",
|
|
12
23
|
appliesTo: ["jsts", "python"],
|
|
@@ -14,8 +25,8 @@ const treeSitterRunner = {
|
|
|
14
25
|
enabledByDefault: true,
|
|
15
26
|
skipTestFiles: false, // Run on test files too (structural issues matter there)
|
|
16
27
|
async run(ctx) {
|
|
17
|
-
//
|
|
18
|
-
const client =
|
|
28
|
+
// Use singleton client — WASM must never be re-initialized after first call
|
|
29
|
+
const client = getSharedClient();
|
|
19
30
|
if (!client.isAvailable()) {
|
|
20
31
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
21
32
|
}
|
|
@@ -105,3 +116,4 @@ const treeSitterRunner = {
|
|
|
105
116
|
},
|
|
106
117
|
};
|
|
107
118
|
export default treeSitterRunner;
|
|
119
|
+
// test ast-grep-napi re-enable
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Executes all loaded tree-sitter query files from rules/tree-sitter-queries/
|
|
5
5
|
* for fast AST-based pattern matching.
|
|
6
|
+
* Updated: ast-grep-napi test
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import path from "node:path";
|
|
@@ -15,6 +16,17 @@ import type {
|
|
|
15
16
|
RunnerResult,
|
|
16
17
|
} from "../types.js";
|
|
17
18
|
|
|
19
|
+
// Module-level singleton: web-tree-sitter WASM must only be initialized once per process.
|
|
20
|
+
// Creating a new TreeSitterClient() on every write resets TRANSFER_BUFFER (a module-level
|
|
21
|
+
// WASM pointer) — concurrent writes race on _ts_init() and corrupt shared WASM state → crash.
|
|
22
|
+
let _sharedClient: TreeSitterClient | null = null;
|
|
23
|
+
function getSharedClient(): TreeSitterClient {
|
|
24
|
+
if (!_sharedClient) {
|
|
25
|
+
_sharedClient = new TreeSitterClient();
|
|
26
|
+
}
|
|
27
|
+
return _sharedClient;
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
const treeSitterRunner: RunnerDefinition = {
|
|
19
31
|
id: "tree-sitter",
|
|
20
32
|
appliesTo: ["jsts", "python"],
|
|
@@ -23,8 +35,8 @@ const treeSitterRunner: RunnerDefinition = {
|
|
|
23
35
|
skipTestFiles: false, // Run on test files too (structural issues matter there)
|
|
24
36
|
|
|
25
37
|
async run(ctx: DispatchContext): Promise<RunnerResult> {
|
|
26
|
-
//
|
|
27
|
-
const client =
|
|
38
|
+
// Use singleton client — WASM must never be re-initialized after first call
|
|
39
|
+
const client = getSharedClient();
|
|
28
40
|
if (!client.isAvailable()) {
|
|
29
41
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
30
42
|
}
|
|
@@ -133,3 +145,4 @@ const treeSitterRunner: RunnerDefinition = {
|
|
|
133
145
|
};
|
|
134
146
|
|
|
135
147
|
export default treeSitterRunner;
|
|
148
|
+
// test ast-grep-napi re-enable
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @deprecated The built-in TypeScriptClient is deprecated. Use --lens-lsp for full LSP support.
|
|
8
8
|
*/
|
|
9
|
-
import { TypeScriptClient } from "../../typescript-client.js";
|
|
10
9
|
import { getLSPService } from "../../lsp/index.js";
|
|
10
|
+
import { TypeScriptClient } from "../../typescript-client.js";
|
|
11
11
|
import { readFileContent } from "./utils.js";
|
|
12
12
|
const tsLspRunner = {
|
|
13
13
|
id: "ts-lsp",
|
|
@@ -45,8 +45,8 @@ async function runWithLSPClient(ctx) {
|
|
|
45
45
|
}
|
|
46
46
|
// Open file in LSP and get diagnostics
|
|
47
47
|
await lspService.openFile(ctx.filePath, content);
|
|
48
|
-
//
|
|
49
|
-
|
|
48
|
+
// getDiagnostics() internally calls waitForDiagnostics() with bus
|
|
49
|
+
// subscription + 150ms debounce + 3s timeout
|
|
50
50
|
const lspDiags = await lspService.getDiagnostics(ctx.filePath);
|
|
51
51
|
// Convert LSP diagnostics to our format
|
|
52
52
|
// Defensive: filter out malformed diagnostics that may lack range
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* @deprecated The built-in TypeScriptClient is deprecated. Use --lens-lsp for full LSP support.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { TypeScriptClient } from "../../typescript-client.js";
|
|
11
10
|
import { getLSPService } from "../../lsp/index.js";
|
|
11
|
+
import { TypeScriptClient } from "../../typescript-client.js";
|
|
12
12
|
import type {
|
|
13
13
|
Diagnostic,
|
|
14
14
|
DispatchContext,
|
|
@@ -60,8 +60,8 @@ async function runWithLSPClient(ctx: DispatchContext): Promise<RunnerResult> {
|
|
|
60
60
|
|
|
61
61
|
// Open file in LSP and get diagnostics
|
|
62
62
|
await lspService.openFile(ctx.filePath, content);
|
|
63
|
-
//
|
|
64
|
-
|
|
63
|
+
// getDiagnostics() internally calls waitForDiagnostics() with bus
|
|
64
|
+
// subscription + 150ms debounce + 3s timeout
|
|
65
65
|
const lspDiags = await lspService.getDiagnostics(ctx.filePath);
|
|
66
66
|
|
|
67
67
|
// Convert LSP diagnostics to our format
|
|
@@ -74,7 +74,8 @@ async function runWithLSPClient(ctx: DispatchContext): Promise<RunnerResult> {
|
|
|
74
74
|
filePath: ctx.filePath,
|
|
75
75
|
line: d.range.start.line + 1,
|
|
76
76
|
column: d.range.start.character + 1,
|
|
77
|
-
severity:
|
|
77
|
+
severity:
|
|
78
|
+
d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info",
|
|
78
79
|
semantic: d.severity === 1 ? "blocking" : "warning",
|
|
79
80
|
tool: "ts-lsp",
|
|
80
81
|
code: String(d.code ?? ""),
|
|
@@ -91,7 +92,9 @@ async function runWithLSPClient(ctx: DispatchContext): Promise<RunnerResult> {
|
|
|
91
92
|
* Run with deprecated built-in TypeScriptClient
|
|
92
93
|
* @deprecated Use runWithLSPClient instead
|
|
93
94
|
*/
|
|
94
|
-
async function runWithBuiltinClient(
|
|
95
|
+
async function runWithBuiltinClient(
|
|
96
|
+
ctx: DispatchContext,
|
|
97
|
+
): Promise<RunnerResult> {
|
|
95
98
|
const tsClient = new TypeScriptClient();
|
|
96
99
|
|
|
97
100
|
const content = readFileContent(ctx.filePath);
|