oh-my-opencode 4.5.12 → 4.7.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/bin/version-mismatch.js +47 -0
- package/bin/version-mismatch.test.ts +120 -0
- 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/codex-ulw-loop.d.ts +12 -0
- package/dist/cli/doctor/checks/tui-plugin-config.d.ts +2 -0
- package/dist/cli/index.js +2189 -529
- 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-permissions.d.ts +1 -0
- package/dist/cli/install-codex/codex-config-reasoning.d.ts +2 -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-model-catalog.d.ts +13 -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/background-agent/concurrency.d.ts +1 -0
- package/dist/features/background-agent/process-cleanup.d.ts +6 -0
- package/dist/features/builtin-skills/skills/debugging.d.ts +2 -0
- package/dist/features/builtin-skills/skills/index.d.ts +1 -0
- package/dist/features/claude-code-session-state/state.d.ts +1 -0
- package/dist/features/opencode-skill-loader/index.d.ts +1 -0
- package/dist/features/opencode-skill-loader/opencode-config-skills-reader.d.ts +5 -0
- package/dist/features/tmux-subagent/attachable-session-status.d.ts +1 -1
- package/dist/features/tmux-subagent/session-status-parser.d.ts +1 -0
- package/dist/hooks/comment-checker/cli.d.ts +1 -0
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/tasks-todowrite-disabler/constants.d.ts +1 -1
- package/dist/index.js +1077 -563
- 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/command-executor/execute-hook-command.d.ts +2 -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/dist/tools/skill/description-formatter.d.ts +5 -1
- package/dist/tools/skill/types.d.ts +1 -0
- package/package.json +22 -18
- package/packages/ast-grep-mcp/dist/cli.js +53 -9
- package/packages/git-bash-mcp/dist/cli.js +367 -0
- package/packages/lsp-tools-mcp/dist/lsp/process.js +1 -1
- 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/hephaestus.md +6 -4
- package/packages/omo-codex/plugin/components/rules/bundled-rules/windows-git-bash.md +10 -0
- package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +0 -2
- 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 +6 -5
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +22 -0
- package/packages/omo-codex/plugin/components/ultrawork/CHANGELOG.md +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/README.md +3 -3
- package/packages/omo-codex/plugin/components/ultrawork/agents/codex-ultrawork-reviewer.toml +4 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +8 -7
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +9 -8
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +32 -6
- 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 +28 -205
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +231 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +12 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint.test.ts +19 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +102 -5
- package/packages/omo-codex/plugin/hooks/hooks.json +35 -2
- package/packages/omo-codex/plugin/model-catalog.json +49 -0
- 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/auto-update.mjs +159 -0
- 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/migrate-codex-config.mjs +269 -0
- package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +89 -0
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +6 -6
- package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +6 -6
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +127 -0
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +9 -0
- package/packages/omo-codex/plugin/skills/refactor/SKILL.md +6 -6
- package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +6 -6
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +33 -8
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +25 -5
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +28 -205
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +231 -0
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +17 -17
- package/packages/omo-codex/plugin/test/aggregate.test.mjs +188 -20
- package/packages/omo-codex/plugin/test/auto-update.test.mjs +129 -0
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +58 -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/migrate-codex-config.test.mjs +146 -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 +67 -0
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +54 -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 +23 -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/legacy-bins.mjs +1 -0
- package/packages/omo-codex/scripts/install/mcp-runtime-cache.mjs +5 -1
- package/packages/omo-codex/scripts/install/model-catalog.mjs +66 -0
- 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 +72 -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-bin-links.test.mjs +23 -0
- 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 +141 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +205 -0
- package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +157 -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/lcx-report-bug/SKILL.md +127 -0
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +9 -0
- package/packages/shared-skills/skills/review-work/SKILL.md +33 -8
- package/packages/shared-skills/skills/start-work/SKILL.md +25 -5
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -11
- package/postinstall.mjs +36 -3
- package/dist/hooks/context-window-monitor.d.ts +0 -19
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdir, 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 worker model and reasoning defaults", 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 = "gpt-5\.5"/);
|
|
26
|
+
assert.match(content, /model_context_window = 400000/);
|
|
27
|
+
assert.match(content, /model_reasoning_effort = "high"/);
|
|
28
|
+
assert.match(content, /plan_mode_reasoning_effort = "xhigh"/);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("#given existing model and reasoning config #when script installer updates config #then replaces stale defaults without duplicate keys", async () => {
|
|
32
|
+
// given
|
|
33
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-reasoning-existing-"));
|
|
34
|
+
const configPath = join(root, "config.toml");
|
|
35
|
+
await writeFile(
|
|
36
|
+
configPath,
|
|
37
|
+
[
|
|
38
|
+
'model = "gpt-5.2"',
|
|
39
|
+
"model_context_window = 272000",
|
|
40
|
+
'model_reasoning_effort = "low"',
|
|
41
|
+
'plan_mode_reasoning_effort = "medium"',
|
|
42
|
+
"",
|
|
43
|
+
"[features]",
|
|
44
|
+
"plugins = false",
|
|
45
|
+
"",
|
|
46
|
+
].join("\n"),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// when
|
|
50
|
+
await updateCodexConfig({
|
|
51
|
+
configPath,
|
|
52
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
53
|
+
marketplaceName: "debug",
|
|
54
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
55
|
+
pluginNames: ["omo"],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// then
|
|
59
|
+
const content = await readFile(configPath, "utf8");
|
|
60
|
+
assert.equal(content.match(/^model\s*=/gm)?.length, 1);
|
|
61
|
+
assert.equal(content.match(/^model_context_window\s*=/gm)?.length, 1);
|
|
62
|
+
assert.equal(content.match(/^model_reasoning_effort\s*=/gm)?.length, 1);
|
|
63
|
+
assert.equal(content.match(/^plan_mode_reasoning_effort\s*=/gm)?.length, 1);
|
|
64
|
+
assert.match(content, /model = "gpt-5\.5"/);
|
|
65
|
+
assert.match(content, /model_context_window = 400000/);
|
|
66
|
+
assert.match(content, /model_reasoning_effort = "high"/);
|
|
67
|
+
assert.match(content, /plan_mode_reasoning_effort = "xhigh"/);
|
|
68
|
+
assert.doesNotMatch(content, /model = "gpt-5\.2"/);
|
|
69
|
+
assert.doesNotMatch(content, /model_context_window = 272000/);
|
|
70
|
+
assert.doesNotMatch(content, /model_reasoning_effort = "low"/);
|
|
71
|
+
assert.doesNotMatch(content, /plan_mode_reasoning_effort = "medium"/);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("#given user-customized model config #when script installer updates config #then preserves user reasoning values", async () => {
|
|
75
|
+
// given
|
|
76
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-reasoning-custom-"));
|
|
77
|
+
const configPath = join(root, "config.toml");
|
|
78
|
+
await writeFile(
|
|
79
|
+
configPath,
|
|
80
|
+
[
|
|
81
|
+
'model = "my-private-model"',
|
|
82
|
+
"model_context_window = 123456",
|
|
83
|
+
'model_reasoning_effort = "medium"',
|
|
84
|
+
'plan_mode_reasoning_effort = "medium"',
|
|
85
|
+
"",
|
|
86
|
+
].join("\n"),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// when
|
|
90
|
+
await updateCodexConfig({
|
|
91
|
+
configPath,
|
|
92
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
93
|
+
marketplaceName: "debug",
|
|
94
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
95
|
+
pluginNames: ["omo"],
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// then
|
|
99
|
+
const content = await readFile(configPath, "utf8");
|
|
100
|
+
assert.match(content, /model = "my-private-model"/);
|
|
101
|
+
assert.match(content, /model_context_window = 123456/);
|
|
102
|
+
assert.match(content, /model_reasoning_effort = "medium"/);
|
|
103
|
+
assert.match(content, /plan_mode_reasoning_effort = "medium"/);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("#given bundled model catalog #when script installer updates config #then reads defaults from catalog", async () => {
|
|
107
|
+
// given
|
|
108
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-reasoning-catalog-"));
|
|
109
|
+
const repoRoot = join(root, "omo-codex");
|
|
110
|
+
const configPath = join(root, "config.toml");
|
|
111
|
+
await mkdir(join(repoRoot, "plugin"), { recursive: true });
|
|
112
|
+
await writeFile(
|
|
113
|
+
join(repoRoot, "plugin", "model-catalog.json"),
|
|
114
|
+
JSON.stringify({
|
|
115
|
+
version: "test.catalog",
|
|
116
|
+
current: {
|
|
117
|
+
model: "catalog-default",
|
|
118
|
+
model_context_window: 123456,
|
|
119
|
+
model_reasoning_effort: "medium",
|
|
120
|
+
plan_mode_reasoning_effort: "high",
|
|
121
|
+
},
|
|
122
|
+
managedProfiles: [],
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// when
|
|
127
|
+
await updateCodexConfig({
|
|
128
|
+
configPath,
|
|
129
|
+
repoRoot,
|
|
130
|
+
marketplaceName: "debug",
|
|
131
|
+
marketplaceSource: { sourceType: "local", source: repoRoot },
|
|
132
|
+
pluginNames: ["omo"],
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// then
|
|
136
|
+
const content = await readFile(configPath, "utf8");
|
|
137
|
+
assert.match(content, /model = "catalog-default"/);
|
|
138
|
+
assert.match(content, /model_context_window = 123456/);
|
|
139
|
+
assert.match(content, /model_reasoning_effort = "medium"/);
|
|
140
|
+
assert.match(content, /plan_mode_reasoning_effort = "high"/);
|
|
141
|
+
});
|
|
@@ -27,6 +27,60 @@ 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 leaves Context7 to the plugin MCP manifest", 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.doesNotMatch(config, /\[mcp_servers\.context7\]/);
|
|
47
|
+
assert.doesNotMatch(config, /@upstash\/context7-mcp/);
|
|
48
|
+
assert.doesNotMatch(config, /YOUR_API_KEY/);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("#given existing Context7 MCP config #when script installer updates config #then leaves user setup untouched", async () => {
|
|
52
|
+
// given
|
|
53
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-context7-existing-"));
|
|
54
|
+
const configPath = join(root, "config.toml");
|
|
55
|
+
await writeFile(
|
|
56
|
+
configPath,
|
|
57
|
+
[
|
|
58
|
+
"[mcp_servers.context7]",
|
|
59
|
+
'command = "node"',
|
|
60
|
+
'args = ["/opt/context7/server.js"]',
|
|
61
|
+
'startup_timeout_sec = 40',
|
|
62
|
+
"",
|
|
63
|
+
].join("\n"),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// when
|
|
67
|
+
await updateCodexConfig({
|
|
68
|
+
configPath,
|
|
69
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
70
|
+
marketplaceName: "debug",
|
|
71
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
72
|
+
pluginNames: ["omo"],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// then
|
|
76
|
+
const config = await readFile(configPath, "utf8");
|
|
77
|
+
assert.match(config, /\[mcp_servers\.context7\]/);
|
|
78
|
+
assert.match(config, /command = "node"/);
|
|
79
|
+
assert.match(config, /args = \["\/opt\/context7\/server\.js"\]/);
|
|
80
|
+
assert.match(config, /startup_timeout_sec = 40/);
|
|
81
|
+
assert.doesNotMatch(config, /YOUR_API_KEY/);
|
|
82
|
+
});
|
|
83
|
+
|
|
30
84
|
test("#given sisyphuslabs config without explicit source #when script installer updates config #then uses local marketplace", async () => {
|
|
31
85
|
// given
|
|
32
86
|
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-sisyphuslabs-"));
|
|
@@ -116,3 +170,154 @@ test("#given legacy boolean MultiAgentV2 flag and table #when script installer u
|
|
|
116
170
|
assert.match(config, /usage_hint_enabled = false/);
|
|
117
171
|
assert.match(config, /max_concurrent_threads_per_session = 10000/);
|
|
118
172
|
});
|
|
173
|
+
|
|
174
|
+
test("#given legacy agents max_threads #when script installer updates config #then removes the conflicting legacy thread cap", async () => {
|
|
175
|
+
// given
|
|
176
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-multi-agent-legacy-threads-"));
|
|
177
|
+
const configPath = join(root, "config.toml");
|
|
178
|
+
await writeFile(
|
|
179
|
+
configPath,
|
|
180
|
+
[
|
|
181
|
+
"[agents]",
|
|
182
|
+
"max_threads = 16",
|
|
183
|
+
"max_depth = 4",
|
|
184
|
+
"job_max_runtime_seconds = 3600",
|
|
185
|
+
"",
|
|
186
|
+
].join("\n"),
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
// when
|
|
190
|
+
await updateCodexConfig({
|
|
191
|
+
configPath,
|
|
192
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
193
|
+
marketplaceName: "debug",
|
|
194
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
195
|
+
pluginNames: ["omo"],
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// then
|
|
199
|
+
const config = await readFile(configPath, "utf8");
|
|
200
|
+
assert.match(config, /\[features\.multi_agent_v2\]/);
|
|
201
|
+
assert.match(config, /enabled = true/);
|
|
202
|
+
assert.match(config, /max_concurrent_threads_per_session = 10000/);
|
|
203
|
+
assert.match(config, /\[agents\]/);
|
|
204
|
+
assert.doesNotMatch(config, /^max_threads\s*=/m);
|
|
205
|
+
assert.match(config, /max_depth = 4/);
|
|
206
|
+
assert.match(config, /job_max_runtime_seconds = 3600/);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("#given managed agent role sections #when script installer updates config #then preserves role config while removing only root agents max_threads", async () => {
|
|
210
|
+
// given
|
|
211
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-multi-agent-role-section-"));
|
|
212
|
+
const configPath = join(root, "config.toml");
|
|
213
|
+
await writeFile(
|
|
214
|
+
configPath,
|
|
215
|
+
[
|
|
216
|
+
"[agents]",
|
|
217
|
+
"max_threads = 16",
|
|
218
|
+
"",
|
|
219
|
+
"[agents.explorer]",
|
|
220
|
+
'description = "read-only explorer"',
|
|
221
|
+
'config_file = "./agents/explorer.toml"',
|
|
222
|
+
"",
|
|
223
|
+
].join("\n"),
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// when
|
|
227
|
+
await updateCodexConfig({
|
|
228
|
+
configPath,
|
|
229
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
230
|
+
marketplaceName: "debug",
|
|
231
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" },
|
|
232
|
+
pluginNames: ["omo"],
|
|
233
|
+
agentConfigs: [{ name: "explorer", configFile: "./agents/explorer.toml" }],
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// then
|
|
237
|
+
const config = await readFile(configPath, "utf8");
|
|
238
|
+
assert.doesNotMatch(config, /^max_threads\s*=/m);
|
|
239
|
+
assert.match(config, /\[agents\.explorer\]/);
|
|
240
|
+
assert.match(config, /description = "read-only explorer"/);
|
|
241
|
+
assert.match(config, /config_file = "\.\/agents\/explorer\.toml"/);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("#given existing trust and lsp blocks #when updating config #then existing blocks are preserved", async () => {
|
|
245
|
+
// given
|
|
246
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-config-baseline-"));
|
|
247
|
+
const configPath = join(root, "config.toml");
|
|
248
|
+
await writeFile(
|
|
249
|
+
configPath,
|
|
250
|
+
[
|
|
251
|
+
'[plugins."omo@sisyphuslabs"]',
|
|
252
|
+
"enabled = true",
|
|
253
|
+
"",
|
|
254
|
+
'[plugins."omo@sisyphuslabs".mcp_servers.lsp]',
|
|
255
|
+
"enabled = true",
|
|
256
|
+
"",
|
|
257
|
+
'[hooks.state."omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0"]',
|
|
258
|
+
'trusted_hash = "sha256:keep"',
|
|
259
|
+
"",
|
|
260
|
+
].join("\n"),
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
// when
|
|
264
|
+
await updateCodexConfig({
|
|
265
|
+
configPath,
|
|
266
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
267
|
+
marketplaceName: "sisyphuslabs",
|
|
268
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex/cache/sisyphuslabs" },
|
|
269
|
+
pluginNames: ["omo"],
|
|
270
|
+
trustedHookStates: [{ key: "omo@sisyphuslabs:hooks/hooks.json:post_tool_use:0:0", trustedHash: "sha256:keep" }],
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// then
|
|
274
|
+
const content = await readFile(configPath, "utf8");
|
|
275
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\]/);
|
|
276
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.lsp\]/);
|
|
277
|
+
assert.match(content, /\[hooks\.state\."omo@sisyphuslabs:hooks\/hooks\.json:post_tool_use:0:0"\]/);
|
|
278
|
+
assert.match(content, /trusted_hash = "sha256:keep"/);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("#given windows platform #when updating config #then enables git_bash plugin mcp policy", async () => {
|
|
282
|
+
// given
|
|
283
|
+
const root = await mkdtemp(join(tmpdir(), "omo-codex-config-git-bash-win32-"));
|
|
284
|
+
const configPath = join(root, "config.toml");
|
|
285
|
+
|
|
286
|
+
// when
|
|
287
|
+
await updateCodexConfig({
|
|
288
|
+
configPath,
|
|
289
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
290
|
+
marketplaceName: "sisyphuslabs",
|
|
291
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex/cache/sisyphuslabs" },
|
|
292
|
+
pluginNames: ["omo"],
|
|
293
|
+
platform: "win32",
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// then
|
|
297
|
+
const content = await readFile(configPath, "utf8");
|
|
298
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.git_bash\]/);
|
|
299
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.git_bash\][\s\S]*?enabled = true/);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("#given non-windows platforms #when updating config #then disables git_bash plugin mcp policy", async () => {
|
|
303
|
+
for (const platform of ["linux", "darwin"]) {
|
|
304
|
+
// given
|
|
305
|
+
const root = await mkdtemp(join(tmpdir(), `omo-codex-config-git-bash-${platform}-`));
|
|
306
|
+
const configPath = join(root, "config.toml");
|
|
307
|
+
|
|
308
|
+
// when
|
|
309
|
+
await updateCodexConfig({
|
|
310
|
+
configPath,
|
|
311
|
+
repoRoot: "/repo/packages/omo-codex",
|
|
312
|
+
marketplaceName: "sisyphuslabs",
|
|
313
|
+
marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex/cache/sisyphuslabs" },
|
|
314
|
+
pluginNames: ["omo"],
|
|
315
|
+
platform,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// then
|
|
319
|
+
const content = await readFile(configPath, "utf8");
|
|
320
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.git_bash\]/);
|
|
321
|
+
assert.match(content, /\[plugins\."omo@sisyphuslabs"\.mcp_servers\.git_bash\][\s\S]*?enabled = false/);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
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 autonomous 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"],
|
|
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 install opt-out #when running the Node installer entrypoint #then preserves existing Codex permission settings", () => {
|
|
76
|
+
// given
|
|
77
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
78
|
+
|
|
79
|
+
// when
|
|
80
|
+
const output = execFileSync(
|
|
81
|
+
process.execPath,
|
|
82
|
+
[scriptPath, "--dry-run", "install", "--no-tui", "--no-codex-autonomous"],
|
|
83
|
+
{ encoding: "utf8" },
|
|
84
|
+
).trim();
|
|
85
|
+
|
|
86
|
+
// then
|
|
87
|
+
assert.equal(output, "npx --yes --package oh-my-openagent omo install --platform=codex --no-tui --no-codex-autonomous");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("#given dry-run doctor #when running the Node installer entrypoint #then prints delegated doctor command", () => {
|
|
91
|
+
// given
|
|
92
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
93
|
+
|
|
94
|
+
// when
|
|
95
|
+
const output = execFileSync(process.execPath, [scriptPath, "--dry-run", "doctor"], {
|
|
96
|
+
encoding: "utf8",
|
|
97
|
+
}).trim();
|
|
98
|
+
|
|
99
|
+
// then
|
|
100
|
+
assert.equal(output, "npx --yes --package oh-my-openagent omo doctor");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("#given dry-run cleanup #when running the Node installer entrypoint #then prints delegated codex cleanup command", () => {
|
|
104
|
+
// given
|
|
105
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
106
|
+
|
|
107
|
+
// when
|
|
108
|
+
const output = execFileSync(
|
|
109
|
+
process.execPath,
|
|
110
|
+
[scriptPath, "--dry-run", "cleanup", "--project", "/tmp/lazycodex-qa"],
|
|
111
|
+
{ encoding: "utf8" },
|
|
112
|
+
).trim();
|
|
113
|
+
|
|
114
|
+
// then
|
|
115
|
+
assert.equal(output, "npx --yes --package oh-my-openagent omo cleanup --platform=codex --project /tmp/lazycodex-qa");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("#given dry-run ulw-loop #when running the Node installer entrypoint #then prints delegated ulw-loop command", () => {
|
|
119
|
+
// given
|
|
120
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
121
|
+
|
|
122
|
+
// when
|
|
123
|
+
const output = execFileSync(process.execPath, [scriptPath, "--dry-run", "ulw-loop", "help"], {
|
|
124
|
+
encoding: "utf8",
|
|
125
|
+
}).trim();
|
|
126
|
+
|
|
127
|
+
// then
|
|
128
|
+
assert.equal(output, "npx --yes --package oh-my-openagent omo ulw-loop help");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("#given the invoking argv path disappears #when importing the Node installer module #then the entrypoint guard does not throw", () => {
|
|
132
|
+
// given
|
|
133
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
134
|
+
const tempDir = mkdtempSync(join(tmpdir(), "lazycodex-import-"));
|
|
135
|
+
const missingArgvPath = join(tempDir, "missing-entrypoint.mjs");
|
|
136
|
+
const probePath = join(tempDir, "probe.mjs");
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
writeFileSync(
|
|
140
|
+
probePath,
|
|
141
|
+
[
|
|
142
|
+
`process.argv[1] = ${JSON.stringify(missingArgvPath)};`,
|
|
143
|
+
`await import(${JSON.stringify(pathToFileURL(scriptPath).href)});`,
|
|
144
|
+
].join("\n"),
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// when
|
|
148
|
+
const output = execFileSync(process.execPath, [probePath], {
|
|
149
|
+
encoding: "utf8",
|
|
150
|
+
}).trim();
|
|
151
|
+
|
|
152
|
+
// then
|
|
153
|
+
assert.equal(output, "");
|
|
154
|
+
} finally {
|
|
155
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
156
|
+
}
|
|
157
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, stat, 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 { installMarketplaceLocally } from "./install-local.mjs";
|
|
8
|
+
|
|
9
|
+
const windowsGitBashPath = "C:\\Program Files\\Git\\bin\\bash.exe";
|
|
10
|
+
const lspCliPath = join(process.cwd(), "packages", "lsp-tools-mcp", "dist", "cli.js");
|
|
11
|
+
|
|
12
|
+
async function withBundledLspRuntimeForTest(run) {
|
|
13
|
+
try {
|
|
14
|
+
await stat(lspCliPath);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (!(error instanceof Error)) throw error;
|
|
17
|
+
await mkdir(join(process.cwd(), "packages", "lsp-tools-mcp", "dist"), { recursive: true });
|
|
18
|
+
await writeFile(lspCliPath, "#!/usr/bin/env node\n");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return run();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
test("#given Windows without Git Bash and auto install skip env #when installing local marketplace #then rejects before marketplace or config mutation", async () => {
|
|
25
|
+
const repoRoot = await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-missing-repo-"));
|
|
26
|
+
const codexHome = await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-missing-home-"));
|
|
27
|
+
const commands = [];
|
|
28
|
+
|
|
29
|
+
await assert.rejects(
|
|
30
|
+
installMarketplaceLocally({
|
|
31
|
+
repoRoot,
|
|
32
|
+
codexHome,
|
|
33
|
+
platform: "win32",
|
|
34
|
+
env: { OMO_CODEX_SKIP_GIT_BASH_AUTO_INSTALL: "1" },
|
|
35
|
+
gitBashResolver: () => ({
|
|
36
|
+
found: false,
|
|
37
|
+
checkedPaths: [windowsGitBashPath],
|
|
38
|
+
installHint: [
|
|
39
|
+
"Git Bash is required.",
|
|
40
|
+
"winget install --id Git.Git -e --source winget",
|
|
41
|
+
"OMO_CODEX_GIT_BASH_PATH=C:\\path\\to\\bash.exe",
|
|
42
|
+
"rerun `npx lazycodex-ai install`",
|
|
43
|
+
].join("\n"),
|
|
44
|
+
}),
|
|
45
|
+
runCommand: async (command, args, options) => {
|
|
46
|
+
commands.push([command, ...args, options.cwd].join(" "));
|
|
47
|
+
},
|
|
48
|
+
log: () => {},
|
|
49
|
+
}),
|
|
50
|
+
/winget install --id Git\.Git -e --source winget/,
|
|
51
|
+
);
|
|
52
|
+
assert.deepEqual(commands, []);
|
|
53
|
+
await assert.rejects(stat(join(codexHome, "config.toml")), /ENOENT/);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("#given Windows without Git Bash #when winget succeeds and resolver recovers #then install continues", async () => {
|
|
57
|
+
const runCalls = [];
|
|
58
|
+
const resolutions = [
|
|
59
|
+
{ found: false, checkedPaths: [windowsGitBashPath], installHint: "install hint before winget" },
|
|
60
|
+
{ found: true, path: windowsGitBashPath, source: "program-files" },
|
|
61
|
+
];
|
|
62
|
+
let resolveCallCount = 0;
|
|
63
|
+
|
|
64
|
+
const result = await withBundledLspRuntimeForTest(async () => installMarketplaceLocally({
|
|
65
|
+
repoRoot: process.cwd(),
|
|
66
|
+
codexHome: await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-auto-home-")),
|
|
67
|
+
binDir: await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-auto-bin-")),
|
|
68
|
+
platform: "win32",
|
|
69
|
+
gitBashResolver: () => resolutions[resolveCallCount++] ?? resolutions[resolutions.length - 1],
|
|
70
|
+
runCommand: async (command, args, options) => {
|
|
71
|
+
runCalls.push([command, ...args, options.cwd].join(" "));
|
|
72
|
+
},
|
|
73
|
+
log: () => {},
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
assert.equal(resolveCallCount, 2);
|
|
77
|
+
assert.match(runCalls.join("\n"), /^winget install --id Git\.Git -e --source winget /m);
|
|
78
|
+
assert.equal(result.gitBashPath, windowsGitBashPath);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("#given non-Windows install #when running installer #then winget is never called", async () => {
|
|
82
|
+
const runCalls = [];
|
|
83
|
+
const result = await withBundledLspRuntimeForTest(async () => installMarketplaceLocally({
|
|
84
|
+
repoRoot: process.cwd(),
|
|
85
|
+
codexHome: await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-no-winget-home-")),
|
|
86
|
+
binDir: await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-no-winget-bin-")),
|
|
87
|
+
platform: "linux",
|
|
88
|
+
runCommand: async (command, args, options) => {
|
|
89
|
+
runCalls.push([command, ...args, options.cwd].join(" "));
|
|
90
|
+
},
|
|
91
|
+
log: () => {},
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
assert.equal(result.gitBashPath, null);
|
|
95
|
+
assert.equal(runCalls.some((command) => command.startsWith("winget ")), false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("#given Windows env override resolves Git Bash #when installing local marketplace #then install continues", async () => {
|
|
99
|
+
const result = await withBundledLspRuntimeForTest(async () => installMarketplaceLocally({
|
|
100
|
+
repoRoot: process.cwd(),
|
|
101
|
+
codexHome: await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-home-")),
|
|
102
|
+
binDir: await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-bin-")),
|
|
103
|
+
platform: "win32",
|
|
104
|
+
gitBashResolver: () => ({ found: true, path: windowsGitBashPath, source: "env" }),
|
|
105
|
+
runCommand: async () => {},
|
|
106
|
+
log: () => {},
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
assert.equal(result.gitBashPath, windowsGitBashPath);
|
|
110
|
+
assert.equal(result.installed.length, 1);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("#given Windows env override in installer options #when no custom resolver is provided #then default resolver uses it", async () => {
|
|
114
|
+
const codexHome = await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-env-home-"));
|
|
115
|
+
const gitBashPath = join(await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-env-")), "bash.exe");
|
|
116
|
+
await writeFile(gitBashPath, "");
|
|
117
|
+
|
|
118
|
+
const result = await withBundledLspRuntimeForTest(async () => installMarketplaceLocally({
|
|
119
|
+
repoRoot: process.cwd(),
|
|
120
|
+
codexHome,
|
|
121
|
+
binDir: await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-env-bin-")),
|
|
122
|
+
platform: "win32",
|
|
123
|
+
env: { OMO_CODEX_GIT_BASH_PATH: gitBashPath },
|
|
124
|
+
runCommand: async () => {},
|
|
125
|
+
log: () => {},
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
assert.equal(result.gitBashPath, gitBashPath);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("#given non-Windows local install #when resolver would fail #then installer keeps existing behavior", async () => {
|
|
132
|
+
const codexHome = await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-linux-home-"));
|
|
133
|
+
const result = await withBundledLspRuntimeForTest(async () => installMarketplaceLocally({
|
|
134
|
+
repoRoot: process.cwd(),
|
|
135
|
+
codexHome,
|
|
136
|
+
binDir: await mkdtemp(join(tmpdir(), "omo-codex-script-git-bash-linux-bin-")),
|
|
137
|
+
platform: "linux",
|
|
138
|
+
gitBashResolver: () => ({ found: false, checkedPaths: [windowsGitBashPath], installHint: "should not be used" }),
|
|
139
|
+
runCommand: async () => {},
|
|
140
|
+
log: () => {},
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
assert.equal(result.gitBashPath, null);
|
|
144
|
+
assert.match(await readFile(join(codexHome, "config.toml"), "utf8"), /\[marketplaces\.sisyphuslabs\]/);
|
|
145
|
+
});
|