pi-lens 2.2.9 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +709 -519
  3. package/clients/__tests__/file-time.test.js +216 -0
  4. package/clients/__tests__/file-time.test.ts +276 -0
  5. package/clients/__tests__/format-service.test.js +245 -0
  6. package/clients/__tests__/format-service.test.ts +339 -0
  7. package/clients/__tests__/formatters.test.js +271 -0
  8. package/clients/__tests__/formatters.test.ts +401 -0
  9. package/clients/amain-types.js +164 -0
  10. package/clients/amain-types.ts +165 -0
  11. package/clients/architect-client.js +56 -12
  12. package/clients/architect-client.ts +81 -16
  13. package/clients/ast-grep-client.js +2 -2
  14. package/clients/ast-grep-client.ts +14 -39
  15. package/clients/ast-grep-parser.ts +1 -1
  16. package/clients/ast-grep-rule-manager.js +8 -0
  17. package/clients/ast-grep-rule-manager.ts +10 -1
  18. package/clients/ast-grep-types.js +9 -0
  19. package/clients/ast-grep-types.ts +106 -0
  20. package/clients/auto-loop.js +10 -0
  21. package/clients/auto-loop.ts +14 -1
  22. package/clients/biome-client.js +81 -19
  23. package/clients/biome-client.ts +103 -22
  24. package/clients/bus/bus.js +191 -0
  25. package/clients/bus/bus.ts +251 -0
  26. package/clients/bus/events.js +214 -0
  27. package/clients/bus/events.ts +279 -0
  28. package/clients/bus/index.js +8 -0
  29. package/clients/bus/index.ts +9 -0
  30. package/clients/bus/integration.js +158 -0
  31. package/clients/bus/integration.ts +214 -0
  32. package/clients/complexity-client.js +13 -7
  33. package/clients/complexity-client.ts +13 -7
  34. package/clients/config-validator.js +465 -0
  35. package/clients/config-validator.ts +558 -0
  36. package/clients/dependency-checker.js +4 -10
  37. package/clients/dependency-checker.ts +4 -10
  38. package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
  39. package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
  40. package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
  41. package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
  42. package/clients/dispatch/bus-dispatcher.js +177 -0
  43. package/clients/dispatch/bus-dispatcher.ts +251 -0
  44. package/clients/dispatch/dispatcher.edge.test.js +82 -0
  45. package/clients/dispatch/dispatcher.edge.test.ts +100 -0
  46. package/clients/dispatch/dispatcher.format.test.js +46 -0
  47. package/clients/dispatch/dispatcher.format.test.ts +58 -0
  48. package/clients/dispatch/dispatcher.inline.test.js +74 -0
  49. package/clients/dispatch/dispatcher.inline.test.ts +93 -0
  50. package/clients/dispatch/dispatcher.js +19 -53
  51. package/clients/dispatch/dispatcher.ts +20 -67
  52. package/clients/dispatch/plan.js +9 -4
  53. package/clients/dispatch/plan.ts +9 -4
  54. package/clients/dispatch/runners/architect.js +21 -7
  55. package/clients/dispatch/runners/architect.test.js +138 -0
  56. package/clients/dispatch/runners/architect.test.ts +162 -0
  57. package/clients/dispatch/runners/architect.ts +22 -7
  58. package/clients/dispatch/runners/ast-grep-napi.js +462 -0
  59. package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
  60. package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
  61. package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
  62. package/clients/dispatch/runners/ast-grep.js +62 -19
  63. package/clients/dispatch/runners/ast-grep.ts +70 -18
  64. package/clients/dispatch/runners/biome.js +29 -53
  65. package/clients/dispatch/runners/biome.ts +29 -63
  66. package/clients/dispatch/runners/config-validation.js +67 -0
  67. package/clients/dispatch/runners/config-validation.ts +82 -0
  68. package/clients/dispatch/runners/go-vet.js +4 -28
  69. package/clients/dispatch/runners/go-vet.ts +4 -32
  70. package/clients/dispatch/runners/index.js +30 -10
  71. package/clients/dispatch/runners/index.ts +30 -10
  72. package/clients/dispatch/runners/oxlint.js +141 -0
  73. package/clients/dispatch/runners/oxlint.test.js +230 -0
  74. package/clients/dispatch/runners/oxlint.test.ts +303 -0
  75. package/clients/dispatch/runners/oxlint.ts +175 -0
  76. package/clients/dispatch/runners/pyright.js +40 -70
  77. package/clients/dispatch/runners/pyright.test.js +16 -2
  78. package/clients/dispatch/runners/pyright.test.ts +14 -2
  79. package/clients/dispatch/runners/pyright.ts +48 -91
  80. package/clients/dispatch/runners/python-slop.js +97 -0
  81. package/clients/dispatch/runners/python-slop.test.js +203 -0
  82. package/clients/dispatch/runners/python-slop.test.ts +298 -0
  83. package/clients/dispatch/runners/python-slop.ts +124 -0
  84. package/clients/dispatch/runners/ruff.js +18 -71
  85. package/clients/dispatch/runners/ruff.ts +19 -79
  86. package/clients/dispatch/runners/rust-clippy.js +28 -32
  87. package/clients/dispatch/runners/rust-clippy.ts +29 -31
  88. package/clients/dispatch/runners/scan_codebase.test.js +89 -0
  89. package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
  90. package/clients/dispatch/runners/shellcheck.js +147 -0
  91. package/clients/dispatch/runners/shellcheck.test.js +98 -0
  92. package/clients/dispatch/runners/shellcheck.test.ts +129 -0
  93. package/clients/dispatch/runners/shellcheck.ts +188 -0
  94. package/clients/dispatch/runners/similarity.js +230 -0
  95. package/clients/dispatch/runners/similarity.ts +339 -0
  96. package/clients/dispatch/runners/spellcheck.js +106 -0
  97. package/clients/dispatch/runners/spellcheck.test.js +158 -0
  98. package/clients/dispatch/runners/spellcheck.test.ts +214 -0
  99. package/clients/dispatch/runners/spellcheck.ts +136 -0
  100. package/clients/dispatch/runners/tree-sitter.js +107 -0
  101. package/clients/dispatch/runners/tree-sitter.ts +135 -0
  102. package/clients/dispatch/runners/ts-lsp.js +104 -33
  103. package/clients/dispatch/runners/ts-lsp.ts +120 -38
  104. package/clients/dispatch/runners/ts-slop.js +113 -0
  105. package/clients/dispatch/runners/ts-slop.test.js +180 -0
  106. package/clients/dispatch/runners/ts-slop.test.ts +230 -0
  107. package/clients/dispatch/runners/ts-slop.ts +142 -0
  108. package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
  109. package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
  110. package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
  111. package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
  112. package/clients/dispatch/runners/utils.js +2 -4
  113. package/clients/dispatch/runners/utils.ts +2 -4
  114. package/clients/dispatch/types.ts +1 -1
  115. package/clients/dispatch/utils/format-utils.js +49 -0
  116. package/clients/dispatch/utils/format-utils.ts +60 -0
  117. package/clients/dogfood.test.js +201 -0
  118. package/clients/dogfood.test.ts +269 -0
  119. package/clients/file-time.js +152 -0
  120. package/clients/file-time.ts +208 -0
  121. package/clients/file-utils.js +40 -0
  122. package/clients/file-utils.ts +44 -0
  123. package/clients/fix-scanners.js +10 -20
  124. package/clients/fix-scanners.ts +10 -22
  125. package/clients/format-service.js +172 -0
  126. package/clients/format-service.ts +254 -0
  127. package/clients/formatters.js +435 -0
  128. package/clients/formatters.ts +508 -0
  129. package/clients/go-client.js +5 -14
  130. package/clients/go-client.ts +5 -13
  131. package/clients/installer/index.js +356 -0
  132. package/clients/installer/index.ts +426 -0
  133. package/clients/jscpd-client.js +11 -9
  134. package/clients/jscpd-client.ts +12 -8
  135. package/clients/knip-client.js +3 -7
  136. package/clients/knip-client.ts +3 -6
  137. package/clients/lsp/__tests__/client.test.js +325 -0
  138. package/clients/lsp/__tests__/client.test.ts +434 -0
  139. package/clients/lsp/__tests__/config.test.js +166 -0
  140. package/clients/lsp/__tests__/config.test.ts +209 -0
  141. package/clients/lsp/__tests__/error-recovery.test.js +213 -0
  142. package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
  143. package/clients/lsp/__tests__/integration.test.js +127 -0
  144. package/clients/lsp/__tests__/integration.test.ts +160 -0
  145. package/clients/lsp/__tests__/launch.test.js +260 -0
  146. package/clients/lsp/__tests__/launch.test.ts +329 -0
  147. package/clients/lsp/__tests__/server.test.js +259 -0
  148. package/clients/lsp/__tests__/server.test.ts +332 -0
  149. package/clients/lsp/__tests__/service.test.js +417 -0
  150. package/clients/lsp/__tests__/service.test.ts +499 -0
  151. package/clients/lsp/client.js +235 -0
  152. package/clients/lsp/client.ts +328 -0
  153. package/clients/lsp/config.js +115 -0
  154. package/clients/lsp/config.ts +149 -0
  155. package/clients/lsp/index.js +222 -0
  156. package/clients/lsp/index.ts +280 -0
  157. package/clients/lsp/installer/index.js +391 -0
  158. package/clients/lsp/interactive-install.js +210 -0
  159. package/clients/lsp/interactive-install.ts +251 -0
  160. package/clients/lsp/language.js +170 -0
  161. package/clients/lsp/language.ts +216 -0
  162. package/clients/lsp/launch.js +174 -0
  163. package/clients/lsp/launch.ts +240 -0
  164. package/clients/lsp/lsp/launch.js +116 -0
  165. package/clients/lsp/lsp/server.js +532 -0
  166. package/clients/lsp/lsp-index.js +10 -0
  167. package/clients/lsp/lsp-index.ts +11 -0
  168. package/clients/lsp/path-utils.js +48 -0
  169. package/clients/lsp/path-utils.ts +52 -0
  170. package/clients/lsp/server.js +615 -0
  171. package/clients/lsp/server.ts +800 -0
  172. package/clients/lsp/test-py-spawn/requirements.txt +1 -0
  173. package/clients/lsp/test-py-spawn/test.py +3 -0
  174. package/clients/lsp/test-py-svc/requirements.txt +1 -0
  175. package/clients/lsp/test-py-svc/test.py +3 -0
  176. package/clients/lsp/test-python-project/requirements.txt +1 -0
  177. package/clients/lsp/test-python-project/test.py +5 -0
  178. package/clients/metrics-history.js +2 -2
  179. package/clients/metrics-history.ts +2 -2
  180. package/clients/production-readiness.js +522 -0
  181. package/clients/production-readiness.ts +556 -0
  182. package/clients/project-index.js +255 -0
  183. package/clients/project-index.ts +383 -0
  184. package/clients/project-metadata.js +531 -0
  185. package/clients/project-metadata.ts +624 -0
  186. package/clients/ruff-client.js +56 -16
  187. package/clients/ruff-client.ts +72 -15
  188. package/clients/runner-tracker.js +152 -0
  189. package/clients/runner-tracker.ts +213 -0
  190. package/clients/rust-client.js +4 -11
  191. package/clients/rust-client.ts +5 -11
  192. package/clients/safe-spawn.js +96 -0
  193. package/clients/safe-spawn.ts +128 -0
  194. package/clients/scan-architectural-debt.js +3 -6
  195. package/clients/scan-architectural-debt.ts +3 -6
  196. package/clients/scan-utils.js +5 -20
  197. package/clients/scan-utils.ts +5 -29
  198. package/clients/secrets-scanner.js +3 -17
  199. package/clients/secrets-scanner.ts +4 -20
  200. package/clients/services/__tests__/effect-integration.test.js +86 -0
  201. package/clients/services/__tests__/effect-integration.test.ts +111 -0
  202. package/clients/services/effect-integration.js +194 -0
  203. package/clients/services/effect-integration.ts +268 -0
  204. package/clients/services/index.js +7 -0
  205. package/clients/services/index.ts +8 -0
  206. package/clients/services/runner-service.js +105 -0
  207. package/clients/services/runner-service.ts +179 -0
  208. package/clients/sg-runner.js +87 -13
  209. package/clients/sg-runner.ts +97 -13
  210. package/clients/state-matrix.js +160 -0
  211. package/clients/state-matrix.ts +202 -0
  212. package/clients/subprocess-client.js +10 -9
  213. package/clients/subprocess-client.ts +10 -8
  214. package/clients/test-runner-client.js +3 -7
  215. package/clients/test-runner-client.ts +3 -6
  216. package/clients/tool-availability.js +4 -10
  217. package/clients/tool-availability.ts +4 -9
  218. package/clients/tree-sitter-client.js +564 -0
  219. package/clients/tree-sitter-client.ts +797 -0
  220. package/clients/tree-sitter-query-loader.js +355 -0
  221. package/clients/tree-sitter-query-loader.ts +425 -0
  222. package/clients/type-coverage-client.js +3 -7
  223. package/clients/type-coverage-client.ts +3 -6
  224. package/clients/typescript-client.codefix.test.js +157 -0
  225. package/clients/typescript-client.codefix.test.ts +186 -0
  226. package/clients/typescript-client.js +43 -0
  227. package/clients/typescript-client.ts +98 -0
  228. package/commands/booboo.js +799 -219
  229. package/commands/booboo.ts +1004 -225
  230. package/commands/clients/ast-grep-client.js +250 -0
  231. package/commands/clients/ast-grep-parser.js +86 -0
  232. package/commands/clients/ast-grep-rule-manager.js +91 -0
  233. package/commands/clients/ast-grep-types.js +9 -0
  234. package/commands/clients/biome-client.js +380 -0
  235. package/commands/clients/complexity-client.js +667 -0
  236. package/commands/clients/file-kinds.js +177 -0
  237. package/commands/clients/file-utils.js +40 -0
  238. package/commands/clients/jscpd-client.js +169 -0
  239. package/commands/clients/knip-client.js +211 -0
  240. package/commands/clients/ruff-client.js +297 -0
  241. package/commands/clients/safe-spawn.js +88 -0
  242. package/commands/clients/scan-utils.js +83 -0
  243. package/commands/clients/sg-runner.js +190 -0
  244. package/commands/clients/types.js +11 -0
  245. package/commands/clients/typescript-client.js +505 -0
  246. package/commands/fix-from-booboo.js +398 -0
  247. package/commands/fix-from-booboo.ts +485 -0
  248. package/commands/fix-simplified.js +618 -0
  249. package/commands/fix-simplified.ts +768 -0
  250. package/commands/rate.js +10 -14
  251. package/commands/rate.ts +9 -16
  252. package/default-architect.yaml +59 -15
  253. package/index.ts +342 -429
  254. package/package.json +16 -3
  255. package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
  256. package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
  257. package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
  258. package/rules/python-slop-rules/.sgconfig.yml +4 -0
  259. package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
  260. package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
  261. package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
  262. package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
  263. package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
  264. package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
  265. package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
  266. package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
  267. package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
  268. package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
  269. package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
  270. package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
  271. package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
  272. package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
  273. package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
  274. package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
  275. package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
  276. package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
  277. package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
  278. package/rules/ts-slop-rules/.sgconfig.yml +4 -0
  279. package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
  280. package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
  281. package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
  282. package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
  283. package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
  284. package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
  285. package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
  286. package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
  287. package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
  288. package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
  289. package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
  290. package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
  291. package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
  292. package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
  293. package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
  294. package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
  295. package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
  296. package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
  297. package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
  298. package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
  299. package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
  300. package/skills/ast-grep/SKILL.md +182 -0
  301. package/clients/dispatch/runners/secrets.js +0 -109
  302. package/commands/fix.js +0 -244
  303. package/commands/fix.ts +0 -373
  304. package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
@@ -3,22 +3,42 @@
3
3
  */
4
4
  import { registerRunner } from "../dispatcher.js";
5
5
  import architectRunner from "./architect.js";
6
- // Import all runners
7
6
  import astGrepRunner from "./ast-grep.js";
7
+ // Import all runners
8
+ import astGrepNapiRunner from "./ast-grep-napi.js";
8
9
  import biomeRunner from "./biome.js";
10
+ import configValidationRunner from "./config-validation.js";
9
11
  import goVetRunner from "./go-vet.js";
12
+ import oxlintRunner from "./oxlint.js";
10
13
  import pyrightRunner from "./pyright.js";
14
+ import pythonSlopRunner from "./python-slop.js";
11
15
  import ruffRunner from "./ruff.js";
12
16
  import rustClippyRunner from "./rust-clippy.js";
17
+ import shellcheckRunner from "./shellcheck.js";
18
+ // Import similarity runner
19
+ import similarityRunner from "./similarity.js";
20
+ import spellcheckRunner from "./spellcheck.js";
21
+ // Import tree-sitter runner
22
+ import treeSitterRunner from "./tree-sitter.js";
13
23
  import tsLspRunner from "./ts-lsp.js";
24
+ import tsSlopRunner from "./ts-slop.js";
14
25
  import typeSafetyRunner from "./type-safety.js";
15
26
  // Register all runners (ordered by priority)
16
- registerRunner(tsLspRunner); // TypeScript type-checking
17
- registerRunner(pyrightRunner); // Python type-checking
18
- registerRunner(biomeRunner);
19
- registerRunner(ruffRunner);
20
- registerRunner(typeSafetyRunner);
21
- registerRunner(astGrepRunner);
22
- registerRunner(architectRunner);
23
- registerRunner(goVetRunner);
24
- registerRunner(rustClippyRunner);
27
+ registerRunner(tsLspRunner); // TypeScript type-checking (priority 5)
28
+ registerRunner(pyrightRunner); // Python type-checking (priority 5)
29
+ registerRunner(configValidationRunner); // Config/env validation (priority 8)
30
+ registerRunner(astGrepNapiRunner); // TS/JS structural analysis via NAPI (priority 15)
31
+ registerRunner(biomeRunner); // Biome formatting/linting (priority 10)
32
+ registerRunner(oxlintRunner); // Oxlint fast JS/TS linter (priority 12)
33
+ registerRunner(treeSitterRunner); // Tree-sitter structural analysis (priority 14)
34
+ registerRunner(ruffRunner); // Python linting (priority 10)
35
+ registerRunner(tsSlopRunner); // DISABLED - TypeScript slop via CLI (disabled, use NAPI)
36
+ registerRunner(pythonSlopRunner); // Python slop via CLI (priority 25)
37
+ registerRunner(typeSafetyRunner); // Type safety checks (priority 20)
38
+ registerRunner(shellcheckRunner); // Shell script linting (priority 20)
39
+ registerRunner(astGrepRunner); // Other languages via CLI (priority 30)
40
+ registerRunner(similarityRunner); // Semantic reuse detection (priority 35)
41
+ registerRunner(architectRunner); // Architectural rules (priority 40)
42
+ registerRunner(spellcheckRunner); // Spellcheck for markdown/docs (priority 30)
43
+ registerRunner(goVetRunner); // Go analysis (priority 50)
44
+ registerRunner(rustClippyRunner); // Rust analysis (priority 50)
@@ -4,23 +4,43 @@
4
4
 
5
5
  import { registerRunner } from "../dispatcher.js";
6
6
  import architectRunner from "./architect.js";
7
- // Import all runners
8
7
  import astGrepRunner from "./ast-grep.js";
8
+ // Import all runners
9
+ import astGrepNapiRunner from "./ast-grep-napi.js";
9
10
  import biomeRunner from "./biome.js";
11
+ import configValidationRunner from "./config-validation.js";
10
12
  import goVetRunner from "./go-vet.js";
13
+ import oxlintRunner from "./oxlint.js";
11
14
  import pyrightRunner from "./pyright.js";
15
+ import pythonSlopRunner from "./python-slop.js";
12
16
  import ruffRunner from "./ruff.js";
13
17
  import rustClippyRunner from "./rust-clippy.js";
18
+ import shellcheckRunner from "./shellcheck.js";
19
+ // Import similarity runner
20
+ import similarityRunner from "./similarity.js";
21
+ import spellcheckRunner from "./spellcheck.js";
22
+ // Import tree-sitter runner
23
+ import treeSitterRunner from "./tree-sitter.js";
14
24
  import tsLspRunner from "./ts-lsp.js";
25
+ import tsSlopRunner from "./ts-slop.js";
15
26
  import typeSafetyRunner from "./type-safety.js";
16
27
 
17
28
  // Register all runners (ordered by priority)
18
- registerRunner(tsLspRunner); // TypeScript type-checking
19
- registerRunner(pyrightRunner); // Python type-checking
20
- registerRunner(biomeRunner);
21
- registerRunner(ruffRunner);
22
- registerRunner(typeSafetyRunner);
23
- registerRunner(astGrepRunner);
24
- registerRunner(architectRunner);
25
- registerRunner(goVetRunner);
26
- registerRunner(rustClippyRunner);
29
+ registerRunner(tsLspRunner); // TypeScript type-checking (priority 5)
30
+ registerRunner(pyrightRunner); // Python type-checking (priority 5)
31
+ registerRunner(configValidationRunner); // Config/env validation (priority 8)
32
+ registerRunner(astGrepNapiRunner); // TS/JS structural analysis via NAPI (priority 15)
33
+ registerRunner(biomeRunner); // Biome formatting/linting (priority 10)
34
+ registerRunner(oxlintRunner); // Oxlint fast JS/TS linter (priority 12)
35
+ registerRunner(treeSitterRunner); // Tree-sitter structural analysis (priority 14)
36
+ registerRunner(ruffRunner); // Python linting (priority 10)
37
+ registerRunner(tsSlopRunner); // DISABLED - TypeScript slop via CLI (disabled, use NAPI)
38
+ registerRunner(pythonSlopRunner); // Python slop via CLI (priority 25)
39
+ registerRunner(typeSafetyRunner); // Type safety checks (priority 20)
40
+ registerRunner(shellcheckRunner); // Shell script linting (priority 20)
41
+ registerRunner(astGrepRunner); // Other languages via CLI (priority 30)
42
+ registerRunner(similarityRunner); // Semantic reuse detection (priority 35)
43
+ registerRunner(architectRunner); // Architectural rules (priority 40)
44
+ registerRunner(spellcheckRunner); // Spellcheck for markdown/docs (priority 30)
45
+ registerRunner(goVetRunner); // Go analysis (priority 50)
46
+ registerRunner(rustClippyRunner); // Rust analysis (priority 50)
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Oxlint runner for dispatch system
3
+ *
4
+ * Fast Rust-based JavaScript/TypeScript linter from the Oxc project.
5
+ * Zero-config by default, compatible with ESLint rules.
6
+ *
7
+ * Why oxlint?
8
+ * - ~100x faster than ESLint (Rust-based)
9
+ * - Zero-config (works out of the box)
10
+ * - Growing rule set (eslint, typescript, react, unicorn, etc.)
11
+ * - JSON output for programmatic use
12
+ *
13
+ * Comparison:
14
+ * - vs Biome: Similar performance, different rule philosophy
15
+ * - vs ESLint: Much faster, fewer rules but catching up
16
+ *
17
+ * Install: npm install -D oxlint
18
+ * Or: cargo install oxlint
19
+ *
20
+ * Config: .oxlintrc.json (optional, zero-config works)
21
+ */
22
+ import { safeSpawn } from "../../safe-spawn.js";
23
+ import { createAvailabilityChecker, createConfigFinder } from "./utils/runner-helpers.js";
24
+ const oxlint = createAvailabilityChecker("oxlint", ".exe");
25
+ const findOxlintConfig = createConfigFinder(".oxlintrc.json");
26
+ /**
27
+ * Parse oxlint JSON output
28
+ *
29
+ * Format: Array of diagnostic objects
30
+ * [{
31
+ * "ruleId": "no-unused-vars",
32
+ * "severity": 2,
33
+ * "message": "'foo' is assigned a value but never used.",
34
+ * "line": 10,
35
+ * "column": 7,
36
+ * "nodeType": "Identifier",
37
+ * "messageId": "unusedVar",
38
+ * "endLine": 10,
39
+ * "endColumn": 10,
40
+ * "fix": { "range": [95, 108], "text": "" }
41
+ * }]
42
+ */
43
+ function parseOxlintOutput(raw, filePath) {
44
+ const diagnostics = [];
45
+ if (!raw.trim()) {
46
+ return diagnostics;
47
+ }
48
+ try {
49
+ const parsed = JSON.parse(raw);
50
+ if (!Array.isArray(parsed)) {
51
+ return diagnostics;
52
+ }
53
+ for (const item of parsed) {
54
+ if (!item.message || !item.line)
55
+ continue;
56
+ const severity = item.severity === 2 ? "error" : "warning";
57
+ diagnostics.push({
58
+ id: `oxlint-${item.line}-${item.ruleId || "unknown"}`,
59
+ message: item.message,
60
+ filePath,
61
+ line: item.line,
62
+ column: item.column || 1,
63
+ severity,
64
+ semantic: severity === "error" ? "blocking" : "warning",
65
+ tool: "oxlint",
66
+ rule: item.ruleId,
67
+ fixable: !!item.fix,
68
+ fixSuggestion: item.fix?.text,
69
+ });
70
+ }
71
+ }
72
+ catch {
73
+ // If JSON parsing fails, try line-based parsing for CLI output
74
+ const lines = raw.split("\n").filter((l) => l.trim());
75
+ for (const line of lines) {
76
+ // Try to match: file.ts:10:7: Error message [ruleId]
77
+ const match = line.match(/^(\d+):(\d+)\s+(.+?)\s*\[(\w+)\]$/);
78
+ if (match) {
79
+ diagnostics.push({
80
+ id: `oxlint-${match[1]}-${match[4]}`,
81
+ message: `${match[4]}: ${match[3]}`,
82
+ filePath,
83
+ line: parseInt(match[1], 10),
84
+ column: parseInt(match[2], 10),
85
+ severity: "warning",
86
+ semantic: "warning",
87
+ tool: "oxlint",
88
+ rule: match[4],
89
+ });
90
+ }
91
+ }
92
+ }
93
+ return diagnostics;
94
+ }
95
+ const oxlintRunner = {
96
+ id: "oxlint",
97
+ appliesTo: ["jsts"],
98
+ priority: 12, // Between biome (10) and slop (25)
99
+ enabledByDefault: false, // Opt-in initially - let users choose between biome/oxlint
100
+ skipTestFiles: true, // Test files often use patterns that trigger false positives
101
+ async run(ctx) {
102
+ // Skip if oxlint is not installed
103
+ if (!oxlint.isAvailable(ctx.cwd || process.cwd())) {
104
+ return { status: "skipped", diagnostics: [], semantic: "none" };
105
+ }
106
+ // Check if user explicitly disabled oxlint (keep biome as primary)
107
+ if (ctx.pi.getFlag("no-oxlint")) {
108
+ return { status: "skipped", diagnostics: [], semantic: "none" };
109
+ }
110
+ // Build args
111
+ // --format json: JSON output
112
+ // --config: Only if config file exists (zero-config otherwise)
113
+ const args = ["--format", "json"];
114
+ // Check for config file
115
+ const configPath = findOxlintConfig(ctx.cwd);
116
+ if (configPath) {
117
+ args.push("--config", configPath);
118
+ }
119
+ // Add file path
120
+ args.push(ctx.filePath);
121
+ const result = safeSpawn(oxlint.getCommand(), args, {
122
+ timeout: 10000, // Fast - should complete quickly
123
+ });
124
+ // oxlint exits with code 1 if issues found, 0 if clean
125
+ if (result.status === 0 && !result.stdout?.trim()) {
126
+ return { status: "succeeded", diagnostics: [], semantic: "none" };
127
+ }
128
+ // Parse diagnostics
129
+ const raw = result.stdout + result.stderr;
130
+ const diagnostics = parseOxlintOutput(raw, ctx.filePath);
131
+ if (diagnostics.length === 0) {
132
+ return { status: "succeeded", diagnostics: [], semantic: "none" };
133
+ }
134
+ return {
135
+ status: "failed",
136
+ diagnostics,
137
+ semantic: "warning",
138
+ };
139
+ },
140
+ };
141
+ export default oxlintRunner;
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Tests for oxlint runner
3
+ */
4
+ import * as fs from "node:fs";
5
+ import { createRequire } from "node:module";
6
+ import * as path from "node:path";
7
+ import { describe, expect, it } from "vitest";
8
+ /**
9
+ * Delay helper for Windows file cleanup
10
+ * Windows may hold file handles briefly after process exit
11
+ */
12
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
13
+ function createMockContext(filePath, overrides = {}) {
14
+ return {
15
+ filePath,
16
+ cwd: process.cwd(),
17
+ kind: "jsts",
18
+ autofix: false,
19
+ deltaMode: false,
20
+ baselines: { get: () => [], add: () => { }, save: () => { } },
21
+ pi: { getFlag: () => false, ...overrides.pi },
22
+ hasTool: async () => false,
23
+ log: () => { },
24
+ ...overrides,
25
+ };
26
+ }
27
+ describe("oxlint runner", () => {
28
+ const require = createRequire(import.meta.url);
29
+ it("should have correct runner definition", async () => {
30
+ const oxlintModule = await import("./oxlint.js");
31
+ const runner = oxlintModule.default;
32
+ expect(runner.id).toBe("oxlint");
33
+ expect(runner.appliesTo).toEqual(["jsts"]);
34
+ expect(runner.priority).toBe(12);
35
+ expect(runner.enabledByDefault).toBe(false); // Opt-in initially
36
+ expect(runner.skipTestFiles).toBe(true);
37
+ });
38
+ it("should detect oxlint availability", () => {
39
+ const { spawnSync } = require("node:child_process");
40
+ const result = spawnSync("oxlint", ["--version"], {
41
+ encoding: "utf-8",
42
+ timeout: 10000,
43
+ shell: true,
44
+ });
45
+ expect(result.error || result.status !== 0 ? "not available" : "available").toBeTruthy(); // May or may not be installed
46
+ });
47
+ it("should detect common lint issues", async () => {
48
+ const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_test_${Date.now()}.ts`);
49
+ fs.writeFileSync(tmpFile, `// Test file with issues
50
+ function test() {
51
+ // Double negation
52
+ const flag = !!value;
53
+
54
+ // Unused variable
55
+ const unused = 42;
56
+
57
+ // Console statement
58
+ console.log("test");
59
+ }
60
+ `);
61
+ try {
62
+ const oxlintModule = await import("./oxlint.js");
63
+ const runner = oxlintModule.default;
64
+ const result = await runner.run(createMockContext(tmpFile));
65
+ // If oxlint is installed, should detect issues
66
+ // If not installed, will be skipped
67
+ if (result.status !== "skipped") {
68
+ // Should detect at least some issues (console, unused vars, etc.)
69
+ expect(result.diagnostics.length).toBeGreaterThanOrEqual(1);
70
+ expect(result.diagnostics.some((d) => d.tool === "oxlint" &&
71
+ (d.message.includes("console") ||
72
+ d.message.includes("unused") ||
73
+ d.message.includes("!!")))).toBe(true);
74
+ }
75
+ }
76
+ finally {
77
+ // Windows may hold file handles briefly - add small delay
78
+ await delay(100);
79
+ if (fs.existsSync(tmpFile)) {
80
+ fs.unlinkSync(tmpFile);
81
+ }
82
+ }
83
+ });
84
+ it("should respect no-oxlint flag", async () => {
85
+ const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_flag_${Date.now()}.ts`);
86
+ fs.writeFileSync(tmpFile, `function test() { console.log("test"); }`);
87
+ try {
88
+ const oxlintModule = await import("./oxlint.js");
89
+ const runner = oxlintModule.default;
90
+ // Create context with no-oxlint flag set to true
91
+ const ctxWithFlag = createMockContext(tmpFile, {
92
+ pi: { getFlag: (name) => name === "no-oxlint" },
93
+ });
94
+ const result = await runner.run(ctxWithFlag);
95
+ expect(result.status).toBe("skipped");
96
+ }
97
+ finally {
98
+ // Windows may hold file handles briefly - add small delay
99
+ await delay(100);
100
+ if (fs.existsSync(tmpFile)) {
101
+ fs.unlinkSync(tmpFile);
102
+ }
103
+ }
104
+ });
105
+ it("should provide fix suggestions when available", async () => {
106
+ const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_fix_${Date.now()}.ts`);
107
+ fs.writeFileSync(tmpFile, `// File with auto-fixable issues
108
+ const x = !!value;
109
+ `);
110
+ try {
111
+ const oxlintModule = await import("./oxlint.js");
112
+ const runner = oxlintModule.default;
113
+ const result = await runner.run(createMockContext(tmpFile));
114
+ if (result.status !== "skipped" && result.diagnostics.length > 0) {
115
+ // Some issues should be fixable
116
+ const fixableDiags = result.diagnostics.filter((d) => d.fixable);
117
+ // At least some diagnostics should have fixes
118
+ expect(fixableDiags.length).toBeGreaterThanOrEqual(0);
119
+ }
120
+ }
121
+ finally {
122
+ // Windows may hold file handles briefly - add small delay
123
+ await delay(100);
124
+ if (fs.existsSync(tmpFile)) {
125
+ fs.unlinkSync(tmpFile);
126
+ }
127
+ }
128
+ });
129
+ it("should pass clean TypeScript files", async () => {
130
+ const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_ok_${Date.now()}.ts`);
131
+ fs.writeFileSync(tmpFile, `// Clean TypeScript file
132
+ function greet(name: string): string {
133
+ return \`Hello, \${name}!\`;
134
+ }
135
+
136
+ const result = greet("world");
137
+ export { greet };
138
+ `);
139
+ try {
140
+ const oxlintModule = await import("./oxlint.js");
141
+ const runner = oxlintModule.default;
142
+ const result = await runner.run(createMockContext(tmpFile));
143
+ if (result.status !== "skipped") {
144
+ // Clean files should have no issues
145
+ expect(result.diagnostics.length).toBe(0);
146
+ expect(result.status).toBe("succeeded");
147
+ }
148
+ }
149
+ finally {
150
+ // Windows may hold file handles briefly - add small delay
151
+ await delay(100);
152
+ if (fs.existsSync(tmpFile)) {
153
+ fs.unlinkSync(tmpFile);
154
+ }
155
+ }
156
+ });
157
+ it("should handle JSON output correctly", async () => {
158
+ const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_json_${Date.now()}.ts`);
159
+ fs.writeFileSync(tmpFile, `const unused = 1;`);
160
+ try {
161
+ const oxlintModule = await import("./oxlint.js");
162
+ const runner = oxlintModule.default;
163
+ const result = await runner.run(createMockContext(tmpFile));
164
+ if (result.status !== "skipped") {
165
+ // All diagnostics should have required fields
166
+ for (const diag of result.diagnostics) {
167
+ expect(diag.id).toBeDefined();
168
+ expect(diag.message).toBeDefined();
169
+ expect(diag.tool).toBe("oxlint");
170
+ expect(diag.line).toBeGreaterThanOrEqual(1);
171
+ expect(diag.severity).toMatch(/^(error|warning|info)$/);
172
+ }
173
+ }
174
+ }
175
+ finally {
176
+ // Windows may hold file handles briefly - add small delay
177
+ await delay(100);
178
+ if (fs.existsSync(tmpFile)) {
179
+ fs.unlinkSync(tmpFile);
180
+ }
181
+ }
182
+ });
183
+ it("should skip when oxlint is not available", async () => {
184
+ const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_skip_${Date.now()}.ts`);
185
+ fs.writeFileSync(tmpFile, `const x = 1;`);
186
+ try {
187
+ const oxlintModule = await import("./oxlint.js");
188
+ const runner = oxlintModule.default;
189
+ // Check if oxlint is available
190
+ const { spawnSync } = require("node:child_process");
191
+ const checkResult = spawnSync("oxlint", ["--version"], {
192
+ encoding: "utf-8",
193
+ timeout: 5000,
194
+ shell: true,
195
+ });
196
+ const isAvailable = !checkResult.error && checkResult.status === 0;
197
+ const result = await runner.run(createMockContext(tmpFile));
198
+ if (!isAvailable) {
199
+ expect(result.status).toBe("skipped");
200
+ expect(result.diagnostics).toHaveLength(0);
201
+ }
202
+ }
203
+ finally {
204
+ // Windows may hold file handles briefly - add small delay
205
+ await delay(100);
206
+ if (fs.existsSync(tmpFile)) {
207
+ fs.unlinkSync(tmpFile);
208
+ }
209
+ }
210
+ });
211
+ it("should handle parsing errors gracefully", async () => {
212
+ const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_parse_${Date.now()}.ts`);
213
+ // Intentionally malformed file
214
+ fs.writeFileSync(tmpFile, `const x = `);
215
+ try {
216
+ const oxlintModule = await import("./oxlint.js");
217
+ const runner = oxlintModule.default;
218
+ const result = await runner.run(createMockContext(tmpFile));
219
+ // Should handle parse errors without crashing
220
+ expect(["succeeded", "failed", "skipped"]).toContain(result.status);
221
+ }
222
+ finally {
223
+ // Windows may hold file handles briefly - add small delay
224
+ await delay(100);
225
+ if (fs.existsSync(tmpFile)) {
226
+ fs.unlinkSync(tmpFile);
227
+ }
228
+ }
229
+ });
230
+ });