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.
- package/CHANGELOG.md +10 -2
- package/package.json +4 -4
- package/tsconfig.json +1 -1
- package/clients/__tests__/file-time.test.js +0 -216
- package/clients/__tests__/file-time.test.ts +0 -276
- package/clients/__tests__/format-service.test.js +0 -245
- package/clients/__tests__/format-service.test.ts +0 -339
- package/clients/__tests__/formatters.test.js +0 -271
- package/clients/__tests__/formatters.test.ts +0 -401
- package/clients/agent-behavior-client.js +0 -110
- package/clients/agent-behavior-client.test.js +0 -94
- package/clients/agent-behavior-client.test.ts +0 -116
- package/clients/amain-types.js +0 -164
- package/clients/architect-client.js +0 -291
- package/clients/ast-grep-client.js +0 -253
- package/clients/ast-grep-parser.js +0 -84
- package/clients/ast-grep-rule-manager.js +0 -89
- package/clients/ast-grep-types.js +0 -9
- package/clients/auto-loop.js +0 -131
- package/clients/biome-client.js +0 -420
- package/clients/biome-client.test.js +0 -144
- package/clients/biome-client.test.ts +0 -163
- package/clients/cache/rule-cache.js +0 -72
- package/clients/cache-manager.js +0 -245
- package/clients/cache-manager.test.js +0 -197
- package/clients/cache-manager.test.ts +0 -299
- package/clients/complexity-client.js +0 -675
- package/clients/complexity-client.test.js +0 -234
- package/clients/complexity-client.test.ts +0 -255
- package/clients/config-validator.js +0 -465
- package/clients/dependency-checker.js +0 -325
- package/clients/dependency-checker.test.js +0 -60
- package/clients/dependency-checker.test.ts +0 -71
- package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
- package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
- package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
- package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
- package/clients/dispatch/debug.log +0 -1
- package/clients/dispatch/dispatcher.edge.test.js +0 -82
- package/clients/dispatch/dispatcher.edge.test.ts +0 -100
- package/clients/dispatch/dispatcher.format.test.js +0 -46
- package/clients/dispatch/dispatcher.format.test.ts +0 -58
- package/clients/dispatch/dispatcher.inline.test.js +0 -74
- package/clients/dispatch/dispatcher.inline.test.ts +0 -93
- package/clients/dispatch/dispatcher.js +0 -381
- package/clients/dispatch/dispatcher.test.js +0 -116
- package/clients/dispatch/dispatcher.test.ts +0 -149
- package/clients/dispatch/integration.js +0 -108
- package/clients/dispatch/plan.js +0 -183
- package/clients/dispatch/runners/architect.js +0 -83
- package/clients/dispatch/runners/architect.test.js +0 -138
- package/clients/dispatch/runners/architect.test.ts +0 -162
- package/clients/dispatch/runners/ast-grep-napi.js +0 -405
- package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
- package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
- package/clients/dispatch/runners/ast-grep.js +0 -157
- package/clients/dispatch/runners/biome.js +0 -55
- package/clients/dispatch/runners/config-validation.js +0 -67
- package/clients/dispatch/runners/go-vet.js +0 -48
- package/clients/dispatch/runners/index.js +0 -47
- package/clients/dispatch/runners/lsp.js +0 -102
- package/clients/dispatch/runners/oxlint.js +0 -67
- package/clients/dispatch/runners/oxlint.test.js +0 -230
- package/clients/dispatch/runners/oxlint.test.ts +0 -303
- package/clients/dispatch/runners/pyright.js +0 -100
- package/clients/dispatch/runners/pyright.test.js +0 -98
- package/clients/dispatch/runners/pyright.test.ts +0 -121
- package/clients/dispatch/runners/python-slop.js +0 -97
- package/clients/dispatch/runners/python-slop.test.js +0 -203
- package/clients/dispatch/runners/python-slop.test.ts +0 -298
- package/clients/dispatch/runners/ruff.js +0 -48
- package/clients/dispatch/runners/rust-clippy.js +0 -102
- package/clients/dispatch/runners/scan_codebase.test.js +0 -89
- package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
- package/clients/dispatch/runners/shellcheck.js +0 -147
- package/clients/dispatch/runners/shellcheck.test.js +0 -98
- package/clients/dispatch/runners/shellcheck.test.ts +0 -129
- package/clients/dispatch/runners/similarity.js +0 -230
- package/clients/dispatch/runners/spellcheck.js +0 -106
- package/clients/dispatch/runners/spellcheck.test.js +0 -158
- package/clients/dispatch/runners/spellcheck.test.ts +0 -214
- package/clients/dispatch/runners/tree-sitter.js +0 -246
- package/clients/dispatch/runners/ts-lsp.js +0 -125
- package/clients/dispatch/runners/ts-slop.js +0 -113
- package/clients/dispatch/runners/type-safety.js +0 -142
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
- package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
- package/clients/dispatch/runners/utils.js +0 -51
- package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
- package/clients/dispatch/types.js +0 -16
- package/clients/dispatch/utils/format-utils.js +0 -44
- package/clients/dogfood.test.js +0 -201
- package/clients/dogfood.test.ts +0 -269
- package/clients/file-kinds.js +0 -177
- package/clients/file-kinds.test.js +0 -169
- package/clients/file-kinds.test.ts +0 -210
- package/clients/file-time.js +0 -152
- package/clients/file-utils.js +0 -40
- package/clients/fix-scanners.js +0 -204
- package/clients/format-service.js +0 -184
- package/clients/formatters.js +0 -488
- package/clients/go-client.js +0 -203
- package/clients/go-client.test.js +0 -127
- package/clients/go-client.test.ts +0 -143
- package/clients/installer/index.js +0 -403
- package/clients/interviewer-templates.js +0 -75
- package/clients/interviewer.js +0 -173
- package/clients/jscpd-client.js +0 -196
- package/clients/jscpd-client.test.js +0 -127
- package/clients/jscpd-client.test.ts +0 -145
- package/clients/knip-client.js +0 -239
- package/clients/knip-client.test.js +0 -112
- package/clients/knip-client.test.ts +0 -128
- package/clients/latency-logger.js +0 -40
- package/clients/lsp/__tests__/client.test.js +0 -310
- package/clients/lsp/__tests__/client.test.ts +0 -412
- package/clients/lsp/__tests__/config.test.js +0 -167
- package/clients/lsp/__tests__/config.test.ts +0 -217
- package/clients/lsp/__tests__/error-recovery.test.js +0 -213
- package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
- package/clients/lsp/__tests__/integration.test.js +0 -127
- package/clients/lsp/__tests__/integration.test.ts +0 -160
- package/clients/lsp/__tests__/launch.test.js +0 -313
- package/clients/lsp/__tests__/launch.test.ts +0 -394
- package/clients/lsp/__tests__/server.test.js +0 -259
- package/clients/lsp/__tests__/server.test.ts +0 -332
- package/clients/lsp/__tests__/service.test.js +0 -438
- package/clients/lsp/__tests__/service.test.ts +0 -530
- package/clients/lsp/client.js +0 -350
- package/clients/lsp/config.js +0 -112
- package/clients/lsp/index.js +0 -318
- package/clients/lsp/installer/index.js +0 -391
- package/clients/lsp/interactive-install.js +0 -221
- package/clients/lsp/language.js +0 -170
- package/clients/lsp/launch.js +0 -329
- package/clients/lsp/lsp/launch.js +0 -116
- package/clients/lsp/lsp/server.js +0 -532
- package/clients/lsp/lsp-index.js +0 -10
- package/clients/lsp/path-utils.js +0 -5
- package/clients/lsp/server.js +0 -725
- package/clients/lsp/test-py-spawn/requirements.txt +0 -1
- package/clients/lsp/test-py-spawn/test.py +0 -3
- package/clients/lsp/test-py-svc/requirements.txt +0 -1
- package/clients/lsp/test-py-svc/test.py +0 -3
- package/clients/lsp/test-python-project/requirements.txt +0 -1
- package/clients/lsp/test-python-project/test.py +0 -5
- package/clients/metrics-client.js +0 -107
- package/clients/metrics-client.test.js +0 -128
- package/clients/metrics-client.test.ts +0 -163
- package/clients/metrics-history.js +0 -367
- package/clients/path-utils.js +0 -142
- package/clients/pipeline.js +0 -272
- package/clients/production-readiness.js +0 -522
- package/clients/project-index.js +0 -255
- package/clients/project-metadata.js +0 -531
- package/clients/ruff-client.js +0 -325
- package/clients/ruff-client.test.js +0 -132
- package/clients/ruff-client.test.ts +0 -153
- package/clients/rules-scanner.js +0 -97
- package/clients/runner-tracker.js +0 -152
- package/clients/rust-client.js +0 -205
- package/clients/rust-client.test.js +0 -108
- package/clients/rust-client.test.ts +0 -130
- package/clients/safe-spawn-async.js +0 -163
- package/clients/safe-spawn.js +0 -241
- package/clients/sanitize.js +0 -291
- package/clients/sanitize.test.js +0 -177
- package/clients/sanitize.test.ts +0 -223
- package/clients/scan-architectural-debt.js +0 -167
- package/clients/scan-utils.js +0 -83
- package/clients/secrets-scanner.js +0 -119
- package/clients/secrets-scanner.test.js +0 -100
- package/clients/secrets-scanner.test.ts +0 -113
- package/clients/sg-runner.js +0 -292
- package/clients/state-matrix.js +0 -160
- package/clients/subprocess-client.js +0 -65
- package/clients/symbol-types.js +0 -5
- package/clients/test-runner-client.js +0 -523
- package/clients/test-runner-client.test.js +0 -192
- package/clients/test-runner-client.test.ts +0 -253
- package/clients/test-utils.js +0 -27
- package/clients/test-utils.ts +0 -36
- package/clients/todo-scanner.js +0 -200
- package/clients/todo-scanner.test.js +0 -301
- package/clients/todo-scanner.test.ts +0 -352
- package/clients/tool-availability.js +0 -207
- package/clients/tree-sitter-client.js +0 -601
- package/clients/tree-sitter-query-loader.js +0 -355
- package/clients/tree-sitter-symbol-extractor.js +0 -289
- package/clients/ts-service.js +0 -129
- package/clients/type-coverage-client.js +0 -127
- package/clients/type-coverage-client.test.js +0 -105
- package/clients/type-coverage-client.test.ts +0 -125
- package/clients/type-safety-client.js +0 -138
- package/clients/types.js +0 -11
- package/clients/typescript-client.codefix.test.js +0 -157
- package/clients/typescript-client.codefix.test.ts +0 -186
- package/clients/typescript-client.js +0 -509
- package/clients/typescript-client.test.js +0 -105
- package/clients/typescript-client.test.ts +0 -126
- package/commands/booboo.js +0 -1007
- package/commands/fix-from-booboo.js +0 -398
- package/commands/fix-simplified.js +0 -618
- package/commands/rate.js +0 -281
- package/commands/rate.test.js +0 -119
- package/commands/rate.test.ts +0 -131
- 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
|
-
});
|