@event4u/agent-config 16.0.0 → 16.2.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 (382) 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 +289 -140
  5. package/CONTRIBUTING.md +9 -0
  6. package/README.md +2 -2
  7. package/dist/agent-src/commands/analyze/inbox.md +26 -0
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
  9. package/dist/agent-src/commands/challenge-me/closure.md +178 -0
  10. package/dist/agent-src/commands/challenge-me.md +9 -2
  11. package/dist/agent-src/commands/council/default.md +25 -5
  12. package/dist/agent-src/commands/feature/plan.md +28 -1
  13. package/dist/agent-src/commands/feature/roadmap.md +28 -1
  14. package/dist/agent-src/commands/implement-ticket.md +27 -0
  15. package/dist/agent-src/commands/jira-ticket.md +27 -0
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/pr/merge.md +60 -9
  18. package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
  19. package/dist/agent-src/commands/roadmap/create.md +74 -27
  20. package/dist/agent-src/commands/roadmap/materialize.md +27 -0
  21. package/dist/agent-src/commands/roadmap/process-full.md +123 -18
  22. package/dist/agent-src/commands/team.md +22 -0
  23. package/dist/agent-src/commands/work.md +4 -0
  24. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  25. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
  26. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  27. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
  28. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  29. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  30. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  31. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  32. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  33. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  34. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  35. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  36. package/dist/agent-src/rules/delegation-policy.md +23 -45
  37. package/dist/agent-src/rules/design-fidelity.md +32 -37
  38. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  39. package/dist/agent-src/rules/test-first.md +41 -0
  40. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  41. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  42. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  43. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  44. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  45. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  46. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  47. package/dist/agent-src/skills/design-review/SKILL.md +80 -4
  48. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  49. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
  50. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  51. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  52. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  53. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  54. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  55. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  56. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  57. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  58. package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
  59. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  60. package/dist/agent-src/templates/agent-settings.md +1 -1
  61. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  62. package/dist/agent-src/templates/roadmaps.md +218 -0
  63. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  64. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  65. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  66. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  67. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  68. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  69. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  70. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  71. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  72. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  73. package/dist/cli/python/knowledge_ingest.js +1 -1
  74. package/dist/cli/python/workspace_drive.js +1 -1
  75. package/dist/cli-delegate/check_update_banner.js +40 -2
  76. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  77. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
  78. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  79. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  80. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  81. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
  82. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  83. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
  84. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
  85. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
  86. package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
  87. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  88. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  89. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  90. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  91. package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
  92. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  93. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  94. package/dist/cli-delegate/cmd_conformance.js +985 -831
  95. package/dist/cli-delegate/cmd_doctor.js +7 -5
  96. package/dist/cli-delegate/cmd_explain.js +2 -2
  97. package/dist/cli-delegate/cmd_export.js +9 -7
  98. package/dist/cli-delegate/cmd_fleet.js +4 -2
  99. package/dist/cli-delegate/cmd_handoff.js +7 -7
  100. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  101. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  102. package/dist/cli-delegate/cmd_migrate.js +2 -2
  103. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  104. package/dist/cli-delegate/cmd_preflight.js +4 -2
  105. package/dist/cli-delegate/cmd_prune.js +10 -8
  106. package/dist/cli-delegate/cmd_route_audit.js +3 -3
  107. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  108. package/dist/cli-delegate/cmd_session_recycle.js +4 -4
  109. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  110. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  111. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  112. package/dist/cli-delegate/cmd_sync.js +10 -8
  113. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  114. package/dist/cli-delegate/cmd_update.js +1 -1
  115. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  116. package/dist/cli-delegate/cmd_validate.js +12 -10
  117. package/dist/cli-delegate/cmd_versions.js +1 -1
  118. package/dist/discovery/deprecation-report.md +1 -1
  119. package/dist/discovery/discovery-manifest.json +90 -43
  120. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  121. package/dist/discovery/discovery-manifest.summary.md +4 -4
  122. package/dist/discovery/dormancy-report.md +1 -1
  123. package/dist/discovery/orphan-report.md +1 -1
  124. package/dist/discovery/packs.json +10 -8
  125. package/dist/discovery/trust-report.md +3 -3
  126. package/dist/discovery/workspaces.json +8 -6
  127. package/dist/hooks/dispatch.js +7682 -2772
  128. package/dist/hooks/dispatch.sha256 +1 -0
  129. package/dist/install/claudeRuleRewrite.js +14 -3
  130. package/dist/install/conflict.js +60 -10
  131. package/dist/install/conflictTracker.js +218 -0
  132. package/dist/install/fsPrimitives.js +54 -0
  133. package/dist/install/install.mjs +2200 -1559
  134. package/dist/install/manifestFiles.js +259 -0
  135. package/dist/install/preflight.js +14 -2
  136. package/dist/install/preserve.js +157 -0
  137. package/dist/install/recordedOwnership.js +132 -0
  138. package/dist/install/txlog.js +11 -2
  139. package/dist/mcp/registry-manifest.json +3 -3
  140. package/dist/mcp/server.json +2 -2
  141. package/dist/mcp/server.mjs +3066 -1470
  142. package/dist/router.json +1 -1
  143. package/dist/scripts/_lib/agent_settings.js +3 -2
  144. package/dist/scripts/_lib/fs_atomic.js +167 -0
  145. package/dist/scripts/_lib/installed_tools.js +460 -0
  146. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  147. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  148. package/dist/server/routes/install.js +8 -0
  149. package/dist/server/routes/wizard.js +9 -3
  150. package/dist/server/schemas/settings.js +40 -5
  151. package/dist/shared/missionExecution.js +97 -0
  152. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  153. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  154. package/dist/ui/index.html +2 -2
  155. package/docs/MIGRATION.md +103 -0
  156. package/docs/contracts/settings-classes.md +28 -7
  157. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  158. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  159. package/docs/guidelines/design-fidelity-routing.md +29 -0
  160. package/package.json +4 -4
  161. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  162. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  163. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  164. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  165. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  166. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  167. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  168. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  169. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  170. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  171. package/src/config/agent-settings.template.yml +168 -27
  172. package/src/config/auto-rule-budget.json +16 -0
  173. package/src/config/ci-local-parity.yml +36 -5
  174. package/src/config/continuity-surface.json +23 -2
  175. package/src/config/gate-coverage.yml +377 -13
  176. package/src/config/gate-violation-baselines.json +10 -6
  177. package/src/config/hook-latency-budget.json +21 -0
  178. package/src/config/hook-token-budget.json +4 -2
  179. package/src/config/loop-surfaces.yaml +215 -0
  180. package/src/config/metric-registry.yml +114 -0
  181. package/src/config/placeholder-drift-budget.json +10 -0
  182. package/src/config/platform-anchor.json +3 -2
  183. package/src/config/release-gate-locality.yml +11 -0
  184. package/src/config/routing-coverage-seed.json +33 -5
  185. package/src/config/rule-enforcement-baseline.json +0 -1
  186. package/src/install/claudeRuleRewrite.ts +16 -2
  187. package/src/install/conflict.ts +65 -9
  188. package/src/install/conflictTracker.ts +299 -0
  189. package/src/install/fsPrimitives.ts +54 -0
  190. package/src/install/manifestFiles.ts +312 -0
  191. package/src/install/preflight.ts +16 -2
  192. package/src/install/preserve.ts +205 -0
  193. package/src/install/recordedOwnership.ts +149 -0
  194. package/src/install/txlog.ts +11 -2
  195. package/src/install/types.ts +19 -4
  196. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  197. package/src/scripts/_cli/cmd_doctor.ts +7 -20
  198. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  199. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  200. package/src/scripts/_cli/doctor_execution.ts +173 -0
  201. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  202. package/src/scripts/_lib/activation_payload.ts +247 -0
  203. package/src/scripts/_lib/agent_settings.ts +4 -2
  204. package/src/scripts/_lib/authority_path.ts +102 -0
  205. package/src/scripts/_lib/body_portable.ts +102 -0
  206. package/src/scripts/_lib/cascade_base.ts +94 -0
  207. package/src/scripts/_lib/cc_transcript.ts +132 -1
  208. package/src/scripts/_lib/changelog_eras.ts +7 -2
  209. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  210. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  211. package/src/scripts/_lib/council_transport.ts +129 -0
  212. package/src/scripts/_lib/delivery_ready.ts +112 -0
  213. package/src/scripts/_lib/forge_protection.ts +381 -0
  214. package/src/scripts/_lib/forge_reader.ts +558 -0
  215. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  216. package/src/scripts/_lib/host_env_write.ts +111 -0
  217. package/src/scripts/_lib/json_python_parity.ts +127 -0
  218. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  219. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  220. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  221. package/src/scripts/_lib/mission_record.ts +190 -0
  222. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  223. package/src/scripts/_lib/obligations.ts +513 -0
  224. package/src/scripts/_lib/orchestration_record.ts +11 -20
  225. package/src/scripts/_lib/probe_inputs.ts +271 -0
  226. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  227. package/src/scripts/_lib/release_highlights.ts +201 -11
  228. package/src/scripts/_lib/release_holds.ts +493 -0
  229. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  230. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  231. package/src/scripts/_lib/rides_along.ts +102 -0
  232. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  233. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  234. package/src/scripts/_lib/structural_hiding.ts +622 -0
  235. package/src/scripts/_lib/test_delta.ts +166 -0
  236. package/src/scripts/_lib/test_provenance.ts +171 -0
  237. package/src/scripts/_lib/turn_end_refusals.ts +525 -4
  238. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  239. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  240. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  241. package/src/scripts/_lib/verification_command.ts +324 -0
  242. package/src/scripts/_lib/verification_evidence.ts +647 -0
  243. package/src/scripts/_lib/verify_clause.ts +280 -0
  244. package/src/scripts/ai_council/config.ts +8 -11
  245. package/src/scripts/ai_council/fallback_config.ts +5 -3
  246. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  247. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  248. package/src/scripts/ask_block_census.ts +285 -15
  249. package/src/scripts/before_complete_hook.ts +340 -23
  250. package/src/scripts/bench_hook_latency.ts +412 -5
  251. package/src/scripts/cache_realization_report.ts +61 -1
  252. package/src/scripts/check_argument_hint.ts +287 -0
  253. package/src/scripts/check_claims.ts +125 -1
  254. package/src/scripts/check_enforcement_coverage.ts +38 -9
  255. package/src/scripts/check_enforcement_matrix.ts +866 -0
  256. package/src/scripts/check_finding_dispositions.ts +22 -3
  257. package/src/scripts/check_gate_reachability.ts +173 -10
  258. package/src/scripts/check_generator_sync.ts +1025 -0
  259. package/src/scripts/check_host_docs_digest.ts +786 -0
  260. package/src/scripts/check_host_format_column.ts +559 -0
  261. package/src/scripts/check_input_references.ts +228 -0
  262. package/src/scripts/check_inputs_parity.ts +235 -0
  263. package/src/scripts/check_kernel_edit_ratified.ts +47 -5
  264. package/src/scripts/check_kill_switch_table.ts +294 -0
  265. package/src/scripts/check_placeholder_drift.ts +221 -0
  266. package/src/scripts/check_preamble_payload_budget.ts +75 -2
  267. package/src/scripts/check_read_surface_coverage.ts +515 -0
  268. package/src/scripts/check_release_highlights.ts +33 -1
  269. package/src/scripts/check_release_holds.ts +568 -0
  270. package/src/scripts/check_routing_coverage.ts +378 -11
  271. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  272. package/src/scripts/check_static_parity.ts +17 -0
  273. package/src/scripts/check_test_delta.ts +294 -0
  274. package/src/scripts/check_test_weakening.ts +299 -0
  275. package/src/scripts/ci_settle.ts +145 -19
  276. package/src/scripts/closure_scan.ts +447 -0
  277. package/src/scripts/compile_hook_manifest.ts +102 -18
  278. package/src/scripts/council_record_shape.ts +209 -0
  279. package/src/scripts/external_sources_denylist.json +29 -4
  280. package/src/scripts/hook_manifest.json +1 -1
  281. package/src/scripts/hook_manifest.yaml +127 -11
  282. package/src/scripts/hooks/block_config_weakening.ts +229 -4
  283. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  284. package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
  285. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  286. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  287. package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
  288. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  289. package/src/scripts/hooks/concern_registry.ts +6 -0
  290. package/src/scripts/hooks/concern_timings.ts +94 -0
  291. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  292. package/src/scripts/hooks/design_pass_hook.ts +221 -5
  293. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  294. package/src/scripts/hooks/dispatch_hook.ts +25 -19
  295. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  296. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  297. package/src/scripts/hooks/evidence_independence.ts +97 -2
  298. package/src/scripts/hooks/exit_codes.ts +128 -0
  299. package/src/scripts/hooks/host_lowering.json +1 -0
  300. package/src/scripts/hooks/host_lowering.ts +128 -3
  301. package/src/scripts/hooks/host_lowering.yaml +262 -46
  302. package/src/scripts/hooks/host_semantics.ts +30 -2
  303. package/src/scripts/hooks/injection_budget.ts +9 -2
  304. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  305. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  306. package/src/scripts/hooks/memory_recall_hook.ts +539 -0
  307. package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
  308. package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
  309. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  310. package/src/scripts/hooks/permission_gate.ts +1 -2
  311. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  312. package/src/scripts/hooks/replay_hook.ts +2 -1
  313. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  314. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  315. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  316. package/src/scripts/hooks/rule_inject_hook.ts +55 -4
  317. package/src/scripts/hooks/run_continuation_hook.ts +30 -35
  318. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  319. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  320. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  321. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  322. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  323. package/src/scripts/hooks/table_fingerprint.ts +49 -0
  324. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  325. package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
  326. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  327. package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
  328. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  329. package/src/scripts/hooks_doctor.ts +85 -0
  330. package/src/scripts/hooks_status.ts +19 -0
  331. package/src/scripts/install.ts +109 -210
  332. package/src/scripts/janitor.ts +7 -0
  333. package/src/scripts/lint_decision_classes.ts +464 -0
  334. package/src/scripts/lint_deny_text.ts +419 -0
  335. package/src/scripts/lint_exit_codes.ts +262 -0
  336. package/src/scripts/lint_hook_manifest.ts +66 -0
  337. package/src/scripts/lint_major_migration_sections.ts +450 -0
  338. package/src/scripts/lint_plan_risk_register.ts +47 -3
  339. package/src/scripts/lint_roadmap_blockers.ts +139 -12
  340. package/src/scripts/lint_roadmap_producers.ts +202 -0
  341. package/src/scripts/lint_skill_descriptions.ts +1 -1
  342. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  343. package/src/scripts/mcp_server/prompts.ts +142 -1
  344. package/src/scripts/measure_rule_budget.ts +101 -5
  345. package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
  346. package/src/scripts/measure_turn_end_gate.ts +193 -0
  347. package/src/scripts/memory_lookup.ts +1 -1
  348. package/src/scripts/new_roadmap.ts +37 -0
  349. package/src/scripts/orchestration_record.ts +0 -5
  350. package/src/scripts/prepack-check.mjs +53 -0
  351. package/src/scripts/release.ts +14 -98
  352. package/src/scripts/release_drill.ts +267 -1
  353. package/src/scripts/release_publication.ts +64 -1
  354. package/src/scripts/report_enforcement_drift.ts +566 -0
  355. package/src/scripts/report_host_injection_effect.ts +3 -11
  356. package/src/scripts/report_invocation_surface.ts +383 -0
  357. package/src/scripts/report_obligation_carriers.ts +699 -0
  358. package/src/scripts/report_obligation_writer_split.ts +236 -0
  359. package/src/scripts/report_skill_menu_census.ts +208 -0
  360. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  361. package/src/scripts/roadmap_verify_share.ts +213 -0
  362. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  363. package/src/scripts/schemas/command.schema.json +48 -0
  364. package/src/scripts/schemas/review-findings.schema.json +4 -0
  365. package/src/scripts/schemas/skill.schema.json +44 -0
  366. package/src/scripts/scope_growth.ts +219 -0
  367. package/src/scripts/self_review_gate.ts +359 -10
  368. package/src/scripts/semantic_parity_census.ts +314 -0
  369. package/src/scripts/session_eol_report.ts +54 -1
  370. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  371. package/src/scripts/trigger_eval_grandfather.json +0 -3
  372. package/src/scripts/trigger_eval_rotation.ts +122 -27
  373. package/src/scripts/ui_conformance_probe.ts +774 -0
  374. package/src/scripts/update_prices.ts +85 -3
  375. package/src/scripts/write_bundle_digest.mjs +74 -0
  376. package/src/server/schemas/settings.ts +62 -7
  377. package/src/shared/missionExecution.ts +126 -0
  378. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  379. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
  380. package/dist/ui/assets/index-D0jaCRPM.js +0 -29
  381. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
  382. /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
@@ -3,7 +3,8 @@
3
3
  * Hard-gate linter for the roadmap `## Blockers` contract
4
4
  * (`templates/roadmaps.md` rule 20 / `roadmap-ci-steps-policy` siblings).
5
5
  *
6
- * Validates, for every active roadmap under `agents/roadmaps/*.md`:
6
+ * Validates, for every active roadmap and every parked idea one directory
7
+ * down in `stubs/`:
7
8
  *
8
9
  * 1. Every `### blocker: <id>` entry declares all five required fields
9
10
  * (Status, Owner, Blocks, What to do, Resolved when).
@@ -22,16 +23,71 @@
22
23
  * road-to-asked-not-parked exists to stop; without this field a marker
23
24
  * cannot distinguish a declined decision from an unoffered one.
24
25
  *
25
- * HARD, not ratcheted, and it can be: no active roadmap carries a real
26
- * checkbox annotation today, so on the day it ships the rule fires on
27
- * nothing — the same "no backlog to grandfather" argument the `Class:`
28
- * contract above makes. `later/` and `archive/` are outside this gate's
29
- * glob and are untouched by it.
26
+ * HARD, not ratcheted, and it can be: no file in scope carries a real
27
+ * checkbox annotation pointing at a blocker today, so on the day it ships
28
+ * the rule fires on nothing — the same "no backlog to grandfather"
29
+ * argument the `Class:` contract above makes. Re-measured when `stubs/`
30
+ * entered scope on 2026-09-28: 7 stubs carry checkboxes and 0 of them
31
+ * carry a `blocked-by:` marker, so the rule stays latent there too.
32
+ * `later/`, `archive/` and `skipped/` are outside this gate's glob and
33
+ * are untouched by it; `stubs/` is inside it — see § SCOPE.
34
+ *
35
+ * 5. An entry that declares `- **Ownership:**` declares one of the three
36
+ * OWNER-OWNED classes (`product-owned`, `business-owned`,
37
+ * `destructive-owned`). A technical class there is a judgement call
38
+ * parked in a file instead of closed, which is the shape ADR-268 § 10
39
+ * retires: a technical decision does not become owner-owned because it is
40
+ * hard, so it routes back through the closure pass rather than into
41
+ * `## Blockers`.
42
+ *
43
+ * HARD rather than ratcheted, on the same "no backlog to grandfather"
44
+ * argument the `Class:` contract makes: `Ownership:` is a new opt-in
45
+ * field, so on the day it ships no entry in the tree declares one and the
46
+ * rule fires on nothing.
47
+ *
48
+ * SCOPE — the active tree plus `stubs/`, and deliberately nothing else.
49
+ *
50
+ * Widened from the active tree alone by an owner ruling on 2026-09-28. Before
51
+ * it, a stub's hold was unreachable by every blocker gate: a parked idea is
52
+ * where a hold sits LONGEST, so excluding it exempted exactly the entries most
53
+ * likely to rot. The rejected alternative was to keep the scope and reword the
54
+ * acceptance criterion that named this gate — refused because a criterion
55
+ * edited to match what was achieved stops being an acceptance test.
56
+ *
57
+ * Measured on the tree the widening shipped against: 121 files under `stubs/`
58
+ * (120 stubs plus the directory README), of which 6 carry a `### blocker:`
59
+ * heading and 4 declare one open inside a `## Blockers` section. Zero hard
60
+ * violations, zero additions to the decidability ratchet, zero new
61
+ * active/archived overlaps. So it lands on nothing — the same "no backlog to
62
+ * grandfather" argument the `Class:` and `Ownership:` contracts make.
63
+ *
64
+ * REACH, stated honestly, because the obvious reading of the paragraph above
65
+ * is wrong: this does NOT hold every future stub blocker to the contract. Both
66
+ * scanners require the entry to sit under a `## Blockers` heading, and 2 of
67
+ * today's 6 put it under `## State` instead — so a third of the population is
68
+ * in the glob and still unread. Stub headings are free-form, unlike the
69
+ * template-driven roadmaps this gate was written for, so that is the likely
70
+ * shape rather than an edge case. Widening the glob fixes WHERE the gate looks
71
+ * and not WHAT it can parse; the second half is the sibling defect recorded in
72
+ * `stubs/road-to-blocker-parse-visibility.md`, and it is untouched here.
73
+ *
74
+ * `later/`, `archive/` and `skipped/` stay OUT, and the reason is not symmetry:
75
+ * those record decisions already taken (parked, closed, dropped), so a blocker
76
+ * left unresolved there is history rather than debt. A stub records a decision
77
+ * still to take. The polarity is pinned in both directions by
78
+ * `tests/scripts/lint_roadmap_blockers.test.ts` § the scanned scope, so
79
+ * widening further cannot happen by accident.
80
+ *
81
+ * One consequence worth naming rather than discovering: `_archiveOverlap`
82
+ * defaults its ACTIVE corpus to this glob, so a stub declaring a blocker open
83
+ * that an archived roadmap also declares open now fails. That is the same
84
+ * self-contradiction the assertion already caught one directory up, and it
85
+ * measured 0 on the widening tree.
30
86
  *
31
87
  * Fenced code blocks are stripped before scanning so a roadmap that shows
32
88
  * the `## Blockers` shape as a documentation example is not flagged.
33
89
  *
34
- * Exit codes: 0 = clean / no active roadmaps, 1 = violations.
90
+ * Exit codes: 0 = clean / nothing in scope, 1 = violations.
35
91
  */
36
92
 
37
93
  import * as fs from 'node:fs';
@@ -45,7 +101,14 @@ const _HERE = fileURLToPath(import.meta.url);
45
101
  const QUIET = process.argv.slice(2).includes('--quiet');
46
102
 
47
103
  const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
48
- const ROADMAP_GLOB = 'agents/roadmaps/*.md';
104
+ /**
105
+ * The scanned scope, for display. Two plain globs rather than one brace
106
+ * expression: nothing globs with this string, and a `{,stubs/}` empty
107
+ * alternative is bash-only syntax that would mislead anyone who tried.
108
+ */
109
+ const ROADMAP_GLOB = 'agents/roadmaps/*.md + agents/roadmaps/stubs/*.md';
110
+ /** The one subdirectory inside the glob. See § SCOPE for why only this one. */
111
+ const SCANNED_SUBDIRS: readonly string[] = ['stubs'];
49
112
 
50
113
  const FENCED_CODE_RE = /^[ \t]*```[^\n]*\n[\s\S]*?^[ \t]*```[ \t]*$/gm;
51
114
  const BLOCKERS_SECTION_RE = /^##[ \t]+Blockers[ \t]*$/im;
@@ -107,6 +170,29 @@ const KNOWN_CLASSES: ReadonlySet<string> = new Set(['0', '1', '2', '3']);
107
170
  /** The classes whose whole claim is that an agent can execute them. */
108
171
  const RUNNABLE_CLASSES: ReadonlySet<string> = new Set(['0', '1']);
109
172
 
173
+ /**
174
+ * The ownership axis, as a blocker may declare it.
175
+ *
176
+ * Only the three owner-owned classes are legal here. A blocker IS the record
177
+ * of a decision the agent correctly did not own; a technical class in that
178
+ * field says the opposite — that something the ownership ladder can close was
179
+ * filed instead. The remaining five ownership classes are deliberately absent
180
+ * rather than listed as "also accepted".
181
+ */
182
+ const OWNERSHIP_FIELD_RE = /^-[ \t]*\*\*Ownership:\*\*[ \t]*`?([a-z-]+)`?/im;
183
+ const OWNER_OWNED_CLASSES: ReadonlySet<string> = new Set([
184
+ 'product-owned',
185
+ 'business-owned',
186
+ 'destructive-owned',
187
+ ]);
188
+ const TECHNICAL_CLASSES: ReadonlySet<string> = new Set([
189
+ 'deterministic',
190
+ 'reversible-technical',
191
+ 'contested-technical',
192
+ 'critical-technical',
193
+ 'spend-exhaustion',
194
+ ]);
195
+
110
196
  /**
111
197
  * The authored class, or `''` when the entry declares none.
112
198
  *
@@ -248,6 +334,28 @@ function _scanBoth(rawText: string): ScanResult {
248
334
  });
249
335
  }
250
336
  }
337
+ const ownership = OWNERSHIP_FIELD_RE.exec(body);
338
+ if (ownership !== null) {
339
+ const declared = ownership[1] as string;
340
+ if (TECHNICAL_CLASSES.has(declared)) {
341
+ violations.push({
342
+ line: _lineAt(text, sectionStart + cur.start),
343
+ message:
344
+ `blocker '${cur.id}' declares ownership '${declared}', which is ` +
345
+ 'a technical class — a technical decision does not become ' +
346
+ 'owner-owned because it is hard (ADR-268 § 10). Route it back ' +
347
+ 'through the closure pass and record the answer as a ' +
348
+ '`## Decisions` row instead of parking it here',
349
+ });
350
+ } else if (!OWNER_OWNED_CLASSES.has(declared)) {
351
+ violations.push({
352
+ line: _lineAt(text, sectionStart + cur.start),
353
+ message:
354
+ `blocker '${cur.id}' declares unknown ownership '${declared}' ` +
355
+ '(expected product-owned, business-owned or destructive-owned)',
356
+ });
357
+ }
358
+ }
251
359
  const gaps = DECIDABILITY_FIELDS.filter(([, re]) => !re.test(body)).map(
252
360
  ([name]) => name,
253
361
  );
@@ -312,13 +420,14 @@ function _scanBoth(rawText: string): ScanResult {
312
420
  return { hard: violations, decidability };
313
421
  }
314
422
 
315
- /** Sorted `agents/roadmaps/*.md` (non-recursive — active roadmaps only). */
316
- function _globRoadmaps(): string[] {
317
- const dir = path.join(REPO_ROOT, 'agents', 'roadmaps');
423
+ /** Sorted `*.md` directly inside `dir` — never recursive. */
424
+ function _globFlat(dir: string): string[] {
318
425
  let entries: fs.Dirent[];
319
426
  try {
320
427
  entries = fs.readdirSync(dir, { withFileTypes: true });
321
428
  } catch {
429
+ // A missing directory is not an error: `stubs/` is optional, and the
430
+ // dead-scope assertion in `main` is what catches a MOVED root.
322
431
  return [];
323
432
  }
324
433
  const out: string[] = [];
@@ -330,6 +439,21 @@ function _globRoadmaps(): string[] {
330
439
  return out.sort();
331
440
  }
332
441
 
442
+ /**
443
+ * The files this gate judges: active roadmaps, plus `stubs/`. See § SCOPE.
444
+ *
445
+ * Takes its root so the scope can be driven from a fixture tree. Asserting the
446
+ * rule against the real tree would only restate today's file list; the test
447
+ * pins which DIRECTORIES are in and which are out, in both directions.
448
+ */
449
+ function _globRoadmaps(roadmapRoot: string = path.join(REPO_ROOT, 'agents', 'roadmaps')): string[] {
450
+ const out = _globFlat(roadmapRoot);
451
+ for (const sub of SCANNED_SUBDIRS) {
452
+ out.push(..._globFlat(path.join(roadmapRoot, sub)));
453
+ }
454
+ return out;
455
+ }
456
+
333
457
  function _relPosix(target: string, root: string): string {
334
458
  return path.relative(root, target).split(path.sep).join('/');
335
459
  }
@@ -503,7 +627,7 @@ function main(): number {
503
627
  const roadmaps = _globRoadmaps();
504
628
  if (roadmaps.length === 0) {
505
629
  if (!QUIET) {
506
- process.stdout.write(`✅ no active roadmaps under ${ROADMAP_GLOB}\n`);
630
+ process.stdout.write(`✅ nothing in scope under ${ROADMAP_GLOB}\n`);
507
631
  }
508
632
  return 0;
509
633
  }
@@ -628,10 +752,13 @@ if (_isCliEntry() || process.argv[1] === _HERE) {
628
752
  export {
629
753
  REPO_ROOT,
630
754
  ROADMAP_GLOB,
755
+ _stripFencedCode,
631
756
  REQUIRED_FIELDS,
632
757
  DECIDABILITY_FIELDS,
633
758
  KNOWN_CLASSES,
634
759
  RUNNABLE_CLASSES,
760
+ OWNER_OWNED_CLASSES,
761
+ TECHNICAL_CLASSES,
635
762
  _blockerClass,
636
763
  _hasExecutableSubstance,
637
764
  _scan,
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Every roadmap producer ends in closure — mechanically, not by convention.
4
+ *
5
+ * A command that produces a plan and hands it back with open decisions in it
6
+ * has moved those decisions into execution, where meeting one costs a run.
7
+ * The entrance list used to be hard-coded in three command bodies, so the
8
+ * other six producers were simply not in it and nothing said so.
9
+ *
10
+ * The declaration is now the command's own: `produces_roadmap: true` in the
11
+ * frontmatter. This gate reds a command that declares it and does not end in
12
+ * the closure pass, which is the half a hard-coded list could never check.
13
+ *
14
+ * What counts as ending in closure: the body references the closure pass by
15
+ * one of its two names — the `/challenge-me closure` sub-command, or the
16
+ * `closure_scan` detector it runs. Deliberately a reference check rather than
17
+ * a prose check: prose drifts, and a gate that guesses at intent from wording
18
+ * is the detector that gets weakened until it finds nothing.
19
+ *
20
+ * Exit codes: 0 clean · 1 violations · 2 internal error / bad argv.
21
+ */
22
+
23
+ import * as fs from 'node:fs';
24
+ import * as path from 'node:path';
25
+ import process from 'node:process';
26
+ import { fileURLToPath } from 'node:url';
27
+
28
+ import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
29
+
30
+ const _HERE = fileURLToPath(import.meta.url);
31
+ const _PROG = 'lint_roadmap_producers';
32
+
33
+ /** The frontmatter declaration a producer carries. */
34
+ export const DECLARATION_RE = /^produces_roadmap:\s*true\s*$/m;
35
+
36
+ /** Either name of the closure pass discharges the obligation. */
37
+ export const CLOSURE_REFS: readonly RegExp[] = [
38
+ /\/challenge-me\s+closure/,
39
+ /challenge-me\/closure/,
40
+ /\bclosure_scan\b/,
41
+ ];
42
+
43
+ export interface Violation {
44
+ readonly file: string;
45
+ readonly reason: string;
46
+ }
47
+
48
+ function _frontmatter(text: string): string {
49
+ if (!text.startsWith('---')) return '';
50
+ const end = text.indexOf('\n---', 3);
51
+ return end < 0 ? '' : text.slice(0, end);
52
+ }
53
+
54
+ export function declaresProducer(text: string): boolean {
55
+ return DECLARATION_RE.test(_frontmatter(text));
56
+ }
57
+
58
+ export function endsInClosure(text: string): boolean {
59
+ return CLOSURE_REFS.some((re) => re.test(text));
60
+ }
61
+
62
+ export function checkFile(rel: string, text: string): Violation[] {
63
+ if (!declaresProducer(text)) return [];
64
+ if (endsInClosure(text)) return [];
65
+ return [
66
+ {
67
+ file: rel,
68
+ reason:
69
+ 'declares `produces_roadmap: true` and does not end in the closure pass — ' +
70
+ 'a producer hands back an execution contract, not a plan with open decisions ' +
71
+ 'in it. Reference `/challenge-me closure` (or the `closure_scan` detector) ' +
72
+ 'as the command’s last step.',
73
+ },
74
+ ];
75
+ }
76
+
77
+ function _walk(dir: string, out: string[]): void {
78
+ let entries: fs.Dirent[];
79
+ try {
80
+ entries = fs.readdirSync(dir, { withFileTypes: true });
81
+ } catch {
82
+ return;
83
+ }
84
+ for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
85
+ const full = path.join(dir, e.name);
86
+ if (e.isDirectory()) _walk(full, out);
87
+ else if (e.isFile() && e.name === 'command.md') out.push(full);
88
+ }
89
+ }
90
+
91
+ export function listCommands(root: string): string[] {
92
+ const out: string[] = [];
93
+ _walk(root, out);
94
+ return out;
95
+ }
96
+
97
+ function _repoRoot(): string {
98
+ return path.resolve(path.dirname(_HERE), '..', '..');
99
+ }
100
+
101
+ function _usage(): string {
102
+ return (
103
+ `usage: ${_PROG} [-h] [--quiet] [--json] [--self-test]\n\n` +
104
+ `Every command declaring \`produces_roadmap: true\` ends in the closure pass.\n`
105
+ );
106
+ }
107
+
108
+ export function main(argv?: readonly string[]): number {
109
+ const args = argv ?? process.argv.slice(2);
110
+ let quiet = false;
111
+ let json = false;
112
+ let selfTest = false;
113
+ const unknown: string[] = [];
114
+ for (const a of args) {
115
+ if (a === '-h' || a === '--help') {
116
+ process.stdout.write(_usage());
117
+ return 0;
118
+ } else if (a === '--quiet') quiet = true;
119
+ else if (a === '--json') json = true;
120
+ else if (a === '--self-test') selfTest = true;
121
+ else unknown.push(a);
122
+ }
123
+ if (unknown.length) {
124
+ process.stderr.write(`${_usage()}${_PROG}: error: unrecognized arguments: ${unknown.join(' ')}\n`);
125
+ return 2;
126
+ }
127
+ if (selfTest) return _selfTest();
128
+
129
+ const repo = _repoRoot();
130
+ const root = path.join(repo, 'src', 'domains');
131
+ const files = listCommands(root);
132
+ const producers = files.filter((f) => declaresProducer(fs.readFileSync(f, 'utf-8')));
133
+
134
+ try {
135
+ // The corpus is the DECLARED producers, not every command: a tree where
136
+ // nobody declares the key is a tree where the declaration was lost, and
137
+ // that is exactly the silent-disarm this assertion exists to catch.
138
+ assertScanned({
139
+ gate: _PROG,
140
+ scanned: producers.length,
141
+ units: 'roadmap producer(s)',
142
+ roots: ['src/domains'],
143
+ });
144
+ } catch (e) {
145
+ if (e instanceof DeadScopeError) {
146
+ process.stderr.write(`❌ ${e.message}\n`);
147
+ return 2;
148
+ }
149
+ throw e;
150
+ }
151
+
152
+ const violations: Violation[] = [];
153
+ for (const f of producers) {
154
+ violations.push(...checkFile(path.relative(repo, f), fs.readFileSync(f, 'utf-8')));
155
+ }
156
+
157
+ if (json) {
158
+ process.stdout.write(JSON.stringify(violations, null, 2) + '\n');
159
+ return violations.length ? 1 : 0;
160
+ }
161
+ if (violations.length) {
162
+ process.stderr.write(`❌ ${_PROG}: ${violations.length} producer(s) do not end in closure\n`);
163
+ for (const v of violations) process.stderr.write(` ${v.file} — ${v.reason}\n`);
164
+ return 1;
165
+ }
166
+ if (!quiet) {
167
+ process.stdout.write(
168
+ `✅ ${_PROG}: ${producers.length} roadmap producer(s), all ending in closure\n`,
169
+ );
170
+ }
171
+ return 0;
172
+ }
173
+
174
+ /** Both directions in-process: a producer without closure reds, one with it does not. */
175
+ function _selfTest(): number {
176
+ const withClosure = '---\nname: x\nproduces_roadmap: true\n---\n\nRun `/challenge-me closure`.\n';
177
+ const without = '---\nname: x\nproduces_roadmap: true\n---\n\nHand the plan back.\n';
178
+ const notAProducer = '---\nname: x\n---\n\nHand the plan back.\n';
179
+ const failures: string[] = [];
180
+ if (checkFile('a.md', without).length === 0) failures.push('a producer without closure did not red');
181
+ if (checkFile('b.md', withClosure).length !== 0) failures.push('a producer with closure red');
182
+ if (checkFile('c.md', notAProducer).length !== 0) failures.push('a non-producer was judged');
183
+ if (failures.length) {
184
+ for (const f of failures) process.stderr.write(`❌ ${_PROG} --self-test: ${f}\n`);
185
+ return 1;
186
+ }
187
+ process.stdout.write(`✅ ${_PROG} --self-test: both directions hold\n`);
188
+ return 0;
189
+ }
190
+
191
+ function _isCliEntry(): boolean {
192
+ if (!process.argv[1]) return false;
193
+ try {
194
+ return fs.realpathSync(_HERE) === fs.realpathSync(path.resolve(process.argv[1]));
195
+ } catch {
196
+ return false;
197
+ }
198
+ }
199
+
200
+ if (_isCliEntry() || process.argv[1] === _HERE) {
201
+ process.exitCode = main();
202
+ }
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S node --import tsx
1
+ #!/usr/bin/env tsx
2
2
  /**
3
3
  * lint_skill_descriptions — description-quality gate (ecosystem-harvest
4
4
  * skill-quality-gates, Phase 1).
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S node --import tsx
1
+ #!/usr/bin/env tsx
2
2
  /**
3
3
  * lint_skill_scripts_readonly — read-only-by-default script convention
4
4
  * (ecosystem-harvest skill-quality-gates, Phase 3).
@@ -69,6 +69,22 @@ export interface SkillPrompt {
69
69
  readonly kind: PromptKind;
70
70
  readonly recommended_for_user_types: readonly string[];
71
71
  readonly user_type_match: UserTypeMatch;
72
+ /**
73
+ * Declared parameters, from the frontmatter `inputs:` block.
74
+ *
75
+ * Empty for every artifact that declares none, which is the whole corpus
76
+ * today — so `arguments: []` on the wire stays the behaviour for an
77
+ * undeclared prompt rather than becoming a special case
78
+ * (road-to-an-invocation-contract-that-reaches-the-wire 3.2).
79
+ */
80
+ readonly inputs: readonly PromptInput[];
81
+ }
82
+
83
+ /** One declared parameter, reduced to what the MCP `arguments` shape carries. */
84
+ export interface PromptInput {
85
+ readonly name: string;
86
+ readonly description: string;
87
+ readonly required: boolean;
72
88
  }
73
89
 
74
90
  /** Walk up from this file to the repo root (parent of `scripts/`). */
@@ -92,6 +108,124 @@ export function _project_root(): string {
92
108
  * Avoids a YAML dependency for Phase 1; the frontmatter shape is
93
109
  * enforced by `task lint-skills` upstream.
94
110
  */
111
+ /** The raw frontmatter block of a document, or `''` when it has none. */
112
+ export function _frontmatter_text(text: string): string {
113
+ if (!text.startsWith('---\n')) return '';
114
+ const parts = _splitMax(text, '---\n', 2);
115
+ return parts.length < 3 ? '' : (parts[1] as string);
116
+ }
117
+
118
+ /**
119
+ * Parse the frontmatter `inputs:` block out of raw frontmatter text.
120
+ *
121
+ * WHY THIS EXISTS RATHER THAN A YAML CALL. `_strip_frontmatter` is a flat
122
+ * `key: value` line scanner and cannot represent a nested list — `inputs:`
123
+ * would land as an empty string and its `- name: x` lines as stray keys. The
124
+ * MCP server is deliberately stdlib-only and is bundled for the wire, so
125
+ * pulling a YAML parser in to read one optional block would pay a dependency
126
+ * on every install for a block almost nothing declares yet.
127
+ *
128
+ * IT READS ONE SHAPE AND REFUSES THE REST. Accepted:
129
+ *
130
+ * inputs:
131
+ * - name: target
132
+ * type: string
133
+ * required: true
134
+ * description: What to act on.
135
+ *
136
+ * Refused, by yielding NOTHING for the whole block: the flow form
137
+ * (`inputs: [{name: a}]`), a block scalar (`description: >`), and any nested
138
+ * mapping under an item key. Refusing is not silent — `check_inputs_parity`
139
+ * compares this reader against a real YAML parse for every declaring artifact
140
+ * and FAILS the build on any difference, so an unsupported form is a red gate
141
+ * with a named remedy rather than a wrong argument on the wire.
142
+ *
143
+ * That gate is the answer to the defect an independent review found here: the
144
+ * key scan used to run at any depth, so a `default:` MAPPING whose first child
145
+ * was `name:` overwrote the parameter's own name and the server offered a host
146
+ * an argument that did not exist. The header promised "no guessed input" while
147
+ * the code guessed. Depth is now tracked, and where this reader cannot be sure
148
+ * it declines and the parity gate makes the decline visible.
149
+ */
150
+ export function _parse_inputs(fm: string): PromptInput[] {
151
+ const lines = fm.split('\n');
152
+ let i = lines.findIndex((l) => /^inputs:/.test(l));
153
+ if (i === -1) return [];
154
+ // The flow form carries its payload on the `inputs:` line itself. This
155
+ // reader does not parse it; the parity gate turns that into a failure.
156
+ if (!/^inputs:\s*$/.test(lines[i] as string)) return [];
157
+
158
+ const out: PromptInput[] = [];
159
+ let current: { name?: string; description?: string; required?: boolean } | null = null;
160
+ /** Indent of the key lines belonging to the item being read. */
161
+ let keyIndent: number | null = null;
162
+ let refused = false;
163
+
164
+ const flush = (): void => {
165
+ if (current?.name !== undefined) {
166
+ out.push({
167
+ name: current.name,
168
+ description: current.description ?? '',
169
+ required: current.required === true,
170
+ });
171
+ }
172
+ current = null;
173
+ };
174
+
175
+ const clean = (raw: string): string => {
176
+ const v = raw.trim();
177
+ // An unquoted trailing comment is not part of the value — the same strip
178
+ // `_strip_frontmatter` applies, kept consistent deliberately.
179
+ const hashed = /^(?!["'])([^#]*?)\s+#.*$/.exec(v);
180
+ return _stripQuotes((hashed === null ? v : (hashed[1] ?? '')).trim());
181
+ };
182
+
183
+ for (i += 1; i < lines.length && !refused; i += 1) {
184
+ const line = lines[i] as string;
185
+ if (line.trim() === '') continue;
186
+ // A comment at any column is not block content and does not end the block.
187
+ if (/^\s*#/.test(line)) continue;
188
+ // A non-indented line ends the block.
189
+ if (!/^\s/.test(line)) break;
190
+
191
+ const indent = (/^(\s*)/.exec(line) as RegExpExecArray)[1]!.replace(/\t/g, ' ').length;
192
+
193
+ const item = /^\s*-\s+([A-Za-z_]\w*):\s*(.*)$/.exec(line);
194
+ if (item !== null) {
195
+ flush();
196
+ current = {};
197
+ keyIndent = line.indexOf('-') + 2;
198
+ if (item[1] === 'name') current.name = clean(item[2] ?? '');
199
+ continue;
200
+ }
201
+
202
+ const kv = /^\s+([A-Za-z_]\w*):\s*(.*)$/.exec(line);
203
+ if (kv === null || current === null || keyIndent === null) continue;
204
+ // Deeper than the item's own keys: a nested mapping or sequence this
205
+ // reader does not model. Decline the whole block rather than absorb it.
206
+ if (indent > keyIndent) {
207
+ refused = true;
208
+ break;
209
+ }
210
+ const key = kv[1] as string;
211
+ const rawValue = (kv[2] ?? '').trim();
212
+ // A block scalar or an empty value means the real content is on the
213
+ // following lines, which this reader does not join.
214
+ if (rawValue === '' || rawValue === '>' || rawValue === '|') {
215
+ refused = true;
216
+ break;
217
+ }
218
+ const value = clean(rawValue);
219
+ if (key === 'name') current.name = value;
220
+ else if (key === 'description') current.description = value;
221
+ else if (key === 'required') current.required = value.toLowerCase() === 'true';
222
+ }
223
+
224
+ if (refused) return [];
225
+ flush();
226
+ return out;
227
+ }
228
+
95
229
  export function _strip_frontmatter(text: string): [Record<string, string>, string] {
96
230
  if (!text.startsWith('---\n')) {
97
231
  return [{}, text];
@@ -365,6 +499,7 @@ function _load_file(p: string, kind: PromptKind, fallback_name: string): SkillPr
365
499
  kind,
366
500
  recommended_for_user_types: _parse_inline_array(meta.recommended_for_user_types ?? ''),
367
501
  user_type_match: '',
502
+ inputs: _parse_inputs(_frontmatter_text(text)),
368
503
  };
369
504
  }
370
505
 
@@ -548,7 +683,13 @@ export function to_mcp_prompt_meta(prompt: SkillPrompt): Record<string, unknown>
548
683
  name: wire,
549
684
  title: prompt.name,
550
685
  description: prompt.description,
551
- arguments: [],
686
+ // Derived from the declaration, never invented. An artifact that
687
+ // declares nothing still serves `[]` — see `_parse_inputs`.
688
+ arguments: prompt.inputs.map((i) => ({
689
+ name: i.name,
690
+ description: i.description,
691
+ required: i.required,
692
+ })),
552
693
  _meta: meta,
553
694
  };
554
695
  }