@event4u/agent-config 14.10.0 → 14.11.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 (399) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +335 -0
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/design.md +88 -0
  7. package/dist/agent-src/commands/project/analyze.md +47 -0
  8. package/dist/agent-src/commands/review/changes.md +67 -6
  9. package/dist/agent-src/commands/roadmap/create.md +49 -0
  10. package/dist/agent-src/commands/roadmap/next.md +27 -11
  11. package/dist/agent-src/commands/roadmap/process-full.md +14 -0
  12. package/dist/agent-src/commands/video/from-script.md +60 -3
  13. package/dist/agent-src/commands/video/stitch.md +14 -0
  14. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
  16. package/dist/agent-src/rules/active-remediation.md +3 -3
  17. package/dist/agent-src/rules/code-provenance.md +2 -2
  18. package/dist/agent-src/rules/context-hygiene.md +13 -33
  19. package/dist/agent-src/rules/council-availability.md +2 -2
  20. package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
  21. package/dist/agent-src/rules/design-fidelity.md +18 -0
  22. package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
  23. package/dist/agent-src/rules/evaluator-independence.md +56 -44
  24. package/dist/agent-src/rules/fix-what-you-see.md +9 -1
  25. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
  26. package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
  27. package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
  28. package/dist/agent-src/rules/playbook-precedence.md +105 -0
  29. package/dist/agent-src/rules/recurring-criticism.md +3 -1
  30. package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
  31. package/dist/agent-src/rules/scale-discipline.md +24 -0
  32. package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
  33. package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
  34. package/dist/agent-src/rules/ui-audit-gate.md +27 -15
  35. package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
  36. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  37. package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
  38. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
  39. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
  40. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
  41. package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
  42. package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
  43. package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
  44. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  45. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  46. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  47. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  52. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  53. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  54. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  55. package/dist/agent-src/skills/condense-memory/SKILL.md +3 -0
  56. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  57. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +3 -0
  58. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  59. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  60. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  61. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  62. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  63. package/dist/agent-src/skills/design-review/SKILL.md +79 -1
  64. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  65. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  66. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  67. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  68. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  69. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  70. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  71. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  72. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  73. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  74. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  75. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  76. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  77. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  78. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  79. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  80. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  81. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  82. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  83. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  84. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  85. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  86. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  87. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  88. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  89. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  90. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  91. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  92. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  93. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  94. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  95. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  96. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  97. package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -0
  98. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  99. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  100. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  101. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  102. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  103. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  104. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  105. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  106. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  107. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  108. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  109. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  110. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  111. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  112. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  113. package/dist/agent-src/skills/receiving-code-review/SKILL.md +2 -2
  114. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  115. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  116. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  117. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  118. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  119. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  120. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  121. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  122. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  123. package/dist/agent-src/skills/secrets-management/SKILL.md +4 -0
  124. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  125. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  126. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  127. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  128. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  129. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  130. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  131. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  132. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  133. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  134. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  135. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  136. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  137. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  138. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  139. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  140. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  141. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  142. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  143. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  144. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  145. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  146. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  147. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  148. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  149. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  150. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  151. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  152. package/dist/agent-src/templates/playbook.md +75 -0
  153. package/dist/agent-src/templates/roadmaps.md +37 -0
  154. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  155. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  156. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  157. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  158. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  159. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  160. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  161. package/dist/cli/commands/uiAudit.js +221 -0
  162. package/dist/cli/commands/uiAudit.js.map +1 -0
  163. package/dist/cli/commands/uiRender.js +206 -0
  164. package/dist/cli/commands/uiRender.js.map +1 -0
  165. package/dist/cli/main.js +22 -1
  166. package/dist/cli/main.js.map +1 -1
  167. package/dist/cli/mcp/content.js +64 -1
  168. package/dist/cli/mcp/content.js.map +1 -1
  169. package/dist/cli/mcp/dispatch.js +175 -10
  170. package/dist/cli/mcp/dispatch.js.map +1 -1
  171. package/dist/cli/registry.js +3 -0
  172. package/dist/cli/registry.js.map +1 -1
  173. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  174. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  175. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  176. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  177. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  178. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  179. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  180. package/dist/cli-delegate/cmd_conformance.js +3 -3
  181. package/dist/cli-delegate/cmd_doctor.js +3 -3
  182. package/dist/cli-delegate/cmd_export.js +4 -4
  183. package/dist/cli-delegate/cmd_handoff.js +1 -1
  184. package/dist/cli-delegate/cmd_preflight.js +2 -2
  185. package/dist/cli-delegate/cmd_prune.js +4 -4
  186. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  187. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  188. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  189. package/dist/cli-delegate/cmd_sync.js +4 -4
  190. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  191. package/dist/cli-delegate/cmd_validate.js +4 -4
  192. package/dist/discovery/deprecation-report.md +1 -1
  193. package/dist/discovery/discovery-manifest.json +359 -124
  194. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  195. package/dist/discovery/discovery-manifest.summary.md +4 -4
  196. package/dist/discovery/dormancy-report.md +1 -1
  197. package/dist/discovery/orphan-report.md +1 -1
  198. package/dist/discovery/packs.json +18 -8
  199. package/dist/discovery/trust-report.md +3 -3
  200. package/dist/discovery/workspaces.json +16 -6
  201. package/dist/hooks/dispatch.js +2337 -1436
  202. package/dist/install/install.mjs +795 -708
  203. package/dist/mcp/registry-manifest.json +3 -3
  204. package/dist/mcp/server.json +2 -2
  205. package/dist/mcp/server.mjs +1685 -1250
  206. package/dist/router.json +1 -1
  207. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  208. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  209. package/dist/scripts/_lib/scoped_projection.js +84 -0
  210. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  211. package/dist/scripts/_lib/ui_surface.js +89 -0
  212. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  213. package/dist/scripts/condense.js +6 -10
  214. package/dist/scripts/condense.js.map +1 -1
  215. package/dist/scripts/project_thin_rules.js +40 -2
  216. package/dist/scripts/project_thin_rules.js.map +1 -1
  217. package/dist/server/schemas/settings.js +3 -0
  218. package/dist/server/schemas/settings.js.map +1 -1
  219. package/dist/shared/skillRanking.js +159 -0
  220. package/dist/shared/skillRanking.js.map +1 -0
  221. package/docs/contracts/provider-lifecycle.md +47 -18
  222. package/docs/contracts/settings-classes.md +14 -4
  223. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  224. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  225. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  226. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  227. package/docs/guidelines/design-antipatterns.md +4 -2
  228. package/docs/guidelines/design-asset-discipline.md +38 -0
  229. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  230. package/docs/guidelines/design-handover-extraction.md +35 -5
  231. package/docs/guidelines/design-modes.md +41 -1
  232. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  233. package/llms.txt +50 -39
  234. package/package.json +1 -1
  235. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  236. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  237. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  238. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  239. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  240. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  241. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  242. package/src/config/agent-settings.template.yml +28 -0
  243. package/src/config/assurance-capability-registry.json +223 -0
  244. package/src/config/assurance-threshold-budget.json +65 -0
  245. package/src/config/ci-local-parity.yml +14 -0
  246. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  247. package/src/config/dispatch-economy-metrics.json +4 -2
  248. package/src/config/evaluator-budgets.json +14 -14
  249. package/src/config/gate-coverage.yml +207 -8
  250. package/src/config/gate-violation-baselines.json +76 -72
  251. package/src/config/hook-latency-budget.json +1 -0
  252. package/src/config/hook-token-budget.json +137 -125
  253. package/src/config/pack-size-budget.json +4 -2
  254. package/src/config/quorum-attendance-budget.json +12 -4
  255. package/src/config/rule-activation-census.json +7 -3
  256. package/src/config/rule-enforcement-baseline.json +0 -1
  257. package/src/config/rule-stub-ceilings.json +112 -91
  258. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  259. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  260. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  261. package/src/scripts/_dispatch.bash +17 -0
  262. package/src/scripts/_lib/as_of.ts +412 -0
  263. package/src/scripts/_lib/exec_evidence.ts +7 -0
  264. package/src/scripts/_lib/hook_settings.ts +33 -0
  265. package/src/scripts/_lib/host_listing_model.ts +192 -0
  266. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  267. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  268. package/src/scripts/_lib/review_independence.ts +62 -10
  269. package/src/scripts/_lib/rule_injection.ts +239 -0
  270. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  271. package/src/scripts/_lib/scoped_projection.ts +96 -0
  272. package/src/scripts/_lib/session_register.ts +44 -2
  273. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  274. package/src/scripts/_lib/transcript_reads.ts +207 -0
  275. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  276. package/src/scripts/_lib/ui_authority.ts +343 -0
  277. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  278. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  279. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  280. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  281. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  282. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  283. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  284. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  285. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  286. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  287. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  288. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  289. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  290. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  291. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  292. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  293. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  294. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  295. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  296. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  297. package/src/scripts/ai-video/stitch.sh +191 -10
  298. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  299. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  300. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  301. package/src/scripts/ai_council/clients.ts +3 -2
  302. package/src/scripts/ai_council/consensus.ts +63 -6
  303. package/src/scripts/ai_council/events_log.ts +46 -1
  304. package/src/scripts/ai_council/orchestrator.ts +103 -108
  305. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  306. package/src/scripts/ai_council/prompts.ts +12 -5
  307. package/src/scripts/ai_council/quorum.ts +82 -1
  308. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  309. package/src/scripts/ai_council/stance_tally.ts +22 -0
  310. package/src/scripts/bench_hook_latency.ts +62 -0
  311. package/src/scripts/build_proof.ts +12 -1
  312. package/src/scripts/capture_skill_catalogue.ts +21 -0
  313. package/src/scripts/check_always_budget.ts +2 -1
  314. package/src/scripts/check_augmentignore.ts +2 -1
  315. package/src/scripts/check_beta_review_markers.ts +2 -1
  316. package/src/scripts/check_composite_arming.ts +364 -0
  317. package/src/scripts/check_corpus_staleness.ts +2 -1
  318. package/src/scripts/check_council_pin_staleness.ts +3 -1
  319. package/src/scripts/check_enforcement_coverage.ts +116 -3
  320. package/src/scripts/check_enforcement_denominator.ts +365 -0
  321. package/src/scripts/check_gate_coverage.ts +2 -1
  322. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  323. package/src/scripts/check_knowledge_cards.ts +2 -1
  324. package/src/scripts/check_knowledge_pages.ts +3 -2
  325. package/src/scripts/check_memory.ts +2 -1
  326. package/src/scripts/check_no_automerge_key.ts +287 -0
  327. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  328. package/src/scripts/check_proposal.ts +2 -1
  329. package/src/scripts/check_reach_staleness.ts +2 -1
  330. package/src/scripts/check_release_adjacent_health.ts +2 -1
  331. package/src/scripts/check_release_pr_shape.ts +5 -0
  332. package/src/scripts/check_review_schema.ts +34 -1
  333. package/src/scripts/check_roadmap_trackable.ts +48 -3
  334. package/src/scripts/check_source_size_budget.ts +2 -1
  335. package/src/scripts/check_standing_payload_delta.ts +505 -0
  336. package/src/scripts/check_trigger_evals.ts +2 -1
  337. package/src/scripts/classify_change_risk.ts +281 -0
  338. package/src/scripts/compute_skill_tiers.ts +186 -0
  339. package/src/scripts/condense.ts +8 -13
  340. package/src/scripts/council_attendance_metrics.ts +183 -0
  341. package/src/scripts/council_cli.ts +10 -8
  342. package/src/scripts/council_parse_rate.ts +116 -0
  343. package/src/scripts/derive_playbooks.ts +406 -0
  344. package/src/scripts/design_slop_rules.test.ts +7 -0
  345. package/src/scripts/design_slop_rules.ts +56 -2
  346. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  347. package/src/scripts/frontend_corpus_hash.ts +116 -0
  348. package/src/scripts/grade_target_readiness.ts +478 -0
  349. package/src/scripts/hook_manifest.json +1 -1
  350. package/src/scripts/hook_manifest.yaml +97 -16
  351. package/src/scripts/hooks/concern_registry.ts +4 -0
  352. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  353. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  354. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  355. package/src/scripts/hot_context_hook.ts +74 -7
  356. package/src/scripts/install.ts +40 -89
  357. package/src/scripts/lint_adapter_tier.ts +663 -0
  358. package/src/scripts/lint_archived_skills.ts +93 -0
  359. package/src/scripts/lint_budget_ownership.ts +2 -1
  360. package/src/scripts/lint_deterministic_time.ts +470 -0
  361. package/src/scripts/lint_framework_leakage.ts +5 -0
  362. package/src/scripts/lint_handoffs.ts +525 -1
  363. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  364. package/src/scripts/lint_marketplace.ts +88 -0
  365. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  366. package/src/scripts/lint_one_off_age.ts +6 -1
  367. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  368. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  369. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  370. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  371. package/src/scripts/lint_spawn_payload.ts +42 -5
  372. package/src/scripts/lint_symptom_intake.ts +2 -1
  373. package/src/scripts/mcp_parity_smoke.ts +30 -4
  374. package/src/scripts/mcp_render.ts +66 -1
  375. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  376. package/src/scripts/measure_risk_class_drift.ts +140 -0
  377. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  378. package/src/scripts/media/lib/adapter-common.sh +211 -1
  379. package/src/scripts/media/lib/adapter-contract.md +74 -6
  380. package/src/scripts/model_rule_injection.ts +907 -0
  381. package/src/scripts/project_thin_rules.ts +45 -2
  382. package/src/scripts/release.ts +66 -860
  383. package/src/scripts/release_drill.ts +59 -3
  384. package/src/scripts/release_env.ts +298 -0
  385. package/src/scripts/release_publication.ts +720 -0
  386. package/src/scripts/roadmap_context.ts +778 -0
  387. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  388. package/src/scripts/schemas/rule.schema.json +24 -2
  389. package/src/scripts/schemas/skill.schema.json +39 -0
  390. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  391. package/src/scripts/session_register_hook.ts +29 -3
  392. package/src/scripts/sessions_cli.ts +67 -3
  393. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  394. package/src/scripts/token_sink_report.ts +239 -0
  395. package/src/scripts/trigger_coverage.ts +122 -3
  396. package/src/scripts/update_counts.ts +7 -3
  397. package/src/scripts/validate_frontmatter.ts +66 -0
  398. package/src/server/schemas/settings.ts +5 -0
  399. package/src/shared/skillRanking.ts +183 -0
@@ -42,10 +42,17 @@
42
42
  * label is recoverable (audit catches it, user can override), but a
43
43
  * crash mid-dispatch is not.
44
44
  *
45
- * Root manifests only, by design: a monorepo caller passes the workspace
46
- * root that carries the manifest it cares about. Note the consequence —
47
- * a caller that always passes the repository root gets `plain` for a
48
- * monorepo, so the caller owns scope selection, not the detector.
45
+ * Workspace-aware since `road-to-monorepo-scope-and-detection`: a repository
46
+ * whose root declares workspaces (`package.json#workspaces`, a
47
+ * `pnpm-workspace.yaml`, a `turbo.json`, an `nx.json`, a `lerna.json`) and
48
+ * whose root manifest is not itself an application is descended into, and the
49
+ * workspace that was chosen is reported as {@link StackResult.scope_root}.
50
+ *
51
+ * The detector still never guesses **between** workspaces: two workspaces on
52
+ * mutually exclusive reactivity layers produce `unknown` plus both names, the
53
+ * same refusal a single conflicted manifest produces. What it no longer does is
54
+ * report `plain` for a monorepo — the previous behaviour, and the reason the
55
+ * repository was handed the generic directive.
49
56
  */
50
57
  import * as fs from 'node:fs';
51
58
  import * as path from 'node:path';
@@ -90,6 +97,8 @@ export const DEFAULT_STACK = 'plain';
90
97
  export const UNSUPPORTED_STACK = 'unknown';
91
98
 
92
99
  const _SHADCN_RADIX_PREFIX = '@radix-ui/';
100
+ /** The unified Radix package (February 2026) that replaced the `@radix-ui/*` scope. */
101
+ const _SHADCN_RADIX_UNIFIED = 'radix-ui';
93
102
  const _SHADCN_PACKAGE_NAMES: ReadonlySet<string> = new Set(['shadcn-ui', 'shadcn']);
94
103
  const _FLUX_PACKAGE = 'livewire/flux';
95
104
  const _LIVEWIRE_PACKAGE = 'livewire/livewire';
@@ -193,16 +202,36 @@ export class StackResult {
193
202
  */
194
203
  readonly ambiguity: ReadonlyArray<string>;
195
204
 
205
+ /**
206
+ * The workspace this result describes, **relative to the project root**.
207
+ *
208
+ * `''` means the project root itself — a non-monorepo, or a monorepo whose
209
+ * scope could not be resolved. Empty rather than `'.'` because that is
210
+ * already what `directives/ui/scaffold.ts` defaults to when the field is
211
+ * absent, so every non-monorepo project keeps byte-identical behaviour.
212
+ *
213
+ * Relative, not absolute, for two reasons that are both load-bearing. The
214
+ * field is serialized into `state.stack` and read back on a later run,
215
+ * possibly from a different absolute location (a container, a worktree, a
216
+ * moved checkout). And its one existing consumer — the playbook scope match
217
+ * at `scaffold.ts` — compares it against a playbook's declared `scope`,
218
+ * which is a repo-relative workspace path like `packages/ui`; an absolute
219
+ * path would silently match nothing but `scope: repo`.
220
+ */
221
+ readonly scope_root: string;
222
+
196
223
  constructor(args: {
197
224
  frontend: string;
198
225
  mtime: number;
199
226
  axes?: StackAxes;
200
227
  ambiguity?: ReadonlyArray<string>;
228
+ scope_root?: string;
201
229
  }) {
202
230
  this.frontend = args.frontend;
203
231
  this.mtime = args.mtime;
204
232
  this.axes = args.axes ?? _EMPTY_AXES;
205
233
  this.ambiguity = args.ambiguity ?? [];
234
+ this.scope_root = args.scope_root ?? '';
206
235
  }
207
236
  }
208
237
 
@@ -241,6 +270,24 @@ export function detect_stack(project_root: string): StackResult {
241
270
  return _r(UNSUPPORTED_STACK);
242
271
  }
243
272
 
273
+ // Workspace root WITH a root manifest — the shape this detector was written
274
+ // for and never reached. The old guard was `mtime === 0.0`, i.e. "no
275
+ // manifest at the repository root at all", but a root `package.json` is the
276
+ // defining file of an npm/pnpm/yarn/bun workspace, so the branch was skipped
277
+ // for every repository it was meant to serve.
278
+ //
279
+ // The second half of the predicate is what keeps a root application out:
280
+ // a root that ships `react` in `dependencies` IS the app and must not be
281
+ // abandoned for one of its own packages. A root that ships `react` only in
282
+ // `devDependencies` beside a workspace declaration is shared test tooling,
283
+ // which is why the check reads runtime sections only.
284
+ if (_is_workspace_root(project_root, pkg) && !_root_carries_frontend(composer, pkg)) {
285
+ const scoped = _resolve_workspace_scope(project_root, mtime, axes);
286
+ if (scoped !== null) {
287
+ return scoped;
288
+ }
289
+ }
290
+
244
291
  if (_is_blade_livewire_flux(composer)) {
245
292
  return _r('blade-livewire-flux');
246
293
  }
@@ -279,39 +326,16 @@ export function detect_stack(project_root: string): StackResult {
279
326
  return _r(UNSUPPORTED_STACK);
280
327
  }
281
328
 
282
- // Monorepo: no manifest at the root, but one sits in a workspace directory.
283
- // That is a wrong-scope call, not a plain project — the caller owns scope
284
- // selection, so say so instead of labelling the whole repo `plain` and
285
- // dispatching generic tooling at it.
329
+ // No manifest at the repository root, but one sits in a workspace
330
+ // directory. Not a monorepo in the declarative sense — nothing declares the
331
+ // workspaces — but a scaffold in progress, and descending is still right.
286
332
  //
287
333
  // A repo with no manifest ANYWHERE stays `plain`: that is greenfield, and
288
334
  // the scaffold path depends on it.
289
335
  if (mtime === 0.0) {
290
- const roots = _nested_frontend_roots(project_root);
291
- if (roots.length === 1) {
292
- // Unambiguous scope: descend once and keep the workspace's own
293
- // axes. Refusing here would punish the common monorepo shape for a
294
- // scope decision the layout already makes.
295
- const inner = detect_stack(roots[0] as string);
296
- return new StackResult({
297
- frontend: inner.frontend,
298
- mtime: inner.mtime,
299
- axes: inner.axes,
300
- ambiguity: inner.ambiguity,
301
- });
302
- }
303
- if (roots.length > 1) {
304
- // Several frontend roots: the caller owns scope selection, so name
305
- // them and let the step ask. Picking the first would be the silent
306
- // guess this detector exists to avoid.
307
- return new StackResult({
308
- frontend: UNSUPPORTED_STACK,
309
- mtime,
310
- axes,
311
- ambiguity: [
312
- `workspace roots: ${roots.map((r) => path.basename(r)).join(' + ')}`,
313
- ],
314
- });
336
+ const scoped = _resolve_workspace_scope(project_root, mtime, axes);
337
+ if (scoped !== null) {
338
+ return scoped;
315
339
  }
316
340
  }
317
341
 
@@ -356,6 +380,13 @@ const _CACHE_KEY_FILES: ReadonlyArray<string> = [
356
380
  'nuxt.config.ts',
357
381
  'nuxt.config.js',
358
382
  'astro.config.mjs',
383
+ // Workspace declarations, for exactly the reason `components.json` is here:
384
+ // adding a `pnpm-workspace.yaml` changes which manifest the result is
385
+ // computed from, while leaving both root manifests untouched — and the
386
+ // cache would otherwise serve the pre-workspace answer indefinitely.
387
+ 'pnpm-workspace.yaml',
388
+ 'turbo.json',
389
+ 'nx.json',
359
390
  ];
360
391
 
361
392
  /**
@@ -469,13 +500,35 @@ const _AXIS_SIGNALS: ReadonlyArray<{
469
500
 
470
501
  // component_lib — marker file beats dependency
471
502
  { axis: 'component_lib', value: 'shadcn', npm: ['shadcn-ui', 'shadcn'], files: ['components.json'] },
472
- { axis: 'component_lib', value: 'radix', npm_prefix: ['@radix-ui/'] },
503
+ // `radix-ui` (no slash) is the unified package shadcn's `new-york` style
504
+ // moved to in February 2026; the scoped `@radix-ui/*` packages are the
505
+ // pre-unification shape and both are still in the wild.
506
+ { axis: 'component_lib', value: 'radix', npm: ['radix-ui'], npm_prefix: ['@radix-ui/'] },
507
+ // Base UI is the other primitive layer shadcn accepts. `@base-ui/react` is
508
+ // the name since Base UI 1.0; `@base-ui-components/react` is what it was
509
+ // called before the rename, and projects pinned to it still exist.
510
+ { axis: 'component_lib', value: 'base-ui', npm: ['@base-ui/react', '@base-ui-components/react'] },
473
511
  { axis: 'component_lib', value: 'flux', composer: ['livewire/flux'] },
474
512
  { axis: 'component_lib', value: 'nuxt-ui', npm: ['@nuxt/ui'] },
475
513
  { axis: 'component_lib', value: 'vuetify', npm: ['vuetify'] },
476
514
  { axis: 'component_lib', value: 'mui', npm: ['@mui/material'] },
477
515
 
478
- // css
516
+ // css — the major is a marker-file question, never a version string.
517
+ // v4 dropped `tailwind.config.*` in favour of CSS-first configuration, so
518
+ // the two majors are told apart by which artefact exists, and a project
519
+ // carrying neither marker stays on the undifferentiated `tailwind` value
520
+ // rather than being guessed into a major.
521
+ { axis: 'css', value: 'tailwind-v4', npm: ['@tailwindcss/vite', '@tailwindcss/postcss'] },
522
+ {
523
+ axis: 'css',
524
+ value: 'tailwind-v3',
525
+ files: [
526
+ 'tailwind.config.js',
527
+ 'tailwind.config.cjs',
528
+ 'tailwind.config.mjs',
529
+ 'tailwind.config.ts',
530
+ ],
531
+ },
479
532
  { axis: 'css', value: 'tailwind', npm: ['tailwindcss'] },
480
533
  { axis: 'css', value: 'bootstrap', npm: ['bootstrap'] },
481
534
  ];
@@ -564,6 +617,238 @@ function _detect_ambiguity(composer: Manifest, pkg: Manifest): string[] {
564
617
  return found;
565
618
  }
566
619
 
620
+ /** Files that declare a workspace root even when `package.json` does not. */
621
+ const _WORKSPACE_MARKER_FILES: ReadonlyArray<string> = [
622
+ 'pnpm-workspace.yaml',
623
+ 'pnpm-workspace.yml',
624
+ 'turbo.json',
625
+ 'nx.json',
626
+ 'lerna.json',
627
+ ];
628
+
629
+ /**
630
+ * True when `project_root` declares itself the root of a workspace.
631
+ *
632
+ * Declaration, not convention: the presence of an `apps/` directory is a guess,
633
+ * whereas `workspaces`, `pnpm-workspace.yaml`, `turbo.json`, `nx.json` and
634
+ * `lerna.json` are the repository saying so itself. The conventional-directory
635
+ * scan still exists one layer down in {@link _nested_frontend_roots}, as a
636
+ * fallback for the roots this predicate has already admitted.
637
+ */
638
+ function _is_workspace_root(project_root: string, pkg: Manifest): boolean {
639
+ if (_workspace_globs(project_root, pkg).length > 0) {
640
+ return true;
641
+ }
642
+ return _WORKSPACE_MARKER_FILES.some((f) => _is_file(path.join(project_root, f)));
643
+ }
644
+
645
+ /**
646
+ * The declared workspace globs, from every declarative source.
647
+ *
648
+ * Three shapes, all real: npm/bun/yarn-modern's `workspaces: [...]`, yarn
649
+ * classic's `workspaces: { packages: [...] }`, and pnpm's `packages:` list in
650
+ * `pnpm-workspace.yaml` — the last of which a pnpm repository frequently has
651
+ * *instead of* any `workspaces` key at all.
652
+ */
653
+ function _workspace_globs(project_root: string, pkg: Manifest): string[] {
654
+ const out: string[] = [];
655
+ const declared = pkg['workspaces'];
656
+ if (Array.isArray(declared)) {
657
+ out.push(...declared.filter((g): g is string => typeof g === 'string'));
658
+ } else if (_isDict(declared) && Array.isArray(declared['packages'])) {
659
+ out.push(
660
+ ...(declared['packages'] as unknown[]).filter((g): g is string => typeof g === 'string'),
661
+ );
662
+ }
663
+ out.push(..._pnpm_workspace_globs(project_root));
664
+ return out;
665
+ }
666
+
667
+ /** Read the `packages:` globs out of `pnpm-workspace.yaml`. */
668
+ function _pnpm_workspace_globs(project_root: string): string[] {
669
+ for (const name of ['pnpm-workspace.yaml', 'pnpm-workspace.yml']) {
670
+ const p = path.join(project_root, name);
671
+ if (!_is_file(p)) {
672
+ continue;
673
+ }
674
+ let text: string;
675
+ try {
676
+ text = fs.readFileSync(p, { encoding: 'utf-8' });
677
+ } catch {
678
+ return [];
679
+ }
680
+ return _parse_pnpm_packages(text);
681
+ }
682
+ return [];
683
+ }
684
+
685
+ /**
686
+ * Extract `packages:` from a `pnpm-workspace.yaml` body.
687
+ *
688
+ * Prefers the `yaml` package, lazily required exactly as
689
+ * `_lib/agent_settings.ts` does, so an exotic file (anchors, flow sequences,
690
+ * block scalars) parses properly. Falls back to a line scanner when `yaml`
691
+ * cannot be resolved — this module ships into consumer repositories that carry
692
+ * their own `node_modules`, and silently reading no globs there would
693
+ * reintroduce the very defect this branch exists to fix, for precisely the
694
+ * pnpm users it was written for.
695
+ */
696
+ function _parse_pnpm_packages(text: string): string[] {
697
+ try {
698
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
699
+ const YAML = require('yaml') as { parse(input: string): unknown };
700
+ const data = YAML.parse(text);
701
+ if (_isDict(data) && Array.isArray(data['packages'])) {
702
+ return (data['packages'] as unknown[]).filter((g): g is string => typeof g === 'string');
703
+ }
704
+ return [];
705
+ } catch {
706
+ // `yaml` unresolvable, or the document did not parse — scan instead.
707
+ }
708
+ return _scan_yaml_string_list(text, 'packages');
709
+ }
710
+
711
+ /**
712
+ * Read a top-level block sequence of strings out of a YAML document.
713
+ *
714
+ * Deliberately not a YAML parser. pnpm's own schema makes `packages` a flat
715
+ * list of glob strings, so the shape is fixed and a scanner cannot silently
716
+ * mis-read it the way a partial parser could.
717
+ */
718
+ function _scan_yaml_string_list(text: string, key: string): string[] {
719
+ const out: string[] = [];
720
+ let inside = false;
721
+ for (const raw of text.split('\n')) {
722
+ const line = raw.replace(/\s+$/, '');
723
+ if (line === '' || line.trimStart().startsWith('#')) {
724
+ continue;
725
+ }
726
+ if (!inside) {
727
+ inside = line === `${key}:`;
728
+ continue;
729
+ }
730
+ const item = /^\s*-\s*(.+)$/.exec(line);
731
+ if (item === null) {
732
+ break; // the block ended
733
+ }
734
+ const value = (item[1] ?? '').trim().replace(/^['"]|['"]$/g, '');
735
+ if (value !== '') {
736
+ out.push(value);
737
+ }
738
+ }
739
+ return out;
740
+ }
741
+
742
+ /**
743
+ * True when the root manifest describes an application rather than a container.
744
+ *
745
+ * Runtime sections only for `package.json`. A workspace root routinely carries
746
+ * a shared test or build setup in `devDependencies` — a Testing-Library stack,
747
+ * a bundler — and reading that as "the root is a React app" is how a Vue
748
+ * monorepo was handed a React lane before this check existed.
749
+ */
750
+ function _root_carries_frontend(composer: Manifest, pkg: Manifest): boolean {
751
+ const php = _all_dependencies(composer, 'require', 'require-dev');
752
+ if (_LIVEWIRE_PACKAGE in php || _FILAMENT_PACKAGE in php) {
753
+ return true;
754
+ }
755
+ const runtime = _all_dependencies(pkg, 'dependencies', 'peerDependencies');
756
+ if ('react' in runtime || 'vue' in runtime) {
757
+ return true;
758
+ }
759
+ if (_UNMODELLED_MARKERS.some((marker) => marker in runtime || marker in php)) {
760
+ return true;
761
+ }
762
+ return Object.keys(runtime).some((name) => name.startsWith(_SHADCN_RADIX_PREFIX));
763
+ }
764
+
765
+ /**
766
+ * Label specificity, most specific first — the label chain's own order.
767
+ *
768
+ * Used only to choose between workspaces that do NOT conflict. A monorepo whose
769
+ * app is `react` and whose design system is `react-shadcn` is one stack seen at
770
+ * two depths, and the deeper signal is the better scope for the UI lane: it is
771
+ * the package that owns `components.json`, which is where components are
772
+ * authored.
773
+ */
774
+ const _LABEL_SPECIFICITY: ReadonlyArray<string> = [
775
+ 'blade-livewire-flux',
776
+ 'filament',
777
+ 'blade-livewire',
778
+ 'react-shadcn',
779
+ 'react',
780
+ 'vue',
781
+ UNSUPPORTED_STACK,
782
+ DEFAULT_STACK,
783
+ ];
784
+
785
+ function _label_rank(label: string): number {
786
+ const i = _LABEL_SPECIFICITY.indexOf(label);
787
+ return i === -1 ? _LABEL_SPECIFICITY.length : i;
788
+ }
789
+
790
+ /**
791
+ * Pick the frontend scope inside a workspace repository, or refuse.
792
+ *
793
+ * `null` means "no workspace carries a frontend" — the caller falls through to
794
+ * its own label, which keeps a backend-only monorepo `plain` instead of
795
+ * inventing a scope for it.
796
+ *
797
+ * Workspaces that resolve to {@link DEFAULT_STACK} are dropped before anything
798
+ * else is decided. That is not a tidy-up: a normal Turborepo ships
799
+ * `packages/eslint-config` and `packages/typescript-config` beside the real
800
+ * ones, and counting those as "frontend roots" made the multi-root refusal fire
801
+ * on repositories that have exactly one frontend.
802
+ */
803
+ function _resolve_workspace_scope(
804
+ project_root: string,
805
+ mtime: number,
806
+ root_axes: StackAxes,
807
+ ): StackResult | null {
808
+ const candidates = _nested_frontend_roots(project_root)
809
+ .map((dir) => ({ dir, result: detect_stack(dir) }))
810
+ .filter((c) => c.result.frontend !== DEFAULT_STACK);
811
+ if (candidates.length === 0) {
812
+ return null;
813
+ }
814
+
815
+ // Two workspaces on mutually exclusive reactivity layers are two stacks, and
816
+ // the standing contract for conflicting signals is to name them rather than
817
+ // pick one. Reuses `_EXCLUSIVE_REACTIVITY` so scope ambiguity and manifest
818
+ // ambiguity cannot drift apart: Alpine beside React is still one stack here,
819
+ // exactly as it is inside a single manifest.
820
+ const layers = new Set(
821
+ candidates
822
+ .map((c) => c.result.axes.reactivity)
823
+ .filter((value) => _EXCLUSIVE_REACTIVITY.has(value)),
824
+ );
825
+ if (layers.size > 1) {
826
+ const names = candidates
827
+ .map((c) => path.basename(c.dir))
828
+ .sort()
829
+ .join(' + ');
830
+ return new StackResult({
831
+ frontend: UNSUPPORTED_STACK,
832
+ mtime,
833
+ axes: root_axes,
834
+ ambiguity: [`workspace roots: ${names}`],
835
+ });
836
+ }
837
+
838
+ const best = [...candidates].sort((a, b) => {
839
+ const delta = _label_rank(a.result.frontend) - _label_rank(b.result.frontend);
840
+ return delta !== 0 ? delta : a.dir.localeCompare(b.dir);
841
+ })[0] as { dir: string; result: StackResult };
842
+
843
+ return new StackResult({
844
+ frontend: best.result.frontend,
845
+ mtime: best.result.mtime,
846
+ axes: best.result.axes,
847
+ ambiguity: best.result.ambiguity,
848
+ scope_root: path.relative(project_root, best.dir),
849
+ });
850
+ }
851
+
567
852
  /** Workspace directories a monorepo conventionally puts its packages in. */
568
853
  const _WORKSPACE_DIRS: ReadonlyArray<string> = ['packages', 'apps', 'services', 'libs'];
569
854
 
@@ -584,10 +869,7 @@ function _nested_frontend_roots(project_root: string): string[] {
584
869
  // `workspaces` is the declarative answer when present; the conventional
585
870
  // directories are the fallback for repos that do not declare them.
586
871
  const root_pkg = _read_json(path.join(project_root, 'package.json'));
587
- const declared = root_pkg['workspaces'];
588
- const globs: string[] = Array.isArray(declared)
589
- ? (declared as unknown[]).filter((g): g is string => typeof g === 'string')
590
- : [];
872
+ const globs = _workspace_globs(project_root, root_pkg);
591
873
  const dirs = new Set<string>(_WORKSPACE_DIRS);
592
874
  for (const g of globs) {
593
875
  const head = g.split('/')[0];
@@ -640,7 +922,10 @@ function _is_react_shadcn(pkg: Manifest, components_json: boolean): boolean {
640
922
  return false;
641
923
  }
642
924
  const names = Object.keys(deps);
643
- const has_radix = names.some((name) => name.startsWith(_SHADCN_RADIX_PREFIX));
925
+ // The unified `radix-ui` package counts exactly like the scoped `@radix-ui/*`
926
+ // prefix it replaced — same primitives, one entry in the manifest.
927
+ const has_radix =
928
+ _SHADCN_RADIX_UNIFIED in deps || names.some((name) => name.startsWith(_SHADCN_RADIX_PREFIX));
644
929
  const has_shadcn_pkg = names.some((name) => _SHADCN_PACKAGE_NAMES.has(name));
645
930
  return has_radix || has_shadcn_pkg || components_json;
646
931
  }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * `agent-config ui:audit <path>` — inventory a UI tree into an artefact.
3
+ *
4
+ * Step E2.1 of `road-to-frontend-power`. Before this command, `state.ui_audit`
5
+ * existed only inside the work-engine dispatcher, which is why
6
+ * `src/rules/ui-audit-gate.md` shipped `enforced_by: none`: a chat session had
7
+ * nothing to point at, and "I ran the audit" is self-report. This writes the
8
+ * artefact a gate can read.
9
+ *
10
+ * CLASS A per `docs/decisions/ADR-124` and `docs/contracts/no-runtime-boundary.md`:
11
+ * it terminates, and its whole state is one rebuildable JSON file under
12
+ * `agents/runtime/state/`. Deleting the artefact changes only speed — the next
13
+ * invocation reproduces it. No process survives the command.
14
+ *
15
+ * The output shape is the one the work engine already expects for
16
+ * `state.ui_audit` (`components_found`, `greenfield`, `audit_path`), so the
17
+ * dispatcher and a chat session read the same object rather than two dialects.
18
+ * `COVERAGE_BUCKETS` is imported from the engine rather than re-listed here:
19
+ * `fe-design` step 3 already had a copied vocabulary, and a second copy is how
20
+ * the two drift.
21
+ *
22
+ * Deliberately NOT a design opinion. This command reports what exists. Whether
23
+ * what exists is good is `design-review`'s job, and mixing the two would make
24
+ * the artefact's meaning depend on taste.
25
+ */
26
+ import * as fs from 'node:fs';
27
+ import * as path from 'node:path';
28
+ import { isUiPath, isUiTreePath } from '../../scripts/_lib/ui_surface.js';
29
+ /**
30
+ * The engine's coverage vocabulary, declared here rather than re-exported.
31
+ *
32
+ * A re-export from `agent-src/templates/scripts/work_engine/...` was tried
33
+ * first and reverted: it creates a RUNTIME edge from the shipped CLI into the
34
+ * work-engine TEMPLATE tree, which is projected to consumers rather than
35
+ * compiled into `dist/cli`. The published bundle then fails to resolve it at
36
+ * import time — caught by `agent-config roadmap:progress` throwing
37
+ * ERR_MODULE_NOT_FOUND on a clean tree.
38
+ *
39
+ * The one-vocabulary guarantee survives without the edge: `uiAudit.test.ts`
40
+ * reads the engine's own `apply.ts` and asserts this array equals it, so drift
41
+ * is a red test rather than two copies quietly diverging. A test-time read into
42
+ * the template tree is fine — the tests already read it; a runtime import is
43
+ * not.
44
+ */
45
+ export const COVERAGE_BUCKETS = ['honoured', 'translated', 'flagged'];
46
+ export const ARTEFACT_REL = path.join('agents', 'runtime', 'state', 'ui-audit.json');
47
+ /** Files that mark an adopted design system, and the marker each implies. */
48
+ const SYSTEM_MARKERS = [
49
+ [/components\/ui\/[a-z-]+\.tsx?$/i, 'shadcn'],
50
+ [/\btailwind\.config\.[cm]?[jt]s$/i, 'tailwind'],
51
+ [/\bDESIGN\.md$/, 'design-md'],
52
+ [/\bPRODUCT\.md$/, 'product-md'],
53
+ [/\btokens\.json$/, 'dtcg-tokens'],
54
+ [/\btokens\.css$/, 'css-tokens'],
55
+ ];
56
+ const TOKEN_DECL = /(--[a-z0-9][a-z0-9-]*)\s*:/gi;
57
+ const FONT_FAMILY_DECL = /font-family\s*:\s*([^;}"'\n]*(?:"[^"]*"|'[^']*')?[^;}\n]*)/gi;
58
+ const JSX_EXPORT = /export\s+(?:default\s+)?(?:function|const|class)\s+([A-Z][A-Za-z0-9_]*)/g;
59
+ function walk(root, out = [], depth = 0) {
60
+ if (depth > 12)
61
+ return out;
62
+ let entries;
63
+ try {
64
+ entries = fs.readdirSync(root, { withFileTypes: true });
65
+ }
66
+ catch {
67
+ return out;
68
+ }
69
+ for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
70
+ if (e.name === 'node_modules' || e.name === '.git' || e.name === 'dist')
71
+ continue;
72
+ const p = path.join(root, e.name);
73
+ if (e.isDirectory())
74
+ walk(p, out, depth + 1);
75
+ else
76
+ out.push(p);
77
+ }
78
+ return out;
79
+ }
80
+ function classify(rel) {
81
+ if (/\.(css|scss|sass|less)$/i.test(rel))
82
+ return 'style';
83
+ if (/(^|\/)(pages|app)\//i.test(rel) || /(^|\/)(page|index)\.[jt]sx?$/i.test(rel))
84
+ return 'page';
85
+ if (/\.blade\.php$/.test(rel) || /resources\/views\//.test(rel))
86
+ return 'view';
87
+ return 'component';
88
+ }
89
+ /**
90
+ * Pure core. Takes (relative path, text) pairs so every branch is testable
91
+ * without a tree on disk — the same reason `ui_authority` takes a record.
92
+ */
93
+ export function buildArtefact(root, files, now = new Date().toISOString()) {
94
+ const uiFiles = files.filter(([rel]) => isUiPath(rel) || isUiTreePath(rel));
95
+ const components = [];
96
+ const tokens = new Set();
97
+ const primitives = new Set();
98
+ const markers = [];
99
+ const families = new Set();
100
+ for (const [rel] of files) {
101
+ for (const [re, marker] of SYSTEM_MARKERS) {
102
+ if (re.test(rel) && !markers.some((m) => m.marker === marker))
103
+ markers.push({ marker, path: rel });
104
+ }
105
+ }
106
+ for (const [rel, text] of uiFiles) {
107
+ for (const m of text.matchAll(TOKEN_DECL))
108
+ tokens.add(m[1].toLowerCase());
109
+ for (const m of text.matchAll(FONT_FAMILY_DECL)) {
110
+ const first = m[1].split(',')[0]?.trim().replace(/^["']|["']$/g, '').toLowerCase();
111
+ if (first && !first.startsWith('var('))
112
+ families.add(first);
113
+ }
114
+ const exports = [];
115
+ for (const m of text.matchAll(JSX_EXPORT)) {
116
+ exports.push(m[1]);
117
+ primitives.add(m[1]);
118
+ }
119
+ components.push({ path: rel, kind: classify(rel), exports });
120
+ }
121
+ // Greenfield is the ABSENCE of a UI surface, not the absence of a
122
+ // DESIGN.md. A1.3 depends on this distinction: a coherent incumbent with no
123
+ // DESIGN.md is emphatically not greenfield.
124
+ const greenfield = components.length === 0;
125
+ // Coherence is evidence of a consistent visual vocabulary, and it has more
126
+ // than one shape. Three independent signals, any of which is sufficient:
127
+ const signals = [];
128
+ if (tokens.size >= 3)
129
+ signals.push(`${tokens.size} declared tokens`);
130
+ if (markers.length > 0)
131
+ signals.push(`design-system marker: ${markers.map((m) => m.marker).join(', ')}`);
132
+ // A single dominant first-choice family is the signal the token heuristic
133
+ // misses entirely: a hand-written stylesheet can be perfectly coherent with
134
+ // literal values and BEM naming and declare no custom property at all.
135
+ if (families.size === 1)
136
+ signals.push(`single type family: ${[...families][0]}`);
137
+ const coherent = signals.length > 0;
138
+ return {
139
+ schema: 1,
140
+ generated_at: now,
141
+ root,
142
+ components_found: components,
143
+ tokens: [...tokens].sort(),
144
+ primitives: [...primitives].sort(),
145
+ design_system_markers: markers,
146
+ greenfield,
147
+ audit_path: greenfield ? 'greenfield' : coherent ? 'high_confidence' : 'ambiguous',
148
+ coherence_signals: signals,
149
+ context_stale: staleness(files, tokens),
150
+ verification: 'verified',
151
+ };
152
+ }
153
+ /**
154
+ * E2.3 — Tier-1 staleness, on data the command has ALREADY opened.
155
+ *
156
+ * The step's constraint is that the file-open count must not rise, which is why
157
+ * this takes the same `files` array rather than re-reading DESIGN.md. A token
158
+ * named in DESIGN.md or PRODUCT.md that no UI file declares is reported; it is
159
+ * never repaired here, because repairing a context document without being asked
160
+ * is a scope expansion, not a service.
161
+ */
162
+ export function staleness(files, declared) {
163
+ const out = [];
164
+ for (const [rel, text] of files) {
165
+ if (!/\b(DESIGN|PRODUCT)\.md$/.test(rel))
166
+ continue;
167
+ for (const m of text.matchAll(/`(--[a-z0-9][a-z0-9-]*)`/gi)) {
168
+ const token = m[1].toLowerCase();
169
+ if (!declared.has(token)) {
170
+ out.push({ document: rel, token, reason: 'named in the document, declared by no UI file' });
171
+ }
172
+ }
173
+ }
174
+ return out;
175
+ }
176
+ export function runUiAudit(opts = {}) {
177
+ const projectRoot = path.resolve(opts.projectRoot ?? process.cwd());
178
+ const target = path.resolve(projectRoot, opts.target ?? '.');
179
+ if (!fs.existsSync(target)) {
180
+ process.stderr.write(`[ui:audit] path not found: ${target}\n`);
181
+ return 1;
182
+ }
183
+ const stat = fs.statSync(target);
184
+ const absFiles = stat.isDirectory() ? walk(target) : [target];
185
+ const pairs = [];
186
+ let unreadable = 0;
187
+ for (const abs of absFiles) {
188
+ try {
189
+ if (fs.statSync(abs).size > 2_000_000)
190
+ continue;
191
+ pairs.push([path.relative(projectRoot, abs).split(path.sep).join('/'), fs.readFileSync(abs, 'utf8')]);
192
+ }
193
+ catch {
194
+ unreadable++;
195
+ }
196
+ }
197
+ let artefact = buildArtefact(path.relative(projectRoot, target).split(path.sep).join('/') || '.', pairs);
198
+ if (unreadable > 0) {
199
+ artefact = {
200
+ ...artefact,
201
+ verification: 'degraded',
202
+ degradation_reason: `${unreadable} file(s) could not be read`,
203
+ };
204
+ }
205
+ const outPath = path.join(projectRoot, ARTEFACT_REL);
206
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
207
+ fs.writeFileSync(outPath, `${JSON.stringify(artefact, null, 2)}\n`);
208
+ if (opts.json)
209
+ process.stdout.write(`${JSON.stringify(artefact, null, 2)}\n`);
210
+ else {
211
+ process.stdout.write(`✅ ui:audit — ${artefact.components_found.length} component(s), ${artefact.tokens.length} token(s), ` +
212
+ `${artefact.design_system_markers.length} system marker(s); path=${artefact.audit_path}\n` +
213
+ ` ${ARTEFACT_REL}\n` +
214
+ (artefact.context_stale.length
215
+ ? ` ⚠️ ${artefact.context_stale.length} CONTEXT_STALE line(s) — reported, not repaired\n`
216
+ : '') +
217
+ (artefact.verification !== 'verified' ? ` ⚠️ ${artefact.degradation_reason}\n` : ''));
218
+ }
219
+ return 0;
220
+ }
221
+ //# sourceMappingURL=uiAudit.js.map