@chemx/starter-kit 26.10.8-344 → 26.10.10-75

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 (805) hide show
  1. package/AGENTS.md +135 -32
  2. package/README.md +253 -199
  3. package/blueprints/atoms/a-button.svelte +11 -9
  4. package/blueprints/atoms/a-button.vue +2 -1
  5. package/blueprints/molecule-capsule/m-chemx-badge/m-chemx-badge.svelte +8 -4
  6. package/blueprints/molecule-capsule/m-chemx-badge/m-chemx-badge.tsx +8 -4
  7. package/blueprints/molecule-capsule/m-chemx-badge/m-chemx-badge.vue +8 -4
  8. package/blueprints/molecule-capsule/m-sample-card.svelte +2 -1
  9. package/blueprints/molecule-capsule/m-sample-card.tsx +2 -1
  10. package/blueprints/molecule-capsule/m-sample-card.vue +4 -2
  11. package/blueprints/workflows/chemx-audit.yml +15 -46
  12. package/cli/agent-json.js +40 -0
  13. package/cli/apply-edits.js +195 -0
  14. package/cli/audit/ai-slop-detector.js +62 -35
  15. package/cli/audit/ast-passes.js +52 -0
  16. package/cli/audit/ast-visitors.js +142 -83
  17. package/cli/audit/audit-plain.js +26 -0
  18. package/cli/audit/audit-summary.js +16 -3
  19. package/cli/audit/autofix-content.js +122 -0
  20. package/cli/audit/autofix.js +74 -159
  21. package/cli/audit/budget-rules.js +84 -0
  22. package/cli/audit/catch-predicates.js +51 -0
  23. package/cli/audit/chemx-dir.js +76 -0
  24. package/cli/audit/clone-detector.js +4 -2
  25. package/cli/audit/comment-regions.js +83 -0
  26. package/cli/audit/coverage.js +48 -0
  27. package/cli/audit/csharp-analyzer.js +47 -22
  28. package/cli/audit/discussion-store.js +9 -4
  29. package/cli/audit/extended-text-patterns.js +104 -54
  30. package/cli/audit/extended-visitors.js +26 -12
  31. package/cli/audit/feed.d.ts +77 -0
  32. package/cli/audit/feed.js +146 -0
  33. package/cli/audit/gate-delta.js +91 -0
  34. package/cli/audit/gate-verdict.js +25 -3
  35. package/cli/audit/history.d.ts +14 -2
  36. package/cli/audit/history.js +89 -101
  37. package/cli/audit/hook-shape-validator.js +18 -9
  38. package/cli/audit/lexicon-predicates.js +125 -0
  39. package/cli/audit/lifecycle-predicates.js +106 -0
  40. package/cli/audit/line-budgets.js +154 -0
  41. package/cli/audit/metrics.js +56 -37
  42. package/cli/audit/mutable-ref-predicate.js +54 -0
  43. package/cli/audit/parseable-code.js +11 -0
  44. package/cli/audit/pattern-detector.js +56 -28
  45. package/cli/audit/project-figures.js +28 -0
  46. package/cli/audit/prompt-rule-lines.js +46 -0
  47. package/cli/audit/prompts.js +74 -46
  48. package/cli/audit/prop-surface.js +74 -0
  49. package/cli/audit/ratchet.js +112 -23
  50. package/cli/audit/render-depth.js +79 -0
  51. package/cli/audit/reporter-banner.js +7 -4
  52. package/cli/audit/reporter-grades.js +52 -34
  53. package/cli/audit/reporter-grouping.js +10 -5
  54. package/cli/audit/reporter-markdown.js +16 -8
  55. package/cli/audit/reporter-sections.js +31 -16
  56. package/cli/audit/reporter-summary.js +28 -16
  57. package/cli/audit/reporter-utils.js +54 -30
  58. package/cli/audit/roadmap.js +20 -15
  59. package/cli/audit/rule-overrides.js +78 -0
  60. package/cli/audit/rule-pillars.js +43 -0
  61. package/cli/audit/rule-revisions.js +42 -0
  62. package/cli/audit/rules-helpers.js +13 -63
  63. package/cli/audit/rules-predicates.d.ts +1 -0
  64. package/cli/audit/rules-predicates.js +84 -64
  65. package/cli/audit/rules-registry.js +94 -10
  66. package/cli/audit/rules.js +92 -171
  67. package/cli/audit/shallow-catch-csharp.js +49 -0
  68. package/cli/audit/shallow-catch-escalation.js +157 -0
  69. package/cli/audit/shallow-catch-reads.js +116 -0
  70. package/cli/audit/social-gh.js +18 -9
  71. package/cli/audit/social-git.js +82 -40
  72. package/cli/audit/social-http.js +17 -9
  73. package/cli/audit/social-publisher.js +14 -7
  74. package/cli/audit/social.js +39 -30
  75. package/cli/audit/staged-delta.js +67 -0
  76. package/cli/audit/status-file.js +67 -0
  77. package/cli/audit/structural-weight-evaluator.js +17 -29
  78. package/cli/audit/structural-weight.js +30 -13
  79. package/cli/audit/suppressions.js +130 -0
  80. package/cli/audit/template-rules.js +82 -0
  81. package/cli/audit/types.d.ts +24 -1
  82. package/cli/audit-engine.js +128 -0
  83. package/cli/audit-preflight-git.js +15 -1
  84. package/cli/audit-preflight.js +6 -4
  85. package/cli/audit-scan.js +180 -0
  86. package/cli/audit-scope.js +31 -14
  87. package/cli/audit.js +6 -201
  88. package/cli/babel-lazy.js +41 -0
  89. package/cli/badge.js +9 -6
  90. package/cli/banner.js +37 -0
  91. package/cli/build/child-registry.js +57 -0
  92. package/cli/build/executor.js +122 -48
  93. package/cli/build.js +75 -25
  94. package/cli/cli-args.js +107 -0
  95. package/cli/columnar.js +6 -3
  96. package/cli/commands/cmd-audit-each.js +164 -0
  97. package/cli/commands/cmd-audit-feed.js +78 -0
  98. package/cli/commands/cmd-audit.js +136 -57
  99. package/cli/commands/cmd-batch.js +60 -0
  100. package/cli/commands/cmd-check.js +74 -0
  101. package/cli/commands/cmd-project.js +18 -9
  102. package/cli/commands/cmd-router.js +148 -32
  103. package/cli/commands/cmd-show.js +56 -0
  104. package/cli/commands/cmd-staged-delta.js +27 -0
  105. package/cli/commands/cmd-wrappers-files.js +77 -0
  106. package/cli/commands/cmd-wrappers-git.js +122 -0
  107. package/cli/commands/cmd-wrappers-json.js +101 -0
  108. package/cli/commands/cmd-wrappers.js +7 -322
  109. package/cli/commands/unknown-flags.js +228 -0
  110. package/cli/commands-schema-badge.js +73 -0
  111. package/cli/commands-schema-commit.js +21 -0
  112. package/cli/commands-schema-edit.js +106 -0
  113. package/cli/commands-schema-host.js +65 -0
  114. package/cli/commands-schema-ops.js +127 -0
  115. package/cli/commands-schema-patterns.js +69 -0
  116. package/cli/commands-schema-report.js +18 -0
  117. package/cli/commands-schema-search.js +121 -0
  118. package/cli/commands-schema-setup.js +30 -0
  119. package/cli/commands-schema-verify.js +89 -0
  120. package/cli/commands-schema-wrappers.js +131 -0
  121. package/cli/commands-schema.js +26 -408
  122. package/cli/comment-ranges.js +108 -0
  123. package/cli/commit/commit-args.js +73 -0
  124. package/cli/commit/commit-cli.js +25 -0
  125. package/cli/commit/commit-facts.js +49 -0
  126. package/cli/commit/commit-git.js +98 -0
  127. package/cli/commit/commit-index.js +35 -0
  128. package/cli/commit/commit-message.js +52 -0
  129. package/cli/commit/commit-record.js +68 -0
  130. package/cli/commit/commit-run.js +160 -0
  131. package/cli/commit/commit-validate.js +62 -0
  132. package/cli/config/index.js +54 -8
  133. package/cli/config/loader.js +77 -8
  134. package/cli/config/profiles.js +7 -0
  135. package/cli/conflicts-cli.js +118 -0
  136. package/cli/conflicts.js +104 -0
  137. package/cli/create.js +11 -6
  138. package/cli/db-project-stamp.js +124 -0
  139. package/cli/docs-check/command-tree.js +27 -0
  140. package/cli/docs-check/extract.js +151 -0
  141. package/cli/docs-check/resolve.js +85 -0
  142. package/cli/docs-check/run.js +88 -0
  143. package/cli/doctor/check-env.js +43 -0
  144. package/cli/doctor/check-host.js +115 -0
  145. package/cli/doctor/check-index.js +93 -0
  146. package/cli/doctor/check-mcp.js +78 -0
  147. package/cli/doctor/doctor-cli.js +60 -0
  148. package/cli/doctor/kit-locate.js +40 -0
  149. package/cli/doctor/proc-scan.js +66 -0
  150. package/cli/edit-atomic.js +75 -0
  151. package/cli/edit-diff.js +98 -0
  152. package/cli/edit-guardrails.js +77 -0
  153. package/cli/edit-locks.js +128 -0
  154. package/cli/embeddings/vectorizer.js +9 -5
  155. package/cli/errors/catcher.js +45 -52
  156. package/cli/errors/formatter.js +14 -10
  157. package/cli/errors/policy.js +26 -0
  158. package/cli/errors/publisher.js +5 -1
  159. package/cli/errors/report-output.js +67 -0
  160. package/cli/errors/sanitizer.js +52 -3
  161. package/cli/errors/storage.js +17 -5
  162. package/cli/explode-importers.js +102 -0
  163. package/cli/explode-path-args.js +115 -0
  164. package/cli/explode-plan.js +153 -0
  165. package/cli/explode-prologue.js +43 -0
  166. package/cli/explode-relocate.js +73 -0
  167. package/cli/exploder.js +205 -193
  168. package/cli/forge/anchors.js +50 -0
  169. package/cli/forge/audit-forge.js +58 -0
  170. package/cli/forge/binding-walk.js +84 -0
  171. package/cli/forge/bindings.js +238 -0
  172. package/cli/forge/blueprint-cli.js +153 -0
  173. package/cli/forge/blueprint-context.js +69 -0
  174. package/cli/forge/blueprint-members.js +82 -0
  175. package/cli/forge/blueprint-store.js +106 -0
  176. package/cli/forge/blueprint-target.js +44 -0
  177. package/cli/forge/blueprint.js +276 -0
  178. package/cli/forge/body-end-cache.js +34 -0
  179. package/cli/forge/body-ends.js +86 -0
  180. package/cli/forge/canon-inline.js +342 -0
  181. package/cli/forge/canon-logic.js +106 -0
  182. package/cli/forge/canon-nodes.js +270 -0
  183. package/cli/forge/canon-print.js +120 -0
  184. package/cli/forge/canonicalize.js +49 -0
  185. package/cli/forge/conventions.js +51 -0
  186. package/cli/forge/drift.js +120 -0
  187. package/cli/forge/exclusions.js +21 -0
  188. package/cli/forge/exits.js +76 -0
  189. package/cli/forge/facets.js +86 -0
  190. package/cli/forge/file-units.js +53 -0
  191. package/cli/forge/fingerprint-file.js +52 -0
  192. package/cli/forge/fingerprint-session.js +215 -0
  193. package/cli/forge/fingerprint-sync.js +74 -0
  194. package/cli/forge/fingerprint-visitors.js +66 -0
  195. package/cli/forge/fixtures/heal/a7-dry-run.diff.txt +179 -0
  196. package/cli/forge/fixtures/heal/cli/commit/commit-facts.js.txt +49 -0
  197. package/cli/forge/fixtures/heal/cli/doctor/check-host.js.txt +117 -0
  198. package/cli/forge/fixtures/heal/cli/doctor/kit-locate.js.txt +42 -0
  199. package/cli/forge/fixtures/heal/cli/hooks/guard-paths.js.txt +80 -0
  200. package/cli/forge/fixtures/heal/cli/hooks/project-status.js.txt +62 -0
  201. package/cli/forge/fixtures/heal/cli/main.js.txt +169 -0
  202. package/cli/forge/fixtures/heal/cli/mcp/context.js.txt +81 -0
  203. package/cli/forge/fixtures/heal/cli/mcp/installer-report.js.txt +47 -0
  204. package/cli/forge/fixtures/heal/cli/path-scope.js.txt +169 -0
  205. package/cli/forge/fixtures/heal/cli/project-detector.js.txt +364 -0
  206. package/cli/forge/fixtures/heal/cli/project-markers.js.txt +42 -0
  207. package/cli/forge/fixtures/heal/cli/team/coordination-root.js.txt +161 -0
  208. package/cli/forge/fixtures/heal/cli/team/usage-reader.js.txt +109 -0
  209. package/cli/forge/fixtures/heal/cli/test-slots.js.txt +129 -0
  210. package/cli/forge/fixtures/social-gh.js.txt +131 -0
  211. package/cli/forge/fixtures/team-flags.js.txt +214 -0
  212. package/cli/forge/fold.js +199 -0
  213. package/cli/forge/forge-groups.js +200 -0
  214. package/cli/forge/forge-report.js +73 -0
  215. package/cli/forge/fuzz/binding-hook.js +36 -0
  216. package/cli/forge/fuzz/choices.js +40 -0
  217. package/cli/forge/fuzz/classes.js +41 -0
  218. package/cli/forge/fuzz/coverage.js +26 -0
  219. package/cli/forge/fuzz/gen-alias.js +61 -0
  220. package/cli/forge/fuzz/gen-control.js +73 -0
  221. package/cli/forge/fuzz/gen-kit.js +75 -0
  222. package/cli/forge/fuzz/gen-logic.js +77 -0
  223. package/cli/forge/fuzz/gen-mixed.js +204 -0
  224. package/cli/forge/fuzz/gen-modules.js +80 -0
  225. package/cli/forge/fuzz/gen-objects.js +92 -0
  226. package/cli/forge/fuzz/gen-scope.js +131 -0
  227. package/cli/forge/fuzz/gen-templates.js +87 -0
  228. package/cli/forge/fuzz/harness.js +314 -0
  229. package/cli/forge/fuzz/pair-check.js +115 -0
  230. package/cli/forge/fuzz/resolve.js +32 -0
  231. package/cli/forge/fuzz/run.js +121 -0
  232. package/cli/forge/fuzz/sandbox.js +108 -0
  233. package/cli/forge/fuzz/seeds.js +119 -0
  234. package/cli/forge/fuzz/slow-shard.js +60 -0
  235. package/cli/forge/fuzz/template-oracle.js +148 -0
  236. package/cli/forge/fuzz/transpile.js +61 -0
  237. package/cli/forge/gates.js +115 -0
  238. package/cli/forge/group-shape.js +137 -0
  239. package/cli/forge/group-store.js +160 -0
  240. package/cli/forge/group.js +280 -0
  241. package/cli/forge/gt-sandbox.js +83 -0
  242. package/cli/forge/gt-scaffold.js +110 -0
  243. package/cli/forge/hash-labels.js +117 -0
  244. package/cli/forge/hash.js +119 -0
  245. package/cli/forge/heal-apply.js +170 -0
  246. package/cli/forge/heal-cli.js +132 -0
  247. package/cli/forge/heal-leases.js +52 -0
  248. package/cli/forge/heal-locate.js +70 -0
  249. package/cli/forge/heal-match.js +208 -0
  250. package/cli/forge/heal-ops.js +197 -0
  251. package/cli/forge/heal-plan.js +143 -0
  252. package/cli/forge/heal-sandbox.js +126 -0
  253. package/cli/forge/heal-specs.js +80 -0
  254. package/cli/forge/heal-store.js +80 -0
  255. package/cli/forge/heal-text.js +60 -0
  256. package/cli/forge/heal-typecheck.js +108 -0
  257. package/cli/forge/heal-undo.js +74 -0
  258. package/cli/forge/heal-verify.js +148 -0
  259. package/cli/forge/hole-kinds.js +126 -0
  260. package/cli/forge/inline-mode.js +6 -0
  261. package/cli/forge/inline-on.setup.js +4 -0
  262. package/cli/forge/lgg-stage.js +125 -0
  263. package/cli/forge/lgg.js +197 -0
  264. package/cli/forge/library-match.js +74 -0
  265. package/cli/forge/murmur.js +86 -0
  266. package/cli/forge/naming.js +101 -0
  267. package/cli/forge/pattern-schema-ddl.js +157 -0
  268. package/cli/forge/placement.js +165 -0
  269. package/cli/forge/rank.js +174 -0
  270. package/cli/forge/refine.js +93 -0
  271. package/cli/forge/rejected-members.js +92 -0
  272. package/cli/forge/rejects.js +77 -0
  273. package/cli/forge/root-shapes.js +84 -0
  274. package/cli/forge/run-cache.js +145 -0
  275. package/cli/forge/sibling-blocks.js +41 -0
  276. package/cli/forge/siblings.js +140 -0
  277. package/cli/forge/store.js +180 -0
  278. package/cli/forge/template-normalize.js +209 -0
  279. package/cli/forge/template-roles.js +86 -0
  280. package/cli/forge/template-units.js +148 -0
  281. package/cli/forge/templates.js +42 -0
  282. package/cli/forge/unify-step.js +65 -0
  283. package/cli/forge/unit-floor.js +74 -0
  284. package/cli/forge/unit-hash.js +229 -0
  285. package/cli/forge/unit-trees.js +153 -0
  286. package/cli/forge/units.js +261 -0
  287. package/cli/forge/windows.js +129 -0
  288. package/cli/friction/friction-cli.js +71 -0
  289. package/cli/friction/friction-export.js +54 -0
  290. package/cli/friction/friction-log.js +54 -0
  291. package/cli/friction/usage-classify.js +56 -0
  292. package/cli/friction/usage-report.js +86 -0
  293. package/cli/friction/wrong-call.js +12 -0
  294. package/cli/fs-json.js +17 -0
  295. package/cli/generator-compact.js +55 -0
  296. package/cli/generator-help.js +16 -15
  297. package/cli/generator-indexer.js +2 -1
  298. package/cli/generator-jig-cli.js +11 -7
  299. package/cli/generator-jig.js +38 -33
  300. package/cli/generator-templates/archetypes/index.js +30 -13
  301. package/cli/generator-templates/archetypes/vector-matcher.js +8 -4
  302. package/cli/generator-templates/compact.js +4 -2
  303. package/cli/generator-templates/controller-converters.js +23 -14
  304. package/cli/generator-templates/controller.js +10 -5
  305. package/cli/generator-templates/hooks.js +4 -2
  306. package/cli/generator-templates/naming.js +2 -1
  307. package/cli/generator-templates/react.js +2 -1
  308. package/cli/generator-templates/specs.js +4 -2
  309. package/cli/generator-templates/svelte.js +4 -2
  310. package/cli/generator-templates/types.js +6 -3
  311. package/cli/generator-templates/views.js +4 -2
  312. package/cli/generator-templates/vue.js +4 -2
  313. package/cli/generator-writes.js +24 -0
  314. package/cli/generator.d.ts +1 -1
  315. package/cli/generator.js +86 -127
  316. package/cli/git-file-listing.js +69 -0
  317. package/cli/help-args.js +18 -0
  318. package/cli/help.d.ts +7 -3
  319. package/cli/help.js +94 -149
  320. package/cli/hooks/bypass-log.js +44 -0
  321. package/cli/hooks/claude-post-edit.js +79 -0
  322. package/cli/hooks/claude-pre-tool.js +178 -0
  323. package/cli/hooks/claude-settings-merge.js +101 -0
  324. package/cli/hooks/dispatch-identity.js +125 -0
  325. package/cli/hooks/entry.js +181 -0
  326. package/cli/hooks/foreign-identity.js +60 -0
  327. package/cli/hooks/guard-args.js +55 -0
  328. package/cli/hooks/guard-cases.js +94 -0
  329. package/cli/hooks/guard-config.js +43 -0
  330. package/cli/hooks/guard-invocation.js +106 -0
  331. package/cli/hooks/guard-paths.js +80 -0
  332. package/cli/hooks/guard-route.js +211 -0
  333. package/cli/hooks/guard-rules-nudge.js +37 -0
  334. package/cli/hooks/guard-rules-reads.js +66 -0
  335. package/cli/hooks/guard-rules-shell.js +239 -0
  336. package/cli/hooks/guard-rules.js +114 -0
  337. package/cli/hooks/hook-commands.js +21 -0
  338. package/cli/hooks/install-hooks-apply.js +36 -0
  339. package/cli/hooks/install-hooks-cli.js +100 -0
  340. package/cli/hooks/install-hooks-config.js +48 -0
  341. package/cli/hooks/install-hooks-pins.js +66 -0
  342. package/cli/hooks/install-hooks-plan.js +56 -0
  343. package/cli/hooks/install-hooks-status.js +7 -0
  344. package/cli/hooks/interpreter-writes.js +195 -0
  345. package/cli/hooks/launcher.js +60 -0
  346. package/cli/hooks/mcp-json-merge.js +39 -0
  347. package/cli/hooks/native-edit-lock.js +40 -0
  348. package/cli/hooks/native-tool-policy.js +152 -0
  349. package/cli/hooks/out-of-band-edits.js +141 -0
  350. package/cli/hooks/post-edit-scope.js +43 -0
  351. package/cli/hooks/project-status.js +59 -0
  352. package/cli/hooks/repo-membership.js +32 -0
  353. package/cli/hooks/run-hook.js +79 -0
  354. package/cli/hooks/session-identity.js +153 -0
  355. package/cli/hooks/session-start.js +50 -0
  356. package/cli/hooks/shell-lexer-readers.js +76 -0
  357. package/cli/hooks/shell-lexer-state.js +59 -0
  358. package/cli/hooks/shell-lexer.js +110 -0
  359. package/cli/hooks/shell-parse.js +232 -0
  360. package/cli/hooks/statusline.js +27 -0
  361. package/cli/hooks/xatoms-hints.js +38 -0
  362. package/cli/host-protocol.js +109 -0
  363. package/cli/host-shims.js +9 -8
  364. package/cli/index-admission.js +41 -0
  365. package/cli/index-freshness.js +83 -0
  366. package/cli/index-row-check.js +58 -0
  367. package/cli/index.js +52 -150
  368. package/cli/installer-templates.js +67 -75
  369. package/cli/installer.d.ts +16 -1
  370. package/cli/installer.js +159 -58
  371. package/cli/languages.js +8 -4
  372. package/cli/lattice-directives.js +24 -0
  373. package/cli/lattice-payload.js +34 -0
  374. package/cli/{tesseract-state.js → lattice-state.js} +8 -4
  375. package/cli/lease-view.js +48 -0
  376. package/cli/library/entry-fp.js +42 -0
  377. package/cli/library/entry-schema.js +90 -0
  378. package/cli/library/library-tasks.js +25 -0
  379. package/cli/library/registry.js +56 -0
  380. package/cli/library/reverify.js +56 -0
  381. package/cli/library/verify-checks.js +89 -0
  382. package/cli/library/verify.js +81 -0
  383. package/cli/license-config.js +124 -0
  384. package/cli/license.js +168 -135
  385. package/cli/line-count.js +23 -0
  386. package/cli/literal-replace.js +107 -0
  387. package/cli/main.js +166 -0
  388. package/cli/mcp/antigravity.js +11 -9
  389. package/cli/mcp/batch.js +59 -0
  390. package/cli/mcp/call-args.js +75 -0
  391. package/cli/mcp/call-effects.js +57 -0
  392. package/cli/mcp/call-scope.js +70 -62
  393. package/cli/mcp/context.js +81 -0
  394. package/cli/mcp/envelope.js +62 -0
  395. package/cli/mcp/fingerprint.js +26 -0
  396. package/cli/mcp/fresh-entry.js +34 -0
  397. package/cli/mcp/fresh-protocol.js +37 -0
  398. package/cli/mcp/fresh-runner.js +40 -0
  399. package/cli/mcp/help.js +53 -0
  400. package/cli/mcp/index.js +9 -5
  401. package/cli/mcp/inflight.js +47 -0
  402. package/cli/mcp/installer-jsonc.js +64 -0
  403. package/cli/mcp/installer-package.js +54 -0
  404. package/cli/mcp/installer-report.js +47 -0
  405. package/cli/mcp/installer-write.js +117 -0
  406. package/cli/mcp/installer.js +88 -187
  407. package/cli/mcp/manifests-analysis.js +156 -0
  408. package/cli/mcp/manifests-runners.js +48 -0
  409. package/cli/mcp/manifests-team.js +100 -0
  410. package/cli/mcp/manifests.js +69 -309
  411. package/cli/mcp/offload-worker.js +11 -0
  412. package/cli/mcp/offload.js +18 -0
  413. package/cli/mcp/patterns-cli.js +76 -0
  414. package/cli/mcp/patterns-forge-cli.js +120 -0
  415. package/cli/mcp/prompts.js +13 -10
  416. package/cli/mcp/resources.js +8 -35
  417. package/cli/mcp/roots.js +46 -0
  418. package/cli/mcp/scorecard-worker.js +7 -0
  419. package/cli/mcp/scorecard.js +42 -0
  420. package/cli/mcp/server-info.js +28 -0
  421. package/cli/mcp/server.js +91 -291
  422. package/cli/mcp/spec-harness.js +62 -0
  423. package/cli/mcp/stale-exec.js +48 -0
  424. package/cli/mcp/staleness.js +26 -0
  425. package/cli/mcp/stdio.js +141 -0
  426. package/cli/mcp/tool-call.js +73 -0
  427. package/cli/mcp/tools-audit-feed.js +33 -0
  428. package/cli/mcp/tools-audit.js +13 -7
  429. package/cli/mcp/tools-generate.js +3 -3
  430. package/cli/mcp/tools-lazy.js +39 -0
  431. package/cli/mcp/tools-patch.js +93 -21
  432. package/cli/mcp/tools-patterns.js +6 -3
  433. package/cli/mcp/tools-project.js +8 -4
  434. package/cli/mcp/tools-q-modes.js +15 -14
  435. package/cli/mcp/tools-q.js +94 -33
  436. package/cli/mcp/tools-read.js +30 -80
  437. package/cli/mcp/tools-team-feed.js +10 -8
  438. package/cli/mcp/tools-team-locks.js +22 -10
  439. package/cli/mcp/tools-team-mailbox.js +8 -7
  440. package/cli/mcp/tools-team-tasks.js +125 -38
  441. package/cli/mcp/tools-team.js +14 -7
  442. package/cli/mcp/tools-verify.js +44 -15
  443. package/cli/mcp/tools-write.js +11 -16
  444. package/cli/mcp/tools.js +202 -68
  445. package/cli/mcp/worker-run.js +24 -0
  446. package/cli/missing-imports.js +52 -0
  447. package/cli/mutation-args.js +96 -0
  448. package/cli/mutators.js +164 -61
  449. package/cli/navigator-actions.js +19 -13
  450. package/cli/navigator-banner-helpers.js +1 -26
  451. package/cli/navigator-banner.js +5 -3
  452. package/cli/navigator-conversion.js +23 -12
  453. package/cli/navigator-grades.js +70 -35
  454. package/cli/navigator-guide.js +13 -11
  455. package/cli/navigator-menu.js +80 -166
  456. package/cli/navigator-paged.js +15 -8
  457. package/cli/navigator-sections.js +32 -0
  458. package/cli/navigator-share.js +53 -39
  459. package/cli/navigator.d.ts +25 -5
  460. package/cli/navigator.js +14 -41
  461. package/cli/network-policy.js +72 -0
  462. package/cli/network-recorder.fixture.mjs +35 -0
  463. package/cli/outline/ast-outline.js +120 -0
  464. package/cli/outline/style-outline.js +42 -0
  465. package/cli/outline/vue-outline.js +88 -0
  466. package/cli/patcher-cli.js +255 -0
  467. package/cli/patcher.d.ts +55 -24
  468. package/cli/patcher.js +167 -358
  469. package/cli/path-scope.js +78 -22
  470. package/cli/patterns/fixtures/gt/A1.txt +109 -0
  471. package/cli/patterns/fixtures/gt/A10.txt +13 -0
  472. package/cli/patterns/fixtures/gt/A11.txt +10 -0
  473. package/cli/patterns/fixtures/gt/A12.txt +29 -0
  474. package/cli/patterns/fixtures/gt/A13.txt +39 -0
  475. package/cli/patterns/fixtures/gt/A14.txt +45 -0
  476. package/cli/patterns/fixtures/gt/A15.txt +70 -0
  477. package/cli/patterns/fixtures/gt/A16.txt +59 -0
  478. package/cli/patterns/fixtures/gt/A17.txt +29 -0
  479. package/cli/patterns/fixtures/gt/A18.txt +9 -0
  480. package/cli/patterns/fixtures/gt/A19.txt +29 -0
  481. package/cli/patterns/fixtures/gt/A2.txt +28 -0
  482. package/cli/patterns/fixtures/gt/A20.txt +83 -0
  483. package/cli/patterns/fixtures/gt/A21.txt +138 -0
  484. package/cli/patterns/fixtures/gt/A22.txt +32 -0
  485. package/cli/patterns/fixtures/gt/A23.txt +37 -0
  486. package/cli/patterns/fixtures/gt/A24.txt +67 -0
  487. package/cli/patterns/fixtures/gt/A25.txt +41 -0
  488. package/cli/patterns/fixtures/gt/A26.txt +29 -0
  489. package/cli/patterns/fixtures/gt/A3.txt +17 -0
  490. package/cli/patterns/fixtures/gt/A4.txt +44 -0
  491. package/cli/patterns/fixtures/gt/A5.txt +32 -0
  492. package/cli/patterns/fixtures/gt/A6.txt +15 -0
  493. package/cli/patterns/fixtures/gt/A7.txt +79 -0
  494. package/cli/patterns/fixtures/gt/A8.txt +41 -0
  495. package/cli/patterns/fixtures/gt/A9.txt +42 -0
  496. package/cli/patterns/fixtures/gt/B1.txt +11 -0
  497. package/cli/patterns/fixtures/gt/B10.txt +41 -0
  498. package/cli/patterns/fixtures/gt/B11.txt +7 -0
  499. package/cli/patterns/fixtures/gt/B2.txt +19 -0
  500. package/cli/patterns/fixtures/gt/B3.txt +70 -0
  501. package/cli/patterns/fixtures/gt/B4.txt +55 -0
  502. package/cli/patterns/fixtures/gt/B5.txt +21 -0
  503. package/cli/patterns/fixtures/gt/B6.txt +15 -0
  504. package/cli/patterns/fixtures/gt/B7.txt +24 -0
  505. package/cli/patterns/fixtures/gt/B8.txt +40 -0
  506. package/cli/patterns/fixtures/gt/B9.txt +18 -0
  507. package/cli/patterns/fixtures/gt/C1.txt +26 -0
  508. package/cli/patterns/fixtures/gt/C2.txt +33 -0
  509. package/cli/patterns/fixtures/gt/C3.txt +26 -0
  510. package/cli/patterns/fixtures/gt/C4.txt +7 -0
  511. package/cli/patterns/fixtures/gt/C5.txt +29 -0
  512. package/cli/patterns/fixtures/gt/C6.txt +7 -0
  513. package/cli/patterns/fixtures/gt/C7.txt +18 -0
  514. package/cli/patterns/fixtures/gt/labels.json +1837 -0
  515. package/cli/patterns/gt-build.js +55 -0
  516. package/cli/patterns/gt-locations.js +76 -0
  517. package/cli/patterns/gt-resolve.js +66 -0
  518. package/cli/patterns/gt-score-cli.js +80 -0
  519. package/cli/patterns/gt-score.js +188 -0
  520. package/cli/patterns/gt-text.js +79 -0
  521. package/cli/pillars-wizard.js +53 -24
  522. package/cli/pillars-write-guard.js +4 -1
  523. package/cli/postinstall.js +22 -19
  524. package/cli/project-detector.js +60 -35
  525. package/cli/project-markers.js +42 -0
  526. package/cli/project-scope.js +20 -0
  527. package/cli/read-revision.js +70 -0
  528. package/cli/reader-cards-gate.js +29 -0
  529. package/cli/reader-cards.js +153 -0
  530. package/cli/reader-cli.js +45 -18
  531. package/cli/reader-format.js +48 -0
  532. package/cli/reader-logic.js +128 -250
  533. package/cli/reader.js +210 -221
  534. package/cli/request-context.js +9 -0
  535. package/cli/result-status.js +23 -0
  536. package/cli/rules.js +81 -0
  537. package/cli/scaffold.js +80 -51
  538. package/cli/scan-ignore.js +12 -0
  539. package/cli/search-args.js +102 -0
  540. package/cli/search-ast-module.js +103 -0
  541. package/cli/search-ast.js +75 -112
  542. package/cli/search-commands-blast.js +70 -0
  543. package/cli/search-commands-graph.js +57 -105
  544. package/cli/search-commands-hazards.js +105 -0
  545. package/cli/search-commands-literal.js +102 -0
  546. package/cli/search-commands-semantic.js +39 -27
  547. package/cli/search-commands.js +93 -186
  548. package/cli/search-db.js +52 -252
  549. package/cli/search-debug.js +13 -0
  550. package/cli/search-graph-edges.js +112 -0
  551. package/cli/search-index-meta.js +66 -0
  552. package/cli/search-index-write.js +153 -0
  553. package/cli/search-literal-files.js +64 -0
  554. package/cli/search-literal.js +150 -0
  555. package/cli/search-output.js +88 -0
  556. package/cli/search-queries-graph.js +120 -193
  557. package/cli/search-queries-semantic.js +41 -14
  558. package/cli/search-queries-similar.js +2 -1
  559. package/cli/search-queries.js +46 -21
  560. package/cli/search-query-view.js +67 -0
  561. package/cli/search-rank.js +54 -0
  562. package/cli/search-replace-blocks.js +120 -0
  563. package/cli/search-resolve.js +52 -0
  564. package/cli/search-root.js +78 -0
  565. package/cli/search-scan.js +102 -0
  566. package/cli/search-schema-ddl.js +172 -0
  567. package/cli/search-schema.js +135 -220
  568. package/cli/search-session.js +11 -0
  569. package/cli/search-sync-paths.js +78 -0
  570. package/cli/search-sync-plan.js +63 -0
  571. package/cli/search-sync-rows.js +96 -0
  572. package/cli/search-sync.js +128 -0
  573. package/cli/search-tokenizer.js +8 -4
  574. package/cli/search.d.ts +26 -5
  575. package/cli/search.js +156 -410
  576. package/cli/sfc/component-resolver.js +96 -0
  577. package/cli/sfc/import-coverage.js +24 -0
  578. package/cli/sfc/module-aliases.js +102 -0
  579. package/cli/sfc/script-asts.js +51 -0
  580. package/cli/sfc/sfc-metadata.js +104 -0
  581. package/cli/sfc/sfc-parse.js +101 -0
  582. package/cli/sfc/svelte-parse.js +46 -0
  583. package/cli/sfc/template-walk.js +78 -0
  584. package/cli/sfc-scripts.js +60 -0
  585. package/cli/share-consent.js +49 -0
  586. package/cli/share-outcome.js +57 -0
  587. package/cli/silence-warnings.js +2 -1
  588. package/cli/source-parse.js +130 -0
  589. package/cli/spec-isolated-home.js +17 -0
  590. package/cli/spec-support/import-trace.mjs +22 -0
  591. package/cli/spec-support/run-cli.js +58 -0
  592. package/cli/sqlite-memory.js +47 -0
  593. package/cli/status-command.js +107 -0
  594. package/cli/symbol-locator.js +121 -0
  595. package/cli/team/agent-identity.js +58 -0
  596. package/cli/team/audit-run-adoption.js +117 -0
  597. package/cli/team/audit-run-bypass.js +177 -0
  598. package/cli/team/audit-run-calls.js +108 -0
  599. package/cli/team/audit-run-db.js +181 -0
  600. package/cli/team/audit-run-identity.js +80 -0
  601. package/cli/team/audit-run-invocations.js +130 -0
  602. package/cli/team/audit-run-protocol.js +258 -0
  603. package/cli/team/audit-run-render.js +107 -0
  604. package/cli/team/audit-run-shas.js +179 -0
  605. package/cli/team/audit-run.js +203 -0
  606. package/cli/team/claim-ignore-deps.js +16 -0
  607. package/cli/team/coordination-busy.js +164 -0
  608. package/cli/team/coordination-db.js +85 -0
  609. package/cli/team/coordination-fixture.js +53 -0
  610. package/cli/team/coordination-repos.js +72 -0
  611. package/cli/team/coordination-root.js +161 -0
  612. package/cli/team/coordination-target.js +114 -0
  613. package/cli/team/dispatch-templates/dispatch-v1.js +228 -0
  614. package/cli/team/edit-shas.js +45 -0
  615. package/cli/team/lease-activity.js +130 -0
  616. package/cli/team/lease-cap.js +67 -0
  617. package/cli/team/lease-contention.js +30 -0
  618. package/cli/team/lease-key.js +46 -0
  619. package/cli/team/lease-lapse.js +83 -0
  620. package/cli/team/lease-reacquire.js +99 -0
  621. package/cli/team/lease-renew.js +97 -0
  622. package/cli/team/lease-roots.js +30 -0
  623. package/cli/team/lease-waiter-notice.js +48 -0
  624. package/cli/team/staged-leases.js +64 -0
  625. package/cli/team/task-completion-output.js +78 -0
  626. package/cli/team/task-detail-sections.js +86 -0
  627. package/cli/team/task-list-view.js +10 -1
  628. package/cli/team/task-ref.js +73 -0
  629. package/cli/team/task-target.js +53 -0
  630. package/cli/team/task-tier.js +17 -0
  631. package/cli/team/team-brief.js +78 -0
  632. package/cli/team/team-commands-board.js +118 -0
  633. package/cli/team/team-commands-dispatch.js +85 -0
  634. package/cli/team/team-commands-lock-staged.js +87 -0
  635. package/cli/team/team-commands-lock-views.js +144 -0
  636. package/cli/team/team-commands-lock.js +115 -26
  637. package/cli/team/team-commands-migrate.js +171 -0
  638. package/cli/team/team-commands-profile.js +44 -0
  639. package/cli/team/team-commands-repo.js +114 -0
  640. package/cli/team/team-commands-task.js +312 -0
  641. package/cli/team/team-commands-tokens.js +57 -0
  642. package/cli/team/team-commands-triage.js +43 -0
  643. package/cli/team/team-commands-vds.js +2 -1
  644. package/cli/team/team-commands.js +108 -463
  645. package/cli/team/team-db-agents.js +12 -6
  646. package/cli/team/team-db-feed.js +25 -13
  647. package/cli/team/team-db-lock-promotion.js +95 -51
  648. package/cli/team/team-db-locks.js +167 -35
  649. package/cli/team/team-db-mailbox.js +15 -8
  650. package/cli/team/team-db-readonly.js +76 -0
  651. package/cli/team/team-db-task-helpers.js +68 -19
  652. package/cli/team/team-db-tasks.js +21 -9
  653. package/cli/team/team-db-transaction.js +20 -5
  654. package/cli/team/team-db.js +11 -4
  655. package/cli/team/team-dispatch-batches.js +271 -0
  656. package/cli/team/team-dispatch-prompts.js +152 -0
  657. package/cli/team/team-dispatch-render.js +154 -0
  658. package/cli/team/team-dispatch-run-cli.js +154 -0
  659. package/cli/team/team-dispatch-runs.js +155 -0
  660. package/cli/team/team-dispatch-script.js +67 -0
  661. package/cli/team/team-dispatch-select.js +220 -0
  662. package/cli/team/team-dispatch-v2-fixture.js +62 -0
  663. package/cli/team/team-dispatch-v2.js +207 -0
  664. package/cli/team/team-dispatch.js +229 -0
  665. package/cli/team/team-dispatcher-authority.js +43 -0
  666. package/cli/team/team-flags.js +222 -48
  667. package/cli/team/team-format.js +80 -26
  668. package/cli/team/team-handoff-hint.js +9 -0
  669. package/cli/team/team-migrate-agents.js +52 -0
  670. package/cli/team/team-migrate-apply.js +178 -0
  671. package/cli/team/team-migrate-junk.js +55 -0
  672. package/cli/team/team-migrate-metadata.js +80 -0
  673. package/cli/team/team-migrate-paths.js +50 -0
  674. package/cli/team/team-migrate-plan.js +85 -0
  675. package/cli/team/team-migrate-report.js +89 -0
  676. package/cli/team/team-migrate-tables.js +85 -0
  677. package/cli/team/team-migrate.js +193 -0
  678. package/cli/team/team-needs.js +99 -0
  679. package/cli/team/team-profile-format.js +126 -0
  680. package/cli/team/team-profile.js +144 -0
  681. package/cli/team/team-projects-coordinator.js +54 -33
  682. package/cli/team/team-projects-format.js +8 -4
  683. package/cli/team/team-projects-memory.js +6 -3
  684. package/cli/team/team-projects.js +2 -1
  685. package/cli/team/team-release-train.js +2 -1
  686. package/cli/team/team-route.js +132 -0
  687. package/cli/team/team-schema-repo.js +48 -0
  688. package/cli/team/team-schema-usage.js +25 -0
  689. package/cli/team/team-schema-vds.js +7 -3
  690. package/cli/team/team-schema.js +24 -4
  691. package/cli/team/team-task-close.js +132 -0
  692. package/cli/team/team-task-guard.js +24 -0
  693. package/cli/team/team-task-handoff.js +122 -0
  694. package/cli/team/team-task-ownership.js +74 -0
  695. package/cli/team/team-task-rules.js +21 -0
  696. package/cli/team/team-task-uncommitted.js +49 -0
  697. package/cli/team/team-telemetry.js +44 -32
  698. package/cli/team/team-tokens.js +4 -1
  699. package/cli/team/team-triage-generate.js +212 -0
  700. package/cli/team/team-triage-log.js +11 -0
  701. package/cli/team/team-triage-verify.js +153 -0
  702. package/cli/team/team-triage.js +92 -339
  703. package/cli/team/team-vds.js +2 -1
  704. package/cli/team/usage-compute.js +70 -0
  705. package/cli/team/usage-pricing.js +62 -0
  706. package/cli/team/usage-reader.js +106 -0
  707. package/cli/team/usage-render.js +72 -0
  708. package/cli/team/usage-store.js +56 -0
  709. package/cli/team/usage-transcript.js +127 -0
  710. package/cli/team/write-lock-guard.js +48 -0
  711. package/cli/telemetry/call-ledger.js +264 -0
  712. package/cli/telemetry/call-schema.js +53 -0
  713. package/cli/telemetry/savings-dbs.js +111 -0
  714. package/cli/telemetry/savings-render.js +68 -0
  715. package/cli/telemetry/savings-report.js +101 -0
  716. package/cli/telemetry/savings-routing.js +66 -0
  717. package/cli/telemetry/savings-tooling.js +140 -0
  718. package/cli/terminal.js +38 -40
  719. package/cli/tesseract-manifesto.js +2 -17
  720. package/cli/tesseract.js +8 -21
  721. package/cli/test-audit.js +218 -0
  722. package/cli/test-changes.js +60 -0
  723. package/cli/test-command.js +201 -0
  724. package/cli/test-depth.js +43 -0
  725. package/cli/test-failures.js +170 -0
  726. package/cli/test-graph-open.js +150 -0
  727. package/cli/test-graph.js +188 -0
  728. package/cli/test-lanes.js +117 -0
  729. package/cli/test-output.js +155 -0
  730. package/cli/test-paths.js +51 -0
  731. package/cli/test-profile.js +64 -0
  732. package/cli/test-report.js +112 -0
  733. package/cli/test-run.js +53 -0
  734. package/cli/test-scope.js +55 -0
  735. package/cli/test-select.js +107 -0
  736. package/cli/test-slots.js +276 -0
  737. package/cli/test-workers.js +45 -0
  738. package/cli/test-workspace.js +83 -0
  739. package/cli/theme.js +6 -3
  740. package/cli/timers.js +6 -0
  741. package/cli/trend.js +10 -5
  742. package/cli/tsconfig-shape.js +20 -0
  743. package/cli/tty-banner.js +11 -0
  744. package/cli/typecheck-audit.js +146 -0
  745. package/cli/typecheck-command.js +94 -0
  746. package/cli/typecheck-sandbox.js +93 -0
  747. package/cli/ui-actions-codebase.js +20 -7
  748. package/cli/ui-actions-helpers.js +9 -0
  749. package/cli/ui-actions-studio.js +11 -7
  750. package/cli/ui-actions-tasks.js +3 -2
  751. package/cli/ui-actions-vds.js +2 -1
  752. package/cli/ui-actions.js +4 -2
  753. package/cli/ui-attention.js +28 -12
  754. package/cli/ui-auth-hosts.js +46 -0
  755. package/cli/ui-auth.js +89 -0
  756. package/cli/ui-client-cert.js +2 -1
  757. package/cli/ui-client-filetree.js +2 -2
  758. package/cli/ui-db-studio.js +23 -33
  759. package/cli/ui-dev-watcher.js +2 -1
  760. package/cli/ui-forum-data.js +45 -46
  761. package/cli/ui-forum-topics.js +6 -3
  762. package/cli/ui-fresh-index.js +11 -0
  763. package/cli/ui-handlers.js +6 -6
  764. package/cli/ui-html.js +6 -12
  765. package/cli/ui-index.html +3 -3
  766. package/cli/ui-route-helpers.js +10 -0
  767. package/cli/ui-server-routes.js +15 -17
  768. package/cli/ui-server.js +49 -50
  769. package/cli/ui-sql-guard.js +76 -0
  770. package/cli/ui-sql-mask.js +61 -0
  771. package/cli/ui-sse.js +5 -4
  772. package/cli/ui-template-filetree.js +4 -4
  773. package/cli/ui-template-kanban.js +4 -7
  774. package/cli/verify-changed.js +43 -0
  775. package/cli/verify-helpers.js +21 -207
  776. package/cli/verify-lint.js +54 -20
  777. package/cli/verify-report.js +117 -0
  778. package/cli/verify-steps.js +71 -0
  779. package/cli/verify.js +111 -372
  780. package/cli/wait-command.js +147 -0
  781. package/cli/workspace-run.js +153 -0
  782. package/cli/workspace.js +147 -0
  783. package/cli/write-append.js +28 -0
  784. package/library/node-js/is-path-inside/entry.json +57 -0
  785. package/library/node-js/is-path-inside/negative/path-scope.js.txt +13 -0
  786. package/library/node-js/is-path-inside/piece.js +15 -0
  787. package/library/node-js/is-path-inside/piece.spec.js +38 -0
  788. package/library/node-js/read-json-or/entry.json +70 -0
  789. package/library/node-js/read-json-or/negative/ratchet.js.txt +14 -0
  790. package/library/node-js/read-json-or/negative/workspace.js.txt +8 -0
  791. package/library/node-js/read-json-or/piece.js +17 -0
  792. package/library/node-js/read-json-or/piece.spec.js +36 -0
  793. package/library/vue-ts/nullable-timer-handle/ambient.d.ts +5 -0
  794. package/library/vue-ts/nullable-timer-handle/entry.json +84 -0
  795. package/library/vue-ts/nullable-timer-handle/negative/const-alias.ts.txt +17 -0
  796. package/library/vue-ts/nullable-timer-handle/negative/inline-guard.ts.txt +15 -0
  797. package/library/vue-ts/nullable-timer-handle/piece.spec.js +48 -0
  798. package/library/vue-ts/nullable-timer-handle/piece.ts +41 -0
  799. package/package.json +9 -3
  800. package/scripts/check-framework-generation.mjs +32 -12
  801. package/scripts/direct-run.mjs +9 -0
  802. package/scripts/pre-commit.sh +48 -74
  803. package/scripts/publish-both.mjs +24 -16
  804. package/scripts/publish-summary.mjs +14 -0
  805. package/scripts/postinstall.mjs +0 -27
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Babel visitor passes for script code (a plain module or an SFC script overlay).
3
+ * Parsing, with the per-block fallback for SFCs whose combined overlay does not
4
+ * parse, lives in cli/sfc/script-asts.js so Forge's standalone path reads the same trees.
5
+ */
6
+ import traverse from '@babel/traverse';
7
+ import { createAstVisitors } from './ast-visitors.js';
8
+ import { createAiSlopVisitors } from './ai-slop-detector.js';
9
+ import { createExtendedVisitors } from './extended-visitors.js';
10
+ import { createPatternVisitors } from './pattern-detector.js';
11
+ import { createHookShapeRegistry } from './hook-shape-validator.js';
12
+ import { createJsxRenderDepthVisitor } from './render-depth.js';
13
+ import { createFingerprintVisitors } from '../forge/fingerprint-visitors.js';
14
+
15
+ export { parseScriptAsts } from '../sfc/script-asts.js';
16
+
17
+ const traverseFn = traverse.default || traverse;
18
+
19
+ const mergeVisitorSets = (visitorSets) => {
20
+ const merged = {};
21
+ for (const visitorSet of visitorSets) {
22
+ for (const [key, fn] of Object.entries(visitorSet)) {
23
+ const previous = merged[key];
24
+ merged[key] = previous ? (p, s) => { previous(p, s); fn(p, s); } : fn;
25
+ }
26
+ }
27
+ return merged;
28
+ };
29
+
30
+ /**
31
+ * Runs every AST visitor family over the parsed script ASTs. options.fingerprint (a Forge collector)
32
+ * fills its binding index in the same traverse and fingerprints each AST right after it; the legacy
33
+ * pattern visitors still run alongside until Forge replaces them.
34
+ */
35
+ export const runAstPasses = (asts, { relativePath, violations, ruleConfig, options, code = '' }) => {
36
+ const hookRegistry = options.hookRegistry || createHookShapeRegistry();
37
+ const fingerprint = options.fingerprint || null;
38
+ for (const ast of asts) {
39
+ const visitorSets = [
40
+ createAstVisitors({ relativePath, violations, hookRegistry, config: ruleConfig }),
41
+ createAiSlopVisitors({ relativePath, violations }),
42
+ createExtendedVisitors({ relativePath, violations }),
43
+ createJsxRenderDepthVisitor({ relativePath, violations, config: ruleConfig }),
44
+ options.patternRegistry ? createPatternVisitors(options.patternRegistry, relativePath) : {},
45
+ fingerprint ? createFingerprintVisitors(fingerprint) : {}
46
+ ];
47
+ traverseFn(ast, mergeVisitorSets(visitorSets));
48
+ if (fingerprint) fingerprint.addScriptAst(ast, code);
49
+ }
50
+ const shouldRunLocalConsistency = !options.hookRegistry;
51
+ if (shouldRunLocalConsistency) violations.push(...hookRegistry.validateCrossHookConsistency());
52
+ };
@@ -1,22 +1,35 @@
1
1
  import * as t from '@babel/types';
2
2
  import { RULE_REGISTRY } from './rules-registry.js';
3
- import { countLogicalOperators } from './rules-helpers.js';
4
3
  import {
5
4
  isCustomHookFunction,
6
5
  resolveStartLine,
7
6
  isZeroDelayTimeout,
8
7
  isUnguardedConsoleCall,
9
8
  isSilentGuardClause,
10
- isSwallowedCatch,
11
9
  countOptionalChainingDepth,
12
10
  isCombinatorCall,
13
- isRawBooleanArg
11
+ isRawBooleanArg,
12
+ isGuardClause
14
13
  } from './rules-predicates.js';
14
+ import { isSwallowedCatch } from './catch-predicates.js';
15
+ import { resolveCatchEscalation, resolveCatchSpan } from './shallow-catch-escalation.js';
16
+ import { isNamedCondition, countJunctionOperators, resolveIfChainLength } from './lexicon-predicates.js';
17
+ import { narrowsMutableRef, MUTABLE_REF_REMEDY } from './mutable-ref-predicate.js';
18
+ import { collectTeardowns, classifyTimerDisposal, isListenerDisposed } from './lifecycle-predicates.js';
15
19
  import { validateHookReturnShape } from './hook-shape-validator.js';
16
20
  import { evaluateComponentStructuralWeight } from './structural-weight-evaluator.js';
17
21
 
22
+ const LOOKUP_CHAIN_MIN_BRANCHES = 3;
23
+ const MAX_TEMPLATE_JUNCTIONS = 2;
24
+ const TIMER_SEVERITY_BY_KIND = { setInterval: 'CRITICAL', setTimeout: 'LOW' };
25
+
18
26
  export const createAstVisitors = ({ relativePath, violations, hookRegistry, config }) => {
27
+ let teardowns = { clearedHandles: new Set(), removedListeners: [] };
19
28
  return {
29
+ Program(programPath) {
30
+ teardowns = collectTeardowns(programPath);
31
+ },
32
+
20
33
  Function(astPath) {
21
34
  evaluateComponentStructuralWeight({
22
35
  funcPath: astPath,
@@ -37,16 +50,19 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
37
50
  astPath.traverse({
38
51
  CallExpression(callPath) {
39
52
  const callee = callPath.node.callee;
40
- if (t.isIdentifier(callee) && /^use[A-Z0-9]/.test(callee.name)) {
53
+ const isHookCall = t.isIdentifier(callee) && /^use[A-Z0-9]/.test(callee.name);
54
+ if (isHookCall) {
41
55
  const isExcluded = CONTROLLER_WRAPPER_EXCLUSIONS.has(callee.name);
42
- if (!isExcluded && callPath.getFunctionParent() === astPath) {
56
+ const isOwnHookCall = !isExcluded && callPath.getFunctionParent() === astPath;
57
+ if (isOwnHookCall) {
43
58
  hookCount += 1;
44
59
  }
45
60
  }
46
61
  }
47
62
  });
48
63
 
49
- if (hookCount > 5) {
64
+ const isHookSaturated = hookCount > 5;
65
+ if (isHookSaturated) {
50
66
  const line = astPath.node.loc?.start.line || 1;
51
67
  const meta = RULE_REGISTRY.HOOK_SATURATION;
52
68
  violations.push({
@@ -65,7 +81,8 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
65
81
  if (isCustomHook) {
66
82
  astPath.traverse({
67
83
  ReturnStatement(retPath) {
68
- if (retPath.getFunctionParent() === astPath) {
84
+ const isOwnReturn = retPath.getFunctionParent() === astPath;
85
+ if (isOwnReturn) {
69
86
  validateHookReturnShape({
70
87
  retPath,
71
88
  astPath,
@@ -82,9 +99,11 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
82
99
  // Pillar 2: Control Flow Complexity
83
100
  JSXExpressionContainer(astPath) {
84
101
  const expr = astPath.node.expression;
85
- if (t.isLogicalExpression(expr) || t.isUnaryExpression(expr)) {
86
- const opCount = countLogicalOperators(expr);
87
- if (opCount > 2) {
102
+ const isBooleanExpression = t.isLogicalExpression(expr) || t.isUnaryExpression(expr);
103
+ if (isBooleanExpression) {
104
+ const opCount = countJunctionOperators(expr);
105
+ const hasTooManyJunctions = opCount > MAX_TEMPLATE_JUNCTIONS;
106
+ if (hasTooManyJunctions) {
88
107
  const line = resolveStartLine(expr, astPath.node, 1);
89
108
  const meta = RULE_REGISTRY.CONTROL_FLOW_INLINE_BOOLEAN;
90
109
  violations.push({
@@ -103,24 +122,24 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
103
122
 
104
123
  IfStatement(astPath) {
105
124
  const expr = astPath.node.test;
106
- if (t.isLogicalExpression(expr)) {
107
- const opCount = countLogicalOperators(expr);
108
- const hasBinaryClauses = (t.isBinaryExpression(expr.left) || t.isLogicalExpression(expr.left)) &&
109
- (t.isBinaryExpression(expr.right) || t.isLogicalExpression(expr.right));
110
- if (opCount > 2 || (opCount >= 2 && hasBinaryClauses)) {
111
- const line = resolveStartLine(expr, astPath.node, 1);
112
- const meta = RULE_REGISTRY.CONTROL_FLOW_INLINE_BOOLEAN;
113
- violations.push({
114
- filePath: relativePath,
115
- line,
116
- column: expr.loc?.start.column || 1,
117
- hazard: `Inline multi-clause boolean comparison in if statement (${opCount} operators). Decompose into 2-stage booleans per Directive 3.A.`,
118
- rule: 'CONTROL_FLOW_INLINE_BOOLEAN',
119
- severity: meta.severity,
120
- pillar: meta.pillar,
121
- directive: meta.directive
122
- });
123
- }
125
+ const isLookupChain = resolveIfChainLength(astPath) >= LOOKUP_CHAIN_MIN_BRANCHES;
126
+ const isUnnamedTest = !isNamedCondition(expr);
127
+ const shouldFlagTest = isUnnamedTest && !isLookupChain;
128
+ if (shouldFlagTest) {
129
+ const line = resolveStartLine(expr, astPath.node, 1);
130
+ const meta = RULE_REGISTRY.CONTROL_FLOW_INLINE_BOOLEAN;
131
+ const isMutableNarrowing = narrowsMutableRef(astPath.get('test'), astPath.get('consequent'));
132
+ const remedy = isMutableNarrowing ? ` ${MUTABLE_REF_REMEDY}` : '';
133
+ violations.push({
134
+ filePath: relativePath,
135
+ line,
136
+ column: expr.loc?.start.column || 1,
137
+ hazard: `The if asks instead of reading a named condition. Name the question first (Directive 3.A).${remedy}`,
138
+ rule: 'CONTROL_FLOW_INLINE_BOOLEAN',
139
+ severity: countJunctionOperators(expr) > 0 ? meta.severity : 'LOW',
140
+ pillar: meta.pillar,
141
+ directive: meta.directive
142
+ });
124
143
  }
125
144
 
126
145
  const silentGuard = isSilentGuardClause(astPath, t);
@@ -141,7 +160,10 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
141
160
  },
142
161
 
143
162
  ConditionalExpression(astPath) {
144
- if (t.isConditionalExpression(astPath.node.consequent) || t.isConditionalExpression(astPath.node.alternate)) {
163
+ const isOutermostOfChain = !astPath.parentPath?.isConditionalExpression();
164
+ const hasNestedBranch = t.isConditionalExpression(astPath.node.consequent) || t.isConditionalExpression(astPath.node.alternate);
165
+ const isNestedTernaryRoot = hasNestedBranch && isOutermostOfChain;
166
+ if (isNestedTernaryRoot) {
145
167
  const line = astPath.node.loc?.start.line || 1;
146
168
  const meta = RULE_REGISTRY.CONTROL_FLOW_NESTED_TERNARY;
147
169
  violations.push({
@@ -159,18 +181,22 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
159
181
 
160
182
  SwitchStatement(astPath) {
161
183
  const cases = astPath.node.cases || [];
162
- if (cases.length < 3) return;
184
+ const hasTooFewCases = cases.length < 3;
185
+ if (hasTooFewCases) return;
163
186
 
164
187
  const isDispatchConsequent = (statements) => {
165
- if (!statements || statements.length === 0) return false;
166
- if (statements.length === 1) {
188
+ const isEmptyConsequent = !statements || statements.length === 0;
189
+ if (isEmptyConsequent) return false;
190
+ const isSingleStatement = statements.length === 1;
191
+ if (isSingleStatement) {
167
192
  const s = statements[0];
168
193
  if (t.isReturnStatement(s)) return true;
169
194
  if (t.isBlockStatement(s)) {
170
195
  return s.body.length === 1 && t.isReturnStatement(s.body[0]);
171
196
  }
172
197
  }
173
- if (statements.length === 2) {
198
+ const isStatementPair = statements.length === 2;
199
+ if (isStatementPair) {
174
200
  const [first, second] = statements;
175
201
  if (t.isBreakStatement(second)) {
176
202
  return t.isExpressionStatement(first) || t.isAssignmentExpression(first);
@@ -183,7 +209,8 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
183
209
  let nonDefaultCount = 0;
184
210
 
185
211
  for (const switchCase of cases) {
186
- if (!switchCase.test) continue;
212
+ const isDefaultCase = !switchCase.test;
213
+ if (isDefaultCase) continue;
187
214
  nonDefaultCount += 1;
188
215
  if (isDispatchConsequent(switchCase.consequent)) {
189
216
  dispatchCaseCount += 1;
@@ -207,14 +234,62 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
207
234
  }
208
235
  },
209
236
 
237
+ BlockStatement(astPath) {
238
+ const body = astPath.node.body;
239
+ const hasTooFewStatements = !body || body.length < 3;
240
+ if (hasTooFewStatements) return;
241
+
242
+ let currentGuards = [];
243
+
244
+ // A cascade is 3+ bail-out guards followed by the action they protect (Directive 3.H);
245
+ // a run of guards that ends the block protects nothing and is not reported.
246
+ const flushCluster = (isFollowedByAction) => {
247
+ const hasCascade = isFollowedByAction && currentGuards.length >= 3;
248
+ if (hasCascade) {
249
+ const firstGuard = currentGuards[0];
250
+ const guardCount = currentGuards.length;
251
+ const line = firstGuard.loc?.start.line || 1;
252
+ const column = firstGuard.loc?.start.column || 1;
253
+ const meta = RULE_REGISTRY.CONTROL_FLOW_CASCADE_GUARDS;
254
+ violations.push({
255
+ filePath: relativePath,
256
+ line,
257
+ column,
258
+ hazard: `Cascading guard clauses detected (${guardCount} guards >= 3 limit). Suggestion: Extract into a domain validator using ruleTree({ scope: { ... } }) with a 1-line callback return per Directive 3.H.`,
259
+ rule: 'CONTROL_FLOW_CASCADE_GUARDS',
260
+ severity: meta.severity,
261
+ pillar: meta.pillar,
262
+ directive: meta.directive
263
+ });
264
+ }
265
+ currentGuards = [];
266
+ };
267
+
268
+ for (const stmt of body) {
269
+ const isGuard = isGuardClause(stmt, t);
270
+ if (isGuard) {
271
+ currentGuards.push(stmt);
272
+ } else if (t.isVariableDeclaration(stmt)) {
273
+ continue;
274
+ } else {
275
+ flushCluster(true);
276
+ }
277
+ }
278
+ flushCluster(false);
279
+ },
280
+
210
281
  // Pillar 5: Design System & Inline Styles
211
282
  JSXAttribute(astPath) {
212
283
  const attrName = astPath.node.name?.name;
213
- if (attrName === 'style') {
284
+ const isStyleAttr = attrName === 'style';
285
+ const isClassAttr = attrName === 'className' || attrName === 'class';
286
+ if (isStyleAttr) {
214
287
  const value = astPath.node.value;
215
- if (t.isJSXExpressionContainer(value) && t.isObjectExpression(value.expression)) {
288
+ const isObjectStyle = t.isJSXExpressionContainer(value) && t.isObjectExpression(value.expression);
289
+ if (isObjectStyle) {
216
290
  const preferTokens = config?.preferDesignTokens || 'warning';
217
- if (preferTokens !== 'off') {
291
+ const isTokenCheckEnabled = preferTokens !== 'off';
292
+ if (isTokenCheckEnabled) {
218
293
  const line = astPath.node.loc?.start.line || 1;
219
294
  const meta = RULE_REGISTRY.RAW_INLINE_STYLE;
220
295
  const severity = preferTokens === 'error' ? 'HIGH' : meta.severity;
@@ -230,11 +305,13 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
230
305
  });
231
306
  }
232
307
  }
233
- } else if (attrName === 'className' || attrName === 'class') {
308
+ } else if (isClassAttr) {
234
309
  const value = astPath.node.value;
235
310
  const strVal = t.isStringLiteral(value) ? value.value : '';
236
- if (strVal && (strVal.includes('fa-') || strVal.includes('fa '))) {
237
- if (/\btext-(primary|secondary|danger|warning|success|info|light|dark|\w+)\b/.test(strVal)) {
311
+ const hasFontAwesomeClass = Boolean(strVal && (strVal.includes('fa-') || strVal.includes('fa ')));
312
+ if (hasFontAwesomeClass) {
313
+ const hasTextColorClass = /\btext-(primary|secondary|danger|warning|success|info|light|dark|\w+)\b/.test(strVal);
314
+ if (hasTextColorClass) {
238
315
  const line = astPath.node.loc?.start.line || 1;
239
316
  const meta = RULE_REGISTRY.ICON_SVG_STYLE_LEAK;
240
317
  violations.push({
@@ -257,7 +334,8 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
257
334
  const callee = astPath.node.callee;
258
335
 
259
336
  // Timer Discipline
260
- if (t.isIdentifier(callee) && (callee.name === 'setInterval' || callee.name === 'setTimeout')) {
337
+ const isTimerCall = t.isIdentifier(callee) && (callee.name === 'setInterval' || callee.name === 'setTimeout');
338
+ if (isTimerCall) {
261
339
  const args = astPath.node.arguments;
262
340
  const delayArg = args[1];
263
341
 
@@ -277,28 +355,19 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
277
355
  });
278
356
  }
279
357
 
280
- const fnParent = astPath.getFunctionParent();
281
- let hasCleanup = false;
282
- if (fnParent) {
283
- fnParent.traverse({
284
- ReturnStatement(retPath) {
285
- if (retPath.node.argument) {
286
- hasCleanup = true;
287
- }
288
- }
289
- });
290
- }
291
-
292
- if (!hasCleanup) {
358
+ const disposal = classifyTimerDisposal(astPath, teardowns);
359
+ const isUndisposed = disposal === 'held' || disposal === 'unheld';
360
+ if (isUndisposed) {
293
361
  const line = astPath.node.loc?.start.line || 1;
294
362
  const meta = RULE_REGISTRY.TIMER_DISCIPLINE;
363
+ const clearName = callee.name === 'setInterval' ? 'clearInterval' : 'clearTimeout';
295
364
  violations.push({
296
365
  filePath: relativePath,
297
366
  line,
298
367
  column: astPath.node.loc?.start.column || 1,
299
- hazard: `Raw ${callee.name} lacking lifecycle scope disposal`,
368
+ hazard: `Raw ${callee.name} with no ${clearName} for its handle anywhere in this module (Directive 6.C)`,
300
369
  rule: 'TIMER_DISCIPLINE',
301
- severity: meta.severity,
370
+ severity: TIMER_SEVERITY_BY_KIND[callee.name] || meta.severity,
302
371
  pillar: meta.pillar,
303
372
  directive: meta.directive
304
373
  });
@@ -322,24 +391,9 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
322
391
  }
323
392
 
324
393
  // Pillar 6: Orphaned Event Listener
325
- if (t.isMemberExpression(callee) && t.isIdentifier(callee.property, { name: 'addEventListener' })) {
326
- const fnParent = astPath.getFunctionParent();
327
- let hasCleanup = false;
328
- if (fnParent) {
329
- const fnName = fnParent.node.id?.name || '';
330
- if (fnName === 'listen') {
331
- hasCleanup = true;
332
- } else {
333
- fnParent.traverse({
334
- CallExpression(innerCall) {
335
- const innerCallee = innerCall.node.callee;
336
- if (t.isMemberExpression(innerCallee) && t.isIdentifier(innerCallee.property, { name: 'removeEventListener' })) {
337
- hasCleanup = true;
338
- }
339
- }
340
- });
341
- }
342
- }
394
+ const isAddListenerCall = t.isMemberExpression(callee) && t.isIdentifier(callee.property, { name: 'addEventListener' });
395
+ if (isAddListenerCall) {
396
+ const hasCleanup = isListenerDisposed(astPath, teardowns);
343
397
  if (!hasCleanup) {
344
398
  const line = astPath.node.loc?.start.line || 1;
345
399
  const meta = RULE_REGISTRY.LIFECYCLE_ORPHANED_LISTENER;
@@ -378,16 +432,18 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
378
432
 
379
433
  // Pillar 2: Swallowed Exceptions
380
434
  CatchClause(astPath) {
381
- if (isSwallowedCatch(astPath, t)) {
382
- const line = astPath.node.loc?.start.line || 1;
435
+ if (isSwallowedCatch(astPath)) {
383
436
  const meta = RULE_REGISTRY.ERROR_SWALLOWED_EXCEPTION;
437
+ const { severity, binding } = resolveCatchEscalation(astPath);
438
+ const finding = binding
439
+ ? `Swallowed exception leaves "${binding}" unset; it is read after the try (silent undefined propagation)`
440
+ : 'Swallowed exception in catch block without active handling, logging, or ResultTuple';
384
441
  violations.push({
385
442
  filePath: relativePath,
386
- line,
387
- column: astPath.node.loc?.start.column || 1,
388
- hazard: 'Swallowed exception in catch block without active handling, logging, or ResultTuple',
443
+ ...resolveCatchSpan(astPath),
444
+ hazard: `${finding}. Annotate intentional cases with // chemx-allow: best-effort <reason>`,
389
445
  rule: 'ERROR_SWALLOWED_EXCEPTION',
390
- severity: meta.severity,
446
+ severity,
391
447
  pillar: meta.pillar,
392
448
  directive: meta.directive
393
449
  });
@@ -399,7 +455,8 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
399
455
  const isParentOptional = t.isOptionalMemberExpression(astPath.parent) || t.isOptionalCallExpression(astPath.parent);
400
456
  if (!isParentOptional) {
401
457
  const depth = countOptionalChainingDepth(astPath.node, t);
402
- if (depth >= 3) {
458
+ const isDeepChain = depth >= 3;
459
+ if (isDeepChain) {
403
460
  const line = astPath.node.loc?.start.line || 1;
404
461
  const meta = RULE_REGISTRY.DATA_FLOW_OPTIONAL_CHAINING_CHURN;
405
462
  violations.push({
@@ -418,8 +475,10 @@ export const createAstVisitors = ({ relativePath, violations, hookRegistry, conf
418
475
 
419
476
  // Pillar 4: Type Co-location
420
477
  TSTypeLiteral(astPath) {
421
- if (astPath.node.members.length > 3) {
422
- if (!astPath.findParent((p) => p.isTSTypeAliasDeclaration() || p.isTSInterfaceDeclaration())) {
478
+ const isLargeLiteral = astPath.node.members.length > 3;
479
+ if (isLargeLiteral) {
480
+ const isInlinedType = !astPath.findParent((p) => p.isTSTypeAliasDeclaration() || p.isTSInterfaceDeclaration());
481
+ if (isInlinedType) {
423
482
  const line = astPath.node.loc?.start.line || 1;
424
483
  const meta = RULE_REGISTRY.TYPE_COLOCATION;
425
484
  violations.push({
@@ -0,0 +1,26 @@
1
+ // Plain-text audit card for pipes, CI logs and agents: the facts of the dashboard banner
2
+ // without ASCII art, borders or color. Input is buildAuditSummary() output.
3
+
4
+ const formatGate = (gate) => {
5
+ const isUnknown = gate.passing === null;
6
+ if (isUnknown) return 'gate: not evaluated';
7
+ const verdict = gate.passing ? 'pass' : 'fail';
8
+ const basis = gate.basis ? ` (${gate.basis})` : '';
9
+ const note = gate.note ? `: ${gate.note}` : '';
10
+ return `gate: ${verdict}${basis}${note}`;
11
+ };
12
+
13
+ export const formatPlainAuditSummary = (summary) => {
14
+ const { critical, highMedium, low } = summary.hazards;
15
+ const total = critical + highMedium + low;
16
+ const lines = [
17
+ `chemx audit ${summary.scope}: grade ${summary.health.grade} (${summary.health.score}/100), ${total} hazards: ${critical} critical, ${highMedium} high/medium, ${low} low`,
18
+ `${summary.files} files, ${summary.loc.toLocaleString('en-US')} LOC, ~${summary.tokens.estimate.toLocaleString('en-US')} tokens`,
19
+ formatGate(summary.gate)
20
+ ];
21
+ for (const { rule, baseline, current } of summary.gate.regressions ?? []) {
22
+ lines.push(` regression: ${rule} ${baseline} -> ${current}`);
23
+ }
24
+ lines.push('detail: chemx audit --json | chemx audit --unroll | chemx q --hazards');
25
+ return `${lines.join('\n')}\n`;
26
+ };
@@ -1,4 +1,4 @@
1
- import { resolveProjectName, resolveCostFigures } from '../navigator-banner-helpers.js';
1
+ import { resolveProjectName, resolveCostFigures } from './project-figures.js';
2
2
 
3
3
  const countHazards = (violations = []) => {
4
4
  const counts = { critical: 0, highMedium: 0, low: 0 };
@@ -12,6 +12,12 @@ const countHazards = (violations = []) => {
12
12
  return counts;
13
13
  };
14
14
 
15
+ const summarizeCoverageForCard = (coverage) => {
16
+ if (!coverage) return null;
17
+ const { files, astPct, parseErrors, textOnly, isPartial, sfc } = coverage;
18
+ return { files, astPct, parseErrors, textOnly, isPartial, sfcTemplates: sfc?.templatesParsed ?? 0 };
19
+ };
20
+
15
21
  export const buildAuditSummary = (report, { projectRoot, scope }) => {
16
22
  const gate = report.gate ?? {};
17
23
  const summary = {
@@ -20,11 +26,18 @@ export const buildAuditSummary = (report, { projectRoot, scope }) => {
20
26
  files: report.metrics?.scannedFiles ?? 0,
21
27
  loc: report.metrics?.totalLoc ?? 0,
22
28
  tokens: { estimate: report.contextAnalysis?.estimatedTokens ?? 0, savingsPct: report.contextAnalysis?.potentialSavingsPct ?? 0 },
23
- health: { score: report.health.score, grade: report.health.grade, label: report.health.label ?? null },
29
+ health: { score: report.health.score, grade: report.health.grade, label: report.health.label ?? null, density: report.health.density ?? null, scoreModel: report.health.scoreModel ?? null },
24
30
  aiSlop: { score: report.aiSlop?.score ?? 100, grade: report.aiSlop?.grade ?? 'A+' },
25
31
  hazards: countHazards(report.violations),
26
32
  cost: resolveCostFigures(report.contextAnalysis),
27
- gate: { passing: gate.isPassing ?? null, basis: gate.basis ?? null, regressions: (gate.regressions ?? []).slice(0, 5), note: gate.note ?? null }
33
+ gate: {
34
+ passing: gate.isPassing ?? null,
35
+ basis: gate.basis ?? null,
36
+ regressions: (gate.regressions ?? []).slice(0, 5),
37
+ adopted: (gate.adopted ?? []).map((a) => a.rule),
38
+ note: gate.note ?? null
39
+ },
40
+ coverage: summarizeCoverageForCard(report.coverage)
28
41
  };
29
42
  return report.rebaseline ? { ...summary, rebaseline: report.rebaseline } : summary;
30
43
  };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Token-aware mechanical fixes for one file's content.
3
+ *
4
+ * Only comments are edited, located by a real tokenizer (see comment-ranges.js): string
5
+ * literals, template literals, JSX text and Vue/Svelte templates are never touched. A comment
6
+ * flagged as AI residue is removed whole (never just its first line). Leaked Markdown fences
7
+ * are removed only as the first/last non-blank line of a JS/TS file, and only when the result
8
+ * parses (a fence pair can parse as tagged templates, so the original is not required to fail). setTimeout(fn, 0) is reported as a suggestion, never rewritten.
9
+ */
10
+ import { findSourceRanges } from '../comment-ranges.js';
11
+ import { parseSource, langForPath } from '../source-parse.js';
12
+ import { lineOfIndex } from '../literal-replace.js';
13
+
14
+ const RESIDUE_PATTERNS = [
15
+ ['hope this', 'helps'].join(' '),
16
+ ['feel free', 'to tweak'].join(' '),
17
+ ['let me know', 'if you need'].join(' '),
18
+ ['as an ai', 'language model'].join(' ')
19
+ ];
20
+ const RESIDUE_REGEX = new RegExp(`\\b(?:${RESIDUE_PATTERNS.join('|')})\\b`, 'i');
21
+ const PREAMBLE_REGEX = /\b(?:here(?:'s| is) the (?:complete|updated|refactored|full) (?:code|implementation|file|component|version))\b/i;
22
+ const TRUNCATION_REGEX = /^(?:\/\/|\/\*)\s*\.\.\.\s*(?:existing|rest of|remaining)\s+(?:code|implementation|logic|imports)/i;
23
+ const FENCE_LINE_REGEX = /^\s*```(?:typescript|javascript|tsx|jsx|vue|js|ts|html|css|scss)?\s*$/;
24
+ const TIMER_REGEX = /setTimeout\([^,]+,\s*0\)/g;
25
+ const EM_DASH = String.fromCharCode(0x2014);
26
+
27
+ const insideAny = (ranges, index) => ranges.some((r) => index >= r.start && index < r.end);
28
+
29
+ const stripEdgeFences = (content, filePath, fixes) => {
30
+ const isScript = Boolean(langForPath(filePath));
31
+ if (!isScript) return content;
32
+ const lines = content.split('\n');
33
+ const nonBlank = lines.map((l, i) => (l.trim() ? i : -1)).filter((i) => i >= 0);
34
+ const edges = [nonBlank[0], nonBlank[nonBlank.length - 1]].filter((i) => i !== undefined && FENCE_LINE_REGEX.test(lines[i]));
35
+ const fenceLines = [...new Set(edges)];
36
+ const candidate = lines.filter((_, i) => !fenceLines.includes(i)).join('\n');
37
+ const isRepaired = fenceLines.length > 0 && parseSource(candidate, filePath).ok;
38
+ if (!isRepaired) return content;
39
+ fenceLines.forEach((i) => fixes.push({ line: i + 1, rule: 'AI_SLOP_CONVERSATIONAL_ARTIFACT', action: 'Removed leaked markdown code fence' }));
40
+ return candidate;
41
+ };
42
+
43
+ const wholeCommentSpan = (content, comment) => {
44
+ const lineStart = content.lastIndexOf('\n', comment.start - 1) + 1;
45
+ const nextNewline = content.indexOf('\n', comment.end);
46
+ const lineEnd = nextNewline === -1 ? content.length : nextNewline;
47
+ const isAloneOnLines = content.slice(lineStart, comment.start).trim() === '' && content.slice(comment.end, lineEnd).trim() === '';
48
+ const isLastLine = nextNewline === -1;
49
+ const lineSpanStart = isLastLine && lineStart > 0 ? lineStart - 1 : lineStart;
50
+ if (isAloneOnLines) return { start: lineSpanStart, end: isLastLine ? lineEnd : lineEnd + 1 };
51
+ let start = comment.start;
52
+ while (start > lineStart && /[ \t]/.test(content[start - 1])) start--;
53
+ return { start, end: comment.end };
54
+ };
55
+
56
+ const classifyComment = (text, shouldFix) => {
57
+ const isResidue = PREAMBLE_REGEX.test(text) || RESIDUE_REGEX.test(text);
58
+ const shouldRemoveResidue = isResidue && shouldFix('AI_SLOP_CONVERSATIONAL_ARTIFACT');
59
+ if (shouldRemoveResidue) return { rule: 'AI_SLOP_CONVERSATIONAL_ARTIFACT', action: 'Removed AI conversational residue comment' };
60
+ const isPlaceholder = TRUNCATION_REGEX.test(text.trim());
61
+ const shouldRemovePlaceholder = isPlaceholder && shouldFix('AI_SLOP_LAZY_PLACEHOLDER');
62
+ if (shouldRemovePlaceholder) return { rule: 'AI_SLOP_LAZY_PLACEHOLDER', action: 'Removed lazy AI truncation placeholder comment' };
63
+ return null;
64
+ };
65
+
66
+ const commentEdits = (content, ranges, shouldFix, fixes) => {
67
+ const edits = [];
68
+ for (const comment of [...ranges.comments].sort((a, b) => a.start - b.start)) {
69
+ const text = content.slice(comment.start, comment.end);
70
+ const removal = classifyComment(text, shouldFix);
71
+ const isRemoved = Boolean(removal);
72
+ if (isRemoved) {
73
+ edits.push({ ...wholeCommentSpan(content, comment), text: '' });
74
+ fixes.push({ line: lineOfIndex(content, comment.start), ...removal });
75
+ continue;
76
+ }
77
+ const hasEmDash = text.includes(EM_DASH) && shouldFix('TYPOGRAPHY_EM_DASH');
78
+ if (!hasEmDash) continue;
79
+ edits.push({ start: comment.start, end: comment.end, text: text.split(EM_DASH).join('-') });
80
+ const dashLines = new Set([...text.matchAll(new RegExp(EM_DASH, 'g'))].map((m) => lineOfIndex(content, comment.start + m.index)));
81
+ dashLines.forEach((line) => fixes.push({ line, rule: 'TYPOGRAPHY_EM_DASH', action: 'Replaced em dash with standard hyphen in a comment' }));
82
+ }
83
+ return edits;
84
+ };
85
+
86
+ const timerSuggestions = (content, ranges, shouldFix) => {
87
+ const isTimerRuleOn = shouldFix('MACRO_TASK_OVER_MICRO_TASK');
88
+ if (!isTimerRuleOn) return [];
89
+ const skip = [...ranges.comments, ...ranges.literals];
90
+ return [...content.matchAll(TIMER_REGEX)]
91
+ .filter((m) => insideAny(ranges.codeRegions, m.index) && !insideAny(skip, m.index))
92
+ .map((m) => ({
93
+ line: lineOfIndex(content, m.index),
94
+ rule: 'MACRO_TASK_OVER_MICRO_TASK',
95
+ suggestion: 'Consider queueMicrotask(fn) if the timer id is never cleared (not applied: queueMicrotask returns undefined, so clearTimeout(id) would stop working).'
96
+ }));
97
+ };
98
+
99
+ /**
100
+ * @param {string} content File content.
101
+ * @param {object} [options] { filePath = 'file.ts', rules }
102
+ * @returns {{ fixedContent: string, fixes: object[], suggestions: object[], skipped: string|null }}
103
+ */
104
+ export const autofixContent = (content, options = {}) => {
105
+ const filePath = options.filePath || 'file.ts';
106
+ const allowedRules = options.rules ? new Set(options.rules) : null;
107
+ const shouldFix = (rule) => !allowedRules || allowedRules.has(rule);
108
+ const fixes = [];
109
+
110
+ const fenceShouldRun = shouldFix('AI_SLOP_CONVERSATIONAL_ARTIFACT');
111
+ const base = fenceShouldRun ? stripEdgeFences(content, filePath, fixes) : content;
112
+ const ranges = findSourceRanges(base, filePath);
113
+ const isTokenized = Boolean(ranges);
114
+ if (!isTokenized) return { fixedContent: base, fixes, suggestions: [], skipped: 'no tokenizer for this file type, or it does not parse' };
115
+
116
+ const edits = commentEdits(base, ranges, shouldFix, fixes);
117
+ let fixedContent = base;
118
+ for (const edit of edits.sort((a, b) => b.start - a.start)) {
119
+ fixedContent = fixedContent.slice(0, edit.start) + edit.text + fixedContent.slice(edit.end);
120
+ }
121
+ return { fixedContent, fixes, suggestions: timerSuggestions(base, ranges, shouldFix), skipped: null };
122
+ };