@event4u/agent-config 16.3.0 → 16.4.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 (486) 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 +251 -0
  5. package/README.md +2 -2
  6. package/agents/templates/.ai-council.yml.example +26 -8
  7. package/dist/agent-src/commands/analyze/inbox.md +10 -7
  8. package/dist/agent-src/commands/bug/investigate.md +1 -1
  9. package/dist/agent-src/commands/commit/in-chunks.md +40 -31
  10. package/dist/agent-src/commands/commit.md +72 -33
  11. package/dist/agent-src/commands/condense.md +2 -8
  12. package/dist/agent-src/commands/council/debate.md +11 -7
  13. package/dist/agent-src/commands/council/default.md +10 -9
  14. package/dist/agent-src/commands/council.md +1 -1
  15. package/dist/agent-src/commands/estimate-ticket.md +1 -1
  16. package/dist/agent-src/commands/feature/explore.md +3 -3
  17. package/dist/agent-src/commands/feature/plan.md +1 -1
  18. package/dist/agent-src/commands/fix/ci.md +17 -3
  19. package/dist/agent-src/commands/fix/commit-messages.md +48 -23
  20. package/dist/agent-src/commands/fix/pr-comments.md +3 -6
  21. package/dist/agent-src/commands/implement-ticket.md +3 -3
  22. package/dist/agent-src/commands/memory/add.md +4 -16
  23. package/dist/agent-src/commands/memory/load.md +5 -6
  24. package/dist/agent-src/commands/memory/promote.md +3 -16
  25. package/dist/agent-src/commands/mission/upgrade.md +3 -2
  26. package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
  27. package/dist/agent-src/commands/optimize/deep.md +4 -2
  28. package/dist/agent-src/commands/optimize/rtk.md +4 -26
  29. package/dist/agent-src/commands/pr/create/description-only.md +18 -15
  30. package/dist/agent-src/commands/pr/create.md +42 -34
  31. package/dist/agent-src/commands/pr/merge.md +136 -9
  32. package/dist/agent-src/commands/prepare-for-review.md +13 -11
  33. package/dist/agent-src/commands/project/analyze.md +7 -35
  34. package/dist/agent-src/commands/refine-ticket.md +1 -1
  35. package/dist/agent-src/commands/review/changes.md +14 -19
  36. package/dist/agent-src/commands/roadmap/next.md +10 -1
  37. package/dist/agent-src/commands/roadmap/process-full.md +52 -38
  38. package/dist/agent-src/commands/roadmap/resolve-blockers.md +64 -14
  39. package/dist/agent-src/commands/worktree/create.md +7 -3
  40. package/dist/agent-src/commands/worktree/verify.md +2 -1
  41. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +5 -3
  42. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
  43. package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
  44. package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
  45. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +1 -1
  46. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
  47. package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
  48. package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
  49. package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
  50. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
  51. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +67 -52
  52. package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
  53. package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
  54. package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
  55. package/dist/agent-src/contexts/execution/toolchain-resolver.md +4 -10
  56. package/dist/agent-src/contexts/subagent-configuration.md +1 -1
  57. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  58. package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  59. package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  60. package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  61. package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
  62. package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  63. package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  64. package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
  65. package/dist/agent-src/guidelines/php/git.md +15 -2
  66. package/dist/agent-src/rules/active-remediation.md +0 -1
  67. package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
  68. package/dist/agent-src/rules/architecture.md +0 -1
  69. package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
  70. package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
  71. package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
  72. package/dist/agent-src/rules/autonomous-execution.md +0 -1
  73. package/dist/agent-src/rules/brand-consistency.md +0 -1
  74. package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
  75. package/dist/agent-src/rules/broken-access-control.md +0 -1
  76. package/dist/agent-src/rules/cli-output-handling.md +0 -1
  77. package/dist/agent-src/rules/code-comment-discipline.md +0 -1
  78. package/dist/agent-src/rules/code-provenance.md +0 -1
  79. package/dist/agent-src/rules/command-suggestion-policy.md +0 -1
  80. package/dist/agent-src/rules/commit-conventions.md +1 -2
  81. package/dist/agent-src/rules/communication-through-line.md +1 -2
  82. package/dist/agent-src/rules/content-quoting-floor.md +1 -2
  83. package/dist/agent-src/rules/context-hygiene.md +5 -5
  84. package/dist/agent-src/rules/copilot-routing.md +1 -2
  85. package/dist/agent-src/rules/council-availability.md +4 -4
  86. package/dist/agent-src/rules/cross-source-consistency.md +4 -6
  87. package/dist/agent-src/rules/decision-revisit-gate.md +2 -3
  88. package/dist/agent-src/rules/delegation-policy.md +2 -3
  89. package/dist/agent-src/rules/design-fidelity.md +0 -1
  90. package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
  91. package/dist/agent-src/rules/devcontainer-routing.md +1 -2
  92. package/dist/agent-src/rules/doc-screenshot-hygiene.md +0 -1
  93. package/dist/agent-src/rules/docker-commands.md +0 -1
  94. package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
  95. package/dist/agent-src/rules/domain-safety-disclaimer.md +0 -1
  96. package/dist/agent-src/rules/domain-safety-pii.md +0 -1
  97. package/dist/agent-src/rules/domain-safety-retention.md +0 -1
  98. package/dist/agent-src/rules/downstream-changes.md +0 -1
  99. package/dist/agent-src/rules/engineering-safety-floor.md +0 -1
  100. package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
  101. package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
  102. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -2
  103. package/dist/agent-src/rules/finance-safety-floor.md +0 -1
  104. package/dist/agent-src/rules/fix-what-you-see.md +6 -32
  105. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
  106. package/dist/agent-src/rules/git-history-discipline.md +1 -2
  107. package/dist/agent-src/rules/guidelines.md +0 -1
  108. package/dist/agent-src/rules/history-discipline.md +0 -1
  109. package/dist/agent-src/rules/icon-consistency.md +0 -1
  110. package/dist/agent-src/rules/image-likeness-and-rights.md +0 -1
  111. package/dist/agent-src/rules/improve-before-implement.md +0 -1
  112. package/dist/agent-src/rules/invite-challenge.md +0 -1
  113. package/dist/agent-src/rules/laravel-routing.md +1 -2
  114. package/dist/agent-src/rules/laravel-translations.md +0 -1
  115. package/dist/agent-src/rules/legal-safety-floor.md +0 -1
  116. package/dist/agent-src/rules/lethal-trifecta-guard.md +0 -1
  117. package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
  118. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +0 -1
  119. package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
  120. package/dist/agent-src/rules/media-governance-routing.md +1 -2
  121. package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
  122. package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
  123. package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
  124. package/dist/agent-src/rules/missing-tool-handling.md +0 -1
  125. package/dist/agent-src/rules/model-recommendation.md +0 -1
  126. package/dist/agent-src/rules/neighbour-precedence.md +64 -0
  127. package/dist/agent-src/rules/no-attribution-footers.md +0 -1
  128. package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
  129. package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
  130. package/dist/agent-src/rules/no-roadmap-references.md +0 -1
  131. package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
  132. package/dist/agent-src/rules/onboarding-gate.md +0 -1
  133. package/dist/agent-src/rules/output-discipline.md +0 -1
  134. package/dist/agent-src/rules/package-ci-checks.md +0 -1
  135. package/dist/agent-src/rules/persona-governance.md +2 -3
  136. package/dist/agent-src/rules/php-coding.md +0 -1
  137. package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
  138. package/dist/agent-src/rules/preservation-guard.md +0 -1
  139. package/dist/agent-src/rules/provider-lifecycle-discipline.md +2 -3
  140. package/dist/agent-src/rules/question-not-instruction.md +0 -1
  141. package/dist/agent-src/rules/reviewer-awareness.md +0 -1
  142. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
  143. package/dist/agent-src/rules/roadmap-progress-sync.md +3 -6
  144. package/dist/agent-src/rules/role-mode-adherence.md +0 -1
  145. package/dist/agent-src/rules/rule-type-governance.md +0 -1
  146. package/dist/agent-src/rules/runtime-safety.md +0 -1
  147. package/dist/agent-src/rules/scale-discipline.md +0 -1
  148. package/dist/agent-src/rules/secret-vcs-guard.md +0 -1
  149. package/dist/agent-src/rules/security-sensitive-stop.md +0 -1
  150. package/dist/agent-src/rules/self-repair-loop.md +0 -1
  151. package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
  152. package/dist/agent-src/rules/session-canary.md +0 -1
  153. package/dist/agent-src/rules/settings-ask-protocol.md +5 -6
  154. package/dist/agent-src/rules/size-enforcement.md +0 -1
  155. package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
  156. package/dist/agent-src/rules/skill-quality.md +0 -1
  157. package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
  158. package/dist/agent-src/rules/source-confidentiality.md +0 -1
  159. package/dist/agent-src/rules/source-discovery-gate.md +0 -1
  160. package/dist/agent-src/rules/source-of-truth.md +0 -1
  161. package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
  162. package/dist/agent-src/rules/strategy-safety-floor.md +0 -1
  163. package/dist/agent-src/rules/symfony-routing.md +1 -2
  164. package/dist/agent-src/rules/think-before-action.md +0 -1
  165. package/dist/agent-src/rules/token-budget-discipline.md +12 -46
  166. package/dist/agent-src/rules/token-efficiency.md +0 -1
  167. package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
  168. package/dist/agent-src/rules/tool-safety.md +0 -1
  169. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  170. package/dist/agent-src/rules/untrusted-input-defense.md +0 -1
  171. package/dist/agent-src/rules/upstream-proposal.md +0 -1
  172. package/dist/agent-src/rules/user-interaction.md +0 -1
  173. package/dist/agent-src/rules/user-interrupt-priority.md +0 -1
  174. package/dist/agent-src/scripts/gate_budget.ts +47 -23
  175. package/dist/agent-src/scripts/gate_execute.ts +12 -4
  176. package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
  177. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
  178. package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
  179. package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
  180. package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
  181. package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
  182. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
  183. package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
  184. package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
  185. package/dist/agent-src/skills/database/data/manifest.json +2 -2
  186. package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
  187. package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
  188. package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
  189. package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
  190. package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
  191. package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
  192. package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
  193. package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
  194. package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
  195. package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
  196. package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
  197. package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
  198. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +4 -2
  199. package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
  200. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +3 -1
  201. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
  202. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  203. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
  204. package/dist/agent-src/templates/AGENTS.md +1 -1
  205. package/dist/agent-src/templates/agent-settings.md +46 -129
  206. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
  207. package/dist/agent-src/templates/command.md +2 -2
  208. package/dist/agent-src/templates/roadmaps.md +6 -1
  209. package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  210. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  211. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  212. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  213. package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  214. package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  215. package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  216. package/dist/agent-src/templates/skill.md +1 -1
  217. package/dist/cli/registry.js +1 -0
  218. package/dist/cli-delegate/check_update_banner.js +195 -119
  219. package/dist/cli-delegate/{chunk-TRI6DQZ6.js → chunk-5GPULQBE.js} +60 -42
  220. package/dist/cli-delegate/{chunk-JQVYKIZA.js → chunk-6SRJTYJS.js} +33 -177
  221. package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
  222. package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
  223. package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
  224. package/dist/cli-delegate/{chunk-NQ7WT43P.js → chunk-AED2VQGM.js} +65 -11
  225. package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
  226. package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
  227. package/dist/cli-delegate/{chunk-NXEILF2K.js → chunk-F3QDG2GT.js} +481 -72
  228. package/dist/cli-delegate/{chunk-CQWX336L.js → chunk-G5VVIZRC.js} +3 -3
  229. package/dist/cli-delegate/{chunk-JY2I2DXV.js → chunk-H76PHZBI.js} +137 -60
  230. package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
  231. package/dist/cli-delegate/{chunk-LIYTLH6O.js → chunk-KXXWJTDO.js} +5 -10
  232. package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
  233. package/dist/cli-delegate/{chunk-S4YWNRKO.js → chunk-MDPPKQCQ.js} +34 -1
  234. package/dist/cli-delegate/{chunk-S4UMZNES.js → chunk-MSUL5NYP.js} +5 -5
  235. package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
  236. package/dist/cli-delegate/{chunk-GKE7ZVFA.js → chunk-PWX7CKMD.js} +1 -1
  237. package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
  238. package/dist/cli-delegate/{chunk-RQNNW7IF.js → chunk-T7XSJSUQ.js} +433 -632
  239. package/dist/cli-delegate/{chunk-EEHDXSYD.js → chunk-WQWXGIWB.js} +12 -273
  240. package/dist/cli-delegate/{chunk-QLDHIPWR.js → chunk-XQTPXKFA.js} +1 -1
  241. package/dist/cli-delegate/cmd_analyze_session.js +223 -43
  242. package/dist/cli-delegate/cmd_brand_status.js +3 -2
  243. package/dist/cli-delegate/cmd_conformance.js +105 -35
  244. package/dist/cli-delegate/cmd_converge.js +3 -3
  245. package/dist/cli-delegate/cmd_doctor.js +15 -12
  246. package/dist/cli-delegate/cmd_doctor_neighbours.js +11 -4
  247. package/dist/cli-delegate/cmd_explain.js +43 -19
  248. package/dist/cli-delegate/cmd_export.js +10 -10
  249. package/dist/cli-delegate/cmd_git_convention.js +1851 -0
  250. package/dist/cli-delegate/cmd_handoff.js +40 -21
  251. package/dist/cli-delegate/cmd_mcp_available.js +3 -2
  252. package/dist/cli-delegate/cmd_migrate.js +3 -3
  253. package/dist/cli-delegate/cmd_packs_active.js +4 -3
  254. package/dist/cli-delegate/cmd_prune.js +10 -10
  255. package/dist/cli-delegate/cmd_route_audit.js +7 -7
  256. package/dist/cli-delegate/cmd_route_explain.js +5 -5
  257. package/dist/cli-delegate/cmd_session_recycle.js +9 -7
  258. package/dist/cli-delegate/cmd_settings_check.js +54 -7
  259. package/dist/cli-delegate/cmd_settings_get.js +18 -8
  260. package/dist/cli-delegate/cmd_settings_migrate.js +10 -10
  261. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  262. package/dist/cli-delegate/cmd_sync.js +10 -10
  263. package/dist/cli-delegate/cmd_uninstall.js +10 -10
  264. package/dist/cli-delegate/cmd_update.js +7 -7
  265. package/dist/cli-delegate/cmd_upgrade.js +7 -7
  266. package/dist/cli-delegate/cmd_validate.js +10 -10
  267. package/dist/cli-delegate/cmd_versions.js +5 -5
  268. package/dist/cli-delegate/gate_budget.js +1 -1
  269. package/dist/cli-delegate/gate_execute.js +2 -2
  270. package/dist/cli-delegate/roadmap_gates.js +2 -2
  271. package/dist/discovery/deprecation-report.md +1 -1
  272. package/dist/discovery/discovery-manifest.json +129 -90
  273. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  274. package/dist/discovery/discovery-manifest.summary.md +3 -3
  275. package/dist/discovery/dormancy-report.md +1 -1
  276. package/dist/discovery/orphan-report.md +1 -1
  277. package/dist/discovery/packs.json +24 -9
  278. package/dist/discovery/trust-report.md +3 -3
  279. package/dist/discovery/workspaces.json +5 -4
  280. package/dist/hooks/dispatch.js +2151 -1820
  281. package/dist/hooks/dispatch.sha256 +1 -1
  282. package/dist/install/install.mjs +462 -431
  283. package/dist/install/installThinLayer.js +40 -12
  284. package/dist/install/installedRuleLinks.js +105 -19
  285. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  286. package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
  287. package/dist/mcp/registry-manifest.json +5 -5
  288. package/dist/mcp/server.json +2 -2
  289. package/dist/mcp/server.mjs +2702 -1995
  290. package/dist/router.json +1 -1
  291. package/dist/scripts/_lib/agent_settings.js +15 -120
  292. package/dist/scripts/_lib/compile_time_toggles.js +12 -21
  293. package/dist/scripts/_lib/git_convention.js +232 -0
  294. package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
  295. package/dist/scripts/_lib/installed_layer.js +344 -18
  296. package/dist/scripts/_lib/package_root.js +68 -0
  297. package/dist/scripts/_lib/rule_law_section.js +1 -1
  298. package/dist/scripts/_lib/settings_removed_keys.js +168 -0
  299. package/dist/scripts/_lib/thin_rules.js +80 -3
  300. package/dist/server/app.js +2 -1
  301. package/dist/server/gitKeysGate.js +105 -0
  302. package/dist/server/routes/settings.js +67 -8
  303. package/dist/server/routes/wizard.js +4 -0
  304. package/dist/server/schemas/settings.js +23 -81
  305. package/dist/shared/skillRanking.js +30 -4
  306. package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
  307. package/dist/ui/index.html +1 -1
  308. package/docs/MIGRATION.md +72 -1
  309. package/docs/contracts/settings-classes.md +35 -65
  310. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  311. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  312. package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  313. package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  314. package/docs/guidelines/agent-infra/layered-settings.md +13 -1
  315. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  316. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  317. package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
  318. package/docs/guidelines/php/git.md +15 -2
  319. package/package.json +1 -1
  320. package/src/agent-src/scripts/gate_budget.ts +47 -23
  321. package/src/agent-src/scripts/gate_execute.ts +12 -4
  322. package/src/agent-src/templates/AGENTS.md +1 -1
  323. package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  324. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  325. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  326. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  327. package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  328. package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  329. package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  330. package/src/config/agent-settings.template.yml +79 -312
  331. package/src/config/agents-paths.yml +16 -0
  332. package/src/config/assurance-capability-registry.json +7 -3
  333. package/src/config/ci-local-parity.yml +3 -0
  334. package/src/config/continuity-surface.json +3 -3
  335. package/src/config/discovery/packs.yml +17 -1
  336. package/src/config/evaluator-budgets.json +19 -6
  337. package/src/config/gate-coverage.yml +65 -23
  338. package/src/config/gate-violation-baselines.json +53 -13
  339. package/src/config/gitignore-block.txt +19 -0
  340. package/src/config/hook-bundle-budget.json +1 -1
  341. package/src/config/release-gate-locality.yml +2 -1
  342. package/src/config/routing-coverage-seed.json +48 -6
  343. package/src/config/rule-law-ceilings.json +12 -6
  344. package/src/config/rule-obligations.json +833 -0
  345. package/src/install/installThinLayer.ts +76 -10
  346. package/src/install/installedRuleLinks.ts +105 -17
  347. package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
  348. package/src/scripts/_cli/cmd_conformance.ts +13 -2
  349. package/src/scripts/_cli/cmd_doctor.ts +10 -10
  350. package/src/scripts/_cli/cmd_doctor_neighbours.ts +133 -0
  351. package/src/scripts/_cli/cmd_explain.ts +72 -14
  352. package/src/scripts/_cli/cmd_git_convention.ts +637 -0
  353. package/src/scripts/_cli/cmd_settings_check.ts +66 -8
  354. package/src/scripts/_cli/cmd_settings_get.ts +18 -3
  355. package/src/scripts/_cli/doctor_execution.ts +72 -1
  356. package/src/scripts/_dispatch.bash +37 -2
  357. package/src/scripts/_lib/agent_settings.ts +15 -124
  358. package/src/scripts/_lib/blocked_by_marker.ts +1 -1
  359. package/src/scripts/_lib/compile_time_toggles.ts +12 -21
  360. package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
  361. package/src/scripts/_lib/git_base_ref.ts +92 -0
  362. package/src/scripts/_lib/git_convention.ts +291 -0
  363. package/src/scripts/_lib/git_convention_carrier.ts +389 -0
  364. package/src/scripts/_lib/git_convention_grammar.ts +246 -0
  365. package/src/scripts/_lib/git_convention_measure.ts +344 -0
  366. package/src/scripts/_lib/global_deploy_inventory.ts +74 -0
  367. package/src/scripts/_lib/graph_feeder_record.ts +236 -13
  368. package/src/scripts/_lib/installed_layer.ts +448 -23
  369. package/src/scripts/_lib/knowledge_global.ts +14 -1
  370. package/src/scripts/_lib/module_reach.ts +456 -0
  371. package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
  372. package/src/scripts/_lib/neighbour_tool_use.ts +8 -10
  373. package/src/scripts/_lib/ratification_artifact.ts +220 -1
  374. package/src/scripts/_lib/ratification_subject.ts +92 -0
  375. package/src/scripts/_lib/release_findings_ingest.ts +15 -7
  376. package/src/scripts/_lib/release_highlights.ts +5 -0
  377. package/src/scripts/_lib/rule_law_section.ts +1 -1
  378. package/src/scripts/_lib/rule_obligations.ts +205 -0
  379. package/src/scripts/_lib/self_repair.ts +1 -1
  380. package/src/scripts/_lib/settings_removed_keys.ts +173 -0
  381. package/src/scripts/_lib/test_red_state.ts +5 -0
  382. package/src/scripts/_lib/thin_rules.ts +83 -3
  383. package/src/scripts/_lib/touched_file_quality.ts +142 -6
  384. package/src/scripts/_lib/worker_budget.ts +1 -1
  385. package/src/scripts/adr/regenerate_index.ts +16 -1
  386. package/src/scripts/ai_council/config.ts +7 -7
  387. package/src/scripts/ai_council/modification_review.ts +100 -0
  388. package/src/scripts/ai_council/modification_review_questions.ts +90 -0
  389. package/src/scripts/ai_council/orchestrator.ts +5 -5
  390. package/src/scripts/ai_council/spend_gate.ts +18 -7
  391. package/src/scripts/ai_team/config.ts +11 -16
  392. package/src/scripts/audit_user_type_axis.ts +20 -8
  393. package/src/scripts/bench_ab_clone.ts +1 -2
  394. package/src/scripts/bench_concern_spawn_path.ts +129 -0
  395. package/src/scripts/bench_graph_feeder_latency.ts +584 -0
  396. package/src/scripts/check_agent_artifact_location.ts +36 -0
  397. package/src/scripts/check_branch_freshness.ts +147 -24
  398. package/src/scripts/check_enforcement_coverage.ts +130 -0
  399. package/src/scripts/check_evaluator_budgets.ts +4 -0
  400. package/src/scripts/check_finding_dispositions.ts +88 -25
  401. package/src/scripts/check_gate_coverage.ts +4 -3
  402. package/src/scripts/check_gate_reachability.ts +48 -1
  403. package/src/scripts/check_generator_sync.ts +48 -0
  404. package/src/scripts/check_hook_bundle_composition.ts +107 -0
  405. package/src/scripts/check_kernel_edit_ratified.ts +86 -6
  406. package/src/scripts/check_pack_size.ts +1 -1
  407. package/src/scripts/check_release_manifest_content.ts +189 -0
  408. package/src/scripts/check_standing_rule_delivery.ts +96 -0
  409. package/src/scripts/check_suppression_hygiene.ts +2 -5
  410. package/src/scripts/ci_settle.ts +42 -5
  411. package/src/scripts/classify_merge_risk.ts +320 -0
  412. package/src/scripts/closure_scan.ts +91 -1
  413. package/src/scripts/command_suggester/settings.ts +8 -108
  414. package/src/scripts/command_suggester/types.ts +4 -5
  415. package/src/scripts/consumer_matrix.ts +102 -0
  416. package/src/scripts/context_hygiene_hook.ts +13 -23
  417. package/src/scripts/cost/preflight.mjs +12 -1
  418. package/src/scripts/council_cli.ts +7 -7
  419. package/src/scripts/dispatch_r2_reviewer.ts +24 -11
  420. package/src/scripts/explain_run.ts +8 -13
  421. package/src/scripts/generate_git_convention_grammar.ts +127 -0
  422. package/src/scripts/generate_host_cost_table.ts +20 -3
  423. package/src/scripts/handoff_context_hook.ts +86 -37
  424. package/src/scripts/hook_manifest.json +1 -1
  425. package/src/scripts/hook_manifest.yaml +25 -6
  426. package/src/scripts/hooks/block_config_weakening.ts +48 -5
  427. package/src/scripts/hooks/block_plumbing_writes.ts +8 -4
  428. package/src/scripts/hooks/concern_registry.ts +2 -0
  429. package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
  430. package/src/scripts/hooks/evidence_independence.ts +12 -4
  431. package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
  432. package/src/scripts/hooks/push_settle_hook.ts +4 -3
  433. package/src/scripts/hooks/rule_inject_hook.ts +44 -3
  434. package/src/scripts/hooks/state_io.ts +8 -4
  435. package/src/scripts/hooks/telemetry_usage_hook.ts +7 -46
  436. package/src/scripts/hooks/turn_end_gate_hook.ts +1 -0
  437. package/src/scripts/install-hooks.sh +42 -14
  438. package/src/scripts/install.ts +7 -31
  439. package/src/scripts/lint_blocker_citations.ts +198 -0
  440. package/src/scripts/lint_evidence_artifacts.ts +9 -0
  441. package/src/scripts/lint_hook_concern_budget.ts +10 -21
  442. package/src/scripts/lint_pack_boundaries.ts +406 -28
  443. package/src/scripts/lint_roadmap_blockers.ts +282 -29
  444. package/src/scripts/lint_roadmap_complexity.ts +4 -2
  445. package/src/scripts/lint_rule_law_section.ts +151 -21
  446. package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
  447. package/src/scripts/lint_settings_classes.ts +1 -1
  448. package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
  449. package/src/scripts/lint_workflow_security.ts +12 -1
  450. package/src/scripts/measure_skill_ranker_baseline.ts +5 -0
  451. package/src/scripts/model_rule_injection.ts +3 -3
  452. package/src/scripts/onboarding_gate_hook.ts +7 -6
  453. package/src/scripts/print_required_checks.ts +15 -3
  454. package/src/scripts/probe_host_compliance.ts +33 -5
  455. package/src/scripts/probe_turnaround.ts +183 -3
  456. package/src/scripts/ratification_header.ts +77 -0
  457. package/src/scripts/refusal_envelope.ts +138 -0
  458. package/src/scripts/release_publication.ts +29 -3
  459. package/src/scripts/report_dispatch_import_closure.ts +76 -0
  460. package/src/scripts/report_installed_rule_links.ts +118 -1
  461. package/src/scripts/report_module_reach.ts +212 -0
  462. package/src/scripts/report_obligation_mechanism.ts +16 -18
  463. package/src/scripts/report_parked_blockers.ts +222 -0
  464. package/src/scripts/report_required_checks_drift.ts +196 -0
  465. package/src/scripts/report_skill_ranker_confusion.ts +103 -2
  466. package/src/scripts/report_standing_payload_by_host.ts +1 -1
  467. package/src/scripts/routing_doctor.ts +2 -2
  468. package/src/scripts/schemas/agent-settings.schema.json +17 -31
  469. package/src/scripts/schemas/command.schema.json +1 -1
  470. package/src/scripts/schemas/skill.schema.json +1 -1
  471. package/src/scripts/self_review_gate.ts +39 -2
  472. package/src/scripts/skill_tools/score_skill_relevance.ts +28 -19
  473. package/src/scripts/sync_pr_branch.ts +403 -132
  474. package/src/scripts/trigger_eval_grandfather.json +0 -16
  475. package/src/server/schemas/settings.ts +33 -177
  476. package/src/shared/skillRanking.ts +53 -4
  477. package/dist/cli-delegate/chunk-NYX3L2PZ.js +0 -99
  478. package/src/scripts/_lib/config_chain.ts +0 -477
  479. package/src/scripts/_lib/conformance_report.ts +0 -403
  480. package/src/scripts/_lib/eval_discrimination.ts +0 -95
  481. package/src/scripts/_lib/evaluator_promotion.ts +0 -225
  482. package/src/scripts/_lib/file_slicer.ts +0 -117
  483. package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
  484. package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
  485. package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
  486. package/src/scripts/_lib/role_split.ts +0 -338
@@ -0,0 +1,1851 @@
1
+ import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
2
+ import {
3
+ GIT_CONVENTION_KEYS,
4
+ checkoutSource,
5
+ conventionDefault,
6
+ conventionReading,
7
+ describeRefusal,
8
+ invalidReason,
9
+ isRefusal,
10
+ parseLayerText,
11
+ readGitConventionKey
12
+ } from "./chunk-6V5FMA3P.js";
13
+ import {
14
+ reportScanned
15
+ } from "./chunk-I3EIN2YG.js";
16
+ import {
17
+ require_dist
18
+ } from "./chunk-OY53J6II.js";
19
+ import {
20
+ find_project_root
21
+ } from "./chunk-H76PHZBI.js";
22
+ import "./chunk-T6BP34WI.js";
23
+ import "./chunk-MCJYCQPF.js";
24
+ import {
25
+ __toESM
26
+ } from "./chunk-J55WI3RR.js";
27
+
28
+ // src/scripts/_cli/cmd_git_convention.ts
29
+ import { spawnSync as spawnSync4 } from "node:child_process";
30
+ import * as fs2 from "node:fs";
31
+ import * as path3 from "node:path";
32
+ import process2 from "node:process";
33
+ import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
34
+
35
+ // src/scripts/_lib/git_base_ref.ts
36
+ var REMOTE_TRACKING = "refs/remotes/";
37
+ var LOCAL_HEADS = "refs/heads/";
38
+ function blankBaseError(value) {
39
+ return value.trim() === "" ? "--base was given an empty value \u2014 name the base branch (an unset variable expands to nothing); nothing was read or merged" : null;
40
+ }
41
+ function baseValueError(value, remotes = []) {
42
+ const blank = blankBaseError(value);
43
+ if (blank !== null) return blank;
44
+ const v = value.trim();
45
+ const branch = v.startsWith("-") ? v : parseBaseRef(v, remotes)?.branch ?? v;
46
+ return branch.startsWith("-") ? `--base ${JSON.stringify(v)} names a branch that starts with "-", which git reads as an option \u2014 no branch has that name; nothing was read or merged` : null;
47
+ }
48
+ function parseBaseRef(value, remotes = []) {
49
+ const v = value.trim();
50
+ if (v === "") return null;
51
+ if (v.startsWith(LOCAL_HEADS) && v.length > LOCAL_HEADS.length) {
52
+ const branch = v.slice(LOCAL_HEADS.length);
53
+ return { remote: "origin", branch, ref: `${REMOTE_TRACKING}origin/${branch}` };
54
+ }
55
+ if (v.startsWith("refs/")) return splitResolvedRef(v);
56
+ const slash = v.indexOf("/");
57
+ const known = /* @__PURE__ */ new Set(["origin", ...remotes]);
58
+ if (slash > 0 && slash < v.length - 1 && known.has(v.slice(0, slash))) {
59
+ return { remote: v.slice(0, slash), branch: v.slice(slash + 1), ref: v };
60
+ }
61
+ return { remote: "origin", branch: v, ref: `origin/${v}` };
62
+ }
63
+ function splitResolvedRef(ref) {
64
+ if (ref.startsWith(LOCAL_HEADS)) return { remote: "origin", branch: ref.slice(LOCAL_HEADS.length), ref };
65
+ if (ref.startsWith(REMOTE_TRACKING)) {
66
+ const rest = ref.slice(REMOTE_TRACKING.length);
67
+ const slash2 = rest.indexOf("/");
68
+ if (slash2 > 0) return { remote: rest.slice(0, slash2), branch: rest.slice(slash2 + 1), ref };
69
+ }
70
+ if (ref.startsWith("refs/")) return { remote: "origin", branch: ref, ref };
71
+ const slash = ref.indexOf("/");
72
+ return slash > 0 ? { remote: ref.slice(0, slash), branch: ref.slice(slash + 1), ref } : { remote: "origin", branch: ref, ref: `origin/${ref}` };
73
+ }
74
+
75
+ // src/scripts/_lib/git_convention_carrier.ts
76
+ import { spawnSync } from "node:child_process";
77
+ import * as fs from "node:fs";
78
+ import * as path from "node:path";
79
+ var CARRIER_PATH = ".git-convention.yml";
80
+ var NETWORK_TIMEOUT_MS = 8e3;
81
+ var CARRIER_FETCH_TIMEOUT_MS = 6e4;
82
+ var runGit = (cmd, args, cwd, timeoutMs) => {
83
+ const r = spawnSync(cmd, [...args], { cwd, encoding: "utf-8", timeout: timeoutMs, maxBuffer: 32 * 1024 * 1024 });
84
+ const code = r.error?.code;
85
+ return { ok: r.status === 0, out: r.stdout ?? "", err: (r.stderr ?? "").trim(), timedOut: code === "ETIMEDOUT" };
86
+ };
87
+ function sh(cmd, args, cwd) {
88
+ return runGit(cmd, args, cwd, NETWORK_TIMEOUT_MS);
89
+ }
90
+ function resolveTarget(deps, override, defaultRef) {
91
+ const given = override === null ? null : parseBaseRef(override, deps.remotes?.() ?? []);
92
+ if (given !== null) return { ref: given.ref, reason: "explicit-base-override" };
93
+ const def = defaultRef === void 0 ? deps.defaultBranch() : defaultRef;
94
+ return def === null ? null : { ref: def, reason: "repository-default-branch" };
95
+ }
96
+ function parseSymrefDefault(lsRemoteOut) {
97
+ for (const line of lsRemoteOut.split("\n")) {
98
+ const t = line.trim();
99
+ if (!t.startsWith("ref:")) continue;
100
+ const name = t.slice("ref:".length).trim().split(/\s+/)[0] ?? "";
101
+ if (!name.startsWith("refs/heads/")) continue;
102
+ const short = name.slice("refs/heads/".length).trim();
103
+ if (short !== "") return `origin/${short}`;
104
+ }
105
+ return null;
106
+ }
107
+ function parseExactHeadSha(lsRemoteOut, branch) {
108
+ const want = `refs/heads/${branch}`;
109
+ for (const line of lsRemoteOut.split("\n")) {
110
+ const [sha, name] = line.trim().split(/\s+/);
111
+ if (name === want && sha !== void 0 && /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(sha)) return sha;
112
+ }
113
+ return null;
114
+ }
115
+ function makeTargetDeps(repo, run = runGit) {
116
+ const ask = (cmd, args) => run(cmd, args, repo, NETWORK_TIMEOUT_MS);
117
+ return {
118
+ // The server first, the local ref second: `refs/remotes/origin/HEAD` is
119
+ // not set in every checkout (see `parseSymrefDefault`).
120
+ defaultBranch: () => {
121
+ const remote = ask("git", ["ls-remote", "--symref", "--", "origin", "HEAD"]);
122
+ const fromServer = remote.ok ? parseSymrefDefault(remote.out) : null;
123
+ if (fromServer !== null) return fromServer;
124
+ const head = ask("git", ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]);
125
+ return head.ok && head.out.trim() !== "" ? head.out.trim() : null;
126
+ },
127
+ remoteSha: (ref) => {
128
+ const { remote, branch } = splitResolvedRef(ref);
129
+ const out = ask("git", ["ls-remote", "--", remote, `refs/heads/${branch}`]);
130
+ return out.ok ? parseExactHeadSha(out.out, branch) : null;
131
+ },
132
+ remotes: () => {
133
+ const out = ask("git", ["remote"]);
134
+ return out.ok ? out.out.split("\n").map((r) => r.trim()).filter((r) => r !== "") : [];
135
+ },
136
+ defaultBranchOf: (remote) => {
137
+ const out = ask("git", ["ls-remote", "--symref", "--", remote, "HEAD"]);
138
+ const named = out.ok ? parseSymrefDefault(out.out) : null;
139
+ return named === null ? null : `${remote}/${named.slice("origin/".length)}`;
140
+ }
141
+ };
142
+ }
143
+ function memoTargetDeps(deps) {
144
+ const cache = /* @__PURE__ */ new Map();
145
+ const once = (key, ask) => {
146
+ if (!cache.has(key)) cache.set(key, ask());
147
+ return cache.get(key);
148
+ };
149
+ return {
150
+ ...deps,
151
+ defaultBranch: () => once("default", () => deps.defaultBranch()),
152
+ remoteSha: (ref) => once(`sha ${ref}`, () => deps.remoteSha(ref)),
153
+ ...deps.remotes === void 0 ? {} : { remotes: () => once("remotes", () => deps.remotes()) },
154
+ ...deps.defaultBranchOf === void 0 ? {} : { defaultBranchOf: (remote) => once(`default ${remote}`, () => deps.defaultBranchOf(remote)) }
155
+ };
156
+ }
157
+ function carrierBlobAt(repo, sha, fetchRef, run = runGit) {
158
+ const local = (args) => run("git", args, repo, NETWORK_TIMEOUT_MS);
159
+ const has = () => local(["cat-file", "-e", `${sha}^{commit}`]).ok;
160
+ if (!has()) {
161
+ const from = fetchRef === null ? null : splitResolvedRef(fetchRef);
162
+ const fetched = from === null ? null : run("git", ["fetch", "-q", "--", from.remote, from.branch], repo, CARRIER_FETCH_TIMEOUT_MS);
163
+ if (fetched?.timedOut === true) return { kind: "no-commit", timedOut: true };
164
+ if (!has()) return { kind: "no-commit" };
165
+ }
166
+ const listed = local(["ls-tree", "--name-only", sha, "--", CARRIER_PATH]);
167
+ if (!listed.ok) return { kind: "no-commit" };
168
+ if (listed.out.trim() === "") return { kind: "absent" };
169
+ const blob = local(["cat-file", "-p", `${sha}:${CARRIER_PATH}`]);
170
+ return blob.ok ? { kind: "present", text: blob.out } : { kind: "no-commit" };
171
+ }
172
+ var IGNORED_CARRIER_PATH = ".git-convention.yaml";
173
+ function ignoredCarrierWarning(root) {
174
+ return fs.existsSync(path.join(root, IGNORED_CARRIER_PATH)) ? `${IGNORED_CARRIER_PATH} is ignored \u2014 the team declaration is read only from ${CARRIER_PATH}; rename it to ${CARRIER_PATH}` : null;
175
+ }
176
+ function _parseCarrier(text) {
177
+ const parsed = parseLayerText(text);
178
+ if (parsed.parsed !== "valid") return parsed;
179
+ const top = Object.keys(parsed.data).filter((k) => GIT_CONVENTION_KEYS.includes(k));
180
+ if (top.length === 0) return parsed;
181
+ const listed = top.map((k) => `\`${k}\``).join(", ");
182
+ return { ...parsed, shapeError: `${listed} set at the top level; the carrier reads its keys under \`git:\` (\`git:\` on one line, then \` ${top[0]}: <value>\` indented)` };
183
+ }
184
+ function _carrierLayer(label, blob) {
185
+ if (blob.kind === "absent") return { path: label, carries: true, parsed: "absent", data: null };
186
+ return { path: label, carries: true, ..._parseCarrier(blob.text) };
187
+ }
188
+ function _workingTreeLayer(root) {
189
+ const p = path.join(root, CARRIER_PATH);
190
+ let text;
191
+ try {
192
+ if (!fs.statSync(p).isFile()) return { path: p, carries: true, parsed: "absent", data: null };
193
+ text = fs.readFileSync(p, "utf-8");
194
+ } catch (err) {
195
+ const parsed = err.code === "ENOENT" ? "absent" : "malformed";
196
+ return { path: p, carries: true, parsed, data: null };
197
+ }
198
+ return { path: p, carries: true, ..._parseCarrier(text) };
199
+ }
200
+ function conventionRoot(cwd) {
201
+ const top = sh("git", ["rev-parse", "--show-toplevel"], cwd);
202
+ if (top.ok && top.out.trim() !== "") return { root: top.out.trim(), git: true };
203
+ return { root: find_project_root(cwd) ?? cwd, git: false };
204
+ }
205
+ function _short(sha) {
206
+ return sha.slice(0, 12);
207
+ }
208
+ function readCommittedConvention(cwd, options = {}) {
209
+ const keys = options.keys ?? GIT_CONVENTION_KEYS;
210
+ const { root, git } = conventionRoot(cwd);
211
+ const developer = checkoutSource(root).layers();
212
+ const readings = {};
213
+ const readAt = {};
214
+ const candidates = {};
215
+ let target = null;
216
+ if (!git) {
217
+ for (const key of keys) {
218
+ readings[key] = readGitConventionKey(key, { layers: () => developer });
219
+ readAt[key] = null;
220
+ }
221
+ return { root, readings, readAt, target, candidates };
222
+ }
223
+ const headSha = sh("git", ["rev-parse", "--verify", "-q", "HEAD"], root).out.trim();
224
+ const headLabel = `${CARRIER_PATH} at ${headSha === "" ? "HEAD" : _short(headSha)}`;
225
+ let headBlob = null;
226
+ const atHead = () => {
227
+ if (headBlob === null) headBlob = headSha === "" ? { kind: "absent" } : carrierBlobAt(root, headSha, null, options.run);
228
+ return headBlob;
229
+ };
230
+ for (const key of keys) {
231
+ if (key !== "update_strategy") {
232
+ const blob2 = atHead();
233
+ readings[key] = blob2.kind === "no-commit" ? conventionReading(key, "unresolvable", null, headLabel, `the carrier at HEAD (${_short(headSha)}) could not be read`) : readGitConventionKey(key, { layers: () => [...developer, _carrierLayer(headLabel, blob2)] });
234
+ readAt[key] = headSha === "" ? null : headSha;
235
+ continue;
236
+ }
237
+ const deps = options.deps ?? makeTargetDeps(root);
238
+ const ref = resolveTarget(deps, options.override ?? null);
239
+ const sha = ref === null ? null : deps.remoteSha(ref.ref);
240
+ target = ref === null ? null : { ...ref, sha };
241
+ readAt[key] = sha;
242
+ if (ref === null || sha === null) {
243
+ readings[key] = conventionReading(
244
+ key,
245
+ "unresolvable",
246
+ null,
247
+ CARRIER_PATH,
248
+ ref === null ? "no --base was given and origin names no default branch, so the target commit is unknown" : `the server reports no commit for ${ref.ref} (no such branch on ${splitResolvedRef(ref.ref).remote}, no such remote, or it is unreachable)`
249
+ );
250
+ continue;
251
+ }
252
+ const blob = carrierBlobAt(root, sha, options.fetch === false ? null : ref.ref, options.run);
253
+ if (blob.kind === "no-commit") {
254
+ readings[key] = conventionReading(
255
+ key,
256
+ "unresolvable",
257
+ null,
258
+ `${CARRIER_PATH} at ${_short(sha)}`,
259
+ blob.timedOut === true ? `fetching commit ${_short(sha)} of ${ref.ref} timed out after ${CARRIER_FETCH_TIMEOUT_MS / 1e3} s` : `commit ${_short(sha)} of ${ref.ref} could not be fetched`
260
+ );
261
+ continue;
262
+ }
263
+ readings[key] = readGitConventionKey(key, { layers: () => [...developer, _carrierLayer(`${CARRIER_PATH} at ${_short(sha)}`, blob)] });
264
+ }
265
+ const tree = _workingTreeLayer(root);
266
+ for (const key of keys) {
267
+ const inForce = readings[key];
268
+ const local = readGitConventionKey(key, { layers: () => [...developer, tree] });
269
+ if (local.value !== inForce.value || local.state !== inForce.state) candidates[key] = local;
270
+ }
271
+ return { root, readings, readAt, target, candidates };
272
+ }
273
+
274
+ // src/scripts/_lib/git_convention_grammar.ts
275
+ var TICKET_GRAMMAR = "[A-Z][A-Z0-9]+-[0-9]+";
276
+ var TICKET_TOKEN = `(?<![A-Za-z0-9])${TICKET_GRAMMAR}(?![0-9])`;
277
+ var TICKET_DENYLIST = ["UTF", "ISO", "SHA", "RFC", "CVE", "CWE", "GHSA"];
278
+ var VERSION_LIKE_KEYS = ["HTTP", "HTTPS", "TLS", "SSL", "OAUTH", "PHP", "PYTHON", "JAVA", "JDK", "NODE", "ES", "HTML", "CSS"];
279
+ var ANY_CASE_TOKEN = `(?<![A-Za-z0-9])[A-Za-z][A-Za-z0-9]+-[0-9]+(?![0-9])`;
280
+ function ticketCandidates(text, keys) {
281
+ const card = keys !== void 0 && keys !== null && keys.length > 0 ? keys : null;
282
+ const out = [];
283
+ for (const m of text.matchAll(new RegExp(card === null ? TICKET_TOKEN : ANY_CASE_TOKEN, "g"))) {
284
+ const dash = m[0].indexOf("-");
285
+ const written = m[0].slice(0, dash);
286
+ const key = written.toUpperCase();
287
+ const number = m[0].slice(dash + 1);
288
+ if (written !== key && !card?.includes(key)) continue;
289
+ let status = "ticket";
290
+ if (TICKET_DENYLIST.includes(key)) status = "standard-name";
291
+ else if (card !== null) status = card.includes(key) ? "ticket" : "unknown-key";
292
+ else if (number.length === 1 && VERSION_LIKE_KEYS.includes(key)) status = "version-like";
293
+ out.push({ token: `${key}-${number}`, key, status });
294
+ }
295
+ return out;
296
+ }
297
+ var KEY = /^[A-Z][A-Z0-9]+$/;
298
+ function parseTicketKeys(text) {
299
+ const line = /^\s*ticket_keys\s*:(.*)$/m.exec(text)?.[1];
300
+ const value = line ?? (text.includes(":") ? "" : text);
301
+ return value.replace(/[[\]"']/g, " ").split(/[\s,]+/).filter((k) => KEY.test(k));
302
+ }
303
+ function commitlintIssuePrefixes(configText) {
304
+ const list = /["']?issuePrefixes["']?\s*:\s*\[([^\]]*)\]/.exec(configText)?.[1] ?? "";
305
+ return [...list.matchAll(/["']([^"']*)["']/g)].map((m) => m[1].replace(/-$/, "")).filter((k) => KEY.test(k));
306
+ }
307
+ var COMMIT_TYPES = ["feat", "fix", "chore", "docs", "refactor", "test", "perf", "style", "build", "ci", "revert"];
308
+ var TYPES = COMMIT_TYPES.join("|");
309
+ var FORMAT_GRAMMAR = {
310
+ "ticket-scope": `^(${TYPES})(\\([^)]+\\))?!?: .+`,
311
+ "ticket-conventional": `^(${TICKET_GRAMMAR} )?(${TYPES})(\\([^)]+\\))?!?: .+`
312
+ };
313
+ var FAMILY_ERE = [
314
+ ["conventional", "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([^)]+\\))?!?: "],
315
+ ["ticket-conventional", `^${TICKET_GRAMMAR} (build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([^)]+\\))?!?: `],
316
+ ["ticket-prefix", `^\\[${TICKET_GRAMMAR}\\][: ]|^${TICKET_GRAMMAR}[: ]`],
317
+ // `[:ascii:]` is not a POSIX class (GNU and BSD grep reject it); a leading
318
+ // character outside printable ASCII, the controls and the spaces is the same set.
319
+ ["gitmoji", "^:[a-z0-9_+-]+:[[:space:]]|^[^ -~[:cntrl:][:space:]]"],
320
+ ["imperative-plain", "^[A-Z][a-z]+[[:space:]].*[^.]$"]
321
+ ];
322
+ function _js(ere) {
323
+ return new RegExp(ere.replaceAll("[^ -~[:cntrl:][:space:]]", "[^\\x00-\\x7F\\s]").replaceAll("[[:space:]]", "\\s"));
324
+ }
325
+ var FAMILY_JS = FAMILY_ERE.map(([f, ere]) => [f, _js(ere)]);
326
+ function classifySubject(subject) {
327
+ return FAMILY_JS.find(([, re]) => re.test(subject))?.[0] ?? "other";
328
+ }
329
+ var SCOPE = /^[^(:]*\(([^)]*)\)/;
330
+ function _ticketLeads(rule) {
331
+ return "format" in rule ? rule.format === "ticket-conventional" : rule.family === "ticket-conventional";
332
+ }
333
+ function ruleName(rule) {
334
+ return "format" in rule ? `git.commit_format: ${rule.format}` : `approved family ${rule.family}`;
335
+ }
336
+ var GIT_OWN_SUBJECT = '^((fixup|squash|amend)! .+|Revert ".+")$';
337
+ var TICKETLESS_FORM = {
338
+ "ticket-prefix": "^\\S.*",
339
+ "ticket-conventional": FAMILY_ERE[0]?.[1] ?? ""
340
+ };
341
+ function checkSubject(subject, rule) {
342
+ if (new RegExp(GIT_OWN_SUBJECT).test(subject)) return { ok: true };
343
+ const grammar = "format" in rule ? new RegExp(FORMAT_GRAMMAR[rule.format]) : FAMILY_JS.find(([f]) => f === rule.family)?.[1];
344
+ const ticketless = "family" in rule ? TICKETLESS_FORM[rule.family] : void 0;
345
+ if (!grammar.test(subject) && ticketless !== void 0) {
346
+ const family = rule.family;
347
+ if (!_js(ticketless).test(subject)) {
348
+ return { ok: false, rule: `${ruleName(rule)} \u2014 does not match ${FAMILY_ERE.find(([f]) => f === family)?.[1] ?? ""}, nor its form without a ticket ${ticketless}` };
349
+ }
350
+ const stray = ticketCandidates(subject).find((c) => c.status === "ticket");
351
+ if (stray !== void 0) {
352
+ return { ok: false, rule: `${ruleName(rule)} \u2014 the ticket \`${stray.token}\` stands outside the leading position; it leads the subject or is left out` };
353
+ }
354
+ return { ok: true };
355
+ }
356
+ if (!grammar.test(subject)) {
357
+ return { ok: false, rule: `${ruleName(rule)} \u2014 does not match ${"format" in rule ? FORMAT_GRAMMAR[rule.format] : FAMILY_ERE.find(([f]) => f === rule.family)?.[1] ?? ""}` };
358
+ }
359
+ if (!_ticketLeads(rule)) return { ok: true };
360
+ const lead = /^(\S+) /.exec(subject)?.[1] ?? "";
361
+ const leading = ticketCandidates(lead).find((c) => c.token === lead);
362
+ if (leading !== void 0 && leading.status === "standard-name") {
363
+ return { ok: false, rule: `${ruleName(rule)} \u2014 \`${lead}\` is a standard name, not a ticket` };
364
+ }
365
+ const scope = SCOPE.exec(lead === "" || leading === void 0 ? subject : subject.slice(lead.length + 1))?.[1] ?? "";
366
+ const inScope = ticketCandidates(scope).find((c) => c.status === "ticket");
367
+ if (inScope !== void 0) {
368
+ return { ok: false, rule: `${ruleName(rule)} \u2014 the ticket \`${inScope.token}\` stands inside the scope; it leads the subject, never the scope` };
369
+ }
370
+ return { ok: true };
371
+ }
372
+ var VALUE = /^[A-Za-z0-9._-]+$/;
373
+ var SEPARATORS = "-/_.";
374
+ function renderBranch(pattern, input) {
375
+ const type = input.type ?? "";
376
+ const ticket = input.ticket ?? "";
377
+ let slug = input.slug;
378
+ if (slug === "" || !VALUE.test(slug)) return { ok: false, reason: `slug ${JSON.stringify(slug)} is empty or outside [A-Za-z0-9._-]` };
379
+ if (type !== "" && !VALUE.test(type)) return { ok: false, reason: `type ${JSON.stringify(type)} is outside [A-Za-z0-9._-]` };
380
+ if (ticket !== "" && !new RegExp(`^${TICKET_GRAMMAR}$`).test(ticket)) {
381
+ return { ok: false, reason: `ticket ${JSON.stringify(ticket)} does not match ${TICKET_GRAMMAR}` };
382
+ }
383
+ if (ticket !== "" && !pattern.includes("{ticket}") && slug !== ticket && !slug.startsWith(`${ticket}-`)) slug = `${ticket}-${slug}`;
384
+ const values = { type, ticket, slug };
385
+ const parts = pattern.split(/(\{(?:type|ticket|slug)\})/).filter((p) => p !== "");
386
+ const out = [];
387
+ for (let i = 0; i < parts.length; i++) {
388
+ const part = parts[i];
389
+ const ph = /^\{(type|ticket|slug)\}$/.exec(part);
390
+ if (ph === null) {
391
+ out.push(part);
392
+ continue;
393
+ }
394
+ const value = values[ph[1]];
395
+ if (value !== "") {
396
+ out.push(value);
397
+ continue;
398
+ }
399
+ const next = parts[i + 1];
400
+ if (next !== void 0 && !next.startsWith("{") && SEPARATORS.includes(next[0])) {
401
+ parts[i + 1] = next.slice(1);
402
+ } else if (next === void 0 && out.length > 0) {
403
+ const prev = out[out.length - 1];
404
+ if (SEPARATORS.includes(prev.slice(-1))) out[out.length - 1] = prev.slice(0, -1);
405
+ }
406
+ }
407
+ const name = out.join("");
408
+ if (name === "" || name.startsWith("/") || name.endsWith("/") || name.includes("//") || name.includes("..")) {
409
+ return { ok: false, reason: `the rendered name ${JSON.stringify(name)} is not a branch name` };
410
+ }
411
+ return { ok: true, name };
412
+ }
413
+
414
+ // src/scripts/_lib/git_convention_measure.ts
415
+ import { spawnSync as spawnSync2 } from "node:child_process";
416
+ var MEASURE_LIMIT = 200;
417
+ var MEASURE_SINCE = "24 months ago";
418
+ var MIN_N = 30;
419
+ var RECENT_WINDOW = MIN_N;
420
+ var SHARE_BAR = 0.8;
421
+ var SMALL_TEAM_SHARE_BAR = 0.9;
422
+ var MIN_CAPPED_AUTHORS = 3;
423
+ var AUTHOR_CAP_PER_HALF = 20;
424
+ var BULK_IMPORT_FILES = 500;
425
+ var MIN_BRANCHES = 10;
426
+ var CLASSIFIER_VERSION = "2026-10-06";
427
+ var BOT_AUTHOR = /\[bot\]|dependabot|renovate|github-actions|semantic-release|release-please/i;
428
+ var AUTOMATION_SUBJECT = /^Revert |^Merge |^chore\(release\)|^Bump |^v?\d+\.\d+\.\d+$/;
429
+ function _key(c) {
430
+ return (c.email || c.author).toLowerCase();
431
+ }
432
+ function _cap(commits) {
433
+ const seen = /* @__PURE__ */ new Map();
434
+ return commits.filter((c) => {
435
+ const k = _key(c);
436
+ const n = (seen.get(k) ?? 0) + 1;
437
+ seen.set(k, n);
438
+ return n <= AUTHOR_CAP_PER_HALF;
439
+ });
440
+ }
441
+ function _shares(commits) {
442
+ const counts = /* @__PURE__ */ new Map();
443
+ for (const c of commits) {
444
+ const f = classifySubject(c.subject);
445
+ counts.set(f, (counts.get(f) ?? 0) + 1);
446
+ }
447
+ const total = commits.length;
448
+ return [...counts.entries()].map(([family, count]) => ({ family, count, share: total === 0 ? 0 : count / total })).sort((a, b) => b.count - a.count || a.family.localeCompare(b.family));
449
+ }
450
+ function _half(commits) {
451
+ const top = _shares(commits)[0];
452
+ return { n: commits.length, family: top?.family ?? null, share: top?.share ?? 0 };
453
+ }
454
+ function _clears(n, top, bar) {
455
+ return n >= MIN_N && top !== void 0 && top.family !== null && top.family !== "other" && top.share >= bar;
456
+ }
457
+ function measureSubjects(commits) {
458
+ const excluded = { bots: 0, automation: 0, bulk: 0 };
459
+ const eligible = commits.filter((c) => {
460
+ if (BOT_AUTHOR.test(c.author) || BOT_AUTHOR.test(c.email)) excluded.bots++;
461
+ else if (AUTOMATION_SUBJECT.test(c.subject)) excluded.automation++;
462
+ else if (c.files !== null && c.files > BULK_IMPORT_FILES) excluded.bulk++;
463
+ else return true;
464
+ return false;
465
+ });
466
+ const authors = new Set(eligible.map(_key)).size;
467
+ const capped = authors >= MIN_CAPPED_AUTHORS;
468
+ const bar = capped ? SHARE_BAR : SMALL_TEAM_SHARE_BAR;
469
+ const cut = Math.ceil(eligible.length / 2);
470
+ const newerRaw = eligible.slice(0, cut);
471
+ const olderRaw = eligible.slice(cut);
472
+ const newerSample = capped ? _cap(newerRaw) : newerRaw;
473
+ const olderSample = capped ? _cap(olderRaw) : olderRaw;
474
+ const sample = [...newerSample, ...olderSample];
475
+ const families = _shares(sample);
476
+ const newer = _half(newerSample);
477
+ const older = _half(olderSample);
478
+ const halvesAgree = newer.family !== null && newer.family === older.family;
479
+ const recent = _half((capped ? _cap(eligible) : eligible).slice(0, RECENT_WINDOW));
480
+ const top = families[0];
481
+ let established = null;
482
+ let migrating = false;
483
+ let migratedBy = null;
484
+ const reasons = [];
485
+ if (eligible.length < MIN_N) reasons.push(`n ${eligible.length} < ${MIN_N}`);
486
+ if (top === void 0 || top.family === "other") reasons.push("no family with a grammar leads");
487
+ else if (top.share < bar) reasons.push(`${top.family} at ${pct(top.share)} < ${pct(bar)}`);
488
+ if (!halvesAgree && eligible.length > 0) reasons.push(`the halves disagree (newer ${newer.family ?? "\u2014"}, older ${older.family ?? "\u2014"})`);
489
+ if (reasons.length === 0 && top !== void 0) established = top.family;
490
+ else if (!halvesAgree && _clears(newer.n, newer, bar)) {
491
+ established = newer.family;
492
+ migrating = true;
493
+ migratedBy = "newer-half";
494
+ }
495
+ if (!migrating && recent.n >= RECENT_WINDOW && recent.family !== top?.family && _clears(recent.n, recent, bar)) {
496
+ established = recent.family;
497
+ migrating = true;
498
+ migratedBy = "recent-window";
499
+ reasons.push(`the newest ${recent.n} read ${recent.family} at ${pct(recent.share)}, not ${top?.family ?? "\u2014"}`);
500
+ }
501
+ return {
502
+ eligible: eligible.length,
503
+ excluded,
504
+ authors,
505
+ capped,
506
+ cappedTotal: sample.length,
507
+ bar,
508
+ families,
509
+ newer,
510
+ older,
511
+ halvesAgree,
512
+ recent,
513
+ migratedBy,
514
+ established,
515
+ migrating,
516
+ reasons: established === null || migrating ? reasons : [],
517
+ strongest: families.filter((f) => f.family !== "other").slice(0, 2).map((f) => f.family)
518
+ };
519
+ }
520
+ var BRANCH_TYPES = [...COMMIT_TYPES, "feature", "bugfix", "hotfix"].join("|");
521
+ var BRANCH_SHAPES = [
522
+ ["{type}/{ticket}-{slug}", new RegExp(`^(${BRANCH_TYPES})/${TICKET_GRAMMAR}-[^/]+$`)],
523
+ ["{type}/{ticket}/{slug}", new RegExp(`^(${BRANCH_TYPES})/${TICKET_GRAMMAR}/[^/]+$`)],
524
+ ["{ticket}-{slug}", new RegExp(`^${TICKET_GRAMMAR}-[^/]+$`)],
525
+ ["{ticket}/{slug}", new RegExp(`^${TICKET_GRAMMAR}/[^/]+$`)],
526
+ ["{type}/{slug}", new RegExp(`^(${BRANCH_TYPES})/[^/]+$`)]
527
+ ];
528
+ var BOT_BRANCH = /^(dependabot|renovate|release-please)[/-]/;
529
+ function classifyBranch(name) {
530
+ return BRANCH_SHAPES.find(([, re]) => re.test(name))?.[0] ?? "other";
531
+ }
532
+ function measureBranches(names) {
533
+ const sample = names.filter((n) => !BOT_BRANCH.test(n));
534
+ const counts = /* @__PURE__ */ new Map();
535
+ for (const n of sample) {
536
+ const s = classifyBranch(n);
537
+ counts.set(s, (counts.get(s) ?? 0) + 1);
538
+ }
539
+ const shapes = [...counts.entries()].map(([shape, count]) => ({ shape, count, share: count / sample.length })).sort((a, b) => b.count - a.count || a.shape.localeCompare(b.shape));
540
+ const top = shapes[0];
541
+ const clear = top !== void 0 && top.shape !== "other" && sample.length >= MIN_BRANCHES && top.share >= SHARE_BAR && invalidReason("branch_pattern", top.shape) === null;
542
+ return { sampled: sample.length, shapes, pattern: clear ? top.shape : null, share: top?.share ?? 0 };
543
+ }
544
+ function measureUpdateStyle(mergeSubjects, defaultBranch) {
545
+ const name = defaultBranch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
546
+ const base = new RegExp(`^Merge (remote-tracking )?branch '(origin/)?${name}'`);
547
+ const baseMerges = mergeSubjects.filter((s) => base.test(s)).length;
548
+ return { merges: mergeSubjects.length, baseMerges, observed: baseMerges > 0 ? "merge" : "linear" };
549
+ }
550
+ function formatForFamily(family) {
551
+ if (family === "conventional") return "ticket-scope";
552
+ if (family === "ticket-conventional") return "ticket-conventional";
553
+ return null;
554
+ }
555
+ function teamFile(family, pattern) {
556
+ const format = family === null ? null : formatForFamily(family);
557
+ if (format === null && pattern === null) return null;
558
+ return ["git:", ...format === null ? [] : [` commit_format: ${format}`], ...pattern === null ? [] : [` branch_pattern: "${pattern}"`], ""].join("\n");
559
+ }
560
+ function pct(x) {
561
+ return `${Math.round(x * 100)} %`;
562
+ }
563
+ function _git(cwd, args) {
564
+ const r = spawnSync2("git", args, { cwd, encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
565
+ return r.status === 0 ? r.stdout : null;
566
+ }
567
+ function _trunk(cwd) {
568
+ const ref = makeTargetDeps(cwd).defaultBranch();
569
+ if (ref === null) {
570
+ return { unresolved: "no default branch could be resolved \u2014 origin names none and refs/remotes/origin/HEAD is not set" };
571
+ }
572
+ if (_git(cwd, ["rev-parse", "-q", "--verify", `${ref}^{commit}`]) === null) {
573
+ return { unresolved: `the default branch ${ref} is not in this checkout \u2014 fetch it first` };
574
+ }
575
+ const slash = ref.indexOf("/");
576
+ return { trunk: ref, remote: ref.slice(0, slash), name: ref.slice(slash + 1) };
577
+ }
578
+ function readHistory(cwd, limit = MEASURE_LIMIT) {
579
+ if (_git(cwd, ["rev-parse", "--git-dir"]) === null) return null;
580
+ const t = _trunk(cwd);
581
+ if ("unresolved" in t) return t;
582
+ const read = (since) => (_git(cwd, ["log", t.trunk, "--no-merges", "-n", String(limit), ...since, "--pretty=format:%x1e%aN%x09%aE%x09%s", "--shortstat"]) ?? "").split("").filter((r) => r.trim() !== "").map((r) => {
583
+ const [head = "", ...rest] = r.split("\n");
584
+ const [author = "", email = "", ...subject] = head.split(" ");
585
+ const stat = /(\d+) files? changed/.exec(rest.join("\n"));
586
+ return { author, email, subject: subject.join(" "), files: stat === null ? null : Number(stat[1]) };
587
+ });
588
+ let commits = read([`--since=${MEASURE_SINCE}`]);
589
+ let windowExtended = false;
590
+ if (commits.length < MIN_N) {
591
+ const all = read([]);
592
+ windowExtended = all.length > commits.length;
593
+ if (windowExtended) commits = all;
594
+ }
595
+ const refs = _git(cwd, ["for-each-ref", "--format=%(refname:short)", "refs/remotes"]) ?? "";
596
+ const branches = refs.split("\n").map((r) => r.trim()).filter((r) => r !== "" && r.includes("/")).map((r) => ({ remote: r.slice(0, r.indexOf("/")), name: r.slice(r.indexOf("/") + 1) })).filter((b) => b.name !== "HEAD" && !(b.name === t.name && b.remote === t.remote)).map((b) => b.name);
597
+ const merges = _git(cwd, ["log", "--all", "--merges", "-n", String(limit), `--since=${MEASURE_SINCE}`, "--pretty=format:%s"]) ?? "";
598
+ return {
599
+ trunk: t.trunk,
600
+ defaultBranch: t.name,
601
+ commits,
602
+ branches,
603
+ mergeSubjects: merges.split("\n").filter((s) => s !== ""),
604
+ windowExtended
605
+ };
606
+ }
607
+
608
+ // src/scripts/sync_pr_branch.ts
609
+ import * as path2 from "node:path";
610
+ import { spawnSync as spawnSync3 } from "node:child_process";
611
+ import { fileURLToPath, pathToFileURL } from "node:url";
612
+
613
+ // src/scripts/_lib/branch_convergence.ts
614
+ var import_yaml = __toESM(require_dist(), 1);
615
+ var POLICY_PATH = ".branch-convergence.yml";
616
+ var MissingBranchConvergencePolicy = class extends Error {
617
+ target;
618
+ constructor(target) {
619
+ super(`MissingBranchConvergencePolicy(target="${target}")`);
620
+ this.name = "MissingBranchConvergencePolicy";
621
+ this.target = target;
622
+ }
623
+ };
624
+ var InvalidBranchConvergencePolicy = class extends Error {
625
+ constructor(detail) {
626
+ super(`InvalidBranchConvergencePolicy: ${detail}`);
627
+ this.name = "InvalidBranchConvergencePolicy";
628
+ }
629
+ };
630
+ var UnresolvableTargetSha = class extends Error {
631
+ target;
632
+ constructor(target) {
633
+ super(`UnresolvableTargetSha(target="${target}")`);
634
+ this.name = "UnresolvableTargetSha";
635
+ this.target = target;
636
+ }
637
+ };
638
+ var TARGET_KEYS = /* @__PURE__ */ new Set(["defaultBranch"]);
639
+ var ROOT_KEYS = /* @__PURE__ */ new Set(["enabled", "targets"]);
640
+ function isPlainObject(v) {
641
+ return typeof v === "object" && v !== null && !Array.isArray(v);
642
+ }
643
+ function parseBranchConvergencePolicy(text) {
644
+ let doc;
645
+ try {
646
+ doc = (0, import_yaml.parse)(text);
647
+ } catch (exc) {
648
+ throw new InvalidBranchConvergencePolicy(exc instanceof Error ? exc.message : String(exc));
649
+ }
650
+ if (!isPlainObject(doc)) {
651
+ return null;
652
+ }
653
+ const section = doc["branchConvergence"];
654
+ if (section === void 0) {
655
+ return null;
656
+ }
657
+ if (!isPlainObject(section)) {
658
+ throw new InvalidBranchConvergencePolicy("`branchConvergence` is not a mapping");
659
+ }
660
+ for (const k of Object.keys(section)) {
661
+ if (!ROOT_KEYS.has(k)) {
662
+ throw new InvalidBranchConvergencePolicy(`unknown field \`branchConvergence.${k}\``);
663
+ }
664
+ }
665
+ const enabled = section["enabled"];
666
+ if (typeof enabled !== "boolean") {
667
+ throw new InvalidBranchConvergencePolicy("`enabled` must be true or false");
668
+ }
669
+ const rawTargets = section["targets"] ?? {};
670
+ if (!isPlainObject(rawTargets)) {
671
+ throw new InvalidBranchConvergencePolicy("`targets` must be a mapping");
672
+ }
673
+ const targets = {};
674
+ for (const [name, entry] of Object.entries(rawTargets)) {
675
+ if (!isPlainObject(entry)) {
676
+ throw new InvalidBranchConvergencePolicy(`\`targets.${name}\` is not a mapping`);
677
+ }
678
+ for (const k of Object.keys(entry)) {
679
+ if (!TARGET_KEYS.has(k)) {
680
+ throw new InvalidBranchConvergencePolicy(`unknown field \`targets.${name}.${k}\``);
681
+ }
682
+ }
683
+ const v = entry["defaultBranch"];
684
+ if (v !== "include" && v !== "exclude") {
685
+ throw new InvalidBranchConvergencePolicy(
686
+ `\`targets.${name}.defaultBranch\` must be include or exclude, got ${JSON.stringify(v)}`
687
+ );
688
+ }
689
+ targets[name] = { defaultBranch: v };
690
+ }
691
+ return { enabled, targets };
692
+ }
693
+ function loadPolicyAtSha(targetSha, read) {
694
+ if (!/^[0-9a-f]{7,40}$/.test(targetSha)) {
695
+ throw new InvalidBranchConvergencePolicy(`\`targetSha\` is not a commit id: ${JSON.stringify(targetSha)}`);
696
+ }
697
+ const text = read(targetSha, POLICY_PATH);
698
+ if (text === null) {
699
+ return null;
700
+ }
701
+ return parseBranchConvergencePolicy(text);
702
+ }
703
+
704
+ // src/scripts/sync_pr_branch.ts
705
+ var NETWORK_TIMEOUT_MS2 = 8e3;
706
+ var MERGE_TIMEOUT_MS = 3e5;
707
+ var GENERATED = [
708
+ // Untracked in THIS repository since 2026-08-21 (it was the #1 conflict
709
+ // path: 830 commits/60d, in the conflict set of every open CONFLICTING PR).
710
+ // The entry STAYS anyway, and the retirement criterion is a declared window
711
+ // rather than "one release" — the AI council flagged the release count as
712
+ // arbitrary and unverifiable. Remove this entry once no open branch predates
713
+ // the untrack commit, which is checkable:
714
+ //
715
+ // git branch -r --contains <untrack-commit> # must list every open branch
716
+ //
717
+ // Until then a straggler branch created before the untrack still carries the
718
+ // tracked file, and a conflict on it is still resolved by regeneration.
719
+ "agents/roadmaps-progress.md",
720
+ "agents/index.md",
721
+ "docs/catalog.md",
722
+ "dist/agent-src/",
723
+ // Written by `src/scripts/adr/regenerate_index.ts`, which takes the decisions
724
+ // directory as an ARGUMENT — so the path literal appears nowhere in the
725
+ // generator and every grep-based audit of this list missed it. Found instead
726
+ // by measurement: 4 of the last 50 sessions resolved a conflict here, and
727
+ // its only correct resolution is `task regenerate-adr-index`, never a hunk
728
+ // merge of two append-shaped indexes.
729
+ "docs/decisions/INDEX.md",
730
+ // Compiled by `compile_router.ts`. The `dist/agent-src/` prefix above does
731
+ // NOT match a sibling one level up under `dist/`, so this file was routed to
732
+ // a human decision despite being pure build output.
733
+ "dist/router.json",
734
+ ".augment/",
735
+ // Compiled from `hook_manifest.yaml` by `task build-ts`, never hand-written.
736
+ // It was classified AUTHORED until 2026-08-20, so this tool told the reader
737
+ // to "read both sides" on a file where mixing hunks yields a concern table
738
+ // matching NEITHER branch. One session resolved this same conflict three
739
+ // times and named it structural: main adds a concern and recompiles, so
740
+ // every open branch collides here. Regenerate; never merge.
741
+ "src/scripts/hook_manifest.json",
742
+ // Written together by one `build_archive_index.ts` call (`:409-410`) from the
743
+ // archived roadmap tree, so neither is ever hand-authored. They were
744
+ // classified AUTHORED until 2026-08-21, which made this tool ask for a human
745
+ // decision about a file whose only correct resolution is regeneration --
746
+ // the identical defect the hook_manifest.json comment above records, on two
747
+ // more paths at 47 commits/60d each. They conflict in 2 of the 7 open PRs
748
+ // measured for road-to-merge-hotspot-drawdown. Regenerate; never merge.
749
+ "agents/roadmaps/archive/INDEX.md",
750
+ "agents/roadmaps/archive/index.json",
751
+ // ── Added 2026-08-25 (road-to-merge-surface-zero 1.1). Each carries its
752
+ // write site, because this list tells a human to DISCARD one side, and an
753
+ // entry added on a conflict count alone would tell them to discard hand
754
+ // work. Counts are from `pr_conflict_census --limit 2000` over 60 days.
755
+ //
756
+ // Written by `build_proof.ts:500` from `docs/CLAIMS.md`. 26 resolutions, and
757
+ // it fails in pairs: a CLAIMS.md edit leaves it stale, which reds both the
758
+ // drift-guard test and `demo-commands-still-pass`.
759
+ "docs/proof.md",
760
+ // Both written by `generate_catalog.ts` (`:166` for llms.txt) from SKILL.md
761
+ // frontmatter. Neither has ever been hand-authored.
762
+ "docs/skills-catalog.md",
763
+ "llms.txt",
764
+ // Written by `lint_originality.ts:340`. Named by step 1.1 and included
765
+ // although the census found ZERO conflicts on it in either window — it is
766
+ // genuinely generated and classifying it costs nothing. Recorded rather than
767
+ // quietly dropped: the step named it on conflict frequency, and that premise
768
+ // does not hold.
769
+ "agents/reports/originality.json",
770
+ "agents/reports/originality.md"
771
+ ];
772
+ var GENERATED_PATTERNS = [
773
+ {
774
+ // `generate_pack_manifests.ts:427`, and the file's own first line is
775
+ // the do-not-edit header that generator writes.
776
+ // 12 resolutions over 60 days across two packs.
777
+ re: /^src\/domains\/[^/]+\/pack\.yaml$/,
778
+ why: "per-pack manifest written by generate_pack_manifests.ts"
779
+ }
780
+ ];
781
+ var REMEASURED = ["src/config/gate-violation-baselines.json"];
782
+ function sh2(cmd, args, cwd, timeout = NETWORK_TIMEOUT_MS2) {
783
+ const r = spawnSync3(cmd, [...args], {
784
+ cwd,
785
+ encoding: "utf-8",
786
+ timeout,
787
+ maxBuffer: 32 * 1024 * 1024
788
+ });
789
+ return { ok: r.status === 0, out: r.stdout ?? "", err: (r.stderr ?? "").trim(), killed: r.signal !== null };
790
+ }
791
+ function mergeTimeoutMs() {
792
+ const n = Number(process.env["AGENT_CONFIG_SYNC_MERGE_TIMEOUT_MS"]);
793
+ return Number.isInteger(n) && n > 0 ? n : MERGE_TIMEOUT_MS;
794
+ }
795
+ var UNTRACKED_BY_DESIGN = [
796
+ "agents/roadmaps-progress.md",
797
+ "agents/roadmaps/archive/INDEX.md",
798
+ "agents/roadmaps/archive/index.json"
799
+ ];
800
+ function isUntrackedByDesign(rel) {
801
+ return UNTRACKED_BY_DESIGN.includes(rel);
802
+ }
803
+ function isGenerated(rel) {
804
+ const norm = rel.replace(/\\/g, "/");
805
+ if (GENERATED.some((g) => g.endsWith("/") ? norm.startsWith(g) : norm === g)) return true;
806
+ return GENERATED_PATTERNS.some((p) => p.re.test(norm));
807
+ }
808
+ function isRemeasured(rel) {
809
+ const norm = rel.replace(/\\/g, "/");
810
+ return REMEASURED.some((g) => g.endsWith("/") ? norm.startsWith(g) : norm === g);
811
+ }
812
+ function behindRefusalMessage(plan, strategy) {
813
+ const t = plan.targetStale;
814
+ return `\u26A0\uFE0F sync_pr_branch: refused \u2014 the branch is behind and git.update_strategy is \`${strategy}\`; this script only merges. Rebase on request instead (git-workflow references/branch-update.md). ${plan.summary ?? ""}. Behind: ${plan.behindDetail ?? "?"}.` + (t === void 0 ? "" : ` The target ${t.target} is itself ${String(t.behind)} commit(s) behind ${t.defaultRef}; once this branch is current with it, the target needs updating too.`);
815
+ }
816
+ function classifyConflicts(files) {
817
+ const generated = [];
818
+ const remeasured = [];
819
+ const authored = [];
820
+ for (const f of files) {
821
+ const rel = f.trim();
822
+ if (rel === "") continue;
823
+ if (isGenerated(rel)) generated.push(rel);
824
+ else if (isRemeasured(rel)) remeasured.push(rel);
825
+ else authored.push(rel);
826
+ }
827
+ return { generated, remeasured, authored };
828
+ }
829
+ function renderConflictReport(plan) {
830
+ const out = [`\u274C ${plan.message}
831
+ `];
832
+ const untracked = plan.generated.filter(isUntrackedByDesign);
833
+ const regenerable = plan.generated.filter((f) => !isUntrackedByDesign(f));
834
+ if (regenerable.length > 0) {
835
+ out.push(`
836
+ GENERATED (${String(regenerable.length)}) \u2014 resolve by REGENERATING, never by mixing hunks:
837
+ `);
838
+ for (const f of regenerable) out.push(` \xB7 ${f}
839
+ `);
840
+ out.push(" \u2192 git checkout --ours <file> && task sync && task generate-tools\n");
841
+ }
842
+ if (untracked.length > 0) {
843
+ out.push(
844
+ `
845
+ UNTRACKED BY DESIGN (${String(untracked.length)}) \u2014 this repository does not commit these; TAKE THE DELETION:
846
+ `
847
+ );
848
+ for (const f of untracked) out.push(` \xB7 ${f}
849
+ `);
850
+ out.push(" \u2192 git rm --cached -- <file> (the working-tree copy stays; regenerate it locally)\n");
851
+ }
852
+ if (plan.remeasured.length > 0) {
853
+ out.push(
854
+ `
855
+ REMEASURED (${String(plan.remeasured.length)}) \u2014 RE-RUN THE MEASUREMENT on the merged tree; never merge, never pick a side:
856
+ `
857
+ );
858
+ for (const f of plan.remeasured) out.push(` \xB7 ${f}
859
+ `);
860
+ out.push(" \u2192 resolve the tree, then re-run the gate that owns the baseline and record its number\n");
861
+ }
862
+ if (plan.authored.length > 0) {
863
+ out.push(`
864
+ AUTHORED (${String(plan.authored.length)}) \u2014 a human decision, read both sides:
865
+ `);
866
+ for (const f of plan.authored) out.push(` \xB7 ${f}
867
+ `);
868
+ }
869
+ return out.join("");
870
+ }
871
+ var UnresolvableBase = class extends Error {
872
+ constructor(detail) {
873
+ super(`unresolvable \u2014 ${detail}`);
874
+ this.name = "UnresolvableBase";
875
+ }
876
+ };
877
+ function makeGitDeps(repo) {
878
+ return {
879
+ ...makeTargetDeps(repo),
880
+ // `git show <sha>:<path>` and nothing else. There is no filesystem read
881
+ // here by construction: a policy the PR head carries must not be able to
882
+ // change the criteria the PR is judged against.
883
+ readAtSha: (sha, rel) => {
884
+ const out = sh2("git", ["show", `${sha}:${rel}`], repo);
885
+ return out.ok ? out.out : null;
886
+ }
887
+ };
888
+ }
889
+ function bareName(ref) {
890
+ return splitResolvedRef(ref).branch;
891
+ }
892
+ function sameRemoteBranch(a, b) {
893
+ const x = splitResolvedRef(a);
894
+ const y = splitResolvedRef(b);
895
+ return x.remote === y.remote && x.branch === y.branch;
896
+ }
897
+ function resolveBase(repo, override, deps = makeGitDeps(repo)) {
898
+ const defaultRef = deps.defaultBranch();
899
+ const target = resolveTarget(deps, override, defaultRef);
900
+ if (target === null) {
901
+ throw new UnresolvableBase("no --base and no origin/HEAD");
902
+ }
903
+ const sameName = defaultRef !== null && sameRemoteBranch(target.ref, defaultRef);
904
+ const targetSha = deps.remoteSha(target.ref);
905
+ const defaultSha = defaultRef === null ? null : deps.remoteSha(defaultRef);
906
+ const sameSha = targetSha !== null && defaultSha !== null && targetSha === defaultSha;
907
+ const targetRemote = splitResolvedRef(target.ref).remote;
908
+ const ownDefault = sameName || sameSha || defaultRef !== null && splitResolvedRef(defaultRef).remote === targetRemote ? null : deps.defaultBranchOf?.(targetRemote) ?? null;
909
+ const ownName = ownDefault !== null && sameRemoteBranch(target.ref, ownDefault);
910
+ if (sameName || sameSha || ownName) {
911
+ return { entries: [target], policyStatus: "not-required" };
912
+ }
913
+ if (targetSha === null) {
914
+ throw new UnresolvableTargetSha(bareName(target.ref));
915
+ }
916
+ const policy = loadPolicyAtSha(targetSha, deps.readAtSha);
917
+ if (policy === null) {
918
+ throw new MissingBranchConvergencePolicy(bareName(target.ref));
919
+ }
920
+ if (!policy.enabled) {
921
+ return { entries: [target], policyStatus: "disabled" };
922
+ }
923
+ const entry = policy.targets[bareName(target.ref)];
924
+ if (entry === void 0) {
925
+ throw new MissingBranchConvergencePolicy(bareName(target.ref));
926
+ }
927
+ if (entry.defaultBranch === "exclude") {
928
+ return { entries: [target], policyStatus: "applied" };
929
+ }
930
+ if (defaultRef === null) {
931
+ throw new UnresolvableBase("policy says include the default branch, but origin/HEAD names none");
932
+ }
933
+ return {
934
+ entries: [target, { ref: defaultRef, reason: "branch-convergence-policy:include-default" }],
935
+ policyStatus: "applied"
936
+ };
937
+ }
938
+ function integrationOrder(r) {
939
+ const policyAdded = r.entries.filter((e) => e.reason === "branch-convergence-policy:include-default");
940
+ const rest = r.entries.filter((e) => e.reason !== "branch-convergence-policy:include-default");
941
+ return [...policyAdded, ...rest].map((e) => e.ref);
942
+ }
943
+ function describeReason(reason) {
944
+ switch (reason) {
945
+ case "explicit-base-override":
946
+ return "given by --base";
947
+ case "repository-default-branch":
948
+ return "the repo default branch";
949
+ case "branch-convergence-policy:include-default":
950
+ return "the default branch, added by the branch-convergence policy";
951
+ }
952
+ }
953
+ function renderBaseSummary(r) {
954
+ const byRef = new Map(r.entries.map((e) => [e.ref, e.reason]));
955
+ const parts = integrationOrder(r).map((ref) => `${ref} (${describeReason(byRef.get(ref))})`);
956
+ const head = r.policyStatus === "disabled" ? "BYPASSED \u2014 branch-convergence policy disabled at the target commit; the default branch was NOT considered" : r.policyStatus === "applied" ? "branch-convergence policy applied" : "no branch-convergence policy required (target is the default branch)";
957
+ return `${head}; integrating in order: ${parts.join(" \u2192 ")}`;
958
+ }
959
+ var MAX_BASE_ATTEMPTS = 3;
960
+ function renderAttempts(attempts) {
961
+ return attempts.map((a) => ` attempt ${String(a.attempt)}: ${a.ref} ${a.before ?? "?"} \u2192 ${a.after ?? "?"}`).join("\n");
962
+ }
963
+ function integrateWithPinnedBase(refs, ops) {
964
+ const attempts = [];
965
+ for (let n = 1; n <= MAX_BASE_ATTEMPTS; n++) {
966
+ const pinned = refs.map((ref) => ({ ref, before: ops.remoteSha(ref) }));
967
+ const stop = n === 1 ? ops.checkPin?.(pinned) ?? null : null;
968
+ if (stop !== null) return { ok: false, stopped: true, attempts, conflicted: [], message: stop };
969
+ let conflicted = [];
970
+ let clean = true;
971
+ for (const q of pinned) {
972
+ const m = ops.merge(q.ref, q.before);
973
+ if (!m.ok && m.error !== void 0) return { ok: false, attempts, conflicted: [], message: m.error };
974
+ if (!m.ok) {
975
+ clean = false;
976
+ conflicted = m.conflicted;
977
+ break;
978
+ }
979
+ }
980
+ const moved = [];
981
+ for (const q of pinned) {
982
+ const after = ops.remoteSha(q.ref);
983
+ attempts.push({ attempt: n, ref: q.ref, before: q.before, after });
984
+ if (after !== q.before) moved.push(q.ref);
985
+ }
986
+ if (!clean) {
987
+ return {
988
+ ok: false,
989
+ attempts,
990
+ conflicted,
991
+ message: `merge hit ${String(conflicted.length)} conflict(s) on attempt ${String(n)}.`
992
+ };
993
+ }
994
+ if (moved.length === 0) {
995
+ return { ok: true, attempts, conflicted: [], message: `integrated ${refs.join(", ")} on attempt ${String(n)}.` };
996
+ }
997
+ }
998
+ return {
999
+ ok: false,
1000
+ attempts,
1001
+ conflicted: [],
1002
+ message: `the base moved under every one of ${String(MAX_BASE_ATTEMPTS)} attempts \u2014 stopping rather than looping.
1003
+ ${renderAttempts(attempts)}
1004
+ \u2192 a base that moves this fast is a finding about landing speed, not a transient race.`
1005
+ };
1006
+ }
1007
+ function mergePinned(repo, ref, sha) {
1008
+ const tracking = () => sh2("git", ["rev-parse", "--verify", "-q", `${ref}^{commit}`], repo).out.trim();
1009
+ let target = ref;
1010
+ if (sha !== null && tracking() !== sha) {
1011
+ const { remote, branch } = splitResolvedRef(ref);
1012
+ sh2("git", ["fetch", "--", remote, `+refs/heads/${branch}:refs/remotes/${remote}/${branch}`], repo);
1013
+ if (tracking() !== sha) {
1014
+ if (!sh2("git", ["cat-file", "-e", `${sha}^{commit}`], repo).ok) {
1015
+ return { ok: false, conflicted: [], error: `the pinned commit ${sha.slice(0, 12)} of ${ref} could not be fetched \u2014 nothing was merged for it.` };
1016
+ }
1017
+ target = sha;
1018
+ }
1019
+ }
1020
+ const indexWasClean = sh2("git", ["diff", "--cached", "--quiet"], repo).ok;
1021
+ const m = sh2("git", ["merge", "--no-edit", target], repo, mergeTimeoutMs());
1022
+ if (!m.ok) {
1023
+ const conflicted = sh2("git", ["diff", "--name-only", "--diff-filter=U"], repo).out.split("\n").filter((p) => p.trim() !== "");
1024
+ if (conflicted.length > 0) return { ok: false, conflicted };
1025
+ const how = m.killed ? `was stopped after ${String(mergeTimeoutMs() / 1e3)} s` : "did not complete";
1026
+ if (sh2("git", ["rev-parse", "-q", "--verify", "MERGE_HEAD"], repo).ok) {
1027
+ return {
1028
+ ok: false,
1029
+ conflicted: [],
1030
+ error: `the merge of ${ref} ${how} and left a merge in progress (MERGE_HEAD is set) \u2014 finish it with \`git commit\` or discard it with \`git merge --abort\`.`
1031
+ };
1032
+ }
1033
+ if (indexWasClean && !sh2("git", ["diff", "--cached", "--quiet"], repo).ok) {
1034
+ return {
1035
+ ok: false,
1036
+ conflicted: [],
1037
+ error: `the merge of ${ref} ${how} and left it half-applied in the index (no MERGE_HEAD) \u2014 discard it with \`git reset --merge\`.`
1038
+ };
1039
+ }
1040
+ if (m.killed) {
1041
+ return { ok: false, conflicted: [], error: `the merge of ${ref} was stopped after ${String(mergeTimeoutMs() / 1e3)} s before it changed anything \u2014 nothing was merged for it.` };
1042
+ }
1043
+ const why = (m.err || m.out).trim().split("\n").join(" ");
1044
+ return { ok: false, conflicted: [], error: `git refused to merge ${ref}: ${why || "no reason given"} \u2014 nothing was merged for it.` };
1045
+ }
1046
+ if (sha !== null && !sh2("git", ["merge-base", "--is-ancestor", sha, "HEAD"], repo).ok) {
1047
+ return { ok: false, conflicted: [], error: `the merge of ${ref} left HEAD without its pinned commit ${sha.slice(0, 12)}.` };
1048
+ }
1049
+ return { ok: true, conflicted: [] };
1050
+ }
1051
+ function autoResolveGenerated(split, ops) {
1052
+ if (split.remeasured.length > 0 || split.authored.length > 0) {
1053
+ return {
1054
+ resolved: false,
1055
+ message: `NOT auto-resolved: the conflict set contains ${String(split.remeasured.length)} remeasured and ${String(split.authored.length)} authored path(s), which have no single correct resolution.`
1056
+ };
1057
+ }
1058
+ if (split.generated.length === 0) {
1059
+ return { resolved: false, message: "nothing to auto-resolve." };
1060
+ }
1061
+ const first = ops.regenerate();
1062
+ if (!first.ok) {
1063
+ return { resolved: false, message: `NOT auto-resolved: regeneration failed \u2014 ${first.err.split("\n")[0] ?? "?"}` };
1064
+ }
1065
+ if (!ops.stage(split.generated)) {
1066
+ return { resolved: false, message: "NOT auto-resolved: could not stage the regenerated paths." };
1067
+ }
1068
+ const second = ops.regenerate();
1069
+ if (!second.ok) {
1070
+ return { resolved: false, message: `NOT auto-resolved: the byte-identity re-run failed \u2014 ${second.err.split("\n")[0] ?? "?"}` };
1071
+ }
1072
+ const drift = ops.dirty(split.generated);
1073
+ if (drift.length > 0) {
1074
+ return {
1075
+ resolved: false,
1076
+ message: `NOT auto-resolved: these paths are not byte-identical to a clean regeneration, so at least one is partly hand-edited \u2014 ${drift.join(", ")}`
1077
+ };
1078
+ }
1079
+ return {
1080
+ resolved: true,
1081
+ message: `auto-resolved ${String(split.generated.length)} generated conflict(s) by regeneration, byte-identity asserted.`
1082
+ };
1083
+ }
1084
+ function gitRegenOps(repo) {
1085
+ return {
1086
+ // The repository's own consistency target — the exact CI mirror the
1087
+ // pre-push hook already runs. Named here rather than open-coded so the
1088
+ // generator this trusts is the one the gate trusts.
1089
+ regenerate: () => {
1090
+ const r = sh2("task", ["consistency"], repo);
1091
+ return { ok: r.ok, err: r.err };
1092
+ },
1093
+ dirty: (paths) => {
1094
+ const r = sh2("git", ["status", "--porcelain", "--", ...paths], repo);
1095
+ if (!r.ok) return [...paths];
1096
+ return r.out.split("\n").map((l) => l.slice(3).trim()).filter((l) => l !== "");
1097
+ },
1098
+ stage: (paths) => sh2("git", ["add", "--", ...paths], repo).ok
1099
+ };
1100
+ }
1101
+ function sync(repo, baseOverride, dryRun, autoResolve = false, deps, opts = {}) {
1102
+ let resolved;
1103
+ try {
1104
+ resolved = resolveBase(repo, baseOverride, deps ?? makeGitDeps(repo));
1105
+ } catch (exc) {
1106
+ return {
1107
+ exit: 1,
1108
+ message: `cannot resolve a base set to update against \u2014 ${exc instanceof Error ? exc.message : String(exc)}`,
1109
+ generated: [],
1110
+ remeasured: [],
1111
+ authored: [],
1112
+ scanned: 0
1113
+ };
1114
+ }
1115
+ const summary = renderBaseSummary(resolved);
1116
+ const order = integrationOrder(resolved);
1117
+ const remotes = [.../* @__PURE__ */ new Set(["origin", ...order.map((ref) => splitResolvedRef(ref).remote)])];
1118
+ const failed = remotes.map((remote) => ({ remote, ...sh2("git", ["fetch", "--prune", "--", remote], repo) })).find((f) => !f.ok);
1119
+ if (failed !== void 0) {
1120
+ return {
1121
+ exit: 0,
1122
+ message: `unverified \u2014 could not fetch ${failed.remote} (${failed.err.split("\n")[0] ?? "?"}). Base freshness NOT checked.`,
1123
+ generated: [],
1124
+ remeasured: [],
1125
+ authored: [],
1126
+ scanned: 0
1127
+ };
1128
+ }
1129
+ for (const ref of order) {
1130
+ const { remote, branch } = splitResolvedRef(ref);
1131
+ const f = sh2("git", ["fetch", "-q", "--", remote, `+refs/heads/${branch}:refs/remotes/${remote}/${branch}`], repo);
1132
+ if (!f.ok) {
1133
+ return {
1134
+ exit: 0,
1135
+ message: `unverified \u2014 could not fetch ${ref} (${f.err.split("\n")[0] ?? "?"}). Base freshness NOT checked.`,
1136
+ generated: [],
1137
+ remeasured: [],
1138
+ authored: [],
1139
+ scanned: 0
1140
+ };
1141
+ }
1142
+ }
1143
+ const countBehind = (from, ref) => {
1144
+ const r = sh2("git", ["rev-list", "--count", `${from}..${ref}`], repo);
1145
+ const n = Number(r.out.trim());
1146
+ return r.ok && r.out.trim() !== "" && Number.isInteger(n) ? n : null;
1147
+ };
1148
+ const uncountable = (ref) => ({
1149
+ exit: 1,
1150
+ message: `cannot count commits behind ${ref} \u2014 it is not in this checkout (a single-branch clone fetches only its own branch). Fetch it: git fetch -- ${splitResolvedRef(ref).remote} +refs/heads/${bareName(ref)}:refs/remotes/${splitResolvedRef(ref).remote}/${bareName(ref)}`,
1151
+ generated: [],
1152
+ remeasured: [],
1153
+ authored: [],
1154
+ scanned: 0
1155
+ });
1156
+ const behindEach = [];
1157
+ for (const ref of order) {
1158
+ const behind = countBehind("HEAD", ref);
1159
+ if (behind === null) return uncountable(ref);
1160
+ behindEach.push({ ref, behind });
1161
+ }
1162
+ const stale = behindEach.filter((b) => b.behind > 0);
1163
+ if (stale.length === 0) {
1164
+ return { exit: 0, message: `already current with every base ref \u2014 ${summary}.`, generated: [], remeasured: [], authored: [], scanned: order.length };
1165
+ }
1166
+ if (dryRun) {
1167
+ const detail = stale.map((b) => `${b.ref} (${String(b.behind)} behind)`).join(", ");
1168
+ const added = resolved.entries.find((e) => e.reason === "branch-convergence-policy:include-default");
1169
+ const target = resolved.entries[0]?.ref ?? "";
1170
+ const targetBehind = added === void 0 ? 0 : countBehind(target, added.ref);
1171
+ if (targetBehind === null) return uncountable(added?.ref ?? target);
1172
+ return {
1173
+ ...targetBehind > 0 && added !== void 0 ? { targetStale: { target, defaultRef: added.ref, behind: targetBehind, branchBehind: behindEach.find((b) => b.ref === target)?.behind ?? 0 } } : {},
1174
+ exit: 0,
1175
+ message: `${summary}. Behind: ${detail} \u2014 would merge in that order. Dry run, nothing changed.`,
1176
+ summary,
1177
+ behindDetail: detail,
1178
+ generated: [],
1179
+ remeasured: [],
1180
+ authored: [],
1181
+ scanned: order.length,
1182
+ behind: stale.length
1183
+ };
1184
+ }
1185
+ const live = opts.live ?? makeGitDeps(repo);
1186
+ const outcome = integrateWithPinnedBase(
1187
+ stale.map((b) => b.ref),
1188
+ {
1189
+ remoteSha: (ref) => live.remoteSha(ref),
1190
+ ...opts.checkPin === void 0 ? {} : { checkPin: opts.checkPin },
1191
+ merge: (ref, sha) => mergePinned(repo, ref, sha)
1192
+ }
1193
+ );
1194
+ if (outcome.ok) {
1195
+ return {
1196
+ exit: 0,
1197
+ message: `${summary}. ${outcome.message} REGENERATE derived files now \u2014 a clean auto-merge of a generated file is still wrong.`,
1198
+ generated: [],
1199
+ remeasured: [],
1200
+ authored: [],
1201
+ scanned: order.length
1202
+ };
1203
+ }
1204
+ if (outcome.conflicted.length === 0) {
1205
+ return { exit: 1, message: `${summary}. ${outcome.message}`, generated: [], remeasured: [], authored: [], scanned: order.length };
1206
+ }
1207
+ const split = classifyConflicts(outcome.conflicted);
1208
+ if (autoResolve) {
1209
+ const attempt = autoResolveGenerated(split, gitRegenOps(repo));
1210
+ if (attempt.resolved) {
1211
+ return { exit: 0, message: `${summary}. ${attempt.message}`, generated: [], remeasured: [], authored: [], scanned: order.length };
1212
+ }
1213
+ return {
1214
+ exit: 1,
1215
+ message: `${summary}. ${outcome.message} ${attempt.message}`,
1216
+ ...split,
1217
+ scanned: order.length
1218
+ };
1219
+ }
1220
+ return {
1221
+ exit: 1,
1222
+ message: `${summary}. ${outcome.message} NOT auto-resolved: a content conflict is where a parallel session's work disappears. (--auto-resolve-generated resolves a set that is GENERATED and nothing else.)`,
1223
+ ...split,
1224
+ scanned: order.length
1225
+ };
1226
+ }
1227
+ function readStrategy(repo, base, deps) {
1228
+ const read = readCommittedConvention(repo, { override: base, keys: ["update_strategy"], ...deps ? { deps } : {} });
1229
+ return { reading: read.readings.update_strategy, baseResolved: read.target !== null && read.target.sha !== null, target: read.target };
1230
+ }
1231
+ function strategyExit(reading, baseResolved, developer) {
1232
+ if (!isRefusal(reading.state)) return { exit: null, line: "", reading };
1233
+ const refused = { exit: 4, line: `\u274C sync_pr_branch: refused \u2014 ${describeRefusal(reading)}. Nothing was checked or merged.`, reading };
1234
+ if (reading.state !== "unresolvable" || isRefusal(developer.state)) return refused;
1235
+ const why = reading.detail ?? "the target commit is unknown";
1236
+ if (!baseResolved) return { exit: 1, line: `\u274C sync_pr_branch: base could not be resolved \u2014 ${why}. Nothing was checked or merged.`, reading };
1237
+ return { exit: 0, line: `\u26A0\uFE0F sync_pr_branch: unverified \u2014 ${why}. Base freshness NOT checked, nothing was merged.`, reading };
1238
+ }
1239
+ function strategyGate(repo, base, targetDeps) {
1240
+ const { reading, baseResolved, target } = readStrategy(repo, base, targetDeps);
1241
+ return { ...strategyExit(reading, baseResolved, readGitConventionKey("update_strategy", checkoutSource(conventionRoot(repo).root))), target };
1242
+ }
1243
+ function strategyInForce(r) {
1244
+ return isRefusal(r.state) ? `unreadable (${r.state})` : r.value ?? "merge";
1245
+ }
1246
+ function policyMovedStop(from, to, planned, pinned) {
1247
+ const was = strategyInForce(planned);
1248
+ const now = strategyInForce(pinned);
1249
+ if (was === now) return null;
1250
+ const why = isRefusal(pinned.state) ? ` (${describeRefusal(pinned)})` : "";
1251
+ return `the target moved from ${from.slice(0, 12)} to ${to.slice(0, 12)} and its git.update_strategy changed from ${was} to ${now}${why} \u2014 nothing was merged; run again so the plan is made under the strategy the new commit carries.`;
1252
+ }
1253
+ function main(argv, deps) {
1254
+ const args = argv ?? process.argv.slice(2);
1255
+ let repo = process.cwd();
1256
+ let base = null;
1257
+ let dryRun = false;
1258
+ let quiet = false;
1259
+ let autoResolve = false;
1260
+ for (let i = 0; i < args.length; i++) {
1261
+ const a = args[i];
1262
+ const val = () => {
1263
+ const v = args[++i];
1264
+ return v === void 0 || v.startsWith("--") ? null : v;
1265
+ };
1266
+ if (a === "--repo") {
1267
+ const v = val();
1268
+ if (v === null) {
1269
+ process.stderr.write("\u274C sync_pr_branch: --repo requires a value\n");
1270
+ reportScanned({ gate: "sync_pr_branch", scanned: 0, units: "base ref(s)", roots: ["origin"], allowEmpty: "argument error" });
1271
+ return 1;
1272
+ }
1273
+ repo = v;
1274
+ } else if (a === "--base") {
1275
+ const v = val();
1276
+ if (v === null) {
1277
+ process.stderr.write("\u274C sync_pr_branch: --base requires a value\n");
1278
+ reportScanned({ gate: "sync_pr_branch", scanned: 0, units: "base ref(s)", roots: ["origin"], allowEmpty: "argument error" });
1279
+ return 1;
1280
+ }
1281
+ const blank = baseValueError(v);
1282
+ if (blank !== null) {
1283
+ process.stderr.write(`\u274C sync_pr_branch: ${blank}
1284
+ `);
1285
+ reportScanned({ gate: "sync_pr_branch", scanned: 0, units: "base ref(s)", roots: ["origin"], allowEmpty: "argument error" });
1286
+ return 2;
1287
+ }
1288
+ base = v;
1289
+ } else if (a === "--dry-run") {
1290
+ dryRun = true;
1291
+ } else if (a === "--auto-resolve-generated") {
1292
+ autoResolve = true;
1293
+ } else if (a === "--quiet") {
1294
+ quiet = true;
1295
+ } else if (a === "-h" || a === "--help") {
1296
+ process.stdout.write(
1297
+ "usage: sync_pr_branch [--repo PATH] [--base REF] [--dry-run] [--auto-resolve-generated] [--quiet]\n Merges the PR base into the current branch so the PR does not go stale.\n Under a git.update_strategy other than merge it only checks: a current\n branch exits 0, a behind one exits 3 and is never merged (--dry-run and\n --auto-resolve-generated have nothing to change there). A strategy that\n cannot be read (unparsable file, typo, user-global-only) exits 4.\n The base is --base; without it the default branch, so a PR into any other\n base must pass --base <its base>; a branch name and refs/heads/<name> mean\n origin/<name>, and <remote>/<name> is used as given. A blank --base\n is a usage error, exit 2.\n A base that cannot be resolved or counted exits 1; one whose commit\n cannot be fetched is unverified, exit 0. A target that moves before the\n merge is read again at the new commit; another strategy there exits 1,\n nothing merged. A conflict is\n reported and never auto-resolved; generated and authored conflicts are\n listed separately because only the first has one correct resolution;\n measured ratchet baselines are a third class, re-measured not merged.\n The base is a SET: a non-default target may also carry the default branch,\n per the branch-convergence policy read at the TARGET commit. Integration\n runs the default first so the broad conflict surfaces first.\n --auto-resolve-generated resolves a conflict set that is GENERATED and\n nothing else, by regenerating and asserting byte-identity.\n"
1298
+ );
1299
+ reportScanned({ gate: "sync_pr_branch", scanned: 0, units: "base ref(s)", roots: ["origin"], allowEmpty: "help output" });
1300
+ return 0;
1301
+ } else {
1302
+ process.stderr.write(`\u274C sync_pr_branch: unknown argument \`${a}\`
1303
+ `);
1304
+ reportScanned({ gate: "sync_pr_branch", scanned: 0, units: "base ref(s)", roots: ["origin"], allowEmpty: "argument error" });
1305
+ return 1;
1306
+ }
1307
+ }
1308
+ const ignored = ignoredCarrierWarning(conventionRoot(repo).root);
1309
+ if (ignored !== null) process.stdout.write(`\u26A0\uFE0F sync_pr_branch: ${ignored}
1310
+ `);
1311
+ const live = deps ?? makeGitDeps(repo);
1312
+ const targetDeps = memoTargetDeps(live);
1313
+ let plan;
1314
+ try {
1315
+ const gate = strategyGate(repo, base, targetDeps);
1316
+ if (gate.exit !== null) {
1317
+ process.stdout.write(`${gate.line}
1318
+ `);
1319
+ const why = gate.exit === 0 ? "target commit not fetched \u2014 stated above" : gate.exit === 1 ? "base unresolvable \u2014 stated above" : "git.update_strategy unreadable";
1320
+ reportScanned({ gate: "sync_pr_branch", scanned: 0, units: "base ref(s)", roots: ["origin"], allowEmpty: why });
1321
+ return gate.exit;
1322
+ }
1323
+ const strategy = gate.reading.value ?? "merge";
1324
+ if (strategy !== "merge") {
1325
+ plan = sync(repo, base, true, false, targetDeps);
1326
+ const t = plan.targetStale;
1327
+ if (plan.exit === 0 && t !== void 0 && t.branchBehind === 0) {
1328
+ process.stdout.write(
1329
+ `\u26A0\uFE0F sync_pr_branch: refused \u2014 TARGET_POLICY_STALE: the target ${t.target} is itself ${String(t.behind)} commit(s) behind ${t.defaultRef}, which the branch-convergence policy requires; updating this branch onto the target cannot make it current. The target needs updating first; this branch has nothing to do until then.
1330
+ `
1331
+ );
1332
+ reportScanned({ gate: "sync_pr_branch", scanned: plan.scanned, units: "base ref(s)", roots: ["origin"] });
1333
+ return 3;
1334
+ }
1335
+ if (plan.exit === 0 && (plan.behind ?? 0) > 0) {
1336
+ process.stdout.write(`${behindRefusalMessage(plan, strategy)}
1337
+ `);
1338
+ reportScanned({ gate: "sync_pr_branch", scanned: plan.scanned, units: "base ref(s)", roots: ["origin"] });
1339
+ return 3;
1340
+ }
1341
+ } else {
1342
+ const read = gate.target ?? null;
1343
+ const checkPin = (pinned) => {
1344
+ const now = read === null ? null : pinned.find((p) => p.ref === read.ref)?.before ?? null;
1345
+ if (read === null || read.sha === null || now === null || now === read.sha) return null;
1346
+ const again = strategyGate(repo, base, { ...targetDeps, remoteSha: () => now });
1347
+ return policyMovedStop(read.sha, now, gate.reading, again.reading);
1348
+ };
1349
+ plan = sync(repo, base, dryRun, autoResolve, targetDeps, { live, checkPin });
1350
+ }
1351
+ } catch (exc) {
1352
+ reportScanned({ gate: "sync_pr_branch", scanned: 0, units: "base ref(s)", roots: ["origin"], allowEmpty: "internal error" });
1353
+ process.stderr.write(`\u274C sync_pr_branch: internal error: ${exc instanceof Error ? exc.message : String(exc)}
1354
+ `);
1355
+ return 2;
1356
+ }
1357
+ if (plan.exit === 1) {
1358
+ process.stdout.write(renderConflictReport(plan));
1359
+ } else if (plan.message.includes("BYPASSED")) {
1360
+ process.stdout.write(`\u26A0\uFE0F sync_pr_branch: ${plan.message}
1361
+ `);
1362
+ } else if (plan.message.startsWith("unverified")) {
1363
+ process.stdout.write(`\u26A0\uFE0F sync_pr_branch: ${plan.message}
1364
+ `);
1365
+ } else if (!quiet || plan.scanned > 0) {
1366
+ process.stdout.write(`\u2705 ${plan.message}
1367
+ `);
1368
+ }
1369
+ reportScanned({
1370
+ gate: "sync_pr_branch",
1371
+ scanned: plan.scanned,
1372
+ units: "base ref(s)",
1373
+ roots: ["origin"],
1374
+ ...plan.scanned === 0 ? { allowEmpty: "base unresolvable or remote unreachable \u2014 stated above" } : {}
1375
+ });
1376
+ return plan.exit;
1377
+ }
1378
+ var _HERE = fileURLToPath(import.meta.url);
1379
+ function _isCliEntry() {
1380
+ if (process.argv[1] === void 0) return false;
1381
+ if (true) return path2.basename(process.argv[1], ".js") === "sync_pr_branch";
1382
+ return import.meta.url === pathToFileURL(path2.resolve(process.argv[1])).href || process.argv[1] === _HERE;
1383
+ }
1384
+ if (_isCliEntry()) {
1385
+ process.exit(main());
1386
+ }
1387
+
1388
+ // src/scripts/_cli/cmd_git_convention.ts
1389
+ var COMMITLINT_FILES = [
1390
+ ".commitlintrc",
1391
+ ".commitlintrc.json",
1392
+ ".commitlintrc.yaml",
1393
+ ".commitlintrc.yml",
1394
+ ".commitlintrc.js",
1395
+ ".commitlintrc.cjs",
1396
+ ".commitlintrc.mjs",
1397
+ ".commitlintrc.ts",
1398
+ ".commitlintrc.cts",
1399
+ ".commitlintrc.mts",
1400
+ "commitlint.config.js",
1401
+ "commitlint.config.cjs",
1402
+ "commitlint.config.mjs",
1403
+ "commitlint.config.ts",
1404
+ "commitlint.config.cts",
1405
+ "commitlint.config.mts"
1406
+ ];
1407
+ function _git2(cwd, ...args) {
1408
+ const r = spawnSync4("git", args, { cwd, encoding: "utf8" });
1409
+ return r.status === 0 ? r.stdout.trim() : null;
1410
+ }
1411
+ function _isExecutableFile(p) {
1412
+ try {
1413
+ const st = fs2.statSync(p);
1414
+ return st.isFile() && (st.mode & 73) !== 0;
1415
+ } catch {
1416
+ return false;
1417
+ }
1418
+ }
1419
+ function commitMessageValidators(cwd) {
1420
+ const found = [];
1421
+ const top = _git2(cwd, "rev-parse", "--show-toplevel") ?? cwd;
1422
+ const hook = _git2(cwd, "rev-parse", "--path-format=absolute", "--git-path", "hooks/commit-msg");
1423
+ if (hook !== null && _isExecutableFile(hook)) found.push({ kind: "commit-msg hook", path: hook });
1424
+ const config = COMMITLINT_FILES.map((name) => path3.join(top, name)).find((p) => fs2.existsSync(p));
1425
+ if (config !== void 0) found.push({ kind: "commitlint config", path: config });
1426
+ else {
1427
+ try {
1428
+ const pkg = JSON.parse(fs2.readFileSync(path3.join(top, "package.json"), "utf-8"));
1429
+ if (pkg.commitlint !== void 0) found.push({ kind: "commitlint config", path: path3.join(top, "package.json") });
1430
+ } catch {
1431
+ }
1432
+ }
1433
+ return found;
1434
+ }
1435
+ function commitMessageValidator(cwd) {
1436
+ return commitMessageValidators(cwd)[0] ?? null;
1437
+ }
1438
+ function _validatorNote(v) {
1439
+ return `note: the ${v.kind} at ${v.path} also runs at commit time and may be stricter`;
1440
+ }
1441
+ function _short2(sha) {
1442
+ return sha === null || sha === void 0 ? "?" : sha.slice(0, 12);
1443
+ }
1444
+ function showConvention(args, cwd, deps) {
1445
+ let json = false;
1446
+ let base = null;
1447
+ const asked = [];
1448
+ for (let i = 0; i < args.length; i++) {
1449
+ const a = args[i];
1450
+ const next = args[i + 1];
1451
+ if (a === "--json") json = true;
1452
+ else if (a === "--base" && next !== void 0 && !next.startsWith("--")) {
1453
+ const blank = baseValueError(next);
1454
+ if (blank !== null) return { code: 2, out: [], err: [blank, USAGE] };
1455
+ base = args[++i];
1456
+ } else if (a === "--key" && next !== void 0 && GIT_CONVENTION_KEYS.includes(next)) {
1457
+ if (!asked.includes(next)) asked.push(next);
1458
+ i++;
1459
+ } else return { code: 2, out: [], err: [a === "--key" ? `unknown key: ${next ?? "(none)"}` : `unknown argument: ${a}`, USAGE] };
1460
+ }
1461
+ const keys = asked.length === 0 ? GIT_CONVENTION_KEYS : GIT_CONVENTION_KEYS.filter((k) => asked.includes(k));
1462
+ const read = readCommittedConvention(cwd, { override: base, keys, ...deps ? { deps } : {} });
1463
+ const readings = read.readings;
1464
+ const validators = commitMessageValidators(cwd);
1465
+ const ok = !keys.some((k) => isRefusal(readings[k].state));
1466
+ const code = ok ? 0 : 1;
1467
+ const established = keys.includes("commit_format") ? _established(readings.commit_format, read.root) : null;
1468
+ const ignored = ignoredCarrierWarning(read.root);
1469
+ const warnings = ignored === null ? [] : [ignored];
1470
+ if (json) {
1471
+ const entries = {};
1472
+ for (const k of keys) {
1473
+ const { key: _key2, ...rest } = readings[k];
1474
+ const cand = read.candidates[k];
1475
+ entries[k] = {
1476
+ ...rest,
1477
+ read_at: read.readAt[k] ?? null,
1478
+ candidate: cand === void 0 ? null : { value: cand.value, source: cand.source, state: cand.state, reason: cand.reason }
1479
+ };
1480
+ }
1481
+ return {
1482
+ code,
1483
+ out: [JSON.stringify({ ok, keys: entries, target: read.target, warnings, convention_established: established, commit_message_validator: validators[0] ?? null, commit_message_validators: validators }, null, 2)],
1484
+ err: []
1485
+ };
1486
+ }
1487
+ const out = [];
1488
+ for (const k of keys) {
1489
+ const r = readings[k];
1490
+ out.push(r.value === null ? `git.${k} \u2014 unknown` : `git.${k} = ${r.value}`);
1491
+ out.push(` state ${r.state}`);
1492
+ out.push(` source ${r.source ?? "(template default)"}`);
1493
+ const at = read.readAt[k];
1494
+ if (k === "update_strategy" && read.target !== null) {
1495
+ out.push(` read at ${_short2(read.target.sha)} \u2014 ${read.target.ref}, the commit this branch is judged against`);
1496
+ } else if (at !== null && at !== void 0) {
1497
+ out.push(` read at ${_short2(at)} \u2014 HEAD`);
1498
+ }
1499
+ if (r.reason !== null) out.push(` \u26A0\uFE0F ${describeRefusal(r)}`);
1500
+ const cand = read.candidates[k];
1501
+ if (cand !== void 0) {
1502
+ out.push(
1503
+ ` candidate ${cand.value ?? "unknown"} (${cand.state}) from ${cand.source ?? "(template default)"} \u2014 this checkout's value; the value above is in force`
1504
+ );
1505
+ if (cand.reason !== null) out.push(` \u26A0\uFE0F candidate only, not in force: ${describeRefusal(cand)}`);
1506
+ }
1507
+ }
1508
+ out.push(`team declaration: ${CARRIER_PATH} at the repository root (ADR-283)`);
1509
+ if (established === false) out.push(NO_CONVENTION);
1510
+ for (const w of warnings) out.push(`\u26A0\uFE0F ${w}`);
1511
+ if (validators.length === 0) out.push("commit-message validator: none in this repository");
1512
+ for (const v of validators) out.push(`commit-message validator: ${v.kind} at ${v.path} \u2014 also runs at commit time and may be stricter than git.commit_format`);
1513
+ return { code, out, err: [] };
1514
+ }
1515
+ var APPROVED_CARD = "agents/memory/curated/conventions/approved/commit-subject.md";
1516
+ function _flags(args, names) {
1517
+ const values = {};
1518
+ const positional = [];
1519
+ let json = false;
1520
+ for (let i = 0; i < args.length; i++) {
1521
+ const a = args[i];
1522
+ if (a === "--json") json = true;
1523
+ else if (a.startsWith("--")) {
1524
+ const v = args[i + 1];
1525
+ if (!names.includes(a.slice(2)) || v === void 0 || v.startsWith("--")) return { values, json, positional, bad: a };
1526
+ values[a.slice(2)] = v;
1527
+ i++;
1528
+ } else positional.push(a);
1529
+ }
1530
+ return { values, json, positional, bad: null };
1531
+ }
1532
+ function _cardFamily(root) {
1533
+ let text;
1534
+ try {
1535
+ text = fs2.readFileSync(path3.join(root, APPROVED_CARD), "utf-8");
1536
+ } catch {
1537
+ return null;
1538
+ }
1539
+ return /^[ \t]*dominant_family:\s*["']?([a-z-]+)/m.exec(text)?.[1] ?? null;
1540
+ }
1541
+ function _declares(reading) {
1542
+ if (reading.state !== "valid") return false;
1543
+ return (reading.source ?? "").startsWith(CARRIER_PATH) || reading.value !== conventionDefault("commit_format");
1544
+ }
1545
+ function _commitlint(cwd) {
1546
+ return commitMessageValidators(cwd).find((v) => v.kind === "commitlint config") ?? null;
1547
+ }
1548
+ function _established(reading, root) {
1549
+ if (isRefusal(reading.state)) return null;
1550
+ return _declares(reading) || _cardFamily(root) !== null || _commitlint(root) !== null;
1551
+ }
1552
+ var NO_CONVENTION = "no convention established \u2014 run git:convention measure";
1553
+ function _planSubject(values, cwd) {
1554
+ if (values.format !== void 0) {
1555
+ if (!["ticket-scope", "ticket-conventional"].includes(values.format)) return { kind: "stop", code: 1, lines: [`unknown format: ${values.format}`] };
1556
+ return { kind: "rule", rule: { format: values.format }, tier: "passed by the caller", notes: [] };
1557
+ }
1558
+ if (values.family !== void 0) {
1559
+ if (!FAMILY_ERE.some(([f]) => f === values.family)) return { kind: "stop", code: 1, lines: [`no grammar for family: ${values.family}`] };
1560
+ return { kind: "rule", rule: { family: values.family }, tier: "passed by the caller", notes: [] };
1561
+ }
1562
+ const read = readCommittedConvention(cwd, { keys: ["commit_format"] });
1563
+ const reading = read.readings.commit_format;
1564
+ if (isRefusal(reading.state)) return { kind: "stop", code: 1, lines: [describeRefusal(reading)] };
1565
+ const ignored = ignoredCarrierWarning(read.root);
1566
+ const notes = [...ignored === null ? [] : [`\u26A0\uFE0F ${ignored}`], ...commitMessageValidators(cwd).map(_validatorNote)];
1567
+ if (_declares(reading)) return { kind: "rule", rule: { format: reading.value }, tier: `declared in ${reading.source}`, notes };
1568
+ const family = _cardFamily(read.root);
1569
+ if (family !== null) {
1570
+ if (!FAMILY_ERE.some(([f]) => f === family)) return { kind: "stop", code: 1, lines: [`the approved family ${family} in ${APPROVED_CARD} has no grammar to validate against`] };
1571
+ return { kind: "rule", rule: { family }, tier: `approved in ${APPROVED_CARD}`, notes };
1572
+ }
1573
+ const lint = _commitlint(cwd);
1574
+ if (lint !== null) return { kind: "rule", rule: { format: reading.value ?? "ticket-scope" }, tier: `default (Conventional Commits); the commitlint config at ${lint.path} governs`, notes };
1575
+ return { kind: "rule", rule: { format: reading.value ?? "ticket-scope" }, tier: "default (Conventional Commits)", notes, established: false };
1576
+ }
1577
+ function _subjectResult(json, code, lines, err = [], extra = {}) {
1578
+ if (!json) return { code, out: lines, err };
1579
+ return { code, out: [JSON.stringify({ ok: code === 0, code, lines, ...extra }, null, 2)], err };
1580
+ }
1581
+ var NO_SUBJECTS = "no subjects on stdin \u2014 the verb ran; pipe the subjects into it, one per line";
1582
+ function subjectCommand(args, cwd, stdin = "") {
1583
+ const f = _flags(args, ["format", "family"]);
1584
+ if (f.bad !== null || f.positional.length > 0) {
1585
+ const why = `unknown argument: ${f.bad ?? f.positional[0]}`;
1586
+ return _subjectResult(f.json, 2, f.json ? [why] : [], [why, USAGE]);
1587
+ }
1588
+ const subjects = stdin.split("\n").map((l) => l.trimEnd()).filter((l) => l !== "");
1589
+ if (subjects.length === 0) return _subjectResult(f.json, 2, f.json ? [NO_SUBJECTS] : [], [NO_SUBJECTS, USAGE]);
1590
+ const plan = _planSubject(f.values, cwd);
1591
+ if (plan.kind === "stop") return _subjectResult(f.json, plan.code, plan.lines);
1592
+ const failures = subjects.map((s) => ({ s, v: checkSubject(s, plan.rule) })).filter((x) => !x.v.ok);
1593
+ const lines = failures.length === 0 ? [`${subjects.length} subject(s) valid under ${ruleName(plan.rule)} (${plan.tier})`, ...plan.notes] : [...failures.map((x) => `\u2717 ${x.s}
1594
+ ${x.v.ok ? "" : x.v.rule}`), ...plan.notes];
1595
+ if (plan.established === false) lines.push(NO_CONVENTION);
1596
+ return _subjectResult(f.json, failures.length === 0 ? 0 : 1, lines, [], {
1597
+ rule: ruleName(plan.rule),
1598
+ tier: plan.tier,
1599
+ notes: plan.notes,
1600
+ convention_established: plan.established === false ? false : plan.tier === "passed by the caller" ? null : true,
1601
+ failures: failures.map((x) => ({ subject: x.s, rule: x.v.ok ? null : x.v.rule }))
1602
+ });
1603
+ }
1604
+ function _commitlintProposal(cwd) {
1605
+ const v = commitMessageValidators(cwd).find((c) => c.kind === "commitlint config");
1606
+ if (v === void 0) return null;
1607
+ try {
1608
+ const keys = commitlintIssuePrefixes(fs2.readFileSync(v.path, "utf-8"));
1609
+ return keys.length === 0 ? null : { keys, from: v.path };
1610
+ } catch {
1611
+ return null;
1612
+ }
1613
+ }
1614
+ function ticketCommand(args, cwd) {
1615
+ const f = _flags(args, ["keys"]);
1616
+ if (f.bad !== null || f.positional.length > 1) return { code: 2, out: [], err: [`unknown argument: ${f.bad ?? f.positional[1]}`, USAGE] };
1617
+ const name = f.positional[0] ?? _git2(cwd, "rev-parse", "--abbrev-ref", "HEAD") ?? "";
1618
+ const keys = f.values.keys === void 0 ? null : parseTicketKeys(f.values.keys);
1619
+ const all = ticketCandidates(name, keys);
1620
+ const ticket = all.find((c) => c.status === "ticket")?.token ?? null;
1621
+ const proposal = keys === null ? _commitlintProposal(cwd) : null;
1622
+ if (f.json) return { code: 0, out: [JSON.stringify({ branch: name, ticket, candidates: all, proposal }, null, 2)], err: [] };
1623
+ const out = [`ticket ${ticket ?? "none"}`, ...all.map((c) => ` ${c.token.padEnd(14)} ${c.status}`)];
1624
+ if (proposal !== null) {
1625
+ out.push(`proposal ticket_keys: ${proposal.keys.join(", ")} (issuePrefixes in ${proposal.from}) \u2014 offer it for the convention card; this verb writes nothing`);
1626
+ }
1627
+ return { code: 0, out, err: [] };
1628
+ }
1629
+ var NO_BRANCH_PATTERN = "no branch_pattern declared \u2014 pass --pattern or declare one";
1630
+ function branchCommand(args, cwd) {
1631
+ const f = _flags(args, ["type", "ticket", "slug", "pattern"]);
1632
+ if (f.bad !== null || f.positional.length > 0 || f.values.slug === void 0) {
1633
+ return { code: 2, out: [], err: [f.bad === null && f.positional.length === 0 ? "--slug is required" : `unknown argument: ${f.bad ?? f.positional[0]}`, USAGE] };
1634
+ }
1635
+ let pattern = f.values.pattern;
1636
+ if (pattern === void 0) {
1637
+ const reading = readCommittedConvention(cwd, { keys: ["branch_pattern"] }).readings.branch_pattern;
1638
+ if (isRefusal(reading.state)) return { code: 1, out: [], err: [describeRefusal(reading)] };
1639
+ if (reading.value === null) return { code: 1, out: [], err: [NO_BRANCH_PATTERN] };
1640
+ pattern = reading.value;
1641
+ } else {
1642
+ const why = invalidReason("branch_pattern", pattern);
1643
+ if (why !== null) return { code: 1, out: [], err: [`invalid pattern: ${why}`] };
1644
+ }
1645
+ const r = renderBranch(pattern, { type: f.values.type ?? null, ticket: f.values.ticket ?? null, slug: f.values.slug });
1646
+ if (!r.ok) return { code: 1, out: [], err: [r.reason] };
1647
+ const check = spawnSync4("git", ["check-ref-format", "--branch", r.name], { encoding: "utf8" });
1648
+ if (check.error === void 0 && check.status !== 0) return { code: 1, out: [], err: [`\`${r.name}\` fails git check-ref-format --branch`] };
1649
+ return { code: 0, out: f.json ? [JSON.stringify({ branch: r.name, pattern })] : [r.name], err: [] };
1650
+ }
1651
+ function cardText(m) {
1652
+ return [
1653
+ "---",
1654
+ `dominant_family: ${m.family}`,
1655
+ `observed_n: ${m.observedN}`,
1656
+ `dominant_share: ${m.share.toFixed(2)}`,
1657
+ `author_count: ${m.authors}`,
1658
+ m.windowExtended === true ? `sample_window: "any age (fewer than ${MIN_N} since ${MEASURE_SINCE}), newest ${MEASURE_LIMIT} non-merge commits"` : `sample_window: "${MEASURE_SINCE}, newest ${MEASURE_LIMIT} non-merge commits"`,
1659
+ `classifier_version: ${CLASSIFIER_VERSION}`,
1660
+ `confirm_against: ${m.trunk}`,
1661
+ "ticket_keys: []",
1662
+ "---",
1663
+ "",
1664
+ `Approved by the user from \`agent-config git:convention measure\`. Re-measure when a tier-1 source appears or the newer half's family changes.`,
1665
+ ""
1666
+ ].join("\n");
1667
+ }
1668
+ function measureCommand(args, cwd) {
1669
+ const f = _flags(args, ["limit", "family"]);
1670
+ const limit = f.values.limit === void 0 ? MEASURE_LIMIT : Number(f.values.limit);
1671
+ if (f.bad !== null || f.positional.length > 0 || !Number.isInteger(limit) || limit < 1) {
1672
+ return { code: 2, out: [], err: [f.bad === null && f.positional.length === 0 ? `--limit must be a positive integer: ${f.values.limit}` : `unknown argument: ${f.bad ?? f.positional[0]}`, USAGE] };
1673
+ }
1674
+ const chosen = f.values.family;
1675
+ if (chosen !== void 0 && !FAMILY_ERE.some(([fam]) => fam === chosen)) return { code: 2, out: [], err: [`no grammar for family: ${chosen}`, USAGE] };
1676
+ const history = readHistory(cwd, limit);
1677
+ if (history === null) return { code: 1, out: [], err: ["not a git repository"] };
1678
+ if ("unresolved" in history) {
1679
+ const line = `sample none \u2014 ${history.unresolved}; nothing was sampled and nothing is proposed. Set it with: git remote set-head origin --auto`;
1680
+ return f.json ? { code: 1, out: [JSON.stringify({ classifier_version: CLASSIFIER_VERSION, sample: { trunk: null, limit, since: MEASURE_SINCE, read: 0 }, trunk_unresolved: history.unresolved }, null, 2)], err: [] } : { code: 1, out: [line], err: [] };
1681
+ }
1682
+ const m = measureSubjects(history.commits);
1683
+ const b = measureBranches(history.branches);
1684
+ const u = measureUpdateStyle(history.mergeSubjects, history.defaultBranch);
1685
+ const read = readCommittedConvention(cwd, { keys: ["commit_format"] });
1686
+ const established = _established(read.readings.commit_format, read.root);
1687
+ const lint = _commitlint(cwd);
1688
+ const ignored = ignoredCarrierWarning(read.root);
1689
+ const family = chosen ?? m.established;
1690
+ const share = family === null ? 0 : m.families.find((x) => x.family === family)?.share ?? 0;
1691
+ const team = teamFile(family, b.pattern);
1692
+ const card = family === null ? null : cardText({ family, observedN: m.eligible, share, authors: m.authors, trunk: history.trunk, windowExtended: history.windowExtended });
1693
+ if (f.json) {
1694
+ return {
1695
+ code: 0,
1696
+ out: [JSON.stringify({
1697
+ convention_established: established,
1698
+ commitlint_config: lint?.path ?? null,
1699
+ classifier_version: CLASSIFIER_VERSION,
1700
+ sample: { trunk: history.trunk, limit, since: MEASURE_SINCE, window_extended: history.windowExtended, read: history.commits.length, eligible: m.eligible, excluded: m.excluded, authors: m.authors, capped: m.capped, capped_total: m.cappedTotal },
1701
+ bar: { min_n: MIN_N, share: m.bar },
1702
+ families: m.families,
1703
+ halves: { newer: m.newer, older: m.older, agree: m.halvesAgree },
1704
+ recent: m.recent,
1705
+ verdict: { established: m.established, migrating: m.migrating, migrated_by: m.migratedBy, reasons: m.reasons, strongest: m.strongest },
1706
+ branches: { sampled: b.sampled, pattern: b.pattern, share: b.share, shapes: b.shapes },
1707
+ update_style: { observed: u.observed, base_merges: u.baseMerges, merges: u.merges, adopted: false },
1708
+ chosen: family,
1709
+ team_file: team,
1710
+ card
1711
+ }, null, 2)],
1712
+ err: []
1713
+ };
1714
+ }
1715
+ const out = [
1716
+ ...ignored === null ? [] : [`\u26A0\uFE0F ${ignored}`],
1717
+ ...lint === null ? [] : [`commitlint: the repository has a commitlint config \u2014 the commitlint config at ${lint.path} governs the subject format; this measurement is advisory`],
1718
+ established === true ? "convention: already established \u2014 a declaration or an approved card is in force; this measurement is advisory" : established === null ? "convention: git.commit_format cannot be read \u2014 see git:convention show" : "convention: none established \u2014 /commit offers the result below once",
1719
+ history.windowExtended ? `sample fewer than ${MIN_N} since ${MEASURE_SINCE} \u2014 sampled the newest ${history.commits.length} regardless of age on ${history.trunk} \xB7 classifier ${CLASSIFIER_VERSION}` : `sample ${history.commits.length} commit(s) on ${history.trunk} since ${MEASURE_SINCE} \xB7 classifier ${CLASSIFIER_VERSION}`,
1720
+ ` eligible ${m.eligible} (excluded: bots ${m.excluded.bots}, automation ${m.excluded.automation}, bulk ${m.excluded.bulk}) \xB7 authors ${m.authors}` + (m.capped ? ` \xB7 capped per author per half \u2192 ${m.cappedTotal}` : " \xB7 uncapped (fewer than three authors)"),
1721
+ ...m.families.map((x) => ` ${x.family.padEnd(20)} ${String(x.count).padStart(4)} ${pct(x.share)}`),
1722
+ `halves newer ${m.newer.family ?? "\u2014"} ${pct(m.newer.share)} \xB7 older ${m.older.family ?? "\u2014"} ${pct(m.older.share)} \u2014 ${m.halvesAgree ? "agree" : "disagree"}`,
1723
+ m.established !== null ? `verdict established: ${m.established}${m.migrating ? ` (migrating to ${m.established} \u2014 ${m.migratedBy === "recent-window" ? `the newest ${m.recent.n} commits` : "the newer half"} alone clear the bar)` : ""} \u2014 bar n \u2265 ${MIN_N}, share \u2265 ${pct(m.bar)}` : `verdict below the bar (${m.reasons.join("; ")}) \u2014 strongest: ${m.strongest.join(", ") || "none with a grammar"}`,
1724
+ b.pattern !== null ? `branches ${b.sampled} sampled \u2014 proposed branch_pattern "${b.pattern}" (${pct(b.share)})` : `branches ${b.sampled} sampled \u2014 no clear pattern`,
1725
+ `update observed ${u.observed} (${u.baseMerges} of ${u.merges} merge(s) bring the default branch into a topic branch) \u2014 shown, never adopted`
1726
+ ];
1727
+ if (team !== null) {
1728
+ out.push(`team file ${chosen === void 0 ? "for the measured result" : `for --family ${chosen}`} \u2014 a human creates and commits ${CARRIER_PATH}, or init --yes creates it on the user's yes; this verb writes nothing:`);
1729
+ out.push(...team.trimEnd().split("\n").map((l) => ` ${l}`));
1730
+ }
1731
+ if (chosen !== void 0 && card !== null) {
1732
+ out.push(`card for ${APPROVED_CARD}, save the lines below as they are:`);
1733
+ out.push(...card.trimEnd().split("\n"));
1734
+ }
1735
+ return { code: 0, out, err: [] };
1736
+ }
1737
+ function initCommand(args, cwd) {
1738
+ let yes = false;
1739
+ const rest = [];
1740
+ for (const a of args) {
1741
+ if (a === "--yes") yes = true;
1742
+ else rest.push(a);
1743
+ }
1744
+ const f = _flags(rest, ["commit-format", "branch-pattern"]);
1745
+ if (f.bad !== null || f.positional.length > 0) return { code: 2, out: [], err: [`unknown argument: ${f.bad ?? f.positional[0]}`, USAGE] };
1746
+ const { root, git } = conventionRoot(cwd);
1747
+ if (!git) return { code: 1, out: [], err: ["not a git repository \u2014 the carrier lives at a repository root"] };
1748
+ const existing = [CARRIER_PATH, IGNORED_CARRIER_PATH].find((name) => fs2.existsSync(path3.join(root, name)));
1749
+ if (existing !== void 0) return { code: 1, out: [], err: [`${existing} already exists at ${root} \u2014 init creates the file once and never changes it; nothing was written`] };
1750
+ if (!yes) return { code: 1, out: [], err: ["init writes only on the user's explicit yes this turn \u2014 pass --yes; nothing was written"] };
1751
+ let format = f.values["commit-format"] ?? null;
1752
+ let pattern = f.values["branch-pattern"] ?? null;
1753
+ if (format === null || pattern === null) {
1754
+ const history = readHistory(cwd);
1755
+ if (history !== null && !("unresolved" in history)) {
1756
+ const family = measureSubjects(history.commits).established;
1757
+ if (format === null && family !== null) format = formatForFamily(family);
1758
+ if (pattern === null) pattern = measureBranches(history.branches).pattern;
1759
+ }
1760
+ }
1761
+ for (const [key, value] of [["commit_format", format], ["branch_pattern", pattern]]) {
1762
+ const why = value === null ? null : invalidReason(key, value);
1763
+ if (why !== null) return { code: 1, out: [], err: [`git.${key}: ${why}; nothing was written`] };
1764
+ }
1765
+ if (format === null && pattern === null) {
1766
+ return { code: 1, out: [], err: ["nothing to write \u2014 measure proposes neither a commit_format nor a branch_pattern; pass --commit-format or --branch-pattern"] };
1767
+ }
1768
+ const content = ["git:", ...format === null ? [] : [` commit_format: ${format}`], ...pattern === null ? [] : [` branch_pattern: "${pattern}"`], ""].join("\n");
1769
+ try {
1770
+ fs2.writeFileSync(path3.join(root, CARRIER_PATH), content, { flag: "wx" });
1771
+ } catch {
1772
+ return { code: 1, out: [], err: [`${CARRIER_PATH} could not be created at ${root} (it may have appeared meanwhile); nothing was written`] };
1773
+ }
1774
+ return {
1775
+ code: 0,
1776
+ out: [`created ${CARRIER_PATH} at ${root} \u2014 NOT committed; review and commit it:`, ...content.trimEnd().split("\n")],
1777
+ err: []
1778
+ };
1779
+ }
1780
+ var SUBCOMMANDS = {
1781
+ show: (args, cwd) => showConvention(args, cwd),
1782
+ subject: (args, cwd, stdin) => subjectCommand(args, cwd, stdin),
1783
+ ticket: (args, cwd) => ticketCommand(args, cwd),
1784
+ branch: (args, cwd) => branchCommand(args, cwd),
1785
+ measure: (args, cwd) => measureCommand(args, cwd),
1786
+ init: (args, cwd) => initCommand(args, cwd),
1787
+ // Prints as it runs; a later `--repo` in `args` overrides the directory.
1788
+ sync: (args, cwd) => ({ code: main(["--repo", cwd, ...args]), out: [], err: [] })
1789
+ };
1790
+ var USAGE = [
1791
+ "usage: agent-config git:convention show [--json] [--base REF] [--key KEY]... (a pull request passes --base origin/<its base>)",
1792
+ " agent-config git:convention subject [--format F | --family F] [--json] (subjects on stdin)",
1793
+ ' agent-config git:convention ticket [BRANCH] [--keys "DEV, OPS"] [--json]',
1794
+ " agent-config git:convention branch --slug S [--type T] [--ticket K] [--pattern P] [--json]",
1795
+ " agent-config git:convention measure [--limit N] [--family F] [--json] (proposes a convention from the history; writes nothing)",
1796
+ " agent-config git:convention init --yes [--commit-format F] [--branch-pattern P] (creates .git-convention.yml once, on the user's yes; never overwrites, never commits)",
1797
+ " agent-config git:convention sync [--base REF] [--dry-run] [--auto-resolve-generated] [--quiet] (without --base: the default branch)"
1798
+ ].join("\n");
1799
+ function runGitConvention(argv, cwd, stdin) {
1800
+ const [sub, ...rest] = argv;
1801
+ if (sub === "-h" || sub === "--help") return { code: 0, out: [USAGE], err: [] };
1802
+ const handler = sub === void 0 ? void 0 : SUBCOMMANDS[sub];
1803
+ if (handler === void 0) {
1804
+ return { code: 2, out: [], err: [sub === void 0 ? USAGE : `unknown subcommand: ${sub}`, ...sub === void 0 ? [] : [USAGE]] };
1805
+ }
1806
+ return handler(rest, cwd, stdin);
1807
+ }
1808
+ async function _stdin() {
1809
+ if (process2.stdin.isTTY) return "";
1810
+ const chunks = [];
1811
+ for await (const chunk of process2.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
1812
+ return Buffer.concat(chunks).toString("utf-8");
1813
+ }
1814
+ async function main2(argv = process2.argv.slice(2)) {
1815
+ const result = runGitConvention(argv, process2.cwd(), argv[0] === "subject" ? await _stdin() : void 0);
1816
+ for (const line of result.out) process2.stdout.write(`${line}
1817
+ `);
1818
+ for (const line of result.err) process2.stderr.write(`${line}
1819
+ `);
1820
+ return result.code;
1821
+ }
1822
+ function _isCliEntry2() {
1823
+ if (process2.argv[1] === void 0) return false;
1824
+ if (import.meta.url === pathToFileURL2(path3.resolve(process2.argv[1])).href) return true;
1825
+ try {
1826
+ return fs2.realpathSync(fileURLToPath2(import.meta.url)) === fs2.realpathSync(path3.resolve(process2.argv[1]));
1827
+ } catch {
1828
+ return false;
1829
+ }
1830
+ }
1831
+ if (_isCliEntry2()) {
1832
+ process2.exitCode = await main2();
1833
+ }
1834
+ export {
1835
+ APPROVED_CARD,
1836
+ NO_BRANCH_PATTERN,
1837
+ NO_CONVENTION,
1838
+ NO_SUBJECTS,
1839
+ SUBCOMMANDS,
1840
+ branchCommand,
1841
+ cardText,
1842
+ commitMessageValidator,
1843
+ commitMessageValidators,
1844
+ initCommand,
1845
+ main2 as main,
1846
+ measureCommand,
1847
+ runGitConvention,
1848
+ showConvention,
1849
+ subjectCommand,
1850
+ ticketCommand
1851
+ };