pi-lens 3.6.2 → 3.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/CHANGELOG.md +10 -2
  2. package/package.json +4 -4
  3. package/tsconfig.json +1 -1
  4. package/clients/__tests__/file-time.test.js +0 -216
  5. package/clients/__tests__/file-time.test.ts +0 -276
  6. package/clients/__tests__/format-service.test.js +0 -245
  7. package/clients/__tests__/format-service.test.ts +0 -339
  8. package/clients/__tests__/formatters.test.js +0 -271
  9. package/clients/__tests__/formatters.test.ts +0 -401
  10. package/clients/agent-behavior-client.js +0 -110
  11. package/clients/agent-behavior-client.test.js +0 -94
  12. package/clients/agent-behavior-client.test.ts +0 -116
  13. package/clients/amain-types.js +0 -164
  14. package/clients/architect-client.js +0 -291
  15. package/clients/ast-grep-client.js +0 -253
  16. package/clients/ast-grep-parser.js +0 -84
  17. package/clients/ast-grep-rule-manager.js +0 -89
  18. package/clients/ast-grep-types.js +0 -9
  19. package/clients/auto-loop.js +0 -131
  20. package/clients/biome-client.js +0 -420
  21. package/clients/biome-client.test.js +0 -144
  22. package/clients/biome-client.test.ts +0 -163
  23. package/clients/cache/rule-cache.js +0 -72
  24. package/clients/cache-manager.js +0 -245
  25. package/clients/cache-manager.test.js +0 -197
  26. package/clients/cache-manager.test.ts +0 -299
  27. package/clients/complexity-client.js +0 -675
  28. package/clients/complexity-client.test.js +0 -234
  29. package/clients/complexity-client.test.ts +0 -255
  30. package/clients/config-validator.js +0 -465
  31. package/clients/dependency-checker.js +0 -325
  32. package/clients/dependency-checker.test.js +0 -60
  33. package/clients/dependency-checker.test.ts +0 -71
  34. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  35. package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
  36. package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
  37. package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
  38. package/clients/dispatch/debug.log +0 -1
  39. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  40. package/clients/dispatch/dispatcher.edge.test.ts +0 -100
  41. package/clients/dispatch/dispatcher.format.test.js +0 -46
  42. package/clients/dispatch/dispatcher.format.test.ts +0 -58
  43. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  44. package/clients/dispatch/dispatcher.inline.test.ts +0 -93
  45. package/clients/dispatch/dispatcher.js +0 -381
  46. package/clients/dispatch/dispatcher.test.js +0 -116
  47. package/clients/dispatch/dispatcher.test.ts +0 -149
  48. package/clients/dispatch/integration.js +0 -108
  49. package/clients/dispatch/plan.js +0 -183
  50. package/clients/dispatch/runners/architect.js +0 -83
  51. package/clients/dispatch/runners/architect.test.js +0 -138
  52. package/clients/dispatch/runners/architect.test.ts +0 -162
  53. package/clients/dispatch/runners/ast-grep-napi.js +0 -405
  54. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
  55. package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
  56. package/clients/dispatch/runners/ast-grep.js +0 -157
  57. package/clients/dispatch/runners/biome.js +0 -55
  58. package/clients/dispatch/runners/config-validation.js +0 -67
  59. package/clients/dispatch/runners/go-vet.js +0 -48
  60. package/clients/dispatch/runners/index.js +0 -47
  61. package/clients/dispatch/runners/lsp.js +0 -102
  62. package/clients/dispatch/runners/oxlint.js +0 -67
  63. package/clients/dispatch/runners/oxlint.test.js +0 -230
  64. package/clients/dispatch/runners/oxlint.test.ts +0 -303
  65. package/clients/dispatch/runners/pyright.js +0 -100
  66. package/clients/dispatch/runners/pyright.test.js +0 -98
  67. package/clients/dispatch/runners/pyright.test.ts +0 -121
  68. package/clients/dispatch/runners/python-slop.js +0 -97
  69. package/clients/dispatch/runners/python-slop.test.js +0 -203
  70. package/clients/dispatch/runners/python-slop.test.ts +0 -298
  71. package/clients/dispatch/runners/ruff.js +0 -48
  72. package/clients/dispatch/runners/rust-clippy.js +0 -102
  73. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  74. package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
  75. package/clients/dispatch/runners/shellcheck.js +0 -147
  76. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  77. package/clients/dispatch/runners/shellcheck.test.ts +0 -129
  78. package/clients/dispatch/runners/similarity.js +0 -230
  79. package/clients/dispatch/runners/spellcheck.js +0 -106
  80. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  81. package/clients/dispatch/runners/spellcheck.test.ts +0 -214
  82. package/clients/dispatch/runners/tree-sitter.js +0 -246
  83. package/clients/dispatch/runners/ts-lsp.js +0 -125
  84. package/clients/dispatch/runners/ts-slop.js +0 -113
  85. package/clients/dispatch/runners/type-safety.js +0 -142
  86. package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
  87. package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
  88. package/clients/dispatch/runners/utils.js +0 -51
  89. package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
  90. package/clients/dispatch/types.js +0 -16
  91. package/clients/dispatch/utils/format-utils.js +0 -44
  92. package/clients/dogfood.test.js +0 -201
  93. package/clients/dogfood.test.ts +0 -269
  94. package/clients/file-kinds.js +0 -177
  95. package/clients/file-kinds.test.js +0 -169
  96. package/clients/file-kinds.test.ts +0 -210
  97. package/clients/file-time.js +0 -152
  98. package/clients/file-utils.js +0 -40
  99. package/clients/fix-scanners.js +0 -204
  100. package/clients/format-service.js +0 -184
  101. package/clients/formatters.js +0 -488
  102. package/clients/go-client.js +0 -203
  103. package/clients/go-client.test.js +0 -127
  104. package/clients/go-client.test.ts +0 -143
  105. package/clients/installer/index.js +0 -403
  106. package/clients/interviewer-templates.js +0 -75
  107. package/clients/interviewer.js +0 -173
  108. package/clients/jscpd-client.js +0 -196
  109. package/clients/jscpd-client.test.js +0 -127
  110. package/clients/jscpd-client.test.ts +0 -145
  111. package/clients/knip-client.js +0 -239
  112. package/clients/knip-client.test.js +0 -112
  113. package/clients/knip-client.test.ts +0 -128
  114. package/clients/latency-logger.js +0 -40
  115. package/clients/lsp/__tests__/client.test.js +0 -310
  116. package/clients/lsp/__tests__/client.test.ts +0 -412
  117. package/clients/lsp/__tests__/config.test.js +0 -167
  118. package/clients/lsp/__tests__/config.test.ts +0 -217
  119. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  120. package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
  121. package/clients/lsp/__tests__/integration.test.js +0 -127
  122. package/clients/lsp/__tests__/integration.test.ts +0 -160
  123. package/clients/lsp/__tests__/launch.test.js +0 -313
  124. package/clients/lsp/__tests__/launch.test.ts +0 -394
  125. package/clients/lsp/__tests__/server.test.js +0 -259
  126. package/clients/lsp/__tests__/server.test.ts +0 -332
  127. package/clients/lsp/__tests__/service.test.js +0 -438
  128. package/clients/lsp/__tests__/service.test.ts +0 -530
  129. package/clients/lsp/client.js +0 -350
  130. package/clients/lsp/config.js +0 -112
  131. package/clients/lsp/index.js +0 -318
  132. package/clients/lsp/installer/index.js +0 -391
  133. package/clients/lsp/interactive-install.js +0 -221
  134. package/clients/lsp/language.js +0 -170
  135. package/clients/lsp/launch.js +0 -329
  136. package/clients/lsp/lsp/launch.js +0 -116
  137. package/clients/lsp/lsp/server.js +0 -532
  138. package/clients/lsp/lsp-index.js +0 -10
  139. package/clients/lsp/path-utils.js +0 -5
  140. package/clients/lsp/server.js +0 -725
  141. package/clients/lsp/test-py-spawn/requirements.txt +0 -1
  142. package/clients/lsp/test-py-spawn/test.py +0 -3
  143. package/clients/lsp/test-py-svc/requirements.txt +0 -1
  144. package/clients/lsp/test-py-svc/test.py +0 -3
  145. package/clients/lsp/test-python-project/requirements.txt +0 -1
  146. package/clients/lsp/test-python-project/test.py +0 -5
  147. package/clients/metrics-client.js +0 -107
  148. package/clients/metrics-client.test.js +0 -128
  149. package/clients/metrics-client.test.ts +0 -163
  150. package/clients/metrics-history.js +0 -367
  151. package/clients/path-utils.js +0 -142
  152. package/clients/pipeline.js +0 -272
  153. package/clients/production-readiness.js +0 -522
  154. package/clients/project-index.js +0 -255
  155. package/clients/project-metadata.js +0 -531
  156. package/clients/ruff-client.js +0 -325
  157. package/clients/ruff-client.test.js +0 -132
  158. package/clients/ruff-client.test.ts +0 -153
  159. package/clients/rules-scanner.js +0 -97
  160. package/clients/runner-tracker.js +0 -152
  161. package/clients/rust-client.js +0 -205
  162. package/clients/rust-client.test.js +0 -108
  163. package/clients/rust-client.test.ts +0 -130
  164. package/clients/safe-spawn-async.js +0 -163
  165. package/clients/safe-spawn.js +0 -241
  166. package/clients/sanitize.js +0 -291
  167. package/clients/sanitize.test.js +0 -177
  168. package/clients/sanitize.test.ts +0 -223
  169. package/clients/scan-architectural-debt.js +0 -167
  170. package/clients/scan-utils.js +0 -83
  171. package/clients/secrets-scanner.js +0 -119
  172. package/clients/secrets-scanner.test.js +0 -100
  173. package/clients/secrets-scanner.test.ts +0 -113
  174. package/clients/sg-runner.js +0 -292
  175. package/clients/state-matrix.js +0 -160
  176. package/clients/subprocess-client.js +0 -65
  177. package/clients/symbol-types.js +0 -5
  178. package/clients/test-runner-client.js +0 -523
  179. package/clients/test-runner-client.test.js +0 -192
  180. package/clients/test-runner-client.test.ts +0 -253
  181. package/clients/test-utils.js +0 -27
  182. package/clients/test-utils.ts +0 -36
  183. package/clients/todo-scanner.js +0 -200
  184. package/clients/todo-scanner.test.js +0 -301
  185. package/clients/todo-scanner.test.ts +0 -352
  186. package/clients/tool-availability.js +0 -207
  187. package/clients/tree-sitter-client.js +0 -601
  188. package/clients/tree-sitter-query-loader.js +0 -355
  189. package/clients/tree-sitter-symbol-extractor.js +0 -289
  190. package/clients/ts-service.js +0 -129
  191. package/clients/type-coverage-client.js +0 -127
  192. package/clients/type-coverage-client.test.js +0 -105
  193. package/clients/type-coverage-client.test.ts +0 -125
  194. package/clients/type-safety-client.js +0 -138
  195. package/clients/types.js +0 -11
  196. package/clients/typescript-client.codefix.test.js +0 -157
  197. package/clients/typescript-client.codefix.test.ts +0 -186
  198. package/clients/typescript-client.js +0 -509
  199. package/clients/typescript-client.test.js +0 -105
  200. package/clients/typescript-client.test.ts +0 -126
  201. package/commands/booboo.js +0 -1007
  202. package/commands/fix-from-booboo.js +0 -398
  203. package/commands/fix-simplified.js +0 -618
  204. package/commands/rate.js +0 -281
  205. package/commands/rate.test.js +0 -119
  206. package/commands/rate.test.ts +0 -131
  207. 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;