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.
- package/README.de.md +4 -4
- package/README.es.md +4 -4
- package/README.fr.md +4 -4
- package/README.it.md +4 -4
- package/README.ja.md +4 -4
- package/README.ko.md +4 -4
- package/README.md +13 -7
- package/README.pt.md +4 -4
- package/README.ru.md +4 -4
- package/README.tr.md +4 -4
- package/README.vi.md +4 -4
- package/README.zh-TW.md +4 -4
- package/README.zh.md +4 -4
- package/dist/agents/__tests__/native-config.test.js +37 -33
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/__tests__/skill-bridge.test.d.ts +2 -0
- package/dist/agents/__tests__/skill-bridge.test.d.ts.map +1 -0
- package/dist/agents/__tests__/skill-bridge.test.js +71 -0
- package/dist/agents/__tests__/skill-bridge.test.js.map +1 -0
- package/dist/agents/native-config.d.ts +18 -6
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +109 -92
- package/dist/agents/native-config.js.map +1 -1
- package/dist/agents/skill-bridge.d.ts +20 -0
- package/dist/agents/skill-bridge.d.ts.map +1 -0
- package/dist/agents/skill-bridge.js +150 -0
- package/dist/agents/skill-bridge.js.map +1 -0
- package/dist/autoresearch/__tests__/contracts.test.js +37 -1
- package/dist/autoresearch/__tests__/contracts.test.js.map +1 -1
- package/dist/autoresearch/__tests__/runtime-parity-extra.test.js +10 -10
- package/dist/autoresearch/__tests__/runtime-parity-extra.test.js.map +1 -1
- package/dist/autoresearch/__tests__/runtime.test.js +2 -2
- package/dist/autoresearch/__tests__/runtime.test.js.map +1 -1
- package/dist/autoresearch/contracts.d.ts.map +1 -1
- package/dist/autoresearch/contracts.js +17 -10
- package/dist/autoresearch/contracts.js.map +1 -1
- package/dist/autoresearch/runtime.d.ts.map +1 -1
- package/dist/autoresearch/runtime.js +71 -96
- package/dist/autoresearch/runtime.js.map +1 -1
- package/dist/cli/__tests__/agents-init.test.js +2 -0
- package/dist/cli/__tests__/agents-init.test.js.map +1 -1
- package/dist/cli/__tests__/agents.test.d.ts +2 -0
- package/dist/cli/__tests__/agents.test.d.ts.map +1 -0
- package/dist/cli/__tests__/agents.test.js +114 -0
- package/dist/cli/__tests__/agents.test.js.map +1 -0
- package/dist/cli/__tests__/autoresearch-guided.test.js +156 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch.test.js +195 -24
- package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
- package/dist/cli/__tests__/cleanup.test.d.ts +2 -0
- package/dist/cli/__tests__/cleanup.test.d.ts.map +1 -0
- package/dist/cli/__tests__/cleanup.test.js +213 -0
- package/dist/cli/__tests__/cleanup.test.js.map +1 -0
- package/dist/cli/__tests__/error-handling-warnings.test.js +1 -1
- package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +3 -3
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +521 -401
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/native-assets.test.js +72 -9
- package/dist/cli/__tests__/native-assets.test.js.map +1 -1
- package/dist/cli/__tests__/ralphthon.test.d.ts +2 -0
- package/dist/cli/__tests__/ralphthon.test.d.ts.map +1 -0
- package/dist/cli/__tests__/ralphthon.test.js +28 -0
- package/dist/cli/__tests__/ralphthon.test.js.map +1 -0
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +36 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js +35 -5
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +2 -2
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +131 -161
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +10 -10
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +28 -2
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +1 -112
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +7 -20
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/agents-init.d.ts.map +1 -1
- package/dist/cli/agents-init.js +99 -95
- package/dist/cli/agents-init.js.map +1 -1
- package/dist/cli/agents.d.ts +14 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +261 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/autoresearch-guided.d.ts +8 -0
- package/dist/cli/autoresearch-guided.d.ts.map +1 -1
- package/dist/cli/autoresearch-guided.js +104 -37
- package/dist/cli/autoresearch-guided.js.map +1 -1
- package/dist/cli/autoresearch-intake.d.ts +60 -0
- package/dist/cli/autoresearch-intake.d.ts.map +1 -0
- package/dist/cli/autoresearch-intake.js +318 -0
- package/dist/cli/autoresearch-intake.js.map +1 -0
- package/dist/cli/autoresearch.d.ts +3 -1
- package/dist/cli/autoresearch.d.ts.map +1 -1
- package/dist/cli/autoresearch.js +64 -10
- package/dist/cli/autoresearch.js.map +1 -1
- package/dist/cli/cleanup.d.ts +52 -0
- package/dist/cli/cleanup.d.ts.map +1 -0
- package/dist/cli/cleanup.js +302 -0
- package/dist/cli/cleanup.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +9 -37
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +5 -4
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +5 -7
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +610 -427
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/native-assets.d.ts +15 -1
- package/dist/cli/native-assets.d.ts.map +1 -1
- package/dist/cli/native-assets.js +134 -32
- package/dist/cli/native-assets.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +38 -1
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/ralphthon.d.ts +14 -0
- package/dist/cli/ralphthon.d.ts.map +1 -0
- package/dist/cli/ralphthon.js +234 -0
- package/dist/cli/ralphthon.js.map +1 -0
- package/dist/cli/setup.d.ts +1 -4
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +111 -76
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/sparkshell.d.ts +3 -1
- package/dist/cli/sparkshell.d.ts.map +1 -1
- package/dist/cli/sparkshell.js +35 -16
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +1 -0
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/uninstall.d.ts +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +82 -64
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +10 -10
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +15 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +0 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +53 -42
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +295 -230
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +49 -24
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher-ralphthon.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-fallback-watcher-ralphthon.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-fallback-watcher-ralphthon.test.js +193 -0
- package/dist/hooks/__tests__/notify-fallback-watcher-ralphthon.test.js.map +1 -0
- package/dist/hooks/agents-overlay.d.ts +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +109 -106
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/modes/base.d.ts +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +1 -1
- package/dist/modes/base.js.map +1 -1
- package/dist/ralphthon/__tests__/bootstrap.test.d.ts +2 -0
- package/dist/ralphthon/__tests__/bootstrap.test.d.ts.map +1 -0
- package/dist/ralphthon/__tests__/bootstrap.test.js +23 -0
- package/dist/ralphthon/__tests__/bootstrap.test.js.map +1 -0
- package/dist/ralphthon/__tests__/orchestrator.test.d.ts +2 -0
- package/dist/ralphthon/__tests__/orchestrator.test.d.ts.map +1 -0
- package/dist/ralphthon/__tests__/orchestrator.test.js +309 -0
- package/dist/ralphthon/__tests__/orchestrator.test.js.map +1 -0
- package/dist/ralphthon/__tests__/prd.test.d.ts +2 -0
- package/dist/ralphthon/__tests__/prd.test.d.ts.map +1 -0
- package/dist/ralphthon/__tests__/prd.test.js +133 -0
- package/dist/ralphthon/__tests__/prd.test.js.map +1 -0
- package/dist/ralphthon/bootstrap.d.ts +3 -0
- package/dist/ralphthon/bootstrap.d.ts.map +1 -0
- package/dist/ralphthon/bootstrap.js +84 -0
- package/dist/ralphthon/bootstrap.js.map +1 -0
- package/dist/ralphthon/orchestrator.d.ts +50 -0
- package/dist/ralphthon/orchestrator.d.ts.map +1 -0
- package/dist/ralphthon/orchestrator.js +362 -0
- package/dist/ralphthon/orchestrator.js.map +1 -0
- package/dist/ralphthon/prd.d.ts +191 -0
- package/dist/ralphthon/prd.d.ts.map +1 -0
- package/dist/ralphthon/prd.js +355 -0
- package/dist/ralphthon/prd.js.map +1 -0
- package/dist/ralphthon/runtime.d.ts +31 -0
- package/dist/ralphthon/runtime.d.ts.map +1 -0
- package/dist/ralphthon/runtime.js +104 -0
- package/dist/ralphthon/runtime.js.map +1 -0
- package/dist/ralphthon/tmux.d.ts +3 -0
- package/dist/ralphthon/tmux.d.ts.map +1 -0
- package/dist/ralphthon/tmux.js +39 -0
- package/dist/ralphthon/tmux.js.map +1 -0
- package/dist/subagents/__tests__/tracker.test.d.ts +2 -0
- package/dist/subagents/__tests__/tracker.test.d.ts.map +1 -0
- package/dist/subagents/__tests__/tracker.test.js +47 -0
- package/dist/subagents/__tests__/tracker.test.js.map +1 -0
- package/dist/subagents/tracker.d.ts +52 -0
- package/dist/subagents/tracker.d.ts.map +1 -0
- package/dist/subagents/tracker.js +175 -0
- package/dist/subagents/tracker.js.map +1 -0
- package/dist/team/__tests__/worker-bootstrap.test.js +189 -163
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/__tests__/worktree.test.js +1 -1
- package/dist/team/__tests__/worktree.test.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +1 -1
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +58 -63
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/team/worktree.js +1 -1
- package/dist/team/worktree.js.map +1 -1
- package/dist/utils/__tests__/agents-md.test.d.ts +2 -0
- package/dist/utils/__tests__/agents-md.test.d.ts.map +1 -0
- package/dist/utils/__tests__/agents-md.test.js +32 -0
- package/dist/utils/__tests__/agents-md.test.js.map +1 -0
- package/dist/utils/__tests__/agents-model-table.test.d.ts +2 -0
- package/dist/utils/__tests__/agents-model-table.test.d.ts.map +1 -0
- package/dist/utils/__tests__/agents-model-table.test.js +84 -0
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -0
- package/dist/utils/__tests__/paths.test.js +78 -83
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +10 -0
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/agents-model-table.d.ts +16 -0
- package/dist/utils/agents-model-table.d.ts.map +1 -0
- package/dist/utils/agents-model-table.js +83 -0
- package/dist/utils/agents-model-table.js.map +1 -0
- package/dist/utils/paths.d.ts +6 -6
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +31 -31
- package/dist/utils/paths.js.map +1 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js +21 -3
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
- package/dist/verification/__tests__/native-release-manifest.test.d.ts +2 -0
- package/dist/verification/__tests__/native-release-manifest.test.d.ts.map +1 -0
- package/dist/verification/__tests__/native-release-manifest.test.js +80 -0
- package/dist/verification/__tests__/native-release-manifest.test.js.map +1 -0
- package/package.json +1 -1
- package/prompts/executor.md +15 -0
- package/scripts/__tests__/smoke-packed-install.test.mjs +137 -8
- package/scripts/eval-adaptive-sort-optimization.py +24 -0
- package/scripts/eval-in-action-cat-shellout-demo.js +31 -0
- package/scripts/eval-ml-kaggle-model-optimization.py +29 -0
- package/scripts/eval-noisy-bayesopt-highdim.py +44 -0
- package/scripts/eval-noisy-latent-subspace-discovery.py +44 -0
- package/scripts/generate-native-release-manifest.mjs +14 -3
- package/scripts/notify-fallback-watcher.js +308 -6
- package/scripts/notify-hook.js +20 -0
- package/scripts/run-autoresearch-showcase.sh +75 -0
- package/scripts/smoke-packed-install.mjs +142 -10
- package/skills/deep-interview/SKILL.md +30 -1
- package/skills/omx-setup/SKILL.md +2 -2
- package/skills/skill/SKILL.md +32 -32
- package/skills/team/SKILL.md +6 -0
- package/skills/worker/SKILL.md +2 -2
- package/templates/AGENTS.md +97 -16
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { describe, it } from
|
|
2
|
-
import assert from
|
|
3
|
-
import { mkdtemp, readFile, writeFile, rm, mkdir } from
|
|
4
|
-
import { join } from
|
|
5
|
-
import { tmpdir } from
|
|
6
|
-
import { generateWorkerOverlay, applyWorkerOverlay, stripWorkerOverlay, writeTeamWorkerInstructionsFile, writeWorkerRoleInstructionsFile, removeTeamWorkerInstructionsFile, generateInitialInbox, generateTaskAssignmentInbox, generateShutdownInbox, generateTriggerMessage, generateMailboxTriggerMessage, generateLeaderMailboxTriggerMessage, } from
|
|
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 ===
|
|
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(
|
|
18
|
-
it(
|
|
19
|
-
const workerSkill = await readFile(join(process.cwd(),
|
|
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(
|
|
29
|
-
const overlay = generateWorkerOverlay(
|
|
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(
|
|
34
|
-
const overlay = generateWorkerOverlay(
|
|
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,
|
|
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, /
|
|
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(
|
|
51
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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,
|
|
54
|
-
await writeFile(agentsMdPath,
|
|
55
|
-
const overlay = generateWorkerOverlay(
|
|
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,
|
|
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(
|
|
68
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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,
|
|
71
|
-
await writeFile(agentsMdPath,
|
|
72
|
-
const overlay = generateWorkerOverlay(
|
|
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,
|
|
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(
|
|
86
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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,
|
|
89
|
-
const base =
|
|
90
|
-
const overlay = generateWorkerOverlay(
|
|
91
|
-
await writeFile(agentsMdPath, `${base}\n${overlay}\n`,
|
|
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,
|
|
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(
|
|
104
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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,
|
|
107
|
-
await writeFile(agentsMdPath,
|
|
108
|
-
const before = await readFile(agentsMdPath,
|
|
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,
|
|
111
|
+
const afterFirst = await readFile(agentsMdPath, "utf8");
|
|
111
112
|
await stripWorkerOverlay(agentsMdPath);
|
|
112
|
-
const afterSecond = await readFile(agentsMdPath,
|
|
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(
|
|
121
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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,
|
|
124
|
-
const overlay = generateWorkerOverlay(
|
|
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,
|
|
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(
|
|
135
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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,
|
|
138
|
-
const lockPath = join(cwd,
|
|
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,
|
|
141
|
-
await writeFile(agentsMdPath,
|
|
142
|
-
const overlay = generateWorkerOverlay(
|
|
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,
|
|
145
|
+
const content = await readFile(agentsMdPath, "utf8");
|
|
145
146
|
assert.match(content, /team "team-stale-lock"/);
|
|
146
|
-
await assert.rejects(readFile(join(lockPath,
|
|
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(
|
|
153
|
+
it("generateInitialInbox includes worker name, team name, and all tasks", () => {
|
|
153
154
|
const tasks = [
|
|
154
155
|
{
|
|
155
|
-
id:
|
|
156
|
-
subject:
|
|
157
|
-
description:
|
|
158
|
-
status:
|
|
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:
|
|
163
|
-
subject:
|
|
164
|
-
description:
|
|
165
|
-
status:
|
|
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(
|
|
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,
|
|
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(
|
|
191
|
+
it("generateInitialInbox shows blocked_by info for blocked tasks", () => {
|
|
191
192
|
const tasks = [
|
|
192
193
|
{
|
|
193
|
-
id:
|
|
194
|
-
subject:
|
|
195
|
-
description:
|
|
196
|
-
status:
|
|
197
|
-
blocked_by: [
|
|
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(
|
|
202
|
+
const inbox = generateInitialInbox("worker-2", "team-blocked", "executor", tasks);
|
|
202
203
|
assert.match(inbox, /Blocked by: 1, 2/);
|
|
203
204
|
});
|
|
204
|
-
it(
|
|
205
|
+
it("generateInitialInbox uses workerRole when provided", () => {
|
|
205
206
|
const tasks = [
|
|
206
|
-
{
|
|
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(
|
|
209
|
-
workerRole:
|
|
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(
|
|
221
|
+
it("generateInitialInbox includes specialization section when rolePromptContent provided", () => {
|
|
215
222
|
const tasks = [
|
|
216
|
-
{
|
|
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(
|
|
219
|
-
workerRole:
|
|
220
|
-
rolePromptContent:
|
|
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(
|
|
239
|
+
it("generateInitialInbox omits specialization section when no rolePromptContent", () => {
|
|
227
240
|
const tasks = [
|
|
228
|
-
{
|
|
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(
|
|
231
|
-
workerRole:
|
|
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(
|
|
254
|
+
it("generateInitialInbox shows task role in task list", () => {
|
|
236
255
|
const tasks = [
|
|
237
|
-
{
|
|
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(
|
|
265
|
+
const inbox = generateInitialInbox("worker-1", "team-task-role", "executor", tasks);
|
|
240
266
|
assert.match(inbox, /Role: test-engineer/);
|
|
241
267
|
});
|
|
242
|
-
it(
|
|
243
|
-
const inbox = generateTaskAssignmentInbox(
|
|
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(
|
|
256
|
-
const inbox = generateShutdownInbox(
|
|
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(
|
|
263
|
-
const message = generateTriggerMessage(
|
|
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(
|
|
267
|
-
const message = generateTriggerMessage(
|
|
268
|
-
assert.equal(message.includes(
|
|
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(
|
|
271
|
-
const message = generateTriggerMessage(
|
|
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(
|
|
279
|
-
const message = generateTriggerMessage(
|
|
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(
|
|
288
|
-
const message = generateMailboxTriggerMessage(
|
|
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(
|
|
292
|
-
const message = generateMailboxTriggerMessage(
|
|
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(
|
|
301
|
-
const message = generateMailboxTriggerMessage(
|
|
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(
|
|
311
|
-
const message = generateLeaderMailboxTriggerMessage(
|
|
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(
|
|
315
|
-
const message = generateLeaderMailboxTriggerMessage(
|
|
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(
|
|
321
|
-
const message = generateLeaderMailboxTriggerMessage(
|
|
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(
|
|
328
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
329
|
-
const restoreCodexHome = setMockCodexHome(join(cwd,
|
|
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,
|
|
332
|
-
await writeFile(join(cwd,
|
|
333
|
-
await writeFile(join(cwd,
|
|
334
|
-
const overlay = generateWorkerOverlay(
|
|
335
|
-
const outPath = await writeTeamWorkerInstructionsFile(
|
|
336
|
-
const content = await readFile(outPath,
|
|
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(
|
|
340
|
-
content.indexOf(
|
|
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,
|
|
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(
|
|
354
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
355
|
-
const restoreCodexHome = setMockCodexHome(join(cwd,
|
|
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,
|
|
358
|
-
const projectAgentsPath = join(cwd,
|
|
359
|
-
const userSkillDir = join(cwd,
|
|
360
|
-
const projectSkillDir = join(cwd,
|
|
361
|
-
await mkdir(join(cwd,
|
|
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,
|
|
365
|
-
await writeFile(join(projectSkillDir,
|
|
366
|
-
await writeFile(userAgentsPath,
|
|
367
|
-
await writeFile(projectAgentsPath,
|
|
368
|
-
const overlay = generateWorkerOverlay(
|
|
369
|
-
const outPath = await writeTeamWorkerInstructionsFile(
|
|
370
|
-
const content = await readFile(outPath,
|
|
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(
|
|
381
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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(
|
|
384
|
-
const basePath = await writeTeamWorkerInstructionsFile(
|
|
385
|
-
const outPath = await writeWorkerRoleInstructionsFile(
|
|
386
|
-
const content = await readFile(outPath,
|
|
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(
|
|
397
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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(
|
|
400
|
-
const outPath = await writeTeamWorkerInstructionsFile(
|
|
401
|
-
const content = await readFile(outPath,
|
|
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(
|
|
410
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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(
|
|
413
|
-
await writeTeamWorkerInstructionsFile(
|
|
414
|
-
await removeTeamWorkerInstructionsFile(
|
|
415
|
-
const { existsSync } = await import(
|
|
416
|
-
const outPath = join(cwd,
|
|
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(
|
|
424
|
-
const cwd = await mkdtemp(join(tmpdir(),
|
|
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(
|
|
453
|
+
await removeTeamWorkerInstructionsFile("nonexistent-team", cwd);
|
|
428
454
|
}
|
|
429
455
|
finally {
|
|
430
456
|
await rm(cwd, { recursive: true, force: true });
|