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.
Files changed (154) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +16 -12
  3. package/clients/ast-grep-client.js +8 -1
  4. package/clients/ast-grep-client.ts +9 -1
  5. package/clients/biome-client.js +51 -38
  6. package/clients/biome-client.ts +60 -58
  7. package/clients/dependency-checker.js +30 -1
  8. package/clients/dependency-checker.ts +35 -1
  9. package/clients/dispatch/__tests__/runner-registration.test.ts +286 -282
  10. package/clients/dispatch/bus-dispatcher.js +15 -14
  11. package/clients/dispatch/bus-dispatcher.ts +32 -25
  12. package/clients/dispatch/dispatcher.js +18 -25
  13. package/clients/dispatch/dispatcher.test.ts +2 -1
  14. package/clients/dispatch/dispatcher.ts +17 -28
  15. package/clients/dispatch/plan.js +77 -32
  16. package/clients/dispatch/plan.ts +78 -32
  17. package/clients/dispatch/runners/ast-grep-napi.js +36 -376
  18. package/clients/dispatch/runners/ast-grep-napi.ts +60 -433
  19. package/clients/dispatch/runners/index.js +8 -4
  20. package/clients/dispatch/runners/index.ts +8 -4
  21. package/clients/dispatch/runners/lsp.js +65 -0
  22. package/clients/dispatch/runners/lsp.ts +125 -0
  23. package/clients/dispatch/runners/oxlint.js +2 -2
  24. package/clients/dispatch/runners/oxlint.ts +2 -2
  25. package/clients/dispatch/runners/pyright.js +24 -8
  26. package/clients/dispatch/runners/pyright.ts +28 -14
  27. package/clients/dispatch/runners/rust-clippy.js +2 -2
  28. package/clients/dispatch/runners/rust-clippy.ts +2 -4
  29. package/clients/dispatch/runners/tree-sitter.js +14 -2
  30. package/clients/dispatch/runners/tree-sitter.ts +15 -2
  31. package/clients/dispatch/runners/ts-lsp.js +3 -3
  32. package/clients/dispatch/runners/ts-lsp.ts +8 -5
  33. package/clients/dispatch/runners/yaml-rule-parser.js +292 -0
  34. package/clients/dispatch/runners/yaml-rule-parser.ts +338 -0
  35. package/clients/dispatch/types.js +3 -0
  36. package/clients/dispatch/types.ts +3 -0
  37. package/clients/formatters.js +67 -14
  38. package/clients/formatters.ts +68 -15
  39. package/clients/installer/index.js +78 -10
  40. package/clients/installer/index.ts +519 -426
  41. package/clients/jscpd-client.js +28 -0
  42. package/clients/jscpd-client.ts +41 -3
  43. package/clients/knip-client.js +30 -1
  44. package/clients/knip-client.ts +34 -2
  45. package/clients/lsp/__tests__/client.test.ts +64 -41
  46. package/clients/lsp/__tests__/config.test.ts +25 -17
  47. package/clients/lsp/__tests__/launch.test.ts +108 -43
  48. package/clients/lsp/__tests__/service.test.ts +76 -48
  49. package/clients/lsp/client.js +87 -2
  50. package/clients/lsp/client.ts +150 -6
  51. package/clients/lsp/config.js +8 -11
  52. package/clients/lsp/config.ts +24 -21
  53. package/clients/lsp/index.js +69 -0
  54. package/clients/lsp/index.ts +82 -0
  55. package/clients/lsp/interactive-install.js +19 -8
  56. package/clients/lsp/interactive-install.ts +52 -27
  57. package/clients/lsp/launch.js +182 -32
  58. package/clients/lsp/launch.ts +241 -38
  59. package/clients/lsp/path-utils.js +3 -46
  60. package/clients/lsp/path-utils.ts +11 -51
  61. package/clients/lsp/server.js +93 -71
  62. package/clients/lsp/server.ts +173 -131
  63. package/clients/path-utils.js +142 -0
  64. package/clients/path-utils.ts +153 -0
  65. package/clients/ruff-client.js +33 -4
  66. package/clients/ruff-client.ts +44 -13
  67. package/clients/safe-spawn.js +3 -1
  68. package/clients/safe-spawn.ts +3 -1
  69. package/clients/services/effect-integration.js +11 -7
  70. package/clients/services/effect-integration.ts +34 -26
  71. package/clients/sg-runner.js +51 -9
  72. package/clients/sg-runner.ts +58 -15
  73. package/clients/tree-sitter-client.js +12 -0
  74. package/clients/tree-sitter-client.ts +12 -0
  75. package/clients/typescript-client.js +6 -2
  76. package/clients/typescript-client.ts +9 -2
  77. package/commands/booboo.js +2 -4
  78. package/commands/booboo.ts +2 -4
  79. package/index.ts +377 -93
  80. package/package.json +2 -1
  81. package/rules/tree-sitter-queries/tsx/no-nested-links.yml +45 -0
  82. package/rules/tree-sitter-queries/typescript/constructor-super.yml +55 -0
  83. package/rules/tree-sitter-queries/typescript/debugger.yml +1 -1
  84. package/rules/tree-sitter-queries/typescript/no-dupe-class-members.yml +47 -0
  85. package/tsconfig.json +1 -1
  86. package/clients/__tests__/file-time.test.js +0 -216
  87. package/clients/__tests__/format-service.test.js +0 -245
  88. package/clients/__tests__/formatters.test.js +0 -271
  89. package/clients/agent-behavior-client.test.js +0 -94
  90. package/clients/ast-grep-client.test.js +0 -129
  91. package/clients/ast-grep-client.test.ts +0 -155
  92. package/clients/biome-client.test.js +0 -144
  93. package/clients/cache-manager.test.js +0 -197
  94. package/clients/complexity-client.test.js +0 -234
  95. package/clients/dependency-checker.test.js +0 -60
  96. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  97. package/clients/dispatch/__tests__/runner-registration.test.js +0 -236
  98. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  99. package/clients/dispatch/dispatcher.format.test.js +0 -46
  100. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  101. package/clients/dispatch/dispatcher.test.js +0 -115
  102. package/clients/dispatch/runners/architect.test.js +0 -138
  103. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -106
  104. package/clients/dispatch/runners/oxlint.test.js +0 -230
  105. package/clients/dispatch/runners/pyright.test.js +0 -98
  106. package/clients/dispatch/runners/python-slop.test.js +0 -203
  107. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  108. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  109. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  110. package/clients/dispatch/runners/ts-slop.test.js +0 -180
  111. package/clients/dispatch/runners/ts-slop.test.ts +0 -230
  112. package/clients/dogfood.test.js +0 -201
  113. package/clients/file-kinds.test.js +0 -169
  114. package/clients/go-client.test.js +0 -127
  115. package/clients/jscpd-client.test.js +0 -127
  116. package/clients/knip-client.test.js +0 -112
  117. package/clients/lsp/__tests__/client.test.js +0 -325
  118. package/clients/lsp/__tests__/config.test.js +0 -166
  119. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  120. package/clients/lsp/__tests__/integration.test.js +0 -127
  121. package/clients/lsp/__tests__/launch.test.js +0 -260
  122. package/clients/lsp/__tests__/server.test.js +0 -259
  123. package/clients/lsp/__tests__/service.test.js +0 -417
  124. package/clients/metrics-client.test.js +0 -141
  125. package/clients/ruff-client.test.js +0 -132
  126. package/clients/rust-client.test.js +0 -108
  127. package/clients/sanitize.test.js +0 -177
  128. package/clients/secrets-scanner.test.js +0 -100
  129. package/clients/services/__tests__/effect-integration.test.js +0 -86
  130. package/clients/test-runner-client.test.js +0 -192
  131. package/clients/todo-scanner.test.js +0 -301
  132. package/clients/type-coverage-client.test.js +0 -105
  133. package/clients/typescript-client.codefix.test.js +0 -157
  134. package/clients/typescript-client.test.js +0 -105
  135. package/commands/clients/ast-grep-client.js +0 -250
  136. package/commands/clients/ast-grep-parser.js +0 -86
  137. package/commands/clients/ast-grep-rule-manager.js +0 -91
  138. package/commands/clients/ast-grep-types.js +0 -9
  139. package/commands/clients/biome-client.js +0 -380
  140. package/commands/clients/complexity-client.js +0 -667
  141. package/commands/clients/file-kinds.js +0 -177
  142. package/commands/clients/file-utils.js +0 -40
  143. package/commands/clients/jscpd-client.js +0 -169
  144. package/commands/clients/knip-client.js +0 -211
  145. package/commands/clients/ruff-client.js +0 -297
  146. package/commands/clients/safe-spawn.js +0 -88
  147. package/commands/clients/scan-utils.js +0 -83
  148. package/commands/clients/sg-runner.js +0 -190
  149. package/commands/clients/types.js +0 -11
  150. package/commands/clients/typescript-client.js +0 -505
  151. package/commands/rate.test.js +0 -119
  152. package/rules/ast-grep-rules/rules/no-dangerously-set-inner-html.yml +0 -13
  153. package/rules/ast-grep-rules/rules/no-debugger.yml +0 -12
  154. package/rules/ast-grep-rules/rules/no-eval.yml +0 -13
@@ -1,9 +0,0 @@
1
- /**
2
- * Shared types for ast-grep client, parser, and rule manager.
3
- *
4
- * Extracted to prevent circular dependencies between:
5
- * - ast-grep-client.ts
6
- * - ast-grep-parser.ts
7
- * - ast-grep-rule-manager.ts
8
- */
9
- export {};
@@ -1,380 +0,0 @@
1
- /**
2
- * Biome Client for pi-lens
3
- *
4
- * All-in-one: formatting + linting for JS/TS/JSX/TSX/CSS/JSON
5
- * Replaces Prettier with 15-50x faster Rust-based tool.
6
- *
7
- * Requires: npm install @biomejs/biome (or npx @biomejs/biome)
8
- * Docs: https://biomejs.dev/
9
- */
10
- import * as fs from "node:fs";
11
- import * as path from "node:path";
12
- import { isFileKind } from "./file-kinds.js";
13
- import { safeSpawn } from "./safe-spawn.js";
14
- // --- Client ---
15
- export class BiomeClient {
16
- biomeAvailable = null;
17
- log;
18
- constructor(verbose = false) {
19
- this.log = verbose
20
- ? (msg) => console.error(`[biome] ${msg}`)
21
- : () => { };
22
- }
23
- /**
24
- * Check if biome CLI is available
25
- */
26
- isAvailable() {
27
- if (this.biomeAvailable !== null)
28
- return this.biomeAvailable;
29
- // Try npx biome first (works without global install)
30
- const result = safeSpawn("npx", ["@biomejs/biome", "--version"], {
31
- timeout: 10000,
32
- });
33
- this.biomeAvailable = !result.error && result.status === 0;
34
- if (this.biomeAvailable) {
35
- const version = result.stdout?.trim() || "unknown";
36
- this.log(`Biome found: ${version}`);
37
- }
38
- else {
39
- this.log("Biome not available — install with: npm install -D @biomejs/biome");
40
- }
41
- return this.biomeAvailable;
42
- }
43
- /**
44
- * Check if a file is supported by Biome
45
- */
46
- isSupportedFile(filePath) {
47
- return isFileKind(filePath, ["jsts", "json", "css"]);
48
- }
49
- // --- Internal helpers ---
50
- /**
51
- * Validate path and availability — returns path or null on failure
52
- */
53
- withValidatedPath(filePath) {
54
- if (!this.isAvailable())
55
- return null;
56
- const absolutePath = path.resolve(filePath);
57
- if (!fs.existsSync(absolutePath))
58
- return null;
59
- return absolutePath;
60
- }
61
- /**
62
- * Run biome check (format + lint) without fixing — returns diagnostics
63
- */
64
- checkFile(filePath) {
65
- const absolutePath = this.withValidatedPath(filePath);
66
- if (!absolutePath)
67
- return [];
68
- try {
69
- const result = safeSpawn("npx", [
70
- "@biomejs/biome",
71
- "check",
72
- "--reporter=json",
73
- "--max-diagnostics=50",
74
- absolutePath,
75
- ], {
76
- timeout: 15000,
77
- });
78
- // Biome exits 0 on success, 1 on issues found
79
- const output = result.stdout || "";
80
- if (!output.trim())
81
- return [];
82
- return this.parseDiagnostics(output, absolutePath);
83
- }
84
- catch (err) {
85
- this.log(`Check error: ${err instanceof Error ? err.message : String(err)}`);
86
- return [];
87
- }
88
- }
89
- /**
90
- * Format a file (writes to disk)
91
- */
92
- formatFile(filePath) {
93
- const absolutePath = this.withValidatedPath(filePath);
94
- if (!absolutePath)
95
- return {
96
- success: false,
97
- changed: false,
98
- error: this.isAvailable() ? "File not found" : "Biome not available",
99
- };
100
- const content = fs.readFileSync(absolutePath, "utf-8");
101
- try {
102
- const result = safeSpawn("npx", ["@biomejs/biome", "format", "--write", absolutePath], {
103
- timeout: 15000,
104
- });
105
- if (result.error) {
106
- return { success: false, changed: false, error: result.error.message };
107
- }
108
- // Re-read to see if changed
109
- const formatted = fs.readFileSync(absolutePath, "utf-8");
110
- const changed = content !== formatted;
111
- if (changed) {
112
- this.log(`Formatted ${path.basename(filePath)}`);
113
- }
114
- return { success: true, changed };
115
- }
116
- catch (err) {
117
- return {
118
- success: false,
119
- changed: false,
120
- error: err instanceof Error ? err.message : String(err),
121
- };
122
- }
123
- }
124
- /**
125
- * Fix both formatting and linting issues (writes to disk)
126
- */
127
- fixFile(filePath) {
128
- const absolutePath = this.withValidatedPath(filePath);
129
- if (!absolutePath)
130
- return {
131
- success: false,
132
- changed: false,
133
- fixed: 0,
134
- error: this.isAvailable() ? "File not found" : "Biome not available",
135
- };
136
- const content = fs.readFileSync(absolutePath, "utf-8");
137
- try {
138
- // First, count issues before fixing
139
- const beforeDiags = this.checkFile(filePath);
140
- const fixableCount = beforeDiags.filter((d) => d.fixable).length;
141
- // Apply fixes
142
- const result = safeSpawn("npx", [
143
- "@biomejs/biome",
144
- "check",
145
- "--write",
146
- "--unsafe", // Apply unsafe fixes too
147
- absolutePath,
148
- ], {
149
- timeout: 15000,
150
- });
151
- if (result.error) {
152
- return {
153
- success: false,
154
- changed: false,
155
- fixed: 0,
156
- error: result.error.message,
157
- };
158
- }
159
- const fixed = fs.readFileSync(absolutePath, "utf-8");
160
- const changed = content !== fixed;
161
- if (changed) {
162
- this.log(`Fixed ${fixableCount} issue(s) in ${path.basename(filePath)}`);
163
- }
164
- return { success: true, changed, fixed: fixableCount };
165
- }
166
- catch (err) {
167
- return {
168
- success: false,
169
- changed: false,
170
- fixed: 0,
171
- error: err instanceof Error ? err.message : String(err),
172
- };
173
- }
174
- }
175
- /**
176
- * Fix multiple files at once (much faster than file-by-file)
177
- */
178
- fixFiles(filePaths) {
179
- if (!this.isAvailable()) {
180
- return {
181
- success: false,
182
- fixed: 0,
183
- changed: 0,
184
- error: "Biome not available",
185
- };
186
- }
187
- // Filter to existing files
188
- const validFiles = filePaths
189
- .map(f => path.resolve(f))
190
- .filter(f => fs.existsSync(f));
191
- if (validFiles.length === 0) {
192
- return { success: true, fixed: 0, changed: 0 };
193
- }
194
- try {
195
- // Count fixable issues before fixing
196
- let totalFixable = 0;
197
- for (const file of validFiles) {
198
- const diags = this.checkFile(file);
199
- totalFixable += diags.filter(d => d.fixable).length;
200
- }
201
- // Run biome once on all files - much faster than npx per file
202
- const result = safeSpawn("npx", [
203
- "@biomejs/biome",
204
- "check",
205
- "--write",
206
- "--unsafe",
207
- ...validFiles,
208
- ], {
209
- timeout: 60000, // Longer timeout for batch
210
- });
211
- if (result.error) {
212
- return {
213
- success: false,
214
- fixed: 0,
215
- changed: 0,
216
- error: result.error.message,
217
- };
218
- }
219
- // Count how many files actually changed
220
- let changedCount = 0;
221
- for (const file of validFiles) {
222
- // We don't know exactly which files changed without re-reading,
223
- // so we report total files processed
224
- changedCount++;
225
- }
226
- this.log(`Fixed ${totalFixable} issue(s) in ${validFiles.length} file(s)`);
227
- return { success: true, fixed: totalFixable, changed: changedCount };
228
- }
229
- catch (err) {
230
- return {
231
- success: false,
232
- fixed: 0,
233
- changed: 0,
234
- error: err instanceof Error ? err.message : String(err),
235
- };
236
- }
237
- }
238
- /**
239
- * Format diagnostics for LLM consumption
240
- */
241
- formatDiagnostics(diags, _filename) {
242
- if (diags.length === 0)
243
- return "";
244
- const lintIssues = diags.filter((d) => d.category === "lint");
245
- const formatIssues = diags.filter((d) => d.category === "format");
246
- const errors = diags.filter((d) => d.severity === "error");
247
- const fixable = diags.filter((d) => d.fixable);
248
- let result = `[Biome] ${diags.length} issue(s)`;
249
- if (lintIssues.length)
250
- result += ` — ${lintIssues.length} lint`;
251
- if (formatIssues.length)
252
- result += ` — ${formatIssues.length} format`;
253
- if (errors.length)
254
- result += ` — ${errors.length} error(s)`;
255
- if (fixable.length)
256
- result += ` — ${fixable.length} fixable`;
257
- result += ":\n";
258
- for (const d of diags.slice(0, 15)) {
259
- const loc = d.line === d.endLine
260
- ? `L${d.line}:${d.column}`
261
- : `L${d.line}:${d.column}-L${d.endLine}:${d.endColumn}`;
262
- const rule = d.rule ? ` [${d.rule}]` : "";
263
- const fix = d.fixable ? " ✓" : "";
264
- result += ` ${loc}${rule} ${d.message}${fix}\n`;
265
- }
266
- if (diags.length > 15) {
267
- result += ` ... and ${diags.length - 15} more\n`;
268
- }
269
- return result;
270
- }
271
- /**
272
- * Generate a diff-like summary of formatting changes
273
- */
274
- getFormatDiff(filePath) {
275
- const absolutePath = this.withValidatedPath(filePath);
276
- if (!absolutePath)
277
- return "";
278
- const content = fs.readFileSync(absolutePath, "utf-8");
279
- try {
280
- // Get formatted output without writing
281
- const result = safeSpawn("npx", ["@biomejs/biome", "format", absolutePath], {
282
- timeout: 15000,
283
- });
284
- if (result.error || !result.stdout)
285
- return "";
286
- const formatted = result.stdout;
287
- if (content === formatted)
288
- return "";
289
- return this.computeDiff(content, formatted);
290
- }
291
- catch (err) {
292
- void err;
293
- return "";
294
- }
295
- }
296
- // --- Internal ---
297
- parseDiagnostics(output, filterFile) {
298
- try {
299
- // Biome JSON output: {"summary": {...}, "diagnostics": [...], ...}
300
- const result = JSON.parse(output);
301
- const diagnostics = [];
302
- const diags = result.diagnostics || [];
303
- const filterPath = path.resolve(filterFile);
304
- for (const item of diags) {
305
- // Filter to our file
306
- const itemPath = item.location?.path;
307
- if (itemPath && path.resolve(itemPath) !== filterPath)
308
- continue;
309
- const loc = item.location || {};
310
- const start = loc.start || {};
311
- const end = loc.end || start;
312
- const isLint = item.category?.startsWith("lint/") || false;
313
- const isFormat = item.category === "format";
314
- const isAssist = item.category?.startsWith("assist/");
315
- // Skip non-lint/format diagnostics (like summaries)
316
- if (!isLint && !isFormat && !isAssist)
317
- continue;
318
- // Determine if fixable based on category
319
- const fixable = isFormat ||
320
- isAssist ||
321
- item.category?.includes("organizeImports") ||
322
- item.message?.includes("fix");
323
- diagnostics.push({
324
- line: start.line ?? 1,
325
- column: start.column ?? 1,
326
- endLine: end.line ?? start.line ?? 1,
327
- endColumn: end.column ?? start.column ?? 1,
328
- severity: item.severity || "warning",
329
- message: item.message || "Unknown issue",
330
- rule: isLint ? item.category?.replace("lint/", "") : undefined,
331
- category: isLint ? "lint" : "format",
332
- fixable,
333
- });
334
- }
335
- return diagnostics;
336
- }
337
- catch (err) {
338
- void err;
339
- this.log("Failed to parse biome JSON output");
340
- return [];
341
- }
342
- }
343
- computeDiff(original, formatted) {
344
- const origLines = original.split("\n");
345
- const formLines = formatted.split("\n");
346
- let changedLines = 0;
347
- const changes = [];
348
- const maxLen = Math.max(origLines.length, formLines.length);
349
- for (let i = 0; i < maxLen; i++) {
350
- const orig = origLines[i] ?? "";
351
- const form = formLines[i] ?? "";
352
- if (orig !== form) {
353
- changedLines++;
354
- if (changes.length < 5) {
355
- if (orig && form) {
356
- changes.push(` L${i + 1}: \`${orig.trim()}\` → \`${form.trim()}\``);
357
- }
358
- else if (!form) {
359
- changes.push(` L${i + 1}: remove line`);
360
- }
361
- else {
362
- changes.push(` L${i + 1}: add line`);
363
- }
364
- }
365
- }
366
- }
367
- let result = ` ${changedLines} line(s) would change`;
368
- if (origLines.length !== formLines.length) {
369
- result += ` (${origLines.length} → ${formLines.length} lines)`;
370
- }
371
- result += "\n";
372
- for (const c of changes) {
373
- result += `${c}\n`;
374
- }
375
- if (changedLines > 5) {
376
- result += ` ... and ${changedLines - 5} more\n`;
377
- }
378
- return result;
379
- }
380
- }