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
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { formatDiagnostic } from "./utils/format-utils.js";
|
|
3
|
+
|
|
4
|
+
describe("formatDiagnostic inline output verification", () => {
|
|
5
|
+
it("should display complete architect messages (NOT truncated to 'No ')", () => {
|
|
6
|
+
// Simulate actual architect diagnostic
|
|
7
|
+
const diagnostic = {
|
|
8
|
+
id: "architect-1",
|
|
9
|
+
message:
|
|
10
|
+
"No absolute Windows paths — breaks CI and cross-platform builds.",
|
|
11
|
+
filePath: "/test.ts",
|
|
12
|
+
line: 5,
|
|
13
|
+
severity: "warning" as const,
|
|
14
|
+
semantic: "warning" as const,
|
|
15
|
+
tool: "architect",
|
|
16
|
+
rule: "no-absolute-windows-paths",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const output = formatDiagnostic(diagnostic);
|
|
20
|
+
|
|
21
|
+
console.log("\n=== Architect Message Output ===");
|
|
22
|
+
console.log(output);
|
|
23
|
+
console.log("=================================\n");
|
|
24
|
+
|
|
25
|
+
// Verify complete message is shown
|
|
26
|
+
expect(output).toBe(
|
|
27
|
+
" L5: No absolute Windows paths — breaks CI and cross-platform builds.",
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should display code fix messages inline correctly", () => {
|
|
32
|
+
// This is what I actually see from ts-lsp runner
|
|
33
|
+
const diagnostic = {
|
|
34
|
+
id: "ts-12-2345",
|
|
35
|
+
message:
|
|
36
|
+
"Property 'debug' is missing in type 'Config'\n💡 Quick fix: Add missing property 'debug'",
|
|
37
|
+
filePath: "/src/config.ts",
|
|
38
|
+
line: 12,
|
|
39
|
+
severity: "error" as const,
|
|
40
|
+
semantic: "blocking" as const,
|
|
41
|
+
tool: "ts-lsp",
|
|
42
|
+
rule: "TS2345",
|
|
43
|
+
fixable: true,
|
|
44
|
+
fixSuggestion: "Add missing property 'debug'",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const output = formatDiagnostic(diagnostic);
|
|
48
|
+
|
|
49
|
+
console.log("\n=== Code Fix Message Output ===");
|
|
50
|
+
console.log(output);
|
|
51
|
+
console.log("================================\n");
|
|
52
|
+
|
|
53
|
+
// Both lines should be properly indented
|
|
54
|
+
expect(output).toBe(
|
|
55
|
+
" L12: Property 'debug' is missing in type 'Config'\n 💡 Quick fix: Add missing property 'debug'",
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should prove architect 'No ' messages are complete (not noise)", () => {
|
|
60
|
+
// All the "No " messages from default-architect.yaml
|
|
61
|
+
const testMessages = [
|
|
62
|
+
"No absolute Windows paths — breaks CI and cross-platform builds.",
|
|
63
|
+
"No hardcoded localhost URLs — use environment variables or a config service.",
|
|
64
|
+
"No empty catch/except blocks. Swallowing errors makes debugging impossible — at least log the error.",
|
|
65
|
+
"No hardcoded secrets — use environment variables or a secrets manager.",
|
|
66
|
+
"No 'any' types — use 'unknown' or define a proper interface to maintain type safety.",
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
for (let i = 0; i < testMessages.length; i++) {
|
|
70
|
+
const diagnostic = {
|
|
71
|
+
id: `architect-${i}`,
|
|
72
|
+
message: testMessages[i],
|
|
73
|
+
filePath: "/test.ts",
|
|
74
|
+
line: i + 1,
|
|
75
|
+
severity: "warning" as const,
|
|
76
|
+
semantic: "warning" as const,
|
|
77
|
+
tool: "architect",
|
|
78
|
+
rule: "test",
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const output = formatDiagnostic(diagnostic);
|
|
82
|
+
|
|
83
|
+
// Each message should be complete, NOT truncated to just "No "
|
|
84
|
+
expect(output.length).toBeGreaterThan(15); // More than " L1: No "
|
|
85
|
+
expect(output).toContain("No ");
|
|
86
|
+
expect(output).toContain("—"); // Contains the em-dash explanation
|
|
87
|
+
|
|
88
|
+
// Verify it's not truncated
|
|
89
|
+
const messageAfterNo = output.split("No ")[1];
|
|
90
|
+
expect(messageAfterNo.length).toBeGreaterThan(5);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* - BaselineStore: Track pre-existing issues for delta mode
|
|
15
15
|
*/
|
|
16
16
|
import { detectFileKind } from "../file-kinds.js";
|
|
17
|
+
import { isTestFile } from "../file-utils.js";
|
|
18
|
+
import { safeSpawn } from "../safe-spawn.js";
|
|
19
|
+
import { formatDiagnostics } from "./utils/format-utils.js";
|
|
17
20
|
// --- In-Memory Baseline Store ---
|
|
18
21
|
export function createBaselineStore() {
|
|
19
22
|
const baselines = new Map();
|
|
@@ -45,10 +48,10 @@ export function getRunnersForKind(kind, filePath) {
|
|
|
45
48
|
if (!kind)
|
|
46
49
|
return [];
|
|
47
50
|
const runners = [];
|
|
48
|
-
const
|
|
51
|
+
const isTest = filePath ? isTestFile(filePath) : false;
|
|
49
52
|
for (const runner of globalRegistry.values()) {
|
|
50
53
|
// Skip runners that shouldn't run on test files
|
|
51
|
-
if (
|
|
54
|
+
if (isTest && runner.skipTestFiles)
|
|
52
55
|
continue;
|
|
53
56
|
if (runner.appliesTo.includes(kind) || runner.appliesTo.length === 0) {
|
|
54
57
|
runners.push(runner);
|
|
@@ -56,19 +59,15 @@ export function getRunnersForKind(kind, filePath) {
|
|
|
56
59
|
}
|
|
57
60
|
return runners.sort((a, b) => a.priority - b.priority);
|
|
58
61
|
}
|
|
59
|
-
function isTest(filePath) {
|
|
60
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
61
|
-
return (normalized.includes(".test.") ||
|
|
62
|
-
normalized.includes(".spec.") ||
|
|
63
|
-
normalized.includes("/test/") ||
|
|
64
|
-
normalized.includes("/tests/") ||
|
|
65
|
-
normalized.includes("__tests__/") ||
|
|
66
|
-
normalized.includes("test-utils") ||
|
|
67
|
-
normalized.startsWith("test-"));
|
|
68
|
-
}
|
|
69
62
|
export function listRunners() {
|
|
70
63
|
return Array.from(globalRegistry.values());
|
|
71
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Clear all registered runners. Used primarily for testing.
|
|
67
|
+
*/
|
|
68
|
+
export function clearRunnerRegistry() {
|
|
69
|
+
globalRegistry.clear();
|
|
70
|
+
}
|
|
72
71
|
// --- Tool Availability Cache ---
|
|
73
72
|
const toolCache = new Map();
|
|
74
73
|
function checkToolAvailability(command) {
|
|
@@ -76,11 +75,8 @@ function checkToolAvailability(command) {
|
|
|
76
75
|
return toolCache.get(command);
|
|
77
76
|
}
|
|
78
77
|
try {
|
|
79
|
-
const
|
|
80
|
-
const result = spawnSync(command, ["--version"], {
|
|
81
|
-
encoding: "utf-8",
|
|
78
|
+
const result = safeSpawn(command, ["--version"], {
|
|
82
79
|
timeout: 5000,
|
|
83
|
-
shell: true,
|
|
84
80
|
});
|
|
85
81
|
const available = result.status === 0;
|
|
86
82
|
toolCache.set(command, available);
|
|
@@ -121,41 +117,6 @@ function filterDelta(after, before, keyFn) {
|
|
|
121
117
|
const newItems = after.filter((d) => !beforeSet.has(keyFn(d)));
|
|
122
118
|
return { new: newItems, fixed };
|
|
123
119
|
}
|
|
124
|
-
// --- Output Formatting ---
|
|
125
|
-
const EMOJI = {
|
|
126
|
-
blocking: "🔴",
|
|
127
|
-
warning: "🟡",
|
|
128
|
-
fixed: "✅",
|
|
129
|
-
info: "ℹ️",
|
|
130
|
-
silent: "📊",
|
|
131
|
-
none: "",
|
|
132
|
-
};
|
|
133
|
-
function formatDiagnostic(d) {
|
|
134
|
-
const line = d.line ? `L${d.line}: ` : "";
|
|
135
|
-
return ` ${line}${d.message}`;
|
|
136
|
-
}
|
|
137
|
-
function formatDiagnostics(diagnostics, semantic, maxDisplay = 10) {
|
|
138
|
-
if (diagnostics.length === 0)
|
|
139
|
-
return "";
|
|
140
|
-
const emoji = EMOJI[semantic] ?? EMOJI.warning;
|
|
141
|
-
let output = "";
|
|
142
|
-
if (semantic === "blocking") {
|
|
143
|
-
output += `\n${emoji} STOP — ${diagnostics.length} issue(s) must be fixed:\n`;
|
|
144
|
-
}
|
|
145
|
-
else if (semantic === "warning") {
|
|
146
|
-
output += `\n${emoji} ${diagnostics.length} warning(s):\n`;
|
|
147
|
-
}
|
|
148
|
-
else if (semantic === "fixed") {
|
|
149
|
-
output += `\n${emoji} Auto-fixed ${diagnostics.length} issue(s):\n`;
|
|
150
|
-
}
|
|
151
|
-
for (const d of diagnostics.slice(0, maxDisplay)) {
|
|
152
|
-
output += `${formatDiagnostic(d)}\n`;
|
|
153
|
-
}
|
|
154
|
-
if (diagnostics.length > maxDisplay) {
|
|
155
|
-
output += ` ... and ${diagnostics.length - maxDisplay} more\n`;
|
|
156
|
-
}
|
|
157
|
-
return output;
|
|
158
|
-
}
|
|
159
120
|
// --- Main Dispatch Function ---
|
|
160
121
|
export async function dispatchForFile(ctx, groups) {
|
|
161
122
|
const allDiagnostics = [];
|
|
@@ -195,8 +156,13 @@ export async function dispatchForFile(ctx, groups) {
|
|
|
195
156
|
ctx.baselines.set(ctx.filePath, [...allDiagnostics, ...diagnostics]);
|
|
196
157
|
}
|
|
197
158
|
allDiagnostics.push(...diagnostics);
|
|
198
|
-
// Check for blockers
|
|
199
|
-
|
|
159
|
+
// Check for blockers - use result semantic (not group default) and check individual diagnostics
|
|
160
|
+
const resultSemantic = result.semantic ?? semantic;
|
|
161
|
+
if (resultSemantic === "blocking" && diagnostics.length > 0) {
|
|
162
|
+
stopped = true;
|
|
163
|
+
}
|
|
164
|
+
// Also check if any individual diagnostic is blocking
|
|
165
|
+
if (diagnostics.some((d) => d.semantic === "blocking")) {
|
|
200
166
|
stopped = true;
|
|
201
167
|
}
|
|
202
168
|
}
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
import type { FileKind } from "../file-kinds.js";
|
|
18
18
|
import { detectFileKind } from "../file-kinds.js";
|
|
19
|
-
|
|
19
|
+
import { isTestFile } from "../file-utils.js";
|
|
20
|
+
import { safeSpawn } from "../safe-spawn.js";
|
|
20
21
|
import type {
|
|
21
22
|
BaselineStore,
|
|
22
23
|
Diagnostic,
|
|
@@ -28,6 +29,7 @@ import type {
|
|
|
28
29
|
RunnerGroup,
|
|
29
30
|
RunnerResult,
|
|
30
31
|
} from "./types.js";
|
|
32
|
+
import { formatDiagnostics } from "./utils/format-utils.js";
|
|
31
33
|
|
|
32
34
|
// --- In-Memory Baseline Store ---
|
|
33
35
|
|
|
@@ -69,11 +71,11 @@ export function getRunnersForKind(
|
|
|
69
71
|
): RunnerDefinition[] {
|
|
70
72
|
if (!kind) return [];
|
|
71
73
|
const runners: RunnerDefinition[] = [];
|
|
72
|
-
const
|
|
74
|
+
const isTest = filePath ? isTestFile(filePath) : false;
|
|
73
75
|
|
|
74
76
|
for (const runner of globalRegistry.values()) {
|
|
75
77
|
// Skip runners that shouldn't run on test files
|
|
76
|
-
if (
|
|
78
|
+
if (isTest && runner.skipTestFiles) continue;
|
|
77
79
|
|
|
78
80
|
if (runner.appliesTo.includes(kind) || runner.appliesTo.length === 0) {
|
|
79
81
|
runners.push(runner);
|
|
@@ -82,23 +84,17 @@ export function getRunnersForKind(
|
|
|
82
84
|
return runners.sort((a, b) => a.priority - b.priority);
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
function isTest(filePath: string): boolean {
|
|
86
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
87
|
-
return (
|
|
88
|
-
normalized.includes(".test.") ||
|
|
89
|
-
normalized.includes(".spec.") ||
|
|
90
|
-
normalized.includes("/test/") ||
|
|
91
|
-
normalized.includes("/tests/") ||
|
|
92
|
-
normalized.includes("__tests__/") ||
|
|
93
|
-
normalized.includes("test-utils") ||
|
|
94
|
-
normalized.startsWith("test-")
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
87
|
export function listRunners(): RunnerDefinition[] {
|
|
99
88
|
return Array.from(globalRegistry.values());
|
|
100
89
|
}
|
|
101
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Clear all registered runners. Used primarily for testing.
|
|
93
|
+
*/
|
|
94
|
+
export function clearRunnerRegistry(): void {
|
|
95
|
+
globalRegistry.clear();
|
|
96
|
+
}
|
|
97
|
+
|
|
102
98
|
// --- Tool Availability Cache ---
|
|
103
99
|
|
|
104
100
|
const toolCache = new Map<string, boolean>();
|
|
@@ -108,11 +104,8 @@ function checkToolAvailability(command: string): boolean {
|
|
|
108
104
|
return toolCache.get(command)!;
|
|
109
105
|
}
|
|
110
106
|
try {
|
|
111
|
-
const
|
|
112
|
-
const result = spawnSync(command, ["--version"], {
|
|
113
|
-
encoding: "utf-8",
|
|
107
|
+
const result = safeSpawn(command, ["--version"], {
|
|
114
108
|
timeout: 5000,
|
|
115
|
-
shell: true,
|
|
116
109
|
});
|
|
117
110
|
const available = result.status === 0;
|
|
118
111
|
toolCache.set(command, available);
|
|
@@ -171,51 +164,6 @@ function filterDelta<T extends { id: string }>(
|
|
|
171
164
|
return { new: newItems, fixed };
|
|
172
165
|
}
|
|
173
166
|
|
|
174
|
-
// --- Output Formatting ---
|
|
175
|
-
|
|
176
|
-
const EMOJI: Record<string, string> = {
|
|
177
|
-
blocking: "🔴",
|
|
178
|
-
warning: "🟡",
|
|
179
|
-
fixed: "✅",
|
|
180
|
-
info: "ℹ️",
|
|
181
|
-
silent: "📊",
|
|
182
|
-
none: "",
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
function formatDiagnostic(d: Diagnostic): string {
|
|
186
|
-
const line = d.line ? `L${d.line}: ` : "";
|
|
187
|
-
return ` ${line}${d.message}`;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function formatDiagnostics(
|
|
191
|
-
diagnostics: Diagnostic[],
|
|
192
|
-
semantic: OutputSemantic,
|
|
193
|
-
maxDisplay = 10,
|
|
194
|
-
): string {
|
|
195
|
-
if (diagnostics.length === 0) return "";
|
|
196
|
-
|
|
197
|
-
const emoji = EMOJI[semantic] ?? EMOJI.warning;
|
|
198
|
-
let output = "";
|
|
199
|
-
|
|
200
|
-
if (semantic === "blocking") {
|
|
201
|
-
output += `\n${emoji} STOP — ${diagnostics.length} issue(s) must be fixed:\n`;
|
|
202
|
-
} else if (semantic === "warning") {
|
|
203
|
-
output += `\n${emoji} ${diagnostics.length} warning(s):\n`;
|
|
204
|
-
} else if (semantic === "fixed") {
|
|
205
|
-
output += `\n${emoji} Auto-fixed ${diagnostics.length} issue(s):\n`;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
for (const d of diagnostics.slice(0, maxDisplay)) {
|
|
209
|
-
output += `${formatDiagnostic(d)}\n`;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (diagnostics.length > maxDisplay) {
|
|
213
|
-
output += ` ... and ${diagnostics.length - maxDisplay} more\n`;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return output;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
167
|
// --- Main Dispatch Function ---
|
|
220
168
|
|
|
221
169
|
export async function dispatchForFile(
|
|
@@ -271,8 +219,13 @@ export async function dispatchForFile(
|
|
|
271
219
|
|
|
272
220
|
allDiagnostics.push(...diagnostics);
|
|
273
221
|
|
|
274
|
-
// Check for blockers
|
|
275
|
-
|
|
222
|
+
// Check for blockers - use result semantic (not group default) and check individual diagnostics
|
|
223
|
+
const resultSemantic = result.semantic ?? semantic;
|
|
224
|
+
if (resultSemantic === "blocking" && diagnostics.length > 0) {
|
|
225
|
+
stopped = true;
|
|
226
|
+
}
|
|
227
|
+
// Also check if any individual diagnostic is blocking
|
|
228
|
+
if (diagnostics.some((d) => d.semantic === "blocking")) {
|
|
276
229
|
stopped = true;
|
|
277
230
|
}
|
|
278
231
|
}
|
package/clients/dispatch/plan.js
CHANGED
|
@@ -21,11 +21,13 @@ export const TOOL_PLANS = {
|
|
|
21
21
|
groups: [
|
|
22
22
|
// TypeScript LSP always runs first - blocks on errors
|
|
23
23
|
{ mode: "all", runnerIds: ["ts-lsp"], filterKinds: ["jsts"] },
|
|
24
|
-
// Then biome for fast linting
|
|
25
|
-
{ mode: "fallback", runnerIds: ["biome-lint"] },
|
|
24
|
+
// Then biome or oxlint for fast linting (user preference)
|
|
25
|
+
{ mode: "fallback", runnerIds: ["biome-lint", "oxlint"] },
|
|
26
|
+
// Fast structural analysis via NAPI (weight >= 4 is blocking)
|
|
27
|
+
{ mode: "all", runnerIds: ["ast-grep-napi"] },
|
|
26
28
|
// Type safety checks
|
|
27
29
|
{ mode: "fallback", runnerIds: ["type-safety"] },
|
|
28
|
-
//
|
|
30
|
+
// Comprehensive structural analysis via CLI (severity: error is blocking)
|
|
29
31
|
{ mode: "fallback", runnerIds: ["ast-grep"] },
|
|
30
32
|
// Architectural rules
|
|
31
33
|
{ mode: "fallback", runnerIds: ["architect"] },
|
|
@@ -93,7 +95,8 @@ export const TOOL_PLANS = {
|
|
|
93
95
|
markdown: {
|
|
94
96
|
name: "Markdown Processing",
|
|
95
97
|
groups: [
|
|
96
|
-
|
|
98
|
+
// Spellcheck for typos
|
|
99
|
+
{ mode: "fallback", runnerIds: ["spellcheck"] },
|
|
97
100
|
],
|
|
98
101
|
},
|
|
99
102
|
/**
|
|
@@ -102,6 +105,8 @@ export const TOOL_PLANS = {
|
|
|
102
105
|
shell: {
|
|
103
106
|
name: "Shell Script Linting",
|
|
104
107
|
groups: [
|
|
108
|
+
// Shellcheck for bash/sh/zsh linting
|
|
109
|
+
{ mode: "fallback", runnerIds: ["shellcheck"] },
|
|
105
110
|
// Architectural rules
|
|
106
111
|
{ mode: "fallback", runnerIds: ["architect"] },
|
|
107
112
|
],
|
package/clients/dispatch/plan.ts
CHANGED
|
@@ -25,11 +25,13 @@ export const TOOL_PLANS: Record<string, ToolPlan> = {
|
|
|
25
25
|
groups: [
|
|
26
26
|
// TypeScript LSP always runs first - blocks on errors
|
|
27
27
|
{ mode: "all", runnerIds: ["ts-lsp"], filterKinds: ["jsts"] },
|
|
28
|
-
// Then biome for fast linting
|
|
29
|
-
{ mode: "fallback", runnerIds: ["biome-lint"] },
|
|
28
|
+
// Then biome or oxlint for fast linting (user preference)
|
|
29
|
+
{ mode: "fallback", runnerIds: ["biome-lint", "oxlint"] },
|
|
30
|
+
// Fast structural analysis via NAPI (weight >= 4 is blocking)
|
|
31
|
+
{ mode: "all", runnerIds: ["ast-grep-napi"] },
|
|
30
32
|
// Type safety checks
|
|
31
33
|
{ mode: "fallback", runnerIds: ["type-safety"] },
|
|
32
|
-
//
|
|
34
|
+
// Comprehensive structural analysis via CLI (severity: error is blocking)
|
|
33
35
|
{ mode: "fallback", runnerIds: ["ast-grep"] },
|
|
34
36
|
// Architectural rules
|
|
35
37
|
{ mode: "fallback", runnerIds: ["architect"] },
|
|
@@ -103,7 +105,8 @@ export const TOOL_PLANS: Record<string, ToolPlan> = {
|
|
|
103
105
|
markdown: {
|
|
104
106
|
name: "Markdown Processing",
|
|
105
107
|
groups: [
|
|
106
|
-
//
|
|
108
|
+
// Spellcheck for typos
|
|
109
|
+
{ mode: "fallback", runnerIds: ["spellcheck"] },
|
|
107
110
|
],
|
|
108
111
|
},
|
|
109
112
|
|
|
@@ -113,6 +116,8 @@ export const TOOL_PLANS: Record<string, ToolPlan> = {
|
|
|
113
116
|
shell: {
|
|
114
117
|
name: "Shell Script Linting",
|
|
115
118
|
groups: [
|
|
119
|
+
// Shellcheck for bash/sh/zsh linting
|
|
120
|
+
{ mode: "fallback", runnerIds: ["shellcheck"] },
|
|
116
121
|
// Architectural rules
|
|
117
122
|
{ mode: "fallback", runnerIds: ["architect"] },
|
|
118
123
|
],
|
|
@@ -15,6 +15,7 @@ const architectRunner = {
|
|
|
15
15
|
appliesTo: ["jsts", "python", "go", "rust", "cxx", "shell", "cmake"],
|
|
16
16
|
priority: 40,
|
|
17
17
|
enabledByDefault: true,
|
|
18
|
+
skipTestFiles: true, // Skip test files - rules can be noisy there
|
|
18
19
|
async run(ctx) {
|
|
19
20
|
const relPath = ctx.filePath.replace(ctx.cwd, "").replace(/\\/g, "/");
|
|
20
21
|
const content = readFileContent(ctx.filePath);
|
|
@@ -30,15 +31,28 @@ const architectRunner = {
|
|
|
30
31
|
// Check for violations
|
|
31
32
|
const violations = architectClient.checkFile(relPath, content);
|
|
32
33
|
for (const v of violations) {
|
|
34
|
+
// Build message with inline fix guidance
|
|
35
|
+
let message = v.message;
|
|
36
|
+
let fixSuggestion = v.fix;
|
|
37
|
+
if (v.fix) {
|
|
38
|
+
const fixPreview = v.fix.length > 60 ? `${v.fix.substring(0, 60)}...` : v.fix;
|
|
39
|
+
message += `\n💡 Suggested fix: ${fixPreview}`;
|
|
40
|
+
}
|
|
41
|
+
else if (v.note) {
|
|
42
|
+
const notePreview = v.note.length > 80 ? `${v.note.substring(0, 80)}...` : v.note;
|
|
43
|
+
message += `\n📝 ${notePreview}`;
|
|
44
|
+
}
|
|
33
45
|
diagnostics.push({
|
|
34
46
|
id: `architect-${v.line || 0}-${v.pattern}`,
|
|
35
|
-
message
|
|
47
|
+
message,
|
|
36
48
|
filePath: ctx.filePath,
|
|
37
49
|
line: v.line,
|
|
38
|
-
severity: "
|
|
39
|
-
semantic: "
|
|
50
|
+
severity: "warning",
|
|
51
|
+
semantic: "warning",
|
|
40
52
|
tool: "architect",
|
|
41
53
|
rule: v.pattern,
|
|
54
|
+
fixable: !!v.fix,
|
|
55
|
+
fixSuggestion,
|
|
42
56
|
});
|
|
43
57
|
}
|
|
44
58
|
// Check file size limit
|
|
@@ -49,8 +63,8 @@ const architectRunner = {
|
|
|
49
63
|
id: `architect-size-${lineCount}`,
|
|
50
64
|
message: sizeViolation.message,
|
|
51
65
|
filePath: ctx.filePath,
|
|
52
|
-
severity: "
|
|
53
|
-
semantic: "
|
|
66
|
+
severity: "warning",
|
|
67
|
+
semantic: "warning",
|
|
54
68
|
tool: "architect",
|
|
55
69
|
rule: "file-size-limit",
|
|
56
70
|
fixSuggestion: "Split into smaller modules",
|
|
@@ -60,9 +74,9 @@ const architectRunner = {
|
|
|
60
74
|
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
61
75
|
}
|
|
62
76
|
return {
|
|
63
|
-
status: "
|
|
77
|
+
status: "succeeded", // Warnings don't fail the run
|
|
64
78
|
diagnostics,
|
|
65
|
-
semantic: "
|
|
79
|
+
semantic: "warning",
|
|
66
80
|
};
|
|
67
81
|
},
|
|
68
82
|
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { describe, expect, it, beforeAll, afterAll } from "vitest";
|
|
4
|
+
function createMockContext(filePath, kind = "jsts", cwd) {
|
|
5
|
+
return {
|
|
6
|
+
filePath,
|
|
7
|
+
cwd: cwd || process.cwd(),
|
|
8
|
+
kind,
|
|
9
|
+
autofix: false,
|
|
10
|
+
deltaMode: false,
|
|
11
|
+
baselines: { get: () => undefined, set: () => { }, clear: () => { } },
|
|
12
|
+
pi: { getFlag: () => false },
|
|
13
|
+
hasTool: async () => false,
|
|
14
|
+
log: () => { },
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
describe("architect runner", () => {
|
|
18
|
+
const testDir = path.join(process.env.TEMP || "/tmp", `architect_test_${Date.now()}`);
|
|
19
|
+
const configPath = path.join(testDir, ".pi-lens", "architect.yaml");
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
// Create test config
|
|
22
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
23
|
+
fs.writeFileSync(configPath, `version: "1.0"
|
|
24
|
+
rules:
|
|
25
|
+
- pattern: "**/*.ts"
|
|
26
|
+
max_lines: 50
|
|
27
|
+
must_not:
|
|
28
|
+
- pattern: 'hardcoded_secret_12345'
|
|
29
|
+
message: "No hardcoded secrets"
|
|
30
|
+
fix: "Use process.env.SECRET"
|
|
31
|
+
- pattern: 'console\.log'
|
|
32
|
+
message: "No console.log in production"
|
|
33
|
+
`);
|
|
34
|
+
});
|
|
35
|
+
afterAll(() => {
|
|
36
|
+
try {
|
|
37
|
+
if (fs.existsSync(testDir)) {
|
|
38
|
+
fs.rmSync(testDir, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Ignore cleanup errors
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
it("should load default config when no user config exists", async () => {
|
|
46
|
+
const module = await import("./architect.js");
|
|
47
|
+
const runner = module.default;
|
|
48
|
+
// Use a unique temp dir with no user config (will fall back to default)
|
|
49
|
+
const noUserConfigDir = path.join(process.env.TEMP || "/tmp", `no_arch_user_config_${Date.now()}`);
|
|
50
|
+
fs.mkdirSync(noUserConfigDir, { recursive: true });
|
|
51
|
+
// Create a very large file that should trigger default max_lines rule
|
|
52
|
+
const tmpFile = path.join(noUserConfigDir, `large_${Date.now()}.ts`);
|
|
53
|
+
fs.writeFileSync(tmpFile, Array(5000).fill("// line").join("\n"));
|
|
54
|
+
try {
|
|
55
|
+
const result = await runner.run(createMockContext(tmpFile, "jsts", noUserConfigDir));
|
|
56
|
+
// Should use default config and find violations
|
|
57
|
+
expect(result.status).toBe("succeeded");
|
|
58
|
+
// Should have size violation from default config
|
|
59
|
+
expect(result.diagnostics.some((d) => d.message.includes("line limit"))).toBe(true);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
try {
|
|
63
|
+
if (fs.existsSync(tmpFile))
|
|
64
|
+
fs.unlinkSync(tmpFile);
|
|
65
|
+
if (fs.existsSync(noUserConfigDir))
|
|
66
|
+
fs.rmdirSync(noUserConfigDir);
|
|
67
|
+
}
|
|
68
|
+
catch { }
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
it("should detect file size violations", async () => {
|
|
72
|
+
const module = await import("./architect.js");
|
|
73
|
+
const runner = module.default;
|
|
74
|
+
const tmpFile = path.join(testDir, `large_file_${Date.now()}.ts`);
|
|
75
|
+
// Create file with 100 lines (exceeds 50 line limit)
|
|
76
|
+
fs.writeFileSync(tmpFile, Array(100).fill("// line").join("\n"));
|
|
77
|
+
try {
|
|
78
|
+
const result = await runner.run(createMockContext(tmpFile, "jsts", testDir));
|
|
79
|
+
expect(result.status).toBe("succeeded");
|
|
80
|
+
expect(result.diagnostics.length).toBeGreaterThan(0);
|
|
81
|
+
expect(result.diagnostics.some((d) => d.message.includes("50 line limit"))).toBe(true);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
try {
|
|
85
|
+
if (fs.existsSync(tmpFile))
|
|
86
|
+
fs.unlinkSync(tmpFile);
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
it("should detect pattern violations", async () => {
|
|
92
|
+
const module = await import("./architect.js");
|
|
93
|
+
const runner = module.default;
|
|
94
|
+
const tmpFile = path.join(testDir, `bad_patterns_${Date.now()}.ts`);
|
|
95
|
+
fs.writeFileSync(tmpFile, `const x = hardcoded_secret_12345;
|
|
96
|
+
console.log(x);
|
|
97
|
+
`);
|
|
98
|
+
try {
|
|
99
|
+
const result = await runner.run(createMockContext(tmpFile, "jsts", testDir));
|
|
100
|
+
expect(result.status).toBe("succeeded");
|
|
101
|
+
expect(result.diagnostics.length).toBeGreaterThanOrEqual(2);
|
|
102
|
+
expect(result.diagnostics.some((d) => d.message.includes("hardcoded"))).toBe(true);
|
|
103
|
+
expect(result.diagnostics.some((d) => d.message.includes("console.log"))).toBe(true);
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
try {
|
|
107
|
+
if (fs.existsSync(tmpFile))
|
|
108
|
+
fs.unlinkSync(tmpFile);
|
|
109
|
+
}
|
|
110
|
+
catch { }
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
it("should return no diagnostics for clean files", async () => {
|
|
114
|
+
const module = await import("./architect.js");
|
|
115
|
+
const runner = module.default;
|
|
116
|
+
const tmpFile = path.join(testDir, `clean_${Date.now()}.ts`);
|
|
117
|
+
// Small file (20 lines) with no violations
|
|
118
|
+
fs.writeFileSync(tmpFile, Array(20).fill("// clean code").join("\n"));
|
|
119
|
+
try {
|
|
120
|
+
const result = await runner.run(createMockContext(tmpFile, "jsts", testDir));
|
|
121
|
+
expect(result.status).toBe("succeeded");
|
|
122
|
+
expect(result.diagnostics.length).toBe(0);
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
try {
|
|
126
|
+
if (fs.existsSync(tmpFile))
|
|
127
|
+
fs.unlinkSync(tmpFile);
|
|
128
|
+
}
|
|
129
|
+
catch { }
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
it("should skip test files", async () => {
|
|
133
|
+
const module = await import("./architect.js");
|
|
134
|
+
const runner = module.default;
|
|
135
|
+
// The runner should have skipTestFiles: true
|
|
136
|
+
expect(runner.skipTestFiles).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
});
|