pi-lens 3.6.3 → 3.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/package.json +3 -3
  2. package/tsconfig.json +1 -1
  3. package/clients/__tests__/file-time.test.js +0 -216
  4. package/clients/__tests__/file-time.test.ts +0 -276
  5. package/clients/__tests__/format-service.test.js +0 -245
  6. package/clients/__tests__/format-service.test.ts +0 -339
  7. package/clients/__tests__/formatters.test.js +0 -271
  8. package/clients/__tests__/formatters.test.ts +0 -401
  9. package/clients/agent-behavior-client.js +0 -110
  10. package/clients/agent-behavior-client.test.js +0 -94
  11. package/clients/agent-behavior-client.test.ts +0 -116
  12. package/clients/amain-types.js +0 -164
  13. package/clients/architect-client.js +0 -291
  14. package/clients/ast-grep-client.js +0 -253
  15. package/clients/ast-grep-parser.js +0 -84
  16. package/clients/ast-grep-rule-manager.js +0 -89
  17. package/clients/ast-grep-types.js +0 -9
  18. package/clients/auto-loop.js +0 -131
  19. package/clients/biome-client.js +0 -420
  20. package/clients/biome-client.test.js +0 -144
  21. package/clients/biome-client.test.ts +0 -163
  22. package/clients/cache/rule-cache.js +0 -72
  23. package/clients/cache-manager.js +0 -245
  24. package/clients/cache-manager.test.js +0 -197
  25. package/clients/cache-manager.test.ts +0 -299
  26. package/clients/complexity-client.js +0 -675
  27. package/clients/complexity-client.test.js +0 -234
  28. package/clients/complexity-client.test.ts +0 -255
  29. package/clients/config-validator.js +0 -465
  30. package/clients/dependency-checker.js +0 -325
  31. package/clients/dependency-checker.test.js +0 -60
  32. package/clients/dependency-checker.test.ts +0 -71
  33. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  34. package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
  35. package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
  36. package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
  37. package/clients/dispatch/debug.log +0 -1
  38. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  39. package/clients/dispatch/dispatcher.edge.test.ts +0 -100
  40. package/clients/dispatch/dispatcher.format.test.js +0 -46
  41. package/clients/dispatch/dispatcher.format.test.ts +0 -58
  42. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  43. package/clients/dispatch/dispatcher.inline.test.ts +0 -93
  44. package/clients/dispatch/dispatcher.js +0 -381
  45. package/clients/dispatch/dispatcher.test.js +0 -116
  46. package/clients/dispatch/dispatcher.test.ts +0 -149
  47. package/clients/dispatch/integration.js +0 -108
  48. package/clients/dispatch/plan.js +0 -183
  49. package/clients/dispatch/runners/architect.js +0 -83
  50. package/clients/dispatch/runners/architect.test.js +0 -138
  51. package/clients/dispatch/runners/architect.test.ts +0 -162
  52. package/clients/dispatch/runners/ast-grep-napi.js +0 -405
  53. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
  54. package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
  55. package/clients/dispatch/runners/ast-grep.js +0 -157
  56. package/clients/dispatch/runners/biome.js +0 -55
  57. package/clients/dispatch/runners/config-validation.js +0 -67
  58. package/clients/dispatch/runners/go-vet.js +0 -48
  59. package/clients/dispatch/runners/index.js +0 -47
  60. package/clients/dispatch/runners/lsp.js +0 -102
  61. package/clients/dispatch/runners/oxlint.js +0 -67
  62. package/clients/dispatch/runners/oxlint.test.js +0 -230
  63. package/clients/dispatch/runners/oxlint.test.ts +0 -303
  64. package/clients/dispatch/runners/pyright.js +0 -100
  65. package/clients/dispatch/runners/pyright.test.js +0 -98
  66. package/clients/dispatch/runners/pyright.test.ts +0 -121
  67. package/clients/dispatch/runners/python-slop.js +0 -97
  68. package/clients/dispatch/runners/python-slop.test.js +0 -203
  69. package/clients/dispatch/runners/python-slop.test.ts +0 -298
  70. package/clients/dispatch/runners/ruff.js +0 -48
  71. package/clients/dispatch/runners/rust-clippy.js +0 -102
  72. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  73. package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
  74. package/clients/dispatch/runners/shellcheck.js +0 -147
  75. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  76. package/clients/dispatch/runners/shellcheck.test.ts +0 -129
  77. package/clients/dispatch/runners/similarity.js +0 -230
  78. package/clients/dispatch/runners/spellcheck.js +0 -106
  79. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  80. package/clients/dispatch/runners/spellcheck.test.ts +0 -214
  81. package/clients/dispatch/runners/tree-sitter.js +0 -246
  82. package/clients/dispatch/runners/ts-lsp.js +0 -125
  83. package/clients/dispatch/runners/ts-slop.js +0 -113
  84. package/clients/dispatch/runners/type-safety.js +0 -142
  85. package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
  86. package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
  87. package/clients/dispatch/runners/utils.js +0 -51
  88. package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
  89. package/clients/dispatch/types.js +0 -16
  90. package/clients/dispatch/utils/format-utils.js +0 -44
  91. package/clients/dogfood.test.js +0 -201
  92. package/clients/dogfood.test.ts +0 -269
  93. package/clients/file-kinds.js +0 -177
  94. package/clients/file-kinds.test.js +0 -169
  95. package/clients/file-kinds.test.ts +0 -210
  96. package/clients/file-time.js +0 -152
  97. package/clients/file-utils.js +0 -40
  98. package/clients/fix-scanners.js +0 -204
  99. package/clients/format-service.js +0 -184
  100. package/clients/formatters.js +0 -488
  101. package/clients/go-client.js +0 -203
  102. package/clients/go-client.test.js +0 -127
  103. package/clients/go-client.test.ts +0 -143
  104. package/clients/installer/index.js +0 -403
  105. package/clients/interviewer-templates.js +0 -75
  106. package/clients/interviewer.js +0 -173
  107. package/clients/jscpd-client.js +0 -196
  108. package/clients/jscpd-client.test.js +0 -127
  109. package/clients/jscpd-client.test.ts +0 -145
  110. package/clients/knip-client.js +0 -239
  111. package/clients/knip-client.test.js +0 -112
  112. package/clients/knip-client.test.ts +0 -128
  113. package/clients/latency-logger.js +0 -40
  114. package/clients/lsp/__tests__/client.test.js +0 -310
  115. package/clients/lsp/__tests__/client.test.ts +0 -412
  116. package/clients/lsp/__tests__/config.test.js +0 -167
  117. package/clients/lsp/__tests__/config.test.ts +0 -217
  118. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  119. package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
  120. package/clients/lsp/__tests__/integration.test.js +0 -127
  121. package/clients/lsp/__tests__/integration.test.ts +0 -160
  122. package/clients/lsp/__tests__/launch.test.js +0 -313
  123. package/clients/lsp/__tests__/launch.test.ts +0 -394
  124. package/clients/lsp/__tests__/server.test.js +0 -259
  125. package/clients/lsp/__tests__/server.test.ts +0 -332
  126. package/clients/lsp/__tests__/service.test.js +0 -438
  127. package/clients/lsp/__tests__/service.test.ts +0 -530
  128. package/clients/lsp/client.js +0 -350
  129. package/clients/lsp/config.js +0 -112
  130. package/clients/lsp/index.js +0 -318
  131. package/clients/lsp/installer/index.js +0 -391
  132. package/clients/lsp/interactive-install.js +0 -221
  133. package/clients/lsp/language.js +0 -170
  134. package/clients/lsp/launch.js +0 -329
  135. package/clients/lsp/lsp/launch.js +0 -116
  136. package/clients/lsp/lsp/server.js +0 -532
  137. package/clients/lsp/lsp-index.js +0 -10
  138. package/clients/lsp/path-utils.js +0 -5
  139. package/clients/lsp/server.js +0 -725
  140. package/clients/lsp/test-py-spawn/requirements.txt +0 -1
  141. package/clients/lsp/test-py-spawn/test.py +0 -3
  142. package/clients/lsp/test-py-svc/requirements.txt +0 -1
  143. package/clients/lsp/test-py-svc/test.py +0 -3
  144. package/clients/lsp/test-python-project/requirements.txt +0 -1
  145. package/clients/lsp/test-python-project/test.py +0 -5
  146. package/clients/metrics-client.js +0 -107
  147. package/clients/metrics-client.test.js +0 -128
  148. package/clients/metrics-client.test.ts +0 -163
  149. package/clients/metrics-history.js +0 -367
  150. package/clients/path-utils.js +0 -142
  151. package/clients/pipeline.js +0 -272
  152. package/clients/production-readiness.js +0 -522
  153. package/clients/project-index.js +0 -255
  154. package/clients/project-metadata.js +0 -531
  155. package/clients/ruff-client.js +0 -325
  156. package/clients/ruff-client.test.js +0 -132
  157. package/clients/ruff-client.test.ts +0 -153
  158. package/clients/rules-scanner.js +0 -97
  159. package/clients/runner-tracker.js +0 -152
  160. package/clients/rust-client.js +0 -205
  161. package/clients/rust-client.test.js +0 -108
  162. package/clients/rust-client.test.ts +0 -130
  163. package/clients/safe-spawn-async.js +0 -163
  164. package/clients/safe-spawn.js +0 -241
  165. package/clients/sanitize.js +0 -291
  166. package/clients/sanitize.test.js +0 -177
  167. package/clients/sanitize.test.ts +0 -223
  168. package/clients/scan-architectural-debt.js +0 -167
  169. package/clients/scan-utils.js +0 -83
  170. package/clients/secrets-scanner.js +0 -119
  171. package/clients/secrets-scanner.test.js +0 -100
  172. package/clients/secrets-scanner.test.ts +0 -113
  173. package/clients/sg-runner.js +0 -292
  174. package/clients/state-matrix.js +0 -160
  175. package/clients/subprocess-client.js +0 -65
  176. package/clients/symbol-types.js +0 -5
  177. package/clients/test-runner-client.js +0 -523
  178. package/clients/test-runner-client.test.js +0 -192
  179. package/clients/test-runner-client.test.ts +0 -253
  180. package/clients/test-utils.js +0 -27
  181. package/clients/test-utils.ts +0 -36
  182. package/clients/todo-scanner.js +0 -200
  183. package/clients/todo-scanner.test.js +0 -301
  184. package/clients/todo-scanner.test.ts +0 -352
  185. package/clients/tool-availability.js +0 -207
  186. package/clients/tree-sitter-client.js +0 -601
  187. package/clients/tree-sitter-query-loader.js +0 -355
  188. package/clients/tree-sitter-symbol-extractor.js +0 -289
  189. package/clients/ts-service.js +0 -129
  190. package/clients/type-coverage-client.js +0 -127
  191. package/clients/type-coverage-client.test.js +0 -105
  192. package/clients/type-coverage-client.test.ts +0 -125
  193. package/clients/type-safety-client.js +0 -138
  194. package/clients/types.js +0 -11
  195. package/clients/typescript-client.codefix.test.js +0 -157
  196. package/clients/typescript-client.codefix.test.ts +0 -186
  197. package/clients/typescript-client.js +0 -509
  198. package/clients/typescript-client.test.js +0 -105
  199. package/clients/typescript-client.test.ts +0 -126
  200. package/commands/booboo.js +0 -1007
  201. package/commands/fix-from-booboo.js +0 -398
  202. package/commands/fix-simplified.js +0 -618
  203. package/commands/rate.js +0 -281
  204. package/commands/rate.test.js +0 -119
  205. package/commands/rate.test.ts +0 -131
  206. package/commands/refactor.js +0 -130
@@ -1,177 +0,0 @@
1
- /**
2
- * Tests for sanitize.ts
3
- * Tool output sanitization
4
- */
5
- import { describe, expect, it } from "vitest";
6
- import { extractErrorMessage, normalizeWhitespace, sanitizeBiomeOutput, sanitizeLine, sanitizeOutput, sanitizeRuffOutput, sanitizeToolOutput, stripAnsi, truncateMessage, } from "./sanitize.js";
7
- describe("stripAnsi", () => {
8
- it("should remove ANSI color codes", () => {
9
- expect(stripAnsi("\x1b[31mError\x1b[0m")).toBe("Error");
10
- expect(stripAnsi("\x1b[1;32mSuccess\x1b[0m")).toBe("Success");
11
- });
12
- it("should remove extended ANSI codes", () => {
13
- expect(stripAnsi("\x1b[1;2;3mText\x1b[0m")).toBe("Text");
14
- });
15
- it("should return original string if no ANSI codes", () => {
16
- expect(stripAnsi("Plain text")).toBe("Plain text");
17
- });
18
- });
19
- describe("normalizeWhitespace", () => {
20
- it("should collapse multiple spaces", () => {
21
- expect(normalizeWhitespace("Hello World")).toBe("Hello World");
22
- });
23
- it("should trim lines", () => {
24
- expect(normalizeWhitespace(" Hello \n World ")).toBe("Hello\nWorld");
25
- });
26
- it("should remove empty lines", () => {
27
- expect(normalizeWhitespace("Hello\n\n\nWorld")).toBe("Hello\nWorld");
28
- });
29
- });
30
- describe("sanitizeLine", () => {
31
- it("should remove common error prefixes", () => {
32
- expect(sanitizeLine("[error] Something went wrong")).toBe("Something went wrong");
33
- expect(sanitizeLine("error: Something went wrong")).toBe("Something went wrong");
34
- });
35
- it("should remove check/cross marks", () => {
36
- expect(sanitizeLine("× Error message")).toBe("Error message");
37
- expect(sanitizeLine("✓ Success message")).toBe("Success message");
38
- });
39
- it("should normalize whitespace", () => {
40
- expect(sanitizeLine(" Error message ")).toBe("Error message");
41
- });
42
- });
43
- describe("sanitizeOutput", () => {
44
- it("should return empty string for empty input", () => {
45
- expect(sanitizeOutput("")).toBe("");
46
- expect(sanitizeOutput(" ")).toBe("");
47
- });
48
- it("should filter out detail lines", () => {
49
- const output = "Error message\n at line 10\n at function foo";
50
- const result = sanitizeOutput(output);
51
- expect(result).toBe("Error message");
52
- });
53
- it("should remove ANSI codes and normalize", () => {
54
- const output = "\x1b[31mError\x1b[0m\n\x1b[32mSuccess\x1b[0m";
55
- const result = sanitizeOutput(output);
56
- expect(result).toBe("Error\nSuccess");
57
- });
58
- });
59
- describe("extractErrorMessage", () => {
60
- it("should return undefined for empty input", () => {
61
- expect(extractErrorMessage("")).toBeUndefined();
62
- expect(extractErrorMessage(" ")).toBeUndefined();
63
- });
64
- it("should return first error line containing error indicator", () => {
65
- // Note: extractErrorMessage sanitizes lines first, then checks isErrorLine
66
- // Since "error:" prefix is removed by sanitizeLine, we need to use a keyword
67
- // that remains in the sanitized line, like "failed" or "syntax"
68
- const output = "Some info\nOperation failed: disk full\nMore info";
69
- const result = extractErrorMessage(output);
70
- expect(result).toBe("Operation failed: disk full");
71
- });
72
- it("should fall back to first non-empty line", () => {
73
- const output = "info line\nanother info\nand more";
74
- expect(extractErrorMessage(output)).toBe("info line");
75
- });
76
- it("should find error by keyword", () => {
77
- const result = extractErrorMessage("warning: low severity\nfailed: operation");
78
- // extractErrorMessage returns the first line with error indicators
79
- // "failed:" contains "failed" which matches ERROR_INDICATORS
80
- expect(result).toBe("failed: operation");
81
- });
82
- });
83
- describe("truncateMessage", () => {
84
- it("should not truncate short messages", () => {
85
- const msg = "Short message";
86
- expect(truncateMessage(msg, 100)).toBe(msg);
87
- });
88
- it("should truncate long messages", () => {
89
- const msg = "A".repeat(200);
90
- const result = truncateMessage(msg, 50);
91
- expect(result).toBe(`${"A".repeat(49)}…`);
92
- });
93
- it("should use default max length of 140", () => {
94
- const msg = "A".repeat(200);
95
- const result = truncateMessage(msg);
96
- expect(result.length).toBe(140);
97
- });
98
- });
99
- describe("sanitizeToolOutput", () => {
100
- it("should return empty result for empty input", () => {
101
- const result = sanitizeToolOutput("");
102
- expect(result.summary).toBeUndefined();
103
- expect(result.details).toBeUndefined();
104
- expect(result.truncated).toBe(false);
105
- });
106
- it("should extract summary and details", () => {
107
- const output = "error: something failed\nline 2\nline 3\nline 4";
108
- const result = sanitizeToolOutput(output);
109
- // sanitizeLine removes "error:" prefix
110
- expect(result.summary).toContain("something failed");
111
- expect(result.details).toBeDefined();
112
- });
113
- it("should mark as truncated when too many lines", () => {
114
- const lines = [];
115
- for (let i = 0; i < 30; i++) {
116
- lines.push(`error line ${i}`);
117
- }
118
- const result = sanitizeToolOutput(lines.join("\n"));
119
- expect(result.truncated).toBe(true);
120
- expect(result.details).toContain("more lines");
121
- });
122
- it("should respect max summary length", () => {
123
- const output = `error: ${"x".repeat(200)}`;
124
- const result = sanitizeToolOutput(output, 50);
125
- expect(result.summary?.length).toBeLessThanOrEqual(50);
126
- });
127
- });
128
- describe("sanitizeBiomeOutput", () => {
129
- it("should parse JSON diagnostics", () => {
130
- const json = JSON.stringify({
131
- diagnostics: [
132
- {
133
- location: {
134
- path: "file.ts",
135
- span: { start: { line: 10, column: 0 } },
136
- },
137
- message: "Unexpected token",
138
- },
139
- ],
140
- });
141
- const result = sanitizeBiomeOutput(json);
142
- expect(result).toContain("file.ts");
143
- expect(result).toContain("11"); // line + 1
144
- expect(result).toContain("Unexpected token");
145
- });
146
- it("should handle text output with errors", () => {
147
- const output = "error: something went wrong\ninfo: some info";
148
- const result = sanitizeBiomeOutput(output);
149
- expect(result).toContain("error");
150
- });
151
- it("should return empty for empty input", () => {
152
- expect(sanitizeBiomeOutput("")).toBe("");
153
- });
154
- });
155
- describe("sanitizeRuffOutput", () => {
156
- it("should parse JSON diagnostics", () => {
157
- const json = JSON.stringify([
158
- {
159
- location: { row: 10, column: 5 },
160
- code: "E501",
161
- message: "Line too long",
162
- },
163
- ]);
164
- const result = sanitizeRuffOutput(json);
165
- expect(result).toContain("10:5");
166
- expect(result).toContain("[E501]");
167
- expect(result).toContain("Line too long");
168
- });
169
- it("should handle text output", () => {
170
- const output = "file.py:10:5 [E501] Line too long";
171
- const result = sanitizeRuffOutput(output);
172
- expect(result).toContain("E501");
173
- });
174
- it("should return empty for empty input", () => {
175
- expect(sanitizeRuffOutput("")).toBe("");
176
- });
177
- });
@@ -1,223 +0,0 @@
1
- /**
2
- * Tests for sanitize.ts
3
- * Tool output sanitization
4
- */
5
-
6
- import { describe, expect, it } from "vitest";
7
- import {
8
- extractErrorMessage,
9
- normalizeWhitespace,
10
- sanitizeBiomeOutput,
11
- sanitizeLine,
12
- sanitizeOutput,
13
- sanitizeRuffOutput,
14
- sanitizeToolOutput,
15
- stripAnsi,
16
- truncateMessage,
17
- } from "./sanitize.js";
18
-
19
- describe("stripAnsi", () => {
20
- it("should remove ANSI color codes", () => {
21
- expect(stripAnsi("\x1b[31mError\x1b[0m")).toBe("Error");
22
- expect(stripAnsi("\x1b[1;32mSuccess\x1b[0m")).toBe("Success");
23
- });
24
-
25
- it("should remove extended ANSI codes", () => {
26
- expect(stripAnsi("\x1b[1;2;3mText\x1b[0m")).toBe("Text");
27
- });
28
-
29
- it("should return original string if no ANSI codes", () => {
30
- expect(stripAnsi("Plain text")).toBe("Plain text");
31
- });
32
- });
33
-
34
- describe("normalizeWhitespace", () => {
35
- it("should collapse multiple spaces", () => {
36
- expect(normalizeWhitespace("Hello World")).toBe("Hello World");
37
- });
38
-
39
- it("should trim lines", () => {
40
- expect(normalizeWhitespace(" Hello \n World ")).toBe("Hello\nWorld");
41
- });
42
-
43
- it("should remove empty lines", () => {
44
- expect(normalizeWhitespace("Hello\n\n\nWorld")).toBe("Hello\nWorld");
45
- });
46
- });
47
-
48
- describe("sanitizeLine", () => {
49
- it("should remove common error prefixes", () => {
50
- expect(sanitizeLine("[error] Something went wrong")).toBe(
51
- "Something went wrong",
52
- );
53
- expect(sanitizeLine("error: Something went wrong")).toBe(
54
- "Something went wrong",
55
- );
56
- });
57
-
58
- it("should remove check/cross marks", () => {
59
- expect(sanitizeLine("× Error message")).toBe("Error message");
60
- expect(sanitizeLine("✓ Success message")).toBe("Success message");
61
- });
62
-
63
- it("should normalize whitespace", () => {
64
- expect(sanitizeLine(" Error message ")).toBe("Error message");
65
- });
66
- });
67
-
68
- describe("sanitizeOutput", () => {
69
- it("should return empty string for empty input", () => {
70
- expect(sanitizeOutput("")).toBe("");
71
- expect(sanitizeOutput(" ")).toBe("");
72
- });
73
-
74
- it("should filter out detail lines", () => {
75
- const output = "Error message\n at line 10\n at function foo";
76
- const result = sanitizeOutput(output);
77
- expect(result).toBe("Error message");
78
- });
79
-
80
- it("should remove ANSI codes and normalize", () => {
81
- const output = "\x1b[31mError\x1b[0m\n\x1b[32mSuccess\x1b[0m";
82
- const result = sanitizeOutput(output);
83
- expect(result).toBe("Error\nSuccess");
84
- });
85
- });
86
-
87
- describe("extractErrorMessage", () => {
88
- it("should return undefined for empty input", () => {
89
- expect(extractErrorMessage("")).toBeUndefined();
90
- expect(extractErrorMessage(" ")).toBeUndefined();
91
- });
92
-
93
- it("should return first error line containing error indicator", () => {
94
- // Note: extractErrorMessage sanitizes lines first, then checks isErrorLine
95
- // Since "error:" prefix is removed by sanitizeLine, we need to use a keyword
96
- // that remains in the sanitized line, like "failed" or "syntax"
97
- const output = "Some info\nOperation failed: disk full\nMore info";
98
- const result = extractErrorMessage(output);
99
- expect(result).toBe("Operation failed: disk full");
100
- });
101
-
102
- it("should fall back to first non-empty line", () => {
103
- const output = "info line\nanother info\nand more";
104
- expect(extractErrorMessage(output)).toBe("info line");
105
- });
106
-
107
- it("should find error by keyword", () => {
108
- const result = extractErrorMessage(
109
- "warning: low severity\nfailed: operation",
110
- );
111
- // extractErrorMessage returns the first line with error indicators
112
- // "failed:" contains "failed" which matches ERROR_INDICATORS
113
- expect(result).toBe("failed: operation");
114
- });
115
- });
116
-
117
- describe("truncateMessage", () => {
118
- it("should not truncate short messages", () => {
119
- const msg = "Short message";
120
- expect(truncateMessage(msg, 100)).toBe(msg);
121
- });
122
-
123
- it("should truncate long messages", () => {
124
- const msg = "A".repeat(200);
125
- const result = truncateMessage(msg, 50);
126
- expect(result).toBe(`${"A".repeat(49)}…`);
127
- });
128
-
129
- it("should use default max length of 140", () => {
130
- const msg = "A".repeat(200);
131
- const result = truncateMessage(msg);
132
- expect(result.length).toBe(140);
133
- });
134
- });
135
-
136
- describe("sanitizeToolOutput", () => {
137
- it("should return empty result for empty input", () => {
138
- const result = sanitizeToolOutput("");
139
- expect(result.summary).toBeUndefined();
140
- expect(result.details).toBeUndefined();
141
- expect(result.truncated).toBe(false);
142
- });
143
-
144
- it("should extract summary and details", () => {
145
- const output = "error: something failed\nline 2\nline 3\nline 4";
146
- const result = sanitizeToolOutput(output);
147
- // sanitizeLine removes "error:" prefix
148
- expect(result.summary).toContain("something failed");
149
- expect(result.details).toBeDefined();
150
- });
151
-
152
- it("should mark as truncated when too many lines", () => {
153
- const lines: string[] = [];
154
- for (let i = 0; i < 30; i++) {
155
- lines.push(`error line ${i}`);
156
- }
157
- const result = sanitizeToolOutput(lines.join("\n"));
158
- expect(result.truncated).toBe(true);
159
- expect(result.details).toContain("more lines");
160
- });
161
-
162
- it("should respect max summary length", () => {
163
- const output = `error: ${"x".repeat(200)}`;
164
- const result = sanitizeToolOutput(output, 50);
165
- expect(result.summary?.length).toBeLessThanOrEqual(50);
166
- });
167
- });
168
-
169
- describe("sanitizeBiomeOutput", () => {
170
- it("should parse JSON diagnostics", () => {
171
- const json = JSON.stringify({
172
- diagnostics: [
173
- {
174
- location: {
175
- path: "file.ts",
176
- span: { start: { line: 10, column: 0 } },
177
- },
178
- message: "Unexpected token",
179
- },
180
- ],
181
- });
182
- const result = sanitizeBiomeOutput(json);
183
- expect(result).toContain("file.ts");
184
- expect(result).toContain("11"); // line + 1
185
- expect(result).toContain("Unexpected token");
186
- });
187
-
188
- it("should handle text output with errors", () => {
189
- const output = "error: something went wrong\ninfo: some info";
190
- const result = sanitizeBiomeOutput(output);
191
- expect(result).toContain("error");
192
- });
193
-
194
- it("should return empty for empty input", () => {
195
- expect(sanitizeBiomeOutput("")).toBe("");
196
- });
197
- });
198
-
199
- describe("sanitizeRuffOutput", () => {
200
- it("should parse JSON diagnostics", () => {
201
- const json = JSON.stringify([
202
- {
203
- location: { row: 10, column: 5 },
204
- code: "E501",
205
- message: "Line too long",
206
- },
207
- ]);
208
- const result = sanitizeRuffOutput(json);
209
- expect(result).toContain("10:5");
210
- expect(result).toContain("[E501]");
211
- expect(result).toContain("Line too long");
212
- });
213
-
214
- it("should handle text output", () => {
215
- const output = "file.py:10:5 [E501] Line too long";
216
- const result = sanitizeRuffOutput(output);
217
- expect(result).toContain("E501");
218
- });
219
-
220
- it("should return empty for empty input", () => {
221
- expect(sanitizeRuffOutput("")).toBe("");
222
- });
223
- });
@@ -1,167 +0,0 @@
1
- /**
2
- * Shared architectural debt scanning.
3
- * Scans ast-grep skip rules + complexity metrics + architect.yaml rules.
4
- */
5
- import * as fs from "node:fs";
6
- import * as path from "node:path";
7
- import { safeSpawn } from "./safe-spawn.js";
8
- import { getSourceFiles, parseAstGrepJson } from "./scan-utils.js";
9
- /**
10
- * Scan for skip-category ast-grep violations grouped by absolute file path.
11
- */
12
- export function scanSkipViolations(astGrepClient, configPath, targetPath, isTsProject, skipRules, ruleActions) {
13
- const skipByFile = new Map();
14
- if (!astGrepClient.isAvailable())
15
- return skipByFile;
16
- const sgResult = safeSpawn("npx", [
17
- "sg",
18
- "scan",
19
- "--config",
20
- configPath,
21
- "--json",
22
- "--globs",
23
- "!**/*.test.ts",
24
- "--globs",
25
- "!**/*.spec.ts",
26
- "--globs",
27
- "!**/test-utils.ts",
28
- "--globs",
29
- "!**/.pi-lens/**",
30
- ...(isTsProject ? ["--globs", "!**/*.js"] : []),
31
- targetPath,
32
- ], {
33
- timeout: 30000,
34
- });
35
- const items = parseAstGrepJson(sgResult.stdout?.trim() ?? "");
36
- for (const item of items) {
37
- const rule = item.ruleId || item.rule?.title || item.name || "unknown";
38
- if (!skipRules.has(rule))
39
- continue;
40
- const line = (item.labels?.[0]?.range?.start?.line ?? item.range?.start?.line ?? 0) +
41
- 1;
42
- const absFile = path.resolve(item.file ?? "");
43
- const list = skipByFile.get(absFile) ?? [];
44
- list.push({ rule, line, note: ruleActions[rule]?.note ?? "" });
45
- skipByFile.set(absFile, list);
46
- }
47
- return skipByFile;
48
- }
49
- /**
50
- * Scan complexity metrics for all supported files, grouped by absolute file path.
51
- */
52
- export function scanComplexityMetrics(complexityClient, targetPath, isTsProject) {
53
- const metricsByFile = new Map();
54
- const files = getSourceFiles(targetPath, isTsProject);
55
- for (const full of files) {
56
- if (complexityClient.isSupportedFile(full) &&
57
- !/\.(test|spec)\.[jt]sx?$/.test(path.basename(full))) {
58
- const m = complexityClient.analyzeFile(full);
59
- if (m)
60
- metricsByFile.set(full, {
61
- mi: m.maintainabilityIndex,
62
- cognitive: m.cognitiveComplexity,
63
- nesting: m.maxNestingDepth,
64
- });
65
- }
66
- }
67
- return metricsByFile;
68
- }
69
- /**
70
- * Scan for architectural rule violations grouped by absolute file path.
71
- * Returns map of absolute file path → list of violation messages.
72
- */
73
- export function scanArchitectViolations(architectClient, targetPath) {
74
- const violationsByFile = new Map();
75
- if (!architectClient.hasConfig())
76
- return violationsByFile;
77
- const isTsProject = fs.existsSync(path.join(targetPath, "tsconfig.json"));
78
- const files = getSourceFiles(targetPath, isTsProject);
79
- for (const full of files) {
80
- const relPath = path.relative(targetPath, full).replace(/\\/g, "/");
81
- const content = fs.readFileSync(full, "utf-8");
82
- const lineCount = content.split("\n").length;
83
- const msgs = [];
84
- // Check pattern violations
85
- for (const v of architectClient.checkFile(relPath, content)) {
86
- const lineStr = v.line ? `L${v.line}: ` : "";
87
- msgs.push(`${lineStr}${v.message}`);
88
- }
89
- // Check file size
90
- const sizeV = architectClient.checkFileSize(relPath, lineCount);
91
- if (sizeV) {
92
- msgs.push(sizeV.message);
93
- }
94
- if (msgs.length > 0) {
95
- violationsByFile.set(full, msgs);
96
- }
97
- }
98
- return violationsByFile;
99
- }
100
- /**
101
- * Score each file by combined debt signal. Higher = worse.
102
- */
103
- export function scoreFiles(skipByFile, metricsByFile, architectViolations) {
104
- const allFiles = new Set([
105
- ...skipByFile.keys(),
106
- ...metricsByFile.keys(),
107
- ...(architectViolations?.keys() ?? []),
108
- ]);
109
- return [...allFiles]
110
- .map((file) => {
111
- let score = 0;
112
- const m = metricsByFile.get(file);
113
- if (m) {
114
- if (m.mi < 20)
115
- score += 5;
116
- else if (m.mi < 40)
117
- score += 3;
118
- else if (m.mi < 60)
119
- score += 1;
120
- if (m.cognitive > 300)
121
- score += 4;
122
- else if (m.cognitive > 150)
123
- score += 2;
124
- else if (m.cognitive > 80)
125
- score += 1;
126
- if (m.nesting > 8)
127
- score += 2;
128
- else if (m.nesting > 5)
129
- score += 1;
130
- }
131
- for (const issue of skipByFile.get(file) ?? []) {
132
- if (issue.rule === "large-class")
133
- score += 5;
134
- else if (issue.rule === "no-as-any")
135
- score += 2;
136
- else
137
- score += 1;
138
- }
139
- // Architect violations are high-priority signals
140
- const archMsgs = architectViolations?.get(file);
141
- if (archMsgs && archMsgs.length > 0) {
142
- score += archMsgs.length * 3; // Each violation = 3 points
143
- }
144
- return { file, score };
145
- })
146
- .filter((f) => f.score > 0)
147
- .sort((a, b) => b.score - a.score);
148
- }
149
- /**
150
- * Read a code snippet around the first violation line.
151
- * Returns { snippet, start, end } or null.
152
- */
153
- export function extractCodeSnippet(filePath, firstLine, contextLines = 2, maxLines = 45) {
154
- try {
155
- const fileLines = fs.readFileSync(filePath, "utf-8").split("\n");
156
- const start = Math.max(0, firstLine - 1 - contextLines);
157
- const end = Math.min(fileLines.length, start + maxLines);
158
- return {
159
- snippet: fileLines.slice(start, end).join("\n"),
160
- start: start + 1,
161
- end,
162
- };
163
- }
164
- catch {
165
- return null;
166
- }
167
- }
@@ -1,83 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { EXCLUDED_DIRS, isTestFile } from "./file-utils.js";
4
- /**
5
- * Common parsing logic for ast-grep JSON output (handles both array and NDJSON).
6
- */
7
- // biome-ignore lint/suspicious/noExplicitAny: ast-grep JSON output is untyped
8
- export function parseAstGrepJson(raw) {
9
- if (!raw)
10
- return [];
11
- const trimmed = raw.trim();
12
- if (trimmed.startsWith("[")) {
13
- try {
14
- return JSON.parse(trimmed);
15
- }
16
- catch {
17
- return [];
18
- }
19
- }
20
- return trimmed.split("\n").flatMap((l) => {
21
- try {
22
- return [JSON.parse(l)];
23
- }
24
- catch {
25
- return [];
26
- }
27
- });
28
- }
29
- /**
30
- * Check if a file should be ignored based on project type and common patterns.
31
- */
32
- export function shouldIgnoreFile(filePath, isTsProject) {
33
- const relPath = filePath.replace(/\\/g, "/");
34
- const _basename = path.basename(relPath);
35
- // Ignore compiled JS in TS projects
36
- const isJs = relPath.endsWith(".js") ||
37
- relPath.endsWith(".mjs") ||
38
- relPath.endsWith(".cjs");
39
- if (isTsProject && isJs)
40
- return true;
41
- // Ignore test scripts and common test patterns
42
- if (isTestFile(filePath))
43
- return true;
44
- // Ignore hidden directories and common build outputs
45
- if (EXCLUDED_DIRS.some((d) => relPath.includes(`/${d}/`)))
46
- return true;
47
- return false;
48
- }
49
- /**
50
- * Recursively find source files in a directory, respecting common excludes.
51
- */
52
- export function getSourceFiles(dir, isTsProject) {
53
- const files = [];
54
- if (!fs.existsSync(dir))
55
- return files;
56
- const scan = (d) => {
57
- let entries = [];
58
- try {
59
- entries = fs.readdirSync(d, { withFileTypes: true });
60
- }
61
- catch {
62
- return;
63
- }
64
- for (const entry of entries) {
65
- const full = path.join(d, entry.name);
66
- if (entry.isDirectory()) {
67
- if (EXCLUDED_DIRS.includes(entry.name))
68
- continue;
69
- scan(full);
70
- }
71
- else if (/\.(ts|tsx|js|jsx|py|go|rs)$/.test(entry.name)) {
72
- // Skip compiled JS if it's a TS project
73
- if (isTsProject &&
74
- entry.name.endsWith(".js") &&
75
- fs.existsSync(full.replace(/\.js$/, ".ts")))
76
- continue;
77
- files.push(full);
78
- }
79
- }
80
- };
81
- scan(dir);
82
- return files;
83
- }