pi-lens 3.6.2 → 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 (207) hide show
  1. package/CHANGELOG.md +10 -2
  2. package/package.json +4 -4
  3. package/tsconfig.json +1 -1
  4. package/clients/__tests__/file-time.test.js +0 -216
  5. package/clients/__tests__/file-time.test.ts +0 -276
  6. package/clients/__tests__/format-service.test.js +0 -245
  7. package/clients/__tests__/format-service.test.ts +0 -339
  8. package/clients/__tests__/formatters.test.js +0 -271
  9. package/clients/__tests__/formatters.test.ts +0 -401
  10. package/clients/agent-behavior-client.js +0 -110
  11. package/clients/agent-behavior-client.test.js +0 -94
  12. package/clients/agent-behavior-client.test.ts +0 -116
  13. package/clients/amain-types.js +0 -164
  14. package/clients/architect-client.js +0 -291
  15. package/clients/ast-grep-client.js +0 -253
  16. package/clients/ast-grep-parser.js +0 -84
  17. package/clients/ast-grep-rule-manager.js +0 -89
  18. package/clients/ast-grep-types.js +0 -9
  19. package/clients/auto-loop.js +0 -131
  20. package/clients/biome-client.js +0 -420
  21. package/clients/biome-client.test.js +0 -144
  22. package/clients/biome-client.test.ts +0 -163
  23. package/clients/cache/rule-cache.js +0 -72
  24. package/clients/cache-manager.js +0 -245
  25. package/clients/cache-manager.test.js +0 -197
  26. package/clients/cache-manager.test.ts +0 -299
  27. package/clients/complexity-client.js +0 -675
  28. package/clients/complexity-client.test.js +0 -234
  29. package/clients/complexity-client.test.ts +0 -255
  30. package/clients/config-validator.js +0 -465
  31. package/clients/dependency-checker.js +0 -325
  32. package/clients/dependency-checker.test.js +0 -60
  33. package/clients/dependency-checker.test.ts +0 -71
  34. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  35. package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
  36. package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
  37. package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
  38. package/clients/dispatch/debug.log +0 -1
  39. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  40. package/clients/dispatch/dispatcher.edge.test.ts +0 -100
  41. package/clients/dispatch/dispatcher.format.test.js +0 -46
  42. package/clients/dispatch/dispatcher.format.test.ts +0 -58
  43. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  44. package/clients/dispatch/dispatcher.inline.test.ts +0 -93
  45. package/clients/dispatch/dispatcher.js +0 -381
  46. package/clients/dispatch/dispatcher.test.js +0 -116
  47. package/clients/dispatch/dispatcher.test.ts +0 -149
  48. package/clients/dispatch/integration.js +0 -108
  49. package/clients/dispatch/plan.js +0 -183
  50. package/clients/dispatch/runners/architect.js +0 -83
  51. package/clients/dispatch/runners/architect.test.js +0 -138
  52. package/clients/dispatch/runners/architect.test.ts +0 -162
  53. package/clients/dispatch/runners/ast-grep-napi.js +0 -405
  54. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
  55. package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
  56. package/clients/dispatch/runners/ast-grep.js +0 -157
  57. package/clients/dispatch/runners/biome.js +0 -55
  58. package/clients/dispatch/runners/config-validation.js +0 -67
  59. package/clients/dispatch/runners/go-vet.js +0 -48
  60. package/clients/dispatch/runners/index.js +0 -47
  61. package/clients/dispatch/runners/lsp.js +0 -102
  62. package/clients/dispatch/runners/oxlint.js +0 -67
  63. package/clients/dispatch/runners/oxlint.test.js +0 -230
  64. package/clients/dispatch/runners/oxlint.test.ts +0 -303
  65. package/clients/dispatch/runners/pyright.js +0 -100
  66. package/clients/dispatch/runners/pyright.test.js +0 -98
  67. package/clients/dispatch/runners/pyright.test.ts +0 -121
  68. package/clients/dispatch/runners/python-slop.js +0 -97
  69. package/clients/dispatch/runners/python-slop.test.js +0 -203
  70. package/clients/dispatch/runners/python-slop.test.ts +0 -298
  71. package/clients/dispatch/runners/ruff.js +0 -48
  72. package/clients/dispatch/runners/rust-clippy.js +0 -102
  73. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  74. package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
  75. package/clients/dispatch/runners/shellcheck.js +0 -147
  76. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  77. package/clients/dispatch/runners/shellcheck.test.ts +0 -129
  78. package/clients/dispatch/runners/similarity.js +0 -230
  79. package/clients/dispatch/runners/spellcheck.js +0 -106
  80. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  81. package/clients/dispatch/runners/spellcheck.test.ts +0 -214
  82. package/clients/dispatch/runners/tree-sitter.js +0 -246
  83. package/clients/dispatch/runners/ts-lsp.js +0 -125
  84. package/clients/dispatch/runners/ts-slop.js +0 -113
  85. package/clients/dispatch/runners/type-safety.js +0 -142
  86. package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
  87. package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
  88. package/clients/dispatch/runners/utils.js +0 -51
  89. package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
  90. package/clients/dispatch/types.js +0 -16
  91. package/clients/dispatch/utils/format-utils.js +0 -44
  92. package/clients/dogfood.test.js +0 -201
  93. package/clients/dogfood.test.ts +0 -269
  94. package/clients/file-kinds.js +0 -177
  95. package/clients/file-kinds.test.js +0 -169
  96. package/clients/file-kinds.test.ts +0 -210
  97. package/clients/file-time.js +0 -152
  98. package/clients/file-utils.js +0 -40
  99. package/clients/fix-scanners.js +0 -204
  100. package/clients/format-service.js +0 -184
  101. package/clients/formatters.js +0 -488
  102. package/clients/go-client.js +0 -203
  103. package/clients/go-client.test.js +0 -127
  104. package/clients/go-client.test.ts +0 -143
  105. package/clients/installer/index.js +0 -403
  106. package/clients/interviewer-templates.js +0 -75
  107. package/clients/interviewer.js +0 -173
  108. package/clients/jscpd-client.js +0 -196
  109. package/clients/jscpd-client.test.js +0 -127
  110. package/clients/jscpd-client.test.ts +0 -145
  111. package/clients/knip-client.js +0 -239
  112. package/clients/knip-client.test.js +0 -112
  113. package/clients/knip-client.test.ts +0 -128
  114. package/clients/latency-logger.js +0 -40
  115. package/clients/lsp/__tests__/client.test.js +0 -310
  116. package/clients/lsp/__tests__/client.test.ts +0 -412
  117. package/clients/lsp/__tests__/config.test.js +0 -167
  118. package/clients/lsp/__tests__/config.test.ts +0 -217
  119. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  120. package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
  121. package/clients/lsp/__tests__/integration.test.js +0 -127
  122. package/clients/lsp/__tests__/integration.test.ts +0 -160
  123. package/clients/lsp/__tests__/launch.test.js +0 -313
  124. package/clients/lsp/__tests__/launch.test.ts +0 -394
  125. package/clients/lsp/__tests__/server.test.js +0 -259
  126. package/clients/lsp/__tests__/server.test.ts +0 -332
  127. package/clients/lsp/__tests__/service.test.js +0 -438
  128. package/clients/lsp/__tests__/service.test.ts +0 -530
  129. package/clients/lsp/client.js +0 -350
  130. package/clients/lsp/config.js +0 -112
  131. package/clients/lsp/index.js +0 -318
  132. package/clients/lsp/installer/index.js +0 -391
  133. package/clients/lsp/interactive-install.js +0 -221
  134. package/clients/lsp/language.js +0 -170
  135. package/clients/lsp/launch.js +0 -329
  136. package/clients/lsp/lsp/launch.js +0 -116
  137. package/clients/lsp/lsp/server.js +0 -532
  138. package/clients/lsp/lsp-index.js +0 -10
  139. package/clients/lsp/path-utils.js +0 -5
  140. package/clients/lsp/server.js +0 -725
  141. package/clients/lsp/test-py-spawn/requirements.txt +0 -1
  142. package/clients/lsp/test-py-spawn/test.py +0 -3
  143. package/clients/lsp/test-py-svc/requirements.txt +0 -1
  144. package/clients/lsp/test-py-svc/test.py +0 -3
  145. package/clients/lsp/test-python-project/requirements.txt +0 -1
  146. package/clients/lsp/test-python-project/test.py +0 -5
  147. package/clients/metrics-client.js +0 -107
  148. package/clients/metrics-client.test.js +0 -128
  149. package/clients/metrics-client.test.ts +0 -163
  150. package/clients/metrics-history.js +0 -367
  151. package/clients/path-utils.js +0 -142
  152. package/clients/pipeline.js +0 -272
  153. package/clients/production-readiness.js +0 -522
  154. package/clients/project-index.js +0 -255
  155. package/clients/project-metadata.js +0 -531
  156. package/clients/ruff-client.js +0 -325
  157. package/clients/ruff-client.test.js +0 -132
  158. package/clients/ruff-client.test.ts +0 -153
  159. package/clients/rules-scanner.js +0 -97
  160. package/clients/runner-tracker.js +0 -152
  161. package/clients/rust-client.js +0 -205
  162. package/clients/rust-client.test.js +0 -108
  163. package/clients/rust-client.test.ts +0 -130
  164. package/clients/safe-spawn-async.js +0 -163
  165. package/clients/safe-spawn.js +0 -241
  166. package/clients/sanitize.js +0 -291
  167. package/clients/sanitize.test.js +0 -177
  168. package/clients/sanitize.test.ts +0 -223
  169. package/clients/scan-architectural-debt.js +0 -167
  170. package/clients/scan-utils.js +0 -83
  171. package/clients/secrets-scanner.js +0 -119
  172. package/clients/secrets-scanner.test.js +0 -100
  173. package/clients/secrets-scanner.test.ts +0 -113
  174. package/clients/sg-runner.js +0 -292
  175. package/clients/state-matrix.js +0 -160
  176. package/clients/subprocess-client.js +0 -65
  177. package/clients/symbol-types.js +0 -5
  178. package/clients/test-runner-client.js +0 -523
  179. package/clients/test-runner-client.test.js +0 -192
  180. package/clients/test-runner-client.test.ts +0 -253
  181. package/clients/test-utils.js +0 -27
  182. package/clients/test-utils.ts +0 -36
  183. package/clients/todo-scanner.js +0 -200
  184. package/clients/todo-scanner.test.js +0 -301
  185. package/clients/todo-scanner.test.ts +0 -352
  186. package/clients/tool-availability.js +0 -207
  187. package/clients/tree-sitter-client.js +0 -601
  188. package/clients/tree-sitter-query-loader.js +0 -355
  189. package/clients/tree-sitter-symbol-extractor.js +0 -289
  190. package/clients/ts-service.js +0 -129
  191. package/clients/type-coverage-client.js +0 -127
  192. package/clients/type-coverage-client.test.js +0 -105
  193. package/clients/type-coverage-client.test.ts +0 -125
  194. package/clients/type-safety-client.js +0 -138
  195. package/clients/types.js +0 -11
  196. package/clients/typescript-client.codefix.test.js +0 -157
  197. package/clients/typescript-client.codefix.test.ts +0 -186
  198. package/clients/typescript-client.js +0 -509
  199. package/clients/typescript-client.test.js +0 -105
  200. package/clients/typescript-client.test.ts +0 -126
  201. package/commands/booboo.js +0 -1007
  202. package/commands/fix-from-booboo.js +0 -398
  203. package/commands/fix-simplified.js +0 -618
  204. package/commands/rate.js +0 -281
  205. package/commands/rate.test.js +0 -119
  206. package/commands/rate.test.ts +0 -131
  207. 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
- });