@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,109 @@
1
+ /**
2
+ * The rule scope governing a GLOBAL deploy.
3
+ *
4
+ * Moved out of `src/scripts/install.ts` by
5
+ * `road-to-an-installed-layer-that-is-thinned` step 1.1, which adds the thinning
6
+ * call to the same deploy loop. `check_source_size_budget` is a one-way ratchet
7
+ * with no per-change headroom, and this repository's stated convention is that
8
+ * the total falls through EXTRACTION and never rises through a baseline edit —
9
+ * the same reason `claudeRuleRewrite` holds the claude-code post-copy reporting
10
+ * rather than the installer. This function is the nearest self-contained block
11
+ * to the change: it is the predicate the deploy loop resolves once per run, it
12
+ * reaches nothing else in the installer, and its four environment touches are
13
+ * already parameters in all but name.
14
+ *
15
+ * BEHAVIOUR IS UNCHANGED, DELIBERATELY. The body is the installer's, verbatim
16
+ * apart from the four injected seams below; `install.ts` keeps a wrapper of the
17
+ * same name and signature, so its re-export and every caller and test see what
18
+ * they saw before. A behaviour change smuggled into an extraction is the one
19
+ * thing that would make the line saving not worth having.
20
+ *
21
+ * The seams are injected rather than imported because each one is an
22
+ * install-time decision the installer owns: where the global settings doc lives,
23
+ * how this build reads and parses YAML, what the packaged template says, and
24
+ * where a warning goes.
25
+ */
26
+
27
+ import { LEGACY_ALL, ruleScopeFromSettings, type RuleScope } from './rule_scope.js';
28
+
29
+ export interface GlobalRuleScopeInputs {
30
+ /** The global settings file, or `null` when no artefact exists at all. */
31
+ readonly settingsPath: string | null;
32
+ /** The package being installed from. */
33
+ readonly packageRoot: string;
34
+ /** Read a file as text; throws like `fs.readFileSync` when it cannot. */
35
+ readonly readText: (p: string) => string;
36
+ /** Parse YAML permissively — anything unparseable comes back non-object. */
37
+ readonly parseYaml: (text: string) => unknown;
38
+ /** The packaged template settings, read only on a genuinely fresh machine. */
39
+ readonly loadDefaults: () => Record<string, unknown>;
40
+ /** Where a loud fallback goes. */
41
+ readonly warn: (message: string) => void;
42
+ }
43
+
44
+ function isPlainObject(v: unknown): boolean {
45
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
46
+ }
47
+
48
+ /**
49
+ * Resolve the rule scope governing THIS global deploy — the same way the wizard
50
+ * does (`src/server/routes/install.ts::_resolveRuleScope`), so the two global
51
+ * paths cannot ship different rule sets for the same settings.
52
+ *
53
+ * Settings resolution mirrors `_resolve_scoped_projection` above: an existing
54
+ * global settings doc is authoritative, and only a genuinely fresh machine falls
55
+ * through to the packaged template. Any read or parse failure resolves to
56
+ * `LEGACY_ALL` — over-shipping is the safe direction, and the compat exclusion
57
+ * (`source-of-truth.md`) still applies even then.
58
+ */
59
+ export function resolveGlobalRuleScope(inputs: GlobalRuleScopeInputs): RuleScope {
60
+ const { packageRoot, warn } = inputs;
61
+ const settings_path = inputs.settingsPath;
62
+
63
+ // No global settings artefact at all — a genuinely fresh machine. Fall
64
+ // through to the packaged template silently: there is no user decision to
65
+ // contradict, and this is the documented upgrade-compat path.
66
+ if (settings_path === null) {
67
+ try {
68
+ return ruleScopeFromSettings(inputs.loadDefaults(), packageRoot);
69
+ } catch {
70
+ return LEGACY_ALL;
71
+ }
72
+ }
73
+
74
+ // A doc EXISTS, so the user has expressed a configuration. Failing to read
75
+ // it is not the same as not having one: falling back to legacy-all here
76
+ // ships the maintainer-only rules the user may have deliberately scoped out,
77
+ // and `_load_yaml_doc` would report that as an indistinguishable `{}`. So
78
+ // parse it explicitly and be LOUD when it does not parse — over-shipping
79
+ // stays the safe direction, but it must not be a silent one.
80
+ let text: string;
81
+ try {
82
+ text = inputs.readText(settings_path);
83
+ } catch (e) {
84
+ warn(
85
+ `could not read ${settings_path} (${String(e)}) — rule scoping falls back ` +
86
+ 'to legacy-all, so ALL rules including maintainer-only ones will be ' +
87
+ 'installed. Fix the file to restore scoping.',
88
+ );
89
+ return LEGACY_ALL;
90
+ }
91
+ const parsed = inputs.parseYaml(text);
92
+ if (!isPlainObject(parsed)) {
93
+ warn(
94
+ `${settings_path} is not a YAML mapping — rule scoping falls back to ` +
95
+ 'legacy-all, so ALL rules including maintainer-only ones will be ' +
96
+ 'installed. Fix the file to restore scoping.',
97
+ );
98
+ return LEGACY_ALL;
99
+ }
100
+ try {
101
+ return ruleScopeFromSettings(parsed as Record<string, unknown>, packageRoot);
102
+ } catch (e) {
103
+ warn(
104
+ `could not derive rule scope from ${settings_path} (${String(e)}) — ` +
105
+ 'falling back to legacy-all; ALL rules will be installed.',
106
+ );
107
+ return LEGACY_ALL;
108
+ }
109
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Which host-rule files this package may delete or overwrite.
3
+ *
4
+ * The defect this closes: `_cleanDir` removed every entry under
5
+ * `.cursor/rules` and `.windsurf/rules` whose NAME was not one this run had
6
+ * just emitted, and `.windsurfrules` was overwritten unconditionally. A rule
7
+ * file another agent package wrote — or one the consumer wrote by hand — was
8
+ * destroyed on every install, which is the opposite of what the uninstall
9
+ * claim promises.
10
+ *
11
+ * Ownership is read from the installed-tools manifest's `files[]` inventory,
12
+ * never from a marker inside the file. The `.mdc` emitter writes no package
13
+ * tag (`condense.ts` `_emit_cursor_mdc`), so a tag-based gate would classify
14
+ * every file this package ever wrote as foreign and therefore never remove a
15
+ * stale one — it would read as conservative and in fact be inert. The manifest
16
+ * is the record of what this installer actually put on disk, so it is the
17
+ * thing that can answer the question.
18
+ *
19
+ * This module only READS that manifest. It never rewrites it: the available
20
+ * reader (`installed_tools.read_manifest`) drops nested `files[]` by design,
21
+ * so a read-modify-write through it would erase every other tool's inventory —
22
+ * the failure `manifestFiles.ts` documents at length.
23
+ *
24
+ * The consequence, stated rather than implied: a consumer whose manifest does
25
+ * not claim a path gets that path KEPT. Unproven means kept, never deleted.
26
+ * That is the safe direction for this lane and it is also a real limit — a
27
+ * stale file of ours sitting under an unclaimed path stays until something
28
+ * claims it.
29
+ */
30
+
31
+ import * as fs from 'node:fs';
32
+ import * as path from 'node:path';
33
+
34
+ import { sha256OfFile } from './fsPrimitives.js';
35
+ import { classifyOwnership, recordedHashesForRoot, type RecordedHashes } from './recordedOwnership.js';
36
+
37
+ /** The first line every `.windsurfrules` this package generates carries. */
38
+ export const WINDSURFRULES_HEADER = '# Auto-generated from .augment/rules/ — do not edit directly';
39
+
40
+ export interface CleanResult {
41
+ /** Entry names removed from the directory. */
42
+ removed: string[];
43
+ /** Entries kept because nothing proves this package wrote them. */
44
+ kept: number;
45
+ }
46
+
47
+ /** Every file path beneath `full`, or `[full]` when it is a file. */
48
+ function filesUnder(full: string): string[] {
49
+ let stat: fs.Stats;
50
+ try {
51
+ stat = fs.lstatSync(full);
52
+ } catch {
53
+ return [];
54
+ }
55
+ if (!stat.isDirectory()) return [full];
56
+ const out: string[] = [];
57
+ for (const name of fs.readdirSync(full)) {
58
+ out.push(...filesUnder(path.join(full, name)));
59
+ }
60
+ return out;
61
+ }
62
+
63
+ /**
64
+ * Is this exact file ours AND still carrying the bytes we wrote?
65
+ *
66
+ * The recorded digest is the point, not the path. A path-membership test
67
+ * answers "did we ever write here", which merges two materially different
68
+ * trees: a generated file still as we left it, and one the consumer has since
69
+ * edited. Deleting the second loses their work, and `recordedOwnership` exists
70
+ * to tell them apart — importing its map and then ignoring its hashes was the
71
+ * defect an independent review named here.
72
+ */
73
+ function isUnchangedOurs(file: string, owned: RecordedHashes): boolean {
74
+ const target = path.resolve(file);
75
+ if (!owned.has(target)) return false;
76
+ return classifyOwnership(owned.get(target), sha256OfFile(target)) === 'recorded-unchanged';
77
+ }
78
+
79
+ /**
80
+ * Remove the stale entries of ours from `dir`, keeping everything else.
81
+ *
82
+ * An entry is removable only when every file it contains is ours AND
83
+ * unmodified: one unclaimed or edited file inside a directory keeps the whole
84
+ * directory, because removing it would take that file with it.
85
+ */
86
+ export function cleanOwnedOnly(
87
+ dir: string,
88
+ valid: ReadonlySet<string>,
89
+ owned: RecordedHashes,
90
+ ): CleanResult {
91
+ let entries: string[];
92
+ try {
93
+ entries = fs.readdirSync(dir);
94
+ } catch {
95
+ return { removed: [], kept: 0 };
96
+ }
97
+ const removed: string[] = [];
98
+ let kept = 0;
99
+ for (const name of entries) {
100
+ if (name === 'README.md' || valid.has(name)) continue;
101
+ const full = path.join(dir, name);
102
+ const contained = filesUnder(full);
103
+ const ours = contained.length > 0 && contained.every((f) => isUnchangedOurs(f, owned));
104
+ if (!ours) {
105
+ kept += 1;
106
+ continue;
107
+ }
108
+ try {
109
+ fs.rmSync(full, { recursive: true, force: true });
110
+ removed.push(name);
111
+ } catch {
112
+ kept += 1;
113
+ }
114
+ }
115
+ return { removed, kept };
116
+ }
117
+
118
+ /**
119
+ * May this package write `.windsurfrules` at `target`?
120
+ *
121
+ * Yes when the file is absent, or when it still carries the header this
122
+ * package generates. Nothing else.
123
+ *
124
+ * A manifest claim is deliberately NOT a second route in. The file is
125
+ * generated and overwritten on every install, so the header IS the ownership
126
+ * signal and it is content-derived: if the header is gone, something replaced
127
+ * the file, and "the manifest says we wrote here once" is exactly the stale
128
+ * fact that would let us overwrite a neighbour's replacement. An independent
129
+ * review caught that route open, with a fixture asserting it.
130
+ */
131
+ export function mayWriteWindsurfRules(target: string, owned: RecordedHashes): boolean {
132
+ void owned;
133
+ if (!fs.existsSync(target)) return true;
134
+ let head: string;
135
+ try {
136
+ head = fs.readFileSync(target, 'utf-8').split('\n', 1)[0] ?? '';
137
+ } catch {
138
+ // Unreadable is unproven, and unproven means do not touch it.
139
+ return false;
140
+ }
141
+ return head.trim() === WINDSURFRULES_HEADER;
142
+ }
143
+
144
+ /**
145
+ * May this package write a rule file at `target`?
146
+ *
147
+ * The cleanup pass is not enough on its own, and an independent review found
148
+ * the hole: `_cleanDir` skips every name this run just emitted, so a
149
+ * neighbour's `foo.mdc` that happens to share a basename with one of our
150
+ * rules was already OVERWRITTEN by the time ownership was consulted. The
151
+ * check has to run BEFORE the write, which is what this is for.
152
+ *
153
+ * Absent, or ours and unmodified → write. Anything else is someone's file or
154
+ * someone's edit, and is left exactly as it is.
155
+ */
156
+ export function mayWriteRuleFile(target: string, owned: RecordedHashes): boolean {
157
+ if (!fs.existsSync(target)) return true;
158
+ return isUnchangedOurs(target, owned);
159
+ }
160
+
161
+ /** The manifest's claimed paths for a consumer root; empty when none resolves. */
162
+ export function ownedPaths(projectRoot: string): RecordedHashes {
163
+ return recordedHashesForRoot(projectRoot);
164
+ }
165
+
166
+ /** The one line the emitter prints when it kept a neighbour's files. */
167
+ export function keptLine(kept: number): string | null {
168
+ if (kept <= 0) return null;
169
+ return `kept: ${kept} neighbour file(s)`;
170
+ }
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Thin an INSTALLED Claude Code rule layer, in place, after the copy.
3
+ *
4
+ * `road-to-an-installed-layer-that-is-thinned` step 1.1. The global install step
5
+ * copies `dist/agent-src/rules` verbatim into `~/.claude/rules`
6
+ * (`install.ts::_copy_dir_dereferencing_symlinks`), so a consumer receives every
7
+ * rule body whether or not any trigger ever fires. The round that opened that
8
+ * roadmap measured a fresh install at 102 unconditional files and 338,225
9
+ * characters against a host that reports a 150,000 budget. Thinning existed —
10
+ * `project_thin_rules::build_thin` — but ran only on the maintainer projection,
11
+ * which no consumer receives.
12
+ *
13
+ * Three properties, each a defect this would otherwise reintroduce.
14
+ *
15
+ * **The stub is built from the SOURCE tree, never from the installed file.**
16
+ * `claudeRuleRewrite` runs immediately before this and reduces each installed
17
+ * rule to the host's activation vocabulary — `paths:` plus the ownership keys —
18
+ * dropping `description:` and the `triggers:` block. Those two are exactly what
19
+ * `thin_entry` reads to write the stub's matching signal, so a stub built from
20
+ * the rewritten file would carry an empty description and no `Fires on:` hint.
21
+ * Reading `<packageRoot>/dist/agent-src/rules/<name>` keeps the signal intact.
22
+ *
23
+ * **The installed frontmatter is kept verbatim.** The roadmap's step 1.1 ends
24
+ * "Ownership keys survive", and it is not decoration: `reap_tagged_orphans`
25
+ * calls the literal `package:` line "the only path with ownership proof
26
+ * independent of inventory history", and doctor's stale-orphan check reads the
27
+ * same tag. A stub that replaced the whole file would delete the reaper's only
28
+ * evidence for every thinned rule. So only the BODY is replaced; the header the
29
+ * copy and the rewrite produced is written back unchanged, which also preserves
30
+ * any `paths:` block — harmless, since a path-only rule is never thinned.
31
+ *
32
+ * **A user-modified file is skipped, not overwritten.** The preserved set comes
33
+ * from the conflict tracker, the same one the copy consulted. Step 1.3 requires
34
+ * an upgrade to preserve and report those files; thinning one would destroy an
35
+ * edit the copy had just declined to destroy.
36
+ *
37
+ * The rollback of step 1.2 needs nothing here, and the reason is the ordering.
38
+ *
39
+ * The install manifest records each deployed file's digest from the bytes ON
40
+ * DISK at manifest-write time, which is after this pass. So the next run sees
41
+ * `recorded-unchanged` for a thinned file, `decideDeployWrite` returns `write`,
42
+ * and the verbatim copy restores the full body. Switching the mode back to
43
+ * `eager-all` therefore un-thins by simply not running this pass — no inverse
44
+ * operation, and no stub left behind. The property that carries it is the
45
+ * ordering, which `tests/scripts/install_thin_layer.test.ts` pins directly.
46
+ *
47
+ * Side-effect-free apart from the writes it is asked to make. No CLI entry, no
48
+ * `process.exit` — this module is reachable from the `build:install-bundle`
49
+ * entry and `check_installer_import_purity` forbids both.
50
+ */
51
+
52
+ import * as fs from 'node:fs';
53
+ import * as path from 'node:path';
54
+
55
+ import * as os from 'node:os';
56
+
57
+ import {
58
+ buildHostLimitRows,
59
+ loadHostInstructionLimits,
60
+ readLayer,
61
+ renderHostLimitRows,
62
+ type LayerReading,
63
+ } from '../scripts/_lib/installed_layer.js';
64
+ import { GLOBAL_RULE_DIRS, globalRuleLayerPath } from './globalRuleLayers.js';
65
+ import { manifest_path } from '../scripts/_lib/installed_tools.js';
66
+ import { NO_RECORDED_HASHES, readRecordedHashes } from './recordedOwnership.js';
67
+ import {
68
+ absoluteBodyLinkPrefix,
69
+ build_thin,
70
+ split_frontmatter,
71
+ } from '../scripts/_lib/thin_rules.js';
72
+
73
+ export interface ThinInstalledLayerResult {
74
+ /** Files that stand as a stub after this pass, whether or not it wrote them. */
75
+ readonly thinned: number;
76
+ /**
77
+ * Of those, the ones whose bytes actually changed.
78
+ *
79
+ * Separate from {@link thinned} so a repeat install's receipt does not claim
80
+ * work it did not do: on an already-thinned layer every file is `thinned`
81
+ * and none is `rewritten`.
82
+ */
83
+ readonly rewritten: number;
84
+ /** Installed rules the predicate keeps full-bodied (kernel, trigger-less, path-only, `no_stub`). */
85
+ readonly kept: number;
86
+ /** Basenames skipped because the conflict tracker preserved a user-modified file. */
87
+ readonly preserved: readonly string[];
88
+ /** Basenames the stub map names that this layer does not carry — out of rule scope. */
89
+ readonly absent: readonly string[];
90
+ /** Per-rule write failures, reported rather than thrown. */
91
+ readonly failed: ReadonlyArray<{ readonly rule: string; readonly reason: string }>;
92
+ /** The absolute prefix every stub's `Body:` link was built on. */
93
+ readonly bodyLinkPrefix: string;
94
+ /** Characters the layer held before and after, over the files this pass examined. */
95
+ readonly charsBefore: number;
96
+ readonly charsAfter: number;
97
+ }
98
+
99
+ export interface ThinInstalledLayerOptions {
100
+ /** The installed rule directory, e.g. `~/.claude/rules`. */
101
+ readonly rulesDir: string;
102
+ /** The package this install was made from — carries `dist/`, `src/config/`. */
103
+ readonly packageRoot: string;
104
+ /** Absolute paths the conflict tracker preserved; never rewritten here. */
105
+ readonly preserved?: ReadonlySet<string>;
106
+ }
107
+
108
+ /**
109
+ * Replace the body of every thinnable rule in `rulesDir` with its stub.
110
+ *
111
+ * Idempotent: a layer already thinned is rewritten to byte-identical content,
112
+ * so a repeated install neither grows nor drifts.
113
+ */
114
+ export function thinInstalledRuleLayer(opts: ThinInstalledLayerOptions): ThinInstalledLayerResult {
115
+ const packageRoot = path.resolve(opts.packageRoot);
116
+ const bodySourceDir = path.join(packageRoot, 'dist', 'agent-src', 'rules');
117
+ const bodyLinkPrefix = absoluteBodyLinkPrefix(packageRoot);
118
+ const preserved = opts.preserved ?? new Set<string>();
119
+
120
+ // Scope is `null` on purpose. The copy already applied the install's rule
121
+ // scope, so the files that EXIST in `rulesDir` are the scoped set; asking
122
+ // `build_thin` to resolve the scope a second time would be a second reader
123
+ // of the same decision, and the two could disagree. Anything the stub map
124
+ // names that is not on disk is simply out of scope and is reported as such.
125
+ const stubs = build_thin(bodySourceDir, null, null, null, { packageRoot, bodyLinkPrefix });
126
+
127
+ let thinned = 0;
128
+ let rewritten = 0;
129
+ let kept = 0;
130
+ let charsBefore = 0;
131
+ let charsAfter = 0;
132
+ const preservedHits: string[] = [];
133
+ const absent: string[] = [];
134
+ const failed: Array<{ rule: string; reason: string }> = [];
135
+
136
+ for (const [name, stubText] of stubs) {
137
+ const target = path.join(opts.rulesDir, name);
138
+ let installed: string;
139
+ try {
140
+ installed = fs.readFileSync(target, 'utf-8');
141
+ } catch (e) {
142
+ // ENOENT is the documented meaning of `absent` — the copy's rule
143
+ // scope did not place this rule here. Anything else (EACCES, EISDIR,
144
+ // EIO) is a FAILURE wearing a scoping outcome's clothes, and the
145
+ // receipt prints failures and does not print absences, so the two
146
+ // must not share a bucket.
147
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') absent.push(name);
148
+ else failed.push({ rule: name, reason: String(e) });
149
+ continue;
150
+ }
151
+ // KEPT-VS-THINNED IS DECIDED BY IDENTITY, NOT BY A CONTENT SNIFF.
152
+ // `build_thin` returns either a stub or the SOURCE TEXT VERBATIM, so
153
+ // the full-bodied branch is exactly "the map value equals the source
154
+ // file". Testing `is_thin_entry(stubText)` instead would misread a
155
+ // source rule whose own body happened to contain the marker — and that
156
+ // rule would then be written as `installedFrontmatter + fullSourceText`,
157
+ // duplicating the frontmatter block. Zero such rules ship today, which
158
+ // makes it latent rather than live, and `THIN_ENTRY_MARKER`'s own
159
+ // docstring argues against exactly this kind of inferred detection.
160
+ let sourceText: string | null;
161
+ try {
162
+ sourceText = fs.readFileSync(path.join(bodySourceDir, name), 'utf-8');
163
+ } catch {
164
+ sourceText = null;
165
+ }
166
+ if (stubText === sourceText) {
167
+ // Kept full-bodied by `build_thin`'s own predicate — kernel,
168
+ // trigger-less, path-only, or a declared `no_stub` member. The
169
+ // installed copy is already the right shape; touching it would be
170
+ // this pass inventing a decision the projector did not make.
171
+ kept += 1;
172
+ charsBefore += installed.length;
173
+ charsAfter += installed.length;
174
+ continue;
175
+ }
176
+ if (preserved.has(path.resolve(target))) {
177
+ preservedHits.push(name);
178
+ charsBefore += installed.length;
179
+ charsAfter += installed.length;
180
+ continue;
181
+ }
182
+ const [frontmatter] = split_frontmatter(installed);
183
+ charsBefore += installed.length;
184
+ // `split_frontmatter` answers `['', text]` for a file that OPENS a
185
+ // frontmatter block and never closes it — a truncated write, a file
186
+ // ending exactly on the closing fence with no trailing newline, CRLF.
187
+ // Writing `'' + stub` there would delete the `package:` line this
188
+ // module's header calls the reaper's only evidence, which is the one
189
+ // corruption this pass could cause. A file that claims a block it does
190
+ // not have is left exactly as found and reported.
191
+ //
192
+ // The test is a REGEX rather than `startsWith('---')` because the
193
+ // claim can be preceded by a BOM or by leading whitespace, and either
194
+ // one took the prefix check's "no block here" branch straight into the
195
+ // write it exists to prevent.
196
+ if (frontmatter === '' && /^\uFEFF?[\s]*---/.test(installed)) {
197
+ failed.push({
198
+ rule: name,
199
+ reason: 'opens a frontmatter block that never closes — left untouched so its ' +
200
+ 'ownership keys survive',
201
+ });
202
+ charsAfter += installed.length;
203
+ continue;
204
+ }
205
+ const next = `${frontmatter}${stubText}`;
206
+ try {
207
+ if (next !== installed) {
208
+ fs.writeFileSync(target, next, 'utf-8');
209
+ rewritten += 1;
210
+ }
211
+ thinned += 1;
212
+ charsAfter += next.length;
213
+ } catch (e) {
214
+ failed.push({ rule: name, reason: String(e) });
215
+ charsAfter += installed.length;
216
+ }
217
+ }
218
+
219
+ if (stubs.size === 0) {
220
+ // A ZERO-STUB MAP AND A ZERO-THINNABLE LAYER PRINT THE SAME RECEIPT, so
221
+ // the one that is a failure has to say so. `_globSortedMd` swallows an
222
+ // unreadable directory and answers `[]`, which is what a wrongly
223
+ // resolved package root produces — the hazard `ThinRoots` exists for.
224
+ // Without this the feature would be silently not applied behind a
225
+ // success-shaped line, and the installer's try/catch sees only throws.
226
+ failed.push({
227
+ rule: bodySourceDir,
228
+ reason: 'no rule bodies found to build stubs from — the layer was left full-bodied',
229
+ });
230
+ }
231
+
232
+ return {
233
+ thinned,
234
+ rewritten,
235
+ kept,
236
+ preserved: preservedHits,
237
+ absent,
238
+ failed,
239
+ bodyLinkPrefix,
240
+ charsBefore,
241
+ charsAfter,
242
+ };
243
+ }
244
+
245
+ /**
246
+ * One receipt line per outcome, for a caller that prints.
247
+ *
248
+ * Returns the lines rather than writing them, so the installer's `--quiet` and
249
+ * its progress-event emitter stay the only things deciding what a consumer sees.
250
+ */
251
+ export function describeThinInstalledLayer(res: ThinInstalledLayerResult): string[] {
252
+ const out: string[] = [];
253
+ out.push(
254
+ res.rewritten === 0 && res.thinned > 0
255
+ ? ` claude-code: ${String(res.thinned)} rule(s) already thinned, ` +
256
+ `${String(res.kept)} kept full-bodied; ` +
257
+ `${String(res.charsAfter)} chars — nothing to rewrite`
258
+ : ` claude-code: thinned ${String(res.rewritten)} rule(s) to stubs, ` +
259
+ `${String(res.kept)} kept full-bodied; ` +
260
+ `${String(res.charsBefore)} -> ${String(res.charsAfter)} chars`,
261
+ );
262
+ if (res.preserved.length > 0) {
263
+ out.push(
264
+ ` claude-code: ${String(res.preserved.length)} rule(s) left full — you edited them ` +
265
+ `and they are preserved, never overwritten (${res.preserved.join(', ')})`,
266
+ );
267
+ }
268
+ for (const f of res.failed) {
269
+ out.push(` claude-code: ${f.rule}: could not write stub — ${f.reason}`);
270
+ }
271
+ return out;
272
+ }
273
+
274
+ /**
275
+ * The install receipt's instruction-budget block — step 1.4's "at install".
276
+ *
277
+ * The step asks for the limit, the split and the 80 % warning AT INSTALL, and
278
+ * the measurement existed only behind the standalone `installed_layer_report`
279
+ * CLI: `buildHostLimitRows`'s own docstring named "the install receipt" as the
280
+ * caller it was exported for, and that caller did not exist. A consumer whose
281
+ * layer is over budget learned it from the host, which is the notice the
282
+ * roadmap exists to remove.
283
+ *
284
+ * Global layers only, deliberately. The install writes `~/.claude/rules` and
285
+ * its siblings; a project layer is not what this run just changed, and folding
286
+ * one in would make the receipt's number disagree with the thing it is a
287
+ * receipt FOR.
288
+ *
289
+ * Never throws. A receipt that cannot be measured is silent rather than fatal —
290
+ * the deploy it describes has already happened correctly.
291
+ */
292
+ export function installReceiptBudgetLines(packageRoot: string, home: string = os.homedir()): string[] {
293
+ try {
294
+ const recorded = (() => {
295
+ try {
296
+ return readRecordedHashes(manifest_path(packageRoot), packageRoot);
297
+ } catch {
298
+ return NO_RECORDED_HASHES;
299
+ }
300
+ })();
301
+ const layers: LayerReading[] = [];
302
+ for (const host of Object.keys(GLOBAL_RULE_DIRS).sort()) {
303
+ const dir = globalRuleLayerPath(host, home);
304
+ if (dir === null) continue;
305
+ layers.push(readLayer(host, 'global', dir, recorded));
306
+ }
307
+ const rows = buildHostLimitRows(layers, loadHostInstructionLimits());
308
+ return rows.length === 0 ? [] : renderHostLimitRows(rows).map((l) => ` ${l}`);
309
+ } catch {
310
+ return [];
311
+ }
312
+ }