oh-my-codex 0.16.4 → 0.17.1
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/Cargo.lock +5 -5
- package/Cargo.toml +1 -1
- package/dist/catalog/__tests__/generator.test.js +2 -0
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +80 -7
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +17 -11
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-serve.test.js +4 -0
- package/dist/cli/__tests__/mcp-serve.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +0 -124
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +8 -3
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +183 -4
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +3 -3
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +166 -42
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +22 -0
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +75 -14
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +8 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +17 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-serve.d.ts.map +1 -1
- package/dist/cli/mcp-serve.js +4 -0
- package/dist/cli/mcp-serve.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +25 -1
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +146 -3
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/question.d.ts +1 -1
- package/dist/cli/question.d.ts.map +1 -1
- package/dist/cli/question.js +98 -4
- package/dist/cli/question.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +1 -49
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +103 -18
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +21 -29
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +7 -1
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/config/__tests__/codex-hooks.test.js +136 -9
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +15 -0
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +13 -14
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +85 -7
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/generator.d.ts +8 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +73 -9
- package/dist/config/generator.js.map +1 -1
- package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
- package/dist/config/omx-first-party-mcp.js +7 -0
- package/dist/config/omx-first-party-mcp.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +29 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/design-skill.test.d.ts +2 -0
- package/dist/hooks/__tests__/design-skill.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/design-skill.test.js +55 -0
- package/dist/hooks/__tests__/design-skill.test.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +265 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +126 -1
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.js +1 -1
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +41 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +6 -3
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +5 -1
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +2 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +47 -2
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +11 -3
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +68 -6
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +63 -0
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.d.ts +2 -0
- package/dist/hud/__tests__/tmux.test.d.ts.map +1 -0
- package/dist/hud/__tests__/tmux.test.js +92 -0
- package/dist/hud/__tests__/tmux.test.js.map +1 -0
- package/dist/hud/reconcile.d.ts +2 -0
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +14 -1
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/tmux.d.ts +12 -0
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +88 -0
- package/dist/hud/tmux.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +3 -0
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/hermes-bridge.test.d.ts +2 -0
- package/dist/mcp/__tests__/hermes-bridge.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/hermes-bridge.test.js +441 -0
- package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -0
- package/dist/mcp/__tests__/state-paths.test.js +96 -13
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +2 -0
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/hermes-bridge.d.ts +111 -0
- package/dist/mcp/hermes-bridge.d.ts.map +1 -0
- package/dist/mcp/hermes-bridge.js +474 -0
- package/dist/mcp/hermes-bridge.js.map +1 -0
- package/dist/mcp/hermes-server.d.ts +374 -0
- package/dist/mcp/hermes-server.d.ts.map +1 -0
- package/dist/mcp/hermes-server.js +158 -0
- package/dist/mcp/hermes-server.js.map +1 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +41 -9
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +31 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +18 -9
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/stages/team-exec.d.ts.map +1 -1
- package/dist/pipeline/stages/team-exec.js +2 -7
- package/dist/pipeline/stages/team-exec.js.map +1 -1
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js +111 -269
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js +31 -72
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js.map +1 -1
- package/dist/planning/__tests__/artifacts.test.js +27 -372
- package/dist/planning/__tests__/artifacts.test.js.map +1 -1
- package/dist/planning/artifacts.d.ts +1 -14
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +11 -31
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/question/__tests__/state.test.js +287 -1
- package/dist/question/__tests__/state.test.js.map +1 -1
- package/dist/question/__tests__/ui.test.js +8 -8
- package/dist/question/__tests__/ui.test.js.map +1 -1
- package/dist/question/events.d.ts +53 -0
- package/dist/question/events.d.ts.map +1 -0
- package/dist/question/events.js +201 -0
- package/dist/question/events.js.map +1 -0
- package/dist/question/state.d.ts +25 -1
- package/dist/question/state.d.ts.map +1 -1
- package/dist/question/state.js +259 -3
- package/dist/question/state.js.map +1 -1
- package/dist/question/types.d.ts +1 -0
- package/dist/question/types.d.ts.map +1 -1
- package/dist/question/types.js.map +1 -1
- package/dist/question/ui.d.ts.map +1 -1
- package/dist/question/ui.js +1 -18
- package/dist/question/ui.js.map +1 -1
- package/dist/ralph/__tests__/completion-audit.test.js +39 -0
- package/dist/ralph/__tests__/completion-audit.test.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +298 -3
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/run-test-files.test.js +22 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +137 -18
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +12 -6
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +91 -2
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/run-test-files.js +12 -1
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/state/mode-state-context.d.ts +2 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +21 -0
- package/dist/state/mode-state-context.js.map +1 -1
- package/dist/team/__tests__/approved-execution.test.js +25 -24
- package/dist/team/__tests__/approved-execution.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +173 -26
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +66 -17
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +42 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +205 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/approved-execution.d.ts +13 -0
- package/dist/team/approved-execution.d.ts.map +1 -1
- package/dist/team/approved-execution.js +65 -30
- package/dist/team/approved-execution.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +28 -24
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +7 -8
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +48 -2
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/ultragoal-context.d.ts +35 -0
- package/dist/team/ultragoal-context.d.ts.map +1 -0
- package/dist/team/ultragoal-context.js +191 -0
- package/dist/team/ultragoal-context.js.map +1 -0
- package/dist/ultragoal/__tests__/artifacts.test.js +121 -0
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +19 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +9 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +105 -3
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +31 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +18 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/wiki/lifecycle.js +3 -3
- package/dist/wiki/lifecycle.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/.mcp.json +8 -0
- package/plugins/oh-my-codex/skills/design/SKILL.md +180 -0
- package/plugins/oh-my-codex/skills/plan/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/skill/SKILL.md +2 -1
- package/plugins/oh-my-codex/skills/team/SKILL.md +6 -0
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +11 -0
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +161 -47
- package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +2 -2
- package/skills/design/SKILL.md +180 -0
- package/skills/frontend-ui-ux/SKILL.md +6 -2
- package/skills/plan/SKILL.md +3 -3
- package/skills/ralph/SKILL.md +2 -2
- package/skills/ralplan/SKILL.md +1 -1
- package/skills/skill/SKILL.md +2 -1
- package/skills/team/SKILL.md +6 -0
- package/skills/ultragoal/SKILL.md +11 -0
- package/skills/ultraqa/SKILL.md +161 -47
- package/skills/visual-ralph/SKILL.md +2 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +339 -2
- package/src/scripts/__tests__/run-test-files.test.ts +32 -0
- package/src/scripts/codex-native-hook.ts +166 -20
- package/src/scripts/codex-native-pre-post.ts +12 -6
- package/src/scripts/notify-hook/tmux-injection.ts +110 -3
- package/src/scripts/run-test-files.ts +13 -2
- package/templates/catalog-manifest.json +9 -2
- package/dist/planning/__tests__/context-pack-status.test.d.ts +0 -2
- package/dist/planning/__tests__/context-pack-status.test.d.ts.map +0 -1
- package/dist/planning/__tests__/context-pack-status.test.js +0 -795
- package/dist/planning/__tests__/context-pack-status.test.js.map +0 -1
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.d.ts +0 -2
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.d.ts.map +0 -1
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js +0 -612
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js.map +0 -1
- package/dist/planning/context-pack-status.d.ts +0 -73
- package/dist/planning/context-pack-status.d.ts.map +0 -1
- package/dist/planning/context-pack-status.js +0 -745
- package/dist/planning/context-pack-status.js.map +0 -1
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
dispatchCodexNativeHook,
|
|
19
19
|
isCodexNativeHookMainModule,
|
|
20
|
+
looksLikeGoalCompletionPrompt,
|
|
20
21
|
mapCodexHookEventToOmxEvent,
|
|
21
22
|
resolveSessionOwnerPidFromAncestry,
|
|
22
23
|
} from "../codex-native-hook.js";
|
|
@@ -25,7 +26,7 @@ import { resetTriageConfigCache } from "../../hooks/triage-config.js";
|
|
|
25
26
|
import { executeStateOperation } from "../../state/operations.js";
|
|
26
27
|
import { OMX_TMUX_HUD_OWNER_ENV } from "../../hud/reconcile.js";
|
|
27
28
|
import { readAllState } from "../../hud/state.js";
|
|
28
|
-
import { writePage } from "../../wiki/storage.js";
|
|
29
|
+
import { getLegacyWikiDir, serializePage, writePage } from "../../wiki/storage.js";
|
|
29
30
|
import { WIKI_SCHEMA_VERSION } from "../../wiki/types.js";
|
|
30
31
|
|
|
31
32
|
function nativeHookScriptPath(): string {
|
|
@@ -199,6 +200,8 @@ const TEAM_ENV_KEYS = [
|
|
|
199
200
|
"TMUX",
|
|
200
201
|
"TMUX_PANE",
|
|
201
202
|
"OMX_TMUX_HUD_OWNER",
|
|
203
|
+
"OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS",
|
|
204
|
+
"OMX_NATIVE_STOP_NO_PROGRESS_IDLE_MS",
|
|
202
205
|
] as const;
|
|
203
206
|
|
|
204
207
|
const priorTeamEnv = new Map<(typeof TEAM_ENV_KEYS)[number], string | undefined>();
|
|
@@ -1094,6 +1097,68 @@ describe("codex native hook dispatch", () => {
|
|
|
1094
1097
|
}
|
|
1095
1098
|
});
|
|
1096
1099
|
|
|
1100
|
+
it("prefers repository project-memory.json during SessionStart while preserving legacy wiki guidance", async () => {
|
|
1101
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-root-memory-legacy-wiki-"));
|
|
1102
|
+
try {
|
|
1103
|
+
const now = new Date().toISOString();
|
|
1104
|
+
const legacyWikiDir = getLegacyWikiDir(cwd);
|
|
1105
|
+
await mkdir(legacyWikiDir, { recursive: true });
|
|
1106
|
+
await writeFile(join(legacyWikiDir, "legacy.md"), serializePage({
|
|
1107
|
+
filename: "legacy.md",
|
|
1108
|
+
frontmatter: {
|
|
1109
|
+
title: "Legacy",
|
|
1110
|
+
tags: ["legacy"],
|
|
1111
|
+
created: now,
|
|
1112
|
+
updated: now,
|
|
1113
|
+
sources: [],
|
|
1114
|
+
links: [],
|
|
1115
|
+
category: "reference",
|
|
1116
|
+
confidence: "medium",
|
|
1117
|
+
schemaVersion: WIKI_SCHEMA_VERSION,
|
|
1118
|
+
},
|
|
1119
|
+
content: "\n# Legacy\n\nLegacy wiki context must remain visible.\n",
|
|
1120
|
+
}));
|
|
1121
|
+
await writeJson(join(cwd, ".omx", "project-memory.json"), {
|
|
1122
|
+
techStack: "Legacy runtime memory should not win",
|
|
1123
|
+
notes: [{ category: "legacy", content: "stale legacy note", timestamp: now }],
|
|
1124
|
+
});
|
|
1125
|
+
await writeJson(join(cwd, "project-memory.json"), {
|
|
1126
|
+
techStack: "Canonical root memory",
|
|
1127
|
+
build: "npm run build && node --test dist/scripts/__tests__/codex-native-hook.test.js",
|
|
1128
|
+
conventions: "prefer repository-visible project memory at startup",
|
|
1129
|
+
directives: [
|
|
1130
|
+
{ directive: "Load root project-memory.json before legacy .omx memory.", priority: "high", timestamp: now },
|
|
1131
|
+
],
|
|
1132
|
+
notes: [
|
|
1133
|
+
{ category: "issue", content: "Regression fixture for issue #2273.", timestamp: now },
|
|
1134
|
+
],
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
const result = await dispatchCodexNativeHook(
|
|
1138
|
+
{
|
|
1139
|
+
hook_event_name: "SessionStart",
|
|
1140
|
+
cwd,
|
|
1141
|
+
session_id: "sess-root-memory-legacy-wiki",
|
|
1142
|
+
},
|
|
1143
|
+
{ cwd, sessionOwnerPid: 43210 },
|
|
1144
|
+
);
|
|
1145
|
+
|
|
1146
|
+
const additionalContext = String(
|
|
1147
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
1148
|
+
);
|
|
1149
|
+
assert.match(additionalContext, /\[Project memory\]/);
|
|
1150
|
+
assert.match(additionalContext, /source: project-memory\.json/);
|
|
1151
|
+
assert.match(additionalContext, /Canonical root memory/);
|
|
1152
|
+
assert.match(additionalContext, /Load root project-memory\.json before legacy \.omx memory\./);
|
|
1153
|
+
assert.match(additionalContext, /Regression fixture for issue #2273\./);
|
|
1154
|
+
assert.doesNotMatch(additionalContext, /Legacy runtime memory should not win/);
|
|
1155
|
+
assert.match(additionalContext, /legacy pages at \.omx\/wiki\//);
|
|
1156
|
+
assert.match(additionalContext, /Legacy wiki fallback is read-only/);
|
|
1157
|
+
} finally {
|
|
1158
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1097
1162
|
it("starts a fresh native session without inheriting stale task-scoped context", async () => {
|
|
1098
1163
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-isolation-"));
|
|
1099
1164
|
try {
|
|
@@ -1347,6 +1412,36 @@ describe("codex native hook dispatch", () => {
|
|
|
1347
1412
|
}
|
|
1348
1413
|
});
|
|
1349
1414
|
|
|
1415
|
+
it("classifies only actionable goal completion wording", () => {
|
|
1416
|
+
const actionable = [
|
|
1417
|
+
"complete this goal now",
|
|
1418
|
+
"Performance goal complete; next call update_goal({status: \"complete\"}).",
|
|
1419
|
+
"get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.",
|
|
1420
|
+
"omx ultragoal checkpoint --goal-id G001-demo --status complete --codex-goal-json goal.json",
|
|
1421
|
+
"Call update_goal({status: \"complete\"}) after verification.",
|
|
1422
|
+
"Goal complete.",
|
|
1423
|
+
"The goal is complete.",
|
|
1424
|
+
"Goal complete: verified with tests.",
|
|
1425
|
+
"Goal complete — verified with tests.",
|
|
1426
|
+
"The goal is complete: verified.",
|
|
1427
|
+
"The goal is complete — verified.",
|
|
1428
|
+
];
|
|
1429
|
+
|
|
1430
|
+
const ordinary = [
|
|
1431
|
+
"my goal is to complete the migration without regressions",
|
|
1432
|
+
"Our goal is to finish this carefully after tests pass.",
|
|
1433
|
+
"The goal of this patch is to close a review gap.",
|
|
1434
|
+
"A goal can be complete only after a human review.",
|
|
1435
|
+
];
|
|
1436
|
+
|
|
1437
|
+
for (const text of actionable) {
|
|
1438
|
+
assert.equal(looksLikeGoalCompletionPrompt(text), true, text);
|
|
1439
|
+
}
|
|
1440
|
+
for (const text of ordinary) {
|
|
1441
|
+
assert.equal(looksLikeGoalCompletionPrompt(text), false, text);
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1350
1445
|
it("warns completion-like prompts when active goal workflows need Codex snapshot reconciliation", async () => {
|
|
1351
1446
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-goal-warning-"));
|
|
1352
1447
|
try {
|
|
@@ -1400,6 +1495,118 @@ describe("codex native hook dispatch", () => {
|
|
|
1400
1495
|
}
|
|
1401
1496
|
});
|
|
1402
1497
|
|
|
1498
|
+
it("blocks ultragoal Stop for concise generic goal completion claims", async () => {
|
|
1499
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-generic-complete-stop-"));
|
|
1500
|
+
try {
|
|
1501
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1502
|
+
version: 1,
|
|
1503
|
+
activeGoalId: "G001-demo",
|
|
1504
|
+
goals: [{ id: "G001-demo", status: "in_progress", objective: "Demo goal" }],
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
const result = await dispatchCodexNativeHook({
|
|
1508
|
+
hook_event_name: "Stop",
|
|
1509
|
+
cwd,
|
|
1510
|
+
session_id: "sess-ultragoal-generic-complete-stop",
|
|
1511
|
+
thread_id: "thread-ultragoal-generic-complete-stop",
|
|
1512
|
+
last_assistant_message: "Goal complete.",
|
|
1513
|
+
}, { cwd });
|
|
1514
|
+
|
|
1515
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
1516
|
+
assert.match(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
1517
|
+
} finally {
|
|
1518
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1519
|
+
}
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
it("does not block ultragoal Stop for ordinary prose about a goal to complete work", async () => {
|
|
1523
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-ordinary-stop-"));
|
|
1524
|
+
try {
|
|
1525
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1526
|
+
version: 1,
|
|
1527
|
+
activeGoalId: "G001-demo",
|
|
1528
|
+
goals: [{ id: "G001-demo", status: "in_progress", objective: "Demo goal" }],
|
|
1529
|
+
});
|
|
1530
|
+
|
|
1531
|
+
const result = await dispatchCodexNativeHook({
|
|
1532
|
+
hook_event_name: "Stop",
|
|
1533
|
+
cwd,
|
|
1534
|
+
session_id: "sess-ultragoal-ordinary-stop",
|
|
1535
|
+
thread_id: "thread-ultragoal-ordinary-stop",
|
|
1536
|
+
last_assistant_message: "My goal is to complete the migration without regressions, so I will keep testing.",
|
|
1537
|
+
}, { cwd });
|
|
1538
|
+
|
|
1539
|
+
assert.notEqual(result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required");
|
|
1540
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
1541
|
+
} finally {
|
|
1542
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1545
|
+
|
|
1546
|
+
it("blocks ultragoal Stop with blocked checkpoint and fresh-thread remediation for completed legacy snapshots", async () => {
|
|
1547
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-legacy-stop-"));
|
|
1548
|
+
try {
|
|
1549
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1550
|
+
version: 1,
|
|
1551
|
+
activeGoalId: "G001-demo",
|
|
1552
|
+
goals: [{ id: "G001-demo", status: "in_progress", objective: "Demo goal" }],
|
|
1553
|
+
});
|
|
1554
|
+
|
|
1555
|
+
const result = await dispatchCodexNativeHook({
|
|
1556
|
+
hook_event_name: "Stop",
|
|
1557
|
+
cwd,
|
|
1558
|
+
session_id: "sess-ultragoal-legacy-stop",
|
|
1559
|
+
thread_id: "thread-ultragoal-legacy-stop",
|
|
1560
|
+
last_assistant_message: "get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.",
|
|
1561
|
+
}, { cwd });
|
|
1562
|
+
|
|
1563
|
+
const output = JSON.stringify(result.outputJson);
|
|
1564
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
1565
|
+
assert.match(output, /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
1566
|
+
assert.match(output, /--status blocked/);
|
|
1567
|
+
assert.match(output, /fresh Codex thread/);
|
|
1568
|
+
assert.match(output, /Hooks must not mutate Codex goal state/);
|
|
1569
|
+
} finally {
|
|
1570
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1571
|
+
}
|
|
1572
|
+
});
|
|
1573
|
+
|
|
1574
|
+
|
|
1575
|
+
it("does not block ultragoal Stop after task-scoped reconciliation finishes exploded bookkeeping", async () => {
|
|
1576
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-reconciled-stop-"));
|
|
1577
|
+
try {
|
|
1578
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1579
|
+
version: 1,
|
|
1580
|
+
codexGoalMode: "aggregate",
|
|
1581
|
+
codexObjective: "Complete all ultragoal stories in .omx/ultragoal/goals.json: many micro goals",
|
|
1582
|
+
activeGoalId: "G001-micro",
|
|
1583
|
+
aggregateCompletion: {
|
|
1584
|
+
status: "complete",
|
|
1585
|
+
completedAt: "2026-05-04T10:04:00.000Z",
|
|
1586
|
+
evidence: "planned work done; validation complete; reviews clean",
|
|
1587
|
+
},
|
|
1588
|
+
goals: Array.from({ length: 136 }, (_, index) => ({
|
|
1589
|
+
id: `G${String(index + 1).padStart(3, "0")}-micro`,
|
|
1590
|
+
status: index === 0 ? "in_progress" : "pending",
|
|
1591
|
+
objective: `Synthetic slice ${index + 1}.`,
|
|
1592
|
+
})),
|
|
1593
|
+
});
|
|
1594
|
+
|
|
1595
|
+
const result = await dispatchCodexNativeHook({
|
|
1596
|
+
hook_event_name: "Stop",
|
|
1597
|
+
cwd,
|
|
1598
|
+
session_id: "sess-ultragoal-reconciled-stop",
|
|
1599
|
+
thread_id: "thread-ultragoal-reconciled-stop",
|
|
1600
|
+
last_assistant_message: "Yes — planned implementation work is done; ultragoal bookkeeping reconciled complete.",
|
|
1601
|
+
}, { cwd });
|
|
1602
|
+
|
|
1603
|
+
assert.notEqual(result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required");
|
|
1604
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id/);
|
|
1605
|
+
} finally {
|
|
1606
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1607
|
+
}
|
|
1608
|
+
});
|
|
1609
|
+
|
|
1403
1610
|
it("does not block Stop for non-passing autoresearch-goal professor-critic verdicts", async () => {
|
|
1404
1611
|
for (const verdict of ["blocked", "fail", "failed"]) {
|
|
1405
1612
|
const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-autoresearch-${verdict}-stop-`));
|
|
@@ -4198,6 +4405,87 @@ exit 0
|
|
|
4198
4405
|
}
|
|
4199
4406
|
});
|
|
4200
4407
|
|
|
4408
|
+
it("stays silent on PreToolUse for compact inline Lore commit with only OmX co-author trailer", async () => {
|
|
4409
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-compact-coauthor-"));
|
|
4410
|
+
try {
|
|
4411
|
+
const result = await dispatchCodexNativeHook(
|
|
4412
|
+
{
|
|
4413
|
+
hook_event_name: "PreToolUse",
|
|
4414
|
+
cwd,
|
|
4415
|
+
tool_name: "Bash",
|
|
4416
|
+
tool_use_id: "tool-git-commit-compact-coauthor",
|
|
4417
|
+
tool_input: {
|
|
4418
|
+
command: [
|
|
4419
|
+
'git commit',
|
|
4420
|
+
'-m "Launch lvisai.xyz intro site"',
|
|
4421
|
+
'-m "Co-authored-by: OmX <omx@oh-my-codex.dev>"',
|
|
4422
|
+
].join(" "),
|
|
4423
|
+
},
|
|
4424
|
+
},
|
|
4425
|
+
{ cwd },
|
|
4426
|
+
);
|
|
4427
|
+
|
|
4428
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
4429
|
+
assert.equal(result.outputJson, null);
|
|
4430
|
+
} finally {
|
|
4431
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4432
|
+
}
|
|
4433
|
+
});
|
|
4434
|
+
|
|
4435
|
+
it("stays silent on PreToolUse for body-omitted inline Lore commit with decision trailers", async () => {
|
|
4436
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-compact-trailers-"));
|
|
4437
|
+
try {
|
|
4438
|
+
const result = await dispatchCodexNativeHook(
|
|
4439
|
+
{
|
|
4440
|
+
hook_event_name: "PreToolUse",
|
|
4441
|
+
cwd,
|
|
4442
|
+
tool_name: "Bash",
|
|
4443
|
+
tool_use_id: "tool-git-commit-compact-trailers",
|
|
4444
|
+
tool_input: {
|
|
4445
|
+
command: [
|
|
4446
|
+
'git commit',
|
|
4447
|
+
'-m "Launch lvisai.xyz intro site"',
|
|
4448
|
+
'-m "Constraint: Native PreToolUse can only inspect inline Bash command text\nTested: node --test dist/scripts/__tests__/codex-native-hook.test.js\n\nCo-authored-by: OmX <omx@oh-my-codex.dev>"',
|
|
4449
|
+
].join(" "),
|
|
4450
|
+
},
|
|
4451
|
+
},
|
|
4452
|
+
{ cwd },
|
|
4453
|
+
);
|
|
4454
|
+
|
|
4455
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
4456
|
+
assert.equal(result.outputJson, null);
|
|
4457
|
+
} finally {
|
|
4458
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4459
|
+
}
|
|
4460
|
+
});
|
|
4461
|
+
|
|
4462
|
+
it("blocks PreToolUse compact inline Lore commit when the blank separator is missing", async () => {
|
|
4463
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-compact-no-separator-"));
|
|
4464
|
+
try {
|
|
4465
|
+
const result = await dispatchCodexNativeHook(
|
|
4466
|
+
{
|
|
4467
|
+
hook_event_name: "PreToolUse",
|
|
4468
|
+
cwd,
|
|
4469
|
+
tool_name: "Bash",
|
|
4470
|
+
tool_use_id: "tool-git-commit-compact-no-separator",
|
|
4471
|
+
tool_input: {
|
|
4472
|
+
command: [
|
|
4473
|
+
'git commit',
|
|
4474
|
+
'--message="Launch lvisai.xyz intro site\nCo-authored-by: OmX <omx@oh-my-codex.dev>"',
|
|
4475
|
+
].join(" "),
|
|
4476
|
+
},
|
|
4477
|
+
},
|
|
4478
|
+
{ cwd },
|
|
4479
|
+
);
|
|
4480
|
+
|
|
4481
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
4482
|
+
assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block");
|
|
4483
|
+
assert.match(JSON.stringify(result.outputJson), /Add a blank line after the subject/);
|
|
4484
|
+
} finally {
|
|
4485
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4486
|
+
}
|
|
4487
|
+
});
|
|
4488
|
+
|
|
4201
4489
|
it("warns on PreToolUse git commit when mapped source changes lack staged docs refresh", async () => {
|
|
4202
4490
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-warn-"));
|
|
4203
4491
|
try {
|
|
@@ -7458,7 +7746,11 @@ exit 0
|
|
|
7458
7746
|
);
|
|
7459
7747
|
|
|
7460
7748
|
assert.equal(result.omxEventName, "stop");
|
|
7461
|
-
|
|
7749
|
+
const reason = String(result.outputJson?.reason);
|
|
7750
|
+
assert.match(reason, /Ralph completion audit is missing required evidence/);
|
|
7751
|
+
assert.match(reason, /state\.completion_audit = \{ passed: true, prompt_to_artifact_checklist: \[\.\.\.\], verification_evidence: \[\.\.\.\] \}/);
|
|
7752
|
+
assert.match(reason, /repo-relative JSON file/);
|
|
7753
|
+
assert.match(reason, /Markdown artifacts and flat top-level checklist\/evidence fields are not accepted/);
|
|
7462
7754
|
assert.equal(result.outputJson?.stopReason, "ralph_completion_audit_missing_completion_audit");
|
|
7463
7755
|
const reopened = JSON.parse(await readFile(statePath, "utf-8")) as Record<string, unknown>;
|
|
7464
7756
|
assert.equal(reopened.active, true);
|
|
@@ -8314,6 +8606,51 @@ exit 0
|
|
|
8314
8606
|
}
|
|
8315
8607
|
});
|
|
8316
8608
|
|
|
8609
|
+
it("bounds repeated ordinary working Stop loops with a diagnostic summary", async () => {
|
|
8610
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-working-loop-"));
|
|
8611
|
+
try {
|
|
8612
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
8613
|
+
process.env.OMX_SESSION_ID = "sess-working-loop";
|
|
8614
|
+
process.env.OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS = "2";
|
|
8615
|
+
process.env.OMX_NATIVE_STOP_NO_PROGRESS_IDLE_MS = "0";
|
|
8616
|
+
|
|
8617
|
+
const payload = {
|
|
8618
|
+
hook_event_name: "Stop",
|
|
8619
|
+
cwd,
|
|
8620
|
+
session_id: "sess-working-loop",
|
|
8621
|
+
thread_id: "thread-working-loop",
|
|
8622
|
+
turn_id: "turn-working-loop-1",
|
|
8623
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
8624
|
+
};
|
|
8625
|
+
|
|
8626
|
+
const first = await dispatchCodexNativeHook(payload, { cwd });
|
|
8627
|
+
assert.equal(first.outputJson?.stopReason, "auto_nudge");
|
|
8628
|
+
|
|
8629
|
+
const repeated = await dispatchCodexNativeHook(
|
|
8630
|
+
{
|
|
8631
|
+
...payload,
|
|
8632
|
+
turn_id: "turn-working-loop-2",
|
|
8633
|
+
stop_hook_active: true,
|
|
8634
|
+
},
|
|
8635
|
+
{ cwd },
|
|
8636
|
+
);
|
|
8637
|
+
|
|
8638
|
+
assert.equal(repeated.omxEventName, "stop");
|
|
8639
|
+
assert.equal(repeated.outputJson?.decision, "block");
|
|
8640
|
+
assert.equal(repeated.outputJson?.stopReason, "ordinary_task_no_progress_guard");
|
|
8641
|
+
assert.match(String(repeated.outputJson?.systemMessage), /no-progress guard triggered/);
|
|
8642
|
+
assert.match(String(repeated.outputJson?.systemMessage), /diagnostic summary/);
|
|
8643
|
+
assert.match(String(repeated.outputJson?.systemMessage), /complete, blocked, failed, or needs missing information/);
|
|
8644
|
+
|
|
8645
|
+
const persisted = JSON.parse(
|
|
8646
|
+
await readFile(join(cwd, ".omx", "state", "native-stop-state.json"), "utf-8"),
|
|
8647
|
+
) as { sessions: Record<string, { ordinary_no_progress_guard?: { repeat_count?: number } }> };
|
|
8648
|
+
assert.equal(persisted.sessions["sess-working-loop"]?.ordinary_no_progress_guard?.repeat_count, 2);
|
|
8649
|
+
} finally {
|
|
8650
|
+
await rm(cwd, { recursive: true, force: true });
|
|
8651
|
+
}
|
|
8652
|
+
});
|
|
8653
|
+
|
|
8317
8654
|
it("re-blocks duplicate native auto-nudge replays for the same Stop reply", async () => {
|
|
8318
8655
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-once-"));
|
|
8319
8656
|
try {
|
|
@@ -47,6 +47,38 @@ describe('run-test-files diagnostics', () => {
|
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
|
|
51
|
+
it('can force-exit Node test runner after successful CI tests to avoid leaked-handle hangs', () => {
|
|
52
|
+
const wd = mkdtempSync(join(tmpdir(), 'omx-run-test-files-'));
|
|
53
|
+
try {
|
|
54
|
+
const testsDir = join(wd, '__tests__');
|
|
55
|
+
mkdirSync(testsDir, { recursive: true });
|
|
56
|
+
writeFileSync(
|
|
57
|
+
join(testsDir, 'leaky-pass.test.js'),
|
|
58
|
+
[
|
|
59
|
+
"import { test } from 'node:test';",
|
|
60
|
+
"test('passes but leaves an interval', () => { setInterval(() => {}, 1_000); });",
|
|
61
|
+
'',
|
|
62
|
+
].join('\n'),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const withoutForceExit = runCompiledRunner(wd, { OMX_NODE_TEST_RUNNER_TIMEOUT_MS: '750' }, 2_000);
|
|
66
|
+
assert.notEqual(withoutForceExit.status, 0);
|
|
67
|
+
assert.match(withoutForceExit.stderr, /force exit disabled/);
|
|
68
|
+
assert.match(withoutForceExit.stderr, /did not exit normally|runner timeout 750ms/);
|
|
69
|
+
|
|
70
|
+
const withForceExit = runCompiledRunner(
|
|
71
|
+
wd,
|
|
72
|
+
{ OMX_NODE_TEST_RUNNER_TIMEOUT_MS: '750', OMX_NODE_TEST_FORCE_EXIT: '1' },
|
|
73
|
+
2_000,
|
|
74
|
+
);
|
|
75
|
+
assert.equal(withForceExit.status, 0, withForceExit.stderr || withForceExit.stdout);
|
|
76
|
+
assert.match(withForceExit.stderr, /force exit enabled/);
|
|
77
|
+
} finally {
|
|
78
|
+
rmSync(wd, { recursive: true, force: true });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
50
82
|
it('logs that per-test timeout is disabled by default', () => {
|
|
51
83
|
const wd = mkdtempSync(join(tmpdir(), 'omx-run-test-files-'));
|
|
52
84
|
try {
|