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
@@ -6,10 +6,10 @@ import { pathToFileURL } from "url";
6
6
  import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
7
7
  import {
8
8
  extractSessionIdFromInitializedStatePath,
9
- getSkillActiveStatePaths,
9
+ getSkillActiveStatePathsForStateDir,
10
10
  listActiveSkills,
11
11
  readSkillActiveState,
12
- readVisibleSkillActiveState,
12
+ readVisibleSkillActiveStateForStateDir,
13
13
  type SkillActiveStateLike,
14
14
  } from "../state/skill-active.js";
15
15
  import {
@@ -33,9 +33,9 @@ import {
33
33
  writeTeamLeaderAttention,
34
34
  writeTeamPhase,
35
35
  } from "../team/state.js";
36
- import { omxNotepadPath, omxProjectMemoryPath } from "../utils/paths.js";
36
+ import { omxNotepadPath, resolveProjectMemoryPath } from "../utils/paths.js";
37
37
  import { findGitLayout } from "../utils/git-layout.js";
38
- import { getStateFilePath, getStatePath } from "../mcp/state-paths.js";
38
+ import { getBaseStateDir, getStateFilePath, getStatePath } from "../mcp/state-paths.js";
39
39
  import {
40
40
  detectKeywords,
41
41
  detectPrimaryKeyword,
@@ -76,6 +76,7 @@ import {
76
76
  } from "../wiki/lifecycle.js";
77
77
  import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
78
78
  import { readRunState } from "../runtime/run-state.js";
79
+ import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
79
80
  import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
80
81
  import { triagePrompt } from "../hooks/triage-heuristic.js";
81
82
  import { readTriageConfig } from "../hooks/triage-config.js";
@@ -156,6 +157,12 @@ function safeObject(value: unknown): Record<string, unknown> {
156
157
  return value && typeof value === "object" ? value as Record<string, unknown> : {};
157
158
  }
158
159
 
160
+ function safeContextSnippet(value: unknown, maxLength = 300): string {
161
+ const text = safeString(value).replace(/\s+/g, " ").trim();
162
+ if (text.length <= maxLength) return text;
163
+ return `${text.slice(0, maxLength - 1).trimEnd()}…`;
164
+ }
165
+
159
166
  interface NativeSubagentSessionStartMetadata {
160
167
  parentThreadId: string;
161
168
  agentNickname?: string;
@@ -485,6 +492,12 @@ interface ActiveRalphStopState {
485
492
  path: string;
486
493
  }
487
494
 
495
+ interface RalphCompletionAuditBlockState {
496
+ state: Record<string, unknown>;
497
+ path: string;
498
+ reason: string;
499
+ }
500
+
488
501
  interface RalphStopOwnershipContext {
489
502
  sessionId: string;
490
503
  payloadSessionId: string;
@@ -559,8 +572,8 @@ async function readCanonicalTerminalRunStateForStop(
559
572
  return shouldHonorCanonicalTerminalRunState(runRecord, mode) ? runRecord : null;
560
573
  }
561
574
 
562
- async function isVisibleRalphActiveForSession(cwd: string, sessionId: string): Promise<boolean> {
563
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
575
+ async function isVisibleRalphActiveForSession(stateDir: string, sessionId: string): Promise<boolean> {
576
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
564
577
  if (!canonicalState) return false;
565
578
  return listActiveSkills(canonicalState).some((entry) => (
566
579
  entry.skill === "ralph"
@@ -568,8 +581,8 @@ async function isVisibleRalphActiveForSession(cwd: string, sessionId: string): P
568
581
  ));
569
582
  }
570
583
 
571
- async function hasConsistentRalphSkillActivation(cwd: string, sessionId: string): Promise<boolean> {
572
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
584
+ async function hasConsistentRalphSkillActivation(stateDir: string, sessionId: string): Promise<boolean> {
585
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
573
586
  if (!canonicalState) return true;
574
587
 
575
588
  const initializedMode = safeString(canonicalState.initialized_mode).trim();
@@ -581,7 +594,74 @@ async function hasConsistentRalphSkillActivation(cwd: string, sessionId: string)
581
594
  return true;
582
595
  }
583
596
 
597
+
598
+ async function readRalphCompletionAuditBlockState(
599
+ cwd: string,
600
+ stateDir: string,
601
+ preferredSessionId?: string,
602
+ ownerContext?: {
603
+ payloadSessionId?: string;
604
+ threadId?: string;
605
+ tmuxPaneId?: string;
606
+ },
607
+ ): Promise<RalphCompletionAuditBlockState | null> {
608
+ const [rawSessionInfo, usableSessionInfo] = await Promise.all([
609
+ readSessionState(cwd),
610
+ readUsableSessionState(cwd),
611
+ ]);
612
+ const currentOmxSessionId = safeString(usableSessionInfo?.session_id).trim();
613
+ const currentNativeSessionId = safeString(usableSessionInfo?.native_session_id).trim();
614
+ const staleCurrentSessionId = rawSessionInfo && !isSessionStateUsable(rawSessionInfo, cwd)
615
+ ? safeString(rawSessionInfo.session_id).trim()
616
+ : "";
617
+ const sessionCandidates = [...new Set([
618
+ safeString(preferredSessionId).trim(),
619
+ currentOmxSessionId,
620
+ ].filter(Boolean))];
621
+
622
+ const evaluateCandidate = (state: Record<string, unknown> | null, path: string, sessionId: string): RalphCompletionAuditBlockState | null => {
623
+ if (!state || state.mode && safeString(state.mode) !== "ralph") return null;
624
+ if (!isRalphCompletePhase(state.current_phase ?? state.currentPhase)) return null;
625
+ if (activeRalphStateMatchesStopOwner(state, {
626
+ sessionId,
627
+ payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
628
+ threadId: safeString(ownerContext?.threadId).trim(),
629
+ currentNativeSessionId,
630
+ tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
631
+ }) !== true) return null;
632
+ const audit = evaluateRalphCompletionAuditEvidence(state, cwd);
633
+ return audit.complete ? null : { state, path, reason: audit.reason };
634
+ };
635
+
636
+ for (const sessionId of sessionCandidates) {
637
+ if (staleCurrentSessionId && sessionId === staleCurrentSessionId) continue;
638
+ const sessionScopedPath = getStateFilePath("ralph-state.json", cwd, sessionId);
639
+ const result = evaluateCandidate(await readJsonIfExists(sessionScopedPath), sessionScopedPath, sessionId);
640
+ if (result) return result;
641
+ }
642
+
643
+ if (sessionCandidates.length > 0) return null;
644
+
645
+ const directPath = join(stateDir, "ralph-state.json");
646
+ return evaluateCandidate(await readJsonIfExists(directPath), directPath, "");
647
+ }
648
+
649
+ async function reopenRalphCompletionAuditBlock(block: RalphCompletionAuditBlockState): Promise<void> {
650
+ const nowIso = new Date().toISOString();
651
+ const next: Record<string, unknown> = {
652
+ ...block.state,
653
+ active: true,
654
+ current_phase: "verifying",
655
+ completion_audit_gate: "blocked",
656
+ completion_audit_missing_reason: block.reason,
657
+ completion_audit_blocked_at: nowIso,
658
+ };
659
+ delete next.completed_at;
660
+ await writeFile(block.path, JSON.stringify(next, null, 2));
661
+ }
662
+
584
663
  async function readActiveRalphState(
664
+ cwd: string,
585
665
  stateDir: string,
586
666
  preferredSessionId?: string,
587
667
  ownerContext?: {
@@ -590,7 +670,6 @@ async function readActiveRalphState(
590
670
  tmuxPaneId?: string;
591
671
  },
592
672
  ): Promise<ActiveRalphStopState | null> {
593
- const cwd = resolve(stateDir, "..", "..");
594
673
  const [rawSessionInfo, usableSessionInfo] = await Promise.all([
595
674
  readSessionState(cwd),
596
675
  readUsableSessionState(cwd),
@@ -620,7 +699,7 @@ async function readActiveRalphState(
620
699
  if (
621
700
  sessionScoped?.active === true
622
701
  && isRalphStartingPhase(sessionScoped)
623
- && !(await isVisibleRalphActiveForSession(cwd, sessionId))
702
+ && !(await isVisibleRalphActiveForSession(stateDir, sessionId))
624
703
  ) {
625
704
  continue;
626
705
  }
@@ -634,7 +713,7 @@ async function readActiveRalphState(
634
713
  currentNativeSessionId,
635
714
  tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
636
715
  })
637
- && await hasConsistentRalphSkillActivation(cwd, sessionId)
716
+ && await hasConsistentRalphSkillActivation(stateDir, sessionId)
638
717
  ) {
639
718
  return { state: sessionScoped, path: sessionScopedPath };
640
719
  }
@@ -1049,28 +1128,31 @@ async function buildSessionStartContext(
1049
1128
  sections.push(["[Active OMX modes]", ...modeSummaries].join("\n"));
1050
1129
  }
1051
1130
 
1052
- const projectMemory = await readJsonIfExists(omxProjectMemoryPath(cwd));
1053
- if (projectMemory) {
1131
+ const projectMemoryPath = resolveProjectMemoryPath(cwd);
1132
+ const projectMemory = projectMemoryPath ? await readJsonIfExists(projectMemoryPath) : null;
1133
+ if (projectMemory && projectMemoryPath) {
1054
1134
  const directives = Array.isArray(projectMemory.directives) ? projectMemory.directives : [];
1055
1135
  const notes = Array.isArray(projectMemory.notes) ? projectMemory.notes : [];
1056
- const techStack = safeString(projectMemory.techStack).trim();
1057
- const conventions = safeString(projectMemory.conventions).trim();
1058
- const build = safeString(projectMemory.build).trim();
1136
+ const techStack = safeContextSnippet(projectMemory.techStack);
1137
+ const conventions = safeContextSnippet(projectMemory.conventions);
1138
+ const build = safeContextSnippet(projectMemory.build);
1059
1139
  const summary: string[] = [];
1140
+ const relativeMemoryPath = relative(cwd, projectMemoryPath).replace(/\\/g, "/");
1141
+ summary.push(`- source: ${relativeMemoryPath === "project-memory.json" ? "project-memory.json" : ".omx/project-memory.json"}`);
1060
1142
  if (techStack) summary.push(`- stack: ${techStack}`);
1061
1143
  if (conventions) summary.push(`- conventions: ${conventions}`);
1062
1144
  if (build) summary.push(`- build: ${build}`);
1063
1145
  if (directives.length > 0) {
1064
1146
  const firstDirective = directives[0] as Record<string, unknown>;
1065
- const directive = safeString(firstDirective.directive).trim();
1147
+ const directive = safeContextSnippet(firstDirective.directive);
1066
1148
  if (directive) summary.push(`- directive: ${directive}`);
1067
1149
  }
1068
1150
  if (notes.length > 0) {
1069
1151
  const firstNote = notes[0] as Record<string, unknown>;
1070
- const note = safeString(firstNote.content).trim();
1152
+ const note = safeContextSnippet(firstNote.content);
1071
1153
  if (note) summary.push(`- note: ${note}`);
1072
1154
  }
1073
- if (summary.length > 0) {
1155
+ if (summary.length > 1) {
1074
1156
  sections.push(["[Project memory]", ...summary].join("\n"));
1075
1157
  }
1076
1158
  }
@@ -1313,6 +1395,10 @@ function buildNativeOutsideTmuxTeamPromptBlockState(
1313
1395
  };
1314
1396
  }
1315
1397
 
1398
+ function buildSkillStateCliInstruction(mode: string, statePath: string): string {
1399
+ 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.`;
1400
+ }
1401
+
1316
1402
  function buildAdditionalContextMessage(
1317
1403
  prompt: string,
1318
1404
  skillState?: SkillActiveState | null,
@@ -1374,7 +1460,7 @@ function buildAdditionalContextMessage(
1374
1460
  promptPriorityMessage,
1375
1461
  ultragoalPromptActivationNote,
1376
1462
  skillState.initialized_mode && skillState.initialized_state_path
1377
- ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
1463
+ ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1378
1464
  : null,
1379
1465
  teamDetected
1380
1466
  ? buildTeamRuntimeInstruction(cwd, payload)
@@ -1386,7 +1472,7 @@ function buildAdditionalContextMessage(
1386
1472
 
1387
1473
  if (teamDetected) {
1388
1474
  const initializedStateMessage = skillState?.initialized_mode && skillState.initialized_state_path
1389
- ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
1475
+ ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1390
1476
  : null;
1391
1477
  return [
1392
1478
  detectedKeywordMessage,
@@ -1413,7 +1499,7 @@ function buildAdditionalContextMessage(
1413
1499
  ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
1414
1500
  : null,
1415
1501
  promptPriorityMessage,
1416
- `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
1502
+ buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path),
1417
1503
  deepInterviewPromptActivationNote,
1418
1504
  ultraworkPromptActivationNote,
1419
1505
  ultragoalPromptActivationNote,
@@ -1480,7 +1566,7 @@ async function resolveTeamWorkerStopDecision(
1480
1566
  const blockWorkerStop = (
1481
1567
  reasonCode: string,
1482
1568
  detail: string,
1483
- stateDirForDecision = join(cwd, ".omx", "state"),
1569
+ stateDirForDecision = getBaseStateDir(cwd),
1484
1570
  ): TeamWorkerStopDecision => ({
1485
1571
  kind: "blocked",
1486
1572
  stateDir: stateDirForDecision,
@@ -1618,20 +1704,33 @@ async function buildModeBasedStopOutput(
1618
1704
  };
1619
1705
  }
1620
1706
 
1621
- function looksLikeGoalCompletionPrompt(text: string): boolean {
1622
- return /\b(?:complete|checkpoint|finish|close|mark)\b.{0,80}\b(?:goal|ultragoal|performance-goal|autoresearch-goal)\b/i.test(text)
1623
- || /\bupdate_goal\s*\(/i.test(text)
1624
- || /\bomx\s+(?:ultragoal|performance-goal|autoresearch-goal)\s+(?:checkpoint|complete)\b/i.test(text);
1707
+ export function looksLikeGoalCompletionPrompt(text: string): boolean {
1708
+ return /\bupdate_goal\s*\(/i.test(text)
1709
+ || /\bomx\s+(?:ultragoal|performance-goal|autoresearch-goal)\s+(?:checkpoint|complete)\b/i.test(text)
1710
+ || /\b(?:complete|checkpoint|finish|close|mark)\b.{0,80}\b(?:goal|ultragoal|performance[-\s]goal|autoresearch[-\s]goal)\b/i.test(text)
1711
+ || /\b(?:ultragoal|performance[-\s]goal|autoresearch[-\s]goal)\b.{0,80}\b(?:complete|checkpoint|finish|close|mark)\b/i.test(text)
1712
+ || /(?:^|[.!?]\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);
1625
1713
  }
1626
1714
 
1627
- async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string } | null> {
1715
+ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string; remediation?: string } | null> {
1628
1716
  const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
1717
+ const aggregateCompletion = safeObject(ultragoal?.aggregateCompletion);
1718
+ const aggregateProductComplete = safeString(aggregateCompletion.status) === "complete";
1629
1719
  const ultragoals = Array.isArray(ultragoal?.goals) ? ultragoal.goals.map(safeObject) : [];
1630
- const activeUltragoal = ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
1720
+ const activeUltragoal = aggregateProductComplete
1721
+ ? undefined
1722
+ : ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
1631
1723
  if (activeUltragoal) {
1724
+ const goalId = safeString(activeUltragoal.id) || "<goal-id>";
1632
1725
  return {
1633
1726
  workflow: "ultragoal",
1634
- command: `omx ultragoal checkpoint --goal-id ${safeString(activeUltragoal.id) || "<goal-id>"} --status complete --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
1727
+ command: `omx ultragoal checkpoint --goal-id ${goalId} --status complete --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
1728
+ remediation: [
1729
+ `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.`,
1730
+ `If get_goal instead returns a different completed legacy objective and complete checkpointing fails, do not repeat --status complete in this thread.`,
1731
+ `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>'.`,
1732
+ "Then continue this ultragoal from a fresh Codex thread in the same repo/worktree and create the intended goal there.",
1733
+ ].join(" "),
1635
1734
  };
1636
1735
  }
1637
1736
 
@@ -1675,7 +1774,8 @@ async function buildGoalWorkflowReconciliationPromptWarning(cwd: string, prompt:
1675
1774
  `OMX ${requirement.workflow} goal workflow requires Codex goal snapshot reconciliation before completion.`,
1676
1775
  "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.",
1677
1776
  `Required command shape: ${requirement.command}.`,
1678
- ].join(" ");
1777
+ requirement.remediation,
1778
+ ].filter(Boolean).join(" ");
1679
1779
  }
1680
1780
 
1681
1781
  async function buildGoalWorkflowReconciliationStopOutput(
@@ -1687,7 +1787,11 @@ async function buildGoalWorkflowReconciliationStopOutput(
1687
1787
  const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
1688
1788
  if (!requirement) return null;
1689
1789
  const systemMessage =
1690
- `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.`;
1790
+ [
1791
+ `OMX ${requirement.workflow} requires get_goal snapshot reconciliation before completion; call get_goal and pass --codex-goal-json to ${requirement.command}.`,
1792
+ requirement.remediation,
1793
+ "Hooks must not mutate Codex goal state.",
1794
+ ].filter(Boolean).join(" ");
1691
1795
  return {
1692
1796
  decision: "block",
1693
1797
  reason: systemMessage,
@@ -1698,6 +1802,7 @@ async function buildGoalWorkflowReconciliationStopOutput(
1698
1802
 
1699
1803
  async function readTeamModeStateForStop(
1700
1804
  cwd: string,
1805
+ stateDir: string,
1701
1806
  sessionId?: string,
1702
1807
  ): Promise<Record<string, unknown> | null> {
1703
1808
  const normalizedSessionId = safeString(sessionId).trim();
@@ -1705,10 +1810,10 @@ async function readTeamModeStateForStop(
1705
1810
  return await readModeState("team", cwd);
1706
1811
  }
1707
1812
 
1708
- const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId);
1813
+ const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId, stateDir);
1709
1814
  if (scopedState) return scopedState;
1710
1815
 
1711
- const rootState = await readJsonIfExists(join(cwd, ".omx", "state", "team-state.json"));
1816
+ const rootState = await readJsonIfExists(join(stateDir, "team-state.json"));
1712
1817
  if (rootState?.active !== true) return null;
1713
1818
 
1714
1819
  const ownerSessionId = safeString(rootState.session_id).trim();
@@ -1723,7 +1828,7 @@ async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Rec
1723
1828
  if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, "team")) {
1724
1829
  return null;
1725
1830
  }
1726
- const teamState = await readTeamModeStateForStop(cwd, sessionId);
1831
+ const teamState = await readTeamModeStateForStop(cwd, getBaseStateDir(cwd), sessionId);
1727
1832
  if (teamState?.active !== true) return null;
1728
1833
  const teamName = safeString(teamState.team_name).trim();
1729
1834
  if (teamName) {
@@ -1781,12 +1886,13 @@ function hasReleaseReadinessMode(payload: CodexHookPayload): boolean {
1781
1886
 
1782
1887
  async function hasReleaseReadinessStopMarker(
1783
1888
  cwd: string,
1889
+ stateDir: string,
1784
1890
  sessionId: string,
1785
1891
  teamName: string,
1786
1892
  ): Promise<boolean> {
1787
1893
  if (!sessionId) return false;
1788
1894
 
1789
- const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId);
1895
+ const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId, stateDir);
1790
1896
  if (markerState?.active !== true || markerState.stable_final_recommendation_emitted !== true) {
1791
1897
  return false;
1792
1898
  }
@@ -1831,8 +1937,11 @@ async function readStopSessionPinnedState(
1831
1937
  fileName: string,
1832
1938
  cwd: string,
1833
1939
  sessionId: string,
1940
+ stateDir?: string,
1834
1941
  ): Promise<Record<string, unknown> | null> {
1835
- const statePath = getStateFilePath(fileName, cwd, sessionId || undefined);
1942
+ const statePath = stateDir && sessionId
1943
+ ? join(stateDir, "sessions", sessionId, fileName)
1944
+ : getStateFilePath(fileName, cwd, sessionId || undefined);
1836
1945
  return readJsonIfExists(statePath);
1837
1946
  }
1838
1947
 
@@ -1883,11 +1992,12 @@ function modeStateMatchesSkillStopContext(
1883
1992
 
1884
1993
  async function readBlockingSkillForStop(
1885
1994
  cwd: string,
1995
+ stateDir: string,
1886
1996
  sessionId: string,
1887
1997
  threadId: string,
1888
1998
  requiredSkill?: string,
1889
1999
  ): Promise<{ skill: string; phase: string; latestPlanPath?: string; planningComplete?: boolean; runOutcome?: string } | null> {
1890
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
2000
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
1891
2001
  const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
1892
2002
  const candidateSkills = requiredSkill
1893
2003
  ? [requiredSkill]
@@ -1897,7 +2007,7 @@ async function readBlockingSkillForStop(
1897
2007
  const terminalRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, skill);
1898
2008
  if (terminalRunState) continue;
1899
2009
 
1900
- const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId);
2010
+ const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
1901
2011
  if (!modeState || modeState.active !== true) continue;
1902
2012
  if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) continue;
1903
2013
 
@@ -1957,11 +2067,12 @@ function isTerminalOrInactiveModeState(state: Record<string, unknown> | null): b
1957
2067
 
1958
2068
  async function readSessionScopedModeStateForRootSkill(
1959
2069
  cwd: string,
2070
+ stateDir: string,
1960
2071
  skill: string,
1961
2072
  sessionIds: string[],
1962
2073
  ): Promise<Record<string, unknown> | null> {
1963
2074
  for (const sessionId of sessionIds) {
1964
- const state = await readJsonIfExists(getStateFilePath(`${skill}-state.json`, cwd, sessionId));
2075
+ const state = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
1965
2076
  if (state) return state;
1966
2077
  }
1967
2078
  return null;
@@ -1969,9 +2080,10 @@ async function readSessionScopedModeStateForRootSkill(
1969
2080
 
1970
2081
  async function reconcileStaleRootSkillActiveStateForStop(
1971
2082
  cwd: string,
2083
+ stateDir: string,
1972
2084
  sessionId: string,
1973
2085
  ): Promise<void> {
1974
- const { rootPath } = getSkillActiveStatePaths(cwd);
2086
+ const { rootPath } = getSkillActiveStatePathsForStateDir(stateDir);
1975
2087
  const rootState = await readSkillActiveState(rootPath);
1976
2088
  if (!rootState?.active) return;
1977
2089
 
@@ -1997,7 +2109,7 @@ async function reconcileStaleRootSkillActiveStateForStop(
1997
2109
  initializedSessionId,
1998
2110
  safeString(rootState.session_id),
1999
2111
  ]);
2000
- const modeState = await readSessionScopedModeStateForRootSkill(cwd, skill, candidateSessionIds);
2112
+ const modeState = await readSessionScopedModeStateForRootSkill(cwd, stateDir, skill, candidateSessionIds);
2001
2113
  if (isTerminalOrInactiveModeState(modeState)) {
2002
2114
  changed = true;
2003
2115
  continue;
@@ -2076,12 +2188,13 @@ function buildRalplanContinuationStatus(
2076
2188
 
2077
2189
  async function readStopAutoNudgePhase(
2078
2190
  cwd: string,
2191
+ stateDir: string,
2079
2192
  sessionId: string,
2080
2193
  threadId: string,
2081
2194
  ): Promise<string> {
2082
2195
  const normalizedSessionId = sessionId.trim();
2083
2196
  if (normalizedSessionId) {
2084
- const scopedModeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId);
2197
+ const scopedModeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId, stateDir);
2085
2198
  if (
2086
2199
  scopedModeState?.active === true
2087
2200
  && safeString(scopedModeState.current_phase).trim().toLowerCase() === "intent-first"
@@ -2089,7 +2202,7 @@ async function readStopAutoNudgePhase(
2089
2202
  return "planning";
2090
2203
  }
2091
2204
  } else {
2092
- const rootModeState = await readJsonIfExists(join(cwd, ".omx", "state", "deep-interview-state.json"));
2205
+ const rootModeState = await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
2093
2206
  if (
2094
2207
  rootModeState?.active === true
2095
2208
  && safeString(rootModeState.current_phase).trim().toLowerCase() === "intent-first"
@@ -2100,7 +2213,7 @@ async function readStopAutoNudgePhase(
2100
2213
 
2101
2214
  if (!normalizedSessionId) return "";
2102
2215
 
2103
- const canonicalState = await readVisibleSkillActiveState(cwd, normalizedSessionId);
2216
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, normalizedSessionId);
2104
2217
  const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
2105
2218
  const deepInterview = visibleEntries.find((entry) => (
2106
2219
  entry.skill === "deep-interview"
@@ -2108,7 +2221,7 @@ async function readStopAutoNudgePhase(
2108
2221
  ));
2109
2222
  if (!deepInterview) return "";
2110
2223
 
2111
- const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId);
2224
+ const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId, stateDir);
2112
2225
  if (!modeState || modeState.active !== true) return "";
2113
2226
 
2114
2227
  const modePhase = safeString(modeState.current_phase).trim().toLowerCase();
@@ -2117,11 +2230,12 @@ async function readStopAutoNudgePhase(
2117
2230
 
2118
2231
  async function buildDeepInterviewQuestionStopOutput(
2119
2232
  cwd: string,
2233
+ stateDir: string,
2120
2234
  sessionId: string,
2121
2235
  threadId: string,
2122
2236
  ): Promise<{ output: Record<string, unknown>; obligationId: string } | null> {
2123
2237
  await reconcileDeepInterviewQuestionEnforcementFromAnsweredRecords(cwd, sessionId);
2124
- const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId);
2238
+ const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir);
2125
2239
  if (!modeState) return null;
2126
2240
 
2127
2241
  const questionEnforcement = safeObject(modeState.question_enforcement);
@@ -2133,7 +2247,7 @@ async function buildDeepInterviewQuestionStopOutput(
2133
2247
  return null;
2134
2248
  }
2135
2249
 
2136
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
2250
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
2137
2251
  if (canonicalState) {
2138
2252
  const blocker = listActiveSkills(canonicalState).find((entry) => (
2139
2253
  entry.skill === "deep-interview"
@@ -2336,9 +2450,10 @@ async function findCanonicalActiveTeamForSession(
2336
2450
 
2337
2451
  async function resolveActiveTeamNameForStop(
2338
2452
  cwd: string,
2453
+ stateDir: string,
2339
2454
  sessionId: string,
2340
2455
  ): Promise<string> {
2341
- const directState = await readTeamModeStateForStop(cwd, sessionId);
2456
+ const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId);
2342
2457
  const directTeamName = safeString(directState?.team_name).trim();
2343
2458
  if (directState?.active === true && directTeamName) return directTeamName;
2344
2459
 
@@ -2354,12 +2469,12 @@ async function maybeBuildReleaseReadinessFinalizeStopOutput(
2354
2469
  ): Promise<{ matched: boolean; output: Record<string, unknown> | null }> {
2355
2470
  if (!sessionId) return { matched: false, output: null };
2356
2471
 
2357
- const teamName = await resolveActiveTeamNameForStop(cwd, sessionId);
2472
+ const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId);
2358
2473
  if (!teamName) return { matched: false, output: null };
2359
2474
 
2360
2475
  const explicitReleaseReadinessContext =
2361
2476
  hasReleaseReadinessMode(payload)
2362
- || await hasReleaseReadinessStopMarker(cwd, sessionId, teamName);
2477
+ || await hasReleaseReadinessStopMarker(cwd, stateDir, sessionId, teamName);
2363
2478
  if (!explicitReleaseReadinessContext) {
2364
2479
  return { matched: false, output: null };
2365
2480
  }
@@ -2397,10 +2512,11 @@ async function maybeBuildReleaseReadinessFinalizeStopOutput(
2397
2512
 
2398
2513
  async function buildSkillStopOutput(
2399
2514
  cwd: string,
2515
+ stateDir: string,
2400
2516
  sessionId: string,
2401
2517
  threadId: string,
2402
2518
  ): Promise<Record<string, unknown> | null> {
2403
- const blocker = await readBlockingSkillForStop(cwd, sessionId, threadId);
2519
+ const blocker = await readBlockingSkillForStop(cwd, stateDir, sessionId, threadId);
2404
2520
  if (!blocker) return null;
2405
2521
 
2406
2522
  const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
@@ -2547,17 +2663,41 @@ async function buildStopHookOutput(
2547
2663
  const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, sessionId);
2548
2664
  const threadId = readPayloadThreadId(payload);
2549
2665
  if (canonicalSessionId) {
2550
- await reconcileStaleRootSkillActiveStateForStop(cwd, canonicalSessionId);
2666
+ await reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, canonicalSessionId);
2551
2667
  }
2552
2668
  const execFollowupOutput = await buildExecFollowupStopOutput(cwd, canonicalSessionId);
2553
2669
  if (execFollowupOutput) return execFollowupOutput;
2670
+ const ralphOwnerContext = {
2671
+ payloadSessionId: sessionId,
2672
+ threadId,
2673
+ tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
2674
+ };
2675
+ const ralphCompletionAuditBlock = options.skipRalphStopBlock === true
2676
+ ? null
2677
+ : await readRalphCompletionAuditBlockState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
2678
+ if (ralphCompletionAuditBlock) {
2679
+ await reopenRalphCompletionAuditBlock(ralphCompletionAuditBlock);
2680
+ const blockingPath = formatStopStatePath(cwd, ralphCompletionAuditBlock.path);
2681
+ const systemMessage =
2682
+ `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.`;
2683
+ return await returnPersistentStopBlock(
2684
+ payload,
2685
+ stateDir,
2686
+ "ralph-completion-audit-stop",
2687
+ `${blockingPath}|${ralphCompletionAuditBlock.reason}`,
2688
+ {
2689
+ decision: "block",
2690
+ reason: systemMessage,
2691
+ stopReason: `ralph_completion_audit_${ralphCompletionAuditBlock.reason}`,
2692
+ systemMessage,
2693
+ },
2694
+ canonicalSessionId,
2695
+ { allowRepeatDuringStopHook: true },
2696
+ );
2697
+ }
2554
2698
  const ralphState = options.skipRalphStopBlock === true
2555
2699
  ? null
2556
- : await readActiveRalphState(stateDir, canonicalSessionId, {
2557
- payloadSessionId: sessionId,
2558
- threadId,
2559
- tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
2560
- });
2700
+ : await readActiveRalphState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
2561
2701
  if (!ralphState) {
2562
2702
  const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
2563
2703
  if (autoresearchState) {
@@ -2667,6 +2807,7 @@ async function buildStopHookOutput(
2667
2807
  if (canonicalSessionId) {
2668
2808
  const deepInterviewQuestionOutput = await buildDeepInterviewQuestionStopOutput(
2669
2809
  cwd,
2810
+ stateDir,
2670
2811
  canonicalSessionId,
2671
2812
  threadId,
2672
2813
  );
@@ -2700,7 +2841,7 @@ async function buildStopHookOutput(
2700
2841
  if (repeatedCanonicalTeamOutput) return repeatedCanonicalTeamOutput;
2701
2842
  }
2702
2843
 
2703
- const skillOutput = await buildSkillStopOutput(cwd, canonicalSessionId, threadId);
2844
+ const skillOutput = await buildSkillStopOutput(cwd, stateDir, canonicalSessionId, threadId);
2704
2845
  if (skillOutput) {
2705
2846
  return await returnPersistentStopBlock(
2706
2847
  payload,
@@ -2730,7 +2871,7 @@ async function buildStopHookOutput(
2730
2871
  );
2731
2872
  }
2732
2873
  const autoNudgeConfig = await loadAutoNudgeConfig();
2733
- const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
2874
+ const autoNudgePhase = await readStopAutoNudgePhase(cwd, stateDir, canonicalSessionId, threadId);
2734
2875
 
2735
2876
  if (
2736
2877
  autoNudgeConfig.enabled
@@ -2816,7 +2957,9 @@ export async function dispatchCodexNativeHook(
2816
2957
  ): Promise<NativeHookDispatchResult> {
2817
2958
  const hookEventName = readHookEventName(payload);
2818
2959
  const cwd = options.cwd ?? (safeString(payload.cwd).trim() || process.cwd());
2819
- const stateDir = join(cwd, ".omx", "state");
2960
+ // Native hooks must use the same authoritative runtime state root as HUD/MCP
2961
+ // when boxed/team roots are active; do not bypass it with cwd/.omx/state.
2962
+ const stateDir = getBaseStateDir(cwd);
2820
2963
  await mkdir(stateDir, { recursive: true });
2821
2964
 
2822
2965
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
@@ -2918,6 +3061,7 @@ export async function dispatchCodexNativeHook(
2918
3061
  turnId || undefined,
2919
3062
  ) ?? await recordSkillActivation({
2920
3063
  stateDir,
3064
+ sourceCwd: cwd,
2921
3065
  text: prompt,
2922
3066
  sessionId: sessionIdForState,
2923
3067
  threadId,
@@ -185,7 +185,10 @@ function resolveOmxParityTarget(toolName: string): { command: OmxParityCommand;
185
185
  if (!match) return null;
186
186
 
187
187
  const [, server, tool] = match;
188
- if (server === "state") return { command: "state", tool };
188
+ if (server === "state") {
189
+ const stateTool = tool.replace(/^state_/, "").replace(/_/g, "-");
190
+ return { command: "state", tool: stateTool };
191
+ }
189
192
  if (server === "trace") return { command: "trace", tool };
190
193
  if (server === "code_intel") return { command: "code-intel", tool };
191
194
  if (server === "memory" && tool.startsWith("notepad_")) {