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,246 +0,0 @@
1
- /**
2
- * Tree-sitter Structural Analysis Runner
3
- *
4
- * Executes all loaded tree-sitter query files from rules/tree-sitter-queries/
5
- * for fast AST-based pattern matching.
6
- * Updated: ast-grep-napi test
7
- */
8
- import * as fs from "node:fs";
9
- import * as path from "node:path";
10
- import { RuleCache } from "../../cache/rule-cache.js";
11
- import { normalizeMapKey } from "../../path-utils.js";
12
- import { TreeSitterClient } from "../../tree-sitter-client.js";
13
- import { queryLoader, } from "../../tree-sitter-query-loader.js";
14
- // Module-level singleton: web-tree-sitter WASM must only be initialized once per process.
15
- // Creating a new TreeSitterClient() on every write resets TRANSFER_BUFFER (a module-level
16
- // WASM pointer) — concurrent writes race on _ts_init() and corrupt shared WASM state → crash.
17
- let _sharedClient = null;
18
- function getSharedClient() {
19
- if (!_sharedClient) {
20
- _sharedClient = new TreeSitterClient();
21
- }
22
- return _sharedClient;
23
- }
24
- /**
25
- * Check if a code block is effectively empty (ignoring comments and whitespace)
26
- */
27
- function isEmptyBlock(blockContent) {
28
- // Remove comments, whitespace, and check if anything remains
29
- const cleaned = blockContent
30
- .replace(/\/\/.*$/gm, "") // Remove single-line comments
31
- .replace(/\/\*[\s\S]*?\*\//g, "") // Remove multi-line comments
32
- .replace(/\s+/g, "") // Remove all whitespace
33
- .trim();
34
- return cleaned.length === 0 || cleaned === "{}";
35
- }
36
- /**
37
- * Extract parameter count from match text
38
- */
39
- function countParameters(matchText) {
40
- // Count commas in parameter list, or check for non-empty params
41
- // Simple heuristic: count commas + 1, or 0 if empty
42
- const paramsMatch = matchText.match(/\((.*)\)/);
43
- if (!paramsMatch)
44
- return 0;
45
- const params = paramsMatch[1].trim();
46
- if (!params)
47
- return 0;
48
- return params.split(",").length;
49
- }
50
- /**
51
- * Apply post-filter to determine if a match should be reported
52
- */
53
- function applyPostFilter(query, captures) {
54
- if (!query.post_filter)
55
- return true; // No filter = always include
56
- switch (query.post_filter) {
57
- case "empty_body": {
58
- // Check if the BODY capture is effectively empty
59
- const body = captures.BODY || captures.body || "";
60
- return isEmptyBlock(body);
61
- }
62
- case "count_params": {
63
- // Check if parameter count meets minimum
64
- const minParams = query.post_filter_params?.min_params || 6;
65
- // Get PARAMS capture which contains the parameter list like "(a, b, c)"
66
- const params = captures.PARAMS || captures.params || captures.PARAM || "";
67
- const paramCount = countParameters(params);
68
- return paramCount >= minParams;
69
- }
70
- case "not_dbg_method":
71
- // Exclude debug methods (for console-statement)
72
- return !/\b(dbg|debug|logDebug)\b/i.test(captures.METHOD || "");
73
- default:
74
- // Unknown filter - include by default (safer than excluding)
75
- return true;
76
- }
77
- }
78
- /**
79
- * Check if variable name matches secret patterns
80
- * This handles the #match? predicate from tree-sitter queries
81
- */
82
- function matchesSecretPattern(varName) {
83
- const secretPatterns = [
84
- /api[_-]?key/i,
85
- /api[_-]?secret/i,
86
- /password/i,
87
- /secret/i,
88
- /token/i,
89
- /auth/i,
90
- /private[_-]?key/i,
91
- /access[_-]?token/i,
92
- /credentials/i,
93
- /aws[_-]?secret/i,
94
- /github[_-]?token/i,
95
- ];
96
- return secretPatterns.some((pattern) => pattern.test(varName));
97
- }
98
- const treeSitterRunner = {
99
- id: "tree-sitter",
100
- appliesTo: ["jsts", "python"],
101
- priority: 14, // Between oxlint (12) and ast-grep-napi (15)
102
- enabledByDefault: true,
103
- skipTestFiles: false, // Run on test files too (structural issues matter there)
104
- async run(ctx) {
105
- // Use singleton client — WASM must never be re-initialized after first call
106
- const client = getSharedClient();
107
- if (!client.isAvailable()) {
108
- return { status: "skipped", diagnostics: [], semantic: "none" };
109
- }
110
- const initialized = await client.init();
111
- if (!initialized) {
112
- return { status: "skipped", diagnostics: [], semantic: "none" };
113
- }
114
- // Determine language from file extension
115
- const filePath = ctx.filePath;
116
- const isPython = filePath.endsWith(".py");
117
- const isTypeScript = filePath.endsWith(".ts");
118
- const isTSX = filePath.endsWith(".tsx");
119
- const isJavaScript = filePath.endsWith(".js") || filePath.endsWith(".jsx");
120
- let languageId;
121
- if (isPython) {
122
- languageId = "python";
123
- }
124
- else if (isTSX) {
125
- languageId = "tsx";
126
- }
127
- else if (isTypeScript) {
128
- languageId = "typescript";
129
- }
130
- else if (isJavaScript) {
131
- languageId = "javascript";
132
- }
133
- else {
134
- return { status: "skipped", diagnostics: [], semantic: "none" };
135
- }
136
- // Try cache first, fall back to loading from disk
137
- let languageQueries = [];
138
- const cache = new RuleCache(languageId);
139
- // Get all rule files for this language (use ctx.cwd for project root)
140
- const rulesDir = path.join(ctx.cwd, "rules", "tree-sitter-queries", languageId);
141
- const ruleFiles = [];
142
- if (fs.existsSync(rulesDir)) {
143
- ruleFiles.push(...fs
144
- .readdirSync(rulesDir)
145
- .filter((f) => f.endsWith(".yml"))
146
- .map((f) => path.join(rulesDir, f)));
147
- }
148
- // Try cache
149
- const cached = cache.get(ruleFiles);
150
- if (cached) {
151
- // Use cached queries
152
- languageQueries = cached.queries.map((q) => ({
153
- ...q,
154
- has_fix: false,
155
- filePath: "",
156
- }));
157
- }
158
- else {
159
- // Load from disk
160
- if (!queryLoader.getAllQueries().length) {
161
- await queryLoader.loadQueries();
162
- }
163
- const allQueries = queryLoader.getAllQueries();
164
- languageQueries = allQueries.filter((q) => q.language === languageId ||
165
- (isJavaScript && q.language === "typescript"));
166
- // Save to cache
167
- cache.set(ruleFiles, languageQueries.map((q) => ({
168
- id: q.id,
169
- name: q.name,
170
- severity: q.severity,
171
- language: q.language,
172
- message: q.message,
173
- query: q.query,
174
- metavars: q.metavars,
175
- post_filter: q.post_filter,
176
- post_filter_params: q.post_filter_params,
177
- })));
178
- }
179
- if (languageQueries.length === 0) {
180
- return { status: "succeeded", diagnostics: [], semantic: "none" };
181
- }
182
- const diagnostics = [];
183
- // Run each query against the file
184
- for (const query of languageQueries) {
185
- try {
186
- // Extract directory from file path (use path.dirname for cross-platform)
187
- const rootDir = path.dirname(filePath);
188
- const matches = await client.structuralSearch(query.id, // Use query ID as pattern (findMatchingQuery will resolve it)
189
- languageId, rootDir, {
190
- maxResults: 10,
191
- fileFilter: (f) => normalizeMapKey(f) === normalizeMapKey(filePath),
192
- });
193
- for (const match of matches) {
194
- // Apply post-filter if defined (pass captures for proper filtering)
195
- if (!applyPostFilter(query, match.captures)) {
196
- continue; // Skip this match - filter didn't pass
197
- }
198
- // For hardcoded-secrets, also check variable name pattern
199
- if (query.id === "hardcoded-secrets") {
200
- // Extract variable name from captures
201
- const varName = match.captures?.VARNAME || "";
202
- if (!varName || !matchesSecretPattern(varName)) {
203
- continue; // Skip - no variable name or doesn't match secret patterns
204
- }
205
- }
206
- // Get line/column from match (already 0-indexed from tree-sitter)
207
- const line = match.line;
208
- const column = match.column;
209
- // Map severity to semantic
210
- const semantic = query.severity === "error"
211
- ? "blocking"
212
- : query.severity === "warning"
213
- ? "warning"
214
- : "none";
215
- diagnostics.push({
216
- id: `tree-sitter:${query.id}:${line}`,
217
- message: query.message,
218
- filePath,
219
- line: line + 1, // 1-indexed
220
- column: column + 1, // 1-indexed
221
- severity: query.severity,
222
- semantic,
223
- tool: "tree-sitter",
224
- rule: query.id,
225
- });
226
- }
227
- }
228
- catch (err) {
229
- // Individual query failure shouldn't stop other queries
230
- console.error(`[tree-sitter] Query ${query.id} failed:`, err);
231
- }
232
- }
233
- if (diagnostics.length === 0) {
234
- return { status: "succeeded", diagnostics: [], semantic: "none" };
235
- }
236
- // Check if any blocking issues
237
- const hasBlocking = diagnostics.some((d) => d.semantic === "blocking");
238
- return {
239
- status: hasBlocking ? "failed" : "succeeded",
240
- diagnostics,
241
- semantic: hasBlocking ? "blocking" : "warning",
242
- };
243
- },
244
- };
245
- export default treeSitterRunner;
246
- // test ast-grep-napi re-enable
@@ -1,125 +0,0 @@
1
- /**
2
- * TypeScript LSP runner for dispatch system
3
- *
4
- * Uses the new LSP client architecture (Phase 3) when --lens-lsp is enabled.
5
- * Falls back to built-in TypeScriptClient for backward compatibility.
6
- *
7
- * @deprecated The built-in TypeScriptClient is deprecated. Use --lens-lsp for full LSP support.
8
- */
9
- import { getLSPService } from "../../lsp/index.js";
10
- import { TypeScriptClient } from "../../typescript-client.js";
11
- import { readFileContent } from "./utils.js";
12
- const tsLspRunner = {
13
- id: "ts-lsp",
14
- appliesTo: ["jsts"],
15
- priority: 5,
16
- enabledByDefault: true,
17
- async run(ctx) {
18
- // Only check TypeScript files
19
- if (!ctx.filePath.match(/\.tsx?$/)) {
20
- return { status: "skipped", diagnostics: [], semantic: "none" };
21
- }
22
- // When --lens-lsp is active, the `lsp` runner (priority 4) already
23
- // handles TypeScript via the LSP service. Skip to avoid duplicate work.
24
- if (ctx.pi.getFlag("lens-lsp")) {
25
- return { status: "skipped", diagnostics: [], semantic: "none" };
26
- }
27
- // DEPRECATED: Fall back to built-in TypeScriptClient
28
- // This path is deprecated and will be removed in a future release
29
- return runWithBuiltinClient(ctx);
30
- },
31
- };
32
- /**
33
- * Run with new LSP client (Phase 3)
34
- */
35
- async function runWithLSPClient(ctx) {
36
- const lspService = getLSPService();
37
- // Check if we have LSP available for this file
38
- const hasLSP = await lspService.hasLSP(ctx.filePath);
39
- if (!hasLSP) {
40
- return { status: "skipped", diagnostics: [], semantic: "none" };
41
- }
42
- // Read file content
43
- const content = readFileContent(ctx.filePath);
44
- if (!content) {
45
- return { status: "skipped", diagnostics: [], semantic: "none" };
46
- }
47
- // Open file in LSP and get diagnostics
48
- await lspService.openFile(ctx.filePath, content);
49
- // getDiagnostics() internally calls waitForDiagnostics() with bus
50
- // subscription + 150ms debounce + 3s timeout
51
- const lspDiags = await lspService.getDiagnostics(ctx.filePath);
52
- // Convert LSP diagnostics to our format
53
- // Defensive: filter out malformed diagnostics that may lack range
54
- const diagnostics = lspDiags
55
- .filter((d) => d.range?.start?.line !== undefined)
56
- .map((d) => ({
57
- id: `ts-lsp:${d.code ?? "unknown"}:${d.range.start.line}`,
58
- message: d.message,
59
- filePath: ctx.filePath,
60
- line: d.range.start.line + 1,
61
- column: d.range.start.character + 1,
62
- severity: d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info",
63
- semantic: d.severity === 1 ? "blocking" : "warning",
64
- tool: "ts-lsp",
65
- code: String(d.code ?? ""),
66
- }));
67
- return {
68
- status: "failed",
69
- diagnostics,
70
- semantic: "blocking",
71
- };
72
- }
73
- /**
74
- * Run with deprecated built-in TypeScriptClient
75
- * @deprecated Use runWithLSPClient instead
76
- */
77
- async function runWithBuiltinClient(ctx) {
78
- const tsClient = new TypeScriptClient();
79
- const content = readFileContent(ctx.filePath);
80
- if (!content) {
81
- return { status: "skipped", diagnostics: [], semantic: "none" };
82
- }
83
- tsClient.updateFile(ctx.filePath, content);
84
- const diags = tsClient.getDiagnostics(ctx.filePath);
85
- if (diags.length === 0) {
86
- return { status: "succeeded", diagnostics: [], semantic: "none" };
87
- }
88
- // Get code fixes for all errors
89
- const allFixes = tsClient.getAllCodeFixes(ctx.filePath);
90
- // Convert to diagnostics
91
- const diagnostics = [];
92
- // The built-in client returns Diagnostic with { range: { start: { line, character } } }
93
- for (const d of diags) {
94
- // Safely access nested properties
95
- if (!d.range?.start)
96
- continue;
97
- const line = d.range.start.line;
98
- const character = d.range.start.character ?? 0;
99
- const severity = d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info";
100
- const semantic = d.severity === 1 ? "blocking" : "warning";
101
- // Find fixes for this line
102
- const lineFixes = allFixes.get(line);
103
- const fixDescription = lineFixes?.[0]?.description;
104
- diagnostics.push({
105
- id: `ts:${d.code}:${line}`,
106
- message: fixDescription
107
- ? `${d.message} [💡 ${fixDescription}]`
108
- : d.message,
109
- filePath: ctx.filePath,
110
- line: line + 1,
111
- column: character + 1,
112
- severity,
113
- semantic,
114
- tool: "ts-lsp",
115
- fixable: !!lineFixes && lineFixes.length > 0,
116
- fixSuggestion: fixDescription,
117
- });
118
- }
119
- return {
120
- status: "failed",
121
- diagnostics,
122
- semantic: "blocking",
123
- };
124
- }
125
- export default tsLspRunner;
@@ -1,113 +0,0 @@
1
- /**
2
- * TypeScript Slop runner for dispatch system
3
- *
4
- * Detects "slop" patterns in TypeScript/JavaScript code:
5
- * - Verbose patterns (ceremony that adds no value)
6
- * - Defensive over-checking (excessive guards)
7
- * - Manual reimplementation of builtins
8
- * - Unnecessary object allocations
9
- *
10
- * Based on slop-code-bench patterns adapted for TypeScript
11
- */
12
- import { safeSpawn } from "../../safe-spawn.js";
13
- import { createConfigFinder, isSgAvailable, } from "./utils/runner-helpers.js";
14
- const findSlopConfig = createConfigFinder("ts-slop-rules");
15
- const tsSlopRunner = {
16
- id: "ts-slop",
17
- // NOTE: TypeScript/JavaScript slop detection is now handled by ast-grep-napi
18
- // This CLI runner is kept as fallback for edge cases but disabled by default
19
- appliesTo: [], // Disabled - use ast-grep-napi instead
20
- priority: 20,
21
- enabledByDefault: false,
22
- skipTestFiles: true,
23
- async run(ctx) {
24
- // Check if ast-grep is available
25
- if (!isSgAvailable()) {
26
- return { status: "skipped", diagnostics: [], semantic: "none" };
27
- }
28
- // Find slop config
29
- const configPath = findSlopConfig(ctx.cwd);
30
- if (!configPath) {
31
- return { status: "skipped", diagnostics: [], semantic: "none" };
32
- }
33
- // Run ast-grep scan
34
- const args = ["sg", "scan", "--config", configPath, "--json", ctx.filePath];
35
- const result = safeSpawn("npx", args, {
36
- timeout: 30000,
37
- });
38
- const raw = result.stdout + result.stderr;
39
- if (result.status === 0 && !raw.trim()) {
40
- return { status: "succeeded", diagnostics: [], semantic: "none" };
41
- }
42
- // Parse results
43
- const diagnostics = parseSlopOutput(raw, ctx.filePath);
44
- if (diagnostics.length === 0) {
45
- return { status: "succeeded", diagnostics: [], semantic: "none" };
46
- }
47
- return {
48
- status: "failed",
49
- diagnostics,
50
- semantic: "warning",
51
- };
52
- },
53
- };
54
- function parseSlopOutput(raw, filePath) {
55
- const diagnostics = [];
56
- try {
57
- const parsed = JSON.parse(raw);
58
- if (Array.isArray(parsed)) {
59
- for (const item of parsed) {
60
- const line = item.range?.start?.line || 1;
61
- const ruleId = item.rule || "unknown";
62
- const message = item.message || "";
63
- // Categorize by severity based on weight from metadata
64
- const weight = item.metadata?.weight || 3;
65
- const severity = weight >= 4 ? "error" : "warning";
66
- const category = item.metadata?.category || "slop";
67
- // Add slop category indicator to message
68
- let enhancedMessage = `[${category}] ${message}`;
69
- if (item.replacement) {
70
- const preview = item.replacement.length > 40
71
- ? `${item.replacement.substring(0, 40)}...`
72
- : item.replacement;
73
- enhancedMessage += `\n💡 Suggested fix: → "${preview}"`;
74
- }
75
- diagnostics.push({
76
- id: `ts-slop-${line}-${ruleId}`,
77
- message: enhancedMessage,
78
- filePath,
79
- line,
80
- column: item.range?.start?.column || 0,
81
- severity,
82
- semantic: severity === "error" ? "blocking" : "warning",
83
- tool: "ts-slop",
84
- rule: ruleId,
85
- fixable: !!item.replacement,
86
- fixSuggestion: item.replacement,
87
- });
88
- }
89
- }
90
- }
91
- catch {
92
- // JSON parse failed, try line-by-line
93
- const lines = raw.split("\n");
94
- for (const line of lines) {
95
- if (line.includes(":") && line.includes("L")) {
96
- const match = line.match(/L(\d+):?\s*(.+)/);
97
- if (match) {
98
- diagnostics.push({
99
- id: `ts-slop-${match[1]}-line`,
100
- message: `[slop] ${match[2].trim()}`,
101
- filePath,
102
- line: parseInt(match[1], 10),
103
- severity: "warning",
104
- semantic: "warning",
105
- tool: "ts-slop",
106
- });
107
- }
108
- }
109
- }
110
- }
111
- return diagnostics;
112
- }
113
- export default tsSlopRunner;
@@ -1,142 +0,0 @@
1
- /**
2
- * Type safety runner for dispatch system
3
- *
4
- * Checks for:
5
- * - Switch exhaustiveness
6
- * - Missing return statements
7
- * - Type safety issues
8
- */
9
- import { readFileContent } from "./utils.js";
10
- const typeSafetyRunner = {
11
- id: "type-safety",
12
- appliesTo: ["jsts"],
13
- priority: 20,
14
- enabledByDefault: true,
15
- async run(ctx) {
16
- // Only check TypeScript files
17
- if (!ctx.filePath.match(/\.tsx?$/)) {
18
- return { status: "skipped", diagnostics: [], semantic: "none" };
19
- }
20
- const content = readFileContent(ctx.filePath);
21
- if (!content) {
22
- return { status: "skipped", diagnostics: [], semantic: "none" };
23
- }
24
- const diagnostics = [];
25
- // Check for switch exhaustiveness patterns
26
- diagnostics.push(...checkSwitchExhaustiveness(content, ctx.filePath));
27
- // Check for missing return patterns
28
- diagnostics.push(...checkMissingReturns(content, ctx.filePath));
29
- // Check for any type usage
30
- diagnostics.push(...checkAnyTypeUsage(content, ctx.filePath));
31
- if (diagnostics.length === 0) {
32
- return { status: "succeeded", diagnostics: [], semantic: "none" };
33
- }
34
- const hasErrors = diagnostics.some((d) => d.severity === "error");
35
- return {
36
- status: hasErrors ? "failed" : "succeeded",
37
- diagnostics,
38
- semantic: hasErrors ? "blocking" : "warning",
39
- };
40
- },
41
- };
42
- function checkSwitchExhaustiveness(content, filePath) {
43
- const diagnostics = [];
44
- const switchRegex = /switch\s*\(\s*(\w+)\s*\)\s*\{/g;
45
- let match;
46
- while ((match = switchRegex.exec(content)) !== null) {
47
- const switchStart = match.index;
48
- const switchVar = match[1];
49
- // Find the switch block
50
- let braceCount = 0;
51
- const blockStart = content.indexOf("{", switchStart);
52
- let blockEnd = blockStart;
53
- while (blockEnd < content.length && braceCount >= 0) {
54
- if (content[blockEnd] === "{")
55
- braceCount++;
56
- if (content[blockEnd] === "}")
57
- braceCount--;
58
- blockEnd++;
59
- }
60
- const switchBlock = content.slice(blockStart, blockEnd);
61
- // Check if it has a default case
62
- if (!/\bdefault\s*:/.test(switchBlock)) {
63
- const caseCount = (switchBlock.match(/\bcase\s+/g) || []).length;
64
- if (caseCount > 2) {
65
- const lineNum = content.slice(0, switchStart).split("\n").length;
66
- diagnostics.push({
67
- id: `switch-${lineNum}-${switchVar}`,
68
- message: `Switch on '${switchVar}' has ${caseCount} cases but no default`,
69
- filePath,
70
- line: lineNum,
71
- severity: "warning",
72
- semantic: "warning",
73
- tool: "type-safety",
74
- rule: "switch-exhaustiveness",
75
- fixSuggestion: "Add 'default: break;' or use exhaustive checking",
76
- });
77
- }
78
- }
79
- }
80
- return diagnostics;
81
- }
82
- function checkMissingReturns(content, filePath) {
83
- const diagnostics = [];
84
- const funcRegex = /function\s+(\w+)\s*\([^)]*\)\s*:\s*([^\s{]+)\s*\{/g;
85
- let match;
86
- while ((match = funcRegex.exec(content)) !== null) {
87
- const returnType = match[2].trim();
88
- if (returnType === "void" ||
89
- returnType === "never" ||
90
- returnType.includes("Promise<void>")) {
91
- continue;
92
- }
93
- const funcStart = match.index;
94
- const funcName = match[1];
95
- // Find function block
96
- let braceCount = 0;
97
- const blockStart = content.indexOf("{", funcStart);
98
- let blockEnd = blockStart;
99
- while (blockEnd < content.length && braceCount >= 0) {
100
- if (content[blockEnd] === "{")
101
- braceCount++;
102
- if (content[blockEnd] === "}")
103
- braceCount--;
104
- blockEnd++;
105
- }
106
- const funcBlock = content.slice(blockStart, blockEnd);
107
- if (!/\breturn\b/.test(funcBlock)) {
108
- const lineNum = content.slice(0, funcStart).split("\n").length;
109
- diagnostics.push({
110
- id: `missing-return-${lineNum}-${funcName}`,
111
- message: `Function '${funcName}' returns '${returnType}' but has no return statement`,
112
- filePath,
113
- line: lineNum,
114
- severity: "error",
115
- semantic: "blocking",
116
- tool: "type-safety",
117
- rule: "missing-return",
118
- });
119
- }
120
- }
121
- return diagnostics;
122
- }
123
- function checkAnyTypeUsage(content, filePath) {
124
- const diagnostics = [];
125
- const anyRegex = /:\s*any\b|as\s+any\b/g;
126
- let match;
127
- while ((match = anyRegex.exec(content)) !== null) {
128
- const lineNum = content.slice(0, match.index).split("\n").length;
129
- diagnostics.push({
130
- id: `any-type-${lineNum}`,
131
- message: "Avoid 'any' type — use 'unknown' or define a proper interface",
132
- filePath,
133
- line: lineNum,
134
- severity: "warning",
135
- semantic: "warning",
136
- tool: "type-safety",
137
- rule: "no-any-type",
138
- });
139
- }
140
- return diagnostics;
141
- }
142
- export default typeSafetyRunner;