oh-my-codex 0.15.0 → 0.15.2

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 (533) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +36 -5
  4. package/crates/omx-explore/src/main.rs +222 -12
  5. package/dist/agents/__tests__/native-config.test.js +40 -0
  6. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  7. package/dist/agents/native-config.d.ts +1 -0
  8. package/dist/agents/native-config.d.ts.map +1 -1
  9. package/dist/agents/native-config.js +6 -1
  10. package/dist/agents/native-config.js.map +1 -1
  11. package/dist/agents/policy.d.ts +1 -0
  12. package/dist/agents/policy.d.ts.map +1 -1
  13. package/dist/agents/policy.js +4 -0
  14. package/dist/agents/policy.js.map +1 -1
  15. package/dist/cli/__tests__/autoresearch-guided.test.js +37 -13
  16. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  17. package/dist/cli/__tests__/codex-plugin-layout.test.js +1 -1
  18. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  19. package/dist/cli/__tests__/doctor-team.test.js +46 -1
  20. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  21. package/dist/cli/__tests__/doctor-warning-copy.test.js +225 -111
  22. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  23. package/dist/cli/__tests__/exec.test.js +96 -1
  24. package/dist/cli/__tests__/exec.test.js.map +1 -1
  25. package/dist/cli/__tests__/explore.test.js +15 -2
  26. package/dist/cli/__tests__/explore.test.js.map +1 -1
  27. package/dist/cli/__tests__/index.test.js +292 -3
  28. package/dist/cli/__tests__/index.test.js.map +1 -1
  29. package/dist/cli/__tests__/launch-fallback.test.js +223 -0
  30. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  31. package/dist/cli/__tests__/mcp-parity.test.js +86 -0
  32. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  33. package/dist/cli/__tests__/package-bin-contract.test.js +23 -0
  34. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  35. package/dist/cli/__tests__/question.test.js +76 -11
  36. package/dist/cli/__tests__/question.test.js.map +1 -1
  37. package/dist/cli/__tests__/setup-agents-overwrite.test.js +140 -1
  38. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  39. package/dist/cli/__tests__/setup-install-mode.test.js +310 -4
  40. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  41. package/dist/cli/__tests__/setup-prompts-overwrite.test.js +78 -19
  42. package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
  43. package/dist/cli/__tests__/setup-refresh.test.js +79 -2
  44. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  45. package/dist/cli/__tests__/sidecar.test.d.ts +2 -0
  46. package/dist/cli/__tests__/sidecar.test.d.ts.map +1 -0
  47. package/dist/cli/__tests__/sidecar.test.js +24 -0
  48. package/dist/cli/__tests__/sidecar.test.js.map +1 -0
  49. package/dist/cli/__tests__/team.test.js +54 -7
  50. package/dist/cli/__tests__/team.test.js.map +1 -1
  51. package/dist/cli/autoresearch-guided.d.ts.map +1 -1
  52. package/dist/cli/autoresearch-guided.js +12 -4
  53. package/dist/cli/autoresearch-guided.js.map +1 -1
  54. package/dist/cli/codex-home.d.ts +4 -6
  55. package/dist/cli/codex-home.d.ts.map +1 -1
  56. package/dist/cli/codex-home.js +9 -41
  57. package/dist/cli/codex-home.js.map +1 -1
  58. package/dist/cli/doctor.d.ts +1 -1
  59. package/dist/cli/doctor.d.ts.map +1 -1
  60. package/dist/cli/doctor.js +509 -279
  61. package/dist/cli/doctor.js.map +1 -1
  62. package/dist/cli/index.d.ts +6 -4
  63. package/dist/cli/index.d.ts.map +1 -1
  64. package/dist/cli/index.js +284 -25
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/cli/omx.js +3 -1
  67. package/dist/cli/omx.js.map +1 -1
  68. package/dist/cli/plugin-marketplace.d.ts +13 -0
  69. package/dist/cli/plugin-marketplace.d.ts.map +1 -0
  70. package/dist/cli/plugin-marketplace.js +77 -0
  71. package/dist/cli/plugin-marketplace.js.map +1 -0
  72. package/dist/cli/question.d.ts +1 -1
  73. package/dist/cli/question.d.ts.map +1 -1
  74. package/dist/cli/question.js +26 -12
  75. package/dist/cli/question.js.map +1 -1
  76. package/dist/cli/setup-preferences.d.ts +20 -0
  77. package/dist/cli/setup-preferences.d.ts.map +1 -0
  78. package/dist/cli/setup-preferences.js +71 -0
  79. package/dist/cli/setup-preferences.js.map +1 -0
  80. package/dist/cli/setup.d.ts +7 -5
  81. package/dist/cli/setup.d.ts.map +1 -1
  82. package/dist/cli/setup.js +271 -152
  83. package/dist/cli/setup.js.map +1 -1
  84. package/dist/cli/team.d.ts +1 -0
  85. package/dist/cli/team.d.ts.map +1 -1
  86. package/dist/cli/team.js +70 -15
  87. package/dist/cli/team.js.map +1 -1
  88. package/dist/config/__tests__/generator-idempotent.test.js +100 -3
  89. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  90. package/dist/config/__tests__/generator-notify.test.js +6 -5
  91. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  92. package/dist/config/__tests__/generator-status-line-presets.test.d.ts +2 -0
  93. package/dist/config/__tests__/generator-status-line-presets.test.d.ts.map +1 -0
  94. package/dist/config/__tests__/generator-status-line-presets.test.js +203 -0
  95. package/dist/config/__tests__/generator-status-line-presets.test.js.map +1 -0
  96. package/dist/config/__tests__/models.test.js +23 -1
  97. package/dist/config/__tests__/models.test.js.map +1 -1
  98. package/dist/config/generator.d.ts +9 -1
  99. package/dist/config/generator.d.ts.map +1 -1
  100. package/dist/config/generator.js +184 -16
  101. package/dist/config/generator.js.map +1 -1
  102. package/dist/config/models.d.ts +5 -1
  103. package/dist/config/models.d.ts.map +1 -1
  104. package/dist/config/models.js +12 -2
  105. package/dist/config/models.js.map +1 -1
  106. package/dist/exec/followup.d.ts +44 -0
  107. package/dist/exec/followup.d.ts.map +1 -0
  108. package/dist/exec/followup.js +349 -0
  109. package/dist/exec/followup.js.map +1 -0
  110. package/dist/hooks/__tests__/autopilot-skill-contract.test.d.ts +2 -0
  111. package/dist/hooks/__tests__/autopilot-skill-contract.test.d.ts.map +1 -0
  112. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +37 -0
  113. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -0
  114. package/dist/hooks/__tests__/codebase-map.test.js +63 -1
  115. package/dist/hooks/__tests__/codebase-map.test.js.map +1 -1
  116. package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +1 -1
  117. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +5 -5
  118. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  119. package/dist/hooks/__tests__/deep-interview-contract.test.js +12 -9
  120. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  121. package/dist/hooks/__tests__/keyword-detector.test.js +25 -18
  122. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  123. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +23 -2
  124. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  125. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +45 -2
  126. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  127. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +17 -0
  128. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  129. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +121 -0
  130. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  131. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +4 -4
  132. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  133. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +103 -0
  134. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  135. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +2 -2
  136. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  137. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +27 -13
  138. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  139. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts +2 -0
  140. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts.map +1 -0
  141. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +35 -0
  142. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +1 -0
  143. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +215 -0
  144. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  145. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +70 -3
  146. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  147. package/dist/hooks/__tests__/pre-context-gate-skills.test.js +5 -0
  148. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  149. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +3 -2
  150. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  151. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +9 -0
  152. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  153. package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts +2 -0
  154. package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts.map +1 -0
  155. package/dist/hooks/__tests__/prompt-refactor-contract.test.js +22 -0
  156. package/dist/hooks/__tests__/prompt-refactor-contract.test.js.map +1 -0
  157. package/dist/hooks/codebase-map.d.ts.map +1 -1
  158. package/dist/hooks/codebase-map.js +83 -6
  159. package/dist/hooks/codebase-map.js.map +1 -1
  160. package/dist/hooks/keyword-detector.d.ts +1 -1
  161. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  162. package/dist/hooks/keyword-detector.js +35 -4
  163. package/dist/hooks/keyword-detector.js.map +1 -1
  164. package/dist/hooks/prompt-guidance-contract.d.ts +6 -0
  165. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  166. package/dist/hooks/prompt-guidance-contract.js +117 -13
  167. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  168. package/dist/hooks/session.d.ts +2 -0
  169. package/dist/hooks/session.d.ts.map +1 -1
  170. package/dist/hooks/session.js +6 -0
  171. package/dist/hooks/session.js.map +1 -1
  172. package/dist/hud/__tests__/index.test.js +4 -4
  173. package/dist/hud/__tests__/index.test.js.map +1 -1
  174. package/dist/hud/__tests__/state.test.js +4 -0
  175. package/dist/hud/__tests__/state.test.js.map +1 -1
  176. package/dist/hud/__tests__/types.test.js +27 -0
  177. package/dist/hud/__tests__/types.test.js.map +1 -1
  178. package/dist/hud/state.d.ts.map +1 -1
  179. package/dist/hud/state.js +8 -0
  180. package/dist/hud/state.js.map +1 -1
  181. package/dist/hud/types.d.ts +9 -0
  182. package/dist/hud/types.d.ts.map +1 -1
  183. package/dist/hud/types.js +3 -0
  184. package/dist/hud/types.js.map +1 -1
  185. package/dist/mcp/__tests__/bootstrap.test.js +23 -5
  186. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  187. package/dist/mcp/__tests__/server-lifecycle.test.js +50 -7
  188. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  189. package/dist/mcp/__tests__/state-server.test.js +70 -12
  190. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  191. package/dist/mcp/bootstrap.d.ts +10 -1
  192. package/dist/mcp/bootstrap.d.ts.map +1 -1
  193. package/dist/mcp/bootstrap.js +71 -26
  194. package/dist/mcp/bootstrap.js.map +1 -1
  195. package/dist/mcp/state-server.d.ts +5 -11
  196. package/dist/mcp/state-server.d.ts.map +1 -1
  197. package/dist/mcp/state-server.js +16 -432
  198. package/dist/mcp/state-server.js.map +1 -1
  199. package/dist/modes/__tests__/base-autoresearch-contract.test.js +1 -1
  200. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  201. package/dist/pipeline/__tests__/orchestrator.test.js +89 -5
  202. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  203. package/dist/pipeline/__tests__/stages.test.js +98 -1
  204. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  205. package/dist/pipeline/index.d.ts +5 -3
  206. package/dist/pipeline/index.d.ts.map +1 -1
  207. package/dist/pipeline/index.js +4 -3
  208. package/dist/pipeline/index.js.map +1 -1
  209. package/dist/pipeline/orchestrator.d.ts +7 -6
  210. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  211. package/dist/pipeline/orchestrator.js +90 -11
  212. package/dist/pipeline/orchestrator.js.map +1 -1
  213. package/dist/pipeline/review-verdict.d.ts +3 -0
  214. package/dist/pipeline/review-verdict.d.ts.map +1 -0
  215. package/dist/pipeline/review-verdict.js +14 -0
  216. package/dist/pipeline/review-verdict.js.map +1 -0
  217. package/dist/pipeline/stages/code-review.d.ts +33 -0
  218. package/dist/pipeline/stages/code-review.d.ts.map +1 -0
  219. package/dist/pipeline/stages/code-review.js +51 -0
  220. package/dist/pipeline/stages/code-review.js.map +1 -0
  221. package/dist/pipeline/stages/ralph-verify.d.ts +12 -2
  222. package/dist/pipeline/stages/ralph-verify.d.ts.map +1 -1
  223. package/dist/pipeline/stages/ralph-verify.js +24 -6
  224. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  225. package/dist/pipeline/stages/ralplan.d.ts +1 -1
  226. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  227. package/dist/pipeline/stages/ralplan.js +21 -1
  228. package/dist/pipeline/stages/ralplan.js.map +1 -1
  229. package/dist/pipeline/types.d.ts +14 -7
  230. package/dist/pipeline/types.d.ts.map +1 -1
  231. package/dist/pipeline/types.js +2 -2
  232. package/dist/planning/__tests__/artifacts.test.js +152 -1
  233. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  234. package/dist/planning/artifacts.d.ts +9 -0
  235. package/dist/planning/artifacts.d.ts.map +1 -1
  236. package/dist/planning/artifacts.js +60 -1
  237. package/dist/planning/artifacts.js.map +1 -1
  238. package/dist/question/__tests__/client.test.js +23 -3
  239. package/dist/question/__tests__/client.test.js.map +1 -1
  240. package/dist/question/__tests__/renderer.test.js +148 -37
  241. package/dist/question/__tests__/renderer.test.js.map +1 -1
  242. package/dist/question/__tests__/types.test.js +21 -0
  243. package/dist/question/__tests__/types.test.js.map +1 -1
  244. package/dist/question/__tests__/ui.test.js +155 -7
  245. package/dist/question/__tests__/ui.test.js.map +1 -1
  246. package/dist/question/client.d.ts +14 -4
  247. package/dist/question/client.d.ts.map +1 -1
  248. package/dist/question/client.js.map +1 -1
  249. package/dist/question/renderer.d.ts +11 -1
  250. package/dist/question/renderer.d.ts.map +1 -1
  251. package/dist/question/renderer.js +102 -7
  252. package/dist/question/renderer.js.map +1 -1
  253. package/dist/question/state.d.ts +2 -2
  254. package/dist/question/state.d.ts.map +1 -1
  255. package/dist/question/state.js +26 -17
  256. package/dist/question/state.js.map +1 -1
  257. package/dist/question/types.d.ts +25 -1
  258. package/dist/question/types.d.ts.map +1 -1
  259. package/dist/question/types.js +48 -13
  260. package/dist/question/types.js.map +1 -1
  261. package/dist/question/ui.d.ts +15 -2
  262. package/dist/question/ui.d.ts.map +1 -1
  263. package/dist/question/ui.js +268 -162
  264. package/dist/question/ui.js.map +1 -1
  265. package/dist/scripts/__tests__/codex-native-hook.test.js +415 -94
  266. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  267. package/dist/scripts/__tests__/generate-release-body.test.js +36 -0
  268. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  269. package/dist/scripts/__tests__/prompt-inventory.test.d.ts +2 -0
  270. package/dist/scripts/__tests__/prompt-inventory.test.d.ts.map +1 -0
  271. package/dist/scripts/__tests__/prompt-inventory.test.js +56 -0
  272. package/dist/scripts/__tests__/prompt-inventory.test.js.map +1 -0
  273. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  274. package/dist/scripts/codex-native-hook.js +232 -54
  275. package/dist/scripts/codex-native-hook.js.map +1 -1
  276. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  277. package/dist/scripts/codex-native-pre-post.js +12 -9
  278. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  279. package/dist/scripts/generate-release-body.d.ts.map +1 -1
  280. package/dist/scripts/generate-release-body.js +12 -3
  281. package/dist/scripts/generate-release-body.js.map +1 -1
  282. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts +2 -0
  283. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts.map +1 -0
  284. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js +153 -0
  285. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js.map +1 -0
  286. package/dist/scripts/notify-hook/managed-tmux.d.ts +4 -2
  287. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  288. package/dist/scripts/notify-hook/managed-tmux.js +188 -6
  289. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  290. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  291. package/dist/scripts/notify-hook/process-runner.js +7 -3
  292. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  293. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  294. package/dist/scripts/notify-hook/team-dispatch.js +96 -11
  295. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  296. package/dist/scripts/notify-hook/team-tmux-guard.js +3 -3
  297. package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts +34 -0
  298. package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts.map +1 -0
  299. package/dist/scripts/notify-hook/team-worker-posttooluse.js +434 -0
  300. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -0
  301. package/dist/scripts/notify-hook/team-worker.d.ts +1 -1
  302. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  303. package/dist/scripts/notify-hook/team-worker.js +3 -43
  304. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  305. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  306. package/dist/scripts/notify-hook/tmux-injection.js +25 -4
  307. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  308. package/dist/scripts/notify-hook.js +36 -5
  309. package/dist/scripts/notify-hook.js.map +1 -1
  310. package/dist/scripts/prompt-inventory.d.ts +29 -0
  311. package/dist/scripts/prompt-inventory.d.ts.map +1 -0
  312. package/dist/scripts/prompt-inventory.js +178 -0
  313. package/dist/scripts/prompt-inventory.js.map +1 -0
  314. package/dist/scripts/run-test-files.js +1 -0
  315. package/dist/scripts/run-test-files.js.map +1 -1
  316. package/dist/sidecar/__tests__/boundary.test.d.ts +2 -0
  317. package/dist/sidecar/__tests__/boundary.test.d.ts.map +1 -0
  318. package/dist/sidecar/__tests__/boundary.test.js +48 -0
  319. package/dist/sidecar/__tests__/boundary.test.js.map +1 -0
  320. package/dist/sidecar/__tests__/collector.test.d.ts +2 -0
  321. package/dist/sidecar/__tests__/collector.test.d.ts.map +1 -0
  322. package/dist/sidecar/__tests__/collector.test.js +162 -0
  323. package/dist/sidecar/__tests__/collector.test.js.map +1 -0
  324. package/dist/sidecar/__tests__/render.test.d.ts +2 -0
  325. package/dist/sidecar/__tests__/render.test.d.ts.map +1 -0
  326. package/dist/sidecar/__tests__/render.test.js +67 -0
  327. package/dist/sidecar/__tests__/render.test.js.map +1 -0
  328. package/dist/sidecar/__tests__/tmux.test.d.ts +2 -0
  329. package/dist/sidecar/__tests__/tmux.test.d.ts.map +1 -0
  330. package/dist/sidecar/__tests__/tmux.test.js +30 -0
  331. package/dist/sidecar/__tests__/tmux.test.js.map +1 -0
  332. package/dist/sidecar/__tests__/watch.test.d.ts +2 -0
  333. package/dist/sidecar/__tests__/watch.test.d.ts.map +1 -0
  334. package/dist/sidecar/__tests__/watch.test.js +42 -0
  335. package/dist/sidecar/__tests__/watch.test.js.map +1 -0
  336. package/dist/sidecar/collector.d.ts +4 -0
  337. package/dist/sidecar/collector.d.ts.map +1 -0
  338. package/dist/sidecar/collector.js +377 -0
  339. package/dist/sidecar/collector.js.map +1 -0
  340. package/dist/sidecar/index.d.ts +25 -0
  341. package/dist/sidecar/index.d.ts.map +1 -0
  342. package/dist/sidecar/index.js +165 -0
  343. package/dist/sidecar/index.js.map +1 -0
  344. package/dist/sidecar/render.d.ts +3 -0
  345. package/dist/sidecar/render.d.ts.map +1 -0
  346. package/dist/sidecar/render.js +72 -0
  347. package/dist/sidecar/render.js.map +1 -0
  348. package/dist/sidecar/tmux.d.ts +13 -0
  349. package/dist/sidecar/tmux.d.ts.map +1 -0
  350. package/dist/sidecar/tmux.js +44 -0
  351. package/dist/sidecar/tmux.js.map +1 -0
  352. package/dist/sidecar/types.d.ts +125 -0
  353. package/dist/sidecar/types.d.ts.map +1 -0
  354. package/dist/sidecar/types.js +2 -0
  355. package/dist/sidecar/types.js.map +1 -0
  356. package/dist/state/__tests__/operations.test.js +50 -22
  357. package/dist/state/__tests__/operations.test.js.map +1 -1
  358. package/dist/state/__tests__/workflow-transition.test.js +9 -1
  359. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  360. package/dist/state/operations.d.ts +1 -1
  361. package/dist/state/operations.d.ts.map +1 -1
  362. package/dist/state/operations.js +19 -7
  363. package/dist/state/operations.js.map +1 -1
  364. package/dist/state/workflow-transition.d.ts.map +1 -1
  365. package/dist/state/workflow-transition.js +1 -0
  366. package/dist/state/workflow-transition.js.map +1 -1
  367. package/dist/team/__tests__/commit-hygiene.test.d.ts +2 -0
  368. package/dist/team/__tests__/commit-hygiene.test.d.ts.map +1 -0
  369. package/dist/team/__tests__/commit-hygiene.test.js +93 -0
  370. package/dist/team/__tests__/commit-hygiene.test.js.map +1 -0
  371. package/dist/team/__tests__/delegation-policy.test.d.ts +2 -0
  372. package/dist/team/__tests__/delegation-policy.test.d.ts.map +1 -0
  373. package/dist/team/__tests__/delegation-policy.test.js +69 -0
  374. package/dist/team/__tests__/delegation-policy.test.js.map +1 -0
  375. package/dist/team/__tests__/events.test.js +54 -4
  376. package/dist/team/__tests__/events.test.js.map +1 -1
  377. package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts +2 -0
  378. package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts.map +1 -0
  379. package/dist/team/__tests__/hook-primary-e2e-contract.test.js +78 -0
  380. package/dist/team/__tests__/hook-primary-e2e-contract.test.js.map +1 -0
  381. package/dist/team/__tests__/model-contract.test.js +16 -0
  382. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  383. package/dist/team/__tests__/repo-aware-decomposition.test.d.ts +2 -0
  384. package/dist/team/__tests__/repo-aware-decomposition.test.d.ts.map +1 -0
  385. package/dist/team/__tests__/repo-aware-decomposition.test.js +95 -0
  386. package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -0
  387. package/dist/team/__tests__/runtime.test.js +623 -14
  388. package/dist/team/__tests__/runtime.test.js.map +1 -1
  389. package/dist/team/__tests__/state-root.test.js +177 -1
  390. package/dist/team/__tests__/state-root.test.js.map +1 -1
  391. package/dist/team/__tests__/state.test.js +110 -0
  392. package/dist/team/__tests__/state.test.js.map +1 -1
  393. package/dist/team/__tests__/tmux-session.test.js +399 -2
  394. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  395. package/dist/team/__tests__/worker-bootstrap.test.js +94 -0
  396. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  397. package/dist/team/commit-hygiene.d.ts +22 -3
  398. package/dist/team/commit-hygiene.d.ts.map +1 -1
  399. package/dist/team/commit-hygiene.js +134 -2
  400. package/dist/team/commit-hygiene.js.map +1 -1
  401. package/dist/team/contracts.d.ts +1 -1
  402. package/dist/team/contracts.d.ts.map +1 -1
  403. package/dist/team/contracts.js +2 -0
  404. package/dist/team/contracts.js.map +1 -1
  405. package/dist/team/dag-schema.d.ts +38 -0
  406. package/dist/team/dag-schema.d.ts.map +1 -0
  407. package/dist/team/dag-schema.js +221 -0
  408. package/dist/team/dag-schema.js.map +1 -0
  409. package/dist/team/delegation-policy.d.ts +3 -0
  410. package/dist/team/delegation-policy.d.ts.map +1 -0
  411. package/dist/team/delegation-policy.js +82 -0
  412. package/dist/team/delegation-policy.js.map +1 -0
  413. package/dist/team/model-contract.d.ts +3 -1
  414. package/dist/team/model-contract.d.ts.map +1 -1
  415. package/dist/team/model-contract.js +44 -5
  416. package/dist/team/model-contract.js.map +1 -1
  417. package/dist/team/repo-aware-decomposition.d.ts +60 -0
  418. package/dist/team/repo-aware-decomposition.d.ts.map +1 -0
  419. package/dist/team/repo-aware-decomposition.js +229 -0
  420. package/dist/team/repo-aware-decomposition.js.map +1 -0
  421. package/dist/team/runtime.d.ts +27 -0
  422. package/dist/team/runtime.d.ts.map +1 -1
  423. package/dist/team/runtime.js +172 -52
  424. package/dist/team/runtime.js.map +1 -1
  425. package/dist/team/state/tasks.d.ts.map +1 -1
  426. package/dist/team/state/tasks.js +33 -0
  427. package/dist/team/state/tasks.js.map +1 -1
  428. package/dist/team/state/types.d.ts +23 -1
  429. package/dist/team/state/types.d.ts.map +1 -1
  430. package/dist/team/state/types.js.map +1 -1
  431. package/dist/team/state-root.d.ts +35 -0
  432. package/dist/team/state-root.d.ts.map +1 -1
  433. package/dist/team/state-root.js +281 -1
  434. package/dist/team/state-root.js.map +1 -1
  435. package/dist/team/state.d.ts +27 -1
  436. package/dist/team/state.d.ts.map +1 -1
  437. package/dist/team/state.js +6 -0
  438. package/dist/team/state.js.map +1 -1
  439. package/dist/team/tmux-session.d.ts +1 -0
  440. package/dist/team/tmux-session.d.ts.map +1 -1
  441. package/dist/team/tmux-session.js +105 -6
  442. package/dist/team/tmux-session.js.map +1 -1
  443. package/dist/team/worker-bootstrap.d.ts +3 -0
  444. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  445. package/dist/team/worker-bootstrap.js +77 -4
  446. package/dist/team/worker-bootstrap.js.map +1 -1
  447. package/dist/utils/agents-md.d.ts +3 -0
  448. package/dist/utils/agents-md.d.ts.map +1 -1
  449. package/dist/utils/agents-md.js +25 -0
  450. package/dist/utils/agents-md.js.map +1 -1
  451. package/package.json +3 -2
  452. package/plugins/oh-my-codex/.codex-plugin/plugin.json +2 -2
  453. package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +1 -1
  454. package/plugins/oh-my-codex/skills/analyze/SKILL.md +1 -1
  455. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +134 -205
  456. package/plugins/oh-my-codex/skills/code-review/SKILL.md +4 -4
  457. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +14 -7
  458. package/plugins/oh-my-codex/skills/doctor/SKILL.md +1 -1
  459. package/plugins/oh-my-codex/skills/help/SKILL.md +1 -1
  460. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +41 -10
  461. package/plugins/oh-my-codex/skills/plan/SKILL.md +12 -14
  462. package/plugins/oh-my-codex/skills/ralph/SKILL.md +2 -4
  463. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -9
  464. package/plugins/oh-my-codex/skills/security-review/SKILL.md +4 -4
  465. package/plugins/oh-my-codex/skills/team/SKILL.md +2 -5
  466. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +2 -5
  467. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +2 -3
  468. package/prompts/analyst.md +2 -2
  469. package/prompts/api-reviewer.md +2 -2
  470. package/prompts/architect.md +2 -2
  471. package/prompts/build-fixer.md +2 -2
  472. package/prompts/code-reviewer.md +15 -5
  473. package/prompts/code-simplifier.md +1 -1
  474. package/prompts/critic.md +35 -83
  475. package/prompts/debugger.md +2 -2
  476. package/prompts/dependency-expert.md +2 -2
  477. package/prompts/designer.md +2 -2
  478. package/prompts/executor.md +40 -114
  479. package/prompts/explore-harness.md +1 -1
  480. package/prompts/explore.md +37 -90
  481. package/prompts/git-master.md +2 -2
  482. package/prompts/information-architect.md +1 -1
  483. package/prompts/performance-reviewer.md +2 -2
  484. package/prompts/planner.md +35 -62
  485. package/prompts/product-analyst.md +2 -2
  486. package/prompts/product-manager.md +2 -2
  487. package/prompts/qa-tester.md +2 -2
  488. package/prompts/quality-reviewer.md +2 -2
  489. package/prompts/quality-strategist.md +2 -2
  490. package/prompts/researcher.md +46 -78
  491. package/prompts/security-reviewer.md +2 -2
  492. package/prompts/sisyphus-lite.md +2 -2
  493. package/prompts/style-reviewer.md +2 -2
  494. package/prompts/team-executor.md +1 -1
  495. package/prompts/test-engineer.md +2 -2
  496. package/prompts/ux-researcher.md +2 -2
  497. package/prompts/verifier.md +29 -34
  498. package/prompts/vision.md +2 -2
  499. package/prompts/writer.md +2 -2
  500. package/skills/ai-slop-cleaner/SKILL.md +1 -1
  501. package/skills/analyze/SKILL.md +1 -1
  502. package/skills/autopilot/SKILL.md +134 -205
  503. package/skills/build-fix/SKILL.md +4 -4
  504. package/skills/code-review/SKILL.md +4 -4
  505. package/skills/deep-interview/SKILL.md +14 -7
  506. package/skills/doctor/SKILL.md +1 -1
  507. package/skills/help/SKILL.md +1 -1
  508. package/skills/omx-setup/SKILL.md +41 -10
  509. package/skills/plan/SKILL.md +12 -14
  510. package/skills/ralph/SKILL.md +2 -4
  511. package/skills/ralplan/SKILL.md +5 -9
  512. package/skills/security-review/SKILL.md +4 -4
  513. package/skills/team/SKILL.md +2 -5
  514. package/skills/ultraqa/SKILL.md +2 -5
  515. package/skills/ultrawork/SKILL.md +2 -3
  516. package/src/scripts/__tests__/codex-native-hook.test.ts +502 -94
  517. package/src/scripts/__tests__/generate-release-body.test.ts +41 -0
  518. package/src/scripts/__tests__/prompt-inventory.test.ts +64 -0
  519. package/src/scripts/codex-native-hook.ts +293 -61
  520. package/src/scripts/codex-native-pre-post.ts +10 -8
  521. package/src/scripts/generate-release-body.ts +13 -2
  522. package/src/scripts/notify-hook/__tests__/team-worker-posttooluse.test.ts +180 -0
  523. package/src/scripts/notify-hook/managed-tmux.ts +196 -9
  524. package/src/scripts/notify-hook/process-runner.ts +7 -3
  525. package/src/scripts/notify-hook/team-dispatch.ts +103 -11
  526. package/src/scripts/notify-hook/team-tmux-guard.ts +3 -3
  527. package/src/scripts/notify-hook/team-worker-posttooluse.ts +536 -0
  528. package/src/scripts/notify-hook/team-worker.ts +4 -48
  529. package/src/scripts/notify-hook/tmux-injection.ts +24 -6
  530. package/src/scripts/notify-hook.ts +36 -5
  531. package/src/scripts/prompt-inventory.ts +218 -0
  532. package/src/scripts/run-test-files.ts +1 -0
  533. package/templates/AGENTS.md +34 -95
@@ -152,6 +152,7 @@ export function detectMcpTransportFailure(
152
152
  payload: CodexHookPayload,
153
153
  ): McpTransportFailureSignal | null {
154
154
  const normalized = normalizePostToolUsePayload(payload);
155
+ if (normalized.isBash) return null;
155
156
  const combined = [
156
157
  normalized.stderrText,
157
158
  normalized.stdoutText,
@@ -591,10 +592,6 @@ function buildGitCommitEnforcementOutput(commandText: string): Record<string, un
591
592
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
592
593
  hookSpecificOutput: {
593
594
  hookEventName: "PreToolUse",
594
- additionalContext: [
595
- "Lore-format git commit enforcement triggered.",
596
- ...errors.map((error) => `- ${error}`),
597
- ].join("\n"),
598
595
  },
599
596
  systemMessage: [
600
597
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -764,10 +761,17 @@ function containsHardFailure(text: string): boolean {
764
761
  return /command not found|permission denied|no such file or directory/i.test(text);
765
762
  }
766
763
 
764
+ function hasActionableBashHardFailure(normalized: NormalizedPostToolUsePayload): boolean {
765
+ if (containsHardFailure(normalized.stderrText)) return true;
766
+ if (normalized.exitCode === null || normalized.exitCode === 0) return false;
767
+ return containsHardFailure(`${normalized.stderrText}\n${normalized.stdoutText}`);
768
+ }
769
+
767
770
  export function buildNativePostToolUseOutput(
768
771
  payload: CodexHookPayload,
769
772
  ): Record<string, unknown> | null {
770
- const mcpTransportFailure = detectMcpTransportFailure(payload);
773
+ const normalized = normalizePostToolUsePayload(payload);
774
+ const mcpTransportFailure = normalized.isBash ? null : detectMcpTransportFailure(payload);
771
775
  if (mcpTransportFailure) {
772
776
  const fallbackCommand = buildOmxParityFallbackCommand(payload, mcpTransportFailure.toolName);
773
777
  const fallbackText = fallbackCommand
@@ -784,12 +788,10 @@ export function buildNativePostToolUseOutput(
784
788
  };
785
789
  }
786
790
 
787
- const normalized = normalizePostToolUsePayload(payload);
788
791
  if (!normalized.isBash) return null;
789
792
 
790
793
  const combined = `${normalized.stderrText}\n${normalized.stdoutText}`.trim();
791
- if (normalized.exitCode === 0) return null;
792
- if (containsHardFailure(combined)) {
794
+ if (hasActionableBashHardFailure(normalized)) {
793
795
  return {
794
796
  decision: "block",
795
797
  reason: "The Bash output indicates a command/setup failure that should be fixed before retrying.",
@@ -75,6 +75,15 @@ export function resolveCurrentTag(cwd: string, explicit?: string): string {
75
75
  throw new Error('unable to determine current release tag; pass --current-tag or set GITHUB_REF_NAME');
76
76
  }
77
77
 
78
+ function isAncestorTag(cwd: string, possibleAncestor: string, currentTag: string): boolean {
79
+ const result = spawnSync('git', ['merge-base', '--is-ancestor', possibleAncestor, currentTag], {
80
+ cwd,
81
+ encoding: 'utf-8',
82
+ stdio: 'pipe',
83
+ });
84
+ return result.status === 0;
85
+ }
86
+
78
87
  export function resolvePreviousTag(cwd: string, currentTag: string, explicit?: string): string | undefined {
79
88
  if (explicit) return explicit;
80
89
  const tags = runGit(['tag', '--list', 'v*', '--sort=-v:refname'], cwd, true)
@@ -83,8 +92,10 @@ export function resolvePreviousTag(cwd: string, currentTag: string, explicit?: s
83
92
  .filter(Boolean);
84
93
  if (tags.length === 0) return undefined;
85
94
  const currentIndex = tags.indexOf(currentTag);
86
- if (currentIndex >= 0) return tags.slice(currentIndex + 1).find(Boolean);
87
- return tags.find((tag) => tag !== currentTag);
95
+ const candidates = currentIndex >= 0
96
+ ? tags.slice(currentIndex + 1)
97
+ : tags.filter((tag) => tag !== currentTag);
98
+ return candidates.find((tag) => isAncestorTag(cwd, tag, currentTag));
88
99
  }
89
100
 
90
101
  function verifyGitCommitRef(cwd: string, ref: string, label: string): void {
@@ -0,0 +1,180 @@
1
+ import assert from 'node:assert/strict';
2
+ import { describe, it } from 'node:test';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { chmod, mkdtemp, mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import { existsSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { tmpdir } from 'node:os';
8
+ import { handleTeamWorkerPostToolUseSuccess, teamWorkerPostToolUseInternals } from '../team-worker-posttooluse.js';
9
+ import { readTeamEvents } from '../../../team/state/events.js';
10
+
11
+ async function initWorkerFixture(): Promise<{ cwd: string; stateRoot: string; env: NodeJS.ProcessEnv }> {
12
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-posttooluse-worker-'));
13
+ execFileSync('git', ['init'], { cwd, stdio: 'ignore' });
14
+ execFileSync('git', ['config', 'user.email', 'test@example.com'], { cwd, stdio: 'ignore' });
15
+ execFileSync('git', ['config', 'user.name', 'Test User'], { cwd, stdio: 'ignore' });
16
+ await writeFile(join(cwd, 'README.md'), 'hello\n', 'utf-8');
17
+ execFileSync('git', ['add', 'README.md'], { cwd, stdio: 'ignore' });
18
+ execFileSync('git', ['commit', '-m', 'init'], { cwd, stdio: 'ignore' });
19
+
20
+ const stateRoot = join(cwd, '.omx', 'state');
21
+ const workerDir = join(stateRoot, 'team', 'demo-team', 'workers', 'worker-1');
22
+ await mkdir(workerDir, { recursive: true });
23
+ await writeFile(join(workerDir, 'identity.json'), JSON.stringify({
24
+ name: 'worker-1',
25
+ team_state_root: stateRoot,
26
+ worktree_path: cwd,
27
+ }, null, 2), 'utf-8');
28
+ await writeFile(join(stateRoot, 'team', 'demo-team', 'phase.json'), JSON.stringify({ current_phase: 'team-exec' }, null, 2), 'utf-8');
29
+ await writeFile(join(stateRoot, 'team', 'demo-team', 'config.json'), JSON.stringify({ leader_cwd: cwd }, null, 2), 'utf-8');
30
+ return {
31
+ cwd,
32
+ stateRoot,
33
+ env: {
34
+ ...process.env,
35
+ OMX_TEAM_WORKER: 'demo-team/worker-1',
36
+ OMX_TEAM_STATE_ROOT: stateRoot,
37
+ },
38
+ };
39
+ }
40
+
41
+ const successPayload = {
42
+ hook_event_name: 'PostToolUse',
43
+ tool_name: 'Bash',
44
+ tool_response: { exit_code: 0 },
45
+ tool_use_id: 'tool-1',
46
+ };
47
+
48
+ describe('handleTeamWorkerPostToolUseSuccess', () => {
49
+ it('creates a safe worker checkpoint, ledger entries, leader signal, and dedupe marker', async () => {
50
+ const fixture = await initWorkerFixture();
51
+ await writeFile(join(fixture.cwd, 'feature.txt'), 'feature\n', 'utf-8');
52
+
53
+ const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
54
+
55
+ assert.equal(result.handled, true);
56
+ assert.equal(result.status, 'applied');
57
+ assert.ok(result.checkpointCommit);
58
+ assert.deepEqual(result.operationKinds, ['auto_checkpoint', 'worker_clean_rebase', 'leader_integration_attempt']);
59
+
60
+ const log = execFileSync('git', ['log', '-1', '--pretty=%s'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
61
+ assert.equal(log, 'omx(team): auto-checkpoint worker-1');
62
+
63
+ const events = await readTeamEvents('demo-team', fixture.cwd, { type: 'worker_integration_attempt_requested' });
64
+ assert.equal(events.length, 1);
65
+ const event = events[0]!;
66
+ assert.equal(event.type, 'worker_integration_attempt_requested');
67
+ assert.equal(event.worker, 'worker-1');
68
+ const metadata = event.metadata as Record<string, unknown>;
69
+ assert.equal(metadata.operation_kind, 'leader_integration_attempt');
70
+ assert.equal(metadata.outcome_status, 'applied');
71
+ assert.equal(metadata.source, 'posttooluse');
72
+ assert.equal(metadata.dedupe_key, result.dedupeKey);
73
+ assert.match(String(metadata.leader_head_observed), /^[0-9a-f]{40}$/);
74
+
75
+ const dedupe = JSON.parse(await readFile(join(fixture.stateRoot, 'team', 'demo-team', 'workers', 'worker-1', 'posttooluse-dedupe.json'), 'utf-8')) as { keys: string[] };
76
+ assert.equal(dedupe.keys.includes(result.dedupeKey!), true);
77
+
78
+ const ledger = JSON.parse(await readFile(join(fixture.cwd, '.omx', 'reports', 'team-commit-hygiene', 'demo-team.ledger.json'), 'utf-8')) as { entries: Array<{ operation: string; status: string }> };
79
+ assert.equal(ledger.entries.some((entry) => entry.operation === 'auto_checkpoint' && entry.status === 'applied'), true);
80
+ assert.equal(ledger.entries.some((entry) => entry.operation === 'worker_clean_rebase' && entry.status === 'noop'), true);
81
+ assert.equal(ledger.entries.some((entry) => entry.operation === 'leader_integration_attempt' && entry.status === 'applied'), true);
82
+ });
83
+
84
+ it('records noop without creating a checkpoint commit', async () => {
85
+ const fixture = await initWorkerFixture();
86
+ const before = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
87
+
88
+ const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
89
+
90
+ const after = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
91
+ assert.equal(result.status, 'noop');
92
+ assert.equal(result.checkpointCommit, null);
93
+ assert.equal(after, before);
94
+ });
95
+
96
+ it('skips protected runtime-only changes', async () => {
97
+ const fixture = await initWorkerFixture();
98
+ await writeFile(join(fixture.cwd, 'AGENTS.md'), 'generated worker instructions\n', 'utf-8');
99
+
100
+ const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
101
+
102
+ assert.equal(result.status, 'skipped');
103
+ assert.equal(result.reason, 'only_protected_paths_changed');
104
+ assert.equal(result.checkpointCommit, null);
105
+ });
106
+
107
+ it('does not checkpoint its own runtime report artifacts on a repeated PostToolUse turn', async () => {
108
+ const fixture = await initWorkerFixture();
109
+ await writeFile(join(fixture.cwd, 'feature.txt'), 'feature\n', 'utf-8');
110
+
111
+ const first = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
112
+ const headAfterFirst = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
113
+ const second = await handleTeamWorkerPostToolUseSuccess({ ...successPayload, tool_use_id: 'tool-2' }, fixture.cwd, fixture.env);
114
+ const headAfterSecond = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
115
+ const events = await readTeamEvents('demo-team', fixture.cwd, { type: 'worker_integration_attempt_requested' });
116
+
117
+ assert.equal(first.status, 'applied');
118
+ assert.equal(second.status, 'noop');
119
+ assert.equal(headAfterSecond, headAfterFirst);
120
+ assert.equal(events.length, 1);
121
+ });
122
+
123
+ it('skips checkpointing when the index is already staged and preserves staged changes', async () => {
124
+ const fixture = await initWorkerFixture();
125
+ await writeFile(join(fixture.cwd, 'pre-staged.txt'), 'already staged\n', 'utf-8');
126
+ execFileSync('git', ['add', 'pre-staged.txt'], { cwd: fixture.cwd, stdio: 'ignore' });
127
+ await writeFile(join(fixture.cwd, 'unstaged.txt'), 'new work\n', 'utf-8');
128
+
129
+ const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
130
+ const staged = execFileSync('git', ['diff', '--name-only', '--cached'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim().split('\n').filter(Boolean);
131
+ const status = execFileSync('git', ['status', '--porcelain=v1', '-uall'], { cwd: fixture.cwd, encoding: 'utf-8' });
132
+
133
+ assert.equal(result.status, 'skipped');
134
+ assert.equal(result.reason, 'index_not_clean');
135
+ assert.deepEqual(staged, ['pre-staged.txt']);
136
+ assert.match(status, /\?\? unstaged\.txt/);
137
+ });
138
+
139
+ it('unstages checkpointable paths when checkpoint commit fails after staging', async () => {
140
+ const fixture = await initWorkerFixture();
141
+ const hookPath = join(fixture.cwd, '.git', 'hooks', 'prepare-commit-msg');
142
+ await writeFile(hookPath, '#!/bin/sh\nexit 42\n', 'utf-8');
143
+ await chmod(hookPath, 0o755);
144
+ await writeFile(join(fixture.cwd, 'commit-fail.txt'), 'must not remain staged\n', 'utf-8');
145
+
146
+ const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
147
+ const staged = execFileSync('git', ['diff', '--name-only', '--cached'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
148
+ const status = execFileSync('git', ['status', '--porcelain=v1', '-uall'], { cwd: fixture.cwd, encoding: 'utf-8' });
149
+
150
+ assert.equal(result.status, 'skipped');
151
+ assert.match(result.reason ?? '', /^git_commit_failed:/);
152
+ assert.equal(staged, '');
153
+ assert.match(status, /\?\? commit-fail\.txt/);
154
+ });
155
+
156
+ it('exports canonical dedupe and protected-path helpers for fallback suppression', () => {
157
+ const key = teamWorkerPostToolUseInternals.buildDedupeKey({
158
+ teamName: 'team',
159
+ workerName: 'worker-1',
160
+ workerHeadAfter: 'after',
161
+ operationKind: 'leader_integration_attempt',
162
+ });
163
+ assert.equal(key, 'team|worker-1|after|leader_integration_attempt');
164
+ assert.equal(teamWorkerPostToolUseInternals.isProtectedCheckpointPath('.omx/state/team/x'), true);
165
+ assert.equal(teamWorkerPostToolUseInternals.isProtectedCheckpointPath('src/index.ts'), false);
166
+ });
167
+
168
+ it('fails closed for missing worker identity without guessed state writes', async () => {
169
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-posttooluse-missing-identity-'));
170
+ const result = await handleTeamWorkerPostToolUseSuccess(successPayload, cwd, {
171
+ ...process.env,
172
+ OMX_TEAM_WORKER: 'demo-team/worker-1',
173
+ OMX_TEAM_STATE_ROOT: join(cwd, '.omx', 'state'),
174
+ });
175
+
176
+ assert.equal(result.handled, false);
177
+ assert.equal(result.status, 'skipped');
178
+ assert.equal(existsSync(join(cwd, '.omx', 'state', 'team')), false);
179
+ });
180
+ });
@@ -6,6 +6,9 @@ import { runProcess } from './process-runner.js';
6
6
  import { safeString } from './utils.js';
7
7
  import { sameFilePath } from '../../utils/paths.js';
8
8
 
9
+ const OMX_INSTANCE_OPTION = '@omx_instance_id';
10
+ const OMX_PANE_INSTANCE_OPTION = '@omx_pane_instance_id';
11
+
9
12
  function sanitizeTmuxToken(value: string): string {
10
13
  const cleaned = safeString(value)
11
14
  .toLowerCase()
@@ -61,6 +64,10 @@ function readNativeSessionId(sessionState: { native_session_id?: unknown; codex_
61
64
  return safeString(sessionState.native_session_id || sessionState.codex_session_id || '').trim();
62
65
  }
63
66
 
67
+ function readAuthoritativeTmuxSessionName(sessionState: { tmux_session_name?: unknown; tmuxSessionName?: unknown }): string {
68
+ return safeString(sessionState.tmux_session_name || sessionState.tmuxSessionName || '').trim();
69
+ }
70
+
64
71
  function readCurrentTmuxSessionName(): string {
65
72
  if (!process.env.TMUX) return '';
66
73
  try {
@@ -74,6 +81,54 @@ function readCurrentTmuxSessionName(): string {
74
81
  }
75
82
  }
76
83
 
84
+ async function readTmuxOption(targetValue: string, optionName: string, { pane = false } = {}): Promise<string> {
85
+ const target = safeString(targetValue).trim();
86
+ if (!target) return '';
87
+ const args = ['show-option', '-qv'];
88
+ if (pane) args.push('-p');
89
+ args.push('-t', target, optionName);
90
+ try {
91
+ const result = await runProcess('tmux', args, 2000);
92
+ return safeString(result.stdout).trim();
93
+ } catch {
94
+ return '';
95
+ }
96
+ }
97
+
98
+ async function readTmuxSessionInstanceId(sessionTarget: string): Promise<string> {
99
+ return readTmuxOption(sessionTarget, OMX_INSTANCE_OPTION);
100
+ }
101
+
102
+ async function readTmuxPaneInstanceId(paneTarget: string): Promise<string> {
103
+ return readTmuxOption(paneTarget, OMX_PANE_INSTANCE_OPTION, { pane: true });
104
+ }
105
+
106
+ function warnPaneInstanceFallback(paneTarget: string): void {
107
+ const target = safeString(paneTarget).trim();
108
+ if (!target) return;
109
+ try {
110
+ console.warn(`[omx] missing ${OMX_PANE_INSTANCE_OPTION} on ${target}; falling back to ${OMX_INSTANCE_OPTION}`);
111
+ } catch {
112
+ // warning is best effort only
113
+ }
114
+ }
115
+
116
+ export async function resolveTmuxSessionForInstance(instanceId: string): Promise<string> {
117
+ const expected = safeString(instanceId).trim();
118
+ if (!expected) return '';
119
+ try {
120
+ const result = await runProcess('tmux', ['list-sessions', '-F', `#{session_name}\t#{${OMX_INSTANCE_OPTION}}`], 2000);
121
+ const rows = safeString(result.stdout).split('\n').map(line => line.trim()).filter(Boolean);
122
+ for (const row of rows) {
123
+ const [sessionName = '', taggedInstanceId = ''] = row.split('\t');
124
+ if (sessionName && taggedInstanceId === expected) return sessionName;
125
+ }
126
+ } catch {
127
+ // best effort only
128
+ }
129
+ return '';
130
+ }
131
+
77
132
  function readParentPid(pid: number): number | null {
78
133
  if (!Number.isInteger(pid) || pid <= 1) return null;
79
134
  try {
@@ -111,7 +166,11 @@ function processHasAncestorPid(targetPid: number, currentPid = process.pid): boo
111
166
  return false;
112
167
  }
113
168
 
114
- export async function resolveManagedSessionContext(cwd: string, payload: any, { allowTeamWorker = true } = {}): Promise<any> {
169
+ export async function resolveManagedSessionContext(
170
+ cwd: string,
171
+ payload: any,
172
+ { allowTeamWorker = true, paneTarget = '' }: { allowTeamWorker?: boolean; paneTarget?: string } = {},
173
+ ): Promise<any> {
115
174
  if (allowTeamWorker && safeString(process.env.OMX_TEAM_WORKER || '').trim() !== '') {
116
175
  return {
117
176
  managed: true,
@@ -150,15 +209,88 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
150
209
  return { managed: false, reason: 'session_id_mismatch', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
151
210
  }
152
211
  if (isSessionStale(sessionState)) {
153
- return { managed: false, reason: 'stale_session', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
212
+ return { managed: false, reason: 'stale_session', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '', taggedTmuxSessionName: '' };
154
213
  }
155
214
 
156
215
  const authoritativeSessionCwd = safeString(sessionState.cwd || cwd).trim() || cwd;
157
- const expectedTmuxSessionName = buildExpectedManagedTmuxSessionName(
158
- authoritativeSessionCwd,
159
- canonicalSessionId || invocationSessionId,
160
- );
216
+ const authoritativeTmuxSessionName = readAuthoritativeTmuxSessionName(sessionState);
217
+ const expectedTmuxSessionName = authoritativeTmuxSessionName
218
+ || buildExpectedManagedTmuxSessionName(
219
+ authoritativeSessionCwd,
220
+ canonicalSessionId || invocationSessionId,
221
+ );
161
222
  const currentTmuxSessionName = readCurrentTmuxSessionName();
223
+ const currentTmuxPaneTarget = safeString(paneTarget || process.env.TMUX_PANE || '').trim();
224
+ const currentTmuxPaneInstanceId = currentTmuxPaneTarget ? await readTmuxPaneInstanceId(currentTmuxPaneTarget) : '';
225
+ if (currentTmuxPaneInstanceId && currentTmuxPaneInstanceId !== invocationSessionId) {
226
+ return {
227
+ managed: false,
228
+ reason: 'pane_instance_mismatch',
229
+ invocationSessionId,
230
+ sessionState,
231
+ expectedTmuxSessionName,
232
+ currentTmuxSessionName,
233
+ currentTmuxPaneTarget,
234
+ currentTmuxPaneInstanceId,
235
+ taggedTmuxSessionName: '',
236
+ };
237
+ }
238
+ if (currentTmuxPaneInstanceId === invocationSessionId) {
239
+ return {
240
+ managed: true,
241
+ reason: 'tmux_pane_instance_match',
242
+ invocationSessionId,
243
+ sessionState,
244
+ expectedTmuxSessionName,
245
+ currentTmuxSessionName,
246
+ currentTmuxPaneTarget,
247
+ currentTmuxPaneInstanceId,
248
+ taggedTmuxSessionName: currentTmuxSessionName,
249
+ };
250
+ }
251
+
252
+ const currentTmuxInstanceId = currentTmuxSessionName ? await readTmuxSessionInstanceId(currentTmuxSessionName) : '';
253
+ if (currentTmuxInstanceId && currentTmuxInstanceId !== invocationSessionId) {
254
+ return {
255
+ managed: false,
256
+ reason: 'tmux_instance_mismatch',
257
+ invocationSessionId,
258
+ sessionState,
259
+ expectedTmuxSessionName,
260
+ currentTmuxSessionName,
261
+ currentTmuxInstanceId,
262
+ taggedTmuxSessionName: '',
263
+ };
264
+ }
265
+ if (currentTmuxInstanceId === invocationSessionId) {
266
+ if (currentTmuxPaneTarget) warnPaneInstanceFallback(currentTmuxPaneTarget);
267
+ return {
268
+ managed: true,
269
+ reason: 'tmux_instance_match',
270
+ invocationSessionId,
271
+ sessionState,
272
+ expectedTmuxSessionName,
273
+ currentTmuxSessionName,
274
+ currentTmuxInstanceId,
275
+ currentTmuxPaneTarget,
276
+ paneInstanceWarning: 'missing_pane_instance_tag_session_fallback',
277
+ taggedTmuxSessionName: currentTmuxSessionName,
278
+ };
279
+ }
280
+
281
+ const taggedTmuxSessionName = await resolveTmuxSessionForInstance(invocationSessionId);
282
+ if (taggedTmuxSessionName) {
283
+ return {
284
+ managed: true,
285
+ reason: 'tmux_instance_tag_match',
286
+ invocationSessionId,
287
+ sessionState,
288
+ expectedTmuxSessionName,
289
+ currentTmuxSessionName,
290
+ taggedTmuxSessionName,
291
+ };
292
+ }
293
+
162
294
  if (currentTmuxSessionName && currentTmuxSessionName === expectedTmuxSessionName) {
163
295
  return {
164
296
  managed: true,
@@ -171,6 +303,19 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
171
303
  currentTmuxSessionName,
172
304
  };
173
305
  }
306
+ if (authoritativeTmuxSessionName && currentTmuxSessionName) {
307
+ return {
308
+ managed: false,
309
+ reason: 'tmux_session_mismatch',
310
+ invocationSessionId,
311
+ canonicalSessionId,
312
+ nativeSessionId,
313
+ sessionState,
314
+ expectedTmuxSessionName,
315
+ currentTmuxSessionName,
316
+ taggedTmuxSessionName: '',
317
+ };
318
+ }
174
319
 
175
320
  if (processHasAncestorPid(sessionState.pid)) {
176
321
  return {
@@ -182,6 +327,7 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
182
327
  sessionState,
183
328
  expectedTmuxSessionName,
184
329
  currentTmuxSessionName: '',
330
+ taggedTmuxSessionName: '',
185
331
  };
186
332
  }
187
333
 
@@ -194,6 +340,7 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
194
340
  sessionState,
195
341
  expectedTmuxSessionName,
196
342
  currentTmuxSessionName,
343
+ taggedTmuxSessionName: '',
197
344
  };
198
345
  } catch {
199
346
  return {
@@ -203,6 +350,7 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
203
350
  sessionState: null,
204
351
  expectedTmuxSessionName: '',
205
352
  currentTmuxSessionName: '',
353
+ taggedTmuxSessionName: '',
206
354
  };
207
355
  }
208
356
  }
@@ -218,7 +366,7 @@ export async function verifyManagedPaneTarget(paneId: string, cwd: string, paylo
218
366
  return { ok: false, reason: 'missing_pane_target', paneTarget: '' };
219
367
  }
220
368
 
221
- const managedContext = await resolveManagedSessionContext(cwd, payload, { allowTeamWorker });
369
+ const managedContext = await resolveManagedSessionContext(cwd, payload, { allowTeamWorker, paneTarget });
222
370
  if (!managedContext.managed) {
223
371
  return { ok: false, reason: managedContext.reason || 'unmanaged_session', paneTarget, managedContext };
224
372
  }
@@ -238,10 +386,49 @@ export async function verifyManagedPaneTarget(paneId: string, cwd: string, paylo
238
386
  if (!paneSessionName) {
239
387
  return { ok: false, reason: 'pane_session_missing', paneTarget, managedContext };
240
388
  }
389
+ const paneInstanceId = await readTmuxPaneInstanceId(paneTarget);
390
+ const sessionInstanceId = paneInstanceId ? '' : await readTmuxSessionInstanceId(paneSessionName);
391
+ if (paneInstanceId && paneInstanceId !== managedContext.invocationSessionId) {
392
+ return { ok: false, reason: 'pane_instance_mismatch', paneTarget, paneSessionName, paneInstanceId, managedContext };
393
+ }
394
+ if (!paneInstanceId && sessionInstanceId) {
395
+ warnPaneInstanceFallback(paneTarget);
396
+ if (sessionInstanceId !== managedContext.invocationSessionId) {
397
+ return {
398
+ ok: false,
399
+ reason: 'pane_instance_mismatch',
400
+ paneTarget,
401
+ paneSessionName,
402
+ paneInstanceId: sessionInstanceId,
403
+ paneInstanceWarning: 'missing_pane_instance_tag_session_fallback',
404
+ managedContext,
405
+ };
406
+ }
407
+ }
241
408
  if (paneSessionName !== expectedSession) {
409
+ const taggedSession = safeString(managedContext.taggedTmuxSessionName).trim();
410
+ if (taggedSession && paneSessionName === taggedSession) {
411
+ return {
412
+ ok: true,
413
+ reason: 'ok',
414
+ paneTarget,
415
+ paneSessionName,
416
+ paneInstanceId: paneInstanceId || sessionInstanceId,
417
+ paneInstanceWarning: paneInstanceId ? '' : 'missing_pane_instance_tag_session_fallback',
418
+ managedContext,
419
+ };
420
+ }
242
421
  return { ok: false, reason: 'pane_not_managed_session', paneTarget, paneSessionName, managedContext };
243
422
  }
244
- return { ok: true, reason: 'ok', paneTarget, paneSessionName, managedContext };
423
+ return {
424
+ ok: true,
425
+ reason: 'ok',
426
+ paneTarget,
427
+ paneSessionName,
428
+ paneInstanceId: paneInstanceId || sessionInstanceId,
429
+ paneInstanceWarning: paneInstanceId ? '' : 'missing_pane_instance_tag_session_fallback',
430
+ managedContext,
431
+ };
245
432
  } catch {
246
433
  return { ok: false, reason: 'pane_session_lookup_failed', paneTarget, managedContext };
247
434
  }
@@ -334,7 +521,7 @@ export async function resolveManagedCurrentPane(cwd: string, payload: any, { all
334
521
  export async function resolveManagedSessionPane(cwd: string, payload: any): Promise<string> {
335
522
  const managedContext = await resolveManagedSessionContext(cwd, payload, { allowTeamWorker: false });
336
523
  if (!managedContext.managed) return '';
337
- const expectedSession = safeString(managedContext.expectedTmuxSessionName).trim();
524
+ const expectedSession = safeString(managedContext.taggedTmuxSessionName || managedContext.expectedTmuxSessionName).trim();
338
525
  if (!expectedSession) return '';
339
526
 
340
527
  try {
@@ -6,7 +6,11 @@ import { spawn } from 'child_process';
6
6
 
7
7
  export function runProcess(command: string, args: string[], timeoutMs = 3000): Promise<{ stdout: string; stderr: string; code: number | null }> {
8
8
  return new Promise((resolve, reject) => {
9
- const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] });
9
+ const usingTestTmux = command === 'tmux' && process.env.OMX_TEST_TMUX_BIN;
10
+ const relaxingTestTmuxTimeout = command === 'tmux' && process.env.OMX_TEST_RELAX_TMUX_TIMEOUT === '1';
11
+ const executable = usingTestTmux ? process.env.OMX_TEST_TMUX_BIN as string : command;
12
+ const effectiveTimeoutMs = usingTestTmux || relaxingTestTmuxTimeout ? Math.max(timeoutMs, 10_000) : timeoutMs;
13
+ const child = spawn(executable, args, { stdio: ['ignore', 'pipe', 'pipe'] });
10
14
  let stdout = '';
11
15
  let stderr = '';
12
16
  let finished = false;
@@ -15,8 +19,8 @@ export function runProcess(command: string, args: string[], timeoutMs = 3000): P
15
19
  if (finished) return;
16
20
  finished = true;
17
21
  child.kill('SIGTERM');
18
- reject(new Error(`timeout after ${timeoutMs}ms`));
19
- }, timeoutMs);
22
+ reject(new Error(`timeout after ${effectiveTimeoutMs}ms`));
23
+ }, effectiveTimeoutMs);
20
24
 
21
25
  child.stdout.on('data', (chunk: Buffer) => {
22
26
  stdout += chunk.toString();