oh-my-codex 0.14.4 → 0.15.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 (614) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/Cargo.lock +6 -6
  3. package/Cargo.toml +2 -1
  4. package/README.md +40 -7
  5. package/crates/omx-explore/Cargo.toml +1 -0
  6. package/crates/omx-explore/src/main.rs +246 -24
  7. package/crates/omx-mux/Cargo.toml +1 -0
  8. package/crates/omx-runtime/Cargo.toml +1 -0
  9. package/crates/omx-runtime-core/Cargo.toml +1 -0
  10. package/crates/omx-runtime-core/src/dispatch.rs +2 -1
  11. package/crates/omx-runtime-core/src/engine.rs +2 -2
  12. package/crates/omx-runtime-core/src/mailbox.rs +2 -1
  13. package/crates/omx-sparkshell/Cargo.toml +1 -0
  14. package/crates/omx-sparkshell/src/codex_bridge.rs +1 -0
  15. package/crates/omx-sparkshell/src/prompt.rs +1 -0
  16. package/crates/omx-sparkshell/src/threshold.rs +1 -0
  17. package/dist/agents/__tests__/native-config.test.js +98 -4
  18. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  19. package/dist/agents/native-config.d.ts +4 -0
  20. package/dist/agents/native-config.d.ts.map +1 -1
  21. package/dist/agents/native-config.js +19 -3
  22. package/dist/agents/native-config.js.map +1 -1
  23. package/dist/agents/policy.d.ts +10 -0
  24. package/dist/agents/policy.d.ts.map +1 -0
  25. package/dist/agents/policy.js +61 -0
  26. package/dist/agents/policy.js.map +1 -0
  27. package/dist/catalog/__tests__/generator.test.js +2 -0
  28. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  29. package/dist/catalog/__tests__/plugin-bundle-ssot.test.d.ts +2 -0
  30. package/dist/catalog/__tests__/plugin-bundle-ssot.test.d.ts.map +1 -0
  31. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +72 -0
  32. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -0
  33. package/dist/catalog/installable.d.ts +5 -0
  34. package/dist/catalog/installable.d.ts.map +1 -0
  35. package/dist/catalog/installable.js +13 -0
  36. package/dist/catalog/installable.js.map +1 -0
  37. package/dist/catalog/skill-mirror.d.ts +20 -0
  38. package/dist/catalog/skill-mirror.d.ts.map +1 -0
  39. package/dist/catalog/skill-mirror.js +104 -0
  40. package/dist/catalog/skill-mirror.js.map +1 -0
  41. package/dist/cli/__tests__/ask.test.js +4 -1
  42. package/dist/cli/__tests__/ask.test.js.map +1 -1
  43. package/dist/cli/__tests__/codex-plugin-layout.test.d.ts +2 -0
  44. package/dist/cli/__tests__/codex-plugin-layout.test.d.ts.map +1 -0
  45. package/dist/cli/__tests__/codex-plugin-layout.test.js +210 -0
  46. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -0
  47. package/dist/cli/__tests__/doctor-team.test.js +46 -1
  48. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  49. package/dist/cli/__tests__/doctor-warning-copy.test.js +225 -111
  50. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  51. package/dist/cli/__tests__/exec.test.js +96 -1
  52. package/dist/cli/__tests__/exec.test.js.map +1 -1
  53. package/dist/cli/__tests__/explore.test.js +26 -9
  54. package/dist/cli/__tests__/explore.test.js.map +1 -1
  55. package/dist/cli/__tests__/index.test.js +301 -7
  56. package/dist/cli/__tests__/index.test.js.map +1 -1
  57. package/dist/cli/__tests__/launch-fallback.test.js +358 -4
  58. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  59. package/dist/cli/__tests__/list.test.d.ts +2 -0
  60. package/dist/cli/__tests__/list.test.d.ts.map +1 -0
  61. package/dist/cli/__tests__/list.test.js +38 -0
  62. package/dist/cli/__tests__/list.test.js.map +1 -0
  63. package/dist/cli/__tests__/mcp-parity.test.js +86 -0
  64. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  65. package/dist/cli/__tests__/mcp-serve.test.d.ts +2 -0
  66. package/dist/cli/__tests__/mcp-serve.test.d.ts.map +1 -0
  67. package/dist/cli/__tests__/mcp-serve.test.js +38 -0
  68. package/dist/cli/__tests__/mcp-serve.test.js.map +1 -0
  69. package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
  70. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  71. package/dist/cli/__tests__/package-bin-contract.test.js +58 -3
  72. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  73. package/dist/cli/__tests__/question.test.js +209 -5
  74. package/dist/cli/__tests__/question.test.js.map +1 -1
  75. package/dist/cli/__tests__/setup-agents-overwrite.test.js +146 -3
  76. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  77. package/dist/cli/__tests__/setup-install-mode.test.d.ts +2 -0
  78. package/dist/cli/__tests__/setup-install-mode.test.d.ts.map +1 -0
  79. package/dist/cli/__tests__/setup-install-mode.test.js +873 -0
  80. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -0
  81. package/dist/cli/__tests__/setup-prompts-overwrite.test.js +83 -22
  82. package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
  83. package/dist/cli/__tests__/setup-refresh.test.js +120 -2
  84. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  85. package/dist/cli/__tests__/setup-skills-overwrite.test.js +32 -13
  86. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  87. package/dist/cli/__tests__/sidecar.test.d.ts +2 -0
  88. package/dist/cli/__tests__/sidecar.test.d.ts.map +1 -0
  89. package/dist/cli/__tests__/sidecar.test.js +24 -0
  90. package/dist/cli/__tests__/sidecar.test.js.map +1 -0
  91. package/dist/cli/__tests__/team.test.js +90 -42
  92. package/dist/cli/__tests__/team.test.js.map +1 -1
  93. package/dist/cli/__tests__/version-sync-contract.test.js +11 -0
  94. package/dist/cli/__tests__/version-sync-contract.test.js.map +1 -1
  95. package/dist/cli/codex-home.d.ts +4 -5
  96. package/dist/cli/codex-home.d.ts.map +1 -1
  97. package/dist/cli/codex-home.js +9 -37
  98. package/dist/cli/codex-home.js.map +1 -1
  99. package/dist/cli/doctor.d.ts +1 -1
  100. package/dist/cli/doctor.d.ts.map +1 -1
  101. package/dist/cli/doctor.js +509 -278
  102. package/dist/cli/doctor.js.map +1 -1
  103. package/dist/cli/index.d.ts +13 -5
  104. package/dist/cli/index.d.ts.map +1 -1
  105. package/dist/cli/index.js +351 -60
  106. package/dist/cli/index.js.map +1 -1
  107. package/dist/cli/list.d.ts +2 -0
  108. package/dist/cli/list.d.ts.map +1 -0
  109. package/dist/cli/list.js +40 -0
  110. package/dist/cli/list.js.map +1 -0
  111. package/dist/cli/mcp-serve.d.ts +12 -0
  112. package/dist/cli/mcp-serve.d.ts.map +1 -0
  113. package/dist/cli/mcp-serve.js +63 -0
  114. package/dist/cli/mcp-serve.js.map +1 -0
  115. package/dist/cli/plugin-marketplace.d.ts +13 -0
  116. package/dist/cli/plugin-marketplace.d.ts.map +1 -0
  117. package/dist/cli/plugin-marketplace.js +77 -0
  118. package/dist/cli/plugin-marketplace.js.map +1 -0
  119. package/dist/cli/question.d.ts.map +1 -1
  120. package/dist/cli/question.js +27 -2
  121. package/dist/cli/question.js.map +1 -1
  122. package/dist/cli/setup-preferences.d.ts +20 -0
  123. package/dist/cli/setup-preferences.d.ts.map +1 -0
  124. package/dist/cli/setup-preferences.js +71 -0
  125. package/dist/cli/setup-preferences.js.map +1 -0
  126. package/dist/cli/setup.d.ts +12 -3
  127. package/dist/cli/setup.d.ts.map +1 -1
  128. package/dist/cli/setup.js +1080 -254
  129. package/dist/cli/setup.js.map +1 -1
  130. package/dist/cli/team.d.ts +1 -0
  131. package/dist/cli/team.d.ts.map +1 -1
  132. package/dist/cli/team.js +22 -3
  133. package/dist/cli/team.js.map +1 -1
  134. package/dist/cli/uninstall.d.ts.map +1 -1
  135. package/dist/cli/uninstall.js +2 -8
  136. package/dist/cli/uninstall.js.map +1 -1
  137. package/dist/config/__tests__/generator-idempotent.test.js +100 -3
  138. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  139. package/dist/config/__tests__/generator-notify.test.js +6 -5
  140. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  141. package/dist/config/__tests__/generator-status-line-presets.test.d.ts +2 -0
  142. package/dist/config/__tests__/generator-status-line-presets.test.d.ts.map +1 -0
  143. package/dist/config/__tests__/generator-status-line-presets.test.js +203 -0
  144. package/dist/config/__tests__/generator-status-line-presets.test.js.map +1 -0
  145. package/dist/config/__tests__/models.test.js +35 -2
  146. package/dist/config/__tests__/models.test.js.map +1 -1
  147. package/dist/config/codex-hooks.d.ts.map +1 -1
  148. package/dist/config/codex-hooks.js +2 -7
  149. package/dist/config/codex-hooks.js.map +1 -1
  150. package/dist/config/generator.d.ts +10 -0
  151. package/dist/config/generator.d.ts.map +1 -1
  152. package/dist/config/generator.js +259 -76
  153. package/dist/config/generator.js.map +1 -1
  154. package/dist/config/models.d.ts +13 -3
  155. package/dist/config/models.d.ts.map +1 -1
  156. package/dist/config/models.js +25 -5
  157. package/dist/config/models.js.map +1 -1
  158. package/dist/config/omx-first-party-mcp.d.ts +18 -0
  159. package/dist/config/omx-first-party-mcp.d.ts.map +1 -0
  160. package/dist/config/omx-first-party-mcp.js +76 -0
  161. package/dist/config/omx-first-party-mcp.js.map +1 -0
  162. package/dist/document-refresh/__tests__/enforcer.test.d.ts +2 -0
  163. package/dist/document-refresh/__tests__/enforcer.test.d.ts.map +1 -0
  164. package/dist/document-refresh/__tests__/enforcer.test.js +128 -0
  165. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -0
  166. package/dist/document-refresh/config.d.ts +9 -0
  167. package/dist/document-refresh/config.d.ts.map +1 -0
  168. package/dist/document-refresh/config.js +70 -0
  169. package/dist/document-refresh/config.js.map +1 -0
  170. package/dist/document-refresh/enforcer.d.ts +43 -0
  171. package/dist/document-refresh/enforcer.d.ts.map +1 -0
  172. package/dist/document-refresh/enforcer.js +329 -0
  173. package/dist/document-refresh/enforcer.js.map +1 -0
  174. package/dist/exec/followup.d.ts +44 -0
  175. package/dist/exec/followup.d.ts.map +1 -0
  176. package/dist/exec/followup.js +349 -0
  177. package/dist/exec/followup.js.map +1 -0
  178. package/dist/hooks/__tests__/codebase-map.test.js +63 -1
  179. package/dist/hooks/__tests__/codebase-map.test.js.map +1 -1
  180. package/dist/hooks/__tests__/deep-interview-contract.test.js +10 -7
  181. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  182. package/dist/hooks/__tests__/keyword-detector.test.js +89 -0
  183. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  184. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +84 -1
  185. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  186. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +23 -2
  187. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  188. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +43 -0
  189. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  190. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +17 -0
  191. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  192. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +53 -0
  193. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  194. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +4 -4
  195. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  196. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +103 -0
  197. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  198. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +27 -13
  199. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  200. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts +2 -0
  201. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts.map +1 -0
  202. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +35 -0
  203. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +1 -0
  204. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +70 -3
  205. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  206. package/dist/hooks/__tests__/pre-context-gate-skills.test.js +5 -0
  207. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  208. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +3 -2
  209. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  210. package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts +2 -0
  211. package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts.map +1 -0
  212. package/dist/hooks/__tests__/prompt-refactor-contract.test.js +22 -0
  213. package/dist/hooks/__tests__/prompt-refactor-contract.test.js.map +1 -0
  214. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.d.ts +2 -0
  215. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.d.ts.map +1 -0
  216. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js +28 -0
  217. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -0
  218. package/dist/hooks/__tests__/triage-heuristic.test.js +59 -4
  219. package/dist/hooks/__tests__/triage-heuristic.test.js.map +1 -1
  220. package/dist/hooks/__tests__/visual-ralph-skill.test.d.ts +2 -0
  221. package/dist/hooks/__tests__/visual-ralph-skill.test.d.ts.map +1 -0
  222. package/dist/hooks/__tests__/visual-ralph-skill.test.js +44 -0
  223. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -0
  224. package/dist/hooks/codebase-map.d.ts.map +1 -1
  225. package/dist/hooks/codebase-map.js +83 -6
  226. package/dist/hooks/codebase-map.js.map +1 -1
  227. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  228. package/dist/hooks/keyword-detector.js +19 -14
  229. package/dist/hooks/keyword-detector.js.map +1 -1
  230. package/dist/hooks/prompt-guidance-contract.d.ts +6 -0
  231. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  232. package/dist/hooks/prompt-guidance-contract.js +89 -0
  233. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  234. package/dist/hooks/session.d.ts +2 -0
  235. package/dist/hooks/session.d.ts.map +1 -1
  236. package/dist/hooks/session.js +6 -0
  237. package/dist/hooks/session.js.map +1 -1
  238. package/dist/hooks/triage-heuristic.d.ts +2 -2
  239. package/dist/hooks/triage-heuristic.d.ts.map +1 -1
  240. package/dist/hooks/triage-heuristic.js +97 -20
  241. package/dist/hooks/triage-heuristic.js.map +1 -1
  242. package/dist/hooks/triage-state.d.ts +1 -1
  243. package/dist/hooks/triage-state.d.ts.map +1 -1
  244. package/dist/hooks/triage-state.js +2 -1
  245. package/dist/hooks/triage-state.js.map +1 -1
  246. package/dist/hud/__tests__/index.test.js +4 -4
  247. package/dist/hud/__tests__/index.test.js.map +1 -1
  248. package/dist/hud/__tests__/state.test.js +4 -0
  249. package/dist/hud/__tests__/state.test.js.map +1 -1
  250. package/dist/hud/__tests__/types.test.js +27 -0
  251. package/dist/hud/__tests__/types.test.js.map +1 -1
  252. package/dist/hud/state.d.ts.map +1 -1
  253. package/dist/hud/state.js +8 -0
  254. package/dist/hud/state.js.map +1 -1
  255. package/dist/hud/types.d.ts +9 -0
  256. package/dist/hud/types.d.ts.map +1 -1
  257. package/dist/hud/types.js +3 -0
  258. package/dist/hud/types.js.map +1 -1
  259. package/dist/mcp/__tests__/bootstrap.test.js +45 -5
  260. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  261. package/dist/mcp/__tests__/server-lifecycle.test.js +50 -7
  262. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  263. package/dist/mcp/__tests__/state-server.test.js +67 -9
  264. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  265. package/dist/mcp/bootstrap.d.ts +12 -1
  266. package/dist/mcp/bootstrap.d.ts.map +1 -1
  267. package/dist/mcp/bootstrap.js +87 -29
  268. package/dist/mcp/bootstrap.js.map +1 -1
  269. package/dist/mcp/state-server.d.ts +5 -11
  270. package/dist/mcp/state-server.d.ts.map +1 -1
  271. package/dist/mcp/state-server.js +16 -432
  272. package/dist/mcp/state-server.js.map +1 -1
  273. package/dist/planning/__tests__/artifacts.test.js +152 -1
  274. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  275. package/dist/planning/artifacts.d.ts +9 -0
  276. package/dist/planning/artifacts.d.ts.map +1 -1
  277. package/dist/planning/artifacts.js +60 -1
  278. package/dist/planning/artifacts.js.map +1 -1
  279. package/dist/question/__tests__/deep-interview.test.js +1 -1
  280. package/dist/question/__tests__/deep-interview.test.js.map +1 -1
  281. package/dist/question/__tests__/renderer.test.js +70 -0
  282. package/dist/question/__tests__/renderer.test.js.map +1 -1
  283. package/dist/question/__tests__/ui.test.js +49 -0
  284. package/dist/question/__tests__/ui.test.js.map +1 -1
  285. package/dist/question/renderer.d.ts +14 -1
  286. package/dist/question/renderer.d.ts.map +1 -1
  287. package/dist/question/renderer.js +109 -10
  288. package/dist/question/renderer.js.map +1 -1
  289. package/dist/question/state.d.ts +2 -0
  290. package/dist/question/state.d.ts.map +1 -1
  291. package/dist/question/state.js +4 -0
  292. package/dist/question/state.js.map +1 -1
  293. package/dist/question/types.d.ts +2 -1
  294. package/dist/question/types.d.ts.map +1 -1
  295. package/dist/question/types.js.map +1 -1
  296. package/dist/question/ui.d.ts.map +1 -1
  297. package/dist/question/ui.js +28 -8
  298. package/dist/question/ui.js.map +1 -1
  299. package/dist/runtime/__tests__/bridge.test.js +143 -1
  300. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  301. package/dist/runtime/bridge.d.ts +21 -0
  302. package/dist/runtime/bridge.d.ts.map +1 -1
  303. package/dist/runtime/bridge.js +54 -4
  304. package/dist/runtime/bridge.js.map +1 -1
  305. package/dist/scripts/__tests__/codex-native-hook.test.js +1904 -230
  306. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  307. package/dist/scripts/__tests__/generate-release-body.test.js +90 -1
  308. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  309. package/dist/scripts/__tests__/hook-derived-watcher.test.js +110 -2
  310. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  311. package/dist/scripts/__tests__/postinstall.test.js +7 -93
  312. package/dist/scripts/__tests__/postinstall.test.js.map +1 -1
  313. package/dist/scripts/__tests__/prompt-inventory.test.d.ts +2 -0
  314. package/dist/scripts/__tests__/prompt-inventory.test.d.ts.map +1 -0
  315. package/dist/scripts/__tests__/prompt-inventory.test.js +56 -0
  316. package/dist/scripts/__tests__/prompt-inventory.test.js.map +1 -0
  317. package/dist/scripts/__tests__/run-test-files.test.d.ts +2 -0
  318. package/dist/scripts/__tests__/run-test-files.test.d.ts.map +1 -0
  319. package/dist/scripts/__tests__/run-test-files.test.js +62 -0
  320. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -0
  321. package/dist/scripts/__tests__/smoke-packed-install.test.js +13 -1
  322. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  323. package/dist/scripts/__tests__/verify-native-agents.test.d.ts +2 -0
  324. package/dist/scripts/__tests__/verify-native-agents.test.d.ts.map +1 -0
  325. package/dist/scripts/__tests__/verify-native-agents.test.js +166 -0
  326. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -0
  327. package/dist/scripts/codex-execution-surface.d.ts +16 -0
  328. package/dist/scripts/codex-execution-surface.d.ts.map +1 -0
  329. package/dist/scripts/codex-execution-surface.js +42 -0
  330. package/dist/scripts/codex-execution-surface.js.map +1 -0
  331. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  332. package/dist/scripts/codex-native-hook.js +418 -76
  333. package/dist/scripts/codex-native-hook.js.map +1 -1
  334. package/dist/scripts/codex-native-pre-post.d.ts +8 -0
  335. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  336. package/dist/scripts/codex-native-pre-post.js +133 -19
  337. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  338. package/dist/scripts/generate-release-body.d.ts +1 -0
  339. package/dist/scripts/generate-release-body.d.ts.map +1 -1
  340. package/dist/scripts/generate-release-body.js +32 -3
  341. package/dist/scripts/generate-release-body.js.map +1 -1
  342. package/dist/scripts/hook-derived-watcher.js +46 -7
  343. package/dist/scripts/hook-derived-watcher.js.map +1 -1
  344. package/dist/scripts/notify-fallback-watcher.js +59 -9
  345. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  346. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts +2 -0
  347. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts.map +1 -0
  348. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js +153 -0
  349. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js.map +1 -0
  350. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  351. package/dist/scripts/notify-hook/managed-tmux.js +18 -1
  352. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  353. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  354. package/dist/scripts/notify-hook/process-runner.js +7 -3
  355. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  356. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  357. package/dist/scripts/notify-hook/team-dispatch.js +96 -11
  358. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  359. package/dist/scripts/notify-hook/team-tmux-guard.js +3 -3
  360. package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts +34 -0
  361. package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts.map +1 -0
  362. package/dist/scripts/notify-hook/team-worker-posttooluse.js +434 -0
  363. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -0
  364. package/dist/scripts/notify-hook/team-worker.d.ts +1 -1
  365. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  366. package/dist/scripts/notify-hook/team-worker.js +3 -43
  367. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  368. package/dist/scripts/notify-hook.js +36 -5
  369. package/dist/scripts/notify-hook.js.map +1 -1
  370. package/dist/scripts/postinstall.d.ts +1 -5
  371. package/dist/scripts/postinstall.d.ts.map +1 -1
  372. package/dist/scripts/postinstall.js +3 -42
  373. package/dist/scripts/postinstall.js.map +1 -1
  374. package/dist/scripts/prompt-inventory.d.ts +29 -0
  375. package/dist/scripts/prompt-inventory.d.ts.map +1 -0
  376. package/dist/scripts/prompt-inventory.js +178 -0
  377. package/dist/scripts/prompt-inventory.js.map +1 -0
  378. package/dist/scripts/run-test-files.js +32 -2
  379. package/dist/scripts/run-test-files.js.map +1 -1
  380. package/dist/scripts/smoke-packed-install.d.ts +3 -0
  381. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  382. package/dist/scripts/smoke-packed-install.js +9 -1
  383. package/dist/scripts/smoke-packed-install.js.map +1 -1
  384. package/dist/scripts/sync-plugin-mirror.d.ts +13 -0
  385. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -0
  386. package/dist/scripts/sync-plugin-mirror.js +242 -0
  387. package/dist/scripts/sync-plugin-mirror.js.map +1 -0
  388. package/dist/scripts/verify-native-agents.d.ts +16 -0
  389. package/dist/scripts/verify-native-agents.d.ts.map +1 -0
  390. package/dist/scripts/verify-native-agents.js +188 -0
  391. package/dist/scripts/verify-native-agents.js.map +1 -0
  392. package/dist/session-history/search.d.ts.map +1 -1
  393. package/dist/session-history/search.js +7 -2
  394. package/dist/session-history/search.js.map +1 -1
  395. package/dist/sidecar/__tests__/boundary.test.d.ts +2 -0
  396. package/dist/sidecar/__tests__/boundary.test.d.ts.map +1 -0
  397. package/dist/sidecar/__tests__/boundary.test.js +48 -0
  398. package/dist/sidecar/__tests__/boundary.test.js.map +1 -0
  399. package/dist/sidecar/__tests__/collector.test.d.ts +2 -0
  400. package/dist/sidecar/__tests__/collector.test.d.ts.map +1 -0
  401. package/dist/sidecar/__tests__/collector.test.js +162 -0
  402. package/dist/sidecar/__tests__/collector.test.js.map +1 -0
  403. package/dist/sidecar/__tests__/render.test.d.ts +2 -0
  404. package/dist/sidecar/__tests__/render.test.d.ts.map +1 -0
  405. package/dist/sidecar/__tests__/render.test.js +67 -0
  406. package/dist/sidecar/__tests__/render.test.js.map +1 -0
  407. package/dist/sidecar/__tests__/tmux.test.d.ts +2 -0
  408. package/dist/sidecar/__tests__/tmux.test.d.ts.map +1 -0
  409. package/dist/sidecar/__tests__/tmux.test.js +30 -0
  410. package/dist/sidecar/__tests__/tmux.test.js.map +1 -0
  411. package/dist/sidecar/__tests__/watch.test.d.ts +2 -0
  412. package/dist/sidecar/__tests__/watch.test.d.ts.map +1 -0
  413. package/dist/sidecar/__tests__/watch.test.js +42 -0
  414. package/dist/sidecar/__tests__/watch.test.js.map +1 -0
  415. package/dist/sidecar/collector.d.ts +4 -0
  416. package/dist/sidecar/collector.d.ts.map +1 -0
  417. package/dist/sidecar/collector.js +377 -0
  418. package/dist/sidecar/collector.js.map +1 -0
  419. package/dist/sidecar/index.d.ts +25 -0
  420. package/dist/sidecar/index.d.ts.map +1 -0
  421. package/dist/sidecar/index.js +165 -0
  422. package/dist/sidecar/index.js.map +1 -0
  423. package/dist/sidecar/render.d.ts +3 -0
  424. package/dist/sidecar/render.d.ts.map +1 -0
  425. package/dist/sidecar/render.js +72 -0
  426. package/dist/sidecar/render.js.map +1 -0
  427. package/dist/sidecar/tmux.d.ts +13 -0
  428. package/dist/sidecar/tmux.d.ts.map +1 -0
  429. package/dist/sidecar/tmux.js +44 -0
  430. package/dist/sidecar/tmux.js.map +1 -0
  431. package/dist/sidecar/types.d.ts +125 -0
  432. package/dist/sidecar/types.d.ts.map +1 -0
  433. package/dist/sidecar/types.js +2 -0
  434. package/dist/sidecar/types.js.map +1 -0
  435. package/dist/state/__tests__/operations.test.js +50 -22
  436. package/dist/state/__tests__/operations.test.js.map +1 -1
  437. package/dist/state/operations.d.ts +1 -1
  438. package/dist/state/operations.d.ts.map +1 -1
  439. package/dist/state/operations.js +19 -7
  440. package/dist/state/operations.js.map +1 -1
  441. package/dist/team/__tests__/commit-hygiene.test.d.ts +2 -0
  442. package/dist/team/__tests__/commit-hygiene.test.d.ts.map +1 -0
  443. package/dist/team/__tests__/commit-hygiene.test.js +93 -0
  444. package/dist/team/__tests__/commit-hygiene.test.js.map +1 -0
  445. package/dist/team/__tests__/delegation-policy.test.d.ts +2 -0
  446. package/dist/team/__tests__/delegation-policy.test.d.ts.map +1 -0
  447. package/dist/team/__tests__/delegation-policy.test.js +69 -0
  448. package/dist/team/__tests__/delegation-policy.test.js.map +1 -0
  449. package/dist/team/__tests__/events.test.js +54 -4
  450. package/dist/team/__tests__/events.test.js.map +1 -1
  451. package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts +2 -0
  452. package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts.map +1 -0
  453. package/dist/team/__tests__/hook-primary-e2e-contract.test.js +78 -0
  454. package/dist/team/__tests__/hook-primary-e2e-contract.test.js.map +1 -0
  455. package/dist/team/__tests__/model-contract.test.js +18 -2
  456. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  457. package/dist/team/__tests__/repo-aware-decomposition.test.d.ts +2 -0
  458. package/dist/team/__tests__/repo-aware-decomposition.test.d.ts.map +1 -0
  459. package/dist/team/__tests__/repo-aware-decomposition.test.js +95 -0
  460. package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -0
  461. package/dist/team/__tests__/runtime.test.js +677 -17
  462. package/dist/team/__tests__/runtime.test.js.map +1 -1
  463. package/dist/team/__tests__/state-root.test.js +177 -1
  464. package/dist/team/__tests__/state-root.test.js.map +1 -1
  465. package/dist/team/__tests__/state.test.js +110 -0
  466. package/dist/team/__tests__/state.test.js.map +1 -1
  467. package/dist/team/__tests__/tmux-session.test.js +301 -2
  468. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  469. package/dist/team/__tests__/worker-bootstrap.test.js +94 -0
  470. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  471. package/dist/team/commit-hygiene.d.ts +22 -3
  472. package/dist/team/commit-hygiene.d.ts.map +1 -1
  473. package/dist/team/commit-hygiene.js +134 -2
  474. package/dist/team/commit-hygiene.js.map +1 -1
  475. package/dist/team/contracts.d.ts +1 -1
  476. package/dist/team/contracts.d.ts.map +1 -1
  477. package/dist/team/contracts.js +2 -0
  478. package/dist/team/contracts.js.map +1 -1
  479. package/dist/team/dag-schema.d.ts +38 -0
  480. package/dist/team/dag-schema.d.ts.map +1 -0
  481. package/dist/team/dag-schema.js +221 -0
  482. package/dist/team/dag-schema.js.map +1 -0
  483. package/dist/team/delegation-policy.d.ts +3 -0
  484. package/dist/team/delegation-policy.d.ts.map +1 -0
  485. package/dist/team/delegation-policy.js +82 -0
  486. package/dist/team/delegation-policy.js.map +1 -0
  487. package/dist/team/model-contract.d.ts +3 -1
  488. package/dist/team/model-contract.d.ts.map +1 -1
  489. package/dist/team/model-contract.js +44 -5
  490. package/dist/team/model-contract.js.map +1 -1
  491. package/dist/team/repo-aware-decomposition.d.ts +60 -0
  492. package/dist/team/repo-aware-decomposition.d.ts.map +1 -0
  493. package/dist/team/repo-aware-decomposition.js +229 -0
  494. package/dist/team/repo-aware-decomposition.js.map +1 -0
  495. package/dist/team/runtime.d.ts +34 -0
  496. package/dist/team/runtime.d.ts.map +1 -1
  497. package/dist/team/runtime.js +191 -50
  498. package/dist/team/runtime.js.map +1 -1
  499. package/dist/team/state/tasks.d.ts.map +1 -1
  500. package/dist/team/state/tasks.js +33 -0
  501. package/dist/team/state/tasks.js.map +1 -1
  502. package/dist/team/state/types.d.ts +23 -1
  503. package/dist/team/state/types.d.ts.map +1 -1
  504. package/dist/team/state/types.js.map +1 -1
  505. package/dist/team/state-root.d.ts +35 -0
  506. package/dist/team/state-root.d.ts.map +1 -1
  507. package/dist/team/state-root.js +281 -1
  508. package/dist/team/state-root.js.map +1 -1
  509. package/dist/team/state.d.ts +27 -1
  510. package/dist/team/state.d.ts.map +1 -1
  511. package/dist/team/state.js +6 -0
  512. package/dist/team/state.js.map +1 -1
  513. package/dist/team/tmux-session.d.ts +1 -0
  514. package/dist/team/tmux-session.d.ts.map +1 -1
  515. package/dist/team/tmux-session.js +83 -6
  516. package/dist/team/tmux-session.js.map +1 -1
  517. package/dist/team/worker-bootstrap.d.ts +3 -0
  518. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  519. package/dist/team/worker-bootstrap.js +77 -4
  520. package/dist/team/worker-bootstrap.js.map +1 -1
  521. package/dist/utils/__tests__/agents-model-table.test.js +8 -0
  522. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  523. package/dist/utils/__tests__/paths.test.js +42 -9
  524. package/dist/utils/__tests__/paths.test.js.map +1 -1
  525. package/dist/utils/agents-md.d.ts +3 -0
  526. package/dist/utils/agents-md.d.ts.map +1 -1
  527. package/dist/utils/agents-md.js +25 -0
  528. package/dist/utils/agents-md.js.map +1 -1
  529. package/dist/utils/agents-model-table.d.ts.map +1 -1
  530. package/dist/utils/agents-model-table.js +2 -3
  531. package/dist/utils/agents-model-table.js.map +1 -1
  532. package/package.json +14 -7
  533. package/plugins/oh-my-codex/.app.json +3 -0
  534. package/plugins/oh-my-codex/.codex-plugin/plugin.json +30 -0
  535. package/plugins/oh-my-codex/.mcp.json +44 -0
  536. package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +114 -0
  537. package/plugins/oh-my-codex/skills/analyze/SKILL.md +148 -0
  538. package/plugins/oh-my-codex/skills/ask-claude/SKILL.md +61 -0
  539. package/plugins/oh-my-codex/skills/ask-gemini/SKILL.md +61 -0
  540. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +231 -0
  541. package/plugins/oh-my-codex/skills/autoresearch/SKILL.md +68 -0
  542. package/plugins/oh-my-codex/skills/cancel/SKILL.md +399 -0
  543. package/plugins/oh-my-codex/skills/code-review/SKILL.md +290 -0
  544. package/plugins/oh-my-codex/skills/configure-notifications/SKILL.md +287 -0
  545. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +464 -0
  546. package/plugins/oh-my-codex/skills/doctor/SKILL.md +239 -0
  547. package/plugins/oh-my-codex/skills/help/SKILL.md +202 -0
  548. package/plugins/oh-my-codex/skills/hud/SKILL.md +98 -0
  549. package/plugins/oh-my-codex/skills/note/SKILL.md +62 -0
  550. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +135 -0
  551. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +86 -0
  552. package/plugins/oh-my-codex/skills/plan/SKILL.md +276 -0
  553. package/plugins/oh-my-codex/skills/ralph/SKILL.md +269 -0
  554. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +162 -0
  555. package/plugins/oh-my-codex/skills/security-review/SKILL.md +300 -0
  556. package/plugins/oh-my-codex/skills/skill/SKILL.md +835 -0
  557. package/plugins/oh-my-codex/skills/team/SKILL.md +510 -0
  558. package/plugins/oh-my-codex/skills/trace/SKILL.md +33 -0
  559. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +143 -0
  560. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +175 -0
  561. package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +153 -0
  562. package/plugins/oh-my-codex/skills/visual-verdict/SKILL.md +76 -0
  563. package/plugins/oh-my-codex/skills/wiki/SKILL.md +57 -0
  564. package/plugins/oh-my-codex/skills/worker/SKILL.md +106 -0
  565. package/prompts/critic.md +35 -83
  566. package/prompts/executor.md +32 -109
  567. package/prompts/explore-harness.md +1 -1
  568. package/prompts/explore.md +37 -90
  569. package/prompts/planner.md +28 -58
  570. package/prompts/researcher.md +46 -78
  571. package/prompts/verifier.md +23 -31
  572. package/skills/autopilot/SKILL.md +1 -4
  573. package/skills/deep-interview/SKILL.md +8 -5
  574. package/skills/doctor/SKILL.md +45 -17
  575. package/skills/help/SKILL.md +2 -2
  576. package/skills/omx-setup/SKILL.md +52 -9
  577. package/skills/plan/SKILL.md +1 -4
  578. package/skills/ralph/SKILL.md +3 -5
  579. package/skills/ralplan/SKILL.md +2 -6
  580. package/skills/team/SKILL.md +2 -5
  581. package/skills/ultraqa/SKILL.md +1 -4
  582. package/skills/ultrawork/SKILL.md +2 -3
  583. package/skills/visual-ralph/SKILL.md +153 -0
  584. package/skills/web-clone/SKILL.md +12 -354
  585. package/src/scripts/__tests__/codex-native-hook.test.ts +2306 -224
  586. package/src/scripts/__tests__/generate-release-body.test.ts +109 -0
  587. package/src/scripts/__tests__/hook-derived-watcher.test.ts +132 -2
  588. package/src/scripts/__tests__/postinstall.test.ts +7 -104
  589. package/src/scripts/__tests__/prompt-inventory.test.ts +64 -0
  590. package/src/scripts/__tests__/run-test-files.test.ts +72 -0
  591. package/src/scripts/__tests__/smoke-packed-install.test.ts +15 -0
  592. package/src/scripts/__tests__/verify-native-agents.test.ts +204 -0
  593. package/src/scripts/codex-execution-surface.ts +73 -0
  594. package/src/scripts/codex-native-hook.ts +579 -82
  595. package/src/scripts/codex-native-pre-post.ts +157 -19
  596. package/src/scripts/generate-release-body.ts +34 -2
  597. package/src/scripts/hook-derived-watcher.ts +51 -8
  598. package/src/scripts/notify-fallback-watcher.ts +65 -9
  599. package/src/scripts/notify-hook/__tests__/team-worker-posttooluse.test.ts +180 -0
  600. package/src/scripts/notify-hook/managed-tmux.ts +22 -4
  601. package/src/scripts/notify-hook/process-runner.ts +7 -3
  602. package/src/scripts/notify-hook/team-dispatch.ts +103 -11
  603. package/src/scripts/notify-hook/team-tmux-guard.ts +3 -3
  604. package/src/scripts/notify-hook/team-worker-posttooluse.ts +536 -0
  605. package/src/scripts/notify-hook/team-worker.ts +4 -48
  606. package/src/scripts/notify-hook.ts +36 -5
  607. package/src/scripts/postinstall.ts +4 -58
  608. package/src/scripts/prompt-inventory.ts +218 -0
  609. package/src/scripts/run-test-files.ts +41 -2
  610. package/src/scripts/smoke-packed-install.ts +10 -1
  611. package/src/scripts/sync-plugin-mirror.ts +360 -0
  612. package/src/scripts/verify-native-agents.ts +245 -0
  613. package/templates/AGENTS.md +26 -91
  614. package/templates/catalog-manifest.json +16 -1
package/dist/cli/setup.js CHANGED
@@ -2,35 +2,49 @@
2
2
  * omx setup - Automated installation of oh-my-codex
3
3
  * Installs skills, prompts, MCP servers config, and AGENTS.md
4
4
  */
5
- import { mkdir, copyFile, readdir, readFile, rename, writeFile, stat, rm, } from "fs/promises";
5
+ import { mkdir, cp, copyFile, readdir, readFile, rename, writeFile, stat, rm, } from "fs/promises";
6
6
  import { join, dirname, relative, basename } from "path";
7
7
  import { existsSync } from "fs";
8
8
  import { spawnSync } from "child_process";
9
9
  import { createInterface } from "readline/promises";
10
10
  import { homedir } from "os";
11
11
  import { codexHome, codexConfigPath, codexPromptsDir, codexAgentsDir, userSkillsDir, omxStateDir, detectLegacySkillRootOverlap, omxPlansDir, omxLogsDir, } from "../utils/paths.js";
12
- import { buildMergedConfig, getRootModelName, hasLegacyOmxTeamRunTable, } from "../config/generator.js";
12
+ import { buildMergedConfig, getRootModelName, hasLegacyOmxTeamRunTable, stripExistingOmxBlocks, stripExistingSharedMcpRegistryBlock, stripOmxEnvSettings, stripOmxFeatureFlags, stripOmxSeededBehavioralDefaults, upsertCodexHooksFeatureFlag, OMX_DEVELOPER_INSTRUCTIONS, } from "../config/generator.js";
13
13
  import { mergeManagedCodexHooksConfig } from "../config/codex-hooks.js";
14
14
  import { getLegacyUnifiedMcpRegistryCandidate, getUnifiedMcpRegistryCandidates, loadUnifiedMcpRegistry, planClaudeCodeMcpSettingsSync, } from "../config/mcp-registry.js";
15
15
  import { generateAgentToml } from "../agents/native-config.js";
16
16
  import { AGENT_DEFINITIONS } from "../agents/definitions.js";
17
+ import { getCatalogAgentStatusByName, getInstallableNativeAgentNames, isNativeAgentInstallableStatus, isSetupPromptAssetName, } from "../agents/policy.js";
17
18
  import { getPackageRoot } from "../utils/package.js";
18
19
  import { readSessionState, isSessionStale } from "../hooks/session.js";
19
20
  import { getCatalogHeadlineCounts } from "./catalog-contract.js";
20
21
  import { tryReadCatalogManifest } from "../catalog/reader.js";
21
22
  import { DEFAULT_FRONTIER_MODEL } from "../config/models.js";
22
- import { addGeneratedAgentsMarker, hasOmxManagedAgentsSections, isOmxGeneratedAgentsMd, } from "../utils/agents-md.js";
23
+ import { addGeneratedAgentsMarker, hasOmxManagedAgentsSections, isOmxGeneratedAgentsMd, upsertManagedAgentsBlock, } from "../utils/agents-md.js";
24
+ import { DEFAULT_HUD_CONFIG } from "../hud/types.js";
25
+ import { SETUP_INSTALL_MODES, SETUP_SCOPES, getSetupScopeFilePath, readPersistedSetupPreferences, } from "./setup-preferences.js";
26
+ import { OMX_LOCAL_MARKETPLACE_NAME, resolvePackagedOmxMarketplace, upsertLocalOmxMarketplaceRegistration, } from "./plugin-marketplace.js";
27
+ async function resolveStatusLinePresetForSetup(projectRoot, options) {
28
+ if (options.force) {
29
+ return DEFAULT_HUD_CONFIG.statusLine.preset;
30
+ }
31
+ const path = join(projectRoot, ".omx", "hud-config.json");
32
+ if (!existsSync(path))
33
+ return undefined;
34
+ try {
35
+ const raw = JSON.parse(await readFile(path, "utf-8"));
36
+ const preset = raw?.statusLine?.preset;
37
+ if (preset === "minimal" || preset === "focused" || preset === "full") {
38
+ return preset;
39
+ }
40
+ }
41
+ catch {
42
+ // Malformed hud-config.json — fall through to default.
43
+ }
44
+ return undefined;
45
+ }
23
46
  import { resolveAgentsModelTableContext, upsertAgentsModelTable, } from "../utils/agents-model-table.js";
24
- import { spawnPlatformCommandSync } from "../utils/platform-command.js";
25
- /**
26
- * Legacy scope values that may appear in persisted setup-scope.json files.
27
- * Both 'project-local' (renamed) and old 'project' (minimal, removed) are
28
- * migrated to the current 'project' scope on read.
29
- */
30
- const LEGACY_SCOPE_MIGRATION = {
31
- "project-local": "project",
32
- };
33
- export const SETUP_SCOPES = ["user", "project"];
47
+ export { SETUP_INSTALL_MODES, SETUP_SCOPES };
34
48
  const PROJECT_GITIGNORE_ENTRIES = [
35
49
  ".omx/",
36
50
  ".codex/*",
@@ -44,6 +58,19 @@ const PROJECT_GITIGNORE_ENTRIES = [
44
58
  ];
45
59
  const LEGACY_PROJECT_GITIGNORE_ENTRIES = [".codex/"];
46
60
  const SETUP_ONLY_INSTALLABLE_SKILLS = new Set(["wiki"]);
61
+ const HARD_DEPRECATED_SKILL_NAMES = new Set(["web-clone"]);
62
+ function isCatalogInstallableStatus(status) {
63
+ return status === "active" || status === "internal";
64
+ }
65
+ function getSetupInstallableSkillNames(manifest = tryReadCatalogManifest()) {
66
+ return new Set([
67
+ ...(manifest?.skills ?? [])
68
+ .filter((skill) => typeof skill.name === "string" &&
69
+ isCatalogInstallableStatus(skill.status))
70
+ .map((skill) => skill.name),
71
+ ...SETUP_ONLY_INSTALLABLE_SKILLS,
72
+ ]);
73
+ }
47
74
  function applyScopePathRewritesToAgentsTemplate(content, scope) {
48
75
  if (scope !== "project")
49
76
  return content;
@@ -55,10 +82,10 @@ const REQUIRED_TEAM_CLI_API_MARKERS = [
55
82
  "TEAM_API_OPERATIONS",
56
83
  ];
57
84
  const DEFAULT_SETUP_SCOPE = "user";
85
+ const DEFAULT_SETUP_INSTALL_MODE = "legacy";
58
86
  const LEGACY_SETUP_MODEL = "gpt-5.3-codex";
59
87
  const DEFAULT_SETUP_MODEL = DEFAULT_FRONTIER_MODEL;
60
88
  const OBSOLETE_NATIVE_AGENT_FIELD = ["skill", "ref"].join("_");
61
- const TUI_OWNED_BY_CODEX_VERSION = [0, 107, 0];
62
89
  function createEmptyCategorySummary() {
63
90
  return {
64
91
  updated: 0,
@@ -234,16 +261,6 @@ async function buildLegacySkillOverlapNotice(scope) {
234
261
  message: `Detected ${overlap.overlappingSkillNames.length} overlapping skill names between canonical ${overlap.canonicalDir} and legacy ${overlap.legacyDir}.${mismatchSuffix} Remove or archive ~/.agents/skills after confirming ${overlap.canonicalDir} is the version you want Codex to load.`,
235
262
  };
236
263
  }
237
- function logCategorySummary(name, summary) {
238
- console.log(` ${name}: updated=${summary.updated}, unchanged=${summary.unchanged}, ` +
239
- `backed_up=${summary.backedUp}, skipped=${summary.skipped}, removed=${summary.removed}`);
240
- }
241
- function isSetupScope(value) {
242
- return SETUP_SCOPES.includes(value);
243
- }
244
- function getScopeFilePath(projectRoot) {
245
- return join(projectRoot, ".omx", "setup-scope.json");
246
- }
247
264
  export function resolveScopeDirectories(scope, projectRoot) {
248
265
  if (scope === "project") {
249
266
  const codexHomeDir = join(projectRoot, ".codex");
@@ -265,33 +282,9 @@ export function resolveScopeDirectories(scope, projectRoot) {
265
282
  skillsDir: userSkillsDir(),
266
283
  };
267
284
  }
268
- async function readPersistedSetupPreferences(projectRoot) {
269
- const scopePath = getScopeFilePath(projectRoot);
270
- if (!existsSync(scopePath))
271
- return undefined;
272
- try {
273
- const raw = await readFile(scopePath, "utf-8");
274
- const parsed = JSON.parse(raw);
275
- const persisted = {};
276
- if (parsed && typeof parsed.scope === "string") {
277
- // Direct match to current scopes
278
- if (isSetupScope(parsed.scope)) {
279
- persisted.scope = parsed.scope;
280
- }
281
- // Migrate legacy scope values (project-local → project)
282
- const migrated = LEGACY_SCOPE_MIGRATION[parsed.scope];
283
- if (migrated) {
284
- console.warn(`[omx] Migrating persisted setup scope "${parsed.scope}" → "${migrated}" ` +
285
- `(see issue #243: simplified to user/project).`);
286
- persisted.scope = migrated;
287
- }
288
- }
289
- return Object.keys(persisted).length > 0 ? persisted : undefined;
290
- }
291
- catch {
292
- // ignore invalid persisted scope and fall back to prompt/default
293
- }
294
- return undefined;
285
+ function logCategorySummary(name, summary) {
286
+ console.log(` ${name}: updated=${summary.updated}, unchanged=${summary.unchanged}, ` +
287
+ `backed_up=${summary.backedUp}, skipped=${summary.skipped}, removed=${summary.removed}`);
295
288
  }
296
289
  async function promptForSetupScope(defaultScope) {
297
290
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
@@ -302,20 +295,89 @@ async function promptForSetupScope(defaultScope) {
302
295
  output: process.stdout,
303
296
  });
304
297
  try {
298
+ const userDefaultMarker = defaultScope === "user" ? " (default)" : "";
299
+ const projectDefaultMarker = defaultScope === "project" ? " (default)" : "";
300
+ const defaultChoice = defaultScope === "project" ? "2" : "1";
305
301
  console.log("Select setup scope:");
306
- console.log(` 1) user (default) — installs to ~/.codex (skills default to ~/.codex/skills)`);
307
- console.log(" 2) project — installs to ./.codex (local to project)");
308
- const answer = (await rl.question("Scope [1-2] (default: 1): "))
302
+ console.log(` 1) user${userDefaultMarker} — installs to ${codexHome()} (skills default to ${userSkillsDir()})`);
303
+ console.log(` 2) project${projectDefaultMarker} — installs to ./.codex (local to project)`);
304
+ const answer = (await rl.question(`Scope [1-2] (default: ${defaultChoice}): `))
309
305
  .trim()
310
306
  .toLowerCase();
311
307
  if (answer === "2" || answer === "project")
312
308
  return "project";
309
+ if (answer === "1" || answer === "user")
310
+ return "user";
313
311
  return defaultScope;
314
312
  }
315
313
  finally {
316
314
  rl.close();
317
315
  }
318
316
  }
317
+ async function promptForSetupInstallMode(defaultMode) {
318
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
319
+ return defaultMode;
320
+ }
321
+ const rl = createInterface({
322
+ input: process.stdin,
323
+ output: process.stdout,
324
+ });
325
+ try {
326
+ console.log("Select user-scope skill delivery mode:");
327
+ console.log(` 1) legacy${defaultMode === "legacy" ? " (default)" : ""} — install/update OMX skills in the resolved user skill root`);
328
+ console.log(` 2) plugin${defaultMode === "plugin" ? " (default)" : ""} — rely on Codex plugin discovery and clean up matching legacy OMX-managed setup artifacts`);
329
+ const defaultChoice = defaultMode === "plugin" ? "2" : "1";
330
+ const answer = (await rl.question(`Install mode [1-2] (default: ${defaultChoice}): `))
331
+ .trim()
332
+ .toLowerCase();
333
+ if (answer === "2" || answer === "plugin")
334
+ return "plugin";
335
+ if (answer === "1" || answer === "legacy")
336
+ return "legacy";
337
+ return defaultMode;
338
+ }
339
+ finally {
340
+ rl.close();
341
+ }
342
+ }
343
+ function hasPersistedSetupPreferences(preferences) {
344
+ return Boolean(preferences?.scope || preferences?.installMode);
345
+ }
346
+ function formatPersistedSetupPreferenceSummary(preferences) {
347
+ return [
348
+ `scope=${preferences.scope ?? "not recorded"}`,
349
+ `installMode=${preferences.installMode ?? "not recorded"}`,
350
+ ].join(", ");
351
+ }
352
+ async function promptForPersistedSetupReview(preferences) {
353
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
354
+ return "keep";
355
+ }
356
+ const rl = createInterface({
357
+ input: process.stdin,
358
+ output: process.stdout,
359
+ });
360
+ try {
361
+ console.log("Existing OMX setup preferences detected:");
362
+ console.log(` ${formatPersistedSetupPreferenceSummary(preferences)}`);
363
+ console.log(" 1) keep — reuse these choices for this setup run");
364
+ console.log(" 2) review — review/change choices, using these values as defaults");
365
+ console.log(" 3) reset — ignore saved choices and run setup as if fresh");
366
+ const answer = (await rl.question("Setup preferences [1-3] (default: 1 keep): "))
367
+ .trim()
368
+ .toLowerCase();
369
+ if (answer === "2" || answer === "review" || answer === "change") {
370
+ return "review";
371
+ }
372
+ if (answer === "3" || answer === "reset" || answer === "fresh") {
373
+ return "reset";
374
+ }
375
+ return "keep";
376
+ }
377
+ finally {
378
+ rl.close();
379
+ }
380
+ }
319
381
  async function promptForModelUpgrade(currentModel, targetModel) {
320
382
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
321
383
  return false;
@@ -334,38 +396,43 @@ async function promptForModelUpgrade(currentModel, targetModel) {
334
396
  rl.close();
335
397
  }
336
398
  }
337
- function parseSemverTriplet(version) {
338
- const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
339
- if (!match)
340
- return null;
341
- return [Number(match[1]), Number(match[2]), Number(match[3])];
342
- }
343
- function semverGte(version, minimum) {
344
- if (version[0] !== minimum[0])
345
- return version[0] > minimum[0];
346
- if (version[1] !== minimum[1])
347
- return version[1] > minimum[1];
348
- return version[2] >= minimum[2];
349
- }
350
- function probeInstalledCodexVersion() {
351
- const { result } = spawnPlatformCommandSync("codex", ["--version"], {
352
- encoding: "utf-8",
353
- stdio: ["pipe", "pipe", "pipe"],
399
+ async function promptForAgentsOverwrite(destinationPath) {
400
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
401
+ return false;
402
+ }
403
+ const rl = createInterface({
404
+ input: process.stdin,
405
+ output: process.stdout,
354
406
  });
355
- if (result.error || result.status !== 0)
356
- return null;
357
- const stdout = (result.stdout || "").trim();
358
- return stdout === "" ? null : stdout;
407
+ try {
408
+ const answer = (await rl.question(`Overwrite existing AGENTS.md at "${destinationPath}"? [y/N]: `))
409
+ .trim()
410
+ .toLowerCase();
411
+ return answer === "y" || answer === "yes";
412
+ }
413
+ finally {
414
+ rl.close();
415
+ }
359
416
  }
360
- function shouldOmxManageTuiFromCodexVersion(versionOutput) {
361
- if (!versionOutput)
362
- return true;
363
- const parsed = parseSemverTriplet(versionOutput);
364
- if (!parsed)
365
- return true;
366
- return !semverGte(parsed, TUI_OWNED_BY_CODEX_VERSION);
417
+ async function promptForPluginAgentsMdDefault(destinationPath) {
418
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
419
+ return false;
420
+ }
421
+ const rl = createInterface({
422
+ input: process.stdin,
423
+ output: process.stdout,
424
+ });
425
+ try {
426
+ const answer = (await rl.question(`Plugin mode: install OMX AGENTS.md defaults at "${destinationPath}"? [y/N]: `))
427
+ .trim()
428
+ .toLowerCase();
429
+ return answer === "y" || answer === "yes";
430
+ }
431
+ finally {
432
+ rl.close();
433
+ }
367
434
  }
368
- async function promptForAgentsOverwrite(destinationPath) {
435
+ async function promptForPluginDeveloperInstructionsDefault(configPath) {
369
436
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
370
437
  return false;
371
438
  }
@@ -374,7 +441,25 @@ async function promptForAgentsOverwrite(destinationPath) {
374
441
  output: process.stdout,
375
442
  });
376
443
  try {
377
- const answer = (await rl.question(`Overwrite existing AGENTS.md at "${destinationPath}"? [y/N]: `))
444
+ const answer = (await rl.question(`Plugin mode: add OMX developer_instructions defaults to "${configPath}"? [y/N]: `))
445
+ .trim()
446
+ .toLowerCase();
447
+ return answer === "y" || answer === "yes";
448
+ }
449
+ finally {
450
+ rl.close();
451
+ }
452
+ }
453
+ async function promptForPluginDeveloperInstructionsOverwrite(configPath) {
454
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
455
+ return false;
456
+ }
457
+ const rl = createInterface({
458
+ input: process.stdin,
459
+ output: process.stdout,
460
+ });
461
+ try {
462
+ const answer = (await rl.question(`Plugin mode: overwrite existing developer_instructions in "${configPath}" with OMX defaults? [y/N]: `))
378
463
  .trim()
379
464
  .toLowerCase();
380
465
  return answer === "y" || answer === "yes";
@@ -383,26 +468,132 @@ async function promptForAgentsOverwrite(destinationPath) {
383
468
  rl.close();
384
469
  }
385
470
  }
386
- async function resolveSetupScope(projectRoot, requestedScope) {
471
+ async function resolveSetupScope(projectRoot, requestedScope, persistedReviewDecision = "keep", persistedPreferences, setupScopePrompt) {
387
472
  if (requestedScope) {
388
473
  return { scope: requestedScope, source: "cli" };
389
474
  }
390
- const persisted = await readPersistedSetupPreferences(projectRoot);
391
- if (persisted?.scope) {
475
+ const persisted = persistedPreferences ?? (await readPersistedSetupPreferences(projectRoot));
476
+ if (persisted?.scope && persistedReviewDecision === "keep") {
392
477
  return { scope: persisted.scope, source: "persisted" };
393
478
  }
394
- if (process.stdin.isTTY && process.stdout.isTTY) {
395
- const scope = await promptForSetupScope(DEFAULT_SETUP_SCOPE);
479
+ if (typeof setupScopePrompt === "function" ||
480
+ (process.stdin.isTTY && process.stdout.isTTY)) {
481
+ const defaultScope = persistedReviewDecision === "review" && persisted?.scope
482
+ ? persisted.scope
483
+ : DEFAULT_SETUP_SCOPE;
484
+ const scope = setupScopePrompt
485
+ ? await setupScopePrompt(defaultScope)
486
+ : await promptForSetupScope(defaultScope);
396
487
  return { scope, source: "prompt" };
397
488
  }
398
489
  return { scope: DEFAULT_SETUP_SCOPE, source: "default" };
399
490
  }
491
+ async function readPluginManifestName(manifestPath) {
492
+ try {
493
+ const parsed = JSON.parse(await readFile(manifestPath, "utf-8"));
494
+ return typeof parsed === "object" &&
495
+ parsed !== null &&
496
+ "name" in parsed &&
497
+ typeof parsed.name === "string"
498
+ ? parsed.name
499
+ : null;
500
+ }
501
+ catch {
502
+ return null;
503
+ }
504
+ }
505
+ async function discoverOmxPluginCacheDir(cacheRoot = join(codexHome(), "plugins", "cache")) {
506
+ if (!existsSync(cacheRoot))
507
+ return null;
508
+ const queue = [
509
+ { path: cacheRoot, depth: 0 },
510
+ ];
511
+ const maxDepth = 5;
512
+ while (queue.length > 0) {
513
+ const current = queue.shift();
514
+ if (!current)
515
+ break;
516
+ const manifestPath = join(current.path, ".codex-plugin", "plugin.json");
517
+ if (existsSync(manifestPath)) {
518
+ const name = await readPluginManifestName(manifestPath);
519
+ if (name === "oh-my-codex") {
520
+ return current.path;
521
+ }
522
+ }
523
+ if (current.depth >= maxDepth)
524
+ continue;
525
+ let entries;
526
+ try {
527
+ entries = await readdir(current.path, { withFileTypes: true });
528
+ }
529
+ catch {
530
+ continue;
531
+ }
532
+ for (const entry of entries) {
533
+ if (!entry.isDirectory())
534
+ continue;
535
+ if (entry.name === ".git" || entry.name === "node_modules")
536
+ continue;
537
+ queue.push({
538
+ path: join(current.path, entry.name),
539
+ depth: current.depth + 1,
540
+ });
541
+ }
542
+ }
543
+ return null;
544
+ }
545
+ async function resolveSetupInstallMode(projectRoot, scope, requestedInstallMode, installModePrompt, persistedReviewDecision = "keep", persistedPreferences) {
546
+ if (requestedInstallMode) {
547
+ return { installMode: requestedInstallMode, source: "cli" };
548
+ }
549
+ const persisted = persistedPreferences ?? (await readPersistedSetupPreferences(projectRoot));
550
+ if (persisted?.installMode &&
551
+ persistedReviewDecision === "keep" &&
552
+ persisted.scope === scope) {
553
+ return { installMode: persisted.installMode, source: "persisted" };
554
+ }
555
+ if (scope !== "user")
556
+ return null;
557
+ const discoveredPluginCacheDir = await discoverOmxPluginCacheDir();
558
+ const defaultMode = persistedReviewDecision === "review" && persisted?.installMode
559
+ ? persisted.installMode
560
+ : discoveredPluginCacheDir
561
+ ? "plugin"
562
+ : DEFAULT_SETUP_INSTALL_MODE;
563
+ if (typeof installModePrompt === "function" ||
564
+ (process.stdin.isTTY && process.stdout.isTTY)) {
565
+ if (discoveredPluginCacheDir) {
566
+ console.log(`Detected installed oh-my-codex Codex plugin cache at ${discoveredPluginCacheDir}.`);
567
+ }
568
+ const installMode = installModePrompt
569
+ ? await installModePrompt(defaultMode)
570
+ : await promptForSetupInstallMode(defaultMode);
571
+ return { installMode, source: "prompt" };
572
+ }
573
+ return { installMode: defaultMode, source: "default" };
574
+ }
400
575
  function hasGitignoreEntry(content, entry) {
401
576
  return content
402
577
  .split(/\r?\n/)
403
578
  .map((line) => line.trim())
404
579
  .some((line) => line === entry);
405
580
  }
581
+ function isProjectPathIgnoredByGit(projectRoot, path) {
582
+ const result = spawnSync("git", ["check-ignore", "--no-index", "-q", path], {
583
+ cwd: projectRoot,
584
+ stdio: "ignore",
585
+ windowsHide: true,
586
+ });
587
+ return result.status === 0;
588
+ }
589
+ function shouldAddProjectGitignoreEntry(projectRoot, content, entry) {
590
+ if (hasGitignoreEntry(content, entry))
591
+ return false;
592
+ if (entry === ".omx/" && isProjectPathIgnoredByGit(projectRoot, entry)) {
593
+ return false;
594
+ }
595
+ return true;
596
+ }
406
597
  function stripLegacyGitignoreEntries(content, legacyEntries) {
407
598
  const legacyEntrySet = new Set(legacyEntries);
408
599
  const lines = content.split(/\r?\n/);
@@ -420,13 +611,13 @@ async function ensureProjectGitignore(projectRoot, backupContext, options) {
420
611
  ? await readFile(gitignorePath, "utf-8")
421
612
  : "";
422
613
  const normalized = stripLegacyGitignoreEntries(existing, LEGACY_PROJECT_GITIGNORE_ENTRIES);
423
- const missingEntries = PROJECT_GITIGNORE_ENTRIES.filter((entry) => !hasGitignoreEntry(normalized.content, entry));
614
+ const missingEntries = PROJECT_GITIGNORE_ENTRIES.filter((entry) => shouldAddProjectGitignoreEntry(projectRoot, normalized.content, entry));
424
615
  if (missingEntries.length === 0 && !normalized.removed) {
425
616
  return "unchanged";
426
617
  }
427
618
  const nextContent = destinationExists
428
619
  ? `${normalized.content}${normalized.content.endsWith("\n") || normalized.content.length === 0 ? "" : "\n"}${missingEntries.join("\n")}${missingEntries.length > 0 ? "\n" : ""}`
429
- : `${PROJECT_GITIGNORE_ENTRIES.join("\n")}\n`;
620
+ : `${missingEntries.join("\n")}\n`;
430
621
  if (await ensureBackup(gitignorePath, destinationExists, backupContext, options)) {
431
622
  // backup created when refreshing a pre-existing .gitignore
432
623
  }
@@ -444,41 +635,379 @@ async function ensureProjectGitignore(projectRoot, backupContext, options) {
444
635
  }
445
636
  return destinationExists ? "updated" : "created";
446
637
  }
447
- async function persistSetupScope(projectRoot, scope, options) {
448
- const scopePath = getScopeFilePath(projectRoot);
638
+ async function persistSetupPreferences(projectRoot, preferences, options) {
639
+ const scopePath = getSetupScopeFilePath(projectRoot);
449
640
  if (options.dryRun) {
450
641
  if (options.verbose)
451
642
  console.log(` dry-run: skip persisting ${scopePath}`);
452
643
  return;
453
644
  }
454
645
  await mkdir(dirname(scopePath), { recursive: true });
455
- const payload = { scope };
456
- await writeFile(scopePath, JSON.stringify(payload, null, 2) + "\n");
646
+ await writeFile(scopePath, JSON.stringify(preferences, null, 2) + "\n");
457
647
  if (options.verbose)
458
648
  console.log(` Wrote ${scopePath}`);
459
649
  }
650
+ async function removeEmptyDirectoryIfPresent(dirPath, options) {
651
+ if (options.dryRun || !existsSync(dirPath))
652
+ return;
653
+ try {
654
+ const remaining = await readdir(dirPath);
655
+ if (remaining.length === 0) {
656
+ await rm(dirPath, { recursive: true, force: true });
657
+ if (options.verbose)
658
+ console.log(` removed empty directory ${dirPath}`);
659
+ }
660
+ }
661
+ catch {
662
+ // Best-effort cleanup only.
663
+ }
664
+ }
665
+ async function cleanupPluginModeLegacyPrompts(srcDir, dstDir, backupContext, options) {
666
+ const summary = createEmptyCategorySummary();
667
+ if (!existsSync(srcDir) || !existsSync(dstDir))
668
+ return summary;
669
+ const manifest = tryReadCatalogManifest();
670
+ for (const file of await readdir(srcDir)) {
671
+ if (!file.endsWith(".md"))
672
+ continue;
673
+ const promptName = file.slice(0, -3);
674
+ if (manifest && !isSetupPromptAssetName(promptName, manifest))
675
+ continue;
676
+ const dst = join(dstDir, file);
677
+ if (!existsSync(dst))
678
+ continue;
679
+ if (await ensureBackup(dst, true, backupContext, options)) {
680
+ summary.backedUp += 1;
681
+ }
682
+ if (!options.dryRun) {
683
+ await rm(dst, { force: true });
684
+ }
685
+ summary.removed += 1;
686
+ if (options.verbose) {
687
+ console.log(` ${options.dryRun ? "would archive and remove" : "archived and removed"} legacy prompt ${file}`);
688
+ }
689
+ }
690
+ await removeEmptyDirectoryIfPresent(dstDir, options);
691
+ return summary;
692
+ }
693
+ async function cleanupPluginModeLegacyNativeAgents(pkgRoot, agentsDir, backupContext, options) {
694
+ const summary = createEmptyCategorySummary();
695
+ if (!existsSync(agentsDir))
696
+ return summary;
697
+ const manifest = tryReadCatalogManifest();
698
+ const agentStatusByName = manifest
699
+ ? getCatalogAgentStatusByName(manifest)
700
+ : null;
701
+ for (const [name, agent] of Object.entries(AGENT_DEFINITIONS)) {
702
+ const status = agentStatusByName?.get(name);
703
+ if (agentStatusByName && !isNativeAgentInstallableStatus(status))
704
+ continue;
705
+ const dst = join(agentsDir, `${name}.toml`);
706
+ const promptPath = join(pkgRoot, "prompts", `${name}.md`);
707
+ if (!existsSync(dst) || !existsSync(promptPath))
708
+ continue;
709
+ const promptContent = await readFile(promptPath, "utf-8");
710
+ const expectedToml = generateAgentToml(agent, promptContent, {
711
+ codexHomeOverride: join(agentsDir, ".."),
712
+ });
713
+ const installedToml = await readFile(dst, "utf-8");
714
+ if (installedToml !== expectedToml &&
715
+ !isGeneratedOmxNativeAgentToml(installedToml, name)) {
716
+ summary.skipped += 1;
717
+ if (options.verbose) {
718
+ console.log(` skipped legacy native agent cleanup for ${name}.toml: installed content is not an OMX-generated native agent`);
719
+ }
720
+ continue;
721
+ }
722
+ if (await ensureBackup(dst, true, backupContext, options)) {
723
+ summary.backedUp += 1;
724
+ }
725
+ if (!options.dryRun) {
726
+ await rm(dst, { force: true });
727
+ }
728
+ summary.removed += 1;
729
+ if (options.verbose) {
730
+ console.log(` ${options.dryRun ? "would archive and remove" : "archived and removed"} legacy native agent ${name}.toml`);
731
+ }
732
+ }
733
+ if (manifest) {
734
+ const generatedCleanup = await cleanupGeneratedNonInstallableNativeAgents(agentsDir, manifest, backupContext, options);
735
+ summary.backedUp += generatedCleanup.backedUp;
736
+ summary.removed += generatedCleanup.removed;
737
+ }
738
+ await removeEmptyDirectoryIfPresent(agentsDir, options);
739
+ return summary;
740
+ }
741
+ function stripPluginModeLegacyRootDefaults(config) {
742
+ const lines = config.split(/\r?\n/);
743
+ const firstTableIndex = lines.findIndex((line) => /^\s*\[/.test(line));
744
+ const boundary = firstTableIndex >= 0 ? firstTableIndex : lines.length;
745
+ const result = [];
746
+ for (let index = 0; index < lines.length; index += 1) {
747
+ const line = lines[index];
748
+ if (index < boundary &&
749
+ line.trim() ===
750
+ "# oh-my-codex top-level settings (must be before any [table])") {
751
+ continue;
752
+ }
753
+ if (index < boundary &&
754
+ /^\s*notify\s*=\s*\["node",\s*".*notify-hook\.js"\]\s*$/.test(line)) {
755
+ continue;
756
+ }
757
+ if (index < boundary &&
758
+ /^\s*model_reasoning_effort\s*=\s*"medium"\s*$/.test(line)) {
759
+ continue;
760
+ }
761
+ if (index < boundary &&
762
+ /^\s*developer_instructions\s*=/.test(line) &&
763
+ line.includes("You have oh-my-codex installed.")) {
764
+ continue;
765
+ }
766
+ result.push(line);
767
+ }
768
+ return result.join("\n").replace(/\n{3,}/g, "\n\n");
769
+ }
770
+ function rootHasTomlKey(config, key) {
771
+ const lines = config.split(/\r?\n/);
772
+ const firstTableIndex = lines.findIndex((line) => /^\s*\[/.test(line));
773
+ const boundary = firstTableIndex >= 0 ? firstTableIndex : lines.length;
774
+ const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
775
+ const pattern = new RegExp(`^\\s*${escapedKey}\\s*=`);
776
+ return lines.slice(0, boundary).some((line) => pattern.test(line));
777
+ }
778
+ function replaceRootTomlKey(config, key, line) {
779
+ const lines = config.trimEnd().split(/\r?\n/);
780
+ const firstTableIndex = lines.findIndex((entry) => /^\s*\[/.test(entry));
781
+ const boundary = firstTableIndex < 0 ? lines.length : firstTableIndex;
782
+ const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
783
+ const pattern = new RegExp(`^\\s*${escapedKey}\\s*=`);
784
+ for (let i = 0; i < boundary; i++) {
785
+ if (pattern.test(lines[i])) {
786
+ lines[i] = line;
787
+ return lines.join("\n") + "\n";
788
+ }
789
+ }
790
+ return insertRootTomlKey(config, line);
791
+ }
792
+ function insertRootTomlKey(config, line) {
793
+ const lines = config.trimEnd().split(/\r?\n/);
794
+ if (lines.length === 1 && lines[0] === "")
795
+ return `${line}\n`;
796
+ const firstTableIndex = lines.findIndex((entry) => /^\s*\[/.test(entry));
797
+ if (firstTableIndex < 0)
798
+ return `${lines.join("\n")}\n${line}\n`;
799
+ const before = lines
800
+ .slice(0, firstTableIndex)
801
+ .filter((entry) => entry.trim() !== "");
802
+ const after = lines.slice(firstTableIndex);
803
+ return [...before, line, "", ...after].join("\n") + "\n";
804
+ }
805
+ async function ensurePluginMarketplaceRegistration(configPath, pkgRoot, backupContext, summary, options) {
806
+ const packagedMarketplace = await resolvePackagedOmxMarketplace(pkgRoot);
807
+ if (!packagedMarketplace) {
808
+ summary.skipped += 1;
809
+ return "unavailable";
810
+ }
811
+ const existingConfig = existsSync(configPath)
812
+ ? await readFile(configPath, "utf-8")
813
+ : "";
814
+ const nextConfig = upsertLocalOmxMarketplaceRegistration(existingConfig, pkgRoot);
815
+ const destinationExists = existsSync(configPath);
816
+ if (nextConfig === existingConfig) {
817
+ summary.unchanged += 1;
818
+ return "unchanged";
819
+ }
820
+ if (await ensureBackup(configPath, destinationExists, backupContext, options)) {
821
+ summary.backedUp += 1;
822
+ }
823
+ if (!options.dryRun) {
824
+ await mkdir(dirname(configPath), { recursive: true });
825
+ await writeFile(configPath, nextConfig);
826
+ }
827
+ summary.updated += 1;
828
+ if (options.verbose) {
829
+ console.log(` ${options.dryRun ? "would register" : "registered"} local Codex plugin marketplace ${OMX_LOCAL_MARKETPLACE_NAME} from ${pkgRoot}`);
830
+ }
831
+ return "updated";
832
+ }
833
+ async function applyPluginModeHooksConfig(configPath, hooksPath, pkgRoot, backupContext, summary, options) {
834
+ const existingConfig = existsSync(configPath)
835
+ ? await readFile(configPath, "utf-8")
836
+ : "";
837
+ const nextConfig = upsertCodexHooksFeatureFlag(existingConfig).trimEnd() + "\n";
838
+ if (nextConfig !== existingConfig) {
839
+ if (await ensureBackup(configPath, existsSync(configPath), backupContext, options)) {
840
+ summary.backedUp += 1;
841
+ }
842
+ if (!options.dryRun) {
843
+ await mkdir(dirname(configPath), { recursive: true });
844
+ await writeFile(configPath, nextConfig);
845
+ }
846
+ summary.updated += 1;
847
+ }
848
+ else {
849
+ summary.unchanged += 1;
850
+ }
851
+ const existingHooksContent = existsSync(hooksPath)
852
+ ? await readFile(hooksPath, "utf-8")
853
+ : null;
854
+ const hooksConfig = mergeManagedCodexHooksConfig(existingHooksContent, pkgRoot);
855
+ await syncManagedContent(hooksConfig, hooksPath, summary, backupContext, options, `native hooks ${hooksPath}`);
856
+ if (options.verbose) {
857
+ console.log(` ${options.dryRun ? "would configure" : "configured"} plugin-mode native hooks at ${hooksPath}`);
858
+ }
859
+ }
860
+ async function applyPluginDeveloperInstructionsDefault(configPath, backupContext, summary, options) {
861
+ const existing = existsSync(configPath)
862
+ ? await readFile(configPath, "utf-8")
863
+ : "";
864
+ const line = `developer_instructions = ${JSON.stringify(OMX_DEVELOPER_INSTRUCTIONS)}`;
865
+ const hasExistingDeveloperInstructions = rootHasTomlKey(existing, "developer_instructions");
866
+ if (hasExistingDeveloperInstructions) {
867
+ const overwrite = options.pluginDeveloperInstructionsOverwritePrompt
868
+ ? await options.pluginDeveloperInstructionsOverwritePrompt(configPath)
869
+ : await promptForPluginDeveloperInstructionsOverwrite(configPath);
870
+ if (!overwrite) {
871
+ summary.skipped += 1;
872
+ if (options.verbose) {
873
+ console.log(" skipped plugin developer_instructions default: root developer_instructions already exists");
874
+ }
875
+ return "exists";
876
+ }
877
+ }
878
+ const nextConfig = hasExistingDeveloperInstructions
879
+ ? replaceRootTomlKey(existing, "developer_instructions", line)
880
+ : insertRootTomlKey(existing, line);
881
+ const destinationExists = existsSync(configPath);
882
+ if (await ensureBackup(configPath, destinationExists, backupContext, options)) {
883
+ summary.backedUp += 1;
884
+ }
885
+ if (!options.dryRun) {
886
+ await mkdir(dirname(configPath), { recursive: true });
887
+ await writeFile(configPath, nextConfig);
888
+ }
889
+ summary.updated += 1;
890
+ if (options.verbose) {
891
+ console.log(` ${options.dryRun ? "would add" : "added"} plugin developer_instructions default to ${configPath}`);
892
+ }
893
+ return "updated";
894
+ }
895
+ async function cleanupPluginModeLegacyConfig(configPath, backupContext, options) {
896
+ if (!existsSync(configPath))
897
+ return false;
898
+ const original = await readFile(configPath, "utf-8");
899
+ let config = original;
900
+ config = stripExistingOmxBlocks(config).cleaned;
901
+ config = stripExistingSharedMcpRegistryBlock(config).cleaned;
902
+ config = stripPluginModeLegacyRootDefaults(config);
903
+ config = stripOmxSeededBehavioralDefaults(config);
904
+ config = stripOmxFeatureFlags(config);
905
+ config = stripOmxEnvSettings(config);
906
+ config = config.trim();
907
+ const nextConfig = config.length > 0 ? `${config}\n` : "";
908
+ if (nextConfig === original)
909
+ return false;
910
+ if (await ensureBackup(configPath, true, backupContext, options)) {
911
+ // backup created for pre-existing config
912
+ }
913
+ if (!options.dryRun) {
914
+ if (nextConfig.length === 0) {
915
+ await rm(configPath, { force: true });
916
+ }
917
+ else {
918
+ await writeFile(configPath, nextConfig);
919
+ }
920
+ }
921
+ if (options.verbose) {
922
+ console.log(` ${options.dryRun ? "would clean" : nextConfig.length === 0 ? "removed" : "cleaned"} legacy OMX config ${basename(configPath)}`);
923
+ }
924
+ return true;
925
+ }
926
+ async function cleanupPluginModeLegacyAgentsMd(agentsMdPath, backupContext, options) {
927
+ if (!existsSync(agentsMdPath))
928
+ return false;
929
+ const content = await readFile(agentsMdPath, "utf-8");
930
+ if (!isOmxGeneratedAgentsMd(content))
931
+ return false;
932
+ if (await ensureBackup(agentsMdPath, true, backupContext, options)) {
933
+ // backup created for pre-existing AGENTS.md
934
+ }
935
+ if (!options.dryRun) {
936
+ await rm(agentsMdPath, { force: true });
937
+ }
938
+ if (options.verbose) {
939
+ console.log(` ${options.dryRun ? "would remove" : "removed"} legacy OMX-generated AGENTS.md`);
940
+ }
941
+ return true;
942
+ }
460
943
  export async function setup(options = {}) {
461
- const { force = false, dryRun = false, scope: requestedScope, verbose = false, modelUpgradePrompt, } = options;
944
+ const { force = false, dryRun = false, installMode: requestedInstallMode, scope: requestedScope, verbose = false, setupScopePrompt, persistedSetupReviewPrompt, installModePrompt, modelUpgradePrompt, pluginAgentsMdPrompt, pluginDeveloperInstructionsPrompt, pluginDeveloperInstructionsOverwritePrompt, } = options;
462
945
  const pkgRoot = getPackageRoot();
463
946
  const projectRoot = process.cwd();
464
- const resolvedScope = await resolveSetupScope(projectRoot, requestedScope);
947
+ const persistedPreferences = await readPersistedSetupPreferences(projectRoot, { warnOnLegacyScope: true });
948
+ let persistedReviewDecision = "keep";
949
+ const effectiveScopeForInstallMode = requestedScope ?? persistedPreferences?.scope ?? DEFAULT_SETUP_SCOPE;
950
+ const wouldUsePersistedScope = !requestedScope && Boolean(persistedPreferences?.scope);
951
+ const wouldUsePersistedInstallMode = !requestedInstallMode &&
952
+ Boolean(persistedPreferences?.installMode) &&
953
+ (!persistedPreferences?.scope ||
954
+ persistedPreferences.scope === effectiveScopeForInstallMode);
955
+ const shouldReviewPersistedSetup = hasPersistedSetupPreferences(persistedPreferences) &&
956
+ (wouldUsePersistedScope || wouldUsePersistedInstallMode) &&
957
+ (typeof persistedSetupReviewPrompt === "function" ||
958
+ (process.stdin.isTTY && process.stdout.isTTY));
959
+ if (shouldReviewPersistedSetup) {
960
+ persistedReviewDecision = persistedSetupReviewPrompt
961
+ ? await persistedSetupReviewPrompt(persistedPreferences)
962
+ : await promptForPersistedSetupReview(persistedPreferences);
963
+ console.log(`Setup preference review: ${persistedReviewDecision} (${formatPersistedSetupPreferenceSummary(persistedPreferences)})\n`);
964
+ }
965
+ const resolvedScope = await resolveSetupScope(projectRoot, requestedScope, persistedReviewDecision, persistedPreferences, setupScopePrompt);
966
+ const resolvedInstallMode = await resolveSetupInstallMode(projectRoot, resolvedScope.scope, requestedInstallMode, installModePrompt, persistedReviewDecision, persistedPreferences);
465
967
  const scopeDirs = resolveScopeDirectories(resolvedScope.scope, projectRoot);
466
968
  const scopeSourceMessage = resolvedScope.source === "persisted" ? " (from .omx/setup-scope.json)" : "";
467
969
  const backupContext = getBackupContext(resolvedScope.scope, projectRoot);
970
+ const isPluginInstallMode = resolvedInstallMode?.installMode === "plugin";
971
+ const pluginAgentsMdDst = resolvedScope.scope === "project"
972
+ ? join(projectRoot, "AGENTS.md")
973
+ : join(scopeDirs.codexHomeDir, "AGENTS.md");
974
+ const usePluginDeveloperInstructionsDefault = isPluginInstallMode
975
+ ? pluginDeveloperInstructionsPrompt
976
+ ? await pluginDeveloperInstructionsPrompt(scopeDirs.codexConfigFile)
977
+ : await promptForPluginDeveloperInstructionsDefault(scopeDirs.codexConfigFile)
978
+ : false;
979
+ const usePluginAgentsMdDefault = isPluginInstallMode
980
+ ? pluginAgentsMdPrompt
981
+ ? await pluginAgentsMdPrompt(pluginAgentsMdDst)
982
+ : await promptForPluginAgentsMdDefault(pluginAgentsMdDst)
983
+ : false;
468
984
  console.log("oh-my-codex setup");
469
985
  console.log("=================\n");
470
986
  console.log(`Using setup scope: ${resolvedScope.scope}${scopeSourceMessage}\n`);
987
+ if (resolvedInstallMode) {
988
+ const installModeSourceMessage = resolvedInstallMode.source === "persisted"
989
+ ? " (from .omx/setup-scope.json)"
990
+ : "";
991
+ console.log(`Using setup install mode: ${resolvedInstallMode.installMode}${installModeSourceMessage}\n`);
992
+ }
471
993
  // Step 1: Ensure directories exist
472
994
  console.log("[1/8] Creating directories...");
473
- const dirs = [
474
- scopeDirs.codexHomeDir,
475
- scopeDirs.promptsDir,
476
- scopeDirs.skillsDir,
477
- scopeDirs.nativeAgentsDir,
478
- omxStateDir(projectRoot),
479
- omxPlansDir(projectRoot),
480
- omxLogsDir(projectRoot),
481
- ];
995
+ const dirs = isPluginInstallMode
996
+ ? [
997
+ scopeDirs.codexHomeDir,
998
+ omxStateDir(projectRoot),
999
+ omxPlansDir(projectRoot),
1000
+ omxLogsDir(projectRoot),
1001
+ ]
1002
+ : [
1003
+ scopeDirs.codexHomeDir,
1004
+ scopeDirs.promptsDir,
1005
+ scopeDirs.skillsDir,
1006
+ scopeDirs.nativeAgentsDir,
1007
+ omxStateDir(projectRoot),
1008
+ omxPlansDir(projectRoot),
1009
+ omxLogsDir(projectRoot),
1010
+ ];
482
1011
  for (const dir of dirs) {
483
1012
  if (!dryRun) {
484
1013
  await mkdir(dir, { recursive: true });
@@ -486,7 +1015,17 @@ export async function setup(options = {}) {
486
1015
  if (verbose)
487
1016
  console.log(` mkdir ${dir}`);
488
1017
  }
489
- await persistSetupScope(projectRoot, resolvedScope.scope, {
1018
+ const setupPreferencesToPersist = resolvedInstallMode &&
1019
+ (resolvedScope.scope === "user" ||
1020
+ resolvedInstallMode.installMode === "plugin")
1021
+ ? {
1022
+ scope: resolvedScope.scope,
1023
+ installMode: resolvedInstallMode.installMode,
1024
+ }
1025
+ : {
1026
+ scope: resolvedScope.scope,
1027
+ };
1028
+ await persistSetupPreferences(projectRoot, setupPreferencesToPersist, {
490
1029
  dryRun,
491
1030
  verbose,
492
1031
  });
@@ -507,37 +1046,63 @@ export async function setup(options = {}) {
507
1046
  {
508
1047
  const promptsSrc = join(pkgRoot, "prompts");
509
1048
  const promptsDst = scopeDirs.promptsDir;
510
- summary.prompts = await installPrompts(promptsSrc, promptsDst, backupContext, { force, dryRun, verbose });
511
- const cleanedLegacyPromptShims = await cleanupLegacySkillPromptShims(promptsSrc, promptsDst, {
512
- dryRun,
513
- verbose,
514
- });
515
- summary.prompts.removed += cleanedLegacyPromptShims;
516
- if (cleanedLegacyPromptShims > 0) {
517
- if (dryRun) {
518
- console.log(` Would remove ${cleanedLegacyPromptShims} legacy skill prompt shim file(s).`);
1049
+ if (isPluginInstallMode) {
1050
+ summary.prompts = await cleanupPluginModeLegacyPrompts(promptsSrc, promptsDst, backupContext, { dryRun, verbose });
1051
+ console.log(summary.prompts.removed > 0
1052
+ ? ` ${dryRun ? "Would archive and remove" : "Archived and removed"} ${summary.prompts.removed} legacy OMX-managed prompt file(s).\n`
1053
+ : " Prompt refresh skipped; no legacy OMX-managed prompt files found.\n");
1054
+ }
1055
+ else {
1056
+ summary.prompts = await installPrompts(promptsSrc, promptsDst, backupContext, { force, dryRun, verbose });
1057
+ const cleanedLegacyPromptShims = await cleanupLegacySkillPromptShims(promptsSrc, promptsDst, {
1058
+ dryRun,
1059
+ verbose,
1060
+ });
1061
+ summary.prompts.removed += cleanedLegacyPromptShims;
1062
+ if (cleanedLegacyPromptShims > 0) {
1063
+ if (dryRun) {
1064
+ console.log(` Would remove ${cleanedLegacyPromptShims} legacy skill prompt shim file(s).`);
1065
+ }
1066
+ else {
1067
+ console.log(` Removed ${cleanedLegacyPromptShims} legacy skill prompt shim file(s).`);
1068
+ }
1069
+ }
1070
+ if (catalogCounts) {
1071
+ console.log(` Prompt refresh complete (catalog baseline: ${catalogCounts.prompts}).\n`);
519
1072
  }
520
1073
  else {
521
- console.log(` Removed ${cleanedLegacyPromptShims} legacy skill prompt shim file(s).`);
1074
+ console.log(" Prompt refresh complete.\n");
522
1075
  }
523
1076
  }
524
- if (catalogCounts) {
525
- console.log(` Prompt refresh complete (catalog baseline: ${catalogCounts.prompts}).\n`);
526
- }
527
- else {
528
- console.log(" Prompt refresh complete.\n");
529
- }
530
1077
  }
531
1078
  // Step 3: Install skills
532
1079
  console.log("[3/8] Installing skills...");
533
1080
  {
534
1081
  const skillsSrc = join(pkgRoot, "skills");
535
1082
  const skillsDst = scopeDirs.skillsDir;
536
- summary.skills = await installSkills(skillsSrc, skillsDst, backupContext, {
537
- force,
538
- dryRun,
539
- verbose,
540
- });
1083
+ if (isPluginInstallMode) {
1084
+ summary.skills = createEmptyCategorySummary();
1085
+ const cleanup = await cleanupLegacyManagedSkills(skillsSrc, skillsDst, backupContext, { dryRun, verbose });
1086
+ summary.skills.backedUp += cleanup.backedUp;
1087
+ summary.skills.removed += cleanup.removedSkillNames.length;
1088
+ summary.skills.skipped += cleanup.skippedSkillNames.length;
1089
+ for (const warning of cleanup.warnings) {
1090
+ console.log(` warning: ${warning}`);
1091
+ }
1092
+ if (cleanup.removedSkillNames.length > 0) {
1093
+ console.log(` ${dryRun ? "Would remove" : "Removed"} ${cleanup.removedSkillNames.length} legacy OMX-managed skill director${cleanup.removedSkillNames.length === 1 ? "y" : "ies"}.`);
1094
+ }
1095
+ else {
1096
+ console.log(" Skill refresh skipped; no removable legacy OMX-managed skill directories found.");
1097
+ }
1098
+ }
1099
+ else {
1100
+ summary.skills = await installSkills(skillsSrc, skillsDst, backupContext, {
1101
+ force,
1102
+ dryRun,
1103
+ verbose,
1104
+ });
1105
+ }
541
1106
  if (catalogCounts) {
542
1107
  console.log(` Skill refresh complete (catalog baseline: ${catalogCounts.skills}).\n`);
543
1108
  }
@@ -547,7 +1112,13 @@ export async function setup(options = {}) {
547
1112
  }
548
1113
  // Step 4: Install native agent configs
549
1114
  console.log("[4/8] Installing native agent configs...");
550
- {
1115
+ if (isPluginInstallMode) {
1116
+ summary.nativeAgents = await cleanupPluginModeLegacyNativeAgents(pkgRoot, scopeDirs.nativeAgentsDir, backupContext, { dryRun, verbose });
1117
+ console.log(summary.nativeAgents.removed > 0
1118
+ ? ` ${dryRun ? "Would archive and remove" : "Archived and removed"} ${summary.nativeAgents.removed} legacy OMX-managed native agent config(s).\n`
1119
+ : " Native agent refresh skipped; no legacy OMX-managed native agent configs found.\n");
1120
+ }
1121
+ else {
551
1122
  summary.nativeAgents = await refreshNativeAgentConfigs(pkgRoot, scopeDirs.nativeAgentsDir, backupContext, {
552
1123
  force,
553
1124
  dryRun,
@@ -557,39 +1128,93 @@ export async function setup(options = {}) {
557
1128
  }
558
1129
  // Step 5: Update config.toml
559
1130
  console.log("[5/8] Updating config.toml...");
560
- const registryCandidates = getUnifiedMcpRegistryCandidates();
561
- const defaultRegistryCandidates = registryCandidates.slice(0, 1);
562
- const legacyRegistryCandidate = getLegacyUnifiedMcpRegistryCandidate();
563
- const sharedMcpRegistry = await loadUnifiedMcpRegistry({
564
- candidates: options.mcpRegistryCandidates ?? defaultRegistryCandidates,
565
- });
566
- if (!options.mcpRegistryCandidates &&
567
- !sharedMcpRegistry.sourcePath &&
568
- existsSync(legacyRegistryCandidate) &&
569
- !existsSync(defaultRegistryCandidates[0])) {
570
- console.log(` warning: legacy shared MCP registry detected at ${legacyRegistryCandidate} but ignored by default; move it to ${defaultRegistryCandidates[0]} if you still want setup to sync those servers`);
571
- }
572
- if (verbose && sharedMcpRegistry.sourcePath) {
573
- console.log(` shared MCP registry: ${sharedMcpRegistry.sourcePath} (${sharedMcpRegistry.servers.length} servers)`);
574
- }
575
- for (const warning of sharedMcpRegistry.warnings) {
576
- console.log(` warning: ${warning}`);
577
- }
578
- const managedConfig = await updateManagedConfig(scopeDirs.codexConfigFile, pkgRoot, sharedMcpRegistry, summary.config, backupContext, { codexVersionProbe: options.codexVersionProbe, dryRun, verbose, modelUpgradePrompt });
579
- const resolvedConfig = managedConfig.finalConfig;
580
- if (managedConfig.repairedLegacyTeamRunTable) {
581
- console.log(" Removed retired [mcp_servers.omx_team_run] config during refresh.");
1131
+ let resolvedConfig = "";
1132
+ let omxManagesTui = false;
1133
+ if (isPluginInstallMode) {
1134
+ const configCleaned = await cleanupPluginModeLegacyConfig(scopeDirs.codexConfigFile, backupContext, { dryRun, verbose });
1135
+ if (configCleaned)
1136
+ summary.config.removed += 1;
1137
+ console.log(configCleaned
1138
+ ? ` ${dryRun ? "Would clean" : "Cleaned"} legacy OMX config entries for plugin mode.\n`
1139
+ : " Config refresh skipped; no legacy OMX config entries found.\n");
1140
+ await applyPluginModeHooksConfig(scopeDirs.codexConfigFile, scopeDirs.codexHooksFile, pkgRoot, backupContext, summary.config, { dryRun, verbose });
1141
+ const pluginMarketplaceResult = await ensurePluginMarketplaceRegistration(scopeDirs.codexConfigFile, pkgRoot, backupContext, summary.config, { dryRun, verbose });
1142
+ if (pluginMarketplaceResult === "unavailable") {
1143
+ console.log(` warning: packaged ${OMX_LOCAL_MARKETPLACE_NAME} Codex plugin marketplace metadata not found; /skills plugin discovery was not registered.`);
1144
+ }
1145
+ else if (pluginMarketplaceResult === "updated") {
1146
+ console.log(` ${dryRun ? "Would register" : "Registered"} local Codex plugin marketplace ${OMX_LOCAL_MARKETPLACE_NAME} (${pkgRoot}).`);
1147
+ }
1148
+ else {
1149
+ console.log(` Local Codex plugin marketplace ${OMX_LOCAL_MARKETPLACE_NAME} already registered (${pkgRoot}).`);
1150
+ }
1151
+ resolvedConfig = existsSync(scopeDirs.codexConfigFile)
1152
+ ? await readFile(scopeDirs.codexConfigFile, "utf-8")
1153
+ : "";
1154
+ console.log(` Native Codex hooks refresh complete (${scopeDirs.codexHooksFile}).\n`);
1155
+ if (usePluginDeveloperInstructionsDefault) {
1156
+ const developerInstructionsResult = await applyPluginDeveloperInstructionsDefault(scopeDirs.codexConfigFile, backupContext, summary.config, {
1157
+ dryRun,
1158
+ verbose,
1159
+ pluginDeveloperInstructionsOverwritePrompt,
1160
+ });
1161
+ if (developerInstructionsResult === "updated") {
1162
+ resolvedConfig = existsSync(scopeDirs.codexConfigFile)
1163
+ ? await readFile(scopeDirs.codexConfigFile, "utf-8")
1164
+ : "";
1165
+ console.log(` ${dryRun ? "Would add" : "Added"} plugin-mode developer_instructions default (${scopeDirs.codexConfigFile}).\n`);
1166
+ }
1167
+ else {
1168
+ console.log(` Preserved existing developer_instructions in ${scopeDirs.codexConfigFile}.\n`);
1169
+ }
1170
+ }
1171
+ else {
1172
+ console.log(" Plugin-mode developer_instructions default not selected.\n");
1173
+ }
582
1174
  }
583
- if (resolvedScope.scope === "user") {
584
- await syncClaudeCodeMcpSettings(sharedMcpRegistry, summary.config, backupContext, { dryRun, verbose });
1175
+ else {
1176
+ const registryCandidates = getUnifiedMcpRegistryCandidates();
1177
+ const defaultRegistryCandidates = registryCandidates.slice(0, 1);
1178
+ const legacyRegistryCandidate = getLegacyUnifiedMcpRegistryCandidate();
1179
+ const sharedMcpRegistry = await loadUnifiedMcpRegistry({
1180
+ candidates: options.mcpRegistryCandidates ?? defaultRegistryCandidates,
1181
+ });
1182
+ if (!options.mcpRegistryCandidates &&
1183
+ !sharedMcpRegistry.sourcePath &&
1184
+ existsSync(legacyRegistryCandidate) &&
1185
+ !existsSync(defaultRegistryCandidates[0])) {
1186
+ console.log(` warning: legacy shared MCP registry detected at ${legacyRegistryCandidate} but ignored by default; move it to ${defaultRegistryCandidates[0]} if you still want setup to sync those servers`);
1187
+ }
1188
+ if (verbose && sharedMcpRegistry.sourcePath) {
1189
+ console.log(` shared MCP registry: ${sharedMcpRegistry.sourcePath} (${sharedMcpRegistry.servers.length} servers)`);
1190
+ }
1191
+ for (const warning of sharedMcpRegistry.warnings) {
1192
+ console.log(` warning: ${warning}`);
1193
+ }
1194
+ const statusLinePreset = await resolveStatusLinePresetForSetup(projectRoot, { force });
1195
+ const managedConfig = await updateManagedConfig(scopeDirs.codexConfigFile, pkgRoot, sharedMcpRegistry, summary.config, backupContext, {
1196
+ dryRun,
1197
+ modelUpgradePrompt,
1198
+ verbose,
1199
+ statusLinePreset,
1200
+ forceStatusLinePreset: force,
1201
+ });
1202
+ resolvedConfig = managedConfig.finalConfig;
1203
+ omxManagesTui = managedConfig.omxManagesTui;
1204
+ if (managedConfig.repairedLegacyTeamRunTable) {
1205
+ console.log(" Removed retired [mcp_servers.omx_team_run] config during refresh.");
1206
+ }
1207
+ if (resolvedScope.scope === "user") {
1208
+ await syncClaudeCodeMcpSettings(sharedMcpRegistry, summary.config, backupContext, { dryRun, verbose });
1209
+ }
1210
+ console.log(` Config refresh complete (${scopeDirs.codexConfigFile}).\n`);
1211
+ const existingHooksContent = existsSync(scopeDirs.codexHooksFile)
1212
+ ? await readFile(scopeDirs.codexHooksFile, "utf-8")
1213
+ : null;
1214
+ const hooksConfig = mergeManagedCodexHooksConfig(existingHooksContent, pkgRoot);
1215
+ await syncManagedContent(hooksConfig, scopeDirs.codexHooksFile, summary.config, backupContext, { dryRun, verbose }, `native hooks ${scopeDirs.codexHooksFile}`);
1216
+ console.log(` Native Codex hooks refresh complete (${scopeDirs.codexHooksFile}).\n`);
585
1217
  }
586
- console.log(` Config refresh complete (${scopeDirs.codexConfigFile}).\n`);
587
- const existingHooksContent = existsSync(scopeDirs.codexHooksFile)
588
- ? await readFile(scopeDirs.codexHooksFile, "utf-8")
589
- : null;
590
- const hooksConfig = mergeManagedCodexHooksConfig(existingHooksContent, pkgRoot);
591
- await syncManagedContent(hooksConfig, scopeDirs.codexHooksFile, summary.config, backupContext, { dryRun, verbose }, `native hooks ${scopeDirs.codexHooksFile}`);
592
- console.log(` Native Codex hooks refresh complete (${scopeDirs.codexHooksFile}).\n`);
593
1218
  // Step 5.5: Verify team CLI interop surface is available.
594
1219
  console.log("[5.5/8] Verifying Team CLI API interop...");
595
1220
  const teamToolsCheck = await verifyTeamCliApiInterop(pkgRoot);
@@ -603,84 +1228,159 @@ export async function setup(options = {}) {
603
1228
  console.log();
604
1229
  // Step 6: Generate AGENTS.md
605
1230
  console.log("[6/8] Generating AGENTS.md...");
606
- const agentsMdSrc = join(pkgRoot, "templates", "AGENTS.md");
607
- const agentsMdDst = resolvedScope.scope === "project"
608
- ? join(projectRoot, "AGENTS.md")
609
- : join(scopeDirs.codexHomeDir, "AGENTS.md");
610
- const agentsMdExists = existsSync(agentsMdDst);
611
- // Guard: refuse to overwrite project-root AGENTS.md during active session
612
- const activeSession = resolvedScope.scope === "project"
613
- ? await readSessionState(projectRoot)
614
- : null;
615
- const sessionIsActive = activeSession && !isSessionStale(activeSession);
616
- if (existsSync(agentsMdSrc)) {
617
- const content = await readFile(agentsMdSrc, "utf-8");
618
- const modelTableContext = resolveAgentsModelTableContext(resolvedConfig, {
619
- codexHomeOverride: scopeDirs.codexHomeDir,
620
- });
621
- const rewritten = upsertAgentsModelTable(addGeneratedAgentsMarker(applyScopePathRewritesToAgentsTemplate(content, resolvedScope.scope)), modelTableContext);
622
- let changed = true;
623
- let canApplyManagedModelRefresh = false;
624
- let managedRefreshContent = "";
625
- if (agentsMdExists) {
626
- const existing = await readFile(agentsMdDst, "utf-8");
627
- changed = existing !== rewritten;
628
- if (hasOmxManagedAgentsSections(existing)) {
629
- managedRefreshContent = upsertAgentsModelTable(existing, modelTableContext);
630
- canApplyManagedModelRefresh = managedRefreshContent !== existing;
1231
+ if (isPluginInstallMode) {
1232
+ const agentsMdRemoved = await cleanupPluginModeLegacyAgentsMd(pluginAgentsMdDst, backupContext, { dryRun, verbose });
1233
+ if (agentsMdRemoved) {
1234
+ summary.agentsMd.removed += 1;
1235
+ console.log(` ${dryRun ? "Would remove" : "Removed"} legacy OMX-generated AGENTS.md for plugin mode.\n`);
1236
+ }
1237
+ if (usePluginAgentsMdDefault) {
1238
+ const agentsMdSrc = join(pkgRoot, "templates", "AGENTS.md");
1239
+ if (existsSync(agentsMdSrc)) {
1240
+ const content = await readFile(agentsMdSrc, "utf-8");
1241
+ const modelTableContext = resolveAgentsModelTableContext(resolvedConfig, {
1242
+ codexHomeOverride: scopeDirs.codexHomeDir,
1243
+ });
1244
+ const rewritten = upsertAgentsModelTable(addGeneratedAgentsMarker(applyScopePathRewritesToAgentsTemplate(content, resolvedScope.scope)), modelTableContext);
1245
+ const result = await syncManagedAgentsContent(rewritten, pluginAgentsMdDst, summary.agentsMd, backupContext, {
1246
+ agentsOverwritePrompt: options.agentsOverwritePrompt,
1247
+ dryRun,
1248
+ force,
1249
+ verbose,
1250
+ });
1251
+ if (result === "updated") {
1252
+ console.log(resolvedScope.scope === "project"
1253
+ ? " Generated plugin-mode AGENTS.md defaults in project root."
1254
+ : ` Generated plugin-mode AGENTS.md defaults in ${scopeDirs.codexHomeDir}.`);
1255
+ }
1256
+ else if (result === "unchanged") {
1257
+ console.log(resolvedScope.scope === "project"
1258
+ ? " Plugin-mode AGENTS.md defaults already up to date in project root."
1259
+ : ` Plugin-mode AGENTS.md defaults already up to date in ${scopeDirs.codexHomeDir}.`);
1260
+ }
1261
+ else {
1262
+ console.log(` Skipped plugin-mode AGENTS.md defaults for ${pluginAgentsMdDst}.`);
1263
+ }
1264
+ }
1265
+ else {
1266
+ summary.agentsMd.skipped += 1;
1267
+ console.log(" AGENTS.md template not found, skipping.");
631
1268
  }
632
1269
  }
633
- if (resolvedScope.scope === "project" &&
634
- sessionIsActive &&
635
- agentsMdExists &&
636
- changed) {
1270
+ else {
637
1271
  summary.agentsMd.skipped += 1;
638
- console.log(" WARNING: Active omx session detected (pid " +
639
- activeSession?.pid +
640
- ").");
641
- console.log(" Skipping AGENTS.md overwrite to avoid corrupting runtime overlay.");
642
- console.log(" Stop the active session first, then re-run setup.");
643
- }
644
- else if (canApplyManagedModelRefresh) {
645
- await syncManagedContent(managedRefreshContent, agentsMdDst, summary.agentsMd, backupContext, { dryRun, verbose }, `AGENTS model table ${agentsMdDst}`);
646
- console.log(resolvedScope.scope === "project"
647
- ? " Refreshed AGENTS.md model capability table in project root."
648
- : ` Refreshed AGENTS.md model capability table in ${scopeDirs.codexHomeDir}.`);
1272
+ console.log(agentsMdRemoved
1273
+ ? " Plugin-mode AGENTS.md defaults not selected.\n"
1274
+ : " AGENTS.md generation skipped; no legacy OMX-generated AGENTS.md found and defaults not selected.\n");
649
1275
  }
650
- else {
651
- const result = await syncManagedAgentsContent(rewritten, agentsMdDst, summary.agentsMd, backupContext, {
652
- agentsOverwritePrompt: options.agentsOverwritePrompt,
653
- dryRun,
654
- force,
655
- verbose,
1276
+ }
1277
+ else {
1278
+ const agentsMdSrc = join(pkgRoot, "templates", "AGENTS.md");
1279
+ const agentsMdDst = resolvedScope.scope === "project"
1280
+ ? join(projectRoot, "AGENTS.md")
1281
+ : join(scopeDirs.codexHomeDir, "AGENTS.md");
1282
+ const agentsMdExists = existsSync(agentsMdDst);
1283
+ // Guard: refuse to overwrite project-root AGENTS.md during active session
1284
+ const activeSession = resolvedScope.scope === "project"
1285
+ ? await readSessionState(projectRoot)
1286
+ : null;
1287
+ const sessionIsActive = activeSession && !isSessionStale(activeSession);
1288
+ if (existsSync(agentsMdSrc)) {
1289
+ const content = await readFile(agentsMdSrc, "utf-8");
1290
+ const modelTableContext = resolveAgentsModelTableContext(resolvedConfig, {
1291
+ codexHomeOverride: scopeDirs.codexHomeDir,
656
1292
  });
657
- if (result === "updated") {
658
- console.log(resolvedScope.scope === "project"
659
- ? " Generated AGENTS.md in project root."
660
- : ` Generated AGENTS.md in ${scopeDirs.codexHomeDir}.`);
1293
+ const rewritten = upsertAgentsModelTable(addGeneratedAgentsMarker(applyScopePathRewritesToAgentsTemplate(content, resolvedScope.scope)), modelTableContext);
1294
+ let changed = true;
1295
+ let canApplyManagedModelRefresh = false;
1296
+ let managedRefreshContent = "";
1297
+ let canApplyManagedAgentsMerge = false;
1298
+ let mergedAgentsContent = "";
1299
+ if (agentsMdExists) {
1300
+ const existing = await readFile(agentsMdDst, "utf-8");
1301
+ changed = existing !== rewritten;
1302
+ if (options.mergeAgents) {
1303
+ mergedAgentsContent = upsertManagedAgentsBlock(existing, rewritten);
1304
+ canApplyManagedAgentsMerge = mergedAgentsContent !== existing;
1305
+ }
1306
+ else {
1307
+ if (hasOmxManagedAgentsSections(existing)) {
1308
+ managedRefreshContent = upsertAgentsModelTable(existing, modelTableContext);
1309
+ canApplyManagedModelRefresh = managedRefreshContent !== existing;
1310
+ }
1311
+ }
1312
+ }
1313
+ if (resolvedScope.scope === "project" &&
1314
+ sessionIsActive &&
1315
+ agentsMdExists &&
1316
+ (changed || canApplyManagedAgentsMerge || canApplyManagedModelRefresh)) {
1317
+ summary.agentsMd.skipped += 1;
1318
+ console.log(" WARNING: Active omx session detected (pid " +
1319
+ activeSession?.pid +
1320
+ ").");
1321
+ console.log(" Skipping AGENTS.md overwrite to avoid corrupting runtime overlay.");
1322
+ console.log(" Stop the active session first, then re-run setup.");
661
1323
  }
662
- else if (result === "unchanged") {
1324
+ else if (options.mergeAgents &&
1325
+ agentsMdExists &&
1326
+ !canApplyManagedAgentsMerge) {
1327
+ summary.agentsMd.unchanged += 1;
663
1328
  console.log(resolvedScope.scope === "project"
664
1329
  ? " AGENTS.md already up to date in project root."
665
1330
  : ` AGENTS.md already up to date in ${scopeDirs.codexHomeDir}.`);
666
1331
  }
667
- else if (agentsMdExists) {
668
- console.log(` Skipped AGENTS.md overwrite for ${agentsMdDst}. Re-run interactively to confirm or use --force.`);
1332
+ else if (canApplyManagedAgentsMerge) {
1333
+ await syncManagedContent(mergedAgentsContent, agentsMdDst, summary.agentsMd, backupContext, { dryRun, verbose }, `merged AGENTS ${agentsMdDst}`);
1334
+ console.log(resolvedScope.scope === "project"
1335
+ ? " Merged OMX-managed AGENTS.md sections into project root."
1336
+ : ` Merged OMX-managed AGENTS.md sections into ${scopeDirs.codexHomeDir}.`);
1337
+ }
1338
+ else if (canApplyManagedModelRefresh) {
1339
+ await syncManagedContent(managedRefreshContent, agentsMdDst, summary.agentsMd, backupContext, { dryRun, verbose }, `AGENTS model table ${agentsMdDst}`);
1340
+ console.log(resolvedScope.scope === "project"
1341
+ ? " Refreshed AGENTS.md model capability table in project root."
1342
+ : ` Refreshed AGENTS.md model capability table in ${scopeDirs.codexHomeDir}.`);
1343
+ }
1344
+ else {
1345
+ const result = await syncManagedAgentsContent(rewritten, agentsMdDst, summary.agentsMd, backupContext, {
1346
+ agentsOverwritePrompt: options.agentsOverwritePrompt,
1347
+ dryRun,
1348
+ force,
1349
+ verbose,
1350
+ });
1351
+ if (result === "updated") {
1352
+ console.log(resolvedScope.scope === "project"
1353
+ ? " Generated AGENTS.md in project root."
1354
+ : ` Generated AGENTS.md in ${scopeDirs.codexHomeDir}.`);
1355
+ }
1356
+ else if (result === "unchanged") {
1357
+ console.log(resolvedScope.scope === "project"
1358
+ ? " AGENTS.md already up to date in project root."
1359
+ : ` AGENTS.md already up to date in ${scopeDirs.codexHomeDir}.`);
1360
+ }
1361
+ else if (agentsMdExists) {
1362
+ console.log(` Skipped AGENTS.md overwrite for ${agentsMdDst}. Re-run interactively to confirm or use --force.`);
1363
+ }
1364
+ }
1365
+ if (resolvedScope.scope === "user") {
1366
+ console.log(" User scope leaves project AGENTS.md unchanged.");
669
1367
  }
670
1368
  }
671
- if (resolvedScope.scope === "user") {
672
- console.log(" User scope leaves project AGENTS.md unchanged.");
1369
+ else {
1370
+ summary.agentsMd.skipped += 1;
1371
+ console.log(" AGENTS.md template not found, skipping.");
673
1372
  }
1373
+ console.log();
674
1374
  }
675
- else {
676
- summary.agentsMd.skipped += 1;
677
- console.log(" AGENTS.md template not found, skipping.");
678
- }
679
- console.log();
680
1375
  // Step 7: Set up notify hook
681
1376
  console.log("[7/8] Configuring notification hook...");
682
- await setupNotifyHook(pkgRoot, { dryRun, verbose });
683
- console.log(" Done.\n");
1377
+ if (isPluginInstallMode) {
1378
+ console.log(" Skipped for plugin skill delivery mode.\n");
1379
+ }
1380
+ else {
1381
+ await setupNotifyHook(pkgRoot, { dryRun, verbose });
1382
+ console.log(" Done.\n");
1383
+ }
684
1384
  // Step 8: Configure HUD
685
1385
  console.log("[8/8] Configuring HUD...");
686
1386
  const hudConfigPath = join(projectRoot, ".omx", "hud-config.json");
@@ -696,12 +1396,9 @@ export async function setup(options = {}) {
696
1396
  else {
697
1397
  console.log(" HUD config already exists (use --force to overwrite).");
698
1398
  }
699
- if (managedConfig.omxManagesTui) {
1399
+ if (omxManagesTui) {
700
1400
  console.log(" StatusLine configured in config.toml via [tui] section.");
701
1401
  }
702
- else {
703
- console.log(" Codex CLI >= 0.107.0 manages [tui]; OMX left that section untouched.");
704
- }
705
1402
  console.log();
706
1403
  console.log("Setup refresh summary:");
707
1404
  logCategorySummary("prompts", summary.prompts);
@@ -722,10 +1419,18 @@ export async function setup(options = {}) {
722
1419
  console.log('Setup complete! Run "omx doctor" to verify installation.');
723
1420
  console.log("\nNext steps:");
724
1421
  console.log(" 1. Start Codex CLI in your project directory");
725
- console.log(" 2. Use role/workflow keywords like $architect, $executor, and $plan in Codex");
726
- console.log(" 3. Browse skills with /skills; AGENTS keyword routing can also activate them implicitly");
727
- console.log(" 4. The AGENTS.md orchestration brain is loaded automatically");
728
- console.log(" 5. Native agent defaults configured in config.toml [agents] and TOML files written to .codex/agents/");
1422
+ if (isPluginInstallMode) {
1423
+ console.log(` 2. Registered Codex marketplace ${OMX_LOCAL_MARKETPLACE_NAME} supplies OMX skills and workflow surfaces`);
1424
+ console.log(" 3. Browse plugin-provided skills with /skills");
1425
+ console.log(" 4. Optional AGENTS.md and developer_instructions defaults are only installed when selected during plugin-mode setup");
1426
+ console.log(" 5. Legacy native-agent TOML defaults remain uninstalled in plugin mode");
1427
+ }
1428
+ else {
1429
+ console.log(" 2. Use role/workflow keywords like $architect, $executor, and $plan in Codex");
1430
+ console.log(" 3. Browse skills with /skills; AGENTS keyword routing can also activate them implicitly");
1431
+ console.log(" 4. The AGENTS.md orchestration brain is loaded automatically");
1432
+ console.log(" 5. Native agent defaults configured in config.toml [agents] and TOML files written to .codex/agents/");
1433
+ }
729
1434
  console.log(' 6. "omx explore" and "omx sparkshell" can hydrate native release binaries on first use; source installs still allow repo-local fallbacks and OMX_EXPLORE_BIN / OMX_SPARKSHELL_BIN overrides');
730
1435
  if (isGitHubCliConfigured()) {
731
1436
  console.log("\nSupport the project: gh repo star Yeachan-Heo/oh-my-codex");
@@ -766,7 +1471,8 @@ async function cleanupLegacySkillPromptShims(promptsSrcDir, promptsDstDir, optio
766
1471
  return removed;
767
1472
  }
768
1473
  function isGitHubCliConfigured() {
769
- const result = spawnSync("gh", ["auth", "status"], { stdio: "ignore",
1474
+ const result = spawnSync("gh", ["auth", "status"], {
1475
+ stdio: "ignore",
770
1476
  windowsHide: true,
771
1477
  });
772
1478
  return result.status === 0;
@@ -872,21 +1578,18 @@ async function installPrompts(srcDir, dstDir, backupContext, options) {
872
1578
  return summary;
873
1579
  const manifest = tryReadCatalogManifest();
874
1580
  const agentStatusByName = manifest
875
- ? new Map(manifest.agents.map((agent) => [agent.name, agent.status]))
1581
+ ? getCatalogAgentStatusByName(manifest)
876
1582
  : null;
877
- const isInstallableStatus = (status) => status === "active" || status === "internal";
878
1583
  const files = await readdir(srcDir);
879
- const staleCandidatePromptNames = new Set(manifest?.agents.map((agent) => agent.name) ?? []);
880
1584
  for (const file of files) {
881
1585
  if (!file.endsWith(".md"))
882
1586
  continue;
883
1587
  const promptName = file.slice(0, -3);
884
- staleCandidatePromptNames.add(promptName);
885
1588
  const status = agentStatusByName?.get(promptName);
886
- if (agentStatusByName && !isInstallableStatus(status)) {
1589
+ if (manifest && !isSetupPromptAssetName(promptName, manifest)) {
887
1590
  summary.skipped += 1;
888
1591
  if (options.verbose) {
889
- const label = status ?? "unlisted";
1592
+ const label = status ?? "unclassified";
890
1593
  console.log(` skipped ${file} (status: ${label})`);
891
1594
  }
892
1595
  continue;
@@ -905,13 +1608,14 @@ async function installPrompts(srcDir, dstDir, backupContext, options) {
905
1608
  continue;
906
1609
  const promptName = file.slice(0, -3);
907
1610
  const status = agentStatusByName?.get(promptName);
908
- if (isInstallableStatus(status))
909
- continue;
910
- if (!staleCandidatePromptNames.has(promptName) && status === undefined)
1611
+ if (isSetupPromptAssetName(promptName, manifest))
911
1612
  continue;
912
1613
  const stalePromptPath = join(dstDir, file);
913
1614
  if (!existsSync(stalePromptPath))
914
1615
  continue;
1616
+ if (await ensureBackup(stalePromptPath, true, backupContext, options)) {
1617
+ summary.backedUp += 1;
1618
+ }
915
1619
  if (!options.dryRun) {
916
1620
  await rm(stalePromptPath, { force: true });
917
1621
  }
@@ -927,6 +1631,55 @@ async function installPrompts(srcDir, dstDir, backupContext, options) {
927
1631
  }
928
1632
  return summary;
929
1633
  }
1634
+ function isGeneratedOmxNativeAgentToml(content, agentName) {
1635
+ const firstLine = content.split(/\r?\n/, 1)[0]?.trim();
1636
+ return firstLine === `# oh-my-codex agent: ${agentName}`;
1637
+ }
1638
+ async function cleanupGeneratedNonInstallableNativeAgents(agentsDir, manifest, backupContext, options) {
1639
+ const summary = createEmptyCategorySummary();
1640
+ if (!existsSync(agentsDir))
1641
+ return summary;
1642
+ const agentStatusByName = getCatalogAgentStatusByName(manifest);
1643
+ const installedFiles = await readdir(agentsDir);
1644
+ for (const file of installedFiles) {
1645
+ if (!file.endsWith(".toml"))
1646
+ continue;
1647
+ const agentName = file.slice(0, -5);
1648
+ const agentStatus = agentStatusByName.get(agentName);
1649
+ if (agentStatus === undefined ||
1650
+ isNativeAgentInstallableStatus(agentStatus)) {
1651
+ continue;
1652
+ }
1653
+ const staleAgentPath = join(agentsDir, file);
1654
+ let content = "";
1655
+ try {
1656
+ content = await readFile(staleAgentPath, "utf-8");
1657
+ }
1658
+ catch {
1659
+ continue;
1660
+ }
1661
+ if (!isGeneratedOmxNativeAgentToml(content, agentName)) {
1662
+ if (options.verbose) {
1663
+ console.log(` skipped stale native agent ${file}: not an OMX-generated native agent`);
1664
+ }
1665
+ continue;
1666
+ }
1667
+ if (await ensureBackup(staleAgentPath, true, backupContext, options)) {
1668
+ summary.backedUp += 1;
1669
+ }
1670
+ if (!options.dryRun) {
1671
+ await rm(staleAgentPath, { force: true });
1672
+ }
1673
+ summary.removed += 1;
1674
+ if (options.verbose) {
1675
+ const prefix = options.dryRun
1676
+ ? "would remove stale generated native agent"
1677
+ : "removed stale generated native agent";
1678
+ console.log(` ${prefix} ${file} (status: ${agentStatus})`);
1679
+ }
1680
+ }
1681
+ return summary;
1682
+ }
930
1683
  async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, options) {
931
1684
  const summary = createEmptyCategorySummary();
932
1685
  if (!options.dryRun) {
@@ -934,17 +1687,18 @@ async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, opti
934
1687
  }
935
1688
  const manifest = tryReadCatalogManifest();
936
1689
  const agentStatusByName = manifest
937
- ? new Map(manifest.agents.map((agent) => [agent.name, agent.status]))
1690
+ ? getCatalogAgentStatusByName(manifest)
938
1691
  : null;
939
- const isInstallableStatus = (status) => status === "active" || status === "internal";
940
1692
  const staleCandidateNativeAgentNames = new Set(manifest?.agents.map((agent) => agent.name) ?? []);
941
- for (const [name, agent] of Object.entries(AGENT_DEFINITIONS)) {
1693
+ const nativeAgentNames = manifest
1694
+ ? [...getInstallableNativeAgentNames(manifest)].sort()
1695
+ : Object.keys(AGENT_DEFINITIONS).sort();
1696
+ for (const name of nativeAgentNames) {
942
1697
  staleCandidateNativeAgentNames.add(name);
943
- const status = agentStatusByName?.get(name);
944
- if (agentStatusByName && !isInstallableStatus(status)) {
1698
+ const agent = AGENT_DEFINITIONS[name];
1699
+ if (!agent) {
945
1700
  if (options.verbose) {
946
- const label = status ?? "unlisted";
947
- console.log(` skipped native agent ${name}.toml (status: ${label})`);
1701
+ console.log(` skipped native agent ${name}.toml (missing definition)`);
948
1702
  }
949
1703
  summary.skipped += 1;
950
1704
  continue;
@@ -961,6 +1715,11 @@ async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, opti
961
1715
  await syncManagedContent(toml, dst, summary, backupContext, options, `native agent ${name}.toml`);
962
1716
  }
963
1717
  summary.removed += await cleanupObsoleteNativeAgents(agentsDir, backupContext, options);
1718
+ if (manifest) {
1719
+ const generatedCleanup = await cleanupGeneratedNonInstallableNativeAgents(agentsDir, manifest, backupContext, options);
1720
+ summary.backedUp += generatedCleanup.backedUp;
1721
+ summary.removed += generatedCleanup.removed;
1722
+ }
964
1723
  if (options.force && manifest && existsSync(agentsDir)) {
965
1724
  const installedFiles = await readdir(agentsDir);
966
1725
  for (const file of installedFiles) {
@@ -968,7 +1727,7 @@ async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, opti
968
1727
  continue;
969
1728
  const agentName = file.slice(0, -5);
970
1729
  const agentStatus = agentStatusByName?.get(agentName);
971
- if (isInstallableStatus(agentStatus))
1730
+ if (isNativeAgentInstallableStatus(agentStatus))
972
1731
  continue;
973
1732
  if (!staleCandidateNativeAgentNames.has(agentName) &&
974
1733
  agentStatus === undefined)
@@ -976,6 +1735,9 @@ async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, opti
976
1735
  const staleAgentPath = join(agentsDir, file);
977
1736
  if (!existsSync(staleAgentPath))
978
1737
  continue;
1738
+ if (await ensureBackup(staleAgentPath, true, backupContext, options)) {
1739
+ summary.backedUp += 1;
1740
+ }
979
1741
  if (!options.dryRun) {
980
1742
  await rm(staleAgentPath, { force: true });
981
1743
  }
@@ -1029,13 +1791,13 @@ export async function installSkills(srcDir, dstDir, backupContext, options) {
1029
1791
  const summary = createEmptyCategorySummary();
1030
1792
  if (!existsSync(srcDir))
1031
1793
  return summary;
1794
+ const installableSkillNames = getSetupInstallableSkillNames();
1032
1795
  const installableSkills = [];
1033
1796
  const manifest = tryReadCatalogManifest();
1034
1797
  const skillStatusByName = manifest
1035
1798
  ? new Map(manifest.skills.map((skill) => [skill.name, skill.status]))
1036
1799
  : null;
1037
- const isInstallableStatus = (status) => status === "active" || status === "internal";
1038
- const isSetupInstallableSkill = (skillName, status) => isInstallableStatus(status) || SETUP_ONLY_INSTALLABLE_SKILLS.has(skillName);
1800
+ const isSetupInstallableSkill = (skillName, status) => isCatalogInstallableStatus(status) || installableSkillNames.has(skillName);
1039
1801
  const entries = await readdir(srcDir, { withFileTypes: true });
1040
1802
  const staleCandidateSkillNames = new Set(manifest?.skills.map((skill) => skill.name) ?? []);
1041
1803
  for (const entry of entries) {
@@ -1086,11 +1848,14 @@ export async function installSkills(srcDir, dstDir, backupContext, options) {
1086
1848
  await syncManagedFileFromDisk(sfPath, dstPath, summary, backupContext, options, `skill ${skillName}/${sf}`);
1087
1849
  }
1088
1850
  }
1089
- if (options.force && manifest && existsSync(dstDir)) {
1851
+ if (manifest && existsSync(dstDir)) {
1090
1852
  for (const staleSkill of staleCandidateSkillNames) {
1091
1853
  const status = skillStatusByName?.get(staleSkill);
1092
1854
  if (isSetupInstallableSkill(staleSkill, status))
1093
1855
  continue;
1856
+ const hardDeprecated = HARD_DEPRECATED_SKILL_NAMES.has(staleSkill);
1857
+ if (!options.force && !hardDeprecated)
1858
+ continue;
1094
1859
  const staleSkillDir = join(dstDir, staleSkill);
1095
1860
  if (!existsSync(staleSkillDir))
1096
1861
  continue;
@@ -1103,12 +1868,72 @@ export async function installSkills(srcDir, dstDir, backupContext, options) {
1103
1868
  ? "would remove stale skill"
1104
1869
  : "removed stale skill";
1105
1870
  const label = status ?? "unlisted";
1106
- console.log(` ${prefix} ${staleSkill}/ (status: ${label})`);
1871
+ const reason = hardDeprecated ? ", hard-deprecated" : "";
1872
+ console.log(` ${prefix} ${staleSkill}/ (status: ${label}${reason})`);
1107
1873
  }
1108
1874
  }
1109
1875
  }
1110
1876
  return summary;
1111
1877
  }
1878
+ async function removeDirectoryCopyAware(sourceDir, backupContext, options) {
1879
+ const destinationExists = existsSync(sourceDir);
1880
+ if (!destinationExists)
1881
+ return false;
1882
+ const relativePath = relative(backupContext.baseRoot, sourceDir);
1883
+ const safeRelativePath = relativePath.startsWith("..") || relativePath === ""
1884
+ ? sourceDir.replace(/^[/]+/, "")
1885
+ : relativePath;
1886
+ const backupPath = join(backupContext.backupRoot, safeRelativePath);
1887
+ if (!options.dryRun) {
1888
+ await mkdir(dirname(backupPath), { recursive: true });
1889
+ await cp(sourceDir, backupPath, { recursive: true });
1890
+ }
1891
+ if (options.verbose) {
1892
+ console.log(` backup ${sourceDir} -> ${backupPath}`);
1893
+ }
1894
+ if (!options.dryRun) {
1895
+ await rm(sourceDir, { recursive: true, force: true });
1896
+ }
1897
+ return true;
1898
+ }
1899
+ async function cleanupLegacyManagedSkills(srcDir, dstDir, backupContext, options) {
1900
+ const result = {
1901
+ backedUp: 0,
1902
+ removedSkillNames: [],
1903
+ skippedSkillNames: [],
1904
+ warnings: [],
1905
+ };
1906
+ if (!existsSync(dstDir) || !existsSync(srcDir)) {
1907
+ return result;
1908
+ }
1909
+ const manifest = tryReadCatalogManifest();
1910
+ const installableSkillNames = getSetupInstallableSkillNames(manifest);
1911
+ for (const skillName of installableSkillNames) {
1912
+ const shippedSkillDir = join(srcDir, skillName);
1913
+ const installedSkillDir = join(dstDir, skillName);
1914
+ const shippedSkillMd = join(shippedSkillDir, "SKILL.md");
1915
+ const installedSkillMd = join(installedSkillDir, "SKILL.md");
1916
+ if (!existsSync(shippedSkillMd) || !existsSync(installedSkillMd))
1917
+ continue;
1918
+ const [shippedSkillContent, installedSkillContent] = await Promise.all([
1919
+ readFile(shippedSkillMd, "utf-8"),
1920
+ readFile(installedSkillMd, "utf-8"),
1921
+ ]);
1922
+ const expectedInstalledContent = rewriteInstalledSkillDescriptionBadge(shippedSkillContent, shippedSkillMd);
1923
+ if (installedSkillContent !== expectedInstalledContent) {
1924
+ const warning = `Skipping legacy skill cleanup for ${skillName}: installed SKILL.md differs from OMX-managed content.`;
1925
+ result.skippedSkillNames.push(skillName);
1926
+ result.warnings.push(warning);
1927
+ continue;
1928
+ }
1929
+ const removed = await removeDirectoryCopyAware(installedSkillDir, backupContext, options);
1930
+ if (removed) {
1931
+ result.backedUp += 1;
1932
+ result.removedSkillNames.push(skillName);
1933
+ }
1934
+ }
1935
+ return result;
1936
+ }
1112
1937
  async function updateManagedConfig(configPath, pkgRoot, sharedMcpRegistry, summary, backupContext, options) {
1113
1938
  const existing = existsSync(configPath)
1114
1939
  ? await readFile(configPath, "utf-8")
@@ -1116,8 +1941,7 @@ async function updateManagedConfig(configPath, pkgRoot, sharedMcpRegistry, summa
1116
1941
  const hadLegacyTeamRunTable = hasLegacyOmxTeamRunTable(existing);
1117
1942
  const currentModel = getRootModelName(existing);
1118
1943
  let modelOverride;
1119
- const codexVersion = options.codexVersionProbe?.() ?? probeInstalledCodexVersion();
1120
- const omxManagesTui = shouldOmxManageTuiFromCodexVersion(codexVersion);
1944
+ const omxManagesTui = true;
1121
1945
  if (currentModel === LEGACY_SETUP_MODEL) {
1122
1946
  const shouldPrompt = typeof options.modelUpgradePrompt === "function" ||
1123
1947
  (process.stdin.isTTY && process.stdout.isTTY);
@@ -1136,6 +1960,8 @@ async function updateManagedConfig(configPath, pkgRoot, sharedMcpRegistry, summa
1136
1960
  sharedMcpServers: sharedMcpRegistry.servers,
1137
1961
  sharedMcpRegistrySource: sharedMcpRegistry.sourcePath,
1138
1962
  verbose: options.verbose,
1963
+ statusLinePreset: options.statusLinePreset,
1964
+ forceStatusLinePreset: options.forceStatusLinePreset,
1139
1965
  });
1140
1966
  const changed = existing !== finalConfig;
1141
1967
  if (!changed) {