oh-my-codex 0.12.6 → 0.13.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 (258) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +39 -6
  4. package/crates/omx-explore/src/main.rs +300 -3
  5. package/dist/adapt/__tests__/foundation.test.d.ts +2 -0
  6. package/dist/adapt/__tests__/foundation.test.d.ts.map +1 -0
  7. package/dist/adapt/__tests__/foundation.test.js +171 -0
  8. package/dist/adapt/__tests__/foundation.test.js.map +1 -0
  9. package/dist/adapt/__tests__/hermes.test.d.ts +2 -0
  10. package/dist/adapt/__tests__/hermes.test.d.ts.map +1 -0
  11. package/dist/adapt/__tests__/hermes.test.js +137 -0
  12. package/dist/adapt/__tests__/hermes.test.js.map +1 -0
  13. package/dist/adapt/contracts.d.ts +157 -0
  14. package/dist/adapt/contracts.d.ts.map +1 -0
  15. package/dist/adapt/contracts.js +10 -0
  16. package/dist/adapt/contracts.js.map +1 -0
  17. package/dist/adapt/hermes.d.ts +83 -0
  18. package/dist/adapt/hermes.d.ts.map +1 -0
  19. package/dist/adapt/hermes.js +371 -0
  20. package/dist/adapt/hermes.js.map +1 -0
  21. package/dist/adapt/index.d.ts +14 -0
  22. package/dist/adapt/index.d.ts.map +1 -0
  23. package/dist/adapt/index.js +293 -0
  24. package/dist/adapt/index.js.map +1 -0
  25. package/dist/adapt/openclaw.d.ts +7 -0
  26. package/dist/adapt/openclaw.d.ts.map +1 -0
  27. package/dist/adapt/openclaw.js +299 -0
  28. package/dist/adapt/openclaw.js.map +1 -0
  29. package/dist/adapt/paths.d.ts +3 -0
  30. package/dist/adapt/paths.d.ts.map +1 -0
  31. package/dist/adapt/paths.js +15 -0
  32. package/dist/adapt/paths.js.map +1 -0
  33. package/dist/adapt/registry.d.ts +4 -0
  34. package/dist/adapt/registry.d.ts.map +1 -0
  35. package/dist/adapt/registry.js +48 -0
  36. package/dist/adapt/registry.js.map +1 -0
  37. package/dist/cli/__tests__/adapt-help.test.d.ts +2 -0
  38. package/dist/cli/__tests__/adapt-help.test.d.ts.map +1 -0
  39. package/dist/cli/__tests__/adapt-help.test.js +39 -0
  40. package/dist/cli/__tests__/adapt-help.test.js.map +1 -0
  41. package/dist/cli/__tests__/adapt.test.d.ts +2 -0
  42. package/dist/cli/__tests__/adapt.test.d.ts.map +1 -0
  43. package/dist/cli/__tests__/adapt.test.js +62 -0
  44. package/dist/cli/__tests__/adapt.test.js.map +1 -0
  45. package/dist/cli/__tests__/cleanup.test.js +61 -2
  46. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  47. package/dist/cli/__tests__/doctor-warning-copy.test.js +85 -0
  48. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  49. package/dist/cli/__tests__/explore-windows-diagnostics.test.d.ts +2 -0
  50. package/dist/cli/__tests__/explore-windows-diagnostics.test.d.ts.map +1 -0
  51. package/dist/cli/__tests__/explore-windows-diagnostics.test.js +17 -0
  52. package/dist/cli/__tests__/explore-windows-diagnostics.test.js.map +1 -0
  53. package/dist/cli/__tests__/explore.test.js +69 -0
  54. package/dist/cli/__tests__/explore.test.js.map +1 -1
  55. package/dist/cli/__tests__/index.test.js +225 -8
  56. package/dist/cli/__tests__/index.test.js.map +1 -1
  57. package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
  58. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  59. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js +5 -0
  60. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  61. package/dist/cli/__tests__/ralph-prd-smoke.test.d.ts +2 -0
  62. package/dist/cli/__tests__/ralph-prd-smoke.test.d.ts.map +1 -0
  63. package/dist/cli/__tests__/ralph-prd-smoke.test.js +167 -0
  64. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -0
  65. package/dist/cli/__tests__/ralph.test.js +103 -1
  66. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  67. package/dist/cli/__tests__/setup-skills-overwrite.test.js +60 -0
  68. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  69. package/dist/cli/adapt.d.ts +6 -0
  70. package/dist/cli/adapt.d.ts.map +1 -0
  71. package/dist/cli/adapt.js +135 -0
  72. package/dist/cli/adapt.js.map +1 -0
  73. package/dist/cli/cleanup.d.ts +3 -1
  74. package/dist/cli/cleanup.d.ts.map +1 -1
  75. package/dist/cli/cleanup.js +57 -6
  76. package/dist/cli/cleanup.js.map +1 -1
  77. package/dist/cli/doctor.d.ts +6 -0
  78. package/dist/cli/doctor.d.ts.map +1 -1
  79. package/dist/cli/doctor.js +72 -3
  80. package/dist/cli/doctor.js.map +1 -1
  81. package/dist/cli/explore.d.ts +2 -0
  82. package/dist/cli/explore.d.ts.map +1 -1
  83. package/dist/cli/explore.js +14 -0
  84. package/dist/cli/explore.js.map +1 -1
  85. package/dist/cli/index.d.ts +4 -1
  86. package/dist/cli/index.d.ts.map +1 -1
  87. package/dist/cli/index.js +52 -18
  88. package/dist/cli/index.js.map +1 -1
  89. package/dist/cli/ralph.d.ts +3 -1
  90. package/dist/cli/ralph.d.ts.map +1 -1
  91. package/dist/cli/ralph.js +83 -0
  92. package/dist/cli/ralph.js.map +1 -1
  93. package/dist/cli/setup.d.ts.map +1 -1
  94. package/dist/cli/setup.js +4 -2
  95. package/dist/cli/setup.js.map +1 -1
  96. package/dist/cli/tmux-hook.d.ts.map +1 -1
  97. package/dist/cli/tmux-hook.js +2 -1
  98. package/dist/cli/tmux-hook.js.map +1 -1
  99. package/dist/config/__tests__/codex-hooks.test.js +26 -2
  100. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  101. package/dist/config/codex-hooks.d.ts +2 -1
  102. package/dist/config/codex-hooks.d.ts.map +1 -1
  103. package/dist/config/codex-hooks.js +23 -2
  104. package/dist/config/codex-hooks.js.map +1 -1
  105. package/dist/hooks/__tests__/agents-overlay.test.js +20 -2
  106. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  107. package/dist/hooks/__tests__/keyword-detector.test.js +32 -0
  108. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  109. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +2 -1
  110. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  111. package/dist/hooks/__tests__/notify-hook-modules.test.js +6 -0
  112. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  113. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +124 -0
  114. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  115. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +2 -1
  116. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  117. package/dist/hooks/__tests__/prompt-guidance-contract.test.js +10 -1
  118. package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
  119. package/dist/hooks/__tests__/session.test.js +21 -0
  120. package/dist/hooks/__tests__/session.test.js.map +1 -1
  121. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  122. package/dist/hooks/agents-overlay.js +9 -0
  123. package/dist/hooks/agents-overlay.js.map +1 -1
  124. package/dist/hooks/extensibility/sdk/tmux.d.ts.map +1 -1
  125. package/dist/hooks/extensibility/sdk/tmux.js +2 -1
  126. package/dist/hooks/extensibility/sdk/tmux.js.map +1 -1
  127. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  128. package/dist/hooks/keyword-detector.js +12 -3
  129. package/dist/hooks/keyword-detector.js.map +1 -1
  130. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  131. package/dist/hooks/prompt-guidance-contract.js +2 -0
  132. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  133. package/dist/hooks/session.d.ts.map +1 -1
  134. package/dist/hooks/session.js +9 -0
  135. package/dist/hooks/session.js.map +1 -1
  136. package/dist/hud/__tests__/hud-tmux-injection.test.js +5 -0
  137. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  138. package/dist/hud/__tests__/state.test.js +116 -0
  139. package/dist/hud/__tests__/state.test.js.map +1 -1
  140. package/dist/hud/index.d.ts +1 -1
  141. package/dist/hud/index.d.ts.map +1 -1
  142. package/dist/hud/index.js +5 -3
  143. package/dist/hud/index.js.map +1 -1
  144. package/dist/hud/reconcile.js +1 -1
  145. package/dist/hud/reconcile.js.map +1 -1
  146. package/dist/hud/state.d.ts.map +1 -1
  147. package/dist/hud/state.js +6 -5
  148. package/dist/hud/state.js.map +1 -1
  149. package/dist/hud/tmux.d.ts +1 -1
  150. package/dist/hud/tmux.d.ts.map +1 -1
  151. package/dist/hud/tmux.js +9 -3
  152. package/dist/hud/tmux.js.map +1 -1
  153. package/dist/mcp/__tests__/bootstrap.test.js +1 -1
  154. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  155. package/dist/mcp/__tests__/state-paths.test.js +23 -1
  156. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  157. package/dist/mcp/bootstrap.d.ts +1 -1
  158. package/dist/mcp/bootstrap.d.ts.map +1 -1
  159. package/dist/mcp/bootstrap.js +3 -11
  160. package/dist/mcp/bootstrap.js.map +1 -1
  161. package/dist/mcp/state-paths.d.ts.map +1 -1
  162. package/dist/mcp/state-paths.js +18 -0
  163. package/dist/mcp/state-paths.js.map +1 -1
  164. package/dist/modes/__tests__/base-tmux-pane.test.js +1 -1
  165. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  166. package/dist/notifications/__tests__/config.test.js +38 -1
  167. package/dist/notifications/__tests__/config.test.js.map +1 -1
  168. package/dist/notifications/__tests__/tmux-detector.test.js +1 -1
  169. package/dist/notifications/__tests__/tmux-detector.test.js.map +1 -1
  170. package/dist/notifications/tmux-detector.d.ts +1 -0
  171. package/dist/notifications/tmux-detector.d.ts.map +1 -1
  172. package/dist/notifications/tmux-detector.js +6 -3
  173. package/dist/notifications/tmux-detector.js.map +1 -1
  174. package/dist/notifications/tmux.d.ts.map +1 -1
  175. package/dist/notifications/tmux.js +20 -38
  176. package/dist/notifications/tmux.js.map +1 -1
  177. package/dist/openclaw/__tests__/config.test.js +97 -10
  178. package/dist/openclaw/__tests__/config.test.js.map +1 -1
  179. package/dist/openclaw/config.d.ts +19 -1
  180. package/dist/openclaw/config.d.ts.map +1 -1
  181. package/dist/openclaw/config.js +220 -20
  182. package/dist/openclaw/config.js.map +1 -1
  183. package/dist/planning/artifacts.d.ts +7 -0
  184. package/dist/planning/artifacts.d.ts.map +1 -1
  185. package/dist/planning/artifacts.js +18 -4
  186. package/dist/planning/artifacts.js.map +1 -1
  187. package/dist/scripts/__tests__/codex-native-hook.test.js +818 -11
  188. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  189. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  190. package/dist/scripts/codex-native-hook.js +87 -36
  191. package/dist/scripts/codex-native-hook.js.map +1 -1
  192. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  193. package/dist/scripts/codex-native-pre-post.js +336 -0
  194. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  195. package/dist/scripts/notify-hook/auto-nudge.d.ts +1 -0
  196. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  197. package/dist/scripts/notify-hook/auto-nudge.js +10 -2
  198. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  199. package/dist/scripts/notify-hook/team-worker.js +1 -1
  200. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  201. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  202. package/dist/scripts/notify-hook/tmux-injection.js +78 -4
  203. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  204. package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
  205. package/dist/scripts/tmux-hook-engine.js +9 -7
  206. package/dist/scripts/tmux-hook-engine.js.map +1 -1
  207. package/dist/state/__tests__/operations.test.js +38 -0
  208. package/dist/state/__tests__/operations.test.js.map +1 -1
  209. package/dist/state/__tests__/workflow-transition.test.js +2 -0
  210. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  211. package/dist/state/skill-active.d.ts +1 -1
  212. package/dist/state/skill-active.d.ts.map +1 -1
  213. package/dist/state/skill-active.js +8 -4
  214. package/dist/state/skill-active.js.map +1 -1
  215. package/dist/state/workflow-transition-reconcile.js +1 -1
  216. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  217. package/dist/state/workflow-transition.js +3 -3
  218. package/dist/state/workflow-transition.js.map +1 -1
  219. package/dist/team/__tests__/leader-activity.test.js +28 -0
  220. package/dist/team/__tests__/leader-activity.test.js.map +1 -1
  221. package/dist/team/__tests__/tmux-session.test.js +42 -0
  222. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  223. package/dist/team/__tests__/worktree.test.js +29 -0
  224. package/dist/team/__tests__/worktree.test.js.map +1 -1
  225. package/dist/team/leader-activity.d.ts.map +1 -1
  226. package/dist/team/leader-activity.js +23 -1
  227. package/dist/team/leader-activity.js.map +1 -1
  228. package/dist/team/worktree.d.ts.map +1 -1
  229. package/dist/team/worktree.js +17 -1
  230. package/dist/team/worktree.js.map +1 -1
  231. package/dist/utils/__tests__/paths.test.js +6 -1
  232. package/dist/utils/__tests__/paths.test.js.map +1 -1
  233. package/dist/utils/__tests__/platform-command.test.js +19 -1
  234. package/dist/utils/__tests__/platform-command.test.js.map +1 -1
  235. package/dist/utils/paths.d.ts +2 -0
  236. package/dist/utils/paths.d.ts.map +1 -1
  237. package/dist/utils/paths.js +4 -0
  238. package/dist/utils/paths.js.map +1 -1
  239. package/dist/utils/platform-command.d.ts +1 -0
  240. package/dist/utils/platform-command.d.ts.map +1 -1
  241. package/dist/utils/platform-command.js +40 -23
  242. package/dist/utils/platform-command.js.map +1 -1
  243. package/dist/verification/__tests__/explore-harness-release-workflow.test.js +1 -1
  244. package/dist/verification/__tests__/pr-check-workflow.test.d.ts +2 -0
  245. package/dist/verification/__tests__/pr-check-workflow.test.d.ts.map +1 -0
  246. package/dist/verification/__tests__/pr-check-workflow.test.js +27 -0
  247. package/dist/verification/__tests__/pr-check-workflow.test.js.map +1 -0
  248. package/package.json +1 -1
  249. package/skills/ralph/SKILL.md +7 -0
  250. package/skills/ralph-init/SKILL.md +2 -1
  251. package/src/scripts/__tests__/codex-native-hook.test.ts +1053 -94
  252. package/src/scripts/codex-native-hook.ts +127 -31
  253. package/src/scripts/codex-native-pre-post.ts +376 -0
  254. package/src/scripts/notify-hook/auto-nudge.ts +12 -2
  255. package/src/scripts/notify-hook/team-worker.ts +1 -1
  256. package/src/scripts/notify-hook/tmux-injection.ts +93 -3
  257. package/src/scripts/tmux-hook-engine.ts +9 -7
  258. package/templates/AGENTS.md +3 -1
package/Cargo.lock CHANGED
@@ -32,11 +32,11 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
32
32
 
33
33
  [[package]]
34
34
  name = "omx-explore-harness"
35
- version = "0.12.6"
35
+ version = "0.13.1"
36
36
 
37
37
  [[package]]
38
38
  name = "omx-mux"
39
- version = "0.12.6"
39
+ version = "0.13.1"
40
40
  dependencies = [
41
41
  "serde",
42
42
  "serde_json",
@@ -44,7 +44,7 @@ dependencies = [
44
44
 
45
45
  [[package]]
46
46
  name = "omx-runtime"
47
- version = "0.12.6"
47
+ version = "0.13.1"
48
48
  dependencies = [
49
49
  "omx-mux",
50
50
  "omx-runtime-core",
@@ -53,7 +53,7 @@ dependencies = [
53
53
 
54
54
  [[package]]
55
55
  name = "omx-runtime-core"
56
- version = "0.12.6"
56
+ version = "0.13.1"
57
57
  dependencies = [
58
58
  "fs2",
59
59
  "serde",
@@ -62,7 +62,7 @@ dependencies = [
62
62
 
63
63
  [[package]]
64
64
  name = "omx-sparkshell"
65
- version = "0.12.6"
65
+ version = "0.13.1"
66
66
  dependencies = [
67
67
  "omx-mux",
68
68
  ]
package/Cargo.toml CHANGED
@@ -10,7 +10,7 @@ resolver = "2"
10
10
 
11
11
  [workspace.package]
12
12
 
13
- version = "0.12.6"
13
+ version = "0.13.1"
14
14
 
15
15
  edition = "2021"
16
16
  license = "MIT"
package/README.md CHANGED
@@ -72,6 +72,7 @@ $team 3:executor "execute the approved plan in parallel"
72
72
  ```
73
73
 
74
74
  That is the main path.
75
+ Before you treat the runtime as ready, run the quick-start smoke test below: `omx doctor` verifies the install shape, while `omx exec` proves the active Codex runtime can actually authenticate and complete a model call from the current environment.
75
76
  Start OMX strongly, clarify first when needed, approve the plan, then choose `$team` for coordinated parallel execution or `$ralph` for the persistent completion loop.
76
77
 
77
78
  ## What OMX is for
@@ -90,12 +91,22 @@ If you want plain Codex with no extra workflow layer, you probably do not need O
90
91
 
91
92
  - Node.js 20+
92
93
  - Codex CLI installed: `npm install -g @openai/codex`
93
- - Codex auth configured
94
+ - Codex auth configured and visible in the same shell/profile that will run OMX
94
95
  - `tmux` on macOS/Linux if you want the recommended durable team runtime
95
96
  - `psmux` on native Windows only if you intentionally want the less-supported Windows team path
96
97
 
97
98
  ### A good first session
98
99
 
100
+ After install, check both boundaries:
101
+
102
+ ```bash
103
+ omx doctor
104
+ codex login status
105
+ omx exec --skip-git-repo-check -C . "Reply with exactly OMX-EXEC-OK"
106
+ ```
107
+
108
+ `omx doctor` catches missing OMX files, hooks, and runtime prerequisites. The real smoke test catches auth, profile, and provider/base-URL problems that only appear when Codex performs an actual request.
109
+
99
110
  Launch OMX the recommended way:
100
111
 
101
112
  ```bash
@@ -135,10 +146,12 @@ Most users should think of OMX as **better task routing + better workflow + bett
135
146
  ## Start here if you are new
136
147
 
137
148
  1. Run `omx setup`
138
- 2. Launch with `omx --madmax --high`
139
- 3. Use `$deep-interview "..."` when the request or boundaries are still unclear
140
- 4. Use `$ralplan "..."` to approve the plan and review tradeoffs
141
- 5. Choose `$team` for coordinated parallel execution or `$ralph` for persistent completion loops
149
+ 2. Run `omx doctor`
150
+ 3. Run a real execution smoke test: `codex login status` and `omx exec --skip-git-repo-check -C . "Reply with exactly OMX-EXEC-OK"`
151
+ 4. Launch with `omx --madmax --high`
152
+ 5. Use `$deep-interview "..."` when the request or boundaries are still unclear
153
+ 6. Use `$ralplan "..."` to approve the plan and review tradeoffs
154
+ 7. Choose `$team` for coordinated parallel execution or `$ralph` for persistent completion loops
142
155
 
143
156
  ## Recommended workflow
144
157
 
@@ -177,7 +190,7 @@ These are operator/support surfaces:
177
190
  - `omx setup` installs prompts, skills, AGENTS scaffolding, `.codex/config.toml`, and OMX-managed native Codex hooks in `.codex/hooks.json`
178
191
  - setup refresh preserves non-OMX hook entries in `.codex/hooks.json` and only rewrites OMX-managed wrappers
179
192
  - `omx uninstall` removes OMX-managed wrappers from `.codex/hooks.json` but keeps the file when user hooks remain
180
- - `omx doctor` verifies the install when something seems wrong
193
+ - `omx doctor` verifies the install when something seems wrong; it does not prove that the active Codex profile can make an authenticated model call
181
194
  - `omx hud --watch` is a monitoring/status surface, not the primary user workflow
182
195
 
183
196
  For non-team sessions, native Codex hooks are now the canonical lifecycle surface:
@@ -187,6 +200,24 @@ For non-team sessions, native Codex hooks are now the canonical lifecycle surfac
187
200
 
188
201
  See [Codex native hook mapping](./docs/codex-native-hooks.md) for the current native / fallback matrix.
189
202
 
203
+
204
+ ### Troubleshooting false-green readiness
205
+
206
+ A green `omx doctor` means the install and local runtime wiring look sane. If real execution still fails, check the environment Codex actually uses:
207
+
208
+ - Run `codex login status` and `omx exec --skip-git-repo-check -C . "Reply with exactly OMX-EXEC-OK"` from the same shell/profile that will launch OMX.
209
+ - In custom HOME, profile, container, or service shells, confirm the active `~/.codex` (or `CODEX_HOME`) is the one with the expected auth and config. Do not assume your normal user `~/.codex` is visible there.
210
+ - If you depend on a local OpenAI-compatible proxy, confirm the active `~/.codex/config.toml` includes the expected `openai_base_url`; otherwise a proxy-issued key can be sent to the default endpoint and fail with `401 Unauthorized`, `Missing bearer or basic authentication in header`, or `Incorrect API key provided`.
211
+ - If `omx doctor --team` or resume reports a stale team such as `resume_blocker` or a missing tmux session, clean the dead runtime state before retrying:
212
+
213
+ ```bash
214
+ omx team shutdown <team-name> --force --confirm-issues
215
+ omx cancel
216
+ omx doctor --team
217
+ ```
218
+
219
+ Only use the forced team shutdown for a team you have confirmed is dead or intentionally abandoned.
220
+
190
221
  ### Explore and sparkshell
191
222
 
192
223
  - `omx explore --prompt "..."` is for read-only repository lookup
@@ -220,6 +251,7 @@ omx wiki refresh --json
220
251
 
221
252
  `omx team` works best on macOS/Linux with `tmux`.
222
253
  Native Windows remains a secondary path, and WSL2 is generally the better choice if you want a Windows-hosted setup.
254
+ On native Windows, OMX accepts `psmux` as the tmux-compatible binary for the existing tmux-backed paths it already uses.
223
255
 
224
256
  | Platform | Install |
225
257
  | --- | --- |
@@ -250,6 +282,7 @@ If this happens, try:
250
282
  - [Skills reference](./docs/skills.html)
251
283
  - [Codex native hook mapping](./docs/codex-native-hooks.md)
252
284
  - [Integrations](./docs/integrations.html)
285
+ - [Troubleshooting execution readiness](./docs/troubleshooting.md)
253
286
  - [OpenClaw / notification gateway guide](./docs/openclaw-integration.md)
254
287
  - [Contributing](./CONTRIBUTING.md)
255
288
  - [Changelog](./CHANGELOG.md)
@@ -12,6 +12,8 @@ const CODEX_BIN_ENV: &str = "OMX_EXPLORE_CODEX_BIN";
12
12
  const HARNESS_ROOT_ENV: &str = "OMX_EXPLORE_ROOT";
13
13
  const INTERNAL_DIRECT_WRAPPER_FLAG: &str = "--internal-allowlist-direct";
14
14
  const INTERNAL_SHELL_WRAPPER_FLAG: &str = "--internal-allowlist-shell";
15
+ const WINDOWS_UNSUPPORTED_ALLOWLIST_MESSAGE: &str =
16
+ "omx explore built-in harness is not ready on Windows because its allowlist runtime relies on POSIX sh/bash wrappers. Set OMX_EXPLORE_BIN to a compatible custom harness, prefer `omx sparkshell` for shell-native read-only lookups, or run `omx doctor` for readiness details.";
15
17
 
16
18
  const ALLOWED_DIRECT_COMMANDS: &[&str] = &[
17
19
  "rg", "grep", "ls", "find", "wc", "cat", "head", "tail", "pwd", "printf",
@@ -270,7 +272,12 @@ fn resolve_codex_binary() -> String {
270
272
  }
271
273
 
272
274
  fn codex_launch_for_binary(codex_binary: &str) -> Option<CodexLaunch> {
273
- let interpreter = read_shebang_interpreter(Path::new(codex_binary))?;
275
+ let codex_path = Path::new(codex_binary);
276
+ if let Some(launch) = codex_launch_for_posix_node_shim(codex_path) {
277
+ return Some(launch);
278
+ }
279
+
280
+ let interpreter = read_shebang_interpreter(codex_path)?;
274
281
  let (program, mut leading_args) = resolve_shebang_launch(&interpreter)?;
275
282
  leading_args.push(codex_binary.to_string());
276
283
  Some(CodexLaunch {
@@ -279,6 +286,21 @@ fn codex_launch_for_binary(codex_binary: &str) -> Option<CodexLaunch> {
279
286
  })
280
287
  }
281
288
 
289
+ fn codex_launch_for_posix_node_shim(codex_path: &Path) -> Option<CodexLaunch> {
290
+ let shebang = read_shebang_interpreter(codex_path)?;
291
+ if !is_posix_shell_shebang(&shebang) {
292
+ return None;
293
+ }
294
+
295
+ let script = read_to_string(codex_path).ok()?;
296
+ let entrypoint = resolve_posix_node_shim_entrypoint(codex_path, &script)?;
297
+ let program = resolve_posix_node_shim_program(codex_path, &script)?;
298
+ Some(CodexLaunch {
299
+ program: program.display().to_string(),
300
+ leading_args: vec![entrypoint.display().to_string()],
301
+ })
302
+ }
303
+
282
304
  fn read_shebang_interpreter(path: &Path) -> Option<String> {
283
305
  let file = File::open(path).ok()?;
284
306
  let mut reader = BufReader::new(file);
@@ -293,6 +315,85 @@ fn read_shebang_interpreter(path: &Path) -> Option<String> {
293
315
  .map(ToOwned::to_owned)
294
316
  }
295
317
 
318
+ fn is_posix_shell_shebang(shebang: &str) -> bool {
319
+ let parts: Vec<&str> = shebang.split_whitespace().collect();
320
+ let interpreter = *parts.first().unwrap_or(&"");
321
+ if interpreter.ends_with("/env") {
322
+ return parts
323
+ .get(1)
324
+ .is_some_and(|target| is_posix_shell_name(target));
325
+ }
326
+ is_posix_shell_name(interpreter)
327
+ }
328
+
329
+ fn is_posix_shell_name(value: &str) -> bool {
330
+ matches!(
331
+ Path::new(value).file_name().and_then(|name| name.to_str()),
332
+ Some("sh" | "bash" | "dash" | "ash" | "ksh" | "zsh")
333
+ )
334
+ }
335
+
336
+ fn resolve_posix_node_shim_entrypoint(codex_path: &Path, script: &str) -> Option<PathBuf> {
337
+ let basedir = codex_path.parent()?;
338
+ quoted_shell_segments(script)
339
+ .into_iter()
340
+ .filter_map(|segment| strip_basedir_prefix(&segment).map(ToOwned::to_owned))
341
+ .map(|relative| normalize_path(basedir.join(relative)))
342
+ .find(|candidate| is_node_entrypoint(candidate))
343
+ }
344
+
345
+ fn resolve_posix_node_shim_program(codex_path: &Path, script: &str) -> Option<PathBuf> {
346
+ let basedir = codex_path.parent()?;
347
+ if quoted_shell_segments(script)
348
+ .into_iter()
349
+ .any(|segment| matches!(strip_basedir_prefix(&segment), Some("node")))
350
+ {
351
+ let sibling_node = basedir.join("node");
352
+ if is_usable_host_command(&sibling_node) {
353
+ return Some(sibling_node);
354
+ }
355
+ }
356
+ resolve_host_command("node")
357
+ }
358
+
359
+ fn quoted_shell_segments(script: &str) -> Vec<String> {
360
+ let mut segments = Vec::new();
361
+ let mut active_quote: Option<char> = None;
362
+ let mut current = String::new();
363
+
364
+ for ch in script.chars() {
365
+ if let Some(quote) = active_quote {
366
+ if ch == quote {
367
+ segments.push(std::mem::take(&mut current));
368
+ active_quote = None;
369
+ } else {
370
+ current.push(ch);
371
+ }
372
+ continue;
373
+ }
374
+
375
+ if ch == '\'' || ch == '"' {
376
+ active_quote = Some(ch);
377
+ }
378
+ }
379
+
380
+ segments
381
+ }
382
+
383
+ fn strip_basedir_prefix(segment: &str) -> Option<&str> {
384
+ segment
385
+ .strip_prefix("$basedir/")
386
+ .or_else(|| segment.strip_prefix("${basedir}/"))
387
+ }
388
+
389
+ fn is_node_entrypoint(path: &Path) -> bool {
390
+ path.is_file()
391
+ && matches!(
392
+ path.extension().and_then(|value| value.to_str()),
393
+ Some("js" | "cjs" | "mjs")
394
+ )
395
+ }
396
+
296
397
  fn resolve_shebang_launch(shebang: &str) -> Option<(String, Vec<String>)> {
297
398
  let parts: Vec<&str> = shebang.split_whitespace().collect();
298
399
  let interpreter = *parts.first()?;
@@ -366,6 +467,9 @@ fn compose_exec_prompt(user_prompt: &str, prompt_contract: &str) -> String {
366
467
  }
367
468
 
368
469
  fn prepare_allowlist_environment() -> Result<AllowlistEnvironment, String> {
470
+ if let Some(message) = allowlist_platform_diagnostic(env::consts::OS) {
471
+ return Err(message.to_string());
472
+ }
369
473
  let root = temp_allowlist_dir()?;
370
474
  let bin_dir = root.path.join("bin");
371
475
  create_dir_all(&bin_dir).map_err(|err| {
@@ -412,6 +516,13 @@ fn prepare_allowlist_environment() -> Result<AllowlistEnvironment, String> {
412
516
  })
413
517
  }
414
518
 
519
+ fn allowlist_platform_diagnostic(os: &str) -> Option<&'static str> {
520
+ if os.eq_ignore_ascii_case("windows") {
521
+ return Some(WINDOWS_UNSUPPORTED_ALLOWLIST_MESSAGE);
522
+ }
523
+ None
524
+ }
525
+
415
526
  fn temp_allowlist_dir() -> Result<TempDirGuard, String> {
416
527
  let dir = env::temp_dir().join(format!(
417
528
  "omx-explore-allowlist-{}-{}",
@@ -469,20 +580,39 @@ fn build_direct_wrapper(self_exe: &Path, command: &str) -> Result<String, String
469
580
 
470
581
  fn resolve_host_command(command: &str) -> Option<PathBuf> {
471
582
  let candidate = Path::new(command);
472
- if candidate.is_absolute() && candidate.exists() {
583
+ if candidate.is_absolute() && is_usable_host_command(candidate) {
473
584
  return Some(candidate.to_path_buf());
474
585
  }
475
586
 
476
587
  let path = env::var_os("PATH")?;
477
588
  for entry in env::split_paths(&path) {
478
589
  let resolved = entry.join(command);
479
- if resolved.exists() {
590
+ if is_usable_host_command(&resolved) {
480
591
  return Some(resolved);
481
592
  }
482
593
  }
483
594
  None
484
595
  }
485
596
 
597
+ fn is_usable_host_command(path: &Path) -> bool {
598
+ let metadata = match path.metadata() {
599
+ Ok(metadata) => metadata,
600
+ Err(_) => return false,
601
+ };
602
+ if !metadata.is_file() {
603
+ return false;
604
+ }
605
+ #[cfg(unix)]
606
+ {
607
+ use std::os::unix::fs::PermissionsExt;
608
+ metadata.permissions().mode() & 0o111 != 0
609
+ }
610
+ #[cfg(not(unix))]
611
+ {
612
+ true
613
+ }
614
+ }
615
+
486
616
  fn shell_quote(value: &str) -> String {
487
617
  format!("'{}'", value.replace('\'', "'\"'\"'"))
488
618
  }
@@ -885,6 +1015,7 @@ mod tests {
885
1015
 
886
1016
  #[test]
887
1017
  fn codex_launch_for_env_node_shebang_uses_host_node_absolute_path() {
1018
+ let _guard = env_lock();
888
1019
  let root = temp_allowlist_dir().expect("temp root");
889
1020
  let script_path = root.path.join("codex-script");
890
1021
  write(&script_path, b"#!/usr/bin/env node\nconsole.log(\"ok\");\n").expect("write script");
@@ -896,6 +1027,162 @@ mod tests {
896
1027
  assert_eq!(launch.leading_args, vec![script_path.display().to_string()]);
897
1028
  }
898
1029
 
1030
+ #[cfg(unix)]
1031
+ #[test]
1032
+ fn codex_launch_for_posix_package_manager_shim_uses_host_node_and_entrypoint() {
1033
+ let _guard = env_lock();
1034
+ let root = temp_allowlist_dir().expect("temp root");
1035
+ let host_bin = root.path.join("host-bin");
1036
+ let shim_dir = root.path.join("node_modules").join(".bin");
1037
+ let entrypoint = root
1038
+ .path
1039
+ .join("node_modules")
1040
+ .join("@openai")
1041
+ .join("codex")
1042
+ .join("bin")
1043
+ .join("codex.js");
1044
+ create_dir_all(&host_bin).expect("create host bin");
1045
+ create_dir_all(&shim_dir).expect("create shim dir");
1046
+ create_dir_all(entrypoint.parent().expect("entrypoint parent"))
1047
+ .expect("create entrypoint dir");
1048
+
1049
+ let fake_node = host_bin.join("node");
1050
+ write_executable(&fake_node, "#!/bin/sh\nexit 0\n").expect("write fake node");
1051
+ write(&entrypoint, "console.log('ok');\n").expect("write entrypoint");
1052
+
1053
+ let shim_path = shim_dir.join("codex");
1054
+ write_executable(
1055
+ &shim_path,
1056
+ r#"#!/bin/sh
1057
+ basedir=$(dirname "$0")
1058
+ if [ -x "$basedir/node" ]; then
1059
+ exec "$basedir/node" "$basedir/../@openai/codex/bin/codex.js" "$@"
1060
+ fi
1061
+ exec node "$basedir/../@openai/codex/bin/codex.js" "$@"
1062
+ "#,
1063
+ )
1064
+ .expect("write shim");
1065
+
1066
+ let original_path = env::var_os("PATH");
1067
+ unsafe {
1068
+ env::set_var("PATH", &host_bin);
1069
+ }
1070
+
1071
+ let launch =
1072
+ codex_launch_for_binary(shim_path.to_str().expect("shim path")).expect("launch config");
1073
+
1074
+ match original_path {
1075
+ Some(value) => unsafe { env::set_var("PATH", value) },
1076
+ None => unsafe { env::remove_var("PATH") },
1077
+ }
1078
+
1079
+ assert_eq!(launch.program, fake_node.display().to_string());
1080
+ assert_eq!(launch.leading_args, vec![entrypoint.display().to_string()]);
1081
+ }
1082
+
1083
+ #[cfg(unix)]
1084
+ #[test]
1085
+ fn resolve_host_command_skips_directory_and_non_executable_path_entries() {
1086
+ let _guard = env_lock();
1087
+ let root = temp_allowlist_dir().expect("temp root");
1088
+ let bad_bin = root.path.join("bad-bin");
1089
+ let blocked_file_bin = root.path.join("blocked-file-bin");
1090
+ let good_bin = root.path.join("good-bin");
1091
+ create_dir_all(&bad_bin).expect("create bad bin");
1092
+ create_dir_all(&blocked_file_bin).expect("create blocked file bin");
1093
+ create_dir_all(&good_bin).expect("create good bin");
1094
+
1095
+ let blocked_directory = bad_bin.join("node");
1096
+ create_dir_all(&blocked_directory).expect("create blocked directory");
1097
+
1098
+ let blocked_node = blocked_file_bin.join("node");
1099
+ write(&blocked_node, "#!/bin/sh\nexit 0\n").expect("write blocked file node");
1100
+ let executable_node = good_bin.join("node");
1101
+ write_executable(&executable_node, "#!/bin/sh\nexit 0\n").expect("write executable node");
1102
+
1103
+ #[cfg(unix)]
1104
+ {
1105
+ use std::fs;
1106
+ use std::os::unix::fs::PermissionsExt;
1107
+ let mut perms = fs::metadata(&blocked_node)
1108
+ .expect("stat blocked node")
1109
+ .permissions();
1110
+ perms.set_mode(0o644);
1111
+ fs::set_permissions(&blocked_node, perms).expect("chmod blocked node");
1112
+ }
1113
+
1114
+ let original_path = env::var_os("PATH");
1115
+ unsafe {
1116
+ env::set_var(
1117
+ "PATH",
1118
+ env::join_paths([
1119
+ bad_bin.as_path(),
1120
+ blocked_file_bin.as_path(),
1121
+ good_bin.as_path(),
1122
+ ])
1123
+ .expect("join path"),
1124
+ );
1125
+ }
1126
+
1127
+ let resolved = resolve_host_command("node");
1128
+
1129
+ match original_path {
1130
+ Some(value) => unsafe { env::set_var("PATH", value) },
1131
+ None => unsafe { env::remove_var("PATH") },
1132
+ }
1133
+
1134
+ assert_eq!(resolved, Some(executable_node));
1135
+ }
1136
+
1137
+ #[cfg(unix)]
1138
+ #[test]
1139
+ fn codex_launch_for_env_node_shebang_skips_non_executable_earlier_node_entry() {
1140
+ let _guard = env_lock();
1141
+ let root = temp_allowlist_dir().expect("temp root");
1142
+ let bad_bin = root.path.join("bad-bin");
1143
+ let good_bin = root.path.join("good-bin");
1144
+ create_dir_all(&bad_bin).expect("create bad bin");
1145
+ create_dir_all(&good_bin).expect("create good bin");
1146
+
1147
+ let blocked_node = bad_bin.join("node");
1148
+ write(&blocked_node, "#!/bin/sh\nexit 0\n").expect("write blocked node");
1149
+ let executable_node = good_bin.join("node");
1150
+ write_executable(&executable_node, "#!/bin/sh\nexit 0\n").expect("write executable node");
1151
+
1152
+ #[cfg(unix)]
1153
+ {
1154
+ use std::fs;
1155
+ use std::os::unix::fs::PermissionsExt;
1156
+ let mut perms = fs::metadata(&blocked_node)
1157
+ .expect("stat blocked node")
1158
+ .permissions();
1159
+ perms.set_mode(0o644);
1160
+ fs::set_permissions(&blocked_node, perms).expect("chmod blocked node");
1161
+ }
1162
+
1163
+ let script_path = root.path.join("codex-script");
1164
+ write(&script_path, b"#!/usr/bin/env node\nconsole.log(\"ok\");\n").expect("write script");
1165
+
1166
+ let original_path = env::var_os("PATH");
1167
+ unsafe {
1168
+ env::set_var(
1169
+ "PATH",
1170
+ env::join_paths([bad_bin.as_path(), good_bin.as_path()]).expect("join path"),
1171
+ );
1172
+ }
1173
+
1174
+ let launch = codex_launch_for_binary(script_path.to_str().expect("script path"))
1175
+ .expect("launch config");
1176
+
1177
+ match original_path {
1178
+ Some(value) => unsafe { env::set_var("PATH", value) },
1179
+ None => unsafe { env::remove_var("PATH") },
1180
+ }
1181
+
1182
+ assert_eq!(launch.program, executable_node.display().to_string());
1183
+ assert_eq!(launch.leading_args, vec![script_path.display().to_string()]);
1184
+ }
1185
+
899
1186
  #[cfg(unix)]
900
1187
  fn create_host_bin_with_commands(commands: &[&str]) -> (TempDirGuard, PathBuf) {
901
1188
  let root = temp_allowlist_dir().expect("temp root");
@@ -972,6 +1259,7 @@ mod tests {
972
1259
  #[cfg(unix)]
973
1260
  #[test]
974
1261
  fn prepare_allowlist_environment_preserves_present_command_wrapper_execution() {
1262
+ let _guard = env_lock();
975
1263
  let self_exe = env::current_exe().expect("current exe");
976
1264
  let pwd = resolve_host_command("pwd").expect("host pwd path");
977
1265
 
@@ -1047,6 +1335,15 @@ mod tests {
1047
1335
  );
1048
1336
  }
1049
1337
 
1338
+ #[test]
1339
+ fn allowlist_platform_diagnostic_blocks_windows_with_actionable_guidance() {
1340
+ let diagnostic = allowlist_platform_diagnostic("windows").expect("windows diagnostic");
1341
+
1342
+ assert!(diagnostic.contains("not ready on Windows"));
1343
+ assert!(diagnostic.contains("OMX_EXPLORE_BIN"));
1344
+ assert!(allowlist_platform_diagnostic("linux").is_none());
1345
+ }
1346
+
1050
1347
  #[test]
1051
1348
  fn validate_direct_command_covers_additional_head_wc_and_tail_rejections() {
1052
1349
  assert!(validate_direct_command("head", &["-".into()]).is_err());
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=foundation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foundation.test.d.ts","sourceRoot":"","sources":["../../../src/adapt/__tests__/foundation.test.ts"],"names":[],"mappings":""}