oh-my-copilot 4.13.1 → 4.13.21

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 (507) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +3 -3
  4. package/bridge/cli.cjs +4709 -1961
  5. package/bridge/mcp-server.cjs +112 -11
  6. package/bridge/runtime-cli.cjs +4917 -2645
  7. package/bridge/team-bridge.cjs +6 -4
  8. package/bridge/team-mcp.cjs +19607 -0
  9. package/bridge/team.js +4895 -2215
  10. package/dist/__tests__/auto-slash-aliases.test.js +91 -0
  11. package/dist/__tests__/auto-slash-aliases.test.js.map +1 -1
  12. package/dist/__tests__/hooks/learner/bridge.test.js +25 -0
  13. package/dist/__tests__/hooks/learner/bridge.test.js.map +1 -1
  14. package/dist/__tests__/hooks/learner/parser.test.js +30 -0
  15. package/dist/__tests__/hooks/learner/parser.test.js.map +1 -1
  16. package/dist/__tests__/hud/extra-usage.test.js +42 -0
  17. package/dist/__tests__/hud/extra-usage.test.js.map +1 -1
  18. package/dist/__tests__/hud/labels.test.d.ts +2 -0
  19. package/dist/__tests__/hud/labels.test.d.ts.map +1 -0
  20. package/dist/__tests__/hud/labels.test.js +158 -0
  21. package/dist/__tests__/hud/labels.test.js.map +1 -0
  22. package/dist/__tests__/hud/render-enterprise.test.d.ts +5 -0
  23. package/dist/__tests__/hud/render-enterprise.test.d.ts.map +1 -0
  24. package/dist/__tests__/hud/render-enterprise.test.js +232 -0
  25. package/dist/__tests__/hud/render-enterprise.test.js.map +1 -0
  26. package/dist/__tests__/hud/render-rate-limits-priority.test.js +185 -0
  27. package/dist/__tests__/hud/render-rate-limits-priority.test.js.map +1 -1
  28. package/dist/__tests__/hud/stdin-session-scope.test.d.ts +2 -0
  29. package/dist/__tests__/hud/stdin-session-scope.test.d.ts.map +1 -0
  30. package/dist/__tests__/hud/stdin-session-scope.test.js +169 -0
  31. package/dist/__tests__/hud/stdin-session-scope.test.js.map +1 -0
  32. package/dist/__tests__/hud/usage-api.test.js +111 -1
  33. package/dist/__tests__/hud/usage-api.test.js.map +1 -1
  34. package/dist/__tests__/hud-marketplace-resolution.test.js +34 -0
  35. package/dist/__tests__/hud-marketplace-resolution.test.js.map +1 -1
  36. package/dist/__tests__/installer-hud-skip.test.js +6 -0
  37. package/dist/__tests__/installer-hud-skip.test.js.map +1 -1
  38. package/dist/__tests__/installer-omc-reference.test.js +1 -1
  39. package/dist/__tests__/installer-omc-reference.test.js.map +1 -1
  40. package/dist/__tests__/keyword-detector-echo-guard.test.d.ts +2 -0
  41. package/dist/__tests__/keyword-detector-echo-guard.test.d.ts.map +1 -0
  42. package/dist/__tests__/keyword-detector-echo-guard.test.js +218 -0
  43. package/dist/__tests__/keyword-detector-echo-guard.test.js.map +1 -0
  44. package/dist/__tests__/mnemosyne/detector.test.js +2 -0
  45. package/dist/__tests__/mnemosyne/detector.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__/pre-tool-enforcer.test.js +84 -1
  49. package/dist/__tests__/pre-tool-enforcer.test.js.map +1 -1
  50. package/dist/__tests__/preemptive-compaction-hook.test.js +63 -0
  51. package/dist/__tests__/preemptive-compaction-hook.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__/skills.test.js +363 -4
  57. package/dist/__tests__/skills.test.js.map +1 -1
  58. package/dist/__tests__/state-root-resolution.test.js +120 -6
  59. package/dist/__tests__/state-root-resolution.test.js.map +1 -1
  60. package/dist/autoresearch/__tests__/runtime.test.js +225 -28
  61. package/dist/autoresearch/__tests__/runtime.test.js.map +1 -1
  62. package/dist/autoresearch/runtime.d.ts.map +1 -1
  63. package/dist/autoresearch/runtime.js +9 -5
  64. package/dist/autoresearch/runtime.js.map +1 -1
  65. package/dist/cli/__tests__/team-status-worktree.test.d.ts +2 -0
  66. package/dist/cli/__tests__/team-status-worktree.test.d.ts.map +1 -0
  67. package/dist/cli/__tests__/team-status-worktree.test.js +96 -0
  68. package/dist/cli/__tests__/team-status-worktree.test.js.map +1 -0
  69. package/dist/cli/__tests__/team.test.js +164 -0
  70. package/dist/cli/__tests__/team.test.js.map +1 -1
  71. package/dist/cli/autoresearch.d.ts +1 -1
  72. package/dist/cli/autoresearch.d.ts.map +1 -1
  73. package/dist/cli/autoresearch.js +4 -4
  74. package/dist/cli/commands/team.d.ts +1 -0
  75. package/dist/cli/commands/team.d.ts.map +1 -1
  76. package/dist/cli/commands/team.js +15 -3
  77. package/dist/cli/commands/team.js.map +1 -1
  78. package/dist/cli/index.js +3 -0
  79. package/dist/cli/index.js.map +1 -1
  80. package/dist/cli/team.d.ts +6 -0
  81. package/dist/cli/team.d.ts.map +1 -1
  82. package/dist/cli/team.js +126 -5
  83. package/dist/cli/team.js.map +1 -1
  84. package/dist/cli/utils/formatting.d.ts +0 -10
  85. package/dist/cli/utils/formatting.d.ts.map +1 -1
  86. package/dist/cli/utils/formatting.js +0 -52
  87. package/dist/cli/utils/formatting.js.map +1 -1
  88. package/dist/config/__tests__/loader.test.js.map +1 -1
  89. package/dist/features/boulder-state/constants.d.ts +1 -1
  90. package/dist/features/boulder-state/constants.d.ts.map +1 -1
  91. package/dist/features/builtin-skills/skills.d.ts.map +1 -1
  92. package/dist/features/builtin-skills/skills.js +11 -3
  93. package/dist/features/builtin-skills/skills.js.map +1 -1
  94. package/dist/features/builtin-skills/types.d.ts +7 -0
  95. package/dist/features/builtin-skills/types.d.ts.map +1 -1
  96. package/dist/features/delegation-enforcer.d.ts +2 -0
  97. package/dist/features/delegation-enforcer.d.ts.map +1 -1
  98. package/dist/features/delegation-enforcer.js +10 -0
  99. package/dist/features/delegation-enforcer.js.map +1 -1
  100. package/dist/features/magic-keywords.d.ts.map +1 -1
  101. package/dist/features/magic-keywords.js +30 -1
  102. package/dist/features/magic-keywords.js.map +1 -1
  103. package/dist/features/rate-limit-wait/tmux-detector.d.ts +4 -4
  104. package/dist/features/rate-limit-wait/tmux-detector.js +9 -9
  105. package/dist/features/rate-limit-wait/tmux-detector.js.map +1 -1
  106. package/dist/hooks/__tests__/bridge-routing.test.js +156 -0
  107. package/dist/hooks/__tests__/bridge-routing.test.js.map +1 -1
  108. package/dist/hooks/auto-slash-command/constants.d.ts.map +1 -1
  109. package/dist/hooks/auto-slash-command/constants.js +1 -0
  110. package/dist/hooks/auto-slash-command/constants.js.map +1 -1
  111. package/dist/hooks/auto-slash-command/executor.d.ts.map +1 -1
  112. package/dist/hooks/auto-slash-command/executor.js +29 -0
  113. package/dist/hooks/auto-slash-command/executor.js.map +1 -1
  114. package/dist/hooks/bridge.d.ts.map +1 -1
  115. package/dist/hooks/bridge.js +184 -1
  116. package/dist/hooks/bridge.js.map +1 -1
  117. package/dist/hooks/learner/bridge.d.ts.map +1 -1
  118. package/dist/hooks/learner/bridge.js +4 -109
  119. package/dist/hooks/learner/bridge.js.map +1 -1
  120. package/dist/hooks/learner/detector.js +1 -1
  121. package/dist/hooks/learner/parser.d.ts +9 -0
  122. package/dist/hooks/learner/parser.d.ts.map +1 -1
  123. package/dist/hooks/learner/parser.js +18 -5
  124. package/dist/hooks/learner/parser.js.map +1 -1
  125. package/dist/hooks/learner/types.d.ts +6 -0
  126. package/dist/hooks/learner/types.d.ts.map +1 -1
  127. package/dist/hooks/mode-registry/__tests__/session-isolation.test.js +16 -0
  128. package/dist/hooks/mode-registry/__tests__/session-isolation.test.js.map +1 -1
  129. package/dist/hooks/notepad/index.d.ts +4 -1
  130. package/dist/hooks/notepad/index.d.ts.map +1 -1
  131. package/dist/hooks/notepad/index.js +7 -4
  132. package/dist/hooks/notepad/index.js.map +1 -1
  133. package/dist/hooks/persistent-mode/__tests__/ralph-verification-flow.test.js +70 -3
  134. package/dist/hooks/persistent-mode/__tests__/ralph-verification-flow.test.js.map +1 -1
  135. package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
  136. package/dist/hooks/persistent-mode/index.js +17 -13
  137. package/dist/hooks/persistent-mode/index.js.map +1 -1
  138. package/dist/hooks/persistent-mode/stop-hook-blocking.test.js +175 -3
  139. package/dist/hooks/persistent-mode/stop-hook-blocking.test.js.map +1 -1
  140. package/dist/hooks/project-memory/__tests__/integration.test.js +117 -3
  141. package/dist/hooks/project-memory/__tests__/integration.test.js.map +1 -1
  142. package/dist/hooks/project-memory/__tests__/storage.test.js +11 -11
  143. package/dist/hooks/project-memory/__tests__/storage.test.js.map +1 -1
  144. package/dist/hooks/project-memory/index.d.ts.map +1 -1
  145. package/dist/hooks/project-memory/index.js +24 -12
  146. package/dist/hooks/project-memory/index.js.map +1 -1
  147. package/dist/hooks/ralph/index.d.ts +1 -1
  148. package/dist/hooks/ralph/index.d.ts.map +1 -1
  149. package/dist/hooks/ralph/index.js +1 -1
  150. package/dist/hooks/ralph/index.js.map +1 -1
  151. package/dist/hooks/ralph/loop.d.ts +6 -6
  152. package/dist/hooks/ralph/loop.d.ts.map +1 -1
  153. package/dist/hooks/ralph/loop.js +18 -18
  154. package/dist/hooks/ralph/loop.js.map +1 -1
  155. package/dist/hooks/ralph/prd.d.ts +23 -12
  156. package/dist/hooks/ralph/prd.d.ts.map +1 -1
  157. package/dist/hooks/ralph/prd.js +88 -42
  158. package/dist/hooks/ralph/prd.js.map +1 -1
  159. package/dist/hooks/session-end/__tests__/mode-state-cleanup.test.js +20 -0
  160. package/dist/hooks/session-end/__tests__/mode-state-cleanup.test.js.map +1 -1
  161. package/dist/hooks/session-end/index.d.ts +9 -0
  162. package/dist/hooks/session-end/index.d.ts.map +1 -1
  163. package/dist/hooks/session-end/index.js +110 -0
  164. package/dist/hooks/session-end/index.js.map +1 -1
  165. package/dist/hooks/skill-bridge.cjs +114 -92
  166. package/dist/hooks/skill-state/index.d.ts.map +1 -1
  167. package/dist/hooks/skill-state/index.js +1 -0
  168. package/dist/hooks/skill-state/index.js.map +1 -1
  169. package/dist/hud/__tests__/enterprise-cost.test.d.ts +5 -0
  170. package/dist/hud/__tests__/enterprise-cost.test.d.ts.map +1 -0
  171. package/dist/hud/__tests__/enterprise-cost.test.js +151 -0
  172. package/dist/hud/__tests__/enterprise-cost.test.js.map +1 -0
  173. package/dist/hud/__tests__/git-status.test.js +18 -0
  174. package/dist/hud/__tests__/git-status.test.js.map +1 -1
  175. package/dist/hud/__tests__/usage-api-enterprise.test.d.ts +5 -0
  176. package/dist/hud/__tests__/usage-api-enterprise.test.d.ts.map +1 -0
  177. package/dist/hud/__tests__/usage-api-enterprise.test.js +249 -0
  178. package/dist/hud/__tests__/usage-api-enterprise.test.js.map +1 -0
  179. package/dist/hud/elements/background.d.ts +3 -3
  180. package/dist/hud/elements/background.d.ts.map +1 -1
  181. package/dist/hud/elements/background.js +5 -4
  182. package/dist/hud/elements/background.js.map +1 -1
  183. package/dist/hud/elements/call-counts.d.ts +2 -2
  184. package/dist/hud/elements/call-counts.d.ts.map +1 -1
  185. package/dist/hud/elements/call-counts.js +7 -6
  186. package/dist/hud/elements/call-counts.js.map +1 -1
  187. package/dist/hud/elements/context.d.ts +3 -3
  188. package/dist/hud/elements/context.d.ts.map +1 -1
  189. package/dist/hud/elements/context.js +5 -4
  190. package/dist/hud/elements/context.js.map +1 -1
  191. package/dist/hud/elements/enterprise-cost.d.ts +16 -0
  192. package/dist/hud/elements/enterprise-cost.d.ts.map +1 -0
  193. package/dist/hud/elements/enterprise-cost.js +63 -0
  194. package/dist/hud/elements/enterprise-cost.js.map +1 -0
  195. package/dist/hud/elements/git.d.ts +2 -1
  196. package/dist/hud/elements/git.d.ts.map +1 -1
  197. package/dist/hud/elements/git.js +7 -6
  198. package/dist/hud/elements/git.js.map +1 -1
  199. package/dist/hud/elements/permission.d.ts.map +1 -1
  200. package/dist/hud/elements/permission.js +2 -5
  201. package/dist/hud/elements/permission.js.map +1 -1
  202. package/dist/hud/elements/ralph.d.ts +2 -2
  203. package/dist/hud/elements/ralph.d.ts.map +1 -1
  204. package/dist/hud/elements/ralph.js +3 -2
  205. package/dist/hud/elements/ralph.js.map +1 -1
  206. package/dist/hud/elements/session.d.ts.map +1 -1
  207. package/dist/hud/elements/session.js +5 -9
  208. package/dist/hud/elements/session.js.map +1 -1
  209. package/dist/hud/elements/thinking.d.ts +2 -2
  210. package/dist/hud/elements/thinking.d.ts.map +1 -1
  211. package/dist/hud/elements/thinking.js +3 -2
  212. package/dist/hud/elements/thinking.js.map +1 -1
  213. package/dist/hud/elements/token-usage.d.ts +2 -2
  214. package/dist/hud/elements/token-usage.d.ts.map +1 -1
  215. package/dist/hud/elements/token-usage.js +3 -2
  216. package/dist/hud/elements/token-usage.js.map +1 -1
  217. package/dist/hud/index.d.ts.map +1 -1
  218. package/dist/hud/index.js +5 -1
  219. package/dist/hud/index.js.map +1 -1
  220. package/dist/hud/render.d.ts.map +1 -1
  221. package/dist/hud/render.js +43 -10
  222. package/dist/hud/render.js.map +1 -1
  223. package/dist/hud/state.d.ts.map +1 -1
  224. package/dist/hud/state.js +18 -1
  225. package/dist/hud/state.js.map +1 -1
  226. package/dist/hud/stdin.d.ts.map +1 -1
  227. package/dist/hud/stdin.js +49 -5
  228. package/dist/hud/stdin.js.map +1 -1
  229. package/dist/hud/types.d.ts +42 -0
  230. package/dist/hud/types.d.ts.map +1 -1
  231. package/dist/hud/types.js +59 -1
  232. package/dist/hud/types.js.map +1 -1
  233. package/dist/hud/usage-api.d.ts +19 -1
  234. package/dist/hud/usage-api.d.ts.map +1 -1
  235. package/dist/hud/usage-api.js +101 -18
  236. package/dist/hud/usage-api.js.map +1 -1
  237. package/dist/installer/__tests__/hud-cache-wrapper.test.d.ts +2 -0
  238. package/dist/installer/__tests__/hud-cache-wrapper.test.d.ts.map +1 -0
  239. package/dist/installer/__tests__/hud-cache-wrapper.test.js +235 -0
  240. package/dist/installer/__tests__/hud-cache-wrapper.test.js.map +1 -0
  241. package/dist/installer/__tests__/standalone-hook-reconcile.test.js +2 -0
  242. package/dist/installer/__tests__/standalone-hook-reconcile.test.js.map +1 -1
  243. package/dist/installer/index.d.ts +1 -0
  244. package/dist/installer/index.d.ts.map +1 -1
  245. package/dist/installer/index.js +83 -31
  246. package/dist/installer/index.js.map +1 -1
  247. package/dist/lib/__tests__/worktree-paths.test.js +162 -39
  248. package/dist/lib/__tests__/worktree-paths.test.js.map +1 -1
  249. package/dist/lib/worktree-paths.d.ts +87 -9
  250. package/dist/lib/worktree-paths.d.ts.map +1 -1
  251. package/dist/lib/worktree-paths.js +209 -27
  252. package/dist/lib/worktree-paths.js.map +1 -1
  253. package/dist/mcp/__tests__/team-server-artifact-convergence.test.js +156 -4
  254. package/dist/mcp/__tests__/team-server-artifact-convergence.test.js.map +1 -1
  255. package/dist/mcp/omc-tools-server.d.ts +11 -17
  256. package/dist/mcp/omc-tools-server.d.ts.map +1 -1
  257. package/dist/mcp/omc-tools-server.js +22 -32
  258. package/dist/mcp/omc-tools-server.js.map +1 -1
  259. package/dist/mcp/team-job-convergence.d.ts +9 -2
  260. package/dist/mcp/team-job-convergence.d.ts.map +1 -1
  261. package/dist/mcp/team-job-convergence.js +35 -14
  262. package/dist/mcp/team-job-convergence.js.map +1 -1
  263. package/dist/mcp/team-server.d.ts.map +1 -1
  264. package/dist/mcp/team-server.js +93 -13
  265. package/dist/mcp/team-server.js.map +1 -1
  266. package/dist/ralphthon/prd.d.ts.map +1 -1
  267. package/dist/ralphthon/prd.js +13 -17
  268. package/dist/ralphthon/prd.js.map +1 -1
  269. package/dist/shared/types.d.ts +2 -0
  270. package/dist/shared/types.d.ts.map +1 -1
  271. package/dist/team/__tests__/api-interop.cleanup.test.d.ts +2 -0
  272. package/dist/team/__tests__/api-interop.cleanup.test.d.ts.map +1 -0
  273. package/dist/team/__tests__/api-interop.cleanup.test.js +234 -0
  274. package/dist/team/__tests__/api-interop.cleanup.test.js.map +1 -0
  275. package/dist/team/__tests__/api-interop.dispatch.test.js +2 -1
  276. package/dist/team/__tests__/api-interop.dispatch.test.js.map +1 -1
  277. package/dist/team/__tests__/auto-merge-restart.test.d.ts +2 -0
  278. package/dist/team/__tests__/auto-merge-restart.test.d.ts.map +1 -0
  279. package/dist/team/__tests__/auto-merge-restart.test.js +128 -0
  280. package/dist/team/__tests__/auto-merge-restart.test.js.map +1 -0
  281. package/dist/team/__tests__/auto-merge.perf.test.d.ts +2 -0
  282. package/dist/team/__tests__/auto-merge.perf.test.d.ts.map +1 -0
  283. package/dist/team/__tests__/auto-merge.perf.test.js +85 -0
  284. package/dist/team/__tests__/auto-merge.perf.test.js.map +1 -0
  285. package/dist/team/__tests__/conflict-mailbox.test.d.ts +2 -0
  286. package/dist/team/__tests__/conflict-mailbox.test.d.ts.map +1 -0
  287. package/dist/team/__tests__/conflict-mailbox.test.js +238 -0
  288. package/dist/team/__tests__/conflict-mailbox.test.js.map +1 -0
  289. package/dist/team/__tests__/git-worktree.test.js +164 -3
  290. package/dist/team/__tests__/git-worktree.test.js.map +1 -1
  291. package/dist/team/__tests__/helpers/git-fixture.d.ts +72 -0
  292. package/dist/team/__tests__/helpers/git-fixture.d.ts.map +1 -0
  293. package/dist/team/__tests__/helpers/git-fixture.js +252 -0
  294. package/dist/team/__tests__/helpers/git-fixture.js.map +1 -0
  295. package/dist/team/__tests__/leader-inbox.test.d.ts +2 -0
  296. package/dist/team/__tests__/leader-inbox.test.d.ts.map +1 -0
  297. package/dist/team/__tests__/leader-inbox.test.js +152 -0
  298. package/dist/team/__tests__/leader-inbox.test.js.map +1 -0
  299. package/dist/team/__tests__/merge-orchestrator.test.d.ts +2 -0
  300. package/dist/team/__tests__/merge-orchestrator.test.d.ts.map +1 -0
  301. package/dist/team/__tests__/merge-orchestrator.test.js +779 -0
  302. package/dist/team/__tests__/merge-orchestrator.test.js.map +1 -0
  303. package/dist/team/__tests__/model-contract.test.js +35 -21
  304. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  305. package/dist/team/__tests__/rebase-smoke.test.d.ts +2 -0
  306. package/dist/team/__tests__/rebase-smoke.test.d.ts.map +1 -0
  307. package/dist/team/__tests__/rebase-smoke.test.js +272 -0
  308. package/dist/team/__tests__/rebase-smoke.test.js.map +1 -0
  309. package/dist/team/__tests__/runtime-cli.test.js +11 -3
  310. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  311. package/dist/team/__tests__/runtime-prompt-mode.test.js +14 -16
  312. package/dist/team/__tests__/runtime-prompt-mode.test.js.map +1 -1
  313. package/dist/team/__tests__/runtime-v2-role-routing.test.js +7 -1
  314. package/dist/team/__tests__/runtime-v2-role-routing.test.js.map +1 -1
  315. package/dist/team/__tests__/runtime-v2.dispatch.test.js +293 -15
  316. package/dist/team/__tests__/runtime-v2.dispatch.test.js.map +1 -1
  317. package/dist/team/__tests__/runtime-v2.monitor.test.d.ts +2 -0
  318. package/dist/team/__tests__/runtime-v2.monitor.test.d.ts.map +1 -0
  319. package/dist/team/__tests__/runtime-v2.monitor.test.js +219 -0
  320. package/dist/team/__tests__/runtime-v2.monitor.test.js.map +1 -0
  321. package/dist/team/__tests__/runtime-v2.shutdown.test.d.ts +2 -0
  322. package/dist/team/__tests__/runtime-v2.shutdown.test.d.ts.map +1 -0
  323. package/dist/team/__tests__/runtime-v2.shutdown.test.js +292 -0
  324. package/dist/team/__tests__/runtime-v2.shutdown.test.js.map +1 -0
  325. package/dist/team/__tests__/scaling-launch-config.test.js +203 -3
  326. package/dist/team/__tests__/scaling-launch-config.test.js.map +1 -1
  327. package/dist/team/__tests__/team-summary-worktree.test.d.ts +2 -0
  328. package/dist/team/__tests__/team-summary-worktree.test.d.ts.map +1 -0
  329. package/dist/team/__tests__/team-summary-worktree.test.js +85 -0
  330. package/dist/team/__tests__/team-summary-worktree.test.js.map +1 -0
  331. package/dist/team/__tests__/teardown-invariant.test.d.ts +2 -0
  332. package/dist/team/__tests__/teardown-invariant.test.d.ts.map +1 -0
  333. package/dist/team/__tests__/teardown-invariant.test.js +176 -0
  334. package/dist/team/__tests__/teardown-invariant.test.js.map +1 -0
  335. package/dist/team/__tests__/tmux-session-liveness.test.d.ts +2 -0
  336. package/dist/team/__tests__/tmux-session-liveness.test.d.ts.map +1 -0
  337. package/dist/team/__tests__/tmux-session-liveness.test.js +37 -0
  338. package/dist/team/__tests__/tmux-session-liveness.test.js.map +1 -0
  339. package/dist/team/__tests__/worker-bootstrap.test.js +47 -1
  340. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  341. package/dist/team/__tests__/worker-commit-cadence.test.d.ts +2 -0
  342. package/dist/team/__tests__/worker-commit-cadence.test.d.ts.map +1 -0
  343. package/dist/team/__tests__/worker-commit-cadence.test.js +315 -0
  344. package/dist/team/__tests__/worker-commit-cadence.test.js.map +1 -0
  345. package/dist/team/__tests__/worktree-contract-fields.test.d.ts +2 -0
  346. package/dist/team/__tests__/worktree-contract-fields.test.d.ts.map +1 -0
  347. package/dist/team/__tests__/worktree-contract-fields.test.js +157 -0
  348. package/dist/team/__tests__/worktree-contract-fields.test.js.map +1 -0
  349. package/dist/team/__tests__/worktree-runtime-e2e.test.d.ts +2 -0
  350. package/dist/team/__tests__/worktree-runtime-e2e.test.d.ts.map +1 -0
  351. package/dist/team/__tests__/worktree-runtime-e2e.test.js +182 -0
  352. package/dist/team/__tests__/worktree-runtime-e2e.test.js.map +1 -0
  353. package/dist/team/api-interop.d.ts +2 -1
  354. package/dist/team/api-interop.d.ts.map +1 -1
  355. package/dist/team/api-interop.js +121 -14
  356. package/dist/team/api-interop.js.map +1 -1
  357. package/dist/team/capabilities.d.ts.map +1 -1
  358. package/dist/team/capabilities.js +2 -0
  359. package/dist/team/capabilities.js.map +1 -1
  360. package/dist/team/cli-worker-contract.d.ts +7 -6
  361. package/dist/team/cli-worker-contract.d.ts.map +1 -1
  362. package/dist/team/cli-worker-contract.js +7 -6
  363. package/dist/team/cli-worker-contract.js.map +1 -1
  364. package/dist/team/conflict-mailbox.d.ts +49 -0
  365. package/dist/team/conflict-mailbox.d.ts.map +1 -0
  366. package/dist/team/conflict-mailbox.js +98 -0
  367. package/dist/team/conflict-mailbox.js.map +1 -0
  368. package/dist/team/events.d.ts +3 -0
  369. package/dist/team/events.d.ts.map +1 -1
  370. package/dist/team/events.js +2 -1
  371. package/dist/team/events.js.map +1 -1
  372. package/dist/team/git-worktree.d.ts +78 -10
  373. package/dist/team/git-worktree.d.ts.map +1 -1
  374. package/dist/team/git-worktree.js +453 -97
  375. package/dist/team/git-worktree.js.map +1 -1
  376. package/dist/team/governance.d.ts +3 -2
  377. package/dist/team/governance.d.ts.map +1 -1
  378. package/dist/team/governance.js.map +1 -1
  379. package/dist/team/leader-inbox.d.ts +28 -0
  380. package/dist/team/leader-inbox.d.ts.map +1 -0
  381. package/dist/team/leader-inbox.js +68 -0
  382. package/dist/team/leader-inbox.js.map +1 -0
  383. package/dist/team/merge-coordinator.d.ts +6 -0
  384. package/dist/team/merge-coordinator.d.ts.map +1 -1
  385. package/dist/team/merge-coordinator.js +6 -2
  386. package/dist/team/merge-coordinator.js.map +1 -1
  387. package/dist/team/merge-orchestrator.d.ts +41 -0
  388. package/dist/team/merge-orchestrator.d.ts.map +1 -0
  389. package/dist/team/merge-orchestrator.js +736 -0
  390. package/dist/team/merge-orchestrator.js.map +1 -0
  391. package/dist/team/model-contract.d.ts +18 -2
  392. package/dist/team/model-contract.d.ts.map +1 -1
  393. package/dist/team/model-contract.js +55 -28
  394. package/dist/team/model-contract.js.map +1 -1
  395. package/dist/team/monitor.d.ts.map +1 -1
  396. package/dist/team/monitor.js +47 -3
  397. package/dist/team/monitor.js.map +1 -1
  398. package/dist/team/runtime-cli.d.ts +1 -0
  399. package/dist/team/runtime-cli.d.ts.map +1 -1
  400. package/dist/team/runtime-cli.js +15 -2
  401. package/dist/team/runtime-cli.js.map +1 -1
  402. package/dist/team/runtime-flags.d.ts +5 -0
  403. package/dist/team/runtime-flags.d.ts.map +1 -0
  404. package/dist/team/runtime-flags.js +12 -0
  405. package/dist/team/runtime-flags.js.map +1 -0
  406. package/dist/team/runtime-v2.d.ts +16 -1
  407. package/dist/team/runtime-v2.d.ts.map +1 -1
  408. package/dist/team/runtime-v2.js +531 -127
  409. package/dist/team/runtime-v2.js.map +1 -1
  410. package/dist/team/runtime.d.ts.map +1 -1
  411. package/dist/team/runtime.js +4 -1
  412. package/dist/team/runtime.js.map +1 -1
  413. package/dist/team/scaling.d.ts.map +1 -1
  414. package/dist/team/scaling.js +110 -13
  415. package/dist/team/scaling.js.map +1 -1
  416. package/dist/team/state/tasks.d.ts.map +1 -1
  417. package/dist/team/state/tasks.js +1 -0
  418. package/dist/team/state/tasks.js.map +1 -1
  419. package/dist/team/team-ops.d.ts.map +1 -1
  420. package/dist/team/team-ops.js +112 -16
  421. package/dist/team/team-ops.js.map +1 -1
  422. package/dist/team/tmux-session.d.ts +17 -29
  423. package/dist/team/tmux-session.d.ts.map +1 -1
  424. package/dist/team/tmux-session.js +117 -108
  425. package/dist/team/tmux-session.js.map +1 -1
  426. package/dist/team/types.d.ts +31 -17
  427. package/dist/team/types.d.ts.map +1 -1
  428. package/dist/team/types.js.map +1 -1
  429. package/dist/team/worker-bootstrap.d.ts +11 -0
  430. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  431. package/dist/team/worker-bootstrap.js +37 -14
  432. package/dist/team/worker-bootstrap.js.map +1 -1
  433. package/dist/team/worker-canonicalization.d.ts.map +1 -1
  434. package/dist/team/worker-canonicalization.js +2 -0
  435. package/dist/team/worker-canonicalization.js.map +1 -1
  436. package/dist/team/worker-commit-cadence.d.ts +70 -0
  437. package/dist/team/worker-commit-cadence.d.ts.map +1 -0
  438. package/dist/team/worker-commit-cadence.js +271 -0
  439. package/dist/team/worker-commit-cadence.js.map +1 -0
  440. package/dist/tools/__tests__/memory-tools.test.js +4 -4
  441. package/dist/tools/__tests__/memory-tools.test.js.map +1 -1
  442. package/dist/tools/__tests__/state-tools.test.js +13 -1
  443. package/dist/tools/__tests__/state-tools.test.js.map +1 -1
  444. package/dist/utils/frontmatter.d.ts.map +1 -1
  445. package/dist/utils/frontmatter.js +1 -16
  446. package/dist/utils/frontmatter.js.map +1 -1
  447. package/dist/utils/resolve-node.d.ts.map +1 -1
  448. package/dist/utils/resolve-node.js +22 -28
  449. package/dist/utils/resolve-node.js.map +1 -1
  450. package/docs/GETTING-STARTED.md +415 -0
  451. package/docs/MIGRATION.md +1003 -0
  452. package/docs/REFERENCE.md +21 -4
  453. package/docs/TEAM-WORKTREE-MODE.md +86 -0
  454. package/docs/company-context-interface.md +5 -16
  455. package/docs/copilot-instructions.md +1 -1
  456. package/package.json +1 -1
  457. package/scripts/keyword-detector.mjs +168 -20
  458. package/scripts/lib/hud-cache-wrapper.sh +122 -0
  459. package/scripts/persistent-mode.cjs +28 -2
  460. package/scripts/persistent-mode.mjs +28 -2
  461. package/scripts/plugin-setup.mjs +10 -1
  462. package/scripts/post-tool-verifier.mjs +153 -3
  463. package/scripts/pre-tool-enforcer.mjs +54 -6
  464. package/scripts/session-start.mjs +194 -2
  465. package/skills/AGENTS.md +22 -91
  466. package/skills/ai-slop-cleaner/SKILL.md +118 -120
  467. package/skills/ask/SKILL.md +6 -8
  468. package/skills/autopilot/SKILL.md +1 -28
  469. package/skills/autoresearch/SKILL.md +5 -5
  470. package/skills/cancel/SKILL.md +13 -13
  471. package/skills/cccg/SKILL.md +1 -0
  472. package/skills/configure-notifications/SKILL.md +80 -215
  473. package/skills/debug/SKILL.md +3 -2
  474. package/skills/deep-dive/SKILL.md +20 -9
  475. package/skills/deep-interview/SKILL.md +32 -4
  476. package/skills/deepinit/SKILL.md +1 -0
  477. package/skills/external-context/SKILL.md +2 -1
  478. package/skills/hud/SKILL.md +22 -52
  479. package/skills/learner/SKILL.md +90 -57
  480. package/skills/mcp-setup/SKILL.md +45 -60
  481. package/skills/omc-doctor/SKILL.md +1 -1
  482. package/skills/omc-reference/SKILL.md +6 -6
  483. package/skills/omc-setup/SKILL.md +30 -74
  484. package/skills/omc-setup/phases/01-install-claude-md.md +97 -0
  485. package/skills/omc-setup/phases/02-configure.md +25 -40
  486. package/skills/omc-setup/phases/03-integrations.md +100 -69
  487. package/skills/omc-setup/phases/04-welcome.md +102 -44
  488. package/skills/omc-teams/SKILL.md +63 -26
  489. package/skills/plan/SKILL.md +11 -9
  490. package/skills/ralph/SKILL.md +42 -41
  491. package/skills/ralplan/SKILL.md +3 -10
  492. package/skills/release/SKILL.md +168 -57
  493. package/skills/remember/SKILL.md +2 -1
  494. package/skills/sciomc/SKILL.md +12 -11
  495. package/skills/self-improve/SKILL.md +4 -4
  496. package/skills/setup/SKILL.md +1 -0
  497. package/skills/skill/SKILL.md +21 -20
  498. package/skills/skillify/SKILL.md +34 -3
  499. package/skills/team/SKILL.md +62 -44
  500. package/skills/ultraqa/SKILL.md +1 -2
  501. package/skills/ultrawork/SKILL.md +28 -21
  502. package/skills/verify/SKILL.md +2 -1
  503. package/skills/writer-memory/SKILL.md +2 -1
  504. package/templates/hooks/keyword-detector.mjs +129 -11
  505. package/templates/hooks/persistent-mode.mjs +28 -2
  506. package/templates/hooks/pre-tool-use.mjs +144 -45
  507. package/templates/hooks/session-start.mjs +25 -1
@@ -1 +1 @@
1
- {"version":3,"file":"git-worktree.d.ts","sourceRoot":"","sources":["../../src/team/git-worktree.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AA4DD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,YAAY,CA0Dd;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,CA0BN;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,YAAY,EAAE,CAEhB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,IAAI,CAON"}
1
+ {"version":3,"file":"git-worktree.d.ts","sourceRoot":"","sources":["../../src/team/git-worktree.ts"],"names":[],"mappings":"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"}
@@ -2,111 +2,375 @@
2
2
  /**
3
3
  * Git worktree manager for team worker isolation.
4
4
  *
5
- * Each MCP worker gets its own git worktree at:
6
- * {repoRoot}/.omcp/worktrees/{team}/{worker}
7
- * Branch naming: omc-team/{teamName}/{workerName}
5
+ * Native team worktrees live at:
6
+ * {repoRoot}/.omcp/team/{team}/worktrees/{worker}
7
+ * Branch naming (branch mode): omc-team/{teamName}/{workerName}
8
+ *
9
+ * The public create/remove helpers are kept for legacy callers, but the
10
+ * implementation is conservative: compatible clean worktrees are reused,
11
+ * dirty team worktrees are preserved, and cleanup never force-removes dirty
12
+ * worker changes.
8
13
  */
9
- import { existsSync, readFileSync, rmSync } from 'node:fs';
10
- import { join } from 'node:path';
14
+ import { existsSync, readFileSync, readdirSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
15
+ import { join, resolve } from 'node:path';
11
16
  import { execFileSync } from 'node:child_process';
12
17
  import { atomicWriteJson, ensureDirWithMode, validateResolvedPath } from './fs-utils.js';
13
18
  import { sanitizeName } from './tmux-session.js';
14
19
  import { withFileLockSync } from '../lib/file-lock.js';
15
- /** Get worktree path for a worker */
16
- function getWorktreePath(repoRoot, teamName, workerName) {
17
- return join(repoRoot, '.omcp', 'worktrees', sanitizeName(teamName), sanitizeName(workerName));
20
+ /** Get canonical native team worktree path for a worker. */
21
+ export function getWorktreePath(repoRoot, teamName, workerName) {
22
+ return join(repoRoot, '.omcp', 'team', sanitizeName(teamName), 'worktrees', sanitizeName(workerName));
18
23
  }
19
- /** Get branch name for a worker */
20
- function getBranchName(teamName, workerName) {
24
+ /** Get branch name for a worker. */
25
+ export function getBranchName(teamName, workerName) {
21
26
  return `omc-team/${sanitizeName(teamName)}/${sanitizeName(workerName)}`;
22
27
  }
23
- function isRegisteredWorktreePath(repoRoot, wtPath) {
28
+ function git(repoRoot, args, cwd = repoRoot) {
29
+ return execFileSync('git', args, { cwd, encoding: 'utf-8', stdio: 'pipe' }).trim();
30
+ }
31
+ function isInsideGitRepo(repoRoot) {
24
32
  try {
25
- const output = execFileSync('git', ['worktree', 'list', '--porcelain'], {
26
- cwd: repoRoot,
27
- encoding: 'utf-8',
28
- stdio: 'pipe',
29
- });
30
- const resolvedWtPath = wtPath.trim();
33
+ git(repoRoot, ['rev-parse', '--show-toplevel']);
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ function assertCleanLeaderWorktree(repoRoot) {
41
+ const status = git(repoRoot, ['status', '--porcelain'])
42
+ .split('\n')
43
+ .filter(line => line.trim() !== '' && !/^\?\? \.omcp?(?:\/|$)/.test(line))
44
+ .join('\n')
45
+ .trim();
46
+ if (status.length > 0) {
47
+ const error = new Error('leader_worktree_dirty: commit, stash, or clean changes before enabling team worktree mode');
48
+ error.code = 'leader_worktree_dirty';
49
+ throw error;
50
+ }
51
+ }
52
+ function getRegisteredWorktreeBranch(repoRoot, wtPath) {
53
+ try {
54
+ const output = git(repoRoot, ['worktree', 'list', '--porcelain']);
55
+ const resolvedWtPath = resolve(wtPath);
56
+ let currentMatches = false;
31
57
  for (const line of output.split('\n')) {
32
- if (!line.startsWith('worktree '))
58
+ if (line.startsWith('worktree ')) {
59
+ currentMatches = resolve(line.slice('worktree '.length).trim()) === resolvedWtPath;
33
60
  continue;
34
- if (line.slice('worktree '.length).trim() === resolvedWtPath) {
35
- return true;
36
61
  }
62
+ if (!currentMatches)
63
+ continue;
64
+ if (line.startsWith('branch '))
65
+ return line.slice('branch '.length).trim().replace(/^refs\/heads\//, '');
66
+ if (line === 'detached')
67
+ return 'HEAD';
37
68
  }
38
69
  }
39
70
  catch {
40
71
  // Best-effort check only.
41
72
  }
42
- return false;
73
+ return undefined;
74
+ }
75
+ function isRegisteredWorktreePath(repoRoot, wtPath) {
76
+ try {
77
+ const output = git(repoRoot, ['worktree', 'list', '--porcelain']);
78
+ const resolvedWtPath = resolve(wtPath);
79
+ return output.split('\n').some(line => (line.startsWith('worktree ') && resolve(line.slice('worktree '.length).trim()) === resolvedWtPath));
80
+ }
81
+ catch {
82
+ return false;
83
+ }
43
84
  }
44
- /** Get worktree metadata path */
85
+ function isDetached(wtPath) {
86
+ try {
87
+ const branch = execFileSync('git', ['branch', '--show-current'], { cwd: wtPath, encoding: 'utf-8', stdio: 'pipe' }).trim();
88
+ return branch.length === 0;
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
94
+ function isWorktreeDirty(wtPath) {
95
+ return isWorktreeDirtyExcept(wtPath).dirty;
96
+ }
97
+ function normalizeStatusPath(rawPath) {
98
+ const trimmed = rawPath.trim();
99
+ if (trimmed.startsWith('\"') && trimmed.endsWith('\"')) {
100
+ try {
101
+ return JSON.parse(trimmed);
102
+ }
103
+ catch {
104
+ return trimmed.slice(1, -1);
105
+ }
106
+ }
107
+ return trimmed;
108
+ }
109
+ function statusEntryPath(line) {
110
+ const payload = line.slice(3);
111
+ const renameSeparator = ' -> ';
112
+ const renameIndex = payload.indexOf(renameSeparator);
113
+ return normalizeStatusPath(renameIndex >= 0 ? payload.slice(renameIndex + renameSeparator.length) : payload);
114
+ }
115
+ function isWorktreeDirtyExcept(wtPath, ignoredRootPaths = []) {
116
+ try {
117
+ const ignored = new Set(ignoredRootPaths);
118
+ const entries = execFileSync('git', ['status', '--porcelain'], { cwd: wtPath, encoding: 'utf-8', stdio: 'pipe' })
119
+ .split('\n')
120
+ .filter(line => line.trim().length > 0);
121
+ const relevantEntries = entries.filter(line => !ignored.has(statusEntryPath(line)));
122
+ return { dirty: relevantEntries.length > 0, entries: relevantEntries };
123
+ }
124
+ catch {
125
+ return { dirty: true, entries: ['git_status_failed'] };
126
+ }
127
+ }
128
+ /** Get worktree metadata path. */
45
129
  function getMetadataPath(repoRoot, teamName) {
130
+ return join(repoRoot, '.omcp', 'state', 'team', sanitizeName(teamName), 'worktrees.json');
131
+ }
132
+ function getLegacyMetadataPath(repoRoot, teamName) {
46
133
  return join(repoRoot, '.omcp', 'state', 'team-bridge', sanitizeName(teamName), 'worktrees.json');
47
134
  }
48
- /** Read worktree metadata */
49
- function readMetadata(repoRoot, teamName) {
50
- const metaPath = getMetadataPath(repoRoot, teamName);
51
- if (!existsSync(metaPath))
52
- return [];
135
+ function getWorkerStateDir(repoRoot, teamName, workerName) {
136
+ return join(repoRoot, '.omcp', 'state', 'team', sanitizeName(teamName), 'workers', sanitizeName(workerName));
137
+ }
138
+ function getRootAgentsBackupPath(repoRoot, teamName, workerName) {
139
+ return join(getWorkerStateDir(repoRoot, teamName, workerName), 'worktree-root-agents.json');
140
+ }
141
+ function readRootAgentsBackup(repoRoot, teamName, workerName) {
142
+ const backupPath = getRootAgentsBackupPath(repoRoot, teamName, workerName);
143
+ if (!existsSync(backupPath))
144
+ return null;
53
145
  try {
54
- return JSON.parse(readFileSync(metaPath, 'utf-8'));
146
+ return JSON.parse(readFileSync(backupPath, 'utf-8'));
55
147
  }
56
148
  catch (err) {
57
- // Log corruption instead of silently returning empty (which would lose all entries)
58
149
  const msg = err instanceof Error ? err.message : String(err);
59
- process.stderr.write(`[omc] warning: worktrees.json parse error: ${msg}\n`);
150
+ process.stderr.write(`[omc] warning: worktree root AGENTS backup parse error: ${msg}\n`);
151
+ const error = new Error(`worktree_root_agents_backup_unreadable:${backupPath}:${msg}`);
152
+ error.code = 'worktree_root_agents_backup_unreadable';
153
+ throw error;
154
+ }
155
+ }
156
+ /**
157
+ * Install the generated worker overlay into the root of a native worker worktree.
158
+ * Existing root AGENTS.md content is backed up under leader-owned state so cleanup
159
+ * can safely restore it. Reinstalling preserves the first original backup instead
160
+ * of treating an older managed overlay as user content.
161
+ */
162
+ export function installWorktreeRootAgents(teamName, workerName, repoRoot, worktreePath, overlayContent) {
163
+ validateResolvedPath(worktreePath, repoRoot);
164
+ const agentsPath = join(worktreePath, 'AGENTS.md');
165
+ validateResolvedPath(agentsPath, repoRoot);
166
+ const backupPath = getRootAgentsBackupPath(repoRoot, teamName, workerName);
167
+ validateResolvedPath(backupPath, repoRoot);
168
+ ensureDirWithMode(getWorkerStateDir(repoRoot, teamName, workerName));
169
+ const previous = readRootAgentsBackup(repoRoot, teamName, workerName);
170
+ const currentContent = existsSync(agentsPath) ? readFileSync(agentsPath, 'utf-8') : undefined;
171
+ if (previous && currentContent !== undefined && currentContent !== previous.installedContent) {
172
+ const error = new Error(`agents_dirty: preserving modified worktree root AGENTS.md at ${agentsPath}`);
173
+ error.code = 'agents_dirty';
174
+ throw error;
175
+ }
176
+ const backup = previous
177
+ ? { ...previous, worktreePath, installedContent: overlayContent, installedAt: new Date().toISOString() }
178
+ : {
179
+ worktreePath,
180
+ hadOriginal: currentContent !== undefined,
181
+ ...(currentContent !== undefined ? { originalContent: currentContent } : {}),
182
+ installedContent: overlayContent,
183
+ installedAt: new Date().toISOString(),
184
+ };
185
+ atomicWriteJson(backupPath, backup);
186
+ writeFileSync(agentsPath, overlayContent, 'utf-8');
187
+ }
188
+ /**
189
+ * Restore or remove a managed worktree-root AGENTS.md when it is still unchanged.
190
+ * If a worker edited AGENTS.md, leave it and report agents_dirty so cleanup can
191
+ * preserve the worktree instead of overwriting user changes.
192
+ */
193
+ export function restoreWorktreeRootAgents(teamName, workerName, repoRoot, worktreePath) {
194
+ const backupPath = getRootAgentsBackupPath(repoRoot, teamName, workerName);
195
+ validateResolvedPath(backupPath, repoRoot);
196
+ const backup = readRootAgentsBackup(repoRoot, teamName, workerName);
197
+ if (!backup)
198
+ return { restored: false, reason: 'no_backup' };
199
+ const resolvedWorktreePath = worktreePath ?? backup.worktreePath;
200
+ validateResolvedPath(resolvedWorktreePath, repoRoot);
201
+ if (!existsSync(resolvedWorktreePath)) {
202
+ try {
203
+ unlinkSync(backupPath);
204
+ }
205
+ catch { /* backup already gone */ }
206
+ return { restored: false, reason: 'worktree_missing' };
207
+ }
208
+ const agentsPath = join(resolvedWorktreePath, 'AGENTS.md');
209
+ validateResolvedPath(agentsPath, repoRoot);
210
+ const currentContent = existsSync(agentsPath) ? readFileSync(agentsPath, 'utf-8') : undefined;
211
+ const isPartialInstallOriginal = backup.hadOriginal && currentContent === (backup.originalContent ?? '');
212
+ if (currentContent !== undefined && currentContent !== backup.installedContent && !isPartialInstallOriginal) {
213
+ return { restored: false, reason: 'agents_dirty' };
214
+ }
215
+ if (backup.hadOriginal) {
216
+ writeFileSync(agentsPath, backup.originalContent ?? '', 'utf-8');
217
+ }
218
+ else if (existsSync(agentsPath)) {
219
+ unlinkSync(agentsPath);
220
+ }
221
+ try {
222
+ unlinkSync(backupPath);
223
+ }
224
+ catch { /* backup already gone */ }
225
+ return { restored: true };
226
+ }
227
+ /** Read worktree metadata, including legacy metadata for cleanup compatibility. */
228
+ function readMetadataResult(repoRoot, teamName) {
229
+ const paths = [getMetadataPath(repoRoot, teamName), getLegacyMetadataPath(repoRoot, teamName)];
230
+ const byWorker = new Map();
231
+ const issues = [];
232
+ for (const metaPath of paths) {
233
+ if (!existsSync(metaPath))
234
+ continue;
235
+ try {
236
+ const entries = JSON.parse(readFileSync(metaPath, 'utf-8'));
237
+ for (const entry of entries)
238
+ byWorker.set(entry.workerName, entry);
239
+ }
240
+ catch (err) {
241
+ const message = err instanceof Error ? err.message : String(err);
242
+ issues.push({ path: metaPath, message });
243
+ process.stderr.write(`[omc] warning: worktrees.json parse error at ${metaPath}: ${message}
244
+ `);
245
+ }
246
+ }
247
+ return { entries: [...byWorker.values()], issues };
248
+ }
249
+ function readMetadata(repoRoot, teamName) {
250
+ return readMetadataResult(repoRoot, teamName).entries;
251
+ }
252
+ function listRootAgentsBackupIssues(repoRoot, teamName, entries) {
253
+ const workersDir = join(repoRoot, '.omcp', 'state', 'team', sanitizeName(teamName), 'workers');
254
+ if (!existsSync(workersDir))
60
255
  return [];
256
+ const knownWorkers = new Set(entries.map((entry) => sanitizeName(entry.workerName)));
257
+ const issues = [];
258
+ for (const workerName of readdirSync(workersDir)) {
259
+ const backupPath = join(workersDir, workerName, 'worktree-root-agents.json');
260
+ if (!existsSync(backupPath))
261
+ continue;
262
+ try {
263
+ JSON.parse(readFileSync(backupPath, 'utf-8'));
264
+ }
265
+ catch (error) {
266
+ const message = error instanceof Error ? error.message : String(error);
267
+ issues.push({ path: backupPath, message: `worktree_root_agents_backup_unreadable:${workerName}:${message}` });
268
+ continue;
269
+ }
270
+ if (!knownWorkers.has(sanitizeName(workerName))) {
271
+ issues.push({
272
+ path: backupPath,
273
+ message: `orphaned_worktree_root_agents_backup:${workerName}`,
274
+ });
275
+ }
61
276
  }
277
+ return issues;
62
278
  }
63
- /** Write worktree metadata */
279
+ /** Write native worktree metadata. */
64
280
  function writeMetadata(repoRoot, teamName, entries) {
65
281
  const metaPath = getMetadataPath(repoRoot, teamName);
66
282
  validateResolvedPath(metaPath, repoRoot);
67
- const dir = join(repoRoot, '.omcp', 'state', 'team-bridge', sanitizeName(teamName));
68
- ensureDirWithMode(dir);
283
+ ensureDirWithMode(join(repoRoot, '.omcp', 'state', 'team', sanitizeName(teamName)));
69
284
  atomicWriteJson(metaPath, entries);
70
285
  }
286
+ function recordMetadata(repoRoot, teamName, info) {
287
+ const metaLockPath = getMetadataPath(repoRoot, teamName) + '.lock';
288
+ withFileLockSync(metaLockPath, () => {
289
+ const existing = readMetadata(repoRoot, teamName).filter(entry => entry.workerName !== info.workerName);
290
+ writeMetadata(repoRoot, teamName, [...existing, info]);
291
+ });
292
+ }
293
+ function forgetMetadataUnlocked(repoRoot, teamName, workerName) {
294
+ const existing = readMetadata(repoRoot, teamName).filter(entry => entry.workerName !== workerName);
295
+ writeMetadata(repoRoot, teamName, existing);
296
+ }
297
+ function assertCompatibleExistingWorktree(repoRoot, wtPath, expectedBranch, mode) {
298
+ const registeredBranch = getRegisteredWorktreeBranch(repoRoot, wtPath);
299
+ if (!registeredBranch) {
300
+ const error = new Error(`worktree_path_mismatch: existing path is not a registered git worktree: ${wtPath}`);
301
+ error.code = 'worktree_path_mismatch';
302
+ throw error;
303
+ }
304
+ if (isWorktreeDirty(wtPath)) {
305
+ const error = new Error(`worktree_dirty: preserving dirty worker worktree at ${wtPath}`);
306
+ error.code = 'worktree_dirty';
307
+ throw error;
308
+ }
309
+ if (mode === 'named' && registeredBranch !== expectedBranch) {
310
+ const error = new Error(`worktree_mismatch: expected branch ${expectedBranch} at ${wtPath}, found ${registeredBranch}`);
311
+ error.code = 'worktree_mismatch';
312
+ throw error;
313
+ }
314
+ if (mode === 'detached' && registeredBranch !== 'HEAD') {
315
+ const error = new Error(`worktree_mismatch: expected detached worktree at ${wtPath}, found ${registeredBranch}`);
316
+ error.code = 'worktree_mismatch';
317
+ throw error;
318
+ }
319
+ }
320
+ export function normalizeTeamWorktreeMode(value) {
321
+ if (typeof value !== 'string')
322
+ return 'disabled';
323
+ const normalized = value.trim().toLowerCase();
324
+ if (['1', 'true', 'yes', 'on', 'enabled', 'detached'].includes(normalized))
325
+ return 'detached';
326
+ if (['branch', 'named', 'named-branch'].includes(normalized))
327
+ return 'named';
328
+ return 'disabled';
329
+ }
71
330
  /**
72
- * Create a git worktree for a team worker.
73
- * Path: {repoRoot}/.omcp/worktrees/{team}/{worker}
74
- * Branch: omc-team/{teamName}/{workerName}
331
+ * Ensure a worker worktree exists according to the selected opt-in mode.
332
+ * Disabled mode is a no-op. Existing clean compatible worktrees are reused;
333
+ * dirty or mismatched existing worktrees throw without deleting files.
75
334
  */
76
- export function createWorkerWorktree(teamName, workerName, repoRoot, baseBranch) {
335
+ export function ensureWorkerWorktree(teamName, workerName, repoRoot, options = {}) {
336
+ const mode = options.mode ?? 'disabled';
337
+ if (mode === 'disabled')
338
+ return null;
339
+ if (!isInsideGitRepo(repoRoot)) {
340
+ throw new Error(`not_a_git_repository: ${repoRoot}`);
341
+ }
342
+ if (options.requireCleanLeader !== false) {
343
+ assertCleanLeaderWorktree(repoRoot);
344
+ }
77
345
  const wtPath = getWorktreePath(repoRoot, teamName, workerName);
78
- const branch = getBranchName(teamName, workerName);
346
+ const branch = mode === 'named' ? getBranchName(teamName, workerName) : 'HEAD';
79
347
  validateResolvedPath(wtPath, repoRoot);
80
- // Prune stale worktrees first
81
348
  try {
82
349
  execFileSync('git', ['worktree', 'prune'], { cwd: repoRoot, stdio: 'pipe' });
83
350
  }
84
351
  catch { /* ignore */ }
85
- // Remove stale worktree if it exists
86
352
  if (existsSync(wtPath)) {
87
- try {
88
- execFileSync('git', ['worktree', 'remove', '--force', wtPath], { cwd: repoRoot, stdio: 'pipe' });
89
- }
90
- catch {
91
- if (isRegisteredWorktreePath(repoRoot, wtPath)) {
92
- throw new Error(`Stale worktree still registered at ${wtPath}. ` +
93
- `Run \`git worktree prune\` or remove it manually before retrying.`);
94
- }
95
- rmSync(wtPath, { recursive: true, force: true });
96
- }
97
- }
98
- // Delete stale branch if it exists
99
- try {
100
- execFileSync('git', ['branch', '-D', branch], { cwd: repoRoot, stdio: 'pipe' });
353
+ assertCompatibleExistingWorktree(repoRoot, wtPath, branch, mode);
354
+ const info = {
355
+ path: wtPath,
356
+ branch,
357
+ workerName,
358
+ teamName,
359
+ createdAt: new Date().toISOString(),
360
+ repoRoot,
361
+ mode,
362
+ detached: isDetached(wtPath),
363
+ created: false,
364
+ reused: true,
365
+ };
366
+ recordMetadata(repoRoot, teamName, info);
367
+ return info;
101
368
  }
102
- catch { /* branch doesn't exist, fine */ }
103
- // Create worktree directory
104
- const wtDir = join(repoRoot, '.omcp', 'worktrees', sanitizeName(teamName));
369
+ const wtDir = join(repoRoot, '.omcp', 'team', sanitizeName(teamName), 'worktrees');
105
370
  ensureDirWithMode(wtDir);
106
- // Create worktree with new branch
107
- const args = ['worktree', 'add', '-b', branch, wtPath];
108
- if (baseBranch)
109
- args.push(baseBranch);
371
+ const args = mode === 'named'
372
+ ? ['worktree', 'add', '-b', branch, wtPath, options.baseRef ?? 'HEAD']
373
+ : ['worktree', 'add', '--detach', wtPath, options.baseRef ?? 'HEAD'];
110
374
  execFileSync('git', args, { cwd: repoRoot, stdio: 'pipe' });
111
375
  const info = {
112
376
  path: wtPath,
@@ -114,62 +378,154 @@ export function createWorkerWorktree(teamName, workerName, repoRoot, baseBranch)
114
378
  workerName,
115
379
  teamName,
116
380
  createdAt: new Date().toISOString(),
381
+ repoRoot,
382
+ mode,
383
+ detached: mode === 'detached',
384
+ created: true,
385
+ reused: false,
117
386
  };
118
- // Update metadata (locked to prevent concurrent read-modify-write races)
119
- const metaLockPath = getMetadataPath(repoRoot, teamName) + '.lock';
120
- withFileLockSync(metaLockPath, () => {
121
- const existing = readMetadata(repoRoot, teamName);
122
- const updated = existing.filter(e => e.workerName !== workerName);
123
- updated.push(info);
124
- writeMetadata(repoRoot, teamName, updated);
387
+ recordMetadata(repoRoot, teamName, info);
388
+ return info;
389
+ }
390
+ /** Legacy creation helper: create or reuse a named-branch worker worktree. */
391
+ export function createWorkerWorktree(teamName, workerName, repoRoot, baseBranch) {
392
+ const info = ensureWorkerWorktree(teamName, workerName, repoRoot, {
393
+ mode: 'named',
394
+ baseRef: baseBranch,
395
+ requireCleanLeader: false,
125
396
  });
397
+ if (!info)
398
+ throw new Error('worktree creation unexpectedly disabled');
126
399
  return info;
127
400
  }
128
401
  /**
129
- * Remove a worker's worktree and branch.
402
+ * Dry-run validation for worker worktree removal. This does not restore/remove
403
+ * managed root AGENTS.md and does not delete backup state.
130
404
  */
131
- export function removeWorkerWorktree(teamName, workerName, repoRoot) {
132
- const wtPath = getWorktreePath(repoRoot, teamName, workerName);
133
- const branch = getBranchName(teamName, workerName);
134
- // Remove worktree
135
- try {
136
- execFileSync('git', ['worktree', 'remove', '--force', wtPath], { cwd: repoRoot, stdio: 'pipe' });
405
+ export function checkWorkerWorktreeRemovalSafety(teamName, workerName, repoRoot, worktreePath) {
406
+ const wtPath = worktreePath ?? getWorktreePath(repoRoot, teamName, workerName);
407
+ const backup = readRootAgentsBackup(repoRoot, teamName, workerName);
408
+ if (!existsSync(wtPath))
409
+ return;
410
+ let ignoreRootAgents = false;
411
+ if (backup) {
412
+ const agentsPath = join(wtPath, 'AGENTS.md');
413
+ validateResolvedPath(agentsPath, repoRoot);
414
+ const currentContent = existsSync(agentsPath) ? readFileSync(agentsPath, 'utf-8') : undefined;
415
+ const isPartialInstallOriginal = backup.hadOriginal && currentContent === (backup.originalContent ?? '');
416
+ if (currentContent !== undefined && currentContent !== backup.installedContent && !isPartialInstallOriginal) {
417
+ const error = new Error(`agents_dirty: preserving modified worktree root AGENTS.md at ${agentsPath}`);
418
+ error.code = 'agents_dirty';
419
+ throw error;
420
+ }
421
+ ignoreRootAgents = true;
137
422
  }
138
- catch { /* may not exist */ }
139
- // Prune to clean up
140
- try {
141
- execFileSync('git', ['worktree', 'prune'], { cwd: repoRoot, stdio: 'pipe' });
423
+ const dirtyCheck = isWorktreeDirtyExcept(wtPath, ignoreRootAgents ? ['AGENTS.md'] : []);
424
+ if (dirtyCheck.dirty) {
425
+ const error = new Error(`worktree_dirty: preserving dirty worker worktree at ${wtPath}`);
426
+ error.code = 'worktree_dirty';
427
+ throw error;
142
428
  }
143
- catch { /* ignore */ }
144
- // Delete branch
145
- try {
146
- execFileSync('git', ['branch', '-D', branch], { cwd: repoRoot, stdio: 'pipe' });
429
+ }
430
+ /**
431
+ * Prepare a worker worktree for later removal without deleting the worktree.
432
+ *
433
+ * This is transactional with respect to managed root AGENTS.md overlays: it first
434
+ * validates the overlay is restorable and that no non-overlay files are dirty.
435
+ * Only after that dry-run succeeds does it restore/remove AGENTS.md and delete
436
+ * the backup. If any other dirty file exists, the worker pane/config can remain
437
+ * intact with the managed overlay and backup still available for a later retry.
438
+ */
439
+ export function prepareWorkerWorktreeForRemoval(teamName, workerName, repoRoot, worktreePath) {
440
+ const wtPath = worktreePath ?? getWorktreePath(repoRoot, teamName, workerName);
441
+ checkWorkerWorktreeRemovalSafety(teamName, workerName, repoRoot, wtPath);
442
+ const agentsRestore = restoreWorktreeRootAgents(teamName, workerName, repoRoot, wtPath);
443
+ if (agentsRestore.reason === 'agents_dirty') {
444
+ const error = new Error(`agents_dirty: preserving modified worktree root AGENTS.md at ${join(wtPath, 'AGENTS.md')}`);
445
+ error.code = 'agents_dirty';
446
+ throw error;
147
447
  }
148
- catch { /* branch may not exist */ }
149
- // Update metadata (locked to prevent concurrent read-modify-write races)
150
- const metaLockPath = getMetadataPath(repoRoot, teamName) + '.lock';
448
+ }
449
+ /** Remove a worker's worktree and branch, preserving dirty worktrees. */
450
+ export function removeWorkerWorktree(teamName, workerName, repoRoot) {
451
+ const wtPath = getWorktreePath(repoRoot, teamName, workerName);
452
+ const branch = getBranchName(teamName, workerName);
453
+ const metaLockPath = `${getMetadataPath(repoRoot, teamName)}.lock`;
151
454
  withFileLockSync(metaLockPath, () => {
152
- const existing = readMetadata(repoRoot, teamName);
153
- const updated = existing.filter(e => e.workerName !== workerName);
154
- writeMetadata(repoRoot, teamName, updated);
455
+ prepareWorkerWorktreeForRemoval(teamName, workerName, repoRoot, wtPath);
456
+ const wasRegisteredWorktree = isRegisteredWorktreePath(repoRoot, wtPath);
457
+ try {
458
+ execFileSync('git', ['worktree', 'remove', wtPath], { cwd: repoRoot, stdio: 'pipe' });
459
+ }
460
+ catch (err) {
461
+ if (wasRegisteredWorktree) {
462
+ const detail = err instanceof Error && err.message ? `: ${err.message}` : '';
463
+ const error = new Error(`worktree_remove_failed: preserving metadata for registered worker worktree at ${wtPath}${detail}`);
464
+ error.code = 'worktree_remove_failed';
465
+ throw error;
466
+ }
467
+ // Unregistered/absent stale paths are best-effort cleanup only.
468
+ }
469
+ try {
470
+ execFileSync('git', ['worktree', 'prune'], { cwd: repoRoot, stdio: 'pipe' });
471
+ }
472
+ catch { /* ignore */ }
473
+ try {
474
+ execFileSync('git', ['branch', '-D', branch], { cwd: repoRoot, stdio: 'pipe' });
475
+ }
476
+ catch { /* branch may not exist */ }
477
+ // If a stale plain directory remains and it is not a registered worktree, remove it.
478
+ if (existsSync(wtPath) && !isRegisteredWorktreePath(repoRoot, wtPath)) {
479
+ rmSync(wtPath, { recursive: true, force: true });
480
+ }
481
+ forgetMetadataUnlocked(repoRoot, teamName, workerName);
155
482
  });
156
483
  }
157
- /**
158
- * List all worktrees for a team.
159
- */
484
+ /** List all worktrees for a team. */
160
485
  export function listTeamWorktrees(teamName, repoRoot) {
161
486
  return readMetadata(repoRoot, teamName);
162
487
  }
163
- /**
164
- * Remove all worktrees for a team (cleanup on shutdown).
165
- */
488
+ export function inspectTeamWorktreeCleanupSafety(teamName, repoRoot) {
489
+ const metadata = readMetadataResult(repoRoot, teamName);
490
+ const entries = metadata.entries;
491
+ const backupIssues = listRootAgentsBackupIssues(repoRoot, teamName, entries);
492
+ return {
493
+ hasEvidence: entries.length > 0 || metadata.issues.length > 0 || backupIssues.length > 0,
494
+ entries,
495
+ blockers: [
496
+ ...metadata.issues.map((issue, index) => ({
497
+ workerName: `metadata-${index + 1}`,
498
+ path: issue.path,
499
+ reason: `worktree_metadata_unreadable:${issue.message}`,
500
+ })),
501
+ ...backupIssues.map((issue, index) => ({
502
+ workerName: `agents-backup-${index + 1}`,
503
+ path: issue.path,
504
+ reason: issue.message,
505
+ })),
506
+ ],
507
+ };
508
+ }
509
+ /** Remove all clean worktrees for a team, preserving dirty worktrees. */
166
510
  export function cleanupTeamWorktrees(teamName, repoRoot) {
167
- const entries = readMetadata(repoRoot, teamName);
511
+ const safety = inspectTeamWorktreeCleanupSafety(teamName, repoRoot);
512
+ const entries = safety.entries;
513
+ const removed = [];
514
+ const preserved = [...safety.blockers];
515
+ if (preserved.length > 0) {
516
+ return { removed, preserved };
517
+ }
168
518
  for (const entry of entries) {
169
519
  try {
170
520
  removeWorkerWorktree(teamName, entry.workerName, repoRoot);
521
+ removed.push(entry.workerName);
522
+ }
523
+ catch (err) {
524
+ const reason = err instanceof Error ? err.message : String(err);
525
+ preserved.push({ workerName: entry.workerName, path: entry.path, reason });
526
+ process.stderr.write(`[omc] warning: preserved worktree ${entry.path}: ${reason}\n`);
171
527
  }
172
- catch { /* best effort */ }
173
528
  }
529
+ return { removed, preserved };
174
530
  }
175
531
  //# sourceMappingURL=git-worktree.js.map