oh-my-codex 0.16.3 → 0.16.4

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 (278) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +3 -3
  4. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +9 -0
  5. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
  6. package/dist/cli/__tests__/cleanup.test.js +27 -0
  7. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  8. package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -5
  9. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  10. package/dist/cli/__tests__/doctor-warning-copy.test.js +101 -6
  11. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  12. package/dist/cli/__tests__/index.test.js +131 -2
  13. package/dist/cli/__tests__/index.test.js.map +1 -1
  14. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +2 -0
  15. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  16. package/dist/cli/__tests__/ralph.test.js +47 -0
  17. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  18. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +2 -2
  19. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  20. package/dist/cli/__tests__/setup-install-mode.test.js +272 -26
  21. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  22. package/dist/cli/__tests__/setup-refresh.test.js +85 -3
  23. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  24. package/dist/cli/__tests__/setup-scope.test.js +1 -1
  25. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  26. package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -1
  27. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  28. package/dist/cli/__tests__/team.test.js +108 -0
  29. package/dist/cli/__tests__/team.test.js.map +1 -1
  30. package/dist/cli/__tests__/ultragoal.test.js +69 -0
  31. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  32. package/dist/cli/__tests__/uninstall.test.js +54 -8
  33. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  34. package/dist/cli/cleanup.d.ts.map +1 -1
  35. package/dist/cli/cleanup.js +8 -4
  36. package/dist/cli/cleanup.js.map +1 -1
  37. package/dist/cli/codex-feature-probe.d.ts +9 -0
  38. package/dist/cli/codex-feature-probe.d.ts.map +1 -0
  39. package/dist/cli/codex-feature-probe.js +28 -0
  40. package/dist/cli/codex-feature-probe.js.map +1 -0
  41. package/dist/cli/doctor.d.ts +1 -0
  42. package/dist/cli/doctor.d.ts.map +1 -1
  43. package/dist/cli/doctor.js +152 -17
  44. package/dist/cli/doctor.js.map +1 -1
  45. package/dist/cli/index.d.ts +9 -2
  46. package/dist/cli/index.d.ts.map +1 -1
  47. package/dist/cli/index.js +135 -17
  48. package/dist/cli/index.js.map +1 -1
  49. package/dist/cli/mcp-parity.js +8 -8
  50. package/dist/cli/mcp-parity.js.map +1 -1
  51. package/dist/cli/plugin-marketplace.d.ts +3 -0
  52. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  53. package/dist/cli/plugin-marketplace.js +88 -0
  54. package/dist/cli/plugin-marketplace.js.map +1 -1
  55. package/dist/cli/ralph.d.ts.map +1 -1
  56. package/dist/cli/ralph.js +21 -0
  57. package/dist/cli/ralph.js.map +1 -1
  58. package/dist/cli/setup-preferences.d.ts +4 -0
  59. package/dist/cli/setup-preferences.d.ts.map +1 -1
  60. package/dist/cli/setup-preferences.js +7 -0
  61. package/dist/cli/setup-preferences.js.map +1 -1
  62. package/dist/cli/setup.d.ts +5 -3
  63. package/dist/cli/setup.d.ts.map +1 -1
  64. package/dist/cli/setup.js +114 -44
  65. package/dist/cli/setup.js.map +1 -1
  66. package/dist/cli/ultragoal.d.ts +1 -1
  67. package/dist/cli/ultragoal.d.ts.map +1 -1
  68. package/dist/cli/ultragoal.js +64 -5
  69. package/dist/cli/ultragoal.js.map +1 -1
  70. package/dist/cli/uninstall.d.ts +2 -0
  71. package/dist/cli/uninstall.d.ts.map +1 -1
  72. package/dist/cli/uninstall.js +12 -3
  73. package/dist/cli/uninstall.js.map +1 -1
  74. package/dist/config/__tests__/codex-feature-flags.test.d.ts +2 -0
  75. package/dist/config/__tests__/codex-feature-flags.test.d.ts.map +1 -0
  76. package/dist/config/__tests__/codex-feature-flags.test.js +35 -0
  77. package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -0
  78. package/dist/config/__tests__/codex-hooks.test.js +7 -0
  79. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  80. package/dist/config/__tests__/generator-idempotent.test.js +70 -9
  81. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  82. package/dist/config/__tests__/generator-notify.test.js +116 -11
  83. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  84. package/dist/config/__tests__/wiki-config-contract.test.js +6 -3
  85. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -1
  86. package/dist/config/codex-feature-flags.d.ts +21 -0
  87. package/dist/config/codex-feature-flags.d.ts.map +1 -0
  88. package/dist/config/codex-feature-flags.js +56 -0
  89. package/dist/config/codex-feature-flags.js.map +1 -0
  90. package/dist/config/codex-hooks.d.ts +2 -0
  91. package/dist/config/codex-hooks.d.ts.map +1 -1
  92. package/dist/config/codex-hooks.js +25 -3
  93. package/dist/config/codex-hooks.js.map +1 -1
  94. package/dist/config/generator.d.ts +11 -2
  95. package/dist/config/generator.d.ts.map +1 -1
  96. package/dist/config/generator.js +221 -123
  97. package/dist/config/generator.js.map +1 -1
  98. package/dist/config/omx-first-party-mcp.d.ts +3 -1
  99. package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
  100. package/dist/config/omx-first-party-mcp.js +2 -2
  101. package/dist/config/omx-first-party-mcp.js.map +1 -1
  102. package/dist/hooks/__tests__/keyword-detector.test.js +92 -2
  103. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  104. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +125 -1
  105. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  106. package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts +2 -0
  107. package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts.map +1 -0
  108. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js +84 -0
  109. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -0
  110. package/dist/hooks/agents-overlay.js +2 -2
  111. package/dist/hooks/agents-overlay.js.map +1 -1
  112. package/dist/hooks/keyword-detector.d.ts +1 -0
  113. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  114. package/dist/hooks/keyword-detector.js +7 -5
  115. package/dist/hooks/keyword-detector.js.map +1 -1
  116. package/dist/hud/__tests__/state.test.js +164 -0
  117. package/dist/hud/__tests__/state.test.js.map +1 -1
  118. package/dist/hud/state.d.ts.map +1 -1
  119. package/dist/hud/state.js +4 -5
  120. package/dist/hud/state.js.map +1 -1
  121. package/dist/mcp/__tests__/state-paths.test.js +61 -0
  122. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  123. package/dist/mcp/__tests__/state-server.test.js +166 -0
  124. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  125. package/dist/mcp/state-paths.d.ts.map +1 -1
  126. package/dist/mcp/state-paths.js +23 -2
  127. package/dist/mcp/state-paths.js.map +1 -1
  128. package/dist/modes/__tests__/base-session-scope.test.js +22 -0
  129. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  130. package/dist/modes/__tests__/base-tmux-pane.test.js +57 -26
  131. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  132. package/dist/modes/base.d.ts.map +1 -1
  133. package/dist/modes/base.js +5 -0
  134. package/dist/modes/base.js.map +1 -1
  135. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts +2 -0
  136. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts.map +1 -0
  137. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js +316 -0
  138. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -0
  139. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts +2 -0
  140. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts.map +1 -0
  141. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js +481 -0
  142. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js.map +1 -0
  143. package/dist/planning/__tests__/artifacts.test.js +533 -4
  144. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  145. package/dist/planning/__tests__/context-pack-status.test.js +524 -0
  146. package/dist/planning/__tests__/context-pack-status.test.js.map +1 -1
  147. package/dist/planning/__tests__/markdown-structure.test.d.ts +2 -0
  148. package/dist/planning/__tests__/markdown-structure.test.d.ts.map +1 -0
  149. package/dist/planning/__tests__/markdown-structure.test.js +459 -0
  150. package/dist/planning/__tests__/markdown-structure.test.js.map +1 -0
  151. package/dist/planning/__tests__/ready-context-pack-role-refs.test.js +523 -1
  152. package/dist/planning/__tests__/ready-context-pack-role-refs.test.js.map +1 -1
  153. package/dist/planning/artifacts.d.ts +1 -1
  154. package/dist/planning/artifacts.d.ts.map +1 -1
  155. package/dist/planning/artifacts.js +227 -28
  156. package/dist/planning/artifacts.js.map +1 -1
  157. package/dist/planning/context-pack-status.d.ts +25 -0
  158. package/dist/planning/context-pack-status.d.ts.map +1 -1
  159. package/dist/planning/context-pack-status.js +272 -31
  160. package/dist/planning/context-pack-status.js.map +1 -1
  161. package/dist/planning/markdown-structure.d.ts +20 -0
  162. package/dist/planning/markdown-structure.d.ts.map +1 -0
  163. package/dist/planning/markdown-structure.js +137 -0
  164. package/dist/planning/markdown-structure.js.map +1 -0
  165. package/dist/ralph/__tests__/completion-audit.test.d.ts +2 -0
  166. package/dist/ralph/__tests__/completion-audit.test.d.ts.map +1 -0
  167. package/dist/ralph/__tests__/completion-audit.test.js +121 -0
  168. package/dist/ralph/__tests__/completion-audit.test.js.map +1 -0
  169. package/dist/ralph/completion-audit.d.ts +8 -0
  170. package/dist/ralph/completion-audit.d.ts.map +1 -0
  171. package/dist/ralph/completion-audit.js +99 -0
  172. package/dist/ralph/completion-audit.js.map +1 -0
  173. package/dist/scripts/__tests__/codex-native-hook.test.js +220 -13
  174. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  175. package/dist/scripts/__tests__/notify-dispatcher.test.d.ts +2 -0
  176. package/dist/scripts/__tests__/notify-dispatcher.test.d.ts.map +1 -0
  177. package/dist/scripts/__tests__/notify-dispatcher.test.js +126 -0
  178. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -0
  179. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  180. package/dist/scripts/codex-native-hook.js +133 -54
  181. package/dist/scripts/codex-native-hook.js.map +1 -1
  182. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  183. package/dist/scripts/codex-native-pre-post.js +4 -2
  184. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  185. package/dist/scripts/notify-dispatcher.js +30 -1
  186. package/dist/scripts/notify-dispatcher.js.map +1 -1
  187. package/dist/scripts/notify-hook.js +3 -1
  188. package/dist/scripts/notify-hook.js.map +1 -1
  189. package/dist/state/__tests__/workflow-transition.test.js +102 -27
  190. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  191. package/dist/state/operations.d.ts.map +1 -1
  192. package/dist/state/operations.js +9 -3
  193. package/dist/state/operations.js.map +1 -1
  194. package/dist/state/skill-active.d.ts +7 -0
  195. package/dist/state/skill-active.d.ts.map +1 -1
  196. package/dist/state/skill-active.js +25 -8
  197. package/dist/state/skill-active.js.map +1 -1
  198. package/dist/state/workflow-transition-reconcile.d.ts +1 -0
  199. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  200. package/dist/state/workflow-transition-reconcile.js +22 -15
  201. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  202. package/dist/state/workflow-transition.js +3 -3
  203. package/dist/state/workflow-transition.js.map +1 -1
  204. package/dist/team/__tests__/approved-execution.test.js +39 -0
  205. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  206. package/dist/team/__tests__/runtime.test.js +5 -0
  207. package/dist/team/__tests__/runtime.test.js.map +1 -1
  208. package/dist/team/__tests__/scaling.test.js +497 -2
  209. package/dist/team/__tests__/scaling.test.js.map +1 -1
  210. package/dist/team/__tests__/state-root.test.js +1 -1
  211. package/dist/team/__tests__/state-root.test.js.map +1 -1
  212. package/dist/team/__tests__/worker-bootstrap.test.js +8 -0
  213. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  214. package/dist/team/approved-execution.d.ts.map +1 -1
  215. package/dist/team/approved-execution.js +3 -0
  216. package/dist/team/approved-execution.js.map +1 -1
  217. package/dist/team/scaling.d.ts.map +1 -1
  218. package/dist/team/scaling.js +43 -0
  219. package/dist/team/scaling.js.map +1 -1
  220. package/dist/team/state-root.d.ts.map +1 -1
  221. package/dist/team/state-root.js +4 -0
  222. package/dist/team/state-root.js.map +1 -1
  223. package/dist/team/state.d.ts.map +1 -1
  224. package/dist/team/state.js +2 -6
  225. package/dist/team/state.js.map +1 -1
  226. package/dist/ultragoal/__tests__/artifacts.test.js +124 -1
  227. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  228. package/dist/ultragoal/__tests__/docs-contract.test.js +21 -0
  229. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  230. package/dist/ultragoal/artifacts.d.ts +44 -2
  231. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  232. package/dist/ultragoal/artifacts.js +197 -13
  233. package/dist/ultragoal/artifacts.js.map +1 -1
  234. package/dist/wiki/lifecycle.js +1 -1
  235. package/dist/wiki/lifecycle.js.map +1 -1
  236. package/package.json +1 -1
  237. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  238. package/plugins/oh-my-codex/.mcp.json +5 -5
  239. package/plugins/oh-my-codex/skills/analyze/SKILL.md +0 -2
  240. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
  241. package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -3
  242. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +5 -7
  243. package/plugins/oh-my-codex/skills/doctor/SKILL.md +2 -2
  244. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +3 -3
  245. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +3 -3
  246. package/plugins/oh-my-codex/skills/plan/SKILL.md +3 -6
  247. package/plugins/oh-my-codex/skills/ralph/SKILL.md +9 -10
  248. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +36 -3
  249. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +21 -24
  250. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +8 -8
  251. package/plugins/oh-my-codex/skills/wiki/SKILL.md +13 -13
  252. package/skills/analyze/SKILL.md +0 -2
  253. package/skills/ask-claude/SKILL.md +5 -3
  254. package/skills/ask-gemini/SKILL.md +5 -3
  255. package/skills/autopilot/SKILL.md +2 -2
  256. package/skills/code-review/SKILL.md +1 -3
  257. package/skills/deep-interview/SKILL.md +5 -7
  258. package/skills/doctor/SKILL.md +2 -2
  259. package/skills/ecomode/SKILL.md +105 -1
  260. package/skills/frontend-ui-ux/SKILL.md +4 -26
  261. package/skills/git-master/SKILL.md +2 -4
  262. package/skills/omx-setup/SKILL.md +3 -3
  263. package/skills/pipeline/SKILL.md +3 -3
  264. package/skills/plan/SKILL.md +3 -6
  265. package/skills/ralph/SKILL.md +9 -10
  266. package/skills/swarm/SKILL.md +5 -3
  267. package/skills/tdd/SKILL.md +95 -1
  268. package/skills/ultragoal/SKILL.md +36 -3
  269. package/skills/ultraqa/SKILL.md +21 -24
  270. package/skills/ultrawork/SKILL.md +8 -8
  271. package/skills/web-clone/SKILL.md +348 -1
  272. package/skills/wiki/SKILL.md +13 -13
  273. package/src/scripts/__tests__/codex-native-hook.test.ts +231 -13
  274. package/src/scripts/__tests__/notify-dispatcher.test.ts +153 -0
  275. package/src/scripts/codex-native-hook.ts +160 -43
  276. package/src/scripts/codex-native-pre-post.ts +4 -1
  277. package/src/scripts/notify-dispatcher.ts +40 -1
  278. package/src/scripts/notify-hook.ts +3 -1
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
3
3
  import { existsSync } from "node:fs";
4
4
  import { cp, mkdir, mkdtemp, readFile, readdir, rm, writeFile, } from "node:fs/promises";
5
5
  import { tmpdir } from "node:os";
6
- import { join } from "node:path";
6
+ import { dirname, join } from "node:path";
7
7
  import { parse as parseToml } from "@iarna/toml";
8
8
  import { setup } from "../setup.js";
9
9
  import { uninstall } from "../uninstall.js";
@@ -128,14 +128,71 @@ describe("notify setup scope", () => {
128
128
  await rm(wd, { recursive: true, force: true });
129
129
  }
130
130
  });
131
+ it("does not preserve stale OMX dispatcher metadata as previous notify", async () => {
132
+ const wd = await mkdtemp(join(tmpdir(), "omx-stale-dispatcher-notify-"));
133
+ try {
134
+ await withIsolatedUserHome(wd, async (codexHomeDir) => {
135
+ await mkdir(codexHomeDir, { recursive: true });
136
+ const metadataPath = join(codexHomeDir, ".omx", "notify-dispatch.json");
137
+ const stalePkgRoot = join(wd, "old-global", "oh-my-codex");
138
+ const staleDispatcher = join(stalePkgRoot, "dist", "scripts", "notify-dispatcher.js");
139
+ await mkdir(dirname(metadataPath), { recursive: true });
140
+ await writeFile(join(codexHomeDir, "config.toml"), `notify = ["node", "${staleDispatcher}", "--metadata", "${metadataPath}"]\napproval_policy = "on-failure"\n`);
141
+ await writeFile(metadataPath, JSON.stringify({
142
+ managedBy: "oh-my-codex",
143
+ version: 1,
144
+ previousNotify: [
145
+ "node",
146
+ staleDispatcher,
147
+ "--metadata",
148
+ metadataPath,
149
+ ],
150
+ omxNotify: [
151
+ "node",
152
+ join(stalePkgRoot, "dist", "scripts", "notify-hook.js"),
153
+ ],
154
+ dispatcherNotify: ["node", staleDispatcher, "--metadata", metadataPath],
155
+ }));
156
+ await withTempCwd(wd, async () => {
157
+ await setup({ scope: "user" });
158
+ });
159
+ const config = await readFile(join(codexHomeDir, "config.toml"), "utf-8");
160
+ assert.match(config, /^notify = \["node", ".*notify-hook\.js"\]$/m);
161
+ assert.doesNotMatch(config, /notify-dispatcher\.js/);
162
+ });
163
+ }
164
+ finally {
165
+ await rm(wd, { recursive: true, force: true });
166
+ }
167
+ });
168
+ it("does not wrap stale global OMX notify hooks as user notify commands", async () => {
169
+ const wd = await mkdtemp(join(tmpdir(), "omx-stale-hook-notify-"));
170
+ try {
171
+ await withIsolatedUserHome(wd, async (codexHomeDir) => {
172
+ await mkdir(codexHomeDir, { recursive: true });
173
+ const staleHook = join("/opt", "homebrew", "lib", "node_modules", "oh-my-codex", "dist", "scripts", "notify-hook.js");
174
+ await writeFile(join(codexHomeDir, "config.toml"), `notify = ["node", "${staleHook}"]\napproval_policy = "on-failure"\n`);
175
+ await withTempCwd(wd, async () => {
176
+ await setup({ scope: "user" });
177
+ });
178
+ const config = await readFile(join(codexHomeDir, "config.toml"), "utf-8");
179
+ assert.match(config, /^notify = \["node", ".*notify-hook\.js"\]$/m);
180
+ assert.doesNotMatch(config, /notify-dispatcher\.js/);
181
+ assert.doesNotMatch(config, /lib\/node_modules\/oh-my-codex\/dist\/scripts\/notify-hook\.js/);
182
+ });
183
+ }
184
+ finally {
185
+ await rm(wd, { recursive: true, force: true });
186
+ }
187
+ });
131
188
  });
132
189
  async function assertProjectPluginModeArtifacts(wd) {
133
190
  const hooks = await readFile(join(wd, ".codex", "hooks.json"), "utf-8");
134
191
  assert.match(hooks, /codex-native-hook\.js/);
135
192
  const config = await readFile(join(wd, ".codex", "config.toml"), "utf-8");
136
- assert.match(config, /^codex_hooks = true$/m);
193
+ assert.match(config, /^hooks = true$/m);
137
194
  assert.match(config, /^goals = true$/m);
138
- assert.doesNotMatch(config, /developer_instructions|notify-hook|mcp_servers/);
195
+ assert.doesNotMatch(config, /developer_instructions|notify-hook/g);
139
196
  assert.equal(existsSync(join(wd, ".codex", "skills", "ask", "SKILL.md")), false);
140
197
  assert.equal(existsSync(join(wd, ".codex", "agents", "planner.toml")), false);
141
198
  assert.equal(existsSync(join(wd, ".codex", "prompts", "executor.md")), false);
@@ -144,6 +201,7 @@ async function assertProjectPluginModeArtifacts(wd) {
144
201
  assert.deepEqual(persisted, {
145
202
  scope: "project",
146
203
  installMode: "plugin",
204
+ mcpMode: "none",
147
205
  });
148
206
  }
149
207
  async function captureConsoleOutput(fn) {
@@ -208,7 +266,7 @@ describe("omx setup install mode behavior", () => {
208
266
  },
209
267
  });
210
268
  });
211
- assert.match(output, /Setup preference review: keep \(scope=user, installMode=legacy\)/);
269
+ assert.match(output, /Setup preference review: keep \(scope=user, installMode=legacy, mcpMode=not recorded\)/);
212
270
  assert.match(output, /Using setup scope: user \(from \.omx\/setup-scope\.json\)/);
213
271
  assert.match(output, /Using setup install mode: legacy \(from \.omx\/setup-scope\.json\)/);
214
272
  });
@@ -237,7 +295,11 @@ describe("omx setup install mode behavior", () => {
237
295
  },
238
296
  });
239
297
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
240
- assert.deepEqual(persisted, { scope: "user", installMode: "plugin" });
298
+ assert.deepEqual(persisted, {
299
+ scope: "user",
300
+ installMode: "plugin",
301
+ mcpMode: "none",
302
+ });
241
303
  });
242
304
  });
243
305
  }
@@ -260,7 +322,7 @@ describe("omx setup install mode behavior", () => {
260
322
  },
261
323
  });
262
324
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
263
- assert.deepEqual(persisted, { scope: "project" });
325
+ assert.deepEqual(persisted, { scope: "project", mcpMode: "none" });
264
326
  });
265
327
  });
266
328
  }
@@ -289,7 +351,11 @@ describe("omx setup install mode behavior", () => {
289
351
  });
290
352
  assert.equal(reviewed, true);
291
353
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
292
- assert.deepEqual(persisted, { scope: "user", installMode: "plugin" });
354
+ assert.deepEqual(persisted, {
355
+ scope: "user",
356
+ installMode: "plugin",
357
+ mcpMode: "none",
358
+ });
293
359
  });
294
360
  });
295
361
  }
@@ -318,7 +384,11 @@ describe("omx setup install mode behavior", () => {
318
384
  });
319
385
  assert.equal(reviewed, true);
320
386
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
321
- assert.deepEqual(persisted, { scope: "user", installMode: "plugin" });
387
+ assert.deepEqual(persisted, {
388
+ scope: "user",
389
+ installMode: "plugin",
390
+ mcpMode: "none",
391
+ });
322
392
  });
323
393
  });
324
394
  }
@@ -345,7 +415,11 @@ describe("omx setup install mode behavior", () => {
345
415
  },
346
416
  });
347
417
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
348
- assert.deepEqual(persisted, { scope: "user", installMode: "legacy" });
418
+ assert.deepEqual(persisted, {
419
+ scope: "user",
420
+ installMode: "legacy",
421
+ mcpMode: "none",
422
+ });
349
423
  });
350
424
  });
351
425
  }
@@ -395,7 +469,43 @@ describe("omx setup install mode behavior", () => {
395
469
  });
396
470
  });
397
471
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
398
- assert.deepEqual(persisted, { scope: "user", installMode: "plugin" });
472
+ assert.deepEqual(persisted, {
473
+ scope: "user",
474
+ installMode: "plugin",
475
+ mcpMode: "none",
476
+ });
477
+ }
478
+ finally {
479
+ await rm(wd, { recursive: true, force: true });
480
+ }
481
+ });
482
+ it("defaults setup to no first-party MCP blocks", async () => {
483
+ const wd = await mkdtemp(join(tmpdir(), "omx-setup-mcp-mode-"));
484
+ try {
485
+ await withIsolatedUserHome(wd, async (codexHomeDir) => {
486
+ await withTempCwd(wd, async () => {
487
+ await setup({ scope: "user", installMode: "legacy" });
488
+ });
489
+ const config = await readFile(join(codexHomeDir, "config.toml"), "utf-8");
490
+ assert.doesNotMatch(config, /^\[mcp_servers\.omx_state\]$/m);
491
+ });
492
+ }
493
+ finally {
494
+ await rm(wd, { recursive: true, force: true });
495
+ }
496
+ });
497
+ it("emits first-party MCP blocks when compat MCP mode is requested", async () => {
498
+ const wd = await mkdtemp(join(tmpdir(), "omx-setup-mcp-mode-"));
499
+ try {
500
+ await withIsolatedUserHome(wd, async (codexHomeDir) => {
501
+ await withTempCwd(wd, async () => {
502
+ await setup({ scope: "user", installMode: "legacy", mcpMode: "compat" });
503
+ });
504
+ const config = await readFile(join(codexHomeDir, "config.toml"), "utf-8");
505
+ assert.match(config, /^\[mcp_servers\.omx_state\]$/m);
506
+ const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
507
+ assert.equal(persisted.mcpMode, "compat");
508
+ });
399
509
  }
400
510
  finally {
401
511
  await rm(wd, { recursive: true, force: true });
@@ -411,7 +521,11 @@ describe("omx setup install mode behavior", () => {
411
521
  await writeFile(join(pluginDir, ".codex-plugin", "plugin.json"), JSON.stringify({ name: "oh-my-codex", version: "local" }));
412
522
  await setup({ scope: "user" });
413
523
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
414
- assert.deepEqual(persisted, { scope: "user", installMode: "plugin" });
524
+ assert.deepEqual(persisted, {
525
+ scope: "user",
526
+ installMode: "plugin",
527
+ mcpMode: "none",
528
+ });
415
529
  assert.equal(existsSync(join(codexHomeDir, "skills", "ask", "SKILL.md")), false);
416
530
  const hooks = await readFile(join(codexHomeDir, "hooks.json"), "utf-8");
417
531
  assert.match(hooks, /codex-native-hook\.js/);
@@ -474,7 +588,7 @@ describe("omx setup install mode behavior", () => {
474
588
  });
475
589
  assert.equal(promptCalls, 0);
476
590
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
477
- assert.deepEqual(persisted, { scope: "project" });
591
+ assert.deepEqual(persisted, { scope: "project", mcpMode: "none" });
478
592
  }
479
593
  finally {
480
594
  await rm(wd, { recursive: true, force: true });
@@ -488,11 +602,11 @@ describe("omx setup install mode behavior", () => {
488
602
  await setup({ scope: "user", installMode: "plugin" });
489
603
  await setup({ scope: "project" });
490
604
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
491
- assert.deepEqual(persisted, { scope: "project" });
605
+ assert.deepEqual(persisted, { scope: "project", mcpMode: "none" });
492
606
  assert.equal(existsSync(join(wd, ".codex", "skills", "ask", "SKILL.md")), true);
493
607
  await setup({ scope: "project" });
494
608
  const repeatedPersisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
495
- assert.deepEqual(repeatedPersisted, { scope: "project" });
609
+ assert.deepEqual(repeatedPersisted, { scope: "project", mcpMode: "none" });
496
610
  assert.equal(existsSync(join(wd, ".codex", "agents", "planner.toml")), true);
497
611
  assert.equal(existsSync(join(wd, ".codex", "prompts", "executor.md")), true);
498
612
  });
@@ -510,7 +624,11 @@ describe("omx setup install mode behavior", () => {
510
624
  await setup({ scope: "project", installMode: "plugin" });
511
625
  await setup({ scope: "user" });
512
626
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
513
- assert.deepEqual(persisted, { scope: "user", installMode: "legacy" });
627
+ assert.deepEqual(persisted, {
628
+ scope: "user",
629
+ installMode: "legacy",
630
+ mcpMode: "none",
631
+ });
514
632
  assert.equal(existsSync(join(codexHomeDir, "skills", "ask", "SKILL.md")), true);
515
633
  assert.equal(existsSync(join(codexHomeDir, "agents", "planner.toml")), true);
516
634
  assert.equal(existsSync(join(codexHomeDir, "prompts", "executor.md")), true);
@@ -549,7 +667,11 @@ describe("omx setup install mode behavior", () => {
549
667
  assert.equal(parsed.marketplaces?.other?.source, "/tmp/other");
550
668
  assert.equal((config.match(/^\[marketplaces\.oh-my-codex-local\]$/gm) ?? [])
551
669
  .length, 1);
552
- assert.match(config, /^codex_hooks = true$/m);
670
+ assert.equal((config.match(/^\[plugins\."oh-my-codex@oh-my-codex-local"\]$/gm) ?? [])
671
+ .length, 1);
672
+ assert.equal(parsed.plugins?.["oh-my-codex@oh-my-codex-local"]?.enabled, true);
673
+ assert.match(config, /^hooks = true$/m);
674
+ assert.doesNotMatch(config, /^codex_hooks = true$/m);
553
675
  assert.doesNotMatch(config, /\[mcp_servers\./);
554
676
  assert.equal(existsSync(join(codexHomeDir, "skills", "ask", "SKILL.md")), false);
555
677
  assert.equal(existsSync(join(codexHomeDir, "agents", "planner.toml")), false);
@@ -561,6 +683,67 @@ describe("omx setup install mode behavior", () => {
561
683
  await rm(wd, { recursive: true, force: true });
562
684
  }
563
685
  });
686
+ it("enables the local Codex plugin while preserving plugin subtable policy", async () => {
687
+ const wd = await mkdtemp(join(tmpdir(), "omx-setup-install-mode-"));
688
+ try {
689
+ await withIsolatedUserHome(wd, async (codexHomeDir) => {
690
+ await withTempCwd(wd, async () => {
691
+ const configPath = join(codexHomeDir, "config.toml");
692
+ await writeFile(configPath, [
693
+ "[plugins.\"oh-my-codex@oh-my-codex-local\"]",
694
+ "enabled = false",
695
+ "",
696
+ "[plugins.\"oh-my-codex@oh-my-codex-local\".mcp_servers.omx_state]",
697
+ "enabled = false",
698
+ "",
699
+ ].join("\n"));
700
+ await setup({ scope: "user", installMode: "plugin", force: true });
701
+ await setup({ scope: "user", installMode: "plugin", force: true });
702
+ const config = await readFile(configPath, "utf-8");
703
+ const parsed = parseToml(config);
704
+ assert.equal((config.match(/^\[plugins\."oh-my-codex@oh-my-codex-local"\]$/gm) ?? [])
705
+ .length, 1);
706
+ assert.equal(parsed.plugins?.["oh-my-codex@oh-my-codex-local"]?.enabled, true);
707
+ assert.equal(parsed.plugins?.["oh-my-codex@oh-my-codex-local"]?.mcp_servers
708
+ ?.omx_state?.enabled, false);
709
+ });
710
+ });
711
+ }
712
+ finally {
713
+ await rm(wd, { recursive: true, force: true });
714
+ }
715
+ });
716
+ it("enables plugin MCP subtables only when compat MCP mode is requested", async () => {
717
+ const wd = await mkdtemp(join(tmpdir(), "omx-setup-install-mode-"));
718
+ try {
719
+ await withIsolatedUserHome(wd, async (codexHomeDir) => {
720
+ await withTempCwd(wd, async () => {
721
+ const configPath = join(codexHomeDir, "config.toml");
722
+ await setup({
723
+ scope: "user",
724
+ installMode: "plugin",
725
+ mcpMode: "compat",
726
+ force: true,
727
+ });
728
+ let parsed = parseToml(await readFile(configPath, "utf-8"));
729
+ assert.equal(parsed.plugins?.["oh-my-codex@oh-my-codex-local"]?.mcp_servers
730
+ ?.omx_state?.enabled, true);
731
+ await setup({
732
+ scope: "user",
733
+ installMode: "plugin",
734
+ mcpMode: "none",
735
+ force: true,
736
+ });
737
+ parsed = parseToml(await readFile(configPath, "utf-8"));
738
+ assert.equal(parsed.plugins?.["oh-my-codex@oh-my-codex-local"]?.mcp_servers
739
+ ?.omx_state?.enabled, false);
740
+ });
741
+ });
742
+ }
743
+ finally {
744
+ await rm(wd, { recursive: true, force: true });
745
+ }
746
+ });
564
747
  it("reports plugin marketplace registration during dry-run without mutating config", async () => {
565
748
  const wd = await mkdtemp(join(tmpdir(), "omx-setup-install-mode-"));
566
749
  try {
@@ -593,13 +776,13 @@ describe("omx setup install mode behavior", () => {
593
776
  const parsed = parseToml(config);
594
777
  const managedHookStateKeys = Object.keys(parsed.hooks?.state ?? {}).filter((key) => key.includes(`${codexHomeDir}/hooks.json:`));
595
778
  assert.equal(managedHookStateKeys.length, 7);
596
- assert.match(config, /^codex_hooks = true$/m);
597
- assert.doesNotMatch(config, /^hooks = true$/m);
779
+ assert.match(config, /^hooks = true$/m);
780
+ assert.doesNotMatch(config, /^codex_hooks = true$/m);
598
781
  assert.match(config, /^goals = true$/m);
599
782
  assert.match(config, /^\[hooks\.state\.".*hooks\.json:pre_tool_use:0:0"\]$/m);
600
783
  assert.equal((config.match(/^\[hooks\.state\.".*hooks\.json:pre_tool_use:0:0"\]$/gm) ?? []).length, 1);
601
784
  assert.match(config, /^trusted_hash = "sha256:[a-f0-9]{64}"$/m);
602
- assert.doesNotMatch(config, /developer_instructions|notify-hook|mcp_servers/);
785
+ assert.doesNotMatch(config, /developer_instructions|notify-hook/g);
603
786
  assert.equal(existsSync(join(codexHomeDir, "skills", "ask", "SKILL.md")), false);
604
787
  assert.equal(existsSync(join(codexHomeDir, "agents", "planner.toml")), false);
605
788
  assert.equal(existsSync(join(codexHomeDir, "prompts", "executor.md")), false);
@@ -634,8 +817,10 @@ describe("omx setup install mode behavior", () => {
634
817
  assert.match(config, /Setup-owned prompt files and native-agent TOML defaults are intentionally omitted unless explicitly installed/);
635
818
  assert.doesNotMatch(config, /Native subagents live in \.codex\/agents/);
636
819
  assert.doesNotMatch(config, /Treat installed prompts as narrower execution surfaces/);
637
- assert.match(config, /^codex_hooks = true$/m);
638
- assert.doesNotMatch(config, /notify-hook|mcp_servers/);
820
+ assert.match(config, /^hooks = true$/m);
821
+ assert.doesNotMatch(config, /notify-hook/);
822
+ assert.doesNotMatch(config, /^\s*\[mcp_servers[.\]]/m);
823
+ assert.match(config, /mcp_servers\.omx_state]\nenabled = false/);
639
824
  const agentsMd = await readFile(join(codexHomeDir, "AGENTS.md"), "utf-8");
640
825
  assert.match(agentsMd, /oh-my-codex - Intelligent Multi-Agent Orchestration/);
641
826
  assert.match(agentsMd, /<!-- omx:generated:agents-md -->/);
@@ -695,7 +880,7 @@ describe("omx setup install mode behavior", () => {
695
880
  });
696
881
  const config = await readFile(configPath, "utf-8");
697
882
  assert.match(config, /^developer_instructions = "custom"$/m);
698
- assert.match(config, /^codex_hooks = true$/m);
883
+ assert.match(config, /^hooks = true$/m);
699
884
  assert.equal((config.match(/^developer_instructions\s*=/gm) ?? []).length, 1);
700
885
  });
701
886
  });
@@ -721,7 +906,7 @@ describe("omx setup install mode behavior", () => {
721
906
  assert.match(config, /You have oh-my-codex installed/);
722
907
  assert.doesNotMatch(config, /^developer_instructions = "custom"$/m);
723
908
  assert.equal((config.match(/^developer_instructions\s*=/gm) ?? []).length, 1);
724
- assert.match(config, /^codex_hooks = true$/m);
909
+ assert.match(config, /^hooks = true$/m);
725
910
  assert.match(config, /^\[hooks\.state\.".*hooks\.json:pre_tool_use:0:0"\]$/m);
726
911
  });
727
912
  });
@@ -730,6 +915,27 @@ describe("omx setup install mode behavior", () => {
730
915
  await rm(wd, { recursive: true, force: true });
731
916
  }
732
917
  });
918
+ it("uses legacy codex_hooks only when the installed Codex reports that hook feature", async () => {
919
+ const wd = await mkdtemp(join(tmpdir(), "omx-setup-install-mode-"));
920
+ try {
921
+ await withIsolatedUserHome(wd, async (codexHomeDir) => {
922
+ await withTempCwd(wd, async () => {
923
+ await setup({
924
+ scope: "user",
925
+ installMode: "plugin",
926
+ codexFeaturesProbe: () => "codex_hooks experimental true\n",
927
+ codexVersionProbe: () => "codex-cli 0.129.0",
928
+ });
929
+ const config = await readFile(join(codexHomeDir, "config.toml"), "utf-8");
930
+ assert.match(config, /^codex_hooks = true$/m);
931
+ assert.doesNotMatch(config, /^hooks = true$/m);
932
+ });
933
+ });
934
+ }
935
+ finally {
936
+ await rm(wd, { recursive: true, force: true });
937
+ }
938
+ });
733
939
  it("preserves existing user hooks while installing plugin-mode native hooks", async () => {
734
940
  const wd = await mkdtemp(join(tmpdir(), "omx-setup-install-mode-"));
735
941
  try {
@@ -743,7 +949,7 @@ describe("omx setup install mode behavior", () => {
743
949
  assert.match(hooks, /"UserPromptSubmit"/);
744
950
  assert.match(hooks, /codex-native-hook\.js/);
745
951
  const config = await readFile(join(codexHomeDir, "config.toml"), "utf-8");
746
- assert.match(config, /^codex_hooks = true$/m);
952
+ assert.match(config, /^hooks = true$/m);
747
953
  });
748
954
  });
749
955
  }
@@ -760,6 +966,7 @@ describe("omx setup install mode behavior", () => {
760
966
  assert.deepEqual(persisted, {
761
967
  scope: "project",
762
968
  installMode: "plugin",
969
+ mcpMode: "none",
763
970
  });
764
971
  await setup({ scope: "project" });
765
972
  assert.equal(existsSync(join(wd, ".codex", "skills", "ask", "SKILL.md")), false);
@@ -781,7 +988,7 @@ describe("omx setup install mode behavior", () => {
781
988
  await writeFile(join(wd, ".omx", "setup-scope.json"), JSON.stringify({ scope: "project", installMode: "plugin" }));
782
989
  await setup({ scope: "project", installMode: "legacy" });
783
990
  const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
784
- assert.deepEqual(persisted, { scope: "project" });
991
+ assert.deepEqual(persisted, { scope: "project", mcpMode: "none" });
785
992
  assert.equal(existsSync(join(wd, ".codex", "skills", "ask", "SKILL.md")), true);
786
993
  assert.equal(existsSync(join(wd, ".codex", "agents", "planner.toml")), true);
787
994
  assert.equal(existsSync(join(wd, ".codex", "prompts", "executor.md")), true);
@@ -803,6 +1010,7 @@ describe("omx setup install mode behavior", () => {
803
1010
  assert.deepEqual(persisted, {
804
1011
  scope: "user",
805
1012
  installMode: "legacy",
1013
+ mcpMode: "none",
806
1014
  });
807
1015
  assert.equal(existsSync(join(codexHomeDir, "skills", "ask", "SKILL.md")), true);
808
1016
  assert.equal(existsSync(join(codexHomeDir, "agents", "planner.toml")), true);
@@ -814,6 +1022,44 @@ describe("omx setup install mode behavior", () => {
814
1022
  await rm(wd, { recursive: true, force: true });
815
1023
  }
816
1024
  });
1025
+ it("dedupes plugin-mode hook trust state when switching user setup back to legacy", async () => {
1026
+ const wd = await mkdtemp(join(tmpdir(), "omx-setup-install-mode-"));
1027
+ try {
1028
+ await withIsolatedUserHome(wd, async (codexHomeDir) => {
1029
+ await withTempCwd(wd, async () => {
1030
+ const configPath = join(codexHomeDir, "config.toml");
1031
+ await setup({ scope: "user", installMode: "plugin", force: true });
1032
+ const pluginConfig = await readFile(configPath, "utf-8");
1033
+ const staleUnfencedPluginConfig = pluginConfig
1034
+ .split(/\r?\n/)
1035
+ .filter((line) => line.trim() !== "# OMX-owned Codex hook trust state" &&
1036
+ line.trim() !==
1037
+ "# Trusts only setup-managed codex-native-hook.js wrappers." &&
1038
+ line.trim() !== "# End OMX-owned Codex hook trust state")
1039
+ .join("\n");
1040
+ await writeFile(configPath, staleUnfencedPluginConfig);
1041
+ assert.doesNotThrow(() => parseToml(staleUnfencedPluginConfig));
1042
+ await setup({ scope: "user", installMode: "legacy", force: true });
1043
+ const legacyConfig = await readFile(configPath, "utf-8");
1044
+ assert.doesNotThrow(() => parseToml(legacyConfig));
1045
+ assert.equal(legacyConfig
1046
+ .split(/\r?\n/)
1047
+ .filter((line) => line.trim() ===
1048
+ `[hooks.state."${join(codexHomeDir, "hooks.json")}:post_compact:0:0"]`).length, 1, "legacy setup should replace stale plugin-mode hook trust state instead of duplicating it");
1049
+ assert.match(legacyConfig, /# OMX-owned Codex hook trust state[\s\S]*# End OMX-owned Codex hook trust state/);
1050
+ const persisted = JSON.parse(await readFile(join(wd, ".omx", "setup-scope.json"), "utf-8"));
1051
+ assert.deepEqual(persisted, {
1052
+ scope: "user",
1053
+ installMode: "legacy",
1054
+ mcpMode: "none",
1055
+ });
1056
+ });
1057
+ });
1058
+ }
1059
+ finally {
1060
+ await rm(wd, { recursive: true, force: true });
1061
+ }
1062
+ });
817
1063
  it("installs project-scoped native hooks when plugin mode is explicitly requested", async () => {
818
1064
  const wd = await mkdtemp(join(tmpdir(), "omx-setup-install-mode-"));
819
1065
  try {
@@ -902,7 +1148,7 @@ describe("omx setup install mode behavior", () => {
902
1148
  const hooks = await readFile(hooksPath, "utf-8");
903
1149
  assert.match(hooks, /codex-native-hook\.js/);
904
1150
  const config = await readFile(configPath, "utf-8");
905
- assert.match(config, /^codex_hooks = true$/m);
1151
+ assert.match(config, /^hooks = true$/m);
906
1152
  assert.doesNotMatch(config, /^\s*(?:notify|developer_instructions)\s*=|^\s*\[mcp_servers[.\]]/m);
907
1153
  });
908
1154
  });