oh-my-codex 0.13.2 → 0.14.1

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 (406) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +14 -8
  4. package/crates/omx-explore/src/main.rs +94 -1
  5. package/crates/omx-sparkshell/src/codex_bridge.rs +59 -12
  6. package/crates/omx-sparkshell/tests/execution.rs +48 -0
  7. package/dist/autoresearch/__tests__/skill-validation.test.d.ts +2 -0
  8. package/dist/autoresearch/__tests__/skill-validation.test.d.ts.map +1 -0
  9. package/dist/autoresearch/__tests__/skill-validation.test.js +91 -0
  10. package/dist/autoresearch/__tests__/skill-validation.test.js.map +1 -0
  11. package/dist/autoresearch/skill-validation.d.ts +13 -0
  12. package/dist/autoresearch/skill-validation.d.ts.map +1 -0
  13. package/dist/autoresearch/skill-validation.js +165 -0
  14. package/dist/autoresearch/skill-validation.js.map +1 -0
  15. package/dist/catalog/__tests__/schema.test.js +6 -0
  16. package/dist/catalog/__tests__/schema.test.js.map +1 -1
  17. package/dist/cli/__tests__/autoresearch-guided.test.js +236 -273
  18. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  19. package/dist/cli/__tests__/autoresearch.test.js +64 -653
  20. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  21. package/dist/cli/__tests__/explore.test.js +33 -1
  22. package/dist/cli/__tests__/explore.test.js.map +1 -1
  23. package/dist/cli/__tests__/index.test.js +18 -2
  24. package/dist/cli/__tests__/index.test.js.map +1 -1
  25. package/dist/cli/__tests__/nested-help-routing.test.js +2 -1
  26. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  27. package/dist/cli/__tests__/package-bin-contract.test.js +5 -0
  28. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  29. package/dist/cli/__tests__/question.test.d.ts +2 -0
  30. package/dist/cli/__tests__/question.test.d.ts.map +1 -0
  31. package/dist/cli/__tests__/question.test.js +166 -0
  32. package/dist/cli/__tests__/question.test.js.map +1 -0
  33. package/dist/cli/__tests__/session-search-help.test.js +1 -1
  34. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  35. package/dist/cli/__tests__/setup-agents-overwrite.test.js +32 -7
  36. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  37. package/dist/cli/__tests__/setup-refresh.test.js +8 -6
  38. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  39. package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -0
  40. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  41. package/dist/cli/__tests__/sparkshell-cli.test.js +23 -0
  42. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  43. package/dist/cli/__tests__/uninstall.test.js +65 -5
  44. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  45. package/dist/cli/__tests__/update.test.js +360 -26
  46. package/dist/cli/__tests__/update.test.js.map +1 -1
  47. package/dist/cli/autoresearch-guided.d.ts +24 -7
  48. package/dist/cli/autoresearch-guided.d.ts.map +1 -1
  49. package/dist/cli/autoresearch-guided.js +189 -130
  50. package/dist/cli/autoresearch-guided.js.map +1 -1
  51. package/dist/cli/autoresearch.d.ts +3 -2
  52. package/dist/cli/autoresearch.d.ts.map +1 -1
  53. package/dist/cli/autoresearch.js +29 -305
  54. package/dist/cli/autoresearch.js.map +1 -1
  55. package/dist/cli/doctor.d.ts.map +1 -1
  56. package/dist/cli/doctor.js +43 -0
  57. package/dist/cli/doctor.js.map +1 -1
  58. package/dist/cli/explore.d.ts.map +1 -1
  59. package/dist/cli/explore.js +18 -3
  60. package/dist/cli/explore.js.map +1 -1
  61. package/dist/cli/index.d.ts +2 -1
  62. package/dist/cli/index.d.ts.map +1 -1
  63. package/dist/cli/index.js +15 -3
  64. package/dist/cli/index.js.map +1 -1
  65. package/dist/cli/question.d.ts +3 -0
  66. package/dist/cli/question.d.ts.map +1 -0
  67. package/dist/cli/question.js +182 -0
  68. package/dist/cli/question.js.map +1 -0
  69. package/dist/cli/setup.d.ts.map +1 -1
  70. package/dist/cli/setup.js +25 -3
  71. package/dist/cli/setup.js.map +1 -1
  72. package/dist/cli/sparkshell.d.ts.map +1 -1
  73. package/dist/cli/sparkshell.js +11 -1
  74. package/dist/cli/sparkshell.js.map +1 -1
  75. package/dist/cli/team.d.ts.map +1 -1
  76. package/dist/cli/team.js +159 -394
  77. package/dist/cli/team.js.map +1 -1
  78. package/dist/cli/uninstall.d.ts.map +1 -1
  79. package/dist/cli/uninstall.js +3 -1
  80. package/dist/cli/uninstall.js.map +1 -1
  81. package/dist/cli/update.d.ts +37 -9
  82. package/dist/cli/update.d.ts.map +1 -1
  83. package/dist/cli/update.js +204 -26
  84. package/dist/cli/update.js.map +1 -1
  85. package/dist/config/__tests__/generator-idempotent.test.js +51 -14
  86. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  87. package/dist/config/__tests__/generator-notify.test.js +35 -10
  88. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  89. package/dist/config/generator.d.ts +1 -0
  90. package/dist/config/generator.d.ts.map +1 -1
  91. package/dist/config/generator.js +61 -7
  92. package/dist/config/generator.js.map +1 -1
  93. package/dist/hooks/__tests__/analyze-routing-contract.test.js +22 -13
  94. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  95. package/dist/hooks/__tests__/anti-slop-workflow.test.js +3 -3
  96. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  97. package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts +2 -0
  98. package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts.map +1 -0
  99. package/dist/hooks/__tests__/code-review-skill-contract.test.js +56 -0
  100. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -0
  101. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +2 -2
  102. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -1
  103. package/dist/hooks/__tests__/deep-interview-contract.test.js +51 -5
  104. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  105. package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts +2 -0
  106. package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts.map +1 -0
  107. package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js +43 -0
  108. package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js.map +1 -0
  109. package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts +2 -0
  110. package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts.map +1 -0
  111. package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js +38 -0
  112. package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js.map +1 -0
  113. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +2 -2
  114. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
  115. package/dist/hooks/__tests__/keyword-detector.test.js +308 -17
  116. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  117. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +570 -2
  118. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  119. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +717 -16
  120. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  121. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +25 -0
  122. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  123. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +894 -1
  124. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  125. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +34 -0
  126. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  127. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +132 -0
  128. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  129. package/dist/hooks/__tests__/prompt-guidance-contract.test.js +22 -4
  130. package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
  131. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +4 -2
  132. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  133. package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts +1 -0
  134. package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts.map +1 -1
  135. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js +19 -1
  136. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  137. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +28 -0
  138. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  139. package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js +5 -4
  140. package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js.map +1 -1
  141. package/dist/hooks/__tests__/prompt-team-routing.test.js +2 -2
  142. package/dist/hooks/__tests__/prompt-team-routing.test.js.map +1 -1
  143. package/dist/hooks/__tests__/triage-config.test.d.ts +2 -0
  144. package/dist/hooks/__tests__/triage-config.test.d.ts.map +1 -0
  145. package/dist/hooks/__tests__/triage-config.test.js +211 -0
  146. package/dist/hooks/__tests__/triage-config.test.js.map +1 -0
  147. package/dist/hooks/__tests__/triage-heuristic.test.d.ts +2 -0
  148. package/dist/hooks/__tests__/triage-heuristic.test.d.ts.map +1 -0
  149. package/dist/hooks/__tests__/triage-heuristic.test.js +230 -0
  150. package/dist/hooks/__tests__/triage-heuristic.test.js.map +1 -0
  151. package/dist/hooks/__tests__/triage-state.test.d.ts +2 -0
  152. package/dist/hooks/__tests__/triage-state.test.d.ts.map +1 -0
  153. package/dist/hooks/__tests__/triage-state.test.js +426 -0
  154. package/dist/hooks/__tests__/triage-state.test.js.map +1 -0
  155. package/dist/hooks/keyword-detector.d.ts +26 -7
  156. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  157. package/dist/hooks/keyword-detector.js +97 -26
  158. package/dist/hooks/keyword-detector.js.map +1 -1
  159. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  160. package/dist/hooks/keyword-registry.js +16 -9
  161. package/dist/hooks/keyword-registry.js.map +1 -1
  162. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  163. package/dist/hooks/prompt-guidance-contract.js +28 -1
  164. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  165. package/dist/hooks/triage-config.d.ts +33 -0
  166. package/dist/hooks/triage-config.d.ts.map +1 -0
  167. package/dist/hooks/triage-config.js +87 -0
  168. package/dist/hooks/triage-config.js.map +1 -0
  169. package/dist/hooks/triage-heuristic.d.ts +20 -0
  170. package/dist/hooks/triage-heuristic.d.ts.map +1 -0
  171. package/dist/hooks/triage-heuristic.js +210 -0
  172. package/dist/hooks/triage-heuristic.js.map +1 -0
  173. package/dist/hooks/triage-state.d.ts +63 -0
  174. package/dist/hooks/triage-state.d.ts.map +1 -0
  175. package/dist/hooks/triage-state.js +138 -0
  176. package/dist/hooks/triage-state.js.map +1 -0
  177. package/dist/hud/__tests__/reconcile.test.js +20 -0
  178. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  179. package/dist/hud/reconcile.d.ts +1 -0
  180. package/dist/hud/reconcile.d.ts.map +1 -1
  181. package/dist/hud/reconcile.js +2 -1
  182. package/dist/hud/reconcile.js.map +1 -1
  183. package/dist/mcp/__tests__/bootstrap.test.js +5 -24
  184. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  185. package/dist/mcp/__tests__/state-server.test.js +127 -0
  186. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  187. package/dist/mcp/bootstrap.d.ts +1 -1
  188. package/dist/mcp/bootstrap.d.ts.map +1 -1
  189. package/dist/mcp/bootstrap.js +3 -11
  190. package/dist/mcp/bootstrap.js.map +1 -1
  191. package/dist/mcp/state-server.d.ts +25 -0
  192. package/dist/mcp/state-server.d.ts.map +1 -1
  193. package/dist/mcp/state-server.js +41 -0
  194. package/dist/mcp/state-server.js.map +1 -1
  195. package/dist/modes/__tests__/base-ralph-contract.test.js +15 -0
  196. package/dist/modes/__tests__/base-ralph-contract.test.js.map +1 -1
  197. package/dist/modes/base.d.ts +1 -0
  198. package/dist/modes/base.d.ts.map +1 -1
  199. package/dist/modes/base.js +22 -6
  200. package/dist/modes/base.js.map +1 -1
  201. package/dist/notifications/__tests__/index.test.js +75 -0
  202. package/dist/notifications/__tests__/index.test.js.map +1 -1
  203. package/dist/notifications/__tests__/session-status.test.js +90 -0
  204. package/dist/notifications/__tests__/session-status.test.js.map +1 -1
  205. package/dist/notifications/index.d.ts.map +1 -1
  206. package/dist/notifications/index.js +39 -22
  207. package/dist/notifications/index.js.map +1 -1
  208. package/dist/notifications/session-status.d.ts +2 -0
  209. package/dist/notifications/session-status.d.ts.map +1 -1
  210. package/dist/notifications/session-status.js +19 -4
  211. package/dist/notifications/session-status.js.map +1 -1
  212. package/dist/openclaw/index.d.ts +5 -3
  213. package/dist/openclaw/index.d.ts.map +1 -1
  214. package/dist/openclaw/index.js +5 -3
  215. package/dist/openclaw/index.js.map +1 -1
  216. package/dist/question/__tests__/client.test.d.ts +2 -0
  217. package/dist/question/__tests__/client.test.d.ts.map +1 -0
  218. package/dist/question/__tests__/client.test.js +70 -0
  219. package/dist/question/__tests__/client.test.js.map +1 -0
  220. package/dist/question/__tests__/deep-interview.test.d.ts +2 -0
  221. package/dist/question/__tests__/deep-interview.test.d.ts.map +1 -0
  222. package/dist/question/__tests__/deep-interview.test.js +118 -0
  223. package/dist/question/__tests__/deep-interview.test.js.map +1 -0
  224. package/dist/question/__tests__/policy.test.d.ts +2 -0
  225. package/dist/question/__tests__/policy.test.d.ts.map +1 -0
  226. package/dist/question/__tests__/policy.test.js +107 -0
  227. package/dist/question/__tests__/policy.test.js.map +1 -0
  228. package/dist/question/__tests__/renderer.test.d.ts +2 -0
  229. package/dist/question/__tests__/renderer.test.d.ts.map +1 -0
  230. package/dist/question/__tests__/renderer.test.js +238 -0
  231. package/dist/question/__tests__/renderer.test.js.map +1 -0
  232. package/dist/question/__tests__/state.test.d.ts +2 -0
  233. package/dist/question/__tests__/state.test.d.ts.map +1 -0
  234. package/dist/question/__tests__/state.test.js +75 -0
  235. package/dist/question/__tests__/state.test.js.map +1 -0
  236. package/dist/question/__tests__/types.test.d.ts +2 -0
  237. package/dist/question/__tests__/types.test.d.ts.map +1 -0
  238. package/dist/question/__tests__/types.test.js +44 -0
  239. package/dist/question/__tests__/types.test.js.map +1 -0
  240. package/dist/question/__tests__/ui.test.d.ts +2 -0
  241. package/dist/question/__tests__/ui.test.d.ts.map +1 -0
  242. package/dist/question/__tests__/ui.test.js +169 -0
  243. package/dist/question/__tests__/ui.test.js.map +1 -0
  244. package/dist/question/client.d.ts +54 -0
  245. package/dist/question/client.d.ts.map +1 -0
  246. package/dist/question/client.js +77 -0
  247. package/dist/question/client.js.map +1 -0
  248. package/dist/question/deep-interview.d.ts +30 -0
  249. package/dist/question/deep-interview.d.ts.map +1 -0
  250. package/dist/question/deep-interview.js +118 -0
  251. package/dist/question/deep-interview.js.map +1 -0
  252. package/dist/question/policy.d.ts +18 -0
  253. package/dist/question/policy.d.ts.map +1 -0
  254. package/dist/question/policy.js +77 -0
  255. package/dist/question/policy.js.map +1 -0
  256. package/dist/question/renderer.d.ts +20 -0
  257. package/dist/question/renderer.d.ts.map +1 -0
  258. package/dist/question/renderer.js +190 -0
  259. package/dist/question/renderer.js.map +1 -0
  260. package/dist/question/state.d.ts +19 -0
  261. package/dist/question/state.d.ts.map +1 -0
  262. package/dist/question/state.js +108 -0
  263. package/dist/question/state.js.map +1 -0
  264. package/dist/question/types.d.ts +66 -0
  265. package/dist/question/types.d.ts.map +1 -0
  266. package/dist/question/types.js +82 -0
  267. package/dist/question/types.js.map +1 -0
  268. package/dist/question/ui.d.ts +38 -0
  269. package/dist/question/ui.d.ts.map +1 -0
  270. package/dist/question/ui.js +321 -0
  271. package/dist/question/ui.js.map +1 -0
  272. package/dist/ralph/contract.d.ts +1 -1
  273. package/dist/ralph/contract.d.ts.map +1 -1
  274. package/dist/ralph/contract.js +4 -1
  275. package/dist/ralph/contract.js.map +1 -1
  276. package/dist/ralplan/runtime.js +1 -1
  277. package/dist/ralplan/runtime.js.map +1 -1
  278. package/dist/runtime/__tests__/run-loop.test.d.ts +2 -0
  279. package/dist/runtime/__tests__/run-loop.test.d.ts.map +1 -0
  280. package/dist/runtime/__tests__/run-loop.test.js +35 -0
  281. package/dist/runtime/__tests__/run-loop.test.js.map +1 -0
  282. package/dist/runtime/__tests__/run-outcome.test.d.ts +2 -0
  283. package/dist/runtime/__tests__/run-outcome.test.d.ts.map +1 -0
  284. package/dist/runtime/__tests__/run-outcome.test.js +102 -0
  285. package/dist/runtime/__tests__/run-outcome.test.js.map +1 -0
  286. package/dist/runtime/__tests__/run-state.test.d.ts +2 -0
  287. package/dist/runtime/__tests__/run-state.test.d.ts.map +1 -0
  288. package/dist/runtime/__tests__/run-state.test.js +37 -0
  289. package/dist/runtime/__tests__/run-state.test.js.map +1 -0
  290. package/dist/runtime/run-loop.d.ts +45 -0
  291. package/dist/runtime/run-loop.d.ts.map +1 -0
  292. package/dist/runtime/run-loop.js +51 -0
  293. package/dist/runtime/run-loop.js.map +1 -0
  294. package/dist/runtime/run-outcome.d.ts +46 -0
  295. package/dist/runtime/run-outcome.d.ts.map +1 -0
  296. package/dist/runtime/run-outcome.js +285 -0
  297. package/dist/runtime/run-outcome.js.map +1 -0
  298. package/dist/runtime/run-state.d.ts +40 -0
  299. package/dist/runtime/run-state.d.ts.map +1 -0
  300. package/dist/runtime/run-state.js +120 -0
  301. package/dist/runtime/run-state.js.map +1 -0
  302. package/dist/runtime/terminal-lifecycle.d.ts +11 -0
  303. package/dist/runtime/terminal-lifecycle.d.ts.map +1 -0
  304. package/dist/runtime/terminal-lifecycle.js +52 -0
  305. package/dist/runtime/terminal-lifecycle.js.map +1 -0
  306. package/dist/scripts/__tests__/codex-native-hook.test.js +1459 -126
  307. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  308. package/dist/scripts/__tests__/postinstall.test.d.ts +2 -0
  309. package/dist/scripts/__tests__/postinstall.test.d.ts.map +1 -0
  310. package/dist/scripts/__tests__/postinstall.test.js +178 -0
  311. package/dist/scripts/__tests__/postinstall.test.js.map +1 -0
  312. package/dist/scripts/codex-native-hook.d.ts +3 -0
  313. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  314. package/dist/scripts/codex-native-hook.js +308 -61
  315. package/dist/scripts/codex-native-hook.js.map +1 -1
  316. package/dist/scripts/notify-fallback-watcher.js +81 -2
  317. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  318. package/dist/scripts/notify-hook/auto-nudge.d.ts +27 -0
  319. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  320. package/dist/scripts/notify-hook/auto-nudge.js +83 -20
  321. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  322. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  323. package/dist/scripts/notify-hook/managed-tmux.js +64 -38
  324. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  325. package/dist/scripts/notify-hook/ralph-session-resume.js +1 -1
  326. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  327. package/dist/scripts/notify-hook.js +15 -5
  328. package/dist/scripts/notify-hook.js.map +1 -1
  329. package/dist/scripts/postinstall.d.ts +22 -0
  330. package/dist/scripts/postinstall.d.ts.map +1 -0
  331. package/dist/scripts/postinstall.js +105 -0
  332. package/dist/scripts/postinstall.js.map +1 -0
  333. package/dist/scripts/sync-prompt-guidance-fragments.js +5 -0
  334. package/dist/scripts/sync-prompt-guidance-fragments.js.map +1 -1
  335. package/dist/state/__tests__/operations-ralph-phase.test.js +21 -0
  336. package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
  337. package/dist/state/__tests__/operations.test.js +18 -0
  338. package/dist/state/__tests__/operations.test.js.map +1 -1
  339. package/dist/state/__tests__/workflow-transition.test.js +11 -0
  340. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  341. package/dist/state/operations.d.ts.map +1 -1
  342. package/dist/state/operations.js +15 -0
  343. package/dist/state/operations.js.map +1 -1
  344. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  345. package/dist/state/workflow-transition-reconcile.js +14 -1
  346. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  347. package/dist/state/workflow-transition.d.ts.map +1 -1
  348. package/dist/state/workflow-transition.js +3 -1
  349. package/dist/state/workflow-transition.js.map +1 -1
  350. package/dist/team/__tests__/followup-planner.test.js +15 -0
  351. package/dist/team/__tests__/followup-planner.test.js.map +1 -1
  352. package/dist/team/__tests__/role-router.test.js +47 -0
  353. package/dist/team/__tests__/role-router.test.js.map +1 -1
  354. package/dist/team/__tests__/runtime.test.js +108 -2
  355. package/dist/team/__tests__/runtime.test.js.map +1 -1
  356. package/dist/team/followup-planner.d.ts.map +1 -1
  357. package/dist/team/followup-planner.js +31 -9
  358. package/dist/team/followup-planner.js.map +1 -1
  359. package/dist/team/role-router.d.ts.map +1 -1
  360. package/dist/team/role-router.js +73 -0
  361. package/dist/team/role-router.js.map +1 -1
  362. package/dist/team/runtime.d.ts.map +1 -1
  363. package/dist/team/runtime.js +18 -4
  364. package/dist/team/runtime.js.map +1 -1
  365. package/dist/utils/__tests__/dep-versions.test.js +25 -8
  366. package/dist/utils/__tests__/dep-versions.test.js.map +1 -1
  367. package/dist/utils/__tests__/paths.test.js +45 -0
  368. package/dist/utils/__tests__/paths.test.js.map +1 -1
  369. package/dist/utils/paths.d.ts +2 -0
  370. package/dist/utils/paths.d.ts.map +1 -1
  371. package/dist/utils/paths.js +22 -7
  372. package/dist/utils/paths.js.map +1 -1
  373. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  374. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  375. package/package.json +4 -2
  376. package/prompts/architect.md +4 -0
  377. package/prompts/code-reviewer.md +3 -0
  378. package/prompts/dependency-expert.md +3 -0
  379. package/prompts/executor.md +5 -0
  380. package/prompts/explore.md +2 -0
  381. package/prompts/planner.md +5 -0
  382. package/prompts/product-analyst.md +8 -8
  383. package/prompts/researcher.md +78 -30
  384. package/prompts/verifier.md +4 -0
  385. package/skills/autoresearch/SKILL.md +68 -0
  386. package/skills/code-review/SKILL.md +94 -28
  387. package/skills/deep-interview/SKILL.md +100 -9
  388. package/skills/help/SKILL.md +3 -1
  389. package/skills/ralplan/SKILL.md +1 -0
  390. package/skills/team/SKILL.md +1 -0
  391. package/skills/ultrawork/SKILL.md +1 -0
  392. package/src/scripts/__tests__/codex-native-hook.test.ts +2373 -692
  393. package/src/scripts/__tests__/postinstall.test.ts +210 -0
  394. package/src/scripts/codex-native-hook.ts +365 -66
  395. package/src/scripts/notify-fallback-watcher.ts +92 -2
  396. package/src/scripts/notify-hook/auto-nudge.ts +89 -20
  397. package/src/scripts/notify-hook/managed-tmux.ts +70 -31
  398. package/src/scripts/notify-hook/ralph-session-resume.ts +1 -1
  399. package/src/scripts/notify-hook.ts +23 -5
  400. package/src/scripts/postinstall-bootstrap.js +23 -0
  401. package/src/scripts/postinstall.ts +161 -0
  402. package/src/scripts/sync-prompt-guidance-fragments.ts +4 -0
  403. package/templates/AGENTS.md +48 -37
  404. package/templates/catalog-manifest.json +7 -0
  405. package/templates/model-instructions/explore-lightweight-AGENTS.md +11 -0
  406. package/templates/model-instructions/sparkshell-lightweight-AGENTS.md +10 -0
@@ -2,11 +2,12 @@ import { execFileSync } from "child_process";
2
2
  import { existsSync, readFileSync } from "fs";
3
3
  import { mkdir, readFile, readdir, writeFile } from "fs/promises";
4
4
  import { join, resolve } from "path";
5
+ import { pathToFileURL } from "url";
5
6
  import { readModeState, readModeStateForSession, updateModeState } from "../modes/base.js";
6
7
  import { listActiveSkills, readVisibleSkillActiveState, } from "../state/skill-active.js";
7
8
  import { readSubagentSessionSummary } from "../subagents/tracker.js";
8
9
  import { resolveCanonicalTeamStateRoot } from "../team/state-root.js";
9
- import { readUsableSessionState, reconcileNativeSessionStart } from "../hooks/session.js";
10
+ import { isSessionStateUsable, readSessionState, readUsableSessionState, reconcileNativeSessionStart, } from "../hooks/session.js";
10
11
  import { appendTeamEvent, readTeamLeaderAttention, readTeamManifestV2, readTeamPhase, writeTeamLeaderAttention, writeTeamPhase, } from "../team/state.js";
11
12
  import { omxNotepadPath, omxProjectMemoryPath } from "../utils/paths.js";
12
13
  import { getStateFilePath, getStatePath } from "../mcp/state-paths.js";
@@ -16,8 +17,15 @@ import { buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTra
16
17
  import { buildNativeHookEvent, } from "../hooks/extensibility/events.js";
17
18
  import { dispatchHookEvent } from "../hooks/extensibility/dispatcher.js";
18
19
  import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
20
+ import { shellEscapeSingle } from "../hud/tmux.js";
19
21
  import { onSessionStart as buildWikiSessionStartContext } from "../wiki/lifecycle.js";
20
- const TERMINAL_RALPH_PHASES = new Set(["complete", "failed", "cancelled"]);
22
+ import { readAutoresearchCompletionStatus, readAutoresearchModeState } from "../autoresearch/skill-validation.js";
23
+ import { shouldContinueRun } from "../runtime/run-loop.js";
24
+ import { triagePrompt } from "../hooks/triage-heuristic.js";
25
+ import { readTriageConfig } from "../hooks/triage-config.js";
26
+ import { readTriageState, writeTriageState, shouldSuppressFollowup, promptSignature, } from "../hooks/triage-state.js";
27
+ import { isPendingDeepInterviewQuestionEnforcement } from "../question/deep-interview.js";
28
+ import { resolveOmxCliEntryPath } from "../utils/paths.js";
21
29
  const TERMINAL_MODE_PHASES = new Set(["complete", "failed", "cancelled"]);
22
30
  const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
23
31
  const TEAM_TERMINAL_TASK_STATUSES = new Set(["completed", "failed"]);
@@ -29,6 +37,18 @@ const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
29
37
  /^\s*decision\s*:\s*(?:yes|no|ship|hold|release|do not release|proceed|do not proceed)\b[^\n\r]*/im,
30
38
  ];
31
39
  const RELEASE_READINESS_FINALIZE_SYSTEM_MESSAGE = "OMX release-readiness detected a stable final recommendation with no active worker tasks; emit one concise final decision summary and finalize.";
40
+ const EXECUTION_HANDOFF_PATTERNS = [
41
+ /^(?:好|好的|行|可以|那就|那现在)?[,,\s]*(?:开始|继续|直接)\s*(?:执行|优化|实现|修改|修复)(?=$|\s|[,,。.!!??])/u,
42
+ /(?:按照|按|基于)(?:这个|上述|当前)?\s*(?:plan|计划|方案).{0,16}(?:开始|继续|直接)?\s*(?:执行|优化|实现|修改|修复)/u,
43
+ /(?:不用|别|不要).{0,6}讨论/u,
44
+ /\b(?:start|begin|go ahead(?: and)?|proceed(?: now)?)\s+(?:to\s+)?(?:implement|execute|apply|fix)\b/i,
45
+ /\b(?:according to|based on)\s+(?:the|this|that)\s+plan\b.{0,20}\b(?:start|begin|proceed(?: now)?|go ahead(?: and)?)\b/i,
46
+ ];
47
+ const SHORT_FOLLOWUP_PRIORITY_PATTERNS = [
48
+ /^(?:继续|接着|然后|那就|那现在|还有(?:一个)?问题|这些优化都做了么|这些都做了么|现在呢|本轮|当前轮|这一轮)/u,
49
+ /(?:按照|按|基于)(?:这个|上述|当前)?(?:plan|计划|方案)/u,
50
+ /\b(?:follow up|latest request|this turn|current turn|newest request)\b/i,
51
+ ];
32
52
  function safeString(value) {
33
53
  return typeof value === "string" ? value : "";
34
54
  }
@@ -45,6 +65,34 @@ function safePositiveInteger(value) {
45
65
  }
46
66
  return null;
47
67
  }
68
+ function normalizePromptSignalText(text) {
69
+ return text.trim().replace(/\s+/g, " ");
70
+ }
71
+ function looksLikeExecutionHandoffPrompt(prompt) {
72
+ const normalized = normalizePromptSignalText(prompt);
73
+ if (!normalized)
74
+ return false;
75
+ return EXECUTION_HANDOFF_PATTERNS.some((pattern) => pattern.test(normalized));
76
+ }
77
+ function looksLikeShortFollowupPrompt(prompt) {
78
+ const normalized = normalizePromptSignalText(prompt);
79
+ if (!normalized)
80
+ return false;
81
+ if (looksLikeExecutionHandoffPrompt(normalized))
82
+ return true;
83
+ if (normalized.length > 240)
84
+ return false;
85
+ return SHORT_FOLLOWUP_PRIORITY_PATTERNS.some((pattern) => pattern.test(normalized));
86
+ }
87
+ function buildPromptPriorityMessage(prompt) {
88
+ if (looksLikeExecutionHandoffPrompt(prompt)) {
89
+ return "Newest user input is an execution handoff for the current task. Treat it as authorization to act now against the latest approved plan/request. Do not restate the prior plan unless the user explicitly asks for a recap or status update.";
90
+ }
91
+ if (looksLikeShortFollowupPrompt(prompt)) {
92
+ return "Newest user input is a same-thread follow-up. Answer that latest follow-up directly and prefer it over older unresolved prompts when choosing what to do next.";
93
+ }
94
+ return null;
95
+ }
48
96
  function readHookEventName(payload) {
49
97
  const raw = safeString(payload.hook_event_name
50
98
  ?? payload.hookEventName
@@ -88,24 +136,31 @@ function readPromptText(payload) {
88
136
  }
89
137
  return "";
90
138
  }
91
- function sanitizePayloadForHookContext(payload, hookEventName) {
92
- if (hookEventName !== "UserPromptSubmit")
93
- return payload;
139
+ function sanitizePayloadForHookContext(payload, hookEventName, canonicalSessionId = "") {
94
140
  const sanitized = { ...payload };
95
- delete sanitized.prompt;
96
- delete sanitized.input;
97
- delete sanitized.user_prompt;
98
- delete sanitized.userPrompt;
99
- delete sanitized.text;
141
+ if (hookEventName === "UserPromptSubmit") {
142
+ delete sanitized.prompt;
143
+ delete sanitized.input;
144
+ delete sanitized.user_prompt;
145
+ delete sanitized.userPrompt;
146
+ delete sanitized.text;
147
+ return sanitized;
148
+ }
149
+ if (hookEventName === "Stop") {
150
+ delete sanitized.stop_hook_active;
151
+ delete sanitized.stopHookActive;
152
+ delete sanitized.sessionId;
153
+ sanitized.session_id = canonicalSessionId.trim() || safeString(payload.session_id ?? payload.sessionId).trim();
154
+ }
100
155
  return sanitized;
101
156
  }
102
- function buildBaseContext(cwd, payload, hookEventName) {
157
+ function buildBaseContext(cwd, payload, hookEventName, canonicalSessionId = "") {
103
158
  return {
104
159
  cwd,
105
160
  project_path: cwd,
106
161
  transcript_path: safeString(payload.transcript_path ?? payload.transcriptPath) || null,
107
162
  source: safeString(payload.source),
108
- payload: sanitizePayloadForHookContext(payload, hookEventName),
163
+ payload: sanitizePayloadForHookContext(payload, hookEventName, canonicalSessionId),
109
164
  };
110
165
  }
111
166
  async function readJsonIfExists(path) {
@@ -126,39 +181,49 @@ function formatPhase(value, fallback = "active") {
126
181
  const phase = safeString(value).trim();
127
182
  return phase || fallback;
128
183
  }
184
+ async function readActiveAutoresearchState(cwd, sessionId) {
185
+ const normalizedSessionId = sessionId?.trim() || undefined;
186
+ if (!normalizedSessionId)
187
+ return null;
188
+ const state = await readAutoresearchModeState(cwd, normalizedSessionId);
189
+ if (state?.active !== true)
190
+ return null;
191
+ if (!isNonTerminalPhase(state.current_phase ?? state.currentPhase ?? 'executing'))
192
+ return null;
193
+ return state;
194
+ }
129
195
  async function readActiveRalphState(stateDir, preferredSessionId) {
130
- const sessionInfo = await readUsableSessionState(resolve(stateDir, "..", ".."));
131
- const currentOmxSessionId = safeString(sessionInfo?.session_id).trim();
196
+ const cwd = resolve(stateDir, "..", "..");
197
+ const [rawSessionInfo, usableSessionInfo] = await Promise.all([
198
+ readSessionState(cwd),
199
+ readUsableSessionState(cwd),
200
+ ]);
201
+ const currentOmxSessionId = safeString(usableSessionInfo?.session_id).trim();
202
+ const staleCurrentSessionId = rawSessionInfo && !isSessionStateUsable(rawSessionInfo, cwd)
203
+ ? safeString(rawSessionInfo.session_id).trim()
204
+ : "";
132
205
  const sessionCandidates = [...new Set([
133
206
  safeString(preferredSessionId).trim(),
134
207
  currentOmxSessionId,
135
208
  ].filter(Boolean))];
209
+ // Ralph Stop stays authoritative-scope-only once the Stop payload is session-bound.
210
+ // That is intentionally stricter than generic state MCP reads: do not scan sibling
211
+ // session scopes or fall back to root when a current/explicit session is in play.
136
212
  for (const sessionId of sessionCandidates) {
137
- const sessionScoped = await readJsonIfExists(join(stateDir, "sessions", sessionId, "ralph-state.json"));
138
- if (sessionScoped?.active === true
139
- && !TERMINAL_RALPH_PHASES.has(safeString(sessionScoped.current_phase).trim().toLowerCase())) {
213
+ if (staleCurrentSessionId && sessionId === staleCurrentSessionId) {
214
+ continue;
215
+ }
216
+ const sessionScoped = await readStopSessionPinnedState("ralph-state.json", cwd, sessionId);
217
+ if (sessionScoped?.active === true && shouldContinueRun(sessionScoped)) {
140
218
  return sessionScoped;
141
219
  }
142
220
  }
143
221
  if (sessionCandidates.length > 0)
144
222
  return null;
145
223
  const direct = await readJsonIfExists(join(stateDir, "ralph-state.json"));
146
- if (direct?.active === true && !TERMINAL_RALPH_PHASES.has(safeString(direct.current_phase).trim().toLowerCase())) {
224
+ if (direct?.active === true && shouldContinueRun(direct)) {
147
225
  return direct;
148
226
  }
149
- const sessionsRoot = join(stateDir, "sessions");
150
- if (!existsSync(sessionsRoot))
151
- return null;
152
- const entries = await readdir(sessionsRoot, { withFileTypes: true }).catch(() => []);
153
- for (const entry of entries) {
154
- if (!entry.isDirectory())
155
- continue;
156
- const candidate = await readJsonIfExists(join(sessionsRoot, entry.name, "ralph-state.json"));
157
- if (candidate?.active === true
158
- && !TERMINAL_RALPH_PHASES.has(safeString(candidate.current_phase).trim().toLowerCase())) {
159
- return candidate;
160
- }
161
- }
162
227
  return null;
163
228
  }
164
229
  function readParentPid(pid) {
@@ -249,38 +314,60 @@ function resolveSessionOwnerPid(payload) {
249
314
  return resolved;
250
315
  return process.pid;
251
316
  }
252
- async function ensureOmxGitignoreEntry(cwd) {
253
- let repoRoot = "";
317
+ function tryReadGitValue(cwd, args) {
254
318
  try {
255
- repoRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
319
+ const value = execFileSync("git", args, {
256
320
  cwd,
257
321
  encoding: "utf-8",
258
322
  stdio: ["ignore", "pipe", "ignore"],
259
323
  windowsHide: true,
260
324
  }).trim();
325
+ return value || null;
261
326
  }
262
327
  catch {
263
- return { changed: false };
328
+ return null;
329
+ }
330
+ }
331
+ function isPathIgnoredByGit(cwd, path) {
332
+ try {
333
+ execFileSync("git", ["check-ignore", "-q", path], {
334
+ cwd,
335
+ stdio: ["ignore", "ignore", "ignore"],
336
+ windowsHide: true,
337
+ });
338
+ return true;
339
+ }
340
+ catch {
341
+ return false;
264
342
  }
343
+ }
344
+ async function ensureOmxLocalIgnoreEntry(cwd) {
345
+ const repoRoot = tryReadGitValue(cwd, ["rev-parse", "--show-toplevel"]);
265
346
  if (!repoRoot)
266
347
  return { changed: false };
267
- const gitignorePath = join(repoRoot, ".gitignore");
268
- const existing = existsSync(gitignorePath)
269
- ? await readFile(gitignorePath, "utf-8")
348
+ if (isPathIgnoredByGit(repoRoot, ".omx/")) {
349
+ return { changed: false };
350
+ }
351
+ const excludePathValue = tryReadGitValue(repoRoot, ["rev-parse", "--git-path", "info/exclude"]);
352
+ if (!excludePathValue)
353
+ return { changed: false };
354
+ const excludePath = resolve(repoRoot, excludePathValue);
355
+ const existing = existsSync(excludePath)
356
+ ? await readFile(excludePath, "utf-8")
270
357
  : "";
271
358
  const lines = existing.split(/\r?\n/).map((line) => line.trim());
272
359
  if (lines.includes(".omx/")) {
273
- return { changed: false, gitignorePath };
360
+ return { changed: false, excludePath };
274
361
  }
275
362
  const next = `${existing}${existing.endsWith("\n") || existing.length === 0 ? "" : "\n"}.omx/\n`;
276
- await writeFile(gitignorePath, next);
277
- return { changed: true, gitignorePath };
363
+ await writeFile(excludePath, next);
364
+ return { changed: true, excludePath };
278
365
  }
279
366
  async function buildSessionStartContext(cwd, sessionId) {
280
367
  const sections = [];
281
- const gitignoreResult = await ensureOmxGitignoreEntry(cwd);
282
- if (gitignoreResult.changed) {
283
- sections.push(`Added .omx/ to ${gitignoreResult.gitignorePath} to keep local OMX state out of source control.`);
368
+ const localIgnoreResult = await ensureOmxLocalIgnoreEntry(cwd);
369
+ if (localIgnoreResult.changed) {
370
+ sections.push(`Added .omx/ to ${localIgnoreResult.excludePath} to keep local OMX state out of source control without mutating tracked repo ignores.`);
284
371
  }
285
372
  const modeSummaries = [];
286
373
  for (const mode of ["ralph", "autopilot", "ultrawork", "ultraqa", "ralplan", "deep-interview", "team"]) {
@@ -366,13 +453,19 @@ async function buildSessionStartContext(cwd, sessionId) {
366
453
  }
367
454
  return sections.length > 0 ? sections.join("\n\n") : null;
368
455
  }
369
- function buildAdditionalContextMessage(prompt, skillState) {
456
+ function buildDeepInterviewQuestionBridgeInstruction(cwd) {
457
+ const omxBin = resolveOmxCliEntryPath({ cwd }) || process.argv[1] || "omx";
458
+ const bridgeCommand = `${shellEscapeSingle(process.execPath)} ${shellEscapeSingle(omxBin)} question`;
459
+ return `Deep-interview must ask each interview round via \`omx question\`; do not fall back to \`request_user_input\` or plain-text questioning. If bare \`omx question\` is unavailable in this reused session, use the current-session CLI bridge command: \`${bridgeCommand}\`. Stop remains blocked while a deep-interview question obligation is pending.`;
460
+ }
461
+ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd()) {
370
462
  if (!prompt)
371
463
  return null;
464
+ const promptPriorityMessage = buildPromptPriorityMessage(prompt);
372
465
  const matches = detectKeywords(prompt);
373
466
  const match = detectPrimaryKeyword(prompt);
374
467
  if (!match)
375
- return null;
468
+ return promptPriorityMessage;
376
469
  const detectedKeywordMessage = matches.length > 1
377
470
  ? `OMX native UserPromptSubmit detected workflow keywords ${matches.map((entry) => `"${entry.keyword}" -> ${entry.skill}`).join(", ")}.`
378
471
  : `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`;
@@ -386,6 +479,9 @@ function buildAdditionalContextMessage(prompt, skillState) {
386
479
  const ralphPromptActivationNote = skillState?.initialized_mode === "ralph"
387
480
  ? "Prompt-side `$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`. Use `omx ralph --prd ...` only when you explicitly want the PRD-gated CLI startup path."
388
481
  : null;
482
+ const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
483
+ ? buildDeepInterviewQuestionBridgeInstruction(cwd)
484
+ : null;
389
485
  const combinedTransitionMessage = (() => {
390
486
  if (!skillState?.transition_message)
391
487
  return null;
@@ -400,6 +496,7 @@ function buildAdditionalContextMessage(prompt, skillState) {
400
496
  return [
401
497
  `OMX native UserPromptSubmit denied workflow keyword "${match.keyword}" -> ${match.skill}.`,
402
498
  skillState.transition_error,
499
+ promptPriorityMessage,
403
500
  'Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.',
404
501
  ].join(' ');
405
502
  }
@@ -411,6 +508,7 @@ function buildAdditionalContextMessage(prompt, skillState) {
411
508
  deferredSkills.length > 0
412
509
  ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
413
510
  : null,
511
+ promptPriorityMessage,
414
512
  skillState.initialized_mode && skillState.initialized_state_path
415
513
  ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
416
514
  : null,
@@ -431,7 +529,9 @@ function buildAdditionalContextMessage(prompt, skillState) {
431
529
  deferredSkills.length > 0
432
530
  ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
433
531
  : null,
532
+ promptPriorityMessage,
434
533
  initializedStateMessage,
534
+ deepInterviewPromptActivationNote,
435
535
  "Use the durable OMX team runtime via `omx team ...` for coordinated execution; do not replace it with in-process fanout.",
436
536
  "If you need runtime syntax, run `omx team --help` yourself.",
437
537
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
@@ -444,12 +544,14 @@ function buildAdditionalContextMessage(prompt, skillState) {
444
544
  deferredSkills.length > 0
445
545
  ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
446
546
  : null,
547
+ promptPriorityMessage,
447
548
  `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
549
+ deepInterviewPromptActivationNote,
448
550
  ralphPromptActivationNote,
449
551
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
450
552
  ].join(" ");
451
553
  }
452
- return `${detectedKeywordMessage} Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.`;
554
+ return [detectedKeywordMessage, promptPriorityMessage, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
453
555
  }
454
556
  function parseTeamWorkerEnv(rawValue) {
455
557
  const match = /^([a-z0-9][a-z0-9-]{0,29})\/(worker-\d+)$/.exec(rawValue.trim());
@@ -549,7 +651,7 @@ async function buildModeBasedStopOutput(mode, cwd, sessionId) {
549
651
  const state = sessionId
550
652
  ? await readModeStateForSession(mode, sessionId, cwd)
551
653
  : await readModeState(mode, cwd);
552
- if (state?.active !== true || !isNonTerminalPhase(state.current_phase))
654
+ if (!state || !shouldContinueRun(state))
553
655
  return null;
554
656
  const phase = formatPhase(state.current_phase);
555
657
  return {
@@ -737,6 +839,42 @@ async function readStopAutoNudgePhase(cwd, sessionId, threadId) {
737
839
  const modePhase = safeString(modeState.current_phase).trim().toLowerCase();
738
840
  return modePhase === "intent-first" ? "planning" : "";
739
841
  }
842
+ async function buildDeepInterviewQuestionStopOutput(cwd, sessionId, threadId) {
843
+ const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId);
844
+ if (!modeState)
845
+ return null;
846
+ const questionEnforcement = safeObject(modeState.question_enforcement);
847
+ const hasPendingQuestionObligation = isPendingDeepInterviewQuestionEnforcement(questionEnforcement);
848
+ if (modeState.active !== true && !hasPendingQuestionObligation)
849
+ return null;
850
+ const phase = formatPhase(modeState.current_phase, "planning");
851
+ if (TERMINAL_MODE_PHASES.has(phase.toLowerCase()) || phase === "completing") {
852
+ return null;
853
+ }
854
+ const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
855
+ if (canonicalState) {
856
+ const blocker = listActiveSkills(canonicalState).find((entry) => (entry.skill === "deep-interview"
857
+ && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
858
+ if (!blocker)
859
+ return null;
860
+ }
861
+ if (!hasPendingQuestionObligation) {
862
+ return null;
863
+ }
864
+ const obligationId = safeString(questionEnforcement.obligation_id).trim();
865
+ if (!obligationId)
866
+ return null;
867
+ const systemMessage = `OMX deep-interview is still active (phase: ${phase}) and requires a structured question via omx question before stopping.`;
868
+ return {
869
+ obligationId,
870
+ output: {
871
+ decision: "block",
872
+ reason: `Deep interview is still active (phase: ${phase}) and has a pending structured question obligation; use \`omx question\` before stopping.`,
873
+ stopReason: "deep_interview_question_required",
874
+ systemMessage,
875
+ },
876
+ };
877
+ }
740
878
  function resolveRepeatableStopSessionId(payload, canonicalSessionId) {
741
879
  return canonicalSessionId?.trim() || readPayloadSessionId(payload) || "";
742
880
  }
@@ -807,8 +945,8 @@ async function maybeReturnRepeatableStopOutput(payload, stateDir, signature, out
807
945
  await persistNativeStopSignature(stateDir, payload, signature, canonicalSessionId);
808
946
  return output;
809
947
  }
810
- async function returnPersistentStopBlock(payload, stateDir, signatureKind, signatureValue, output, canonicalSessionId) {
811
- return await maybeReturnRepeatableStopOutput(payload, stateDir, buildRepeatableStopSignature(payload, signatureKind, signatureValue, canonicalSessionId), output, canonicalSessionId, { allowRepeatDuringStopHook: true });
948
+ async function returnPersistentStopBlock(payload, stateDir, signatureKind, signatureValue, output, canonicalSessionId, options = { allowRepeatDuringStopHook: true }) {
949
+ return await maybeReturnRepeatableStopOutput(payload, stateDir, buildRepeatableStopSignature(payload, signatureKind, signatureValue, canonicalSessionId), output, canonicalSessionId, options);
812
950
  }
813
951
  async function findCanonicalActiveTeamForSession(cwd, sessionId) {
814
952
  if (!sessionId.trim())
@@ -979,6 +1117,20 @@ async function buildStopHookOutput(payload, cwd, stateDir) {
979
1117
  const threadId = readPayloadThreadId(payload);
980
1118
  const ralphState = await readActiveRalphState(stateDir, canonicalSessionId);
981
1119
  if (!ralphState) {
1120
+ const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
1121
+ if (autoresearchState) {
1122
+ const completion = await readAutoresearchCompletionStatus(cwd, canonicalSessionId.trim());
1123
+ if (!completion.complete) {
1124
+ const currentPhase = safeString(autoresearchState.current_phase ?? autoresearchState.currentPhase).trim() || 'executing';
1125
+ const systemMessage = `OMX autoresearch is still active (phase: ${currentPhase}); continue until validator evidence is complete before stopping.`;
1126
+ return await maybeReturnRepeatableStopOutput(payload, stateDir, buildRepeatableStopSignature(payload, 'autoresearch-stop', `${currentPhase}|${completion.reason}`, canonicalSessionId), {
1127
+ decision: 'block',
1128
+ reason: systemMessage,
1129
+ stopReason: `autoresearch_${currentPhase}`,
1130
+ systemMessage,
1131
+ }, canonicalSessionId, { allowRepeatDuringStopHook: true });
1132
+ }
1133
+ }
982
1134
  const teamWorkerOutput = await buildTeamWorkerStopOutput(cwd);
983
1135
  if (hasTeamWorkerContext() && teamWorkerOutput)
984
1136
  return teamWorkerOutput;
@@ -988,7 +1140,7 @@ async function buildStopHookOutput(payload, cwd, stateDir) {
988
1140
  }
989
1141
  const ultraworkOutput = await buildModeBasedStopOutput("ultrawork", cwd, canonicalSessionId);
990
1142
  if (ultraworkOutput) {
991
- return await returnPersistentStopBlock(payload, stateDir, "ultrawork-stop", safeString(ultraworkOutput.stopReason), ultraworkOutput, canonicalSessionId);
1143
+ return await returnPersistentStopBlock(payload, stateDir, "ultrawork-stop", safeString(ultraworkOutput.stopReason), ultraworkOutput, canonicalSessionId, { allowRepeatDuringStopHook: false });
992
1144
  }
993
1145
  const ultraqaOutput = await buildModeBasedStopOutput("ultraqa", cwd, canonicalSessionId);
994
1146
  if (ultraqaOutput) {
@@ -1002,6 +1154,10 @@ async function buildStopHookOutput(payload, cwd, stateDir) {
1002
1154
  return await returnPersistentStopBlock(payload, stateDir, "team-stop", safeString(teamOutput.stopReason), teamOutput, canonicalSessionId);
1003
1155
  }
1004
1156
  if (canonicalSessionId) {
1157
+ const deepInterviewQuestionOutput = await buildDeepInterviewQuestionStopOutput(cwd, canonicalSessionId, threadId);
1158
+ if (deepInterviewQuestionOutput) {
1159
+ return await returnPersistentStopBlock(payload, stateDir, "deep-interview-question-stop", deepInterviewQuestionOutput.obligationId, deepInterviewQuestionOutput.output, canonicalSessionId);
1160
+ }
1005
1161
  const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, canonicalSessionId);
1006
1162
  if (canonicalTeam) {
1007
1163
  const canonicalTeamOutput = buildTeamStopOutputForPhase(canonicalTeam.teamName, canonicalTeam.phase);
@@ -1046,21 +1202,36 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1046
1202
  await mkdir(stateDir, { recursive: true });
1047
1203
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
1048
1204
  let skillState = null;
1205
+ let triageAdditionalContext = null;
1049
1206
  const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
1050
1207
  const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
1051
1208
  const turnId = safeString(payload.turn_id ?? payload.turnId).trim();
1052
- let canonicalSessionId = safeString((await readUsableSessionState(cwd))?.session_id).trim();
1209
+ const currentSessionState = await readUsableSessionState(cwd);
1210
+ let canonicalSessionId = safeString(currentSessionState?.session_id).trim();
1211
+ let resolvedNativeSessionId = nativeSessionId;
1053
1212
  if (hookEventName === "SessionStart" && nativeSessionId) {
1054
1213
  const sessionState = await reconcileNativeSessionStart(cwd, nativeSessionId, {
1055
1214
  pid: options.sessionOwnerPid ?? resolveSessionOwnerPid(payload),
1056
1215
  });
1057
1216
  canonicalSessionId = safeString(sessionState.session_id).trim();
1217
+ resolvedNativeSessionId = safeString(sessionState.native_session_id).trim() || nativeSessionId;
1058
1218
  }
1059
1219
  else if (!canonicalSessionId) {
1060
- canonicalSessionId = safeString((await readUsableSessionState(cwd))?.session_id).trim();
1220
+ canonicalSessionId = safeString(currentSessionState?.session_id).trim();
1221
+ }
1222
+ if (hookEventName === "Stop") {
1223
+ const stopCanonicalSessionId = await resolveInternalSessionIdForPayload(cwd, readPayloadSessionId(payload));
1224
+ if (stopCanonicalSessionId) {
1225
+ canonicalSessionId = stopCanonicalSessionId;
1226
+ }
1227
+ if (canonicalSessionId && safeString(currentSessionState?.session_id).trim() === canonicalSessionId) {
1228
+ resolvedNativeSessionId =
1229
+ safeString(currentSessionState?.native_session_id).trim() || resolvedNativeSessionId;
1230
+ }
1061
1231
  }
1062
1232
  const eventSessionId = canonicalSessionId || nativeSessionId || undefined;
1063
1233
  const sessionIdForState = canonicalSessionId || nativeSessionId;
1234
+ let outputJson = null;
1064
1235
  if (hookEventName === "UserPromptSubmit") {
1065
1236
  const prompt = readPromptText(payload);
1066
1237
  if (prompt) {
@@ -1072,13 +1243,85 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1072
1243
  turnId,
1073
1244
  });
1074
1245
  }
1075
- await reconcileHudForPromptSubmit(cwd).catch(() => { });
1246
+ // --- Triage classifier (advisory-only, non-keyword prompts) ---
1247
+ if (prompt && skillState === null) {
1248
+ try {
1249
+ if (readTriageConfig().enabled) {
1250
+ const normalized = prompt.trim().toLowerCase();
1251
+ const previous = readTriageState({ cwd, sessionId: sessionIdForState || null });
1252
+ const suppress = shouldSuppressFollowup({
1253
+ previous,
1254
+ currentPrompt: normalized,
1255
+ currentHasKeyword: false,
1256
+ });
1257
+ if (!suppress) {
1258
+ const decision = triagePrompt(prompt);
1259
+ const nowIso = new Date().toISOString();
1260
+ const effectiveTurnId = turnId || nowIso;
1261
+ if (decision.lane === "HEAVY") {
1262
+ triageAdditionalContext =
1263
+ "OMX native UserPromptSubmit triage detected a multi-step goal with no workflow keyword. This is advisory prompt-routing context only; it did not activate autopilot or initialize workflow state. Prefer the existing autopilot-style workflow if AGENTS.md/runtime conditions allow it, unless newer user context narrows or opts out.";
1264
+ const newState = {
1265
+ version: 1,
1266
+ last_triage: {
1267
+ lane: "HEAVY",
1268
+ destination: "autopilot",
1269
+ reason: decision.reason,
1270
+ prompt_signature: promptSignature(normalized),
1271
+ turn_id: effectiveTurnId,
1272
+ created_at: nowIso,
1273
+ },
1274
+ suppress_followup: true,
1275
+ };
1276
+ writeTriageState({ cwd, sessionId: sessionIdForState || null, state: newState });
1277
+ }
1278
+ else if (decision.lane === "LIGHT") {
1279
+ if (decision.destination === "explore") {
1280
+ triageAdditionalContext =
1281
+ "OMX native UserPromptSubmit triage detected a read-only/question-shaped request with no workflow keyword. This is advisory prompt-routing context only. Prefer the explore role surface rather than escalating to autopilot.";
1282
+ }
1283
+ else if (decision.destination === "executor") {
1284
+ triageAdditionalContext =
1285
+ "OMX native UserPromptSubmit triage detected a narrow edit-shaped request with no workflow keyword. This is advisory prompt-routing context only. Prefer the executor role surface rather than autopilot.";
1286
+ }
1287
+ else if (decision.destination === "designer") {
1288
+ triageAdditionalContext =
1289
+ "OMX native UserPromptSubmit triage detected a visual/style request with no workflow keyword. This is advisory prompt-routing context only. Prefer the designer role surface.";
1290
+ }
1291
+ if (triageAdditionalContext !== null) {
1292
+ const dest = decision.destination;
1293
+ const newState = {
1294
+ version: 1,
1295
+ last_triage: {
1296
+ lane: "LIGHT",
1297
+ destination: dest,
1298
+ reason: decision.reason,
1299
+ prompt_signature: promptSignature(normalized),
1300
+ turn_id: effectiveTurnId,
1301
+ created_at: nowIso,
1302
+ },
1303
+ suppress_followup: true,
1304
+ };
1305
+ writeTriageState({ cwd, sessionId: sessionIdForState || null, state: newState });
1306
+ }
1307
+ }
1308
+ // lane === "PASS": no context, no state write
1309
+ }
1310
+ }
1311
+ }
1312
+ catch {
1313
+ // Swallow all triage errors; never break the hook
1314
+ triageAdditionalContext = null;
1315
+ }
1316
+ }
1317
+ const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
1318
+ await reconcileHudForPromptSubmitFn(cwd, { sessionId: canonicalSessionId || sessionIdForState || undefined }).catch(() => { });
1076
1319
  }
1077
1320
  if (omxEventName) {
1078
- const baseContext = buildBaseContext(cwd, payload, hookEventName);
1079
- if (nativeSessionId) {
1080
- baseContext.native_session_id = nativeSessionId;
1081
- baseContext.codex_session_id = nativeSessionId;
1321
+ const baseContext = buildBaseContext(cwd, payload, hookEventName, canonicalSessionId);
1322
+ if (resolvedNativeSessionId) {
1323
+ baseContext.native_session_id = resolvedNativeSessionId;
1324
+ baseContext.codex_session_id = resolvedNativeSessionId;
1082
1325
  }
1083
1326
  if (canonicalSessionId) {
1084
1327
  baseContext.omx_session_id = canonicalSessionId;
@@ -1091,11 +1334,10 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1091
1334
  });
1092
1335
  await dispatchHookEvent(event, { cwd });
1093
1336
  }
1094
- let outputJson = null;
1095
1337
  if (hookEventName === "SessionStart" || hookEventName === "UserPromptSubmit") {
1096
1338
  const additionalContext = hookEventName === "SessionStart"
1097
1339
  ? await buildSessionStartContext(cwd, canonicalSessionId || nativeSessionId)
1098
- : buildAdditionalContextMessage(readPromptText(payload), skillState);
1340
+ : (buildAdditionalContextMessage(readPromptText(payload), skillState, cwd) ?? triageAdditionalContext);
1099
1341
  if (additionalContext) {
1100
1342
  outputJson = {
1101
1343
  hookSpecificOutput: {
@@ -1124,6 +1366,11 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1124
1366
  outputJson,
1125
1367
  };
1126
1368
  }
1369
+ export function isCodexNativeHookMainModule(moduleUrl, argv1) {
1370
+ if (!argv1)
1371
+ return false;
1372
+ return moduleUrl === pathToFileURL(argv1).href;
1373
+ }
1127
1374
  async function readStdinJson() {
1128
1375
  const chunks = [];
1129
1376
  for await (const chunk of process.stdin) {
@@ -1164,7 +1411,7 @@ export async function runCodexNativeHookCli() {
1164
1411
  process.stdout.write(`${JSON.stringify(result.outputJson)}\n`);
1165
1412
  }
1166
1413
  }
1167
- if (import.meta.url === `file://${process.argv[1]}`) {
1414
+ if (isCodexNativeHookMainModule(import.meta.url, process.argv[1])) {
1168
1415
  runCodexNativeHookCli().catch((error) => {
1169
1416
  process.stderr.write(`[omx] codex-native-hook failed: ${error instanceof Error ? error.message : String(error)}\n`);
1170
1417
  process.exitCode = 1;