lazycodex-ai 5.1.12 → 5.1.14
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/dist/cli/index.js +45 -18
- package/dist/cli-node/index.js +45 -18
- package/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +4 -4
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +33 -19
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +27 -17
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +36 -18
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-not-installed.test.ts +203 -0
- package/packages/omo-codex/plugin/components/rules/CHANGELOG.md +1 -0
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-6.md +2 -2
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +89 -28
- package/packages/omo-codex/plugin/components/ulw-loop/dist/registered-agent-roles.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/registered-agent-roles.js +61 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-role-guard.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-role-guard.js +6 -2
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/registered-agent-roles.ts +60 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-role-guard.ts +8 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-role-matrix.test.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-role-registered.test.ts +128 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/browser/SKILL.md +26 -2
- package/packages/omo-codex/plugin/skills/browser/references/commands.md +10 -1
- package/packages/omo-codex/plugin/skills/browser/references/install.md +7 -0
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-engine-guard.mjs +506 -0
- package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +29 -2
- package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +4 -3
- package/packages/omo-codex/plugin/test/aggregate.test.mjs +2 -2
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
- package/packages/shared-skills/skills/browser/SKILL.md +26 -2
- package/packages/shared-skills/skills/browser/references/commands.md +10 -1
- package/packages/shared-skills/skills/browser/references/install.md +7 -0
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/browser/scripts/browser-engine-guard.mjs +506 -0
- package/packages/shared-skills/skills/browser/scripts/omowright.mjs +29 -2
|
@@ -35,17 +35,21 @@ export interface CodexPostCompactInput {
|
|
|
35
35
|
interface DiagnosticBlock {
|
|
36
36
|
filePath: string;
|
|
37
37
|
diagnostics: string;
|
|
38
|
+
blocking: boolean;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
interface
|
|
41
|
-
decision: "block";
|
|
42
|
-
reason: string;
|
|
41
|
+
interface PostToolUseHookContext {
|
|
43
42
|
hookSpecificOutput: {
|
|
44
43
|
hookEventName: "PostToolUse";
|
|
45
44
|
additionalContext: string;
|
|
46
45
|
};
|
|
47
46
|
}
|
|
48
47
|
|
|
48
|
+
interface PostToolUseHookOutput extends PostToolUseHookContext {
|
|
49
|
+
decision: "block";
|
|
50
|
+
reason: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
49
53
|
const DIAGNOSTIC_START_PATTERN = /(?:error|warning|information|hint)\[[^\]\r\n]+\] \(\d+\) at \d+:\d+:/g;
|
|
50
54
|
const DIAGNOSTIC_CHUNK_PATTERN = /^(?:error|warning|information|hint)\[[^\]\r\n]+\] \(\d+\) at \d+:\d+:/;
|
|
51
55
|
const DEFAULT_MAX_HOOK_FEEDBACK_CHARS = 8000;
|
|
@@ -70,18 +74,34 @@ function postEditOutcomeFromDaemonResult(result: {
|
|
|
70
74
|
readonly content: readonly { readonly type: string; readonly text?: string }[];
|
|
71
75
|
readonly details?: unknown;
|
|
72
76
|
}): PostEditDiagnosticsOutcome {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
const text = result.content.map((block) => block.text).join("\n");
|
|
78
|
+
const availability = availabilityDetails(result.details);
|
|
79
|
+
if (availability === undefined) return text;
|
|
80
|
+
if (availability["kind"] === "not_configured") {
|
|
81
|
+
const extension = availability["extension"];
|
|
82
|
+
return typeof extension === "string" && extension.length > 0 ? { kind: "not_configured", extension } : text;
|
|
83
|
+
}
|
|
84
|
+
if (availability["kind"] === "not_installed") return notInstalledOutcome(availability, text) ?? text;
|
|
85
|
+
return text;
|
|
76
86
|
}
|
|
77
87
|
|
|
78
|
-
function
|
|
88
|
+
function availabilityDetails(details: unknown): Record<string, unknown> | undefined {
|
|
79
89
|
if (!isRecord(details)) return undefined;
|
|
80
90
|
const availability = details["availability"];
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
return isRecord(availability) ? availability : undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function notInstalledOutcome(
|
|
95
|
+
availability: Record<string, unknown>,
|
|
96
|
+
text: string,
|
|
97
|
+
): PostEditDiagnosticsOutcome | undefined {
|
|
98
|
+
const serverId = availability["serverId"];
|
|
99
|
+
const installDecisionTool = availability["installDecisionTool"];
|
|
100
|
+
if (typeof serverId !== "string" || serverId.length === 0 || typeof installDecisionTool !== "boolean")
|
|
101
|
+
return undefined;
|
|
102
|
+
const decision = availability["decision"];
|
|
103
|
+
const base = { kind: "not_installed", serverId, installDecisionTool, text } as const;
|
|
104
|
+
return decision === "declined" || decision === "allowed" ? { ...base, decision } : base;
|
|
85
105
|
}
|
|
86
106
|
|
|
87
107
|
export function codexLspRequestContext(
|
|
@@ -116,14 +136,12 @@ export async function runLspPostToolUseHook(
|
|
|
116
136
|
|
|
117
137
|
const rawReason = blocks.map(formatDiagnosticBlock).join("\n\n");
|
|
118
138
|
const reason = limitHookText(rawReason, hookFeedbackLimit(input.transcript_path));
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
reason,
|
|
122
|
-
hookSpecificOutput: {
|
|
123
|
-
hookEventName: "PostToolUse",
|
|
124
|
-
additionalContext: reason,
|
|
125
|
-
},
|
|
139
|
+
const context: PostToolUseHookContext = {
|
|
140
|
+
hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: reason },
|
|
126
141
|
};
|
|
142
|
+
// Missing-server guidance alone is a note for the model; only real diagnostics block the edit.
|
|
143
|
+
if (!blocks.some((block) => block.blocking)) return `${JSON.stringify(context)}\n`;
|
|
144
|
+
const output: PostToolUseHookOutput = { decision: "block", reason, ...context };
|
|
127
145
|
return `${JSON.stringify(output)}\n`;
|
|
128
146
|
}
|
|
129
147
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { runLspPostToolUseHook } from "../src/codex-hook.js";
|
|
7
|
+
|
|
8
|
+
interface DaemonResult {
|
|
9
|
+
readonly content: readonly { readonly type: string; readonly text?: string }[];
|
|
10
|
+
readonly details?: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const daemonResults = vi.hoisted(() => new Map<string, DaemonResult>());
|
|
14
|
+
|
|
15
|
+
vi.mock("@code-yeongyu/lsp-daemon/client", async (importOriginal) => ({
|
|
16
|
+
...(await importOriginal<Record<string, unknown>>()),
|
|
17
|
+
callDiagnosticsViaDaemon: async (filePath: string) => {
|
|
18
|
+
const result = daemonResults.get(filePath);
|
|
19
|
+
if (result === undefined) throw new Error(`unexpected diagnostics request for ${filePath}`);
|
|
20
|
+
return result;
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
const UNDECIDED_TEXT = [
|
|
25
|
+
"LSP server 'biome' for .json, .jsonc is NOT INSTALLED.",
|
|
26
|
+
"",
|
|
27
|
+
"Command not found: biome",
|
|
28
|
+
"",
|
|
29
|
+
"To install, run:",
|
|
30
|
+
" npm install -g @biomejs/biome",
|
|
31
|
+
].join("\n");
|
|
32
|
+
const DECLINED_TEXT =
|
|
33
|
+
"LSP server 'biome' (.json, .jsonc) is NOT INSTALLED; user previously declined installation — proceed without LSP.";
|
|
34
|
+
const TS_ERROR = "error[typescript] (2322) at 1:7: Type 'string' is not assignable to type 'number'.";
|
|
35
|
+
|
|
36
|
+
const tempDirs: string[] = [];
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
daemonResults.clear();
|
|
40
|
+
for (const tempDir of tempDirs.splice(0)) rmSync(tempDir, { recursive: true, force: true });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("codex PostToolUse hook with a language server that is not installed", () => {
|
|
44
|
+
it("#given biome is not installed and the user has not decided #when a .json file is edited #then the install guidance is a note, not a block", async () => {
|
|
45
|
+
// given
|
|
46
|
+
daemonAnswering({ "config.json": biomeNotInstalled(null, UNDECIDED_TEXT) });
|
|
47
|
+
|
|
48
|
+
// when
|
|
49
|
+
const output = await withPluginData(() => runLspPostToolUseHook(editOf("s-undecided", "config.json")));
|
|
50
|
+
|
|
51
|
+
// then
|
|
52
|
+
const parsed = parseHookOutput(output);
|
|
53
|
+
expect(parsed["decision"]).toBeUndefined();
|
|
54
|
+
expect(parsed["reason"]).toBeUndefined();
|
|
55
|
+
expect(additionalContext(parsed)).toContain("npm install -g @biomejs/biome");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("#given the user declined biome #when a .json file is edited #then the hook does not block", async () => {
|
|
59
|
+
// given
|
|
60
|
+
daemonAnswering({ "config.json": biomeNotInstalled("declined", DECLINED_TEXT) });
|
|
61
|
+
|
|
62
|
+
// when
|
|
63
|
+
const output = await withPluginData(() => runLspPostToolUseHook(editOf("s-declined", "config.json")));
|
|
64
|
+
|
|
65
|
+
// then
|
|
66
|
+
const parsed = parseHookOutput(output);
|
|
67
|
+
expect(parsed["decision"]).toBeUndefined();
|
|
68
|
+
expect(additionalContext(parsed)).toContain("proceed without LSP");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("#given biome is not installed #when the same session edits .json twice #then neither edit is blocked", async () => {
|
|
72
|
+
// given
|
|
73
|
+
daemonAnswering({ "config.json": biomeNotInstalled(null, UNDECIDED_TEXT) });
|
|
74
|
+
|
|
75
|
+
// when
|
|
76
|
+
const outputs = await withPluginData(async () => [
|
|
77
|
+
await runLspPostToolUseHook(editOf("s-repeat", "config.json")),
|
|
78
|
+
await runLspPostToolUseHook(editOf("s-repeat", "config.json")),
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
// then
|
|
82
|
+
for (const output of outputs) expect(parseHookOutput(output)["decision"]).toBeUndefined();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("#given one edit touches a .json without biome and a .ts with a type error #when the hook runs #then the type error still blocks", async () => {
|
|
86
|
+
// given
|
|
87
|
+
daemonAnswering({
|
|
88
|
+
"config.json": biomeNotInstalled(null, UNDECIDED_TEXT),
|
|
89
|
+
"src/value.ts": diagnosticsText(TS_ERROR),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// when
|
|
93
|
+
const output = await withPluginData(() =>
|
|
94
|
+
runLspPostToolUseHook(patchOf("s-mixed", ["config.json", "src/value.ts"])),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// then
|
|
98
|
+
const parsed = parseHookOutput(output);
|
|
99
|
+
expect(parsed["decision"]).toBe("block");
|
|
100
|
+
expect(parsed["reason"]).toContain(TS_ERROR);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("#given only a real type error #when a .ts file is edited #then the hook blocks with the diagnostic", async () => {
|
|
104
|
+
// given
|
|
105
|
+
daemonAnswering({ "src/value.ts": diagnosticsText(TS_ERROR) });
|
|
106
|
+
|
|
107
|
+
// when
|
|
108
|
+
const output = await withPluginData(() => runLspPostToolUseHook(editOf("s-real", "src/value.ts")));
|
|
109
|
+
|
|
110
|
+
// then
|
|
111
|
+
const parsed = parseHookOutput(output);
|
|
112
|
+
expect(parsed["decision"]).toBe("block");
|
|
113
|
+
expect(parsed["reason"]).toContain(TS_ERROR);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
function daemonAnswering(results: Readonly<Record<string, DaemonResult>>): void {
|
|
118
|
+
for (const [filePath, result] of Object.entries(results)) daemonResults.set(filePath, result);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function biomeNotInstalled(decision: "declined" | null, text: string): DaemonResult {
|
|
122
|
+
return {
|
|
123
|
+
content: [{ type: "text", text }],
|
|
124
|
+
details: {
|
|
125
|
+
error: text,
|
|
126
|
+
errorKind: "missing_dependency",
|
|
127
|
+
availability: {
|
|
128
|
+
kind: "not_installed",
|
|
129
|
+
serverId: "biome",
|
|
130
|
+
command: ["biome", "lsp-proxy"],
|
|
131
|
+
extensions: [".json", ".jsonc"],
|
|
132
|
+
installHint: "npm install -g @biomejs/biome",
|
|
133
|
+
installDecisionTool: true,
|
|
134
|
+
installDecisionsPath: "/codex-home/lsp-install-decisions.json",
|
|
135
|
+
decision,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function diagnosticsText(text: string): DaemonResult {
|
|
142
|
+
return { content: [{ type: "text", text }] };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function editOf(sessionId: string, filePath: string) {
|
|
146
|
+
return hookInput(sessionId, "write", { path: filePath });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function patchOf(sessionId: string, filePaths: readonly string[]) {
|
|
150
|
+
const command = [
|
|
151
|
+
"*** Begin Patch",
|
|
152
|
+
...filePaths.flatMap((file) => [`*** Update File: ${file}`, "@@", "-a", "+b"]),
|
|
153
|
+
"*** End Patch",
|
|
154
|
+
];
|
|
155
|
+
return hookInput(sessionId, "apply_patch", { command: command.join("\n") });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function hookInput(sessionId: string, toolName: string, toolInput: Record<string, unknown>) {
|
|
159
|
+
return {
|
|
160
|
+
cwd: "/repo",
|
|
161
|
+
hook_event_name: "PostToolUse",
|
|
162
|
+
model: "gpt-5.5",
|
|
163
|
+
permission_mode: "default",
|
|
164
|
+
session_id: sessionId,
|
|
165
|
+
tool_input: toolInput,
|
|
166
|
+
tool_name: toolName,
|
|
167
|
+
tool_response: { ok: true },
|
|
168
|
+
tool_use_id: "tool-use-1",
|
|
169
|
+
transcript_path: null,
|
|
170
|
+
turn_id: "turn-1",
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function withPluginData<T>(fn: () => Promise<T>): Promise<T> {
|
|
175
|
+
const pluginData = mkdtempSync(path.join(tmpdir(), "codex-lsp-not-installed-"));
|
|
176
|
+
tempDirs.push(pluginData);
|
|
177
|
+
const previous = process.env["PLUGIN_DATA"];
|
|
178
|
+
process.env["PLUGIN_DATA"] = pluginData;
|
|
179
|
+
try {
|
|
180
|
+
return await fn();
|
|
181
|
+
} finally {
|
|
182
|
+
if (previous === undefined) delete process.env["PLUGIN_DATA"];
|
|
183
|
+
else process.env["PLUGIN_DATA"] = previous;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function parseHookOutput(output: string): Record<string, unknown> {
|
|
188
|
+
expect(output).not.toBe("");
|
|
189
|
+
const parsed: unknown = JSON.parse(output);
|
|
190
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
191
|
+
throw new Error(`hook output is not an object: ${output}`);
|
|
192
|
+
return { ...parsed };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function additionalContext(parsed: Record<string, unknown>): string {
|
|
196
|
+
const specific = parsed["hookSpecificOutput"];
|
|
197
|
+
if (typeof specific !== "object" || specific === null) throw new Error("hookSpecificOutput missing");
|
|
198
|
+
const context = Reflect.get(specific, "additionalContext");
|
|
199
|
+
const eventName = Reflect.get(specific, "hookEventName");
|
|
200
|
+
expect(eventName).toBe("PostToolUse");
|
|
201
|
+
if (typeof context !== "string") throw new Error("additionalContext missing");
|
|
202
|
+
return context;
|
|
203
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Align the Hephaestus GPT-6 rule's delegation paragraph with the senpi GPT-6 preset (oh-my-openagent#9499): reading, lookups and own-change checks stay in the session however many calls they take; a subagent is for a track that runs beside the session's own and lands the task sooner.
|
|
5
6
|
- Restrict the default `PostToolUse` hook matcher to Codex's canonical `apply_patch` tool name.
|
|
6
7
|
- Add opt-in `NODE_DEBUG=codex-rules` phase timing logs for `PostToolUse` debugging.
|
|
7
8
|
- Harden dynamic hook coverage for additional-context JSON output, disabled/static modes, failed tool responses, and duplicate suppression.
|
|
@@ -41,7 +41,7 @@ omo-codex auto-runs LSP diagnostics after every edit and injects the result: any
|
|
|
41
41
|
|
|
42
42
|
# Subagents
|
|
43
43
|
|
|
44
|
-
Read-only Codex subagent roles live in `CODEX_HOME/agents/`. Spawn: `multi_agent_v1.spawn_agent({"message":"TASK: act as a <role>. ...","agent_type":"<role>","fork_context":false})`. If your tool list instead has a flat `spawn_agent` with a required `task_name` (`multi_agent_v2`): `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":"TASK: act as a <role>. ...","fork_turns":"none"})` — finished agents end on their own; `wait_agent` takes only `timeout_ms`.
|
|
44
|
+
Read-only Codex subagent roles live in `CODEX_HOME/agents/`. Spawn: `multi_agent_v1.spawn_agent({"message":"TASK: act as a <role>. ...","agent_type":"<role>","fork_context":false})`. If your tool list instead has a flat `spawn_agent` with a required `task_name` (`multi_agent_v2`): `spawn_agent({"task_name":"<lowercase_digits_underscores>","agent_type":"<role>","message":"TASK: act as a <role>. ...","fork_turns":"none"})` — finished agents end on their own; `wait_agent` takes only `timeout_ms`.
|
|
45
45
|
|
|
46
46
|
- `explorer` - codebase search
|
|
47
47
|
- `librarian` - external docs, OSS code, API contracts
|
|
@@ -31,7 +31,7 @@ Waiting is not free: a status poll replays the whole accumulated context through
|
|
|
31
31
|
|
|
32
32
|
# Subagents
|
|
33
33
|
|
|
34
|
-
Read-only Codex subagent roles live in `CODEX_HOME/agents/`. Spawn: `multi_agent_v1.spawn_agent({"message":"TASK: act as a <role>. GOAL: ... STOP WHEN: ... EVIDENCE: ...","agent_type":"<role>","fork_context":false})`. If your tool list instead has a flat `spawn_agent` with a required `task_name` (`multi_agent_v2`): `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":"TASK: act as a <role>. GOAL: ... STOP WHEN: ... EVIDENCE: ...","fork_turns":"none"})` - finished agents end on their own; `wait_agent` takes only `timeout_ms`.
|
|
34
|
+
Read-only Codex subagent roles live in `CODEX_HOME/agents/`. Spawn: `multi_agent_v1.spawn_agent({"message":"TASK: act as a <role>. GOAL: ... STOP WHEN: ... EVIDENCE: ...","agent_type":"<role>","fork_context":false})`. If your tool list instead has a flat `spawn_agent` with a required `task_name` (`multi_agent_v2`): `spawn_agent({"task_name":"<lowercase_digits_underscores>","agent_type":"<role>","message":"TASK: act as a <role>. GOAL: ... STOP WHEN: ... EVIDENCE: ...","fork_turns":"none"})` - finished agents end on their own; `wait_agent` takes only `timeout_ms`.
|
|
35
35
|
|
|
36
36
|
- `explorer` - codebase search
|
|
37
37
|
- `librarian` - external docs, OSS code, API contracts
|
|
@@ -37,7 +37,7 @@ Use direct calls when batching buys nothing: a lone call, an already-small resul
|
|
|
37
37
|
|
|
38
38
|
Memory of file contents is unreliable: read before claiming and re-read before editing or accepting a hand-off. Where LSP tools exist, use them for definitions, callers, rename impact, workspace symbols, and diagnostics on touched files. Text search is for literal strings, filenames, and commit history. Stop searching once a wave answers the question or two waves add nothing new. A finding that looks too simple deserves one more layer of callers or dependencies; prefer the root fix over the symptom.
|
|
39
39
|
|
|
40
|
-
Do the work yourself by default
|
|
40
|
+
Do the work yourself by default: reading, lookups, and checks on your own change are yours however many calls they take, and a follow-up on delegated work is yours to take back, not forward. A subagent is for a track that runs beside yours and lands the task sooner - a wide investigation across many files, or an implementation unit beyond one coherent edit in files you are not touching. Spawn those tracks together in the background, each brief naming its output, allowed edit paths, stop condition, and returned evidence. Do non-overlapping work while they run, then check their evidence and integrate the results. Never duplicate a running search. Agent messages and final answers are read by people: full sentences, proper spaces between words and numbers, no private shorthand. Concrete spawn contracts are below.
|
|
41
41
|
|
|
42
42
|
Use `update_plan` for multi-step work, uncertain scope, multiple files, or branching investigation. Cut items into the smallest standalone outcomes, each pairing an edit with its proof. A one-step ask carries no list. Keep exactly one item `in_progress`; move each item the instant it opens, finishes, is discovered and appended, or is abandoned and removed. Update the plan in the same response when discovery changes it. Before ending, reconcile every item as completed, blocked with a reason, or removed with a reason. Commit follow-up work to the plan only if you will do it now.
|
|
43
43
|
|
|
@@ -78,7 +78,7 @@ When an approach fails, change something material - an algorithm, library, or pa
|
|
|
78
78
|
|
|
79
79
|
## Codex tool and skills notes
|
|
80
80
|
|
|
81
|
-
The actual Codex tool list and schemas determine the route. Read-only subagent roles live in `CODEX_HOME/agents/`. For `multi_agent_v1`, use `multi_agent_v1.spawn_agent({"message":"TASK: act as a <role>. GOAL: ... STOP WHEN: ... EVIDENCE: ...","agent_type":"<role>","fork_context":false})`. If the tool list instead exposes a flat `spawn_agent` requiring `task_name` (`multi_agent_v2`), use `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":"TASK: act as a <role>. GOAL: ... STOP WHEN: ... EVIDENCE: ...","fork_turns":"none"})`. Finished agents end on their own; `wait_agent` takes only `timeout_ms`. Keep the two payloads distinct and do not send v1 fields to v2 or vice versa.
|
|
81
|
+
The actual Codex tool list and schemas determine the route. Read-only subagent roles live in `CODEX_HOME/agents/`. For `multi_agent_v1`, use `multi_agent_v1.spawn_agent({"message":"TASK: act as a <role>. GOAL: ... STOP WHEN: ... EVIDENCE: ...","agent_type":"<role>","fork_context":false})`. If the tool list instead exposes a flat `spawn_agent` requiring `task_name` (`multi_agent_v2`), use `spawn_agent({"task_name":"<lowercase_digits_underscores>","agent_type":"<role>","message":"TASK: act as a <role>. GOAL: ... STOP WHEN: ... EVIDENCE: ...","fork_turns":"none"})`. Finished agents end on their own; `wait_agent` takes only `timeout_ms`. Keep the two payloads distinct and do not send v1 fields to v2 or vice versa.
|
|
82
82
|
|
|
83
83
|
- `explorer`: codebase search.
|
|
84
84
|
- `librarian`: external docs, OSS code, and API contracts.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
|
|
9
9
|
"timeout": 10,
|
|
10
|
-
"statusMessage": "(OmO 5.1.
|
|
10
|
+
"statusMessage": "(OmO 5.1.14) Loading Project Rules"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"type": "command",
|
|
20
20
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
|
|
21
21
|
"timeout": 10,
|
|
22
|
-
"statusMessage": "(OmO 5.1.
|
|
22
|
+
"statusMessage": "(OmO 5.1.14) Loading Project Rules"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"type": "command",
|
|
33
33
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
|
|
34
34
|
"timeout": 10,
|
|
35
|
-
"statusMessage": "(OmO 5.1.
|
|
35
|
+
"statusMessage": "(OmO 5.1.14) Matching Project Rules"
|
|
36
36
|
}
|
|
37
37
|
]
|
|
38
38
|
}
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"type": "command",
|
|
46
46
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
|
|
47
47
|
"timeout": 10,
|
|
48
|
-
"statusMessage": "(OmO 5.1.
|
|
48
|
+
"statusMessage": "(OmO 5.1.14) Resetting Project Rule Cache"
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/codex-ultrawork",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.14",
|
|
4
4
|
"description": "Codex plugin that injects the ultrawork orchestration directive and ships LazyCodex planning, review, QA, and gate agent roles.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@11.12.1",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/components/ulw-execute-continuation/dist/cli.js\" hook stop",
|
|
9
9
|
"timeout": 10,
|
|
10
|
-
"statusMessage": "(OmO 5.1.
|
|
10
|
+
"statusMessage": "(OmO 5.1.14) Checking Ulw-Execute Continuation"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|