pi-lens 2.0.7 → 2.0.8

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.
Files changed (34) hide show
  1. package/clients/ast-grep-client.test.ts +146 -116
  2. package/clients/ast-grep-client.ts +645 -551
  3. package/clients/biome-client.test.ts +154 -137
  4. package/clients/biome-client.ts +397 -337
  5. package/clients/complexity-client.test.ts +188 -200
  6. package/clients/complexity-client.ts +815 -667
  7. package/clients/dependency-checker.test.ts +55 -55
  8. package/clients/dependency-checker.ts +358 -333
  9. package/clients/go-client.test.ts +121 -111
  10. package/clients/go-client.ts +218 -216
  11. package/clients/jscpd-client.test.ts +132 -132
  12. package/clients/jscpd-client.ts +155 -118
  13. package/clients/knip-client.test.ts +123 -133
  14. package/clients/knip-client.ts +231 -218
  15. package/clients/metrics-client.test.ts +171 -167
  16. package/clients/metrics-client.ts +283 -252
  17. package/clients/ruff-client.test.ts +128 -117
  18. package/clients/ruff-client.ts +300 -269
  19. package/clients/rust-client.test.ts +104 -85
  20. package/clients/rust-client.ts +241 -234
  21. package/clients/subprocess-client.ts +1 -1
  22. package/clients/test-runner-client.test.ts +248 -215
  23. package/clients/test-runner-client.ts +728 -608
  24. package/clients/test-utils.ts +10 -3
  25. package/clients/todo-scanner.test.ts +288 -202
  26. package/clients/todo-scanner.ts +225 -187
  27. package/clients/type-coverage-client.test.ts +119 -119
  28. package/clients/type-coverage-client.ts +142 -115
  29. package/clients/types.ts +28 -28
  30. package/clients/typescript-client.test.ts +99 -93
  31. package/clients/typescript-client.ts +527 -502
  32. package/index.ts +662 -212
  33. package/package.json +1 -1
  34. package/tsconfig.json +12 -12
@@ -8,238 +8,240 @@
8
8
  */
9
9
 
10
10
  import { spawnSync } from "node:child_process";
11
- import * as path from "node:path";
12
11
  import * as fs from "node:fs";
12
+ import * as path from "node:path";
13
13
 
14
14
  // --- Types ---
15
15
 
16
16
  export interface GoDiagnostic {
17
- line: number;
18
- column: number;
19
- endLine: number;
20
- endColumn: number;
21
- severity: "error" | "warning" | "info";
22
- message: string;
23
- rule?: string;
24
- file: string;
17
+ line: number;
18
+ column: number;
19
+ endLine: number;
20
+ endColumn: number;
21
+ severity: "error" | "warning" | "info";
22
+ message: string;
23
+ rule?: string;
24
+ file: string;
25
25
  }
26
26
 
27
27
  // --- Common install paths ---
28
28
 
29
29
  const GO_WINDOWS_PATHS = [
30
- "C:\\Program Files\\Go\\bin\\go.exe",
31
- "C:\\Go\\bin\\go.exe",
32
- "go.exe", // PATH
30
+ "C:\\Program Files\\Go\\bin\\go.exe",
31
+ "C:\\Go\\bin\\go.exe",
32
+ "go.exe", // PATH
33
33
  ];
34
34
 
35
35
  const GO_UNIX_PATHS = [
36
- "/usr/local/go/bin/go",
37
- "/usr/bin/go",
38
- "go", // PATH
36
+ "/usr/local/go/bin/go",
37
+ "/usr/bin/go",
38
+ "go", // PATH
39
39
  ];
40
40
 
41
41
  // --- Client ---
42
42
 
43
43
  export class GoClient {
44
- private goplsAvailable: boolean | null = null;
45
- private goAvailable: boolean | null = null;
46
- private goPath: string | null = null;
47
- private log: (msg: string) => void;
48
-
49
- constructor(verbose = false) {
50
- this.log = verbose
51
- ? (msg: string) => console.log(`[go] ${msg}`)
52
- : () => {};
53
- }
54
-
55
- /**
56
- * Find go executable path
57
- */
58
- private findGoPath(): string | null {
59
- if (this.goPath) return this.goPath;
60
-
61
- const paths = process.platform === "win32" ? GO_WINDOWS_PATHS : GO_UNIX_PATHS;
62
-
63
- for (const p of paths) {
64
- try {
65
- if (p.includes("\\") || p.includes("/")) {
66
- // Absolute path - check if exists
67
- if (fs.existsSync(p)) {
68
- this.goPath = p;
69
- return p;
70
- }
71
- } else {
72
- // Relative (PATH) - try running it
73
- const result = spawnSync(p, ["version"], {
74
- encoding: "utf-8",
75
- timeout: 3000,
76
- shell: true,
77
- });
78
- if (!result.error && result.status === 0) {
79
- this.goPath = p;
80
- return p;
81
- }
82
- }
83
- } catch {}
84
- }
85
-
86
- return null;
87
- }
88
-
89
- /**
90
- * Check if Go is installed
91
- */
92
- isGoAvailable(): boolean {
93
- if (this.goAvailable !== null) return this.goAvailable;
94
- this.goAvailable = this.findGoPath() !== null;
95
- if (this.goAvailable) {
96
- this.log(`Go found: ${this.goPath}`);
97
- }
98
- return this.goAvailable;
99
- }
100
-
101
- /**
102
- * Check if gopls is installed
103
- */
104
- isGoplsAvailable(): boolean {
105
- if (this.goplsAvailable !== null) return this.goplsAvailable;
106
-
107
- const result = spawnSync("gopls", ["version"], {
108
- encoding: "utf-8",
109
- timeout: 5000,
110
- shell: true,
111
- });
112
-
113
- this.goplsAvailable = !result.error && result.status === 0;
114
- if (this.goplsAvailable) {
115
- this.log(`gopls found: ${result.stdout?.trim()}`);
116
- }
117
- return this.goplsAvailable;
118
- }
119
-
120
- /**
121
- * Check if a file is a Go file
122
- */
123
- isGoFile(filePath: string): boolean {
124
- return path.extname(filePath).toLowerCase() === ".go";
125
- }
126
-
127
- /**
128
- * Run go vet on a file and return diagnostics
129
- */
130
- checkFile(filePath: string): GoDiagnostic[] {
131
- const goExe = this.findGoPath();
132
- if (!goExe) return [];
133
-
134
- const absolutePath = path.resolve(filePath);
135
- if (!fs.existsSync(absolutePath)) return [];
136
-
137
- const dir = path.dirname(absolutePath);
138
- const fileName = path.basename(absolutePath);
139
-
140
- try {
141
- // Run go vet on the specific file
142
- const goCmd = goExe.includes(" ") ? `"${goExe}"` : goExe;
143
- const result = spawnSync(goCmd, ["vet", fileName], {
144
- encoding: "utf-8",
145
- timeout: 15000,
146
- cwd: dir,
147
- shell: true,
148
- });
149
-
150
- const output = (result.stderr || "") + (result.stdout || "");
151
- return this.parseOutput(output, absolutePath);
152
- } catch (err: any) {
153
- this.log(`Check error: ${err.message}`);
154
- return [];
155
- }
156
- }
157
-
158
- /**
159
- * Run go build to check for compilation errors
160
- */
161
- buildCheck(cwd: string): GoDiagnostic[] {
162
- if (!this.isGoAvailable()) return [];
163
-
164
- try {
165
- const result = spawnSync("go", ["build", "./..."], {
166
- encoding: "utf-8",
167
- timeout: 30000,
168
- cwd,
169
- shell: true,
170
- });
171
-
172
- const output = (result.stderr || "") + (result.stdout || "");
173
- return this.parseOutput(output, cwd);
174
- } catch (err: any) {
175
- this.log(`Build check error: ${err.message}`);
176
- return [];
177
- }
178
- }
179
-
180
- /**
181
- * Format diagnostics for LLM consumption
182
- */
183
- formatDiagnostics(diags: GoDiagnostic[], maxItems = 10): string {
184
- if (diags.length === 0) return "";
185
-
186
- const errors = diags.filter((d) => d.severity === "error");
187
- const warnings = diags.filter((d) => d.severity === "warning");
188
-
189
- let output = `[Go] ${diags.length} issue(s)`;
190
- if (errors.length) output += ` — ${errors.length} error(s)`;
191
- if (warnings.length) output += ` — ${warnings.length} warning(s)`;
192
- output += ":\n";
193
-
194
- for (const d of diags.slice(0, maxItems)) {
195
- const loc = `L${d.line}:${d.column}`;
196
- const rule = d.rule ? ` [${d.rule}]` : "";
197
- output += ` [${d.severity}] ${loc} ${d.message}${rule}\n`;
198
- }
199
-
200
- if (diags.length > maxItems) {
201
- output += ` ... and ${diags.length - maxItems} more\n`;
202
- }
203
-
204
- return output;
205
- }
206
-
207
- // --- Internal ---
208
-
209
- private parseOutput(output: string, fileOrDir: string): GoDiagnostic[] {
210
- if (!output.trim()) return [];
211
-
212
- const diags: GoDiagnostic[] = [];
213
- // Go vet/build output format: "file.go:line:col: message"
214
- const pattern = /^(.+?):(\d+):(?:(\d+):)?\s*(?:([^:]+):\s*)?(.+)$/gm;
215
- let match;
216
-
217
- while ((match = pattern.exec(output)) !== null) {
218
- const [, file, line, col, rule, message] = match;
219
- const lineNum = parseInt(line, 10);
220
- const colNum = col ? parseInt(col, 10) : 1;
221
-
222
- // Filter to the specific file if a file path was provided
223
- const absFile = path.isAbsolute(file) ? file : path.resolve(path.dirname(fileOrDir), file);
224
- if (path.extname(absFile) !== ".go") continue;
225
-
226
- const isError = message.includes("undefined") ||
227
- message.includes("cannot") ||
228
- message.includes("syntax error") ||
229
- rule === "compile";
230
-
231
- diags.push({
232
- line: lineNum,
233
- column: colNum - 1,
234
- endLine: lineNum,
235
- endColumn: colNum,
236
- severity: isError ? "error" : "warning",
237
- message: message.trim().slice(0, 300),
238
- rule: rule?.trim(),
239
- file: absFile,
240
- });
241
- }
242
-
243
- return diags;
244
- }
44
+ private goplsAvailable: boolean | null = null;
45
+ private goAvailable: boolean | null = null;
46
+ private goPath: string | null = null;
47
+ private log: (msg: string) => void;
48
+
49
+ constructor(verbose = false) {
50
+ this.log = verbose ? (msg: string) => console.log(`[go] ${msg}`) : () => {};
51
+ }
52
+
53
+ /**
54
+ * Find go executable path
55
+ */
56
+ private findGoPath(): string | null {
57
+ if (this.goPath) return this.goPath;
58
+
59
+ const paths =
60
+ process.platform === "win32" ? GO_WINDOWS_PATHS : GO_UNIX_PATHS;
61
+
62
+ for (const p of paths) {
63
+ try {
64
+ if (p.includes("\\") || p.includes("/")) {
65
+ // Absolute path - check if exists
66
+ if (fs.existsSync(p)) {
67
+ this.goPath = p;
68
+ return p;
69
+ }
70
+ } else {
71
+ // Relative (PATH) - try running it
72
+ const result = spawnSync(p, ["version"], {
73
+ encoding: "utf-8",
74
+ timeout: 3000,
75
+ shell: true,
76
+ });
77
+ if (!result.error && result.status === 0) {
78
+ this.goPath = p;
79
+ return p;
80
+ }
81
+ }
82
+ } catch (err) { void err; }
83
+ }
84
+
85
+ return null;
86
+ }
87
+
88
+ /**
89
+ * Check if Go is installed
90
+ */
91
+ isGoAvailable(): boolean {
92
+ if (this.goAvailable !== null) return this.goAvailable;
93
+ this.goAvailable = this.findGoPath() !== null;
94
+ if (this.goAvailable) {
95
+ this.log(`Go found: ${this.goPath}`);
96
+ }
97
+ return this.goAvailable;
98
+ }
99
+
100
+ /**
101
+ * Check if gopls is installed
102
+ */
103
+ isGoplsAvailable(): boolean {
104
+ if (this.goplsAvailable !== null) return this.goplsAvailable;
105
+
106
+ const result = spawnSync("gopls", ["version"], {
107
+ encoding: "utf-8",
108
+ timeout: 5000,
109
+ shell: true,
110
+ });
111
+
112
+ this.goplsAvailable = !result.error && result.status === 0;
113
+ if (this.goplsAvailable) {
114
+ this.log(`gopls found: ${result.stdout?.trim()}`);
115
+ }
116
+ return this.goplsAvailable;
117
+ }
118
+
119
+ /**
120
+ * Check if a file is a Go file
121
+ */
122
+ isGoFile(filePath: string): boolean {
123
+ return path.extname(filePath).toLowerCase() === ".go";
124
+ }
125
+
126
+ /**
127
+ * Run go vet on a file and return diagnostics
128
+ */
129
+ checkFile(filePath: string): GoDiagnostic[] {
130
+ const goExe = this.findGoPath();
131
+ if (!goExe) return [];
132
+
133
+ const absolutePath = path.resolve(filePath);
134
+ if (!fs.existsSync(absolutePath)) return [];
135
+
136
+ const dir = path.dirname(absolutePath);
137
+ const fileName = path.basename(absolutePath);
138
+
139
+ try {
140
+ // Run go vet on the specific file
141
+ const goCmd = goExe.includes(" ") ? `"${goExe}"` : goExe;
142
+ const result = spawnSync(goCmd, ["vet", fileName], {
143
+ encoding: "utf-8",
144
+ timeout: 15000,
145
+ cwd: dir,
146
+ shell: true,
147
+ });
148
+
149
+ const output = (result.stderr || "") + (result.stdout || "");
150
+ return this.parseOutput(output, absolutePath);
151
+ } catch (err: any) {
152
+ this.log(`Check error: ${err.message}`);
153
+ return [];
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Run go build to check for compilation errors
159
+ */
160
+ buildCheck(cwd: string): GoDiagnostic[] {
161
+ if (!this.isGoAvailable()) return [];
162
+
163
+ try {
164
+ const result = spawnSync("go", ["build", "./..."], {
165
+ encoding: "utf-8",
166
+ timeout: 30000,
167
+ cwd,
168
+ shell: true,
169
+ });
170
+
171
+ const output = (result.stderr || "") + (result.stdout || "");
172
+ return this.parseOutput(output, cwd);
173
+ } catch (err: any) {
174
+ this.log(`Build check error: ${err.message}`);
175
+ return [];
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Format diagnostics for LLM consumption
181
+ */
182
+ formatDiagnostics(diags: GoDiagnostic[], maxItems = 10): string {
183
+ if (diags.length === 0) return "";
184
+
185
+ const errors = diags.filter((d) => d.severity === "error");
186
+ const warnings = diags.filter((d) => d.severity === "warning");
187
+
188
+ let output = `[Go] ${diags.length} issue(s)`;
189
+ if (errors.length) output += ` ${errors.length} error(s)`;
190
+ if (warnings.length) output += ` — ${warnings.length} warning(s)`;
191
+ output += ":\n";
192
+
193
+ for (const d of diags.slice(0, maxItems)) {
194
+ const loc = `L${d.line}:${d.column}`;
195
+ const rule = d.rule ? ` [${d.rule}]` : "";
196
+ output += ` [${d.severity}] ${loc} ${d.message}${rule}\n`;
197
+ }
198
+
199
+ if (diags.length > maxItems) {
200
+ output += ` ... and ${diags.length - maxItems} more\n`;
201
+ }
202
+
203
+ return output;
204
+ }
205
+
206
+ // --- Internal ---
207
+
208
+ private parseOutput(output: string, fileOrDir: string): GoDiagnostic[] {
209
+ if (!output.trim()) return [];
210
+
211
+ const diags: GoDiagnostic[] = [];
212
+ // Go vet/build output format: "file.go:line:col: message"
213
+ const pattern = /^(.+?):(\d+):(?:(\d+):)?\s*(?:([^:]+):\s*)?(.+)$/gm;
214
+ let match;
215
+
216
+ while ((match = pattern.exec(output)) !== null) {
217
+ const [, file, line, col, rule, message] = match;
218
+ const lineNum = parseInt(line, 10);
219
+ const colNum = col ? parseInt(col, 10) : 1;
220
+
221
+ // Filter to the specific file if a file path was provided
222
+ const absFile = path.isAbsolute(file)
223
+ ? file
224
+ : path.resolve(path.dirname(fileOrDir), file);
225
+ if (path.extname(absFile) !== ".go") continue;
226
+
227
+ const isError =
228
+ message.includes("undefined") ||
229
+ message.includes("cannot") ||
230
+ message.includes("syntax error") ||
231
+ rule === "compile";
232
+
233
+ diags.push({
234
+ line: lineNum,
235
+ column: colNum - 1,
236
+ endLine: lineNum,
237
+ endColumn: colNum,
238
+ severity: isError ? "error" : "warning",
239
+ message: message.trim().slice(0, 300),
240
+ rule: rule?.trim(),
241
+ file: absFile,
242
+ });
243
+ }
244
+
245
+ return diags;
246
+ }
245
247
  }