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
@@ -1,109 +0,0 @@
1
- /**
2
- * Secrets scanner runner
3
- *
4
- * Scans file content for potential secret patterns before write.
5
- * Works on all file types via regex matching.
6
- *
7
- * Patterns detected:
8
- * - Stripe/OpenAI keys (sk-*)
9
- * - GitHub tokens (ghp_*, gho_*, github_pat_*)
10
- * - AWS keys (AKIA*)
11
- * - Slack tokens (xoxp-*, xoxb-*)
12
- * - Private keys (BEGIN PRIVATE KEY)
13
- * - Generic API key patterns
14
- */
15
- const SECRET_PATTERNS = [
16
- {
17
- pattern: /sk-[a-zA-Z0-9]{20,}/g,
18
- name: "stripe-openai-key",
19
- message: "Possible Stripe or OpenAI API key (sk-*)",
20
- },
21
- {
22
- pattern: /ghp_[a-zA-Z0-9]{36}/g,
23
- name: "github-personal-token",
24
- message: "GitHub personal access token (ghp_*)",
25
- },
26
- {
27
- pattern: /gho_[a-zA-Z0-9]{36}/g,
28
- name: "github-oauth-token",
29
- message: "GitHub OAuth token (gho_*)",
30
- },
31
- {
32
- pattern: /github_pat_[a-zA-Z_]{82}/g,
33
- name: "github-fine-grained-pat",
34
- message: "GitHub fine-grained PAT (github_pat_*)",
35
- },
36
- {
37
- pattern: /AKIA[0-9A-Z]{16}/g,
38
- name: "aws-access-key",
39
- message: "AWS access key ID (AKIA*)",
40
- },
41
- {
42
- pattern: /xox[bp]-[a-zA-Z0-9]{10,}/g,
43
- name: "slack-token",
44
- message: "Slack token (xoxb-*/xoxp-*)",
45
- },
46
- {
47
- pattern: /-----BEGIN\s+(RSA\s+)?PRIVATE KEY-----/g,
48
- name: "private-key",
49
- message: "Private key material detected",
50
- },
51
- {
52
- pattern: /password\s*[:=]\s*["'][^"']{8,}["']/gi,
53
- name: "hardcoded-password",
54
- message: "Possible hardcoded password",
55
- },
56
- {
57
- pattern: /(?:secret|api_?key|token)\s*[:=]\s*["'][a-zA-Z0-9]{20,}["']/gi,
58
- name: "generic-api-secret",
59
- message: "Possible hardcoded secret, API key, or token",
60
- },
61
- ];
62
- function scanContent(content) {
63
- const findings = [];
64
- const lines = content.split("\n");
65
- for (let i = 0; i < lines.length; i++) {
66
- const line = lines[i];
67
- for (const secret of SECRET_PATTERNS) {
68
- secret.pattern.lastIndex = 0; // Reset regex state
69
- const match = secret.pattern.exec(line);
70
- if (match) {
71
- findings.push({
72
- line: i + 1,
73
- pattern: secret,
74
- match: match[0].slice(0, 20) + "...",
75
- });
76
- }
77
- }
78
- }
79
- return findings;
80
- }
81
- async function run(ctx) {
82
- const diagnostics = [];
83
- // Get the content being written - check both full content and the diff
84
- const content = ctx.content;
85
- if (!content)
86
- return { diagnostics };
87
- const findings = scanContent(content);
88
- for (const finding of findings) {
89
- diagnostics.push({
90
- id: `secrets-${finding.pattern.name}-${finding.line}`,
91
- message: `${finding.pattern.message} at line ${finding.line}. Remove before committing.`,
92
- filePath: ctx.filePath,
93
- line: finding.line,
94
- severity: "error",
95
- semantic: "blocking", // Always blocking - secrets should never be committed
96
- tool: "secrets",
97
- rule: finding.pattern.name,
98
- fixable: false,
99
- });
100
- }
101
- return { diagnostics };
102
- }
103
- const runner = {
104
- id: "secrets",
105
- appliesTo: ["*"], // All file types
106
- priority: 0, // Run first - block before other checks
107
- run,
108
- };
109
- export default runner;
package/commands/fix.js DELETED
@@ -1,244 +0,0 @@
1
- /**
2
- * Fix command for pi-lens
3
- *
4
- * Automated fix loop that scans for issues and generates fix plans.
5
- * Supports --loop flag for automatic iteration via auto-loop engine.
6
- */
7
- import * as childProcess from "node:child_process";
8
- import * as nodeFs from "node:fs";
9
- import * as path from "node:path";
10
- import { createAutoLoop } from "../clients/auto-loop.js";
11
- import { scanAll, } from "../clients/fix-scanners.js";
12
- // --- Auto-loop singleton ---
13
- let fixLoop = null;
14
- function getFixLoop(pi) {
15
- if (!fixLoop) {
16
- fixLoop = createAutoLoop(pi, {
17
- name: "fix",
18
- maxIterations: 3,
19
- command: "/lens-booboo-fix --loop",
20
- exitPatterns: [
21
- /✅ BOOBOO FIX LOOP COMPLETE/,
22
- /⚠️ BOOBOO FIX LOOP STOPPED/,
23
- /No more fixable issues/,
24
- /Max iterations.*reached/,
25
- ],
26
- });
27
- }
28
- return fixLoop;
29
- }
30
- function loadSession(sessionFile) {
31
- try {
32
- const session = JSON.parse(nodeFs.readFileSync(sessionFile, "utf-8"));
33
- return {
34
- iteration: session.iteration || 0,
35
- counts: session.counts || {},
36
- falsePositives: session.falsePositives || [],
37
- };
38
- }
39
- catch {
40
- return { iteration: 0, counts: {}, falsePositives: [] };
41
- }
42
- }
43
- function saveSession(sessionFile, session) {
44
- nodeFs.mkdirSync(path.dirname(sessionFile), { recursive: true });
45
- nodeFs.writeFileSync(sessionFile, JSON.stringify(session, null, 2), "utf-8");
46
- }
47
- function resetSession(sessionFile) {
48
- try {
49
- nodeFs.unlinkSync(sessionFile);
50
- }
51
- catch {
52
- // Ignore if doesn't exist
53
- }
54
- return { iteration: 0, counts: {}, falsePositives: [] };
55
- }
56
- // --- Issue ID helpers ---
57
- const issueId = (type, file, line) => line !== undefined ? `${type}:${file}:${line}` : `${type}:${file}`;
58
- const isFalsePositive = (id, session) => session.falsePositives.includes(id);
59
- // --- Plan generation ---
60
- function generatePlan(results, session, _isTsProject, prevCounts) {
61
- const MAX_ITERATIONS = 3;
62
- // Filter out false positives
63
- const filteredDups = results.duplicates.filter((c) => !isFalsePositive(issueId("duplicate", c.fileA, c.startA), session));
64
- const filteredDeadCode = results.deadCode.filter((i) => !isFalsePositive(issueId("dead_code", i.file ?? i.name), session));
65
- const filteredBiome = results.biomeIssues.filter((i) => !isFalsePositive(issueId("biome", i.file, i.line), session));
66
- const filteredSlop = results.slopFiles.filter((f) => !isFalsePositive(issueId("slop", f.file), session));
67
- // Filter ast issues (exclude skip rules and false positives)
68
- const agentTasks = results.astIssues.filter((i) => !isFalsePositive(issueId("ast", i.file, i.line), session));
69
- const totalFixable = filteredDups.length +
70
- filteredDeadCode.length +
71
- agentTasks.length +
72
- filteredBiome.length +
73
- filteredSlop.length;
74
- // Check for no progress
75
- const prevTotal = Object.values(prevCounts).reduce((a, b) => a + b, 0);
76
- const noProgress = session.iteration > 1 && prevTotal === totalFixable && totalFixable > 0;
77
- // Completion/stopped messages
78
- if (totalFixable === 0) {
79
- const fpNote = session.falsePositives.length > 0
80
- ? `\n\n📝 ${session.falsePositives.length} item(s) marked as false positives.`
81
- : "";
82
- return `✅ BOOBOO FIX LOOP COMPLETE — No more fixable issues found after ${session.iteration} iteration(s).${fpNote}`;
83
- }
84
- if (noProgress) {
85
- return `⚠️ BOOBOO FIX LOOP STOPPED — No progress after ${session.iteration} iteration(s).\n\nRemaining items may be false positives. Mark with: /lens-booboo-fix --false-positive "<type>:<file>:<line>"`;
86
- }
87
- // --- Write TSV plan file for agent to read ---
88
- const reportDir = path.join(process.cwd(), ".pi-lens", "reports");
89
- nodeFs.mkdirSync(reportDir, { recursive: true });
90
- const reportPath = path.join(reportDir, "fix-plan.tsv");
91
- const tsvRows = ["type\tfile\trule\tmessage"];
92
- // Duplicates
93
- for (const clone of filteredDups) {
94
- tsvRows.push(`dup\t${clone.fileA}:${clone.startA}\tduplicate-code\t${clone.lines} lines duplicated with ${clone.fileB}:${clone.startB}`);
95
- }
96
- // Dead code
97
- for (const issue of filteredDeadCode) {
98
- tsvRows.push(`dead\t${issue.file || issue.name}\t${issue.type}\t${issue.name} is unused`);
99
- }
100
- // AST issues
101
- for (const issue of agentTasks) {
102
- tsvRows.push(`ast\t${issue.file}:${issue.line}\t${issue.rule}\t${issue.message}`);
103
- }
104
- // Biome
105
- for (const issue of filteredBiome) {
106
- tsvRows.push(`biome\t${issue.file}:${issue.line}\t${issue.rule}\t${issue.message}`);
107
- }
108
- // Slop
109
- for (const { file, warnings } of filteredSlop) {
110
- for (const w of warnings) {
111
- tsvRows.push(`slop\t${file}\tcomplexity\t${w}`);
112
- }
113
- }
114
- nodeFs.writeFileSync(reportPath, tsvRows.join("\n"), "utf-8");
115
- // --- Build actionable list for terminal (no TSV reading needed) ---
116
- const lines = [];
117
- lines.push(`📋 FIX PLAN — Iteration ${session.iteration}/${MAX_ITERATIONS} — ${totalFixable} issues:\n`);
118
- // Duplicates
119
- if (filteredDups.length > 0) {
120
- for (const clone of filteredDups.slice(0, 10)) {
121
- lines.push(`🔁 ${clone.fileA}:${clone.startA} — ${clone.lines} dup from ${clone.fileB}:${clone.startB}`);
122
- }
123
- if (filteredDups.length > 10)
124
- lines.push(` ... +${filteredDups.length - 10} more`);
125
- lines.push("");
126
- }
127
- // Dead code
128
- if (filteredDeadCode.length > 0) {
129
- for (const issue of filteredDeadCode.slice(0, 10)) {
130
- lines.push(`🗑️ ${issue.file || issue.name} — ${issue.name} unused`);
131
- }
132
- if (filteredDeadCode.length > 10)
133
- lines.push(` ... +${filteredDeadCode.length - 10} more`);
134
- lines.push("");
135
- }
136
- // AST lint
137
- if (agentTasks.length > 0) {
138
- for (const issue of agentTasks.slice(0, 15)) {
139
- lines.push(`🔨 ${issue.file}:${issue.line} — ${issue.rule}`);
140
- }
141
- if (agentTasks.length > 15)
142
- lines.push(` ... +${agentTasks.length - 15} more`);
143
- lines.push("");
144
- }
145
- // Biome
146
- if (filteredBiome.length > 0) {
147
- for (const issue of filteredBiome.slice(0, 10)) {
148
- lines.push(`🟠 ${issue.file}:${issue.line} — ${issue.rule}`);
149
- }
150
- if (filteredBiome.length > 10)
151
- lines.push(` ... +${filteredBiome.length - 10} more`);
152
- lines.push("");
153
- }
154
- // AI Slop
155
- if (filteredSlop.length > 0) {
156
- for (const { file, warnings } of filteredSlop.slice(0, 5)) {
157
- lines.push(`🤖 ${file} — ${warnings[0]}`);
158
- }
159
- if (filteredSlop.length > 5)
160
- lines.push(` ... +${filteredSlop.length - 5} more`);
161
- lines.push("");
162
- }
163
- lines.push("---");
164
- lines.push("🚀 Fix items above, then run `/lens-booboo-fix --loop`");
165
- lines.push('🚫 False positive: `/lens-booboo-fix --false-positive "type:file:line"`');
166
- return lines.join("\n");
167
- }
168
- // --- Main handler ---
169
- export async function handleFix(args, ctx, clients, pi, _ruleActions) {
170
- const resetRequested = args.includes("--reset");
171
- const loopMode = args.includes("--loop");
172
- const fpMatch = args.match(/--false-positive\s+"([^"]+)"/);
173
- const falsePositiveId = fpMatch?.[1];
174
- // Clean args
175
- const cleanArgs = args
176
- .replace("--reset", "")
177
- .replace("--loop", "")
178
- .replace(/--false-positive\s+"[^"]+"/, "")
179
- .trim();
180
- const targetPath = cleanArgs || ctx.cwd || process.cwd();
181
- const sessionFile = path.join(process.cwd(), ".pi-lens", "fix-session.json");
182
- const configPath = path.join(typeof __dirname !== "undefined" ? __dirname : ".", "..", "rules", "ast-grep-rules", ".sgconfig.yml");
183
- // Load session
184
- let session = loadSession(sessionFile);
185
- if (resetRequested) {
186
- session = resetSession(sessionFile);
187
- ctx.ui.notify("🔄 Fix session reset.", "info");
188
- }
189
- // Handle false positive marking
190
- if (falsePositiveId) {
191
- if (!session.falsePositives.includes(falsePositiveId)) {
192
- session.falsePositives.push(falsePositiveId);
193
- saveSession(sessionFile, session);
194
- ctx.ui.notify(`✅ Marked as false positive: "${falsePositiveId}"`, "info");
195
- }
196
- return;
197
- }
198
- // Start auto-loop if requested
199
- const loop = getFixLoop(pi);
200
- if (loopMode && !loop.getState().active) {
201
- loop.start(ctx);
202
- }
203
- ctx.ui.notify("🔧 Running booboo fix loop...", "info");
204
- const isTsProject = nodeFs.existsSync(path.join(targetPath, "tsconfig.json"));
205
- // Auto-fix with Biome + Ruff
206
- if (!pi.getFlag("no-biome") && clients.biome.isAvailable()) {
207
- childProcess.spawnSync("npx", ["@biomejs/biome", "check", "--write", "--unsafe", targetPath], {
208
- encoding: "utf-8",
209
- timeout: 30000,
210
- shell: true,
211
- });
212
- }
213
- if (!pi.getFlag("no-ruff") && clients.ruff.isAvailable()) {
214
- childProcess.spawnSync("ruff", ["check", "--fix", targetPath], {
215
- encoding: "utf-8",
216
- timeout: 15000,
217
- shell: true,
218
- });
219
- childProcess.spawnSync("ruff", ["format", targetPath], {
220
- encoding: "utf-8",
221
- timeout: 15000,
222
- shell: true,
223
- });
224
- }
225
- // Run all scanners
226
- const prevCounts = { ...session.counts };
227
- session.iteration++;
228
- const results = scanAll(clients, targetPath, isTsProject, configPath);
229
- // Update session counts
230
- session.counts = {
231
- duplicates: results.duplicates.length,
232
- dead_code: results.deadCode.length,
233
- ast_issues: results.astIssues.length,
234
- biome_issues: results.biomeIssues.length,
235
- slop_files: results.slopFiles.length,
236
- };
237
- saveSession(sessionFile, session);
238
- // Generate and send plan
239
- const plan = generatePlan(results, session, isTsProject, prevCounts);
240
- const planPath = path.join(process.cwd(), ".pi-lens", "fix-plan.md");
241
- nodeFs.writeFileSync(planPath, `# Fix Plan — Iteration ${session.iteration}\n\n${plan}`, "utf-8");
242
- ctx.ui.notify(`📄 Fix plan saved: ${planPath}`, "info");
243
- pi.sendUserMessage(plan, { deliverAs: "followUp" });
244
- }