oh-my-codex 0.10.2 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +2 -2
- package/Cargo.toml +1 -1
- 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/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/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 +84 -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 +483 -25
- 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 +530 -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__/ralph-deslop-contract.test.d.ts +2 -0
- package/dist/cli/__tests__/ralph-deslop-contract.test.d.ts.map +1 -0
- package/dist/cli/__tests__/ralph-deslop-contract.test.js +28 -0
- package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -0
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.js +4 -0
- package/dist/cli/__tests__/ralph-prd-deep-interview.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 +62 -0
- package/dist/cli/autoresearch-intake.d.ts.map +1 -0
- package/dist/cli/autoresearch-intake.js +336 -0
- package/dist/cli/autoresearch-intake.js.map +1 -0
- package/dist/cli/autoresearch.d.ts +4 -1
- package/dist/cli/autoresearch.d.ts.map +1 -1
- package/dist/cli/autoresearch.js +181 -32
- 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 +623 -451
- 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__/anti-slop-workflow.test.js +3 -0
- package/dist/hooks/__tests__/anti-slop-workflow.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/hud/constants.d.ts +1 -1
- package/dist/hud/constants.js +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/notifications/__tests__/formatter.test.js +36 -2
- package/dist/notifications/__tests__/formatter.test.js.map +1 -1
- package/dist/notifications/formatter.d.ts +3 -2
- package/dist/notifications/formatter.d.ts.map +1 -1
- package/dist/notifications/formatter.js +33 -9
- package/dist/notifications/formatter.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 +359 -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 +108 -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__/runtime.test.js +2 -2
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +34 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- 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/tmux-session.d.ts +4 -4
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +48 -15
- package/dist/team/tmux-session.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/ai-slop-cleaner/SKILL.md +7 -0
- package/skills/deep-interview/SKILL.md +30 -1
- package/skills/omx-setup/SKILL.md +2 -2
- package/skills/ralph/SKILL.md +15 -0
- 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
- package/dist/cli/__tests__/runtime-native-resolution.test.d.ts +0 -2
- package/dist/cli/__tests__/runtime-native-resolution.test.d.ts.map +0 -1
- package/dist/cli/__tests__/runtime-native-resolution.test.js.map +0 -1
- package/dist/cli/__tests__/runtime-native.test.d.ts +0 -2
- package/dist/cli/__tests__/runtime-native.test.d.ts.map +0 -1
- package/dist/cli/__tests__/runtime-native.test.js.map +0 -1
- package/dist/cli/runtime-native.d.ts +0 -23
- package/dist/cli/runtime-native.d.ts.map +0 -1
- package/dist/cli/runtime-native.js +0 -86
- package/dist/cli/runtime-native.js.map +0 -1
- package/dist/mcp/__tests__/runtime-run-native-cutover.test.d.ts +0 -2
- package/dist/mcp/__tests__/runtime-run-native-cutover.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/runtime-run-native-cutover.test.js.map +0 -1
|
@@ -4,213 +4,255 @@
|
|
|
4
4
|
* Covers: overlay generation, apply/strip roundtrip, idempotency,
|
|
5
5
|
* size cap enforcement, and graceful handling of missing state.
|
|
6
6
|
*/
|
|
7
|
-
import { describe, it, before, after } from
|
|
8
|
-
import assert from
|
|
9
|
-
import { mkdtemp, rm, mkdir, writeFile, readFile } from
|
|
10
|
-
import { existsSync } from
|
|
11
|
-
import { join } from
|
|
12
|
-
import { tmpdir } from
|
|
13
|
-
import { generateOverlay, applyOverlay, stripOverlay, hasOverlay, resolveSessionOrchestrationMode, writeSessionModelInstructionsFile, removeSessionModelInstructionsFile, sessionModelInstructionsPath, } from
|
|
14
|
-
const RUNTIME_START =
|
|
15
|
-
const RUNTIME_END =
|
|
16
|
-
const WORKER_START =
|
|
17
|
-
const WORKER_END =
|
|
7
|
+
import { describe, it, before, after } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { mkdtemp, rm, mkdir, writeFile, readFile } from "fs/promises";
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { tmpdir } from "os";
|
|
13
|
+
import { generateOverlay, applyOverlay, stripOverlay, hasOverlay, resolveSessionOrchestrationMode, writeSessionModelInstructionsFile, removeSessionModelInstructionsFile, sessionModelInstructionsPath, } from "../agents-overlay.js";
|
|
14
|
+
const RUNTIME_START = "<!-- OMX:RUNTIME:START -->";
|
|
15
|
+
const RUNTIME_END = "<!-- OMX:RUNTIME:END -->";
|
|
16
|
+
const WORKER_START = "<!-- OMX:TEAM:WORKER:START -->";
|
|
17
|
+
const WORKER_END = "<!-- OMX:TEAM:WORKER:END -->";
|
|
18
18
|
async function makeTempDir() {
|
|
19
|
-
const dir = await mkdtemp(join(tmpdir(),
|
|
20
|
-
await mkdir(join(dir,
|
|
19
|
+
const dir = await mkdtemp(join(tmpdir(), "omx-overlay-test-"));
|
|
20
|
+
await mkdir(join(dir, ".omx", "state"), { recursive: true });
|
|
21
21
|
return dir;
|
|
22
22
|
}
|
|
23
23
|
function setMockCodexHome(codexHomePath) {
|
|
24
24
|
const previous = process.env.CODEX_HOME;
|
|
25
25
|
process.env.CODEX_HOME = codexHomePath;
|
|
26
26
|
return () => {
|
|
27
|
-
if (typeof previous ===
|
|
27
|
+
if (typeof previous === "string")
|
|
28
28
|
process.env.CODEX_HOME = previous;
|
|
29
29
|
else
|
|
30
30
|
delete process.env.CODEX_HOME;
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
describe(
|
|
33
|
+
describe("generateOverlay", () => {
|
|
34
34
|
let tempDir;
|
|
35
|
-
before(async () => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
before(async () => {
|
|
36
|
+
tempDir = await makeTempDir();
|
|
37
|
+
});
|
|
38
|
+
after(async () => {
|
|
39
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
40
|
+
});
|
|
41
|
+
it("generates overlay with no state files (empty but valid)", async () => {
|
|
42
|
+
const overlay = await generateOverlay(tempDir, "test-session-1");
|
|
43
|
+
assert.ok(overlay.includes("<!-- OMX:RUNTIME:START -->"));
|
|
44
|
+
assert.ok(overlay.includes("<!-- OMX:RUNTIME:END -->"));
|
|
45
|
+
assert.ok(overlay.includes("test-session-1"));
|
|
46
|
+
assert.ok(overlay.includes("Compaction Protocol"));
|
|
47
|
+
});
|
|
48
|
+
it("includes the team orchestrator overlay only when orchestration mode is team", async () => {
|
|
49
|
+
const overlay = await generateOverlay(tempDir, "team-session", {
|
|
50
|
+
orchestrationMode: "team",
|
|
51
|
+
});
|
|
46
52
|
assert.match(overlay, /\*\*Orchestration Mode:\*\* team/);
|
|
47
53
|
assert.match(overlay, /supervised, high-overhead coordination surface/i);
|
|
48
|
-
const defaultOverlay = await generateOverlay(tempDir,
|
|
54
|
+
const defaultOverlay = await generateOverlay(tempDir, "default-session", {
|
|
55
|
+
orchestrationMode: "default",
|
|
56
|
+
});
|
|
49
57
|
assert.doesNotMatch(defaultOverlay, /\*\*Orchestration Mode:\*\* team/);
|
|
50
58
|
});
|
|
51
|
-
it(
|
|
59
|
+
it("adds advisory explore routing guidance only when USE_OMX_EXPLORE_CMD is enabled", async () => {
|
|
52
60
|
const previous = process.env.USE_OMX_EXPLORE_CMD;
|
|
53
61
|
try {
|
|
54
62
|
delete process.env.USE_OMX_EXPLORE_CMD;
|
|
55
|
-
const disabledOverlay = await generateOverlay(tempDir,
|
|
63
|
+
const disabledOverlay = await generateOverlay(tempDir, "explore-routing-off");
|
|
56
64
|
assert.doesNotMatch(disabledOverlay, /\*\*Explore Command Preference:\*\*/);
|
|
57
|
-
process.env.USE_OMX_EXPLORE_CMD =
|
|
58
|
-
const enabledOverlay = await generateOverlay(tempDir,
|
|
65
|
+
process.env.USE_OMX_EXPLORE_CMD = "1";
|
|
66
|
+
const enabledOverlay = await generateOverlay(tempDir, "explore-routing-on");
|
|
59
67
|
assert.match(enabledOverlay, /\*\*Explore Command Preference:\*\* enabled via `USE_OMX_EXPLORE_CMD`/);
|
|
60
68
|
assert.match(enabledOverlay, /strongly prefer `omx explore`/);
|
|
61
69
|
assert.match(enabledOverlay, /advisory steering/i);
|
|
62
70
|
}
|
|
63
71
|
finally {
|
|
64
|
-
if (typeof previous ===
|
|
72
|
+
if (typeof previous === "string")
|
|
65
73
|
process.env.USE_OMX_EXPLORE_CMD = previous;
|
|
66
74
|
else
|
|
67
75
|
delete process.env.USE_OMX_EXPLORE_CMD;
|
|
68
76
|
}
|
|
69
77
|
});
|
|
70
|
-
it(
|
|
71
|
-
const sessionId =
|
|
72
|
-
const sessionDir = join(tempDir,
|
|
78
|
+
it("generates overlay with active modes", async () => {
|
|
79
|
+
const sessionId = "test-session-2";
|
|
80
|
+
const sessionDir = join(tempDir, ".omx", "state", "sessions", sessionId);
|
|
73
81
|
await mkdir(sessionDir, { recursive: true });
|
|
74
|
-
await writeFile(join(sessionDir,
|
|
82
|
+
await writeFile(join(sessionDir, "ralph-state.json"), JSON.stringify({
|
|
83
|
+
active: true,
|
|
84
|
+
iteration: 3,
|
|
85
|
+
max_iterations: 10,
|
|
86
|
+
current_phase: "executing",
|
|
87
|
+
}));
|
|
75
88
|
const overlay = await generateOverlay(tempDir, sessionId);
|
|
76
|
-
assert.ok(overlay.includes(
|
|
77
|
-
assert.ok(overlay.includes(
|
|
78
|
-
});
|
|
79
|
-
it(
|
|
80
|
-
await mkdir(join(tempDir,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
assert.ok(overlay.includes(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
assert.ok(overlay.includes("ralph"));
|
|
90
|
+
assert.ok(overlay.includes("iteration 3/10"));
|
|
91
|
+
});
|
|
92
|
+
it("generates overlay with session-scoped active modes for current session", async () => {
|
|
93
|
+
await mkdir(join(tempDir, ".omx", "state", "sessions", "sess1"), {
|
|
94
|
+
recursive: true,
|
|
95
|
+
});
|
|
96
|
+
await writeFile(join(tempDir, ".omx", "state", "sessions", "sess1", "team-state.json"), JSON.stringify({
|
|
97
|
+
active: true,
|
|
98
|
+
iteration: 1,
|
|
99
|
+
max_iterations: 5,
|
|
100
|
+
current_phase: "running",
|
|
101
|
+
}));
|
|
102
|
+
const overlay = await generateOverlay(tempDir, "sess1");
|
|
103
|
+
assert.ok(overlay.includes("team"));
|
|
104
|
+
assert.ok(overlay.includes("iteration 1/5"));
|
|
105
|
+
});
|
|
106
|
+
it("generates overlay with notepad priority content", async () => {
|
|
107
|
+
await writeFile(join(tempDir, ".omx", "notepad.md"), "## PRIORITY\nFocus on auth module refactor.\n\n## WORKING\nSome working notes.");
|
|
108
|
+
const overlay = await generateOverlay(tempDir, "test-session-3");
|
|
109
|
+
assert.ok(overlay.includes("Focus on auth module refactor"));
|
|
110
|
+
assert.ok(overlay.includes("Priority Notes"));
|
|
111
|
+
});
|
|
112
|
+
it("generates overlay with project memory summary", async () => {
|
|
113
|
+
await writeFile(join(tempDir, ".omx", "project-memory.json"), JSON.stringify({
|
|
114
|
+
techStack: "TypeScript + Node.js",
|
|
115
|
+
conventions: "ESM modules, strict mode",
|
|
116
|
+
build: "npx tsc",
|
|
97
117
|
directives: [
|
|
98
|
-
{ directive:
|
|
99
|
-
{ directive:
|
|
118
|
+
{ directive: "Always use strict TypeScript", priority: "high" },
|
|
119
|
+
{ directive: "Low priority thing", priority: "normal" },
|
|
100
120
|
],
|
|
101
121
|
}));
|
|
102
|
-
const overlay = await generateOverlay(tempDir,
|
|
103
|
-
assert.ok(overlay.includes(
|
|
104
|
-
assert.ok(overlay.includes(
|
|
105
|
-
assert.ok(!overlay.includes(
|
|
106
|
-
});
|
|
107
|
-
it(
|
|
108
|
-
const longText =
|
|
109
|
-
await writeFile(join(tempDir,
|
|
110
|
-
await writeFile(join(tempDir,
|
|
111
|
-
|
|
122
|
+
const overlay = await generateOverlay(tempDir, "test-session-4");
|
|
123
|
+
assert.ok(overlay.includes("TypeScript + Node.js"));
|
|
124
|
+
assert.ok(overlay.includes("Always use strict TypeScript"));
|
|
125
|
+
assert.ok(!overlay.includes("Low priority thing"));
|
|
126
|
+
});
|
|
127
|
+
it("enforces size cap (overlay <= 3500 chars)", async () => {
|
|
128
|
+
const longText = "A".repeat(5000);
|
|
129
|
+
await writeFile(join(tempDir, ".omx", "notepad.md"), `## PRIORITY\n${longText}`);
|
|
130
|
+
await writeFile(join(tempDir, ".omx", "project-memory.json"), JSON.stringify({
|
|
131
|
+
techStack: "B".repeat(2000),
|
|
132
|
+
conventions: "C".repeat(2000),
|
|
133
|
+
}));
|
|
134
|
+
const overlay = await generateOverlay(tempDir, "test-session-5");
|
|
112
135
|
assert.ok(overlay.length <= 3500, `Overlay too large: ${overlay.length} chars`);
|
|
113
|
-
assert.ok(overlay.includes(
|
|
114
|
-
assert.ok(overlay.includes(
|
|
136
|
+
assert.ok(overlay.includes("<!-- OMX:RUNTIME:START -->"));
|
|
137
|
+
assert.ok(overlay.includes("<!-- OMX:RUNTIME:END -->"));
|
|
115
138
|
});
|
|
116
|
-
it(
|
|
117
|
-
const sessionId =
|
|
118
|
-
const sessionDir = join(tempDir,
|
|
139
|
+
it("uses deterministic overflow policy under size cap", async () => {
|
|
140
|
+
const sessionId = "overflow-session";
|
|
141
|
+
const sessionDir = join(tempDir, ".omx", "state", "sessions", sessionId);
|
|
119
142
|
await mkdir(sessionDir, { recursive: true });
|
|
120
143
|
// Inflate optional sections so overflow behavior is exercised.
|
|
121
144
|
// Per-section truncation limits mean the total max body (~2640 chars) fits
|
|
122
145
|
// within MAX_OVERLAY_SIZE (3500), so we verify: size cap, required sections
|
|
123
146
|
// present, and determinism (identical output on repeated calls).
|
|
124
147
|
for (let i = 0; i < 40; i++) {
|
|
125
|
-
await writeFile(join(sessionDir, `mode-${i}-state.json`), JSON.stringify({
|
|
148
|
+
await writeFile(join(sessionDir, `mode-${i}-state.json`), JSON.stringify({
|
|
149
|
+
active: true,
|
|
150
|
+
iteration: i + 1,
|
|
151
|
+
max_iterations: 99,
|
|
152
|
+
current_phase: "run",
|
|
153
|
+
}));
|
|
126
154
|
}
|
|
127
|
-
await writeFile(join(tempDir,
|
|
128
|
-
await writeFile(join(tempDir,
|
|
129
|
-
techStack:
|
|
130
|
-
conventions:
|
|
131
|
-
directives: [{ directive:
|
|
155
|
+
await writeFile(join(tempDir, ".omx", "notepad.md"), `## PRIORITY\n${"N".repeat(8000)}`);
|
|
156
|
+
await writeFile(join(tempDir, ".omx", "project-memory.json"), JSON.stringify({
|
|
157
|
+
techStack: "T".repeat(9000),
|
|
158
|
+
conventions: "C".repeat(9000),
|
|
159
|
+
directives: [{ directive: "D".repeat(3000), priority: "high" }],
|
|
132
160
|
}));
|
|
133
161
|
const overlay1 = await generateOverlay(tempDir, sessionId);
|
|
134
162
|
const overlay2 = await generateOverlay(tempDir, sessionId);
|
|
135
163
|
for (const overlay of [overlay1, overlay2]) {
|
|
136
164
|
assert.ok(overlay.length <= 3500, `Overlay too large: ${overlay.length} chars`);
|
|
137
|
-
assert.ok(overlay.includes(
|
|
138
|
-
assert.ok(overlay.includes(
|
|
139
|
-
assert.ok(overlay.includes(
|
|
165
|
+
assert.ok(overlay.includes("**Active Modes:**"));
|
|
166
|
+
assert.ok(overlay.includes("**Priority Notes:**"));
|
|
167
|
+
assert.ok(overlay.includes("**Compaction Protocol:**"));
|
|
140
168
|
}
|
|
141
169
|
});
|
|
142
|
-
it(
|
|
143
|
-
await writeFile(join(tempDir,
|
|
144
|
-
const overlay = await generateOverlay(tempDir,
|
|
145
|
-
assert.ok(!overlay.includes(
|
|
170
|
+
it("skips inactive modes", async () => {
|
|
171
|
+
await writeFile(join(tempDir, ".omx", "state", "autopilot-state.json"), JSON.stringify({ active: false, current_phase: "cancelled" }));
|
|
172
|
+
const overlay = await generateOverlay(tempDir, "test-session-6");
|
|
173
|
+
assert.ok(!overlay.includes("autopilot"));
|
|
146
174
|
});
|
|
147
|
-
it(
|
|
148
|
-
const sessionId =
|
|
149
|
-
const sessionDir = join(tempDir,
|
|
175
|
+
it("adds blocked ralph planning gate when PRD/test spec are missing", async () => {
|
|
176
|
+
const sessionId = "ralph-gate-blocked";
|
|
177
|
+
const sessionDir = join(tempDir, ".omx", "state", "sessions", sessionId);
|
|
150
178
|
await mkdir(sessionDir, { recursive: true });
|
|
151
|
-
await writeFile(join(sessionDir,
|
|
152
|
-
|
|
179
|
+
await writeFile(join(sessionDir, "ralph-state.json"), JSON.stringify({
|
|
180
|
+
active: true,
|
|
181
|
+
iteration: 0,
|
|
182
|
+
max_iterations: 50,
|
|
183
|
+
current_phase: "starting",
|
|
184
|
+
}));
|
|
185
|
+
await mkdir(join(tempDir, ".omx", "plans"), { recursive: true });
|
|
153
186
|
const overlay = await generateOverlay(tempDir, sessionId);
|
|
154
187
|
assert.match(overlay, /\*\*Ralph Ralplan-First Gate:\*\* BLOCKED/);
|
|
155
188
|
assert.match(overlay, /`prd-\*\.md`/);
|
|
156
189
|
assert.match(overlay, /`test-spec-\*\.md`/);
|
|
157
190
|
});
|
|
158
|
-
it(
|
|
159
|
-
const sessionId =
|
|
160
|
-
const sessionDir = join(tempDir,
|
|
191
|
+
it("unlocks ralph planning gate when PRD and test spec exist", async () => {
|
|
192
|
+
const sessionId = "ralph-gate-unlocked";
|
|
193
|
+
const sessionDir = join(tempDir, ".omx", "state", "sessions", sessionId);
|
|
161
194
|
await mkdir(sessionDir, { recursive: true });
|
|
162
|
-
await writeFile(join(sessionDir,
|
|
163
|
-
|
|
195
|
+
await writeFile(join(sessionDir, "ralph-state.json"), JSON.stringify({
|
|
196
|
+
active: true,
|
|
197
|
+
iteration: 1,
|
|
198
|
+
max_iterations: 50,
|
|
199
|
+
current_phase: "starting",
|
|
200
|
+
}));
|
|
201
|
+
const plansDir = join(tempDir, ".omx", "plans");
|
|
164
202
|
await mkdir(plansDir, { recursive: true });
|
|
165
|
-
await writeFile(join(plansDir,
|
|
166
|
-
await writeFile(join(plansDir,
|
|
203
|
+
await writeFile(join(plansDir, "prd-issue-259.md"), "# PRD\n");
|
|
204
|
+
await writeFile(join(plansDir, "test-spec-issue-259.md"), "# Test Spec\n");
|
|
167
205
|
const overlay = await generateOverlay(tempDir, sessionId);
|
|
168
206
|
assert.match(overlay, /\*\*Ralph Ralplan-First Gate:\*\* UNLOCKED/);
|
|
169
207
|
assert.match(overlay, /Planning artifacts present: PRD \+ test spec/);
|
|
170
208
|
});
|
|
171
209
|
});
|
|
172
|
-
describe(
|
|
210
|
+
describe("resolveSessionOrchestrationMode", () => {
|
|
173
211
|
let tempDir;
|
|
174
|
-
before(async () => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
});
|
|
180
|
-
it(
|
|
181
|
-
const
|
|
182
|
-
|
|
212
|
+
before(async () => {
|
|
213
|
+
tempDir = await makeTempDir();
|
|
214
|
+
});
|
|
215
|
+
after(async () => {
|
|
216
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
217
|
+
});
|
|
218
|
+
it("uses explicit activeSkill when provided", async () => {
|
|
219
|
+
const mode = await resolveSessionOrchestrationMode(tempDir, "sess-explicit", "team");
|
|
220
|
+
assert.equal(mode, "team");
|
|
221
|
+
});
|
|
222
|
+
it("reads persisted team skill state from the current session scope", async () => {
|
|
223
|
+
const sessionId = "sess-team";
|
|
224
|
+
const sessionDir = join(tempDir, ".omx", "state", "sessions", sessionId);
|
|
183
225
|
await mkdir(sessionDir, { recursive: true });
|
|
184
|
-
await writeFile(join(sessionDir,
|
|
226
|
+
await writeFile(join(sessionDir, "skill-active-state.json"), JSON.stringify({ active: true, skill: "team" }));
|
|
185
227
|
const mode = await resolveSessionOrchestrationMode(tempDir, sessionId);
|
|
186
|
-
assert.equal(mode,
|
|
228
|
+
assert.equal(mode, "team");
|
|
187
229
|
});
|
|
188
|
-
it(
|
|
189
|
-
const sessionId =
|
|
190
|
-
const sessionDir = join(tempDir,
|
|
230
|
+
it("falls back to default mode for non-team skill state", async () => {
|
|
231
|
+
const sessionId = "sess-autopilot";
|
|
232
|
+
const sessionDir = join(tempDir, ".omx", "state", "sessions", sessionId);
|
|
191
233
|
await mkdir(sessionDir, { recursive: true });
|
|
192
|
-
await writeFile(join(sessionDir,
|
|
234
|
+
await writeFile(join(sessionDir, "skill-active-state.json"), JSON.stringify({ active: true, skill: "autopilot" }));
|
|
193
235
|
const mode = await resolveSessionOrchestrationMode(tempDir, sessionId);
|
|
194
|
-
assert.equal(mode,
|
|
236
|
+
assert.equal(mode, "default");
|
|
195
237
|
});
|
|
196
|
-
it(
|
|
197
|
-
const sessionId =
|
|
198
|
-
const rootStatePath = join(tempDir,
|
|
199
|
-
const sessionDir = join(tempDir,
|
|
238
|
+
it("does not resurrect stale root team skill state when session-scoped skill state is inactive", async () => {
|
|
239
|
+
const sessionId = "sess-team-complete";
|
|
240
|
+
const rootStatePath = join(tempDir, ".omx", "state", "skill-active-state.json");
|
|
241
|
+
const sessionDir = join(tempDir, ".omx", "state", "sessions", sessionId);
|
|
200
242
|
await mkdir(sessionDir, { recursive: true });
|
|
201
|
-
await writeFile(rootStatePath, JSON.stringify({ active: true, skill:
|
|
202
|
-
await writeFile(join(sessionDir,
|
|
243
|
+
await writeFile(rootStatePath, JSON.stringify({ active: true, skill: "team" }));
|
|
244
|
+
await writeFile(join(sessionDir, "skill-active-state.json"), JSON.stringify({ active: false, skill: "team", phase: "completing" }));
|
|
203
245
|
const mode = await resolveSessionOrchestrationMode(tempDir, sessionId);
|
|
204
|
-
assert.equal(mode,
|
|
246
|
+
assert.equal(mode, "default");
|
|
205
247
|
});
|
|
206
|
-
it(
|
|
207
|
-
const sessionId =
|
|
208
|
-
await writeFile(join(tempDir,
|
|
248
|
+
it("falls back to root team skill state only when no session-scoped skill state exists", async () => {
|
|
249
|
+
const sessionId = "sess-root-fallback";
|
|
250
|
+
await writeFile(join(tempDir, ".omx", "state", "skill-active-state.json"), JSON.stringify({ active: true, skill: "team" }));
|
|
209
251
|
const mode = await resolveSessionOrchestrationMode(tempDir, sessionId);
|
|
210
|
-
assert.equal(mode,
|
|
252
|
+
assert.equal(mode, "team");
|
|
211
253
|
});
|
|
212
254
|
});
|
|
213
|
-
describe(
|
|
255
|
+
describe("applyOverlay + stripOverlay roundtrip", () => {
|
|
214
256
|
let tempDir;
|
|
215
257
|
const originalContent = `# My AGENTS.md
|
|
216
258
|
|
|
@@ -219,65 +261,88 @@ This is the original content.
|
|
|
219
261
|
## Section 1
|
|
220
262
|
Some instructions here.
|
|
221
263
|
`;
|
|
222
|
-
before(async () => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
264
|
+
before(async () => {
|
|
265
|
+
tempDir = await makeTempDir();
|
|
266
|
+
});
|
|
267
|
+
after(async () => {
|
|
268
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
269
|
+
});
|
|
270
|
+
it("apply then strip restores original (roundtrip)", async () => {
|
|
271
|
+
const agentsMd = join(tempDir, "AGENTS.md");
|
|
226
272
|
await writeFile(agentsMd, originalContent);
|
|
227
|
-
const overlay = await generateOverlay(tempDir,
|
|
273
|
+
const overlay = await generateOverlay(tempDir, "roundtrip-test");
|
|
228
274
|
await applyOverlay(agentsMd, overlay, tempDir);
|
|
229
|
-
const withOverlay = await readFile(agentsMd,
|
|
275
|
+
const withOverlay = await readFile(agentsMd, "utf-8");
|
|
230
276
|
assert.ok(hasOverlay(withOverlay));
|
|
231
|
-
assert.ok(withOverlay.includes(
|
|
277
|
+
assert.ok(withOverlay.includes("roundtrip-test"));
|
|
232
278
|
await stripOverlay(agentsMd, tempDir);
|
|
233
|
-
const restored = await readFile(agentsMd,
|
|
279
|
+
const restored = await readFile(agentsMd, "utf-8");
|
|
234
280
|
assert.ok(!hasOverlay(restored));
|
|
235
281
|
assert.equal(restored.trim(), originalContent.trim());
|
|
236
282
|
});
|
|
237
|
-
it(
|
|
238
|
-
const agentsMd = join(tempDir,
|
|
283
|
+
it("stripOverlay preserves a top-of-file autonomy directive header", async () => {
|
|
284
|
+
const agentsMd = join(tempDir, "AGENTS-autonomy.md");
|
|
285
|
+
const autonomyContent = `<!-- AUTONOMY DIRECTIVE — DO NOT REMOVE -->
|
|
286
|
+
YOU ARE AN AUTONOMOUS CODING AGENT. EXECUTE TASKS TO COMPLETION WITHOUT ASKING FOR PERMISSION.
|
|
287
|
+
DO NOT STOP TO ASK "SHOULD I PROCEED?" — PROCEED. DO NOT WAIT FOR CONFIRMATION ON OBVIOUS NEXT STEPS.
|
|
288
|
+
IF BLOCKED, TRY AN ALTERNATIVE APPROACH. ONLY ASK WHEN TRULY AMBIGUOUS OR DESTRUCTIVE.
|
|
289
|
+
<!-- END AUTONOMY DIRECTIVE -->
|
|
290
|
+
|
|
291
|
+
# oh-my-codex - Intelligent Multi-Agent Orchestration
|
|
292
|
+
`;
|
|
293
|
+
await writeFile(agentsMd, autonomyContent);
|
|
294
|
+
const overlay = await generateOverlay(tempDir, "autonomy-header");
|
|
295
|
+
await applyOverlay(agentsMd, overlay, tempDir);
|
|
296
|
+
await stripOverlay(agentsMd, tempDir);
|
|
297
|
+
const restored = await readFile(agentsMd, "utf-8");
|
|
298
|
+
assert.equal(restored, autonomyContent);
|
|
299
|
+
});
|
|
300
|
+
it("applyOverlay is idempotent (apply twice, no duplication)", async () => {
|
|
301
|
+
const agentsMd = join(tempDir, "AGENTS-idem.md");
|
|
239
302
|
await writeFile(agentsMd, originalContent);
|
|
240
|
-
const overlay = await generateOverlay(tempDir,
|
|
303
|
+
const overlay = await generateOverlay(tempDir, "idempotent-test");
|
|
241
304
|
await applyOverlay(agentsMd, overlay, tempDir);
|
|
242
|
-
const firstApply = await readFile(agentsMd,
|
|
305
|
+
const firstApply = await readFile(agentsMd, "utf-8");
|
|
243
306
|
await applyOverlay(agentsMd, overlay, tempDir);
|
|
244
|
-
const secondApply = await readFile(agentsMd,
|
|
307
|
+
const secondApply = await readFile(agentsMd, "utf-8");
|
|
245
308
|
assert.equal(secondApply, firstApply);
|
|
246
|
-
const startCount = (secondApply.match(/<!-- OMX:RUNTIME:START -->/g) || [])
|
|
309
|
+
const startCount = (secondApply.match(/<!-- OMX:RUNTIME:START -->/g) || [])
|
|
310
|
+
.length;
|
|
247
311
|
assert.equal(startCount, 1);
|
|
248
312
|
});
|
|
249
|
-
it(
|
|
250
|
-
const agentsMd = join(tempDir,
|
|
313
|
+
it("handles stale markers from previous session", async () => {
|
|
314
|
+
const agentsMd = join(tempDir, "AGENTS-stale.md");
|
|
251
315
|
const staleContent = originalContent +
|
|
252
|
-
|
|
316
|
+
"\n<!-- OMX:RUNTIME:START -->\n<session_context>\nOld stale content\n</session_context>\n<!-- OMX:RUNTIME:END -->\n";
|
|
253
317
|
await writeFile(agentsMd, staleContent);
|
|
254
|
-
const overlay = await generateOverlay(tempDir,
|
|
318
|
+
const overlay = await generateOverlay(tempDir, "fresh-session");
|
|
255
319
|
await applyOverlay(agentsMd, overlay, tempDir);
|
|
256
|
-
const result = await readFile(agentsMd,
|
|
257
|
-
assert.ok(result.includes(
|
|
258
|
-
assert.ok(!result.includes(
|
|
259
|
-
const startCount = (result.match(/<!-- OMX:RUNTIME:START -->/g) || [])
|
|
320
|
+
const result = await readFile(agentsMd, "utf-8");
|
|
321
|
+
assert.ok(result.includes("fresh-session"));
|
|
322
|
+
assert.ok(!result.includes("Old stale content"));
|
|
323
|
+
const startCount = (result.match(/<!-- OMX:RUNTIME:START -->/g) || [])
|
|
324
|
+
.length;
|
|
260
325
|
assert.equal(startCount, 1);
|
|
261
326
|
});
|
|
262
|
-
it(
|
|
263
|
-
const agentsMd = join(tempDir,
|
|
327
|
+
it("stripOverlay is no-op when no overlay exists", async () => {
|
|
328
|
+
const agentsMd = join(tempDir, "AGENTS-noop.md");
|
|
264
329
|
await writeFile(agentsMd, originalContent);
|
|
265
330
|
await stripOverlay(agentsMd, tempDir);
|
|
266
|
-
const result = await readFile(agentsMd,
|
|
331
|
+
const result = await readFile(agentsMd, "utf-8");
|
|
267
332
|
assert.equal(result, originalContent);
|
|
268
333
|
});
|
|
269
|
-
it(
|
|
270
|
-
const agentsMd = join(tempDir,
|
|
271
|
-
const overlay = await generateOverlay(tempDir,
|
|
334
|
+
it("creates AGENTS.md if it does not exist during apply", async () => {
|
|
335
|
+
const agentsMd = join(tempDir, "AGENTS-new.md");
|
|
336
|
+
const overlay = await generateOverlay(tempDir, "new-file-test");
|
|
272
337
|
await applyOverlay(agentsMd, overlay, tempDir);
|
|
273
|
-
const result = await readFile(agentsMd,
|
|
338
|
+
const result = await readFile(agentsMd, "utf-8");
|
|
274
339
|
assert.ok(hasOverlay(result));
|
|
275
|
-
assert.ok(result.includes(
|
|
340
|
+
assert.ok(result.includes("new-file-test"));
|
|
276
341
|
});
|
|
277
|
-
it(
|
|
278
|
-
const agentsMd = join(tempDir,
|
|
342
|
+
it("stripOverlay removes runtime overlay and preserves worker overlay (runtime->worker order)", async () => {
|
|
343
|
+
const agentsMd = join(tempDir, "AGENTS-stacked-rw.md");
|
|
279
344
|
await writeFile(agentsMd, originalContent);
|
|
280
|
-
const runtimeOverlay = await generateOverlay(tempDir,
|
|
345
|
+
const runtimeOverlay = await generateOverlay(tempDir, "stacked-rw");
|
|
281
346
|
await applyOverlay(agentsMd, runtimeOverlay, tempDir);
|
|
282
347
|
const workerOverlay = `${WORKER_START}
|
|
283
348
|
<team_worker_protocol>
|
|
@@ -285,17 +350,17 @@ worker protocol body
|
|
|
285
350
|
</team_worker_protocol>
|
|
286
351
|
${WORKER_END}
|
|
287
352
|
`;
|
|
288
|
-
const withRuntime = await readFile(agentsMd,
|
|
353
|
+
const withRuntime = await readFile(agentsMd, "utf-8");
|
|
289
354
|
await writeFile(agentsMd, `${withRuntime.trimEnd()}\n\n${workerOverlay}`);
|
|
290
355
|
await stripOverlay(agentsMd, tempDir);
|
|
291
|
-
const result = await readFile(agentsMd,
|
|
356
|
+
const result = await readFile(agentsMd, "utf-8");
|
|
292
357
|
assert.ok(!result.includes(RUNTIME_START));
|
|
293
358
|
assert.ok(!result.includes(RUNTIME_END));
|
|
294
359
|
assert.ok(result.includes(WORKER_START));
|
|
295
360
|
assert.ok(result.includes(WORKER_END));
|
|
296
361
|
});
|
|
297
|
-
it(
|
|
298
|
-
const agentsMd = join(tempDir,
|
|
362
|
+
it("stripOverlay removes runtime overlay and preserves worker overlay (worker->runtime order)", async () => {
|
|
363
|
+
const agentsMd = join(tempDir, "AGENTS-stacked-wr.md");
|
|
299
364
|
const workerOverlay = `${WORKER_START}
|
|
300
365
|
<team_worker_protocol>
|
|
301
366
|
worker protocol body
|
|
@@ -303,17 +368,17 @@ worker protocol body
|
|
|
303
368
|
${WORKER_END}
|
|
304
369
|
`;
|
|
305
370
|
await writeFile(agentsMd, `${originalContent.trimEnd()}\n\n${workerOverlay}`);
|
|
306
|
-
const runtimeOverlay = await generateOverlay(tempDir,
|
|
371
|
+
const runtimeOverlay = await generateOverlay(tempDir, "stacked-wr");
|
|
307
372
|
await applyOverlay(agentsMd, runtimeOverlay, tempDir);
|
|
308
373
|
await stripOverlay(agentsMd, tempDir);
|
|
309
|
-
const result = await readFile(agentsMd,
|
|
374
|
+
const result = await readFile(agentsMd, "utf-8");
|
|
310
375
|
assert.ok(!result.includes(RUNTIME_START));
|
|
311
376
|
assert.ok(!result.includes(RUNTIME_END));
|
|
312
377
|
assert.ok(result.includes(WORKER_START));
|
|
313
378
|
assert.ok(result.includes(WORKER_END));
|
|
314
379
|
});
|
|
315
|
-
it(
|
|
316
|
-
const agentsMd = join(tempDir,
|
|
380
|
+
it("stripOverlay removes duplicate runtime marker blocks", async () => {
|
|
381
|
+
const agentsMd = join(tempDir, "AGENTS-duplicate-runtime.md");
|
|
317
382
|
const dup = `${originalContent.trimEnd()}
|
|
318
383
|
|
|
319
384
|
${RUNTIME_START}
|
|
@@ -326,13 +391,13 @@ ${RUNTIME_END}
|
|
|
326
391
|
`;
|
|
327
392
|
await writeFile(agentsMd, dup);
|
|
328
393
|
await stripOverlay(agentsMd, tempDir);
|
|
329
|
-
const result = await readFile(agentsMd,
|
|
394
|
+
const result = await readFile(agentsMd, "utf-8");
|
|
330
395
|
assert.ok(!result.includes(RUNTIME_START));
|
|
331
396
|
assert.ok(!result.includes(RUNTIME_END));
|
|
332
397
|
assert.equal(result.trim(), originalContent.trim());
|
|
333
398
|
});
|
|
334
|
-
it(
|
|
335
|
-
const agentsMd = join(tempDir,
|
|
399
|
+
it("stripOverlay handles malformed runtime start marker without deleting worker overlay", async () => {
|
|
400
|
+
const agentsMd = join(tempDir, "AGENTS-malformed-runtime.md");
|
|
336
401
|
const malformed = `${originalContent.trimEnd()}
|
|
337
402
|
|
|
338
403
|
${RUNTIME_START}
|
|
@@ -347,99 +412,99 @@ ${WORKER_END}
|
|
|
347
412
|
`;
|
|
348
413
|
await writeFile(agentsMd, malformed);
|
|
349
414
|
await stripOverlay(agentsMd, tempDir);
|
|
350
|
-
const result = await readFile(agentsMd,
|
|
415
|
+
const result = await readFile(agentsMd, "utf-8");
|
|
351
416
|
assert.ok(!result.includes(RUNTIME_START));
|
|
352
417
|
assert.ok(result.includes(WORKER_START));
|
|
353
418
|
assert.ok(result.includes(WORKER_END));
|
|
354
419
|
});
|
|
355
420
|
});
|
|
356
|
-
describe(
|
|
421
|
+
describe("session-scoped model instructions file", () => {
|
|
357
422
|
let tempDir;
|
|
358
423
|
let restoreCodexHome;
|
|
359
424
|
before(async () => {
|
|
360
425
|
tempDir = await makeTempDir();
|
|
361
|
-
restoreCodexHome = setMockCodexHome(join(tempDir,
|
|
426
|
+
restoreCodexHome = setMockCodexHome(join(tempDir, "home", ".codex"));
|
|
362
427
|
});
|
|
363
428
|
after(async () => {
|
|
364
429
|
restoreCodexHome?.();
|
|
365
430
|
await rm(tempDir, { recursive: true, force: true });
|
|
366
431
|
});
|
|
367
|
-
it(
|
|
368
|
-
const userAgentsMd = join(tempDir,
|
|
369
|
-
const projectAgentsMd = join(tempDir,
|
|
370
|
-
await mkdir(join(tempDir,
|
|
371
|
-
await writeFile(userAgentsMd,
|
|
372
|
-
const projectContent =
|
|
432
|
+
it("writes user + project AGENTS.md + runtime overlay into session-scoped file", async () => {
|
|
433
|
+
const userAgentsMd = join(tempDir, "home", ".codex", "AGENTS.md");
|
|
434
|
+
const projectAgentsMd = join(tempDir, "AGENTS.md");
|
|
435
|
+
await mkdir(join(tempDir, "home", ".codex"), { recursive: true });
|
|
436
|
+
await writeFile(userAgentsMd, "# User instructions\n\nStart globally.\n");
|
|
437
|
+
const projectContent = "# Project instructions\n\nStay in scope.\n";
|
|
373
438
|
await writeFile(projectAgentsMd, projectContent);
|
|
374
|
-
const overlay = await generateOverlay(tempDir,
|
|
375
|
-
const writtenPath = await writeSessionModelInstructionsFile(tempDir,
|
|
376
|
-
const sessionContent = await readFile(writtenPath,
|
|
377
|
-
const projectAfter = await readFile(projectAgentsMd,
|
|
378
|
-
assert.equal(writtenPath, sessionModelInstructionsPath(tempDir,
|
|
439
|
+
const overlay = await generateOverlay(tempDir, "session-a");
|
|
440
|
+
const writtenPath = await writeSessionModelInstructionsFile(tempDir, "session-a", overlay);
|
|
441
|
+
const sessionContent = await readFile(writtenPath, "utf-8");
|
|
442
|
+
const projectAfter = await readFile(projectAgentsMd, "utf-8");
|
|
443
|
+
assert.equal(writtenPath, sessionModelInstructionsPath(tempDir, "session-a"));
|
|
379
444
|
assert.match(sessionContent, /# User instructions/);
|
|
380
445
|
assert.match(sessionContent, /# Project instructions/);
|
|
381
|
-
assert.ok(sessionContent.indexOf(
|
|
382
|
-
sessionContent.indexOf(
|
|
446
|
+
assert.ok(sessionContent.indexOf("# User instructions") <
|
|
447
|
+
sessionContent.indexOf("# Project instructions"));
|
|
383
448
|
assert.match(sessionContent, /<!-- OMX:RUNTIME:START -->/);
|
|
384
449
|
assert.equal(projectAfter, projectContent);
|
|
385
450
|
});
|
|
386
|
-
it(
|
|
387
|
-
const userAgentsMd = join(tempDir,
|
|
388
|
-
const projectAgentsMd = join(tempDir,
|
|
389
|
-
const userSkillDir = join(tempDir,
|
|
390
|
-
const projectSkillDir = join(tempDir,
|
|
391
|
-
await mkdir(join(tempDir,
|
|
451
|
+
it("deduplicates duplicate skill references when project and user scopes both install the same skill", async () => {
|
|
452
|
+
const userAgentsMd = join(tempDir, "home", ".codex", "AGENTS.md");
|
|
453
|
+
const projectAgentsMd = join(tempDir, "AGENTS.md");
|
|
454
|
+
const userSkillDir = join(tempDir, "home", ".codex", "skills", "help");
|
|
455
|
+
const projectSkillDir = join(tempDir, ".codex", "skills", "help");
|
|
456
|
+
await mkdir(join(tempDir, "home", ".codex"), { recursive: true });
|
|
392
457
|
await mkdir(userSkillDir, { recursive: true });
|
|
393
458
|
await mkdir(projectSkillDir, { recursive: true });
|
|
394
|
-
await writeFile(join(userSkillDir,
|
|
395
|
-
await writeFile(join(projectSkillDir,
|
|
459
|
+
await writeFile(join(userSkillDir, "SKILL.md"), "# user help\n");
|
|
460
|
+
await writeFile(join(projectSkillDir, "SKILL.md"), "# project help\n");
|
|
396
461
|
await writeFile(userAgentsMd, [
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
].join(
|
|
462
|
+
"# User instructions",
|
|
463
|
+
"",
|
|
464
|
+
"- help: user copy (file: /tmp/home/.codex/skills/help/SKILL.md)",
|
|
465
|
+
].join("\n"));
|
|
401
466
|
await writeFile(projectAgentsMd, [
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
].join(
|
|
406
|
-
const overlay = await generateOverlay(tempDir,
|
|
407
|
-
const writtenPath = await writeSessionModelInstructionsFile(tempDir,
|
|
408
|
-
const sessionContent = await readFile(writtenPath,
|
|
467
|
+
"# Project instructions",
|
|
468
|
+
"",
|
|
469
|
+
"- help: project copy (file: /tmp/project/.codex/skills/help/SKILL.md)",
|
|
470
|
+
].join("\n"));
|
|
471
|
+
const overlay = await generateOverlay(tempDir, "session-dedupe");
|
|
472
|
+
const writtenPath = await writeSessionModelInstructionsFile(tempDir, "session-dedupe", overlay);
|
|
473
|
+
const sessionContent = await readFile(writtenPath, "utf-8");
|
|
409
474
|
assert.equal((sessionContent.match(/skills\/help\/SKILL\.md/g) || []).length, 1);
|
|
410
475
|
assert.doesNotMatch(sessionContent, /user copy/);
|
|
411
476
|
assert.match(sessionContent, /project copy/);
|
|
412
477
|
});
|
|
413
|
-
it(
|
|
414
|
-
await rm(join(tempDir,
|
|
415
|
-
await rm(join(tempDir,
|
|
416
|
-
const overlay = await generateOverlay(tempDir,
|
|
417
|
-
const writtenPath = await writeSessionModelInstructionsFile(tempDir,
|
|
418
|
-
const sessionContent = await readFile(writtenPath,
|
|
419
|
-
assert.ok(sessionContent.includes(
|
|
420
|
-
assert.ok(sessionContent.includes(
|
|
421
|
-
});
|
|
422
|
-
it(
|
|
423
|
-
const projectAgentsMd = join(tempDir,
|
|
424
|
-
const projectContent =
|
|
478
|
+
it("writes overlay-only session file when no base AGENTS.md files exist", async () => {
|
|
479
|
+
await rm(join(tempDir, "home"), { recursive: true, force: true });
|
|
480
|
+
await rm(join(tempDir, "AGENTS.md"), { force: true });
|
|
481
|
+
const overlay = await generateOverlay(tempDir, "session-b");
|
|
482
|
+
const writtenPath = await writeSessionModelInstructionsFile(tempDir, "session-b", overlay);
|
|
483
|
+
const sessionContent = await readFile(writtenPath, "utf-8");
|
|
484
|
+
assert.ok(sessionContent.includes("<!-- OMX:RUNTIME:START -->"));
|
|
485
|
+
assert.ok(sessionContent.includes("<!-- OMX:RUNTIME:END -->"));
|
|
486
|
+
});
|
|
487
|
+
it("removes session-scoped file without touching project AGENTS.md", async () => {
|
|
488
|
+
const projectAgentsMd = join(tempDir, "AGENTS.md");
|
|
489
|
+
const projectContent = "# Keep me unchanged\n";
|
|
425
490
|
await writeFile(projectAgentsMd, projectContent);
|
|
426
|
-
const overlay = await generateOverlay(tempDir,
|
|
427
|
-
const writtenPath = await writeSessionModelInstructionsFile(tempDir,
|
|
428
|
-
await removeSessionModelInstructionsFile(tempDir,
|
|
491
|
+
const overlay = await generateOverlay(tempDir, "session-c");
|
|
492
|
+
const writtenPath = await writeSessionModelInstructionsFile(tempDir, "session-c", overlay);
|
|
493
|
+
await removeSessionModelInstructionsFile(tempDir, "session-c");
|
|
429
494
|
assert.equal(existsSync(writtenPath), false);
|
|
430
|
-
assert.equal(await readFile(projectAgentsMd,
|
|
495
|
+
assert.equal(await readFile(projectAgentsMd, "utf-8"), projectContent);
|
|
431
496
|
});
|
|
432
497
|
});
|
|
433
|
-
describe(
|
|
434
|
-
it(
|
|
435
|
-
const content =
|
|
498
|
+
describe("hasOverlay", () => {
|
|
499
|
+
it("returns true when both markers present", () => {
|
|
500
|
+
const content = "start\n<!-- OMX:RUNTIME:START -->\nmiddle\n<!-- OMX:RUNTIME:END -->\nend";
|
|
436
501
|
assert.ok(hasOverlay(content));
|
|
437
502
|
});
|
|
438
|
-
it(
|
|
439
|
-
assert.ok(!hasOverlay(
|
|
503
|
+
it("returns false when no markers", () => {
|
|
504
|
+
assert.ok(!hasOverlay("plain content"));
|
|
440
505
|
});
|
|
441
|
-
it(
|
|
442
|
-
assert.ok(!hasOverlay(
|
|
506
|
+
it("returns false when only start marker", () => {
|
|
507
|
+
assert.ok(!hasOverlay("<!-- OMX:RUNTIME:START -->\nbroken"));
|
|
443
508
|
});
|
|
444
509
|
});
|
|
445
510
|
//# sourceMappingURL=agents-overlay.test.js.map
|