@event4u/agent-config 14.9.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 (426) 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 +384 -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/async-python-patterns/SKILL.md +1 -1
  45. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  46. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  47. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  52. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  53. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  54. package/dist/agent-src/skills/code-refactoring/SKILL.md +1 -1
  55. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  56. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  57. package/dist/agent-src/skills/condense-memory/SKILL.md +4 -1
  58. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  59. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +4 -1
  60. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  61. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  62. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  63. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  64. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  65. package/dist/agent-src/skills/design-review/SKILL.md +80 -2
  66. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  67. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  68. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  69. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  70. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  71. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  72. package/dist/agent-src/skills/discovery-interview/SKILL.md +1 -1
  73. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
  74. package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -1
  75. package/dist/agent-src/skills/editorial-calendar/SKILL.md +1 -1
  76. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  78. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  79. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  80. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  81. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  82. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  83. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  84. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  85. package/dist/agent-src/skills/github-ci/SKILL.md +1 -1
  86. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  87. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  88. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  89. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  90. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  91. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  92. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  93. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  94. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  95. package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
  96. package/dist/agent-src/skills/judge-injection-defense/SKILL.md +1 -1
  97. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  98. package/dist/agent-src/skills/laravel-validation/SKILL.md +1 -1
  99. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  100. package/dist/agent-src/skills/legal-intake-triage/SKILL.md +1 -1
  101. package/dist/agent-src/skills/legal-practice-profile/SKILL.md +1 -1
  102. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  103. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  104. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  105. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  106. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  107. package/dist/agent-src/skills/messaging-architecture/SKILL.md +1 -1
  108. package/dist/agent-src/skills/migration-architect/SKILL.md +1 -1
  109. package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +1 -1
  110. package/dist/agent-src/skills/module-management/SKILL.md +1 -1
  111. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  112. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  113. package/dist/agent-src/skills/motion-choreographer/SKILL.md +7 -1
  114. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  115. package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
  116. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  117. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  118. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  119. package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
  120. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  121. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  122. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  123. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  124. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  125. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  126. package/dist/agent-src/skills/prompt-optimizer/SKILL.md +1 -1
  127. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  128. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  129. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  130. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  131. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  132. package/dist/agent-src/skills/receiving-code-review/SKILL.md +3 -3
  133. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  134. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  135. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  136. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  137. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  138. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  139. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  140. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  141. package/dist/agent-src/skills/scene-expander/SKILL.md +1 -1
  142. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  143. package/dist/agent-src/skills/secrets-management/SKILL.md +5 -1
  144. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  145. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  146. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  147. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  148. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  149. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  150. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  151. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  152. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  153. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  154. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  155. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  156. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  157. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  158. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  159. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  160. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  161. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  162. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  163. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  164. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  165. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  166. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  167. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  168. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  169. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  170. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  171. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  172. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  173. package/dist/agent-src/templates/playbook.md +75 -0
  174. package/dist/agent-src/templates/roadmaps.md +37 -0
  175. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  176. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  177. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  178. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  179. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  180. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  181. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  182. package/dist/cli/commands/uiAudit.js +221 -0
  183. package/dist/cli/commands/uiAudit.js.map +1 -0
  184. package/dist/cli/commands/uiRender.js +206 -0
  185. package/dist/cli/commands/uiRender.js.map +1 -0
  186. package/dist/cli/main.js +22 -1
  187. package/dist/cli/main.js.map +1 -1
  188. package/dist/cli/mcp/content.js +64 -1
  189. package/dist/cli/mcp/content.js.map +1 -1
  190. package/dist/cli/mcp/dispatch.js +175 -10
  191. package/dist/cli/mcp/dispatch.js.map +1 -1
  192. package/dist/cli/registry.js +3 -0
  193. package/dist/cli/registry.js.map +1 -1
  194. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  195. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  196. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  197. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  198. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  199. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  200. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  201. package/dist/cli-delegate/cmd_conformance.js +3 -3
  202. package/dist/cli-delegate/cmd_doctor.js +3 -3
  203. package/dist/cli-delegate/cmd_export.js +4 -4
  204. package/dist/cli-delegate/cmd_handoff.js +1 -1
  205. package/dist/cli-delegate/cmd_preflight.js +2 -2
  206. package/dist/cli-delegate/cmd_prune.js +4 -4
  207. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  208. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  209. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  210. package/dist/cli-delegate/cmd_sync.js +4 -4
  211. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  212. package/dist/cli-delegate/cmd_validate.js +4 -4
  213. package/dist/discovery/deprecation-report.md +1 -1
  214. package/dist/discovery/discovery-manifest.json +380 -145
  215. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  216. package/dist/discovery/discovery-manifest.summary.md +4 -4
  217. package/dist/discovery/dormancy-report.md +1 -1
  218. package/dist/discovery/orphan-report.md +1 -1
  219. package/dist/discovery/packs.json +18 -8
  220. package/dist/discovery/trust-report.md +3 -3
  221. package/dist/discovery/workspaces.json +16 -6
  222. package/dist/hooks/dispatch.js +2337 -1436
  223. package/dist/install/install.mjs +795 -708
  224. package/dist/mcp/registry-manifest.json +3 -3
  225. package/dist/mcp/server.json +2 -2
  226. package/dist/mcp/server.mjs +1685 -1250
  227. package/dist/router.json +1 -1
  228. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  229. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  230. package/dist/scripts/_lib/scoped_projection.js +84 -0
  231. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  232. package/dist/scripts/_lib/ui_surface.js +89 -0
  233. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  234. package/dist/scripts/condense.js +6 -10
  235. package/dist/scripts/condense.js.map +1 -1
  236. package/dist/scripts/project_thin_rules.js +40 -2
  237. package/dist/scripts/project_thin_rules.js.map +1 -1
  238. package/dist/server/schemas/settings.js +3 -0
  239. package/dist/server/schemas/settings.js.map +1 -1
  240. package/dist/shared/skillRanking.js +159 -0
  241. package/dist/shared/skillRanking.js.map +1 -0
  242. package/docs/contracts/provider-lifecycle.md +47 -18
  243. package/docs/contracts/settings-classes.md +14 -4
  244. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  245. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  246. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  247. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  248. package/docs/guidelines/design-antipatterns.md +4 -2
  249. package/docs/guidelines/design-asset-discipline.md +38 -0
  250. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  251. package/docs/guidelines/design-handover-extraction.md +35 -5
  252. package/docs/guidelines/design-modes.md +41 -1
  253. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  254. package/llms.txt +50 -39
  255. package/package.json +1 -1
  256. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  257. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  258. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  259. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  260. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  261. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  262. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  263. package/src/config/agent-settings.template.yml +28 -0
  264. package/src/config/assurance-capability-registry.json +223 -0
  265. package/src/config/assurance-threshold-budget.json +65 -0
  266. package/src/config/ci-local-parity.yml +14 -0
  267. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  268. package/src/config/dispatch-economy-metrics.json +4 -2
  269. package/src/config/evaluator-budgets.json +14 -14
  270. package/src/config/gate-coverage.yml +227 -8
  271. package/src/config/gate-violation-baselines.json +76 -72
  272. package/src/config/hook-latency-budget.json +1 -0
  273. package/src/config/hook-token-budget.json +137 -125
  274. package/src/config/pack-size-budget.json +4 -2
  275. package/src/config/preamble-payload-budget.json +71 -67
  276. package/src/config/quorum-attendance-budget.json +12 -4
  277. package/src/config/rule-activation-census.json +7 -3
  278. package/src/config/rule-enforcement-baseline.json +0 -1
  279. package/src/config/rule-stub-ceilings.json +112 -91
  280. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  281. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  282. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  283. package/src/scripts/_dispatch.bash +17 -0
  284. package/src/scripts/_lib/as_of.ts +412 -0
  285. package/src/scripts/_lib/exec_evidence.ts +7 -0
  286. package/src/scripts/_lib/hook_settings.ts +33 -0
  287. package/src/scripts/_lib/host_listing_model.ts +192 -0
  288. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  289. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  290. package/src/scripts/_lib/review_independence.ts +62 -10
  291. package/src/scripts/_lib/rule_injection.ts +239 -0
  292. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  293. package/src/scripts/_lib/scoped_projection.ts +96 -0
  294. package/src/scripts/_lib/session_register.ts +44 -2
  295. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  296. package/src/scripts/_lib/transcript_reads.ts +207 -0
  297. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  298. package/src/scripts/_lib/ui_authority.ts +343 -0
  299. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  300. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  301. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  302. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  303. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  304. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  305. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  306. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  307. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  308. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  309. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  310. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  311. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  312. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  313. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  314. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  315. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  316. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  317. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  318. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  319. package/src/scripts/ai-video/stitch.sh +191 -10
  320. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  321. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  322. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  323. package/src/scripts/ai_council/clients.ts +3 -2
  324. package/src/scripts/ai_council/consensus.ts +63 -6
  325. package/src/scripts/ai_council/events_log.ts +46 -1
  326. package/src/scripts/ai_council/orchestrator.ts +103 -108
  327. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  328. package/src/scripts/ai_council/prompts.ts +12 -5
  329. package/src/scripts/ai_council/quorum.ts +82 -1
  330. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  331. package/src/scripts/ai_council/seating.ts +211 -0
  332. package/src/scripts/ai_council/stance_tally.ts +22 -0
  333. package/src/scripts/bench_hook_latency.ts +62 -0
  334. package/src/scripts/build_proof.ts +12 -1
  335. package/src/scripts/capture_skill_catalogue.ts +21 -0
  336. package/src/scripts/check_always_budget.ts +2 -1
  337. package/src/scripts/check_augmentignore.ts +2 -1
  338. package/src/scripts/check_beta_review_markers.ts +2 -1
  339. package/src/scripts/check_composite_arming.ts +364 -0
  340. package/src/scripts/check_corpus_staleness.ts +2 -1
  341. package/src/scripts/check_council_pin_staleness.ts +3 -1
  342. package/src/scripts/check_enforcement_coverage.ts +116 -3
  343. package/src/scripts/check_enforcement_denominator.ts +365 -0
  344. package/src/scripts/check_gate_coverage.ts +2 -1
  345. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  346. package/src/scripts/check_knowledge_cards.ts +2 -1
  347. package/src/scripts/check_knowledge_pages.ts +3 -2
  348. package/src/scripts/check_memory.ts +2 -1
  349. package/src/scripts/check_no_automerge_key.ts +287 -0
  350. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  351. package/src/scripts/check_pack_passport_reconciliation.ts +274 -0
  352. package/src/scripts/check_proposal.ts +2 -1
  353. package/src/scripts/check_reach_staleness.ts +2 -1
  354. package/src/scripts/check_release_adjacent_health.ts +2 -1
  355. package/src/scripts/check_release_pr_shape.ts +5 -0
  356. package/src/scripts/check_review_schema.ts +34 -1
  357. package/src/scripts/check_roadmap_trackable.ts +48 -3
  358. package/src/scripts/check_source_size_budget.ts +2 -1
  359. package/src/scripts/check_standing_payload_delta.ts +505 -0
  360. package/src/scripts/check_trigger_evals.ts +2 -1
  361. package/src/scripts/classify_change_risk.ts +281 -0
  362. package/src/scripts/compute_skill_tiers.ts +186 -0
  363. package/src/scripts/condense.ts +8 -13
  364. package/src/scripts/council_attendance_metrics.ts +183 -0
  365. package/src/scripts/council_cli.ts +10 -8
  366. package/src/scripts/council_parse_rate.ts +116 -0
  367. package/src/scripts/derive_playbooks.ts +406 -0
  368. package/src/scripts/design_slop_rules.test.ts +7 -0
  369. package/src/scripts/design_slop_rules.ts +56 -2
  370. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  371. package/src/scripts/frontend_corpus_hash.ts +116 -0
  372. package/src/scripts/generate_pack_manifests.ts +78 -0
  373. package/src/scripts/grade_target_readiness.ts +478 -0
  374. package/src/scripts/hook_manifest.json +1 -1
  375. package/src/scripts/hook_manifest.yaml +97 -16
  376. package/src/scripts/hooks/concern_registry.ts +4 -0
  377. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  378. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  379. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  380. package/src/scripts/hot_context_hook.ts +74 -7
  381. package/src/scripts/install.ts +40 -89
  382. package/src/scripts/lint_adapter_tier.ts +663 -0
  383. package/src/scripts/lint_archived_skills.ts +93 -0
  384. package/src/scripts/lint_budget_ownership.ts +2 -1
  385. package/src/scripts/lint_deterministic_time.ts +470 -0
  386. package/src/scripts/lint_framework_leakage.ts +5 -0
  387. package/src/scripts/lint_handoffs.ts +525 -1
  388. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  389. package/src/scripts/lint_marketplace.ts +88 -0
  390. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  391. package/src/scripts/lint_one_off_age.ts +6 -1
  392. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  393. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  394. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  395. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  396. package/src/scripts/lint_spawn_payload.ts +42 -5
  397. package/src/scripts/lint_symptom_intake.ts +2 -1
  398. package/src/scripts/mcp_parity_smoke.ts +30 -4
  399. package/src/scripts/mcp_render.ts +66 -1
  400. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  401. package/src/scripts/measure_risk_class_drift.ts +140 -0
  402. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  403. package/src/scripts/media/lib/adapter-common.sh +211 -1
  404. package/src/scripts/media/lib/adapter-contract.md +74 -6
  405. package/src/scripts/model_rule_injection.ts +907 -0
  406. package/src/scripts/project_thin_rules.ts +45 -2
  407. package/src/scripts/release.ts +66 -860
  408. package/src/scripts/release_drill.ts +59 -3
  409. package/src/scripts/release_env.ts +298 -0
  410. package/src/scripts/release_publication.ts +720 -0
  411. package/src/scripts/roadmap_context.ts +778 -0
  412. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  413. package/src/scripts/schemas/pack.schema.json +62 -4
  414. package/src/scripts/schemas/rule.schema.json +24 -2
  415. package/src/scripts/schemas/skill.schema.json +41 -2
  416. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  417. package/src/scripts/session_register_hook.ts +29 -3
  418. package/src/scripts/sessions_cli.ts +67 -3
  419. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  420. package/src/scripts/tighten_skill_descriptions.ts +264 -0
  421. package/src/scripts/token_sink_report.ts +239 -0
  422. package/src/scripts/trigger_coverage.ts +122 -3
  423. package/src/scripts/update_counts.ts +7 -3
  424. package/src/scripts/validate_frontmatter.ts +66 -0
  425. package/src/server/schemas/settings.ts +5 -0
  426. package/src/shared/skillRanking.ts +183 -0
@@ -45,12 +45,14 @@ import { fileURLToPath } from 'node:url';
45
45
 
46
46
  import { write_atomic } from './_lib/fs_atomic.js';
47
47
  import {
48
+ classify_collisions,
48
49
  iso_now,
49
50
  read_live_records,
50
51
  register_dir,
51
52
  safe_stem,
52
53
  ttl_is_measured,
53
54
  ttl_seconds_for,
55
+ type SessionRecord,
54
56
  } from './_lib/session_register.js';
55
57
  import { claim_file, claim_is_stale, claim_read_paths } from './session_register_hook.js';
56
58
 
@@ -62,7 +64,7 @@ function usage(): number {
62
64
  ' live sessions, plus unmerged branches',
63
65
  ' held by other worktrees (--branches adds',
64
66
  ' that axis to the JSON form)',
65
- ' agent-config sessions:claim <slug> claim a roadmap for this session',
67
+ ' agent-config sessions:claim <slug> [--paths <a,b,…>] claim a roadmap for this session',
66
68
  ' agent-config sessions:claim <slug> --force claim it even though a peer holds it',
67
69
  ' agent-config sessions:claim --release drop this session\'s roadmap claim',
68
70
  '',
@@ -168,6 +170,30 @@ export function other_worktree_branches_detailed(
168
170
  return { rows, filtered: unmerged !== null };
169
171
  }
170
172
 
173
+ /**
174
+ * `PATH OVERLAP` lines for this session against every live peer.
175
+ *
176
+ * Kept a separate, separately-labelled line rather than folded into the slug or
177
+ * branch report, because the three collisions call for three different moves: a
178
+ * slug collision means stop, a branch collision means coordinate, a path
179
+ * collision means reorder — take the disjoint steps first if ordering allows,
180
+ * otherwise name it and continue. The register is advisory and never a lock.
181
+ *
182
+ * Pure over the record arrays so the fixture test needs no register on disk.
183
+ */
184
+ export function path_overlap_lines(
185
+ others: readonly SessionRecord[],
186
+ here: { branch: string | null; roadmap_slug: string | null; owned_paths?: readonly string[] },
187
+ ): string[] {
188
+ return classify_collisions(others, here)
189
+ .filter((c) => c.kind === 'path')
190
+ .map(
191
+ (c) =>
192
+ ` PATH OVERLAP ${c.record.session_id} · ${(c.paths ?? []).length} shared path(s): ` +
193
+ `${(c.paths ?? []).join(', ')}`,
194
+ );
195
+ }
196
+
171
197
  function cmd_list(argv: string[], root: string): number {
172
198
  const as_json = argv.includes('--json');
173
199
  const dir = register_dir(root);
@@ -229,6 +255,21 @@ function cmd_list(argv: string[], root: string): number {
229
255
  );
230
256
  }
231
257
  write_held_branches(held, filtered);
258
+ // The path axis. Silent when this session has declared no owned paths, which
259
+ // is every session that has not run `sessions:claim --paths`.
260
+ const sid = env_session_id();
261
+ const mine = sid === null ? undefined : records.find((r) => r.session_id === sid);
262
+ const overlaps = path_overlap_lines(
263
+ records.filter((r) => r.session_id !== mine?.session_id),
264
+ {
265
+ branch: mine?.branch ?? null,
266
+ roadmap_slug: mine?.roadmap_slug ?? null,
267
+ ...(mine?.owned_paths !== undefined ? { owned_paths: mine.owned_paths } : {}),
268
+ },
269
+ );
270
+ if (overlaps.length > 0) {
271
+ process.stdout.write(`\n${overlaps.join('\n')}\n`);
272
+ }
232
273
  process.stdout.write(
233
274
  'Advisory only — this register is not a lock, and an idle session\n' +
234
275
  'disappears from it after its TTL although its user may return.\n',
@@ -432,7 +473,15 @@ function cmd_claim(argv: string[], root: string): number {
432
473
  );
433
474
  return 0;
434
475
  }
435
- const slug = argv.find((a) => !a.startsWith('-'));
476
+ const paths_idx = argv.indexOf('--paths');
477
+ const paths_arg = paths_idx >= 0 ? (argv[paths_idx + 1] ?? '') : '';
478
+ const owned_paths = paths_arg
479
+ .split(',')
480
+ .map((x) => x.trim())
481
+ .filter((x) => x !== '');
482
+ // The slug is the first bare token that is not the VALUE of --paths; without
483
+ // this the comma list would be read as the slug on `claim --paths a,b road-to-x`.
484
+ const slug = argv.find((a, i) => !a.startsWith('-') && i !== paths_idx + 1);
436
485
  if (slug === undefined || slug.trim() === '') {
437
486
  return usage();
438
487
  }
@@ -476,7 +525,19 @@ function cmd_claim(argv: string[], root: string): number {
476
525
  try {
477
526
  write_atomic(
478
527
  target,
479
- `${JSON.stringify({ slug: slug.trim(), written_at: iso_now(), session_id: sid }, null, 2)}\n`,
528
+ `${JSON.stringify(
529
+ {
530
+ slug: slug.trim(),
531
+ written_at: iso_now(),
532
+ session_id: sid,
533
+ // Absent, not empty, when nothing was declared: the whole
534
+ // additive guarantee of `owned_paths` rests on the field not
535
+ // being written at all in the ordinary case.
536
+ ...(owned_paths.length > 0 ? { paths: owned_paths } : {}),
537
+ },
538
+ null,
539
+ 2,
540
+ )}\n`,
480
541
  );
481
542
  } catch (exc) {
482
543
  process.stderr.write(`sessions:claim: could not write the claim — ${String(exc)}\n`);
@@ -484,6 +545,9 @@ function cmd_claim(argv: string[], root: string): number {
484
545
  }
485
546
  process.stdout.write(
486
547
  `Claimed "${slug.trim()}" for this session. It becomes visible to other\n` +
548
+ (owned_paths.length > 0
549
+ ? `Declared ${owned_paths.length} owned path(s); peers sharing one are labelled PATH OVERLAP.\n`
550
+ : '') +
487
551
  'sessions on the next turn, when the heartbeat lifts it into the register.\n' +
488
552
  (sid === null
489
553
  ? 'NOTE: this host exports no session id, so the claim is scoped to this\n' +
@@ -34,7 +34,17 @@ import * as fs from 'node:fs';
34
34
  import * as path from 'node:path';
35
35
  import { fileURLToPath, pathToFileURL } from 'node:url';
36
36
 
37
- import { pyRound } from '../_lib/value_ladder.js';
37
+ // The formula, the stopword list and the tokenizer live in one Node-free
38
+ // module so the pure stdio-lite dispatcher can share them instead of growing
39
+ // a second copy (road-to-skill-delivery-over-mcp risk 6). Everything below is
40
+ // the disk half — globbing, frontmatter, the CLI — which stays here.
41
+ import {
42
+ scoreSkill,
43
+ skillTerms as _sharedSkillTerms,
44
+ tokenize as _sharedTokenize,
45
+ triggerTextFromFlatLines,
46
+ type RankOptions,
47
+ } from '../../shared/skillRanking.js';
38
48
 
39
49
  const _HERE = fileURLToPath(import.meta.url);
40
50
  // src/scripts/skill_tools/score_skill_relevance.ts → parents[3] of the .py
@@ -42,19 +52,6 @@ const _HERE = fileURLToPath(import.meta.url);
42
52
  export const ROOT = path.resolve(path.dirname(_HERE), '..', '..', '..');
43
53
  export const DEFAULT_SKILLS_DIR = path.join(ROOT, '.agent-src.uncondensed', 'skills');
44
54
 
45
- // re.compile(r"[a-z][a-z0-9]+") — applied to the lowercased text.
46
- const TOKEN_RE = /[a-z][a-z0-9]+/g;
47
-
48
- const STOPWORDS: ReadonlySet<string> = new Set([
49
- 'the', 'a', 'an', 'and', 'or', 'but', 'of', 'for', 'with', 'to', 'in',
50
- 'on', 'at', 'by', 'from', 'as', 'is', 'are', 'was', 'were', 'be', 'been',
51
- 'this', 'that', 'these', 'those', 'it', 'its', 'use', 'when', 'even',
52
- 'via', 'via:', 'into', 'onto', 'use:', 'skill', 'skills', 'task', 'tasks',
53
- 'code', 'file', 'files', 'doing', 'make', 'do', 'go', 'get', 'set',
54
- 'not', 'no', 'yes', 'any', 'some', 'all', 'one', 'two', 'new', 'old',
55
- 'user', 'users', 'our', 'your', 'their', 'they', 'we', 'you', 'i', 'me',
56
- ]);
57
-
58
55
  /** Mirror Python len(str) — count Unicode code points, not UTF-16 units. */
59
56
  function pyLen(s: string): number {
60
57
  let n = 0;
@@ -65,16 +62,9 @@ function pyLen(s: string): number {
65
62
  }
66
63
 
67
64
  export function _tokenize(text: string): Set<string> {
68
- const out = new Set<string>();
69
- // The TOKEN_RE stopwords never contain digits, so the `len(t) > 2`
70
- // filter is on code-point length (matches Python `len`).
71
- const matches = text.toLowerCase().match(TOKEN_RE) ?? [];
72
- for (const t of matches) {
73
- if (!STOPWORDS.has(t) && pyLen(t) > 2) {
74
- out.add(t);
75
- }
76
- }
77
- return out;
65
+ // Single-sourced in `src/shared/skillRanking.ts`. Kept as an export because
66
+ // the CLI-parity suite and the hook path both import this name.
67
+ return _sharedTokenize(text);
78
68
  }
79
69
 
80
70
  export interface Frontmatter {
@@ -152,6 +142,8 @@ export interface Skill {
152
142
  description: string;
153
143
  personas: string[];
154
144
  terms: Set<string>;
145
+ /** `triggers[].keyword` / `.phrase` prose. Indexed only under keyword-v2. */
146
+ triggerText: string[];
155
147
  }
156
148
 
157
149
  function _load_skills(skillsDir: string): Skill[] {
@@ -167,11 +159,16 @@ function _load_skills(skillsDir: string): Skill[] {
167
159
  personas = [personas];
168
160
  }
169
161
  const personaList = [...personas];
162
+ const rawTriggers = fm['triggers'];
163
+ const triggerText = Array.isArray(rawTriggers)
164
+ ? triggerTextFromFlatLines(rawTriggers as string[])
165
+ : [];
170
166
  skills.push({
171
167
  name,
172
168
  description: desc,
173
169
  personas: personaList,
174
170
  terms: _tokenize(name + ' ' + desc),
171
+ triggerText,
175
172
  });
176
173
  }
177
174
  return skills;
@@ -214,43 +211,29 @@ function _globSkillMd(root: string): string[] {
214
211
  return dirs.map((name) => path.join(root, name, 'SKILL.md'));
215
212
  }
216
213
 
217
- function _score(taskTerms: Set<string>, skill: Skill): number {
218
- if (taskTerms.size === 0) {
219
- return 0;
220
- }
221
- const skillTerms = skill.terms;
222
- let inter = 0;
223
- for (const t of taskTerms) {
224
- if (skillTerms.has(t)) {
225
- inter++;
226
- }
227
- }
228
- // overlap = |task ∩ skill| / max(|task|, 1)
229
- const overlap = inter / Math.max(taskTerms.size, 1);
230
- let personaHit = 0.0;
231
- // task_lower = " ".join(task_terms) — Set iteration order = insertion order
232
- // (matches Python set-iteration only incidentally; the membership tests
233
- // below are order-independent so the result is deterministic).
234
- const taskLower = [...taskTerms].join(' ');
235
- for (const persona of skill.personas) {
236
- const slug = String(persona).toLowerCase();
237
- const parts = slug.split('-');
238
- if (taskLower.includes(slug) || parts.some((part) => taskTerms.has(part))) {
239
- personaHit = 1.0;
240
- break;
241
- }
242
- }
243
- return pyRound(overlap * 70 + personaHit * 30, 0);
214
+ function _score(taskTerms: Set<string>, skill: Skill, opts: RankOptions = {}): number {
215
+ // Single-sourced in `src/shared/skillRanking.ts`. Under keyword-v1 (the
216
+ // default) `skill.terms` is already `tokenize(name + ' ' + description)`, so
217
+ // the precomputed set is reused unchanged; keyword-v2 re-derives the term
218
+ // set with the skill's trigger prose folded in.
219
+ const rankable = {
220
+ name: skill.name,
221
+ description: skill.description,
222
+ personas: skill.personas,
223
+ triggerText: skill.triggerText,
224
+ };
225
+ const terms = opts.includeTriggers ? _sharedSkillTerms(rankable, opts) : skill.terms;
226
+ return scoreSkill(taskTerms, rankable, terms);
244
227
  }
245
228
 
246
229
  export type RankRow = [string, number, string[]];
247
230
 
248
- export function rank(task: string, skillsDir: string): RankRow[] {
231
+ export function rank(task: string, skillsDir: string, opts: RankOptions = {}): RankRow[] {
249
232
  const taskTerms = _tokenize(task);
250
233
  const skills = _load_skills(skillsDir);
251
234
  const rows: RankRow[] = [];
252
235
  for (const s of skills) {
253
- const score = _score(taskTerms, s);
236
+ const score = _score(taskTerms, s, opts);
254
237
  if (score > 0) {
255
238
  rows.push([s.name, score, [...s.personas]]);
256
239
  }
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Tighten skill descriptions toward a cap WITHOUT touching trigger vocabulary.
4
+ *
5
+ * The description is the routing surface. `lint_skill_descriptions` gates its
6
+ * shape, `audit_skill_descriptions` ranks it and `optimize_skill_description`
7
+ * tunes ONE skill against its own `evals/triggers.json`. None of them shortens
8
+ * a corpus, which is why this exists — and why it is deliberately timid.
9
+ *
10
+ * THE FAILURE IT IS BUILT AGAINST. A rewriter asked to cut N chars strips
11
+ * trigger vocabulary before it strips filler, because filler is the harder of
12
+ * the two to identify. A description that loses its trigger phrase costs more
13
+ * in missed routing than it ever saves in tokens, so this tool never rewrites:
14
+ * it applies a closed list of audited, meaning-preserving transformations and
15
+ * then REFUSES to write unless the informative-token set came out identical.
16
+ *
17
+ * "Informative token" is mechanical: lowercased, ≥4 chars, not in STOPWORDS.
18
+ * The set — not the count — must match, so a transformation that swapped one
19
+ * informative word for another of the same length is rejected too.
20
+ *
21
+ * A skill the transformations cannot bring under the cap is reported as a MISS
22
+ * with the residual filler budget beside it, and left alone. That number is
23
+ * what separates "a hand rewrite could still find filler here" from "the only
24
+ * chars left to cut are informative ones" — the second is a genuine
25
+ * trigger-loss wall and the honest reason to stop, and reporting it is the
26
+ * point rather than a consolation for having stopped.
27
+ *
28
+ * Usage:
29
+ * tighten_skill_descriptions --cap 189 # report only (default)
30
+ * tighten_skill_descriptions --cap 189 --apply # write the wins
31
+ * tighten_skill_descriptions --self-test
32
+ *
33
+ * Exit 0 on report and on apply; 1 only on a self-test failure or a usage
34
+ * error. This is an authoring tool, not a gate.
35
+ */
36
+ import * as fs from 'node:fs';
37
+ import * as path from 'node:path';
38
+
39
+ import { SRC_SKILLS } from './_lib/agent_src.js';
40
+
41
+ /** Words too short or too common to carry routing signal. */
42
+ const STOPWORDS = new Set([
43
+ 'when', 'with', 'from', 'that', 'this', 'they', 'them', 'then', 'than', 'into', 'over',
44
+ 'your', 'each', 'both', 'only', 'also', 'such', 'been', 'were', 'will', 'would', 'have',
45
+ 'here', 'what', 'which', 'while', 'their', 'there', 'these', 'those', 'about', 'after',
46
+ 'before', 'every', 'other', 'under', 'until', 'using', 'uses', 'used', 'more', 'most',
47
+ 'less', 'least', 'same', 'does', 'doing', 'done', 'make', 'makes', 'made',
48
+ ]);
49
+
50
+ export function informativeTokens(s: string): Set<string> {
51
+ const out = new Set<string>();
52
+ for (const raw of s.toLowerCase().split(/[^a-z0-9'’\-]+/)) {
53
+ const t = raw.replace(/^[-']+|[-']+$/g, '');
54
+ if (t.length < 4) continue;
55
+ if (STOPWORDS.has(t)) continue;
56
+ out.add(t);
57
+ }
58
+ return out;
59
+ }
60
+
61
+ /**
62
+ * The closed transformation list. Each is meaning-preserving in English and
63
+ * removes only connective or punctuation characters — never a content word.
64
+ * `why` is printed in the report so a reviewer can audit the list itself.
65
+ */
66
+ export const TRANSFORMS: readonly { why: string; apply: (s: string) => string }[] = [
67
+ { why: '"Use when X" → "When X" (the imperative adds nothing a reader needs)', apply: (s) => s.replace(/^Use when /, 'When ') },
68
+ { why: '"in order to" → "to"', apply: (s) => s.replace(/\bin order to\b/g, 'to') },
69
+ { why: 'Oxford comma removed (house style)', apply: (s) => s.replace(/,(\s+)and\b/g, '$1and') },
70
+ { why: 'collapsed runs of whitespace', apply: (s) => s.replace(/ +/g, ' ') },
71
+ ];
72
+
73
+ /**
74
+ * Filler still available AFTER the transforms — the chars a hand rewrite could
75
+ * remove without touching an informative token. Deliberately generous: it
76
+ * counts every stopword occurrence and every separator run, so a MISS reported
77
+ * with a small budget is a strong claim and one with a large budget is an
78
+ * invitation to rewrite by hand, not a wall.
79
+ */
80
+ export function fillerBudget(s: string): number {
81
+ let n = 0;
82
+ for (const raw of s.toLowerCase().split(/([^a-z0-9'’\-]+)/)) {
83
+ const t = raw.replace(/^[-']+|[-']+$/g, '');
84
+ if (t.length === 0) continue;
85
+ if (/^[^a-z0-9]+$/.test(raw) && raw.length > 1) n += raw.length - 1;
86
+ else if (t.length < 4 || STOPWORDS.has(t)) n += t.length + 1;
87
+ }
88
+ return n;
89
+ }
90
+
91
+ export type Outcome = 'already_under' | 'tightened' | 'miss';
92
+
93
+ export interface Result {
94
+ skill: string;
95
+ before: number;
96
+ after: number;
97
+ outcome: Outcome;
98
+ /** Chars of filler still removable by hand at `after`. Only set on a miss. */
99
+ filler_left: number;
100
+ applied: string[];
101
+ text: string | null;
102
+ }
103
+
104
+ export function tighten(skill: string, desc: string, cap: number): Result {
105
+ const before = desc.length;
106
+ if (before <= cap) {
107
+ return { skill, before, after: before, outcome: 'already_under', filler_left: 0, applied: [], text: null };
108
+ }
109
+ const want = informativeTokens(desc);
110
+ let cur = desc;
111
+ const applied: string[] = [];
112
+ for (const t of TRANSFORMS) {
113
+ const next = t.apply(cur).trim();
114
+ if (next === cur) continue;
115
+ // Reject any transformation that moved the informative set, even if it
116
+ // shortened the string. A same-length swap is still a routing change.
117
+ const got = informativeTokens(next);
118
+ if (got.size !== want.size || [...want].some((w) => !got.has(w))) continue;
119
+ cur = next;
120
+ applied.push(t.why);
121
+ }
122
+ if (cur.length <= cap) {
123
+ return { skill, before, after: cur.length, outcome: 'tightened', filler_left: 0, applied, text: cur };
124
+ }
125
+ return {
126
+ skill,
127
+ before,
128
+ after: cur.length,
129
+ outcome: 'miss',
130
+ filler_left: fillerBudget(cur),
131
+ applied,
132
+ text: null,
133
+ };
134
+ }
135
+
136
+ const DESC_RE = /^(description:[ \t]*)(.*?)[ \t]*$/m;
137
+
138
+ function unquote(v: string): { text: string; quote: string } {
139
+ const q = v.length > 1 && (v[0] === '"' || v[0] === "'") && v[v.length - 1] === v[0] ? String(v[0]) : '';
140
+ return { text: q ? v.slice(1, -1) : v, quote: q };
141
+ }
142
+
143
+ interface Skill {
144
+ name: string;
145
+ file: string;
146
+ raw: string;
147
+ desc: string;
148
+ quote: string;
149
+ }
150
+
151
+ function collect(root: string): Skill[] {
152
+ const out: Skill[] = [];
153
+ if (!fs.existsSync(root)) return out;
154
+ for (const d of fs.readdirSync(root, { withFileTypes: true })) {
155
+ if (!d.isDirectory()) continue;
156
+ const file = path.join(root, d.name, 'SKILL.md');
157
+ if (!fs.existsSync(file)) continue;
158
+ const raw = fs.readFileSync(file, 'utf-8');
159
+ const m = DESC_RE.exec(raw);
160
+ if (m === null) continue;
161
+ const { text, quote } = unquote(String(m[2]));
162
+ out.push({ name: d.name, file, raw, desc: text, quote });
163
+ }
164
+ return out;
165
+ }
166
+
167
+ function selfTest(): number {
168
+ const cases: { name: string; desc: string; cap: number; outcome: Outcome; after?: number }[] = [
169
+ { name: 'under cap is untouched', desc: 'Short one.', cap: 189, outcome: 'already_under' },
170
+ {
171
+ name: 'the Use-when prefix is the cheapest real win',
172
+ desc: 'Use when ' + 'x'.repeat(184),
173
+ cap: 189,
174
+ outcome: 'tightened',
175
+ after: 189,
176
+ },
177
+ {
178
+ name: 'nothing to remove is a miss, not a silent truncation',
179
+ desc: 'sqlinjection '.repeat(16),
180
+ cap: 100,
181
+ outcome: 'miss',
182
+ },
183
+ ];
184
+ let pass = 0;
185
+ let fail = 0;
186
+ for (const c of cases) {
187
+ const r = tighten(c.name, c.desc, c.cap);
188
+ const ok = r.outcome === c.outcome && (c.after === undefined || r.after === c.after);
189
+ if (ok) pass += 1;
190
+ else {
191
+ fail += 1;
192
+ console.error(`❌ ${c.name}: outcome=${r.outcome} after=${r.after}`);
193
+ }
194
+ }
195
+ // The load-bearing guard: a transformation that changed the informative
196
+ // set must be refused even though it shortens.
197
+ const before = informativeTokens('deploy kubernetes manifests');
198
+ const after = informativeTokens('deploy kubernetes manifest');
199
+ if (before.size === after.size && [...before].every((t) => after.has(t))) {
200
+ fail += 1;
201
+ console.error('❌ informativeTokens: singular/plural must NOT compare equal — the guard would let a word change through');
202
+ } else {
203
+ pass += 1;
204
+ }
205
+ process.stdout.write(`tighten_skill_descriptions --self-test: ${pass} pass, ${fail} fail\n`);
206
+ return fail === 0 ? 0 : 1;
207
+ }
208
+
209
+ function main(argv: string[]): number {
210
+ if (argv.includes('--self-test')) return selfTest();
211
+ const capIdx = argv.indexOf('--cap');
212
+ const cap = capIdx >= 0 && argv[capIdx + 1] !== undefined ? Number(argv[capIdx + 1]) : 189;
213
+ if (!Number.isInteger(cap) || cap < 40) {
214
+ process.stderr.write('--cap must be an integer ≥ 40\n');
215
+ return 1;
216
+ }
217
+ const apply = argv.includes('--apply');
218
+ const skills = collect(SRC_SKILLS());
219
+ if (skills.length === 0) {
220
+ process.stderr.write(`no skills found under ${SRC_SKILLS()} — refusing to report a zero\n`);
221
+ return 1;
222
+ }
223
+ const results = skills.map((s) => tighten(s.name, s.desc, cap));
224
+ const wins = results.filter((r) => r.outcome === 'tightened');
225
+ const misses = results.filter((r) => r.outcome === 'miss');
226
+
227
+ if (apply) {
228
+ for (const r of wins) {
229
+ const s = skills.find((x) => x.name === r.skill);
230
+ if (s === undefined || r.text === null) continue;
231
+ const replaced = s.raw.replace(DESC_RE, (_m, k: string) => `${k}${s.quote}${String(r.text)}${s.quote}`);
232
+ fs.writeFileSync(s.file, replaced);
233
+ }
234
+ }
235
+
236
+ const savedChars = wins.reduce((a, r) => a + (r.before - r.after), 0);
237
+ process.stdout.write(`cap ${cap} · ${skills.length} skill(s)\n`);
238
+ process.stdout.write(`tightened: ${wins.length} (${savedChars} chars, ≈${Math.round(savedChars / 4)} tok)${apply ? ' — WRITTEN' : ' — dry run'}\n`);
239
+ process.stdout.write(
240
+ `misses: ${misses.length} (left alone — the closed transformation list did not reach the cap)\n`,
241
+ );
242
+ if (misses.length > 0) {
243
+ const need = Math.max(...misses.map((m) => m.after - cap));
244
+ const minFiller = Math.min(...misses.map((m) => m.filler_left));
245
+ process.stdout.write(
246
+ minFiller > need
247
+ ? ` read the table: every miss still holds ≥ ${minFiller} chars of filler against a need of ≤ ${need}, ` +
248
+ 'so these are limits of THIS tool, not trigger-loss walls. A hand rewrite is available.\n'
249
+ : ' at least one miss is close to its filler floor — there a cut would start taking informative tokens.\n',
250
+ );
251
+ }
252
+ if (misses.length > 0) {
253
+ process.stdout.write('\n| skill | len | over | filler left |\n|---|---|---|---|\n');
254
+ for (const m of misses.sort((a, b) => b.after - a.after)) {
255
+ process.stdout.write(`| \`${m.skill}\` | ${m.after} | +${m.after - cap} | ${m.filler_left} |\n`);
256
+ }
257
+ }
258
+ process.stdout.write(`scanned: ${skills.length}\n`);
259
+ return 0;
260
+ }
261
+
262
+ if (process.argv[1] !== undefined && process.argv[1].includes('tighten_skill_descriptions')) {
263
+ process.exit(main(process.argv.slice(2)));
264
+ }