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
|
@@ -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", () => {
|
|
@@ -55,6 +55,17 @@
|
|
|
55
55
|
}
|
|
56
56
|
],
|
|
57
57
|
"PreToolUse": [
|
|
58
|
+
{
|
|
59
|
+
"matcher": "^Bash$",
|
|
60
|
+
"hooks": [
|
|
61
|
+
{
|
|
62
|
+
"type": "command",
|
|
63
|
+
"command": "node \"${PLUGIN_ROOT}/components/git-bash/dist/cli.js\" hook pre-tool-use",
|
|
64
|
+
"timeout": 5,
|
|
65
|
+
"statusMessage": "LazyCodex(0.1.0): Recommending Git Bash Mcp"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
58
69
|
{
|
|
59
70
|
"matcher": "^create_goal$",
|
|
60
71
|
"hooks": [
|
|
@@ -75,13 +86,13 @@
|
|
|
75
86
|
"type": "command",
|
|
76
87
|
"command": "node \"${PLUGIN_ROOT}/components/comment-checker/dist/cli.js\" hook post-tool-use",
|
|
77
88
|
"timeout": 30,
|
|
78
|
-
"statusMessage": "LazyCodex(0.1.
|
|
89
|
+
"statusMessage": "LazyCodex(0.1.1): Checking Comments"
|
|
79
90
|
},
|
|
80
91
|
{
|
|
81
92
|
"type": "command",
|
|
82
93
|
"command": "node \"${PLUGIN_ROOT}/components/lsp/dist/cli.js\" hook post-tool-use",
|
|
83
94
|
"timeout": 60,
|
|
84
|
-
"statusMessage": "LazyCodex(0.
|
|
95
|
+
"statusMessage": "LazyCodex(0.2.0): Checking LSP Diagnostics"
|
|
85
96
|
}
|
|
86
97
|
]
|
|
87
98
|
},
|
|
@@ -98,6 +109,17 @@
|
|
|
98
109
|
}
|
|
99
110
|
],
|
|
100
111
|
"PostCompact": [
|
|
112
|
+
{
|
|
113
|
+
"matcher": "manual|auto",
|
|
114
|
+
"hooks": [
|
|
115
|
+
{
|
|
116
|
+
"type": "command",
|
|
117
|
+
"command": "node \"${PLUGIN_ROOT}/components/git-bash/dist/cli.js\" hook post-compact",
|
|
118
|
+
"timeout": 5,
|
|
119
|
+
"statusMessage": "LazyCodex(0.1.0): Resetting Git Bash Mcp Reminder"
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
},
|
|
101
123
|
{
|
|
102
124
|
"matcher": "manual|auto",
|
|
103
125
|
"hooks": [
|
|
@@ -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
|
}
|
|
@@ -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;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { access, readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
import { formatLazyCodexHookStatusMessage, normalizeLazyCodexHookStatusLabel } from "./hook-status-message.mjs";
|
|
8
|
+
|
|
9
|
+
const defaultRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
10
|
+
|
|
11
|
+
async function exists(path) {
|
|
12
|
+
try {
|
|
13
|
+
await access(path);
|
|
14
|
+
return true;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function readJson(path) {
|
|
22
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function writeJson(path, value) {
|
|
26
|
+
await writeFile(path, `${JSON.stringify(value, null, "\t")}\n`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function readPackageVersion(path) {
|
|
30
|
+
const packageJson = await readJson(path);
|
|
31
|
+
return packageJson.version;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function readComponentVersions(root) {
|
|
35
|
+
const componentsRoot = join(root, "components");
|
|
36
|
+
const entries = await readdir(componentsRoot, { withFileTypes: true });
|
|
37
|
+
const versions = new Map();
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
if (!entry.isDirectory()) continue;
|
|
40
|
+
versions.set(entry.name, await readPackageVersion(join(componentsRoot, entry.name, "package.json")));
|
|
41
|
+
}
|
|
42
|
+
return versions;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function componentVersionForCommand(command, componentVersions, fallbackVersion) {
|
|
46
|
+
for (const [componentName, version] of componentVersions.entries()) {
|
|
47
|
+
if (command.includes(`/components/${componentName}/dist/cli.js`)) return version;
|
|
48
|
+
}
|
|
49
|
+
return fallbackVersion;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function syncHooksJson(hooksJson, versionForCommand) {
|
|
53
|
+
for (const groups of Object.values(hooksJson.hooks)) {
|
|
54
|
+
for (const group of groups) {
|
|
55
|
+
for (const hook of group.hooks) {
|
|
56
|
+
if (hook.type !== "command") continue;
|
|
57
|
+
const label = normalizeLazyCodexHookStatusLabel(hook.statusMessage);
|
|
58
|
+
hook.statusMessage = formatLazyCodexHookStatusMessage(versionForCommand(hook.command), label);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function syncComponentHooks(root, componentName, version) {
|
|
65
|
+
const hooksPath = join(root, "components", componentName, "hooks", "hooks.json");
|
|
66
|
+
if (!(await exists(hooksPath))) return;
|
|
67
|
+
const hooksJson = await readJson(hooksPath);
|
|
68
|
+
syncHooksJson(hooksJson, () => version);
|
|
69
|
+
await writeJson(hooksPath, hooksJson);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function syncHookStatusMessages(root = defaultRoot) {
|
|
73
|
+
const aggregateVersion = await readPackageVersion(join(root, ".codex-plugin", "plugin.json"));
|
|
74
|
+
const componentVersions = await readComponentVersions(root);
|
|
75
|
+
const aggregateHooksPath = join(root, "hooks", "hooks.json");
|
|
76
|
+
const aggregateHooks = await readJson(aggregateHooksPath);
|
|
77
|
+
syncHooksJson(aggregateHooks, (command) => componentVersionForCommand(command, componentVersions, aggregateVersion));
|
|
78
|
+
await writeJson(aggregateHooksPath, aggregateHooks);
|
|
79
|
+
|
|
80
|
+
for (const [componentName, version] of componentVersions.entries()) {
|
|
81
|
+
await syncComponentHooks(root, componentName, version);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
86
|
+
await syncHookStatusMessages();
|
|
87
|
+
}
|
|
@@ -18,6 +18,26 @@ This skill may include examples copied from the OpenCode harness. In Codex, do n
|
|
|
18
18
|
|
|
19
19
|
When translating `load_skills=[...]`, include the requested skill names in the spawned agent's `message`. If a code block below conflicts with this section, this section wins.
|
|
20
20
|
|
|
21
|
+
## Codex Subagent Reliability
|
|
22
|
+
|
|
23
|
+
Every `spawn_agent` message must be self-contained. Start with
|
|
24
|
+
`TASK: <imperative assignment>`, then name `DELIVERABLE`, `SCOPE`, and
|
|
25
|
+
`VERIFY`. State that it is an executable assignment, not a context
|
|
26
|
+
handoff. Role selection requires `agent_type`; `model` +
|
|
27
|
+
`reasoning_effort` alone creates a default agent, not a reviewer or
|
|
28
|
+
worker. Prefer `fork_turns: "none"` unless full history is truly
|
|
29
|
+
required; paste only the review context that worker needs.
|
|
30
|
+
|
|
31
|
+
Plan and reviewer agents may run for a long time; spawn them in the background, keep doing independent root work, and poll with short wait_agent cycles. Never use a single long blocking wait for them.
|
|
32
|
+
|
|
33
|
+
Use `wait_agent` for completion signals, but treat `wait_agent` as a
|
|
34
|
+
mailbox signal, not proof of completion, content, or errors. After two
|
|
35
|
+
waits with no substantive result, send one targeted followup:
|
|
36
|
+
`TASK STILL ACTIVE: return <deliverable> or BLOCKED: <reason>`. If the
|
|
37
|
+
child stays silent or ack-only, mark that review lane inconclusive, do
|
|
38
|
+
not count it as PASS or approval, close if safe, and respawn a smaller
|
|
39
|
+
`fork_turns: "none"` reviewer with the missing deliverable.
|
|
40
|
+
|
|
21
41
|
# Review Work - 5-Agent Parallel Review Orchestrator
|
|
22
42
|
|
|
23
43
|
Launch 5 specialized sub-agents in parallel to review completed implementation work from every angle. All 5 must pass for the review to pass. If even ONE fails, the review fails.
|
|
@@ -493,9 +513,12 @@ OUTPUT FORMAT:
|
|
|
493
513
|
|
|
494
514
|
## Phase 2: Wait & Collect
|
|
495
515
|
|
|
496
|
-
After launching all 5 agents in one turn,
|
|
516
|
+
After launching all 5 agents in one turn, wait for completions in bounded
|
|
517
|
+
cycles. Do not treat a timeout, ack-only reply, or empty child result as
|
|
518
|
+
a PASS.
|
|
497
519
|
|
|
498
|
-
As each completes, collect via
|
|
520
|
+
As each completes, collect via the Codex mapping above (`wait_agent`,
|
|
521
|
+
then the child's substantive final result). Store each verdict:
|
|
499
522
|
|
|
500
523
|
| Agent | Verdict | Notes |
|
|
501
524
|
|-------|---------|-------|
|
|
@@ -506,6 +529,8 @@ As each completes, collect via `background_output(task_id="bg_...")`. Store each
|
|
|
506
529
|
| 5. Context Mining | pending | - |
|
|
507
530
|
|
|
508
531
|
Do NOT deliver the final report until ALL 5 have completed.
|
|
532
|
+
If a lane remains silent after the reliability followup, record it as
|
|
533
|
+
inconclusive and respawn a smaller reviewer/worker for that exact lane.
|
|
509
534
|
|
|
510
535
|
---
|
|
511
536
|
|
|
@@ -20,6 +20,26 @@ This skill ports the OpenCode `/start-work` flow onto Codex. Any OpenCode-only t
|
|
|
20
20
|
|
|
21
21
|
When translating `load_skills=[...]`, name the skills inside the spawned agent's `message`. If a code block below conflicts with this section, this section wins.
|
|
22
22
|
|
|
23
|
+
## Codex Subagent Reliability
|
|
24
|
+
|
|
25
|
+
Every `spawn_agent` message must be self-contained. Start with
|
|
26
|
+
`TASK: <imperative assignment>`, then name `DELIVERABLE`, `SCOPE`, and
|
|
27
|
+
`VERIFY`. State that it is an executable assignment, not a context
|
|
28
|
+
handoff. Role selection requires `agent_type`; `model` +
|
|
29
|
+
`reasoning_effort` alone creates a default agent, not a reviewer or
|
|
30
|
+
worker. Prefer `fork_turns: "none"` unless full history is truly
|
|
31
|
+
required; paste only the context the child needs.
|
|
32
|
+
|
|
33
|
+
Plan and reviewer agents may run for a long time; spawn them in the background, keep doing independent root work, and poll with short wait_agent cycles. Never use a single long blocking wait for them.
|
|
34
|
+
|
|
35
|
+
Use `wait_agent` for completion signals, but treat `wait_agent` as a
|
|
36
|
+
mailbox signal, not proof of completion, content, or errors. After two
|
|
37
|
+
waits with no substantive result, send one targeted followup:
|
|
38
|
+
`TASK STILL ACTIVE: return <deliverable> or BLOCKED: <reason>`. If the
|
|
39
|
+
child stays silent or ack-only, record the result as inconclusive, do
|
|
40
|
+
not count it as pass/review approval, close if safe, and respawn a
|
|
41
|
+
smaller `fork_turns: "none"` task with the missing deliverable.
|
|
42
|
+
|
|
23
43
|
# start-work
|
|
24
44
|
|
|
25
45
|
Execute a Prometheus work plan until every top-level checkbox is complete. This skill pairs with the Codex `Stop` / `SubagentStop` continuation hook in `components/start-work-continuation`, which re-injects the next turn while `.omo/boulder.json` says the current `codex:<session_id>` still has unchecked plan work.
|