oh-my-opencode 4.5.12 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/opencode-qa/SKILL.md +194 -0
- package/.agents/skills/opencode-qa/references/cli-commands.md +188 -0
- package/.agents/skills/opencode-qa/references/db-investigation.md +197 -0
- package/.agents/skills/opencode-qa/references/events-hooks.md +110 -0
- package/.agents/skills/opencode-qa/references/sdk.md +96 -0
- package/.agents/skills/opencode-qa/references/server-api.md +200 -0
- package/.agents/skills/opencode-qa/references/testing-harness.md +218 -0
- package/.agents/skills/opencode-qa/references/tui-tmux.md +52 -0
- package/.agents/skills/opencode-qa/scripts/db-session-by-id.sh +53 -0
- package/.agents/skills/opencode-qa/scripts/db-session-by-name.sh +57 -0
- package/.agents/skills/opencode-qa/scripts/db-session-by-text.sh +158 -0
- package/.agents/skills/opencode-qa/scripts/export-roundtrip.sh +57 -0
- package/.agents/skills/opencode-qa/scripts/lib/common.sh +216 -0
- package/.agents/skills/opencode-qa/scripts/server-smoke.sh +64 -0
- package/.agents/skills/opencode-qa/scripts/sse-hook-probe.sh +106 -0
- package/.agents/skills/opencode-qa/scripts/tui-smoke.sh +89 -0
- package/README.ja.md +13 -3
- package/README.ko.md +13 -3
- package/README.md +24 -14
- package/README.ru.md +13 -3
- package/README.zh-cn.md +13 -3
- package/bin/oh-my-opencode.js +4 -3
- package/bin/oh-my-opencode.test.ts +35 -7
- package/bin/platform.d.ts +1 -1
- package/bin/platform.js +4 -4
- package/bin/platform.test.ts +31 -9
- package/dist/cli/cleanup-command.d.ts +4 -0
- package/dist/cli/cleanup.d.ts +11 -0
- package/dist/cli/cli-program.d.ts +2 -1
- package/dist/cli/index.js +1837 -450
- package/dist/cli/install-codex/codex-cache.d.ts +1 -0
- package/dist/cli/install-codex/codex-cleanup-config.d.ts +6 -0
- package/dist/cli/install-codex/codex-cleanup.d.ts +21 -0
- package/dist/cli/install-codex/codex-config-mcp.d.ts +1 -0
- package/dist/cli/install-codex/codex-config-permissions.d.ts +1 -0
- package/dist/cli/install-codex/codex-config-reasoning.d.ts +1 -0
- package/dist/cli/install-codex/codex-config-toml.d.ts +2 -1
- package/dist/cli/install-codex/codex-installation-detection.d.ts +36 -0
- package/dist/cli/install-codex/codex-package-layout.d.ts +1 -0
- package/dist/cli/install-codex/codex-project-local-cleanup-best-effort.d.ts +7 -0
- package/dist/cli/install-codex/codex-project-local-cleanup.d.ts +35 -0
- package/dist/cli/install-codex/git-bash.d.ts +35 -0
- package/dist/cli/install-codex/index.d.ts +4 -0
- package/dist/cli/install-codex/toml-section-editor.d.ts +2 -0
- package/dist/cli/install-codex/types.d.ts +20 -0
- package/dist/cli/run/event-state.d.ts +1 -0
- package/dist/cli/run/poll-for-completion.d.ts +1 -0
- package/dist/cli/run/prompt-start.d.ts +7 -0
- package/dist/cli/star-request.d.ts +9 -0
- package/dist/config/schema/hooks.d.ts +0 -1
- package/dist/create-hooks.d.ts +0 -1
- package/dist/features/builtin-skills/skills/debugging.d.ts +2 -0
- package/dist/features/builtin-skills/skills/index.d.ts +1 -0
- package/dist/hooks/index.d.ts +0 -1
- package/dist/index.js +267 -114
- package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
- package/dist/plugin/messages-transform.d.ts +8 -1
- package/dist/plugin/user-abort-interrupted-recovery-guard.d.ts +6 -0
- package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +14 -0
- package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +7 -0
- package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +1 -0
- package/dist/shared/prompt-async-gate/timing.d.ts +1 -0
- package/dist/shared/prompt-async-gate/types.d.ts +2 -0
- package/dist/shared/prompt-async-gate.d.ts +1 -1
- package/package.json +22 -17
- package/packages/git-bash-mcp/dist/cli.js +367 -0
- package/packages/omo-codex/plugin/.mcp.json +11 -0
- package/packages/omo-codex/plugin/components/comment-checker/README.md +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +29 -0
- package/packages/omo-codex/plugin/components/git-bash/package.json +23 -0
- package/packages/omo-codex/plugin/components/git-bash/src/cli.ts +33 -0
- package/packages/omo-codex/plugin/components/git-bash/src/codex-hook.ts +180 -0
- package/packages/omo-codex/plugin/components/git-bash/src/index.ts +10 -0
- package/packages/omo-codex/plugin/components/git-bash/test/codex-hook.test.ts +195 -0
- package/packages/omo-codex/plugin/components/git-bash/tsconfig.build.json +13 -0
- package/packages/omo-codex/plugin/components/git-bash/tsconfig.json +25 -0
- package/packages/omo-codex/plugin/components/lsp/README.md +1 -1
- package/packages/omo-codex/plugin/components/lsp/src/cli.ts +5 -5
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +33 -0
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +19 -27
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-cli.test.ts +28 -0
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-errors.test.ts +55 -0
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +7 -5
- package/packages/omo-codex/plugin/components/rules/README.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/windows-git-bash.md +10 -0
- package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +3 -1
- package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +97 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +5 -4
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +22 -0
- package/packages/omo-codex/plugin/components/ultrawork/README.md +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/codex-ultrawork-reviewer.toml +1 -0
- package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +8 -7
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +31 -5
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +27 -4
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +25 -0
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +27 -205
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +230 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +102 -5
- package/packages/omo-codex/plugin/hooks/hooks.json +24 -2
- package/packages/omo-codex/plugin/package-lock.json +19 -0
- package/packages/omo-codex/plugin/package.json +3 -1
- package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +16 -1
- package/packages/omo-codex/plugin/scripts/build-components.mjs +2 -1
- package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +87 -0
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +27 -2
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +20 -0
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +27 -205
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +230 -0
- package/packages/omo-codex/plugin/test/aggregate.test.mjs +23 -8
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +56 -11
- package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +34 -0
- package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +21 -0
- package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +48 -0
- package/packages/omo-codex/plugin/test/subagent-guidance.test.mjs +76 -0
- package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +66 -0
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +32 -2
- package/packages/omo-codex/scripts/install/cache.mjs +5 -3
- package/packages/omo-codex/scripts/install/cli-args.mjs +112 -0
- package/packages/omo-codex/scripts/install/config.mjs +36 -1
- package/packages/omo-codex/scripts/install/delegated-command.mjs +25 -0
- package/packages/omo-codex/scripts/install/git-bash.mjs +99 -0
- package/packages/omo-codex/scripts/install/git-bash.test.mjs +174 -0
- package/packages/omo-codex/scripts/install/mcp-runtime-cache.mjs +5 -1
- package/packages/omo-codex/scripts/install/multi-agent-v2-config.mjs +7 -1
- package/packages/omo-codex/scripts/install/permissions.d.mts +1 -0
- package/packages/omo-codex/scripts/install/permissions.mjs +26 -0
- package/packages/omo-codex/scripts/install/project-local-cleanup.mjs +229 -0
- package/packages/omo-codex/scripts/install/reasoning-config.mjs +14 -0
- package/packages/omo-codex/scripts/install/source-package-build.mjs +20 -0
- package/packages/omo-codex/scripts/install/toml-editor.mjs +19 -2
- package/packages/omo-codex/scripts/install-cli-args.test.mjs +146 -0
- package/packages/omo-codex/scripts/install-config-autonomous.test.mjs +48 -0
- package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +62 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +206 -0
- package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +129 -0
- package/packages/omo-codex/scripts/install-local-git-bash-preflight.test.mjs +145 -0
- package/packages/omo-codex/scripts/install-local.mjs +91 -8
- package/packages/omo-codex/scripts/install-local.test.mjs +15 -0
- package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +60 -0
- package/packages/omo-codex/scripts/install-packaged-local.test.mjs +67 -0
- package/packages/omo-codex/scripts/install-project-local-cleanup.test.mjs +277 -0
- package/packages/shared-skills/skills/review-work/SKILL.md +27 -2
- package/packages/shared-skills/skills/start-work/SKILL.md +20 -0
- package/dist/hooks/context-window-monitor.d.ts +0 -19
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { runLspPostToolUseHook } from "../src/codex-hook.js";
|
|
4
|
+
|
|
5
|
+
describe("codex PostToolUse diagnostics errors", () => {
|
|
6
|
+
it("#given diagnostics runner throws for a mutated file #when the hook evaluates diagnostics #then it returns blocked output with the thrown message", async () => {
|
|
7
|
+
// given
|
|
8
|
+
const output = await runLspPostToolUseHook(
|
|
9
|
+
{
|
|
10
|
+
tool_name: "write",
|
|
11
|
+
tool_input: { path: "src/missing.ts" },
|
|
12
|
+
tool_response: { ok: true },
|
|
13
|
+
},
|
|
14
|
+
async (filePath) => {
|
|
15
|
+
expect(filePath).toBe("src/missing.ts");
|
|
16
|
+
throw new Error("ENOENT: no such file or directory, open 'src/missing.ts'");
|
|
17
|
+
},
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// when
|
|
21
|
+
const parsed: unknown = JSON.parse(output);
|
|
22
|
+
if (!isPostToolUseHookOutput(parsed)) throw new TypeError("Expected PostToolUse hook output");
|
|
23
|
+
|
|
24
|
+
// then
|
|
25
|
+
expect(parsed.reason).toBe(
|
|
26
|
+
"LSP diagnostics after editing src/missing.ts:\n\nENOENT: no such file or directory, open 'src/missing.ts'",
|
|
27
|
+
);
|
|
28
|
+
expect(parsed.hookSpecificOutput.additionalContext).toBe(parsed.reason);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
interface PostToolUseHookOutput {
|
|
33
|
+
readonly decision: "block";
|
|
34
|
+
readonly reason: string;
|
|
35
|
+
readonly hookSpecificOutput: {
|
|
36
|
+
readonly hookEventName: "PostToolUse";
|
|
37
|
+
readonly additionalContext: string;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isPostToolUseHookOutput(value: unknown): value is PostToolUseHookOutput {
|
|
42
|
+
if (!isRecord(value)) return false;
|
|
43
|
+
const hookSpecificOutput = value["hookSpecificOutput"];
|
|
44
|
+
return (
|
|
45
|
+
value["decision"] === "block" &&
|
|
46
|
+
typeof value["reason"] === "string" &&
|
|
47
|
+
isRecord(hookSpecificOutput) &&
|
|
48
|
+
hookSpecificOutput["hookEventName"] === "PostToolUse" &&
|
|
49
|
+
typeof hookSpecificOutput["additionalContext"] === "string"
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
54
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
55
|
+
}
|
|
@@ -56,6 +56,7 @@ describe("plugin package metadata", () => {
|
|
|
56
56
|
const hooksJson = readHooksJson("hooks/hooks.json");
|
|
57
57
|
const mcpJson = readMcpJson(".mcp.json");
|
|
58
58
|
const cliSource = readFileSync("src/cli.ts", "utf8");
|
|
59
|
+
const codexHookCliSource = readFileSync("src/codex-hook-cli.ts", "utf8");
|
|
59
60
|
const codexHookSource = readFileSync("src/codex-hook.ts", "utf8");
|
|
60
61
|
const sourceFiles = readdirSync("src");
|
|
61
62
|
|
|
@@ -79,11 +80,12 @@ describe("plugin package metadata", () => {
|
|
|
79
80
|
expect(lspServer?.command).toBe("node");
|
|
80
81
|
expect(lspServer?.args).toEqual(["../../../../lsp-tools-mcp/dist/cli.js", "mcp"]);
|
|
81
82
|
expect(cliSource).not.toContain("./lazy-lsp-mcp.js");
|
|
82
|
-
expect(cliSource).
|
|
83
|
-
expect(cliSource).toContain("../../../../../lsp-tools-mcp/dist/cli.js");
|
|
84
|
-
expect(
|
|
85
|
-
expect(codexHookSource).toContain("
|
|
86
|
-
expect(
|
|
83
|
+
expect(cliSource).toContain("@code-yeongyu/lsp-tools-mcp/dist/cli.js");
|
|
84
|
+
expect(cliSource).not.toContain("../../../../../lsp-tools-mcp/dist/cli.js");
|
|
85
|
+
expect(codexHookCliSource).toContain("@code-yeongyu/lsp-tools-mcp/dist/lsp/manager.js");
|
|
86
|
+
expect(codexHookSource).toContain("@code-yeongyu/lsp-tools-mcp/dist/tools.js");
|
|
87
|
+
expect(codexHookCliSource).not.toContain("../../../../../lsp-tools-mcp/dist/lsp/manager.js");
|
|
88
|
+
expect(codexHookSource).not.toContain("../../../../../lsp-tools-mcp/dist/tools.js");
|
|
87
89
|
expect(sourceFiles.filter((name) => name.startsWith("lazy-mcp") || name === "lazy-lsp-mcp.ts")).toEqual([]);
|
|
88
90
|
});
|
|
89
91
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Windows Git Bash guidance for Codex
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
On Windows native Codex sessions, prefer Git Bash for shell commands.
|
|
7
|
+
|
|
8
|
+
Use `shell: "bash"` when `bash.exe` is on PATH. Otherwise use the absolute Git Bash path from `OMO_CODEX_GIT_BASH_PATH` or `C:\Program Files\Git\bin\bash.exe`.
|
|
9
|
+
|
|
10
|
+
Use PowerShell only for Windows-native operations that need PowerShell.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
1
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
3
|
|
|
4
4
|
type PackageJson = {
|
|
@@ -51,6 +51,7 @@ describe("plugin package metadata", () => {
|
|
|
51
51
|
const pluginJson = readPluginJson(".codex-plugin/plugin.json");
|
|
52
52
|
const hooksJson = readHooksJson("hooks/hooks.json");
|
|
53
53
|
const cliSource = readFileSync("src/cli.ts", "utf8");
|
|
54
|
+
const bundledRules = readdirSync("bundled-rules").sort();
|
|
54
55
|
|
|
55
56
|
// when
|
|
56
57
|
const hookConfig = hooksJson.hooks;
|
|
@@ -69,6 +70,7 @@ describe("plugin package metadata", () => {
|
|
|
69
70
|
expect(packageJson.dependencies ?? {}).toEqual({ picomatch: "^4.0.3" });
|
|
70
71
|
expect(packageJson.bin["omo-rules"]).toBe("./dist/cli.js");
|
|
71
72
|
expect(packageJson.files).toContain("bundled-rules");
|
|
73
|
+
expect(bundledRules).toContain("windows-git-bash.md");
|
|
72
74
|
expect(pluginJson.hooks).toBe("./hooks/hooks.json");
|
|
73
75
|
expect(cliSource.startsWith("#!/usr/bin/env node")).toBe(true);
|
|
74
76
|
expect(commands).toEqual([
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { runSessionStartHook, type CodexSessionStartInput } from "../src/codex-hook.js";
|
|
7
|
+
import { findPluginBundledCandidates } from "../src/rules/finder.js";
|
|
8
|
+
|
|
9
|
+
const WINDOWS_RULE_DESCRIPTION = "Windows Git Bash guidance for Codex";
|
|
10
|
+
const WINDOWS_RULE_PATH = "bundled-rules/windows-git-bash.md";
|
|
11
|
+
const WINDOWS_GUIDANCE = "On Windows native Codex sessions, prefer Git Bash for shell commands.";
|
|
12
|
+
const BUNDLED_ONLY_ENV = {
|
|
13
|
+
CODEX_RULES_ENABLED_SOURCES: "plugin-bundled",
|
|
14
|
+
};
|
|
15
|
+
const PROJECT_AND_BUNDLED_ENV = {
|
|
16
|
+
CODEX_RULES_ENABLED_SOURCES: ".omo/rules,plugin-bundled",
|
|
17
|
+
};
|
|
18
|
+
const tempDirectories: string[] = [];
|
|
19
|
+
let originalPluginRoot: string | undefined;
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
restoreEnv("PLUGIN_ROOT", originalPluginRoot);
|
|
23
|
+
for (const directory of tempDirectories.splice(0)) {
|
|
24
|
+
rmSync(directory, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function makeProject(): { readonly root: string; readonly pluginData: string } {
|
|
29
|
+
originalPluginRoot = process.env["PLUGIN_ROOT"];
|
|
30
|
+
process.env["PLUGIN_ROOT"] = process.cwd();
|
|
31
|
+
const root = mkdtempSync(join(tmpdir(), "codex-rules-windows-git-bash-project-"));
|
|
32
|
+
const pluginData = mkdtempSync(join(tmpdir(), "codex-rules-windows-git-bash-data-"));
|
|
33
|
+
tempDirectories.push(root, pluginData);
|
|
34
|
+
writeFileSync(join(root, "package.json"), JSON.stringify({ name: "fixture" }));
|
|
35
|
+
return { root, pluginData };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function sessionStartInput(root: string): CodexSessionStartInput {
|
|
39
|
+
return {
|
|
40
|
+
session_id: "session-1",
|
|
41
|
+
transcript_path: null,
|
|
42
|
+
cwd: root,
|
|
43
|
+
hook_event_name: "SessionStart",
|
|
44
|
+
model: "gpt-5.5",
|
|
45
|
+
permission_mode: "default",
|
|
46
|
+
source: "startup",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function restoreEnv(name: string, value: string | undefined): void {
|
|
51
|
+
if (value === undefined) {
|
|
52
|
+
delete process.env[name];
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
process.env[name] = value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function occurrenceCount(value: string, search: string): number {
|
|
59
|
+
return value.split(search).length - 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe("Windows Git Bash bundled rule", () => {
|
|
63
|
+
it("#given packaged bundled rules #when discovering plugin-bundled candidates #then Windows Git Bash rule is included", () => {
|
|
64
|
+
const candidates = findPluginBundledCandidates({ pluginRoot: process.cwd() });
|
|
65
|
+
|
|
66
|
+
expect(candidates.map((candidate) => candidate.relativePath)).toContain(WINDOWS_RULE_PATH);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("#given bundled rules enabled #when SessionStart runs #then Windows Git Bash guidance is injected once", async () => {
|
|
70
|
+
const { root, pluginData } = makeProject();
|
|
71
|
+
|
|
72
|
+
const output = await runSessionStartHook(sessionStartInput(root), {
|
|
73
|
+
pluginDataRoot: pluginData,
|
|
74
|
+
env: BUNDLED_ONLY_ENV,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(occurrenceCount(output, WINDOWS_GUIDANCE)).toBe(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("#given project rule with same description #when static rules load #then project guidance overrides bundled guidance", async () => {
|
|
81
|
+
const { root, pluginData } = makeProject();
|
|
82
|
+
const projectGuidance = "Project-specific Windows shell policy.";
|
|
83
|
+
mkdirSync(join(root, ".omo", "rules"), { recursive: true });
|
|
84
|
+
writeFileSync(
|
|
85
|
+
join(root, ".omo", "rules", "windows-git-bash.md"),
|
|
86
|
+
["---", `description: ${WINDOWS_RULE_DESCRIPTION}`, "alwaysApply: true", "---", "", projectGuidance].join("\n"),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const output = await runSessionStartHook(sessionStartInput(root), {
|
|
90
|
+
pluginDataRoot: pluginData,
|
|
91
|
+
env: PROJECT_AND_BUNDLED_ENV,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(output).toContain(projectGuidance);
|
|
95
|
+
expect(output).not.toContain(WINDOWS_GUIDANCE);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -18,10 +18,11 @@ You are mid-flight on a Prometheus work plan. The turn just ended without finish
|
|
|
18
18
|
1. Read `{{PLAN_PATH}}` AND `{{LEDGER_PATH}}` first — ground truth for what remains and what evidence has already been recorded. The plan checkbox and the ledger are the only sources of truth; do not trust your own memory of prior turns.
|
|
19
19
|
2. Pick the FIRST unchecked top-level checkbox in `## TODOs` or `## Final Verification Wave`. Ignore nested checkboxes under Acceptance Criteria / Evidence / Definition of Done.
|
|
20
20
|
3. Follow the `start-work` skill in full. The skill is already loaded from your earlier turn — re-read its file at `packages/omo-codex/plugin/skills/start-work/SKILL.md` if you have lost context.
|
|
21
|
-
4. Decompose the checkbox into atomic sub-tasks. Dispatch them in PARALLEL via `spawn_agent` calls in this same response unless a sub-task has a NAMED blocking dependency (input from another sub-task or shared file).
|
|
22
|
-
5. Every sub-task message MUST include all 7 sections and name one Manual-QA channel with its exact tool and exact invocation (the literal `curl` / `send-keys` / `page.click` with concrete inputs and the binary PASS/FAIL observable), plus the applicable ultraqa adversarial classes, a captured artifact, and a cleanup receipt. Channels: HTTP call (`curl -i`); tmux (`send-keys` + `capture-pane`); browser use — use Chrome to drive the page, else download and use agent-browser (https://github.com/vercel-labs/agent-browser); computer use — OS-level GUI automation for a desktop app. Tests are the floor; the channel artifact plus probed adversarial classes are the ceiling. All are required.
|
|
23
|
-
6.
|
|
24
|
-
7.
|
|
21
|
+
4. Decompose the checkbox into atomic sub-tasks. Dispatch them in PARALLEL via `spawn_agent` calls in this same response unless a sub-task has a NAMED blocking dependency (input from another sub-task or shared file). Prefer `fork_turns: "none"` unless full history is truly required. Every dispatch sets `agent_type`; `model` + `reasoning_effort` alone creates a default agent, not the requested role.
|
|
22
|
+
5. Every sub-task message MUST be self-contained and start with `TASK: <imperative assignment>`, then name `DELIVERABLE`, `SCOPE`, and `VERIFY`. State that it is an executable assignment, not a context handoff. It must include all 7 sections and name one Manual-QA channel with its exact tool and exact invocation (the literal `curl` / `send-keys` / `page.click` with concrete inputs and the binary PASS/FAIL observable), plus the applicable ultraqa adversarial classes, a captured artifact, and a cleanup receipt. Channels: HTTP call (`curl -i`); tmux (`send-keys` + `capture-pane`); browser use — use Chrome to drive the page, else download and use agent-browser (https://github.com/vercel-labs/agent-browser); computer use — OS-level GUI automation for a desktop app. Tests are the floor; the channel artifact plus probed adversarial classes are the ceiling. All are required.
|
|
23
|
+
6. Use `wait_agent` for completion signals, but treat `wait_agent` as a mailbox signal, not proof of completion, content, or errors. After two waits with no substantive result, send one targeted followup: `TASK STILL ACTIVE: return <deliverable> or BLOCKED: <reason>`. If still silent or ack-only, record inconclusive, do not count it as pass/review approval, close if safe, and respawn a smaller `fork_turns: "none"` task with the missing deliverable.
|
|
24
|
+
7. After verification of ALL sub-tasks under this checkbox: `apply_patch` the plan to change `- [ ]` → `- [x]`, re-read the plan to confirm the count decreased, append a `task-completed` line to the ledger, then continue.
|
|
25
|
+
8. Do not start fresh on a sub-agent failure. Re-dispatch the same `task_name` with a fix-message: `FAILED: <exact error>` + `Diagnosis: <observation>` + `Fix: <instruction>`.
|
|
25
26
|
|
|
26
27
|
# Hard constraints
|
|
27
28
|
|
package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts
CHANGED
|
@@ -49,6 +49,28 @@ describe("start-work Stop hook", () => {
|
|
|
49
49
|
expect(parsed.reason).toContain("- Your session id in boulder.json: `codex:sess_abc`");
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
it("#given active codex work #when continuation directive is emitted #then subagent guidance is reliable", () => {
|
|
53
|
+
// given
|
|
54
|
+
const fs = createMemoryFs({
|
|
55
|
+
[BOULDER_PATH]: createBoulderJson({
|
|
56
|
+
sessionIds: ["codex:sess_abc"],
|
|
57
|
+
status: "active",
|
|
58
|
+
}),
|
|
59
|
+
[PLAN_PATH]: ["# Plan", "", "## TODOs", "- [ ] First"].join("\n"),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// when
|
|
63
|
+
const output = runStopHook(createStopInput(), fs);
|
|
64
|
+
|
|
65
|
+
// then
|
|
66
|
+
const parsed = parseBlockOutput(output);
|
|
67
|
+
expect(parsed.reason).toMatch(/TASK:/);
|
|
68
|
+
expect(parsed.reason).toMatch(/fork_turns:\s*"none"/);
|
|
69
|
+
expect(parsed.reason).toMatch(/wait_agent.*signal, not proof/);
|
|
70
|
+
expect(parsed.reason).toMatch(/one targeted followup/);
|
|
71
|
+
expect(parsed.reason).toMatch(/respawn.*smaller/);
|
|
72
|
+
});
|
|
73
|
+
|
|
52
74
|
it("#given active work belongs to another harness #when hook runs #then returns empty output", () => {
|
|
53
75
|
// given
|
|
54
76
|
const fs = createMemoryFs({
|
|
@@ -20,7 +20,7 @@ The directive is currently 10,951 chars / 231 lines and follows the GPT-5.5 prom
|
|
|
20
20
|
## Install (via this marketplace)
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
|
|
23
|
+
npx lazycodex-ai install
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
The installer copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, writes the stable Codex marketplace snapshot at `~/.codex/.tmp/marketplaces/sisyphuslabs/`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, enables `omo@sisyphuslabs` in `~/.codex/config.toml`, registers the `UserPromptSubmit` hook, and installs the bundled agent TOMLs into `~/.codex/agents/` (symlinks on Unix, copies on Windows). A `.installed-agents.json` manifest is written next to the bundled TOMLs' source root for clean uninstall tracking.
|
|
@@ -49,7 +49,7 @@ Expect `<ultrawork-mode>` ... directive body.
|
|
|
49
49
|
|
|
50
50
|
## Agent role smoke test
|
|
51
51
|
|
|
52
|
-
Run `
|
|
52
|
+
Run `npx lazycodex-ai install`, then inspect `~/.codex/agents/`. On Linux / macOS you should see symlinks; on Windows you should see file copies. Each TOML should declare a non-empty `name`, `description`, and `developer_instructions`.
|
|
53
53
|
|
|
54
54
|
## License
|
|
55
55
|
|
|
@@ -8,6 +8,7 @@ developer_instructions = """You are the ultrawork verification reviewer.
|
|
|
8
8
|
Review only. Do not implement.
|
|
9
9
|
|
|
10
10
|
Input should include the goal, success criteria, full diff, QA evidence, and notepad path.
|
|
11
|
+
If Codex delivers parent review context as inter-agent commentary, treat the latest parent message with goal/diff/evidence as your active review assignment, not passive context.
|
|
11
12
|
|
|
12
13
|
Verdict rules:
|
|
13
14
|
- Return `UNCONDITIONAL APPROVAL` only when the diff satisfies every success criterion and the evidence proves the real surface works.
|
|
@@ -55,7 +55,7 @@ If the user names a version ("React 18", "Next.js 14", "v2.x"):
|
|
|
55
55
|
|
|
56
56
|
## Step 4 - targeted investigation
|
|
57
57
|
- `webfetch(<specific-doc-page-from-sitemap>)`.
|
|
58
|
-
- If
|
|
58
|
+
- If `context7` is available, query it for the specific topic. Otherwise rely on the sitemap-driven webfetch pages.
|
|
59
59
|
|
|
60
60
|
## Skip Phase 0.5 when
|
|
61
61
|
- TYPE B (implementation) - you're cloning the repo anyway.
|
|
@@ -70,7 +70,7 @@ If the user names a version ("React 18", "Next.js 14", "v2.x"):
|
|
|
70
70
|
Run Phase 0.5 first, then in parallel:
|
|
71
71
|
- `web_search` for current-year usage examples + best practices.
|
|
72
72
|
- `webfetch` for the targeted doc pages identified by the sitemap.
|
|
73
|
-
- `gh search code "<usage pattern>" --language <lang
|
|
73
|
+
- `grep_app` for broad GitHub code search; fall back to `gh search code "<usage pattern>" --language <lang>`.
|
|
74
74
|
|
|
75
75
|
## TYPE B - IMPLEMENTATION REFERENCE
|
|
76
76
|
Execute in sequence:
|
|
@@ -81,9 +81,9 @@ Execute in sequence:
|
|
|
81
81
|
|
|
82
82
|
Parallel acceleration (4+ calls in one batch when independent):
|
|
83
83
|
- Shallow clone.
|
|
84
|
-
- `gh search code "<function-name>" --repo <owner>/<repo>`.
|
|
84
|
+
- `grep_app` broad code search or `gh search code "<function-name>" --repo <owner>/<repo>`.
|
|
85
85
|
- `gh api repos/<owner>/<repo>/commits/HEAD --jq '.sha'`.
|
|
86
|
-
-
|
|
86
|
+
- `context7` or sitemap-targeted `webfetch` of the relevant docs page for the same API surface.
|
|
87
87
|
|
|
88
88
|
## TYPE C - CONTEXT & HISTORY
|
|
89
89
|
Execute in parallel (4+ calls):
|
|
@@ -100,7 +100,7 @@ For a specific issue / PR:
|
|
|
100
100
|
## TYPE D - COMPREHENSIVE
|
|
101
101
|
Run Phase 0.5 first, then execute 6+ parallel calls:
|
|
102
102
|
- 2 docs calls: `webfetch` targeted doc pages + (if available) a docs-indexer query.
|
|
103
|
-
- 2 code-search calls: `gh search code` with varied queries (different angles).
|
|
103
|
+
- 2 code-search calls: `grep_app` or `gh search code` with varied queries (different angles).
|
|
104
104
|
- 1 source clone for deep inspection.
|
|
105
105
|
- 1 issues/PRs query for context.
|
|
106
106
|
|
|
@@ -147,8 +147,9 @@ Never link to a branch name (`/blob/main/...`) - always pin to a SHA so the line
|
|
|
147
147
|
- Sitemap -> `webfetch(<base>/sitemap.xml)` (fallbacks: `/sitemap-0.xml`, `/sitemap_index.xml`).
|
|
148
148
|
- Read a specific page -> `webfetch(<page-url>)`.
|
|
149
149
|
- Latest info -> `web_search("<query> <CURRENT_YEAR>")`.
|
|
150
|
-
-
|
|
151
|
-
- Code search (
|
|
150
|
+
- Docs index -> `context7` when available; use sitemap-driven pages when it is not.
|
|
151
|
+
- Code search (fast, broad) -> `grep_app` for web-scale GitHub search; `gh search code "<query>" --language <lang>` when you need GitHub CLI filters.
|
|
152
|
+
- Code search (deep, repo-scoped) -> after cloning, `rg` / `ast_grep` over the clone.
|
|
152
153
|
- Clone -> `gh repo clone <o>/<r> "${TMPDIR:-/tmp}/<name>" -- --depth 1`.
|
|
153
154
|
- Issues / PRs -> `gh search issues|prs`, `gh issue|pr view <n> --comments`.
|
|
154
155
|
- Release info -> `gh api repos/<o>/<r>/releases/latest`.
|
|
@@ -29,7 +29,8 @@ Never plan blind. Fire parallel research BEFORE drafting:
|
|
|
29
29
|
- Spawn parallel read-only subagents for internal-source aspects (codebase patterns, conventions, existing implementations, test infrastructure, naming/registration patterns). One subagent per aspect.
|
|
30
30
|
- Spawn parallel read-only subagents for external-source aspects (official docs, OSS reference implementations, API contracts, RFCs). One subagent per aspect.
|
|
31
31
|
- While they run, use direct read-only tools (`read`, `rg`, `ast_grep_search`, `lsp_*`) for immediate context. Do not idle.
|
|
32
|
-
- The role's own system prompt determines each subagent's output shape. Do not re-specify it; pass only
|
|
32
|
+
- The role's own system prompt determines each subagent's output shape. Do not re-specify it; pass only a self-contained `TASK: <question to answer now>`, the minimal context you have, `DELIVERABLE`, and what decision the answer informs.
|
|
33
|
+
- Prefer `fork_turns: "none"` for research subagents unless full history is truly required. Treat `wait_agent` as a signal, not proof; if a child is silent or ack-only after one targeted followup, mark that lane inconclusive and answer from direct evidence or respawn smaller.
|
|
33
34
|
|
|
34
35
|
Wait for context to converge before drafting. Rushed plans fail.
|
|
35
36
|
|
|
@@ -185,8 +185,31 @@ Until every success-criteria scenario PASSES with BOTH evidence pieces:
|
|
|
185
185
|
|
|
186
186
|
Parallel-batch independent reads / searches / subagents within a step,
|
|
187
187
|
but NEVER parallelise RED and GREEN of the same criterion.
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
|
|
189
|
+
# Codex subagent reliability
|
|
190
|
+
Every `spawn_agent` message is self-contained and starts with
|
|
191
|
+
`TASK: <imperative assignment>`, then names `DELIVERABLE`, `SCOPE`, and
|
|
192
|
+
`VERIFY`. State that it is an executable assignment, not a context
|
|
193
|
+
handoff. Prefer `fork_turns: "none"` unless full history is truly
|
|
194
|
+
required; paste only the context the child needs. Full-history forks can
|
|
195
|
+
make the child continue old parent context instead of the delegated task.
|
|
196
|
+
|
|
197
|
+
Do not use `list_agents` as a polling or status tool in long or
|
|
198
|
+
high-context runs; it can replay large agent status and latest-message
|
|
199
|
+
payloads. Track spawned agent names locally. Plan and reviewer agents
|
|
200
|
+
may run for a long time; spawn them in the background, keep doing
|
|
201
|
+
independent root work, and poll with short wait_agent cycles. Never use
|
|
202
|
+
a single long blocking wait for them. Use `wait_agent` for completion
|
|
203
|
+
signals, but treat `wait_agent` as a mailbox signal, not proof of
|
|
204
|
+
completion, content, or errors. A worker/reviewer counts only after you
|
|
205
|
+
receive substantive output and verify its diff/evidence.
|
|
206
|
+
After two waits with no substantive result, send one targeted followup:
|
|
207
|
+
`TASK STILL ACTIVE: return <deliverable> or BLOCKED: <reason>`. If it is
|
|
208
|
+
still silent or ack-only, record the result as inconclusive, do not
|
|
209
|
+
count it as approval/pass, close it if safe, and respawn a smaller
|
|
210
|
+
`fork_turns: "none"` task with the missing deliverable. Use targeted
|
|
211
|
+
followups only when needed, and `close_agent` after integrating each
|
|
212
|
+
result.
|
|
190
213
|
|
|
191
214
|
# Verification gate (TRIGGERED, NOT OPTIONAL)
|
|
192
215
|
|
|
@@ -197,9 +220,12 @@ Trigger when ANY apply:
|
|
|
197
220
|
anything the user called deep.
|
|
198
221
|
|
|
199
222
|
Procedure (NON-NEGOTIABLE):
|
|
200
|
-
1. Spawn agent_type
|
|
201
|
-
|
|
202
|
-
|
|
223
|
+
1. Spawn `agent_type="codex-ultrawork-reviewer"` with
|
|
224
|
+
`fork_turns: "none"`. If unavailable, spawn `agent_type="worker"`
|
|
225
|
+
with a self-contained reviewer assignment and tight scope. `model` +
|
|
226
|
+
`reasoning_effort` alone creates a default agent, not a reviewer.
|
|
227
|
+
Pass: goal, success-criteria, scenario evidence, full diff, notepad
|
|
228
|
+
path.
|
|
203
229
|
2. Treat the reviewer's verdict as binding. There is NO "false
|
|
204
230
|
positive". Every concern is real. Do not argue. Do not minimise. Do
|
|
205
231
|
not explain it away.
|
|
@@ -161,11 +161,34 @@ describe("codex ultrawork hook", () => {
|
|
|
161
161
|
const directive = parsed.hookSpecificOutput.additionalContext;
|
|
162
162
|
expect(directive).toMatch(/list_agents/);
|
|
163
163
|
expect(directive).toMatch(/polling or status tool/);
|
|
164
|
-
expect(directive).toMatch(/replay large agent status and latest-message
|
|
164
|
+
expect(directive).toMatch(/replay large agent status and latest-message\s+payloads/);
|
|
165
165
|
expect(directive).toMatch(/Track spawned agent names locally/);
|
|
166
|
-
expect(directive).toMatch(/wait_agent
|
|
167
|
-
expect(directive).toMatch(/targeted
|
|
168
|
-
expect(directive).toMatch(/close_agent
|
|
166
|
+
expect(directive).toMatch(/wait_agent[\s\S]*completion/);
|
|
167
|
+
expect(directive).toMatch(/targeted\s+followups only when needed/);
|
|
168
|
+
expect(directive).toMatch(/close_agent[\s\S]*after integrating each\s+result/);
|
|
169
|
+
expect(directive).toMatch(/Plan and reviewer agents\s+may run for a long time/);
|
|
170
|
+
expect(directive).toMatch(/short wait_agent cycles/);
|
|
171
|
+
expect(directive).toMatch(/single long blocking wait/);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("#given directive #when inspected #then hardens Codex subagent assignment ambiguity", () => {
|
|
175
|
+
// given
|
|
176
|
+
const payload = {
|
|
177
|
+
hook_event_name: "UserPromptSubmit",
|
|
178
|
+
prompt: "please ultrawork",
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// when
|
|
182
|
+
const output = runUserPromptSubmitHook(payload);
|
|
183
|
+
const parsed = parseHookOutput(output);
|
|
184
|
+
|
|
185
|
+
// then
|
|
186
|
+
const directive = parsed.hookSpecificOutput.additionalContext;
|
|
187
|
+
expect(directive).toMatch(/TASK:/);
|
|
188
|
+
expect(directive).toMatch(/fork_turns:\s*"none"/);
|
|
189
|
+
expect(directive).toMatch(/wait_agent[\s\S]*signal, not\s+proof/);
|
|
190
|
+
expect(directive).toMatch(/one targeted followup/);
|
|
191
|
+
expect(directive).toMatch(/respawn.*smaller/);
|
|
169
192
|
});
|
|
170
193
|
});
|
|
171
194
|
|
|
@@ -34,6 +34,31 @@ describe("codex ultrawork package metadata", () => {
|
|
|
34
34
|
expect(hookCommands).toContain(`node "${pluginRoot}/dist/cli.js" hook user-prompt-submit`);
|
|
35
35
|
expect(hookCommands).not.toContainEqual(expect.stringMatching(/\bpython3?\b|ultrawork-detector\.py/));
|
|
36
36
|
});
|
|
37
|
+
|
|
38
|
+
it("#given explorer guidance #when inspected #then names the packaged code-search MCP surface", () => {
|
|
39
|
+
// given
|
|
40
|
+
const explorer = readFileSync("agents/explorer.toml", "utf8");
|
|
41
|
+
|
|
42
|
+
// when
|
|
43
|
+
const guidance = explorer.toLowerCase();
|
|
44
|
+
|
|
45
|
+
// then
|
|
46
|
+
expect(guidance).toContain("ast_grep");
|
|
47
|
+
expect(guidance).toContain("structural");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("#given librarian guidance #when inspected #then names the packaged research MCP surfaces", () => {
|
|
51
|
+
// given
|
|
52
|
+
const librarian = readFileSync("agents/librarian.toml", "utf8");
|
|
53
|
+
|
|
54
|
+
// when
|
|
55
|
+
const guidance = librarian.toLowerCase();
|
|
56
|
+
|
|
57
|
+
// then
|
|
58
|
+
expect(guidance).toContain("grep_app");
|
|
59
|
+
expect(guidance).toContain("context7");
|
|
60
|
+
expect(guidance).toContain("ast_grep");
|
|
61
|
+
});
|
|
37
62
|
});
|
|
38
63
|
|
|
39
64
|
function readJson(path: string): unknown {
|
|
@@ -47,7 +47,7 @@ npm pack --dry-run
|
|
|
47
47
|
## Local Codex Installation
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
|
|
50
|
+
npx lazycodex-ai install
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
The installer builds and copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, installs runtime dependencies there, and enables:
|