oh-my-codex 0.16.2 → 0.16.4

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 (340) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +3 -3
  4. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +9 -0
  5. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
  6. package/dist/cli/__tests__/cleanup.test.js +27 -0
  7. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  8. package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -5
  9. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  10. package/dist/cli/__tests__/doctor-warning-copy.test.js +137 -6
  11. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  12. package/dist/cli/__tests__/index.test.js +303 -4
  13. package/dist/cli/__tests__/index.test.js.map +1 -1
  14. package/dist/cli/__tests__/launch-fallback.test.js +58 -0
  15. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  16. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +2 -0
  17. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  18. package/dist/cli/__tests__/ralph.test.js +48 -0
  19. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  20. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +8 -0
  21. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  22. package/dist/cli/__tests__/setup-install-mode.test.js +350 -27
  23. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  24. package/dist/cli/__tests__/setup-refresh.test.js +85 -3
  25. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  26. package/dist/cli/__tests__/setup-scope.test.js +1 -1
  27. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  28. package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -1
  29. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  30. package/dist/cli/__tests__/team.test.js +269 -0
  31. package/dist/cli/__tests__/team.test.js.map +1 -1
  32. package/dist/cli/__tests__/ultragoal.test.js +69 -0
  33. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  34. package/dist/cli/__tests__/uninstall.test.js +90 -6
  35. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  36. package/dist/cli/__tests__/update.test.js +109 -19
  37. package/dist/cli/__tests__/update.test.js.map +1 -1
  38. package/dist/cli/cleanup.d.ts.map +1 -1
  39. package/dist/cli/cleanup.js +8 -4
  40. package/dist/cli/cleanup.js.map +1 -1
  41. package/dist/cli/codex-feature-probe.d.ts +9 -0
  42. package/dist/cli/codex-feature-probe.d.ts.map +1 -0
  43. package/dist/cli/codex-feature-probe.js +28 -0
  44. package/dist/cli/codex-feature-probe.js.map +1 -0
  45. package/dist/cli/doctor.d.ts +1 -0
  46. package/dist/cli/doctor.d.ts.map +1 -1
  47. package/dist/cli/doctor.js +168 -16
  48. package/dist/cli/doctor.js.map +1 -1
  49. package/dist/cli/index.d.ts +9 -2
  50. package/dist/cli/index.d.ts.map +1 -1
  51. package/dist/cli/index.js +168 -20
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/cli/mcp-parity.js +8 -8
  54. package/dist/cli/mcp-parity.js.map +1 -1
  55. package/dist/cli/plugin-marketplace.d.ts +3 -0
  56. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  57. package/dist/cli/plugin-marketplace.js +88 -0
  58. package/dist/cli/plugin-marketplace.js.map +1 -1
  59. package/dist/cli/ralph.d.ts.map +1 -1
  60. package/dist/cli/ralph.js +21 -0
  61. package/dist/cli/ralph.js.map +1 -1
  62. package/dist/cli/setup-preferences.d.ts +4 -0
  63. package/dist/cli/setup-preferences.d.ts.map +1 -1
  64. package/dist/cli/setup-preferences.js +7 -0
  65. package/dist/cli/setup-preferences.js.map +1 -1
  66. package/dist/cli/setup.d.ts +5 -3
  67. package/dist/cli/setup.d.ts.map +1 -1
  68. package/dist/cli/setup.js +177 -43
  69. package/dist/cli/setup.js.map +1 -1
  70. package/dist/cli/team.d.ts.map +1 -1
  71. package/dist/cli/team.js +54 -15
  72. package/dist/cli/team.js.map +1 -1
  73. package/dist/cli/ultragoal.d.ts +1 -1
  74. package/dist/cli/ultragoal.d.ts.map +1 -1
  75. package/dist/cli/ultragoal.js +64 -5
  76. package/dist/cli/ultragoal.js.map +1 -1
  77. package/dist/cli/uninstall.d.ts +2 -0
  78. package/dist/cli/uninstall.d.ts.map +1 -1
  79. package/dist/cli/uninstall.js +76 -5
  80. package/dist/cli/uninstall.js.map +1 -1
  81. package/dist/cli/update.d.ts +10 -2
  82. package/dist/cli/update.d.ts.map +1 -1
  83. package/dist/cli/update.js +99 -5
  84. package/dist/cli/update.js.map +1 -1
  85. package/dist/config/__tests__/codex-feature-flags.test.d.ts +2 -0
  86. package/dist/config/__tests__/codex-feature-flags.test.d.ts.map +1 -0
  87. package/dist/config/__tests__/codex-feature-flags.test.js +35 -0
  88. package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -0
  89. package/dist/config/__tests__/codex-hooks.test.js +188 -4
  90. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  91. package/dist/config/__tests__/generator-idempotent.test.js +129 -10
  92. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  93. package/dist/config/__tests__/generator-notify.test.js +148 -7
  94. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  95. package/dist/config/__tests__/wiki-config-contract.test.js +6 -3
  96. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -1
  97. package/dist/config/codex-feature-flags.d.ts +21 -0
  98. package/dist/config/codex-feature-flags.d.ts.map +1 -0
  99. package/dist/config/codex-feature-flags.js +56 -0
  100. package/dist/config/codex-feature-flags.js.map +1 -0
  101. package/dist/config/codex-hooks.d.ts +40 -4
  102. package/dist/config/codex-hooks.d.ts.map +1 -1
  103. package/dist/config/codex-hooks.js +204 -18
  104. package/dist/config/codex-hooks.js.map +1 -1
  105. package/dist/config/generator.d.ts +19 -1
  106. package/dist/config/generator.d.ts.map +1 -1
  107. package/dist/config/generator.js +319 -83
  108. package/dist/config/generator.js.map +1 -1
  109. package/dist/config/omx-first-party-mcp.d.ts +3 -1
  110. package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
  111. package/dist/config/omx-first-party-mcp.js +2 -2
  112. package/dist/config/omx-first-party-mcp.js.map +1 -1
  113. package/dist/hooks/__tests__/keyword-detector.test.js +92 -2
  114. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  115. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +29 -1
  116. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  117. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +10 -0
  118. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  119. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +1 -0
  120. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  121. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.d.ts +2 -0
  122. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.d.ts.map +1 -0
  123. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +176 -0
  124. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -0
  125. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +148 -0
  126. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  127. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +3 -0
  128. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  129. package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts +2 -0
  130. package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts.map +1 -0
  131. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js +84 -0
  132. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -0
  133. package/dist/hooks/__tests__/wiki-docs-contract.test.js +1 -2
  134. package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -1
  135. package/dist/hooks/agents-overlay.js +2 -2
  136. package/dist/hooks/agents-overlay.js.map +1 -1
  137. package/dist/hooks/keyword-detector.d.ts +1 -0
  138. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  139. package/dist/hooks/keyword-detector.js +7 -5
  140. package/dist/hooks/keyword-detector.js.map +1 -1
  141. package/dist/hud/__tests__/state.test.js +164 -0
  142. package/dist/hud/__tests__/state.test.js.map +1 -1
  143. package/dist/hud/state.d.ts.map +1 -1
  144. package/dist/hud/state.js +4 -5
  145. package/dist/hud/state.js.map +1 -1
  146. package/dist/mcp/__tests__/state-paths.test.js +61 -0
  147. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  148. package/dist/mcp/__tests__/state-server.test.js +166 -0
  149. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  150. package/dist/mcp/state-paths.d.ts.map +1 -1
  151. package/dist/mcp/state-paths.js +23 -2
  152. package/dist/mcp/state-paths.js.map +1 -1
  153. package/dist/modes/__tests__/base-session-scope.test.js +22 -0
  154. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  155. package/dist/modes/__tests__/base-tmux-pane.test.js +57 -26
  156. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  157. package/dist/modes/base.d.ts.map +1 -1
  158. package/dist/modes/base.js +5 -0
  159. package/dist/modes/base.js.map +1 -1
  160. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts +2 -0
  161. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts.map +1 -0
  162. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js +316 -0
  163. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -0
  164. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts +2 -0
  165. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts.map +1 -0
  166. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js +481 -0
  167. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js.map +1 -0
  168. package/dist/planning/__tests__/artifacts.test.js +597 -4
  169. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  170. package/dist/planning/__tests__/context-pack-status.test.js +524 -0
  171. package/dist/planning/__tests__/context-pack-status.test.js.map +1 -1
  172. package/dist/planning/__tests__/markdown-structure.test.d.ts +2 -0
  173. package/dist/planning/__tests__/markdown-structure.test.d.ts.map +1 -0
  174. package/dist/planning/__tests__/markdown-structure.test.js +459 -0
  175. package/dist/planning/__tests__/markdown-structure.test.js.map +1 -0
  176. package/dist/planning/__tests__/ready-context-pack-role-refs.test.d.ts +2 -0
  177. package/dist/planning/__tests__/ready-context-pack-role-refs.test.d.ts.map +1 -0
  178. package/dist/planning/__tests__/ready-context-pack-role-refs.test.js +612 -0
  179. package/dist/planning/__tests__/ready-context-pack-role-refs.test.js.map +1 -0
  180. package/dist/planning/artifacts.d.ts +7 -2
  181. package/dist/planning/artifacts.d.ts.map +1 -1
  182. package/dist/planning/artifacts.js +279 -26
  183. package/dist/planning/artifacts.js.map +1 -1
  184. package/dist/planning/context-pack-status.d.ts +31 -0
  185. package/dist/planning/context-pack-status.d.ts.map +1 -1
  186. package/dist/planning/context-pack-status.js +291 -25
  187. package/dist/planning/context-pack-status.js.map +1 -1
  188. package/dist/planning/markdown-structure.d.ts +20 -0
  189. package/dist/planning/markdown-structure.d.ts.map +1 -0
  190. package/dist/planning/markdown-structure.js +137 -0
  191. package/dist/planning/markdown-structure.js.map +1 -0
  192. package/dist/ralph/__tests__/completion-audit.test.d.ts +2 -0
  193. package/dist/ralph/__tests__/completion-audit.test.d.ts.map +1 -0
  194. package/dist/ralph/__tests__/completion-audit.test.js +121 -0
  195. package/dist/ralph/__tests__/completion-audit.test.js.map +1 -0
  196. package/dist/ralph/completion-audit.d.ts +8 -0
  197. package/dist/ralph/completion-audit.d.ts.map +1 -0
  198. package/dist/ralph/completion-audit.js +99 -0
  199. package/dist/ralph/completion-audit.js.map +1 -0
  200. package/dist/ralph/persistence.d.ts +1 -1
  201. package/dist/ralph/persistence.d.ts.map +1 -1
  202. package/dist/ralph/persistence.js +8 -2
  203. package/dist/ralph/persistence.js.map +1 -1
  204. package/dist/scripts/__tests__/codex-native-hook.test.js +359 -24
  205. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  206. package/dist/scripts/__tests__/notify-dispatcher.test.d.ts +2 -0
  207. package/dist/scripts/__tests__/notify-dispatcher.test.d.ts.map +1 -0
  208. package/dist/scripts/__tests__/notify-dispatcher.test.js +126 -0
  209. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -0
  210. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  211. package/dist/scripts/codex-native-hook.js +142 -76
  212. package/dist/scripts/codex-native-hook.js.map +1 -1
  213. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  214. package/dist/scripts/codex-native-pre-post.js +4 -2
  215. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  216. package/dist/scripts/notify-dispatcher.d.ts +7 -0
  217. package/dist/scripts/notify-dispatcher.d.ts.map +1 -0
  218. package/dist/scripts/notify-dispatcher.js +87 -0
  219. package/dist/scripts/notify-dispatcher.js.map +1 -0
  220. package/dist/scripts/notify-fallback-watcher.js +4 -0
  221. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  222. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  223. package/dist/scripts/notify-hook/ralph-session-resume.js +96 -8
  224. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  225. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  226. package/dist/scripts/notify-hook/state-io.js +6 -2
  227. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  228. package/dist/scripts/notify-hook/visual-verdict.js +3 -3
  229. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  230. package/dist/scripts/notify-hook.js +127 -1
  231. package/dist/scripts/notify-hook.js.map +1 -1
  232. package/dist/state/__tests__/workflow-transition.test.js +102 -27
  233. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  234. package/dist/state/operations.d.ts.map +1 -1
  235. package/dist/state/operations.js +9 -3
  236. package/dist/state/operations.js.map +1 -1
  237. package/dist/state/skill-active.d.ts +7 -0
  238. package/dist/state/skill-active.d.ts.map +1 -1
  239. package/dist/state/skill-active.js +25 -8
  240. package/dist/state/skill-active.js.map +1 -1
  241. package/dist/state/workflow-transition-reconcile.d.ts +1 -0
  242. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  243. package/dist/state/workflow-transition-reconcile.js +22 -15
  244. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  245. package/dist/state/workflow-transition.js +3 -3
  246. package/dist/state/workflow-transition.js.map +1 -1
  247. package/dist/team/__tests__/approved-execution.test.js +84 -1
  248. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  249. package/dist/team/__tests__/runtime.test.js +178 -19
  250. package/dist/team/__tests__/runtime.test.js.map +1 -1
  251. package/dist/team/__tests__/scaling.test.js +497 -2
  252. package/dist/team/__tests__/scaling.test.js.map +1 -1
  253. package/dist/team/__tests__/state-root.test.js +1 -1
  254. package/dist/team/__tests__/state-root.test.js.map +1 -1
  255. package/dist/team/__tests__/worker-bootstrap.test.js +45 -0
  256. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  257. package/dist/team/approved-execution.d.ts +1 -0
  258. package/dist/team/approved-execution.d.ts.map +1 -1
  259. package/dist/team/approved-execution.js +53 -0
  260. package/dist/team/approved-execution.js.map +1 -1
  261. package/dist/team/delivery-log.d.ts.map +1 -1
  262. package/dist/team/delivery-log.js +8 -1
  263. package/dist/team/delivery-log.js.map +1 -1
  264. package/dist/team/runtime.d.ts.map +1 -1
  265. package/dist/team/runtime.js +104 -18
  266. package/dist/team/runtime.js.map +1 -1
  267. package/dist/team/scaling.d.ts.map +1 -1
  268. package/dist/team/scaling.js +43 -0
  269. package/dist/team/scaling.js.map +1 -1
  270. package/dist/team/state/mailbox.d.ts +1 -0
  271. package/dist/team/state/mailbox.d.ts.map +1 -1
  272. package/dist/team/state/mailbox.js +10 -1
  273. package/dist/team/state/mailbox.js.map +1 -1
  274. package/dist/team/state-root.d.ts.map +1 -1
  275. package/dist/team/state-root.js +5 -1
  276. package/dist/team/state-root.js.map +1 -1
  277. package/dist/team/state.d.ts.map +1 -1
  278. package/dist/team/state.js +3 -7
  279. package/dist/team/state.js.map +1 -1
  280. package/dist/team/worker-bootstrap.d.ts +7 -2
  281. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  282. package/dist/team/worker-bootstrap.js +17 -4
  283. package/dist/team/worker-bootstrap.js.map +1 -1
  284. package/dist/ultragoal/__tests__/artifacts.test.js +124 -1
  285. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  286. package/dist/ultragoal/__tests__/docs-contract.test.js +21 -0
  287. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  288. package/dist/ultragoal/artifacts.d.ts +44 -2
  289. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  290. package/dist/ultragoal/artifacts.js +197 -13
  291. package/dist/ultragoal/artifacts.js.map +1 -1
  292. package/dist/wiki/lifecycle.js +1 -1
  293. package/dist/wiki/lifecycle.js.map +1 -1
  294. package/package.json +1 -1
  295. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  296. package/plugins/oh-my-codex/.mcp.json +5 -5
  297. package/plugins/oh-my-codex/skills/analyze/SKILL.md +0 -2
  298. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
  299. package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -3
  300. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +5 -7
  301. package/plugins/oh-my-codex/skills/doctor/SKILL.md +2 -2
  302. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +3 -3
  303. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +3 -3
  304. package/plugins/oh-my-codex/skills/plan/SKILL.md +3 -6
  305. package/plugins/oh-my-codex/skills/ralph/SKILL.md +9 -10
  306. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +36 -3
  307. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +21 -24
  308. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +8 -8
  309. package/plugins/oh-my-codex/skills/wiki/SKILL.md +13 -13
  310. package/skills/analyze/SKILL.md +0 -2
  311. package/skills/ask-claude/SKILL.md +5 -3
  312. package/skills/ask-gemini/SKILL.md +5 -3
  313. package/skills/autopilot/SKILL.md +2 -2
  314. package/skills/code-review/SKILL.md +1 -3
  315. package/skills/deep-interview/SKILL.md +5 -7
  316. package/skills/doctor/SKILL.md +2 -2
  317. package/skills/ecomode/SKILL.md +105 -1
  318. package/skills/frontend-ui-ux/SKILL.md +4 -26
  319. package/skills/git-master/SKILL.md +2 -4
  320. package/skills/omx-setup/SKILL.md +3 -3
  321. package/skills/pipeline/SKILL.md +3 -3
  322. package/skills/plan/SKILL.md +3 -6
  323. package/skills/ralph/SKILL.md +9 -10
  324. package/skills/swarm/SKILL.md +5 -3
  325. package/skills/tdd/SKILL.md +95 -1
  326. package/skills/ultragoal/SKILL.md +36 -3
  327. package/skills/ultraqa/SKILL.md +21 -24
  328. package/skills/ultrawork/SKILL.md +8 -8
  329. package/skills/web-clone/SKILL.md +348 -1
  330. package/skills/wiki/SKILL.md +13 -13
  331. package/src/scripts/__tests__/codex-native-hook.test.ts +389 -24
  332. package/src/scripts/__tests__/notify-dispatcher.test.ts +153 -0
  333. package/src/scripts/codex-native-hook.ts +168 -64
  334. package/src/scripts/codex-native-pre-post.ts +4 -1
  335. package/src/scripts/notify-dispatcher.ts +113 -0
  336. package/src/scripts/notify-fallback-watcher.ts +6 -2
  337. package/src/scripts/notify-hook/ralph-session-resume.ts +117 -8
  338. package/src/scripts/notify-hook/state-io.ts +4 -2
  339. package/src/scripts/notify-hook/visual-verdict.ts +3 -3
  340. package/src/scripts/notify-hook.ts +119 -1
@@ -14,6 +14,7 @@ import { writeSessionStart } from "../../hooks/session.js";
14
14
  import { resetTriageConfigCache } from "../../hooks/triage-config.js";
15
15
  import { executeStateOperation } from "../../state/operations.js";
16
16
  import { OMX_TMUX_HUD_OWNER_ENV } from "../../hud/reconcile.js";
17
+ import { readAllState } from "../../hud/state.js";
17
18
  import { writePage } from "../../wiki/storage.js";
18
19
  import { WIKI_SCHEMA_VERSION } from "../../wiki/types.js";
19
20
  function nativeHookScriptPath() {
@@ -183,7 +184,7 @@ describe("codex native hook config", () => {
183
184
  "Stop",
184
185
  ]);
185
186
  const sessionStart = config.hooks.SessionStart[0];
186
- assert.equal(sessionStart.matcher, "startup|resume");
187
+ assert.equal(sessionStart.matcher, "startup|resume|clear");
187
188
  assert.equal(sessionStart.hooks?.[0]?.statusMessage, undefined);
188
189
  const preToolUse = config.hooks.PreToolUse[0];
189
190
  assert.equal(preToolUse.matcher, "Bash");
@@ -199,6 +200,10 @@ describe("codex native hook config", () => {
199
200
  assert.equal(userPromptSubmit.hooks?.[0]?.statusMessage, undefined);
200
201
  const stop = config.hooks.Stop[0];
201
202
  assert.equal(stop.hooks?.[0]?.timeout, 30);
203
+ const postCompact = config.hooks.PostCompact[0];
204
+ assert.equal(postCompact.matcher, undefined);
205
+ assert.match(String(postCompact.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/);
206
+ assert.doesNotMatch(String(postCompact.hooks?.[0]?.command || ""), /PostCompact Nudge|additionalContext|printf/);
202
207
  });
203
208
  });
204
209
  describe("codex native hook dispatch", () => {
@@ -400,7 +405,7 @@ describe("codex native hook dispatch", () => {
400
405
  assert.equal(mapCodexHookEventToOmxEvent("PostCompact"), "post-compact");
401
406
  assert.equal(mapCodexHookEventToOmxEvent("Stop"), "stop");
402
407
  });
403
- it("returns bounded wiki context for PreCompact", async () => {
408
+ it("does not write PreCompact stdout that Codex rejects as hook JSON", async () => {
404
409
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-precompact-"));
405
410
  try {
406
411
  writePage(cwd, {
@@ -425,16 +430,42 @@ describe("codex native hook dispatch", () => {
425
430
  });
426
431
  assert.equal(result.hookEventName, "PreCompact");
427
432
  assert.equal(result.omxEventName, "pre-compact");
428
- const additionalContext = result.outputJson
429
- ?.hookSpecificOutput?.additionalContext ?? "";
430
- assert.match(additionalContext, /Wiki: 1 pages/);
431
- assert.match(additionalContext, /architecture/);
433
+ assert.equal(result.outputJson, null);
434
+ }
435
+ finally {
436
+ await rm(cwd, { recursive: true, force: true });
437
+ }
438
+ });
439
+ it("emits no CLI stdout for PreCompact when no Codex action is needed", async () => {
440
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-precompact-cli-"));
441
+ try {
442
+ writePage(cwd, {
443
+ filename: "architecture.md",
444
+ frontmatter: {
445
+ title: "Architecture",
446
+ tags: ["architecture"],
447
+ created: "2026-05-08T00:00:00.000Z",
448
+ updated: "2026-05-08T00:00:00.000Z",
449
+ sources: [],
450
+ links: [],
451
+ category: "architecture",
452
+ confidence: "high",
453
+ schemaVersion: WIKI_SCHEMA_VERSION,
454
+ },
455
+ content: "\n# Architecture\n\nCompaction-relevant architecture note.\n",
456
+ });
457
+ const stdout = runNativeHookCli({
458
+ hook_event_name: "PreCompact",
459
+ cwd,
460
+ session_id: "sess-precompact-cli",
461
+ });
462
+ assert.equal(stdout, "");
432
463
  }
433
464
  finally {
434
465
  await rm(cwd, { recursive: true, force: true });
435
466
  }
436
467
  });
437
- it("returns a PostCompact nudge to write compaction artifacts to omx_wiki", async () => {
468
+ it("does not write PostCompact stdout that Codex rejects as hook JSON", async () => {
438
469
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-postcompact-"));
439
470
  try {
440
471
  const result = await dispatchCodexNativeHook({
@@ -444,11 +475,21 @@ describe("codex native hook dispatch", () => {
444
475
  });
445
476
  assert.equal(result.hookEventName, "PostCompact");
446
477
  assert.equal(result.omxEventName, "post-compact");
447
- const additionalContext = result.outputJson
448
- ?.hookSpecificOutput?.additionalContext ?? "";
449
- assert.match(additionalContext, /PostCompact Nudge/);
450
- assert.match(additionalContext, /omx_wiki/);
451
- assert.match(additionalContext, /compaction artifacts/);
478
+ assert.equal(result.outputJson, null);
479
+ }
480
+ finally {
481
+ await rm(cwd, { recursive: true, force: true });
482
+ }
483
+ });
484
+ it("emits no CLI stdout for PostCompact when no Codex action is needed", async () => {
485
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-postcompact-cli-"));
486
+ try {
487
+ const stdout = runNativeHookCli({
488
+ hook_event_name: "PostCompact",
489
+ cwd,
490
+ session_id: "sess-postcompact-cli",
491
+ });
492
+ assert.equal(stdout, "");
452
493
  }
453
494
  finally {
454
495
  await rm(cwd, { recursive: true, force: true });
@@ -922,7 +963,7 @@ describe("codex native hook dispatch", () => {
922
963
  assert.equal(result.omxEventName, "keyword-detector");
923
964
  assert.equal(result.skillState?.skill, "ralplan");
924
965
  assert.ok(result.outputJson, "UserPromptSubmit should emit developer context");
925
- assert.match(JSON.stringify(result.outputJson), /skill: ralplan activated and initial state initialized at \.omx\/state\/sessions\/sess-1\/ralplan-state\.json; write subsequent updates via omx_state MCP\./);
966
+ assert.match(JSON.stringify(result.outputJson), /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
926
967
  assert.equal(existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, "session-scoped keyword activation should not write root skill-active-state.json");
927
968
  const statePath = join(cwd, ".omx", "state", "sessions", "sess-1", "skill-active-state.json");
928
969
  assert.equal(existsSync(statePath), true);
@@ -936,6 +977,114 @@ describe("codex native hook dispatch", () => {
936
977
  await rm(cwd, { recursive: true, force: true });
937
978
  }
938
979
  });
980
+ it("records boxed keyword activation mode detail and skill state under OMX_ROOT", async () => {
981
+ const root = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-"));
982
+ const cwd = join(root, "source");
983
+ const omxRoot = join(root, "box");
984
+ const sessionId = "sess-boxed-ralplan";
985
+ const previousOmxRoot = process.env.OMX_ROOT;
986
+ const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
987
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
988
+ const previousOmxSessionId = process.env.OMX_SESSION_ID;
989
+ try {
990
+ await mkdir(cwd, { recursive: true });
991
+ process.env.OMX_ROOT = omxRoot;
992
+ delete process.env.OMX_STATE_ROOT;
993
+ delete process.env.OMX_TEAM_STATE_ROOT;
994
+ process.env.OMX_SESSION_ID = sessionId;
995
+ const result = await dispatchCodexNativeHook({
996
+ hook_event_name: "UserPromptSubmit",
997
+ cwd,
998
+ session_id: sessionId,
999
+ thread_id: "thread-boxed",
1000
+ turn_id: "turn-boxed",
1001
+ prompt: "$ralplan implement issue #1307",
1002
+ }, { cwd });
1003
+ assert.equal(result.omxEventName, "keyword-detector");
1004
+ assert.equal(result.skillState?.skill, "ralplan");
1005
+ const boxedSessionDir = join(omxRoot, ".omx", "state", "sessions", sessionId);
1006
+ assert.equal(existsSync(join(boxedSessionDir, "skill-active-state.json")), true);
1007
+ assert.equal(existsSync(join(boxedSessionDir, "ralplan-state.json")), true);
1008
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json")), false);
1009
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json")), false);
1010
+ const hudState = await readAllState(cwd);
1011
+ assert.equal(hudState.ralplan?.active, true);
1012
+ assert.equal(hudState.ralplan?.current_phase, "planning");
1013
+ }
1014
+ finally {
1015
+ if (typeof previousOmxRoot === "string")
1016
+ process.env.OMX_ROOT = previousOmxRoot;
1017
+ else
1018
+ delete process.env.OMX_ROOT;
1019
+ if (typeof previousOmxStateRoot === "string")
1020
+ process.env.OMX_STATE_ROOT = previousOmxStateRoot;
1021
+ else
1022
+ delete process.env.OMX_STATE_ROOT;
1023
+ if (typeof previousTeamStateRoot === "string")
1024
+ process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
1025
+ else
1026
+ delete process.env.OMX_TEAM_STATE_ROOT;
1027
+ if (typeof previousOmxSessionId === "string")
1028
+ process.env.OMX_SESSION_ID = previousOmxSessionId;
1029
+ else
1030
+ delete process.env.OMX_SESSION_ID;
1031
+ await rm(root, { recursive: true, force: true });
1032
+ }
1033
+ });
1034
+ it("records native keyword activation mode detail and skill state under OMX_TEAM_STATE_ROOT", async () => {
1035
+ const root = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-"));
1036
+ const cwd = join(root, "source");
1037
+ const teamStateRoot = join(root, "team-state");
1038
+ const sessionId = "sess-team-root-ralplan";
1039
+ const previousOmxRoot = process.env.OMX_ROOT;
1040
+ const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
1041
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
1042
+ const previousOmxSessionId = process.env.OMX_SESSION_ID;
1043
+ try {
1044
+ await mkdir(cwd, { recursive: true });
1045
+ delete process.env.OMX_ROOT;
1046
+ delete process.env.OMX_STATE_ROOT;
1047
+ process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
1048
+ process.env.OMX_SESSION_ID = sessionId;
1049
+ const result = await dispatchCodexNativeHook({
1050
+ hook_event_name: "UserPromptSubmit",
1051
+ cwd,
1052
+ session_id: sessionId,
1053
+ thread_id: "thread-team-root",
1054
+ turn_id: "turn-team-root",
1055
+ prompt: "$ralplan implement issue #1307",
1056
+ }, { cwd });
1057
+ assert.equal(result.omxEventName, "keyword-detector");
1058
+ assert.equal(result.skillState?.skill, "ralplan");
1059
+ const teamSessionDir = join(teamStateRoot, "sessions", sessionId);
1060
+ assert.equal(existsSync(join(teamSessionDir, "skill-active-state.json")), true);
1061
+ assert.equal(existsSync(join(teamSessionDir, "ralplan-state.json")), true);
1062
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json")), false);
1063
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json")), false);
1064
+ const hudState = await readAllState(cwd);
1065
+ assert.equal(hudState.ralplan?.active, true);
1066
+ assert.equal(hudState.ralplan?.current_phase, "planning");
1067
+ }
1068
+ finally {
1069
+ if (typeof previousOmxRoot === "string")
1070
+ process.env.OMX_ROOT = previousOmxRoot;
1071
+ else
1072
+ delete process.env.OMX_ROOT;
1073
+ if (typeof previousOmxStateRoot === "string")
1074
+ process.env.OMX_STATE_ROOT = previousOmxStateRoot;
1075
+ else
1076
+ delete process.env.OMX_STATE_ROOT;
1077
+ if (typeof previousTeamStateRoot === "string")
1078
+ process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
1079
+ else
1080
+ delete process.env.OMX_TEAM_STATE_ROOT;
1081
+ if (typeof previousOmxSessionId === "string")
1082
+ process.env.OMX_SESSION_ID = previousOmxSessionId;
1083
+ else
1084
+ delete process.env.OMX_SESSION_ID;
1085
+ await rm(root, { recursive: true, force: true });
1086
+ }
1087
+ });
939
1088
  it("warns completion-like prompts when active goal workflows need Codex snapshot reconciliation", async () => {
940
1089
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-goal-warning-"));
941
1090
  try {
@@ -985,6 +1134,94 @@ describe("codex native hook dispatch", () => {
985
1134
  await rm(cwd, { recursive: true, force: true });
986
1135
  }
987
1136
  });
1137
+ it("does not block Stop for non-passing autoresearch-goal professor-critic verdicts", async () => {
1138
+ for (const verdict of ["blocked", "fail", "failed"]) {
1139
+ const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-autoresearch-${verdict}-stop-`));
1140
+ const slug = `${verdict}-mission`;
1141
+ try {
1142
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", slug, "mission.json"), {
1143
+ version: 1,
1144
+ workflow: "autoresearch-goal",
1145
+ slug,
1146
+ topic: "Blocked research",
1147
+ status: verdict === "blocked" ? "blocked" : "failed",
1148
+ });
1149
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", slug, "completion.json"), {
1150
+ verdict,
1151
+ passed: false,
1152
+ });
1153
+ const result = await dispatchCodexNativeHook({
1154
+ hook_event_name: "Stop",
1155
+ cwd,
1156
+ session_id: `sess-autoresearch-${verdict}-stop`,
1157
+ thread_id: `thread-autoresearch-${verdict}-stop`,
1158
+ last_assistant_message: "Autoresearch goal complete; next call update_goal({status: \"complete\"}).",
1159
+ }, { cwd });
1160
+ assert.notEqual(result.outputJson?.decision, "block");
1161
+ assert.doesNotMatch(JSON.stringify(result.outputJson), new RegExp(`autoresearch-goal complete --slug ${slug}`));
1162
+ assert.doesNotMatch(JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/);
1163
+ }
1164
+ finally {
1165
+ await rm(cwd, { recursive: true, force: true });
1166
+ }
1167
+ }
1168
+ });
1169
+ it("blocks Stop for passing autoresearch-goal professor-critic verdicts that need reconciliation", async () => {
1170
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autoresearch-pass-stop-"));
1171
+ try {
1172
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", "passing-mission", "mission.json"), {
1173
+ version: 1,
1174
+ workflow: "autoresearch-goal",
1175
+ slug: "passing-mission",
1176
+ topic: "Passing research",
1177
+ status: "validation_passed",
1178
+ });
1179
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", "passing-mission", "completion.json"), {
1180
+ verdict: "fail",
1181
+ passed: true,
1182
+ });
1183
+ const result = await dispatchCodexNativeHook({
1184
+ hook_event_name: "Stop",
1185
+ cwd,
1186
+ session_id: "sess-autoresearch-pass-stop",
1187
+ thread_id: "thread-autoresearch-pass-stop",
1188
+ last_assistant_message: "Autoresearch goal complete; next call update_goal({status: \"complete\"}).",
1189
+ }, { cwd });
1190
+ assert.equal(result.outputJson?.decision, "block");
1191
+ assert.match(JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/);
1192
+ assert.match(JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug passing-mission/);
1193
+ }
1194
+ finally {
1195
+ await rm(cwd, { recursive: true, force: true });
1196
+ }
1197
+ });
1198
+ it("blocks Stop for autoresearch-goal verdict=pass even when passed is omitted", async () => {
1199
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autoresearch-verdict-pass-stop-"));
1200
+ try {
1201
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", "verdict-pass-mission", "mission.json"), {
1202
+ version: 1,
1203
+ workflow: "autoresearch-goal",
1204
+ slug: "verdict-pass-mission",
1205
+ topic: "Passing research",
1206
+ status: "validation_passed",
1207
+ });
1208
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", "verdict-pass-mission", "completion.json"), {
1209
+ verdict: "pass",
1210
+ });
1211
+ const result = await dispatchCodexNativeHook({
1212
+ hook_event_name: "Stop",
1213
+ cwd,
1214
+ session_id: "sess-autoresearch-verdict-pass-stop",
1215
+ thread_id: "thread-autoresearch-verdict-pass-stop",
1216
+ last_assistant_message: "Autoresearch goal complete; next call update_goal({status: \"complete\"}).",
1217
+ }, { cwd });
1218
+ assert.equal(result.outputJson?.decision, "block");
1219
+ assert.match(JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug verdict-pass-mission/);
1220
+ }
1221
+ finally {
1222
+ await rm(cwd, { recursive: true, force: true });
1223
+ }
1224
+ });
988
1225
  it("treats workflow keywords in native subagent prompt text as literal delegation text", async () => {
989
1226
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-keyword-literal-"));
990
1227
  try {
@@ -1060,7 +1297,7 @@ describe("codex native hook dispatch", () => {
1060
1297
  assert.equal(result.skillState?.skill, "ralplan");
1061
1298
  const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
1062
1299
  assert.match(message, /\$oh-my-codex:ralplan" -> ralplan/);
1063
- assert.match(message, /skill: ralplan activated and initial state initialized at \.omx\/state\/sessions\/sess-plugin-1\/ralplan-state\.json; write subsequent updates via omx_state MCP\./);
1300
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1064
1301
  assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-plugin-1", "ralplan-state.json")), true);
1065
1302
  }
1066
1303
  finally {
@@ -1234,7 +1471,7 @@ describe("codex native hook dispatch", () => {
1234
1471
  assert.equal(result.skillState?.skill, "ralph");
1235
1472
  const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
1236
1473
  assert.match(message, /\$ralph" -> ralph/);
1237
- assert.match(message, /skill: ralph activated and initial state initialized at \.omx\/state\/sessions\/sess-ralph-msg\/ralph-state\.json; write subsequent updates via omx_state MCP\./);
1474
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1238
1475
  assert.match(message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./);
1239
1476
  assert.match(message, /Use `omx ralph --prd \.\.\.` only when you explicitly want the PRD-gated CLI startup path\./);
1240
1477
  }
@@ -1258,7 +1495,7 @@ describe("codex native hook dispatch", () => {
1258
1495
  assert.equal(result.skillState?.skill, "ralph");
1259
1496
  const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
1260
1497
  assert.match(message, /\$oh-my-codex:ralph" -> ralph/);
1261
- assert.match(message, /skill: ralph activated and initial state initialized at \.omx\/state\/sessions\/sess-plugin-ralph-msg\/ralph-state\.json; write subsequent updates via omx_state MCP\./);
1498
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1262
1499
  assert.match(message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./);
1263
1500
  }
1264
1501
  finally {
@@ -1327,7 +1564,7 @@ describe("codex native hook dispatch", () => {
1327
1564
  assert.equal(result.skillState?.skill, "deep-interview");
1328
1565
  const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
1329
1566
  assert.match(message, /\$deep-interview" -> deep-interview/);
1330
- assert.match(message, /skill: deep-interview activated and initial state initialized at \.omx\/state\/sessions\/sess-deep-interview-msg\/deep-interview-state\.json; write subsequent updates via omx_state MCP\./);
1567
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1331
1568
  assert.match(message, /Deep-interview is active, but this session is not attached to tmux/);
1332
1569
  assert.match(message, /Do not invoke `omx question`, `omx hud`, or `omx team`/);
1333
1570
  assert.match(message, /native structured question tool when available/);
@@ -1717,8 +1954,11 @@ export async function onHookEvent(event) {
1717
1954
  }, { cwd });
1718
1955
  assert.match(JSON.stringify(denied.outputJson), /denied workflow keyword/i);
1719
1956
  assert.match(JSON.stringify(denied.outputJson), /Unsupported workflow overlap: team \+ autopilot\./);
1720
- assert.match(JSON.stringify(denied.outputJson), /`omx state clear --mode <mode>`/);
1721
- assert.match(JSON.stringify(denied.outputJson), /`omx_state\.\*` MCP tools/);
1957
+ assert.match(JSON.stringify(denied.outputJson), /omx state clear --input/);
1958
+ assert.match(JSON.stringify(denied.outputJson), /mode\\":\\"<mode>/);
1959
+ assert.match(JSON.stringify(denied.outputJson), /--json/);
1960
+ assert.match(JSON.stringify(denied.outputJson), /explicit MCP compatibility is enabled/);
1961
+ assert.match(JSON.stringify(denied.outputJson), /`omx_state\.\*` tools/);
1722
1962
  assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-deny-1", "autopilot-state.json")), false);
1723
1963
  }
1724
1964
  finally {
@@ -1777,7 +2017,7 @@ export async function onHookEvent(event) {
1777
2017
  assert.match(message, /\$ralph" -> ralph/);
1778
2018
  assert.doesNotMatch(message, /mode transiting:/);
1779
2019
  assert.match(message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./);
1780
- assert.match(message, /skill: ralplan activated and initial state initialized at \.omx\/state\/sessions\/sess-multi-1\/ralplan-state\.json; write subsequent updates via omx_state MCP\./);
2020
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1781
2021
  assert.doesNotMatch(message, /Use the durable OMX team runtime via `omx team \.\.\.`/);
1782
2022
  }
1783
2023
  finally {
@@ -1802,7 +2042,7 @@ export async function onHookEvent(event) {
1802
2042
  assert.match(message, /\$oh-my-codex:ralph" -> ralph/);
1803
2043
  assert.doesNotMatch(message, /mode transiting:/);
1804
2044
  assert.match(message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./);
1805
- assert.match(message, /skill: ralplan activated and initial state initialized at \.omx\/state\/sessions\/sess-plugin-multi-1\/ralplan-state\.json; write subsequent updates via omx_state MCP\./);
2045
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1806
2046
  }
1807
2047
  finally {
1808
2048
  await rm(cwd, { recursive: true, force: true });
@@ -3572,7 +3812,7 @@ exit 0
3572
3812
  assert.equal(output?.decision, "block");
3573
3813
  assert.equal(output?.reason, "The MCP tool appears to have lost its transport/server connection. Preserve state, debug the transport failure, and use OMX CLI/file-backed fallbacks instead of retrying blindly.");
3574
3814
  const additionalContext = String(output?.hookSpecificOutput?.additionalContext ?? "");
3575
- assert.match(additionalContext, /omx state state_write --input/);
3815
+ assert.match(additionalContext, /omx state write --input/);
3576
3816
  assert.match(additionalContext, /plain Node stdio processes/i);
3577
3817
  assert.match(additionalContext, /read-stall-state/);
3578
3818
  assert.match(additionalContext, /OMX_MCP_TRANSPORT_DEBUG=1/);
@@ -3742,7 +3982,7 @@ exit 0
3742
3982
  assert.match(String(result.outputJson?.reason || ""), /lost its transport\/server connection/);
3743
3983
  const hookSpecificOutput = result.outputJson?.hookSpecificOutput;
3744
3984
  assert.equal(hookSpecificOutput?.hookEventName, "PostToolUse");
3745
- assert.match(String(hookSpecificOutput?.additionalContext || ""), /Retry via CLI parity with `omx state state_write --input '\{\}' --json`\./);
3985
+ assert.match(String(hookSpecificOutput?.additionalContext || ""), /Retry via CLI parity with `omx state write --input '\{\}' --json`\./);
3746
3986
  assert.match(String(hookSpecificOutput?.additionalContext || ""), /omx team api read-stall-state/);
3747
3987
  const phase = JSON.parse(await readFile(join(cwd, ".omx", "state", "team", "mcp-transport-dead-team", "phase.json"), "utf-8"));
3748
3988
  assert.equal(phase.current_phase, "failed");
@@ -3800,7 +4040,7 @@ exit 0
3800
4040
  reason: "The MCP tool appears to have lost its transport/server connection. Preserve state, debug the transport failure, and use OMX CLI/file-backed fallbacks instead of retrying blindly.",
3801
4041
  hookSpecificOutput: {
3802
4042
  hookEventName: "PostToolUse",
3803
- additionalContext: "Clear MCP transport-death signal detected. Preserve current team/runtime state. Retry via CLI parity with `omx state state_write --input '{\"mode\":\"team\",\"active\":true}' --json`. OMX MCP servers are plain Node stdio processes, so they still shut down when stdin/transport closes. If this happened during team runtime, inspect first with `omx team status <team>` or `omx team api read-stall-state --input '{\"team_name\":\"<team>\"}' --json`, and only force cleanup after capturing needed state. For root-cause debugging, rerun with `OMX_MCP_TRANSPORT_DEBUG=1` to log why the stdio transport closed.",
4043
+ additionalContext: "Clear MCP transport-death signal detected. Preserve current team/runtime state. Retry via CLI parity with `omx state write --input '{\"mode\":\"team\",\"active\":true}' --json`. OMX MCP servers are plain Node stdio processes, so they still shut down when stdin/transport closes. If this happened during team runtime, inspect first with `omx team status <team>` or `omx team api read-stall-state --input '{\"team_name\":\"<team>\"}' --json`, and only force cleanup after capturing needed state. For root-cause debugging, rerun with `OMX_MCP_TRANSPORT_DEBUG=1` to log why the stdio transport closed.",
3804
4044
  },
3805
4045
  });
3806
4046
  const phase = await readTeamPhase("transport-team", cwd);
@@ -4965,6 +5205,38 @@ exit 0
4965
5205
  await rm(cwd, { recursive: true, force: true });
4966
5206
  }
4967
5207
  });
5208
+ it("ignores stale source-root team Stop fallback when OMX_TEAM_STATE_ROOT is authoritative", async () => {
5209
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-stale-source-root-"));
5210
+ const teamStateRoot = join(cwd, "shared-team-state");
5211
+ const priorTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
5212
+ try {
5213
+ process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
5214
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
5215
+ await mkdir(join(teamStateRoot, "team", "stale-source-team"), { recursive: true });
5216
+ await writeJson(join(cwd, ".omx", "state", "team-state.json"), {
5217
+ active: true,
5218
+ team_name: "stale-source-team",
5219
+ current_phase: "team-exec",
5220
+ });
5221
+ await writeJson(join(teamStateRoot, "team", "stale-source-team", "phase.json"), {
5222
+ current_phase: "team-exec",
5223
+ });
5224
+ const result = await dispatchCodexNativeHook({
5225
+ hook_event_name: "Stop",
5226
+ cwd,
5227
+ session_id: "sess-stale-source-team",
5228
+ }, { cwd });
5229
+ assert.equal(result.omxEventName, "stop");
5230
+ assert.equal(result.outputJson, null);
5231
+ }
5232
+ finally {
5233
+ if (typeof priorTeamStateRoot === "string")
5234
+ process.env.OMX_TEAM_STATE_ROOT = priorTeamStateRoot;
5235
+ else
5236
+ delete process.env.OMX_TEAM_STATE_ROOT;
5237
+ await rm(cwd, { recursive: true, force: true });
5238
+ }
5239
+ });
4968
5240
  it("returns Stop continuation output from canonical team state rooted via OMX_TEAM_STATE_ROOT", async () => {
4969
5241
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-env-root-"));
4970
5242
  const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
@@ -5856,6 +6128,69 @@ exit 0
5856
6128
  await rm(cwd, { recursive: true, force: true });
5857
6129
  }
5858
6130
  });
6131
+ it("blocks Codex App Stop when Ralph is marked complete without completion-audit evidence", async () => {
6132
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-missing-"));
6133
+ try {
6134
+ const sessionId = "sess-ralph-complete-missing";
6135
+ const statePath = join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json");
6136
+ await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd });
6137
+ await writeJson(statePath, {
6138
+ active: false,
6139
+ mode: "ralph",
6140
+ current_phase: "complete",
6141
+ session_id: sessionId,
6142
+ completed_at: "2026-05-10T12:00:00.000Z",
6143
+ });
6144
+ const result = await dispatchCodexNativeHook({
6145
+ hook_event_name: "Stop",
6146
+ cwd,
6147
+ session_id: sessionId,
6148
+ last_assistant_message: "Done. Ralph complete.",
6149
+ }, { cwd });
6150
+ assert.equal(result.omxEventName, "stop");
6151
+ assert.match(String(result.outputJson?.reason), /Ralph completion audit is missing required evidence/);
6152
+ assert.equal(result.outputJson?.stopReason, "ralph_completion_audit_missing_completion_audit");
6153
+ const reopened = JSON.parse(await readFile(statePath, "utf-8"));
6154
+ assert.equal(reopened.active, true);
6155
+ assert.equal(reopened.current_phase, "verifying");
6156
+ assert.equal(reopened.completion_audit_gate, "blocked");
6157
+ assert.equal(reopened.completion_audit_missing_reason, "missing_completion_audit");
6158
+ assert.equal(typeof reopened.completed_at, "undefined");
6159
+ }
6160
+ finally {
6161
+ await rm(cwd, { recursive: true, force: true });
6162
+ }
6163
+ });
6164
+ it("allows Codex App Stop when complete Ralph state carries checklist and verification evidence", async () => {
6165
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-present-"));
6166
+ try {
6167
+ const sessionId = "sess-ralph-complete-present";
6168
+ await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd });
6169
+ await writeJson(join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json"), {
6170
+ active: false,
6171
+ mode: "ralph",
6172
+ current_phase: "complete",
6173
+ session_id: sessionId,
6174
+ completed_at: "2026-05-10T12:00:00.000Z",
6175
+ completion_audit: {
6176
+ passed: true,
6177
+ prompt_to_artifact_checklist: ["issue #2260 fixed", "tests added"],
6178
+ verification_evidence: ["node --test dist/scripts/__tests__/codex-native-hook.test.js"],
6179
+ },
6180
+ });
6181
+ const result = await dispatchCodexNativeHook({
6182
+ hook_event_name: "Stop",
6183
+ cwd,
6184
+ session_id: sessionId,
6185
+ last_assistant_message: "Done with completion audit evidence recorded.",
6186
+ }, { cwd });
6187
+ assert.equal(result.omxEventName, "stop");
6188
+ assert.equal(result.outputJson, null);
6189
+ }
6190
+ finally {
6191
+ await rm(cwd, { recursive: true, force: true });
6192
+ }
6193
+ });
5859
6194
  it("returns Stop continuation output while Ralph is active without an explicit session pin", async () => {
5860
6195
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-"));
5861
6196
  try {