pi-lens 3.6.3 → 3.6.4

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 (206) hide show
  1. package/package.json +3 -3
  2. package/tsconfig.json +1 -1
  3. package/clients/__tests__/file-time.test.js +0 -216
  4. package/clients/__tests__/file-time.test.ts +0 -276
  5. package/clients/__tests__/format-service.test.js +0 -245
  6. package/clients/__tests__/format-service.test.ts +0 -339
  7. package/clients/__tests__/formatters.test.js +0 -271
  8. package/clients/__tests__/formatters.test.ts +0 -401
  9. package/clients/agent-behavior-client.js +0 -110
  10. package/clients/agent-behavior-client.test.js +0 -94
  11. package/clients/agent-behavior-client.test.ts +0 -116
  12. package/clients/amain-types.js +0 -164
  13. package/clients/architect-client.js +0 -291
  14. package/clients/ast-grep-client.js +0 -253
  15. package/clients/ast-grep-parser.js +0 -84
  16. package/clients/ast-grep-rule-manager.js +0 -89
  17. package/clients/ast-grep-types.js +0 -9
  18. package/clients/auto-loop.js +0 -131
  19. package/clients/biome-client.js +0 -420
  20. package/clients/biome-client.test.js +0 -144
  21. package/clients/biome-client.test.ts +0 -163
  22. package/clients/cache/rule-cache.js +0 -72
  23. package/clients/cache-manager.js +0 -245
  24. package/clients/cache-manager.test.js +0 -197
  25. package/clients/cache-manager.test.ts +0 -299
  26. package/clients/complexity-client.js +0 -675
  27. package/clients/complexity-client.test.js +0 -234
  28. package/clients/complexity-client.test.ts +0 -255
  29. package/clients/config-validator.js +0 -465
  30. package/clients/dependency-checker.js +0 -325
  31. package/clients/dependency-checker.test.js +0 -60
  32. package/clients/dependency-checker.test.ts +0 -71
  33. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  34. package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
  35. package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
  36. package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
  37. package/clients/dispatch/debug.log +0 -1
  38. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  39. package/clients/dispatch/dispatcher.edge.test.ts +0 -100
  40. package/clients/dispatch/dispatcher.format.test.js +0 -46
  41. package/clients/dispatch/dispatcher.format.test.ts +0 -58
  42. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  43. package/clients/dispatch/dispatcher.inline.test.ts +0 -93
  44. package/clients/dispatch/dispatcher.js +0 -381
  45. package/clients/dispatch/dispatcher.test.js +0 -116
  46. package/clients/dispatch/dispatcher.test.ts +0 -149
  47. package/clients/dispatch/integration.js +0 -108
  48. package/clients/dispatch/plan.js +0 -183
  49. package/clients/dispatch/runners/architect.js +0 -83
  50. package/clients/dispatch/runners/architect.test.js +0 -138
  51. package/clients/dispatch/runners/architect.test.ts +0 -162
  52. package/clients/dispatch/runners/ast-grep-napi.js +0 -405
  53. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
  54. package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
  55. package/clients/dispatch/runners/ast-grep.js +0 -157
  56. package/clients/dispatch/runners/biome.js +0 -55
  57. package/clients/dispatch/runners/config-validation.js +0 -67
  58. package/clients/dispatch/runners/go-vet.js +0 -48
  59. package/clients/dispatch/runners/index.js +0 -47
  60. package/clients/dispatch/runners/lsp.js +0 -102
  61. package/clients/dispatch/runners/oxlint.js +0 -67
  62. package/clients/dispatch/runners/oxlint.test.js +0 -230
  63. package/clients/dispatch/runners/oxlint.test.ts +0 -303
  64. package/clients/dispatch/runners/pyright.js +0 -100
  65. package/clients/dispatch/runners/pyright.test.js +0 -98
  66. package/clients/dispatch/runners/pyright.test.ts +0 -121
  67. package/clients/dispatch/runners/python-slop.js +0 -97
  68. package/clients/dispatch/runners/python-slop.test.js +0 -203
  69. package/clients/dispatch/runners/python-slop.test.ts +0 -298
  70. package/clients/dispatch/runners/ruff.js +0 -48
  71. package/clients/dispatch/runners/rust-clippy.js +0 -102
  72. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  73. package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
  74. package/clients/dispatch/runners/shellcheck.js +0 -147
  75. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  76. package/clients/dispatch/runners/shellcheck.test.ts +0 -129
  77. package/clients/dispatch/runners/similarity.js +0 -230
  78. package/clients/dispatch/runners/spellcheck.js +0 -106
  79. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  80. package/clients/dispatch/runners/spellcheck.test.ts +0 -214
  81. package/clients/dispatch/runners/tree-sitter.js +0 -246
  82. package/clients/dispatch/runners/ts-lsp.js +0 -125
  83. package/clients/dispatch/runners/ts-slop.js +0 -113
  84. package/clients/dispatch/runners/type-safety.js +0 -142
  85. package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
  86. package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
  87. package/clients/dispatch/runners/utils.js +0 -51
  88. package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
  89. package/clients/dispatch/types.js +0 -16
  90. package/clients/dispatch/utils/format-utils.js +0 -44
  91. package/clients/dogfood.test.js +0 -201
  92. package/clients/dogfood.test.ts +0 -269
  93. package/clients/file-kinds.js +0 -177
  94. package/clients/file-kinds.test.js +0 -169
  95. package/clients/file-kinds.test.ts +0 -210
  96. package/clients/file-time.js +0 -152
  97. package/clients/file-utils.js +0 -40
  98. package/clients/fix-scanners.js +0 -204
  99. package/clients/format-service.js +0 -184
  100. package/clients/formatters.js +0 -488
  101. package/clients/go-client.js +0 -203
  102. package/clients/go-client.test.js +0 -127
  103. package/clients/go-client.test.ts +0 -143
  104. package/clients/installer/index.js +0 -403
  105. package/clients/interviewer-templates.js +0 -75
  106. package/clients/interviewer.js +0 -173
  107. package/clients/jscpd-client.js +0 -196
  108. package/clients/jscpd-client.test.js +0 -127
  109. package/clients/jscpd-client.test.ts +0 -145
  110. package/clients/knip-client.js +0 -239
  111. package/clients/knip-client.test.js +0 -112
  112. package/clients/knip-client.test.ts +0 -128
  113. package/clients/latency-logger.js +0 -40
  114. package/clients/lsp/__tests__/client.test.js +0 -310
  115. package/clients/lsp/__tests__/client.test.ts +0 -412
  116. package/clients/lsp/__tests__/config.test.js +0 -167
  117. package/clients/lsp/__tests__/config.test.ts +0 -217
  118. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  119. package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
  120. package/clients/lsp/__tests__/integration.test.js +0 -127
  121. package/clients/lsp/__tests__/integration.test.ts +0 -160
  122. package/clients/lsp/__tests__/launch.test.js +0 -313
  123. package/clients/lsp/__tests__/launch.test.ts +0 -394
  124. package/clients/lsp/__tests__/server.test.js +0 -259
  125. package/clients/lsp/__tests__/server.test.ts +0 -332
  126. package/clients/lsp/__tests__/service.test.js +0 -438
  127. package/clients/lsp/__tests__/service.test.ts +0 -530
  128. package/clients/lsp/client.js +0 -350
  129. package/clients/lsp/config.js +0 -112
  130. package/clients/lsp/index.js +0 -318
  131. package/clients/lsp/installer/index.js +0 -391
  132. package/clients/lsp/interactive-install.js +0 -221
  133. package/clients/lsp/language.js +0 -170
  134. package/clients/lsp/launch.js +0 -329
  135. package/clients/lsp/lsp/launch.js +0 -116
  136. package/clients/lsp/lsp/server.js +0 -532
  137. package/clients/lsp/lsp-index.js +0 -10
  138. package/clients/lsp/path-utils.js +0 -5
  139. package/clients/lsp/server.js +0 -725
  140. package/clients/lsp/test-py-spawn/requirements.txt +0 -1
  141. package/clients/lsp/test-py-spawn/test.py +0 -3
  142. package/clients/lsp/test-py-svc/requirements.txt +0 -1
  143. package/clients/lsp/test-py-svc/test.py +0 -3
  144. package/clients/lsp/test-python-project/requirements.txt +0 -1
  145. package/clients/lsp/test-python-project/test.py +0 -5
  146. package/clients/metrics-client.js +0 -107
  147. package/clients/metrics-client.test.js +0 -128
  148. package/clients/metrics-client.test.ts +0 -163
  149. package/clients/metrics-history.js +0 -367
  150. package/clients/path-utils.js +0 -142
  151. package/clients/pipeline.js +0 -272
  152. package/clients/production-readiness.js +0 -522
  153. package/clients/project-index.js +0 -255
  154. package/clients/project-metadata.js +0 -531
  155. package/clients/ruff-client.js +0 -325
  156. package/clients/ruff-client.test.js +0 -132
  157. package/clients/ruff-client.test.ts +0 -153
  158. package/clients/rules-scanner.js +0 -97
  159. package/clients/runner-tracker.js +0 -152
  160. package/clients/rust-client.js +0 -205
  161. package/clients/rust-client.test.js +0 -108
  162. package/clients/rust-client.test.ts +0 -130
  163. package/clients/safe-spawn-async.js +0 -163
  164. package/clients/safe-spawn.js +0 -241
  165. package/clients/sanitize.js +0 -291
  166. package/clients/sanitize.test.js +0 -177
  167. package/clients/sanitize.test.ts +0 -223
  168. package/clients/scan-architectural-debt.js +0 -167
  169. package/clients/scan-utils.js +0 -83
  170. package/clients/secrets-scanner.js +0 -119
  171. package/clients/secrets-scanner.test.js +0 -100
  172. package/clients/secrets-scanner.test.ts +0 -113
  173. package/clients/sg-runner.js +0 -292
  174. package/clients/state-matrix.js +0 -160
  175. package/clients/subprocess-client.js +0 -65
  176. package/clients/symbol-types.js +0 -5
  177. package/clients/test-runner-client.js +0 -523
  178. package/clients/test-runner-client.test.js +0 -192
  179. package/clients/test-runner-client.test.ts +0 -253
  180. package/clients/test-utils.js +0 -27
  181. package/clients/test-utils.ts +0 -36
  182. package/clients/todo-scanner.js +0 -200
  183. package/clients/todo-scanner.test.js +0 -301
  184. package/clients/todo-scanner.test.ts +0 -352
  185. package/clients/tool-availability.js +0 -207
  186. package/clients/tree-sitter-client.js +0 -601
  187. package/clients/tree-sitter-query-loader.js +0 -355
  188. package/clients/tree-sitter-symbol-extractor.js +0 -289
  189. package/clients/ts-service.js +0 -129
  190. package/clients/type-coverage-client.js +0 -127
  191. package/clients/type-coverage-client.test.js +0 -105
  192. package/clients/type-coverage-client.test.ts +0 -125
  193. package/clients/type-safety-client.js +0 -138
  194. package/clients/types.js +0 -11
  195. package/clients/typescript-client.codefix.test.js +0 -157
  196. package/clients/typescript-client.codefix.test.ts +0 -186
  197. package/clients/typescript-client.js +0 -509
  198. package/clients/typescript-client.test.js +0 -105
  199. package/clients/typescript-client.test.ts +0 -126
  200. package/commands/booboo.js +0 -1007
  201. package/commands/fix-from-booboo.js +0 -398
  202. package/commands/fix-simplified.js +0 -618
  203. package/commands/rate.js +0 -281
  204. package/commands/rate.test.js +0 -119
  205. package/commands/rate.test.ts +0 -131
  206. package/commands/refactor.js +0 -130
@@ -1,298 +0,0 @@
1
- import * as fs from "node:fs";
2
- import { createRequire } from "node:module";
3
- import * as path from "node:path";
4
- import { describe, expect, it } from "vitest";
5
- import type { DispatchContext } from "../types.js";
6
-
7
- function createMockContext(filePath: string): DispatchContext {
8
- return {
9
- filePath,
10
- cwd: process.cwd(),
11
- kind: "python" as any,
12
- autofix: false,
13
- deltaMode: false,
14
- baselines: { get: () => [], add: () => {}, save: () => {} } as any,
15
- pi: {} as any,
16
- hasTool: async () => false,
17
- log: () => {},
18
- };
19
- }
20
-
21
- // Helper for safe file cleanup
22
- function safeUnlink(filePath: string): void {
23
- try {
24
- if (fs.existsSync(filePath)) {
25
- fs.unlinkSync(filePath);
26
- }
27
- } catch {
28
- // Ignore cleanup errors on Windows
29
- }
30
- }
31
-
32
- describe("python-slop runner", () => {
33
- const require = createRequire(import.meta.url);
34
-
35
- it("should have correct runner definition", async () => {
36
- const slopModule = await import("./python-slop.js");
37
- const runner = slopModule.default;
38
-
39
- expect(runner.id).toBe("python-slop");
40
- expect(runner.appliesTo).toEqual(["python"]);
41
- expect(runner.priority).toBe(25);
42
- expect(runner.enabledByDefault).toBe(true);
43
- expect(runner.skipTestFiles).toBe(true);
44
- });
45
-
46
- it("should detect ast-grep availability", () => {
47
- const { spawnSync } =
48
- require("node:child_process") as typeof import("node:child_process");
49
- const result = spawnSync("npx", ["sg", "--version"], {
50
- encoding: "utf-8",
51
- timeout: 10000,
52
- shell: true,
53
- });
54
- expect(
55
- result.error || result.status !== 0 ? "not available" : "available",
56
- ).toBe("available");
57
- });
58
-
59
- it("should detect verbose range-len pattern", async () => {
60
- const tmpFile = path.join(
61
- process.env.TEMP || "/tmp",
62
- `slop_test_range_${Date.now()}.py`,
63
- );
64
- fs.writeFileSync(
65
- tmpFile,
66
- `# Slop: using range(len()) instead of enumerate
67
- def process_items(items):
68
- for i in range(len(items)):
69
- print(items[i])
70
- `,
71
- );
72
-
73
- try {
74
- const slopModule = await import("./python-slop.js");
75
- const runner = slopModule.default;
76
- const result = await runner.run(createMockContext(tmpFile));
77
-
78
- expect(result.diagnostics.length).toBeGreaterThanOrEqual(1);
79
- expect(
80
- result.diagnostics.some(
81
- (d) =>
82
- d.tool === "python-slop" &&
83
- d.message.includes("range(len") &&
84
- d.message.includes("enumerate"),
85
- ),
86
- ).toBe(true);
87
- } finally {
88
- safeUnlink(tmpFile);
89
- }
90
- });
91
-
92
- it("should detect manual min/max pattern", async () => {
93
- const tmpFile = path.join(
94
- process.env.TEMP || "/tmp",
95
- `slop_test_minmax_${Date.now()}.py`,
96
- );
97
- fs.writeFileSync(
98
- tmpFile,
99
- `# Slop: manual min/max instead of built-in
100
- def find_max(a, b):
101
- if a > b:
102
- m = a
103
- else:
104
- m = b
105
- return m
106
- `,
107
- );
108
-
109
- try {
110
- const slopModule = await import("./python-slop.js");
111
- const runner = slopModule.default;
112
- const result = await runner.run(createMockContext(tmpFile));
113
-
114
- expect(result.diagnostics.length).toBeGreaterThanOrEqual(1);
115
- expect(
116
- result.diagnostics.some(
117
- (d) =>
118
- d.tool === "python-slop" &&
119
- (d.message.includes("min") || d.message.includes("max")),
120
- ),
121
- ).toBe(true);
122
- } finally {
123
- safeUnlink(tmpFile);
124
- }
125
- });
126
-
127
- it("should detect defensive None guard", async () => {
128
- const tmpFile = path.join(
129
- process.env.TEMP || "/tmp",
130
- `slop_test_guard_${Date.now()}.py`,
131
- );
132
- fs.writeFileSync(
133
- tmpFile,
134
- `# Slop: defensive None guard
135
- def process(data):
136
- if data is None:
137
- return None
138
- return data.upper()
139
- `,
140
- );
141
-
142
- try {
143
- const slopModule = await import("./python-slop.js");
144
- const runner = slopModule.default;
145
- const result = await runner.run(createMockContext(tmpFile));
146
-
147
- expect(result.diagnostics.length).toBeGreaterThanOrEqual(1);
148
- expect(
149
- result.diagnostics.some(
150
- (d) =>
151
- d.tool === "python-slop" &&
152
- (d.message.includes("defensive") ||
153
- d.message.includes("guard")),
154
- ),
155
- ).toBe(true);
156
- } finally {
157
- safeUnlink(tmpFile);
158
- }
159
- });
160
-
161
- it("should detect list comprehension ceremony", async () => {
162
- const tmpFile = path.join(
163
- process.env.TEMP || "/tmp",
164
- `slop_test_list_${Date.now()}.py`,
165
- );
166
- fs.writeFileSync(
167
- tmpFile,
168
- `# Slop: redundant list comprehension
169
- def convert(items):
170
- return [x for x in items]
171
- `,
172
- );
173
-
174
- try {
175
- const slopModule = await import("./python-slop.js");
176
- const runner = slopModule.default;
177
- const result = await runner.run(createMockContext(tmpFile));
178
-
179
- expect(result.diagnostics.length).toBeGreaterThanOrEqual(1);
180
- expect(
181
- result.diagnostics.some(
182
- (d) =>
183
- d.tool === "python-slop" &&
184
- d.message.includes("list") &&
185
- d.message.includes("unnecessary"),
186
- ),
187
- ).toBe(true);
188
- } finally {
189
- safeUnlink(tmpFile);
190
- }
191
- });
192
-
193
- it("should detect chained comparison opportunity", async () => {
194
- const tmpFile = path.join(
195
- process.env.TEMP || "/tmp",
196
- `slop_test_chain_${Date.now()}.py`,
197
- );
198
- fs.writeFileSync(
199
- tmpFile,
200
- `# Slop: could use chained comparison
201
- def check_range(x, a, b):
202
- return a < x and x < b
203
- `,
204
- );
205
-
206
- try {
207
- const slopModule = await import("./python-slop.js");
208
- const runner = slopModule.default;
209
- const result = await runner.run(createMockContext(tmpFile));
210
-
211
- expect(result.diagnostics.length).toBeGreaterThanOrEqual(1);
212
- expect(
213
- result.diagnostics.some(
214
- (d) =>
215
- d.tool === "python-slop" &&
216
- d.message.includes("chained"),
217
- ),
218
- ).toBe(true);
219
- } finally {
220
- safeUnlink(tmpFile);
221
- }
222
- });
223
-
224
- it("should pass clean Python files", async () => {
225
- const tmpFile = path.join(
226
- process.env.TEMP || "/tmp",
227
- `slop_test_ok_${Date.now()}.py`,
228
- );
229
- fs.writeFileSync(
230
- tmpFile,
231
- `# Clean Python code
232
- def process_items(items):
233
- """Process items using proper Python idioms."""
234
- for i, item in enumerate(items):
235
- print(f"{i}: {item}")
236
-
237
- def find_max(a, b):
238
- return max(a, b)
239
-
240
- def check_range(x, min_val, max_val):
241
- return min_val < x < max_val
242
-
243
- def convert(items):
244
- return list(items)
245
- `,
246
- );
247
-
248
- try {
249
- const slopModule = await import("./python-slop.js");
250
- const runner = slopModule.default;
251
- const result = await runner.run(createMockContext(tmpFile));
252
-
253
- // Should have no slop issues
254
- const slopIssues = result.diagnostics.filter(
255
- (d) => d.tool === "python-slop",
256
- );
257
- expect(slopIssues.length).toBe(0);
258
- } finally {
259
- safeUnlink(tmpFile);
260
- }
261
- });
262
-
263
- it("should categorize by weight correctly", async () => {
264
- const tmpFile = path.join(
265
- process.env.TEMP || "/tmp",
266
- `slop_test_weight_${Date.now()}.py`,
267
- );
268
- fs.writeFileSync(
269
- tmpFile,
270
- `# Multiple slop patterns - weight 3 and weight 4
271
- def bad_code(items):
272
- # Weight 3: range(len)
273
- for i in range(len(items)):
274
- print(items[i])
275
-
276
- # Weight 3: redundant list comprehension
277
- return [x for x in items]
278
- `,
279
- );
280
-
281
- try {
282
- const slopModule = await import("./python-slop.js");
283
- const runner = slopModule.default;
284
- const result = await runner.run(createMockContext(tmpFile));
285
-
286
- // Should detect at least the range(len) pattern
287
- expect(result.diagnostics.length).toBeGreaterThanOrEqual(1);
288
-
289
- // All should be warnings (weight 3)
290
- const warnings = result.diagnostics.filter(
291
- (d) => d.severity === "warning",
292
- );
293
- expect(warnings.length).toBeGreaterThanOrEqual(1);
294
- } finally {
295
- safeUnlink(tmpFile);
296
- }
297
- });
298
- });
@@ -1,48 +0,0 @@
1
- /**
2
- * Ruff runner for dispatch system
3
- *
4
- * Ruff handles both formatting and linting for Python files.
5
- * Supports venv-local installations.
6
- */
7
- import { ensureTool } from "../../installer/index.js";
8
- import { safeSpawnAsync } from "../../safe-spawn.js";
9
- import { stripAnsi } from "../../sanitize.js";
10
- import { parseRuffOutput } from "./utils/diagnostic-parsers.js";
11
- import { createAvailabilityChecker } from "./utils/runner-helpers.js";
12
- const ruff = createAvailabilityChecker("ruff", ".exe");
13
- const ruffRunner = {
14
- id: "ruff-lint",
15
- appliesTo: ["python"],
16
- priority: 10,
17
- enabledByDefault: true,
18
- async run(ctx) {
19
- const cwd = ctx.cwd || process.cwd();
20
- // Auto-install ruff if not available (it's one of the 4 auto-install tools)
21
- if (!ruff.isAvailable(cwd)) {
22
- const installed = await ensureTool("ruff");
23
- if (!installed) {
24
- return { status: "skipped", diagnostics: [], semantic: "none" };
25
- }
26
- }
27
- // IMPORTANT: Never use --fix in dispatch runner to prevent infinite loops.
28
- // Writing to the file would trigger another tool_result event, which would
29
- // call dispatchLint again, creating a feedback loop.
30
- // Fixes should be applied through explicit commands or user edits.
31
- const args = ["check", ctx.filePath];
32
- const result = await safeSpawnAsync(ruff.getCommand(), args, {
33
- timeout: 30000,
34
- });
35
- const raw = stripAnsi(result.stdout + result.stderr);
36
- if (result.status === 0) {
37
- return { status: "succeeded", diagnostics: [], semantic: "none" };
38
- }
39
- // Parse diagnostics
40
- const diagnostics = parseRuffOutput(raw, ctx.filePath);
41
- return {
42
- status: "failed",
43
- diagnostics,
44
- semantic: "warning",
45
- };
46
- },
47
- };
48
- export default ruffRunner;
@@ -1,102 +0,0 @@
1
- /**
2
- * Rust clippy runner for dispatch system
3
- *
4
- * Runs `cargo clippy` for Rust files to catch common mistakes.
5
- */
6
- import { existsSync } from "node:fs";
7
- import { dirname, join } from "node:path";
8
- import { safeSpawn } from "../../safe-spawn.js";
9
- import { stripAnsi } from "../../sanitize.js";
10
- const rustClippyRunner = {
11
- id: "rust-clippy",
12
- appliesTo: ["rust"],
13
- priority: 15,
14
- enabledByDefault: true,
15
- async run(ctx) {
16
- // Check if cargo is available
17
- const check = safeSpawn("cargo", ["--version"], {
18
- timeout: 5000,
19
- });
20
- if (check.error || check.status !== 0) {
21
- return { status: "skipped", diagnostics: [], semantic: "none" };
22
- }
23
- // Find the package root (where Cargo.toml is)
24
- const cargoToml = findCargoToml(ctx.filePath);
25
- if (!cargoToml) {
26
- return { status: "skipped", diagnostics: [], semantic: "none" };
27
- }
28
- // Run cargo clippy on the package
29
- const result = safeSpawn("cargo", ["clippy", "--message-format=json", "-q"], {
30
- timeout: 60000,
31
- cwd: cargoToml.replace("Cargo.toml", ""),
32
- });
33
- const raw = stripAnsi(result.stdout + result.stderr);
34
- if (result.status === 0 && !raw.trim()) {
35
- return { status: "succeeded", diagnostics: [], semantic: "none" };
36
- }
37
- // Parse JSON output
38
- const diagnostics = parseClippyOutput(raw, ctx.filePath);
39
- if (diagnostics.length === 0) {
40
- // Non-parseable output
41
- return {
42
- status: "failed",
43
- diagnostics: [],
44
- semantic: "warning",
45
- rawOutput: raw.substring(0, 500),
46
- };
47
- }
48
- return {
49
- status: "failed",
50
- diagnostics,
51
- semantic: "warning",
52
- };
53
- },
54
- };
55
- function findCargoToml(filePath) {
56
- let dir = dirname(filePath);
57
- while (dir !== "/" && dir !== ".") {
58
- const cargoPath = join(dir, "Cargo.toml");
59
- if (existsSync(cargoPath)) {
60
- return cargoPath;
61
- }
62
- const parent = dirname(dir);
63
- if (parent === dir)
64
- break;
65
- dir = parent;
66
- }
67
- return undefined;
68
- }
69
- function parseClippyOutput(raw, filePath) {
70
- const diagnostics = [];
71
- const lines = raw.split("\n").filter((l) => l.trim());
72
- for (const line of lines) {
73
- try {
74
- const msg = JSON.parse(line);
75
- if (msg.reason !== "compiler-message")
76
- continue;
77
- const message = msg.message;
78
- if (!message)
79
- continue;
80
- // Only include messages for this file or project-wide
81
- const span = message.spans?.[0];
82
- if (!span)
83
- continue;
84
- diagnostics.push({
85
- id: `clippy-${message.code?.code || "unknown"}`,
86
- message: message.message || "Clippy warning",
87
- filePath: span.file || filePath,
88
- line: span.line_start || 0,
89
- column: span.column_start || 0,
90
- severity: message.level === "error" ? "error" : "warning",
91
- semantic: message.level === "error" ? "blocking" : "warning",
92
- tool: "rust-clippy",
93
- rule: message.code?.code,
94
- });
95
- }
96
- catch {
97
- // Not a JSON line, skip
98
- }
99
- }
100
- return diagnostics;
101
- }
102
- export default rustClippyRunner;
@@ -1,89 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { describe, expect, it } from "vitest";
4
- // Find all TS files
5
- function findTsFiles(dir) {
6
- const files = [];
7
- const entries = fs.readdirSync(dir, { withFileTypes: true });
8
- for (const entry of entries) {
9
- const fullPath = path.join(dir, entry.name);
10
- // Skip node_modules, .git, etc
11
- if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === ".pi-lens") {
12
- continue;
13
- }
14
- if (entry.isDirectory()) {
15
- files.push(...findTsFiles(fullPath));
16
- }
17
- else if (entry.isFile() && fullPath.endsWith(".ts") && !fullPath.endsWith(".test.ts")) {
18
- files.push(fullPath);
19
- }
20
- }
21
- return files;
22
- }
23
- function createContext(filePath) {
24
- return {
25
- filePath,
26
- cwd: process.cwd(),
27
- kind: "jsts",
28
- autofix: false,
29
- deltaMode: false,
30
- baselines: { get: () => [], add: () => { }, save: () => { } },
31
- pi: {},
32
- hasTool: async () => false,
33
- log: () => { },
34
- };
35
- }
36
- describe("Codebase scan with NAPI runner", () => {
37
- it("should scan all TypeScript files and report findings", async () => {
38
- const tsFiles = findTsFiles(process.cwd());
39
- console.log(`\nFound ${tsFiles.length} TypeScript files to scan\n`);
40
- const runner = (await import("./ast-grep-napi.js")).default;
41
- const allIssues = [];
42
- let totalTime = 0;
43
- let filesWithIssues = 0;
44
- for (let i = 0; i < Math.min(tsFiles.length, 50); i++) { // Limit to 50 for test speed
45
- const file = tsFiles[i];
46
- const ctx = createContext(file);
47
- const start = Date.now();
48
- const result = await runner.run(ctx);
49
- const elapsed = Date.now() - start;
50
- totalTime += elapsed;
51
- if (result.diagnostics.length > 0) {
52
- filesWithIssues++;
53
- console.log(`${path.relative(process.cwd(), file)} (${elapsed}ms):`);
54
- for (const d of result.diagnostics.slice(0, 5)) { // Show max 5 per file
55
- const line = d.line ?? 0;
56
- const rule = d.rule ?? "unknown";
57
- const message = d.message?.split('\n')[0] ?? "";
58
- console.log(` Line ${line}: [${rule}] ${message}`);
59
- allIssues.push({
60
- file: path.relative(process.cwd(), file),
61
- line,
62
- rule,
63
- message,
64
- });
65
- }
66
- if (result.diagnostics.length > 5) {
67
- console.log(` ... and ${result.diagnostics.length - 5} more`);
68
- }
69
- }
70
- }
71
- console.log(`\n=== SUMMARY (first 50 files) ===`);
72
- console.log(`Files scanned: ${Math.min(tsFiles.length, 50)}/${tsFiles.length}`);
73
- console.log(`Total time: ${totalTime}ms`);
74
- console.log(`Files with issues: ${filesWithIssues}`);
75
- console.log(`Total issues: ${allIssues.length}`);
76
- console.log(`Avg time per file: ${(totalTime / Math.min(tsFiles.length, 50)).toFixed(1)}ms`);
77
- // Group by rule
78
- const byRule = {};
79
- for (const issue of allIssues) {
80
- byRule[issue.rule] = (byRule[issue.rule] || 0) + 1;
81
- }
82
- console.log(`\n=== BY RULE ===`);
83
- for (const [rule, count] of Object.entries(byRule).sort((a, b) => b[1] - a[1])) {
84
- console.log(` ${rule}: ${count}`);
85
- }
86
- // This test should pass - we're just scanning
87
- expect(true).toBe(true);
88
- }, 60000); // 60 second timeout for scanning
89
- });
@@ -1,105 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { describe, expect, it } from "vitest";
4
- import type { DispatchContext } from "../types.js";
5
-
6
- // Find all TS files
7
- function findTsFiles(dir: string): string[] {
8
- const files: string[] = [];
9
- const entries = fs.readdirSync(dir, { withFileTypes: true });
10
-
11
- for (const entry of entries) {
12
- const fullPath = path.join(dir, entry.name);
13
-
14
- // Skip node_modules, .git, etc
15
- if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === ".pi-lens") {
16
- continue;
17
- }
18
-
19
- if (entry.isDirectory()) {
20
- files.push(...findTsFiles(fullPath));
21
- } else if (entry.isFile() && fullPath.endsWith(".ts") && !fullPath.endsWith(".test.ts")) {
22
- files.push(fullPath);
23
- }
24
- }
25
-
26
- return files;
27
- }
28
-
29
- function createContext(filePath: string): DispatchContext {
30
- return {
31
- filePath,
32
- cwd: process.cwd(),
33
- kind: "jsts",
34
- autofix: false,
35
- deltaMode: false,
36
- baselines: { get: () => [], add: () => {}, save: () => {} } as any,
37
- pi: {} as any,
38
- hasTool: async () => false,
39
- log: () => {},
40
- };
41
- }
42
-
43
- describe("Codebase scan with NAPI runner", () => {
44
- it("should scan all TypeScript files and report findings", async () => {
45
- const tsFiles = findTsFiles(process.cwd());
46
- console.log(`\nFound ${tsFiles.length} TypeScript files to scan\n`);
47
-
48
- const runner = (await import("./ast-grep-napi.js")).default;
49
-
50
- const allIssues: Array<{ file: string; line: number; rule: string; message: string }> = [];
51
- let totalTime = 0;
52
- let filesWithIssues = 0;
53
-
54
- for (let i = 0; i < Math.min(tsFiles.length, 50); i++) { // Limit to 50 for test speed
55
- const file = tsFiles[i];
56
- const ctx = createContext(file);
57
-
58
- const start = Date.now();
59
- const result = await runner.run(ctx);
60
- const elapsed = Date.now() - start;
61
- totalTime += elapsed;
62
-
63
- if (result.diagnostics.length > 0) {
64
- filesWithIssues++;
65
- console.log(`${path.relative(process.cwd(), file)} (${elapsed}ms):`);
66
- for (const d of result.diagnostics.slice(0, 5)) { // Show max 5 per file
67
- const line = d.line ?? 0;
68
- const rule = d.rule ?? "unknown";
69
- const message = d.message?.split('\n')[0] ?? "";
70
- console.log(` Line ${line}: [${rule}] ${message}`);
71
- allIssues.push({
72
- file: path.relative(process.cwd(), file),
73
- line,
74
- rule,
75
- message,
76
- });
77
- }
78
- if (result.diagnostics.length > 5) {
79
- console.log(` ... and ${result.diagnostics.length - 5} more`);
80
- }
81
- }
82
- }
83
-
84
- console.log(`\n=== SUMMARY (first 50 files) ===`);
85
- console.log(`Files scanned: ${Math.min(tsFiles.length, 50)}/${tsFiles.length}`);
86
- console.log(`Total time: ${totalTime}ms`);
87
- console.log(`Files with issues: ${filesWithIssues}`);
88
- console.log(`Total issues: ${allIssues.length}`);
89
- console.log(`Avg time per file: ${(totalTime / Math.min(tsFiles.length, 50)).toFixed(1)}ms`);
90
-
91
- // Group by rule
92
- const byRule: Record<string, number> = {};
93
- for (const issue of allIssues) {
94
- byRule[issue.rule] = (byRule[issue.rule] || 0) + 1;
95
- }
96
-
97
- console.log(`\n=== BY RULE ===`);
98
- for (const [rule, count] of Object.entries(byRule).sort((a, b) => b[1] - a[1])) {
99
- console.log(` ${rule}: ${count}`);
100
- }
101
-
102
- // This test should pass - we're just scanning
103
- expect(true).toBe(true);
104
- }, 60000); // 60 second timeout for scanning
105
- });