oh-my-codex 0.10.2 → 0.10.3

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 (260) hide show
  1. package/README.de.md +4 -4
  2. package/README.es.md +4 -4
  3. package/README.fr.md +4 -4
  4. package/README.it.md +4 -4
  5. package/README.ja.md +4 -4
  6. package/README.ko.md +4 -4
  7. package/README.md +13 -7
  8. package/README.pt.md +4 -4
  9. package/README.ru.md +4 -4
  10. package/README.tr.md +4 -4
  11. package/README.vi.md +4 -4
  12. package/README.zh-TW.md +4 -4
  13. package/README.zh.md +4 -4
  14. package/dist/agents/__tests__/native-config.test.js +37 -33
  15. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  16. package/dist/agents/__tests__/skill-bridge.test.d.ts +2 -0
  17. package/dist/agents/__tests__/skill-bridge.test.d.ts.map +1 -0
  18. package/dist/agents/__tests__/skill-bridge.test.js +71 -0
  19. package/dist/agents/__tests__/skill-bridge.test.js.map +1 -0
  20. package/dist/agents/native-config.d.ts +18 -6
  21. package/dist/agents/native-config.d.ts.map +1 -1
  22. package/dist/agents/native-config.js +109 -92
  23. package/dist/agents/native-config.js.map +1 -1
  24. package/dist/agents/skill-bridge.d.ts +20 -0
  25. package/dist/agents/skill-bridge.d.ts.map +1 -0
  26. package/dist/agents/skill-bridge.js +150 -0
  27. package/dist/agents/skill-bridge.js.map +1 -0
  28. package/dist/autoresearch/__tests__/contracts.test.js +37 -1
  29. package/dist/autoresearch/__tests__/contracts.test.js.map +1 -1
  30. package/dist/autoresearch/__tests__/runtime-parity-extra.test.js +10 -10
  31. package/dist/autoresearch/__tests__/runtime-parity-extra.test.js.map +1 -1
  32. package/dist/autoresearch/__tests__/runtime.test.js +2 -2
  33. package/dist/autoresearch/__tests__/runtime.test.js.map +1 -1
  34. package/dist/autoresearch/contracts.d.ts.map +1 -1
  35. package/dist/autoresearch/contracts.js +17 -10
  36. package/dist/autoresearch/contracts.js.map +1 -1
  37. package/dist/autoresearch/runtime.d.ts.map +1 -1
  38. package/dist/autoresearch/runtime.js +71 -96
  39. package/dist/autoresearch/runtime.js.map +1 -1
  40. package/dist/cli/__tests__/agents-init.test.js +2 -0
  41. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  42. package/dist/cli/__tests__/agents.test.d.ts +2 -0
  43. package/dist/cli/__tests__/agents.test.d.ts.map +1 -0
  44. package/dist/cli/__tests__/agents.test.js +114 -0
  45. package/dist/cli/__tests__/agents.test.js.map +1 -0
  46. package/dist/cli/__tests__/autoresearch-guided.test.js +156 -1
  47. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  48. package/dist/cli/__tests__/autoresearch.test.js +195 -24
  49. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  50. package/dist/cli/__tests__/cleanup.test.d.ts +2 -0
  51. package/dist/cli/__tests__/cleanup.test.d.ts.map +1 -0
  52. package/dist/cli/__tests__/cleanup.test.js +213 -0
  53. package/dist/cli/__tests__/cleanup.test.js.map +1 -0
  54. package/dist/cli/__tests__/error-handling-warnings.test.js +1 -1
  55. package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
  56. package/dist/cli/__tests__/explore.test.js +3 -3
  57. package/dist/cli/__tests__/explore.test.js.map +1 -1
  58. package/dist/cli/__tests__/index.test.js +521 -401
  59. package/dist/cli/__tests__/index.test.js.map +1 -1
  60. package/dist/cli/__tests__/native-assets.test.js +72 -9
  61. package/dist/cli/__tests__/native-assets.test.js.map +1 -1
  62. package/dist/cli/__tests__/ralphthon.test.d.ts +2 -0
  63. package/dist/cli/__tests__/ralphthon.test.d.ts.map +1 -0
  64. package/dist/cli/__tests__/ralphthon.test.js +28 -0
  65. package/dist/cli/__tests__/ralphthon.test.js.map +1 -0
  66. package/dist/cli/__tests__/setup-agents-overwrite.test.js +36 -1
  67. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  68. package/dist/cli/__tests__/setup-prompts-overwrite.test.js +35 -5
  69. package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
  70. package/dist/cli/__tests__/setup-refresh.test.js +2 -2
  71. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  72. package/dist/cli/__tests__/setup-scope.test.js +131 -161
  73. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  74. package/dist/cli/__tests__/setup-skills-overwrite.test.js +10 -10
  75. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  76. package/dist/cli/__tests__/sparkshell-cli.test.js +28 -2
  77. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  78. package/dist/cli/__tests__/team.test.js +1 -112
  79. package/dist/cli/__tests__/team.test.js.map +1 -1
  80. package/dist/cli/__tests__/uninstall.test.js +7 -20
  81. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  82. package/dist/cli/agents-init.d.ts.map +1 -1
  83. package/dist/cli/agents-init.js +99 -95
  84. package/dist/cli/agents-init.js.map +1 -1
  85. package/dist/cli/agents.d.ts +14 -0
  86. package/dist/cli/agents.d.ts.map +1 -0
  87. package/dist/cli/agents.js +261 -0
  88. package/dist/cli/agents.js.map +1 -0
  89. package/dist/cli/autoresearch-guided.d.ts +8 -0
  90. package/dist/cli/autoresearch-guided.d.ts.map +1 -1
  91. package/dist/cli/autoresearch-guided.js +104 -37
  92. package/dist/cli/autoresearch-guided.js.map +1 -1
  93. package/dist/cli/autoresearch-intake.d.ts +60 -0
  94. package/dist/cli/autoresearch-intake.d.ts.map +1 -0
  95. package/dist/cli/autoresearch-intake.js +318 -0
  96. package/dist/cli/autoresearch-intake.js.map +1 -0
  97. package/dist/cli/autoresearch.d.ts +3 -1
  98. package/dist/cli/autoresearch.d.ts.map +1 -1
  99. package/dist/cli/autoresearch.js +64 -10
  100. package/dist/cli/autoresearch.js.map +1 -1
  101. package/dist/cli/cleanup.d.ts +52 -0
  102. package/dist/cli/cleanup.d.ts.map +1 -0
  103. package/dist/cli/cleanup.js +302 -0
  104. package/dist/cli/cleanup.js.map +1 -0
  105. package/dist/cli/doctor.d.ts.map +1 -1
  106. package/dist/cli/doctor.js +9 -37
  107. package/dist/cli/doctor.js.map +1 -1
  108. package/dist/cli/explore.d.ts.map +1 -1
  109. package/dist/cli/explore.js +5 -4
  110. package/dist/cli/explore.js.map +1 -1
  111. package/dist/cli/index.d.ts +5 -7
  112. package/dist/cli/index.d.ts.map +1 -1
  113. package/dist/cli/index.js +610 -427
  114. package/dist/cli/index.js.map +1 -1
  115. package/dist/cli/native-assets.d.ts +15 -1
  116. package/dist/cli/native-assets.d.ts.map +1 -1
  117. package/dist/cli/native-assets.js +134 -32
  118. package/dist/cli/native-assets.js.map +1 -1
  119. package/dist/cli/ralph.d.ts.map +1 -1
  120. package/dist/cli/ralph.js +38 -1
  121. package/dist/cli/ralph.js.map +1 -1
  122. package/dist/cli/ralphthon.d.ts +14 -0
  123. package/dist/cli/ralphthon.d.ts.map +1 -0
  124. package/dist/cli/ralphthon.js +234 -0
  125. package/dist/cli/ralphthon.js.map +1 -0
  126. package/dist/cli/setup.d.ts +1 -4
  127. package/dist/cli/setup.d.ts.map +1 -1
  128. package/dist/cli/setup.js +111 -76
  129. package/dist/cli/setup.js.map +1 -1
  130. package/dist/cli/sparkshell.d.ts +3 -1
  131. package/dist/cli/sparkshell.d.ts.map +1 -1
  132. package/dist/cli/sparkshell.js +35 -16
  133. package/dist/cli/sparkshell.js.map +1 -1
  134. package/dist/cli/team.d.ts.map +1 -1
  135. package/dist/cli/team.js +1 -0
  136. package/dist/cli/team.js.map +1 -1
  137. package/dist/cli/uninstall.d.ts +1 -1
  138. package/dist/cli/uninstall.d.ts.map +1 -1
  139. package/dist/cli/uninstall.js +82 -64
  140. package/dist/cli/uninstall.js.map +1 -1
  141. package/dist/config/__tests__/generator-idempotent.test.js +10 -10
  142. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  143. package/dist/config/__tests__/generator-notify.test.js +15 -0
  144. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  145. package/dist/config/generator.d.ts +0 -1
  146. package/dist/config/generator.d.ts.map +1 -1
  147. package/dist/config/generator.js +53 -42
  148. package/dist/config/generator.js.map +1 -1
  149. package/dist/hooks/__tests__/agents-overlay.test.js +295 -230
  150. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  151. package/dist/hooks/__tests__/deep-interview-contract.test.js +49 -24
  152. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  153. package/dist/hooks/__tests__/notify-fallback-watcher-ralphthon.test.d.ts +2 -0
  154. package/dist/hooks/__tests__/notify-fallback-watcher-ralphthon.test.d.ts.map +1 -0
  155. package/dist/hooks/__tests__/notify-fallback-watcher-ralphthon.test.js +193 -0
  156. package/dist/hooks/__tests__/notify-fallback-watcher-ralphthon.test.js.map +1 -0
  157. package/dist/hooks/agents-overlay.d.ts +1 -1
  158. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  159. package/dist/hooks/agents-overlay.js +109 -106
  160. package/dist/hooks/agents-overlay.js.map +1 -1
  161. package/dist/modes/base.d.ts +1 -1
  162. package/dist/modes/base.d.ts.map +1 -1
  163. package/dist/modes/base.js +1 -1
  164. package/dist/modes/base.js.map +1 -1
  165. package/dist/ralphthon/__tests__/bootstrap.test.d.ts +2 -0
  166. package/dist/ralphthon/__tests__/bootstrap.test.d.ts.map +1 -0
  167. package/dist/ralphthon/__tests__/bootstrap.test.js +23 -0
  168. package/dist/ralphthon/__tests__/bootstrap.test.js.map +1 -0
  169. package/dist/ralphthon/__tests__/orchestrator.test.d.ts +2 -0
  170. package/dist/ralphthon/__tests__/orchestrator.test.d.ts.map +1 -0
  171. package/dist/ralphthon/__tests__/orchestrator.test.js +309 -0
  172. package/dist/ralphthon/__tests__/orchestrator.test.js.map +1 -0
  173. package/dist/ralphthon/__tests__/prd.test.d.ts +2 -0
  174. package/dist/ralphthon/__tests__/prd.test.d.ts.map +1 -0
  175. package/dist/ralphthon/__tests__/prd.test.js +133 -0
  176. package/dist/ralphthon/__tests__/prd.test.js.map +1 -0
  177. package/dist/ralphthon/bootstrap.d.ts +3 -0
  178. package/dist/ralphthon/bootstrap.d.ts.map +1 -0
  179. package/dist/ralphthon/bootstrap.js +84 -0
  180. package/dist/ralphthon/bootstrap.js.map +1 -0
  181. package/dist/ralphthon/orchestrator.d.ts +50 -0
  182. package/dist/ralphthon/orchestrator.d.ts.map +1 -0
  183. package/dist/ralphthon/orchestrator.js +362 -0
  184. package/dist/ralphthon/orchestrator.js.map +1 -0
  185. package/dist/ralphthon/prd.d.ts +191 -0
  186. package/dist/ralphthon/prd.d.ts.map +1 -0
  187. package/dist/ralphthon/prd.js +355 -0
  188. package/dist/ralphthon/prd.js.map +1 -0
  189. package/dist/ralphthon/runtime.d.ts +31 -0
  190. package/dist/ralphthon/runtime.d.ts.map +1 -0
  191. package/dist/ralphthon/runtime.js +104 -0
  192. package/dist/ralphthon/runtime.js.map +1 -0
  193. package/dist/ralphthon/tmux.d.ts +3 -0
  194. package/dist/ralphthon/tmux.d.ts.map +1 -0
  195. package/dist/ralphthon/tmux.js +39 -0
  196. package/dist/ralphthon/tmux.js.map +1 -0
  197. package/dist/subagents/__tests__/tracker.test.d.ts +2 -0
  198. package/dist/subagents/__tests__/tracker.test.d.ts.map +1 -0
  199. package/dist/subagents/__tests__/tracker.test.js +47 -0
  200. package/dist/subagents/__tests__/tracker.test.js.map +1 -0
  201. package/dist/subagents/tracker.d.ts +52 -0
  202. package/dist/subagents/tracker.d.ts.map +1 -0
  203. package/dist/subagents/tracker.js +175 -0
  204. package/dist/subagents/tracker.js.map +1 -0
  205. package/dist/team/__tests__/worker-bootstrap.test.js +189 -163
  206. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  207. package/dist/team/__tests__/worktree.test.js +1 -1
  208. package/dist/team/__tests__/worktree.test.js.map +1 -1
  209. package/dist/team/worker-bootstrap.d.ts +1 -1
  210. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  211. package/dist/team/worker-bootstrap.js +58 -63
  212. package/dist/team/worker-bootstrap.js.map +1 -1
  213. package/dist/team/worktree.js +1 -1
  214. package/dist/team/worktree.js.map +1 -1
  215. package/dist/utils/__tests__/agents-md.test.d.ts +2 -0
  216. package/dist/utils/__tests__/agents-md.test.d.ts.map +1 -0
  217. package/dist/utils/__tests__/agents-md.test.js +32 -0
  218. package/dist/utils/__tests__/agents-md.test.js.map +1 -0
  219. package/dist/utils/__tests__/agents-model-table.test.d.ts +2 -0
  220. package/dist/utils/__tests__/agents-model-table.test.d.ts.map +1 -0
  221. package/dist/utils/__tests__/agents-model-table.test.js +84 -0
  222. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -0
  223. package/dist/utils/__tests__/paths.test.js +78 -83
  224. package/dist/utils/__tests__/paths.test.js.map +1 -1
  225. package/dist/utils/agents-md.d.ts.map +1 -1
  226. package/dist/utils/agents-md.js +10 -0
  227. package/dist/utils/agents-md.js.map +1 -1
  228. package/dist/utils/agents-model-table.d.ts +16 -0
  229. package/dist/utils/agents-model-table.d.ts.map +1 -0
  230. package/dist/utils/agents-model-table.js +83 -0
  231. package/dist/utils/agents-model-table.js.map +1 -0
  232. package/dist/utils/paths.d.ts +6 -6
  233. package/dist/utils/paths.d.ts.map +1 -1
  234. package/dist/utils/paths.js +31 -31
  235. package/dist/utils/paths.js.map +1 -1
  236. package/dist/verification/__tests__/explore-harness-release-workflow.test.js +21 -3
  237. package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
  238. package/dist/verification/__tests__/native-release-manifest.test.d.ts +2 -0
  239. package/dist/verification/__tests__/native-release-manifest.test.d.ts.map +1 -0
  240. package/dist/verification/__tests__/native-release-manifest.test.js +80 -0
  241. package/dist/verification/__tests__/native-release-manifest.test.js.map +1 -0
  242. package/package.json +1 -1
  243. package/prompts/executor.md +15 -0
  244. package/scripts/__tests__/smoke-packed-install.test.mjs +137 -8
  245. package/scripts/eval-adaptive-sort-optimization.py +24 -0
  246. package/scripts/eval-in-action-cat-shellout-demo.js +31 -0
  247. package/scripts/eval-ml-kaggle-model-optimization.py +29 -0
  248. package/scripts/eval-noisy-bayesopt-highdim.py +44 -0
  249. package/scripts/eval-noisy-latent-subspace-discovery.py +44 -0
  250. package/scripts/generate-native-release-manifest.mjs +14 -3
  251. package/scripts/notify-fallback-watcher.js +308 -6
  252. package/scripts/notify-hook.js +20 -0
  253. package/scripts/run-autoresearch-showcase.sh +75 -0
  254. package/scripts/smoke-packed-install.mjs +142 -10
  255. package/skills/deep-interview/SKILL.md +30 -1
  256. package/skills/omx-setup/SKILL.md +2 -2
  257. package/skills/skill/SKILL.md +32 -32
  258. package/skills/team/SKILL.md +6 -0
  259. package/skills/worker/SKILL.md +2 -2
  260. package/templates/AGENTS.md +97 -16
@@ -1,22 +1,22 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { mkdtemp, readFile, writeFile, rm, mkdir } from 'fs/promises';
4
- import { join } from 'path';
5
- import { tmpdir } from 'os';
6
- import { generateWorkerOverlay, applyWorkerOverlay, stripWorkerOverlay, writeTeamWorkerInstructionsFile, writeWorkerRoleInstructionsFile, removeTeamWorkerInstructionsFile, generateInitialInbox, generateTaskAssignmentInbox, generateShutdownInbox, generateTriggerMessage, generateMailboxTriggerMessage, generateLeaderMailboxTriggerMessage, } from '../worker-bootstrap.js';
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtemp, readFile, writeFile, rm, mkdir } from "fs/promises";
4
+ import { join } from "path";
5
+ import { tmpdir } from "os";
6
+ import { generateWorkerOverlay, applyWorkerOverlay, stripWorkerOverlay, writeTeamWorkerInstructionsFile, writeWorkerRoleInstructionsFile, removeTeamWorkerInstructionsFile, generateInitialInbox, generateTaskAssignmentInbox, generateShutdownInbox, generateTriggerMessage, generateMailboxTriggerMessage, generateLeaderMailboxTriggerMessage, } from "../worker-bootstrap.js";
7
7
  function setMockCodexHome(codexHomePath) {
8
8
  const previous = process.env.CODEX_HOME;
9
9
  process.env.CODEX_HOME = codexHomePath;
10
10
  return () => {
11
- if (typeof previous === 'string')
11
+ if (typeof previous === "string")
12
12
  process.env.CODEX_HOME = previous;
13
13
  else
14
14
  delete process.env.CODEX_HOME;
15
15
  };
16
16
  }
17
- describe('worker bootstrap', () => {
18
- it('worker skill lifecycle instructions are claim-safe (issue #448)', async () => {
19
- const workerSkill = await readFile(join(process.cwd(), 'skills', 'worker', 'SKILL.md'), 'utf8');
17
+ describe("worker bootstrap", () => {
18
+ it("worker skill lifecycle instructions are claim-safe (issue #448)", async () => {
19
+ const workerSkill = await readFile(join(process.cwd(), "skills", "worker", "SKILL.md"), "utf8");
20
20
  assert.match(workerSkill, /omx team api claim-task/);
21
21
  assert.match(workerSkill, /omx team api transition-task-status/);
22
22
  assert.match(workerSkill, /omx team api release-task-claim/);
@@ -25,16 +25,16 @@ describe('worker bootstrap', () => {
25
25
  assert.doesNotMatch(workerSkill, /`?\{"status":"completed","result":"\.\.\."\}`?/);
26
26
  assert.doesNotMatch(workerSkill, /`?\{"status":"failed","error":"\.\.\."\}`?/);
27
27
  });
28
- it('generateWorkerOverlay produces markdown with correct start/end markers', () => {
29
- const overlay = generateWorkerOverlay('alpha-team');
28
+ it("generateWorkerOverlay produces markdown with correct start/end markers", () => {
29
+ const overlay = generateWorkerOverlay("alpha-team");
30
30
  assert.match(overlay, /<!-- OMX:TEAM:WORKER:START -->/);
31
31
  assert.match(overlay, /<!-- OMX:TEAM:WORKER:END -->/);
32
32
  });
33
- it('generateWorkerOverlay includes the team name', () => {
34
- const overlay = generateWorkerOverlay('my-team');
33
+ it("generateWorkerOverlay includes the team name", () => {
34
+ const overlay = generateWorkerOverlay("my-team");
35
35
  assert.match(overlay, /team "my-team"/);
36
36
  assert.match(overlay, /\$\{CODEX_HOME:-~\/\.codex\}\/skills\/worker\/SKILL\.md/);
37
- assert.match(overlay, /~\/\.agents\/skills\/worker\/SKILL\.md/);
37
+ assert.match(overlay, /<leader_cwd>\/\.codex\/skills\/worker\/SKILL\.md/);
38
38
  assert.match(overlay, /Resolve canonical team state root/i);
39
39
  assert.match(overlay, /<team_state_root>\/team\/my-team\/tasks/);
40
40
  assert.match(overlay, /tasks\/task-<id>\.json/);
@@ -43,18 +43,19 @@ describe('worker bootstrap', () => {
43
43
  assert.match(overlay, /omx team api transition-task-status/);
44
44
  assert.match(overlay, /omx team api release-task-claim/);
45
45
  assert.doesNotMatch(overlay, /On completion: write \{"status": "completed"/);
46
- assert.match(overlay, /Do NOT spawn sub-agents/);
46
+ assert.match(overlay, /You may spawn Codex native subagents when parallel execution improves throughput/);
47
+ assert.match(overlay, /Use subagents only for independent, bounded subtasks/);
47
48
  assert.match(overlay, /do not pass workingDirectory unless the lead explicitly tells you to/);
48
49
  assert.doesNotMatch(overlay, /tasks\/\{id\}\.json/);
49
50
  });
50
- it('applyWorkerOverlay appends to existing AGENTS.md content', async () => {
51
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
51
+ it("applyWorkerOverlay appends to existing AGENTS.md content", async () => {
52
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
52
53
  try {
53
- const agentsMdPath = join(cwd, 'AGENTS.md');
54
- await writeFile(agentsMdPath, '# Base AGENTS\n\nBase content.\n', 'utf8');
55
- const overlay = generateWorkerOverlay('team-a');
54
+ const agentsMdPath = join(cwd, "AGENTS.md");
55
+ await writeFile(agentsMdPath, "# Base AGENTS\n\nBase content.\n", "utf8");
56
+ const overlay = generateWorkerOverlay("team-a");
56
57
  await applyWorkerOverlay(agentsMdPath, overlay);
57
- const content = await readFile(agentsMdPath, 'utf8');
58
+ const content = await readFile(agentsMdPath, "utf8");
58
59
  assert.match(content, /# Base AGENTS/);
59
60
  assert.match(content, /Base content\./);
60
61
  assert.match(content, /<!-- OMX:TEAM:WORKER:START -->/);
@@ -64,15 +65,15 @@ describe('worker bootstrap', () => {
64
65
  await rm(cwd, { recursive: true, force: true });
65
66
  }
66
67
  });
67
- it('applyWorkerOverlay is idempotent (calling twice doesn\'t duplicate)', async () => {
68
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
68
+ it("applyWorkerOverlay is idempotent (calling twice doesn't duplicate)", async () => {
69
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
69
70
  try {
70
- const agentsMdPath = join(cwd, 'AGENTS.md');
71
- await writeFile(agentsMdPath, '# Base\n', 'utf8');
72
- const overlay = generateWorkerOverlay('team-idempotent');
71
+ const agentsMdPath = join(cwd, "AGENTS.md");
72
+ await writeFile(agentsMdPath, "# Base\n", "utf8");
73
+ const overlay = generateWorkerOverlay("team-idempotent");
73
74
  await applyWorkerOverlay(agentsMdPath, overlay);
74
75
  await applyWorkerOverlay(agentsMdPath, overlay);
75
- const content = await readFile(agentsMdPath, 'utf8');
76
+ const content = await readFile(agentsMdPath, "utf8");
76
77
  const starts = content.match(/<!-- OMX:TEAM:WORKER:START -->/g) ?? [];
77
78
  const ends = content.match(/<!-- OMX:TEAM:WORKER:END -->/g) ?? [];
78
79
  assert.equal(starts.length, 1);
@@ -82,15 +83,15 @@ describe('worker bootstrap', () => {
82
83
  await rm(cwd, { recursive: true, force: true });
83
84
  }
84
85
  });
85
- it('stripWorkerOverlay removes the overlay section', async () => {
86
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
86
+ it("stripWorkerOverlay removes the overlay section", async () => {
87
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
87
88
  try {
88
- const agentsMdPath = join(cwd, 'AGENTS.md');
89
- const base = '# Base\n\nKeep me.\n';
90
- const overlay = generateWorkerOverlay('team-strip');
91
- await writeFile(agentsMdPath, `${base}\n${overlay}\n`, 'utf8');
89
+ const agentsMdPath = join(cwd, "AGENTS.md");
90
+ const base = "# Base\n\nKeep me.\n";
91
+ const overlay = generateWorkerOverlay("team-strip");
92
+ await writeFile(agentsMdPath, `${base}\n${overlay}\n`, "utf8");
92
93
  await stripWorkerOverlay(agentsMdPath);
93
- const content = await readFile(agentsMdPath, 'utf8');
94
+ const content = await readFile(agentsMdPath, "utf8");
94
95
  assert.match(content, /# Base/);
95
96
  assert.match(content, /Keep me\./);
96
97
  assert.doesNotMatch(content, /<!-- OMX:TEAM:WORKER:START -->/);
@@ -100,16 +101,16 @@ describe('worker bootstrap', () => {
100
101
  await rm(cwd, { recursive: true, force: true });
101
102
  }
102
103
  });
103
- it('stripWorkerOverlay is idempotent (calling on already-stripped is no-op)', async () => {
104
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
104
+ it("stripWorkerOverlay is idempotent (calling on already-stripped is no-op)", async () => {
105
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
105
106
  try {
106
- const agentsMdPath = join(cwd, 'AGENTS.md');
107
- await writeFile(agentsMdPath, '# Base only\n', 'utf8');
108
- const before = await readFile(agentsMdPath, 'utf8');
107
+ const agentsMdPath = join(cwd, "AGENTS.md");
108
+ await writeFile(agentsMdPath, "# Base only\n", "utf8");
109
+ const before = await readFile(agentsMdPath, "utf8");
109
110
  await stripWorkerOverlay(agentsMdPath);
110
- const afterFirst = await readFile(agentsMdPath, 'utf8');
111
+ const afterFirst = await readFile(agentsMdPath, "utf8");
111
112
  await stripWorkerOverlay(agentsMdPath);
112
- const afterSecond = await readFile(agentsMdPath, 'utf8');
113
+ const afterSecond = await readFile(agentsMdPath, "utf8");
113
114
  assert.equal(afterFirst, before);
114
115
  assert.equal(afterSecond, before);
115
116
  }
@@ -117,13 +118,13 @@ describe('worker bootstrap', () => {
117
118
  await rm(cwd, { recursive: true, force: true });
118
119
  }
119
120
  });
120
- it('applyWorkerOverlay works on non-existent file (creates it)', async () => {
121
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
121
+ it("applyWorkerOverlay works on non-existent file (creates it)", async () => {
122
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
122
123
  try {
123
- const agentsMdPath = join(cwd, 'AGENTS.md');
124
- const overlay = generateWorkerOverlay('new-team');
124
+ const agentsMdPath = join(cwd, "AGENTS.md");
125
+ const overlay = generateWorkerOverlay("new-team");
125
126
  await applyWorkerOverlay(agentsMdPath, overlay);
126
- const content = await readFile(agentsMdPath, 'utf8');
127
+ const content = await readFile(agentsMdPath, "utf8");
127
128
  assert.match(content, /<!-- OMX:TEAM:WORKER:START -->/);
128
129
  assert.match(content, /team "new-team"/);
129
130
  }
@@ -131,42 +132,42 @@ describe('worker bootstrap', () => {
131
132
  await rm(cwd, { recursive: true, force: true });
132
133
  }
133
134
  });
134
- it('applyWorkerOverlay reaps stale AGENTS lock directory', async () => {
135
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
135
+ it("applyWorkerOverlay reaps stale AGENTS lock directory", async () => {
136
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
136
137
  try {
137
- const agentsMdPath = join(cwd, 'AGENTS.md');
138
- const lockPath = join(cwd, '.omx', 'state', 'agents-md.lock');
138
+ const agentsMdPath = join(cwd, "AGENTS.md");
139
+ const lockPath = join(cwd, ".omx", "state", "agents-md.lock");
139
140
  await mkdir(lockPath, { recursive: true });
140
- await writeFile(join(lockPath, 'owner.json'), JSON.stringify({ pid: 999_999_999, ts: Date.now() - 60_000 }), 'utf8');
141
- await writeFile(agentsMdPath, '# Base\n', 'utf8');
142
- const overlay = generateWorkerOverlay('team-stale-lock');
141
+ await writeFile(join(lockPath, "owner.json"), JSON.stringify({ pid: 999_999_999, ts: Date.now() - 60_000 }), "utf8");
142
+ await writeFile(agentsMdPath, "# Base\n", "utf8");
143
+ const overlay = generateWorkerOverlay("team-stale-lock");
143
144
  await applyWorkerOverlay(agentsMdPath, overlay);
144
- const content = await readFile(agentsMdPath, 'utf8');
145
+ const content = await readFile(agentsMdPath, "utf8");
145
146
  assert.match(content, /team "team-stale-lock"/);
146
- await assert.rejects(readFile(join(lockPath, 'owner.json'), 'utf8'));
147
+ await assert.rejects(readFile(join(lockPath, "owner.json"), "utf8"));
147
148
  }
148
149
  finally {
149
150
  await rm(cwd, { recursive: true, force: true });
150
151
  }
151
152
  });
152
- it('generateInitialInbox includes worker name, team name, and all tasks', () => {
153
+ it("generateInitialInbox includes worker name, team name, and all tasks", () => {
153
154
  const tasks = [
154
155
  {
155
- id: '1',
156
- subject: 'First task',
157
- description: 'Do first thing',
158
- status: 'pending',
156
+ id: "1",
157
+ subject: "First task",
158
+ description: "Do first thing",
159
+ status: "pending",
159
160
  created_at: new Date().toISOString(),
160
161
  },
161
162
  {
162
- id: '2',
163
- subject: 'Second task',
164
- description: 'Do second thing',
165
- status: 'in_progress',
163
+ id: "2",
164
+ subject: "Second task",
165
+ description: "Do second thing",
166
+ status: "in_progress",
166
167
  created_at: new Date().toISOString(),
167
168
  },
168
169
  ];
169
- const inbox = generateInitialInbox('worker-1', 'team-inbox', 'executor', tasks);
170
+ const inbox = generateInitialInbox("worker-1", "team-inbox", "executor", tasks);
170
171
  assert.match(inbox, /# Worker Assignment: worker-1/);
171
172
  assert.match(inbox, /\*\*Team:\*\* team-inbox/);
172
173
  assert.match(inbox, /\*\*Role:\*\* executor/);
@@ -178,7 +179,7 @@ describe('worker bootstrap', () => {
178
179
  assert.match(inbox, /omx team api transition-task-status/);
179
180
  assert.match(inbox, /omx team api release-task-claim/);
180
181
  assert.match(inbox, /\$\{CODEX_HOME:-~\/\.codex\}\/skills\/worker\/SKILL\.md/);
181
- assert.match(inbox, /~\/\.agents\/skills\/worker\/SKILL\.md/);
182
+ assert.match(inbox, /\/\.codex\/skills\/worker\/SKILL\.md/);
182
183
  assert.match(inbox, /ACK: worker-1 initialized/);
183
184
  assert.match(inbox, /Mailbox Delivery Protocol \(Required\)/);
184
185
  assert.match(inbox, /mailbox-mark-delivered/);
@@ -187,60 +188,85 @@ describe('worker bootstrap', () => {
187
188
  assert.match(inbox, /Verification Requirements/);
188
189
  assert.match(inbox, /Fix-Verify Loop/);
189
190
  });
190
- it('generateInitialInbox shows blocked_by info for blocked tasks', () => {
191
+ it("generateInitialInbox shows blocked_by info for blocked tasks", () => {
191
192
  const tasks = [
192
193
  {
193
- id: '3',
194
- subject: 'Blocked task',
195
- description: 'Wait on dependencies',
196
- status: 'pending',
197
- blocked_by: ['1', '2'],
194
+ id: "3",
195
+ subject: "Blocked task",
196
+ description: "Wait on dependencies",
197
+ status: "pending",
198
+ blocked_by: ["1", "2"],
198
199
  created_at: new Date().toISOString(),
199
200
  },
200
201
  ];
201
- const inbox = generateInitialInbox('worker-2', 'team-blocked', 'executor', tasks);
202
+ const inbox = generateInitialInbox("worker-2", "team-blocked", "executor", tasks);
202
203
  assert.match(inbox, /Blocked by: 1, 2/);
203
204
  });
204
- it('generateInitialInbox uses workerRole when provided', () => {
205
+ it("generateInitialInbox uses workerRole when provided", () => {
205
206
  const tasks = [
206
- { id: '1', subject: 'Test task', description: 'Write tests', status: 'pending', created_at: new Date().toISOString() },
207
+ {
208
+ id: "1",
209
+ subject: "Test task",
210
+ description: "Write tests",
211
+ status: "pending",
212
+ created_at: new Date().toISOString(),
213
+ },
207
214
  ];
208
- const inbox = generateInitialInbox('worker-1', 'team-role', 'executor', tasks, {
209
- workerRole: 'test-engineer',
215
+ const inbox = generateInitialInbox("worker-1", "team-role", "executor", tasks, {
216
+ workerRole: "test-engineer",
210
217
  });
211
218
  assert.match(inbox, /\*\*Role:\*\* test-engineer/);
212
219
  assert.doesNotMatch(inbox, /\*\*Role:\*\* executor/);
213
220
  });
214
- it('generateInitialInbox includes specialization section when rolePromptContent provided', () => {
221
+ it("generateInitialInbox includes specialization section when rolePromptContent provided", () => {
215
222
  const tasks = [
216
- { id: '1', subject: 'Design UI', description: 'Build component', status: 'pending', created_at: new Date().toISOString() },
223
+ {
224
+ id: "1",
225
+ subject: "Design UI",
226
+ description: "Build component",
227
+ status: "pending",
228
+ created_at: new Date().toISOString(),
229
+ },
217
230
  ];
218
- const inbox = generateInitialInbox('worker-2', 'team-spec', 'executor', tasks, {
219
- workerRole: 'designer',
220
- rolePromptContent: 'You focus on UI/UX design and component architecture.',
231
+ const inbox = generateInitialInbox("worker-2", "team-spec", "executor", tasks, {
232
+ workerRole: "designer",
233
+ rolePromptContent: "You focus on UI/UX design and component architecture.",
221
234
  });
222
235
  assert.match(inbox, /## Your Specialization/);
223
236
  assert.match(inbox, /\*\*designer\*\* agent/);
224
237
  assert.match(inbox, /UI\/UX design and component architecture/);
225
238
  });
226
- it('generateInitialInbox omits specialization section when no rolePromptContent', () => {
239
+ it("generateInitialInbox omits specialization section when no rolePromptContent", () => {
227
240
  const tasks = [
228
- { id: '1', subject: 'Task', description: 'Do work', status: 'pending', created_at: new Date().toISOString() },
241
+ {
242
+ id: "1",
243
+ subject: "Task",
244
+ description: "Do work",
245
+ status: "pending",
246
+ created_at: new Date().toISOString(),
247
+ },
229
248
  ];
230
- const inbox = generateInitialInbox('worker-1', 'team-no-spec', 'executor', tasks, {
231
- workerRole: 'executor',
249
+ const inbox = generateInitialInbox("worker-1", "team-no-spec", "executor", tasks, {
250
+ workerRole: "executor",
232
251
  });
233
252
  assert.doesNotMatch(inbox, /## Your Specialization/);
234
253
  });
235
- it('generateInitialInbox shows task role in task list', () => {
254
+ it("generateInitialInbox shows task role in task list", () => {
236
255
  const tasks = [
237
- { id: '1', subject: 'Test task', description: 'Write tests', status: 'pending', role: 'test-engineer', created_at: new Date().toISOString() },
256
+ {
257
+ id: "1",
258
+ subject: "Test task",
259
+ description: "Write tests",
260
+ status: "pending",
261
+ role: "test-engineer",
262
+ created_at: new Date().toISOString(),
263
+ },
238
264
  ];
239
- const inbox = generateInitialInbox('worker-1', 'team-task-role', 'executor', tasks);
265
+ const inbox = generateInitialInbox("worker-1", "team-task-role", "executor", tasks);
240
266
  assert.match(inbox, /Role: test-engineer/);
241
267
  });
242
- it('generateTaskAssignmentInbox includes task ID and description', () => {
243
- const inbox = generateTaskAssignmentInbox('worker-3', 'team-followup', '42', 'Implement parser update');
268
+ it("generateTaskAssignmentInbox includes task ID and description", () => {
269
+ const inbox = generateTaskAssignmentInbox("worker-3", "team-followup", "42", "Implement parser update");
244
270
  assert.match(inbox, /\*\*Task ID:\*\* 42/);
245
271
  assert.match(inbox, /Implement parser update/);
246
272
  assert.match(inbox, /team_state_root/);
@@ -252,31 +278,31 @@ describe('worker bootstrap', () => {
252
278
  assert.match(inbox, /Verification Requirements/);
253
279
  assert.match(inbox, /PASS\/FAIL/);
254
280
  });
255
- it('generateShutdownInbox contains exit instruction and concrete ack path', () => {
256
- const inbox = generateShutdownInbox('team-x', 'worker-1');
281
+ it("generateShutdownInbox contains exit instruction and concrete ack path", () => {
282
+ const inbox = generateShutdownInbox("team-x", "worker-1");
257
283
  assert.match(inbox, /Shutdown Request/);
258
284
  assert.match(inbox, /team_state_root/);
259
285
  assert.match(inbox, /team\/team-x\/workers\/worker-1\/shutdown-ack\.json/);
260
286
  assert.match(inbox, /Type `exit` or press Ctrl\+C/);
261
287
  });
262
- it('generateTriggerMessage is always < 200 characters', () => {
263
- const message = generateTriggerMessage('worker-very-long-name', 'team-with-a-reasonably-long-name');
288
+ it("generateTriggerMessage is always < 200 characters", () => {
289
+ const message = generateTriggerMessage("worker-very-long-name", "team-with-a-reasonably-long-name");
264
290
  assert.ok(message.length < 200);
265
291
  });
266
- it('generateTriggerMessage does not contain [OMX_TMUX_INJECT]', () => {
267
- const message = generateTriggerMessage('worker-1', 'team-safe');
268
- assert.equal(message.includes('[OMX_TMUX_INJECT]'), false);
292
+ it("generateTriggerMessage does not contain [OMX_TMUX_INJECT]", () => {
293
+ const message = generateTriggerMessage("worker-1", "team-safe");
294
+ assert.equal(message.includes("[OMX_TMUX_INJECT]"), false);
269
295
  });
270
- it('generateTriggerMessage contains the inbox path', () => {
271
- const message = generateTriggerMessage('worker-9', 'team-path');
296
+ it("generateTriggerMessage contains the inbox path", () => {
297
+ const message = generateTriggerMessage("worker-9", "team-path");
272
298
  assert.match(message, /\.omx\/state\/team\/team-path\/workers\/worker-9\/inbox\.md/);
273
299
  assert.match(message, /start work now/i);
274
300
  assert.match(message, /concrete progress/i);
275
301
  assert.match(message, /continue assigned work/i);
276
302
  assert.match(message, /next feasible task/i);
277
303
  });
278
- it('generateTriggerMessage uses provided state-root reference for worktree workers', () => {
279
- const message = generateTriggerMessage('worker-9', 'team-path', '$OMX_TEAM_STATE_ROOT');
304
+ it("generateTriggerMessage uses provided state-root reference for worktree workers", () => {
305
+ const message = generateTriggerMessage("worker-9", "team-path", "$OMX_TEAM_STATE_ROOT");
280
306
  assert.match(message, /\$OMX_TEAM_STATE_ROOT\/team\/team-path\/workers\/worker-9\/inbox\.md/);
281
307
  assert.match(message, /work now/i);
282
308
  assert.match(message, /report progress/i);
@@ -284,12 +310,12 @@ describe('worker bootstrap', () => {
284
310
  assert.match(message, /next feasible task/i);
285
311
  assert.ok(message.length < 200);
286
312
  });
287
- it('generateMailboxTriggerMessage is always < 200 characters', () => {
288
- const message = generateMailboxTriggerMessage('worker-long-name', 'team-with-long-name', 42);
313
+ it("generateMailboxTriggerMessage is always < 200 characters", () => {
314
+ const message = generateMailboxTriggerMessage("worker-long-name", "team-with-long-name", 42);
289
315
  assert.ok(message.length < 200);
290
316
  });
291
- it('generateMailboxTriggerMessage contains mailbox path and count', () => {
292
- const message = generateMailboxTriggerMessage('worker-2', 'team-mail', 3);
317
+ it("generateMailboxTriggerMessage contains mailbox path and count", () => {
318
+ const message = generateMailboxTriggerMessage("worker-2", "team-mail", 3);
293
319
  assert.match(message, /3 new message/);
294
320
  assert.match(message, /Read .*\.omx\/state\/team\/team-mail\/mailbox\/worker-2\.json/);
295
321
  assert.match(message, /act now/i);
@@ -297,8 +323,8 @@ describe('worker bootstrap', () => {
297
323
  assert.match(message, /continue assigned work/i);
298
324
  assert.match(message, /next feasible task/i);
299
325
  });
300
- it('generateMailboxTriggerMessage uses provided state-root reference for worktree workers', () => {
301
- const message = generateMailboxTriggerMessage('worker-2', 'team-mail', 3, '$OMX_TEAM_STATE_ROOT');
326
+ it("generateMailboxTriggerMessage uses provided state-root reference for worktree workers", () => {
327
+ const message = generateMailboxTriggerMessage("worker-2", "team-mail", 3, "$OMX_TEAM_STATE_ROOT");
302
328
  assert.match(message, /3 new msg/);
303
329
  assert.match(message, /read .*\$OMX_TEAM_STATE_ROOT\/team\/team-mail\/mailbox\/worker-2\.json/i);
304
330
  assert.match(message, /act/i);
@@ -307,42 +333,42 @@ describe('worker bootstrap', () => {
307
333
  assert.match(message, /next feasible task/i);
308
334
  assert.ok(message.length < 200);
309
335
  });
310
- it('generateLeaderMailboxTriggerMessage is always < 200 characters', () => {
311
- const message = generateLeaderMailboxTriggerMessage('team-with-long-name', 'worker-long-name');
336
+ it("generateLeaderMailboxTriggerMessage is always < 200 characters", () => {
337
+ const message = generateLeaderMailboxTriggerMessage("team-with-long-name", "worker-long-name");
312
338
  assert.ok(message.length < 200);
313
339
  });
314
- it('generateLeaderMailboxTriggerMessage tells the leader to read the mailbox and reply', () => {
315
- const message = generateLeaderMailboxTriggerMessage('team-mail', 'worker-2');
340
+ it("generateLeaderMailboxTriggerMessage tells the leader to read the mailbox and reply", () => {
341
+ const message = generateLeaderMailboxTriggerMessage("team-mail", "worker-2");
316
342
  assert.match(message, /Read .*\.omx\/state\/team\/team-mail\/mailbox\/leader-fixed\.json/);
317
343
  assert.match(message, /worker-2 sent a new message/);
318
344
  assert.match(message, /Reply with the next concrete step/);
319
345
  });
320
- it('generateLeaderMailboxTriggerMessage uses provided state-root reference for worktree leaders', () => {
321
- const message = generateLeaderMailboxTriggerMessage('team-mail', 'worker-2', '$OMX_TEAM_STATE_ROOT');
346
+ it("generateLeaderMailboxTriggerMessage uses provided state-root reference for worktree leaders", () => {
347
+ const message = generateLeaderMailboxTriggerMessage("team-mail", "worker-2", "$OMX_TEAM_STATE_ROOT");
322
348
  assert.match(message, /read .*\$OMX_TEAM_STATE_ROOT\/team\/team-mail\/mailbox\/leader-fixed\.json/i);
323
349
  assert.match(message, /new msg from worker-2/i);
324
350
  assert.match(message, /reply next step/i);
325
351
  assert.ok(message.length < 200);
326
352
  });
327
- it('writeTeamWorkerInstructionsFile composes user + project AGENTS.md with overlay', async () => {
328
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
329
- const restoreCodexHome = setMockCodexHome(join(cwd, 'home', '.codex'));
353
+ it("writeTeamWorkerInstructionsFile composes user + project AGENTS.md with overlay", async () => {
354
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
355
+ const restoreCodexHome = setMockCodexHome(join(cwd, "home", ".codex"));
330
356
  try {
331
- await mkdir(join(cwd, 'home', '.codex'), { recursive: true });
332
- await writeFile(join(cwd, 'home', '.codex', 'AGENTS.md'), '# User Instructions\n\nStart globally.\n', 'utf8');
333
- await writeFile(join(cwd, 'AGENTS.md'), '# Project Instructions\n\nDo good work.\n', 'utf8');
334
- const overlay = generateWorkerOverlay('compose-team');
335
- const outPath = await writeTeamWorkerInstructionsFile('compose-team', cwd, overlay);
336
- const content = await readFile(outPath, 'utf8');
357
+ await mkdir(join(cwd, "home", ".codex"), { recursive: true });
358
+ await writeFile(join(cwd, "home", ".codex", "AGENTS.md"), "# User Instructions\n\nStart globally.\n", "utf8");
359
+ await writeFile(join(cwd, "AGENTS.md"), "# Project Instructions\n\nDo good work.\n", "utf8");
360
+ const overlay = generateWorkerOverlay("compose-team");
361
+ const outPath = await writeTeamWorkerInstructionsFile("compose-team", cwd, overlay);
362
+ const content = await readFile(outPath, "utf8");
337
363
  assert.match(content, /# User Instructions/);
338
364
  assert.match(content, /# Project Instructions/);
339
- assert.ok(content.indexOf('# User Instructions') <
340
- content.indexOf('# Project Instructions'));
365
+ assert.ok(content.indexOf("# User Instructions") <
366
+ content.indexOf("# Project Instructions"));
341
367
  assert.match(content, /Do good work/);
342
368
  assert.match(content, /<!-- OMX:TEAM:WORKER:START -->/);
343
369
  assert.match(content, /<!-- OMX:TEAM:WORKER:END -->/);
344
370
  // Verify project AGENTS.md was NOT modified
345
- const projectContent = await readFile(join(cwd, 'AGENTS.md'), 'utf8');
371
+ const projectContent = await readFile(join(cwd, "AGENTS.md"), "utf8");
346
372
  assert.doesNotMatch(projectContent, /<!-- OMX:TEAM:WORKER:START -->/);
347
373
  }
348
374
  finally {
@@ -350,24 +376,24 @@ describe('worker bootstrap', () => {
350
376
  await rm(cwd, { recursive: true, force: true });
351
377
  }
352
378
  });
353
- it('writeTeamWorkerInstructionsFile deduplicates duplicate skill references in favor of project scope', async () => {
354
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
355
- const restoreCodexHome = setMockCodexHome(join(cwd, 'home', '.codex'));
379
+ it("writeTeamWorkerInstructionsFile deduplicates duplicate skill references in favor of project scope", async () => {
380
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
381
+ const restoreCodexHome = setMockCodexHome(join(cwd, "home", ".codex"));
356
382
  try {
357
- const userAgentsPath = join(cwd, 'home', '.codex', 'AGENTS.md');
358
- const projectAgentsPath = join(cwd, 'AGENTS.md');
359
- const userSkillDir = join(cwd, 'home', '.codex', 'skills', 'help');
360
- const projectSkillDir = join(cwd, '.agents', 'skills', 'help');
361
- await mkdir(join(cwd, 'home', '.codex'), { recursive: true });
383
+ const userAgentsPath = join(cwd, "home", ".codex", "AGENTS.md");
384
+ const projectAgentsPath = join(cwd, "AGENTS.md");
385
+ const userSkillDir = join(cwd, "home", ".codex", "skills", "help");
386
+ const projectSkillDir = join(cwd, ".codex", "skills", "help");
387
+ await mkdir(join(cwd, "home", ".codex"), { recursive: true });
362
388
  await mkdir(userSkillDir, { recursive: true });
363
389
  await mkdir(projectSkillDir, { recursive: true });
364
- await writeFile(join(userSkillDir, 'SKILL.md'), '# user help\n', 'utf8');
365
- await writeFile(join(projectSkillDir, 'SKILL.md'), '# project help\n', 'utf8');
366
- await writeFile(userAgentsPath, '- help user (file: /tmp/home/.codex/skills/help/SKILL.md)\n', 'utf8');
367
- await writeFile(projectAgentsPath, '- help project (file: /tmp/project/.agents/skills/help/SKILL.md)\n', 'utf8');
368
- const overlay = generateWorkerOverlay('dedupe-team');
369
- const outPath = await writeTeamWorkerInstructionsFile('dedupe-team', cwd, overlay);
370
- const content = await readFile(outPath, 'utf8');
390
+ await writeFile(join(userSkillDir, "SKILL.md"), "# user help\n", "utf8");
391
+ await writeFile(join(projectSkillDir, "SKILL.md"), "# project help\n", "utf8");
392
+ await writeFile(userAgentsPath, "- help user (file: /tmp/home/.codex/skills/help/SKILL.md)\n", "utf8");
393
+ await writeFile(projectAgentsPath, "- help project (file: /tmp/project/.codex/skills/help/SKILL.md)\n", "utf8");
394
+ const overlay = generateWorkerOverlay("dedupe-team");
395
+ const outPath = await writeTeamWorkerInstructionsFile("dedupe-team", cwd, overlay);
396
+ const content = await readFile(outPath, "utf8");
371
397
  assert.equal((content.match(/skills\/help\/SKILL\.md/g) || []).length, 1);
372
398
  assert.doesNotMatch(content, /help user/);
373
399
  assert.match(content, /help project/);
@@ -377,13 +403,13 @@ describe('worker bootstrap', () => {
377
403
  await rm(cwd, { recursive: true, force: true });
378
404
  }
379
405
  });
380
- it('writeWorkerRoleInstructionsFile layers role prompt on top of team worker instructions', async () => {
381
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
406
+ it("writeWorkerRoleInstructionsFile layers role prompt on top of team worker instructions", async () => {
407
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
382
408
  try {
383
- const overlay = generateWorkerOverlay('role-team');
384
- const basePath = await writeTeamWorkerInstructionsFile('role-team', cwd, overlay);
385
- const outPath = await writeWorkerRoleInstructionsFile('role-team', 'worker-2', cwd, basePath, 'writer', '<identity>Writer role prompt</identity>');
386
- const content = await readFile(outPath, 'utf8');
409
+ const overlay = generateWorkerOverlay("role-team");
410
+ const basePath = await writeTeamWorkerInstructionsFile("role-team", cwd, overlay);
411
+ const outPath = await writeWorkerRoleInstructionsFile("role-team", "worker-2", cwd, basePath, "writer", "<identity>Writer role prompt</identity>");
412
+ const content = await readFile(outPath, "utf8");
387
413
  assert.match(content, /team "role-team"/);
388
414
  assert.match(content, /<!-- OMX:TEAM:ROLE:START -->/);
389
415
  assert.match(content, /\*\*writer\*\* role/);
@@ -393,12 +419,12 @@ describe('worker bootstrap', () => {
393
419
  await rm(cwd, { recursive: true, force: true });
394
420
  }
395
421
  });
396
- it('writeTeamWorkerInstructionsFile works without project AGENTS.md', async () => {
397
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
422
+ it("writeTeamWorkerInstructionsFile works without project AGENTS.md", async () => {
423
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
398
424
  try {
399
- const overlay = generateWorkerOverlay('no-agents-team');
400
- const outPath = await writeTeamWorkerInstructionsFile('no-agents-team', cwd, overlay);
401
- const content = await readFile(outPath, 'utf8');
425
+ const overlay = generateWorkerOverlay("no-agents-team");
426
+ const outPath = await writeTeamWorkerInstructionsFile("no-agents-team", cwd, overlay);
427
+ const content = await readFile(outPath, "utf8");
402
428
  assert.match(content, /<!-- OMX:TEAM:WORKER:START -->/);
403
429
  assert.match(content, /team "no-agents-team"/);
404
430
  }
@@ -406,25 +432,25 @@ describe('worker bootstrap', () => {
406
432
  await rm(cwd, { recursive: true, force: true });
407
433
  }
408
434
  });
409
- it('removeTeamWorkerInstructionsFile cleans up the file', async () => {
410
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
435
+ it("removeTeamWorkerInstructionsFile cleans up the file", async () => {
436
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
411
437
  try {
412
- const overlay = generateWorkerOverlay('cleanup-team');
413
- await writeTeamWorkerInstructionsFile('cleanup-team', cwd, overlay);
414
- await removeTeamWorkerInstructionsFile('cleanup-team', cwd);
415
- const { existsSync } = await import('fs');
416
- const outPath = join(cwd, '.omx', 'state', 'team', 'cleanup-team', 'worker-agents.md');
438
+ const overlay = generateWorkerOverlay("cleanup-team");
439
+ await writeTeamWorkerInstructionsFile("cleanup-team", cwd, overlay);
440
+ await removeTeamWorkerInstructionsFile("cleanup-team", cwd);
441
+ const { existsSync } = await import("fs");
442
+ const outPath = join(cwd, ".omx", "state", "team", "cleanup-team", "worker-agents.md");
417
443
  assert.equal(existsSync(outPath), false);
418
444
  }
419
445
  finally {
420
446
  await rm(cwd, { recursive: true, force: true });
421
447
  }
422
448
  });
423
- it('removeTeamWorkerInstructionsFile is safe to call when file does not exist', async () => {
424
- const cwd = await mkdtemp(join(tmpdir(), 'omx-worker-bootstrap-'));
449
+ it("removeTeamWorkerInstructionsFile is safe to call when file does not exist", async () => {
450
+ const cwd = await mkdtemp(join(tmpdir(), "omx-worker-bootstrap-"));
425
451
  try {
426
452
  // Should not throw
427
- await removeTeamWorkerInstructionsFile('nonexistent-team', cwd);
453
+ await removeTeamWorkerInstructionsFile("nonexistent-team", cwd);
428
454
  }
429
455
  finally {
430
456
  await rm(cwd, { recursive: true, force: true });