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
@@ -24,6 +24,7 @@ import { writeSessionStart } from "../../hooks/session.js";
24
24
  import { resetTriageConfigCache } from "../../hooks/triage-config.js";
25
25
  import { executeStateOperation } from "../../state/operations.js";
26
26
  import { OMX_TMUX_HUD_OWNER_ENV } from "../../hud/reconcile.js";
27
+ import { readAllState } from "../../hud/state.js";
27
28
  import { writePage } from "../../wiki/storage.js";
28
29
  import { WIKI_SCHEMA_VERSION } from "../../wiki/types.js";
29
30
 
@@ -236,7 +237,7 @@ describe("codex native hook config", () => {
236
237
  matcher?: string;
237
238
  hooks?: Array<Record<string, unknown>>;
238
239
  };
239
- assert.equal(sessionStart.matcher, "startup|resume");
240
+ assert.equal(sessionStart.matcher, "startup|resume|clear");
240
241
  assert.equal(sessionStart.hooks?.[0]?.statusMessage, undefined);
241
242
 
242
243
  const preToolUse = config.hooks.PreToolUse[0] as {
@@ -276,6 +277,20 @@ describe("codex native hook config", () => {
276
277
  hooks?: Array<Record<string, unknown>>;
277
278
  };
278
279
  assert.equal(stop.hooks?.[0]?.timeout, 30);
280
+
281
+ const postCompact = config.hooks.PostCompact[0] as {
282
+ matcher?: string;
283
+ hooks?: Array<Record<string, unknown>>;
284
+ };
285
+ assert.equal(postCompact.matcher, undefined);
286
+ assert.match(
287
+ String(postCompact.hooks?.[0]?.command || ""),
288
+ /codex-native-hook\.js"?$/,
289
+ );
290
+ assert.doesNotMatch(
291
+ String(postCompact.hooks?.[0]?.command || ""),
292
+ /PostCompact Nudge|additionalContext|printf/,
293
+ );
279
294
  });
280
295
  });
281
296
 
@@ -522,7 +537,7 @@ describe("codex native hook dispatch", () => {
522
537
 
523
538
 
524
539
 
525
- it("returns bounded wiki context for PreCompact", async () => {
540
+ it("does not write PreCompact stdout that Codex rejects as hook JSON", async () => {
526
541
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-precompact-"));
527
542
  try {
528
543
  writePage(cwd, {
@@ -549,16 +564,44 @@ describe("codex native hook dispatch", () => {
549
564
 
550
565
  assert.equal(result.hookEventName, "PreCompact");
551
566
  assert.equal(result.omxEventName, "pre-compact");
552
- const additionalContext = (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)
553
- ?.hookSpecificOutput?.additionalContext ?? "";
554
- assert.match(additionalContext, /Wiki: 1 pages/);
555
- assert.match(additionalContext, /architecture/);
567
+ assert.equal(result.outputJson, null);
556
568
  } finally {
557
569
  await rm(cwd, { recursive: true, force: true });
558
570
  }
559
571
  });
560
572
 
561
- it("returns a PostCompact nudge to write compaction artifacts to omx_wiki", async () => {
573
+ it("emits no CLI stdout for PreCompact when no Codex action is needed", async () => {
574
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-precompact-cli-"));
575
+ try {
576
+ writePage(cwd, {
577
+ filename: "architecture.md",
578
+ frontmatter: {
579
+ title: "Architecture",
580
+ tags: ["architecture"],
581
+ created: "2026-05-08T00:00:00.000Z",
582
+ updated: "2026-05-08T00:00:00.000Z",
583
+ sources: [],
584
+ links: [],
585
+ category: "architecture",
586
+ confidence: "high",
587
+ schemaVersion: WIKI_SCHEMA_VERSION,
588
+ },
589
+ content: "\n# Architecture\n\nCompaction-relevant architecture note.\n",
590
+ });
591
+
592
+ const stdout = runNativeHookCli({
593
+ hook_event_name: "PreCompact",
594
+ cwd,
595
+ session_id: "sess-precompact-cli",
596
+ });
597
+
598
+ assert.equal(stdout, "");
599
+ } finally {
600
+ await rm(cwd, { recursive: true, force: true });
601
+ }
602
+ });
603
+
604
+ it("does not write PostCompact stdout that Codex rejects as hook JSON", async () => {
562
605
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-postcompact-"));
563
606
  try {
564
607
  const result = await dispatchCodexNativeHook({
@@ -569,11 +612,22 @@ describe("codex native hook dispatch", () => {
569
612
 
570
613
  assert.equal(result.hookEventName, "PostCompact");
571
614
  assert.equal(result.omxEventName, "post-compact");
572
- const additionalContext = (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)
573
- ?.hookSpecificOutput?.additionalContext ?? "";
574
- assert.match(additionalContext, /PostCompact Nudge/);
575
- assert.match(additionalContext, /omx_wiki/);
576
- assert.match(additionalContext, /compaction artifacts/);
615
+ assert.equal(result.outputJson, null);
616
+ } finally {
617
+ await rm(cwd, { recursive: true, force: true });
618
+ }
619
+ });
620
+
621
+ it("emits no CLI stdout for PostCompact when no Codex action is needed", async () => {
622
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-postcompact-cli-"));
623
+ try {
624
+ const stdout = runNativeHookCli({
625
+ hook_event_name: "PostCompact",
626
+ cwd,
627
+ session_id: "sess-postcompact-cli",
628
+ });
629
+
630
+ assert.equal(stdout, "");
577
631
  } finally {
578
632
  await rm(cwd, { recursive: true, force: true });
579
633
  }
@@ -1164,7 +1218,7 @@ describe("codex native hook dispatch", () => {
1164
1218
  assert.equal(result.omxEventName, "keyword-detector");
1165
1219
  assert.equal(result.skillState?.skill, "ralplan");
1166
1220
  assert.ok(result.outputJson, "UserPromptSubmit should emit developer context");
1167
- 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\./);
1221
+ assert.match(JSON.stringify(result.outputJson), /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1168
1222
 
1169
1223
  assert.equal(
1170
1224
  existsSync(join(cwd, ".omx", "state", "skill-active-state.json")),
@@ -1187,6 +1241,112 @@ describe("codex native hook dispatch", () => {
1187
1241
  }
1188
1242
  });
1189
1243
 
1244
+ it("records boxed keyword activation mode detail and skill state under OMX_ROOT", async () => {
1245
+ const root = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-"));
1246
+ const cwd = join(root, "source");
1247
+ const omxRoot = join(root, "box");
1248
+ const sessionId = "sess-boxed-ralplan";
1249
+ const previousOmxRoot = process.env.OMX_ROOT;
1250
+ const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
1251
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
1252
+ const previousOmxSessionId = process.env.OMX_SESSION_ID;
1253
+ try {
1254
+ await mkdir(cwd, { recursive: true });
1255
+ process.env.OMX_ROOT = omxRoot;
1256
+ delete process.env.OMX_STATE_ROOT;
1257
+ delete process.env.OMX_TEAM_STATE_ROOT;
1258
+ process.env.OMX_SESSION_ID = sessionId;
1259
+
1260
+ const result = await dispatchCodexNativeHook(
1261
+ {
1262
+ hook_event_name: "UserPromptSubmit",
1263
+ cwd,
1264
+ session_id: sessionId,
1265
+ thread_id: "thread-boxed",
1266
+ turn_id: "turn-boxed",
1267
+ prompt: "$ralplan implement issue #1307",
1268
+ },
1269
+ { cwd },
1270
+ );
1271
+
1272
+ assert.equal(result.omxEventName, "keyword-detector");
1273
+ assert.equal(result.skillState?.skill, "ralplan");
1274
+
1275
+ const boxedSessionDir = join(omxRoot, ".omx", "state", "sessions", sessionId);
1276
+ assert.equal(existsSync(join(boxedSessionDir, "skill-active-state.json")), true);
1277
+ assert.equal(existsSync(join(boxedSessionDir, "ralplan-state.json")), true);
1278
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json")), false);
1279
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json")), false);
1280
+
1281
+ const hudState = await readAllState(cwd);
1282
+ assert.equal(hudState.ralplan?.active, true);
1283
+ assert.equal(hudState.ralplan?.current_phase, "planning");
1284
+ } finally {
1285
+ if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot;
1286
+ else delete process.env.OMX_ROOT;
1287
+ if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot;
1288
+ else delete process.env.OMX_STATE_ROOT;
1289
+ if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
1290
+ else delete process.env.OMX_TEAM_STATE_ROOT;
1291
+ if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId;
1292
+ else delete process.env.OMX_SESSION_ID;
1293
+ await rm(root, { recursive: true, force: true });
1294
+ }
1295
+ });
1296
+
1297
+ it("records native keyword activation mode detail and skill state under OMX_TEAM_STATE_ROOT", async () => {
1298
+ const root = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-"));
1299
+ const cwd = join(root, "source");
1300
+ const teamStateRoot = join(root, "team-state");
1301
+ const sessionId = "sess-team-root-ralplan";
1302
+ const previousOmxRoot = process.env.OMX_ROOT;
1303
+ const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
1304
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
1305
+ const previousOmxSessionId = process.env.OMX_SESSION_ID;
1306
+ try {
1307
+ await mkdir(cwd, { recursive: true });
1308
+ delete process.env.OMX_ROOT;
1309
+ delete process.env.OMX_STATE_ROOT;
1310
+ process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
1311
+ process.env.OMX_SESSION_ID = sessionId;
1312
+
1313
+ const result = await dispatchCodexNativeHook(
1314
+ {
1315
+ hook_event_name: "UserPromptSubmit",
1316
+ cwd,
1317
+ session_id: sessionId,
1318
+ thread_id: "thread-team-root",
1319
+ turn_id: "turn-team-root",
1320
+ prompt: "$ralplan implement issue #1307",
1321
+ },
1322
+ { cwd },
1323
+ );
1324
+
1325
+ assert.equal(result.omxEventName, "keyword-detector");
1326
+ assert.equal(result.skillState?.skill, "ralplan");
1327
+
1328
+ const teamSessionDir = join(teamStateRoot, "sessions", sessionId);
1329
+ assert.equal(existsSync(join(teamSessionDir, "skill-active-state.json")), true);
1330
+ assert.equal(existsSync(join(teamSessionDir, "ralplan-state.json")), true);
1331
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json")), false);
1332
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json")), false);
1333
+
1334
+ const hudState = await readAllState(cwd);
1335
+ assert.equal(hudState.ralplan?.active, true);
1336
+ assert.equal(hudState.ralplan?.current_phase, "planning");
1337
+ } finally {
1338
+ if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot;
1339
+ else delete process.env.OMX_ROOT;
1340
+ if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot;
1341
+ else delete process.env.OMX_STATE_ROOT;
1342
+ if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
1343
+ else delete process.env.OMX_TEAM_STATE_ROOT;
1344
+ if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId;
1345
+ else delete process.env.OMX_SESSION_ID;
1346
+ await rm(root, { recursive: true, force: true });
1347
+ }
1348
+ });
1349
+
1190
1350
  it("warns completion-like prompts when active goal workflows need Codex snapshot reconciliation", async () => {
1191
1351
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-goal-warning-"));
1192
1352
  try {
@@ -1240,6 +1400,100 @@ describe("codex native hook dispatch", () => {
1240
1400
  }
1241
1401
  });
1242
1402
 
1403
+ it("does not block Stop for non-passing autoresearch-goal professor-critic verdicts", async () => {
1404
+ for (const verdict of ["blocked", "fail", "failed"]) {
1405
+ const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-autoresearch-${verdict}-stop-`));
1406
+ const slug = `${verdict}-mission`;
1407
+ try {
1408
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", slug, "mission.json"), {
1409
+ version: 1,
1410
+ workflow: "autoresearch-goal",
1411
+ slug,
1412
+ topic: "Blocked research",
1413
+ status: verdict === "blocked" ? "blocked" : "failed",
1414
+ });
1415
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", slug, "completion.json"), {
1416
+ verdict,
1417
+ passed: false,
1418
+ });
1419
+
1420
+ const result = await dispatchCodexNativeHook({
1421
+ hook_event_name: "Stop",
1422
+ cwd,
1423
+ session_id: `sess-autoresearch-${verdict}-stop`,
1424
+ thread_id: `thread-autoresearch-${verdict}-stop`,
1425
+ last_assistant_message: "Autoresearch goal complete; next call update_goal({status: \"complete\"}).",
1426
+ }, { cwd });
1427
+
1428
+ assert.notEqual(result.outputJson?.decision, "block");
1429
+ assert.doesNotMatch(JSON.stringify(result.outputJson), new RegExp(`autoresearch-goal complete --slug ${slug}`));
1430
+ assert.doesNotMatch(JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/);
1431
+ } finally {
1432
+ await rm(cwd, { recursive: true, force: true });
1433
+ }
1434
+ }
1435
+ });
1436
+
1437
+ it("blocks Stop for passing autoresearch-goal professor-critic verdicts that need reconciliation", async () => {
1438
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autoresearch-pass-stop-"));
1439
+ try {
1440
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", "passing-mission", "mission.json"), {
1441
+ version: 1,
1442
+ workflow: "autoresearch-goal",
1443
+ slug: "passing-mission",
1444
+ topic: "Passing research",
1445
+ status: "validation_passed",
1446
+ });
1447
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", "passing-mission", "completion.json"), {
1448
+ verdict: "fail",
1449
+ passed: true,
1450
+ });
1451
+
1452
+ const result = await dispatchCodexNativeHook({
1453
+ hook_event_name: "Stop",
1454
+ cwd,
1455
+ session_id: "sess-autoresearch-pass-stop",
1456
+ thread_id: "thread-autoresearch-pass-stop",
1457
+ last_assistant_message: "Autoresearch goal complete; next call update_goal({status: \"complete\"}).",
1458
+ }, { cwd });
1459
+
1460
+ assert.equal(result.outputJson?.decision, "block");
1461
+ assert.match(JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/);
1462
+ assert.match(JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug passing-mission/);
1463
+ } finally {
1464
+ await rm(cwd, { recursive: true, force: true });
1465
+ }
1466
+ });
1467
+
1468
+ it("blocks Stop for autoresearch-goal verdict=pass even when passed is omitted", async () => {
1469
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autoresearch-verdict-pass-stop-"));
1470
+ try {
1471
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", "verdict-pass-mission", "mission.json"), {
1472
+ version: 1,
1473
+ workflow: "autoresearch-goal",
1474
+ slug: "verdict-pass-mission",
1475
+ topic: "Passing research",
1476
+ status: "validation_passed",
1477
+ });
1478
+ await writeJson(join(cwd, ".omx", "goals", "autoresearch", "verdict-pass-mission", "completion.json"), {
1479
+ verdict: "pass",
1480
+ });
1481
+
1482
+ const result = await dispatchCodexNativeHook({
1483
+ hook_event_name: "Stop",
1484
+ cwd,
1485
+ session_id: "sess-autoresearch-verdict-pass-stop",
1486
+ thread_id: "thread-autoresearch-verdict-pass-stop",
1487
+ last_assistant_message: "Autoresearch goal complete; next call update_goal({status: \"complete\"}).",
1488
+ }, { cwd });
1489
+
1490
+ assert.equal(result.outputJson?.decision, "block");
1491
+ assert.match(JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug verdict-pass-mission/);
1492
+ } finally {
1493
+ await rm(cwd, { recursive: true, force: true });
1494
+ }
1495
+ });
1496
+
1243
1497
  it("treats workflow keywords in native subagent prompt text as literal delegation text", async () => {
1244
1498
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-keyword-literal-"));
1245
1499
  try {
@@ -1327,7 +1581,7 @@ describe("codex native hook dispatch", () => {
1327
1581
  (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
1328
1582
  );
1329
1583
  assert.match(message, /\$oh-my-codex:ralplan" -> ralplan/);
1330
- 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\./);
1584
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1331
1585
  assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-plugin-1", "ralplan-state.json")), true);
1332
1586
  } finally {
1333
1587
  await rm(cwd, { recursive: true, force: true });
@@ -1544,7 +1798,7 @@ describe("codex native hook dispatch", () => {
1544
1798
  (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
1545
1799
  );
1546
1800
  assert.match(message, /\$ralph" -> ralph/);
1547
- 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\./);
1801
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1548
1802
  assert.match(message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./);
1549
1803
  assert.match(message, /Use `omx ralph --prd \.\.\.` only when you explicitly want the PRD-gated CLI startup path\./);
1550
1804
  } finally {
@@ -1574,7 +1828,7 @@ describe("codex native hook dispatch", () => {
1574
1828
  (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
1575
1829
  );
1576
1830
  assert.match(message, /\$oh-my-codex:ralph" -> ralph/);
1577
- 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\./);
1831
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1578
1832
  assert.match(message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./);
1579
1833
  } finally {
1580
1834
  await rm(cwd, { recursive: true, force: true });
@@ -1656,7 +1910,7 @@ describe("codex native hook dispatch", () => {
1656
1910
  (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
1657
1911
  );
1658
1912
  assert.match(message, /\$deep-interview" -> deep-interview/);
1659
- 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\./);
1913
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
1660
1914
  assert.match(message, /Deep-interview is active, but this session is not attached to tmux/);
1661
1915
  assert.match(message, /Do not invoke `omx question`, `omx hud`, or `omx team`/);
1662
1916
  assert.match(message, /native structured question tool when available/);
@@ -2132,8 +2386,11 @@ export async function onHookEvent(event) {
2132
2386
 
2133
2387
  assert.match(JSON.stringify(denied.outputJson), /denied workflow keyword/i);
2134
2388
  assert.match(JSON.stringify(denied.outputJson), /Unsupported workflow overlap: team \+ autopilot\./);
2135
- assert.match(JSON.stringify(denied.outputJson), /`omx state clear --mode <mode>`/);
2136
- assert.match(JSON.stringify(denied.outputJson), /`omx_state\.\*` MCP tools/);
2389
+ assert.match(JSON.stringify(denied.outputJson), /omx state clear --input/);
2390
+ assert.match(JSON.stringify(denied.outputJson), /mode\\":\\"<mode>/);
2391
+ assert.match(JSON.stringify(denied.outputJson), /--json/);
2392
+ assert.match(JSON.stringify(denied.outputJson), /explicit MCP compatibility is enabled/);
2393
+ assert.match(JSON.stringify(denied.outputJson), /`omx_state\.\*` tools/);
2137
2394
  assert.equal(
2138
2395
  existsSync(join(cwd, ".omx", "state", "sessions", "sess-deny-1", "autopilot-state.json")),
2139
2396
  false,
@@ -2210,7 +2467,7 @@ export async function onHookEvent(event) {
2210
2467
  assert.match(message, /\$ralph" -> ralph/);
2211
2468
  assert.doesNotMatch(message, /mode transiting:/);
2212
2469
  assert.match(message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./);
2213
- 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\./);
2470
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
2214
2471
  assert.doesNotMatch(message, /Use the durable OMX team runtime via `omx team \.\.\.`/);
2215
2472
  } finally {
2216
2473
  await rm(cwd, { recursive: true, force: true });
@@ -2242,7 +2499,7 @@ export async function onHookEvent(event) {
2242
2499
  assert.match(message, /\$oh-my-codex:ralph" -> ralph/);
2243
2500
  assert.doesNotMatch(message, /mode transiting:/);
2244
2501
  assert.match(message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./);
2245
- 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\./);
2502
+ assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
2246
2503
  } finally {
2247
2504
  await rm(cwd, { recursive: true, force: true });
2248
2505
  }
@@ -4352,7 +4609,7 @@ exit 0
4352
4609
  );
4353
4610
  assert.match(
4354
4611
  additionalContext,
4355
- /omx state state_write --input/,
4612
+ /omx state write --input/,
4356
4613
  );
4357
4614
  assert.match(
4358
4615
  additionalContext,
@@ -4600,7 +4857,7 @@ exit 0
4600
4857
  assert.equal(hookSpecificOutput?.hookEventName, "PostToolUse");
4601
4858
  assert.match(
4602
4859
  String(hookSpecificOutput?.additionalContext || ""),
4603
- /Retry via CLI parity with `omx state state_write --input '\{\}' --json`\./,
4860
+ /Retry via CLI parity with `omx state write --input '\{\}' --json`\./,
4604
4861
  );
4605
4862
  assert.match(
4606
4863
  String(hookSpecificOutput?.additionalContext || ""),
@@ -4686,7 +4943,7 @@ exit 0
4686
4943
  hookSpecificOutput: {
4687
4944
  hookEventName: "PostToolUse",
4688
4945
  additionalContext:
4689
- "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.",
4946
+ "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.",
4690
4947
  },
4691
4948
  });
4692
4949
 
@@ -6106,6 +6363,41 @@ exit 0
6106
6363
  }
6107
6364
  });
6108
6365
 
6366
+ it("ignores stale source-root team Stop fallback when OMX_TEAM_STATE_ROOT is authoritative", async () => {
6367
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-stale-source-root-"));
6368
+ const teamStateRoot = join(cwd, "shared-team-state");
6369
+ const priorTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
6370
+ try {
6371
+ process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
6372
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
6373
+ await mkdir(join(teamStateRoot, "team", "stale-source-team"), { recursive: true });
6374
+ await writeJson(join(cwd, ".omx", "state", "team-state.json"), {
6375
+ active: true,
6376
+ team_name: "stale-source-team",
6377
+ current_phase: "team-exec",
6378
+ });
6379
+ await writeJson(join(teamStateRoot, "team", "stale-source-team", "phase.json"), {
6380
+ current_phase: "team-exec",
6381
+ });
6382
+
6383
+ const result = await dispatchCodexNativeHook(
6384
+ {
6385
+ hook_event_name: "Stop",
6386
+ cwd,
6387
+ session_id: "sess-stale-source-team",
6388
+ },
6389
+ { cwd },
6390
+ );
6391
+
6392
+ assert.equal(result.omxEventName, "stop");
6393
+ assert.equal(result.outputJson, null);
6394
+ } finally {
6395
+ if (typeof priorTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = priorTeamStateRoot;
6396
+ else delete process.env.OMX_TEAM_STATE_ROOT;
6397
+ await rm(cwd, { recursive: true, force: true });
6398
+ }
6399
+ });
6400
+
6109
6401
  it("returns Stop continuation output from canonical team state rooted via OMX_TEAM_STATE_ROOT", async () => {
6110
6402
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-env-root-"));
6111
6403
  const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
@@ -7141,6 +7433,79 @@ exit 0
7141
7433
  }
7142
7434
  });
7143
7435
 
7436
+ it("blocks Codex App Stop when Ralph is marked complete without completion-audit evidence", async () => {
7437
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-missing-"));
7438
+ try {
7439
+ const sessionId = "sess-ralph-complete-missing";
7440
+ const statePath = join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json");
7441
+ await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd });
7442
+ await writeJson(statePath, {
7443
+ active: false,
7444
+ mode: "ralph",
7445
+ current_phase: "complete",
7446
+ session_id: sessionId,
7447
+ completed_at: "2026-05-10T12:00:00.000Z",
7448
+ });
7449
+
7450
+ const result = await dispatchCodexNativeHook(
7451
+ {
7452
+ hook_event_name: "Stop",
7453
+ cwd,
7454
+ session_id: sessionId,
7455
+ last_assistant_message: "Done. Ralph complete.",
7456
+ },
7457
+ { cwd },
7458
+ );
7459
+
7460
+ assert.equal(result.omxEventName, "stop");
7461
+ assert.match(String(result.outputJson?.reason), /Ralph completion audit is missing required evidence/);
7462
+ assert.equal(result.outputJson?.stopReason, "ralph_completion_audit_missing_completion_audit");
7463
+ const reopened = JSON.parse(await readFile(statePath, "utf-8")) as Record<string, unknown>;
7464
+ assert.equal(reopened.active, true);
7465
+ assert.equal(reopened.current_phase, "verifying");
7466
+ assert.equal(reopened.completion_audit_gate, "blocked");
7467
+ assert.equal(reopened.completion_audit_missing_reason, "missing_completion_audit");
7468
+ assert.equal(typeof reopened.completed_at, "undefined");
7469
+ } finally {
7470
+ await rm(cwd, { recursive: true, force: true });
7471
+ }
7472
+ });
7473
+
7474
+ it("allows Codex App Stop when complete Ralph state carries checklist and verification evidence", async () => {
7475
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-present-"));
7476
+ try {
7477
+ const sessionId = "sess-ralph-complete-present";
7478
+ await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd });
7479
+ await writeJson(join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json"), {
7480
+ active: false,
7481
+ mode: "ralph",
7482
+ current_phase: "complete",
7483
+ session_id: sessionId,
7484
+ completed_at: "2026-05-10T12:00:00.000Z",
7485
+ completion_audit: {
7486
+ passed: true,
7487
+ prompt_to_artifact_checklist: ["issue #2260 fixed", "tests added"],
7488
+ verification_evidence: ["node --test dist/scripts/__tests__/codex-native-hook.test.js"],
7489
+ },
7490
+ });
7491
+
7492
+ const result = await dispatchCodexNativeHook(
7493
+ {
7494
+ hook_event_name: "Stop",
7495
+ cwd,
7496
+ session_id: sessionId,
7497
+ last_assistant_message: "Done with completion audit evidence recorded.",
7498
+ },
7499
+ { cwd },
7500
+ );
7501
+
7502
+ assert.equal(result.omxEventName, "stop");
7503
+ assert.equal(result.outputJson, null);
7504
+ } finally {
7505
+ await rm(cwd, { recursive: true, force: true });
7506
+ }
7507
+ });
7508
+
7144
7509
  it("returns Stop continuation output while Ralph is active without an explicit session pin", async () => {
7145
7510
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-"));
7146
7511
  try {