oh-my-codex 0.16.3 → 0.17.0

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 (339) 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__/generator.test.js +2 -0
  5. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  6. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +9 -0
  7. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
  8. package/dist/cli/__tests__/cleanup.test.js +27 -0
  9. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  10. package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -5
  11. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  12. package/dist/cli/__tests__/doctor-warning-copy.test.js +175 -7
  13. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  14. package/dist/cli/__tests__/index.test.js +147 -12
  15. package/dist/cli/__tests__/index.test.js.map +1 -1
  16. package/dist/cli/__tests__/mcp-serve.test.js +4 -0
  17. package/dist/cli/__tests__/mcp-serve.test.js.map +1 -1
  18. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +2 -0
  19. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  20. package/dist/cli/__tests__/ralph.test.js +47 -0
  21. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  22. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +10 -5
  23. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  24. package/dist/cli/__tests__/setup-install-mode.test.js +299 -27
  25. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  26. package/dist/cli/__tests__/setup-refresh.test.js +85 -3
  27. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  28. package/dist/cli/__tests__/setup-scope.test.js +1 -1
  29. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  30. package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -1
  31. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  32. package/dist/cli/__tests__/team.test.js +108 -0
  33. package/dist/cli/__tests__/team.test.js.map +1 -1
  34. package/dist/cli/__tests__/ultragoal.test.js +91 -0
  35. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  36. package/dist/cli/__tests__/uninstall.test.js +54 -8
  37. package/dist/cli/__tests__/uninstall.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 +214 -23
  48. package/dist/cli/doctor.js.map +1 -1
  49. package/dist/cli/index.d.ts +17 -4
  50. package/dist/cli/index.d.ts.map +1 -1
  51. package/dist/cli/index.js +152 -24
  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/mcp-serve.d.ts.map +1 -1
  56. package/dist/cli/mcp-serve.js +4 -0
  57. package/dist/cli/mcp-serve.js.map +1 -1
  58. package/dist/cli/plugin-marketplace.d.ts +23 -0
  59. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  60. package/dist/cli/plugin-marketplace.js +203 -1
  61. package/dist/cli/plugin-marketplace.js.map +1 -1
  62. package/dist/cli/ralph.d.ts.map +1 -1
  63. package/dist/cli/ralph.js +21 -0
  64. package/dist/cli/ralph.js.map +1 -1
  65. package/dist/cli/setup-preferences.d.ts +4 -0
  66. package/dist/cli/setup-preferences.d.ts.map +1 -1
  67. package/dist/cli/setup-preferences.js +7 -0
  68. package/dist/cli/setup-preferences.js.map +1 -1
  69. package/dist/cli/setup.d.ts +5 -3
  70. package/dist/cli/setup.d.ts.map +1 -1
  71. package/dist/cli/setup.js +140 -51
  72. package/dist/cli/setup.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 +70 -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 +12 -3
  80. package/dist/cli/uninstall.js.map +1 -1
  81. package/dist/config/__tests__/codex-feature-flags.test.d.ts +2 -0
  82. package/dist/config/__tests__/codex-feature-flags.test.d.ts.map +1 -0
  83. package/dist/config/__tests__/codex-feature-flags.test.js +35 -0
  84. package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -0
  85. package/dist/config/__tests__/codex-hooks.test.js +143 -9
  86. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  87. package/dist/config/__tests__/generator-idempotent.test.js +85 -9
  88. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  89. package/dist/config/__tests__/generator-notify.test.js +116 -11
  90. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  91. package/dist/config/__tests__/wiki-config-contract.test.js +6 -3
  92. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -1
  93. package/dist/config/codex-feature-flags.d.ts +21 -0
  94. package/dist/config/codex-feature-flags.d.ts.map +1 -0
  95. package/dist/config/codex-feature-flags.js +56 -0
  96. package/dist/config/codex-feature-flags.js.map +1 -0
  97. package/dist/config/codex-hooks.d.ts +14 -13
  98. package/dist/config/codex-hooks.d.ts.map +1 -1
  99. package/dist/config/codex-hooks.js +108 -8
  100. package/dist/config/codex-hooks.js.map +1 -1
  101. package/dist/config/generator.d.ts +15 -3
  102. package/dist/config/generator.d.ts.map +1 -1
  103. package/dist/config/generator.js +233 -129
  104. package/dist/config/generator.js.map +1 -1
  105. package/dist/config/omx-first-party-mcp.d.ts +3 -1
  106. package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
  107. package/dist/config/omx-first-party-mcp.js +9 -2
  108. package/dist/config/omx-first-party-mcp.js.map +1 -1
  109. package/dist/hooks/__tests__/design-skill.test.d.ts +2 -0
  110. package/dist/hooks/__tests__/design-skill.test.d.ts.map +1 -0
  111. package/dist/hooks/__tests__/design-skill.test.js +55 -0
  112. package/dist/hooks/__tests__/design-skill.test.js.map +1 -0
  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-hook-non-omx-guard.test.js +125 -1
  116. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  117. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +265 -0
  118. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  119. package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts +2 -0
  120. package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts.map +1 -0
  121. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js +84 -0
  122. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -0
  123. package/dist/hooks/__tests__/skill-guidance-contract.test.js +41 -0
  124. package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
  125. package/dist/hooks/agents-overlay.js +2 -2
  126. package/dist/hooks/agents-overlay.js.map +1 -1
  127. package/dist/hooks/keyword-detector.d.ts +1 -0
  128. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  129. package/dist/hooks/keyword-detector.js +12 -6
  130. package/dist/hooks/keyword-detector.js.map +1 -1
  131. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  132. package/dist/hooks/keyword-registry.js +2 -0
  133. package/dist/hooks/keyword-registry.js.map +1 -1
  134. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  135. package/dist/hooks/prompt-guidance-contract.js +47 -2
  136. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  137. package/dist/hud/__tests__/state.test.js +164 -0
  138. package/dist/hud/__tests__/state.test.js.map +1 -1
  139. package/dist/hud/state.d.ts.map +1 -1
  140. package/dist/hud/state.js +4 -5
  141. package/dist/hud/state.js.map +1 -1
  142. package/dist/mcp/__tests__/bootstrap.test.js +3 -0
  143. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  144. package/dist/mcp/__tests__/hermes-bridge.test.d.ts +2 -0
  145. package/dist/mcp/__tests__/hermes-bridge.test.d.ts.map +1 -0
  146. package/dist/mcp/__tests__/hermes-bridge.test.js +374 -0
  147. package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -0
  148. package/dist/mcp/__tests__/state-paths.test.js +155 -11
  149. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  150. package/dist/mcp/__tests__/state-server.test.js +166 -0
  151. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  152. package/dist/mcp/bootstrap.d.ts +1 -1
  153. package/dist/mcp/bootstrap.d.ts.map +1 -1
  154. package/dist/mcp/bootstrap.js +2 -0
  155. package/dist/mcp/bootstrap.js.map +1 -1
  156. package/dist/mcp/hermes-bridge.d.ts +81 -0
  157. package/dist/mcp/hermes-bridge.d.ts.map +1 -0
  158. package/dist/mcp/hermes-bridge.js +400 -0
  159. package/dist/mcp/hermes-bridge.js.map +1 -0
  160. package/dist/mcp/hermes-server.d.ts +269 -0
  161. package/dist/mcp/hermes-server.d.ts.map +1 -0
  162. package/dist/mcp/hermes-server.js +121 -0
  163. package/dist/mcp/hermes-server.js.map +1 -0
  164. package/dist/mcp/state-paths.d.ts.map +1 -1
  165. package/dist/mcp/state-paths.js +64 -11
  166. package/dist/mcp/state-paths.js.map +1 -1
  167. package/dist/modes/__tests__/base-session-scope.test.js +22 -0
  168. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  169. package/dist/modes/__tests__/base-tmux-pane.test.js +88 -27
  170. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  171. package/dist/modes/base.d.ts.map +1 -1
  172. package/dist/modes/base.js +5 -0
  173. package/dist/modes/base.js.map +1 -1
  174. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts +2 -0
  175. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts.map +1 -0
  176. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js +316 -0
  177. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -0
  178. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts +2 -0
  179. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts.map +1 -0
  180. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js +481 -0
  181. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js.map +1 -0
  182. package/dist/planning/__tests__/artifacts.test.js +533 -4
  183. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  184. package/dist/planning/__tests__/context-pack-status.test.js +524 -0
  185. package/dist/planning/__tests__/context-pack-status.test.js.map +1 -1
  186. package/dist/planning/__tests__/markdown-structure.test.d.ts +2 -0
  187. package/dist/planning/__tests__/markdown-structure.test.d.ts.map +1 -0
  188. package/dist/planning/__tests__/markdown-structure.test.js +459 -0
  189. package/dist/planning/__tests__/markdown-structure.test.js.map +1 -0
  190. package/dist/planning/__tests__/ready-context-pack-role-refs.test.js +523 -1
  191. package/dist/planning/__tests__/ready-context-pack-role-refs.test.js.map +1 -1
  192. package/dist/planning/artifacts.d.ts +1 -1
  193. package/dist/planning/artifacts.d.ts.map +1 -1
  194. package/dist/planning/artifacts.js +227 -28
  195. package/dist/planning/artifacts.js.map +1 -1
  196. package/dist/planning/context-pack-status.d.ts +25 -0
  197. package/dist/planning/context-pack-status.d.ts.map +1 -1
  198. package/dist/planning/context-pack-status.js +272 -31
  199. package/dist/planning/context-pack-status.js.map +1 -1
  200. package/dist/planning/markdown-structure.d.ts +20 -0
  201. package/dist/planning/markdown-structure.d.ts.map +1 -0
  202. package/dist/planning/markdown-structure.js +137 -0
  203. package/dist/planning/markdown-structure.js.map +1 -0
  204. package/dist/ralph/__tests__/completion-audit.test.d.ts +2 -0
  205. package/dist/ralph/__tests__/completion-audit.test.d.ts.map +1 -0
  206. package/dist/ralph/__tests__/completion-audit.test.js +121 -0
  207. package/dist/ralph/__tests__/completion-audit.test.js.map +1 -0
  208. package/dist/ralph/completion-audit.d.ts +8 -0
  209. package/dist/ralph/completion-audit.d.ts.map +1 -0
  210. package/dist/ralph/completion-audit.js +99 -0
  211. package/dist/ralph/completion-audit.js.map +1 -0
  212. package/dist/scripts/__tests__/codex-native-hook.test.js +407 -15
  213. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  214. package/dist/scripts/__tests__/notify-dispatcher.test.d.ts +2 -0
  215. package/dist/scripts/__tests__/notify-dispatcher.test.d.ts.map +1 -0
  216. package/dist/scripts/__tests__/notify-dispatcher.test.js +126 -0
  217. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -0
  218. package/dist/scripts/codex-native-hook.d.ts +1 -0
  219. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  220. package/dist/scripts/codex-native-hook.js +177 -71
  221. package/dist/scripts/codex-native-hook.js.map +1 -1
  222. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  223. package/dist/scripts/codex-native-pre-post.js +4 -2
  224. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  225. package/dist/scripts/notify-dispatcher.js +30 -1
  226. package/dist/scripts/notify-dispatcher.js.map +1 -1
  227. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  228. package/dist/scripts/notify-hook/tmux-injection.js +91 -2
  229. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  230. package/dist/scripts/notify-hook.js +3 -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/mode-state-context.d.ts +2 -0
  235. package/dist/state/mode-state-context.d.ts.map +1 -1
  236. package/dist/state/mode-state-context.js +21 -0
  237. package/dist/state/mode-state-context.js.map +1 -1
  238. package/dist/state/operations.d.ts.map +1 -1
  239. package/dist/state/operations.js +9 -3
  240. package/dist/state/operations.js.map +1 -1
  241. package/dist/state/skill-active.d.ts +7 -0
  242. package/dist/state/skill-active.d.ts.map +1 -1
  243. package/dist/state/skill-active.js +25 -8
  244. package/dist/state/skill-active.js.map +1 -1
  245. package/dist/state/workflow-transition-reconcile.d.ts +1 -0
  246. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  247. package/dist/state/workflow-transition-reconcile.js +22 -15
  248. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  249. package/dist/state/workflow-transition.js +3 -3
  250. package/dist/state/workflow-transition.js.map +1 -1
  251. package/dist/team/__tests__/approved-execution.test.js +39 -0
  252. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  253. package/dist/team/__tests__/runtime.test.js +5 -0
  254. package/dist/team/__tests__/runtime.test.js.map +1 -1
  255. package/dist/team/__tests__/scaling.test.js +497 -2
  256. package/dist/team/__tests__/scaling.test.js.map +1 -1
  257. package/dist/team/__tests__/state-root.test.js +1 -1
  258. package/dist/team/__tests__/state-root.test.js.map +1 -1
  259. package/dist/team/__tests__/worker-bootstrap.test.js +8 -0
  260. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  261. package/dist/team/approved-execution.d.ts.map +1 -1
  262. package/dist/team/approved-execution.js +3 -0
  263. package/dist/team/approved-execution.js.map +1 -1
  264. package/dist/team/scaling.d.ts.map +1 -1
  265. package/dist/team/scaling.js +43 -0
  266. package/dist/team/scaling.js.map +1 -1
  267. package/dist/team/state-root.d.ts.map +1 -1
  268. package/dist/team/state-root.js +4 -0
  269. package/dist/team/state-root.js.map +1 -1
  270. package/dist/team/state.d.ts.map +1 -1
  271. package/dist/team/state.js +2 -6
  272. package/dist/team/state.js.map +1 -1
  273. package/dist/ultragoal/__tests__/artifacts.test.js +245 -1
  274. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  275. package/dist/ultragoal/__tests__/docs-contract.test.js +21 -0
  276. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  277. package/dist/ultragoal/artifacts.d.ts +52 -2
  278. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  279. package/dist/ultragoal/artifacts.js +301 -15
  280. package/dist/ultragoal/artifacts.js.map +1 -1
  281. package/dist/utils/__tests__/paths.test.js +31 -1
  282. package/dist/utils/__tests__/paths.test.js.map +1 -1
  283. package/dist/utils/paths.d.ts +6 -0
  284. package/dist/utils/paths.d.ts.map +1 -1
  285. package/dist/utils/paths.js +18 -0
  286. package/dist/utils/paths.js.map +1 -1
  287. package/dist/wiki/lifecycle.js +4 -4
  288. package/dist/wiki/lifecycle.js.map +1 -1
  289. package/package.json +1 -1
  290. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  291. package/plugins/oh-my-codex/.mcp.json +13 -5
  292. package/plugins/oh-my-codex/skills/analyze/SKILL.md +0 -2
  293. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
  294. package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -3
  295. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +5 -7
  296. package/plugins/oh-my-codex/skills/design/SKILL.md +180 -0
  297. package/plugins/oh-my-codex/skills/doctor/SKILL.md +2 -2
  298. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +3 -3
  299. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +3 -3
  300. package/plugins/oh-my-codex/skills/plan/SKILL.md +3 -6
  301. package/plugins/oh-my-codex/skills/ralph/SKILL.md +9 -10
  302. package/plugins/oh-my-codex/skills/skill/SKILL.md +2 -1
  303. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +36 -3
  304. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +175 -64
  305. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +8 -8
  306. package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +2 -2
  307. package/plugins/oh-my-codex/skills/wiki/SKILL.md +13 -13
  308. package/skills/analyze/SKILL.md +0 -2
  309. package/skills/ask-claude/SKILL.md +5 -3
  310. package/skills/ask-gemini/SKILL.md +5 -3
  311. package/skills/autopilot/SKILL.md +2 -2
  312. package/skills/code-review/SKILL.md +1 -3
  313. package/skills/deep-interview/SKILL.md +5 -7
  314. package/skills/design/SKILL.md +180 -0
  315. package/skills/doctor/SKILL.md +2 -2
  316. package/skills/ecomode/SKILL.md +105 -1
  317. package/skills/frontend-ui-ux/SKILL.md +6 -24
  318. package/skills/git-master/SKILL.md +2 -4
  319. package/skills/omx-setup/SKILL.md +3 -3
  320. package/skills/pipeline/SKILL.md +3 -3
  321. package/skills/plan/SKILL.md +3 -6
  322. package/skills/ralph/SKILL.md +9 -10
  323. package/skills/skill/SKILL.md +2 -1
  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 +175 -64
  328. package/skills/ultrawork/SKILL.md +8 -8
  329. package/skills/visual-ralph/SKILL.md +2 -2
  330. package/skills/web-clone/SKILL.md +348 -1
  331. package/skills/wiki/SKILL.md +13 -13
  332. package/src/scripts/__tests__/codex-native-hook.test.ts +437 -14
  333. package/src/scripts/__tests__/notify-dispatcher.test.ts +153 -0
  334. package/src/scripts/codex-native-hook.ts +205 -61
  335. package/src/scripts/codex-native-pre-post.ts +4 -1
  336. package/src/scripts/notify-dispatcher.ts +40 -1
  337. package/src/scripts/notify-hook/tmux-injection.ts +110 -3
  338. package/src/scripts/notify-hook.ts +3 -1
  339. package/templates/catalog-manifest.json +9 -2
@@ -4,14 +4,14 @@ import { appendFile, mkdir, readFile, readdir, writeFile } from "fs/promises";
4
4
  import { extname, join, relative, resolve } from "path";
5
5
  import { pathToFileURL } from "url";
6
6
  import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
7
- import { extractSessionIdFromInitializedStatePath, getSkillActiveStatePaths, listActiveSkills, readSkillActiveState, readVisibleSkillActiveState, } from "../state/skill-active.js";
7
+ import { extractSessionIdFromInitializedStatePath, getSkillActiveStatePathsForStateDir, listActiveSkills, readSkillActiveState, readVisibleSkillActiveStateForStateDir, } from "../state/skill-active.js";
8
8
  import { readSubagentSessionSummary, recordSubagentTurnForSession, } from "../subagents/tracker.js";
9
9
  import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
10
10
  import { appendToLog, isSessionStateUsable, readSessionState, readUsableSessionState, reconcileNativeSessionStart, } from "../hooks/session.js";
11
11
  import { appendTeamEvent, readTeamLeaderAttention, readTeamManifestV2, readTeamPhase, writeTeamLeaderAttention, writeTeamPhase, } from "../team/state.js";
12
- import { omxNotepadPath, omxProjectMemoryPath } from "../utils/paths.js";
12
+ import { omxNotepadPath, resolveProjectMemoryPath } from "../utils/paths.js";
13
13
  import { findGitLayout } from "../utils/git-layout.js";
14
- import { getStateFilePath, getStatePath } from "../mcp/state-paths.js";
14
+ import { getBaseStateDir, getStateFilePath, getStatePath } from "../mcp/state-paths.js";
15
15
  import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, } from "../hooks/keyword-detector.js";
16
16
  import { detectNativeStopStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, resolveEffectiveAutoNudgeResponse, } from "./notify-hook/auto-nudge.js";
17
17
  import { SLOPPY_FALLBACK_GROUNDING_PATTERNS, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS, SLOPPY_FALLBACK_PHRASE_PATTERNS, buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTransportFailure, hasAnyPattern, } from "./codex-native-pre-post.js";
@@ -24,6 +24,7 @@ import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
24
24
  import { onPreCompact as buildWikiPreCompactContext, onSessionStart as buildWikiSessionStartContext, } from "../wiki/lifecycle.js";
25
25
  import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
26
26
  import { readRunState } from "../runtime/run-state.js";
27
+ import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
27
28
  import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
28
29
  import { triagePrompt } from "../hooks/triage-heuristic.js";
29
30
  import { readTriageConfig } from "../hooks/triage-config.js";
@@ -62,6 +63,12 @@ function safeString(value) {
62
63
  function safeObject(value) {
63
64
  return value && typeof value === "object" ? value : {};
64
65
  }
66
+ function safeContextSnippet(value, maxLength = 300) {
67
+ const text = safeString(value).replace(/\s+/g, " ").trim();
68
+ if (text.length <= maxLength)
69
+ return text;
70
+ return `${text.slice(0, maxLength - 1).trimEnd()}…`;
71
+ }
65
72
  function readBoundedFirstLineSync(path) {
66
73
  const fd = openSync(path, "r");
67
74
  try {
@@ -381,15 +388,15 @@ async function readCanonicalTerminalRunStateForStop(cwd, sessionId, mode) {
381
388
  const runRecord = runState;
382
389
  return shouldHonorCanonicalTerminalRunState(runRecord, mode) ? runRecord : null;
383
390
  }
384
- async function isVisibleRalphActiveForSession(cwd, sessionId) {
385
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
391
+ async function isVisibleRalphActiveForSession(stateDir, sessionId) {
392
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
386
393
  if (!canonicalState)
387
394
  return false;
388
395
  return listActiveSkills(canonicalState).some((entry) => (entry.skill === "ralph"
389
396
  && matchesSkillStopContext(entry, canonicalState, sessionId, "")));
390
397
  }
391
- async function hasConsistentRalphSkillActivation(cwd, sessionId) {
392
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
398
+ async function hasConsistentRalphSkillActivation(stateDir, sessionId) {
399
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
393
400
  if (!canonicalState)
394
401
  return true;
395
402
  const initializedMode = safeString(canonicalState.initialized_mode).trim();
@@ -400,8 +407,63 @@ async function hasConsistentRalphSkillActivation(cwd, sessionId) {
400
407
  return false;
401
408
  return true;
402
409
  }
403
- async function readActiveRalphState(stateDir, preferredSessionId, ownerContext) {
404
- const cwd = resolve(stateDir, "..", "..");
410
+ async function readRalphCompletionAuditBlockState(cwd, stateDir, preferredSessionId, ownerContext) {
411
+ const [rawSessionInfo, usableSessionInfo] = await Promise.all([
412
+ readSessionState(cwd),
413
+ readUsableSessionState(cwd),
414
+ ]);
415
+ const currentOmxSessionId = safeString(usableSessionInfo?.session_id).trim();
416
+ const currentNativeSessionId = safeString(usableSessionInfo?.native_session_id).trim();
417
+ const staleCurrentSessionId = rawSessionInfo && !isSessionStateUsable(rawSessionInfo, cwd)
418
+ ? safeString(rawSessionInfo.session_id).trim()
419
+ : "";
420
+ const sessionCandidates = [...new Set([
421
+ safeString(preferredSessionId).trim(),
422
+ currentOmxSessionId,
423
+ ].filter(Boolean))];
424
+ const evaluateCandidate = (state, path, sessionId) => {
425
+ if (!state || state.mode && safeString(state.mode) !== "ralph")
426
+ return null;
427
+ if (!isRalphCompletePhase(state.current_phase ?? state.currentPhase))
428
+ return null;
429
+ if (activeRalphStateMatchesStopOwner(state, {
430
+ sessionId,
431
+ payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
432
+ threadId: safeString(ownerContext?.threadId).trim(),
433
+ currentNativeSessionId,
434
+ tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
435
+ }) !== true)
436
+ return null;
437
+ const audit = evaluateRalphCompletionAuditEvidence(state, cwd);
438
+ return audit.complete ? null : { state, path, reason: audit.reason };
439
+ };
440
+ for (const sessionId of sessionCandidates) {
441
+ if (staleCurrentSessionId && sessionId === staleCurrentSessionId)
442
+ continue;
443
+ const sessionScopedPath = getStateFilePath("ralph-state.json", cwd, sessionId);
444
+ const result = evaluateCandidate(await readJsonIfExists(sessionScopedPath), sessionScopedPath, sessionId);
445
+ if (result)
446
+ return result;
447
+ }
448
+ if (sessionCandidates.length > 0)
449
+ return null;
450
+ const directPath = join(stateDir, "ralph-state.json");
451
+ return evaluateCandidate(await readJsonIfExists(directPath), directPath, "");
452
+ }
453
+ async function reopenRalphCompletionAuditBlock(block) {
454
+ const nowIso = new Date().toISOString();
455
+ const next = {
456
+ ...block.state,
457
+ active: true,
458
+ current_phase: "verifying",
459
+ completion_audit_gate: "blocked",
460
+ completion_audit_missing_reason: block.reason,
461
+ completion_audit_blocked_at: nowIso,
462
+ };
463
+ delete next.completed_at;
464
+ await writeFile(block.path, JSON.stringify(next, null, 2));
465
+ }
466
+ async function readActiveRalphState(cwd, stateDir, preferredSessionId, ownerContext) {
405
467
  const [rawSessionInfo, usableSessionInfo] = await Promise.all([
406
468
  readSessionState(cwd),
407
469
  readUsableSessionState(cwd),
@@ -429,7 +491,7 @@ async function readActiveRalphState(stateDir, preferredSessionId, ownerContext)
429
491
  const sessionScoped = await readJsonIfExists(sessionScopedPath);
430
492
  if (sessionScoped?.active === true
431
493
  && isRalphStartingPhase(sessionScoped)
432
- && !(await isVisibleRalphActiveForSession(cwd, sessionId))) {
494
+ && !(await isVisibleRalphActiveForSession(stateDir, sessionId))) {
433
495
  continue;
434
496
  }
435
497
  if (sessionScoped?.active === true
@@ -441,7 +503,7 @@ async function readActiveRalphState(stateDir, preferredSessionId, ownerContext)
441
503
  currentNativeSessionId,
442
504
  tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
443
505
  })
444
- && await hasConsistentRalphSkillActivation(cwd, sessionId)) {
506
+ && await hasConsistentRalphSkillActivation(stateDir, sessionId)) {
445
507
  return { state: sessionScoped, path: sessionScopedPath };
446
508
  }
447
509
  }
@@ -818,14 +880,17 @@ async function buildSessionStartContext(cwd, sessionId, options = {}) {
818
880
  if (modeSummaries.length > 0) {
819
881
  sections.push(["[Active OMX modes]", ...modeSummaries].join("\n"));
820
882
  }
821
- const projectMemory = await readJsonIfExists(omxProjectMemoryPath(cwd));
822
- if (projectMemory) {
883
+ const projectMemoryPath = resolveProjectMemoryPath(cwd);
884
+ const projectMemory = projectMemoryPath ? await readJsonIfExists(projectMemoryPath) : null;
885
+ if (projectMemory && projectMemoryPath) {
823
886
  const directives = Array.isArray(projectMemory.directives) ? projectMemory.directives : [];
824
887
  const notes = Array.isArray(projectMemory.notes) ? projectMemory.notes : [];
825
- const techStack = safeString(projectMemory.techStack).trim();
826
- const conventions = safeString(projectMemory.conventions).trim();
827
- const build = safeString(projectMemory.build).trim();
888
+ const techStack = safeContextSnippet(projectMemory.techStack);
889
+ const conventions = safeContextSnippet(projectMemory.conventions);
890
+ const build = safeContextSnippet(projectMemory.build);
828
891
  const summary = [];
892
+ const relativeMemoryPath = relative(cwd, projectMemoryPath).replace(/\\/g, "/");
893
+ summary.push(`- source: ${relativeMemoryPath === "project-memory.json" ? "project-memory.json" : ".omx/project-memory.json"}`);
829
894
  if (techStack)
830
895
  summary.push(`- stack: ${techStack}`);
831
896
  if (conventions)
@@ -834,17 +899,17 @@ async function buildSessionStartContext(cwd, sessionId, options = {}) {
834
899
  summary.push(`- build: ${build}`);
835
900
  if (directives.length > 0) {
836
901
  const firstDirective = directives[0];
837
- const directive = safeString(firstDirective.directive).trim();
902
+ const directive = safeContextSnippet(firstDirective.directive);
838
903
  if (directive)
839
904
  summary.push(`- directive: ${directive}`);
840
905
  }
841
906
  if (notes.length > 0) {
842
907
  const firstNote = notes[0];
843
- const note = safeString(firstNote.content).trim();
908
+ const note = safeContextSnippet(firstNote.content);
844
909
  if (note)
845
910
  summary.push(`- note: ${note}`);
846
911
  }
847
- if (summary.length > 0) {
912
+ if (summary.length > 1) {
848
913
  sections.push(["[Project memory]", ...summary].join("\n"));
849
914
  }
850
915
  }
@@ -1030,6 +1095,9 @@ function buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessio
1030
1095
  transition_error: "Codex App/native outside-tmux sessions cannot activate the tmux-only `team` workflow directly. Launch OMX CLI from an attached tmux shell first, then run `omx team ...` there.",
1031
1096
  };
1032
1097
  }
1098
+ function buildSkillStateCliInstruction(mode, statePath) {
1099
+ return `skill: ${mode} activated and initial state initialized at ${statePath}; use CLI-first state updates via \`omx state write/read/clear --input '<json>' --json\`; use omx_state MCP only when explicit MCP compatibility is enabled.`;
1100
+ }
1033
1101
  function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(), payload) {
1034
1102
  if (!prompt)
1035
1103
  return null;
@@ -1089,7 +1157,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1089
1157
  promptPriorityMessage,
1090
1158
  ultragoalPromptActivationNote,
1091
1159
  skillState.initialized_mode && skillState.initialized_state_path
1092
- ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
1160
+ ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1093
1161
  : null,
1094
1162
  teamDetected
1095
1163
  ? buildTeamRuntimeInstruction(cwd, payload)
@@ -1100,7 +1168,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1100
1168
  }
1101
1169
  if (teamDetected) {
1102
1170
  const initializedStateMessage = skillState?.initialized_mode && skillState.initialized_state_path
1103
- ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
1171
+ ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1104
1172
  : null;
1105
1173
  return [
1106
1174
  detectedKeywordMessage,
@@ -1126,7 +1194,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1126
1194
  ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
1127
1195
  : null,
1128
1196
  promptPriorityMessage,
1129
- `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
1197
+ buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path),
1130
1198
  deepInterviewPromptActivationNote,
1131
1199
  ultraworkPromptActivationNote,
1132
1200
  ultragoalPromptActivationNote,
@@ -1164,7 +1232,7 @@ async function resolveTeamWorkerStopDecision(cwd) {
1164
1232
  || parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER));
1165
1233
  if (!workerContext)
1166
1234
  return { kind: "unresolved", reason: "missing_worker_context" };
1167
- const blockWorkerStop = (reasonCode, detail, stateDirForDecision = join(cwd, ".omx", "state")) => ({
1235
+ const blockWorkerStop = (reasonCode, detail, stateDirForDecision = getBaseStateDir(cwd)) => ({
1168
1236
  kind: "blocked",
1169
1237
  stateDir: stateDirForDecision,
1170
1238
  workerContext,
@@ -1281,19 +1349,32 @@ async function buildModeBasedStopOutput(mode, cwd, sessionId) {
1281
1349
  systemMessage: `OMX ${mode} is still active (phase: ${phase}).`,
1282
1350
  };
1283
1351
  }
1284
- function looksLikeGoalCompletionPrompt(text) {
1285
- return /\b(?:complete|checkpoint|finish|close|mark)\b.{0,80}\b(?:goal|ultragoal|performance-goal|autoresearch-goal)\b/i.test(text)
1286
- || /\bupdate_goal\s*\(/i.test(text)
1287
- || /\bomx\s+(?:ultragoal|performance-goal|autoresearch-goal)\s+(?:checkpoint|complete)\b/i.test(text);
1352
+ export function looksLikeGoalCompletionPrompt(text) {
1353
+ return /\bupdate_goal\s*\(/i.test(text)
1354
+ || /\bomx\s+(?:ultragoal|performance-goal|autoresearch-goal)\s+(?:checkpoint|complete)\b/i.test(text)
1355
+ || /\b(?:complete|checkpoint|finish|close|mark)\b.{0,80}\b(?:goal|ultragoal|performance[-\s]goal|autoresearch[-\s]goal)\b/i.test(text)
1356
+ || /\b(?:ultragoal|performance[-\s]goal|autoresearch[-\s]goal)\b.{0,80}\b(?:complete|checkpoint|finish|close|mark)\b/i.test(text)
1357
+ || /(?:^|[.!?]\s+)(?:the\s+)?goal\s+(?:is\s+|now\s+|has\s+been\s+)?(?:complete|completed|finished|closed)(?:\s*(?:[.!?]|$)|\s*[:;]\s*\S|\s*[—–-]\s*\S)/i.test(text);
1288
1358
  }
1289
1359
  async function findActiveGoalWorkflowReconciliationRequirement(cwd) {
1290
1360
  const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
1361
+ const aggregateCompletion = safeObject(ultragoal?.aggregateCompletion);
1362
+ const aggregateProductComplete = safeString(aggregateCompletion.status) === "complete";
1291
1363
  const ultragoals = Array.isArray(ultragoal?.goals) ? ultragoal.goals.map(safeObject) : [];
1292
- const activeUltragoal = ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
1364
+ const activeUltragoal = aggregateProductComplete
1365
+ ? undefined
1366
+ : ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
1293
1367
  if (activeUltragoal) {
1368
+ const goalId = safeString(activeUltragoal.id) || "<goal-id>";
1294
1369
  return {
1295
1370
  workflow: "ultragoal",
1296
- command: `omx ultragoal checkpoint --goal-id ${safeString(activeUltragoal.id) || "<goal-id>"} --status complete --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
1371
+ command: `omx ultragoal checkpoint --goal-id ${goalId} --status complete --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
1372
+ remediation: [
1373
+ `If get_goal returns a completed task-scoped objective for the same aggregate ultragoal plan, checkpoint ${goalId} with evidence naming ${goalId} plus .omx/ultragoal/goals.json or ledger.jsonl and pass final quality-gate JSON; OMX will reconcile the completed planned scope without mutating Codex goal state.`,
1374
+ `If get_goal instead returns a different completed legacy objective and complete checkpointing fails, do not repeat --status complete in this thread.`,
1375
+ `Record the non-terminal blocker with: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<different completed get_goal JSON or path>' --evidence '<completed legacy Codex goal blocks create_goal in this thread>'.`,
1376
+ "Then continue this ultragoal from a fresh Codex thread in the same repo/worktree and create the intended goal there.",
1377
+ ].join(" "),
1297
1378
  };
1298
1379
  }
1299
1380
  const performanceRoot = join(cwd, ".omx", "goals", "performance");
@@ -1337,7 +1418,8 @@ async function buildGoalWorkflowReconciliationPromptWarning(cwd, prompt) {
1337
1418
  `OMX ${requirement.workflow} goal workflow requires Codex goal snapshot reconciliation before completion.`,
1338
1419
  "Call get_goal, pass the resulting JSON or a path with --codex-goal-json, and do not rely on hooks or shell commands to mutate Codex-owned goal state.",
1339
1420
  `Required command shape: ${requirement.command}.`,
1340
- ].join(" ");
1421
+ requirement.remediation,
1422
+ ].filter(Boolean).join(" ");
1341
1423
  }
1342
1424
  async function buildGoalWorkflowReconciliationStopOutput(payload, cwd) {
1343
1425
  const lastAssistantMessage = safeString(payload.last_assistant_message ?? payload.lastAssistantMessage);
@@ -1346,7 +1428,11 @@ async function buildGoalWorkflowReconciliationStopOutput(payload, cwd) {
1346
1428
  const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
1347
1429
  if (!requirement)
1348
1430
  return null;
1349
- const systemMessage = `OMX ${requirement.workflow} requires get_goal snapshot reconciliation before completion; call get_goal and pass --codex-goal-json to ${requirement.command}. Hooks must not mutate Codex goal state.`;
1431
+ const systemMessage = [
1432
+ `OMX ${requirement.workflow} requires get_goal snapshot reconciliation before completion; call get_goal and pass --codex-goal-json to ${requirement.command}.`,
1433
+ requirement.remediation,
1434
+ "Hooks must not mutate Codex goal state.",
1435
+ ].filter(Boolean).join(" ");
1350
1436
  return {
1351
1437
  decision: "block",
1352
1438
  reason: systemMessage,
@@ -1354,15 +1440,15 @@ async function buildGoalWorkflowReconciliationStopOutput(payload, cwd) {
1354
1440
  systemMessage,
1355
1441
  };
1356
1442
  }
1357
- async function readTeamModeStateForStop(cwd, sessionId) {
1443
+ async function readTeamModeStateForStop(cwd, stateDir, sessionId) {
1358
1444
  const normalizedSessionId = safeString(sessionId).trim();
1359
1445
  if (!normalizedSessionId) {
1360
1446
  return await readModeState("team", cwd);
1361
1447
  }
1362
- const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId);
1448
+ const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId, stateDir);
1363
1449
  if (scopedState)
1364
1450
  return scopedState;
1365
- const rootState = await readJsonIfExists(join(cwd, ".omx", "state", "team-state.json"));
1451
+ const rootState = await readJsonIfExists(join(stateDir, "team-state.json"));
1366
1452
  if (rootState?.active !== true)
1367
1453
  return null;
1368
1454
  const ownerSessionId = safeString(rootState.session_id).trim();
@@ -1375,7 +1461,7 @@ async function buildTeamStopOutput(cwd, sessionId) {
1375
1461
  if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, "team")) {
1376
1462
  return null;
1377
1463
  }
1378
- const teamState = await readTeamModeStateForStop(cwd, sessionId);
1464
+ const teamState = await readTeamModeStateForStop(cwd, getBaseStateDir(cwd), sessionId);
1379
1465
  if (teamState?.active !== true)
1380
1466
  return null;
1381
1467
  const teamName = safeString(teamState.team_name).trim();
@@ -1425,10 +1511,10 @@ function hasReleaseReadinessMode(payload) {
1425
1511
  const mode = safeString(payload.mode).trim().toLowerCase();
1426
1512
  return mode === "release-readiness";
1427
1513
  }
1428
- async function hasReleaseReadinessStopMarker(cwd, sessionId, teamName) {
1514
+ async function hasReleaseReadinessStopMarker(cwd, stateDir, sessionId, teamName) {
1429
1515
  if (!sessionId)
1430
1516
  return false;
1431
- const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId);
1517
+ const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId, stateDir);
1432
1518
  if (markerState?.active !== true || markerState.stable_final_recommendation_emitted !== true) {
1433
1519
  return false;
1434
1520
  }
@@ -1463,8 +1549,10 @@ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId) {
1463
1549
  return canonicalSessionId;
1464
1550
  return payloadSessionId;
1465
1551
  }
1466
- async function readStopSessionPinnedState(fileName, cwd, sessionId) {
1467
- const statePath = getStateFilePath(fileName, cwd, sessionId || undefined);
1552
+ async function readStopSessionPinnedState(fileName, cwd, sessionId, stateDir) {
1553
+ const statePath = stateDir && sessionId
1554
+ ? join(stateDir, "sessions", sessionId, fileName)
1555
+ : getStateFilePath(fileName, cwd, sessionId || undefined);
1468
1556
  return readJsonIfExists(statePath);
1469
1557
  }
1470
1558
  function matchesSkillStopContext(entry, state, sessionId, threadId) {
@@ -1499,8 +1587,8 @@ function modeStateMatchesSkillStopContext(state, cwd, sessionId) {
1499
1587
  }
1500
1588
  return true;
1501
1589
  }
1502
- async function readBlockingSkillForStop(cwd, sessionId, threadId, requiredSkill) {
1503
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
1590
+ async function readBlockingSkillForStop(cwd, stateDir, sessionId, threadId, requiredSkill) {
1591
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
1504
1592
  const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
1505
1593
  const candidateSkills = requiredSkill
1506
1594
  ? [requiredSkill]
@@ -1509,7 +1597,7 @@ async function readBlockingSkillForStop(cwd, sessionId, threadId, requiredSkill)
1509
1597
  const terminalRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, skill);
1510
1598
  if (terminalRunState)
1511
1599
  continue;
1512
- const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId);
1600
+ const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
1513
1601
  if (!modeState || modeState.active !== true)
1514
1602
  continue;
1515
1603
  if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId))
@@ -1557,16 +1645,16 @@ function isTerminalOrInactiveModeState(state) {
1557
1645
  const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
1558
1646
  return phase !== "" && TERMINAL_MODE_PHASES.has(phase);
1559
1647
  }
1560
- async function readSessionScopedModeStateForRootSkill(cwd, skill, sessionIds) {
1648
+ async function readSessionScopedModeStateForRootSkill(cwd, stateDir, skill, sessionIds) {
1561
1649
  for (const sessionId of sessionIds) {
1562
- const state = await readJsonIfExists(getStateFilePath(`${skill}-state.json`, cwd, sessionId));
1650
+ const state = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
1563
1651
  if (state)
1564
1652
  return state;
1565
1653
  }
1566
1654
  return null;
1567
1655
  }
1568
- async function reconcileStaleRootSkillActiveStateForStop(cwd, sessionId) {
1569
- const { rootPath } = getSkillActiveStatePaths(cwd);
1656
+ async function reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, sessionId) {
1657
+ const { rootPath } = getSkillActiveStatePathsForStateDir(stateDir);
1570
1658
  const rootState = await readSkillActiveState(rootPath);
1571
1659
  if (!rootState?.active)
1572
1660
  return;
@@ -1593,7 +1681,7 @@ async function reconcileStaleRootSkillActiveStateForStop(cwd, sessionId) {
1593
1681
  initializedSessionId,
1594
1682
  safeString(rootState.session_id),
1595
1683
  ]);
1596
- const modeState = await readSessionScopedModeStateForRootSkill(cwd, skill, candidateSessionIds);
1684
+ const modeState = await readSessionScopedModeStateForRootSkill(cwd, stateDir, skill, candidateSessionIds);
1597
1685
  if (isTerminalOrInactiveModeState(modeState)) {
1598
1686
  changed = true;
1599
1687
  continue;
@@ -1652,17 +1740,17 @@ function buildRalplanContinuationStatus(blocker, activeSubagentCount) {
1652
1740
  systemMessage: `OMX ralplan status: continue_from_artifact at phase ${phase}; continue from the current ralplan artifact and finish by stating whether ralplan is complete, paused for review, waiting for input, or still continuing.`,
1653
1741
  };
1654
1742
  }
1655
- async function readStopAutoNudgePhase(cwd, sessionId, threadId) {
1743
+ async function readStopAutoNudgePhase(cwd, stateDir, sessionId, threadId) {
1656
1744
  const normalizedSessionId = sessionId.trim();
1657
1745
  if (normalizedSessionId) {
1658
- const scopedModeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId);
1746
+ const scopedModeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId, stateDir);
1659
1747
  if (scopedModeState?.active === true
1660
1748
  && safeString(scopedModeState.current_phase).trim().toLowerCase() === "intent-first") {
1661
1749
  return "planning";
1662
1750
  }
1663
1751
  }
1664
1752
  else {
1665
- const rootModeState = await readJsonIfExists(join(cwd, ".omx", "state", "deep-interview-state.json"));
1753
+ const rootModeState = await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
1666
1754
  if (rootModeState?.active === true
1667
1755
  && safeString(rootModeState.current_phase).trim().toLowerCase() === "intent-first") {
1668
1756
  return "planning";
@@ -1670,21 +1758,21 @@ async function readStopAutoNudgePhase(cwd, sessionId, threadId) {
1670
1758
  }
1671
1759
  if (!normalizedSessionId)
1672
1760
  return "";
1673
- const canonicalState = await readVisibleSkillActiveState(cwd, normalizedSessionId);
1761
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, normalizedSessionId);
1674
1762
  const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
1675
1763
  const deepInterview = visibleEntries.find((entry) => (entry.skill === "deep-interview"
1676
1764
  && matchesSkillStopContext(entry, canonicalState ?? {}, normalizedSessionId, threadId)));
1677
1765
  if (!deepInterview)
1678
1766
  return "";
1679
- const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId);
1767
+ const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId, stateDir);
1680
1768
  if (!modeState || modeState.active !== true)
1681
1769
  return "";
1682
1770
  const modePhase = safeString(modeState.current_phase).trim().toLowerCase();
1683
1771
  return modePhase === "intent-first" ? "planning" : "";
1684
1772
  }
1685
- async function buildDeepInterviewQuestionStopOutput(cwd, sessionId, threadId) {
1773
+ async function buildDeepInterviewQuestionStopOutput(cwd, stateDir, sessionId, threadId) {
1686
1774
  await reconcileDeepInterviewQuestionEnforcementFromAnsweredRecords(cwd, sessionId);
1687
- const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId);
1775
+ const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir);
1688
1776
  if (!modeState)
1689
1777
  return null;
1690
1778
  const questionEnforcement = safeObject(modeState.question_enforcement);
@@ -1695,7 +1783,7 @@ async function buildDeepInterviewQuestionStopOutput(cwd, sessionId, threadId) {
1695
1783
  if (TERMINAL_MODE_PHASES.has(phase.toLowerCase()) || phase === "completing") {
1696
1784
  return null;
1697
1785
  }
1698
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
1786
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
1699
1787
  if (canonicalState) {
1700
1788
  const blocker = listActiveSkills(canonicalState).find((entry) => (entry.skill === "deep-interview"
1701
1789
  && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
@@ -1836,8 +1924,8 @@ async function findCanonicalActiveTeamForSession(cwd, sessionId) {
1836
1924
  }
1837
1925
  return null;
1838
1926
  }
1839
- async function resolveActiveTeamNameForStop(cwd, sessionId) {
1840
- const directState = await readTeamModeStateForStop(cwd, sessionId);
1927
+ async function resolveActiveTeamNameForStop(cwd, stateDir, sessionId) {
1928
+ const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId);
1841
1929
  const directTeamName = safeString(directState?.team_name).trim();
1842
1930
  if (directState?.active === true && directTeamName)
1843
1931
  return directTeamName;
@@ -1847,11 +1935,11 @@ async function resolveActiveTeamNameForStop(cwd, sessionId) {
1847
1935
  async function maybeBuildReleaseReadinessFinalizeStopOutput(payload, cwd, stateDir, sessionId) {
1848
1936
  if (!sessionId)
1849
1937
  return { matched: false, output: null };
1850
- const teamName = await resolveActiveTeamNameForStop(cwd, sessionId);
1938
+ const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId);
1851
1939
  if (!teamName)
1852
1940
  return { matched: false, output: null };
1853
1941
  const explicitReleaseReadinessContext = hasReleaseReadinessMode(payload)
1854
- || await hasReleaseReadinessStopMarker(cwd, sessionId, teamName);
1942
+ || await hasReleaseReadinessStopMarker(cwd, stateDir, sessionId, teamName);
1855
1943
  if (!explicitReleaseReadinessContext) {
1856
1944
  return { matched: false, output: null };
1857
1945
  }
@@ -1873,8 +1961,8 @@ async function maybeBuildReleaseReadinessFinalizeStopOutput(payload, cwd, stateD
1873
1961
  }, sessionId);
1874
1962
  return { matched: true, output };
1875
1963
  }
1876
- async function buildSkillStopOutput(cwd, sessionId, threadId) {
1877
- const blocker = await readBlockingSkillForStop(cwd, sessionId, threadId);
1964
+ async function buildSkillStopOutput(cwd, stateDir, sessionId, threadId) {
1965
+ const blocker = await readBlockingSkillForStop(cwd, stateDir, sessionId, threadId);
1878
1966
  if (!blocker)
1879
1967
  return null;
1880
1968
  const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
@@ -1983,18 +2071,33 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
1983
2071
  const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, sessionId);
1984
2072
  const threadId = readPayloadThreadId(payload);
1985
2073
  if (canonicalSessionId) {
1986
- await reconcileStaleRootSkillActiveStateForStop(cwd, canonicalSessionId);
2074
+ await reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, canonicalSessionId);
1987
2075
  }
1988
2076
  const execFollowupOutput = await buildExecFollowupStopOutput(cwd, canonicalSessionId);
1989
2077
  if (execFollowupOutput)
1990
2078
  return execFollowupOutput;
2079
+ const ralphOwnerContext = {
2080
+ payloadSessionId: sessionId,
2081
+ threadId,
2082
+ tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
2083
+ };
2084
+ const ralphCompletionAuditBlock = options.skipRalphStopBlock === true
2085
+ ? null
2086
+ : await readRalphCompletionAuditBlockState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
2087
+ if (ralphCompletionAuditBlock) {
2088
+ await reopenRalphCompletionAuditBlock(ralphCompletionAuditBlock);
2089
+ const blockingPath = formatStopStatePath(cwd, ralphCompletionAuditBlock.path);
2090
+ const systemMessage = `OMX Ralph completion audit is missing required evidence (${ralphCompletionAuditBlock.reason}; state: ${blockingPath}); continue verification, record a prompt-to-artifact checklist plus verification evidence, and do not report complete yet.`;
2091
+ return await returnPersistentStopBlock(payload, stateDir, "ralph-completion-audit-stop", `${blockingPath}|${ralphCompletionAuditBlock.reason}`, {
2092
+ decision: "block",
2093
+ reason: systemMessage,
2094
+ stopReason: `ralph_completion_audit_${ralphCompletionAuditBlock.reason}`,
2095
+ systemMessage,
2096
+ }, canonicalSessionId, { allowRepeatDuringStopHook: true });
2097
+ }
1991
2098
  const ralphState = options.skipRalphStopBlock === true
1992
2099
  ? null
1993
- : await readActiveRalphState(stateDir, canonicalSessionId, {
1994
- payloadSessionId: sessionId,
1995
- threadId,
1996
- tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
1997
- });
2100
+ : await readActiveRalphState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
1998
2101
  if (!ralphState) {
1999
2102
  const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
2000
2103
  if (autoresearchState) {
@@ -2047,7 +2150,7 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
2047
2150
  return await returnPersistentStopBlock(payload, stateDir, "team-stop", safeString(teamOutput.stopReason), teamOutput, canonicalSessionId);
2048
2151
  }
2049
2152
  if (canonicalSessionId) {
2050
- const deepInterviewQuestionOutput = await buildDeepInterviewQuestionStopOutput(cwd, canonicalSessionId, threadId);
2153
+ const deepInterviewQuestionOutput = await buildDeepInterviewQuestionStopOutput(cwd, stateDir, canonicalSessionId, threadId);
2051
2154
  if (deepInterviewQuestionOutput) {
2052
2155
  return await returnPersistentStopBlock(payload, stateDir, "deep-interview-question-stop", deepInterviewQuestionOutput.obligationId, deepInterviewQuestionOutput.output, canonicalSessionId);
2053
2156
  }
@@ -2060,7 +2163,7 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
2060
2163
  if (repeatedCanonicalTeamOutput)
2061
2164
  return repeatedCanonicalTeamOutput;
2062
2165
  }
2063
- const skillOutput = await buildSkillStopOutput(cwd, canonicalSessionId, threadId);
2166
+ const skillOutput = await buildSkillStopOutput(cwd, stateDir, canonicalSessionId, threadId);
2064
2167
  if (skillOutput) {
2065
2168
  return await returnPersistentStopBlock(payload, stateDir, "skill-stop", safeString(skillOutput.stopReason), skillOutput, canonicalSessionId);
2066
2169
  }
@@ -2071,7 +2174,7 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
2071
2174
  return await returnPersistentStopBlock(payload, stateDir, "goal-workflow-reconciliation-stop", safeString(goalWorkflowStopOutput.stopReason), goalWorkflowStopOutput, canonicalSessionId, { allowRepeatDuringStopHook: true });
2072
2175
  }
2073
2176
  const autoNudgeConfig = await loadAutoNudgeConfig();
2074
- const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
2177
+ const autoNudgePhase = await readStopAutoNudgePhase(cwd, stateDir, canonicalSessionId, threadId);
2075
2178
  if (autoNudgeConfig.enabled
2076
2179
  && detectNativeStopStallPattern(lastAssistantMessage, autoNudgeConfig.patterns, autoNudgePhase)) {
2077
2180
  const effectiveResponse = resolveEffectiveAutoNudgeResponse(autoNudgeConfig.response);
@@ -2109,7 +2212,9 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
2109
2212
  export async function dispatchCodexNativeHook(payload, options = {}) {
2110
2213
  const hookEventName = readHookEventName(payload);
2111
2214
  const cwd = options.cwd ?? (safeString(payload.cwd).trim() || process.cwd());
2112
- const stateDir = join(cwd, ".omx", "state");
2215
+ // Native hooks must use the same authoritative runtime state root as HUD/MCP
2216
+ // when boxed/team roots are active; do not bypass it with cwd/.omx/state.
2217
+ const stateDir = getBaseStateDir(cwd);
2113
2218
  await mkdir(stateDir, { recursive: true });
2114
2219
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
2115
2220
  let skillState = null;
@@ -2179,6 +2284,7 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
2179
2284
  if (prompt && !isSubagentPromptSubmit) {
2180
2285
  skillState = buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessionIdForState, threadId || undefined, turnId || undefined) ?? await recordSkillActivation({
2181
2286
  stateDir,
2287
+ sourceCwd: cwd,
2182
2288
  text: prompt,
2183
2289
  sessionId: sessionIdForState,
2184
2290
  threadId,