@event4u/agent-config 16.1.0 → 16.3.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 (467) 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 +98 -257
  5. package/README.md +14 -12
  6. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  7. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  8. package/dist/agent-src/commands/bug/fix.md +17 -2
  9. package/dist/agent-src/commands/feature/plan.md +1 -1
  10. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  11. package/dist/agent-src/commands/fix/ci.md +1 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/jira-ticket.md +1 -1
  14. package/dist/agent-src/commands/roadmap/create.md +1 -1
  15. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  16. package/dist/agent-src/commands/roadmap/process-full.md +5 -5
  17. package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
  18. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  19. package/dist/agent-src/commands/roadmap.md +18 -4
  20. package/dist/agent-src/commands/tests/create.md +17 -3
  21. package/dist/agent-src/commands/tests/execute.md +22 -5
  22. package/dist/agent-src/commands/work.md +4 -0
  23. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  25. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
  27. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  28. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  29. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  30. package/dist/agent-src/rules/active-remediation.md +1 -1
  31. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  32. package/dist/agent-src/rules/broken-access-control.md +1 -0
  33. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  34. package/dist/agent-src/rules/context-hygiene.md +1 -1
  35. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  36. package/dist/agent-src/rules/design-fidelity.md +13 -16
  37. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  39. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  40. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  41. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  42. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  43. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  44. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  45. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  46. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  47. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  48. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  49. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  50. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  51. package/dist/agent-src/rules/persona-governance.md +2 -2
  52. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  53. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  54. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  55. package/dist/agent-src/rules/runtime-safety.md +1 -0
  56. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  57. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  58. package/dist/agent-src/rules/session-canary.md +8 -62
  59. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  60. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  61. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  62. package/dist/agent-src/rules/tool-safety.md +1 -0
  63. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  64. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  65. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  66. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  67. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  68. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  69. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  70. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
  71. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  72. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  73. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  74. package/dist/agent-src/skills/design-review/SKILL.md +30 -4
  75. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  76. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
  78. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  79. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  80. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  81. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  82. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  83. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  84. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  85. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  86. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  87. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  88. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  89. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  90. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  91. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  92. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  93. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  94. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  95. package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
  96. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  97. package/dist/agent-src/templates/roadmaps.md +30 -0
  98. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  99. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  100. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  101. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  102. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  103. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  104. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  105. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  106. package/dist/cli/python/knowledge_ingest.js +1 -1
  107. package/dist/cli/python/workspace_drive.js +1 -1
  108. package/dist/cli/registry.js +1 -1
  109. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  110. package/dist/cli-delegate/check_update_banner.js +12 -1
  111. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  112. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  113. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  114. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  115. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  116. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  117. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  118. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  119. package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
  120. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  121. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  122. package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
  123. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  124. package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
  125. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  126. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  127. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  128. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  129. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  130. package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
  131. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
  132. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  133. package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
  134. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  135. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  136. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  137. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  138. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  139. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  140. package/dist/cli-delegate/cmd_conformance.js +1862 -809
  141. package/dist/cli-delegate/cmd_doctor.js +13 -6
  142. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  143. package/dist/cli-delegate/cmd_explain.js +5 -5
  144. package/dist/cli-delegate/cmd_export.js +11 -8
  145. package/dist/cli-delegate/cmd_fleet.js +3 -3
  146. package/dist/cli-delegate/cmd_handoff.js +10 -7
  147. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  148. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  149. package/dist/cli-delegate/cmd_migrate.js +2 -2
  150. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  151. package/dist/cli-delegate/cmd_preflight.js +6 -6
  152. package/dist/cli-delegate/cmd_prune.js +11 -8
  153. package/dist/cli-delegate/cmd_refresh.js +2 -1
  154. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  155. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  156. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  157. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  158. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  159. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  160. package/dist/cli-delegate/cmd_sync.js +11 -8
  161. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  162. package/dist/cli-delegate/cmd_update.js +1 -1
  163. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  164. package/dist/cli-delegate/cmd_validate.js +11 -8
  165. package/dist/cli-delegate/cmd_versions.js +1 -1
  166. package/dist/cli-delegate/deferral_carry.js +16 -0
  167. package/dist/cli-delegate/gate_execute.js +2 -2
  168. package/dist/cli-delegate/roadmap_gates.js +5 -5
  169. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  170. package/dist/discovery/deprecation-report.md +1 -1
  171. package/dist/discovery/discovery-manifest.json +132 -80
  172. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  173. package/dist/discovery/discovery-manifest.summary.md +3 -3
  174. package/dist/discovery/dormancy-report.md +1 -1
  175. package/dist/discovery/orphan-report.md +1 -1
  176. package/dist/discovery/packs.json +7 -5
  177. package/dist/discovery/trust-report.md +2 -2
  178. package/dist/discovery/workspaces.json +6 -4
  179. package/dist/hooks/dispatch.js +7972 -5186
  180. package/dist/hooks/dispatch.sha256 +1 -0
  181. package/dist/install/claudePathsPlan.js +47 -6
  182. package/dist/install/claudeRuleRewrite.js +7 -0
  183. package/dist/install/emit_host_rules_cli.js +39 -36
  184. package/dist/install/globalRuleScope.js +87 -0
  185. package/dist/install/host_rule_ownership.js +160 -0
  186. package/dist/install/install.mjs +2226 -1055
  187. package/dist/install/installThinLayer.js +261 -0
  188. package/dist/install/installedRuleLinks.js +176 -0
  189. package/dist/install/wizard-plan.js +24 -0
  190. package/dist/mcp/registry-manifest.json +3 -3
  191. package/dist/mcp/server.json +2 -2
  192. package/dist/mcp/server.mjs +40913 -34865
  193. package/dist/scripts/_lib/agent_settings.js +11 -0
  194. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  195. package/dist/scripts/_lib/installed_layer.js +378 -0
  196. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  197. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  198. package/dist/scripts/_lib/rule_injection.js +67 -10
  199. package/dist/scripts/_lib/rule_law_section.js +122 -0
  200. package/dist/scripts/_lib/thin_rules.js +458 -0
  201. package/dist/scripts/_lib/windsurf_render.js +60 -0
  202. package/dist/scripts/condense.js +14 -26
  203. package/dist/scripts/project_thin_rules.js +14 -294
  204. package/dist/server/schemas/settings.js +29 -0
  205. package/dist/shared/skillRanking.js +74 -13
  206. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  207. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  208. package/dist/ui/index.html +2 -2
  209. package/docs/contracts/settings-classes.md +10 -6
  210. package/package.json +4 -4
  211. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  212. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  213. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  214. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  215. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  216. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  217. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  218. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  219. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  220. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  221. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  222. package/src/config/agent-settings.template.yml +91 -22
  223. package/src/config/ci-local-parity.yml +18 -0
  224. package/src/config/continuity-surface.json +9 -2
  225. package/src/config/evaluator-budgets.json +4 -4
  226. package/src/config/gate-coverage.yml +327 -3
  227. package/src/config/gate-violation-baselines.json +21 -9
  228. package/src/config/hook-bundle-budget.json +25 -0
  229. package/src/config/hook-latency-budget.json +21 -0
  230. package/src/config/hook-token-budget.json +8 -3
  231. package/src/config/host-instruction-limits.json +33 -0
  232. package/src/config/metric-registry.yml +114 -0
  233. package/src/config/placeholder-drift-budget.json +10 -0
  234. package/src/config/routing-coverage-seed.json +38 -3
  235. package/src/config/rule-activation-census.json +7 -3
  236. package/src/config/rule-consequence-class.json +161 -0
  237. package/src/config/rule-law-ceilings.json +99 -0
  238. package/src/install/claudePathsPlan.ts +46 -4
  239. package/src/install/claudeRuleRewrite.ts +10 -1
  240. package/src/install/emit_host_rules_cli.ts +43 -34
  241. package/src/install/globalRuleScope.ts +109 -0
  242. package/src/install/host_rule_ownership.ts +170 -0
  243. package/src/install/installThinLayer.ts +312 -0
  244. package/src/install/installedRuleLinks.ts +220 -0
  245. package/src/install/wizard-plan.ts +24 -0
  246. package/src/scripts/_cli/cmd_doctor.ts +28 -90
  247. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  248. package/src/scripts/_cli/doctor_execution.ts +100 -17
  249. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  250. package/src/scripts/_dispatch.bash +16 -2
  251. package/src/scripts/_lib/agent_settings.ts +11 -0
  252. package/src/scripts/_lib/body_portable.ts +102 -0
  253. package/src/scripts/_lib/cc_transcript.ts +132 -1
  254. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  255. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  256. package/src/scripts/_lib/estate_offsets.ts +241 -0
  257. package/src/scripts/_lib/exemption_shape.ts +143 -0
  258. package/src/scripts/_lib/fnv.ts +23 -0
  259. package/src/scripts/_lib/foreign_scan.ts +106 -0
  260. package/src/scripts/_lib/forge_protection.ts +114 -0
  261. package/src/scripts/_lib/forge_reader.ts +558 -0
  262. package/src/scripts/_lib/gitignore_block.ts +314 -0
  263. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  264. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  265. package/src/scripts/_lib/hook_settings.ts +73 -0
  266. package/src/scripts/_lib/host_capability.ts +40 -19
  267. package/src/scripts/_lib/host_env_write.ts +111 -0
  268. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  269. package/src/scripts/_lib/installed_layer.ts +538 -0
  270. package/src/scripts/_lib/json_merge.ts +82 -0
  271. package/src/scripts/_lib/json_pointers.ts +93 -11
  272. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  273. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  274. package/src/scripts/_lib/machine_wake.ts +16 -3
  275. package/src/scripts/_lib/neighbour_census.ts +760 -0
  276. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  277. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  278. package/src/scripts/_lib/obligations.ts +119 -8
  279. package/src/scripts/_lib/probe_inputs.ts +271 -0
  280. package/src/scripts/_lib/prompt_shape.ts +46 -0
  281. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  282. package/src/scripts/_lib/release_highlights.ts +2 -1
  283. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  284. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  285. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  286. package/src/scripts/_lib/review_coverage.ts +134 -0
  287. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  288. package/src/scripts/_lib/rule_injection.ts +130 -10
  289. package/src/scripts/_lib/rule_law_section.ts +147 -0
  290. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  291. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  292. package/src/scripts/_lib/skill_origin.ts +254 -0
  293. package/src/scripts/_lib/structural_hiding.ts +622 -0
  294. package/src/scripts/_lib/structured_ask.ts +64 -21
  295. package/src/scripts/_lib/thin_rules.ts +540 -0
  296. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  297. package/src/scripts/_lib/turn_end_refusals.ts +600 -4
  298. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  299. package/src/scripts/_lib/verification_command.ts +324 -0
  300. package/src/scripts/_lib/verification_evidence.ts +647 -0
  301. package/src/scripts/_lib/verify_clause.ts +280 -0
  302. package/src/scripts/_lib/windsurf_render.ts +63 -0
  303. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  304. package/src/scripts/ai_council/clients.ts +26 -57
  305. package/src/scripts/ai_council/config.ts +1 -1
  306. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  307. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  308. package/src/scripts/audit_skill_overlap.ts +261 -0
  309. package/src/scripts/before_complete_hook.ts +573 -25
  310. package/src/scripts/bench_hook_latency.ts +412 -5
  311. package/src/scripts/bench_touched_file_quality.ts +238 -0
  312. package/src/scripts/cache_realization_report.ts +61 -1
  313. package/src/scripts/check_argument_hint.ts +287 -0
  314. package/src/scripts/check_claims.ts +125 -1
  315. package/src/scripts/check_enforcement_coverage.ts +38 -9
  316. package/src/scripts/check_enforcement_matrix.ts +621 -22
  317. package/src/scripts/check_estate_count.ts +130 -153
  318. package/src/scripts/check_finding_dispositions.ts +138 -7
  319. package/src/scripts/check_generator_sync.ts +71 -0
  320. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  321. package/src/scripts/check_host_docs_digest.ts +786 -0
  322. package/src/scripts/check_host_format_column.ts +559 -0
  323. package/src/scripts/check_input_references.ts +228 -0
  324. package/src/scripts/check_inputs_parity.ts +235 -0
  325. package/src/scripts/check_kernel_edit_ratified.ts +89 -9
  326. package/src/scripts/check_kill_switch_table.ts +294 -0
  327. package/src/scripts/check_memory.ts +7 -1
  328. package/src/scripts/check_placeholder_drift.ts +221 -0
  329. package/src/scripts/check_read_surface_coverage.ts +515 -0
  330. package/src/scripts/check_release_highlights.ts +10 -0
  331. package/src/scripts/check_release_holds.ts +2 -1
  332. package/src/scripts/check_release_pr_shape.ts +38 -15
  333. package/src/scripts/check_routing_coverage.ts +378 -11
  334. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  335. package/src/scripts/check_static_parity.ts +17 -0
  336. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  337. package/src/scripts/ci_settle.ts +145 -19
  338. package/src/scripts/closure_scan.ts +115 -1
  339. package/src/scripts/code_graph/detect.ts +127 -7
  340. package/src/scripts/code_graph/foreign.ts +194 -0
  341. package/src/scripts/code_graph/query.ts +217 -4
  342. package/src/scripts/code_graph/types.ts +26 -2
  343. package/src/scripts/code_graph/validate.ts +3 -1
  344. package/src/scripts/code_graph/verbs.ts +103 -0
  345. package/src/scripts/compile_hook_manifest.ts +90 -14
  346. package/src/scripts/condense.ts +14 -27
  347. package/src/scripts/estate_exemption_shape.ts +202 -0
  348. package/src/scripts/external_sources_denylist.json +29 -4
  349. package/src/scripts/generate_host_cost_table.ts +1 -1
  350. package/src/scripts/hook_manifest.json +1 -1
  351. package/src/scripts/hook_manifest.yaml +192 -14
  352. package/src/scripts/hooks/block_config_weakening.ts +294 -4
  353. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  354. package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
  355. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  356. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  357. package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
  358. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  359. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  360. package/src/scripts/hooks/concern_registry.ts +2 -0
  361. package/src/scripts/hooks/concern_timings.ts +94 -0
  362. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  363. package/src/scripts/hooks/design_pass_hook.ts +100 -9
  364. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  365. package/src/scripts/hooks/dispatch_hook.ts +77 -71
  366. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  367. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  368. package/src/scripts/hooks/evidence_independence.ts +1 -2
  369. package/src/scripts/hooks/exit_codes.ts +129 -0
  370. package/src/scripts/hooks/host_lowering.json +1 -0
  371. package/src/scripts/hooks/host_lowering.ts +146 -3
  372. package/src/scripts/hooks/host_lowering.yaml +262 -46
  373. package/src/scripts/hooks/host_semantics.ts +30 -2
  374. package/src/scripts/hooks/injection_budget.ts +9 -2
  375. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  376. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  377. package/src/scripts/hooks/memory_recall_hook.ts +1 -2
  378. package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
  379. package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
  380. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  381. package/src/scripts/hooks/payload_stub.ts +10 -1
  382. package/src/scripts/hooks/permission_gate.ts +1 -2
  383. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  384. package/src/scripts/hooks/replay_hook.ts +2 -1
  385. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  386. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  387. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  388. package/src/scripts/hooks/rule_inject_hook.ts +765 -68
  389. package/src/scripts/hooks/run_continuation_hook.ts +13 -15
  390. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  391. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  392. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  393. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  394. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  395. package/src/scripts/hooks/table_fingerprint.ts +70 -0
  396. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  397. package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
  398. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  399. package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
  400. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  401. package/src/scripts/hooks_status.ts +112 -0
  402. package/src/scripts/injection_scan_hook.ts +7 -0
  403. package/src/scripts/install.ts +75 -189
  404. package/src/scripts/installed_layer_report.ts +103 -0
  405. package/src/scripts/language_mirror_hook.ts +15 -6
  406. package/src/scripts/learning_sidecar.ts +119 -8
  407. package/src/scripts/lint_deny_text.ts +419 -0
  408. package/src/scripts/lint_exit_codes.ts +262 -0
  409. package/src/scripts/lint_hook_manifest.ts +108 -0
  410. package/src/scripts/lint_major_migration_sections.ts +156 -26
  411. package/src/scripts/lint_pack_boundaries.ts +63 -5
  412. package/src/scripts/lint_roadmap_blockers.ts +78 -12
  413. package/src/scripts/lint_rule_law_section.ts +517 -0
  414. package/src/scripts/lint_skill_descriptions.ts +1 -1
  415. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  416. package/src/scripts/lint_workflow_security.ts +161 -5
  417. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  418. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  419. package/src/scripts/mcp_server/prompts.ts +142 -1
  420. package/src/scripts/measure_routing_signal.ts +54 -16
  421. package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
  422. package/src/scripts/measure_turn_end_gate.ts +193 -0
  423. package/src/scripts/memory_learn_hook.ts +91 -6
  424. package/src/scripts/memory_lookup.ts +1 -1
  425. package/src/scripts/model_rule_injection.ts +52 -5
  426. package/src/scripts/prepack-check.mjs +53 -0
  427. package/src/scripts/project_thin_rules.ts +34 -327
  428. package/src/scripts/release_publication.ts +13 -1
  429. package/src/scripts/report_evidence_temperature.ts +657 -0
  430. package/src/scripts/report_installed_rule_links.ts +157 -0
  431. package/src/scripts/report_invocation_surface.ts +383 -0
  432. package/src/scripts/report_label_agreement.ts +233 -0
  433. package/src/scripts/report_obligation_carriers.ts +699 -0
  434. package/src/scripts/report_obligation_mechanism.ts +230 -0
  435. package/src/scripts/report_obligation_writer_split.ts +236 -0
  436. package/src/scripts/report_single_token_triggers.ts +277 -0
  437. package/src/scripts/report_skill_menu_census.ts +208 -0
  438. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  439. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  440. package/src/scripts/report_verification_record_coverage.ts +306 -0
  441. package/src/scripts/roadmap_verify_share.ts +213 -0
  442. package/src/scripts/rule_activation_census.ts +5 -2
  443. package/src/scripts/rule_trigger_eval.ts +9 -9
  444. package/src/scripts/schemas/command.schema.json +44 -0
  445. package/src/scripts/schemas/review-findings.schema.json +7 -2
  446. package/src/scripts/schemas/rule.schema.json +5 -0
  447. package/src/scripts/schemas/skill.schema.json +44 -0
  448. package/src/scripts/self_review_gate.ts +421 -17
  449. package/src/scripts/semantic_parity_census.ts +314 -0
  450. package/src/scripts/session_eol_report.ts +54 -1
  451. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  452. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  453. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  454. package/src/scripts/sync_gitignore.ts +29 -235
  455. package/src/scripts/trigger_eval_grandfather.json +0 -7
  456. package/src/scripts/trigger_eval_rotation.ts +122 -27
  457. package/src/scripts/ui_conformance_probe.ts +156 -4
  458. package/src/scripts/update_prices.ts +85 -3
  459. package/src/scripts/write_bundle_digest.mjs +74 -0
  460. package/src/server/schemas/settings.ts +41 -0
  461. package/src/shared/skillRanking.ts +117 -11
  462. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  463. package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
  464. package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
  465. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
  466. package/dist/ui/assets/index-DH1xc62v.js +0 -29
  467. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
@@ -0,0 +1,147 @@
1
+ /**
2
+ * One spelling of "a rule's law section" — the unit three gates and one
3
+ * projector all price.
4
+ *
5
+ * A **law section** is a heading whose title matches `\biron\s+laws?\b` plus
6
+ * everything under it up to the next heading of the same or a shallower depth.
7
+ * A rule may carry several (`direct-answers` has three, `no-cheap-questions`
8
+ * six); the rule's law is all of them, joined by a blank line, because each one
9
+ * states an obligation and a stub carrying only the first would ship a rule
10
+ * whose other laws silently vanished.
11
+ *
12
+ * WHY A SHARED MODULE RATHER THAN A REGEX PER CALLER. The projector
13
+ * (`project_thin_rules.ts`) byte-copies this text into a stub and digests it;
14
+ * the lint (`lint_rule_law_section.ts`) holds it under a ceiling. If those two
15
+ * disagreed about where a section ends, the lint would certify a length the
16
+ * projection does not carry — the exact drift `THIN_ENTRY_MARKER`'s own comment
17
+ * argues against one file over.
18
+ *
19
+ * The heading regex is deliberately the SAME one `check_iron_law_prominence.ts`
20
+ * uses, so "has a law section" here and "is an Iron Law heading" there cannot
21
+ * diverge. That gate owns prominence (H2, near the top); this module owns
22
+ * extent and size.
23
+ *
24
+ * WHAT THIS DOES NOT CLAIM. Not that the extracted text IS the rule's obligation. A rule can state its
25
+ * law in bold prose or a bare fence under no heading at all — several of the 35
26
+ * routed rules without a heading match do exactly that. A heading match is a
27
+ * STRUCTURAL property, checkable; "this sentence is the obligation" is not, and
28
+ * `report_obligation_carriers` plus the `# obligation: line N` marker is where
29
+ * that question already lives.
30
+ */
31
+ import * as fs from 'node:fs';
32
+
33
+ /** Same literal as `check_iron_law_prominence.ts` — one definition, two readers. */
34
+ export const IRON_LAW_RE = /\biron\s+laws?\b/i;
35
+
36
+ const HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
37
+ const FENCE_RE = /^\s*```/;
38
+
39
+ export interface LawSection {
40
+ /** Heading title, verbatim. */
41
+ title: string;
42
+ /** Heading depth (1–6). */
43
+ depth: number;
44
+ /** 1-based line of the heading within the body. */
45
+ line: number;
46
+ /** Heading line plus its section, trimmed. */
47
+ text: string;
48
+ }
49
+
50
+ /** Return `[frontmatter_including_fences, body]`. Empty frontmatter if none. */
51
+ export function splitFrontmatter(text: string): [string, string] {
52
+ if (text.startsWith('---\n')) {
53
+ const end = text.indexOf('\n---\n', 4);
54
+ if (end !== -1) {
55
+ return [text.slice(0, end + 5), text.slice(end + 5)];
56
+ }
57
+ }
58
+ return ['', text];
59
+ }
60
+
61
+ /**
62
+ * The body a runtime delivery carries: frontmatter removed, HTML comments
63
+ * removed, trimmed.
64
+ *
65
+ * Comments are stripped because they are authoring scaffolding
66
+ * (`<!-- risk-review: … -->`, `<!-- harvest:… -->`, `ref-ignore` markers) that
67
+ * no host renders to a model, so charging a rule for them would price the wrong
68
+ * thing. Frontmatter is excluded for the reason `check_rule_stub_ceiling` gives
69
+ * for the same exclusion: it is the routing surface, and a rule that gains a
70
+ * trigger must not trip a ceiling whose message tells the author to move prose.
71
+ */
72
+ export function ruleBody(text: string): string {
73
+ const [, body] = splitFrontmatter(text);
74
+ return body.replace(/<!--[\s\S]*?-->/g, '').trim();
75
+ }
76
+
77
+ /** Every law section in a rule body, in document order. */
78
+ export function lawSections(body: string): LawSection[] {
79
+ const lines = body.split('\n');
80
+ let inFence = false;
81
+ const headings: Array<{ i: number; depth: number; title: string }> = [];
82
+ for (let i = 0; i < lines.length; i += 1) {
83
+ const raw = lines[i] as string;
84
+ if (FENCE_RE.test(raw)) {
85
+ inFence = !inFence;
86
+ continue;
87
+ }
88
+ if (inFence) continue;
89
+ const m = HEADING_RE.exec(raw);
90
+ if (m) headings.push({ i, depth: (m[1] as string).length, title: (m[2] as string).trim() });
91
+ }
92
+ const out: LawSection[] = [];
93
+ for (let k = 0; k < headings.length; k += 1) {
94
+ const h = headings[k] as { i: number; depth: number; title: string };
95
+ if (!IRON_LAW_RE.test(h.title)) continue;
96
+ let end = lines.length;
97
+ for (let j = k + 1; j < headings.length; j += 1) {
98
+ const n = headings[j] as { i: number; depth: number; title: string };
99
+ if (n.depth <= h.depth) {
100
+ end = n.i;
101
+ break;
102
+ }
103
+ }
104
+ out.push({
105
+ title: h.title,
106
+ depth: h.depth,
107
+ line: h.i + 1,
108
+ text: lines.slice(h.i, end).join('\n').trim(),
109
+ });
110
+ }
111
+ return out;
112
+ }
113
+
114
+ /**
115
+ * The rule's law, as one byte string — all sections joined by a blank line.
116
+ * `null` when the rule has none, which is a different answer from `''`.
117
+ */
118
+ export function lawText(body: string): string | null {
119
+ const sections = lawSections(body);
120
+ if (sections.length === 0) return null;
121
+ return sections.map((s) => s.text).join('\n\n');
122
+ }
123
+
124
+ export interface RuleLawMeasure {
125
+ id: string;
126
+ /** Characters of the law text, 0 when there is none. */
127
+ lawChars: number;
128
+ /** Number of law sections found. */
129
+ sections: number;
130
+ /** Characters of the comment-stripped body. */
131
+ bodyChars: number;
132
+ /** The law text itself, `null` when absent. */
133
+ law: string | null;
134
+ }
135
+
136
+ /** Measure one rule file. */
137
+ export function measureRuleFile(id: string, filePath: string): RuleLawMeasure {
138
+ const body = ruleBody(fs.readFileSync(filePath, 'utf-8'));
139
+ const law = lawText(body);
140
+ return {
141
+ id,
142
+ lawChars: law === null ? 0 : law.length,
143
+ sections: lawSections(body).length,
144
+ bodyChars: body.length,
145
+ law,
146
+ };
147
+ }
@@ -25,6 +25,7 @@
25
25
  */
26
26
 
27
27
  import * as fs from 'node:fs';
28
+ import * as os from 'node:os';
28
29
  import * as path from 'node:path';
29
30
 
30
31
  /**
@@ -140,6 +141,63 @@ export function compareLayers(
140
141
  };
141
142
  }
142
143
 
144
+ /**
145
+ * The two directories Claude Code loads rules from, user layer first.
146
+ *
147
+ * ONE SOURCE OF TRUTH FOR SCOPE (`road-to-a-rule-carrier-that-works-outside-the-repo`
148
+ * step 1.3). Two readers need this pair and both used to spell it out inline:
149
+ * the installer's overlap gate, and — once the carrier started resolving bodies
150
+ * from the package rather than from the workspace — the delivery scope itself.
151
+ * A carrier scoping on one list while the installer gates on another would
152
+ * deliver a rule the install never wrote, which is the state step 1.3 exists to
153
+ * end.
154
+ *
155
+ * It is a UNION and not a precedence chain. The host loads both with no dedup —
156
+ * that is the whole reason this module exists — so a rule present in either
157
+ * layer is loaded, and "what the install carries" is the union of the two.
158
+ *
159
+ * `os.homedir()` rather than a settings key: the installer resolves the user
160
+ * layer the same way, and a second spelling of "where is home" is a second
161
+ * thing to disagree about.
162
+ */
163
+ export function hostRuleLayerDirs(projectRoot: string): [string, string] {
164
+ return [
165
+ path.join(os.homedir(), '.claude', 'rules'),
166
+ path.join(projectRoot, '.claude', 'rules'),
167
+ ];
168
+ }
169
+
170
+ /**
171
+ * Rule ids present in either host rule layer, or `null` when NEITHER directory
172
+ * exists.
173
+ *
174
+ * `null` IS A DIFFERENT ANSWER FROM AN EMPTY SET, and the difference is the
175
+ * contract. An empty set means the install wrote a rule layer and it carries
176
+ * nothing, so nothing is in scope. `null` means no rule layer exists to declare
177
+ * a scope at all: a maintainer checkout, or a host this install writes no rules
178
+ * directory for. Scoping to the empty set there would re-create exactly the
179
+ * silence step 1.1 repaired, so the caller applies no filter instead.
180
+ *
181
+ * Names only, never bodies. This runs on the `user_prompt_submit` hot path, and
182
+ * {@link readRuleLayer} beside it reads every file's contents because ITS
183
+ * caller compares them. A scope check never needs a byte of any of them.
184
+ */
185
+ export function hostRuleLayerIds(projectRoot: string): Set<string> | null {
186
+ const ids = new Set<string>();
187
+ let found = false;
188
+ for (const dir of hostRuleLayerDirs(projectRoot)) {
189
+ let names: string[];
190
+ try {
191
+ names = fs.readdirSync(dir);
192
+ } catch {
193
+ continue;
194
+ }
195
+ found = true;
196
+ for (const n of names) if (n.endsWith('.md')) ids.add(n.slice(0, -3));
197
+ }
198
+ return found ? ids : null;
199
+ }
200
+
143
201
  /**
144
202
  * Read every `.md` in `dir` into a {@link RuleLayer}, or `null` when the
145
203
  * directory does not exist.
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The shell shapes that mean "this path is being WRITTEN", shared by the
3
+ * PreToolUse guards that refuse a write to a governed file.
4
+ *
5
+ * WHY IT IS SHARED. `block_kernel_rule_writes.ts` grew this parser after the
6
+ * S0.2 spike measured a real gap: a guard keyed on the TOOL NAME let
7
+ * `Bash sed -i … src/rules/commit-policy.md` reach the outcome the Write
8
+ * branch refuses, and a two-step `Write` → `Bash mv` sequence did the same with
9
+ * every individual step allowed. `block_plumbing_writes.ts`
10
+ * (road-to-a-kernel-that-guards-its-plumbing 1.2) refuses a different file set
11
+ * against the same shapes. Two copies of a shell-shape parser is two places a
12
+ * verb can be added to one and forgotten in the other, and the guard that
13
+ * silently stopped recognising `tee` would look exactly like the guard that
14
+ * never had to.
15
+ *
16
+ * DELIBERATELY NARROW (council 2026-08-02 cut, option ii, recorded on the
17
+ * kernel guard and inherited here unchanged): only redirection into the path,
18
+ * an in-place `sed`, a `tee`/`truncate`/`rm`/`shred` naming it, or a
19
+ * `mv`/`cp`/`install`/`rsync` whose DESTINATION it is. Reads stay allowed —
20
+ * `cat`, `grep`, `head`, `diff`, `git show` carry none of these shapes — and no
21
+ * attempt is made to understand arbitrary shell. Recognising every conceivable
22
+ * write verb would make this a shell sandbox, which is the failure mode the
23
+ * council named.
24
+ *
25
+ * KNOWN-OPEN, stated rather than implied: a write routed through an
26
+ * interpreter (`python -c`, a heredoc fed to `sh`) carries none of these
27
+ * shapes and is not detected. This is a guard, not write mediation.
28
+ */
29
+ import * as path from 'node:path';
30
+
31
+ /** In-place mutators that name their target as a plain argument. */
32
+ const MUTATOR_VERBS: ReadonlySet<string> = new Set(['sed', 'tee', 'truncate', 'rm', 'shred']);
33
+
34
+ /** Verbs whose LAST positional argument is the destination. */
35
+ const DEST_LAST_VERBS: ReadonlySet<string> = new Set(['mv', 'cp', 'install', 'rsync']);
36
+
37
+ /**
38
+ * The first governed target a command WRITES, or `null`.
39
+ *
40
+ * @param command the raw shell string off the tool envelope
41
+ * @param match classifies one token: a non-null return names the governed
42
+ * target that token resolves to, and is what this returns
43
+ */
44
+ export function shellWriteTarget<T>(command: string, match: (token: string) => T | null): T | null {
45
+ const tokens = command.split(/\s+/).filter((t) => t.length > 0);
46
+ const stripped = tokens.map((t) => t.replace(/^['"]|['"]$/g, ''));
47
+
48
+ // Cheap reject first: no governed path in the string at all.
49
+ if (!stripped.some((t) => match(t) !== null)) {
50
+ return null;
51
+ }
52
+
53
+ // 1. Redirection into the path: `> p`, `>> p`, `>p`, `>>p`.
54
+ for (let i = 0; i < stripped.length; i += 1) {
55
+ const tok = stripped[i] as string;
56
+ const inline = /^>{1,2}(.+)$/.exec(tok);
57
+ if (inline) {
58
+ const r = match(inline[1] as string);
59
+ if (r !== null) {
60
+ return r;
61
+ }
62
+ }
63
+ if (tok === '>' || tok === '>>') {
64
+ const next = stripped[i + 1];
65
+ if (typeof next === 'string') {
66
+ const r = match(next);
67
+ if (r !== null) {
68
+ return r;
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ // 2. A mutator verb anywhere in the pipeline, with the path as an argument.
75
+ // `sed` only counts in its in-place form — `sed 's/x/y/' file` prints.
76
+ const verbs = new Set<string>();
77
+ let prevWasSeparator = true;
78
+ for (const tok of stripped) {
79
+ if (['&&', '||', ';', '|'].includes(tok)) {
80
+ prevWasSeparator = true;
81
+ continue;
82
+ }
83
+ if (prevWasSeparator && !tok.startsWith('-')) {
84
+ verbs.add(path.basename(tok));
85
+ prevWasSeparator = false;
86
+ }
87
+ }
88
+ const sedInPlace = verbs.has('sed') && stripped.some((t) => /^-i/.test(t));
89
+ for (const verb of verbs) {
90
+ if (verb === 'sed' && !sedInPlace) {
91
+ continue;
92
+ }
93
+ if (MUTATOR_VERBS.has(verb)) {
94
+ const hit = stripped.map(match).find((r) => r !== null);
95
+ if (hit !== undefined && hit !== null) {
96
+ return hit;
97
+ }
98
+ }
99
+ if (DEST_LAST_VERBS.has(verb)) {
100
+ const last = stripped[stripped.length - 1] as string;
101
+ const r = match(last);
102
+ if (r !== null) {
103
+ return r;
104
+ }
105
+ }
106
+ }
107
+ return null;
108
+ }
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Who put a ranked skill on disk, and whether its body has been looked at.
3
+ *
4
+ * SPLIT OUT OF `neighbour_census.ts` FOR ONE CONCRETE REASON. The ranker
5
+ * (`skill_tools/score_skill_relevance`) needs these primitives, and the ranker
6
+ * runs inside the shared hook bundle on `user_prompt_submit`. The census module
7
+ * reaches the host-hook merger, the secret detector and the install-ownership
8
+ * reader; every byte of that would be inlined into one bundle that every concern
9
+ * loads on every dispatch, under a ceiling `check_hook_bundle_composition` holds.
10
+ * So the primitives live here, importing only the manifest reader, and the census
11
+ * re-exports them — one definition, two call sites, no second module graph.
12
+ *
13
+ * ORIGIN IS READ FROM A CLAIM RECORD, NEVER INFERRED FROM A ROOT. The temptation
14
+ * is to call `~/.claude/skills` foreign and the workspace ours. That is wrong in
15
+ * both directions: `agent-config install` writes this package's own skills into
16
+ * the host-global root, and a consumer's hand-written skills sit in the project
17
+ * root. The one artifact that records what this package actually wrote is the
18
+ * installed-tools manifest, so the manifest is the authority and the root is only
19
+ * a tie-break between the two NON-package labels.
20
+ */
21
+ import { createHash } from 'node:crypto';
22
+ import * as fs from 'node:fs';
23
+ import * as os from 'node:os';
24
+ import * as path from 'node:path';
25
+
26
+ import { parse as parseYaml } from 'yaml';
27
+
28
+ import { manifest_path } from './installed_tools.js';
29
+
30
+ /**
31
+ * Where a RANKED skill came from — a question about AUTHORSHIP.
32
+ *
33
+ * Deliberately not the census's `origin` id segment, which is WHERE a file was
34
+ * found (`project`, `user`, `cursor`, …). Location does not answer authorship in
35
+ * either direction, which is the whole reason this type exists beside that one.
36
+ *
37
+ * `home` is spelled `home` rather than the census's `user` because this value is
38
+ * printed on a ROUTE LINE beside a skill name, where it has to read as a place a
39
+ * reader can go and look.
40
+ */
41
+ export type SkillOrigin = 'package' | 'project' | 'home';
42
+
43
+ /** One `compat` label per neighbour skill. Order decided in {@link classifyCompat}. */
44
+ export type Compat = 'shadowed' | 'overlapping' | 'unscanned' | 'unclassified';
45
+
46
+ /** The authored source tree, relative to a maintainer checkout's root. */
47
+ export const AUTHORED_SKILL_ROOT = path.join('src', 'skills');
48
+
49
+ /** Where the scan verdicts the ranker reads are written, relative to the root. */
50
+ export const SCAN_CACHE_RELATIVE = path.join('agents', 'reports', 'neighbour-scan.json');
51
+
52
+ /** Default scan-cache path for a project root. */
53
+ export function scanCachePath(projectRoot: string): string {
54
+ return path.join(projectRoot, SCAN_CACHE_RELATIVE);
55
+ }
56
+
57
+ /**
58
+ * What a route line prints: bare for ours, `origin:name` for a neighbour.
59
+ *
60
+ * One function, so the ranker, the census and every report agree on the
61
+ * spelling. Two spellings of one name is how a collision stops being visible,
62
+ * which is the defect this whole step exists to close.
63
+ */
64
+ export function qualifiedSkillName(name: string, origin: SkillOrigin): string {
65
+ return origin === 'package' ? name : `${origin}:${name}`;
66
+ }
67
+
68
+ /** Split a qualified name back apart. A bare name resolves to `package`. */
69
+ export function splitQualifiedName(qualified: string): { origin: SkillOrigin; name: string } {
70
+ const m = /^(project|home):([\s\S]*)$/.exec(qualified);
71
+ if (m) return { origin: m[1] as SkillOrigin, name: m[2] as string };
72
+ return { origin: 'package', name: qualified };
73
+ }
74
+
75
+ /**
76
+ * Every absolute path this package's installed-tools manifest claims, or `null`
77
+ * when there is no manifest to claim anything.
78
+ *
79
+ * `null` and an empty set are different answers and callers must keep them
80
+ * apart. An empty set means the manifest was read and claims nothing; `null`
81
+ * means nothing recorded a claim at all, which is a tree that has never run an
82
+ * install — this repository, for one.
83
+ *
84
+ * WHY THIS PARSES THE MANIFEST ITSELF, which is a thing to justify rather than
85
+ * do quietly. Three readers of this file already exist and none answers this
86
+ * question at a cost this path can pay. `installed_tools.read_manifest` drops
87
+ * the nested `tools[].files[]` rows by design — its own header says so, and
88
+ * those rows are the entire input here. `install_drift.collect_drift` and
89
+ * `recordedOwnership.readRecordedHashes` both parse the YAML directly FOR THAT
90
+ * REASON, so parsing it directly is the established answer rather than a new
91
+ * idea; what is new is the fourth question — the PATHS only, with no sha, no
92
+ * tool grouping and no hash map. `readRecordedHashes` would have answered it
93
+ * and was measured costing 1,706 bytes of the shared hook bundle, which is the
94
+ * budget this whole module was split out to protect. The `~` expansion below is
95
+ * the same one all three use.
96
+ */
97
+ export function packageClaimedPaths(projectRoot: string): ReadonlySet<string> | null {
98
+ const manifest = manifest_path(projectRoot);
99
+ let doc: unknown;
100
+ try {
101
+ doc = parseYaml(fs.readFileSync(manifest, 'utf-8'), { version: '1.1' });
102
+ } catch {
103
+ // Unreadable and absent are the same answer: nothing recorded a claim.
104
+ return null;
105
+ }
106
+ if (doc === null || typeof doc !== 'object' || Array.isArray(doc)) return null;
107
+ const tools = (doc as Record<string, unknown>)['tools'];
108
+ const out = new Set<string>();
109
+ if (!Array.isArray(tools)) return out;
110
+ for (const tool of tools) {
111
+ if (tool === null || typeof tool !== 'object' || Array.isArray(tool)) continue;
112
+ const files = (tool as Record<string, unknown>)['files'];
113
+ if (!Array.isArray(files)) continue;
114
+ for (const entry of files) {
115
+ if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) continue;
116
+ const raw = (entry as Record<string, unknown>)['path'];
117
+ if (typeof raw !== 'string' || raw === '') continue;
118
+ out.add(path.resolve(projectRoot, _expanduser(raw)));
119
+ }
120
+ }
121
+ return out;
122
+ }
123
+
124
+ /** Expand a leading `~`, matching the three sibling manifest readers. */
125
+ function _expanduser(p: string): string {
126
+ if (p === '~') return os.homedir();
127
+ if (p.startsWith('~/') || (process.platform === 'win32' && p.startsWith('~\\'))) {
128
+ return path.join(os.homedir(), p.slice(2));
129
+ }
130
+ return p;
131
+ }
132
+
133
+ export interface SkillOriginOptions {
134
+ /** Pre-read claim set, to avoid a second manifest read. `null` = no manifest. */
135
+ claims?: ReadonlySet<string> | null;
136
+ /** Maintainer checkout root whose `src/skills` is the authored tree. */
137
+ packageRoot?: string;
138
+ }
139
+
140
+ /**
141
+ * Resolve one `SKILL.md` path to the origin of the skill it defines.
142
+ *
143
+ * NO CLAIM RECORD MEANS NO QUALIFIER. With no manifest, nothing on disk carries
144
+ * evidence that any skill is foreign, so everything resolves to `package`, the
145
+ * ranker qualifies nothing, and its output is exactly what it was before this
146
+ * function existed. That is deliberate rather than a gap being tolerated: a
147
+ * `home:` prefix asserted without a claim record is a guess printed in the shape
148
+ * of a fact, and a reader cannot tell the two apart. The cost is stated rather
149
+ * than hidden — where no manifest exists a foreign skill is invisible here, and
150
+ * the thing that fixes it is running an install, not loosening this rule.
151
+ *
152
+ * The authored tree is the one exception, and it is a construction rather than a
153
+ * guess: `<root>/src/skills` exists only in a maintainer checkout of THIS
154
+ * package and holds its source of truth, so nothing foreign can be under it.
155
+ */
156
+ export function makeSkillOriginResolver(
157
+ projectRoot: string,
158
+ opts: SkillOriginOptions = {},
159
+ ): (skillMdPath: string) => SkillOrigin {
160
+ const claims = opts.claims !== undefined ? opts.claims : packageClaimedPaths(projectRoot);
161
+ const root = path.resolve(projectRoot);
162
+ const authored = path.join(path.resolve(opts.packageRoot ?? projectRoot), AUTHORED_SKILL_ROOT);
163
+ const under = (abs: string, dir: string): boolean =>
164
+ abs === dir || abs.startsWith(dir + path.sep);
165
+ return (skillMdPath: string): SkillOrigin => {
166
+ const abs = path.resolve(skillMdPath);
167
+ if (under(abs, authored)) return 'package';
168
+ if (claims === null) return 'package';
169
+ if (claims.has(abs)) return 'package';
170
+ return under(abs, root) ? 'project' : 'home';
171
+ };
172
+ }
173
+
174
+ /**
175
+ * The one label, and the order it is decided in.
176
+ *
177
+ * `unscanned` — the shape scan refused the body. FIRST, because an overlap
178
+ * score is derived FROM a body this census declined to read,
179
+ * and publishing it would be the refusal cancelling itself.
180
+ * `shadowed` — one of ours carries the same bare name. Name-derived, so it
181
+ * stays true under `unscanned`; it loses the slot only
182
+ * because `unscanned` is the stronger call to action.
183
+ * `overlapping` — content similarity against one of ours at or above the
184
+ * threshold `audit_skill_overlap` already calibrates.
185
+ * `unclassified` — none of the above fired. Not a clearance.
186
+ */
187
+ export function classifyCompat(args: {
188
+ unscanned: string | null;
189
+ shadows: readonly string[];
190
+ overlaps: readonly string[];
191
+ }): Compat {
192
+ if (args.unscanned !== null) return 'unscanned';
193
+ if (args.shadows.length > 0) return 'shadowed';
194
+ if (args.overlaps.length > 0) return 'overlapping';
195
+ return 'unclassified';
196
+ }
197
+
198
+ /** One recorded scan verdict. `unscanned` is `null` when every shape passed. */
199
+ export interface ScanRecord {
200
+ qualified: string;
201
+ digest: string;
202
+ unscanned: string | null;
203
+ }
204
+
205
+ export interface ScanCache {
206
+ schema_version: 1;
207
+ entries: ScanRecord[];
208
+ }
209
+
210
+ /** Read the scan cache, or `null` when it is absent or unreadable. */
211
+ export function readScanCache(file: string): ScanCache | null {
212
+ let parsed: unknown;
213
+ try {
214
+ parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
215
+ } catch {
216
+ return null;
217
+ }
218
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return null;
219
+ const entries = (parsed as Record<string, unknown>)['entries'];
220
+ if (!Array.isArray(entries)) return null;
221
+ return { schema_version: 1, entries: entries as ScanRecord[] };
222
+ }
223
+
224
+ /** sha256 of a file's bytes, or `null` when it cannot be read. */
225
+ export function fileDigest(file: string): string | null {
226
+ try {
227
+ return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
228
+ } catch {
229
+ return null;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Whether a neighbour body may be indexed, and why not when it may not.
235
+ *
236
+ * THREE REFUSALS, REPORTED SEPARATELY, because they ask the maintainer for three
237
+ * different things: run the census, re-run it, or read the finding. A missing
238
+ * record is a refusal and not a pass — a gate that passes on missing evidence is
239
+ * satisfied by deleting the evidence, which is the opposite of a floor. The
240
+ * digest compare is what "a changed digest rescans first" means from the reading
241
+ * side: a body edited after its scan is unscanned again until the census runs.
242
+ */
243
+ export function scanVerdict(
244
+ cache: ScanCache | null,
245
+ qualified: string,
246
+ digest: string | null,
247
+ ): { ok: true } | { ok: false; kind: string } {
248
+ if (cache === null) return { ok: false, kind: 'no-scan-record' };
249
+ const rec = cache.entries.find((e) => e.qualified === qualified);
250
+ if (rec === undefined) return { ok: false, kind: 'no-scan-record' };
251
+ if (digest === null || rec.digest !== digest) return { ok: false, kind: 'digest-changed' };
252
+ if (rec.unscanned !== null) return { ok: false, kind: rec.unscanned };
253
+ return { ok: true };
254
+ }