pi-lens 2.2.9 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +198 -0
- package/README.md +709 -519
- package/clients/__tests__/file-time.test.js +216 -0
- package/clients/__tests__/file-time.test.ts +276 -0
- package/clients/__tests__/format-service.test.js +245 -0
- package/clients/__tests__/format-service.test.ts +339 -0
- package/clients/__tests__/formatters.test.js +271 -0
- package/clients/__tests__/formatters.test.ts +401 -0
- package/clients/amain-types.js +164 -0
- package/clients/amain-types.ts +165 -0
- package/clients/architect-client.js +56 -12
- package/clients/architect-client.ts +81 -16
- package/clients/ast-grep-client.js +2 -2
- package/clients/ast-grep-client.ts +14 -39
- package/clients/ast-grep-parser.ts +1 -1
- package/clients/ast-grep-rule-manager.js +8 -0
- package/clients/ast-grep-rule-manager.ts +10 -1
- package/clients/ast-grep-types.js +9 -0
- package/clients/ast-grep-types.ts +106 -0
- package/clients/auto-loop.js +10 -0
- package/clients/auto-loop.ts +14 -1
- package/clients/biome-client.js +81 -19
- package/clients/biome-client.ts +103 -22
- package/clients/bus/bus.js +191 -0
- package/clients/bus/bus.ts +251 -0
- package/clients/bus/events.js +214 -0
- package/clients/bus/events.ts +279 -0
- package/clients/bus/index.js +8 -0
- package/clients/bus/index.ts +9 -0
- package/clients/bus/integration.js +158 -0
- package/clients/bus/integration.ts +214 -0
- package/clients/complexity-client.js +13 -7
- package/clients/complexity-client.ts +13 -7
- package/clients/config-validator.js +465 -0
- package/clients/config-validator.ts +558 -0
- package/clients/dependency-checker.js +4 -10
- package/clients/dependency-checker.ts +4 -10
- package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
- package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
- package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
- package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
- package/clients/dispatch/bus-dispatcher.js +177 -0
- package/clients/dispatch/bus-dispatcher.ts +251 -0
- package/clients/dispatch/dispatcher.edge.test.js +82 -0
- package/clients/dispatch/dispatcher.edge.test.ts +100 -0
- package/clients/dispatch/dispatcher.format.test.js +46 -0
- package/clients/dispatch/dispatcher.format.test.ts +58 -0
- package/clients/dispatch/dispatcher.inline.test.js +74 -0
- package/clients/dispatch/dispatcher.inline.test.ts +93 -0
- package/clients/dispatch/dispatcher.js +19 -53
- package/clients/dispatch/dispatcher.ts +20 -67
- package/clients/dispatch/plan.js +9 -4
- package/clients/dispatch/plan.ts +9 -4
- package/clients/dispatch/runners/architect.js +21 -7
- package/clients/dispatch/runners/architect.test.js +138 -0
- package/clients/dispatch/runners/architect.test.ts +162 -0
- package/clients/dispatch/runners/architect.ts +22 -7
- package/clients/dispatch/runners/ast-grep-napi.js +462 -0
- package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
- package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
- package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
- package/clients/dispatch/runners/ast-grep.js +62 -19
- package/clients/dispatch/runners/ast-grep.ts +70 -18
- package/clients/dispatch/runners/biome.js +29 -53
- package/clients/dispatch/runners/biome.ts +29 -63
- package/clients/dispatch/runners/config-validation.js +67 -0
- package/clients/dispatch/runners/config-validation.ts +82 -0
- package/clients/dispatch/runners/go-vet.js +4 -28
- package/clients/dispatch/runners/go-vet.ts +4 -32
- package/clients/dispatch/runners/index.js +30 -10
- package/clients/dispatch/runners/index.ts +30 -10
- package/clients/dispatch/runners/oxlint.js +141 -0
- package/clients/dispatch/runners/oxlint.test.js +230 -0
- package/clients/dispatch/runners/oxlint.test.ts +303 -0
- package/clients/dispatch/runners/oxlint.ts +175 -0
- package/clients/dispatch/runners/pyright.js +40 -70
- package/clients/dispatch/runners/pyright.test.js +16 -2
- package/clients/dispatch/runners/pyright.test.ts +14 -2
- package/clients/dispatch/runners/pyright.ts +48 -91
- package/clients/dispatch/runners/python-slop.js +97 -0
- package/clients/dispatch/runners/python-slop.test.js +203 -0
- package/clients/dispatch/runners/python-slop.test.ts +298 -0
- package/clients/dispatch/runners/python-slop.ts +124 -0
- package/clients/dispatch/runners/ruff.js +18 -71
- package/clients/dispatch/runners/ruff.ts +19 -79
- package/clients/dispatch/runners/rust-clippy.js +28 -32
- package/clients/dispatch/runners/rust-clippy.ts +29 -31
- package/clients/dispatch/runners/scan_codebase.test.js +89 -0
- package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
- package/clients/dispatch/runners/shellcheck.js +147 -0
- package/clients/dispatch/runners/shellcheck.test.js +98 -0
- package/clients/dispatch/runners/shellcheck.test.ts +129 -0
- package/clients/dispatch/runners/shellcheck.ts +188 -0
- package/clients/dispatch/runners/similarity.js +230 -0
- package/clients/dispatch/runners/similarity.ts +339 -0
- package/clients/dispatch/runners/spellcheck.js +106 -0
- package/clients/dispatch/runners/spellcheck.test.js +158 -0
- package/clients/dispatch/runners/spellcheck.test.ts +214 -0
- package/clients/dispatch/runners/spellcheck.ts +136 -0
- package/clients/dispatch/runners/tree-sitter.js +107 -0
- package/clients/dispatch/runners/tree-sitter.ts +135 -0
- package/clients/dispatch/runners/ts-lsp.js +104 -33
- package/clients/dispatch/runners/ts-lsp.ts +120 -38
- package/clients/dispatch/runners/ts-slop.js +113 -0
- package/clients/dispatch/runners/ts-slop.test.js +180 -0
- package/clients/dispatch/runners/ts-slop.test.ts +230 -0
- package/clients/dispatch/runners/ts-slop.ts +142 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
- package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
- package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
- package/clients/dispatch/runners/utils.js +2 -4
- package/clients/dispatch/runners/utils.ts +2 -4
- package/clients/dispatch/types.ts +1 -1
- package/clients/dispatch/utils/format-utils.js +49 -0
- package/clients/dispatch/utils/format-utils.ts +60 -0
- package/clients/dogfood.test.js +201 -0
- package/clients/dogfood.test.ts +269 -0
- package/clients/file-time.js +152 -0
- package/clients/file-time.ts +208 -0
- package/clients/file-utils.js +40 -0
- package/clients/file-utils.ts +44 -0
- package/clients/fix-scanners.js +10 -20
- package/clients/fix-scanners.ts +10 -22
- package/clients/format-service.js +172 -0
- package/clients/format-service.ts +254 -0
- package/clients/formatters.js +435 -0
- package/clients/formatters.ts +508 -0
- package/clients/go-client.js +5 -14
- package/clients/go-client.ts +5 -13
- package/clients/installer/index.js +356 -0
- package/clients/installer/index.ts +426 -0
- package/clients/jscpd-client.js +11 -9
- package/clients/jscpd-client.ts +12 -8
- package/clients/knip-client.js +3 -7
- package/clients/knip-client.ts +3 -6
- package/clients/lsp/__tests__/client.test.js +325 -0
- package/clients/lsp/__tests__/client.test.ts +434 -0
- package/clients/lsp/__tests__/config.test.js +166 -0
- package/clients/lsp/__tests__/config.test.ts +209 -0
- package/clients/lsp/__tests__/error-recovery.test.js +213 -0
- package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
- package/clients/lsp/__tests__/integration.test.js +127 -0
- package/clients/lsp/__tests__/integration.test.ts +160 -0
- package/clients/lsp/__tests__/launch.test.js +260 -0
- package/clients/lsp/__tests__/launch.test.ts +329 -0
- package/clients/lsp/__tests__/server.test.js +259 -0
- package/clients/lsp/__tests__/server.test.ts +332 -0
- package/clients/lsp/__tests__/service.test.js +417 -0
- package/clients/lsp/__tests__/service.test.ts +499 -0
- package/clients/lsp/client.js +235 -0
- package/clients/lsp/client.ts +328 -0
- package/clients/lsp/config.js +115 -0
- package/clients/lsp/config.ts +149 -0
- package/clients/lsp/index.js +222 -0
- package/clients/lsp/index.ts +280 -0
- package/clients/lsp/installer/index.js +391 -0
- package/clients/lsp/interactive-install.js +210 -0
- package/clients/lsp/interactive-install.ts +251 -0
- package/clients/lsp/language.js +170 -0
- package/clients/lsp/language.ts +216 -0
- package/clients/lsp/launch.js +174 -0
- package/clients/lsp/launch.ts +240 -0
- package/clients/lsp/lsp/launch.js +116 -0
- package/clients/lsp/lsp/server.js +532 -0
- package/clients/lsp/lsp-index.js +10 -0
- package/clients/lsp/lsp-index.ts +11 -0
- package/clients/lsp/path-utils.js +48 -0
- package/clients/lsp/path-utils.ts +52 -0
- package/clients/lsp/server.js +615 -0
- package/clients/lsp/server.ts +800 -0
- package/clients/lsp/test-py-spawn/requirements.txt +1 -0
- package/clients/lsp/test-py-spawn/test.py +3 -0
- package/clients/lsp/test-py-svc/requirements.txt +1 -0
- package/clients/lsp/test-py-svc/test.py +3 -0
- package/clients/lsp/test-python-project/requirements.txt +1 -0
- package/clients/lsp/test-python-project/test.py +5 -0
- package/clients/metrics-history.js +2 -2
- package/clients/metrics-history.ts +2 -2
- package/clients/production-readiness.js +522 -0
- package/clients/production-readiness.ts +556 -0
- package/clients/project-index.js +255 -0
- package/clients/project-index.ts +383 -0
- package/clients/project-metadata.js +531 -0
- package/clients/project-metadata.ts +624 -0
- package/clients/ruff-client.js +56 -16
- package/clients/ruff-client.ts +72 -15
- package/clients/runner-tracker.js +152 -0
- package/clients/runner-tracker.ts +213 -0
- package/clients/rust-client.js +4 -11
- package/clients/rust-client.ts +5 -11
- package/clients/safe-spawn.js +96 -0
- package/clients/safe-spawn.ts +128 -0
- package/clients/scan-architectural-debt.js +3 -6
- package/clients/scan-architectural-debt.ts +3 -6
- package/clients/scan-utils.js +5 -20
- package/clients/scan-utils.ts +5 -29
- package/clients/secrets-scanner.js +3 -17
- package/clients/secrets-scanner.ts +4 -20
- package/clients/services/__tests__/effect-integration.test.js +86 -0
- package/clients/services/__tests__/effect-integration.test.ts +111 -0
- package/clients/services/effect-integration.js +194 -0
- package/clients/services/effect-integration.ts +268 -0
- package/clients/services/index.js +7 -0
- package/clients/services/index.ts +8 -0
- package/clients/services/runner-service.js +105 -0
- package/clients/services/runner-service.ts +179 -0
- package/clients/sg-runner.js +87 -13
- package/clients/sg-runner.ts +97 -13
- package/clients/state-matrix.js +160 -0
- package/clients/state-matrix.ts +202 -0
- package/clients/subprocess-client.js +10 -9
- package/clients/subprocess-client.ts +10 -8
- package/clients/test-runner-client.js +3 -7
- package/clients/test-runner-client.ts +3 -6
- package/clients/tool-availability.js +4 -10
- package/clients/tool-availability.ts +4 -9
- package/clients/tree-sitter-client.js +564 -0
- package/clients/tree-sitter-client.ts +797 -0
- package/clients/tree-sitter-query-loader.js +355 -0
- package/clients/tree-sitter-query-loader.ts +425 -0
- package/clients/type-coverage-client.js +3 -7
- package/clients/type-coverage-client.ts +3 -6
- package/clients/typescript-client.codefix.test.js +157 -0
- package/clients/typescript-client.codefix.test.ts +186 -0
- package/clients/typescript-client.js +43 -0
- package/clients/typescript-client.ts +98 -0
- package/commands/booboo.js +799 -219
- package/commands/booboo.ts +1004 -225
- package/commands/clients/ast-grep-client.js +250 -0
- package/commands/clients/ast-grep-parser.js +86 -0
- package/commands/clients/ast-grep-rule-manager.js +91 -0
- package/commands/clients/ast-grep-types.js +9 -0
- package/commands/clients/biome-client.js +380 -0
- package/commands/clients/complexity-client.js +667 -0
- package/commands/clients/file-kinds.js +177 -0
- package/commands/clients/file-utils.js +40 -0
- package/commands/clients/jscpd-client.js +169 -0
- package/commands/clients/knip-client.js +211 -0
- package/commands/clients/ruff-client.js +297 -0
- package/commands/clients/safe-spawn.js +88 -0
- package/commands/clients/scan-utils.js +83 -0
- package/commands/clients/sg-runner.js +190 -0
- package/commands/clients/types.js +11 -0
- package/commands/clients/typescript-client.js +505 -0
- package/commands/fix-from-booboo.js +398 -0
- package/commands/fix-from-booboo.ts +485 -0
- package/commands/fix-simplified.js +618 -0
- package/commands/fix-simplified.ts +768 -0
- package/commands/rate.js +10 -14
- package/commands/rate.ts +9 -16
- package/default-architect.yaml +59 -15
- package/index.ts +342 -429
- package/package.json +16 -3
- package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
- package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
- package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
- package/rules/python-slop-rules/.sgconfig.yml +4 -0
- package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
- package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
- package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
- package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
- package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
- package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
- package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
- package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
- package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
- package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
- package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
- package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
- package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
- package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
- package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
- package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
- package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
- package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
- package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
- package/rules/ts-slop-rules/.sgconfig.yml +4 -0
- package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
- package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
- package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
- package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
- package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
- package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
- package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
- package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
- package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
- package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
- package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
- package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
- package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
- package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
- package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
- package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
- package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
- package/skills/ast-grep/SKILL.md +182 -0
- package/clients/dispatch/runners/secrets.js +0 -109
- package/commands/fix.js +0 -244
- package/commands/fix.ts +0 -373
- package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
package/clients/ruff-client.js
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* Requires: pip install ruff
|
|
8
8
|
* Docs: https://docs.astral.sh/ruff/
|
|
9
9
|
*/
|
|
10
|
-
import { spawnSync } from "node:child_process";
|
|
11
10
|
import * as fs from "node:fs";
|
|
12
11
|
import * as path from "node:path";
|
|
13
12
|
import { isFileKind } from "./file-kinds.js";
|
|
13
|
+
import { safeSpawn } from "./safe-spawn.js";
|
|
14
14
|
// --- Client ---
|
|
15
15
|
export class RuffClient {
|
|
16
16
|
constructor(verbose = false) {
|
|
@@ -26,10 +26,8 @@ export class RuffClient {
|
|
|
26
26
|
if (this.ruffAvailable !== null)
|
|
27
27
|
return this.ruffAvailable;
|
|
28
28
|
try {
|
|
29
|
-
const result =
|
|
30
|
-
encoding: "utf-8",
|
|
29
|
+
const result = safeSpawn("ruff", ["--version"], {
|
|
31
30
|
timeout: 5000,
|
|
32
|
-
shell: true,
|
|
33
31
|
});
|
|
34
32
|
this.ruffAvailable = !result.error && result.status === 0;
|
|
35
33
|
if (this.ruffAvailable) {
|
|
@@ -58,7 +56,7 @@ export class RuffClient {
|
|
|
58
56
|
if (!fs.existsSync(absolutePath))
|
|
59
57
|
return [];
|
|
60
58
|
try {
|
|
61
|
-
const result =
|
|
59
|
+
const result = safeSpawn("ruff", [
|
|
62
60
|
"check",
|
|
63
61
|
"--output-format",
|
|
64
62
|
"json",
|
|
@@ -66,9 +64,7 @@ export class RuffClient {
|
|
|
66
64
|
"py310",
|
|
67
65
|
absolutePath,
|
|
68
66
|
], {
|
|
69
|
-
encoding: "utf-8",
|
|
70
67
|
timeout: 10000,
|
|
71
|
-
shell: true,
|
|
72
68
|
});
|
|
73
69
|
// ruff exits 1 when it finds issues (normal)
|
|
74
70
|
const output = result.stdout || "";
|
|
@@ -91,10 +87,8 @@ export class RuffClient {
|
|
|
91
87
|
if (!fs.existsSync(absolutePath))
|
|
92
88
|
return "";
|
|
93
89
|
try {
|
|
94
|
-
const result =
|
|
95
|
-
encoding: "utf-8",
|
|
90
|
+
const result = safeSpawn("ruff", ["format", "--check", "--diff", absolutePath], {
|
|
96
91
|
timeout: 10000,
|
|
97
|
-
shell: true,
|
|
98
92
|
});
|
|
99
93
|
// ruff format --check exits 1 when changes needed
|
|
100
94
|
if (result.status === 0)
|
|
@@ -136,10 +130,8 @@ export class RuffClient {
|
|
|
136
130
|
try {
|
|
137
131
|
const beforeDiags = this.checkFile(filePath);
|
|
138
132
|
const fixableCount = beforeDiags.filter((d) => d.fixable).length;
|
|
139
|
-
const result =
|
|
140
|
-
encoding: "utf-8",
|
|
133
|
+
const result = safeSpawn("ruff", ["check", "--fix", absolutePath], {
|
|
141
134
|
timeout: 15000,
|
|
142
|
-
shell: true,
|
|
143
135
|
});
|
|
144
136
|
if (result.error) {
|
|
145
137
|
return {
|
|
@@ -160,6 +152,56 @@ export class RuffClient {
|
|
|
160
152
|
return { success: false, changed: false, fixed: 0, error: err.message };
|
|
161
153
|
}
|
|
162
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Fix multiple Python files at once (much faster than file-by-file)
|
|
157
|
+
*/
|
|
158
|
+
fixFiles(filePaths) {
|
|
159
|
+
if (!this.isAvailable()) {
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
fixed: 0,
|
|
163
|
+
changed: 0,
|
|
164
|
+
error: "Ruff not available",
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// Filter to existing Python files
|
|
168
|
+
const validFiles = filePaths
|
|
169
|
+
.map(f => path.resolve(f))
|
|
170
|
+
.filter(f => fs.existsSync(f) && f.endsWith(".py"));
|
|
171
|
+
if (validFiles.length === 0) {
|
|
172
|
+
return { success: true, fixed: 0, changed: 0 };
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
// Count fixable issues before fixing
|
|
176
|
+
let totalFixable = 0;
|
|
177
|
+
for (const file of validFiles) {
|
|
178
|
+
const diags = this.checkFile(file);
|
|
179
|
+
totalFixable += diags.filter(d => d.fixable).length;
|
|
180
|
+
}
|
|
181
|
+
// Run ruff once on all files - much faster than per file
|
|
182
|
+
const result = safeSpawn("ruff", ["check", "--fix", ...validFiles], {
|
|
183
|
+
timeout: 60000, // Longer timeout for batch
|
|
184
|
+
});
|
|
185
|
+
if (result.error) {
|
|
186
|
+
return {
|
|
187
|
+
success: false,
|
|
188
|
+
fixed: 0,
|
|
189
|
+
changed: 0,
|
|
190
|
+
error: result.error.message,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
this.log(`Fixed ${totalFixable} issue(s) in ${validFiles.length} file(s)`);
|
|
194
|
+
return { success: true, fixed: totalFixable, changed: validFiles.length };
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
fixed: 0,
|
|
200
|
+
changed: 0,
|
|
201
|
+
error: err.message,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
163
205
|
/**
|
|
164
206
|
* Format a Python file (writes to disk)
|
|
165
207
|
*/
|
|
@@ -171,10 +213,8 @@ export class RuffClient {
|
|
|
171
213
|
return { success: false, changed: false, error: "File not found" };
|
|
172
214
|
const content = fs.readFileSync(absolutePath, "utf-8");
|
|
173
215
|
try {
|
|
174
|
-
const result =
|
|
175
|
-
encoding: "utf-8",
|
|
216
|
+
const result = safeSpawn("ruff", ["format", absolutePath], {
|
|
176
217
|
timeout: 10000,
|
|
177
|
-
shell: true,
|
|
178
218
|
});
|
|
179
219
|
if (result.error) {
|
|
180
220
|
return { success: false, changed: false, error: result.error.message };
|
package/clients/ruff-client.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { spawnSync } from "node:child_process";
|
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
13
|
import * as path from "node:path";
|
|
14
14
|
import { isFileKind } from "./file-kinds.js";
|
|
15
|
+
import { safeSpawn } from "./safe-spawn.js";
|
|
15
16
|
|
|
16
17
|
// --- Types ---
|
|
17
18
|
|
|
@@ -56,10 +57,8 @@ export class RuffClient {
|
|
|
56
57
|
if (this.ruffAvailable !== null) return this.ruffAvailable;
|
|
57
58
|
|
|
58
59
|
try {
|
|
59
|
-
const result =
|
|
60
|
-
encoding: "utf-8",
|
|
60
|
+
const result = safeSpawn("ruff", ["--version"], {
|
|
61
61
|
timeout: 5000,
|
|
62
|
-
shell: true,
|
|
63
62
|
});
|
|
64
63
|
this.ruffAvailable = !result.error && result.status === 0;
|
|
65
64
|
if (this.ruffAvailable) {
|
|
@@ -90,7 +89,7 @@ export class RuffClient {
|
|
|
90
89
|
if (!fs.existsSync(absolutePath)) return [];
|
|
91
90
|
|
|
92
91
|
try {
|
|
93
|
-
const result =
|
|
92
|
+
const result = safeSpawn(
|
|
94
93
|
"ruff",
|
|
95
94
|
[
|
|
96
95
|
"check",
|
|
@@ -101,9 +100,7 @@ export class RuffClient {
|
|
|
101
100
|
absolutePath,
|
|
102
101
|
],
|
|
103
102
|
{
|
|
104
|
-
encoding: "utf-8",
|
|
105
103
|
timeout: 10000,
|
|
106
|
-
shell: true,
|
|
107
104
|
},
|
|
108
105
|
);
|
|
109
106
|
|
|
@@ -128,13 +125,11 @@ export class RuffClient {
|
|
|
128
125
|
if (!fs.existsSync(absolutePath)) return "";
|
|
129
126
|
|
|
130
127
|
try {
|
|
131
|
-
const result =
|
|
128
|
+
const result = safeSpawn(
|
|
132
129
|
"ruff",
|
|
133
130
|
["format", "--check", "--diff", absolutePath],
|
|
134
131
|
{
|
|
135
|
-
encoding: "utf-8",
|
|
136
132
|
timeout: 10000,
|
|
137
|
-
shell: true,
|
|
138
133
|
},
|
|
139
134
|
);
|
|
140
135
|
|
|
@@ -187,10 +182,8 @@ export class RuffClient {
|
|
|
187
182
|
const beforeDiags = this.checkFile(filePath);
|
|
188
183
|
const fixableCount = beforeDiags.filter((d) => d.fixable).length;
|
|
189
184
|
|
|
190
|
-
const result =
|
|
191
|
-
encoding: "utf-8",
|
|
185
|
+
const result = safeSpawn("ruff", ["check", "--fix", absolutePath], {
|
|
192
186
|
timeout: 15000,
|
|
193
|
-
shell: true,
|
|
194
187
|
});
|
|
195
188
|
|
|
196
189
|
if (result.error) {
|
|
@@ -217,6 +210,72 @@ export class RuffClient {
|
|
|
217
210
|
}
|
|
218
211
|
}
|
|
219
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Fix multiple Python files at once (much faster than file-by-file)
|
|
215
|
+
*/
|
|
216
|
+
fixFiles(filePaths: string[]): {
|
|
217
|
+
success: boolean;
|
|
218
|
+
fixed: number;
|
|
219
|
+
changed: number;
|
|
220
|
+
error?: string;
|
|
221
|
+
} {
|
|
222
|
+
if (!this.isAvailable()) {
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
fixed: 0,
|
|
226
|
+
changed: 0,
|
|
227
|
+
error: "Ruff not available",
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Filter to existing Python files
|
|
232
|
+
const validFiles = filePaths
|
|
233
|
+
.map(f => path.resolve(f))
|
|
234
|
+
.filter(f => fs.existsSync(f) && f.endsWith(".py"));
|
|
235
|
+
|
|
236
|
+
if (validFiles.length === 0) {
|
|
237
|
+
return { success: true, fixed: 0, changed: 0 };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
// Count fixable issues before fixing
|
|
242
|
+
let totalFixable = 0;
|
|
243
|
+
for (const file of validFiles) {
|
|
244
|
+
const diags = this.checkFile(file);
|
|
245
|
+
totalFixable += diags.filter(d => d.fixable).length;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Run ruff once on all files - much faster than per file
|
|
249
|
+
const result = safeSpawn(
|
|
250
|
+
"ruff",
|
|
251
|
+
["check", "--fix", ...validFiles],
|
|
252
|
+
{
|
|
253
|
+
timeout: 60000, // Longer timeout for batch
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
if (result.error) {
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
fixed: 0,
|
|
261
|
+
changed: 0,
|
|
262
|
+
error: result.error.message,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
this.log(`Fixed ${totalFixable} issue(s) in ${validFiles.length} file(s)`);
|
|
267
|
+
|
|
268
|
+
return { success: true, fixed: totalFixable, changed: validFiles.length };
|
|
269
|
+
} catch (err: any) {
|
|
270
|
+
return {
|
|
271
|
+
success: false,
|
|
272
|
+
fixed: 0,
|
|
273
|
+
changed: 0,
|
|
274
|
+
error: err.message,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
220
279
|
/**
|
|
221
280
|
* Format a Python file (writes to disk)
|
|
222
281
|
*/
|
|
@@ -235,10 +294,8 @@ export class RuffClient {
|
|
|
235
294
|
const content = fs.readFileSync(absolutePath, "utf-8");
|
|
236
295
|
|
|
237
296
|
try {
|
|
238
|
-
const result =
|
|
239
|
-
encoding: "utf-8",
|
|
297
|
+
const result = safeSpawn("ruff", ["format", absolutePath], {
|
|
240
298
|
timeout: 10000,
|
|
241
|
-
shell: true,
|
|
242
299
|
});
|
|
243
300
|
|
|
244
301
|
if (result.error) {
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner tracker for /lens-booboo and related commands
|
|
3
|
+
*
|
|
4
|
+
* Tracks execution time and findings for each analysis runner,
|
|
5
|
+
* producing a summary of what each runner found.
|
|
6
|
+
*/
|
|
7
|
+
export class RunnerTracker {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.runners = [];
|
|
10
|
+
this.onProgress = options?.onProgress;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Run a function with timing and tracking
|
|
14
|
+
*/
|
|
15
|
+
async run(name, runFn, options) {
|
|
16
|
+
const startMs = Date.now();
|
|
17
|
+
const index = this.runners.length;
|
|
18
|
+
const runner = {
|
|
19
|
+
name,
|
|
20
|
+
status: "running",
|
|
21
|
+
findings: 0,
|
|
22
|
+
elapsedMs: 0,
|
|
23
|
+
};
|
|
24
|
+
this.runners.push(runner);
|
|
25
|
+
// Notify start
|
|
26
|
+
this.onProgress?.(runner, index);
|
|
27
|
+
try {
|
|
28
|
+
const result = await runFn();
|
|
29
|
+
const elapsedMs = Date.now() - startMs;
|
|
30
|
+
// Extract findings if result has it
|
|
31
|
+
const findings = typeof result === "object" &&
|
|
32
|
+
result !== null &&
|
|
33
|
+
"findings" in result &&
|
|
34
|
+
typeof result.findings === "number"
|
|
35
|
+
? result.findings
|
|
36
|
+
: 0;
|
|
37
|
+
runner.status = "done";
|
|
38
|
+
runner.elapsedMs = elapsedMs;
|
|
39
|
+
runner.findings = findings;
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const elapsedMs = Date.now() - startMs;
|
|
44
|
+
runner.status = "error";
|
|
45
|
+
runner.elapsedMs = elapsedMs;
|
|
46
|
+
runner.message = String(err);
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Mark a runner as skipped (for when preconditions aren't met)
|
|
52
|
+
*/
|
|
53
|
+
skip(name, message) {
|
|
54
|
+
this.runners.push({
|
|
55
|
+
name,
|
|
56
|
+
status: "skipped",
|
|
57
|
+
findings: 0,
|
|
58
|
+
elapsedMs: 0,
|
|
59
|
+
message,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Update findings for a runner (useful when findings are discovered asynchronously)
|
|
64
|
+
*/
|
|
65
|
+
updateFindings(runnerName, findings) {
|
|
66
|
+
const runner = this.runners.find((r) => r.name === runnerName);
|
|
67
|
+
if (runner) {
|
|
68
|
+
runner.findings = findings;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get all tracked runners
|
|
73
|
+
*/
|
|
74
|
+
getRunners() {
|
|
75
|
+
return [...this.runners];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get summary statistics
|
|
79
|
+
*/
|
|
80
|
+
getStats() {
|
|
81
|
+
return {
|
|
82
|
+
total: this.runners.length,
|
|
83
|
+
done: this.runners.filter((r) => r.status === "done").length,
|
|
84
|
+
skipped: this.runners.filter((r) => r.status === "skipped").length,
|
|
85
|
+
errors: this.runners.filter((r) => r.status === "error").length,
|
|
86
|
+
totalFindings: this.runners.reduce((sum, r) => sum + r.findings, 0),
|
|
87
|
+
totalTimeMs: this.runners.reduce((sum, r) => sum + r.elapsedMs, 0),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Format a single runner result for display
|
|
92
|
+
*/
|
|
93
|
+
formatRunner(runner, index) {
|
|
94
|
+
const prefix = index !== undefined ? `[${index + 1}] ` : "";
|
|
95
|
+
const statusIcon = runner.status === "done"
|
|
96
|
+
? "✓"
|
|
97
|
+
: runner.status === "skipped"
|
|
98
|
+
? "⊘"
|
|
99
|
+
: runner.status === "error"
|
|
100
|
+
? "✗"
|
|
101
|
+
: "○";
|
|
102
|
+
const findings = runner.findings > 0 ? ` (${runner.findings} findings)` : "";
|
|
103
|
+
const time = this.formatElapsed(runner.elapsedMs);
|
|
104
|
+
const message = runner.message ? ` — ${runner.message}` : "";
|
|
105
|
+
return `${prefix}${statusIcon} ${runner.name}${findings} — ${time}${message}`;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Format all runners as a summary table
|
|
109
|
+
*/
|
|
110
|
+
formatSummary() {
|
|
111
|
+
const lines = ["📊 Runner Summary:", ""];
|
|
112
|
+
for (let i = 0; i < this.runners.length; i++) {
|
|
113
|
+
lines.push(` ${this.formatRunner(this.runners[i], i)}`);
|
|
114
|
+
}
|
|
115
|
+
const stats = this.getStats();
|
|
116
|
+
lines.push("");
|
|
117
|
+
lines.push(` Total: ${stats.totalFindings} findings in ${this.formatElapsed(stats.totalTimeMs)}`);
|
|
118
|
+
return lines.join("\n");
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Format elapsed time in human-readable form
|
|
122
|
+
*/
|
|
123
|
+
formatElapsed(ms) {
|
|
124
|
+
if (ms < 1000)
|
|
125
|
+
return `${ms}ms`;
|
|
126
|
+
if (ms < 60000)
|
|
127
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
128
|
+
const mins = Math.floor(ms / 60000);
|
|
129
|
+
const secs = ((ms % 60000) / 1000).toFixed(0);
|
|
130
|
+
return `${mins}m${secs.padStart(2, "0")}s`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Convenience function to create a tracker and run a sequence
|
|
135
|
+
*/
|
|
136
|
+
export async function runSequence(sequence, onProgress) {
|
|
137
|
+
const tracker = new RunnerTracker({ onProgress });
|
|
138
|
+
const results = [];
|
|
139
|
+
for (const item of sequence) {
|
|
140
|
+
const result = await tracker.run(item.name, item.run, {
|
|
141
|
+
index: results.length,
|
|
142
|
+
total: sequence.length,
|
|
143
|
+
});
|
|
144
|
+
results.push(result);
|
|
145
|
+
// Update findings if handler provided
|
|
146
|
+
if (item.onFindings) {
|
|
147
|
+
const findings = item.onFindings(result);
|
|
148
|
+
tracker.updateFindings(item.name, findings);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return { results, tracker };
|
|
152
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner tracker for /lens-booboo and related commands
|
|
3
|
+
*
|
|
4
|
+
* Tracks execution time and findings for each analysis runner,
|
|
5
|
+
* producing a summary of what each runner found.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface TrackedRunner {
|
|
9
|
+
name: string;
|
|
10
|
+
status: "running" | "done" | "skipped" | "error";
|
|
11
|
+
findings: number;
|
|
12
|
+
elapsedMs: number;
|
|
13
|
+
message?: string;
|
|
14
|
+
/** Severity indicator for the runner (derived from findings) */
|
|
15
|
+
severity?: "error" | "warning" | "info";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RunOptions {
|
|
19
|
+
/** Index in the sequence (for "[2/9]" style progress) */
|
|
20
|
+
index?: number;
|
|
21
|
+
/** Total number of runners (for "[2/9]" style progress) */
|
|
22
|
+
total?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class RunnerTracker {
|
|
26
|
+
private runners: TrackedRunner[] = [];
|
|
27
|
+
private onProgress?: (runner: TrackedRunner, index: number) => void;
|
|
28
|
+
|
|
29
|
+
constructor(options?: { onProgress?: (runner: TrackedRunner, index: number) => void }) {
|
|
30
|
+
this.onProgress = options?.onProgress;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Run a function with timing and tracking
|
|
35
|
+
*/
|
|
36
|
+
async run<T>(
|
|
37
|
+
name: string,
|
|
38
|
+
runFn: () => Promise<T> | T,
|
|
39
|
+
options?: RunOptions,
|
|
40
|
+
): Promise<T> {
|
|
41
|
+
const startMs = Date.now();
|
|
42
|
+
const index = this.runners.length;
|
|
43
|
+
const runner: TrackedRunner = {
|
|
44
|
+
name,
|
|
45
|
+
status: "running",
|
|
46
|
+
findings: 0,
|
|
47
|
+
elapsedMs: 0,
|
|
48
|
+
};
|
|
49
|
+
this.runners.push(runner);
|
|
50
|
+
|
|
51
|
+
// Notify start
|
|
52
|
+
this.onProgress?.(runner, index);
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const result = await runFn();
|
|
56
|
+
const elapsedMs = Date.now() - startMs;
|
|
57
|
+
|
|
58
|
+
// Extract findings if result has it
|
|
59
|
+
const findings =
|
|
60
|
+
typeof result === "object" &&
|
|
61
|
+
result !== null &&
|
|
62
|
+
"findings" in result &&
|
|
63
|
+
typeof (result as { findings?: number }).findings === "number"
|
|
64
|
+
? (result as { findings: number }).findings
|
|
65
|
+
: 0;
|
|
66
|
+
|
|
67
|
+
runner.status = "done";
|
|
68
|
+
runner.elapsedMs = elapsedMs;
|
|
69
|
+
runner.findings = findings;
|
|
70
|
+
|
|
71
|
+
return result;
|
|
72
|
+
} catch (err) {
|
|
73
|
+
const elapsedMs = Date.now() - startMs;
|
|
74
|
+
runner.status = "error";
|
|
75
|
+
runner.elapsedMs = elapsedMs;
|
|
76
|
+
runner.message = String(err);
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Mark a runner as skipped (for when preconditions aren't met)
|
|
83
|
+
*/
|
|
84
|
+
skip(name: string, message?: string): void {
|
|
85
|
+
this.runners.push({
|
|
86
|
+
name,
|
|
87
|
+
status: "skipped",
|
|
88
|
+
findings: 0,
|
|
89
|
+
elapsedMs: 0,
|
|
90
|
+
message,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Update findings for a runner (useful when findings are discovered asynchronously)
|
|
96
|
+
*/
|
|
97
|
+
updateFindings(runnerName: string, findings: number): void {
|
|
98
|
+
const runner = this.runners.find((r) => r.name === runnerName);
|
|
99
|
+
if (runner) {
|
|
100
|
+
runner.findings = findings;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get all tracked runners
|
|
106
|
+
*/
|
|
107
|
+
getRunners(): TrackedRunner[] {
|
|
108
|
+
return [...this.runners];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get summary statistics
|
|
113
|
+
*/
|
|
114
|
+
getStats(): {
|
|
115
|
+
total: number;
|
|
116
|
+
done: number;
|
|
117
|
+
skipped: number;
|
|
118
|
+
errors: number;
|
|
119
|
+
totalFindings: number;
|
|
120
|
+
totalTimeMs: number;
|
|
121
|
+
} {
|
|
122
|
+
return {
|
|
123
|
+
total: this.runners.length,
|
|
124
|
+
done: this.runners.filter((r) => r.status === "done").length,
|
|
125
|
+
skipped: this.runners.filter((r) => r.status === "skipped").length,
|
|
126
|
+
errors: this.runners.filter((r) => r.status === "error").length,
|
|
127
|
+
totalFindings: this.runners.reduce((sum, r) => sum + r.findings, 0),
|
|
128
|
+
totalTimeMs: this.runners.reduce((sum, r) => sum + r.elapsedMs, 0),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Format a single runner result for display
|
|
134
|
+
*/
|
|
135
|
+
formatRunner(runner: TrackedRunner, index?: number): string {
|
|
136
|
+
const prefix = index !== undefined ? `[${index + 1}] ` : "";
|
|
137
|
+
const statusIcon =
|
|
138
|
+
runner.status === "done"
|
|
139
|
+
? "✓"
|
|
140
|
+
: runner.status === "skipped"
|
|
141
|
+
? "⊘"
|
|
142
|
+
: runner.status === "error"
|
|
143
|
+
? "✗"
|
|
144
|
+
: "○";
|
|
145
|
+
const findings =
|
|
146
|
+
runner.findings > 0 ? ` (${runner.findings} findings)` : "";
|
|
147
|
+
const time = this.formatElapsed(runner.elapsedMs);
|
|
148
|
+
const message = runner.message ? ` — ${runner.message}` : "";
|
|
149
|
+
|
|
150
|
+
return `${prefix}${statusIcon} ${runner.name}${findings} — ${time}${message}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Format all runners as a summary table
|
|
155
|
+
*/
|
|
156
|
+
formatSummary(): string {
|
|
157
|
+
const lines = ["📊 Runner Summary:", ""];
|
|
158
|
+
|
|
159
|
+
for (let i = 0; i < this.runners.length; i++) {
|
|
160
|
+
lines.push(` ${this.formatRunner(this.runners[i], i)}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const stats = this.getStats();
|
|
164
|
+
lines.push("");
|
|
165
|
+
lines.push(
|
|
166
|
+
` Total: ${stats.totalFindings} findings in ${this.formatElapsed(stats.totalTimeMs)}`,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
return lines.join("\n");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Format elapsed time in human-readable form
|
|
174
|
+
*/
|
|
175
|
+
private formatElapsed(ms: number): string {
|
|
176
|
+
if (ms < 1000) return `${ms}ms`;
|
|
177
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
178
|
+
const mins = Math.floor(ms / 60000);
|
|
179
|
+
const secs = ((ms % 60000) / 1000).toFixed(0);
|
|
180
|
+
return `${mins}m${secs.padStart(2, "0")}s`;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Convenience function to create a tracker and run a sequence
|
|
186
|
+
*/
|
|
187
|
+
export async function runSequence<T>(
|
|
188
|
+
sequence: Array<{
|
|
189
|
+
name: string;
|
|
190
|
+
run: () => Promise<T> | T;
|
|
191
|
+
onFindings?: (result: T) => number;
|
|
192
|
+
}>,
|
|
193
|
+
onProgress?: (runner: TrackedRunner, index: number) => void,
|
|
194
|
+
): Promise<{ results: T[]; tracker: RunnerTracker }> {
|
|
195
|
+
const tracker = new RunnerTracker({ onProgress });
|
|
196
|
+
const results: T[] = [];
|
|
197
|
+
|
|
198
|
+
for (const item of sequence) {
|
|
199
|
+
const result = await tracker.run(item.name, item.run, {
|
|
200
|
+
index: results.length,
|
|
201
|
+
total: sequence.length,
|
|
202
|
+
});
|
|
203
|
+
results.push(result);
|
|
204
|
+
|
|
205
|
+
// Update findings if handler provided
|
|
206
|
+
if (item.onFindings) {
|
|
207
|
+
const findings = item.onFindings(result);
|
|
208
|
+
tracker.updateFindings(item.name, findings);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return { results, tracker };
|
|
213
|
+
}
|
package/clients/rust-client.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* Requires: cargo (rustup)
|
|
7
7
|
* Docs: https://doc.rust-lang.org/cargo/
|
|
8
8
|
*/
|
|
9
|
-
import { spawnSync } from "node:child_process";
|
|
10
9
|
import * as fs from "node:fs";
|
|
11
10
|
import * as path from "node:path";
|
|
11
|
+
import { safeSpawn } from "./safe-spawn.js";
|
|
12
12
|
// --- Common install paths ---
|
|
13
13
|
const CARGO_WINDOWS_PATHS = [
|
|
14
14
|
path.join(process.env.USERPROFILE || "", ".cargo", "bin", "cargo.exe"),
|
|
@@ -46,10 +46,8 @@ export class RustClient {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
else {
|
|
49
|
-
const result =
|
|
50
|
-
encoding: "utf-8",
|
|
49
|
+
const result = safeSpawn(p, ["--version"], {
|
|
51
50
|
timeout: 3000,
|
|
52
|
-
shell: true,
|
|
53
51
|
});
|
|
54
52
|
if (!result.error && result.status === 0) {
|
|
55
53
|
this.cargoPath = p;
|
|
@@ -92,12 +90,9 @@ export class RustClient {
|
|
|
92
90
|
if (!fs.existsSync(absolutePath))
|
|
93
91
|
return [];
|
|
94
92
|
try {
|
|
95
|
-
const
|
|
96
|
-
const result = spawnSync(cargoCmd, ["check", "--message-format", "json"], {
|
|
97
|
-
encoding: "utf-8",
|
|
93
|
+
const result = safeSpawn(cargoExe, ["check", "--message-format", "json"], {
|
|
98
94
|
timeout: 60000,
|
|
99
95
|
cwd,
|
|
100
|
-
shell: true,
|
|
101
96
|
});
|
|
102
97
|
const output = result.stdout || "";
|
|
103
98
|
return this.parseJsonOutput(output, absolutePath);
|
|
@@ -114,11 +109,9 @@ export class RustClient {
|
|
|
114
109
|
if (!this.isAvailable())
|
|
115
110
|
return [];
|
|
116
111
|
try {
|
|
117
|
-
const result =
|
|
118
|
-
encoding: "utf-8",
|
|
112
|
+
const result = safeSpawn("cargo", ["clippy", "--message-format", "json"], {
|
|
119
113
|
timeout: 60000,
|
|
120
114
|
cwd,
|
|
121
|
-
shell: true,
|
|
122
115
|
});
|
|
123
116
|
const output = result.stdout || "";
|
|
124
117
|
return this.parseJsonOutput(output, "");
|