oh-my-opencode 4.5.12 → 4.6.0
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/.agents/skills/opencode-qa/SKILL.md +194 -0
- package/.agents/skills/opencode-qa/references/cli-commands.md +188 -0
- package/.agents/skills/opencode-qa/references/db-investigation.md +197 -0
- package/.agents/skills/opencode-qa/references/events-hooks.md +110 -0
- package/.agents/skills/opencode-qa/references/sdk.md +96 -0
- package/.agents/skills/opencode-qa/references/server-api.md +200 -0
- package/.agents/skills/opencode-qa/references/testing-harness.md +218 -0
- package/.agents/skills/opencode-qa/references/tui-tmux.md +52 -0
- package/.agents/skills/opencode-qa/scripts/db-session-by-id.sh +53 -0
- package/.agents/skills/opencode-qa/scripts/db-session-by-name.sh +57 -0
- package/.agents/skills/opencode-qa/scripts/db-session-by-text.sh +158 -0
- package/.agents/skills/opencode-qa/scripts/export-roundtrip.sh +57 -0
- package/.agents/skills/opencode-qa/scripts/lib/common.sh +216 -0
- package/.agents/skills/opencode-qa/scripts/server-smoke.sh +64 -0
- package/.agents/skills/opencode-qa/scripts/sse-hook-probe.sh +106 -0
- package/.agents/skills/opencode-qa/scripts/tui-smoke.sh +89 -0
- package/README.ja.md +13 -3
- package/README.ko.md +13 -3
- package/README.md +24 -14
- package/README.ru.md +13 -3
- package/README.zh-cn.md +13 -3
- package/bin/oh-my-opencode.js +4 -3
- package/bin/oh-my-opencode.test.ts +35 -7
- package/bin/platform.d.ts +1 -1
- package/bin/platform.js +4 -4
- package/bin/platform.test.ts +31 -9
- package/dist/cli/cleanup-command.d.ts +4 -0
- package/dist/cli/cleanup.d.ts +11 -0
- package/dist/cli/cli-program.d.ts +2 -1
- package/dist/cli/index.js +1837 -450
- package/dist/cli/install-codex/codex-cache.d.ts +1 -0
- package/dist/cli/install-codex/codex-cleanup-config.d.ts +6 -0
- package/dist/cli/install-codex/codex-cleanup.d.ts +21 -0
- package/dist/cli/install-codex/codex-config-mcp.d.ts +1 -0
- package/dist/cli/install-codex/codex-config-permissions.d.ts +1 -0
- package/dist/cli/install-codex/codex-config-reasoning.d.ts +1 -0
- package/dist/cli/install-codex/codex-config-toml.d.ts +2 -1
- package/dist/cli/install-codex/codex-installation-detection.d.ts +36 -0
- package/dist/cli/install-codex/codex-package-layout.d.ts +1 -0
- package/dist/cli/install-codex/codex-project-local-cleanup-best-effort.d.ts +7 -0
- package/dist/cli/install-codex/codex-project-local-cleanup.d.ts +35 -0
- package/dist/cli/install-codex/git-bash.d.ts +35 -0
- package/dist/cli/install-codex/index.d.ts +4 -0
- package/dist/cli/install-codex/toml-section-editor.d.ts +2 -0
- package/dist/cli/install-codex/types.d.ts +20 -0
- package/dist/cli/run/event-state.d.ts +1 -0
- package/dist/cli/run/poll-for-completion.d.ts +1 -0
- package/dist/cli/run/prompt-start.d.ts +7 -0
- package/dist/cli/star-request.d.ts +9 -0
- package/dist/config/schema/hooks.d.ts +0 -1
- package/dist/create-hooks.d.ts +0 -1
- package/dist/features/builtin-skills/skills/debugging.d.ts +2 -0
- package/dist/features/builtin-skills/skills/index.d.ts +1 -0
- package/dist/hooks/index.d.ts +0 -1
- package/dist/index.js +267 -114
- package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
- package/dist/plugin/messages-transform.d.ts +8 -1
- package/dist/plugin/user-abort-interrupted-recovery-guard.d.ts +6 -0
- package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +14 -0
- package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +7 -0
- package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +1 -0
- package/dist/shared/prompt-async-gate/timing.d.ts +1 -0
- package/dist/shared/prompt-async-gate/types.d.ts +2 -0
- package/dist/shared/prompt-async-gate.d.ts +1 -1
- package/package.json +22 -17
- package/packages/git-bash-mcp/dist/cli.js +367 -0
- package/packages/omo-codex/plugin/.mcp.json +11 -0
- package/packages/omo-codex/plugin/components/comment-checker/README.md +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +29 -0
- package/packages/omo-codex/plugin/components/git-bash/package.json +23 -0
- package/packages/omo-codex/plugin/components/git-bash/src/cli.ts +33 -0
- package/packages/omo-codex/plugin/components/git-bash/src/codex-hook.ts +180 -0
- package/packages/omo-codex/plugin/components/git-bash/src/index.ts +10 -0
- package/packages/omo-codex/plugin/components/git-bash/test/codex-hook.test.ts +195 -0
- package/packages/omo-codex/plugin/components/git-bash/tsconfig.build.json +13 -0
- package/packages/omo-codex/plugin/components/git-bash/tsconfig.json +25 -0
- package/packages/omo-codex/plugin/components/lsp/README.md +1 -1
- package/packages/omo-codex/plugin/components/lsp/src/cli.ts +5 -5
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +33 -0
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +19 -27
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-cli.test.ts +28 -0
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-errors.test.ts +55 -0
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +7 -5
- package/packages/omo-codex/plugin/components/rules/README.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/windows-git-bash.md +10 -0
- package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +3 -1
- package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +97 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +5 -4
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +22 -0
- package/packages/omo-codex/plugin/components/ultrawork/README.md +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/codex-ultrawork-reviewer.toml +1 -0
- package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +8 -7
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +31 -5
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +27 -4
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +25 -0
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +27 -205
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +230 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +102 -5
- package/packages/omo-codex/plugin/hooks/hooks.json +24 -2
- package/packages/omo-codex/plugin/package-lock.json +19 -0
- package/packages/omo-codex/plugin/package.json +3 -1
- package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +16 -1
- package/packages/omo-codex/plugin/scripts/build-components.mjs +2 -1
- package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +87 -0
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +27 -2
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +20 -0
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +27 -205
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +230 -0
- package/packages/omo-codex/plugin/test/aggregate.test.mjs +23 -8
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +56 -11
- package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +34 -0
- package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +21 -0
- package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +48 -0
- package/packages/omo-codex/plugin/test/subagent-guidance.test.mjs +76 -0
- package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +66 -0
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +32 -2
- package/packages/omo-codex/scripts/install/cache.mjs +5 -3
- package/packages/omo-codex/scripts/install/cli-args.mjs +112 -0
- package/packages/omo-codex/scripts/install/config.mjs +36 -1
- package/packages/omo-codex/scripts/install/delegated-command.mjs +25 -0
- package/packages/omo-codex/scripts/install/git-bash.mjs +99 -0
- package/packages/omo-codex/scripts/install/git-bash.test.mjs +174 -0
- package/packages/omo-codex/scripts/install/mcp-runtime-cache.mjs +5 -1
- package/packages/omo-codex/scripts/install/multi-agent-v2-config.mjs +7 -1
- package/packages/omo-codex/scripts/install/permissions.d.mts +1 -0
- package/packages/omo-codex/scripts/install/permissions.mjs +26 -0
- package/packages/omo-codex/scripts/install/project-local-cleanup.mjs +229 -0
- package/packages/omo-codex/scripts/install/reasoning-config.mjs +14 -0
- package/packages/omo-codex/scripts/install/source-package-build.mjs +20 -0
- package/packages/omo-codex/scripts/install/toml-editor.mjs +19 -2
- package/packages/omo-codex/scripts/install-cli-args.test.mjs +146 -0
- package/packages/omo-codex/scripts/install-config-autonomous.test.mjs +48 -0
- package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +62 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +206 -0
- package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +129 -0
- package/packages/omo-codex/scripts/install-local-git-bash-preflight.test.mjs +145 -0
- package/packages/omo-codex/scripts/install-local.mjs +91 -8
- package/packages/omo-codex/scripts/install-local.test.mjs +15 -0
- package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +60 -0
- package/packages/omo-codex/scripts/install-packaged-local.test.mjs +67 -0
- package/packages/omo-codex/scripts/install-project-local-cleanup.test.mjs +277 -0
- package/packages/shared-skills/skills/review-work/SKILL.md +27 -2
- package/packages/shared-skills/skills/start-work/SKILL.md +20 -0
- package/dist/hooks/context-window-monitor.d.ts +0 -19
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { parseLazyCodexInstallCliArgs } from "./install/cli-args.mjs";
|
|
5
|
+
|
|
6
|
+
test("#given lazycodex install flags #when parsing Node installer argv #then keeps Codex autonomous intent", () => {
|
|
7
|
+
// given
|
|
8
|
+
const argv = ["install", "--no-tui", "--codex-autonomous", "--platform=codex"];
|
|
9
|
+
|
|
10
|
+
// when
|
|
11
|
+
const parsed = parseLazyCodexInstallCliArgs(argv);
|
|
12
|
+
|
|
13
|
+
// then
|
|
14
|
+
assert.deepEqual(parsed, {
|
|
15
|
+
kind: "install",
|
|
16
|
+
autonomousPermissions: true,
|
|
17
|
+
repoRoot: undefined,
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("#given unsupported OpenCode platform override #when parsing Node installer argv #then rejects the Bun-backed path", () => {
|
|
22
|
+
// given
|
|
23
|
+
const argv = ["install", "--platform=both"];
|
|
24
|
+
|
|
25
|
+
// when
|
|
26
|
+
const parse = () => parseLazyCodexInstallCliArgs(argv);
|
|
27
|
+
|
|
28
|
+
// then
|
|
29
|
+
assert.throws(parse, /lazycodex-ai installs the Codex Light edition only/);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("#given missing platform value #when parsing Node installer argv #then rejects the incomplete option", () => {
|
|
33
|
+
// given
|
|
34
|
+
const argv = ["install", "--platform"];
|
|
35
|
+
|
|
36
|
+
// when
|
|
37
|
+
const parse = () => parseLazyCodexInstallCliArgs(argv);
|
|
38
|
+
|
|
39
|
+
// then
|
|
40
|
+
assert.throws(parse, /--platform requires a value/);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("#given repo root equals option #when parsing Node installer argv #then keeps the explicit path", () => {
|
|
44
|
+
// given
|
|
45
|
+
const argv = ["install", "--repo-root=/tmp/project"];
|
|
46
|
+
|
|
47
|
+
// when
|
|
48
|
+
const parsed = parseLazyCodexInstallCliArgs(argv);
|
|
49
|
+
|
|
50
|
+
// then
|
|
51
|
+
assert.deepEqual(parsed, {
|
|
52
|
+
kind: "install",
|
|
53
|
+
autonomousPermissions: undefined,
|
|
54
|
+
repoRoot: "/tmp/project",
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("#given unknown positional command #when parsing Node installer argv #then rejects instead of treating it as a repo root", () => {
|
|
59
|
+
// given
|
|
60
|
+
const argv = ["banana"];
|
|
61
|
+
|
|
62
|
+
// when
|
|
63
|
+
const parse = () => parseLazyCodexInstallCliArgs(argv);
|
|
64
|
+
|
|
65
|
+
// then
|
|
66
|
+
assert.throws(parse, /Unsupported lazycodex-ai command: banana/);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("#given install help flag #when parsing Node installer argv #then returns help", () => {
|
|
70
|
+
// given
|
|
71
|
+
const argv = ["install", "--help"];
|
|
72
|
+
|
|
73
|
+
// when
|
|
74
|
+
const parsed = parseLazyCodexInstallCliArgs(argv);
|
|
75
|
+
|
|
76
|
+
// then
|
|
77
|
+
assert.deepEqual(parsed, { kind: "help" });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("#given dry-run install with codex autonomy flags #when parsing Node installer argv #then keeps delegated install command and dry-run intent", () => {
|
|
81
|
+
// given
|
|
82
|
+
const argv = ["--dry-run", "install", "--no-tui", "--codex-autonomous"];
|
|
83
|
+
|
|
84
|
+
// when
|
|
85
|
+
const parsed = parseLazyCodexInstallCliArgs(argv);
|
|
86
|
+
|
|
87
|
+
// then
|
|
88
|
+
assert.deepEqual(parsed, {
|
|
89
|
+
kind: "command",
|
|
90
|
+
command: "install",
|
|
91
|
+
dryRun: true,
|
|
92
|
+
noTui: true,
|
|
93
|
+
skipAuth: false,
|
|
94
|
+
autonomousPermissions: true,
|
|
95
|
+
repoRoot: undefined,
|
|
96
|
+
args: [],
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("#given dry-run doctor command #when parsing Node installer argv #then returns delegated doctor command", () => {
|
|
101
|
+
// given
|
|
102
|
+
const argv = ["--dry-run", "doctor"];
|
|
103
|
+
|
|
104
|
+
// when
|
|
105
|
+
const parsed = parseLazyCodexInstallCliArgs(argv);
|
|
106
|
+
|
|
107
|
+
// then
|
|
108
|
+
assert.deepEqual(parsed, {
|
|
109
|
+
kind: "command",
|
|
110
|
+
command: "doctor",
|
|
111
|
+
dryRun: true,
|
|
112
|
+
args: [],
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("#given dry-run cleanup command #when parsing Node installer argv #then returns delegated codex cleanup command", () => {
|
|
117
|
+
// given
|
|
118
|
+
const argv = ["--dry-run", "cleanup", "--project", "/tmp/lazycodex-qa"];
|
|
119
|
+
|
|
120
|
+
// when
|
|
121
|
+
const parsed = parseLazyCodexInstallCliArgs(argv);
|
|
122
|
+
|
|
123
|
+
// then
|
|
124
|
+
assert.deepEqual(parsed, {
|
|
125
|
+
kind: "command",
|
|
126
|
+
command: "cleanup",
|
|
127
|
+
dryRun: true,
|
|
128
|
+
args: ["--project", "/tmp/lazycodex-qa"],
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("#given doctor flags #when parsing Node installer argv #then preserves pass-through arguments", () => {
|
|
133
|
+
// given
|
|
134
|
+
const argv = ["doctor", "--json"];
|
|
135
|
+
|
|
136
|
+
// when
|
|
137
|
+
const parsed = parseLazyCodexInstallCliArgs(argv);
|
|
138
|
+
|
|
139
|
+
// then
|
|
140
|
+
assert.deepEqual(parsed, {
|
|
141
|
+
kind: "command",
|
|
142
|
+
command: "doctor",
|
|
143
|
+
dryRun: false,
|
|
144
|
+
args: ["--json"],
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { updateCodexConfig } from "./install/config.mjs";
|
|
8
|
+
|
|
9
|
+
test("#given autonomous permissions requested #when script installer updates config #then enables full Codex autonomy", async () => {
|
|
10
|
+
// given
|
|
11
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-autonomous-"));
|
|
12
|
+
const configPath = join(root, "config.toml");
|
|
13
|
+
await writeFile(
|
|
14
|
+
configPath,
|
|
15
|
+
[
|
|
16
|
+
'approval_policy = "on-request"',
|
|
17
|
+
'sandbox_mode = "workspace-write"',
|
|
18
|
+
'network_access = "disabled"',
|
|
19
|
+
"",
|
|
20
|
+
"[notice]",
|
|
21
|
+
"hide_full_access_warning = false",
|
|
22
|
+
"",
|
|
23
|
+
"[windows]",
|
|
24
|
+
'sandbox = "workspace-write"',
|
|
25
|
+
"",
|
|
26
|
+
].join("\n"),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// when
|
|
30
|
+
await updateCodexConfig({
|
|
31
|
+
configPath,
|
|
32
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
33
|
+
marketplaceName: "debug",
|
|
34
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
35
|
+
pluginNames: ["omo"],
|
|
36
|
+
autonomousPermissions: true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// then
|
|
40
|
+
const config = await readFile(configPath, "utf8");
|
|
41
|
+
assert.match(config, /approval_policy = "never"/);
|
|
42
|
+
assert.match(config, /sandbox_mode = "danger-full-access"/);
|
|
43
|
+
assert.match(config, /network_access = "enabled"/);
|
|
44
|
+
assert.match(config, /\[notice\]/);
|
|
45
|
+
assert.match(config, /hide_full_access_warning = true/);
|
|
46
|
+
assert.match(config, /hide_world_writable_warning = true/);
|
|
47
|
+
assert.doesNotMatch(config, /sandbox = "workspace-write"/);
|
|
48
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { updateCodexConfig } from "./install/config.mjs";
|
|
8
|
+
|
|
9
|
+
test("#given empty Codex config #when script installer updates config #then sets default and Plan-mode reasoning effort", async () => {
|
|
10
|
+
// given
|
|
11
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-reasoning-"));
|
|
12
|
+
const configPath = join(root, "config.toml");
|
|
13
|
+
|
|
14
|
+
// when
|
|
15
|
+
await updateCodexConfig({
|
|
16
|
+
configPath,
|
|
17
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
18
|
+
marketplaceName: "debug",
|
|
19
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
20
|
+
pluginNames: ["omo"],
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// then
|
|
24
|
+
const content = await readFile(configPath, "utf8");
|
|
25
|
+
assert.match(content, /model_reasoning_effort = "high"/);
|
|
26
|
+
assert.match(content, /plan_mode_reasoning_effort = "xhigh"/);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("#given existing reasoning config #when script installer updates config #then replaces stale defaults without duplicate keys", async () => {
|
|
30
|
+
// given
|
|
31
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-reasoning-existing-"));
|
|
32
|
+
const configPath = join(root, "config.toml");
|
|
33
|
+
await writeFile(
|
|
34
|
+
configPath,
|
|
35
|
+
[
|
|
36
|
+
'model_reasoning_effort = "low"',
|
|
37
|
+
'plan_mode_reasoning_effort = "medium"',
|
|
38
|
+
"",
|
|
39
|
+
"[features]",
|
|
40
|
+
"plugins = false",
|
|
41
|
+
"",
|
|
42
|
+
].join("\n"),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// when
|
|
46
|
+
await updateCodexConfig({
|
|
47
|
+
configPath,
|
|
48
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
49
|
+
marketplaceName: "debug",
|
|
50
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
51
|
+
pluginNames: ["omo"],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// then
|
|
55
|
+
const content = await readFile(configPath, "utf8");
|
|
56
|
+
assert.equal(content.match(/^model_reasoning_effort\s*=/gm)?.length, 1);
|
|
57
|
+
assert.equal(content.match(/^plan_mode_reasoning_effort\s*=/gm)?.length, 1);
|
|
58
|
+
assert.match(content, /model_reasoning_effort = "high"/);
|
|
59
|
+
assert.match(content, /plan_mode_reasoning_effort = "xhigh"/);
|
|
60
|
+
assert.doesNotMatch(content, /model_reasoning_effort = "low"/);
|
|
61
|
+
assert.doesNotMatch(content, /plan_mode_reasoning_effort = "medium"/);
|
|
62
|
+
});
|
|
@@ -27,6 +27,61 @@ test("#given empty Codex config #when script installer updates config #then enab
|
|
|
27
27
|
assert.match(config, /max_concurrent_threads_per_session = 10000/);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
test("#given empty Codex config #when script installer updates config #then installs Context7 MCP", async () => {
|
|
31
|
+
// given
|
|
32
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-context7-"));
|
|
33
|
+
const configPath = join(root, "config.toml");
|
|
34
|
+
|
|
35
|
+
// when
|
|
36
|
+
await updateCodexConfig({
|
|
37
|
+
configPath,
|
|
38
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
39
|
+
marketplaceName: "debug",
|
|
40
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
41
|
+
pluginNames: ["omo"],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// then
|
|
45
|
+
const config = await readFile(configPath, "utf8");
|
|
46
|
+
assert.match(config, /\[mcp_servers\.context7\]/);
|
|
47
|
+
assert.match(config, /command = "npx"/);
|
|
48
|
+
assert.match(config, /args = \["-y", "@upstash\/context7-mcp", "--api-key", "YOUR_API_KEY"\]/);
|
|
49
|
+
assert.match(config, /startup_timeout_sec = 20/);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("#given existing Context7 MCP config #when script installer updates config #then preserves user setup", async () => {
|
|
53
|
+
// given
|
|
54
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-context7-existing-"));
|
|
55
|
+
const configPath = join(root, "config.toml");
|
|
56
|
+
await writeFile(
|
|
57
|
+
configPath,
|
|
58
|
+
[
|
|
59
|
+
"[mcp_servers.context7]",
|
|
60
|
+
'command = "node"',
|
|
61
|
+
'args = ["/opt/context7/server.js"]',
|
|
62
|
+
'startup_timeout_sec = 40',
|
|
63
|
+
"",
|
|
64
|
+
].join("\n"),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// when
|
|
68
|
+
await updateCodexConfig({
|
|
69
|
+
configPath,
|
|
70
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
71
|
+
marketplaceName: "debug",
|
|
72
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
73
|
+
pluginNames: ["omo"],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// then
|
|
77
|
+
const config = await readFile(configPath, "utf8");
|
|
78
|
+
assert.match(config, /\[mcp_servers\.context7\]/);
|
|
79
|
+
assert.match(config, /command = "node"/);
|
|
80
|
+
assert.match(config, /args = \["\/opt\/context7\/server\.js"\]/);
|
|
81
|
+
assert.match(config, /startup_timeout_sec = 40/);
|
|
82
|
+
assert.doesNotMatch(config, /YOUR_API_KEY/);
|
|
83
|
+
});
|
|
84
|
+
|
|
30
85
|
test("#given sisyphuslabs config without explicit source #when script installer updates config #then uses local marketplace", async () => {
|
|
31
86
|
// given
|
|
32
87
|
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-sisyphuslabs-"));
|
|
@@ -116,3 +171,154 @@ test("#given legacy boolean MultiAgentV2 flag and table #when script installer u
|
|
|
116
171
|
assert.match(config, /usage_hint_enabled = false/);
|
|
117
172
|
assert.match(config, /max_concurrent_threads_per_session = 10000/);
|
|
118
173
|
});
|
|
174
|
+
|
|
175
|
+
test("#given legacy agents max_threads #when script installer updates config #then removes the conflicting legacy thread cap", async () => {
|
|
176
|
+
// given
|
|
177
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-multi-agent-legacy-threads-"));
|
|
178
|
+
const configPath = join(root, "config.toml");
|
|
179
|
+
await writeFile(
|
|
180
|
+
configPath,
|
|
181
|
+
[
|
|
182
|
+
"[agents]",
|
|
183
|
+
"max_threads = 16",
|
|
184
|
+
"max_depth = 4",
|
|
185
|
+
"job_max_runtime_seconds = 3600",
|
|
186
|
+
"",
|
|
187
|
+
].join("\n"),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// when
|
|
191
|
+
await updateCodexConfig({
|
|
192
|
+
configPath,
|
|
193
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
194
|
+
marketplaceName: "debug",
|
|
195
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
196
|
+
pluginNames: ["omo"],
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// then
|
|
200
|
+
const config = await readFile(configPath, "utf8");
|
|
201
|
+
assert.match(config, /\[features\.multi_agent_v2\]/);
|
|
202
|
+
assert.match(config, /enabled = true/);
|
|
203
|
+
assert.match(config, /max_concurrent_threads_per_session = 10000/);
|
|
204
|
+
assert.match(config, /\[agents\]/);
|
|
205
|
+
assert.doesNotMatch(config, /^max_threads\s*=/m);
|
|
206
|
+
assert.match(config, /max_depth = 4/);
|
|
207
|
+
assert.match(config, /job_max_runtime_seconds = 3600/);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("#given managed agent role sections #when script installer updates config #then preserves role config while removing only root agents max_threads", async () => {
|
|
211
|
+
// given
|
|
212
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-multi-agent-role-section-"));
|
|
213
|
+
const configPath = join(root, "config.toml");
|
|
214
|
+
await writeFile(
|
|
215
|
+
configPath,
|
|
216
|
+
[
|
|
217
|
+
"[agents]",
|
|
218
|
+
"max_threads = 16",
|
|
219
|
+
"",
|
|
220
|
+
"[agents.explorer]",
|
|
221
|
+
'description = "read-only explorer"',
|
|
222
|
+
'config_file = "./agents/explorer.toml"',
|
|
223
|
+
"",
|
|
224
|
+
].join("\n"),
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// when
|
|
228
|
+
await updateCodexConfig({
|
|
229
|
+
configPath,
|
|
230
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
231
|
+
marketplaceName: "debug",
|
|
232
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
233
|
+
pluginNames: ["omo"],
|
|
234
|
+
agentConfigs: [{ name: "explorer", configFile: "./agents/explorer.toml" }],
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// then
|
|
238
|
+
const config = await readFile(configPath, "utf8");
|
|
239
|
+
assert.doesNotMatch(config, /^max_threads\s*=/m);
|
|
240
|
+
assert.match(config, /\[agents\.explorer\]/);
|
|
241
|
+
assert.match(config, /description = "read-only explorer"/);
|
|
242
|
+
assert.match(config, /config_file = "\.\/agents\/explorer\.toml"/);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("#given existing trust and lsp blocks #when updating config #then existing blocks are preserved", async () => {
|
|
246
|
+
// given
|
|
247
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-config-baseline-"));
|
|
248
|
+
const configPath = join(root, "config.toml");
|
|
249
|
+
await writeFile(
|
|
250
|
+
configPath,
|
|
251
|
+
[
|
|
252
|
+
'[plugins."omo@sisyphuslabs"]',
|
|
253
|
+
"enabled = true",
|
|
254
|
+
"",
|
|
255
|
+
'[plugins."omo@sisyphuslabs".mcp_servers.lsp]',
|
|
256
|
+
"enabled = true",
|
|
257
|
+
"",
|
|
258
|
+
'[hooks.state."omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0"]',
|
|
259
|
+
'trusted_hash = "sha256:keep"',
|
|
260
|
+
"",
|
|
261
|
+
].join("\n"),
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
// when
|
|
265
|
+
await updateCodexConfig({
|
|
266
|
+
configPath,
|
|
267
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
268
|
+
marketplaceName: "sisyphuslabs",
|
|
269
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex/cache/sisyphuslabs" },
|
|
270
|
+
pluginNames: ["omo"],
|
|
271
|
+
trustedHookStates: [{ key: "omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:keep" }],
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// then
|
|
275
|
+
const content = await readFile(configPath, "utf8");
|
|
276
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\]/);
|
|
277
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.lsp\]/);
|
|
278
|
+
assert.match(content, /\[hooks\.state\."omo@sisyphuslabs:hooks\/hooks\.json:post_tool_use:0:0"\]/);
|
|
279
|
+
assert.match(content, /trusted_hash = "sha256:keep"/);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("#given windows platform #when updating config #then enables git_bash plugin mcp policy", async () => {
|
|
283
|
+
// given
|
|
284
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-config-git-bash-win32-"));
|
|
285
|
+
const configPath = join(root, "config.toml");
|
|
286
|
+
|
|
287
|
+
// when
|
|
288
|
+
await updateCodexConfig({
|
|
289
|
+
configPath,
|
|
290
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
291
|
+
marketplaceName: "sisyphuslabs",
|
|
292
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex/cache/sisyphuslabs" },
|
|
293
|
+
pluginNames: ["omo"],
|
|
294
|
+
platform: "win32",
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// then
|
|
298
|
+
const content = await readFile(configPath, "utf8");
|
|
299
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.git_bash\]/);
|
|
300
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.git_bash\][\s\S]*?enabled = true/);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("#given non-windows platforms #when updating config #then disables git_bash plugin mcp policy", async () => {
|
|
304
|
+
for (const platform of ["linux", "darwin"]) {
|
|
305
|
+
// given
|
|
306
|
+
const root = await mkdtemp(join(tmpdir(), `omo-codex-config-git-bash-${platform}-`));
|
|
307
|
+
const configPath = join(root, "config.toml");
|
|
308
|
+
|
|
309
|
+
// when
|
|
310
|
+
await updateCodexConfig({
|
|
311
|
+
configPath,
|
|
312
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
313
|
+
marketplaceName: "sisyphuslabs",
|
|
314
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex/cache/sisyphuslabs" },
|
|
315
|
+
pluginNames: ["omo"],
|
|
316
|
+
platform,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// then
|
|
320
|
+
const content = await readFile(configPath, "utf8");
|
|
321
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.git_bash\]/);
|
|
322
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.git_bash\][\s\S]*?enabled = false/);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
|
+
|
|
9
|
+
import { resolveDefaultRepoRoot } from "./install-local.mjs";
|
|
10
|
+
|
|
11
|
+
test("#given published lazycodex bin runs outside the package #when resolving default repo root #then uses installer location", () => {
|
|
12
|
+
// given
|
|
13
|
+
const scriptsDir = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
|
|
15
|
+
// when
|
|
16
|
+
const repoRoot = resolveDefaultRepoRoot();
|
|
17
|
+
|
|
18
|
+
// then
|
|
19
|
+
assert.equal(repoRoot, join(scriptsDir, "..", "..", ".."));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("#given lazycodex version flag #when running the Node installer entrypoint #then prints the package version", () => {
|
|
23
|
+
// given
|
|
24
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
25
|
+
const manifestPath = fileURLToPath(new URL("../../../package.json", import.meta.url));
|
|
26
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
27
|
+
|
|
28
|
+
// when
|
|
29
|
+
const output = execFileSync(process.execPath, [scriptPath, "--version"], {
|
|
30
|
+
encoding: "utf8",
|
|
31
|
+
}).trim();
|
|
32
|
+
|
|
33
|
+
// then
|
|
34
|
+
assert.equal(output, `lazycodex-ai ${manifest.version}`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("#given lazycodex runs through an npm bin symlink #when running the Node installer entrypoint #then it still executes main", { skip: process.platform === "win32" }, () => {
|
|
38
|
+
// given
|
|
39
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
40
|
+
const manifestPath = fileURLToPath(new URL("../../../package.json", import.meta.url));
|
|
41
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
42
|
+
const tempDir = mkdtempSync(join(tmpdir(), "lazycodex-bin-"));
|
|
43
|
+
const binPath = join(tempDir, "lazycodex-ai");
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
symlinkSync(scriptPath, binPath);
|
|
47
|
+
|
|
48
|
+
// when
|
|
49
|
+
const output = execFileSync(process.execPath, [binPath, "--version"], {
|
|
50
|
+
encoding: "utf8",
|
|
51
|
+
}).trim();
|
|
52
|
+
|
|
53
|
+
// then
|
|
54
|
+
assert.equal(output, `lazycodex-ai ${manifest.version}`);
|
|
55
|
+
} finally {
|
|
56
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("#given dry-run install flags #when running the Node installer entrypoint #then prints delegated codex install command", () => {
|
|
61
|
+
// given
|
|
62
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
63
|
+
|
|
64
|
+
// when
|
|
65
|
+
const output = execFileSync(
|
|
66
|
+
process.execPath,
|
|
67
|
+
[scriptPath, "--dry-run", "install", "--no-tui", "--codex-autonomous"],
|
|
68
|
+
{ encoding: "utf8" },
|
|
69
|
+
).trim();
|
|
70
|
+
|
|
71
|
+
// then
|
|
72
|
+
assert.equal(output, "npx --yes --package oh-my-openagent omo install --platform=codex --no-tui --codex-autonomous");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("#given dry-run doctor #when running the Node installer entrypoint #then prints delegated doctor command", () => {
|
|
76
|
+
// given
|
|
77
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
78
|
+
|
|
79
|
+
// when
|
|
80
|
+
const output = execFileSync(process.execPath, [scriptPath, "--dry-run", "doctor"], {
|
|
81
|
+
encoding: "utf8",
|
|
82
|
+
}).trim();
|
|
83
|
+
|
|
84
|
+
// then
|
|
85
|
+
assert.equal(output, "npx --yes --package oh-my-openagent omo doctor");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("#given dry-run cleanup #when running the Node installer entrypoint #then prints delegated codex cleanup command", () => {
|
|
89
|
+
// given
|
|
90
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
91
|
+
|
|
92
|
+
// when
|
|
93
|
+
const output = execFileSync(
|
|
94
|
+
process.execPath,
|
|
95
|
+
[scriptPath, "--dry-run", "cleanup", "--project", "/tmp/lazycodex-qa"],
|
|
96
|
+
{ encoding: "utf8" },
|
|
97
|
+
).trim();
|
|
98
|
+
|
|
99
|
+
// then
|
|
100
|
+
assert.equal(output, "npx --yes --package oh-my-openagent omo cleanup --platform=codex --project /tmp/lazycodex-qa");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("#given the invoking argv path disappears #when importing the Node installer module #then the entrypoint guard does not throw", () => {
|
|
104
|
+
// given
|
|
105
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
106
|
+
const tempDir = mkdtempSync(join(tmpdir(), "lazycodex-import-"));
|
|
107
|
+
const missingArgvPath = join(tempDir, "missing-entrypoint.mjs");
|
|
108
|
+
const probePath = join(tempDir, "probe.mjs");
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
writeFileSync(
|
|
112
|
+
probePath,
|
|
113
|
+
[
|
|
114
|
+
`process.argv[1] = ${JSON.stringify(missingArgvPath)};`,
|
|
115
|
+
`await import(${JSON.stringify(pathToFileURL(scriptPath).href)});`,
|
|
116
|
+
].join("\n"),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// when
|
|
120
|
+
const output = execFileSync(process.execPath, [probePath], {
|
|
121
|
+
encoding: "utf8",
|
|
122
|
+
}).trim();
|
|
123
|
+
|
|
124
|
+
// then
|
|
125
|
+
assert.equal(output, "");
|
|
126
|
+
} finally {
|
|
127
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
128
|
+
}
|
|
129
|
+
});
|