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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// biome-ignore-all format: smoke test pulls verbatim JSON for structural assertion.
|
|
2
|
-
import {
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { chmod, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
3
5
|
import { dirname, join, resolve } from "node:path";
|
|
4
6
|
import { fileURLToPath } from "node:url";
|
|
5
7
|
import { describe, expect, it } from "vitest";
|
|
@@ -14,6 +16,41 @@ async function readJson(relative: string): Promise<unknown> {
|
|
|
14
16
|
return JSON.parse(await readText(relative));
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
type ShellResult = {
|
|
20
|
+
readonly code: number | null;
|
|
21
|
+
readonly stdout: string;
|
|
22
|
+
readonly stderr: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function bootstrapScriptFrom(text: string): string {
|
|
26
|
+
const heading = text.indexOf("### 1. Create goals from the brief");
|
|
27
|
+
expect(heading).toBeGreaterThanOrEqual(0);
|
|
28
|
+
const blockStart = text.indexOf("```sh\n", heading);
|
|
29
|
+
expect(blockStart).toBeGreaterThanOrEqual(0);
|
|
30
|
+
const codeStart = blockStart + "```sh\n".length;
|
|
31
|
+
const blockEnd = text.indexOf("\n```", codeStart);
|
|
32
|
+
expect(blockEnd).toBeGreaterThan(codeStart);
|
|
33
|
+
return text.slice(codeStart, blockEnd);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function runShell(script: string, env: NodeJS.ProcessEnv): Promise<ShellResult> {
|
|
37
|
+
return new Promise((resolvePromise, reject) => {
|
|
38
|
+
const child = spawn("/bin/sh", ["-c", script], { env });
|
|
39
|
+
const stdout: Buffer[] = [];
|
|
40
|
+
const stderr: Buffer[] = [];
|
|
41
|
+
child.stdout.on("data", (chunk: Buffer) => stdout.push(chunk));
|
|
42
|
+
child.stderr.on("data", (chunk: Buffer) => stderr.push(chunk));
|
|
43
|
+
child.on("error", reject);
|
|
44
|
+
child.on("close", (code) => {
|
|
45
|
+
resolvePromise({
|
|
46
|
+
code,
|
|
47
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
48
|
+
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
17
54
|
describe("package.json", () => {
|
|
18
55
|
it("declares ESM + npm + Node >=20", async () => {
|
|
19
56
|
const pkg = await readJson("package.json") as Record<string, unknown>;
|
|
@@ -102,13 +139,13 @@ describe("skills/ulw-loop/SKILL.md", () => {
|
|
|
102
139
|
});
|
|
103
140
|
|
|
104
141
|
it("references the success criteria and record-evidence vocabulary", async () => {
|
|
105
|
-
const text = await readText("skills/ulw-loop/
|
|
142
|
+
const text = await readText("skills/ulw-loop/references/full-workflow.md");
|
|
106
143
|
expect(text.toLowerCase()).toMatch(/success criteria|successcriteria/);
|
|
107
144
|
expect(text.toLowerCase()).toContain("record-evidence");
|
|
108
145
|
});
|
|
109
146
|
|
|
110
147
|
it("#given omo is absent from PATH #when bootstrap instructions are read #then local cached CLI fallback is documented", async () => {
|
|
111
|
-
const text = await readText("skills/ulw-loop/
|
|
148
|
+
const text = await readText("skills/ulw-loop/references/full-workflow.md");
|
|
112
149
|
|
|
113
150
|
expect(text).toContain("If `omo` is absent from PATH");
|
|
114
151
|
expect(text).toContain("ULW_LOOP_CLI");
|
|
@@ -116,7 +153,7 @@ describe("skills/ulw-loop/SKILL.md", () => {
|
|
|
116
153
|
});
|
|
117
154
|
|
|
118
155
|
it("#given empty PATH #when bootstrap instructions are read #then handles empty PATH without losing notepad bootstrap", async () => {
|
|
119
|
-
const text = await readText("skills/ulw-loop/
|
|
156
|
+
const text = await readText("skills/ulw-loop/references/full-workflow.md");
|
|
120
157
|
|
|
121
158
|
expect(text).toContain("If PATH is empty");
|
|
122
159
|
expect(text).toContain("ULW_LOOP_NODE");
|
|
@@ -124,13 +161,57 @@ describe("skills/ulw-loop/SKILL.md", () => {
|
|
|
124
161
|
expect(text).not.toContain("ls -1");
|
|
125
162
|
});
|
|
126
163
|
|
|
164
|
+
it("#given PATH omo lacks ulw-loop #when bootstrap runs #then falls back to cached ulw-loop CLI", async () => {
|
|
165
|
+
const text = await readText("skills/ulw-loop/references/full-workflow.md");
|
|
166
|
+
const bootstrap = bootstrapScriptFrom(text);
|
|
167
|
+
const root = await mkdtemp(join(tmpdir(), "omo-ulw-loop-bootstrap-"));
|
|
168
|
+
try {
|
|
169
|
+
const badBin = join(root, "bad-bin");
|
|
170
|
+
const home = join(root, "home");
|
|
171
|
+
const codexHome = join(home, ".codex");
|
|
172
|
+
const cachedCli = join(codexHome, "plugins", "cache", "sisyphuslabs", "omo", "0.1.0", "components", "ulw-loop", "dist", "cli.js");
|
|
173
|
+
await mkdir(badBin, { recursive: true });
|
|
174
|
+
await mkdir(dirname(cachedCli), { recursive: true });
|
|
175
|
+
await writeFile(join(badBin, "omo"), "#!/bin/sh\nprintf '%s\\n' \"error: unknown command 'ulw-loop'\" >&2\nexit 1\n");
|
|
176
|
+
await chmod(join(badBin, "omo"), 0o755);
|
|
177
|
+
await writeFile(
|
|
178
|
+
cachedCli,
|
|
179
|
+
[
|
|
180
|
+
"#!/usr/bin/env node",
|
|
181
|
+
"const args = process.argv.slice(2);",
|
|
182
|
+
"if (args[0] === 'ulw-loop' && args[1] === 'help') process.exit(0);",
|
|
183
|
+
"if (args[0] === 'ulw-loop' && args[1] === 'status' && args.includes('--json')) {",
|
|
184
|
+
" console.log(JSON.stringify({ ok: true, source: 'cached-ulw-loop' }));",
|
|
185
|
+
" process.exit(0);",
|
|
186
|
+
"}",
|
|
187
|
+
"console.error('unexpected args: ' + args.join(' '));",
|
|
188
|
+
"process.exit(1);",
|
|
189
|
+
"",
|
|
190
|
+
].join("\n"),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const result = await runShell(`${bootstrap}\nomo ulw-loop status --json`, {
|
|
194
|
+
...process.env,
|
|
195
|
+
CODEX_HOME: codexHome,
|
|
196
|
+
HOME: home,
|
|
197
|
+
PATH: `${badBin}:${process.env["PATH"] ?? ""}`,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
expect(result.code).toBe(0);
|
|
201
|
+
expect(result.stdout).toContain('"source":"cached-ulw-loop"');
|
|
202
|
+
expect(result.stderr).not.toContain("unknown command");
|
|
203
|
+
} finally {
|
|
204
|
+
await rm(root, { recursive: true, force: true });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
127
208
|
it("uses the .omo workspace path", async () => {
|
|
128
209
|
const text = await readText("skills/ulw-loop/SKILL.md");
|
|
129
210
|
expect(text).toContain(".omo/ulw-loop");
|
|
130
211
|
});
|
|
131
212
|
|
|
132
213
|
it("#given long Codex runs #when worker guidance is inspected #then avoids context-expensive agent polling", async () => {
|
|
133
|
-
const text = await readText("skills/ulw-loop/
|
|
214
|
+
const text = await readText("skills/ulw-loop/references/full-workflow.md");
|
|
134
215
|
|
|
135
216
|
expect(text).toMatch(/list_agents/);
|
|
136
217
|
expect(text).toMatch(/polling or status tool/);
|
|
@@ -139,9 +220,25 @@ describe("skills/ulw-loop/SKILL.md", () => {
|
|
|
139
220
|
expect(text).toMatch(/wait_agent.*completion/);
|
|
140
221
|
expect(text).toMatch(/targeted followups only when needed/);
|
|
141
222
|
expect(text).toMatch(/close_agent.*after integrating each result/);
|
|
223
|
+
expect(text).toMatch(/Plan and reviewer agents may run for a long time/);
|
|
224
|
+
expect(text).toMatch(/short wait_agent cycles/);
|
|
225
|
+
expect(text).toMatch(/single long blocking wait/);
|
|
142
226
|
expect(text).toContain("Every worker message MUST carry");
|
|
143
227
|
expect(text).toContain("Each worker does strict TDD");
|
|
144
228
|
});
|
|
229
|
+
|
|
230
|
+
it("#given Codex subagent delegation #when worker guidance is inspected #then assignment ambiguity is hardened", async () => {
|
|
231
|
+
const text = await readText("skills/ulw-loop/SKILL.md");
|
|
232
|
+
|
|
233
|
+
expect(text).toMatch(/TASK:/);
|
|
234
|
+
expect(text).toMatch(/fork_turns:\s*"none"/);
|
|
235
|
+
expect(text).toMatch(/wait_agent.*signal, not proof/);
|
|
236
|
+
expect(text).toMatch(/one targeted followup/);
|
|
237
|
+
expect(text).toMatch(/respawn.*smaller/);
|
|
238
|
+
expect(text).toMatch(/Plan and reviewer agents may run for a long time/);
|
|
239
|
+
expect(text).toMatch(/short wait_agent cycles/);
|
|
240
|
+
expect(text).toMatch(/single long blocking wait/);
|
|
241
|
+
});
|
|
145
242
|
});
|
|
146
243
|
|
|
147
244
|
describe("source LOC budget", () => {
|
|
@@ -20,6 +20,17 @@
|
|
|
20
20
|
"statusMessage": "LazyCodex(0.1.0): Recording Session Telemetry"
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"matcher": "^startup$",
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "node \"${PLUGIN_ROOT}/scripts/auto-update.mjs\" hook session-start",
|
|
30
|
+
"timeout": 5,
|
|
31
|
+
"statusMessage": "LazyCodex(0.1.0): Checking Auto Update"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
23
34
|
}
|
|
24
35
|
],
|
|
25
36
|
"UserPromptSubmit": [
|
|
@@ -55,6 +66,17 @@
|
|
|
55
66
|
}
|
|
56
67
|
],
|
|
57
68
|
"PreToolUse": [
|
|
69
|
+
{
|
|
70
|
+
"matcher": "^Bash$",
|
|
71
|
+
"hooks": [
|
|
72
|
+
{
|
|
73
|
+
"type": "command",
|
|
74
|
+
"command": "node \"${PLUGIN_ROOT}/components/git-bash/dist/cli.js\" hook pre-tool-use",
|
|
75
|
+
"timeout": 5,
|
|
76
|
+
"statusMessage": "LazyCodex(0.1.0): Recommending Git Bash Mcp"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
},
|
|
58
80
|
{
|
|
59
81
|
"matcher": "^create_goal$",
|
|
60
82
|
"hooks": [
|
|
@@ -75,13 +97,13 @@
|
|
|
75
97
|
"type": "command",
|
|
76
98
|
"command": "node \"${PLUGIN_ROOT}/components/comment-checker/dist/cli.js\" hook post-tool-use",
|
|
77
99
|
"timeout": 30,
|
|
78
|
-
"statusMessage": "LazyCodex(0.1.
|
|
100
|
+
"statusMessage": "LazyCodex(0.1.1): Checking Comments"
|
|
79
101
|
},
|
|
80
102
|
{
|
|
81
103
|
"type": "command",
|
|
82
104
|
"command": "node \"${PLUGIN_ROOT}/components/lsp/dist/cli.js\" hook post-tool-use",
|
|
83
105
|
"timeout": 60,
|
|
84
|
-
"statusMessage": "LazyCodex(0.
|
|
106
|
+
"statusMessage": "LazyCodex(0.2.0): Checking LSP Diagnostics"
|
|
85
107
|
}
|
|
86
108
|
]
|
|
87
109
|
},
|
|
@@ -98,6 +120,17 @@
|
|
|
98
120
|
}
|
|
99
121
|
],
|
|
100
122
|
"PostCompact": [
|
|
123
|
+
{
|
|
124
|
+
"matcher": "manual|auto",
|
|
125
|
+
"hooks": [
|
|
126
|
+
{
|
|
127
|
+
"type": "command",
|
|
128
|
+
"command": "node \"${PLUGIN_ROOT}/components/git-bash/dist/cli.js\" hook post-compact",
|
|
129
|
+
"timeout": 5,
|
|
130
|
+
"statusMessage": "LazyCodex(0.1.0): Resetting Git Bash Mcp Reminder"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
},
|
|
101
134
|
{
|
|
102
135
|
"matcher": "manual|auto",
|
|
103
136
|
"hooks": [
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2026-06-03.gpt-5.5-400k",
|
|
3
|
+
"current": {
|
|
4
|
+
"model": "gpt-5.5",
|
|
5
|
+
"model_context_window": 400000,
|
|
6
|
+
"model_reasoning_effort": "high",
|
|
7
|
+
"plan_mode_reasoning_effort": "xhigh"
|
|
8
|
+
},
|
|
9
|
+
"roles": {
|
|
10
|
+
"default": {
|
|
11
|
+
"model": "gpt-5.5",
|
|
12
|
+
"model_context_window": 400000,
|
|
13
|
+
"model_reasoning_effort": "high",
|
|
14
|
+
"plan_mode_reasoning_effort": "xhigh"
|
|
15
|
+
},
|
|
16
|
+
"verifier": {
|
|
17
|
+
"model": "gpt-5.5",
|
|
18
|
+
"model_reasoning_effort": "xhigh"
|
|
19
|
+
},
|
|
20
|
+
"worker": {
|
|
21
|
+
"model": "gpt-5.4",
|
|
22
|
+
"model_reasoning_effort": "high"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"managedProfiles": [
|
|
26
|
+
{
|
|
27
|
+
"version": "legacy.gpt-5.2",
|
|
28
|
+
"match": {
|
|
29
|
+
"model": "gpt-5.2"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"version": "legacy.gpt-5.4-1m",
|
|
34
|
+
"match": {
|
|
35
|
+
"model": "gpt-5.4",
|
|
36
|
+
"model_context_window": 1000000,
|
|
37
|
+
"model_reasoning_effort": "high",
|
|
38
|
+
"plan_mode_reasoning_effort": "xhigh"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"version": "legacy.gpt-5.5-272k",
|
|
43
|
+
"match": {
|
|
44
|
+
"model": "gpt-5.5",
|
|
45
|
+
"model_context_window": 272000
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"version": "0.1.0",
|
|
10
10
|
"workspaces": [
|
|
11
11
|
"components/comment-checker",
|
|
12
|
+
"components/git-bash",
|
|
12
13
|
"components/rules",
|
|
13
14
|
"components/lsp",
|
|
14
15
|
"components/telemetry",
|
|
@@ -61,6 +62,20 @@
|
|
|
61
62
|
"@code-yeongyu/comment-checker": "^0.8.0"
|
|
62
63
|
}
|
|
63
64
|
},
|
|
65
|
+
"components/git-bash": {
|
|
66
|
+
"name": "@sisyphuslabs/codex-git-bash-hook",
|
|
67
|
+
"version": "0.1.0",
|
|
68
|
+
"bin": {
|
|
69
|
+
"omo-git-bash-hook": "dist/cli.js"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/node": "^25.7.0",
|
|
73
|
+
"typescript": "^6.0.3"
|
|
74
|
+
},
|
|
75
|
+
"engines": {
|
|
76
|
+
"node": ">=20.0.0"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
64
79
|
"components/lsp": {
|
|
65
80
|
"name": "@code-yeongyu/codex-lsp",
|
|
66
81
|
"version": "0.2.0",
|
|
@@ -765,6 +780,10 @@
|
|
|
765
780
|
"dev": true,
|
|
766
781
|
"license": "MIT"
|
|
767
782
|
},
|
|
783
|
+
"node_modules/@sisyphuslabs/codex-git-bash-hook": {
|
|
784
|
+
"resolved": "components/git-bash",
|
|
785
|
+
"link": true
|
|
786
|
+
},
|
|
768
787
|
"node_modules/@standard-schema/spec": {
|
|
769
788
|
"version": "1.1.0",
|
|
770
789
|
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"private": true,
|
|
8
8
|
"workspaces": [
|
|
9
9
|
"components/comment-checker",
|
|
10
|
+
"components/git-bash",
|
|
10
11
|
"components/rules",
|
|
11
12
|
"components/lsp",
|
|
12
13
|
"components/telemetry",
|
|
@@ -18,8 +19,9 @@
|
|
|
18
19
|
"@oh-my-opencode/shared-skills": "file:../../shared-skills"
|
|
19
20
|
},
|
|
20
21
|
"scripts": {
|
|
21
|
-
"build": "node scripts/build-bundled-mcp-runtimes.mjs && node scripts/sync-skills.mjs && node ../scripts/sync-telemetry-component.mjs && node scripts/build-components.mjs",
|
|
22
|
+
"build": "node scripts/sync-hook-status-messages.mjs && node scripts/build-bundled-mcp-runtimes.mjs && node scripts/sync-skills.mjs && node ../scripts/sync-telemetry-component.mjs && node scripts/build-components.mjs",
|
|
22
23
|
"check": "npm run build && npm test",
|
|
24
|
+
"sync:hooks": "node scripts/sync-hook-status-messages.mjs",
|
|
23
25
|
"sync:skills": "node scripts/sync-skills.mjs",
|
|
24
26
|
"test": "node --test test/*.test.mjs"
|
|
25
27
|
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
4
|
+
import { mkdir, open, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
import { migrateCodexConfig } from "./migrate-codex-config.mjs";
|
|
9
|
+
|
|
10
|
+
const DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1_000;
|
|
11
|
+
const DEFAULT_LOCK_STALE_MS = 10 * 60 * 1_000;
|
|
12
|
+
|
|
13
|
+
export function resolveAutoUpdatePlan({ env = process.env, now = Date.now(), lastCheckedAt } = {}) {
|
|
14
|
+
if (env.LAZYCODEX_AUTO_UPDATE_DISABLED === "1" || env.OMO_CODEX_AUTO_UPDATE_DISABLED === "1") {
|
|
15
|
+
return { shouldRun: false, reason: "disabled" };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const intervalMs = parsePositiveInteger(env.LAZYCODEX_AUTO_UPDATE_INTERVAL_MS, DEFAULT_INTERVAL_MS);
|
|
19
|
+
if (typeof lastCheckedAt === "number" && intervalMs > 0 && now - lastCheckedAt < intervalMs) {
|
|
20
|
+
return { shouldRun: false, reason: "throttled" };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
shouldRun: true,
|
|
25
|
+
command: resolveCommand(env),
|
|
26
|
+
args: resolveArgs(env),
|
|
27
|
+
env: {
|
|
28
|
+
...env,
|
|
29
|
+
LAZYCODEX_AUTO_UPDATE_DISABLED: "1",
|
|
30
|
+
OMO_CODEX_AUTO_UPDATE_DISABLED: "1",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function runAutoUpdateCheck({ env = process.env, now = Date.now() } = {}) {
|
|
36
|
+
await runConfigMigration({ env });
|
|
37
|
+
const statePath = resolveStatePath(env);
|
|
38
|
+
const state = await readState(statePath);
|
|
39
|
+
const plan = resolveAutoUpdatePlan({ env, now, lastCheckedAt: state.lastCheckedAt });
|
|
40
|
+
if (!plan.shouldRun) return { started: false, reason: plan.reason };
|
|
41
|
+
|
|
42
|
+
const lock = await acquireLock(resolveLockPath(env, statePath), now, env);
|
|
43
|
+
if (lock === null) return { started: false, reason: "locked" };
|
|
44
|
+
try {
|
|
45
|
+
await writeState(statePath, { lastCheckedAt: now });
|
|
46
|
+
if (env.LAZYCODEX_AUTO_UPDATE_WAIT === "1") {
|
|
47
|
+
const result = spawnSync(plan.command, plan.args, {
|
|
48
|
+
env: plan.env,
|
|
49
|
+
stdio: "ignore",
|
|
50
|
+
});
|
|
51
|
+
return { started: true, status: result.status ?? 0 };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const child = spawn(plan.command, plan.args, {
|
|
55
|
+
env: plan.env,
|
|
56
|
+
stdio: "ignore",
|
|
57
|
+
detached: true,
|
|
58
|
+
});
|
|
59
|
+
child.unref();
|
|
60
|
+
return { started: true };
|
|
61
|
+
} finally {
|
|
62
|
+
await lock.release();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function runConfigMigration({ env }) {
|
|
67
|
+
if (env.LAZYCODEX_CONFIG_MIGRATION_DISABLED === "1" || env.OMO_CODEX_CONFIG_MIGRATION_DISABLED === "1") return;
|
|
68
|
+
try {
|
|
69
|
+
await migrateCodexConfig({ env });
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (!(error instanceof Error)) throw error;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function resolveCommand(env) {
|
|
77
|
+
return env.LAZYCODEX_AUTO_UPDATE_COMMAND?.trim() || "npx";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resolveArgs(env) {
|
|
81
|
+
if (env.LAZYCODEX_AUTO_UPDATE_ARGS_JSON) {
|
|
82
|
+
const parsed = JSON.parse(env.LAZYCODEX_AUTO_UPDATE_ARGS_JSON);
|
|
83
|
+
if (!Array.isArray(parsed) || parsed.some((value) => typeof value !== "string")) {
|
|
84
|
+
throw new TypeError("LAZYCODEX_AUTO_UPDATE_ARGS_JSON must be a JSON string array");
|
|
85
|
+
}
|
|
86
|
+
return parsed;
|
|
87
|
+
}
|
|
88
|
+
return ["--yes", "lazycodex-ai@latest", "install", "--no-tui", "--skip-auth"];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function resolveStatePath(env) {
|
|
92
|
+
if (env.LAZYCODEX_AUTO_UPDATE_STATE_PATH?.trim()) return env.LAZYCODEX_AUTO_UPDATE_STATE_PATH;
|
|
93
|
+
const dataRoot = env.PLUGIN_DATA?.trim() || join(homedir(), ".local", "share", "lazycodex");
|
|
94
|
+
return join(dataRoot, "auto-update.json");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveLockPath(env, statePath) {
|
|
98
|
+
if (env.LAZYCODEX_AUTO_UPDATE_LOCK_PATH?.trim()) return env.LAZYCODEX_AUTO_UPDATE_LOCK_PATH;
|
|
99
|
+
return `${statePath}.lock`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function acquireLock(lockPath, now, env) {
|
|
103
|
+
await mkdir(dirname(lockPath), { recursive: true });
|
|
104
|
+
const staleMs = parsePositiveInteger(env.LAZYCODEX_AUTO_UPDATE_LOCK_STALE_MS, DEFAULT_LOCK_STALE_MS);
|
|
105
|
+
try {
|
|
106
|
+
const handle = await open(lockPath, "wx");
|
|
107
|
+
await handle.writeFile(`${now}\n`);
|
|
108
|
+
await handle.close();
|
|
109
|
+
return {
|
|
110
|
+
release: () => rm(lockPath, { force: true }),
|
|
111
|
+
};
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) throw error;
|
|
114
|
+
if (!(await removeStaleLock(lockPath, now, staleMs))) return null;
|
|
115
|
+
return acquireLock(lockPath, now, { ...env, LAZYCODEX_AUTO_UPDATE_LOCK_STALE_MS: "0" });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function removeStaleLock(lockPath, now, staleMs) {
|
|
120
|
+
if (staleMs <= 0) return false;
|
|
121
|
+
try {
|
|
122
|
+
const lockStat = await stat(lockPath);
|
|
123
|
+
if (now - lockStat.mtimeMs < staleMs) return false;
|
|
124
|
+
await rm(lockPath, { force: true });
|
|
125
|
+
return true;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return true;
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function readState(statePath) {
|
|
133
|
+
try {
|
|
134
|
+
const raw = await readFile(statePath, "utf8");
|
|
135
|
+
const parsed = JSON.parse(raw);
|
|
136
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return {};
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function writeState(statePath, state) {
|
|
144
|
+
await mkdir(dirname(statePath), { recursive: true });
|
|
145
|
+
await writeFile(statePath, `${JSON.stringify(state, null, 2)}\n`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function parsePositiveInteger(value, fallback) {
|
|
149
|
+
if (value === undefined || value === "") return fallback;
|
|
150
|
+
const parsed = Number.parseInt(value, 10);
|
|
151
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
155
|
+
runAutoUpdateCheck().catch((error) => {
|
|
156
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
157
|
+
process.exit(0);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
@@ -18,6 +18,11 @@ const runtimes = [
|
|
|
18
18
|
packageRoot: join(repoPackagesRoot, "ast-grep-mcp"),
|
|
19
19
|
requiredOutputs: ["dist/cli.js"],
|
|
20
20
|
},
|
|
21
|
+
{
|
|
22
|
+
label: "git-bash-mcp",
|
|
23
|
+
packageRoot: join(repoPackagesRoot, "git-bash-mcp"),
|
|
24
|
+
requiredOutputs: ["dist/cli.js"],
|
|
25
|
+
},
|
|
21
26
|
];
|
|
22
27
|
|
|
23
28
|
for (const runtime of runtimes) {
|
|
@@ -25,20 +30,30 @@ for (const runtime of runtimes) {
|
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
function buildRuntime(runtime) {
|
|
33
|
+
if (hasBundledDist(runtime)) {
|
|
34
|
+
console.log(`Using bundled ${runtime.label} dist`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
if (!existsSync(join(runtime.packageRoot, "package.json"))) {
|
|
29
39
|
assertBundledDist(runtime);
|
|
30
40
|
console.log(`Using bundled ${runtime.label} dist`);
|
|
31
41
|
return;
|
|
32
42
|
}
|
|
33
43
|
|
|
34
|
-
const result = spawnSync("
|
|
44
|
+
const result = spawnSync("npm", ["run", "build"], {
|
|
35
45
|
cwd: runtime.packageRoot,
|
|
46
|
+
shell: process.platform === "win32",
|
|
36
47
|
stdio: "inherit",
|
|
37
48
|
});
|
|
38
49
|
if (result.error !== undefined) throw result.error;
|
|
39
50
|
if (result.status !== 0) process.exit(result.status ?? 1);
|
|
40
51
|
}
|
|
41
52
|
|
|
53
|
+
function hasBundledDist(runtime) {
|
|
54
|
+
return runtime.requiredOutputs.every((output) => existsSync(join(runtime.packageRoot, output)));
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
function assertBundledDist(runtime) {
|
|
43
58
|
const missingOutputs = runtime.requiredOutputs.filter((output) => !existsSync(join(runtime.packageRoot, output)));
|
|
44
59
|
if (missingOutputs.length === 0) return;
|
|
@@ -14,8 +14,9 @@ for (const workspace of workspaces) {
|
|
|
14
14
|
if (typeof workspacePackageJson.scripts?.build !== "string") continue;
|
|
15
15
|
|
|
16
16
|
console.log(`Building ${workspace}`);
|
|
17
|
-
const result = spawnSync("
|
|
17
|
+
const result = spawnSync("npm", ["run", "--workspace", workspace, "build"], {
|
|
18
18
|
cwd: root,
|
|
19
|
+
shell: process.platform === "win32",
|
|
19
20
|
stdio: "inherit",
|
|
20
21
|
});
|
|
21
22
|
if (result.error !== undefined) throw result.error;
|