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
package/bridge/team.js CHANGED
@@ -2105,15 +2105,42 @@ var init_ssrf_guard = __esm({
2105
2105
  });
2106
2106
 
2107
2107
  // src/config/models.ts
2108
+ function readEnvValue(key) {
2109
+ const value = process.env[key]?.trim();
2110
+ return value || void 0;
2111
+ }
2108
2112
  function resolveTierModelFromEnv(tier) {
2109
2113
  for (const key of TIER_ENV_KEYS[tier]) {
2110
- const value = process.env[key]?.trim();
2114
+ const value = readEnvValue(key);
2115
+ if (value) {
2116
+ return value;
2117
+ }
2118
+ }
2119
+ return void 0;
2120
+ }
2121
+ function getDirectModelEnvValue() {
2122
+ for (const key of DIRECT_MODEL_ENV_KEYS) {
2123
+ const value = readEnvValue(key);
2111
2124
  if (value) {
2112
2125
  return value;
2113
2126
  }
2114
2127
  }
2115
2128
  return void 0;
2116
2129
  }
2130
+ function getProviderDetectionModelEnvValues() {
2131
+ const directModel = getDirectModelEnvValue();
2132
+ if (directModel) {
2133
+ return [directModel];
2134
+ }
2135
+ const values = /* @__PURE__ */ new Set();
2136
+ for (const tier of INHERIT_TIER_PRIORITY) {
2137
+ const value = resolveTierModelFromEnv(tier);
2138
+ if (value) {
2139
+ values.add(value);
2140
+ }
2141
+ }
2142
+ return [...values];
2143
+ }
2117
2144
  function getDefaultModelHigh() {
2118
2145
  return resolveTierModelFromEnv("HIGH") || BUILTIN_TIER_MODEL_DEFAULTS.HIGH;
2119
2146
  }
@@ -2146,17 +2173,19 @@ function isBedrock() {
2146
2173
  if (modelId && /^((us|eu|ap|global)\.anthropic\.|anthropic\.claude)/i.test(modelId)) {
2147
2174
  return true;
2148
2175
  }
2176
+ if (modelId && /^arn:aws(-[^:]+)?:bedrock:/i.test(modelId) && /:(inference-profile|application-inference-profile)\//i.test(modelId) && modelId.toLowerCase().includes("claude")) {
2177
+ return true;
2178
+ }
2149
2179
  return false;
2150
2180
  }
2151
2181
  function isVertexAI() {
2152
2182
  if (process.env.CLAUDE_CODE_USE_VERTEX === "1") {
2153
2183
  return true;
2154
2184
  }
2155
- const modelId = process.env.CLAUDE_MODEL || process.env.ANTHROPIC_MODEL || "";
2156
- if (modelId && modelId.toLowerCase().startsWith("vertex_ai/")) {
2157
- return true;
2158
- }
2159
- return false;
2185
+ return hasVertexModelId(getProviderDetectionModelEnvValues());
2186
+ }
2187
+ function hasVertexModelId(modelIds) {
2188
+ return modelIds.some((modelId) => modelId.toLowerCase().startsWith("vertex_ai/"));
2160
2189
  }
2161
2190
  function isProviderSpecificModelId(modelId) {
2162
2191
  return isBedrockModelId(modelId) || isVertexModelId(modelId);
@@ -2195,11 +2224,13 @@ function isNonCopilotProvider() {
2195
2224
  }
2196
2225
  return false;
2197
2226
  }
2198
- var TIER_ENV_KEYS, COPILOT_FAMILY_DEFAULTS, BUILTIN_TIER_MODEL_DEFAULTS, COPILOT_FAMILY_HIGH_VARIANTS, BUILTIN_EXTERNAL_MODEL_DEFAULTS;
2227
+ var DIRECT_MODEL_ENV_KEYS, INHERIT_TIER_PRIORITY, TIER_ENV_KEYS, COPILOT_FAMILY_DEFAULTS, BUILTIN_TIER_MODEL_DEFAULTS, COPILOT_FAMILY_HIGH_VARIANTS, BUILTIN_EXTERNAL_MODEL_DEFAULTS;
2199
2228
  var init_models = __esm({
2200
2229
  "src/config/models.ts"() {
2201
2230
  "use strict";
2202
2231
  init_ssrf_guard();
2232
+ DIRECT_MODEL_ENV_KEYS = ["CLAUDE_MODEL", "ANTHROPIC_MODEL"];
2233
+ INHERIT_TIER_PRIORITY = ["MEDIUM", "HIGH", "LOW"];
2203
2234
  TIER_ENV_KEYS = {
2204
2235
  LOW: [
2205
2236
  "OMC_MODEL_LOW",
@@ -3192,6 +3223,7 @@ var init_definitions = __esm({
3192
3223
  init_utils();
3193
3224
  init_loader();
3194
3225
  init_strict_mode_guidance();
3226
+ init_models();
3195
3227
  init_architect();
3196
3228
  init_designer();
3197
3229
  init_writer();
@@ -3275,6 +3307,9 @@ var init_definitions = __esm({
3275
3307
 
3276
3308
  // src/features/delegation-enforcer.ts
3277
3309
  function normalizeToCcAlias(model) {
3310
+ if (isProviderSpecificModelId(model)) {
3311
+ return model;
3312
+ }
3278
3313
  const family = resolveClaudeFamily(model);
3279
3314
  return family ? CC_FAMILY_TO_ALIAS[family] ?? model : model;
3280
3315
  }
@@ -3451,6 +3486,9 @@ function resolveCliBinaryPath(binary) {
3451
3486
  resolvedPathCache.set(binary, resolvedPath);
3452
3487
  return resolvedPath;
3453
3488
  }
3489
+ function shouldUseClaudeBareMode(env = process.env) {
3490
+ return typeof env.ANTHROPIC_API_KEY === "string" && env.ANTHROPIC_API_KEY.trim().length > 0;
3491
+ }
3454
3492
  function getContract(agentType) {
3455
3493
  const contract = CONTRACTS[agentType];
3456
3494
  if (!contract) {
@@ -3560,6 +3598,9 @@ var init_model_contract = __esm({
3560
3598
  installInstructions: "Install Claude CLI: https://claude.ai/download",
3561
3599
  buildLaunchArgs(model, extraFlags = []) {
3562
3600
  const args = ["--dangerously-skip-permissions"];
3601
+ if (shouldUseClaudeBareMode() && !extraFlags.includes("--bare")) {
3602
+ args.push("--bare");
3603
+ }
3563
3604
  if (model) {
3564
3605
  const resolved = isProviderSpecificModelId(model) ? model : normalizeToCcAlias(model);
3565
3606
  args.push("--model", resolved);
@@ -3749,7 +3790,7 @@ function generateWorkerOverlay(params) {
3749
3790
  const shutdownAckPath = buildTeamStateInstructionPath(teamName, instructionStateRoot, "workers", workerName, "shutdown-ack.json");
3750
3791
  const claimTaskCommand = formatOmcCliInvocation(`team api claim-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"worker\\":\\"${workerName}\\"}" --json`);
3751
3792
  const sendAckCommand = formatOmcCliInvocation(`team api send-message --input "{\\"team_name\\":\\"${teamName}\\",\\"from_worker\\":\\"${workerName}\\",\\"to_worker\\":\\"leader-fixed\\",\\"body\\":\\"ACK: ${workerName} initialized\\"}" --json`);
3752
- 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`);
3793
+ 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`);
3753
3794
  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`);
3754
3795
  const readTaskCommand = formatOmcCliInvocation(`team api read-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\"}" --json`);
3755
3796
  const releaseClaimCommand = formatOmcCliInvocation(`team api release-task-claim --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"claim_token\\":\\"<claim_token>\\",\\"worker\\":\\"${workerName}\\"}" --json`);
@@ -3802,6 +3843,11 @@ Use the CLI API for all task lifecycle operations. Do NOT directly edit task fil
3802
3843
  - Complete task: \`${completeTaskCommand}\`
3803
3844
  - Fail task: \`${failTaskCommand}\`
3804
3845
  - Release claim (rollback): \`${releaseClaimCommand}\`
3846
+ - Delegation compliance evidence (required for broad delegated tasks):
3847
+ - The completion command MUST include a \`result\` string with summary and verification evidence.
3848
+ - Because worker protocol forbids nested sub-agents, use: \`Subagent skip reason: <why in-session execution was safer/sufficient>\`
3849
+ - 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>\`
3850
+ - Completion is rejected with \`missing_delegation_compliance_evidence\` when required evidence is absent.
3805
3851
 
3806
3852
  ## Canonical Team State Root
3807
3853
  - 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}.
@@ -3865,6 +3911,17 @@ async function composeInitialInbox(teamName, workerName, content, cwd, cliOutput
3865
3911
  ${cliOutputContract}` : content;
3866
3912
  await writeFile3(inboxPath, finalContent, "utf-8");
3867
3913
  }
3914
+ async function appendToInbox(teamName, workerName, message, cwd) {
3915
+ const safeTeam = sanitizeName(teamName);
3916
+ const safeWorker = sanitizeName(workerName);
3917
+ const inboxPath = join14(cwd, `.omcp/state/team/${safeTeam}/workers/${safeWorker}/inbox.md`);
3918
+ validateResolvedPath(inboxPath, cwd);
3919
+ await mkdir3(dirname7(inboxPath), { recursive: true });
3920
+ await appendFile2(inboxPath, `
3921
+
3922
+ ---
3923
+ ${message}`, "utf-8");
3924
+ }
3868
3925
  async function ensureWorkerStateDir(teamName, workerName, cwd) {
3869
3926
  const workerDir = join14(cwd, `.omcp/state/team/${teamName}/workers/${workerName}`);
3870
3927
  await mkdir3(workerDir, { recursive: true });
@@ -3887,6 +3944,8 @@ var init_worker_bootstrap = __esm({
3887
3944
  "use strict";
3888
3945
  init_prompt_helpers();
3889
3946
  init_omc_cli_rendering();
3947
+ init_tmux_session();
3948
+ init_fs_utils();
3890
3949
  init_model_contract();
3891
3950
  DEFAULT_INSTRUCTION_STATE_ROOT = ".omcp/state";
3892
3951
  }
@@ -4523,6 +4582,9 @@ function removeWorkerWorktree(teamName, workerName, repoRoot) {
4523
4582
  forgetMetadataUnlocked(repoRoot, teamName, workerName);
4524
4583
  });
4525
4584
  }
4585
+ function listTeamWorktrees(teamName, repoRoot) {
4586
+ return readMetadata(repoRoot, teamName);
4587
+ }
4526
4588
  function inspectTeamWorktreeCleanupSafety(teamName, repoRoot) {
4527
4589
  const metadata = readMetadataResult(repoRoot, teamName);
4528
4590
  const entries = metadata.entries;
@@ -4650,10 +4712,10 @@ async function readJsonSafe3(filePath) {
4650
4712
  }
4651
4713
  }
4652
4714
  async function writeAtomic2(filePath, data) {
4653
- const { writeFile: writeFile6 } = await import("fs/promises");
4715
+ const { writeFile: writeFile8 } = await import("fs/promises");
4654
4716
  await mkdir5(dirname10(filePath), { recursive: true });
4655
4717
  const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
4656
- await writeFile6(tmpPath, data, "utf-8");
4718
+ await writeFile8(tmpPath, data, "utf-8");
4657
4719
  const { rename: rename3 } = await import("fs/promises");
4658
4720
  await rename3(tmpPath, filePath);
4659
4721
  }
@@ -5334,54 +5396,1039 @@ function parseCliWorkerVerdict(raw) {
5334
5396
  if (typeof summary !== "string") {
5335
5397
  throw new Error("verdict_missing_summary");
5336
5398
  }
5337
- const findingsRaw = obj.findings;
5338
- if (!Array.isArray(findingsRaw)) {
5339
- throw new Error("verdict_findings_not_array");
5399
+ const findingsRaw = obj.findings;
5400
+ if (!Array.isArray(findingsRaw)) {
5401
+ throw new Error("verdict_findings_not_array");
5402
+ }
5403
+ const findings = findingsRaw.map((entry, idx) => {
5404
+ if (!entry || typeof entry !== "object") {
5405
+ throw new Error(`verdict_finding_${idx}_not_object`);
5406
+ }
5407
+ const f = entry;
5408
+ const severity = f.severity;
5409
+ if (typeof severity !== "string" || !VALID_SEVERITIES.has(severity)) {
5410
+ throw new Error(`verdict_finding_${idx}_invalid_severity:${String(severity)}`);
5411
+ }
5412
+ const message = f.message;
5413
+ if (typeof message !== "string" || !message) {
5414
+ throw new Error(`verdict_finding_${idx}_missing_message`);
5415
+ }
5416
+ const finding = {
5417
+ severity,
5418
+ message
5419
+ };
5420
+ if (typeof f.file === "string" && f.file) finding.file = f.file;
5421
+ if (typeof f.line === "number" && Number.isFinite(f.line)) finding.line = f.line;
5422
+ return finding;
5423
+ });
5424
+ return {
5425
+ role,
5426
+ task_id: taskId,
5427
+ verdict,
5428
+ summary,
5429
+ findings
5430
+ };
5431
+ }
5432
+ function cliWorkerOutputFilePath(teamStateRootAbs, workerName) {
5433
+ return `${teamStateRootAbs.replaceAll("\\", "/")}/workers/${workerName}/verdict.json`;
5434
+ }
5435
+ var CONTRACT_ROLES, VALID_VERDICTS, VALID_SEVERITIES;
5436
+ var init_cli_worker_contract = __esm({
5437
+ "src/team/cli-worker-contract.ts"() {
5438
+ "use strict";
5439
+ CONTRACT_ROLES = /* @__PURE__ */ new Set([
5440
+ "critic",
5441
+ "code-reviewer",
5442
+ "security-reviewer",
5443
+ "test-engineer"
5444
+ ]);
5445
+ VALID_VERDICTS = /* @__PURE__ */ new Set(["approve", "revise", "reject"]);
5446
+ VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "major", "minor", "nit"]);
5447
+ }
5448
+ });
5449
+
5450
+ // src/team/runtime-flags.ts
5451
+ function isRuntimeV2Enabled(env = process.env) {
5452
+ const raw = env.OMC_RUNTIME_V2;
5453
+ if (!raw) return true;
5454
+ const normalized = raw.trim().toLowerCase();
5455
+ return !["0", "false", "no", "off"].includes(normalized);
5456
+ }
5457
+ var init_runtime_flags = __esm({
5458
+ "src/team/runtime-flags.ts"() {
5459
+ "use strict";
5460
+ }
5461
+ });
5462
+
5463
+ // src/team/merge-coordinator.ts
5464
+ import { execFileSync as execFileSync3 } from "node:child_process";
5465
+ function validateBranchName(branch) {
5466
+ if (!BRANCH_NAME_RE.test(branch)) {
5467
+ throw new Error(`Invalid branch name: "${branch}" \u2014 must match ${BRANCH_NAME_RE}`);
5468
+ }
5469
+ }
5470
+ function checkMergeConflicts(workerBranch, baseBranch, repoRoot) {
5471
+ validateBranchName(workerBranch);
5472
+ validateBranchName(baseBranch);
5473
+ try {
5474
+ execFileSync3(
5475
+ "git",
5476
+ ["merge-tree", "--write-tree", baseBranch, workerBranch],
5477
+ { cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5478
+ );
5479
+ return [];
5480
+ } catch (err) {
5481
+ const error = err;
5482
+ if (error.status === 1 && typeof error.stdout === "string") {
5483
+ const lines = error.stdout.split("\n");
5484
+ const conflicts = [];
5485
+ for (const line of lines) {
5486
+ const match = line.match(/^CONFLICT\s.*?:\s+.*?\s+in\s+(.+)$/);
5487
+ if (match) {
5488
+ conflicts.push(match[1].trim());
5489
+ }
5490
+ }
5491
+ return conflicts.length > 0 ? conflicts : ["(merge-tree reported conflicts)"];
5492
+ }
5493
+ }
5494
+ const mergeBase = execFileSync3(
5495
+ "git",
5496
+ ["merge-base", baseBranch, workerBranch],
5497
+ { cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5498
+ ).trim();
5499
+ const baseDiff = execFileSync3(
5500
+ "git",
5501
+ ["diff", "--name-only", mergeBase, baseBranch],
5502
+ { cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5503
+ ).trim();
5504
+ const workerDiff = execFileSync3(
5505
+ "git",
5506
+ ["diff", "--name-only", mergeBase, workerBranch],
5507
+ { cwd: repoRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5508
+ ).trim();
5509
+ if (!baseDiff || !workerDiff) {
5510
+ return [];
5511
+ }
5512
+ const baseFiles = new Set(baseDiff.split("\n").filter((f) => f));
5513
+ const workerFiles = workerDiff.split("\n").filter((f) => f);
5514
+ return workerFiles.filter((f) => baseFiles.has(f));
5515
+ }
5516
+ function mergeWorkerBranch(workerBranch, baseBranch, repoRoot) {
5517
+ validateBranchName(workerBranch);
5518
+ validateBranchName(baseBranch);
5519
+ const workerName = workerBranch.split("/").pop() || workerBranch;
5520
+ try {
5521
+ try {
5522
+ execFileSync3("git", ["diff-index", "--quiet", "HEAD", "--"], {
5523
+ cwd: repoRoot,
5524
+ stdio: "pipe"
5525
+ });
5526
+ } catch {
5527
+ throw new Error("Working tree has uncommitted changes \u2014 commit or stash before merging");
5528
+ }
5529
+ execFileSync3("git", ["checkout", baseBranch], {
5530
+ cwd: repoRoot,
5531
+ stdio: "pipe"
5532
+ });
5533
+ execFileSync3("git", ["merge", "--no-ff", "-m", `Merge ${workerBranch} into ${baseBranch}`, workerBranch], {
5534
+ cwd: repoRoot,
5535
+ stdio: "pipe"
5536
+ });
5537
+ const mergeCommit = execFileSync3("git", ["rev-parse", "HEAD"], {
5538
+ cwd: repoRoot,
5539
+ encoding: "utf-8",
5540
+ stdio: "pipe"
5541
+ }).trim();
5542
+ return {
5543
+ workerName,
5544
+ branch: workerBranch,
5545
+ success: true,
5546
+ conflicts: [],
5547
+ mergeCommit
5548
+ };
5549
+ } catch (_err) {
5550
+ try {
5551
+ execFileSync3("git", ["merge", "--abort"], { cwd: repoRoot, stdio: "pipe" });
5552
+ } catch {
5553
+ }
5554
+ const conflicts = checkMergeConflicts(workerBranch, baseBranch, repoRoot);
5555
+ return {
5556
+ workerName,
5557
+ branch: workerBranch,
5558
+ success: false,
5559
+ conflicts
5560
+ };
5561
+ }
5562
+ }
5563
+ var BRANCH_NAME_RE;
5564
+ var init_merge_coordinator = __esm({
5565
+ "src/team/merge-coordinator.ts"() {
5566
+ "use strict";
5567
+ init_git_worktree();
5568
+ BRANCH_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9/_.-]*$/;
5569
+ }
5570
+ });
5571
+
5572
+ // src/team/leader-inbox.ts
5573
+ import { appendFile as appendFile4, mkdir as mkdir7, writeFile as writeFile5 } from "fs/promises";
5574
+ import { existsSync as existsSync16 } from "fs";
5575
+ import { dirname as dirname12, join as join19 } from "path";
5576
+ function leaderInboxPath(teamName, cwd) {
5577
+ const safe = sanitizeName(teamName);
5578
+ return join19(cwd, `.omcp/state/team/${safe}/leader/inbox.md`);
5579
+ }
5580
+ async function ensureLeaderInbox(teamName, cwd) {
5581
+ const inboxPath = leaderInboxPath(teamName, cwd);
5582
+ validateResolvedPath(inboxPath, cwd);
5583
+ await mkdir7(dirname12(inboxPath), { recursive: true });
5584
+ if (!existsSync16(inboxPath)) {
5585
+ await writeFile5(inboxPath, LEADER_INBOX_HEADER, "utf-8");
5586
+ }
5587
+ return inboxPath;
5588
+ }
5589
+ async function appendToLeaderInbox(teamName, message, cwd) {
5590
+ const inboxPath = leaderInboxPath(teamName, cwd);
5591
+ validateResolvedPath(inboxPath, cwd);
5592
+ await mkdir7(dirname12(inboxPath), { recursive: true });
5593
+ await appendFile4(inboxPath, `
5594
+
5595
+ ---
5596
+ ${message}`, "utf-8");
5597
+ }
5598
+ function extendLeaderBootstrapPrompt(teamName) {
5599
+ const safe = sanitizeName(teamName);
5600
+ const path4 = `.omcp/state/team/${safe}/leader/inbox.md`;
5601
+ return `Runtime notifications appear at ${path4} \u2014 check this file periodically and after long-running operations.`;
5602
+ }
5603
+ var LEADER_INBOX_HEADER;
5604
+ var init_leader_inbox = __esm({
5605
+ "src/team/leader-inbox.ts"() {
5606
+ "use strict";
5607
+ init_tmux_session();
5608
+ init_fs_utils();
5609
+ LEADER_INBOX_HEADER = `# Leader Inbox
5610
+
5611
+ Runtime notifications (merge conflicts, rebase events, etc.) appear here.
5612
+ Check this file periodically and after long-running operations.
5613
+
5614
+ ---
5615
+ `;
5616
+ }
5617
+ });
5618
+
5619
+ // src/team/conflict-mailbox.ts
5620
+ function sanitizeConflictPath(path4) {
5621
+ return path4.replace(/[`\r\n]/g, "?");
5622
+ }
5623
+ function formatMergeConflictForLeader(args) {
5624
+ const { workerName, workerBranch, leaderBranch, conflictingFiles, mergeBaseSha, observedAt } = args;
5625
+ const ts = new Date(observedAt).toISOString();
5626
+ const safeFiles = conflictingFiles.map(sanitizeConflictPath);
5627
+ const fileList = safeFiles.map((f) => `- \`${f}\``).join("\n");
5628
+ return `### Merge conflict: ${workerName} \u2192 ${leaderBranch}
5629
+
5630
+ **Worker branch:** \`${workerBranch}\`
5631
+ **Leader branch:** \`${leaderBranch}\`
5632
+ **Merge base:** \`${mergeBaseSha}\`
5633
+ **Observed at:** ${ts}
5634
+
5635
+ **Conflicting files:**
5636
+ ${fileList}
5637
+
5638
+ **Leader: choose strategy.** To resolve, run:
5639
+
5640
+ \`\`\`sh
5641
+ git checkout ${leaderBranch} && git merge --no-ff ${workerBranch}
5642
+ # resolve conflicts in the files listed above
5643
+ git add ${safeFiles.join(" ")}
5644
+ git commit
5645
+ \`\`\`
5646
+
5647
+ Or abort with \`git merge --abort\` to defer resolution.`;
5648
+ }
5649
+ function formatRebaseConflictForWorker(args) {
5650
+ const { workerName, workerBranch, leaderBranch, conflictingFiles, baseSha, worktreePath, observedAt } = args;
5651
+ const ts = new Date(observedAt).toISOString();
5652
+ const safeFiles = conflictingFiles.map(sanitizeConflictPath);
5653
+ const fileList = safeFiles.map((f) => `- \`${f}\``).join("\n");
5654
+ return `### Rebase conflict: ${workerName} onto ${leaderBranch}
5655
+
5656
+ **Worker branch:** \`${workerBranch}\`
5657
+ **Base branch:** \`${leaderBranch}\`
5658
+ **Base SHA:** \`${baseSha}\`
5659
+ **Worktree:** \`${worktreePath}\`
5660
+ **Observed at:** ${ts}
5661
+
5662
+ **Conflicting files:**
5663
+ ${fileList}
5664
+
5665
+ Resolve conflicts in your own pane, then \`git add <files>\` and \`git rebase --continue\`.
5666
+ Cadence stays paused until \`.git/rebase-merge\` is gone.
5667
+
5668
+ Or run \`git rebase --abort\` to bail and return to the pre-rebase state.`;
5669
+ }
5670
+ var init_conflict_mailbox = __esm({
5671
+ "src/team/conflict-mailbox.ts"() {
5672
+ "use strict";
5673
+ init_worker_bootstrap();
5674
+ init_leader_inbox();
5675
+ }
5676
+ });
5677
+
5678
+ // src/team/worker-commit-cadence.ts
5679
+ import { existsSync as existsSync17, watch as fsWatch } from "fs";
5680
+ import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir8, unlink as unlink2 } from "fs/promises";
5681
+ import { join as join20, dirname as dirname13 } from "path";
5682
+ import { exec as exec2 } from "child_process";
5683
+ function assertSafeWorkerName(workerName) {
5684
+ if (!WORKER_NAME_RE.test(workerName)) {
5685
+ throw new Error(
5686
+ `Invalid worker name for shell hook: "${workerName}" \u2014 must match ${WORKER_NAME_RE}`
5687
+ );
5688
+ }
5689
+ }
5690
+ function buildHookCommand(workerName) {
5691
+ assertSafeWorkerName(workerName);
5692
+ 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 ${workerName} at $(date -Iseconds)")'`;
5693
+ }
5694
+ async function mergeSettingsWithHook(settingsPath, hookCommand) {
5695
+ let existing = { hooks: { PostToolUse: [] } };
5696
+ try {
5697
+ const raw = await readFile9(settingsPath, "utf-8");
5698
+ const parsed = JSON.parse(raw);
5699
+ existing = {
5700
+ ...parsed,
5701
+ hooks: {
5702
+ PostToolUse: [],
5703
+ ...parsed.hooks ?? {}
5704
+ }
5705
+ };
5706
+ } catch {
5707
+ }
5708
+ const filteredHooks = (existing.hooks.PostToolUse ?? []).filter(
5709
+ (h) => h.matcher !== HOOK_MATCHER
5710
+ );
5711
+ const newEntry = {
5712
+ matcher: HOOK_MATCHER,
5713
+ hooks: [{ type: "command", command: hookCommand }]
5714
+ };
5715
+ return {
5716
+ ...existing,
5717
+ hooks: {
5718
+ ...existing.hooks,
5719
+ PostToolUse: [...filteredHooks, newEntry]
5720
+ }
5721
+ };
5722
+ }
5723
+ async function installPostToolUseHook(worktreePath, workerName) {
5724
+ assertSafeWorkerName(workerName);
5725
+ if (isHookPaused(worktreePath)) {
5726
+ return;
5727
+ }
5728
+ const claudeDir = join20(worktreePath, ".claude");
5729
+ await mkdir8(claudeDir, { recursive: true });
5730
+ const settingsPath = join20(claudeDir, "settings.json");
5731
+ const hookCommand = buildHookCommand(workerName);
5732
+ const merged = await mergeSettingsWithHook(settingsPath, hookCommand);
5733
+ await writeFile6(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
5734
+ }
5735
+ async function pauseHookViaSentinel(worktreePath) {
5736
+ const sentinelPath = join20(worktreePath, SENTINEL_FILENAME);
5737
+ await mkdir8(dirname13(sentinelPath), { recursive: true });
5738
+ await writeFile6(sentinelPath, "", "utf-8");
5739
+ }
5740
+ async function resumeHookViaSentinel(worktreePath) {
5741
+ const sentinelPath = join20(worktreePath, SENTINEL_FILENAME);
5742
+ try {
5743
+ await unlink2(sentinelPath);
5744
+ } catch {
5745
+ }
5746
+ }
5747
+ function isHookPaused(worktreePath) {
5748
+ return existsSync17(join20(worktreePath, SENTINEL_FILENAME));
5749
+ }
5750
+ function startFallbackPoller(worktreePath, workerName, opts) {
5751
+ assertSafeWorkerName(workerName);
5752
+ const debounceMs = opts?.intervalMs ?? DEFAULT_POLL_DEBOUNCE_MS;
5753
+ let debounceTimer = null;
5754
+ let stopped = false;
5755
+ const runAutoCommit = () => {
5756
+ if (stopped) return;
5757
+ if (isHookPaused(worktreePath)) return;
5758
+ const cmd = buildHookCommand(workerName);
5759
+ exec2(cmd, { cwd: worktreePath }, (_err) => {
5760
+ });
5761
+ };
5762
+ const scheduleDebounce = () => {
5763
+ if (stopped) return;
5764
+ if (debounceTimer !== null) {
5765
+ clearTimeout(debounceTimer);
5766
+ }
5767
+ debounceTimer = setTimeout(() => {
5768
+ debounceTimer = null;
5769
+ runAutoCommit();
5770
+ }, debounceMs);
5771
+ };
5772
+ const watcher = fsWatch(worktreePath, { recursive: true }, (eventType, filename) => {
5773
+ if (stopped) return;
5774
+ if (filename && (filename.startsWith(".git") || filename.startsWith(".git/"))) return;
5775
+ scheduleDebounce();
5776
+ });
5777
+ return {
5778
+ stop() {
5779
+ stopped = true;
5780
+ if (debounceTimer !== null) {
5781
+ clearTimeout(debounceTimer);
5782
+ debounceTimer = null;
5783
+ }
5784
+ watcher.close();
5785
+ }
5786
+ };
5787
+ }
5788
+ async function installCommitCadence(ctx) {
5789
+ if (!ctx.enabled) {
5790
+ return { method: "none" };
5791
+ }
5792
+ if (ctx.agentType === "claude") {
5793
+ await installPostToolUseHook(ctx.worktreePath, ctx.workerName);
5794
+ return { method: "hook" };
5795
+ }
5796
+ return { method: "fallback-poll" };
5797
+ }
5798
+ async function uninstallCommitCadence(ctx) {
5799
+ if (ctx.agentType !== "claude") return;
5800
+ const settingsPath = join20(ctx.worktreePath, ".claude", "settings.json");
5801
+ try {
5802
+ const raw = await readFile9(settingsPath, "utf-8");
5803
+ const parsed = JSON.parse(raw);
5804
+ const filtered = (parsed.hooks?.PostToolUse ?? []).filter(
5805
+ (h) => h.matcher !== HOOK_MATCHER
5806
+ );
5807
+ const updated = {
5808
+ ...parsed,
5809
+ hooks: {
5810
+ ...parsed.hooks,
5811
+ PostToolUse: filtered
5812
+ }
5813
+ };
5814
+ await writeFile6(settingsPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
5815
+ } catch {
5816
+ }
5817
+ }
5818
+ var SENTINEL_FILENAME, HOOK_MATCHER, DEFAULT_POLL_DEBOUNCE_MS, WORKER_NAME_RE;
5819
+ var init_worker_commit_cadence = __esm({
5820
+ "src/team/worker-commit-cadence.ts"() {
5821
+ "use strict";
5822
+ SENTINEL_FILENAME = ".hook-paused";
5823
+ HOOK_MATCHER = "Write|Edit|MultiEdit";
5824
+ DEFAULT_POLL_DEBOUNCE_MS = 3e3;
5825
+ WORKER_NAME_RE = /^[A-Za-z0-9_-]{1,50}$/;
5826
+ }
5827
+ });
5828
+
5829
+ // src/team/merge-orchestrator.ts
5830
+ import { execFileSync as execFileSync4 } from "node:child_process";
5831
+ import { existsSync as existsSync18 } from "node:fs";
5832
+ import { mkdir as mkdir9, appendFile as appendFile5 } from "node:fs/promises";
5833
+ import { dirname as dirname14, join as join21 } from "node:path";
5834
+ function mergerWorktreePathFor(repoRoot, teamName) {
5835
+ return join21(repoRoot, ".omcp", "team", sanitizeName(teamName), "merger");
5836
+ }
5837
+ function persistedStatePath(repoRoot, teamName) {
5838
+ return join21(
5839
+ repoRoot,
5840
+ ".omcp",
5841
+ "state",
5842
+ "team",
5843
+ sanitizeName(teamName),
5844
+ "auto-merge-state.json"
5845
+ );
5846
+ }
5847
+ function teardownAuditPath(repoRoot, teamName) {
5848
+ return join21(
5849
+ repoRoot,
5850
+ ".omcp",
5851
+ "state",
5852
+ "team",
5853
+ sanitizeName(teamName),
5854
+ "teardown-audit.jsonl"
5855
+ );
5856
+ }
5857
+ function orchestratorEventLogPath(repoRoot, teamName) {
5858
+ return join21(
5859
+ repoRoot,
5860
+ ".omcp",
5861
+ "state",
5862
+ "team",
5863
+ sanitizeName(teamName),
5864
+ "orchestrator-events.jsonl"
5865
+ );
5866
+ }
5867
+ function assertLeaderBranchAllowed(leaderBranch) {
5868
+ const stripped = leaderBranch.replace(/^refs\/heads\//i, "").toLowerCase();
5869
+ if (stripped === "main" || stripped === "master") {
5870
+ throw new Error("auto-merge refuses main/master leader branch \u2014 use a feature branch");
5871
+ }
5872
+ }
5873
+ function assertRuntimeV2Gate() {
5874
+ if (!isRuntimeV2Enabled()) {
5875
+ throw new Error("auto-merge requires runtime v2 (OMC_RUNTIME_V2 is explicitly disabled).");
5876
+ }
5877
+ }
5878
+ async function appendEvent(repoRoot, teamName, event) {
5879
+ const path4 = orchestratorEventLogPath(repoRoot, teamName);
5880
+ await mkdir9(dirname14(path4), { recursive: true });
5881
+ const full = {
5882
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
5883
+ team: teamName,
5884
+ ...event
5885
+ };
5886
+ await appendFile5(path4, `${JSON.stringify(full)}
5887
+ `, "utf-8");
5888
+ }
5889
+ function createMutex() {
5890
+ let lock = Promise.resolve();
5891
+ return (fn) => {
5892
+ const next = lock.then(fn, fn);
5893
+ lock = next.catch(() => void 0);
5894
+ return next;
5895
+ };
5896
+ }
5897
+ function gitRevParseHead(repoRoot, branch) {
5898
+ return execFileSync4("git", ["rev-parse", `refs/heads/${branch}`], {
5899
+ cwd: repoRoot,
5900
+ encoding: "utf-8",
5901
+ stdio: "pipe"
5902
+ }).trim();
5903
+ }
5904
+ function gitPath(worktreePath, gitPathName) {
5905
+ try {
5906
+ const resolved = execFileSync4("git", ["rev-parse", "--git-path", gitPathName], {
5907
+ cwd: worktreePath,
5908
+ encoding: "utf-8",
5909
+ stdio: "pipe"
5910
+ }).trim();
5911
+ if (resolved) return resolved;
5912
+ } catch {
5913
+ }
5914
+ return join21(worktreePath, ".git", gitPathName);
5915
+ }
5916
+ function isRebaseInProgress(worktreePath) {
5917
+ return existsSync18(gitPath(worktreePath, "rebase-merge"));
5918
+ }
5919
+ function isWorktreeRegistered(repoRoot, wtPath) {
5920
+ try {
5921
+ const out = execFileSync4("git", ["worktree", "list", "--porcelain"], {
5922
+ cwd: repoRoot,
5923
+ encoding: "utf-8",
5924
+ stdio: "pipe"
5925
+ });
5926
+ for (const line of out.split("\n")) {
5927
+ if (line.startsWith("worktree ")) {
5928
+ if (line.slice("worktree ".length).trim() === wtPath) return true;
5929
+ }
5930
+ }
5931
+ } catch {
5932
+ }
5933
+ return false;
5934
+ }
5935
+ function ensureMergerWorktree(repoRoot, mergerPath, leaderBranch) {
5936
+ ensureDirWithMode(dirname14(mergerPath));
5937
+ if (existsSync18(mergerPath) && isWorktreeRegistered(repoRoot, mergerPath)) {
5938
+ return;
5939
+ }
5940
+ execFileSync4("git", ["worktree", "add", "--force", mergerPath, leaderBranch], {
5941
+ cwd: repoRoot,
5942
+ stdio: "pipe"
5943
+ });
5944
+ }
5945
+ function preflightMergerWorktree(mergerPath, leaderBranch) {
5946
+ try {
5947
+ execFileSync4("git", ["fetch", "--no-tags", "origin", leaderBranch], {
5948
+ cwd: mergerPath,
5949
+ stdio: "pipe"
5950
+ });
5951
+ } catch {
5952
+ }
5953
+ execFileSync4("git", ["reset", "--hard", leaderBranch], {
5954
+ cwd: mergerPath,
5955
+ stdio: "pipe"
5956
+ });
5957
+ }
5958
+ function parseUUFiles(porcelainOutput) {
5959
+ const files = [];
5960
+ for (const line of porcelainOutput.split("\n")) {
5961
+ if (line.startsWith("UU ")) {
5962
+ files.push(line.slice(3).trim());
5963
+ } else if (line.startsWith("AA ") || line.startsWith("DD ")) {
5964
+ files.push(line.slice(3).trim());
5965
+ }
5966
+ }
5967
+ return files;
5968
+ }
5969
+ async function startMergeOrchestrator(config) {
5970
+ assertRuntimeV2Gate();
5971
+ assertLeaderBranchAllowed(config.leaderBranch);
5972
+ validateBranchName(config.leaderBranch);
5973
+ const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
5974
+ const drainTimeoutMs = config.drainTimeoutMs ?? DEFAULT_DRAIN_TIMEOUT_MS;
5975
+ const mergerPath = mergerWorktreePathFor(config.repoRoot, config.teamName);
5976
+ validateResolvedPath(mergerPath, config.repoRoot);
5977
+ ensureMergerWorktree(config.repoRoot, mergerPath, config.leaderBranch);
5978
+ await ensureLeaderInbox(config.teamName, config.cwd);
5979
+ const persistedPath = persistedStatePath(config.repoRoot, config.teamName);
5980
+ let persisted = { lastShas: {} };
5981
+ if (existsSync18(persistedPath)) {
5982
+ try {
5983
+ const { readFileSync: readFileSync13 } = await import("node:fs");
5984
+ persisted = JSON.parse(readFileSync13(persistedPath, "utf-8"));
5985
+ } catch {
5986
+ persisted = { lastShas: {} };
5987
+ }
5988
+ }
5989
+ const workers = /* @__PURE__ */ new Map();
5990
+ const pausedWorkers = /* @__PURE__ */ new Set();
5991
+ const mutex = createMutex();
5992
+ let stopped = false;
5993
+ function persistState() {
5994
+ const payload = {
5995
+ lastShas: Object.fromEntries(
5996
+ Array.from(workers.values()).map((w) => [w.workerName, w.lastObservedSha])
5997
+ )
5998
+ };
5999
+ atomicWriteJson(persistedPath, payload);
6000
+ }
6001
+ async function fanOutRebase(triggeringWorker) {
6002
+ for (const other of workers.values()) {
6003
+ if (other.workerName === triggeringWorker) continue;
6004
+ const wtPath = other.workerWorktreePath;
6005
+ if (isRebaseInProgress(wtPath)) {
6006
+ await appendEvent(config.repoRoot, config.teamName, {
6007
+ type: "rebase_skipped_in_progress",
6008
+ worker: other.workerName,
6009
+ reason: "rebase-already-in-progress"
6010
+ });
6011
+ continue;
6012
+ }
6013
+ await appendEvent(config.repoRoot, config.teamName, {
6014
+ type: "rebase_triggered",
6015
+ worker: other.workerName
6016
+ });
6017
+ await pauseHookViaSentinel(wtPath);
6018
+ pausedWorkers.add(other.workerName);
6019
+ try {
6020
+ execFileSync4("git", ["fetch", "--no-tags", "origin", config.leaderBranch], {
6021
+ cwd: wtPath,
6022
+ stdio: "pipe"
6023
+ });
6024
+ } catch {
6025
+ }
6026
+ try {
6027
+ execFileSync4("git", ["rebase", config.leaderBranch], {
6028
+ cwd: wtPath,
6029
+ stdio: "pipe"
6030
+ });
6031
+ await resumeHookViaSentinel(wtPath);
6032
+ pausedWorkers.delete(other.workerName);
6033
+ await appendEvent(config.repoRoot, config.teamName, {
6034
+ type: "rebase_succeeded",
6035
+ worker: other.workerName
6036
+ });
6037
+ } catch {
6038
+ let conflictingFiles = [];
6039
+ try {
6040
+ const status = execFileSync4("git", ["status", "--porcelain"], {
6041
+ cwd: wtPath,
6042
+ encoding: "utf-8",
6043
+ stdio: "pipe"
6044
+ });
6045
+ conflictingFiles = parseUUFiles(status);
6046
+ } catch {
6047
+ conflictingFiles = ["(rebase status unavailable)"];
6048
+ }
6049
+ const baseSha = (() => {
6050
+ try {
6051
+ return execFileSync4("git", ["rev-parse", `refs/heads/${config.leaderBranch}`], {
6052
+ cwd: config.repoRoot,
6053
+ encoding: "utf-8",
6054
+ stdio: "pipe"
6055
+ }).trim();
6056
+ } catch {
6057
+ return "unknown";
6058
+ }
6059
+ })();
6060
+ const message = formatRebaseConflictForWorker({
6061
+ workerName: other.workerName,
6062
+ workerBranch: other.workerBranch,
6063
+ leaderBranch: config.leaderBranch,
6064
+ conflictingFiles,
6065
+ baseSha,
6066
+ worktreePath: wtPath,
6067
+ observedAt: Date.now()
6068
+ });
6069
+ try {
6070
+ await appendToInbox(config.teamName, other.workerName, message, config.cwd);
6071
+ } catch {
6072
+ }
6073
+ await appendEvent(config.repoRoot, config.teamName, {
6074
+ type: "rebase_conflict",
6075
+ worker: other.workerName,
6076
+ data: { conflictingFiles }
6077
+ });
6078
+ }
6079
+ }
6080
+ }
6081
+ async function attemptMergeForWorker(entry) {
6082
+ await mutex(async () => {
6083
+ const targetSha = entry.lastObservedSha;
6084
+ await appendEvent(config.repoRoot, config.teamName, {
6085
+ type: "merge_attempted",
6086
+ worker: entry.workerName,
6087
+ data: { targetSha }
6088
+ });
6089
+ try {
6090
+ preflightMergerWorktree(mergerPath, config.leaderBranch);
6091
+ } catch (err) {
6092
+ const reason = err instanceof Error ? err.message : String(err);
6093
+ entry.consecutiveFailures += 1;
6094
+ await appendEvent(config.repoRoot, config.teamName, {
6095
+ type: "merge_conflict",
6096
+ worker: entry.workerName,
6097
+ reason: `preflight_failed:${reason}`
6098
+ });
6099
+ return;
6100
+ }
6101
+ const conflicts = checkMergeConflicts(
6102
+ entry.workerBranch,
6103
+ config.leaderBranch,
6104
+ mergerPath
6105
+ );
6106
+ if (conflicts.length > 0) {
6107
+ let mergeBaseSha = "unknown";
6108
+ try {
6109
+ mergeBaseSha = execFileSync4(
6110
+ "git",
6111
+ ["merge-base", config.leaderBranch, entry.workerBranch],
6112
+ { cwd: mergerPath, encoding: "utf-8", stdio: "pipe" }
6113
+ ).trim();
6114
+ } catch {
6115
+ }
6116
+ const message = formatMergeConflictForLeader({
6117
+ workerName: entry.workerName,
6118
+ workerBranch: entry.workerBranch,
6119
+ leaderBranch: config.leaderBranch,
6120
+ conflictingFiles: conflicts,
6121
+ mergeBaseSha,
6122
+ observedAt: Date.now()
6123
+ });
6124
+ try {
6125
+ await appendToLeaderInbox(config.teamName, message, config.cwd);
6126
+ } catch {
6127
+ }
6128
+ await appendEvent(config.repoRoot, config.teamName, {
6129
+ type: "merge_conflict",
6130
+ worker: entry.workerName,
6131
+ data: { conflictingFiles: conflicts, mergeBaseSha }
6132
+ });
6133
+ entry.consecutiveFailures += 1;
6134
+ return;
6135
+ }
6136
+ const result = mergeWorkerBranch(
6137
+ entry.workerBranch,
6138
+ config.leaderBranch,
6139
+ mergerPath
6140
+ );
6141
+ if (!result.success) {
6142
+ const message = formatMergeConflictForLeader({
6143
+ workerName: entry.workerName,
6144
+ workerBranch: entry.workerBranch,
6145
+ leaderBranch: config.leaderBranch,
6146
+ conflictingFiles: result.conflicts.length > 0 ? result.conflicts : ["(merge failed after clean check)"],
6147
+ mergeBaseSha: "unknown",
6148
+ observedAt: Date.now()
6149
+ });
6150
+ try {
6151
+ await appendToLeaderInbox(config.teamName, message, config.cwd);
6152
+ } catch {
6153
+ }
6154
+ await appendEvent(config.repoRoot, config.teamName, {
6155
+ type: "merge_conflict",
6156
+ worker: entry.workerName,
6157
+ data: { conflictingFiles: result.conflicts }
6158
+ });
6159
+ entry.consecutiveFailures += 1;
6160
+ return;
6161
+ }
6162
+ entry.lastMergedSha = targetSha;
6163
+ entry.consecutiveFailures = 0;
6164
+ await appendEvent(config.repoRoot, config.teamName, {
6165
+ type: "merge_succeeded",
6166
+ worker: entry.workerName,
6167
+ data: { mergeCommit: result.mergeCommit, targetSha }
6168
+ });
6169
+ if (stopped) return;
6170
+ await fanOutRebase(entry.workerName);
6171
+ });
6172
+ }
6173
+ async function runPollOnce() {
6174
+ if (stopped) return;
6175
+ for (const entry of workers.values()) {
6176
+ const skipModulo = Math.min(30, Math.pow(2, entry.consecutiveFailures));
6177
+ if (skipModulo > 1 && pollTickCount % skipModulo !== 0) {
6178
+ continue;
6179
+ }
6180
+ if (pausedWorkers.has(entry.workerName)) {
6181
+ if (!isRebaseInProgress(entry.workerWorktreePath)) {
6182
+ await handleRebaseResolution(entry);
6183
+ } else {
6184
+ continue;
6185
+ }
6186
+ }
6187
+ let currentSha;
6188
+ try {
6189
+ currentSha = gitRevParseHead(config.repoRoot, entry.workerBranch);
6190
+ } catch (err) {
6191
+ entry.consecutiveFailures += 1;
6192
+ const reason = err instanceof Error ? err.message : String(err);
6193
+ await appendEvent(config.repoRoot, config.teamName, {
6194
+ type: "commit_observed",
6195
+ worker: entry.workerName,
6196
+ reason: `rev_parse_failed:${reason}`
6197
+ });
6198
+ continue;
6199
+ }
6200
+ if (currentSha && currentSha !== entry.lastObservedSha) {
6201
+ entry.lastObservedSha = currentSha;
6202
+ try {
6203
+ persistState();
6204
+ } catch {
6205
+ }
6206
+ await appendEvent(config.repoRoot, config.teamName, {
6207
+ type: "commit_observed",
6208
+ worker: entry.workerName,
6209
+ data: { sha: currentSha }
6210
+ });
6211
+ try {
6212
+ await attemptMergeForWorker(entry);
6213
+ } catch (err) {
6214
+ entry.consecutiveFailures += 1;
6215
+ const reason = err instanceof Error ? err.message : String(err);
6216
+ await appendEvent(config.repoRoot, config.teamName, {
6217
+ type: "merge_conflict",
6218
+ worker: entry.workerName,
6219
+ reason: `merge_threw:${reason}`
6220
+ });
6221
+ }
6222
+ }
6223
+ }
6224
+ }
6225
+ async function handleRebaseResolution(entry) {
6226
+ pausedWorkers.delete(entry.workerName);
6227
+ try {
6228
+ const status = execFileSync4("git", ["status", "--porcelain"], {
6229
+ cwd: entry.workerWorktreePath,
6230
+ encoding: "utf-8",
6231
+ stdio: "pipe"
6232
+ }).trim();
6233
+ if (status.length > 0) {
6234
+ const dirtyFiles = status.split("\n").map((l) => l.trim().replace(/^\S+\s+/, "")).filter((s) => s.length > 0);
6235
+ const audit = `## Auto-commit audit: the following files were modified during rebase pause and will be folded into the next auto-commit:
6236
+ ${dirtyFiles.map((f) => `- \`${f}\``).join("\n")}`;
6237
+ try {
6238
+ await appendToInbox(config.teamName, entry.workerName, audit, config.cwd);
6239
+ } catch {
6240
+ }
6241
+ }
6242
+ } catch {
6243
+ }
6244
+ await resumeHookViaSentinel(entry.workerWorktreePath);
6245
+ await appendEvent(config.repoRoot, config.teamName, {
6246
+ type: "rebase_resolved",
6247
+ worker: entry.workerName
6248
+ });
6249
+ }
6250
+ let pollTickCount = 0;
6251
+ const interval = setInterval(() => {
6252
+ pollTickCount += 1;
6253
+ void runPollOnce().catch(() => {
6254
+ });
6255
+ }, pollIntervalMs);
6256
+ if (typeof interval.unref === "function") interval.unref();
6257
+ return {
6258
+ async registerWorker(workerName) {
6259
+ if (workers.has(workerName)) return;
6260
+ const workerBranch = getBranchName(config.teamName, workerName);
6261
+ validateBranchName(workerBranch);
6262
+ const wtPath = getWorktreePath(config.repoRoot, config.teamName, workerName);
6263
+ let seedSha = persisted.lastShas[workerName] ?? "";
6264
+ if (!seedSha) {
6265
+ try {
6266
+ seedSha = gitRevParseHead(config.repoRoot, workerBranch);
6267
+ } catch {
6268
+ seedSha = "";
6269
+ }
6270
+ }
6271
+ workers.set(workerName, {
6272
+ workerName,
6273
+ workerBranch,
6274
+ workerWorktreePath: wtPath,
6275
+ lastObservedSha: seedSha,
6276
+ lastMergedSha: seedSha,
6277
+ consecutiveFailures: 0
6278
+ });
6279
+ try {
6280
+ persistState();
6281
+ } catch {
6282
+ }
6283
+ },
6284
+ async unregisterWorker(workerName) {
6285
+ workers.delete(workerName);
6286
+ pausedWorkers.delete(workerName);
6287
+ try {
6288
+ persistState();
6289
+ } catch {
6290
+ }
6291
+ },
6292
+ async pollOnce() {
6293
+ await runPollOnce();
6294
+ },
6295
+ async drainAndStop() {
6296
+ stopped = true;
6297
+ clearInterval(interval);
6298
+ const start = Date.now();
6299
+ const unmerged = [];
6300
+ const candidates = Array.from(workers.values()).filter(
6301
+ (w) => w.lastObservedSha && w.lastObservedSha !== w.lastMergedSha
6302
+ );
6303
+ for (const entry of candidates) {
6304
+ const remaining = drainTimeoutMs - (Date.now() - start);
6305
+ if (remaining <= 0) {
6306
+ unmerged.push({ workerName: entry.workerName, reason: "drain-timeout" });
6307
+ continue;
6308
+ }
6309
+ const merged = await Promise.race([
6310
+ (async () => {
6311
+ try {
6312
+ await attemptMergeForWorker(entry);
6313
+ return true;
6314
+ } catch {
6315
+ return false;
6316
+ }
6317
+ })(),
6318
+ new Promise((resolve5) => {
6319
+ const t = setTimeout(() => resolve5(false), remaining);
6320
+ if (typeof t.unref === "function") t.unref();
6321
+ })
6322
+ ]);
6323
+ if (!merged || entry.lastMergedSha !== entry.lastObservedSha) {
6324
+ unmerged.push({
6325
+ workerName: entry.workerName,
6326
+ reason: merged ? "merge-conflict" : "drain-timeout"
6327
+ });
6328
+ }
6329
+ }
6330
+ if (unmerged.length > 0) {
6331
+ const auditPath = teardownAuditPath(config.repoRoot, config.teamName);
6332
+ await mkdir9(dirname14(auditPath), { recursive: true });
6333
+ for (const u of unmerged) {
6334
+ const row = JSON.stringify({
6335
+ type: "unmerged_at_shutdown",
6336
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
6337
+ team: config.teamName,
6338
+ worker: u.workerName,
6339
+ reason: u.reason
6340
+ });
6341
+ try {
6342
+ await appendFile5(auditPath, `${row}
6343
+ `, "utf-8");
6344
+ } catch {
6345
+ }
6346
+ }
6347
+ const message = `## Teardown audit: unmerged worker branches at shutdown
6348
+
6349
+ ${unmerged.map((u) => `- ${u.workerName}: ${u.reason}`).join("\n")}`;
6350
+ try {
6351
+ await appendToLeaderInbox(config.teamName, message, config.cwd);
6352
+ } catch {
6353
+ }
6354
+ }
6355
+ return { unmerged };
6356
+ },
6357
+ getState() {
6358
+ return {
6359
+ workers: Array.from(workers.keys()),
6360
+ lastShas: Object.fromEntries(
6361
+ Array.from(workers.values()).map((w) => [w.workerName, w.lastObservedSha])
6362
+ ),
6363
+ mergerWorktreePath: mergerPath
6364
+ };
6365
+ }
6366
+ };
6367
+ }
6368
+ async function recoverFromRestart(config) {
6369
+ const persistedPath = persistedStatePath(config.repoRoot, config.teamName);
6370
+ let persistedShasLoaded = 0;
6371
+ if (existsSync18(persistedPath)) {
6372
+ try {
6373
+ const { readFileSync: readFileSync13 } = await import("node:fs");
6374
+ const persisted = JSON.parse(readFileSync13(persistedPath, "utf-8"));
6375
+ persistedShasLoaded = Object.keys(persisted.lastShas ?? {}).length;
6376
+ } catch {
6377
+ persistedShasLoaded = 0;
6378
+ }
6379
+ }
6380
+ const orphanedRebases = [];
6381
+ let entries = [];
6382
+ try {
6383
+ entries = listTeamWorktrees(config.teamName, config.repoRoot).map((w) => ({
6384
+ workerName: w.workerName,
6385
+ path: w.path
6386
+ }));
6387
+ } catch {
6388
+ entries = [];
5340
6389
  }
5341
- const findings = findingsRaw.map((entry, idx) => {
5342
- if (!entry || typeof entry !== "object") {
5343
- throw new Error(`verdict_finding_${idx}_not_object`);
5344
- }
5345
- const f = entry;
5346
- const severity = f.severity;
5347
- if (typeof severity !== "string" || !VALID_SEVERITIES.has(severity)) {
5348
- throw new Error(`verdict_finding_${idx}_invalid_severity:${String(severity)}`);
6390
+ for (const entry of entries) {
6391
+ if (!isRebaseInProgress(entry.path)) continue;
6392
+ orphanedRebases.push(entry.workerName);
6393
+ const message = `### Runtime restart recovery \u2014 your branch is mid-rebase
6394
+
6395
+ Runtime restarted while your branch was mid-rebase onto \`${config.leaderBranch}\`.
6396
+
6397
+ **Worktree:** \`${entry.path}\`
6398
+
6399
+ Cadence remains paused. Resolve and \`git rebase --continue\`, or \`git rebase --abort\` to bail.
6400
+ Cadence resumes once the git rebase state is gone.`;
6401
+ try {
6402
+ await appendToInbox(config.teamName, entry.workerName, message, config.cwd);
6403
+ } catch {
5349
6404
  }
5350
- const message = f.message;
5351
- if (typeof message !== "string" || !message) {
5352
- throw new Error(`verdict_finding_${idx}_missing_message`);
6405
+ }
6406
+ if (orphanedRebases.length > 0 || persistedShasLoaded > 0) {
6407
+ try {
6408
+ await appendEvent(config.repoRoot, config.teamName, {
6409
+ type: "restart_recovery",
6410
+ data: { orphanedRebases, persistedShasLoaded }
6411
+ });
6412
+ } catch {
5353
6413
  }
5354
- const finding = {
5355
- severity,
5356
- message
5357
- };
5358
- if (typeof f.file === "string" && f.file) finding.file = f.file;
5359
- if (typeof f.line === "number" && Number.isFinite(f.line)) finding.line = f.line;
5360
- return finding;
5361
- });
5362
- return {
5363
- role,
5364
- task_id: taskId,
5365
- verdict,
5366
- summary,
5367
- findings
5368
- };
5369
- }
5370
- function cliWorkerOutputFilePath(teamStateRootAbs, workerName) {
5371
- return `${teamStateRootAbs.replaceAll("\\", "/")}/workers/${workerName}/verdict.json`;
6414
+ }
6415
+ return { orphanedRebases, persistedShasLoaded };
5372
6416
  }
5373
- var CONTRACT_ROLES, VALID_VERDICTS, VALID_SEVERITIES;
5374
- var init_cli_worker_contract = __esm({
5375
- "src/team/cli-worker-contract.ts"() {
6417
+ var DEFAULT_POLL_INTERVAL_MS, DEFAULT_DRAIN_TIMEOUT_MS;
6418
+ var init_merge_orchestrator = __esm({
6419
+ "src/team/merge-orchestrator.ts"() {
5376
6420
  "use strict";
5377
- CONTRACT_ROLES = /* @__PURE__ */ new Set([
5378
- "critic",
5379
- "code-reviewer",
5380
- "security-reviewer",
5381
- "test-engineer"
5382
- ]);
5383
- VALID_VERDICTS = /* @__PURE__ */ new Set(["approve", "revise", "reject"]);
5384
- VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "major", "minor", "nit"]);
6421
+ init_fs_utils();
6422
+ init_runtime_flags();
6423
+ init_tmux_session();
6424
+ init_git_worktree();
6425
+ init_merge_coordinator();
6426
+ init_worker_bootstrap();
6427
+ init_leader_inbox();
6428
+ init_conflict_mailbox();
6429
+ init_worker_commit_cadence();
6430
+ DEFAULT_POLL_INTERVAL_MS = 1e3;
6431
+ DEFAULT_DRAIN_TIMEOUT_MS = 1e4;
5385
6432
  }
5386
6433
  });
5387
6434
 
@@ -5399,15 +6446,53 @@ __export(runtime_v2_exports, {
5399
6446
  startTeamV2: () => startTeamV2,
5400
6447
  writeWatchdogFailedMarker: () => writeWatchdogFailedMarker
5401
6448
  });
5402
- import { join as join19, resolve as resolve4 } from "path";
5403
- import { existsSync as existsSync16 } from "fs";
5404
- import { mkdir as mkdir7, readdir as readdir2, readFile as readFile9, rm as rm4, writeFile as writeFile5 } from "fs/promises";
6449
+ import { join as join22, resolve as resolve4 } from "path";
6450
+ import { existsSync as existsSync19 } from "fs";
6451
+ import { mkdir as mkdir10, readdir as readdir2, readFile as readFile10, rm as rm4, writeFile as writeFile7 } from "fs/promises";
5405
6452
  import { performance } from "perf_hooks";
5406
- function isRuntimeV2Enabled(env = process.env) {
5407
- const raw = env.OMC_RUNTIME_V2;
5408
- if (!raw) return true;
5409
- const normalized = raw.trim().toLowerCase();
5410
- return !["0", "false", "no", "off"].includes(normalized);
6453
+ import { execFileSync as execFileSync5 } from "node:child_process";
6454
+ function registerTeamOrchestrator(teamName, handle) {
6455
+ orchestratorByTeam.set(teamName, handle);
6456
+ }
6457
+ function getTeamOrchestrator(teamName) {
6458
+ return orchestratorByTeam.get(teamName);
6459
+ }
6460
+ function unregisterTeamOrchestrator(teamName) {
6461
+ orchestratorByTeam.delete(teamName);
6462
+ }
6463
+ function registerTeamCadence(teamName, context, poller) {
6464
+ const entry = cadenceByTeam.get(teamName) ?? { pollers: [], contexts: [] };
6465
+ entry.contexts.push(context);
6466
+ if (poller) entry.pollers.push(poller);
6467
+ cadenceByTeam.set(teamName, entry);
6468
+ }
6469
+ async function stopTeamCadence(teamName) {
6470
+ const entry = cadenceByTeam.get(teamName);
6471
+ if (!entry) return;
6472
+ cadenceByTeam.delete(teamName);
6473
+ for (const poller of entry.pollers) {
6474
+ try {
6475
+ poller.stop();
6476
+ } catch {
6477
+ }
6478
+ }
6479
+ for (const context of entry.contexts) {
6480
+ try {
6481
+ await uninstallCommitCadence(context);
6482
+ } catch {
6483
+ }
6484
+ }
6485
+ }
6486
+ function resolveLeaderBranch(cwd) {
6487
+ const out = execFileSync5("git", ["branch", "--show-current"], {
6488
+ cwd,
6489
+ encoding: "utf-8",
6490
+ stdio: ["pipe", "pipe", "pipe"]
6491
+ }).trim();
6492
+ if (!out) {
6493
+ throw new Error("auto-merge requires a non-detached leader branch (git branch --show-current returned empty)");
6494
+ }
6495
+ return out;
5411
6496
  }
5412
6497
  function resolveTaskAssignment(task, resolvedRouting, roleRoutingConfig, resolvedBinaryPaths, fallbackAgent) {
5413
6498
  const canonicalRoles = new Set(CANONICAL_TEAM_ROLES);
@@ -5499,7 +6584,7 @@ function buildV2TaskInstruction(teamName, workerName, task, taskId, cliOutputCon
5499
6584
  {}
5500
6585
  );
5501
6586
  const completeTaskCommand = formatOmcCliInvocation(
5502
- `team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "completed", claim_token: "<claim_token>" })}' --json`
6587
+ `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`
5503
6588
  );
5504
6589
  const failTaskCommand = formatOmcCliInvocation(
5505
6590
  `team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "failed", claim_token: "<claim_token>" })}' --json`
@@ -5514,6 +6599,7 @@ function buildV2TaskInstruction(teamName, workerName, task, taskId, cliOutputCon
5514
6599
  `2. Do the work described below.`,
5515
6600
  `3. On completion (use claim_token from step 1):`,
5516
6601
  ` ${completeTaskCommand}`,
6602
+ ` 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>".`,
5517
6603
  `4. On failure (use claim_token from step 1):`,
5518
6604
  ` ${failTaskCommand}`,
5519
6605
  `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.`,
@@ -5550,7 +6636,7 @@ function hasWorkerStatusProgress(status, taskId) {
5550
6636
  }
5551
6637
  async function hasWorkerTaskClaimEvidence(teamName, workerName, cwd, taskId) {
5552
6638
  try {
5553
- const raw = await readFile9(absPath(cwd, TeamPaths.taskFile(teamName, taskId)), "utf-8");
6639
+ const raw = await readFile10(absPath(cwd, TeamPaths.taskFile(teamName, taskId)), "utf-8");
5554
6640
  const task = JSON.parse(raw);
5555
6641
  return task.owner === workerName && ["in_progress", "completed", "failed"].includes(task.status);
5556
6642
  } catch {
@@ -5649,6 +6735,18 @@ async function spawnV2Worker(opts) {
5649
6735
  if (usePromptMode) {
5650
6736
  launchArgs.push(...getPromptModeArgs(opts.agentType, promptModeStartupPrompt));
5651
6737
  }
6738
+ if (opts.autoMerge && opts.worktreePath) {
6739
+ const cadenceContext = {
6740
+ teamName: opts.teamName,
6741
+ workerName: opts.workerName,
6742
+ worktreePath: opts.worktreePath,
6743
+ agentType: opts.agentType,
6744
+ enabled: true
6745
+ };
6746
+ const cadence = await installCommitCadence(cadenceContext);
6747
+ const poller = cadence.method === "fallback-poll" ? startFallbackPoller(opts.worktreePath, opts.workerName) : void 0;
6748
+ registerTeamCadence(opts.teamName, cadenceContext, poller);
6749
+ }
5652
6750
  const paneConfig = {
5653
6751
  teamName: opts.teamName,
5654
6752
  workerName: opts.workerName,
@@ -5752,16 +6850,16 @@ async function rollbackUnpersistedNativeWorktreeStartup(teamName, cwd, cause) {
5752
6850
  await rm4(teamRoot, { recursive: true, force: true });
5753
6851
  return;
5754
6852
  }
5755
- await mkdir7(teamRoot, { recursive: true });
5756
- await writeFile5(join19(teamRoot, "startup-failure.json"), JSON.stringify({
6853
+ await mkdir10(teamRoot, { recursive: true });
6854
+ await writeFile7(join22(teamRoot, "startup-failure.json"), JSON.stringify({
5757
6855
  reason: "startup_failed_before_config_persisted",
5758
6856
  error: errorMessage,
5759
6857
  preserved: cleanup.preserved,
5760
6858
  recorded_at: (/* @__PURE__ */ new Date()).toISOString()
5761
6859
  }, null, 2), "utf-8");
5762
6860
  } catch (rollbackError) {
5763
- await mkdir7(teamRoot, { recursive: true });
5764
- await writeFile5(join19(teamRoot, "startup-failure.json"), JSON.stringify({
6861
+ await mkdir10(teamRoot, { recursive: true });
6862
+ await writeFile7(join22(teamRoot, "startup-failure.json"), JSON.stringify({
5765
6863
  reason: "startup_failed_before_config_persisted",
5766
6864
  error: errorMessage,
5767
6865
  rollback_error: rollbackError instanceof Error ? rollbackError.message : String(rollbackError),
@@ -5791,9 +6889,23 @@ async function startTeamV2(config) {
5791
6889
  validateTeamName(sanitized);
5792
6890
  const pluginCfg = config.pluginConfig ?? loadConfig();
5793
6891
  const resolvedRouting = buildResolvedRoutingSnapshot(pluginCfg);
5794
- const worktreeMode = normalizeTeamWorktreeMode(
6892
+ let worktreeMode = normalizeTeamWorktreeMode(
5795
6893
  process.env.OMC_TEAM_WORKTREE_MODE ?? pluginCfg.team?.ops?.worktreeMode
5796
6894
  );
6895
+ let autoMergeLeaderBranch;
6896
+ if (config.autoMerge) {
6897
+ if (!isRuntimeV2Enabled()) {
6898
+ throw new Error("auto-merge requires OMC_RUNTIME_V2=1 (this feature is v2-only).");
6899
+ }
6900
+ autoMergeLeaderBranch = resolveLeaderBranch(leaderCwd);
6901
+ const stripped = autoMergeLeaderBranch.replace(/^refs\/heads\//i, "").toLowerCase();
6902
+ if (stripped === "main" || stripped === "master") {
6903
+ throw new Error("auto-merge refuses main/master leader branch \u2014 use a feature branch");
6904
+ }
6905
+ if (worktreeMode !== "named") {
6906
+ worktreeMode = "named";
6907
+ }
6908
+ }
5797
6909
  const workspaceMode = worktreeMode === "disabled" ? "single" : "worktree";
5798
6910
  const agentTypes = config.agentTypes;
5799
6911
  const resolvedBinaryPaths = {};
@@ -5823,9 +6935,9 @@ async function startTeamV2(config) {
5823
6935
  } catch {
5824
6936
  }
5825
6937
  }
5826
- await mkdir7(absPath(leaderCwd, TeamPaths.tasks(sanitized)), { recursive: true });
5827
- await mkdir7(absPath(leaderCwd, TeamPaths.workers(sanitized)), { recursive: true });
5828
- await mkdir7(join19(leaderCwd, ".omcp", "state", "team", sanitized, "mailbox"), { recursive: true });
6938
+ await mkdir10(absPath(leaderCwd, TeamPaths.tasks(sanitized)), { recursive: true });
6939
+ await mkdir10(absPath(leaderCwd, TeamPaths.workers(sanitized)), { recursive: true });
6940
+ await mkdir10(join22(leaderCwd, ".omcp", "state", "team", sanitized, "mailbox"), { recursive: true });
5829
6941
  const missingBinaryLogFailure = createSwallowedErrorLogger(
5830
6942
  "team.runtime-v2.startTeamV2 cli_binary_missing event failed"
5831
6943
  );
@@ -5843,14 +6955,15 @@ async function startTeamV2(config) {
5843
6955
  for (let i = 0; i < config.tasks.length; i++) {
5844
6956
  const taskId = String(i + 1);
5845
6957
  const taskFilePath = absPath(leaderCwd, TeamPaths.taskFile(sanitized, taskId));
5846
- await mkdir7(join19(taskFilePath, ".."), { recursive: true });
5847
- await writeFile5(taskFilePath, JSON.stringify({
6958
+ await mkdir10(join22(taskFilePath, ".."), { recursive: true });
6959
+ await writeFile7(taskFilePath, JSON.stringify({
5848
6960
  id: taskId,
5849
6961
  subject: config.tasks[i].subject,
5850
6962
  description: config.tasks[i].description,
5851
6963
  status: "pending",
5852
6964
  owner: null,
5853
6965
  result: null,
6966
+ ...config.tasks[i].delegation ? { delegation: config.tasks[i].delegation } : {},
5854
6967
  created_at: (/* @__PURE__ */ new Date()).toISOString()
5855
6968
  }, null, 2), "utf-8");
5856
6969
  }
@@ -5916,7 +7029,7 @@ async function startTeamV2(config) {
5916
7029
  });
5917
7030
  const worktree = workerWorktrees.get(wName);
5918
7031
  if (worktree) {
5919
- const overlayContent = await readFile9(overlayPath, "utf-8");
7032
+ const overlayContent = await readFile10(overlayPath, "utf-8");
5920
7033
  installWorktreeRootAgents(sanitized, wName, leaderCwd, worktree.path, overlayContent);
5921
7034
  }
5922
7035
  }
@@ -6026,7 +7139,7 @@ async function startTeamV2(config) {
6026
7139
  next_worker_index: teamConfig.next_worker_index
6027
7140
  };
6028
7141
  try {
6029
- await writeFile5(absPath(leaderCwd, TeamPaths.manifest(sanitized)), JSON.stringify(teamManifest, null, 2), "utf-8");
7142
+ await writeFile7(absPath(leaderCwd, TeamPaths.manifest(sanitized)), JSON.stringify(teamManifest, null, 2), "utf-8");
6030
7143
  } catch (error) {
6031
7144
  await rollbackStartedNativeWorktreeStartup({
6032
7145
  teamName: sanitized,
@@ -6075,6 +7188,7 @@ async function startTeamV2(config) {
6075
7188
  cwd: leaderCwd,
6076
7189
  workerCwd: workersInfo[workerIndex]?.working_dir ?? leaderCwd,
6077
7190
  worktreePath: workersInfo[workerIndex]?.worktree_path,
7191
+ autoMerge: Boolean(config.autoMerge),
6078
7192
  resolvedBinaryPaths,
6079
7193
  ...assignment.model ? { model: assignment.model } : {},
6080
7194
  ...assignment.role ? { role: assignment.role } : {}
@@ -6137,6 +7251,51 @@ async function startTeamV2(config) {
6137
7251
  worker: "leader-fixed",
6138
7252
  reason: `start_team_v2: workers=${config.workerCount} tasks=${config.tasks.length} panes=${workerPaneIds.length}`
6139
7253
  }, leaderCwd).catch(logEventFailure);
7254
+ if (config.autoMerge && autoMergeLeaderBranch) {
7255
+ try {
7256
+ await ensureLeaderInbox(sanitized, leaderCwd);
7257
+ await appendToLeaderInbox(
7258
+ sanitized,
7259
+ extendLeaderBootstrapPrompt(sanitized),
7260
+ leaderCwd
7261
+ );
7262
+ try {
7263
+ await recoverFromRestart({
7264
+ teamName: sanitized,
7265
+ repoRoot: leaderCwd,
7266
+ leaderBranch: autoMergeLeaderBranch,
7267
+ cwd: leaderCwd
7268
+ });
7269
+ } catch (recErr) {
7270
+ process.stderr.write(`[team/runtime-v2] auto-merge recover-from-restart failed: ${recErr}
7271
+ `);
7272
+ }
7273
+ const orchestrator = await startMergeOrchestrator({
7274
+ teamName: sanitized,
7275
+ repoRoot: leaderCwd,
7276
+ leaderBranch: autoMergeLeaderBranch,
7277
+ cwd: leaderCwd
7278
+ });
7279
+ registerTeamOrchestrator(sanitized, orchestrator);
7280
+ for (const w of workersInfo) {
7281
+ await orchestrator.registerWorker(w.name);
7282
+ }
7283
+ } catch (orchErr) {
7284
+ await stopTeamCadence(sanitized);
7285
+ unregisterTeamOrchestrator(sanitized);
7286
+ await rollbackStartedNativeWorktreeStartup({
7287
+ teamName: sanitized,
7288
+ cwd: leaderCwd,
7289
+ cause: orchErr,
7290
+ sessionName: sessionName2,
7291
+ leaderPaneId,
7292
+ workerPaneIds,
7293
+ sessionMode: session.sessionMode
7294
+ });
7295
+ const reason = orchErr instanceof Error ? orchErr.message : String(orchErr);
7296
+ throw new Error(`auto-merge startup failed: ${reason}`);
7297
+ }
7298
+ }
6140
7299
  return {
6141
7300
  teamName: sanitized,
6142
7301
  sanitizedName: sanitized,
@@ -6147,16 +7306,16 @@ async function startTeamV2(config) {
6147
7306
  };
6148
7307
  }
6149
7308
  async function writeWatchdogFailedMarker(teamName, cwd, reason) {
6150
- const { writeFile: writeFile6 } = await import("fs/promises");
7309
+ const { writeFile: writeFile8 } = await import("fs/promises");
6151
7310
  const marker = {
6152
7311
  failedAt: Date.now(),
6153
7312
  reason,
6154
7313
  writtenBy: "runtime-v2"
6155
7314
  };
6156
7315
  const root = absPath(cwd, TeamPaths.root(sanitizeTeamName(teamName)));
6157
- const markerPath = join19(root, "watchdog-failed.json");
6158
- await mkdir7(root, { recursive: true });
6159
- await writeFile6(markerPath, JSON.stringify(marker, null, 2), "utf-8");
7316
+ const markerPath = join22(root, "watchdog-failed.json");
7317
+ await mkdir10(root, { recursive: true });
7318
+ await writeFile8(markerPath, JSON.stringify(marker, null, 2), "utf-8");
6160
7319
  }
6161
7320
  async function requeueDeadWorkerTasks(teamName, deadWorkerNames, cwd) {
6162
7321
  const logEventFailure = createSwallowedErrorLogger(
@@ -6176,15 +7335,15 @@ async function requeueDeadWorkerTasks(teamName, deadWorkerNames, cwd) {
6176
7335
  retryCount: 0,
6177
7336
  lastFailedAt: (/* @__PURE__ */ new Date()).toISOString()
6178
7337
  };
6179
- const { writeFile: writeFile6 } = await import("fs/promises");
6180
- await mkdir7(absPath(cwd, TeamPaths.tasks(sanitized)), { recursive: true });
6181
- await writeFile6(sidecarPath, JSON.stringify(sidecar, null, 2), "utf-8");
7338
+ const { writeFile: writeFile8 } = await import("fs/promises");
7339
+ await mkdir10(absPath(cwd, TeamPaths.tasks(sanitized)), { recursive: true });
7340
+ await writeFile8(sidecarPath, JSON.stringify(sidecar, null, 2), "utf-8");
6182
7341
  const taskPath2 = absPath(cwd, TeamPaths.taskFile(sanitized, task.id));
6183
7342
  try {
6184
- const { readFileSync: readFileSync12, writeFileSync: writeFileSync4 } = await import("fs");
7343
+ const { readFileSync: readFileSync13, writeFileSync: writeFileSync4 } = await import("fs");
6185
7344
  const { withFileLockSync: withFileLockSync2 } = await Promise.resolve().then(() => (init_file_lock(), file_lock_exports));
6186
7345
  withFileLockSync2(taskPath2 + ".lock", () => {
6187
- const raw = readFileSync12(taskPath2, "utf-8");
7346
+ const raw = readFileSync13(taskPath2, "utf-8");
6188
7347
  const taskData = JSON.parse(raw);
6189
7348
  if (taskData.status === "in_progress") {
6190
7349
  taskData.status = "pending";
@@ -6214,7 +7373,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
6214
7373
  "team.runtime-v2.processCliWorkerVerdicts appendTeamEvent failed"
6215
7374
  );
6216
7375
  const { rename: rename3 } = await import("fs/promises");
6217
- const { readFileSync: readFileSync12, writeFileSync: writeFileSync4, existsSync: fsExistsSync } = await import("fs");
7376
+ const { readFileSync: readFileSync13, writeFileSync: writeFileSync4, existsSync: fsExistsSync } = await import("fs");
6218
7377
  const { withFileLockSync: withFileLockSync2 } = await Promise.resolve().then(() => (init_file_lock(), file_lock_exports));
6219
7378
  for (const worker of config.workers) {
6220
7379
  const outputFile = worker.output_file;
@@ -6227,7 +7386,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
6227
7386
  }
6228
7387
  let payload;
6229
7388
  try {
6230
- const raw = await readFile9(outputFile, "utf-8");
7389
+ const raw = await readFile10(outputFile, "utf-8");
6231
7390
  payload = parseCliWorkerVerdict(raw);
6232
7391
  } catch (err) {
6233
7392
  const reason = err instanceof Error ? err.message : String(err);
@@ -6248,7 +7407,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
6248
7407
  const taskPath2 = absPath(cwd, TeamPaths.taskFile(sanitized, taskId));
6249
7408
  if (!fsExistsSync(taskPath2)) continue;
6250
7409
  try {
6251
- const taskRaw = readFileSync12(taskPath2, "utf-8");
7410
+ const taskRaw = readFileSync13(taskPath2, "utf-8");
6252
7411
  const taskData = JSON.parse(taskRaw);
6253
7412
  if (taskData.owner === worker.name && taskData.status === "in_progress") {
6254
7413
  targetTaskId = taskId;
@@ -6276,7 +7435,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
6276
7435
  let transitionOk = false;
6277
7436
  try {
6278
7437
  withFileLockSync2(targetTaskPath + ".lock", () => {
6279
- const raw = readFileSync12(targetTaskPath, "utf-8");
7438
+ const raw = readFileSync13(targetTaskPath, "utf-8");
6280
7439
  const taskData = JSON.parse(raw);
6281
7440
  if (taskData.status !== "in_progress" || taskData.owner !== worker.name) {
6282
7441
  return;
@@ -6510,6 +7669,39 @@ async function shutdownTeamV2(teamName, cwd, options = {}) {
6510
7669
  const timeoutMs = options.timeoutMs ?? 15e3;
6511
7670
  const sanitized = sanitizeTeamName(teamName);
6512
7671
  const config = await readTeamConfig(sanitized, cwd);
7672
+ const finalizeAutoMerge = async () => {
7673
+ const orchestrator = getTeamOrchestrator(sanitized);
7674
+ if (orchestrator) {
7675
+ try {
7676
+ const drainResult = await orchestrator.drainAndStop();
7677
+ if (drainResult.unmerged.length > 0) {
7678
+ await appendTeamEvent(sanitized, {
7679
+ type: "team_leader_nudge",
7680
+ worker: "leader-fixed",
7681
+ reason: `auto_merge_drain_unmerged:${drainResult.unmerged.map((u) => `${u.workerName}:${u.reason}`).join(",")}`
7682
+ }, cwd).catch(logEventFailure);
7683
+ }
7684
+ for (const w of config?.workers ?? []) {
7685
+ try {
7686
+ await orchestrator.unregisterWorker(w.name);
7687
+ } catch (err) {
7688
+ process.stderr.write(
7689
+ `[team/runtime-v2] orchestrator.unregisterWorker(${w.name}) failed: ${err}
7690
+ `
7691
+ );
7692
+ }
7693
+ }
7694
+ } catch (err) {
7695
+ process.stderr.write(`[team/runtime-v2] orchestrator drainAndStop: ${err}
7696
+ `);
7697
+ } finally {
7698
+ await stopTeamCadence(sanitized);
7699
+ unregisterTeamOrchestrator(sanitized);
7700
+ }
7701
+ } else {
7702
+ await stopTeamCadence(sanitized);
7703
+ }
7704
+ };
6513
7705
  if (!config) {
6514
7706
  const cleanupSafety = inspectTeamWorktreeCleanupSafety(sanitized, cwd);
6515
7707
  if (cleanupSafety.hasEvidence) {
@@ -6644,12 +7836,14 @@ Then exit your session.
6644
7836
  if (aliveWorkers.length > 0) {
6645
7837
  process.stderr.write(`[team/runtime-v2] preserving worktrees/state because worker pane(s) are still alive: ${aliveWorkers.join(", ")}
6646
7838
  `);
7839
+ await finalizeAutoMerge();
6647
7840
  return;
6648
7841
  }
6649
7842
  const unknownWorkers = liveness.filter(([, state]) => state === "unknown").map(([paneId]) => paneById.get(paneId) ?? paneId);
6650
7843
  if (unknownWorkers.length > 0) {
6651
7844
  process.stderr.write(`[team/runtime-v2] preserving worktrees/state because worker pane liveness is unknown: ${unknownWorkers.join(", ")}
6652
7845
  `);
7846
+ await finalizeAutoMerge();
6653
7847
  return;
6654
7848
  }
6655
7849
  } catch (err) {
@@ -6657,6 +7851,7 @@ Then exit your session.
6657
7851
  `);
6658
7852
  if (recordedWorkerPaneIds.length > 0) {
6659
7853
  process.stderr.write("[team/runtime-v2] preserving worktrees/state because tmux cleanup did not prove worker panes exited\n");
7854
+ await finalizeAutoMerge();
6660
7855
  return;
6661
7856
  }
6662
7857
  }
@@ -6671,6 +7866,7 @@ Then exit your session.
6671
7866
  reason: `ralph_cleanup_summary: total=${finalTasks.length} completed=${completed} failed=${failed} pending=${pending} force=${force}`
6672
7867
  }, cwd).catch(logEventFailure);
6673
7868
  }
7869
+ await finalizeAutoMerge();
6674
7870
  let preservedWorktrees = 0;
6675
7871
  try {
6676
7872
  const worktreeCleanup = cleanupTeamWorktrees(sanitized, cwd);
@@ -6707,8 +7903,8 @@ async function resumeTeamV2(teamName, cwd) {
6707
7903
  }
6708
7904
  }
6709
7905
  async function findActiveTeamsV2(cwd) {
6710
- const root = join19(cwd, ".omcp", "state", "team");
6711
- if (!existsSync16(root)) return [];
7906
+ const root = join22(cwd, ".omcp", "state", "team");
7907
+ if (!existsSync19(root)) return [];
6712
7908
  const entries = await readdir2(root, { withFileTypes: true });
6713
7909
  const active = [];
6714
7910
  for (const e of entries) {
@@ -6721,7 +7917,7 @@ async function findActiveTeamsV2(cwd) {
6721
7917
  }
6722
7918
  return active;
6723
7919
  }
6724
- var MONITOR_SIGNAL_STALE_MS, CIRCUIT_BREAKER_THRESHOLD, CircuitBreakerV2;
7920
+ var orchestratorByTeam, cadenceByTeam, MONITOR_SIGNAL_STALE_MS, CIRCUIT_BREAKER_THRESHOLD, CircuitBreakerV2;
6725
7921
  var init_runtime_v2 = __esm({
6726
7922
  "src/team/runtime-v2.ts"() {
6727
7923
  "use strict";
@@ -6746,6 +7942,13 @@ var init_runtime_v2 = __esm({
6746
7942
  init_role_router();
6747
7943
  init_types2();
6748
7944
  init_cli_worker_contract();
7945
+ init_merge_orchestrator();
7946
+ init_leader_inbox();
7947
+ init_runtime_flags();
7948
+ init_worker_commit_cadence();
7949
+ init_runtime_flags();
7950
+ orchestratorByTeam = /* @__PURE__ */ new Map();
7951
+ cadenceByTeam = /* @__PURE__ */ new Map();
6749
7952
  MONITOR_SIGNAL_STALE_MS = 3e4;
6750
7953
  CIRCUIT_BREAKER_THRESHOLD = 3;
6751
7954
  CircuitBreakerV2 = class {
@@ -6781,16 +7984,16 @@ var init_runtime_v2 = __esm({
6781
7984
  // src/cli/team.ts
6782
7985
  import { randomUUID as randomUUID6 } from "crypto";
6783
7986
  import { spawn } from "child_process";
6784
- import { existsSync as existsSync18, mkdirSync as mkdirSync3, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
6785
- import { readFile as readFile10, readdir as readdir3, rm as rm5 } from "fs/promises";
7987
+ import { existsSync as existsSync22, mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
7988
+ import { readFile as readFile11, readdir as readdir3, rm as rm5 } from "fs/promises";
6786
7989
  import { homedir as homedir3 } from "os";
6787
- import { dirname as dirname13, join as join21 } from "path";
7990
+ import { dirname as dirname16, join as join25 } from "path";
6788
7991
  import { fileURLToPath as fileURLToPath3 } from "url";
6789
7992
 
6790
7993
  // src/team/api-interop.ts
6791
7994
  init_contracts();
6792
- import { existsSync as existsSync17, readFileSync as readFileSync10 } from "node:fs";
6793
- import { dirname as dirname12, join as join20, resolve as resolvePath } from "node:path";
7995
+ import { existsSync as existsSync20, readFileSync as readFileSync10 } from "node:fs";
7996
+ import { dirname as dirname15, join as join23, resolve as resolvePath } from "node:path";
6794
7997
 
6795
7998
  // src/team/team-ops.ts
6796
7999
  init_state_paths();
@@ -6855,7 +8058,31 @@ async function claimTask(taskId, workerName, expectedVersion, deps) {
6855
8058
  if (!lock.ok) return { ok: false, error: "claim_conflict" };
6856
8059
  return lock.value;
6857
8060
  }
6858
- async function transitionTaskStatus(taskId, from, to, claimToken, deps) {
8061
+ function extractDelegationComplianceEvidence(task, terminalData) {
8062
+ const plan = task.delegation;
8063
+ if (!plan || plan.mode === "none") return null;
8064
+ if (plan.mode === "optional" && plan.required_parallel_probe !== true) return null;
8065
+ const result = typeof terminalData?.result === "string" ? terminalData.result : "";
8066
+ const spawnMatch = result.match(/^\s*Subagent spawn evidence:\s*(.+)$/im);
8067
+ if (spawnMatch?.[1]?.trim()) {
8068
+ const detail = spawnMatch[1].trim();
8069
+ if (!/^none\b|^0\b/i.test(detail)) {
8070
+ return { status: "spawned", source: "terminal_result", detail, recorded_at: (/* @__PURE__ */ new Date()).toISOString() };
8071
+ }
8072
+ }
8073
+ if (plan.skip_allowed_reason_required === true) {
8074
+ const skipMatch = result.match(/^\s*Subagent skip reason:\s*(.+)$/im);
8075
+ if (skipMatch?.[1]?.trim()) {
8076
+ return { status: "skipped", source: "terminal_result", detail: skipMatch[1].trim(), recorded_at: (/* @__PURE__ */ new Date()).toISOString() };
8077
+ }
8078
+ }
8079
+ return null;
8080
+ }
8081
+ function requiresDelegationComplianceEvidence(task) {
8082
+ const plan = task.delegation;
8083
+ return !!plan && (plan.mode === "auto" || plan.mode === "required" || plan.required_parallel_probe === true);
8084
+ }
8085
+ async function transitionTaskStatus(taskId, from, to, claimToken, terminalData, deps) {
6859
8086
  if (!deps.canTransitionTaskStatus(from, to)) return { ok: false, error: "invalid_transition" };
6860
8087
  const lock = await deps.withTaskClaimLock(deps.teamName, taskId, deps.cwd, async () => {
6861
8088
  const current = await deps.readTask(deps.teamName, taskId, deps.cwd);
@@ -6868,10 +8095,19 @@ async function transitionTaskStatus(taskId, from, to, claimToken, deps) {
6868
8095
  return { ok: false, error: "claim_conflict" };
6869
8096
  }
6870
8097
  if (new Date(v.claim.leased_until) <= /* @__PURE__ */ new Date()) return { ok: false, error: "lease_expired" };
8098
+ const normalizedResult = typeof terminalData?.result === "string" ? terminalData.result : void 0;
8099
+ const normalizedError = typeof terminalData?.error === "string" ? terminalData.error : void 0;
8100
+ const delegationCompliance = to === "completed" ? extractDelegationComplianceEvidence(v, terminalData) : null;
8101
+ if (to === "completed" && requiresDelegationComplianceEvidence(v) && !delegationCompliance) {
8102
+ return { ok: false, error: "missing_delegation_compliance_evidence" };
8103
+ }
6871
8104
  const updated = {
6872
8105
  ...v,
6873
8106
  status: to,
6874
8107
  completed_at: to === "completed" ? (/* @__PURE__ */ new Date()).toISOString() : v.completed_at,
8108
+ result: to === "completed" ? normalizedResult : void 0,
8109
+ error: to === "failed" ? normalizedError : void 0,
8110
+ delegation_compliance: to === "completed" ? delegationCompliance ?? v.delegation_compliance : v.delegation_compliance,
6875
8111
  claim: void 0,
6876
8112
  version: v.version + 1
6877
8113
  };
@@ -7231,8 +8467,8 @@ async function teamClaimTask(teamName, taskId, workerName, expectedVersion, cwd)
7231
8467
  writeAtomic
7232
8468
  });
7233
8469
  }
7234
- async function teamTransitionTaskStatus(teamName, taskId, from, to, claimToken, cwd) {
7235
- return transitionTaskStatus(taskId, from, to, claimToken, {
8470
+ async function teamTransitionTaskStatus(teamName, taskId, from, to, claimToken, cwd, terminalData) {
8471
+ return transitionTaskStatus(taskId, from, to, claimToken, terminalData, {
7236
8472
  teamName,
7237
8473
  cwd,
7238
8474
  readTask: teamReadTask,
@@ -7725,7 +8961,7 @@ async function resumeTeam(teamName, cwd) {
7725
8961
  init_runtime_v2();
7726
8962
  init_git_worktree();
7727
8963
  init_swallowed_error();
7728
- var TEAM_UPDATE_TASK_MUTABLE_FIELDS = /* @__PURE__ */ new Set(["subject", "description", "blocked_by", "requires_code_change"]);
8964
+ var TEAM_UPDATE_TASK_MUTABLE_FIELDS = /* @__PURE__ */ new Set(["subject", "description", "blocked_by", "requires_code_change", "delegation"]);
7729
8965
  var TEAM_UPDATE_TASK_REQUEST_FIELDS = /* @__PURE__ */ new Set(["team_name", "task_id", "workingDirectory", ...TEAM_UPDATE_TASK_MUTABLE_FIELDS]);
7730
8966
  var TEAM_API_OPERATIONS = [
7731
8967
  "send-message",
@@ -7778,10 +9014,64 @@ function parseValidatedTaskIdArray(value, fieldName) {
7778
9014
  }
7779
9015
  return taskIds;
7780
9016
  }
9017
+ function parseTaskDelegationPlan(value) {
9018
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
9019
+ throw new Error("delegation must be an object");
9020
+ }
9021
+ const raw = value;
9022
+ const mode = raw.mode;
9023
+ if (mode !== "none" && mode !== "optional" && mode !== "auto" && mode !== "required") {
9024
+ throw new Error("delegation.mode must be one of: none, optional, auto, required");
9025
+ }
9026
+ const plan = { mode };
9027
+ if ("max_parallel_subtasks" in raw) {
9028
+ if (!isFiniteInteger(raw.max_parallel_subtasks) || raw.max_parallel_subtasks < 1) {
9029
+ throw new Error("delegation.max_parallel_subtasks must be a positive integer when provided");
9030
+ }
9031
+ plan.max_parallel_subtasks = raw.max_parallel_subtasks;
9032
+ }
9033
+ if ("required_parallel_probe" in raw) {
9034
+ if (typeof raw.required_parallel_probe !== "boolean") throw new Error("delegation.required_parallel_probe must be a boolean when provided");
9035
+ plan.required_parallel_probe = raw.required_parallel_probe;
9036
+ }
9037
+ if ("spawn_before_serial_search_threshold" in raw) {
9038
+ if (!isFiniteInteger(raw.spawn_before_serial_search_threshold) || raw.spawn_before_serial_search_threshold < 1) {
9039
+ throw new Error("delegation.spawn_before_serial_search_threshold must be a positive integer when provided");
9040
+ }
9041
+ plan.spawn_before_serial_search_threshold = raw.spawn_before_serial_search_threshold;
9042
+ }
9043
+ if ("child_model_policy" in raw) {
9044
+ const policy = raw.child_model_policy;
9045
+ if (policy !== "standard" && policy !== "fast" && policy !== "inherit" && policy !== "frontier") {
9046
+ throw new Error("delegation.child_model_policy must be one of: standard, fast, inherit, frontier");
9047
+ }
9048
+ plan.child_model_policy = policy;
9049
+ }
9050
+ if ("child_model" in raw) {
9051
+ if (typeof raw.child_model !== "string") throw new Error("delegation.child_model must be a string when provided");
9052
+ plan.child_model = raw.child_model;
9053
+ }
9054
+ if ("subtask_candidates" in raw) {
9055
+ if (!Array.isArray(raw.subtask_candidates) || !raw.subtask_candidates.every((item) => typeof item === "string")) {
9056
+ throw new Error("delegation.subtask_candidates must be an array of strings when provided");
9057
+ }
9058
+ plan.subtask_candidates = raw.subtask_candidates;
9059
+ }
9060
+ if ("child_report_format" in raw) {
9061
+ const format = raw.child_report_format;
9062
+ if (format !== "bullets" && format !== "json") throw new Error("delegation.child_report_format must be bullets or json when provided");
9063
+ plan.child_report_format = format;
9064
+ }
9065
+ if ("skip_allowed_reason_required" in raw) {
9066
+ if (typeof raw.skip_allowed_reason_required !== "boolean") throw new Error("delegation.skip_allowed_reason_required must be a boolean when provided");
9067
+ plan.skip_allowed_reason_required = raw.skip_allowed_reason_required;
9068
+ }
9069
+ return plan;
9070
+ }
7781
9071
  function teamStateExists(teamName, candidateCwd) {
7782
9072
  if (!TEAM_NAME_SAFE_PATTERN.test(teamName)) return false;
7783
- const teamRoot = join20(candidateCwd, ".omcp", "state", "team", teamName);
7784
- return existsSync17(join20(teamRoot, "config.json")) || existsSync17(join20(teamRoot, "tasks")) || existsSync17(teamRoot);
9073
+ const teamRoot = join23(candidateCwd, ".omcp", "state", "team", teamName);
9074
+ return existsSync20(join23(teamRoot, "config.json")) || existsSync20(join23(teamRoot, "tasks")) || existsSync20(teamRoot);
7785
9075
  }
7786
9076
  function parseTeamWorkerEnv(raw) {
7787
9077
  if (typeof raw !== "string" || raw.trim() === "") return null;
@@ -7835,7 +9125,7 @@ async function executeTeamCleanupViaRuntime(teamName, cwd) {
7835
9125
  await teamCleanup(teamName, cwd);
7836
9126
  }
7837
9127
  function readTeamStateRootFromFile(path4) {
7838
- if (!existsSync17(path4)) return null;
9128
+ if (!existsSync20(path4)) return null;
7839
9129
  try {
7840
9130
  const parsed = JSON.parse(readFileSync10(path4, "utf8"));
7841
9131
  return typeof parsed.team_state_root === "string" && parsed.team_state_root.trim() !== "" ? parsed.team_state_root.trim() : null;
@@ -7851,7 +9141,7 @@ function stateRootToWorkingDirectory(stateRoot2) {
7851
9141
  if (idx >= 0) {
7852
9142
  const workspaceRoot = absolute.slice(0, idx);
7853
9143
  if (workspaceRoot && workspaceRoot !== "/") return workspaceRoot;
7854
- return dirname12(dirname12(dirname12(dirname12(absolute))));
9144
+ return dirname15(dirname15(dirname15(dirname15(absolute))));
7855
9145
  }
7856
9146
  }
7857
9147
  for (const marker of ["/.omcp/state", "/.omx/state"]) {
@@ -7859,22 +9149,22 @@ function stateRootToWorkingDirectory(stateRoot2) {
7859
9149
  if (idx >= 0) {
7860
9150
  const workspaceRoot = absolute.slice(0, idx);
7861
9151
  if (workspaceRoot && workspaceRoot !== "/") return workspaceRoot;
7862
- return dirname12(dirname12(absolute));
9152
+ return dirname15(dirname15(absolute));
7863
9153
  }
7864
9154
  }
7865
- return dirname12(dirname12(absolute));
9155
+ return dirname15(dirname15(absolute));
7866
9156
  }
7867
9157
  function resolveTeamWorkingDirectoryFromMetadata(teamName, candidateCwd, workerContext) {
7868
- const teamRoot = join20(candidateCwd, ".omcp", "state", "team", teamName);
7869
- if (!existsSync17(teamRoot)) return null;
9158
+ const teamRoot = join23(candidateCwd, ".omcp", "state", "team", teamName);
9159
+ if (!existsSync20(teamRoot)) return null;
7870
9160
  if (workerContext?.teamName === teamName) {
7871
- const workerRoot = readTeamStateRootFromFile(join20(teamRoot, "workers", workerContext.workerName, "identity.json"));
9161
+ const workerRoot = readTeamStateRootFromFile(join23(teamRoot, "workers", workerContext.workerName, "identity.json"));
7872
9162
  if (workerRoot) return stateRootToWorkingDirectory(workerRoot);
7873
9163
  }
7874
- const fromConfig = readTeamStateRootFromFile(join20(teamRoot, "config.json"));
9164
+ const fromConfig = readTeamStateRootFromFile(join23(teamRoot, "config.json"));
7875
9165
  if (fromConfig) return stateRootToWorkingDirectory(fromConfig);
7876
9166
  for (const manifestName of ["manifest.json", "manifest.v2.json"]) {
7877
- const fromManifest = readTeamStateRootFromFile(join20(teamRoot, manifestName));
9167
+ const fromManifest = readTeamStateRootFromFile(join23(teamRoot, manifestName));
7878
9168
  if (fromManifest) return stateRootToWorkingDirectory(fromManifest);
7879
9169
  }
7880
9170
  return null;
@@ -7901,7 +9191,7 @@ function resolveTeamWorkingDirectory(teamName, preferredCwd) {
7901
9191
  if (teamStateExists(normalizedTeamName, cursor)) {
7902
9192
  return resolveTeamWorkingDirectoryFromMetadata(normalizedTeamName, cursor, workerContext) ?? cursor;
7903
9193
  }
7904
- const parent = dirname12(cursor);
9194
+ const parent = dirname15(cursor);
7905
9195
  if (!parent || parent === cursor) break;
7906
9196
  cursor = parent;
7907
9197
  }
@@ -8149,13 +9439,22 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
8149
9439
  const owner = args.owner;
8150
9440
  const blockedBy = args.blocked_by;
8151
9441
  const requiresCodeChange = args.requires_code_change;
9442
+ let delegation;
9443
+ if ("delegation" in args) {
9444
+ try {
9445
+ delegation = parseTaskDelegationPlan(args.delegation);
9446
+ } catch (error) {
9447
+ return { ok: false, operation, error: { code: "invalid_input", message: error.message } };
9448
+ }
9449
+ }
8152
9450
  const task = await teamCreateTask(teamName, {
8153
9451
  subject,
8154
9452
  description,
8155
9453
  status: "pending",
8156
9454
  owner: owner || void 0,
8157
9455
  blocked_by: blockedBy,
8158
- requires_code_change: requiresCodeChange
9456
+ requires_code_change: requiresCodeChange,
9457
+ ...delegation ? { delegation } : {}
8159
9458
  }, cwd);
8160
9459
  return { ok: true, operation, data: { task } };
8161
9460
  }
@@ -8217,6 +9516,13 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
8217
9516
  return { ok: false, operation, error: { code: "invalid_input", message: error.message } };
8218
9517
  }
8219
9518
  }
9519
+ if ("delegation" in args) {
9520
+ try {
9521
+ updates.delegation = parseTaskDelegationPlan(args.delegation);
9522
+ } catch (error) {
9523
+ return { ok: false, operation, error: { code: "invalid_input", message: error.message } };
9524
+ }
9525
+ }
8220
9526
  const task = await teamUpdateTask(teamName, taskId, updates, cwd);
8221
9527
  return task ? { ok: true, operation, data: { task } } : { ok: false, operation, error: { code: "task_not_found", message: "task_not_found" } };
8222
9528
  }
@@ -8240,6 +9546,8 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
8240
9546
  const from = String(args.from || "").trim();
8241
9547
  const to = String(args.to || "").trim();
8242
9548
  const claimToken = String(args.claim_token || "").trim();
9549
+ const transitionResult = args.result;
9550
+ const transitionError = args.error;
8243
9551
  if (!teamName || !taskId || !from || !to || !claimToken) {
8244
9552
  return { ok: false, operation, error: { code: "invalid_input", message: "team_name, task_id, from, to, claim_token are required" } };
8245
9553
  }
@@ -8247,7 +9555,24 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
8247
9555
  if (!allowed.has(from) || !allowed.has(to)) {
8248
9556
  return { ok: false, operation, error: { code: "invalid_input", message: "from and to must be valid task statuses" } };
8249
9557
  }
8250
- const result = await teamTransitionTaskStatus(teamName, taskId, from, to, claimToken, cwd);
9558
+ if (transitionResult !== void 0 && typeof transitionResult !== "string") {
9559
+ return { ok: false, operation, error: { code: "invalid_input", message: "result must be a string when provided" } };
9560
+ }
9561
+ if (transitionError !== void 0 && typeof transitionError !== "string") {
9562
+ return { ok: false, operation, error: { code: "invalid_input", message: "error must be a string when provided" } };
9563
+ }
9564
+ const result = await teamTransitionTaskStatus(
9565
+ teamName,
9566
+ taskId,
9567
+ from,
9568
+ to,
9569
+ claimToken,
9570
+ cwd,
9571
+ {
9572
+ result: typeof transitionResult === "string" ? transitionResult : void 0,
9573
+ error: typeof transitionError === "string" ? transitionError : void 0
9574
+ }
9575
+ );
8251
9576
  return { ok: true, operation, data: result };
8252
9577
  }
8253
9578
  case "release-task-claim": {
@@ -8470,6 +9795,278 @@ init_tmux_session();
8470
9795
  init_team_name();
8471
9796
  init_monitor();
8472
9797
  init_process_utils();
9798
+
9799
+ // src/planning/artifacts.ts
9800
+ import { readdirSync as readdirSync5, readFileSync as readFileSync11, existsSync as existsSync21 } from "fs";
9801
+ import { join as join24 } from "path";
9802
+
9803
+ // src/planning/artifact-names.ts
9804
+ import { basename as basename7 } from "path";
9805
+ var PLANNING_ARTIFACT_TIMESTAMP_PATTERN = /^\d{8}T\d{6}Z$/;
9806
+ function legacyTestSpecSlug(fileNameOrPath) {
9807
+ const match = basename7(fileNameOrPath).match(/^test-?spec-(?<slug>.+)\.md$/i);
9808
+ return match?.groups?.slug ?? null;
9809
+ }
9810
+ function requiredTimestampedTestSpecFileName(prdArtifact) {
9811
+ return prdArtifact.kind === "prd" && prdArtifact.timestamp ? `test-spec-${prdArtifact.timestamp}-${prdArtifact.slug}.md` : null;
9812
+ }
9813
+ function splitTimestampPrefix(rawSlug) {
9814
+ const separatorIndex = rawSlug.indexOf("-");
9815
+ if (separatorIndex === -1) {
9816
+ return { slug: rawSlug };
9817
+ }
9818
+ const prefix = rawSlug.slice(0, separatorIndex);
9819
+ if (!PLANNING_ARTIFACT_TIMESTAMP_PATTERN.test(prefix)) {
9820
+ return { slug: rawSlug };
9821
+ }
9822
+ return {
9823
+ timestamp: prefix,
9824
+ slug: rawSlug.slice(separatorIndex + 1)
9825
+ };
9826
+ }
9827
+ function parsePlanningArtifactFileName(fileNameOrPath) {
9828
+ const fileName = basename7(fileNameOrPath);
9829
+ const autoresearchDeepInterviewMatch = fileName.match(
9830
+ /^deep-interview-autoresearch-(?<slug>.+)\.md$/i
9831
+ );
9832
+ if (autoresearchDeepInterviewMatch?.groups?.slug) {
9833
+ const parsedSlug = splitTimestampPrefix(autoresearchDeepInterviewMatch.groups.slug);
9834
+ if (!parsedSlug.slug) return null;
9835
+ return {
9836
+ kind: "deep-interview-autoresearch",
9837
+ ...parsedSlug
9838
+ };
9839
+ }
9840
+ const deepInterviewMatch = fileName.match(/^deep-interview-(?<slug>.+)\.md$/i);
9841
+ if (deepInterviewMatch?.groups?.slug) {
9842
+ const parsedSlug = splitTimestampPrefix(deepInterviewMatch.groups.slug);
9843
+ if (!parsedSlug.slug) return null;
9844
+ return {
9845
+ kind: "deep-interview",
9846
+ ...parsedSlug
9847
+ };
9848
+ }
9849
+ const prdMatch = fileName.match(/^prd-(?<slug>.+)\.md$/i);
9850
+ if (prdMatch?.groups?.slug) {
9851
+ const parsedSlug = splitTimestampPrefix(prdMatch.groups.slug);
9852
+ if (!parsedSlug.slug) return null;
9853
+ return {
9854
+ kind: "prd",
9855
+ ...parsedSlug
9856
+ };
9857
+ }
9858
+ const testSpecMatch = fileName.match(/^test-?spec-(?<slug>.+)\.md$/i);
9859
+ if (testSpecMatch?.groups?.slug) {
9860
+ const parsedSlug = splitTimestampPrefix(testSpecMatch.groups.slug);
9861
+ if (!parsedSlug.slug) return null;
9862
+ return {
9863
+ kind: "test-spec",
9864
+ ...parsedSlug
9865
+ };
9866
+ }
9867
+ return null;
9868
+ }
9869
+ function comparePlanningArtifactPaths(left, right) {
9870
+ const leftParsed = parsePlanningArtifactFileName(left);
9871
+ const rightParsed = parsePlanningArtifactFileName(right);
9872
+ if (leftParsed?.timestamp && rightParsed?.timestamp && leftParsed.timestamp !== rightParsed.timestamp) {
9873
+ return leftParsed.timestamp.localeCompare(rightParsed.timestamp);
9874
+ }
9875
+ if (leftParsed?.timestamp && !rightParsed?.timestamp) {
9876
+ return 1;
9877
+ }
9878
+ if (!leftParsed?.timestamp && rightParsed?.timestamp) {
9879
+ return -1;
9880
+ }
9881
+ return left.localeCompare(right);
9882
+ }
9883
+ function selectMatchingTestSpecsForPrd(prdPath, testSpecPaths) {
9884
+ if (!prdPath) {
9885
+ return [];
9886
+ }
9887
+ const prdArtifact = parsePlanningArtifactFileName(prdPath);
9888
+ if (prdArtifact?.kind !== "prd") {
9889
+ return [];
9890
+ }
9891
+ const requiredTimestampedFileName = requiredTimestampedTestSpecFileName(prdArtifact);
9892
+ return (requiredTimestampedFileName ? testSpecPaths.filter((path4) => basename7(path4) === requiredTimestampedFileName) : testSpecPaths.filter((path4) => legacyTestSpecSlug(path4) === prdArtifact.slug)).sort(comparePlanningArtifactPaths);
9893
+ }
9894
+ function selectLatestPlanningArtifactPath(paths) {
9895
+ return [...paths].sort(comparePlanningArtifactPaths).at(-1) ?? null;
9896
+ }
9897
+
9898
+ // src/planning/artifacts.ts
9899
+ function readFileSafe(path4) {
9900
+ try {
9901
+ return readFileSync11(path4, "utf-8");
9902
+ } catch {
9903
+ return null;
9904
+ }
9905
+ }
9906
+ function escapeRegex(value) {
9907
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9908
+ }
9909
+ function getSectionContent(markdown, heading) {
9910
+ const headingRe = new RegExp(
9911
+ `^##\\s+${escapeRegex(heading)}[ \\t]*$`,
9912
+ "im"
9913
+ );
9914
+ const headingMatch = headingRe.exec(markdown);
9915
+ if (!headingMatch || headingMatch.index === void 0) return null;
9916
+ const bodyStart = headingMatch.index + headingMatch[0].length;
9917
+ const rest = markdown.slice(bodyStart).replace(/^\r?\n/, "");
9918
+ const nextHeadingMatch = /\r?\n##\s+/.exec(rest);
9919
+ const body = (nextHeadingMatch ? rest.slice(0, nextHeadingMatch.index) : rest).trim();
9920
+ return body.length > 0 ? body : null;
9921
+ }
9922
+ function hasRequiredSections(markdown, headings) {
9923
+ return headings.every(
9924
+ (heading) => getSectionContent(markdown, heading) !== null
9925
+ );
9926
+ }
9927
+ function getPlansDirCandidates(cwd) {
9928
+ return [join24(cwd, ".omc", "plans"), join24(cwd, ".omx", "plans")];
9929
+ }
9930
+ function sortArtifactPathsDescending(paths) {
9931
+ return [...paths].sort((a, b) => comparePlanningArtifactPaths(b, a));
9932
+ }
9933
+ function hasCompletePlanningPair(prdPath, matchingTestSpecPaths) {
9934
+ if (matchingTestSpecPaths.length === 0) {
9935
+ return false;
9936
+ }
9937
+ const prd = readFileSafe(prdPath);
9938
+ const testSpec = readFileSafe(matchingTestSpecPaths[0]);
9939
+ if (!prd || !testSpec) {
9940
+ return false;
9941
+ }
9942
+ return hasRequiredSections(prd, [
9943
+ "Acceptance criteria",
9944
+ "Requirement coverage map"
9945
+ ]) && hasRequiredSections(testSpec, [
9946
+ "Unit coverage",
9947
+ "Verification mapping"
9948
+ ]);
9949
+ }
9950
+ function readPlanningArtifacts(cwd) {
9951
+ let entries;
9952
+ const prdPaths = [];
9953
+ const testSpecPaths = [];
9954
+ for (const plansDir of getPlansDirCandidates(cwd)) {
9955
+ if (!existsSync21(plansDir)) {
9956
+ continue;
9957
+ }
9958
+ try {
9959
+ entries = readdirSync5(plansDir);
9960
+ } catch {
9961
+ continue;
9962
+ }
9963
+ for (const entry of entries) {
9964
+ if (entry.startsWith("prd-") && entry.endsWith(".md")) {
9965
+ prdPaths.push(join24(plansDir, entry));
9966
+ } else if (entry.startsWith("test-spec-") && entry.endsWith(".md")) {
9967
+ testSpecPaths.push(join24(plansDir, entry));
9968
+ }
9969
+ }
9970
+ }
9971
+ return {
9972
+ prdPaths: sortArtifactPathsDescending(prdPaths),
9973
+ testSpecPaths: sortArtifactPathsDescending(testSpecPaths)
9974
+ };
9975
+ }
9976
+ function decodeQuotedValue(raw) {
9977
+ const normalized = raw.trim();
9978
+ if (!normalized) return null;
9979
+ try {
9980
+ return JSON.parse(normalized);
9981
+ } catch {
9982
+ if (normalized.startsWith('"') && normalized.endsWith('"') || normalized.startsWith("'") && normalized.endsWith("'")) {
9983
+ return normalized.slice(1, -1);
9984
+ }
9985
+ return null;
9986
+ }
9987
+ }
9988
+ function launchHintPattern(mode) {
9989
+ return mode === "team" ? /(?<command>(?:om[cx]\s+team|\$team)(?:\s+ralph)?(?:\s+(?<count>\d+)(?::(?<role>[a-z][a-z0-9-]*))?)?\s+(?<task>"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')(?<flags>(?:\s+--[\w-]+)*))/gi : /(?<command>(?:om[cx]\s+ralph|\$ralph)\s+(?<task>"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')(?<flags>(?:\s+--[\w-]+)*))/gi;
9990
+ }
9991
+ function collectLaunchHintMatches(content, mode) {
9992
+ return [...content.matchAll(launchHintPattern(mode))];
9993
+ }
9994
+ function selectLaunchHintMatch(matches, normalizedTask, normalizedCommand) {
9995
+ const decodedMatches = matches.flatMap((match) => {
9996
+ const command = match[0]?.trim();
9997
+ const task = match.groups?.task ? decodeQuotedValue(match.groups.task) : null;
9998
+ if (!command || task == null) return [];
9999
+ const flags = match.groups?.flags ?? "";
10000
+ const workerCount = match.groups?.count ? Number.parseInt(match.groups.count, 10) : void 0;
10001
+ return [{
10002
+ command,
10003
+ task,
10004
+ ...workerCount == null ? {} : { workerCount },
10005
+ agentType: match.groups?.role || void 0,
10006
+ linkedRalph: /\sralph(?:\s|$)/.test(command) || parseFlags(flags).linkedRalph
10007
+ }];
10008
+ });
10009
+ const matchesToConsider = normalizedCommand ? decodedMatches.filter((match) => match.command === normalizedCommand) : normalizedTask ? decodedMatches.filter((match) => match.task.trim() === normalizedTask) : decodedMatches;
10010
+ if (matchesToConsider.length === 0) return { status: "no-match" };
10011
+ if (matchesToConsider.length > 1) return { status: "ambiguous" };
10012
+ return { status: "unique", ...matchesToConsider[0] };
10013
+ }
10014
+ function parseFlags(flagStr) {
10015
+ return {
10016
+ linkedRalph: /--linked-ralph/.test(flagStr)
10017
+ };
10018
+ }
10019
+ function readApprovedExecutionLaunchHintOutcome(cwd, mode, options = {}) {
10020
+ const artifacts = readPlanningArtifacts(cwd);
10021
+ if (artifacts.prdPaths.length === 0) return { status: "absent" };
10022
+ const prdPath = options.prdPath ? artifacts.prdPaths.includes(options.prdPath) ? options.prdPath : null : selectLatestPlanningArtifactPath(artifacts.prdPaths);
10023
+ const matchingTestSpecs = selectMatchingTestSpecsForPrd(
10024
+ prdPath,
10025
+ artifacts.testSpecPaths
10026
+ );
10027
+ if (!prdPath) return { status: "absent" };
10028
+ if (artifacts.testSpecPaths.length > 0 && matchingTestSpecs.length === 0) {
10029
+ return { status: "absent" };
10030
+ }
10031
+ const content = readFileSafe(prdPath);
10032
+ if (!content) return { status: "absent" };
10033
+ const selected = selectLaunchHintMatch(
10034
+ collectLaunchHintMatches(content, mode),
10035
+ options.task?.trim(),
10036
+ options.command?.trim()
10037
+ );
10038
+ if (selected.status === "ambiguous") return { status: "ambiguous" };
10039
+ if (selected.status !== "unique") return { status: "absent" };
10040
+ if (options.requirePlanningComplete && !hasCompletePlanningPair(prdPath, matchingTestSpecs)) {
10041
+ return { status: "incomplete" };
10042
+ }
10043
+ if (mode === "team") {
10044
+ return {
10045
+ status: "resolved",
10046
+ hint: {
10047
+ mode: "team",
10048
+ command: selected.command,
10049
+ task: selected.task,
10050
+ workerCount: selected.workerCount,
10051
+ agentType: selected.agentType,
10052
+ linkedRalph: selected.linkedRalph,
10053
+ sourcePath: prdPath
10054
+ }
10055
+ };
10056
+ }
10057
+ return {
10058
+ status: "resolved",
10059
+ hint: {
10060
+ mode: "ralph",
10061
+ command: selected.command,
10062
+ task: selected.task,
10063
+ linkedRalph: selected.linkedRalph,
10064
+ sourcePath: prdPath
10065
+ }
10066
+ };
10067
+ }
10068
+
10069
+ // src/cli/team.ts
8473
10070
  var JOB_ID_PATTERN = /^omc-[a-z0-9]{1,16}$/;
8474
10071
  var VALID_CLI_AGENT_TYPES = /* @__PURE__ */ new Set(["claude", "copilot", "codex", "gemini", "cursor"]);
8475
10072
  var SUBCOMMANDS = /* @__PURE__ */ new Set(["start", "status", "wait", "cleanup", "resume", "shutdown", "api", "help", "--help", "-h"]);
@@ -8503,31 +10100,31 @@ function assertTeamSpawnAllowed(env = process.env) {
8503
10100
  );
8504
10101
  }
8505
10102
  function resolveJobsDir(env = process.env) {
8506
- return env.OMC_JOBS_DIR || join21(homedir3(), ".omcp", "team-jobs");
10103
+ return env.OMC_JOBS_DIR || join25(homedir3(), ".omcp", "team-jobs");
8507
10104
  }
8508
10105
  function resolveRuntimeCliPath(env = process.env) {
8509
10106
  if (env.OMC_RUNTIME_CLI_PATH) {
8510
10107
  return env.OMC_RUNTIME_CLI_PATH;
8511
10108
  }
8512
- const moduleDir = dirname13(fileURLToPath3(import.meta.url));
8513
- return join21(moduleDir, "../../bridge/runtime-cli.cjs");
10109
+ const moduleDir = dirname16(fileURLToPath3(import.meta.url));
10110
+ return join25(moduleDir, "../../bridge/runtime-cli.cjs");
8514
10111
  }
8515
10112
  function ensureJobsDir(jobsDir) {
8516
- if (!existsSync18(jobsDir)) {
10113
+ if (!existsSync22(jobsDir)) {
8517
10114
  mkdirSync3(jobsDir, { recursive: true });
8518
10115
  }
8519
10116
  }
8520
10117
  function jobPath(jobsDir, jobId) {
8521
- return join21(jobsDir, `${jobId}.json`);
10118
+ return join25(jobsDir, `${jobId}.json`);
8522
10119
  }
8523
10120
  function resultArtifactPath(jobsDir, jobId) {
8524
- return join21(jobsDir, `${jobId}-result.json`);
10121
+ return join25(jobsDir, `${jobId}-result.json`);
8525
10122
  }
8526
10123
  function panesArtifactPath(jobsDir, jobId) {
8527
- return join21(jobsDir, `${jobId}-panes.json`);
10124
+ return join25(jobsDir, `${jobId}-panes.json`);
8528
10125
  }
8529
10126
  function teamStateRoot2(cwd, teamName) {
8530
- return join21(cwd, ".omcp", "state", "team", teamName);
10127
+ return join25(cwd, ".omcp", "state", "team", teamName);
8531
10128
  }
8532
10129
  function validateJobId(jobId) {
8533
10130
  if (!JOB_ID_PATTERN.test(jobId)) {
@@ -8542,7 +10139,7 @@ function parseJsonSafe(content) {
8542
10139
  }
8543
10140
  }
8544
10141
  async function resolveCleanupPaneEvidence(job, jobsDir, jobId) {
8545
- const paneArtifact = await readFile10(panesArtifactPath(jobsDir, jobId), "utf-8").then((content) => parseJsonSafe(content)).catch(() => null);
10142
+ const paneArtifact = await readFile11(panesArtifactPath(jobsDir, jobId), "utf-8").then((content) => parseJsonSafe(content)).catch(() => null);
8546
10143
  if (paneArtifact?.paneIds?.length) return { paneArtifact };
8547
10144
  const config = await readTeamConfig(job.teamName, job.cwd).catch(() => null);
8548
10145
  if (!config) {
@@ -8567,7 +10164,7 @@ async function resolveCleanupPaneEvidence(job, jobsDir, jobId) {
8567
10164
  }
8568
10165
  function readJobFromDisk(jobId, jobsDir) {
8569
10166
  try {
8570
- const content = readFileSync11(jobPath(jobsDir, jobId), "utf-8");
10167
+ const content = readFileSync12(jobPath(jobsDir, jobId), "utf-8");
8571
10168
  return parseJsonSafe(content);
8572
10169
  } catch {
8573
10170
  return null;
@@ -8596,7 +10193,7 @@ function generateJobId(now = Date.now()) {
8596
10193
  }
8597
10194
  function convergeWithResultArtifact(jobId, job, jobsDir) {
8598
10195
  try {
8599
- const artifactRaw = readFileSync11(resultArtifactPath(jobsDir, jobId), "utf-8");
10196
+ const artifactRaw = readFileSync12(resultArtifactPath(jobsDir, jobId), "utf-8");
8600
10197
  const artifactParsed = parseJsonSafe(artifactRaw);
8601
10198
  if (artifactParsed?.status === "completed" || artifactParsed?.status === "failed") {
8602
10199
  return {
@@ -8686,7 +10283,8 @@ async function startTeamJob(input) {
8686
10283
  newWindow: input.newWindow,
8687
10284
  pollIntervalMs: input.pollIntervalMs,
8688
10285
  sentinelGateTimeoutMs: input.sentinelGateTimeoutMs,
8689
- sentinelGatePollIntervalMs: input.sentinelGatePollIntervalMs
10286
+ sentinelGatePollIntervalMs: input.sentinelGatePollIntervalMs,
10287
+ autoMerge: input.autoMerge
8690
10288
  };
8691
10289
  child.stdin.write(JSON.stringify(payload));
8692
10290
  child.stdin.end();
@@ -8981,7 +10579,7 @@ async function teamCleanupCommand(jobId, cleanupOptions = {}, options = {}) {
8981
10579
  }
8982
10580
  var TEAM_USAGE = `
8983
10581
  Usage:
8984
- omcp team start --agent <claude|copilot|codex|gemini|cursor>[,<agent>...] --task "<task>" [--count N] [--name TEAM] [--cwd DIR] [--new-window] [--json]
10582
+ omcp team start --agent <claude|copilot|codex|gemini|cursor>[,<agent>...] --task "<task>" [--count N] [--name TEAM] [--cwd DIR] [--new-window] [--auto-merge] [--json]
8985
10583
  omcp team status <job_id|team_name> [--json] [--cwd DIR]
8986
10584
  omcp team wait <job_id> [--timeout-ms MS] [--json]
8987
10585
  omcp team cleanup <job_id> [--grace-ms MS] [--json]
@@ -8994,6 +10592,13 @@ Worktrees:
8994
10592
  Native per-worker git worktree mode is opt-in/config-gated with team.ops.worktreeMode or OMC_TEAM_WORKTREE_MODE=detached|named.
8995
10593
  Status JSON includes workspace_mode, worktree_mode, team_state_root, and per-worker worktree metadata.
8996
10594
 
10595
+ Auto-merge (v2-only):
10596
+ --auto-merge Enable per-commit auto-merge to leader and auto-rebase fanout.
10597
+ Each worker runs in a dedicated git worktree on omc-team/{team}/{worker}.
10598
+ Bursts of rapid worker commits coalesce to a single merge of HEAD.
10599
+ Requires OMC_RUNTIME_V2=1. Leader branch must not be 'main' or 'master'.
10600
+ Equivalent to OMC_TEAMS_AUTO_MERGE=1.
10601
+
8997
10602
  Examples:
8998
10603
  omcp team start --agent codex --count 2 --task "review auth flow" --new-window
8999
10604
  omcp team status omc-abc123
@@ -9015,6 +10620,7 @@ function parseStartArgs(args) {
9015
10620
  let pollIntervalMs;
9016
10621
  let sentinelGateTimeoutMs;
9017
10622
  let sentinelGatePollIntervalMs;
10623
+ let autoMerge = process.env.OMC_TEAMS_AUTO_MERGE === "1";
9018
10624
  for (let i = 0; i < args.length; i += 1) {
9019
10625
  const token = args[i];
9020
10626
  const next = args[i + 1];
@@ -9026,6 +10632,10 @@ function parseStartArgs(args) {
9026
10632
  newWindow = true;
9027
10633
  continue;
9028
10634
  }
10635
+ if (token === "--auto-merge") {
10636
+ autoMerge = true;
10637
+ continue;
10638
+ }
9029
10639
  if (token === "--agent") {
9030
10640
  if (!next) throw new Error("Missing value after --agent");
9031
10641
  agentValues.push(...next.split(",").map(normalizeAgentType));
@@ -9143,7 +10753,8 @@ function parseStartArgs(args) {
9143
10753
  ...newWindow ? { newWindow: true } : {},
9144
10754
  ...pollIntervalMs != null ? { pollIntervalMs } : {},
9145
10755
  ...sentinelGateTimeoutMs != null ? { sentinelGateTimeoutMs } : {},
9146
- ...sentinelGatePollIntervalMs != null ? { sentinelGatePollIntervalMs } : {}
10756
+ ...sentinelGatePollIntervalMs != null ? { sentinelGatePollIntervalMs } : {},
10757
+ ...autoMerge ? { autoMerge: true } : {}
9147
10758
  },
9148
10759
  json
9149
10760
  };
@@ -9326,14 +10937,15 @@ function parseLegacyStartAlias(args) {
9326
10937
  if (!spec) return null;
9327
10938
  const match = spec.match(/^(\d+):([a-zA-Z0-9_-]+)(?::([a-zA-Z0-9_-]+))?$/);
9328
10939
  if (!match) return null;
9329
- const workerCount = toInt(match[1], "worker-count");
10940
+ let workerCount = toInt(match[1], "worker-count");
9330
10941
  if (workerCount < 1) throw new Error("worker-count must be >= 1");
9331
- const agentType = normalizeAgentType(match[2]);
10942
+ let agentType = normalizeAgentType(match[2]);
9332
10943
  const role = match[3] || void 0;
9333
10944
  index += 1;
9334
10945
  let json = false;
9335
10946
  let cwd = process.cwd();
9336
10947
  let newWindow = false;
10948
+ let autoMerge = process.env.OMC_TEAMS_AUTO_MERGE === "1";
9337
10949
  const taskParts = [];
9338
10950
  for (let i = index; i < args.length; i += 1) {
9339
10951
  const token = args[i];
@@ -9346,6 +10958,10 @@ function parseLegacyStartAlias(args) {
9346
10958
  newWindow = true;
9347
10959
  continue;
9348
10960
  }
10961
+ if (token === "--auto-merge") {
10962
+ autoMerge = true;
10963
+ continue;
10964
+ }
9349
10965
  if (token === "--cwd") {
9350
10966
  if (!next) throw new Error("Missing value after --cwd");
9351
10967
  cwd = next;
@@ -9358,8 +10974,35 @@ function parseLegacyStartAlias(args) {
9358
10974
  }
9359
10975
  taskParts.push(token);
9360
10976
  }
9361
- const task = taskParts.join(" ").trim();
10977
+ let task = taskParts.join(" ").trim();
9362
10978
  if (!task) throw new Error("Legacy start alias requires a task string");
10979
+ const shortFollowup = ["team", "/team", "team please", "run team", "start team"].includes(task.toLowerCase());
10980
+ if (shortFollowup) {
10981
+ const approvedHintOutcome = readApprovedExecutionLaunchHintOutcome(cwd, "team", {
10982
+ requirePlanningComplete: true
10983
+ });
10984
+ if (approvedHintOutcome.status === "ambiguous") {
10985
+ throw new Error("approved_execution_hint_ambiguous:team");
10986
+ }
10987
+ if (approvedHintOutcome.status === "incomplete") {
10988
+ throw new Error("approved_execution_hint_incomplete:team");
10989
+ }
10990
+ if (approvedHintOutcome.status === "resolved") {
10991
+ task = approvedHintOutcome.hint.task;
10992
+ workerCount = approvedHintOutcome.hint.workerCount ?? workerCount;
10993
+ agentType = approvedHintOutcome.hint.agentType ? normalizeAgentType(approvedHintOutcome.hint.agentType) : agentType;
10994
+ ralph = approvedHintOutcome.hint.linkedRalph === true ? true : ralph;
10995
+ }
10996
+ } else {
10997
+ const command = `omc team ${ralph ? "ralph " : ""}${spec} ${JSON.stringify(task)}`;
10998
+ const approvedHintOutcome = readApprovedExecutionLaunchHintOutcome(cwd, "team", {
10999
+ task,
11000
+ command
11001
+ });
11002
+ if (approvedHintOutcome.status === "ambiguous") {
11003
+ throw new Error("approved_execution_hint_ambiguous:team");
11004
+ }
11005
+ }
9363
11006
  return {
9364
11007
  workerCount,
9365
11008
  agentType,
@@ -9369,7 +11012,8 @@ function parseLegacyStartAlias(args) {
9369
11012
  ralph,
9370
11013
  json,
9371
11014
  cwd,
9372
- ...newWindow ? { newWindow: true } : {}
11015
+ ...newWindow ? { newWindow: true } : {},
11016
+ ...autoMerge ? { autoMerge: true } : {}
9373
11017
  };
9374
11018
  }
9375
11019
  async function teamCommand(argv) {
@@ -9445,7 +11089,8 @@ async function teamCommand(argv) {
9445
11089
  agentTypes: Array.from({ length: legacy.workerCount }, () => legacy.agentType),
9446
11090
  tasks,
9447
11091
  cwd: legacy.cwd,
9448
- ...legacy.newWindow ? { newWindow: true } : {}
11092
+ ...legacy.newWindow ? { newWindow: true } : {},
11093
+ ...legacy.autoMerge ? { autoMerge: true } : {}
9449
11094
  });
9450
11095
  output(result, legacy.json);
9451
11096
  return;