oh-my-copilot 4.13.2 → 4.13.45

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 (445) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +9 -1
  4. package/agents/code-reviewer.agent.md +24 -6
  5. package/agents/designer.agent.md +12 -0
  6. package/bridge/cli.cjs +4069 -1732
  7. package/bridge/mcp-server.cjs +774 -269
  8. package/bridge/runtime-cli.cjs +1311 -141
  9. package/bridge/team.js +1778 -133
  10. package/dist/__tests__/agent-registry.test.js +92 -1
  11. package/dist/__tests__/agent-registry.test.js.map +1 -1
  12. package/dist/__tests__/auto-slash-aliases.test.js +91 -0
  13. package/dist/__tests__/auto-slash-aliases.test.js.map +1 -1
  14. package/dist/__tests__/bedrock-model-routing.test.js +18 -17
  15. package/dist/__tests__/bedrock-model-routing.test.js.map +1 -1
  16. package/dist/__tests__/delegation-enforcer.test.js +46 -6
  17. package/dist/__tests__/delegation-enforcer.test.js.map +1 -1
  18. package/dist/__tests__/hooks/session-end-cleanup.test.d.ts +2 -0
  19. package/dist/__tests__/hooks/session-end-cleanup.test.d.ts.map +1 -0
  20. package/dist/__tests__/hooks/session-end-cleanup.test.js +93 -0
  21. package/dist/__tests__/hooks/session-end-cleanup.test.js.map +1 -0
  22. package/dist/__tests__/hud/labels.test.d.ts +2 -0
  23. package/dist/__tests__/hud/labels.test.d.ts.map +1 -0
  24. package/dist/__tests__/hud/labels.test.js +158 -0
  25. package/dist/__tests__/hud/labels.test.js.map +1 -0
  26. package/dist/__tests__/hud/render-rate-limits-priority.test.js +26 -0
  27. package/dist/__tests__/hud/render-rate-limits-priority.test.js.map +1 -1
  28. package/dist/__tests__/hud/stdin.test.d.ts +2 -0
  29. package/dist/__tests__/hud/stdin.test.d.ts.map +1 -0
  30. package/dist/__tests__/hud/stdin.test.js +556 -0
  31. package/dist/__tests__/hud/stdin.test.js.map +1 -0
  32. package/dist/__tests__/hud-marketplace-resolution.test.js +34 -0
  33. package/dist/__tests__/hud-marketplace-resolution.test.js.map +1 -1
  34. package/dist/__tests__/installer-hud-skip.test.js +6 -0
  35. package/dist/__tests__/installer-hud-skip.test.js.map +1 -1
  36. package/dist/__tests__/installer-omc-reference.test.js +1 -1
  37. package/dist/__tests__/installer-omc-reference.test.js.map +1 -1
  38. package/dist/__tests__/mnemosyne/detector.test.js +2 -0
  39. package/dist/__tests__/mnemosyne/detector.test.js.map +1 -1
  40. package/dist/__tests__/model-routing-override-three-site-sync.test.d.ts +2 -0
  41. package/dist/__tests__/model-routing-override-three-site-sync.test.d.ts.map +1 -0
  42. package/dist/__tests__/model-routing-override-three-site-sync.test.js +75 -0
  43. package/dist/__tests__/model-routing-override-three-site-sync.test.js.map +1 -0
  44. package/dist/__tests__/non-claude-provider-detection.test.js +37 -0
  45. package/dist/__tests__/non-claude-provider-detection.test.js.map +1 -1
  46. package/dist/__tests__/notepad.test.js +2 -2
  47. package/dist/__tests__/notepad.test.js.map +1 -1
  48. package/dist/__tests__/package-dir-resolution-regression.test.js +25 -0
  49. package/dist/__tests__/package-dir-resolution-regression.test.js.map +1 -1
  50. package/dist/__tests__/pre-tool-enforcer.test.js +135 -1
  51. package/dist/__tests__/pre-tool-enforcer.test.js.map +1 -1
  52. package/dist/__tests__/ralph-prd-mandatory.test.js +11 -1
  53. package/dist/__tests__/ralph-prd-mandatory.test.js.map +1 -1
  54. package/dist/__tests__/ralph-prd.test.js +40 -2
  55. package/dist/__tests__/ralph-prd.test.js.map +1 -1
  56. package/dist/__tests__/session-start-script-context.test.js +121 -0
  57. package/dist/__tests__/session-start-script-context.test.js.map +1 -1
  58. package/dist/__tests__/setup-claude-md-script.test.js +44 -1
  59. package/dist/__tests__/setup-claude-md-script.test.js.map +1 -1
  60. package/dist/__tests__/skills.test.js +363 -4
  61. package/dist/__tests__/skills.test.js.map +1 -1
  62. package/dist/__tests__/state-root-resolution.test.js +56 -6
  63. package/dist/__tests__/state-root-resolution.test.js.map +1 -1
  64. package/dist/agents/definitions.d.ts.map +1 -1
  65. package/dist/agents/definitions.js +2 -1
  66. package/dist/agents/definitions.js.map +1 -1
  67. package/dist/autoresearch/__tests__/decide-outcome.test.d.ts +2 -0
  68. package/dist/autoresearch/__tests__/decide-outcome.test.d.ts.map +1 -0
  69. package/dist/autoresearch/__tests__/decide-outcome.test.js +69 -0
  70. package/dist/autoresearch/__tests__/decide-outcome.test.js.map +1 -0
  71. package/dist/autoresearch/__tests__/runtime.test.js +225 -28
  72. package/dist/autoresearch/__tests__/runtime.test.js.map +1 -1
  73. package/dist/autoresearch/runtime.d.ts.map +1 -1
  74. package/dist/autoresearch/runtime.js +25 -7
  75. package/dist/autoresearch/runtime.js.map +1 -1
  76. package/dist/cli/__tests__/launch.test.js +23 -0
  77. package/dist/cli/__tests__/launch.test.js.map +1 -1
  78. package/dist/cli/__tests__/self-improve-paths.test.js +3 -2
  79. package/dist/cli/__tests__/self-improve-paths.test.js.map +1 -1
  80. package/dist/cli/__tests__/team.test.js +100 -0
  81. package/dist/cli/__tests__/team.test.js.map +1 -1
  82. package/dist/cli/autoresearch.d.ts +1 -1
  83. package/dist/cli/autoresearch.d.ts.map +1 -1
  84. package/dist/cli/autoresearch.js +4 -4
  85. package/dist/cli/commands/__tests__/team-role-shorthand.test.js +30 -0
  86. package/dist/cli/commands/__tests__/team-role-shorthand.test.js.map +1 -1
  87. package/dist/cli/commands/__tests__/team.test.js +48 -1
  88. package/dist/cli/commands/__tests__/team.test.js.map +1 -1
  89. package/dist/cli/commands/team.d.ts +3 -0
  90. package/dist/cli/commands/team.d.ts.map +1 -1
  91. package/dist/cli/commands/team.js +23 -4
  92. package/dist/cli/commands/team.js.map +1 -1
  93. package/dist/cli/index.js +3 -0
  94. package/dist/cli/index.js.map +1 -1
  95. package/dist/cli/launch.d.ts.map +1 -1
  96. package/dist/cli/launch.js +7 -0
  97. package/dist/cli/launch.js.map +1 -1
  98. package/dist/cli/team.d.ts +6 -0
  99. package/dist/cli/team.d.ts.map +1 -1
  100. package/dist/cli/team.js +57 -4
  101. package/dist/cli/team.js.map +1 -1
  102. package/dist/config/__tests__/loader.test.js +35 -4
  103. package/dist/config/__tests__/loader.test.js.map +1 -1
  104. package/dist/config/__tests__/models.test.js +126 -3
  105. package/dist/config/__tests__/models.test.js.map +1 -1
  106. package/dist/config/models.d.ts +8 -0
  107. package/dist/config/models.d.ts.map +1 -1
  108. package/dist/config/models.js +119 -8
  109. package/dist/config/models.js.map +1 -1
  110. package/dist/features/boulder-state/constants.d.ts +1 -1
  111. package/dist/features/boulder-state/constants.d.ts.map +1 -1
  112. package/dist/features/builtin-skills/skills.d.ts.map +1 -1
  113. package/dist/features/builtin-skills/skills.js +11 -3
  114. package/dist/features/builtin-skills/skills.js.map +1 -1
  115. package/dist/features/builtin-skills/types.d.ts +7 -0
  116. package/dist/features/builtin-skills/types.d.ts.map +1 -1
  117. package/dist/features/delegation-enforcer.d.ts.map +1 -1
  118. package/dist/features/delegation-enforcer.js +10 -9
  119. package/dist/features/delegation-enforcer.js.map +1 -1
  120. package/dist/features/magic-keywords.d.ts.map +1 -1
  121. package/dist/features/magic-keywords.js +30 -1
  122. package/dist/features/magic-keywords.js.map +1 -1
  123. package/dist/features/rate-limit-wait/tmux-detector.d.ts +4 -4
  124. package/dist/features/rate-limit-wait/tmux-detector.js +9 -9
  125. package/dist/features/rate-limit-wait/tmux-detector.js.map +1 -1
  126. package/dist/hooks/__tests__/bridge-routing.test.js +157 -0
  127. package/dist/hooks/__tests__/bridge-routing.test.js.map +1 -1
  128. package/dist/hooks/auto-slash-command/constants.d.ts.map +1 -1
  129. package/dist/hooks/auto-slash-command/constants.js +1 -0
  130. package/dist/hooks/auto-slash-command/constants.js.map +1 -1
  131. package/dist/hooks/auto-slash-command/executor.d.ts.map +1 -1
  132. package/dist/hooks/auto-slash-command/executor.js +29 -0
  133. package/dist/hooks/auto-slash-command/executor.js.map +1 -1
  134. package/dist/hooks/autopilot/enforcement.d.ts.map +1 -1
  135. package/dist/hooks/autopilot/enforcement.js +19 -0
  136. package/dist/hooks/autopilot/enforcement.js.map +1 -1
  137. package/dist/hooks/bridge.d.ts.map +1 -1
  138. package/dist/hooks/bridge.js +210 -5
  139. package/dist/hooks/bridge.js.map +1 -1
  140. package/dist/hooks/learner/detector.js +1 -1
  141. package/dist/hooks/mode-registry/__tests__/session-isolation.test.js +16 -0
  142. package/dist/hooks/mode-registry/__tests__/session-isolation.test.js.map +1 -1
  143. package/dist/hooks/notepad/index.d.ts +4 -1
  144. package/dist/hooks/notepad/index.d.ts.map +1 -1
  145. package/dist/hooks/notepad/index.js +7 -4
  146. package/dist/hooks/notepad/index.js.map +1 -1
  147. package/dist/hooks/persistent-mode/__tests__/ralph-verification-flow.test.js +70 -3
  148. package/dist/hooks/persistent-mode/__tests__/ralph-verification-flow.test.js.map +1 -1
  149. package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
  150. package/dist/hooks/persistent-mode/index.js +10 -9
  151. package/dist/hooks/persistent-mode/index.js.map +1 -1
  152. package/dist/hooks/persistent-mode/stop-hook-blocking.test.js +101 -10
  153. package/dist/hooks/persistent-mode/stop-hook-blocking.test.js.map +1 -1
  154. package/dist/hooks/project-memory/__tests__/integration.test.js +117 -3
  155. package/dist/hooks/project-memory/__tests__/integration.test.js.map +1 -1
  156. package/dist/hooks/project-memory/__tests__/storage.test.js +11 -11
  157. package/dist/hooks/project-memory/__tests__/storage.test.js.map +1 -1
  158. package/dist/hooks/project-memory/index.d.ts.map +1 -1
  159. package/dist/hooks/project-memory/index.js +24 -12
  160. package/dist/hooks/project-memory/index.js.map +1 -1
  161. package/dist/hooks/ralph/index.d.ts +1 -1
  162. package/dist/hooks/ralph/index.d.ts.map +1 -1
  163. package/dist/hooks/ralph/index.js +1 -1
  164. package/dist/hooks/ralph/index.js.map +1 -1
  165. package/dist/hooks/ralph/loop.d.ts +6 -6
  166. package/dist/hooks/ralph/loop.d.ts.map +1 -1
  167. package/dist/hooks/ralph/loop.js +18 -18
  168. package/dist/hooks/ralph/loop.js.map +1 -1
  169. package/dist/hooks/ralph/prd.d.ts +23 -12
  170. package/dist/hooks/ralph/prd.d.ts.map +1 -1
  171. package/dist/hooks/ralph/prd.js +88 -42
  172. package/dist/hooks/ralph/prd.js.map +1 -1
  173. package/dist/hooks/session-end/__tests__/mode-state-cleanup.test.js +20 -0
  174. package/dist/hooks/session-end/__tests__/mode-state-cleanup.test.js.map +1 -1
  175. package/dist/hooks/session-end/index.d.ts +18 -2
  176. package/dist/hooks/session-end/index.d.ts.map +1 -1
  177. package/dist/hooks/session-end/index.js +157 -35
  178. package/dist/hooks/session-end/index.js.map +1 -1
  179. package/dist/hooks/skill-bridge.cjs +6 -5
  180. package/dist/hooks/skill-state/index.d.ts.map +1 -1
  181. package/dist/hooks/skill-state/index.js +1 -0
  182. package/dist/hooks/skill-state/index.js.map +1 -1
  183. package/dist/hud/__tests__/git-status.test.js +18 -0
  184. package/dist/hud/__tests__/git-status.test.js.map +1 -1
  185. package/dist/hud/elements/background.d.ts +3 -3
  186. package/dist/hud/elements/background.d.ts.map +1 -1
  187. package/dist/hud/elements/background.js +5 -4
  188. package/dist/hud/elements/background.js.map +1 -1
  189. package/dist/hud/elements/call-counts.d.ts +2 -2
  190. package/dist/hud/elements/call-counts.d.ts.map +1 -1
  191. package/dist/hud/elements/call-counts.js +7 -6
  192. package/dist/hud/elements/call-counts.js.map +1 -1
  193. package/dist/hud/elements/context.d.ts +3 -3
  194. package/dist/hud/elements/context.d.ts.map +1 -1
  195. package/dist/hud/elements/context.js +5 -4
  196. package/dist/hud/elements/context.js.map +1 -1
  197. package/dist/hud/elements/git.d.ts +2 -1
  198. package/dist/hud/elements/git.d.ts.map +1 -1
  199. package/dist/hud/elements/git.js +7 -6
  200. package/dist/hud/elements/git.js.map +1 -1
  201. package/dist/hud/elements/ralph.d.ts +2 -2
  202. package/dist/hud/elements/ralph.d.ts.map +1 -1
  203. package/dist/hud/elements/ralph.js +3 -2
  204. package/dist/hud/elements/ralph.js.map +1 -1
  205. package/dist/hud/elements/thinking.d.ts +2 -2
  206. package/dist/hud/elements/thinking.d.ts.map +1 -1
  207. package/dist/hud/elements/thinking.js +3 -2
  208. package/dist/hud/elements/thinking.js.map +1 -1
  209. package/dist/hud/elements/token-usage.d.ts +2 -2
  210. package/dist/hud/elements/token-usage.d.ts.map +1 -1
  211. package/dist/hud/elements/token-usage.js +3 -2
  212. package/dist/hud/elements/token-usage.js.map +1 -1
  213. package/dist/hud/render.d.ts.map +1 -1
  214. package/dist/hud/render.js +11 -10
  215. package/dist/hud/render.js.map +1 -1
  216. package/dist/hud/state.d.ts.map +1 -1
  217. package/dist/hud/state.js +18 -1
  218. package/dist/hud/state.js.map +1 -1
  219. package/dist/hud/stdin.d.ts +12 -1
  220. package/dist/hud/stdin.d.ts.map +1 -1
  221. package/dist/hud/stdin.js +123 -18
  222. package/dist/hud/stdin.js.map +1 -1
  223. package/dist/hud/types.d.ts +27 -0
  224. package/dist/hud/types.d.ts.map +1 -1
  225. package/dist/hud/types.js +58 -0
  226. package/dist/hud/types.js.map +1 -1
  227. package/dist/installer/__tests__/hud-cache-wrapper.test.d.ts +2 -0
  228. package/dist/installer/__tests__/hud-cache-wrapper.test.d.ts.map +1 -0
  229. package/dist/installer/__tests__/hud-cache-wrapper.test.js +235 -0
  230. package/dist/installer/__tests__/hud-cache-wrapper.test.js.map +1 -0
  231. package/dist/installer/__tests__/session-start-template.test.d.ts +2 -0
  232. package/dist/installer/__tests__/session-start-template.test.d.ts.map +1 -0
  233. package/dist/installer/__tests__/session-start-template.test.js +174 -0
  234. package/dist/installer/__tests__/session-start-template.test.js.map +1 -0
  235. package/dist/installer/__tests__/standalone-hook-reconcile.test.js +2 -0
  236. package/dist/installer/__tests__/standalone-hook-reconcile.test.js.map +1 -1
  237. package/dist/installer/index.d.ts.map +1 -1
  238. package/dist/installer/index.js +19 -7
  239. package/dist/installer/index.js.map +1 -1
  240. package/dist/lib/__tests__/mode-state-io.test.js +75 -12
  241. package/dist/lib/__tests__/mode-state-io.test.js.map +1 -1
  242. package/dist/lib/__tests__/worktree-paths.test.js +162 -39
  243. package/dist/lib/__tests__/worktree-paths.test.js.map +1 -1
  244. package/dist/lib/mode-state-io.d.ts +11 -0
  245. package/dist/lib/mode-state-io.d.ts.map +1 -1
  246. package/dist/lib/mode-state-io.js +64 -0
  247. package/dist/lib/mode-state-io.js.map +1 -1
  248. package/dist/lib/worktree-paths.d.ts +99 -9
  249. package/dist/lib/worktree-paths.d.ts.map +1 -1
  250. package/dist/lib/worktree-paths.js +284 -27
  251. package/dist/lib/worktree-paths.js.map +1 -1
  252. package/dist/planning/__tests__/artifacts.test.d.ts +2 -0
  253. package/dist/planning/__tests__/artifacts.test.d.ts.map +1 -0
  254. package/dist/planning/__tests__/artifacts.test.js +486 -0
  255. package/dist/planning/__tests__/artifacts.test.js.map +1 -0
  256. package/dist/planning/artifact-names.d.ts +13 -0
  257. package/dist/planning/artifact-names.d.ts.map +1 -0
  258. package/dist/planning/artifact-names.js +108 -0
  259. package/dist/planning/artifact-names.js.map +1 -0
  260. package/dist/planning/artifacts.d.ts +47 -0
  261. package/dist/planning/artifacts.d.ts.map +1 -0
  262. package/dist/planning/artifacts.js +221 -0
  263. package/dist/planning/artifacts.js.map +1 -0
  264. package/dist/team/__tests__/api-interop.compatibility.test.js +194 -5
  265. package/dist/team/__tests__/api-interop.compatibility.test.js.map +1 -1
  266. package/dist/team/__tests__/auto-merge-restart.test.d.ts +2 -0
  267. package/dist/team/__tests__/auto-merge-restart.test.d.ts.map +1 -0
  268. package/dist/team/__tests__/auto-merge-restart.test.js +128 -0
  269. package/dist/team/__tests__/auto-merge-restart.test.js.map +1 -0
  270. package/dist/team/__tests__/auto-merge.perf.test.d.ts +2 -0
  271. package/dist/team/__tests__/auto-merge.perf.test.d.ts.map +1 -0
  272. package/dist/team/__tests__/auto-merge.perf.test.js +85 -0
  273. package/dist/team/__tests__/auto-merge.perf.test.js.map +1 -0
  274. package/dist/team/__tests__/conflict-mailbox.test.d.ts +2 -0
  275. package/dist/team/__tests__/conflict-mailbox.test.d.ts.map +1 -0
  276. package/dist/team/__tests__/conflict-mailbox.test.js +238 -0
  277. package/dist/team/__tests__/conflict-mailbox.test.js.map +1 -0
  278. package/dist/team/__tests__/helpers/git-fixture.d.ts +72 -0
  279. package/dist/team/__tests__/helpers/git-fixture.d.ts.map +1 -0
  280. package/dist/team/__tests__/helpers/git-fixture.js +252 -0
  281. package/dist/team/__tests__/helpers/git-fixture.js.map +1 -0
  282. package/dist/team/__tests__/leader-inbox.test.d.ts +2 -0
  283. package/dist/team/__tests__/leader-inbox.test.d.ts.map +1 -0
  284. package/dist/team/__tests__/leader-inbox.test.js +152 -0
  285. package/dist/team/__tests__/leader-inbox.test.js.map +1 -0
  286. package/dist/team/__tests__/merge-orchestrator.test.d.ts +2 -0
  287. package/dist/team/__tests__/merge-orchestrator.test.d.ts.map +1 -0
  288. package/dist/team/__tests__/merge-orchestrator.test.js +779 -0
  289. package/dist/team/__tests__/merge-orchestrator.test.js.map +1 -0
  290. package/dist/team/__tests__/model-contract.test.js +92 -2
  291. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  292. package/dist/team/__tests__/rebase-smoke.test.d.ts +2 -0
  293. package/dist/team/__tests__/rebase-smoke.test.d.ts.map +1 -0
  294. package/dist/team/__tests__/rebase-smoke.test.js +272 -0
  295. package/dist/team/__tests__/rebase-smoke.test.js.map +1 -0
  296. package/dist/team/__tests__/runtime-cli.test.js +9 -1
  297. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  298. package/dist/team/__tests__/runtime-v2.dispatch.test.js +180 -1
  299. package/dist/team/__tests__/runtime-v2.dispatch.test.js.map +1 -1
  300. package/dist/team/__tests__/scaling.test.js +39 -1
  301. package/dist/team/__tests__/scaling.test.js.map +1 -1
  302. package/dist/team/__tests__/teardown-invariant.test.d.ts +2 -0
  303. package/dist/team/__tests__/teardown-invariant.test.d.ts.map +1 -0
  304. package/dist/team/__tests__/teardown-invariant.test.js +176 -0
  305. package/dist/team/__tests__/teardown-invariant.test.js.map +1 -0
  306. package/dist/team/__tests__/worker-bootstrap.test.js +6 -0
  307. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  308. package/dist/team/__tests__/worker-commit-cadence.test.d.ts +2 -0
  309. package/dist/team/__tests__/worker-commit-cadence.test.d.ts.map +1 -0
  310. package/dist/team/__tests__/worker-commit-cadence.test.js +315 -0
  311. package/dist/team/__tests__/worker-commit-cadence.test.js.map +1 -0
  312. package/dist/team/__tests__/worktree-runtime-e2e.test.d.ts +2 -0
  313. package/dist/team/__tests__/worktree-runtime-e2e.test.d.ts.map +1 -0
  314. package/dist/team/__tests__/worktree-runtime-e2e.test.js +182 -0
  315. package/dist/team/__tests__/worktree-runtime-e2e.test.js.map +1 -0
  316. package/dist/team/api-interop.d.ts.map +1 -1
  317. package/dist/team/api-interop.js +89 -2
  318. package/dist/team/api-interop.js.map +1 -1
  319. package/dist/team/conflict-mailbox.d.ts +49 -0
  320. package/dist/team/conflict-mailbox.d.ts.map +1 -0
  321. package/dist/team/conflict-mailbox.js +98 -0
  322. package/dist/team/conflict-mailbox.js.map +1 -0
  323. package/dist/team/delegation-evidence.d.ts +5 -0
  324. package/dist/team/delegation-evidence.d.ts.map +1 -0
  325. package/dist/team/delegation-evidence.js +29 -0
  326. package/dist/team/delegation-evidence.js.map +1 -0
  327. package/dist/team/leader-inbox.d.ts +28 -0
  328. package/dist/team/leader-inbox.d.ts.map +1 -0
  329. package/dist/team/leader-inbox.js +68 -0
  330. package/dist/team/leader-inbox.js.map +1 -0
  331. package/dist/team/merge-coordinator.d.ts +6 -0
  332. package/dist/team/merge-coordinator.d.ts.map +1 -1
  333. package/dist/team/merge-coordinator.js +6 -2
  334. package/dist/team/merge-coordinator.js.map +1 -1
  335. package/dist/team/merge-orchestrator.d.ts +41 -0
  336. package/dist/team/merge-orchestrator.d.ts.map +1 -0
  337. package/dist/team/merge-orchestrator.js +736 -0
  338. package/dist/team/merge-orchestrator.js.map +1 -0
  339. package/dist/team/model-contract.d.ts +8 -0
  340. package/dist/team/model-contract.d.ts.map +1 -1
  341. package/dist/team/model-contract.js +13 -0
  342. package/dist/team/model-contract.js.map +1 -1
  343. package/dist/team/runtime-cli.d.ts +1 -0
  344. package/dist/team/runtime-cli.d.ts.map +1 -1
  345. package/dist/team/runtime-cli.js +15 -2
  346. package/dist/team/runtime-cli.js.map +1 -1
  347. package/dist/team/runtime-flags.d.ts +5 -0
  348. package/dist/team/runtime-flags.d.ts.map +1 -0
  349. package/dist/team/runtime-flags.js +12 -0
  350. package/dist/team/runtime-flags.js.map +1 -0
  351. package/dist/team/runtime-v2.d.ts +9 -2
  352. package/dist/team/runtime-v2.d.ts.map +1 -1
  353. package/dist/team/runtime-v2.js +188 -9
  354. package/dist/team/runtime-v2.js.map +1 -1
  355. package/dist/team/state/tasks.d.ts +4 -1
  356. package/dist/team/state/tasks.d.ts.map +1 -1
  357. package/dist/team/state/tasks.js +38 -1
  358. package/dist/team/state/tasks.js.map +1 -1
  359. package/dist/team/team-ops.d.ts +4 -1
  360. package/dist/team/team-ops.d.ts.map +1 -1
  361. package/dist/team/team-ops.js +2 -2
  362. package/dist/team/team-ops.js.map +1 -1
  363. package/dist/team/types.d.ts +22 -1
  364. package/dist/team/types.d.ts.map +1 -1
  365. package/dist/team/types.js.map +1 -1
  366. package/dist/team/worker-bootstrap.d.ts +4 -0
  367. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  368. package/dist/team/worker-bootstrap.js +16 -2
  369. package/dist/team/worker-bootstrap.js.map +1 -1
  370. package/dist/team/worker-commit-cadence.d.ts +70 -0
  371. package/dist/team/worker-commit-cadence.d.ts.map +1 -0
  372. package/dist/team/worker-commit-cadence.js +271 -0
  373. package/dist/team/worker-commit-cadence.js.map +1 -0
  374. package/dist/tools/__tests__/memory-tools.test.js +4 -4
  375. package/dist/tools/__tests__/memory-tools.test.js.map +1 -1
  376. package/dist/tools/__tests__/state-tools.test.js +335 -6
  377. package/dist/tools/__tests__/state-tools.test.js.map +1 -1
  378. package/dist/tools/__tests__/wiki-tools-working-directory.test.d.ts +2 -0
  379. package/dist/tools/__tests__/wiki-tools-working-directory.test.d.ts.map +1 -0
  380. package/dist/tools/__tests__/wiki-tools-working-directory.test.js +66 -0
  381. package/dist/tools/__tests__/wiki-tools-working-directory.test.js.map +1 -0
  382. package/dist/tools/state-tools.d.ts +3 -10
  383. package/dist/tools/state-tools.d.ts.map +1 -1
  384. package/dist/tools/state-tools.js +393 -91
  385. package/dist/tools/state-tools.js.map +1 -1
  386. package/dist/tools/wiki-tools.js +8 -8
  387. package/dist/tools/wiki-tools.js.map +1 -1
  388. package/docs/GETTING-STARTED.md +422 -0
  389. package/docs/MIGRATION.md +66 -66
  390. package/docs/REFERENCE.md +21 -4
  391. package/docs/company-context-interface.md +5 -16
  392. package/docs/copilot-instructions.md +1 -1
  393. package/package.json +1 -1
  394. package/scripts/lib/hud-cache-wrapper.sh +122 -0
  395. package/scripts/lib/model-routing-override-message.mjs +21 -0
  396. package/scripts/persistent-mode.cjs +36 -0
  397. package/scripts/persistent-mode.mjs +36 -0
  398. package/scripts/plugin-setup.mjs +10 -1
  399. package/scripts/post-tool-verifier.mjs +119 -5
  400. package/scripts/pre-tool-enforcer.mjs +114 -6
  401. package/scripts/session-start.mjs +234 -3
  402. package/scripts/setup-copilot-instructions.sh +12 -2
  403. package/skills/AGENTS.md +22 -91
  404. package/skills/ai-slop-cleaner/SKILL.md +118 -120
  405. package/skills/ask/SKILL.md +6 -8
  406. package/skills/autopilot/SKILL.md +1 -28
  407. package/skills/autoresearch/SKILL.md +5 -5
  408. package/skills/cancel/SKILL.md +13 -13
  409. package/skills/cccg/SKILL.md +1 -0
  410. package/skills/configure-notifications/SKILL.md +77 -212
  411. package/skills/debug/SKILL.md +3 -2
  412. package/skills/deep-dive/SKILL.md +20 -9
  413. package/skills/deep-interview/SKILL.md +32 -4
  414. package/skills/deepinit/SKILL.md +1 -0
  415. package/skills/external-context/SKILL.md +2 -1
  416. package/skills/hud/SKILL.md +22 -52
  417. package/skills/learner/SKILL.md +90 -57
  418. package/skills/omc-doctor/SKILL.md +1 -1
  419. package/skills/omc-reference/SKILL.md +6 -6
  420. package/skills/omc-setup/SKILL.md +29 -73
  421. package/skills/omc-setup/phases/01-install-claude-md.md +97 -0
  422. package/skills/omc-setup/phases/02-configure.md +25 -40
  423. package/skills/omc-setup/phases/03-integrations.md +100 -69
  424. package/skills/omc-setup/phases/04-welcome.md +104 -46
  425. package/skills/omc-teams/SKILL.md +63 -26
  426. package/skills/plan/SKILL.md +11 -9
  427. package/skills/project-session-manager/SKILL.md +2 -2
  428. package/skills/project-session-manager/psm.sh +1 -1
  429. package/skills/ralph/SKILL.md +42 -41
  430. package/skills/ralplan/SKILL.md +3 -10
  431. package/skills/release/SKILL.md +168 -57
  432. package/skills/remember/SKILL.md +2 -1
  433. package/skills/sciomc/SKILL.md +12 -11
  434. package/skills/self-improve/SKILL.md +4 -4
  435. package/skills/setup/SKILL.md +1 -0
  436. package/skills/skill/SKILL.md +21 -20
  437. package/skills/skillify/SKILL.md +34 -3
  438. package/skills/team/SKILL.md +62 -44
  439. package/skills/ultraqa/SKILL.md +1 -2
  440. package/skills/ultrawork/SKILL.md +28 -21
  441. package/skills/verify/SKILL.md +2 -1
  442. package/skills/writer-memory/SKILL.md +2 -1
  443. package/templates/hooks/lib/model-routing-override-message.mjs +21 -0
  444. package/templates/hooks/pre-tool-use.mjs +144 -45
  445. package/templates/hooks/session-start.mjs +62 -0
@@ -207,7 +207,7 @@ function getDefaultShell() {
207
207
  return process.env.COMSPEC || "cmd.exe";
208
208
  }
209
209
  const shell = process.env.SHELL || "/bin/bash";
210
- const name = (0, import_path11.basename)(shell.replace(/\\/g, "/")).replace(/\.(exe|cmd|bat)$/i, "");
210
+ const name = (0, import_path10.basename)(shell.replace(/\\/g, "/")).replace(/\.(exe|cmd|bat)$/i, "");
211
211
  if (!SUPPORTED_POSIX_SHELLS.has(name)) {
212
212
  return "/bin/sh";
213
213
  }
@@ -217,7 +217,7 @@ function pathEntries(envPath) {
217
217
  return (envPath ?? "").split(process.platform === "win32" ? ";" : ":").map((entry) => entry.trim()).filter(Boolean);
218
218
  }
219
219
  function pathCandidateNames(candidatePath) {
220
- const base = (0, import_path11.basename)(candidatePath.replace(/\\/g, "/"));
220
+ const base = (0, import_path10.basename)(candidatePath.replace(/\\/g, "/"));
221
221
  const bare = base.replace(/\.(exe|cmd|bat)$/i, "");
222
222
  if (process.platform === "win32") {
223
223
  return Array.from(/* @__PURE__ */ new Set([`${bare}.exe`, `${bare}.cmd`, `${bare}.bat`, bare]));
@@ -227,7 +227,7 @@ function pathCandidateNames(candidatePath) {
227
227
  function resolveShellFromPath(candidatePath) {
228
228
  for (const dir of pathEntries(process.env.PATH)) {
229
229
  for (const name of pathCandidateNames(candidatePath)) {
230
- const full = (0, import_path11.join)(dir, name);
230
+ const full = (0, import_path10.join)(dir, name);
231
231
  if ((0, import_fs7.existsSync)(full)) return full;
232
232
  }
233
233
  }
@@ -243,7 +243,7 @@ function resolveShellFromCandidates(paths, rcFile) {
243
243
  }
244
244
  function resolveSupportedShellAffinity(shellPath) {
245
245
  if (!shellPath) return null;
246
- const name = (0, import_path11.basename)(shellPath.replace(/\\/g, "/")).replace(/\.(exe|cmd|bat)$/i, "");
246
+ const name = (0, import_path10.basename)(shellPath.replace(/\\/g, "/")).replace(/\.(exe|cmd|bat)$/i, "");
247
247
  if (name !== "zsh" && name !== "bash") return null;
248
248
  if (!(0, import_fs7.existsSync)(shellPath)) return null;
249
249
  const home = process.env.HOME ?? "";
@@ -269,7 +269,7 @@ function escapeForCmdSet(value) {
269
269
  return value.replace(/"/g, '""');
270
270
  }
271
271
  function shellNameFromPath(shellPath) {
272
- const shellName = (0, import_path11.basename)(shellPath.replace(/\\/g, "/"));
272
+ const shellName = (0, import_path10.basename)(shellPath.replace(/\\/g, "/"));
273
273
  return shellName.replace(/\.(exe|cmd|bat)$/i, "");
274
274
  }
275
275
  function shellEscape(value) {
@@ -281,7 +281,7 @@ function assertSafeEnvKey(key) {
281
281
  }
282
282
  }
283
283
  function isAbsoluteLaunchBinaryPath(value) {
284
- return (0, import_path11.isAbsolute)(value) || import_path11.win32.isAbsolute(value);
284
+ return (0, import_path10.isAbsolute)(value) || import_path10.win32.isAbsolute(value);
285
285
  }
286
286
  function assertSafeLaunchBinary(launchBinary) {
287
287
  if (launchBinary.trim().length === 0) {
@@ -794,9 +794,9 @@ async function isWorkerAlive(paneId) {
794
794
  async function killWorkerPanes(opts) {
795
795
  const { paneIds, leaderPaneId, teamName, cwd, graceMs = 1e4 } = opts;
796
796
  if (!paneIds.length) return;
797
- const shutdownPath = (0, import_path11.join)(cwd, ".omc", "state", "team", teamName, "shutdown.json");
797
+ const shutdownPath = (0, import_path10.join)(cwd, ".omc", "state", "team", teamName, "shutdown.json");
798
798
  try {
799
- await import_promises2.default.writeFile(shutdownPath, JSON.stringify({ requestedAt: Date.now() }));
799
+ await import_promises.default.writeFile(shutdownPath, JSON.stringify({ requestedAt: Date.now() }));
800
800
  const aliveChecks = await Promise.all(paneIds.map((id) => isWorkerAlive(id)));
801
801
  if (aliveChecks.some((alive) => alive)) {
802
802
  await sleep(graceMs);
@@ -873,13 +873,13 @@ async function killTeamSession(sessionName2, workerPaneIds, leaderPaneId, option
873
873
  } catch {
874
874
  }
875
875
  }
876
- var import_fs7, import_path11, import_promises2, sleep, TMUX_SESSION_PREFIX, SUPPORTED_POSIX_SHELLS, ZSH_CANDIDATES, BASH_CANDIDATES, DANGEROUS_LAUNCH_BINARY_CHARS;
876
+ var import_fs7, import_path10, import_promises, sleep, TMUX_SESSION_PREFIX, SUPPORTED_POSIX_SHELLS, ZSH_CANDIDATES, BASH_CANDIDATES, DANGEROUS_LAUNCH_BINARY_CHARS;
877
877
  var init_tmux_session = __esm({
878
878
  "src/team/tmux-session.ts"() {
879
879
  "use strict";
880
880
  import_fs7 = require("fs");
881
- import_path11 = require("path");
882
- import_promises2 = __toESM(require("fs/promises"), 1);
881
+ import_path10 = require("path");
882
+ import_promises = __toESM(require("fs/promises"), 1);
883
883
  init_team_name();
884
884
  init_tmux_utils();
885
885
  sleep = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -1133,6 +1133,7 @@ var init_file_lock = __esm({
1133
1133
  // src/team/runtime-cli.ts
1134
1134
  var runtime_cli_exports = {};
1135
1135
  __export(runtime_cli_exports, {
1136
+ assertAutoMergeRuntimeSupported: () => assertAutoMergeRuntimeSupported,
1136
1137
  buildCliOutput: () => buildCliOutput,
1137
1138
  buildTerminalCliResult: () => buildTerminalCliResult,
1138
1139
  checkWatchdogFailedMarker: () => checkWatchdogFailedMarker,
@@ -1140,9 +1141,9 @@ __export(runtime_cli_exports, {
1140
1141
  writeResultArtifact: () => writeResultArtifact
1141
1142
  });
1142
1143
  module.exports = __toCommonJS(runtime_cli_exports);
1143
- var import_fs19 = require("fs");
1144
- var import_promises8 = require("fs/promises");
1145
- var import_path21 = require("path");
1144
+ var import_fs21 = require("fs");
1145
+ var import_promises11 = require("fs/promises");
1146
+ var import_path23 = require("path");
1146
1147
 
1147
1148
  // src/team/runtime.ts
1148
1149
  var import_promises3 = require("fs/promises");
@@ -1417,6 +1418,8 @@ function validateAnthropicBaseUrl(urlString) {
1417
1418
  }
1418
1419
 
1419
1420
  // src/config/models.ts
1421
+ var DIRECT_MODEL_ENV_KEYS = ["CLAUDE_MODEL", "ANTHROPIC_MODEL"];
1422
+ var INHERIT_TIER_PRIORITY = ["MEDIUM", "HIGH", "LOW"];
1420
1423
  var TIER_ENV_KEYS = {
1421
1424
  LOW: [
1422
1425
  "OMC_MODEL_LOW",
@@ -1453,15 +1456,42 @@ var BUILTIN_EXTERNAL_MODEL_DEFAULTS = {
1453
1456
  codexModel: "gpt-5.3-codex",
1454
1457
  geminiModel: "gemini-3.1-pro-preview"
1455
1458
  };
1459
+ function readEnvValue(key) {
1460
+ const value = process.env[key]?.trim();
1461
+ return value || void 0;
1462
+ }
1456
1463
  function resolveTierModelFromEnv(tier) {
1457
1464
  for (const key of TIER_ENV_KEYS[tier]) {
1458
- const value = process.env[key]?.trim();
1465
+ const value = readEnvValue(key);
1466
+ if (value) {
1467
+ return value;
1468
+ }
1469
+ }
1470
+ return void 0;
1471
+ }
1472
+ function getDirectModelEnvValue() {
1473
+ for (const key of DIRECT_MODEL_ENV_KEYS) {
1474
+ const value = readEnvValue(key);
1459
1475
  if (value) {
1460
1476
  return value;
1461
1477
  }
1462
1478
  }
1463
1479
  return void 0;
1464
1480
  }
1481
+ function getProviderDetectionModelEnvValues() {
1482
+ const directModel = getDirectModelEnvValue();
1483
+ if (directModel) {
1484
+ return [directModel];
1485
+ }
1486
+ const values = /* @__PURE__ */ new Set();
1487
+ for (const tier of INHERIT_TIER_PRIORITY) {
1488
+ const value = resolveTierModelFromEnv(tier);
1489
+ if (value) {
1490
+ values.add(value);
1491
+ }
1492
+ }
1493
+ return [...values];
1494
+ }
1465
1495
  function getDefaultModelHigh() {
1466
1496
  return resolveTierModelFromEnv("HIGH") || BUILTIN_TIER_MODEL_DEFAULTS.HIGH;
1467
1497
  }
@@ -1494,17 +1524,19 @@ function isBedrock() {
1494
1524
  if (modelId && /^((us|eu|ap|global)\.anthropic\.|anthropic\.claude)/i.test(modelId)) {
1495
1525
  return true;
1496
1526
  }
1527
+ if (modelId && /^arn:aws(-[^:]+)?:bedrock:/i.test(modelId) && /:(inference-profile|application-inference-profile)\//i.test(modelId) && modelId.toLowerCase().includes("claude")) {
1528
+ return true;
1529
+ }
1497
1530
  return false;
1498
1531
  }
1499
1532
  function isVertexAI() {
1500
1533
  if (process.env.CLAUDE_CODE_USE_VERTEX === "1") {
1501
1534
  return true;
1502
1535
  }
1503
- const modelId = process.env.CLAUDE_MODEL || process.env.ANTHROPIC_MODEL || "";
1504
- if (modelId && modelId.toLowerCase().startsWith("vertex_ai/")) {
1505
- return true;
1506
- }
1507
- return false;
1536
+ return hasVertexModelId(getProviderDetectionModelEnvValues());
1537
+ }
1538
+ function hasVertexModelId(modelIds) {
1539
+ return modelIds.some((modelId) => modelId.toLowerCase().startsWith("vertex_ai/"));
1508
1540
  }
1509
1541
  function isProviderSpecificModelId(modelId) {
1510
1542
  return isBedrockModelId(modelId) || isVertexModelId(modelId);
@@ -2462,6 +2494,9 @@ var CC_FAMILY_TO_ALIAS = {
2462
2494
  HAIKU: "haiku"
2463
2495
  };
2464
2496
  function normalizeToCcAlias(model) {
2497
+ if (isProviderSpecificModelId(model)) {
2498
+ return model;
2499
+ }
2465
2500
  const family = resolveClaudeFamily(model);
2466
2501
  return family ? CC_FAMILY_TO_ALIAS[family] ?? model : model;
2467
2502
  }
@@ -2614,6 +2649,9 @@ function resolveCliBinaryPath(binary) {
2614
2649
  resolvedPathCache.set(binary, resolvedPath);
2615
2650
  return resolvedPath;
2616
2651
  }
2652
+ function shouldUseClaudeBareMode(env = process.env) {
2653
+ return typeof env.ANTHROPIC_API_KEY === "string" && env.ANTHROPIC_API_KEY.trim().length > 0;
2654
+ }
2617
2655
  var CONTRACTS = {
2618
2656
  claude: {
2619
2657
  agentType: "claude",
@@ -2621,6 +2659,9 @@ var CONTRACTS = {
2621
2659
  installInstructions: "Install Claude CLI: https://claude.ai/download",
2622
2660
  buildLaunchArgs(model, extraFlags = []) {
2623
2661
  const args = ["--dangerously-skip-permissions"];
2662
+ if (shouldUseClaudeBareMode() && !extraFlags.includes("--bare")) {
2663
+ args.push("--bare");
2664
+ }
2624
2665
  if (model) {
2625
2666
  const resolved = isProviderSpecificModelId(model) ? model : normalizeToCcAlias(model);
2626
2667
  args.push("--model", resolved);
@@ -2819,8 +2860,8 @@ function getPromptModeArgs(agentType, instruction) {
2819
2860
  init_team_name();
2820
2861
 
2821
2862
  // src/team/worker-bootstrap.ts
2822
- var import_promises = require("fs/promises");
2823
- var import_path10 = require("path");
2863
+ var import_promises2 = require("fs/promises");
2864
+ var import_path12 = require("path");
2824
2865
 
2825
2866
  // src/agents/prompt-helpers.ts
2826
2867
  var import_fs6 = require("fs");
@@ -2923,10 +2964,74 @@ function formatOmcCliInvocation(commandSuffix, options = {}) {
2923
2964
  return `${resolveInvocationPrefix(suffix, options)} ${suffix}`.trim();
2924
2965
  }
2925
2966
 
2967
+ // src/team/worker-bootstrap.ts
2968
+ init_tmux_session();
2969
+
2970
+ // src/team/fs-utils.ts
2971
+ var import_fs8 = require("fs");
2972
+ var import_path11 = require("path");
2973
+ function atomicWriteJson(filePath, data, mode = 384) {
2974
+ const dir = (0, import_path11.dirname)(filePath);
2975
+ if (!(0, import_fs8.existsSync)(dir)) (0, import_fs8.mkdirSync)(dir, { recursive: true, mode: 448 });
2976
+ const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
2977
+ const serialized = JSON.stringify(data, null, 2) + "\n";
2978
+ (0, import_fs8.writeFileSync)(tmpPath, serialized, { encoding: "utf-8", mode });
2979
+ try {
2980
+ (0, import_fs8.renameSync)(tmpPath, filePath);
2981
+ } catch (err) {
2982
+ if (process.platform === "win32" && err && typeof err === "object" && "code" in err && err.code === "EPERM") {
2983
+ try {
2984
+ (0, import_fs8.writeFileSync)(filePath, serialized, { encoding: "utf-8", mode });
2985
+ } finally {
2986
+ try {
2987
+ (0, import_fs8.unlinkSync)(tmpPath);
2988
+ } catch {
2989
+ }
2990
+ }
2991
+ } else {
2992
+ try {
2993
+ (0, import_fs8.unlinkSync)(tmpPath);
2994
+ } catch {
2995
+ }
2996
+ throw err;
2997
+ }
2998
+ }
2999
+ }
3000
+ function ensureDirWithMode(dirPath, mode = 448) {
3001
+ if (!(0, import_fs8.existsSync)(dirPath)) (0, import_fs8.mkdirSync)(dirPath, { recursive: true, mode });
3002
+ }
3003
+ function safeRealpath(p) {
3004
+ try {
3005
+ return (0, import_fs8.realpathSync)(p);
3006
+ } catch {
3007
+ const segments = [];
3008
+ let current = (0, import_path11.resolve)(p);
3009
+ while (!(0, import_fs8.existsSync)(current)) {
3010
+ segments.unshift((0, import_path11.basename)(current));
3011
+ const parent = (0, import_path11.dirname)(current);
3012
+ if (parent === current) break;
3013
+ current = parent;
3014
+ }
3015
+ try {
3016
+ return (0, import_path11.join)((0, import_fs8.realpathSync)(current), ...segments);
3017
+ } catch {
3018
+ return (0, import_path11.resolve)(p);
3019
+ }
3020
+ }
3021
+ }
3022
+ function validateResolvedPath(resolvedPath, expectedBase) {
3023
+ const absResolved = safeRealpath(resolvedPath);
3024
+ const absBase = safeRealpath(expectedBase);
3025
+ const rel = (0, import_path11.relative)(absBase, absResolved);
3026
+ if (rel.startsWith("..") || (0, import_path11.resolve)(absBase, rel) !== absResolved) {
3027
+ throw new Error(`Path traversal detected: "${resolvedPath}" escapes base "${expectedBase}"`);
3028
+ }
3029
+ }
3030
+
2926
3031
  // src/team/worker-bootstrap.ts
2927
3032
  var DEFAULT_INSTRUCTION_STATE_ROOT = ".omcp/state";
2928
3033
  function buildInstructionPath(...parts) {
2929
- return (0, import_path10.join)(...parts).replaceAll("\\", "/");
3034
+ return (0, import_path12.join)(...parts).replaceAll("\\", "/");
2930
3035
  }
2931
3036
  function buildTeamStateInstructionPath(teamName, instructionStateRoot, ...teamRelativeParts) {
2932
3037
  const baseParts = instructionStateRoot === DEFAULT_INSTRUCTION_STATE_ROOT ? [instructionStateRoot, "team", teamName] : [instructionStateRoot];
@@ -2995,7 +3100,7 @@ function generateWorkerOverlay(params) {
2995
3100
  const shutdownAckPath = buildTeamStateInstructionPath(teamName, instructionStateRoot, "workers", workerName2, "shutdown-ack.json");
2996
3101
  const claimTaskCommand = formatOmcCliInvocation(`team api claim-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"worker\\":\\"${workerName2}\\"}" --json`);
2997
3102
  const sendAckCommand = formatOmcCliInvocation(`team api send-message --input "{\\"team_name\\":\\"${teamName}\\",\\"from_worker\\":\\"${workerName2}\\",\\"to_worker\\":\\"leader-fixed\\",\\"body\\":\\"ACK: ${workerName2} initialized\\"}" --json`);
2998
- const completeTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"completed\\",\\"claim_token\\":\\"<claim_token>\\"}" --json`);
3103
+ const completeTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"completed\\",\\"claim_token\\":\\"<claim_token>\\",\\"result\\":\\"Summary: <what changed>\\\\nVerification: <tests/checks run>\\\\nSubagent skip reason: worker protocol forbids nested subagents; completed focused probe in-session\\"}" --json`);
2999
3104
  const failTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"failed\\",\\"claim_token\\":\\"<claim_token>\\"}" --json`);
3000
3105
  const readTaskCommand = formatOmcCliInvocation(`team api read-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\"}" --json`);
3001
3106
  const releaseClaimCommand = formatOmcCliInvocation(`team api release-task-claim --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"claim_token\\":\\"<claim_token>\\",\\"worker\\":\\"${workerName2}\\"}" --json`);
@@ -3048,6 +3153,11 @@ Use the CLI API for all task lifecycle operations. Do NOT directly edit task fil
3048
3153
  - Complete task: \`${completeTaskCommand}\`
3049
3154
  - Fail task: \`${failTaskCommand}\`
3050
3155
  - Release claim (rollback): \`${releaseClaimCommand}\`
3156
+ - Delegation compliance evidence (required for broad delegated tasks):
3157
+ - The completion command MUST include a \`result\` string with summary and verification evidence.
3158
+ - Because worker protocol forbids nested sub-agents, use: \`Subagent skip reason: <why in-session execution was safer/sufficient>\`
3159
+ - Only if the leader explicitly grants an exception to spawn nested help, use: \`Subagent spawn evidence: <count, child task names/thread ids, and integrated findings>\`
3160
+ - Completion is rejected with \`missing_delegation_compliance_evidence\` when required evidence is absent.
3051
3161
 
3052
3162
  ## Canonical Team State Root
3053
3163
  - Resolve the team state root in this order: \`OMC_TEAM_STATE_ROOT\` env -> worker identity \`team_state_root\` -> config/manifest \`team_state_root\` -> ${params.cwd}/.omcp/state/team/${teamName}.
@@ -3105,26 +3215,37 @@ ${bootstrapInstructions}
3105
3215
  ` : ""}`;
3106
3216
  }
3107
3217
  async function composeInitialInbox(teamName, workerName2, content, cwd, cliOutputContract) {
3108
- const inboxPath = (0, import_path10.join)(cwd, `.omcp/state/team/${teamName}/workers/${workerName2}/inbox.md`);
3109
- await (0, import_promises.mkdir)((0, import_path10.dirname)(inboxPath), { recursive: true });
3218
+ const inboxPath = (0, import_path12.join)(cwd, `.omcp/state/team/${teamName}/workers/${workerName2}/inbox.md`);
3219
+ await (0, import_promises2.mkdir)((0, import_path12.dirname)(inboxPath), { recursive: true });
3110
3220
  const finalContent = cliOutputContract && !content.includes(cliOutputContract) ? `${content}
3111
3221
  ${cliOutputContract}` : content;
3112
- await (0, import_promises.writeFile)(inboxPath, finalContent, "utf-8");
3222
+ await (0, import_promises2.writeFile)(inboxPath, finalContent, "utf-8");
3223
+ }
3224
+ async function appendToInbox(teamName, workerName2, message, cwd) {
3225
+ const safeTeam = sanitizeName(teamName);
3226
+ const safeWorker = sanitizeName(workerName2);
3227
+ const inboxPath = (0, import_path12.join)(cwd, `.omcp/state/team/${safeTeam}/workers/${safeWorker}/inbox.md`);
3228
+ validateResolvedPath(inboxPath, cwd);
3229
+ await (0, import_promises2.mkdir)((0, import_path12.dirname)(inboxPath), { recursive: true });
3230
+ await (0, import_promises2.appendFile)(inboxPath, `
3231
+
3232
+ ---
3233
+ ${message}`, "utf-8");
3113
3234
  }
3114
3235
  async function ensureWorkerStateDir(teamName, workerName2, cwd) {
3115
- const workerDir = (0, import_path10.join)(cwd, `.omcp/state/team/${teamName}/workers/${workerName2}`);
3116
- await (0, import_promises.mkdir)(workerDir, { recursive: true });
3117
- const mailboxDir = (0, import_path10.join)(cwd, `.omcp/state/team/${teamName}/mailbox`);
3118
- await (0, import_promises.mkdir)(mailboxDir, { recursive: true });
3119
- const tasksDir = (0, import_path10.join)(cwd, `.omcp/state/team/${teamName}/tasks`);
3120
- await (0, import_promises.mkdir)(tasksDir, { recursive: true });
3236
+ const workerDir = (0, import_path12.join)(cwd, `.omcp/state/team/${teamName}/workers/${workerName2}`);
3237
+ await (0, import_promises2.mkdir)(workerDir, { recursive: true });
3238
+ const mailboxDir = (0, import_path12.join)(cwd, `.omcp/state/team/${teamName}/mailbox`);
3239
+ await (0, import_promises2.mkdir)(mailboxDir, { recursive: true });
3240
+ const tasksDir = (0, import_path12.join)(cwd, `.omcp/state/team/${teamName}/tasks`);
3241
+ await (0, import_promises2.mkdir)(tasksDir, { recursive: true });
3121
3242
  }
3122
3243
  async function writeWorkerOverlay(params) {
3123
3244
  const { teamName, workerName: workerName2, cwd } = params;
3124
3245
  const overlay = generateWorkerOverlay(params);
3125
- const overlayPath = (0, import_path10.join)(cwd, `.omcp/state/team/${teamName}/workers/${workerName2}/AGENTS.md`);
3126
- await (0, import_promises.mkdir)((0, import_path10.dirname)(overlayPath), { recursive: true });
3127
- await (0, import_promises.writeFile)(overlayPath, overlay, "utf-8");
3246
+ const overlayPath = (0, import_path12.join)(cwd, `.omcp/state/team/${teamName}/workers/${workerName2}/AGENTS.md`);
3247
+ await (0, import_promises2.mkdir)((0, import_path12.dirname)(overlayPath), { recursive: true });
3248
+ await (0, import_promises2.writeFile)(overlayPath, overlay, "utf-8");
3128
3249
  return overlayPath;
3129
3250
  }
3130
3251
 
@@ -3141,69 +3262,6 @@ init_tmux_session();
3141
3262
  var import_node_fs = require("node:fs");
3142
3263
  var import_node_path = require("node:path");
3143
3264
  var import_node_child_process = require("node:child_process");
3144
-
3145
- // src/team/fs-utils.ts
3146
- var import_fs8 = require("fs");
3147
- var import_path12 = require("path");
3148
- function atomicWriteJson(filePath, data, mode = 384) {
3149
- const dir = (0, import_path12.dirname)(filePath);
3150
- if (!(0, import_fs8.existsSync)(dir)) (0, import_fs8.mkdirSync)(dir, { recursive: true, mode: 448 });
3151
- const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
3152
- const serialized = JSON.stringify(data, null, 2) + "\n";
3153
- (0, import_fs8.writeFileSync)(tmpPath, serialized, { encoding: "utf-8", mode });
3154
- try {
3155
- (0, import_fs8.renameSync)(tmpPath, filePath);
3156
- } catch (err) {
3157
- if (process.platform === "win32" && err && typeof err === "object" && "code" in err && err.code === "EPERM") {
3158
- try {
3159
- (0, import_fs8.writeFileSync)(filePath, serialized, { encoding: "utf-8", mode });
3160
- } finally {
3161
- try {
3162
- (0, import_fs8.unlinkSync)(tmpPath);
3163
- } catch {
3164
- }
3165
- }
3166
- } else {
3167
- try {
3168
- (0, import_fs8.unlinkSync)(tmpPath);
3169
- } catch {
3170
- }
3171
- throw err;
3172
- }
3173
- }
3174
- }
3175
- function ensureDirWithMode(dirPath, mode = 448) {
3176
- if (!(0, import_fs8.existsSync)(dirPath)) (0, import_fs8.mkdirSync)(dirPath, { recursive: true, mode });
3177
- }
3178
- function safeRealpath(p) {
3179
- try {
3180
- return (0, import_fs8.realpathSync)(p);
3181
- } catch {
3182
- const segments = [];
3183
- let current = (0, import_path12.resolve)(p);
3184
- while (!(0, import_fs8.existsSync)(current)) {
3185
- segments.unshift((0, import_path12.basename)(current));
3186
- const parent = (0, import_path12.dirname)(current);
3187
- if (parent === current) break;
3188
- current = parent;
3189
- }
3190
- try {
3191
- return (0, import_path12.join)((0, import_fs8.realpathSync)(current), ...segments);
3192
- } catch {
3193
- return (0, import_path12.resolve)(p);
3194
- }
3195
- }
3196
- }
3197
- function validateResolvedPath(resolvedPath, expectedBase) {
3198
- const absResolved = safeRealpath(resolvedPath);
3199
- const absBase = safeRealpath(expectedBase);
3200
- const rel = (0, import_path12.relative)(absBase, absResolved);
3201
- if (rel.startsWith("..") || (0, import_path12.resolve)(absBase, rel) !== absResolved) {
3202
- throw new Error(`Path traversal detected: "${resolvedPath}" escapes base "${expectedBase}"`);
3203
- }
3204
- }
3205
-
3206
- // src/team/git-worktree.ts
3207
3265
  init_tmux_session();
3208
3266
  init_file_lock();
3209
3267
  function getWorktreePath(repoRoot, teamName, workerName2) {
@@ -3587,6 +3645,9 @@ function removeWorkerWorktree(teamName, workerName2, repoRoot) {
3587
3645
  forgetMetadataUnlocked(repoRoot, teamName, workerName2);
3588
3646
  });
3589
3647
  }
3648
+ function listTeamWorktrees(teamName, repoRoot) {
3649
+ return readMetadata(repoRoot, teamName);
3650
+ }
3590
3651
  function inspectTeamWorktreeCleanupSafety(teamName, repoRoot) {
3591
3652
  const metadata = readMetadataResult(repoRoot, teamName);
3592
3653
  const entries = metadata.entries;
@@ -4157,8 +4218,8 @@ function watchdogCliWorkers(runtime, intervalMs) {
4157
4218
  unresponsiveCounts.delete(wName);
4158
4219
  await markTaskFromDone(root, runtime.teamName, runtime.cwd, signal.taskId || active.taskId, signal.status, signal.summary);
4159
4220
  try {
4160
- const { unlink: unlink3 } = await import("fs/promises");
4161
- await unlink3(donePath);
4221
+ const { unlink: unlink4 } = await import("fs/promises");
4222
+ await unlink4(donePath);
4162
4223
  } catch {
4163
4224
  }
4164
4225
  await killWorkerPane(runtime, wName, active.paneId);
@@ -5056,9 +5117,9 @@ async function waitForSentinelReadiness(options = {}) {
5056
5117
 
5057
5118
  // src/team/runtime-v2.ts
5058
5119
  init_tmux_utils();
5059
- var import_path20 = require("path");
5060
- var import_fs18 = require("fs");
5061
- var import_promises7 = require("fs/promises");
5120
+ var import_path22 = require("path");
5121
+ var import_fs20 = require("fs");
5122
+ var import_promises10 = require("fs/promises");
5062
5123
  var import_perf_hooks = require("perf_hooks");
5063
5124
 
5064
5125
  // src/team/allocation-policy.ts
@@ -5271,10 +5332,10 @@ async function readJsonSafe2(filePath) {
5271
5332
  }
5272
5333
  }
5273
5334
  async function writeAtomic(filePath, data) {
5274
- const { writeFile: writeFile6 } = await import("fs/promises");
5335
+ const { writeFile: writeFile8 } = await import("fs/promises");
5275
5336
  await (0, import_promises5.mkdir)((0, import_path18.dirname)(filePath), { recursive: true });
5276
5337
  const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
5277
- await writeFile6(tmpPath, data, "utf-8");
5338
+ await writeFile8(tmpPath, data, "utf-8");
5278
5339
  const { rename: rename4 } = await import("fs/promises");
5279
5340
  await rename4(tmpPath, filePath);
5280
5341
  }
@@ -6198,13 +6259,996 @@ function cliWorkerOutputFilePath(teamStateRootAbs, workerName2) {
6198
6259
  return `${teamStateRootAbs.replaceAll("\\", "/")}/workers/${workerName2}/verdict.json`;
6199
6260
  }
6200
6261
 
6201
- // src/team/runtime-v2.ts
6262
+ // src/team/merge-orchestrator.ts
6263
+ var import_node_child_process3 = require("node:child_process");
6264
+ var import_node_fs2 = require("node:fs");
6265
+ var import_promises9 = require("node:fs/promises");
6266
+ var import_node_path2 = require("node:path");
6267
+
6268
+ // src/team/runtime-flags.ts
6202
6269
  function isRuntimeV2Enabled(env = process.env) {
6203
6270
  const raw = env.OMC_RUNTIME_V2;
6204
6271
  if (!raw) return true;
6205
6272
  const normalized = raw.trim().toLowerCase();
6206
6273
  return !["0", "false", "no", "off"].includes(normalized);
6207
6274
  }
6275
+
6276
+ // src/team/merge-orchestrator.ts
6277
+ init_tmux_session();
6278
+
6279
+ // src/team/merge-coordinator.ts
6280
+ var import_node_child_process2 = require("node:child_process");
6281
+ var BRANCH_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9/_.-]*$/;
6282
+ function validateBranchName(branch) {
6283
+ if (!BRANCH_NAME_RE.test(branch)) {
6284
+ throw new Error(`Invalid branch name: "${branch}" \u2014 must match ${BRANCH_NAME_RE}`);
6285
+ }
6286
+ }
6287
+ function checkMergeConflicts(workerBranch, baseBranch, repoRoot) {
6288
+ validateBranchName(workerBranch);
6289
+ validateBranchName(baseBranch);
6290
+ try {
6291
+ (0, import_node_child_process2.execFileSync)(
6292
+ "git",
6293
+ ["merge-tree", "--write-tree", baseBranch, workerBranch],
6294
+ { cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6295
+ );
6296
+ return [];
6297
+ } catch (err) {
6298
+ const error = err;
6299
+ if (error.status === 1 && typeof error.stdout === "string") {
6300
+ const lines = error.stdout.split("\n");
6301
+ const conflicts = [];
6302
+ for (const line of lines) {
6303
+ const match = line.match(/^CONFLICT\s.*?:\s+.*?\s+in\s+(.+)$/);
6304
+ if (match) {
6305
+ conflicts.push(match[1].trim());
6306
+ }
6307
+ }
6308
+ return conflicts.length > 0 ? conflicts : ["(merge-tree reported conflicts)"];
6309
+ }
6310
+ }
6311
+ const mergeBase = (0, import_node_child_process2.execFileSync)(
6312
+ "git",
6313
+ ["merge-base", baseBranch, workerBranch],
6314
+ { cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6315
+ ).trim();
6316
+ const baseDiff = (0, import_node_child_process2.execFileSync)(
6317
+ "git",
6318
+ ["diff", "--name-only", mergeBase, baseBranch],
6319
+ { cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6320
+ ).trim();
6321
+ const workerDiff = (0, import_node_child_process2.execFileSync)(
6322
+ "git",
6323
+ ["diff", "--name-only", mergeBase, workerBranch],
6324
+ { cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6325
+ ).trim();
6326
+ if (!baseDiff || !workerDiff) {
6327
+ return [];
6328
+ }
6329
+ const baseFiles = new Set(baseDiff.split("\n").filter((f) => f));
6330
+ const workerFiles = workerDiff.split("\n").filter((f) => f);
6331
+ return workerFiles.filter((f) => baseFiles.has(f));
6332
+ }
6333
+ function mergeWorkerBranch(workerBranch, baseBranch, repoRoot) {
6334
+ validateBranchName(workerBranch);
6335
+ validateBranchName(baseBranch);
6336
+ const workerName2 = workerBranch.split("/").pop() || workerBranch;
6337
+ try {
6338
+ try {
6339
+ (0, import_node_child_process2.execFileSync)("git", ["diff-index", "--quiet", "HEAD", "--"], {
6340
+ cwd: repoRoot,
6341
+ stdio: "pipe"
6342
+ });
6343
+ } catch {
6344
+ throw new Error("Working tree has uncommitted changes \u2014 commit or stash before merging");
6345
+ }
6346
+ (0, import_node_child_process2.execFileSync)("git", ["checkout", baseBranch], {
6347
+ cwd: repoRoot,
6348
+ stdio: "pipe"
6349
+ });
6350
+ (0, import_node_child_process2.execFileSync)("git", ["merge", "--no-ff", "-m", `Merge ${workerBranch} into ${baseBranch}`, workerBranch], {
6351
+ cwd: repoRoot,
6352
+ stdio: "pipe"
6353
+ });
6354
+ const mergeCommit = (0, import_node_child_process2.execFileSync)("git", ["rev-parse", "HEAD"], {
6355
+ cwd: repoRoot,
6356
+ encoding: "utf-8",
6357
+ stdio: "pipe"
6358
+ }).trim();
6359
+ return {
6360
+ workerName: workerName2,
6361
+ branch: workerBranch,
6362
+ success: true,
6363
+ conflicts: [],
6364
+ mergeCommit
6365
+ };
6366
+ } catch (_err) {
6367
+ try {
6368
+ (0, import_node_child_process2.execFileSync)("git", ["merge", "--abort"], { cwd: repoRoot, stdio: "pipe" });
6369
+ } catch {
6370
+ }
6371
+ const conflicts = checkMergeConflicts(workerBranch, baseBranch, repoRoot);
6372
+ return {
6373
+ workerName: workerName2,
6374
+ branch: workerBranch,
6375
+ success: false,
6376
+ conflicts
6377
+ };
6378
+ }
6379
+ }
6380
+
6381
+ // src/team/leader-inbox.ts
6382
+ var import_promises7 = require("fs/promises");
6383
+ var import_fs18 = require("fs");
6384
+ var import_path20 = require("path");
6385
+ init_tmux_session();
6386
+ var LEADER_INBOX_HEADER = `# Leader Inbox
6387
+
6388
+ Runtime notifications (merge conflicts, rebase events, etc.) appear here.
6389
+ Check this file periodically and after long-running operations.
6390
+
6391
+ ---
6392
+ `;
6393
+ function leaderInboxPath(teamName, cwd) {
6394
+ const safe = sanitizeName(teamName);
6395
+ return (0, import_path20.join)(cwd, `.omcp/state/team/${safe}/leader/inbox.md`);
6396
+ }
6397
+ async function ensureLeaderInbox(teamName, cwd) {
6398
+ const inboxPath = leaderInboxPath(teamName, cwd);
6399
+ validateResolvedPath(inboxPath, cwd);
6400
+ await (0, import_promises7.mkdir)((0, import_path20.dirname)(inboxPath), { recursive: true });
6401
+ if (!(0, import_fs18.existsSync)(inboxPath)) {
6402
+ await (0, import_promises7.writeFile)(inboxPath, LEADER_INBOX_HEADER, "utf-8");
6403
+ }
6404
+ return inboxPath;
6405
+ }
6406
+ async function appendToLeaderInbox(teamName, message, cwd) {
6407
+ const inboxPath = leaderInboxPath(teamName, cwd);
6408
+ validateResolvedPath(inboxPath, cwd);
6409
+ await (0, import_promises7.mkdir)((0, import_path20.dirname)(inboxPath), { recursive: true });
6410
+ await (0, import_promises7.appendFile)(inboxPath, `
6411
+
6412
+ ---
6413
+ ${message}`, "utf-8");
6414
+ }
6415
+ function extendLeaderBootstrapPrompt(teamName) {
6416
+ const safe = sanitizeName(teamName);
6417
+ const path4 = `.omcp/state/team/${safe}/leader/inbox.md`;
6418
+ return `Runtime notifications appear at ${path4} \u2014 check this file periodically and after long-running operations.`;
6419
+ }
6420
+
6421
+ // src/team/conflict-mailbox.ts
6422
+ function sanitizeConflictPath(path4) {
6423
+ return path4.replace(/[`\r\n]/g, "?");
6424
+ }
6425
+ function formatMergeConflictForLeader(args) {
6426
+ const { workerName: workerName2, workerBranch, leaderBranch, conflictingFiles, mergeBaseSha, observedAt } = args;
6427
+ const ts = new Date(observedAt).toISOString();
6428
+ const safeFiles = conflictingFiles.map(sanitizeConflictPath);
6429
+ const fileList = safeFiles.map((f) => `- \`${f}\``).join("\n");
6430
+ return `### Merge conflict: ${workerName2} \u2192 ${leaderBranch}
6431
+
6432
+ **Worker branch:** \`${workerBranch}\`
6433
+ **Leader branch:** \`${leaderBranch}\`
6434
+ **Merge base:** \`${mergeBaseSha}\`
6435
+ **Observed at:** ${ts}
6436
+
6437
+ **Conflicting files:**
6438
+ ${fileList}
6439
+
6440
+ **Leader: choose strategy.** To resolve, run:
6441
+
6442
+ \`\`\`sh
6443
+ git checkout ${leaderBranch} && git merge --no-ff ${workerBranch}
6444
+ # resolve conflicts in the files listed above
6445
+ git add ${safeFiles.join(" ")}
6446
+ git commit
6447
+ \`\`\`
6448
+
6449
+ Or abort with \`git merge --abort\` to defer resolution.`;
6450
+ }
6451
+ function formatRebaseConflictForWorker(args) {
6452
+ const { workerName: workerName2, workerBranch, leaderBranch, conflictingFiles, baseSha, worktreePath, observedAt } = args;
6453
+ const ts = new Date(observedAt).toISOString();
6454
+ const safeFiles = conflictingFiles.map(sanitizeConflictPath);
6455
+ const fileList = safeFiles.map((f) => `- \`${f}\``).join("\n");
6456
+ return `### Rebase conflict: ${workerName2} onto ${leaderBranch}
6457
+
6458
+ **Worker branch:** \`${workerBranch}\`
6459
+ **Base branch:** \`${leaderBranch}\`
6460
+ **Base SHA:** \`${baseSha}\`
6461
+ **Worktree:** \`${worktreePath}\`
6462
+ **Observed at:** ${ts}
6463
+
6464
+ **Conflicting files:**
6465
+ ${fileList}
6466
+
6467
+ Resolve conflicts in your own pane, then \`git add <files>\` and \`git rebase --continue\`.
6468
+ Cadence stays paused until \`.git/rebase-merge\` is gone.
6469
+
6470
+ Or run \`git rebase --abort\` to bail and return to the pre-rebase state.`;
6471
+ }
6472
+
6473
+ // src/team/worker-commit-cadence.ts
6474
+ var import_fs19 = require("fs");
6475
+ var import_promises8 = require("fs/promises");
6476
+ var import_path21 = require("path");
6477
+ var import_child_process5 = require("child_process");
6478
+ var SENTINEL_FILENAME = ".hook-paused";
6479
+ var HOOK_MATCHER = "Write|Edit|MultiEdit";
6480
+ var DEFAULT_POLL_DEBOUNCE_MS = 3e3;
6481
+ var WORKER_NAME_RE = /^[A-Za-z0-9_-]{1,50}$/;
6482
+ function assertSafeWorkerName(workerName2) {
6483
+ if (!WORKER_NAME_RE.test(workerName2)) {
6484
+ throw new Error(
6485
+ `Invalid worker name for shell hook: "${workerName2}" \u2014 must match ${WORKER_NAME_RE}`
6486
+ );
6487
+ }
6488
+ }
6489
+ function buildHookCommand(workerName2) {
6490
+ assertSafeWorkerName(workerName2);
6491
+ return `sh -c 'rebase_dir=$(git rev-parse --git-path rebase-merge 2>/dev/null || printf %s .git/rebase-merge); merge_head=$(git rev-parse --git-path MERGE_HEAD 2>/dev/null || printf %s .git/MERGE_HEAD); if [ -d "$rebase_dir" ] || [ -f "$merge_head" ] || [ -e ${SENTINEL_FILENAME} ]; then exit 0; fi; git add -A && (git diff --cached --quiet || git commit -m "auto-commit by worker ${workerName2} at $(date -Iseconds)")'`;
6492
+ }
6493
+ async function mergeSettingsWithHook(settingsPath, hookCommand) {
6494
+ let existing = { hooks: { PostToolUse: [] } };
6495
+ try {
6496
+ const raw = await (0, import_promises8.readFile)(settingsPath, "utf-8");
6497
+ const parsed = JSON.parse(raw);
6498
+ existing = {
6499
+ ...parsed,
6500
+ hooks: {
6501
+ PostToolUse: [],
6502
+ ...parsed.hooks ?? {}
6503
+ }
6504
+ };
6505
+ } catch {
6506
+ }
6507
+ const filteredHooks = (existing.hooks.PostToolUse ?? []).filter(
6508
+ (h) => h.matcher !== HOOK_MATCHER
6509
+ );
6510
+ const newEntry = {
6511
+ matcher: HOOK_MATCHER,
6512
+ hooks: [{ type: "command", command: hookCommand }]
6513
+ };
6514
+ return {
6515
+ ...existing,
6516
+ hooks: {
6517
+ ...existing.hooks,
6518
+ PostToolUse: [...filteredHooks, newEntry]
6519
+ }
6520
+ };
6521
+ }
6522
+ async function installPostToolUseHook(worktreePath, workerName2) {
6523
+ assertSafeWorkerName(workerName2);
6524
+ if (isHookPaused(worktreePath)) {
6525
+ return;
6526
+ }
6527
+ const claudeDir = (0, import_path21.join)(worktreePath, ".claude");
6528
+ await (0, import_promises8.mkdir)(claudeDir, { recursive: true });
6529
+ const settingsPath = (0, import_path21.join)(claudeDir, "settings.json");
6530
+ const hookCommand = buildHookCommand(workerName2);
6531
+ const merged = await mergeSettingsWithHook(settingsPath, hookCommand);
6532
+ await (0, import_promises8.writeFile)(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
6533
+ }
6534
+ async function pauseHookViaSentinel(worktreePath) {
6535
+ const sentinelPath = (0, import_path21.join)(worktreePath, SENTINEL_FILENAME);
6536
+ await (0, import_promises8.mkdir)((0, import_path21.dirname)(sentinelPath), { recursive: true });
6537
+ await (0, import_promises8.writeFile)(sentinelPath, "", "utf-8");
6538
+ }
6539
+ async function resumeHookViaSentinel(worktreePath) {
6540
+ const sentinelPath = (0, import_path21.join)(worktreePath, SENTINEL_FILENAME);
6541
+ try {
6542
+ await (0, import_promises8.unlink)(sentinelPath);
6543
+ } catch {
6544
+ }
6545
+ }
6546
+ function isHookPaused(worktreePath) {
6547
+ return (0, import_fs19.existsSync)((0, import_path21.join)(worktreePath, SENTINEL_FILENAME));
6548
+ }
6549
+ function startFallbackPoller(worktreePath, workerName2, opts) {
6550
+ assertSafeWorkerName(workerName2);
6551
+ const debounceMs = opts?.intervalMs ?? DEFAULT_POLL_DEBOUNCE_MS;
6552
+ let debounceTimer = null;
6553
+ let stopped = false;
6554
+ const runAutoCommit = () => {
6555
+ if (stopped) return;
6556
+ if (isHookPaused(worktreePath)) return;
6557
+ const cmd = buildHookCommand(workerName2);
6558
+ (0, import_child_process5.exec)(cmd, { cwd: worktreePath }, (_err) => {
6559
+ });
6560
+ };
6561
+ const scheduleDebounce = () => {
6562
+ if (stopped) return;
6563
+ if (debounceTimer !== null) {
6564
+ clearTimeout(debounceTimer);
6565
+ }
6566
+ debounceTimer = setTimeout(() => {
6567
+ debounceTimer = null;
6568
+ runAutoCommit();
6569
+ }, debounceMs);
6570
+ };
6571
+ const watcher = (0, import_fs19.watch)(worktreePath, { recursive: true }, (eventType, filename) => {
6572
+ if (stopped) return;
6573
+ if (filename && (filename.startsWith(".git") || filename.startsWith(".git/"))) return;
6574
+ scheduleDebounce();
6575
+ });
6576
+ return {
6577
+ stop() {
6578
+ stopped = true;
6579
+ if (debounceTimer !== null) {
6580
+ clearTimeout(debounceTimer);
6581
+ debounceTimer = null;
6582
+ }
6583
+ watcher.close();
6584
+ }
6585
+ };
6586
+ }
6587
+ async function installCommitCadence(ctx) {
6588
+ if (!ctx.enabled) {
6589
+ return { method: "none" };
6590
+ }
6591
+ if (ctx.agentType === "claude") {
6592
+ await installPostToolUseHook(ctx.worktreePath, ctx.workerName);
6593
+ return { method: "hook" };
6594
+ }
6595
+ return { method: "fallback-poll" };
6596
+ }
6597
+ async function uninstallCommitCadence(ctx) {
6598
+ if (ctx.agentType !== "claude") return;
6599
+ const settingsPath = (0, import_path21.join)(ctx.worktreePath, ".claude", "settings.json");
6600
+ try {
6601
+ const raw = await (0, import_promises8.readFile)(settingsPath, "utf-8");
6602
+ const parsed = JSON.parse(raw);
6603
+ const filtered = (parsed.hooks?.PostToolUse ?? []).filter(
6604
+ (h) => h.matcher !== HOOK_MATCHER
6605
+ );
6606
+ const updated = {
6607
+ ...parsed,
6608
+ hooks: {
6609
+ ...parsed.hooks,
6610
+ PostToolUse: filtered
6611
+ }
6612
+ };
6613
+ await (0, import_promises8.writeFile)(settingsPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
6614
+ } catch {
6615
+ }
6616
+ }
6617
+
6618
+ // src/team/merge-orchestrator.ts
6619
+ var DEFAULT_POLL_INTERVAL_MS = 1e3;
6620
+ var DEFAULT_DRAIN_TIMEOUT_MS = 1e4;
6621
+ function mergerWorktreePathFor(repoRoot, teamName) {
6622
+ return (0, import_node_path2.join)(repoRoot, ".omcp", "team", sanitizeName(teamName), "merger");
6623
+ }
6624
+ function persistedStatePath(repoRoot, teamName) {
6625
+ return (0, import_node_path2.join)(
6626
+ repoRoot,
6627
+ ".omcp",
6628
+ "state",
6629
+ "team",
6630
+ sanitizeName(teamName),
6631
+ "auto-merge-state.json"
6632
+ );
6633
+ }
6634
+ function teardownAuditPath(repoRoot, teamName) {
6635
+ return (0, import_node_path2.join)(
6636
+ repoRoot,
6637
+ ".omcp",
6638
+ "state",
6639
+ "team",
6640
+ sanitizeName(teamName),
6641
+ "teardown-audit.jsonl"
6642
+ );
6643
+ }
6644
+ function orchestratorEventLogPath(repoRoot, teamName) {
6645
+ return (0, import_node_path2.join)(
6646
+ repoRoot,
6647
+ ".omcp",
6648
+ "state",
6649
+ "team",
6650
+ sanitizeName(teamName),
6651
+ "orchestrator-events.jsonl"
6652
+ );
6653
+ }
6654
+ function assertLeaderBranchAllowed(leaderBranch) {
6655
+ const stripped = leaderBranch.replace(/^refs\/heads\//i, "").toLowerCase();
6656
+ if (stripped === "main" || stripped === "master") {
6657
+ throw new Error("auto-merge refuses main/master leader branch \u2014 use a feature branch");
6658
+ }
6659
+ }
6660
+ function assertRuntimeV2Gate() {
6661
+ if (!isRuntimeV2Enabled()) {
6662
+ throw new Error("auto-merge requires runtime v2 (OMC_RUNTIME_V2 is explicitly disabled).");
6663
+ }
6664
+ }
6665
+ async function appendEvent(repoRoot, teamName, event) {
6666
+ const path4 = orchestratorEventLogPath(repoRoot, teamName);
6667
+ await (0, import_promises9.mkdir)((0, import_node_path2.dirname)(path4), { recursive: true });
6668
+ const full = {
6669
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
6670
+ team: teamName,
6671
+ ...event
6672
+ };
6673
+ await (0, import_promises9.appendFile)(path4, `${JSON.stringify(full)}
6674
+ `, "utf-8");
6675
+ }
6676
+ function createMutex() {
6677
+ let lock = Promise.resolve();
6678
+ return (fn) => {
6679
+ const next = lock.then(fn, fn);
6680
+ lock = next.catch(() => void 0);
6681
+ return next;
6682
+ };
6683
+ }
6684
+ function gitRevParseHead(repoRoot, branch) {
6685
+ return (0, import_node_child_process3.execFileSync)("git", ["rev-parse", `refs/heads/${branch}`], {
6686
+ cwd: repoRoot,
6687
+ encoding: "utf-8",
6688
+ stdio: "pipe"
6689
+ }).trim();
6690
+ }
6691
+ function gitPath(worktreePath, gitPathName) {
6692
+ try {
6693
+ const resolved = (0, import_node_child_process3.execFileSync)("git", ["rev-parse", "--git-path", gitPathName], {
6694
+ cwd: worktreePath,
6695
+ encoding: "utf-8",
6696
+ stdio: "pipe"
6697
+ }).trim();
6698
+ if (resolved) return resolved;
6699
+ } catch {
6700
+ }
6701
+ return (0, import_node_path2.join)(worktreePath, ".git", gitPathName);
6702
+ }
6703
+ function isRebaseInProgress(worktreePath) {
6704
+ return (0, import_node_fs2.existsSync)(gitPath(worktreePath, "rebase-merge"));
6705
+ }
6706
+ function isWorktreeRegistered(repoRoot, wtPath) {
6707
+ try {
6708
+ const out = (0, import_node_child_process3.execFileSync)("git", ["worktree", "list", "--porcelain"], {
6709
+ cwd: repoRoot,
6710
+ encoding: "utf-8",
6711
+ stdio: "pipe"
6712
+ });
6713
+ for (const line of out.split("\n")) {
6714
+ if (line.startsWith("worktree ")) {
6715
+ if (line.slice("worktree ".length).trim() === wtPath) return true;
6716
+ }
6717
+ }
6718
+ } catch {
6719
+ }
6720
+ return false;
6721
+ }
6722
+ function ensureMergerWorktree(repoRoot, mergerPath, leaderBranch) {
6723
+ ensureDirWithMode((0, import_node_path2.dirname)(mergerPath));
6724
+ if ((0, import_node_fs2.existsSync)(mergerPath) && isWorktreeRegistered(repoRoot, mergerPath)) {
6725
+ return;
6726
+ }
6727
+ (0, import_node_child_process3.execFileSync)("git", ["worktree", "add", "--force", mergerPath, leaderBranch], {
6728
+ cwd: repoRoot,
6729
+ stdio: "pipe"
6730
+ });
6731
+ }
6732
+ function preflightMergerWorktree(mergerPath, leaderBranch) {
6733
+ try {
6734
+ (0, import_node_child_process3.execFileSync)("git", ["fetch", "--no-tags", "origin", leaderBranch], {
6735
+ cwd: mergerPath,
6736
+ stdio: "pipe"
6737
+ });
6738
+ } catch {
6739
+ }
6740
+ (0, import_node_child_process3.execFileSync)("git", ["reset", "--hard", leaderBranch], {
6741
+ cwd: mergerPath,
6742
+ stdio: "pipe"
6743
+ });
6744
+ }
6745
+ function parseUUFiles(porcelainOutput) {
6746
+ const files = [];
6747
+ for (const line of porcelainOutput.split("\n")) {
6748
+ if (line.startsWith("UU ")) {
6749
+ files.push(line.slice(3).trim());
6750
+ } else if (line.startsWith("AA ") || line.startsWith("DD ")) {
6751
+ files.push(line.slice(3).trim());
6752
+ }
6753
+ }
6754
+ return files;
6755
+ }
6756
+ async function startMergeOrchestrator(config) {
6757
+ assertRuntimeV2Gate();
6758
+ assertLeaderBranchAllowed(config.leaderBranch);
6759
+ validateBranchName(config.leaderBranch);
6760
+ const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
6761
+ const drainTimeoutMs = config.drainTimeoutMs ?? DEFAULT_DRAIN_TIMEOUT_MS;
6762
+ const mergerPath = mergerWorktreePathFor(config.repoRoot, config.teamName);
6763
+ validateResolvedPath(mergerPath, config.repoRoot);
6764
+ ensureMergerWorktree(config.repoRoot, mergerPath, config.leaderBranch);
6765
+ await ensureLeaderInbox(config.teamName, config.cwd);
6766
+ const persistedPath = persistedStatePath(config.repoRoot, config.teamName);
6767
+ let persisted = { lastShas: {} };
6768
+ if ((0, import_node_fs2.existsSync)(persistedPath)) {
6769
+ try {
6770
+ const { readFileSync: readFileSync12 } = await import("node:fs");
6771
+ persisted = JSON.parse(readFileSync12(persistedPath, "utf-8"));
6772
+ } catch {
6773
+ persisted = { lastShas: {} };
6774
+ }
6775
+ }
6776
+ const workers = /* @__PURE__ */ new Map();
6777
+ const pausedWorkers = /* @__PURE__ */ new Set();
6778
+ const mutex = createMutex();
6779
+ let stopped = false;
6780
+ function persistState() {
6781
+ const payload = {
6782
+ lastShas: Object.fromEntries(
6783
+ Array.from(workers.values()).map((w) => [w.workerName, w.lastObservedSha])
6784
+ )
6785
+ };
6786
+ atomicWriteJson(persistedPath, payload);
6787
+ }
6788
+ async function fanOutRebase(triggeringWorker) {
6789
+ for (const other of workers.values()) {
6790
+ if (other.workerName === triggeringWorker) continue;
6791
+ const wtPath = other.workerWorktreePath;
6792
+ if (isRebaseInProgress(wtPath)) {
6793
+ await appendEvent(config.repoRoot, config.teamName, {
6794
+ type: "rebase_skipped_in_progress",
6795
+ worker: other.workerName,
6796
+ reason: "rebase-already-in-progress"
6797
+ });
6798
+ continue;
6799
+ }
6800
+ await appendEvent(config.repoRoot, config.teamName, {
6801
+ type: "rebase_triggered",
6802
+ worker: other.workerName
6803
+ });
6804
+ await pauseHookViaSentinel(wtPath);
6805
+ pausedWorkers.add(other.workerName);
6806
+ try {
6807
+ (0, import_node_child_process3.execFileSync)("git", ["fetch", "--no-tags", "origin", config.leaderBranch], {
6808
+ cwd: wtPath,
6809
+ stdio: "pipe"
6810
+ });
6811
+ } catch {
6812
+ }
6813
+ try {
6814
+ (0, import_node_child_process3.execFileSync)("git", ["rebase", config.leaderBranch], {
6815
+ cwd: wtPath,
6816
+ stdio: "pipe"
6817
+ });
6818
+ await resumeHookViaSentinel(wtPath);
6819
+ pausedWorkers.delete(other.workerName);
6820
+ await appendEvent(config.repoRoot, config.teamName, {
6821
+ type: "rebase_succeeded",
6822
+ worker: other.workerName
6823
+ });
6824
+ } catch {
6825
+ let conflictingFiles = [];
6826
+ try {
6827
+ const status = (0, import_node_child_process3.execFileSync)("git", ["status", "--porcelain"], {
6828
+ cwd: wtPath,
6829
+ encoding: "utf-8",
6830
+ stdio: "pipe"
6831
+ });
6832
+ conflictingFiles = parseUUFiles(status);
6833
+ } catch {
6834
+ conflictingFiles = ["(rebase status unavailable)"];
6835
+ }
6836
+ const baseSha = (() => {
6837
+ try {
6838
+ return (0, import_node_child_process3.execFileSync)("git", ["rev-parse", `refs/heads/${config.leaderBranch}`], {
6839
+ cwd: config.repoRoot,
6840
+ encoding: "utf-8",
6841
+ stdio: "pipe"
6842
+ }).trim();
6843
+ } catch {
6844
+ return "unknown";
6845
+ }
6846
+ })();
6847
+ const message = formatRebaseConflictForWorker({
6848
+ workerName: other.workerName,
6849
+ workerBranch: other.workerBranch,
6850
+ leaderBranch: config.leaderBranch,
6851
+ conflictingFiles,
6852
+ baseSha,
6853
+ worktreePath: wtPath,
6854
+ observedAt: Date.now()
6855
+ });
6856
+ try {
6857
+ await appendToInbox(config.teamName, other.workerName, message, config.cwd);
6858
+ } catch {
6859
+ }
6860
+ await appendEvent(config.repoRoot, config.teamName, {
6861
+ type: "rebase_conflict",
6862
+ worker: other.workerName,
6863
+ data: { conflictingFiles }
6864
+ });
6865
+ }
6866
+ }
6867
+ }
6868
+ async function attemptMergeForWorker(entry) {
6869
+ await mutex(async () => {
6870
+ const targetSha = entry.lastObservedSha;
6871
+ await appendEvent(config.repoRoot, config.teamName, {
6872
+ type: "merge_attempted",
6873
+ worker: entry.workerName,
6874
+ data: { targetSha }
6875
+ });
6876
+ try {
6877
+ preflightMergerWorktree(mergerPath, config.leaderBranch);
6878
+ } catch (err) {
6879
+ const reason = err instanceof Error ? err.message : String(err);
6880
+ entry.consecutiveFailures += 1;
6881
+ await appendEvent(config.repoRoot, config.teamName, {
6882
+ type: "merge_conflict",
6883
+ worker: entry.workerName,
6884
+ reason: `preflight_failed:${reason}`
6885
+ });
6886
+ return;
6887
+ }
6888
+ const conflicts = checkMergeConflicts(
6889
+ entry.workerBranch,
6890
+ config.leaderBranch,
6891
+ mergerPath
6892
+ );
6893
+ if (conflicts.length > 0) {
6894
+ let mergeBaseSha = "unknown";
6895
+ try {
6896
+ mergeBaseSha = (0, import_node_child_process3.execFileSync)(
6897
+ "git",
6898
+ ["merge-base", config.leaderBranch, entry.workerBranch],
6899
+ { cwd: mergerPath, encoding: "utf-8", stdio: "pipe" }
6900
+ ).trim();
6901
+ } catch {
6902
+ }
6903
+ const message = formatMergeConflictForLeader({
6904
+ workerName: entry.workerName,
6905
+ workerBranch: entry.workerBranch,
6906
+ leaderBranch: config.leaderBranch,
6907
+ conflictingFiles: conflicts,
6908
+ mergeBaseSha,
6909
+ observedAt: Date.now()
6910
+ });
6911
+ try {
6912
+ await appendToLeaderInbox(config.teamName, message, config.cwd);
6913
+ } catch {
6914
+ }
6915
+ await appendEvent(config.repoRoot, config.teamName, {
6916
+ type: "merge_conflict",
6917
+ worker: entry.workerName,
6918
+ data: { conflictingFiles: conflicts, mergeBaseSha }
6919
+ });
6920
+ entry.consecutiveFailures += 1;
6921
+ return;
6922
+ }
6923
+ const result = mergeWorkerBranch(
6924
+ entry.workerBranch,
6925
+ config.leaderBranch,
6926
+ mergerPath
6927
+ );
6928
+ if (!result.success) {
6929
+ const message = formatMergeConflictForLeader({
6930
+ workerName: entry.workerName,
6931
+ workerBranch: entry.workerBranch,
6932
+ leaderBranch: config.leaderBranch,
6933
+ conflictingFiles: result.conflicts.length > 0 ? result.conflicts : ["(merge failed after clean check)"],
6934
+ mergeBaseSha: "unknown",
6935
+ observedAt: Date.now()
6936
+ });
6937
+ try {
6938
+ await appendToLeaderInbox(config.teamName, message, config.cwd);
6939
+ } catch {
6940
+ }
6941
+ await appendEvent(config.repoRoot, config.teamName, {
6942
+ type: "merge_conflict",
6943
+ worker: entry.workerName,
6944
+ data: { conflictingFiles: result.conflicts }
6945
+ });
6946
+ entry.consecutiveFailures += 1;
6947
+ return;
6948
+ }
6949
+ entry.lastMergedSha = targetSha;
6950
+ entry.consecutiveFailures = 0;
6951
+ await appendEvent(config.repoRoot, config.teamName, {
6952
+ type: "merge_succeeded",
6953
+ worker: entry.workerName,
6954
+ data: { mergeCommit: result.mergeCommit, targetSha }
6955
+ });
6956
+ if (stopped) return;
6957
+ await fanOutRebase(entry.workerName);
6958
+ });
6959
+ }
6960
+ async function runPollOnce() {
6961
+ if (stopped) return;
6962
+ for (const entry of workers.values()) {
6963
+ const skipModulo = Math.min(30, Math.pow(2, entry.consecutiveFailures));
6964
+ if (skipModulo > 1 && pollTickCount % skipModulo !== 0) {
6965
+ continue;
6966
+ }
6967
+ if (pausedWorkers.has(entry.workerName)) {
6968
+ if (!isRebaseInProgress(entry.workerWorktreePath)) {
6969
+ await handleRebaseResolution(entry);
6970
+ } else {
6971
+ continue;
6972
+ }
6973
+ }
6974
+ let currentSha;
6975
+ try {
6976
+ currentSha = gitRevParseHead(config.repoRoot, entry.workerBranch);
6977
+ } catch (err) {
6978
+ entry.consecutiveFailures += 1;
6979
+ const reason = err instanceof Error ? err.message : String(err);
6980
+ await appendEvent(config.repoRoot, config.teamName, {
6981
+ type: "commit_observed",
6982
+ worker: entry.workerName,
6983
+ reason: `rev_parse_failed:${reason}`
6984
+ });
6985
+ continue;
6986
+ }
6987
+ if (currentSha && currentSha !== entry.lastObservedSha) {
6988
+ entry.lastObservedSha = currentSha;
6989
+ try {
6990
+ persistState();
6991
+ } catch {
6992
+ }
6993
+ await appendEvent(config.repoRoot, config.teamName, {
6994
+ type: "commit_observed",
6995
+ worker: entry.workerName,
6996
+ data: { sha: currentSha }
6997
+ });
6998
+ try {
6999
+ await attemptMergeForWorker(entry);
7000
+ } catch (err) {
7001
+ entry.consecutiveFailures += 1;
7002
+ const reason = err instanceof Error ? err.message : String(err);
7003
+ await appendEvent(config.repoRoot, config.teamName, {
7004
+ type: "merge_conflict",
7005
+ worker: entry.workerName,
7006
+ reason: `merge_threw:${reason}`
7007
+ });
7008
+ }
7009
+ }
7010
+ }
7011
+ }
7012
+ async function handleRebaseResolution(entry) {
7013
+ pausedWorkers.delete(entry.workerName);
7014
+ try {
7015
+ const status = (0, import_node_child_process3.execFileSync)("git", ["status", "--porcelain"], {
7016
+ cwd: entry.workerWorktreePath,
7017
+ encoding: "utf-8",
7018
+ stdio: "pipe"
7019
+ }).trim();
7020
+ if (status.length > 0) {
7021
+ const dirtyFiles = status.split("\n").map((l) => l.trim().replace(/^\S+\s+/, "")).filter((s) => s.length > 0);
7022
+ const audit = `## Auto-commit audit: the following files were modified during rebase pause and will be folded into the next auto-commit:
7023
+ ${dirtyFiles.map((f) => `- \`${f}\``).join("\n")}`;
7024
+ try {
7025
+ await appendToInbox(config.teamName, entry.workerName, audit, config.cwd);
7026
+ } catch {
7027
+ }
7028
+ }
7029
+ } catch {
7030
+ }
7031
+ await resumeHookViaSentinel(entry.workerWorktreePath);
7032
+ await appendEvent(config.repoRoot, config.teamName, {
7033
+ type: "rebase_resolved",
7034
+ worker: entry.workerName
7035
+ });
7036
+ }
7037
+ let pollTickCount = 0;
7038
+ const interval = setInterval(() => {
7039
+ pollTickCount += 1;
7040
+ void runPollOnce().catch(() => {
7041
+ });
7042
+ }, pollIntervalMs);
7043
+ if (typeof interval.unref === "function") interval.unref();
7044
+ return {
7045
+ async registerWorker(workerName2) {
7046
+ if (workers.has(workerName2)) return;
7047
+ const workerBranch = getBranchName(config.teamName, workerName2);
7048
+ validateBranchName(workerBranch);
7049
+ const wtPath = getWorktreePath(config.repoRoot, config.teamName, workerName2);
7050
+ let seedSha = persisted.lastShas[workerName2] ?? "";
7051
+ if (!seedSha) {
7052
+ try {
7053
+ seedSha = gitRevParseHead(config.repoRoot, workerBranch);
7054
+ } catch {
7055
+ seedSha = "";
7056
+ }
7057
+ }
7058
+ workers.set(workerName2, {
7059
+ workerName: workerName2,
7060
+ workerBranch,
7061
+ workerWorktreePath: wtPath,
7062
+ lastObservedSha: seedSha,
7063
+ lastMergedSha: seedSha,
7064
+ consecutiveFailures: 0
7065
+ });
7066
+ try {
7067
+ persistState();
7068
+ } catch {
7069
+ }
7070
+ },
7071
+ async unregisterWorker(workerName2) {
7072
+ workers.delete(workerName2);
7073
+ pausedWorkers.delete(workerName2);
7074
+ try {
7075
+ persistState();
7076
+ } catch {
7077
+ }
7078
+ },
7079
+ async pollOnce() {
7080
+ await runPollOnce();
7081
+ },
7082
+ async drainAndStop() {
7083
+ stopped = true;
7084
+ clearInterval(interval);
7085
+ const start = Date.now();
7086
+ const unmerged = [];
7087
+ const candidates = Array.from(workers.values()).filter(
7088
+ (w) => w.lastObservedSha && w.lastObservedSha !== w.lastMergedSha
7089
+ );
7090
+ for (const entry of candidates) {
7091
+ const remaining = drainTimeoutMs - (Date.now() - start);
7092
+ if (remaining <= 0) {
7093
+ unmerged.push({ workerName: entry.workerName, reason: "drain-timeout" });
7094
+ continue;
7095
+ }
7096
+ const merged = await Promise.race([
7097
+ (async () => {
7098
+ try {
7099
+ await attemptMergeForWorker(entry);
7100
+ return true;
7101
+ } catch {
7102
+ return false;
7103
+ }
7104
+ })(),
7105
+ new Promise((resolve6) => {
7106
+ const t = setTimeout(() => resolve6(false), remaining);
7107
+ if (typeof t.unref === "function") t.unref();
7108
+ })
7109
+ ]);
7110
+ if (!merged || entry.lastMergedSha !== entry.lastObservedSha) {
7111
+ unmerged.push({
7112
+ workerName: entry.workerName,
7113
+ reason: merged ? "merge-conflict" : "drain-timeout"
7114
+ });
7115
+ }
7116
+ }
7117
+ if (unmerged.length > 0) {
7118
+ const auditPath = teardownAuditPath(config.repoRoot, config.teamName);
7119
+ await (0, import_promises9.mkdir)((0, import_node_path2.dirname)(auditPath), { recursive: true });
7120
+ for (const u of unmerged) {
7121
+ const row = JSON.stringify({
7122
+ type: "unmerged_at_shutdown",
7123
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
7124
+ team: config.teamName,
7125
+ worker: u.workerName,
7126
+ reason: u.reason
7127
+ });
7128
+ try {
7129
+ await (0, import_promises9.appendFile)(auditPath, `${row}
7130
+ `, "utf-8");
7131
+ } catch {
7132
+ }
7133
+ }
7134
+ const message = `## Teardown audit: unmerged worker branches at shutdown
7135
+
7136
+ ${unmerged.map((u) => `- ${u.workerName}: ${u.reason}`).join("\n")}`;
7137
+ try {
7138
+ await appendToLeaderInbox(config.teamName, message, config.cwd);
7139
+ } catch {
7140
+ }
7141
+ }
7142
+ return { unmerged };
7143
+ },
7144
+ getState() {
7145
+ return {
7146
+ workers: Array.from(workers.keys()),
7147
+ lastShas: Object.fromEntries(
7148
+ Array.from(workers.values()).map((w) => [w.workerName, w.lastObservedSha])
7149
+ ),
7150
+ mergerWorktreePath: mergerPath
7151
+ };
7152
+ }
7153
+ };
7154
+ }
7155
+ async function recoverFromRestart(config) {
7156
+ const persistedPath = persistedStatePath(config.repoRoot, config.teamName);
7157
+ let persistedShasLoaded = 0;
7158
+ if ((0, import_node_fs2.existsSync)(persistedPath)) {
7159
+ try {
7160
+ const { readFileSync: readFileSync12 } = await import("node:fs");
7161
+ const persisted = JSON.parse(readFileSync12(persistedPath, "utf-8"));
7162
+ persistedShasLoaded = Object.keys(persisted.lastShas ?? {}).length;
7163
+ } catch {
7164
+ persistedShasLoaded = 0;
7165
+ }
7166
+ }
7167
+ const orphanedRebases = [];
7168
+ let entries = [];
7169
+ try {
7170
+ entries = listTeamWorktrees(config.teamName, config.repoRoot).map((w) => ({
7171
+ workerName: w.workerName,
7172
+ path: w.path
7173
+ }));
7174
+ } catch {
7175
+ entries = [];
7176
+ }
7177
+ for (const entry of entries) {
7178
+ if (!isRebaseInProgress(entry.path)) continue;
7179
+ orphanedRebases.push(entry.workerName);
7180
+ const message = `### Runtime restart recovery \u2014 your branch is mid-rebase
7181
+
7182
+ Runtime restarted while your branch was mid-rebase onto \`${config.leaderBranch}\`.
7183
+
7184
+ **Worktree:** \`${entry.path}\`
7185
+
7186
+ Cadence remains paused. Resolve and \`git rebase --continue\`, or \`git rebase --abort\` to bail.
7187
+ Cadence resumes once the git rebase state is gone.`;
7188
+ try {
7189
+ await appendToInbox(config.teamName, entry.workerName, message, config.cwd);
7190
+ } catch {
7191
+ }
7192
+ }
7193
+ if (orphanedRebases.length > 0 || persistedShasLoaded > 0) {
7194
+ try {
7195
+ await appendEvent(config.repoRoot, config.teamName, {
7196
+ type: "restart_recovery",
7197
+ data: { orphanedRebases, persistedShasLoaded }
7198
+ });
7199
+ } catch {
7200
+ }
7201
+ }
7202
+ return { orphanedRebases, persistedShasLoaded };
7203
+ }
7204
+
7205
+ // src/team/runtime-v2.ts
7206
+ var import_node_child_process4 = require("node:child_process");
7207
+ var orchestratorByTeam = /* @__PURE__ */ new Map();
7208
+ var cadenceByTeam = /* @__PURE__ */ new Map();
7209
+ function registerTeamOrchestrator(teamName, handle) {
7210
+ orchestratorByTeam.set(teamName, handle);
7211
+ }
7212
+ function getTeamOrchestrator(teamName) {
7213
+ return orchestratorByTeam.get(teamName);
7214
+ }
7215
+ function unregisterTeamOrchestrator(teamName) {
7216
+ orchestratorByTeam.delete(teamName);
7217
+ }
7218
+ function registerTeamCadence(teamName, context, poller) {
7219
+ const entry = cadenceByTeam.get(teamName) ?? { pollers: [], contexts: [] };
7220
+ entry.contexts.push(context);
7221
+ if (poller) entry.pollers.push(poller);
7222
+ cadenceByTeam.set(teamName, entry);
7223
+ }
7224
+ async function stopTeamCadence(teamName) {
7225
+ const entry = cadenceByTeam.get(teamName);
7226
+ if (!entry) return;
7227
+ cadenceByTeam.delete(teamName);
7228
+ for (const poller of entry.pollers) {
7229
+ try {
7230
+ poller.stop();
7231
+ } catch {
7232
+ }
7233
+ }
7234
+ for (const context of entry.contexts) {
7235
+ try {
7236
+ await uninstallCommitCadence(context);
7237
+ } catch {
7238
+ }
7239
+ }
7240
+ }
7241
+ function resolveLeaderBranch(cwd) {
7242
+ const out = (0, import_node_child_process4.execFileSync)("git", ["branch", "--show-current"], {
7243
+ cwd,
7244
+ encoding: "utf-8",
7245
+ stdio: ["pipe", "pipe", "pipe"]
7246
+ }).trim();
7247
+ if (!out) {
7248
+ throw new Error("auto-merge requires a non-detached leader branch (git branch --show-current returned empty)");
7249
+ }
7250
+ return out;
7251
+ }
6208
7252
  var MONITOR_SIGNAL_STALE_MS = 3e4;
6209
7253
  function resolveTaskAssignment(task, resolvedRouting, roleRoutingConfig, resolvedBinaryPaths, fallbackAgent) {
6210
7254
  const canonicalRoles = new Set(CANONICAL_TEAM_ROLES);
@@ -6296,7 +7340,7 @@ function buildV2TaskInstruction(teamName, workerName2, task, taskId, cliOutputCo
6296
7340
  {}
6297
7341
  );
6298
7342
  const completeTaskCommand = formatOmcCliInvocation(
6299
- `team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "completed", claim_token: "<claim_token>" })}' --json`
7343
+ `team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "completed", claim_token: "<claim_token>", result: "Summary: <what changed>\\nVerification: <tests/checks run>\\nSubagent skip reason: worker protocol forbids nested subagents; completed focused probe in-session" })}' --json`
6300
7344
  );
6301
7345
  const failTaskCommand = formatOmcCliInvocation(
6302
7346
  `team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "failed", claim_token: "<claim_token>" })}' --json`
@@ -6311,6 +7355,7 @@ function buildV2TaskInstruction(teamName, workerName2, task, taskId, cliOutputCo
6311
7355
  `2. Do the work described below.`,
6312
7356
  `3. On completion (use claim_token from step 1):`,
6313
7357
  ` ${completeTaskCommand}`,
7358
+ ` The result field is required for completion evidence. For broad delegated tasks, include either "Subagent skip reason: <why no nested worker was needed/allowed>" or, only when explicitly allowed by the leader, "Subagent spawn evidence: <child task names/thread ids and integrated findings>".`,
6314
7359
  `4. On failure (use claim_token from step 1):`,
6315
7360
  ` ${failTaskCommand}`,
6316
7361
  `5. ACK/progress replies are not a stop signal. Keep executing your assigned or next feasible work until the task is actually complete or failed, then transition and exit.`,
@@ -6347,7 +7392,7 @@ function hasWorkerStatusProgress(status, taskId) {
6347
7392
  }
6348
7393
  async function hasWorkerTaskClaimEvidence(teamName, workerName2, cwd, taskId) {
6349
7394
  try {
6350
- const raw = await (0, import_promises7.readFile)(absPath(cwd, TeamPaths.taskFile(teamName, taskId)), "utf-8");
7395
+ const raw = await (0, import_promises10.readFile)(absPath(cwd, TeamPaths.taskFile(teamName, taskId)), "utf-8");
6351
7396
  const task = JSON.parse(raw);
6352
7397
  return task.owner === workerName2 && ["in_progress", "completed", "failed"].includes(task.status);
6353
7398
  } catch {
@@ -6446,6 +7491,18 @@ async function spawnV2Worker(opts) {
6446
7491
  if (usePromptMode) {
6447
7492
  launchArgs.push(...getPromptModeArgs(opts.agentType, promptModeStartupPrompt));
6448
7493
  }
7494
+ if (opts.autoMerge && opts.worktreePath) {
7495
+ const cadenceContext = {
7496
+ teamName: opts.teamName,
7497
+ workerName: opts.workerName,
7498
+ worktreePath: opts.worktreePath,
7499
+ agentType: opts.agentType,
7500
+ enabled: true
7501
+ };
7502
+ const cadence = await installCommitCadence(cadenceContext);
7503
+ const poller = cadence.method === "fallback-poll" ? startFallbackPoller(opts.worktreePath, opts.workerName) : void 0;
7504
+ registerTeamCadence(opts.teamName, cadenceContext, poller);
7505
+ }
6449
7506
  const paneConfig = {
6450
7507
  teamName: opts.teamName,
6451
7508
  workerName: opts.workerName,
@@ -6546,19 +7603,19 @@ async function rollbackUnpersistedNativeWorktreeStartup(teamName, cwd, cause) {
6546
7603
  try {
6547
7604
  const cleanup = cleanupTeamWorktrees(teamName, cwd);
6548
7605
  if (cleanup.preserved.length === 0) {
6549
- await (0, import_promises7.rm)(teamRoot, { recursive: true, force: true });
7606
+ await (0, import_promises10.rm)(teamRoot, { recursive: true, force: true });
6550
7607
  return;
6551
7608
  }
6552
- await (0, import_promises7.mkdir)(teamRoot, { recursive: true });
6553
- await (0, import_promises7.writeFile)((0, import_path20.join)(teamRoot, "startup-failure.json"), JSON.stringify({
7609
+ await (0, import_promises10.mkdir)(teamRoot, { recursive: true });
7610
+ await (0, import_promises10.writeFile)((0, import_path22.join)(teamRoot, "startup-failure.json"), JSON.stringify({
6554
7611
  reason: "startup_failed_before_config_persisted",
6555
7612
  error: errorMessage,
6556
7613
  preserved: cleanup.preserved,
6557
7614
  recorded_at: (/* @__PURE__ */ new Date()).toISOString()
6558
7615
  }, null, 2), "utf-8");
6559
7616
  } catch (rollbackError) {
6560
- await (0, import_promises7.mkdir)(teamRoot, { recursive: true });
6561
- await (0, import_promises7.writeFile)((0, import_path20.join)(teamRoot, "startup-failure.json"), JSON.stringify({
7617
+ await (0, import_promises10.mkdir)(teamRoot, { recursive: true });
7618
+ await (0, import_promises10.writeFile)((0, import_path22.join)(teamRoot, "startup-failure.json"), JSON.stringify({
6562
7619
  reason: "startup_failed_before_config_persisted",
6563
7620
  error: errorMessage,
6564
7621
  rollback_error: rollbackError instanceof Error ? rollbackError.message : String(rollbackError),
@@ -6584,13 +7641,27 @@ async function rollbackStartedNativeWorktreeStartup(args) {
6584
7641
  }
6585
7642
  async function startTeamV2(config) {
6586
7643
  const sanitized = sanitizeTeamName(config.teamName);
6587
- const leaderCwd = (0, import_path20.resolve)(config.cwd);
7644
+ const leaderCwd = (0, import_path22.resolve)(config.cwd);
6588
7645
  validateTeamName(sanitized);
6589
7646
  const pluginCfg = config.pluginConfig ?? loadConfig();
6590
7647
  const resolvedRouting = buildResolvedRoutingSnapshot(pluginCfg);
6591
- const worktreeMode = normalizeTeamWorktreeMode(
7648
+ let worktreeMode = normalizeTeamWorktreeMode(
6592
7649
  process.env.OMC_TEAM_WORKTREE_MODE ?? pluginCfg.team?.ops?.worktreeMode
6593
7650
  );
7651
+ let autoMergeLeaderBranch;
7652
+ if (config.autoMerge) {
7653
+ if (!isRuntimeV2Enabled()) {
7654
+ throw new Error("auto-merge requires OMC_RUNTIME_V2=1 (this feature is v2-only).");
7655
+ }
7656
+ autoMergeLeaderBranch = resolveLeaderBranch(leaderCwd);
7657
+ const stripped = autoMergeLeaderBranch.replace(/^refs\/heads\//i, "").toLowerCase();
7658
+ if (stripped === "main" || stripped === "master") {
7659
+ throw new Error("auto-merge refuses main/master leader branch \u2014 use a feature branch");
7660
+ }
7661
+ if (worktreeMode !== "named") {
7662
+ worktreeMode = "named";
7663
+ }
7664
+ }
6594
7665
  const workspaceMode = worktreeMode === "disabled" ? "single" : "worktree";
6595
7666
  const agentTypes = config.agentTypes;
6596
7667
  const resolvedBinaryPaths = {};
@@ -6620,9 +7691,9 @@ async function startTeamV2(config) {
6620
7691
  } catch {
6621
7692
  }
6622
7693
  }
6623
- await (0, import_promises7.mkdir)(absPath(leaderCwd, TeamPaths.tasks(sanitized)), { recursive: true });
6624
- await (0, import_promises7.mkdir)(absPath(leaderCwd, TeamPaths.workers(sanitized)), { recursive: true });
6625
- await (0, import_promises7.mkdir)((0, import_path20.join)(leaderCwd, ".omcp", "state", "team", sanitized, "mailbox"), { recursive: true });
7694
+ await (0, import_promises10.mkdir)(absPath(leaderCwd, TeamPaths.tasks(sanitized)), { recursive: true });
7695
+ await (0, import_promises10.mkdir)(absPath(leaderCwd, TeamPaths.workers(sanitized)), { recursive: true });
7696
+ await (0, import_promises10.mkdir)((0, import_path22.join)(leaderCwd, ".omcp", "state", "team", sanitized, "mailbox"), { recursive: true });
6626
7697
  const missingBinaryLogFailure = createSwallowedErrorLogger(
6627
7698
  "team.runtime-v2.startTeamV2 cli_binary_missing event failed"
6628
7699
  );
@@ -6640,14 +7711,15 @@ async function startTeamV2(config) {
6640
7711
  for (let i = 0; i < config.tasks.length; i++) {
6641
7712
  const taskId = String(i + 1);
6642
7713
  const taskFilePath = absPath(leaderCwd, TeamPaths.taskFile(sanitized, taskId));
6643
- await (0, import_promises7.mkdir)((0, import_path20.join)(taskFilePath, ".."), { recursive: true });
6644
- await (0, import_promises7.writeFile)(taskFilePath, JSON.stringify({
7714
+ await (0, import_promises10.mkdir)((0, import_path22.join)(taskFilePath, ".."), { recursive: true });
7715
+ await (0, import_promises10.writeFile)(taskFilePath, JSON.stringify({
6645
7716
  id: taskId,
6646
7717
  subject: config.tasks[i].subject,
6647
7718
  description: config.tasks[i].description,
6648
7719
  status: "pending",
6649
7720
  owner: null,
6650
7721
  result: null,
7722
+ ...config.tasks[i].delegation ? { delegation: config.tasks[i].delegation } : {},
6651
7723
  created_at: (/* @__PURE__ */ new Date()).toISOString()
6652
7724
  }, null, 2), "utf-8");
6653
7725
  }
@@ -6713,7 +7785,7 @@ async function startTeamV2(config) {
6713
7785
  });
6714
7786
  const worktree = workerWorktrees.get(wName);
6715
7787
  if (worktree) {
6716
- const overlayContent = await (0, import_promises7.readFile)(overlayPath, "utf-8");
7788
+ const overlayContent = await (0, import_promises10.readFile)(overlayPath, "utf-8");
6717
7789
  installWorktreeRootAgents(sanitized, wName, leaderCwd, worktree.path, overlayContent);
6718
7790
  }
6719
7791
  }
@@ -6823,7 +7895,7 @@ async function startTeamV2(config) {
6823
7895
  next_worker_index: teamConfig.next_worker_index
6824
7896
  };
6825
7897
  try {
6826
- await (0, import_promises7.writeFile)(absPath(leaderCwd, TeamPaths.manifest(sanitized)), JSON.stringify(teamManifest, null, 2), "utf-8");
7898
+ await (0, import_promises10.writeFile)(absPath(leaderCwd, TeamPaths.manifest(sanitized)), JSON.stringify(teamManifest, null, 2), "utf-8");
6827
7899
  } catch (error) {
6828
7900
  await rollbackStartedNativeWorktreeStartup({
6829
7901
  teamName: sanitized,
@@ -6872,6 +7944,7 @@ async function startTeamV2(config) {
6872
7944
  cwd: leaderCwd,
6873
7945
  workerCwd: workersInfo[workerIndex]?.working_dir ?? leaderCwd,
6874
7946
  worktreePath: workersInfo[workerIndex]?.worktree_path,
7947
+ autoMerge: Boolean(config.autoMerge),
6875
7948
  resolvedBinaryPaths,
6876
7949
  ...assignment.model ? { model: assignment.model } : {},
6877
7950
  ...assignment.role ? { role: assignment.role } : {}
@@ -6934,6 +8007,51 @@ async function startTeamV2(config) {
6934
8007
  worker: "leader-fixed",
6935
8008
  reason: `start_team_v2: workers=${config.workerCount} tasks=${config.tasks.length} panes=${workerPaneIds.length}`
6936
8009
  }, leaderCwd).catch(logEventFailure);
8010
+ if (config.autoMerge && autoMergeLeaderBranch) {
8011
+ try {
8012
+ await ensureLeaderInbox(sanitized, leaderCwd);
8013
+ await appendToLeaderInbox(
8014
+ sanitized,
8015
+ extendLeaderBootstrapPrompt(sanitized),
8016
+ leaderCwd
8017
+ );
8018
+ try {
8019
+ await recoverFromRestart({
8020
+ teamName: sanitized,
8021
+ repoRoot: leaderCwd,
8022
+ leaderBranch: autoMergeLeaderBranch,
8023
+ cwd: leaderCwd
8024
+ });
8025
+ } catch (recErr) {
8026
+ process.stderr.write(`[team/runtime-v2] auto-merge recover-from-restart failed: ${recErr}
8027
+ `);
8028
+ }
8029
+ const orchestrator = await startMergeOrchestrator({
8030
+ teamName: sanitized,
8031
+ repoRoot: leaderCwd,
8032
+ leaderBranch: autoMergeLeaderBranch,
8033
+ cwd: leaderCwd
8034
+ });
8035
+ registerTeamOrchestrator(sanitized, orchestrator);
8036
+ for (const w of workersInfo) {
8037
+ await orchestrator.registerWorker(w.name);
8038
+ }
8039
+ } catch (orchErr) {
8040
+ await stopTeamCadence(sanitized);
8041
+ unregisterTeamOrchestrator(sanitized);
8042
+ await rollbackStartedNativeWorktreeStartup({
8043
+ teamName: sanitized,
8044
+ cwd: leaderCwd,
8045
+ cause: orchErr,
8046
+ sessionName: sessionName2,
8047
+ leaderPaneId,
8048
+ workerPaneIds,
8049
+ sessionMode: session.sessionMode
8050
+ });
8051
+ const reason = orchErr instanceof Error ? orchErr.message : String(orchErr);
8052
+ throw new Error(`auto-merge startup failed: ${reason}`);
8053
+ }
8054
+ }
6937
8055
  return {
6938
8056
  teamName: sanitized,
6939
8057
  sanitizedName: sanitized,
@@ -6965,7 +8083,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
6965
8083
  }
6966
8084
  let payload;
6967
8085
  try {
6968
- const raw = await (0, import_promises7.readFile)(outputFile, "utf-8");
8086
+ const raw = await (0, import_promises10.readFile)(outputFile, "utf-8");
6969
8087
  payload = parseCliWorkerVerdict(raw);
6970
8088
  } catch (err) {
6971
8089
  const reason = err instanceof Error ? err.message : String(err);
@@ -7248,6 +8366,39 @@ async function shutdownTeamV2(teamName, cwd, options = {}) {
7248
8366
  const timeoutMs = options.timeoutMs ?? 15e3;
7249
8367
  const sanitized = sanitizeTeamName(teamName);
7250
8368
  const config = await readTeamConfig(sanitized, cwd);
8369
+ const finalizeAutoMerge = async () => {
8370
+ const orchestrator = getTeamOrchestrator(sanitized);
8371
+ if (orchestrator) {
8372
+ try {
8373
+ const drainResult = await orchestrator.drainAndStop();
8374
+ if (drainResult.unmerged.length > 0) {
8375
+ await appendTeamEvent(sanitized, {
8376
+ type: "team_leader_nudge",
8377
+ worker: "leader-fixed",
8378
+ reason: `auto_merge_drain_unmerged:${drainResult.unmerged.map((u) => `${u.workerName}:${u.reason}`).join(",")}`
8379
+ }, cwd).catch(logEventFailure);
8380
+ }
8381
+ for (const w of config?.workers ?? []) {
8382
+ try {
8383
+ await orchestrator.unregisterWorker(w.name);
8384
+ } catch (err) {
8385
+ process.stderr.write(
8386
+ `[team/runtime-v2] orchestrator.unregisterWorker(${w.name}) failed: ${err}
8387
+ `
8388
+ );
8389
+ }
8390
+ }
8391
+ } catch (err) {
8392
+ process.stderr.write(`[team/runtime-v2] orchestrator drainAndStop: ${err}
8393
+ `);
8394
+ } finally {
8395
+ await stopTeamCadence(sanitized);
8396
+ unregisterTeamOrchestrator(sanitized);
8397
+ }
8398
+ } else {
8399
+ await stopTeamCadence(sanitized);
8400
+ }
8401
+ };
7251
8402
  if (!config) {
7252
8403
  const cleanupSafety = inspectTeamWorktreeCleanupSafety(sanitized, cwd);
7253
8404
  if (cleanupSafety.hasEvidence) {
@@ -7382,12 +8533,14 @@ Then exit your session.
7382
8533
  if (aliveWorkers.length > 0) {
7383
8534
  process.stderr.write(`[team/runtime-v2] preserving worktrees/state because worker pane(s) are still alive: ${aliveWorkers.join(", ")}
7384
8535
  `);
8536
+ await finalizeAutoMerge();
7385
8537
  return;
7386
8538
  }
7387
8539
  const unknownWorkers = liveness.filter(([, state]) => state === "unknown").map(([paneId]) => paneById.get(paneId) ?? paneId);
7388
8540
  if (unknownWorkers.length > 0) {
7389
8541
  process.stderr.write(`[team/runtime-v2] preserving worktrees/state because worker pane liveness is unknown: ${unknownWorkers.join(", ")}
7390
8542
  `);
8543
+ await finalizeAutoMerge();
7391
8544
  return;
7392
8545
  }
7393
8546
  } catch (err) {
@@ -7395,6 +8548,7 @@ Then exit your session.
7395
8548
  `);
7396
8549
  if (recordedWorkerPaneIds.length > 0) {
7397
8550
  process.stderr.write("[team/runtime-v2] preserving worktrees/state because tmux cleanup did not prove worker panes exited\n");
8551
+ await finalizeAutoMerge();
7398
8552
  return;
7399
8553
  }
7400
8554
  }
@@ -7409,6 +8563,7 @@ Then exit your session.
7409
8563
  reason: `ralph_cleanup_summary: total=${finalTasks.length} completed=${completed} failed=${failed} pending=${pending} force=${force}`
7410
8564
  }, cwd).catch(logEventFailure);
7411
8565
  }
8566
+ await finalizeAutoMerge();
7412
8567
  let preservedWorktrees = 0;
7413
8568
  try {
7414
8569
  const worktreeCleanup = cleanupTeamWorktrees(sanitized, cwd);
@@ -7427,6 +8582,11 @@ Then exit your session.
7427
8582
  }
7428
8583
 
7429
8584
  // src/team/runtime-cli.ts
8585
+ function assertAutoMergeRuntimeSupported(useV2, autoMerge) {
8586
+ if (autoMerge && !useV2) {
8587
+ throw new Error("--auto-merge requires runtime v2; unset OMC_RUNTIME_V2=0 or disable --auto-merge");
8588
+ }
8589
+ }
7430
8590
  function getTerminalStatus(taskCounts, expectedTaskCount) {
7431
8591
  const active = taskCounts.pending + taskCounts.inProgress;
7432
8592
  const terminal = taskCounts.completed + taskCounts.failed;
@@ -7444,10 +8604,10 @@ function parseWatchdogFailedAt(marker) {
7444
8604
  throw new Error("watchdog marker missing valid failedAt");
7445
8605
  }
7446
8606
  async function checkWatchdogFailedMarker(stateRoot2, startTime) {
7447
- const markerPath = (0, import_path21.join)(stateRoot2, "watchdog-failed.json");
8607
+ const markerPath = (0, import_path23.join)(stateRoot2, "watchdog-failed.json");
7448
8608
  let raw;
7449
8609
  try {
7450
- raw = await (0, import_promises8.readFile)(markerPath, "utf-8");
8610
+ raw = await (0, import_promises11.readFile)(markerPath, "utf-8");
7451
8611
  } catch (err) {
7452
8612
  const code = err.code;
7453
8613
  if (code === "ENOENT") return { failed: false };
@@ -7469,21 +8629,21 @@ async function checkWatchdogFailedMarker(stateRoot2, startTime) {
7469
8629
  return { failed: true, reason: `Watchdog marked team failed at ${new Date(failedAt).toISOString()}` };
7470
8630
  }
7471
8631
  try {
7472
- await (0, import_promises8.unlink)(markerPath);
8632
+ await (0, import_promises11.unlink)(markerPath);
7473
8633
  } catch {
7474
8634
  }
7475
8635
  return { failed: false };
7476
8636
  }
7477
8637
  async function writeResultArtifact(output, finishedAt, jobId = process.env.OMC_JOB_ID, omcJobsDir = process.env.OMC_JOBS_DIR) {
7478
8638
  if (!jobId || !omcJobsDir) return;
7479
- const resultPath = (0, import_path21.join)(omcJobsDir, `${jobId}-result.json`);
8639
+ const resultPath = (0, import_path23.join)(omcJobsDir, `${jobId}-result.json`);
7480
8640
  const tmpPath = `${resultPath}.tmp`;
7481
- await (0, import_promises8.writeFile)(
8641
+ await (0, import_promises11.writeFile)(
7482
8642
  tmpPath,
7483
8643
  JSON.stringify({ ...output, finishedAt }),
7484
8644
  "utf-8"
7485
8645
  );
7486
- await (0, import_promises8.rename)(tmpPath, resultPath);
8646
+ await (0, import_promises11.rename)(tmpPath, resultPath);
7487
8647
  }
7488
8648
  function buildCliOutput(stateRoot2, teamName, status, workerCount, startTimeMs) {
7489
8649
  const taskResults = collectTaskResults(stateRoot2);
@@ -7508,20 +8668,20 @@ function buildTerminalCliResult(stateRoot2, teamName, phase, workerCount, startT
7508
8668
  async function writePanesFile(jobId, paneIds, leaderPaneId, sessionName2, ownsWindow) {
7509
8669
  const omcJobsDir = process.env.OMC_JOBS_DIR;
7510
8670
  if (!jobId || !omcJobsDir) return;
7511
- const panesPath = (0, import_path21.join)(omcJobsDir, `${jobId}-panes.json`);
7512
- await (0, import_promises8.writeFile)(
8671
+ const panesPath = (0, import_path23.join)(omcJobsDir, `${jobId}-panes.json`);
8672
+ await (0, import_promises11.writeFile)(
7513
8673
  panesPath + ".tmp",
7514
8674
  JSON.stringify({ paneIds: [...paneIds], leaderPaneId, sessionName: sessionName2, ownsWindow })
7515
8675
  );
7516
- await (0, import_promises8.rename)(panesPath + ".tmp", panesPath);
8676
+ await (0, import_promises11.rename)(panesPath + ".tmp", panesPath);
7517
8677
  }
7518
8678
  function collectTaskResults(stateRoot2) {
7519
- const tasksDir = (0, import_path21.join)(stateRoot2, "tasks");
8679
+ const tasksDir = (0, import_path23.join)(stateRoot2, "tasks");
7520
8680
  try {
7521
- const files = (0, import_fs19.readdirSync)(tasksDir).filter((f) => f.endsWith(".json"));
8681
+ const files = (0, import_fs21.readdirSync)(tasksDir).filter((f) => f.endsWith(".json"));
7522
8682
  return files.map((f) => {
7523
8683
  try {
7524
- const raw = (0, import_fs19.readFileSync)((0, import_path21.join)(tasksDir, f), "utf-8");
8684
+ const raw = (0, import_fs21.readFileSync)((0, import_path23.join)(tasksDir, f), "utf-8");
7525
8685
  const task = JSON.parse(raw);
7526
8686
  return {
7527
8687
  taskId: task.id ?? f.replace(".json", ""),
@@ -7572,10 +8732,11 @@ async function main() {
7572
8732
  newWindow = false,
7573
8733
  pollIntervalMs = 5e3,
7574
8734
  sentinelGateTimeoutMs = 3e4,
7575
- sentinelGatePollIntervalMs = 250
8735
+ sentinelGatePollIntervalMs = 250,
8736
+ autoMerge = false
7576
8737
  } = input;
7577
8738
  const workerCount = input.workerCount ?? agentTypes.length;
7578
- const stateRoot2 = (0, import_path21.join)(cwd, `.omc/state/team/${teamName}`);
8739
+ const stateRoot2 = (0, import_path23.join)(cwd, `.omcp/state/team/${teamName}`);
7579
8740
  const config = {
7580
8741
  teamName,
7581
8742
  workerCount,
@@ -7585,6 +8746,13 @@ async function main() {
7585
8746
  newWindow
7586
8747
  };
7587
8748
  const useV2 = isRuntimeV2Enabled();
8749
+ try {
8750
+ assertAutoMergeRuntimeSupported(useV2, autoMerge);
8751
+ } catch (err) {
8752
+ process.stderr.write(`[runtime-cli] ${err instanceof Error ? err.message : String(err)}
8753
+ `);
8754
+ process.exit(1);
8755
+ }
7588
8756
  let runtime = null;
7589
8757
  let finalStatus = "failed";
7590
8758
  let pollActive = true;
@@ -7649,7 +8817,8 @@ async function main() {
7649
8817
  agentTypes,
7650
8818
  tasks,
7651
8819
  cwd,
7652
- newWindow
8820
+ newWindow,
8821
+ autoMerge
7653
8822
  });
7654
8823
  const v2PaneIds = v2Runtime.config.workers.map((w) => w.pane_id).filter((p) => typeof p === "string");
7655
8824
  runtime = {
@@ -7769,7 +8938,7 @@ async function main() {
7769
8938
  if (snap.allTasksTerminal) {
7770
8939
  const hasFailures = snap.tasks.failed > 0;
7771
8940
  if (!hasFailures) {
7772
- const sentinelLogPath = (0, import_path21.join)(cwd, "sentinel_stop.jsonl");
8941
+ const sentinelLogPath = (0, import_path23.join)(cwd, "sentinel_stop.jsonl");
7773
8942
  const gateResult = await waitForSentinelReadiness({
7774
8943
  workspace: cwd,
7775
8944
  logPath: sentinelLogPath,
@@ -7846,7 +9015,7 @@ async function main() {
7846
9015
  mismatchStreak = 0;
7847
9016
  const terminalStatus = getTerminalStatus(snap.taskCounts, expectedTaskCount);
7848
9017
  if (terminalStatus === "completed") {
7849
- const sentinelLogPath = (0, import_path21.join)(cwd, "sentinel_stop.jsonl");
9018
+ const sentinelLogPath = (0, import_path23.join)(cwd, "sentinel_stop.jsonl");
7850
9019
  const gateResult = await waitForSentinelReadiness({
7851
9020
  workspace: cwd,
7852
9021
  logPath: sentinelLogPath,
@@ -7890,6 +9059,7 @@ if (require.main === module) {
7890
9059
  }
7891
9060
  // Annotate the CommonJS export names for ESM import in node:
7892
9061
  0 && (module.exports = {
9062
+ assertAutoMergeRuntimeSupported,
7893
9063
  buildCliOutput,
7894
9064
  buildTerminalCliResult,
7895
9065
  checkWatchdogFailedMarker,