oh-my-codex 0.18.13 → 0.18.15

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 (360) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +41 -18
  4. package/dist/agents/__tests__/native-config.test.js +62 -0
  5. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  6. package/dist/agents/native-config.d.ts.map +1 -1
  7. package/dist/agents/native-config.js +15 -2
  8. package/dist/agents/native-config.js.map +1 -1
  9. package/dist/autopilot/__tests__/fsm.test.js +7 -0
  10. package/dist/autopilot/__tests__/fsm.test.js.map +1 -1
  11. package/dist/autopilot/__tests__/planner-routing.test.d.ts +2 -0
  12. package/dist/autopilot/__tests__/planner-routing.test.d.ts.map +1 -0
  13. package/dist/autopilot/__tests__/planner-routing.test.js +74 -0
  14. package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -0
  15. package/dist/autopilot/__tests__/ralplan-gate.test.js +315 -0
  16. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  17. package/dist/autopilot/completion-gate.js +1 -1
  18. package/dist/autopilot/completion-gate.js.map +1 -1
  19. package/dist/autopilot/fsm.d.ts +2 -2
  20. package/dist/autopilot/fsm.d.ts.map +1 -1
  21. package/dist/autopilot/fsm.js +3 -0
  22. package/dist/autopilot/fsm.js.map +1 -1
  23. package/dist/autopilot/planner-routing.d.ts +21 -0
  24. package/dist/autopilot/planner-routing.d.ts.map +1 -0
  25. package/dist/autopilot/planner-routing.js +59 -0
  26. package/dist/autopilot/planner-routing.js.map +1 -0
  27. package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
  28. package/dist/autopilot/ralplan-gate.js +30 -0
  29. package/dist/autopilot/ralplan-gate.js.map +1 -1
  30. package/dist/autoresearch/goal.d.ts.map +1 -1
  31. package/dist/autoresearch/goal.js +10 -1
  32. package/dist/autoresearch/goal.js.map +1 -1
  33. package/dist/cli/__tests__/autoresearch-goal.test.js +33 -0
  34. package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
  35. package/dist/cli/__tests__/autoresearch-guided.test.js +3 -0
  36. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  37. package/dist/cli/__tests__/codex-plugin-layout.test.js +30 -0
  38. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts +2 -0
  40. package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts.map +1 -0
  41. package/dist/cli/__tests__/doctor-artifact-ownership.test.js +158 -0
  42. package/dist/cli/__tests__/doctor-artifact-ownership.test.js.map +1 -0
  43. package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts +2 -0
  44. package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts.map +1 -0
  45. package/dist/cli/__tests__/doctor-explore-deprecated.test.js +33 -0
  46. package/dist/cli/__tests__/doctor-explore-deprecated.test.js.map +1 -0
  47. package/dist/cli/__tests__/doctor-spark-routing.test.js +13 -0
  48. package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
  49. package/dist/cli/__tests__/doctor-warning-copy.test.js +132 -2
  50. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  51. package/dist/cli/__tests__/index.test.js +5 -0
  52. package/dist/cli/__tests__/index.test.js.map +1 -1
  53. package/dist/cli/__tests__/mcp-parity.test.js +40 -0
  54. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  55. package/dist/cli/__tests__/nested-help-routing.test.js +4 -0
  56. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  57. package/dist/cli/__tests__/package-bin-contract.test.js +7 -1
  58. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  59. package/dist/cli/__tests__/performance-goal.test.js +22 -0
  60. package/dist/cli/__tests__/performance-goal.test.js.map +1 -1
  61. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts +2 -0
  62. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts.map +1 -0
  63. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +64 -0
  64. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -0
  65. package/dist/cli/__tests__/resume.test.js +96 -0
  66. package/dist/cli/__tests__/resume.test.js.map +1 -1
  67. package/dist/cli/__tests__/session-scoped-runtime.test.js +162 -0
  68. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  69. package/dist/cli/__tests__/session-search.test.js +36 -0
  70. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  71. package/dist/cli/__tests__/setup-agents-overwrite.test.js +70 -1
  72. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  73. package/dist/cli/__tests__/setup-install-mode.test.js +33 -0
  74. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  75. package/dist/cli/__tests__/team.test.js +17 -3
  76. package/dist/cli/__tests__/team.test.js.map +1 -1
  77. package/dist/cli/__tests__/ultragoal.test.js +37 -0
  78. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  79. package/dist/cli/__tests__/update.test.js +81 -2
  80. package/dist/cli/__tests__/update.test.js.map +1 -1
  81. package/dist/cli/__tests__/url.test.d.ts +2 -0
  82. package/dist/cli/__tests__/url.test.d.ts.map +1 -0
  83. package/dist/cli/__tests__/url.test.js +82 -0
  84. package/dist/cli/__tests__/url.test.js.map +1 -0
  85. package/dist/cli/autoresearch-goal.d.ts.map +1 -1
  86. package/dist/cli/autoresearch-goal.js +2 -1
  87. package/dist/cli/autoresearch-goal.js.map +1 -1
  88. package/dist/cli/autoresearch-intake.d.ts.map +1 -1
  89. package/dist/cli/autoresearch-intake.js +2 -0
  90. package/dist/cli/autoresearch-intake.js.map +1 -1
  91. package/dist/cli/doctor.d.ts +39 -1
  92. package/dist/cli/doctor.d.ts.map +1 -1
  93. package/dist/cli/doctor.js +460 -40
  94. package/dist/cli/doctor.js.map +1 -1
  95. package/dist/cli/index.d.ts +2 -2
  96. package/dist/cli/index.d.ts.map +1 -1
  97. package/dist/cli/index.js +71 -4
  98. package/dist/cli/index.js.map +1 -1
  99. package/dist/cli/performance-goal.d.ts.map +1 -1
  100. package/dist/cli/performance-goal.js +4 -2
  101. package/dist/cli/performance-goal.js.map +1 -1
  102. package/dist/cli/plugin-marketplace.d.ts +1 -0
  103. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  104. package/dist/cli/plugin-marketplace.js +88 -67
  105. package/dist/cli/plugin-marketplace.js.map +1 -1
  106. package/dist/cli/project-runtime-codex-homes.d.ts +2 -0
  107. package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -1
  108. package/dist/cli/project-runtime-codex-homes.js +112 -5
  109. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  110. package/dist/cli/setup.d.ts.map +1 -1
  111. package/dist/cli/setup.js +15 -6
  112. package/dist/cli/setup.js.map +1 -1
  113. package/dist/cli/ultragoal.d.ts.map +1 -1
  114. package/dist/cli/ultragoal.js +5 -1
  115. package/dist/cli/ultragoal.js.map +1 -1
  116. package/dist/cli/update.d.ts.map +1 -1
  117. package/dist/cli/update.js +23 -4
  118. package/dist/cli/update.js.map +1 -1
  119. package/dist/cli/url.d.ts +8 -0
  120. package/dist/cli/url.d.ts.map +1 -0
  121. package/dist/cli/url.js +69 -0
  122. package/dist/cli/url.js.map +1 -0
  123. package/dist/config/__tests__/codex-hooks.test.js +20 -4
  124. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  125. package/dist/config/__tests__/models.test.js +21 -1
  126. package/dist/config/__tests__/models.test.js.map +1 -1
  127. package/dist/config/codex-hooks.d.ts.map +1 -1
  128. package/dist/config/codex-hooks.js +1 -4
  129. package/dist/config/codex-hooks.js.map +1 -1
  130. package/dist/config/models.d.ts +5 -0
  131. package/dist/config/models.d.ts.map +1 -1
  132. package/dist/config/models.js +23 -0
  133. package/dist/config/models.js.map +1 -1
  134. package/dist/goal-workflows/codex-goal-snapshot.d.ts +2 -0
  135. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  136. package/dist/goal-workflows/codex-goal-snapshot.js +10 -0
  137. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  138. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +7 -0
  139. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  140. package/dist/hooks/__tests__/keyword-detector.test.js +46 -0
  141. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  142. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +100 -0
  143. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  144. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +75 -0
  145. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  146. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +225 -0
  147. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  148. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +75 -0
  149. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  150. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +563 -0
  151. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  152. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +187 -12
  153. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  154. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +225 -0
  155. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  156. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
  157. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  158. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +75 -0
  159. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  160. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  161. package/dist/hooks/keyword-detector.js +4 -0
  162. package/dist/hooks/keyword-detector.js.map +1 -1
  163. package/dist/hud/__tests__/reconcile.test.js +137 -2
  164. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  165. package/dist/hud/__tests__/state.test.js +107 -0
  166. package/dist/hud/__tests__/state.test.js.map +1 -1
  167. package/dist/hud/__tests__/tmux.test.js +17 -0
  168. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  169. package/dist/hud/reconcile.d.ts.map +1 -1
  170. package/dist/hud/reconcile.js +21 -7
  171. package/dist/hud/reconcile.js.map +1 -1
  172. package/dist/hud/render.d.ts.map +1 -1
  173. package/dist/hud/render.js +9 -0
  174. package/dist/hud/render.js.map +1 -1
  175. package/dist/hud/state.d.ts.map +1 -1
  176. package/dist/hud/state.js +43 -5
  177. package/dist/hud/state.js.map +1 -1
  178. package/dist/hud/types.d.ts +6 -0
  179. package/dist/hud/types.d.ts.map +1 -1
  180. package/dist/hud/types.js.map +1 -1
  181. package/dist/index.d.ts +1 -0
  182. package/dist/index.d.ts.map +1 -1
  183. package/dist/index.js +1 -0
  184. package/dist/index.js.map +1 -1
  185. package/dist/mcp/__tests__/bootstrap.test.js +24 -1
  186. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  187. package/dist/mcp/__tests__/state-paths.test.js +53 -0
  188. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  189. package/dist/mcp/bootstrap.d.ts +2 -0
  190. package/dist/mcp/bootstrap.d.ts.map +1 -1
  191. package/dist/mcp/bootstrap.js +32 -6
  192. package/dist/mcp/bootstrap.js.map +1 -1
  193. package/dist/mcp/state-paths.d.ts.map +1 -1
  194. package/dist/mcp/state-paths.js +11 -5
  195. package/dist/mcp/state-paths.js.map +1 -1
  196. package/dist/modes/__tests__/base-autopilot-gates.test.js +48 -0
  197. package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
  198. package/dist/modes/base.d.ts.map +1 -1
  199. package/dist/modes/base.js +1 -0
  200. package/dist/modes/base.js.map +1 -1
  201. package/dist/notifications/__tests__/http-client.test.js +9 -0
  202. package/dist/notifications/__tests__/http-client.test.js.map +1 -1
  203. package/dist/notifications/http-client.js +1 -1
  204. package/dist/notifications/http-client.js.map +1 -1
  205. package/dist/performance-goal/artifacts.d.ts.map +1 -1
  206. package/dist/performance-goal/artifacts.js +10 -1
  207. package/dist/performance-goal/artifacts.js.map +1 -1
  208. package/dist/ralplan/__tests__/consensus-gate.test.js +112 -0
  209. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  210. package/dist/ralplan/consensus-gate.d.ts +23 -0
  211. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  212. package/dist/ralplan/consensus-gate.js +133 -21
  213. package/dist/ralplan/consensus-gate.js.map +1 -1
  214. package/dist/scripts/__tests__/codex-native-hook.test.js +1251 -13
  215. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  216. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  217. package/dist/scripts/codex-native-hook.js +601 -24
  218. package/dist/scripts/codex-native-hook.js.map +1 -1
  219. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +1 -0
  220. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  221. package/dist/scripts/notify-hook/team-leader-nudge.js +279 -86
  222. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  223. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  224. package/dist/scripts/notify-hook/team-tmux-guard.js +87 -8
  225. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  226. package/dist/scripts/notify-hook/tmux-injection.js +1 -1
  227. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  228. package/dist/scripts/run-compiled-ci.d.ts +2 -0
  229. package/dist/scripts/run-compiled-ci.d.ts.map +1 -0
  230. package/dist/scripts/run-compiled-ci.js +77 -0
  231. package/dist/scripts/run-compiled-ci.js.map +1 -0
  232. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  233. package/dist/scripts/sync-plugin-mirror.js +1 -0
  234. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  235. package/dist/session-history/search.d.ts.map +1 -1
  236. package/dist/session-history/search.js +24 -9
  237. package/dist/session-history/search.js.map +1 -1
  238. package/dist/sidecar/__tests__/args.test.d.ts +2 -0
  239. package/dist/sidecar/__tests__/args.test.d.ts.map +1 -0
  240. package/dist/sidecar/__tests__/args.test.js +18 -0
  241. package/dist/sidecar/__tests__/args.test.js.map +1 -0
  242. package/dist/sidecar/index.d.ts.map +1 -1
  243. package/dist/sidecar/index.js +12 -4
  244. package/dist/sidecar/index.js.map +1 -1
  245. package/dist/state/__tests__/operations.test.js +107 -3
  246. package/dist/state/__tests__/operations.test.js.map +1 -1
  247. package/dist/state/operations.d.ts.map +1 -1
  248. package/dist/state/operations.js +57 -2
  249. package/dist/state/operations.js.map +1 -1
  250. package/dist/team/__tests__/delivery-e2e-smoke.test.js +22 -1
  251. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  252. package/dist/team/__tests__/model-contract.test.js +57 -1
  253. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  254. package/dist/team/__tests__/runtime.test.js +1468 -110
  255. package/dist/team/__tests__/runtime.test.js.map +1 -1
  256. package/dist/team/__tests__/scaling.test.js +63 -0
  257. package/dist/team/__tests__/scaling.test.js.map +1 -1
  258. package/dist/team/__tests__/state.test.js +24 -1
  259. package/dist/team/__tests__/state.test.js.map +1 -1
  260. package/dist/team/__tests__/tmux-session.test.js +512 -0
  261. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  262. package/dist/team/__tests__/worker-bootstrap.test.js +42 -9
  263. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  264. package/dist/team/model-contract.d.ts +16 -0
  265. package/dist/team/model-contract.d.ts.map +1 -1
  266. package/dist/team/model-contract.js +41 -1
  267. package/dist/team/model-contract.js.map +1 -1
  268. package/dist/team/runtime.d.ts.map +1 -1
  269. package/dist/team/runtime.js +116 -30
  270. package/dist/team/runtime.js.map +1 -1
  271. package/dist/team/scaling.d.ts.map +1 -1
  272. package/dist/team/scaling.js +9 -1
  273. package/dist/team/scaling.js.map +1 -1
  274. package/dist/team/state/types.d.ts +2 -0
  275. package/dist/team/state/types.d.ts.map +1 -1
  276. package/dist/team/state/types.js.map +1 -1
  277. package/dist/team/state.d.ts +3 -0
  278. package/dist/team/state.d.ts.map +1 -1
  279. package/dist/team/state.js +20 -0
  280. package/dist/team/state.js.map +1 -1
  281. package/dist/team/tmux-session.d.ts +38 -3
  282. package/dist/team/tmux-session.d.ts.map +1 -1
  283. package/dist/team/tmux-session.js +275 -36
  284. package/dist/team/tmux-session.js.map +1 -1
  285. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  286. package/dist/team/worker-bootstrap.js +26 -1
  287. package/dist/team/worker-bootstrap.js.map +1 -1
  288. package/dist/ultragoal/__tests__/artifacts.test.js +388 -0
  289. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  290. package/dist/ultragoal/__tests__/docs-contract.test.js +3 -0
  291. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  292. package/dist/ultragoal/artifacts.d.ts +23 -0
  293. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  294. package/dist/ultragoal/artifacts.js +275 -12
  295. package/dist/ultragoal/artifacts.js.map +1 -1
  296. package/dist/url-reader/__tests__/url-reader.test.d.ts +2 -0
  297. package/dist/url-reader/__tests__/url-reader.test.d.ts.map +1 -0
  298. package/dist/url-reader/__tests__/url-reader.test.js +433 -0
  299. package/dist/url-reader/__tests__/url-reader.test.js.map +1 -0
  300. package/dist/url-reader/index.d.ts +4 -0
  301. package/dist/url-reader/index.d.ts.map +1 -0
  302. package/dist/url-reader/index.js +522 -0
  303. package/dist/url-reader/index.js.map +1 -0
  304. package/dist/url-reader/types.d.ts +41 -0
  305. package/dist/url-reader/types.d.ts.map +1 -0
  306. package/dist/url-reader/types.js +2 -0
  307. package/dist/url-reader/types.js.map +1 -0
  308. package/dist/utils/__tests__/agents-md.test.js +39 -1
  309. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  310. package/dist/utils/__tests__/agents-model-table.test.js +28 -0
  311. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  312. package/dist/utils/agents-md.d.ts +4 -0
  313. package/dist/utils/agents-md.d.ts.map +1 -1
  314. package/dist/utils/agents-md.js +28 -0
  315. package/dist/utils/agents-md.js.map +1 -1
  316. package/dist/utils/agents-model-table.d.ts +7 -3
  317. package/dist/utils/agents-model-table.d.ts.map +1 -1
  318. package/dist/utils/agents-model-table.js +16 -8
  319. package/dist/utils/agents-model-table.js.map +1 -1
  320. package/dist/utils/platform-command.d.ts +7 -1
  321. package/dist/utils/platform-command.d.ts.map +1 -1
  322. package/dist/utils/platform-command.js +12 -1
  323. package/dist/utils/platform-command.js.map +1 -1
  324. package/dist/verification/__tests__/ci-rust-gates.test.js +4 -0
  325. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  326. package/dist/vscode/__tests__/index.test.d.ts +2 -0
  327. package/dist/vscode/__tests__/index.test.d.ts.map +1 -0
  328. package/dist/vscode/__tests__/index.test.js +236 -0
  329. package/dist/vscode/__tests__/index.test.js.map +1 -0
  330. package/dist/vscode/index.d.ts +76 -0
  331. package/dist/vscode/index.d.ts.map +1 -0
  332. package/dist/vscode/index.js +196 -0
  333. package/dist/vscode/index.js.map +1 -0
  334. package/package.json +3 -3
  335. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  336. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +59 -8
  337. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +9 -5
  338. package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +3 -0
  339. package/plugins/oh-my-codex/skills/performance-goal/SKILL.md +3 -0
  340. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
  341. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +22 -4
  342. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +2 -2
  343. package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +61 -0
  344. package/skills/autopilot/SKILL.md +9 -5
  345. package/skills/autoresearch-goal/SKILL.md +3 -0
  346. package/skills/ecomode/SKILL.md +1 -1
  347. package/skills/ecomode/references/agent-tiers.md +61 -0
  348. package/skills/performance-goal/SKILL.md +3 -0
  349. package/skills/ralplan/SKILL.md +1 -1
  350. package/skills/ultragoal/SKILL.md +22 -4
  351. package/skills/ultrawork/SKILL.md +2 -2
  352. package/skills/ultrawork/references/agent-tiers.md +61 -0
  353. package/src/scripts/__tests__/codex-native-hook.test.ts +1560 -181
  354. package/src/scripts/codex-native-hook.ts +663 -24
  355. package/src/scripts/notify-hook/team-leader-nudge.ts +298 -91
  356. package/src/scripts/notify-hook/team-tmux-guard.ts +92 -8
  357. package/src/scripts/notify-hook/tmux-injection.ts +1 -1
  358. package/src/scripts/run-compiled-ci.ts +85 -0
  359. package/src/scripts/sync-plugin-mirror.ts +1 -0
  360. package/templates/AGENTS.md +6 -0
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * omx doctor - Validate oh-my-codex installation
3
3
  */
4
- import { existsSync, readFileSync } from "fs";
5
- import { mkdtemp, readdir, readFile, rm } from "fs/promises";
4
+ import { constants, existsSync, readFileSync } from "fs";
5
+ import { access, chown, lstat, mkdtemp, readdir, readFile, rm } from "fs/promises";
6
6
  import { spawnSync } from "child_process";
7
- import { basename, join } from "path";
7
+ import { basename, join, relative } from "path";
8
8
  import { tmpdir } from "os";
9
9
  import { codexHome, codexConfigPath, codexPromptsDir, userSkillsDir, projectSkillsDir, omxStateDir, detectLegacySkillRootOverlap, codexAgentsDir, } from "../utils/paths.js";
10
10
  import { classifySpawnError, spawnPlatformCommandSync, } from "../utils/platform-command.js";
@@ -13,7 +13,7 @@ import { parse as parseToml } from "@iarna/toml";
13
13
  import { getBuiltinExploreHarnessUnsupportedReason, resolvePackagedExploreHarnessCommand, EXPLORE_BIN_ENV, } from "./explore.js";
14
14
  import { getPackageRoot } from "../utils/package.js";
15
15
  import { hasLegacyOmxTeamRunTable, getModelContextRecommendation, } from "../config/generator.js";
16
- import { MANAGED_HOOK_EVENTS, buildManagedCodexNativeHookCommand, discoverCodexHookConfigPaths, getManagedCodexHookCommandsForEvent, getMissingManagedCodexHookEvents, hasCodexHooksJsonTopLevelState, } from "../config/codex-hooks.js";
16
+ import { MANAGED_HOOK_EVENTS, buildManagedCodexNativeHookCommand, discoverCodexHookConfigPaths, resolveWindowsPowerShellPath, getManagedCodexHookCommandsForEvent, getMissingManagedCodexHookEvents, hasCodexHooksJsonTopLevelState, } from "../config/codex-hooks.js";
17
17
  import { OMX_FIRST_PARTY_MCP_SERVER_NAMES } from "../config/omx-first-party-mcp.js";
18
18
  import { getDefaultBridge, isBridgeEnabled } from "../runtime/bridge.js";
19
19
  import { OMX_EXPLORE_CMD_ENV, isExploreCommandRoutingEnabled, } from "../hooks/explore-routing.js";
@@ -24,10 +24,11 @@ import { readTriageConfig } from "../hooks/triage-config.js";
24
24
  import { readPersistedSetupPreferences, } from "./setup-preferences.js";
25
25
  import { OMX_LOCAL_MARKETPLACE_NAME, OMX_LOCAL_PLUGIN_CONFIG_KEY, discoverOmxPluginCacheDirs, expectedPackagedOmxSkillNames, packagedOmxPluginVersion, pluginHookCacheMatchesPackaged, readOmxPluginCacheState, resolvePackagedOmxMarketplace, } from "./plugin-marketplace.js";
26
26
  import { hasOmxAgentsContract } from "../utils/agents-md.js";
27
- import { OMX_DEFAULT_SPARK_MODEL_ENV, OMX_SPARK_MODEL_ENV, getCodexConfigRootModelProvider, getEnvConfiguredSparkDefaultModel, getMainDefaultModel, getSparkDefaultModel, getStandardDefaultModel, } from "../config/models.js";
27
+ import { OMX_DEFAULT_SPARK_MODEL_ENV, OMX_SPARK_MODEL_ENV, getAgentModelOverride, getCodexConfigRootModelProvider, getEnvConfiguredSparkDefaultModel, getMainDefaultModel, getSparkDefaultModel, getStandardDefaultModel, } from "../config/models.js";
28
28
  import { AGENT_DEFINITIONS } from "../agents/definitions.js";
29
29
  import { getInstallableNativeAgentNames } from "../agents/policy.js";
30
30
  import { readCatalogManifest } from "../catalog/reader.js";
31
+ const REPO_ARTIFACT_DIRS = [".omx", ".beads"];
31
32
  async function resolveDoctorScope(cwd) {
32
33
  const persisted = await readPersistedSetupPreferences(cwd);
33
34
  if (persisted?.scope) {
@@ -128,6 +129,24 @@ export async function doctor(options = {}) {
128
129
  : scopeResolution.source === "config"
129
130
  ? " (inferred from Codex plugin config)"
130
131
  : "";
132
+ if (options.force) {
133
+ if (options.dryRun) {
134
+ const check = await checkRepoArtifactOwnership(cwd);
135
+ if (check.status !== "pass") {
136
+ console.log(`Dry run: ${check.message}`);
137
+ console.log();
138
+ }
139
+ }
140
+ else {
141
+ const repair = await repairRepoArtifactOwnership(cwd);
142
+ if (repair.repaired > 0 || repair.skipped.length > 0) {
143
+ console.log(`Repo artifact ownership repair: ${repair.repaired} path(s) repaired${repair.skipped.length > 0
144
+ ? `, ${repair.skipped.length} skipped (${repair.skipped.join("; ")})`
145
+ : ""}`);
146
+ console.log();
147
+ }
148
+ }
149
+ }
131
150
  console.log("oh-my-codex doctor");
132
151
  console.log("==================\n");
133
152
  console.log(`Resolved setup scope: ${scopeResolution.scope}${scopeSourceMessage}`);
@@ -144,7 +163,10 @@ export async function doctor(options = {}) {
144
163
  // Check 2: Node.js version
145
164
  checks.push(checkNodeVersion());
146
165
  // Check 2.5: Explore harness readiness
147
- checks.push(checkExploreHarness());
166
+ const exploreRoutingState = await resolveExploreRoutingState(paths.configPath);
167
+ checks.push(checkExploreHarness(process.platform, process.env, {
168
+ exploreRoutingEnabled: exploreRoutingState.enabled,
169
+ }));
148
170
  // Check 3: Codex home directory
149
171
  checks.push(checkDirectory("Codex home", paths.codexHomeDir));
150
172
  // Check 4: Config file
@@ -168,13 +190,20 @@ export async function doctor(options = {}) {
168
190
  if (runtimeMirrorCheck)
169
191
  checks.push(runtimeMirrorCheck);
170
192
  // Check 4.5: Explore routing default
171
- checks.push(await checkExploreRouting(paths.configPath));
193
+ checks.push(checkExploreRoutingFromState(exploreRoutingState));
172
194
  // Check 4.6: Lore commit guard default
173
195
  checks.push(await checkLoreCommitGuard(paths.configPath));
196
+ // Check 4.7: External process guards
197
+ const externalProcessGuardCheck = await checkExternalCodexProcessGuards();
198
+ if (externalProcessGuardCheck)
199
+ checks.push(externalProcessGuardCheck);
174
200
  // Check 5: Prompts installed
175
201
  checks.push(await checkPrompts(paths.promptsDir, scopeResolution.installMode));
176
202
  // Check 6: Skills installed
177
203
  checks.push(await checkSkills(paths, scopeResolution.installMode));
204
+ if (scopeResolution.installMode === "plugin") {
205
+ checks.push(await checkPluginVersionDiagnostics(paths.codexHomeDir));
206
+ }
178
207
  // Check 6.25: Native reviewer roles required by RALPLAN/Autopilot
179
208
  const nativeReviewerRolesCheck = checkNativeReviewerRoles(paths, scopeResolution.installMode);
180
209
  if (nativeReviewerRolesCheck)
@@ -189,6 +218,7 @@ export async function doctor(options = {}) {
189
218
  checks.push(checkAgentsMd(scopeResolution.scope, paths.codexHomeDir, scopeResolution.installMode));
190
219
  // Check 8: State directory
191
220
  checks.push(checkDirectory("State dir", paths.stateDir));
221
+ checks.push(await checkRepoArtifactOwnership(cwd));
192
222
  // Check 9: MCP servers configured
193
223
  checks.push(await checkMcpServers(paths.configPath, scopeResolution.installMode, scopeResolution.mcpMode));
194
224
  // Check 10: Prompt triage
@@ -559,7 +589,16 @@ function checkNodeVersion() {
559
589
  message: `v${process.versions.node} (need >= 20)`,
560
590
  };
561
591
  }
562
- export function checkExploreHarness(platform = process.platform, env = process.env) {
592
+ export function checkExploreHarness(platform = process.platform, env = process.env, options = {}) {
593
+ const override = env[EXPLORE_BIN_ENV]?.trim();
594
+ const exploreRoutingEnabled = options.exploreRoutingEnabled ?? isExploreCommandRoutingEnabled(env);
595
+ if (!override && !exploreRoutingEnabled) {
596
+ return {
597
+ name: "Explore Harness",
598
+ status: "pass",
599
+ message: "skipped: omx explore is hard-deprecated and explore routing is disabled by default; use omx sparkshell for shell-native read-only evidence",
600
+ };
601
+ }
563
602
  const packageRoot = getPackageRoot();
564
603
  const manifestPath = join(packageRoot, "crates", "omx-explore", "Cargo.toml");
565
604
  if (!existsSync(manifestPath)) {
@@ -569,7 +608,6 @@ export function checkExploreHarness(platform = process.platform, env = process.e
569
608
  message: "Rust harness sources not found in this install (omx explore unavailable until packaged or OMX_EXPLORE_BIN is set)",
570
609
  };
571
610
  }
572
- const override = env[EXPLORE_BIN_ENV]?.trim();
573
611
  if (override) {
574
612
  const resolved = join(packageRoot, override);
575
613
  if (existsSync(override) || existsSync(resolved)) {
@@ -640,6 +678,166 @@ function checkDirectory(name, path) {
640
678
  }
641
679
  return { name, status: "warn", message: `${path} (not created yet)` };
642
680
  }
681
+ function currentProcessUid() {
682
+ return typeof process.getuid === "function" ? process.getuid() : undefined;
683
+ }
684
+ function currentProcessGid() {
685
+ return typeof process.getgid === "function" ? process.getgid() : undefined;
686
+ }
687
+ function remediationCommand(repoRoot) {
688
+ return `sudo chown -R $(id -u):$(id -g) ${JSON.stringify(repoRoot)}`;
689
+ }
690
+ function formatArtifactPath(repoRoot, path) {
691
+ const rel = relative(repoRoot, path);
692
+ return rel === "" ? "." : rel;
693
+ }
694
+ function formatArtifactIssue(repoRoot, issue) {
695
+ const owner = typeof issue.uid === "number" && typeof issue.gid === "number"
696
+ ? ` uid=${issue.uid} gid=${issue.gid}`
697
+ : "";
698
+ return `${formatArtifactPath(repoRoot, issue.path)} (${issue.reason}${owner})`;
699
+ }
700
+ function shouldReportOwnerMismatch(uid, currentUid) {
701
+ if (typeof uid !== "number")
702
+ return false;
703
+ if (uid === 0)
704
+ return true;
705
+ return typeof currentUid === "number" && uid !== currentUid;
706
+ }
707
+ function isOwnershipIssue(issue) {
708
+ return issue.type === "ownership";
709
+ }
710
+ async function collectRepoArtifactOwnershipIssues(repoRoot, options = {}) {
711
+ if (process.platform === "win32")
712
+ return [];
713
+ const currentUid = options.currentUid ?? currentProcessUid();
714
+ const maxExamples = options.maxExamples ?? 20;
715
+ const statPath = options.statPath ?? lstat;
716
+ const readDir = options.readDir ?? readdir;
717
+ const accessPath = options.accessPath ?? access;
718
+ const issues = [];
719
+ const visited = new Set();
720
+ async function visit(path) {
721
+ if (issues.length >= maxExamples)
722
+ return;
723
+ let info;
724
+ try {
725
+ info = await statPath(path);
726
+ }
727
+ catch {
728
+ return;
729
+ }
730
+ if (info.isSymbolicLink())
731
+ return;
732
+ const uid = typeof info.uid === "number" ? info.uid : undefined;
733
+ const gid = typeof info.gid === "number" ? info.gid : undefined;
734
+ let reason = null;
735
+ if (uid === 0)
736
+ reason = "root-owned";
737
+ else if (shouldReportOwnerMismatch(uid, currentUid))
738
+ reason = "owner-mismatch";
739
+ else {
740
+ try {
741
+ await accessPath(path, constants.W_OK);
742
+ }
743
+ catch {
744
+ reason = "not-writable";
745
+ }
746
+ }
747
+ if (reason) {
748
+ issues.push({
749
+ path,
750
+ reason,
751
+ type: reason === "not-writable" ? "writability" : "ownership",
752
+ uid,
753
+ gid,
754
+ });
755
+ }
756
+ if (issues.length >= maxExamples || !info.isDirectory())
757
+ return;
758
+ if (visited.has(path))
759
+ return;
760
+ visited.add(path);
761
+ let entries;
762
+ try {
763
+ entries = await readDir(path);
764
+ }
765
+ catch {
766
+ return;
767
+ }
768
+ for (const entry of entries) {
769
+ await visit(join(path, entry));
770
+ if (issues.length >= maxExamples)
771
+ return;
772
+ }
773
+ }
774
+ for (const dir of REPO_ARTIFACT_DIRS) {
775
+ const root = join(repoRoot, dir);
776
+ if (existsSync(root))
777
+ await visit(root);
778
+ }
779
+ return issues;
780
+ }
781
+ export async function checkRepoArtifactOwnership(repoRoot, options = {}) {
782
+ const issues = await collectRepoArtifactOwnershipIssues(repoRoot, options);
783
+ if (issues.length === 0) {
784
+ return {
785
+ name: "Repo artifact ownership",
786
+ status: "pass",
787
+ message: "repo-local .omx/.beads artifacts are writable by the current user",
788
+ };
789
+ }
790
+ const examples = issues
791
+ .slice(0, options.maxExamples ?? 5)
792
+ .map((issue) => formatArtifactIssue(repoRoot, issue))
793
+ .join("; ");
794
+ const repair = remediationCommand(repoRoot);
795
+ return {
796
+ name: "Repo artifact ownership",
797
+ status: "warn",
798
+ message: `${issues.length} root-owned, owner-mismatched, or non-writable repo artifact(s): ${examples}. Safe remediation: ${repair}. Automatic repair is only run by \"omx doctor --force\" when the repo root is owned by the current user.`,
799
+ };
800
+ }
801
+ export async function repairRepoArtifactOwnership(repoRoot, options = {}) {
802
+ if (process.platform === "win32")
803
+ return { repaired: 0, skipped: [] };
804
+ const currentUid = options.currentUid ?? currentProcessUid();
805
+ const currentGid = options.currentGid ?? currentProcessGid();
806
+ if (typeof currentUid !== "number" || typeof currentGid !== "number") {
807
+ return { repaired: 0, skipped: ["current uid/gid unavailable"] };
808
+ }
809
+ const statPath = options.statPath ?? lstat;
810
+ const repoInfo = await statPath(repoRoot);
811
+ if (repoInfo.uid !== currentUid) {
812
+ return { repaired: 0, skipped: ["repo root is not owned by the current user"] };
813
+ }
814
+ const issues = await collectRepoArtifactOwnershipIssues(repoRoot, {
815
+ ...options,
816
+ currentUid,
817
+ currentGid,
818
+ maxExamples: Number.MAX_SAFE_INTEGER,
819
+ statPath,
820
+ });
821
+ const ownershipIssues = issues.filter(isOwnershipIssue);
822
+ const writabilityIssues = issues.filter((issue) => !isOwnershipIssue(issue));
823
+ const chownPath = options.chownPath ?? chown;
824
+ let repaired = 0;
825
+ const skipped = [];
826
+ for (const issue of writabilityIssues) {
827
+ skipped.push(`${formatArtifactPath(repoRoot, issue.path)}: not writable by current user`);
828
+ }
829
+ for (const issue of ownershipIssues) {
830
+ try {
831
+ await chownPath(issue.path, currentUid, currentGid);
832
+ repaired++;
833
+ }
834
+ catch (error) {
835
+ const message = error instanceof Error ? error.message : String(error);
836
+ skipped.push(`${formatArtifactPath(repoRoot, issue.path)}: ${message}`);
837
+ }
838
+ }
839
+ return { repaired, skipped };
840
+ }
643
841
  function validateToml(content) {
644
842
  try {
645
843
  parseToml(content);
@@ -738,10 +936,36 @@ async function checkModelContextRecommendation(configPath) {
738
936
  return null;
739
937
  }
740
938
  }
741
- async function checkExploreRouting(configPath) {
742
- const envValue = process.env[OMX_EXPLORE_CMD_ENV];
939
+ async function resolveExploreRoutingState(configPath, env = process.env) {
940
+ const envValue = env[OMX_EXPLORE_CMD_ENV];
743
941
  if (typeof envValue === "string") {
744
- if (isExploreCommandRoutingEnabled(process.env)) {
942
+ return { source: "env", enabled: isExploreCommandRoutingEnabled(env) };
943
+ }
944
+ if (!existsSync(configPath)) {
945
+ return { source: "default", enabled: false, reason: "missing-config" };
946
+ }
947
+ try {
948
+ const content = await readFile(configPath, "utf-8");
949
+ const parsed = parseToml(content);
950
+ const configuredValue = parsed?.shell_environment_policy?.set?.USE_OMX_EXPLORE_CMD ??
951
+ parsed?.env?.USE_OMX_EXPLORE_CMD;
952
+ if (typeof configuredValue === "string") {
953
+ return {
954
+ source: "config",
955
+ enabled: isExploreCommandRoutingEnabled({
956
+ USE_OMX_EXPLORE_CMD: configuredValue,
957
+ }),
958
+ };
959
+ }
960
+ return { source: "default", enabled: false, reason: "unset" };
961
+ }
962
+ catch {
963
+ return { source: "unreadable", enabled: false };
964
+ }
965
+ }
966
+ function checkExploreRoutingFromState(state) {
967
+ if (state.source === "env") {
968
+ if (state.enabled) {
745
969
  return {
746
970
  name: "Explore routing",
747
971
  status: "warn",
@@ -754,47 +978,34 @@ async function checkExploreRouting(configPath) {
754
978
  message: "deprecated compatibility routing disabled by environment override (recommended)",
755
979
  };
756
980
  }
757
- if (!existsSync(configPath)) {
758
- return {
759
- name: "Explore routing",
760
- status: "pass",
761
- message: "deprecated by default (config.toml not found yet)",
762
- };
763
- }
764
- try {
765
- const content = await readFile(configPath, "utf-8");
766
- const parsed = parseToml(content);
767
- const configuredValue = parsed?.shell_environment_policy?.set?.USE_OMX_EXPLORE_CMD ??
768
- parsed?.env?.USE_OMX_EXPLORE_CMD;
769
- if (typeof configuredValue === "string") {
770
- if (isExploreCommandRoutingEnabled({
771
- USE_OMX_EXPLORE_CMD: configuredValue,
772
- })) {
773
- return {
774
- name: "Explore routing",
775
- status: "warn",
776
- message: 'deprecated compatibility routing enabled in config.toml; set USE_OMX_EXPLORE_CMD = "0" under [shell_environment_policy.set] and use normal Codex repo inspection or omx sparkshell instead',
777
- };
778
- }
981
+ if (state.source === "config") {
982
+ if (state.enabled) {
779
983
  return {
780
984
  name: "Explore routing",
781
- status: "pass",
782
- message: "deprecated compatibility routing disabled in config.toml (recommended)",
985
+ status: "warn",
986
+ message: 'deprecated compatibility routing enabled in config.toml; set USE_OMX_EXPLORE_CMD = "0" under [shell_environment_policy.set] and use normal Codex repo inspection or omx sparkshell instead',
783
987
  };
784
988
  }
785
989
  return {
786
990
  name: "Explore routing",
787
991
  status: "pass",
788
- message: "deprecated by default",
992
+ message: "deprecated compatibility routing disabled in config.toml (recommended)",
789
993
  };
790
994
  }
791
- catch {
995
+ if (state.source === "unreadable") {
792
996
  return {
793
997
  name: "Explore routing",
794
998
  status: "fail",
795
999
  message: "cannot read config.toml for explore routing check",
796
1000
  };
797
1001
  }
1002
+ return {
1003
+ name: "Explore routing",
1004
+ status: "pass",
1005
+ message: state.reason === "missing-config"
1006
+ ? "deprecated by default (config.toml not found yet)"
1007
+ : "deprecated by default",
1008
+ };
798
1009
  }
799
1010
  const LORE_COMMIT_GUARD_EXPLICIT_OPT_OUT_VALUES = new Set([
800
1011
  "0",
@@ -877,6 +1088,117 @@ async function checkLoreCommitGuard(configPath) {
877
1088
  function isExplicitLoreCommitGuardOptOut(value) {
878
1089
  return LORE_COMMIT_GUARD_EXPLICIT_OPT_OUT_VALUES.has(value.trim().toLowerCase());
879
1090
  }
1091
+ function decodeBasicXmlEntities(value) {
1092
+ return value
1093
+ .replaceAll("&", "&")
1094
+ .replaceAll("&lt;", "<")
1095
+ .replaceAll("&gt;", ">")
1096
+ .replaceAll("&quot;", '"')
1097
+ .replaceAll("&apos;", "'");
1098
+ }
1099
+ function extractPlistStrings(content) {
1100
+ return [...content.matchAll(/<string>([^<]+)<\/string>/g)].map((match) => decodeBasicXmlEntities(match[1]?.trim() ?? ""));
1101
+ }
1102
+ function extractPlistLabel(content, fallback) {
1103
+ const labelMatch = content.match(/<key>Label<\/key>\s*<string>([^<]+)<\/string>/);
1104
+ return decodeBasicXmlEntities(labelMatch?.[1]?.trim() || fallback);
1105
+ }
1106
+ async function readExistingTextFile(path) {
1107
+ try {
1108
+ return await readFile(path, "utf-8");
1109
+ }
1110
+ catch {
1111
+ return "";
1112
+ }
1113
+ }
1114
+ function expandLaunchAgentPath(value, homeDir) {
1115
+ if (value === "~")
1116
+ return homeDir;
1117
+ if (value.startsWith("~/"))
1118
+ return join(homeDir, value.slice(2));
1119
+ if (value === "$HOME")
1120
+ return homeDir;
1121
+ if (value.startsWith("$HOME/"))
1122
+ return join(homeDir, value.slice(6));
1123
+ if (value === "${HOME}")
1124
+ return homeDir;
1125
+ if (value.startsWith("${HOME}/"))
1126
+ return join(homeDir, value.slice(8));
1127
+ return value;
1128
+ }
1129
+ function classifyExternalCodexProcessGuard(plistContent, combinedContent) {
1130
+ const lowerPlist = plistContent.toLowerCase();
1131
+ const lower = combinedContent.toLowerCase();
1132
+ if (lower.includes("codex-mcp-child-guard") ||
1133
+ lower.includes("codex_mcp_child_guard") ||
1134
+ combinedContent.includes("CODEX_MCP_GUARD_DEDUPE_APP_CHILDREN") ||
1135
+ (lower.includes("codex app-server") &&
1136
+ lower.includes("pgrep -p") &&
1137
+ lower.includes("kill"))) {
1138
+ return "Codex app-server MCP child dedupe";
1139
+ }
1140
+ if ((lowerPlist.includes("codex-xcode-mcp-guard") ||
1141
+ lowerPlist.includes("codex_xcode_mcp_guard")) &&
1142
+ lower.includes("xcodebuildmcp") &&
1143
+ lower.includes("kill")) {
1144
+ return "XcodeBuildMCP cleanup";
1145
+ }
1146
+ return null;
1147
+ }
1148
+ export async function checkExternalCodexProcessGuards(options = {}) {
1149
+ const platform = options.platform ?? process.platform;
1150
+ if (platform !== "darwin")
1151
+ return null;
1152
+ const homeDir = options.homeDir ?? process.env.HOME;
1153
+ if (!homeDir)
1154
+ return null;
1155
+ const launchAgentsDir = join(homeDir, "Library", "LaunchAgents");
1156
+ if (!existsSync(launchAgentsDir))
1157
+ return null;
1158
+ let entries;
1159
+ try {
1160
+ entries = await readdir(launchAgentsDir);
1161
+ }
1162
+ catch {
1163
+ return null;
1164
+ }
1165
+ const findings = [];
1166
+ for (const entry of entries) {
1167
+ if (!entry.endsWith(".plist"))
1168
+ continue;
1169
+ const plistPath = join(launchAgentsDir, entry);
1170
+ const plistContent = await readExistingTextFile(plistPath);
1171
+ if (plistContent.trim() === "")
1172
+ continue;
1173
+ const loweredPlist = plistContent.toLowerCase();
1174
+ if (!loweredPlist.includes("codex") &&
1175
+ !loweredPlist.includes("mcp") &&
1176
+ !loweredPlist.includes("omx")) {
1177
+ continue;
1178
+ }
1179
+ let combinedContent = plistContent;
1180
+ for (const value of extractPlistStrings(plistContent)) {
1181
+ const expandedValue = expandLaunchAgentPath(value, homeDir);
1182
+ if (!expandedValue.startsWith("/"))
1183
+ continue;
1184
+ if (!existsSync(expandedValue))
1185
+ continue;
1186
+ combinedContent += `\n${await readExistingTextFile(expandedValue)}`;
1187
+ }
1188
+ const reason = classifyExternalCodexProcessGuard(plistContent, combinedContent);
1189
+ if (!reason)
1190
+ continue;
1191
+ const label = extractPlistLabel(plistContent, entry);
1192
+ findings.push(`${label} (${reason})`);
1193
+ }
1194
+ if (findings.length === 0)
1195
+ return null;
1196
+ return {
1197
+ name: "External process guards",
1198
+ status: "warn",
1199
+ message: `external LaunchAgent(s) may terminate Codex/MCP helper processes outside OMX setup ownership: ${findings.join(", ")}; unload/remove them before attributing SIGTERM or transport resets to standard OMX MCP configuration`,
1200
+ };
1201
+ }
880
1202
  function isEnabledTomlValue(value) {
881
1203
  return value === true || (typeof value === "string" && ["1", "true", "yes", "on"].includes(value.trim().toLowerCase()));
882
1204
  }
@@ -1222,6 +1544,27 @@ export function classifyPostCompactHookStdout(stdout) {
1222
1544
  };
1223
1545
  }
1224
1546
  }
1547
+ export function buildPostCompactSmokeSpawnInvocation(expectedCommand, options = {}) {
1548
+ const platform = options.platform ?? process.platform;
1549
+ if (platform === "win32") {
1550
+ return {
1551
+ command: resolveWindowsPowerShellPath(options.env),
1552
+ args: [
1553
+ "-NoProfile",
1554
+ "-ExecutionPolicy",
1555
+ "Bypass",
1556
+ "-Command",
1557
+ expectedCommand,
1558
+ ],
1559
+ shell: false,
1560
+ };
1561
+ }
1562
+ return {
1563
+ command: expectedCommand,
1564
+ args: [],
1565
+ shell: true,
1566
+ };
1567
+ }
1225
1568
  async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
1226
1569
  if (!existsSync(hooksPath))
1227
1570
  return null;
@@ -1254,7 +1597,8 @@ async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
1254
1597
  cwd: smokeCwd,
1255
1598
  session_id: "omx-doctor-postcompact-smoke",
1256
1599
  });
1257
- const result = spawnSync(expectedCommand, {
1600
+ const smokeInvocation = buildPostCompactSmokeSpawnInvocation(expectedCommand);
1601
+ const result = spawnSync(smokeInvocation.command, smokeInvocation.args, {
1258
1602
  cwd,
1259
1603
  encoding: "utf-8",
1260
1604
  env: {
@@ -1262,7 +1606,7 @@ async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
1262
1606
  OMX_NATIVE_HOOK_DOCTOR_SMOKE: "1",
1263
1607
  },
1264
1608
  input: payload,
1265
- shell: true,
1609
+ shell: smokeInvocation.shell,
1266
1610
  timeout: 5_000,
1267
1611
  });
1268
1612
  if (result.error) {
@@ -1490,6 +1834,70 @@ async function checkPluginMarketplaceRegistration(configPath, codexHomeDir) {
1490
1834
  };
1491
1835
  }
1492
1836
  }
1837
+ async function readDoctorInstallStamp(codexHomeDir) {
1838
+ try {
1839
+ const parsed = JSON.parse(await readFile(join(codexHomeDir, ".omx", "install-state.json"), "utf-8"));
1840
+ return {
1841
+ ...(typeof parsed.install_channel === "string" ? { install_channel: parsed.install_channel } : {}),
1842
+ ...(typeof parsed.dev_base_version === "string" ? { dev_base_version: parsed.dev_base_version } : {}),
1843
+ ...(typeof parsed.install_revision === "string" ? { install_revision: parsed.install_revision } : {}),
1844
+ };
1845
+ }
1846
+ catch {
1847
+ return null;
1848
+ }
1849
+ }
1850
+ async function checkPluginVersionDiagnostics(codexHomeDir) {
1851
+ const packagedMarketplace = await resolvePackagedOmxMarketplace(getPackageRoot());
1852
+ if (!packagedMarketplace) {
1853
+ return {
1854
+ name: "Plugin versions",
1855
+ status: "warn",
1856
+ message: `packaged ${OMX_LOCAL_MARKETPLACE_NAME} metadata was not found; reinstall oh-my-codex`,
1857
+ };
1858
+ }
1859
+ const [manifestVersion, stamp] = await Promise.all([
1860
+ packagedOmxPluginVersion(packagedMarketplace),
1861
+ readDoctorInstallStamp(codexHomeDir),
1862
+ ]);
1863
+ if (!manifestVersion) {
1864
+ return {
1865
+ name: "Plugin versions",
1866
+ status: "warn",
1867
+ message: "packaged plugin manifest has no version; reinstall oh-my-codex",
1868
+ };
1869
+ }
1870
+ const cacheDir = join(codexHomeDir, "plugins", "cache", OMX_LOCAL_MARKETPLACE_NAME, "oh-my-codex", manifestVersion);
1871
+ const cacheState = await readOmxPluginCacheState(cacheDir);
1872
+ if (cacheState?.manifestVersion !== manifestVersion) {
1873
+ return {
1874
+ name: "Plugin versions",
1875
+ status: "warn",
1876
+ message: `expected cache directory ${cacheDir} is not materialized with packaged plugin manifest version ${manifestVersion}; run "omx setup --plugin --force" to refresh the plugin cache`,
1877
+ };
1878
+ }
1879
+ if (stamp?.install_channel === "dev") {
1880
+ const devDisplay = stamp.dev_base_version && stamp.install_revision
1881
+ ? `v${stamp.dev_base_version}-dev-${stamp.install_revision}`
1882
+ : null;
1883
+ const stampDetail = [
1884
+ `package/plugin manifest version ${manifestVersion}`,
1885
+ devDisplay ? `dev display version ${devDisplay}` : null,
1886
+ stamp.dev_base_version ? `dev_base_version ${stamp.dev_base_version}` : null,
1887
+ stamp.install_revision ? `install_revision ${stamp.install_revision}` : null,
1888
+ ].filter(Boolean).join("; ");
1889
+ return {
1890
+ name: "Plugin versions",
1891
+ status: "pass",
1892
+ message: `${stampDetail}; Codex may keep current-session plugin skill metadata until a new Codex session starts`,
1893
+ };
1894
+ }
1895
+ return {
1896
+ name: "Plugin versions",
1897
+ status: "pass",
1898
+ message: `cache directory version matches packaged plugin manifest version ${manifestVersion}`,
1899
+ };
1900
+ }
1493
1901
  const REQUIRED_NATIVE_REVIEWER_ROLES = ["architect", "critic"];
1494
1902
  const ADVISORY_NATIVE_REVIEWER_ROLES = ["scholastic"];
1495
1903
  function getParsedAgentTables(configPath) {
@@ -1633,6 +2041,9 @@ export function checkSparkRouting(paths) {
1633
2041
  const standardModel = getStandardDefaultModel(codexHomeOverride);
1634
2042
  const sparkSource = resolveSparkModelSource(codexHomeOverride);
1635
2043
  const rootProvider = getCodexConfigRootModelProvider(codexHomeOverride);
2044
+ const explicitSparkAgentOverrides = new Map(getInstallableSparkLaneAgentNames()
2045
+ .map((agentName) => [agentName, getAgentModelOverride(agentName, codexHomeOverride)])
2046
+ .filter((entry) => typeof entry[1] === "string"));
1636
2047
  const laneSummary = `lanes: frontier=\`${frontierModel}\`, standard=\`${standardModel}\`, ` +
1637
2048
  `spark=\`${sparkModel}\` (source: ${sparkSource})`;
1638
2049
  const sparkAgents = getInstallableSparkLaneAgentNames();
@@ -1656,6 +2067,15 @@ export function checkSparkRouting(paths) {
1656
2067
  problems.push(`${agentName}.toml has no model field (stale install; run \`omx setup --force\`)`);
1657
2068
  continue;
1658
2069
  }
2070
+ const explicitOverride = explicitSparkAgentOverrides.get(agentName);
2071
+ if (explicitOverride) {
2072
+ if (info.model !== explicitOverride) {
2073
+ problems.push(`${agentName}.toml model is \`${info.model}\` but agentModels.${agentName} explicitly resolves to \`${explicitOverride}\` (stale install; run \`omx setup --force\`)`);
2074
+ continue;
2075
+ }
2076
+ wired.push(`${agentName} -> \`${info.model}\` (agentModels override)${info.modelProvider ? ` (provider: ${info.modelProvider})` : ""}`);
2077
+ continue;
2078
+ }
1659
2079
  if (info.model !== sparkModel) {
1660
2080
  problems.push(`${agentName}.toml model is \`${info.model}\` but the resolved Spark model is \`${sparkModel}\` (stale install; run \`omx setup --force\`)`);
1661
2081
  continue;