oh-my-codex 0.16.3 → 0.17.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/Cargo.lock +5 -5
- package/Cargo.toml +1 -1
- package/README.md +3 -3
- package/dist/catalog/__tests__/generator.test.js +2 -0
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +9 -0
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
- package/dist/cli/__tests__/cleanup.test.js +27 -0
- package/dist/cli/__tests__/cleanup.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -5
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +175 -7
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +147 -12
- 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 +2 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +47 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +10 -5
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +299 -27
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +85 -3
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +1 -1
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +108 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +91 -0
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +54 -8
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/cleanup.d.ts.map +1 -1
- package/dist/cli/cleanup.js +8 -4
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/codex-feature-probe.d.ts +9 -0
- package/dist/cli/codex-feature-probe.d.ts.map +1 -0
- package/dist/cli/codex-feature-probe.js +28 -0
- package/dist/cli/codex-feature-probe.js.map +1 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +214 -23
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +17 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +152 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.js +8 -8
- package/dist/cli/mcp-parity.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 +23 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +203 -1
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +21 -0
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +4 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +7 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +5 -3
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +140 -51
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/ultragoal.d.ts +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +70 -5
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/uninstall.d.ts +2 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +12 -3
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/codex-feature-flags.test.d.ts +2 -0
- package/dist/config/__tests__/codex-feature-flags.test.d.ts.map +1 -0
- package/dist/config/__tests__/codex-feature-flags.test.js +35 -0
- package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -0
- package/dist/config/__tests__/codex-hooks.test.js +143 -9
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +85 -9
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +116 -11
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/wiki-config-contract.test.js +6 -3
- package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -1
- package/dist/config/codex-feature-flags.d.ts +21 -0
- package/dist/config/codex-feature-flags.d.ts.map +1 -0
- package/dist/config/codex-feature-flags.js +56 -0
- package/dist/config/codex-feature-flags.js.map +1 -0
- package/dist/config/codex-hooks.d.ts +14 -13
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +108 -8
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/generator.d.ts +15 -3
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +233 -129
- package/dist/config/generator.js.map +1 -1
- package/dist/config/omx-first-party-mcp.d.ts +3 -1
- package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
- package/dist/config/omx-first-party-mcp.js +9 -2
- package/dist/config/omx-first-party-mcp.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__/keyword-detector.test.js +92 -2
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +125 -1
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
- 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__/skill-catalog-hygiene.test.d.ts +2 -0
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.js +84 -0
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -0
- 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.js +2 -2
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +1 -0
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +12 -6
- 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/hud/__tests__/state.test.js +164 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +4 -5
- package/dist/hud/state.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 +374 -0
- package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -0
- package/dist/mcp/__tests__/state-paths.test.js +155 -11
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +166 -0
- package/dist/mcp/__tests__/state-server.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 +81 -0
- package/dist/mcp/hermes-bridge.d.ts.map +1 -0
- package/dist/mcp/hermes-bridge.js +400 -0
- package/dist/mcp/hermes-bridge.js.map +1 -0
- package/dist/mcp/hermes-server.d.ts +269 -0
- package/dist/mcp/hermes-server.d.ts.map +1 -0
- package/dist/mcp/hermes-server.js +121 -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 +64 -11
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/__tests__/base-session-scope.test.js +22 -0
- package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +88 -27
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +5 -0
- package/dist/modes/base.js.map +1 -1
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts +2 -0
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts.map +1 -0
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js +316 -0
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts +2 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts.map +1 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js +481 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js.map +1 -0
- package/dist/planning/__tests__/artifacts.test.js +533 -4
- package/dist/planning/__tests__/artifacts.test.js.map +1 -1
- package/dist/planning/__tests__/context-pack-status.test.js +524 -0
- package/dist/planning/__tests__/context-pack-status.test.js.map +1 -1
- package/dist/planning/__tests__/markdown-structure.test.d.ts +2 -0
- package/dist/planning/__tests__/markdown-structure.test.d.ts.map +1 -0
- package/dist/planning/__tests__/markdown-structure.test.js +459 -0
- package/dist/planning/__tests__/markdown-structure.test.js.map +1 -0
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js +523 -1
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js.map +1 -1
- package/dist/planning/artifacts.d.ts +1 -1
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +227 -28
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/planning/context-pack-status.d.ts +25 -0
- package/dist/planning/context-pack-status.d.ts.map +1 -1
- package/dist/planning/context-pack-status.js +272 -31
- package/dist/planning/context-pack-status.js.map +1 -1
- package/dist/planning/markdown-structure.d.ts +20 -0
- package/dist/planning/markdown-structure.d.ts.map +1 -0
- package/dist/planning/markdown-structure.js +137 -0
- package/dist/planning/markdown-structure.js.map +1 -0
- package/dist/ralph/__tests__/completion-audit.test.d.ts +2 -0
- package/dist/ralph/__tests__/completion-audit.test.d.ts.map +1 -0
- package/dist/ralph/__tests__/completion-audit.test.js +121 -0
- package/dist/ralph/__tests__/completion-audit.test.js.map +1 -0
- package/dist/ralph/completion-audit.d.ts +8 -0
- package/dist/ralph/completion-audit.d.ts.map +1 -0
- package/dist/ralph/completion-audit.js +99 -0
- package/dist/ralph/completion-audit.js.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +407 -15
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-dispatcher.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.js +126 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -0
- 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 +177 -71
- 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 +4 -2
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/notify-dispatcher.js +30 -1
- package/dist/scripts/notify-dispatcher.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/notify-hook.js +3 -1
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +102 -27
- package/dist/state/__tests__/workflow-transition.test.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/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +9 -3
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +7 -0
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +25 -8
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts +1 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +22 -15
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.js +3 -3
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/team/__tests__/approved-execution.test.js +39 -0
- package/dist/team/__tests__/approved-execution.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +5 -0
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +497 -2
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state-root.test.js +1 -1
- package/dist/team/__tests__/state-root.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +8 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/approved-execution.d.ts.map +1 -1
- package/dist/team/approved-execution.js +3 -0
- package/dist/team/approved-execution.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +43 -0
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state-root.d.ts.map +1 -1
- package/dist/team/state-root.js +4 -0
- package/dist/team/state-root.js.map +1 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +2 -6
- package/dist/team/state.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +245 -1
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +21 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +52 -2
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +301 -15
- 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 +4 -4
- 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 +13 -5
- package/plugins/oh-my-codex/skills/analyze/SKILL.md +0 -2
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -3
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +5 -7
- package/plugins/oh-my-codex/skills/design/SKILL.md +180 -0
- package/plugins/oh-my-codex/skills/doctor/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/pipeline/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/plan/SKILL.md +3 -6
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +9 -10
- package/plugins/oh-my-codex/skills/skill/SKILL.md +2 -1
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +36 -3
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +175 -64
- package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +8 -8
- package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/wiki/SKILL.md +13 -13
- package/skills/analyze/SKILL.md +0 -2
- package/skills/ask-claude/SKILL.md +5 -3
- package/skills/ask-gemini/SKILL.md +5 -3
- package/skills/autopilot/SKILL.md +2 -2
- package/skills/code-review/SKILL.md +1 -3
- package/skills/deep-interview/SKILL.md +5 -7
- package/skills/design/SKILL.md +180 -0
- package/skills/doctor/SKILL.md +2 -2
- package/skills/ecomode/SKILL.md +105 -1
- package/skills/frontend-ui-ux/SKILL.md +6 -24
- package/skills/git-master/SKILL.md +2 -4
- package/skills/omx-setup/SKILL.md +3 -3
- package/skills/pipeline/SKILL.md +3 -3
- package/skills/plan/SKILL.md +3 -6
- package/skills/ralph/SKILL.md +9 -10
- package/skills/skill/SKILL.md +2 -1
- package/skills/swarm/SKILL.md +5 -3
- package/skills/tdd/SKILL.md +95 -1
- package/skills/ultragoal/SKILL.md +36 -3
- package/skills/ultraqa/SKILL.md +175 -64
- package/skills/ultrawork/SKILL.md +8 -8
- package/skills/visual-ralph/SKILL.md +2 -2
- package/skills/web-clone/SKILL.md +348 -1
- package/skills/wiki/SKILL.md +13 -13
- package/src/scripts/__tests__/codex-native-hook.test.ts +437 -14
- package/src/scripts/__tests__/notify-dispatcher.test.ts +153 -0
- package/src/scripts/codex-native-hook.ts +205 -61
- package/src/scripts/codex-native-pre-post.ts +4 -1
- package/src/scripts/notify-dispatcher.ts +40 -1
- package/src/scripts/notify-hook/tmux-injection.ts +110 -3
- package/src/scripts/notify-hook.ts +3 -1
- package/templates/catalog-manifest.json +9 -2
|
@@ -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";
|
|
@@ -24,7 +25,8 @@ import { writeSessionStart } from "../../hooks/session.js";
|
|
|
24
25
|
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
|
-
import {
|
|
28
|
+
import { readAllState } from "../../hud/state.js";
|
|
29
|
+
import { getLegacyWikiDir, serializePage, writePage } from "../../wiki/storage.js";
|
|
28
30
|
import { WIKI_SCHEMA_VERSION } from "../../wiki/types.js";
|
|
29
31
|
|
|
30
32
|
function nativeHookScriptPath(): string {
|
|
@@ -236,7 +238,7 @@ describe("codex native hook config", () => {
|
|
|
236
238
|
matcher?: string;
|
|
237
239
|
hooks?: Array<Record<string, unknown>>;
|
|
238
240
|
};
|
|
239
|
-
assert.equal(sessionStart.matcher, "startup|resume");
|
|
241
|
+
assert.equal(sessionStart.matcher, "startup|resume|clear");
|
|
240
242
|
assert.equal(sessionStart.hooks?.[0]?.statusMessage, undefined);
|
|
241
243
|
|
|
242
244
|
const preToolUse = config.hooks.PreToolUse[0] as {
|
|
@@ -1093,6 +1095,68 @@ describe("codex native hook dispatch", () => {
|
|
|
1093
1095
|
}
|
|
1094
1096
|
});
|
|
1095
1097
|
|
|
1098
|
+
it("prefers repository project-memory.json during SessionStart while preserving legacy wiki guidance", async () => {
|
|
1099
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-root-memory-legacy-wiki-"));
|
|
1100
|
+
try {
|
|
1101
|
+
const now = new Date().toISOString();
|
|
1102
|
+
const legacyWikiDir = getLegacyWikiDir(cwd);
|
|
1103
|
+
await mkdir(legacyWikiDir, { recursive: true });
|
|
1104
|
+
await writeFile(join(legacyWikiDir, "legacy.md"), serializePage({
|
|
1105
|
+
filename: "legacy.md",
|
|
1106
|
+
frontmatter: {
|
|
1107
|
+
title: "Legacy",
|
|
1108
|
+
tags: ["legacy"],
|
|
1109
|
+
created: now,
|
|
1110
|
+
updated: now,
|
|
1111
|
+
sources: [],
|
|
1112
|
+
links: [],
|
|
1113
|
+
category: "reference",
|
|
1114
|
+
confidence: "medium",
|
|
1115
|
+
schemaVersion: WIKI_SCHEMA_VERSION,
|
|
1116
|
+
},
|
|
1117
|
+
content: "\n# Legacy\n\nLegacy wiki context must remain visible.\n",
|
|
1118
|
+
}));
|
|
1119
|
+
await writeJson(join(cwd, ".omx", "project-memory.json"), {
|
|
1120
|
+
techStack: "Legacy runtime memory should not win",
|
|
1121
|
+
notes: [{ category: "legacy", content: "stale legacy note", timestamp: now }],
|
|
1122
|
+
});
|
|
1123
|
+
await writeJson(join(cwd, "project-memory.json"), {
|
|
1124
|
+
techStack: "Canonical root memory",
|
|
1125
|
+
build: "npm run build && node --test dist/scripts/__tests__/codex-native-hook.test.js",
|
|
1126
|
+
conventions: "prefer repository-visible project memory at startup",
|
|
1127
|
+
directives: [
|
|
1128
|
+
{ directive: "Load root project-memory.json before legacy .omx memory.", priority: "high", timestamp: now },
|
|
1129
|
+
],
|
|
1130
|
+
notes: [
|
|
1131
|
+
{ category: "issue", content: "Regression fixture for issue #2273.", timestamp: now },
|
|
1132
|
+
],
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
const result = await dispatchCodexNativeHook(
|
|
1136
|
+
{
|
|
1137
|
+
hook_event_name: "SessionStart",
|
|
1138
|
+
cwd,
|
|
1139
|
+
session_id: "sess-root-memory-legacy-wiki",
|
|
1140
|
+
},
|
|
1141
|
+
{ cwd, sessionOwnerPid: 43210 },
|
|
1142
|
+
);
|
|
1143
|
+
|
|
1144
|
+
const additionalContext = String(
|
|
1145
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
1146
|
+
);
|
|
1147
|
+
assert.match(additionalContext, /\[Project memory\]/);
|
|
1148
|
+
assert.match(additionalContext, /source: project-memory\.json/);
|
|
1149
|
+
assert.match(additionalContext, /Canonical root memory/);
|
|
1150
|
+
assert.match(additionalContext, /Load root project-memory\.json before legacy \.omx memory\./);
|
|
1151
|
+
assert.match(additionalContext, /Regression fixture for issue #2273\./);
|
|
1152
|
+
assert.doesNotMatch(additionalContext, /Legacy runtime memory should not win/);
|
|
1153
|
+
assert.match(additionalContext, /legacy pages at \.omx\/wiki\//);
|
|
1154
|
+
assert.match(additionalContext, /Legacy wiki fallback is read-only/);
|
|
1155
|
+
} finally {
|
|
1156
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1096
1160
|
it("starts a fresh native session without inheriting stale task-scoped context", async () => {
|
|
1097
1161
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-isolation-"));
|
|
1098
1162
|
try {
|
|
@@ -1217,7 +1281,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1217
1281
|
assert.equal(result.omxEventName, "keyword-detector");
|
|
1218
1282
|
assert.equal(result.skillState?.skill, "ralplan");
|
|
1219
1283
|
assert.ok(result.outputJson, "UserPromptSubmit should emit developer context");
|
|
1220
|
-
assert.match(JSON.stringify(result.outputJson), /
|
|
1284
|
+
assert.match(JSON.stringify(result.outputJson), /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1221
1285
|
|
|
1222
1286
|
assert.equal(
|
|
1223
1287
|
existsSync(join(cwd, ".omx", "state", "skill-active-state.json")),
|
|
@@ -1240,6 +1304,142 @@ describe("codex native hook dispatch", () => {
|
|
|
1240
1304
|
}
|
|
1241
1305
|
});
|
|
1242
1306
|
|
|
1307
|
+
it("records boxed keyword activation mode detail and skill state under OMX_ROOT", async () => {
|
|
1308
|
+
const root = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-"));
|
|
1309
|
+
const cwd = join(root, "source");
|
|
1310
|
+
const omxRoot = join(root, "box");
|
|
1311
|
+
const sessionId = "sess-boxed-ralplan";
|
|
1312
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
1313
|
+
const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
1314
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
1315
|
+
const previousOmxSessionId = process.env.OMX_SESSION_ID;
|
|
1316
|
+
try {
|
|
1317
|
+
await mkdir(cwd, { recursive: true });
|
|
1318
|
+
process.env.OMX_ROOT = omxRoot;
|
|
1319
|
+
delete process.env.OMX_STATE_ROOT;
|
|
1320
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
1321
|
+
process.env.OMX_SESSION_ID = sessionId;
|
|
1322
|
+
|
|
1323
|
+
const result = await dispatchCodexNativeHook(
|
|
1324
|
+
{
|
|
1325
|
+
hook_event_name: "UserPromptSubmit",
|
|
1326
|
+
cwd,
|
|
1327
|
+
session_id: sessionId,
|
|
1328
|
+
thread_id: "thread-boxed",
|
|
1329
|
+
turn_id: "turn-boxed",
|
|
1330
|
+
prompt: "$ralplan implement issue #1307",
|
|
1331
|
+
},
|
|
1332
|
+
{ cwd },
|
|
1333
|
+
);
|
|
1334
|
+
|
|
1335
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
1336
|
+
assert.equal(result.skillState?.skill, "ralplan");
|
|
1337
|
+
|
|
1338
|
+
const boxedSessionDir = join(omxRoot, ".omx", "state", "sessions", sessionId);
|
|
1339
|
+
assert.equal(existsSync(join(boxedSessionDir, "skill-active-state.json")), true);
|
|
1340
|
+
assert.equal(existsSync(join(boxedSessionDir, "ralplan-state.json")), true);
|
|
1341
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json")), false);
|
|
1342
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json")), false);
|
|
1343
|
+
|
|
1344
|
+
const hudState = await readAllState(cwd);
|
|
1345
|
+
assert.equal(hudState.ralplan?.active, true);
|
|
1346
|
+
assert.equal(hudState.ralplan?.current_phase, "planning");
|
|
1347
|
+
} finally {
|
|
1348
|
+
if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot;
|
|
1349
|
+
else delete process.env.OMX_ROOT;
|
|
1350
|
+
if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot;
|
|
1351
|
+
else delete process.env.OMX_STATE_ROOT;
|
|
1352
|
+
if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
1353
|
+
else delete process.env.OMX_TEAM_STATE_ROOT;
|
|
1354
|
+
if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId;
|
|
1355
|
+
else delete process.env.OMX_SESSION_ID;
|
|
1356
|
+
await rm(root, { recursive: true, force: true });
|
|
1357
|
+
}
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
it("records native keyword activation mode detail and skill state under OMX_TEAM_STATE_ROOT", async () => {
|
|
1361
|
+
const root = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-"));
|
|
1362
|
+
const cwd = join(root, "source");
|
|
1363
|
+
const teamStateRoot = join(root, "team-state");
|
|
1364
|
+
const sessionId = "sess-team-root-ralplan";
|
|
1365
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
1366
|
+
const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
1367
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
1368
|
+
const previousOmxSessionId = process.env.OMX_SESSION_ID;
|
|
1369
|
+
try {
|
|
1370
|
+
await mkdir(cwd, { recursive: true });
|
|
1371
|
+
delete process.env.OMX_ROOT;
|
|
1372
|
+
delete process.env.OMX_STATE_ROOT;
|
|
1373
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
1374
|
+
process.env.OMX_SESSION_ID = sessionId;
|
|
1375
|
+
|
|
1376
|
+
const result = await dispatchCodexNativeHook(
|
|
1377
|
+
{
|
|
1378
|
+
hook_event_name: "UserPromptSubmit",
|
|
1379
|
+
cwd,
|
|
1380
|
+
session_id: sessionId,
|
|
1381
|
+
thread_id: "thread-team-root",
|
|
1382
|
+
turn_id: "turn-team-root",
|
|
1383
|
+
prompt: "$ralplan implement issue #1307",
|
|
1384
|
+
},
|
|
1385
|
+
{ cwd },
|
|
1386
|
+
);
|
|
1387
|
+
|
|
1388
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
1389
|
+
assert.equal(result.skillState?.skill, "ralplan");
|
|
1390
|
+
|
|
1391
|
+
const teamSessionDir = join(teamStateRoot, "sessions", sessionId);
|
|
1392
|
+
assert.equal(existsSync(join(teamSessionDir, "skill-active-state.json")), true);
|
|
1393
|
+
assert.equal(existsSync(join(teamSessionDir, "ralplan-state.json")), true);
|
|
1394
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json")), false);
|
|
1395
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json")), false);
|
|
1396
|
+
|
|
1397
|
+
const hudState = await readAllState(cwd);
|
|
1398
|
+
assert.equal(hudState.ralplan?.active, true);
|
|
1399
|
+
assert.equal(hudState.ralplan?.current_phase, "planning");
|
|
1400
|
+
} finally {
|
|
1401
|
+
if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot;
|
|
1402
|
+
else delete process.env.OMX_ROOT;
|
|
1403
|
+
if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot;
|
|
1404
|
+
else delete process.env.OMX_STATE_ROOT;
|
|
1405
|
+
if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
1406
|
+
else delete process.env.OMX_TEAM_STATE_ROOT;
|
|
1407
|
+
if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId;
|
|
1408
|
+
else delete process.env.OMX_SESSION_ID;
|
|
1409
|
+
await rm(root, { recursive: true, force: true });
|
|
1410
|
+
}
|
|
1411
|
+
});
|
|
1412
|
+
|
|
1413
|
+
it("classifies only actionable goal completion wording", () => {
|
|
1414
|
+
const actionable = [
|
|
1415
|
+
"complete this goal now",
|
|
1416
|
+
"Performance goal complete; next call update_goal({status: \"complete\"}).",
|
|
1417
|
+
"get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.",
|
|
1418
|
+
"omx ultragoal checkpoint --goal-id G001-demo --status complete --codex-goal-json goal.json",
|
|
1419
|
+
"Call update_goal({status: \"complete\"}) after verification.",
|
|
1420
|
+
"Goal complete.",
|
|
1421
|
+
"The goal is complete.",
|
|
1422
|
+
"Goal complete: verified with tests.",
|
|
1423
|
+
"Goal complete — verified with tests.",
|
|
1424
|
+
"The goal is complete: verified.",
|
|
1425
|
+
"The goal is complete — verified.",
|
|
1426
|
+
];
|
|
1427
|
+
|
|
1428
|
+
const ordinary = [
|
|
1429
|
+
"my goal is to complete the migration without regressions",
|
|
1430
|
+
"Our goal is to finish this carefully after tests pass.",
|
|
1431
|
+
"The goal of this patch is to close a review gap.",
|
|
1432
|
+
"A goal can be complete only after a human review.",
|
|
1433
|
+
];
|
|
1434
|
+
|
|
1435
|
+
for (const text of actionable) {
|
|
1436
|
+
assert.equal(looksLikeGoalCompletionPrompt(text), true, text);
|
|
1437
|
+
}
|
|
1438
|
+
for (const text of ordinary) {
|
|
1439
|
+
assert.equal(looksLikeGoalCompletionPrompt(text), false, text);
|
|
1440
|
+
}
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1243
1443
|
it("warns completion-like prompts when active goal workflows need Codex snapshot reconciliation", async () => {
|
|
1244
1444
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-goal-warning-"));
|
|
1245
1445
|
try {
|
|
@@ -1293,6 +1493,118 @@ describe("codex native hook dispatch", () => {
|
|
|
1293
1493
|
}
|
|
1294
1494
|
});
|
|
1295
1495
|
|
|
1496
|
+
it("blocks ultragoal Stop for concise generic goal completion claims", async () => {
|
|
1497
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-generic-complete-stop-"));
|
|
1498
|
+
try {
|
|
1499
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1500
|
+
version: 1,
|
|
1501
|
+
activeGoalId: "G001-demo",
|
|
1502
|
+
goals: [{ id: "G001-demo", status: "in_progress", objective: "Demo goal" }],
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
const result = await dispatchCodexNativeHook({
|
|
1506
|
+
hook_event_name: "Stop",
|
|
1507
|
+
cwd,
|
|
1508
|
+
session_id: "sess-ultragoal-generic-complete-stop",
|
|
1509
|
+
thread_id: "thread-ultragoal-generic-complete-stop",
|
|
1510
|
+
last_assistant_message: "Goal complete.",
|
|
1511
|
+
}, { cwd });
|
|
1512
|
+
|
|
1513
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
1514
|
+
assert.match(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
1515
|
+
} finally {
|
|
1516
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
|
|
1520
|
+
it("does not block ultragoal Stop for ordinary prose about a goal to complete work", async () => {
|
|
1521
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-ordinary-stop-"));
|
|
1522
|
+
try {
|
|
1523
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1524
|
+
version: 1,
|
|
1525
|
+
activeGoalId: "G001-demo",
|
|
1526
|
+
goals: [{ id: "G001-demo", status: "in_progress", objective: "Demo goal" }],
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
const result = await dispatchCodexNativeHook({
|
|
1530
|
+
hook_event_name: "Stop",
|
|
1531
|
+
cwd,
|
|
1532
|
+
session_id: "sess-ultragoal-ordinary-stop",
|
|
1533
|
+
thread_id: "thread-ultragoal-ordinary-stop",
|
|
1534
|
+
last_assistant_message: "My goal is to complete the migration without regressions, so I will keep testing.",
|
|
1535
|
+
}, { cwd });
|
|
1536
|
+
|
|
1537
|
+
assert.notEqual(result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required");
|
|
1538
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
1539
|
+
} finally {
|
|
1540
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1541
|
+
}
|
|
1542
|
+
});
|
|
1543
|
+
|
|
1544
|
+
it("blocks ultragoal Stop with blocked checkpoint and fresh-thread remediation for completed legacy snapshots", async () => {
|
|
1545
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-legacy-stop-"));
|
|
1546
|
+
try {
|
|
1547
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1548
|
+
version: 1,
|
|
1549
|
+
activeGoalId: "G001-demo",
|
|
1550
|
+
goals: [{ id: "G001-demo", status: "in_progress", objective: "Demo goal" }],
|
|
1551
|
+
});
|
|
1552
|
+
|
|
1553
|
+
const result = await dispatchCodexNativeHook({
|
|
1554
|
+
hook_event_name: "Stop",
|
|
1555
|
+
cwd,
|
|
1556
|
+
session_id: "sess-ultragoal-legacy-stop",
|
|
1557
|
+
thread_id: "thread-ultragoal-legacy-stop",
|
|
1558
|
+
last_assistant_message: "get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.",
|
|
1559
|
+
}, { cwd });
|
|
1560
|
+
|
|
1561
|
+
const output = JSON.stringify(result.outputJson);
|
|
1562
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
1563
|
+
assert.match(output, /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
1564
|
+
assert.match(output, /--status blocked/);
|
|
1565
|
+
assert.match(output, /fresh Codex thread/);
|
|
1566
|
+
assert.match(output, /Hooks must not mutate Codex goal state/);
|
|
1567
|
+
} finally {
|
|
1568
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1569
|
+
}
|
|
1570
|
+
});
|
|
1571
|
+
|
|
1572
|
+
|
|
1573
|
+
it("does not block ultragoal Stop after task-scoped reconciliation finishes exploded bookkeeping", async () => {
|
|
1574
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-reconciled-stop-"));
|
|
1575
|
+
try {
|
|
1576
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1577
|
+
version: 1,
|
|
1578
|
+
codexGoalMode: "aggregate",
|
|
1579
|
+
codexObjective: "Complete all ultragoal stories in .omx/ultragoal/goals.json: many micro goals",
|
|
1580
|
+
activeGoalId: "G001-micro",
|
|
1581
|
+
aggregateCompletion: {
|
|
1582
|
+
status: "complete",
|
|
1583
|
+
completedAt: "2026-05-04T10:04:00.000Z",
|
|
1584
|
+
evidence: "planned work done; validation complete; reviews clean",
|
|
1585
|
+
},
|
|
1586
|
+
goals: Array.from({ length: 136 }, (_, index) => ({
|
|
1587
|
+
id: `G${String(index + 1).padStart(3, "0")}-micro`,
|
|
1588
|
+
status: index === 0 ? "in_progress" : "pending",
|
|
1589
|
+
objective: `Synthetic slice ${index + 1}.`,
|
|
1590
|
+
})),
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
const result = await dispatchCodexNativeHook({
|
|
1594
|
+
hook_event_name: "Stop",
|
|
1595
|
+
cwd,
|
|
1596
|
+
session_id: "sess-ultragoal-reconciled-stop",
|
|
1597
|
+
thread_id: "thread-ultragoal-reconciled-stop",
|
|
1598
|
+
last_assistant_message: "Yes — planned implementation work is done; ultragoal bookkeeping reconciled complete.",
|
|
1599
|
+
}, { cwd });
|
|
1600
|
+
|
|
1601
|
+
assert.notEqual(result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required");
|
|
1602
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id/);
|
|
1603
|
+
} finally {
|
|
1604
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1296
1608
|
it("does not block Stop for non-passing autoresearch-goal professor-critic verdicts", async () => {
|
|
1297
1609
|
for (const verdict of ["blocked", "fail", "failed"]) {
|
|
1298
1610
|
const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-autoresearch-${verdict}-stop-`));
|
|
@@ -1474,7 +1786,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1474
1786
|
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
|
|
1475
1787
|
);
|
|
1476
1788
|
assert.match(message, /\$oh-my-codex:ralplan" -> ralplan/);
|
|
1477
|
-
assert.match(message, /
|
|
1789
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1478
1790
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-plugin-1", "ralplan-state.json")), true);
|
|
1479
1791
|
} finally {
|
|
1480
1792
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -1691,7 +2003,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1691
2003
|
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
|
|
1692
2004
|
);
|
|
1693
2005
|
assert.match(message, /\$ralph" -> ralph/);
|
|
1694
|
-
assert.match(message, /
|
|
2006
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1695
2007
|
assert.match(message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./);
|
|
1696
2008
|
assert.match(message, /Use `omx ralph --prd \.\.\.` only when you explicitly want the PRD-gated CLI startup path\./);
|
|
1697
2009
|
} finally {
|
|
@@ -1721,7 +2033,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1721
2033
|
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
|
|
1722
2034
|
);
|
|
1723
2035
|
assert.match(message, /\$oh-my-codex:ralph" -> ralph/);
|
|
1724
|
-
assert.match(message, /
|
|
2036
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1725
2037
|
assert.match(message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./);
|
|
1726
2038
|
} finally {
|
|
1727
2039
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -1803,7 +2115,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1803
2115
|
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
|
|
1804
2116
|
);
|
|
1805
2117
|
assert.match(message, /\$deep-interview" -> deep-interview/);
|
|
1806
|
-
assert.match(message, /
|
|
2118
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1807
2119
|
assert.match(message, /Deep-interview is active, but this session is not attached to tmux/);
|
|
1808
2120
|
assert.match(message, /Do not invoke `omx question`, `omx hud`, or `omx team`/);
|
|
1809
2121
|
assert.match(message, /native structured question tool when available/);
|
|
@@ -2279,8 +2591,11 @@ export async function onHookEvent(event) {
|
|
|
2279
2591
|
|
|
2280
2592
|
assert.match(JSON.stringify(denied.outputJson), /denied workflow keyword/i);
|
|
2281
2593
|
assert.match(JSON.stringify(denied.outputJson), /Unsupported workflow overlap: team \+ autopilot\./);
|
|
2282
|
-
assert.match(JSON.stringify(denied.outputJson),
|
|
2283
|
-
assert.match(JSON.stringify(denied.outputJson),
|
|
2594
|
+
assert.match(JSON.stringify(denied.outputJson), /omx state clear --input/);
|
|
2595
|
+
assert.match(JSON.stringify(denied.outputJson), /mode\\":\\"<mode>/);
|
|
2596
|
+
assert.match(JSON.stringify(denied.outputJson), /--json/);
|
|
2597
|
+
assert.match(JSON.stringify(denied.outputJson), /explicit MCP compatibility is enabled/);
|
|
2598
|
+
assert.match(JSON.stringify(denied.outputJson), /`omx_state\.\*` tools/);
|
|
2284
2599
|
assert.equal(
|
|
2285
2600
|
existsSync(join(cwd, ".omx", "state", "sessions", "sess-deny-1", "autopilot-state.json")),
|
|
2286
2601
|
false,
|
|
@@ -2357,7 +2672,7 @@ export async function onHookEvent(event) {
|
|
|
2357
2672
|
assert.match(message, /\$ralph" -> ralph/);
|
|
2358
2673
|
assert.doesNotMatch(message, /mode transiting:/);
|
|
2359
2674
|
assert.match(message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./);
|
|
2360
|
-
assert.match(message, /
|
|
2675
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
2361
2676
|
assert.doesNotMatch(message, /Use the durable OMX team runtime via `omx team \.\.\.`/);
|
|
2362
2677
|
} finally {
|
|
2363
2678
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -2389,7 +2704,7 @@ export async function onHookEvent(event) {
|
|
|
2389
2704
|
assert.match(message, /\$oh-my-codex:ralph" -> ralph/);
|
|
2390
2705
|
assert.doesNotMatch(message, /mode transiting:/);
|
|
2391
2706
|
assert.match(message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./);
|
|
2392
|
-
assert.match(message, /
|
|
2707
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
2393
2708
|
} finally {
|
|
2394
2709
|
await rm(cwd, { recursive: true, force: true });
|
|
2395
2710
|
}
|
|
@@ -4499,7 +4814,7 @@ exit 0
|
|
|
4499
4814
|
);
|
|
4500
4815
|
assert.match(
|
|
4501
4816
|
additionalContext,
|
|
4502
|
-
/omx state
|
|
4817
|
+
/omx state write --input/,
|
|
4503
4818
|
);
|
|
4504
4819
|
assert.match(
|
|
4505
4820
|
additionalContext,
|
|
@@ -4747,7 +5062,7 @@ exit 0
|
|
|
4747
5062
|
assert.equal(hookSpecificOutput?.hookEventName, "PostToolUse");
|
|
4748
5063
|
assert.match(
|
|
4749
5064
|
String(hookSpecificOutput?.additionalContext || ""),
|
|
4750
|
-
/Retry via CLI parity with `omx state
|
|
5065
|
+
/Retry via CLI parity with `omx state write --input '\{\}' --json`\./,
|
|
4751
5066
|
);
|
|
4752
5067
|
assert.match(
|
|
4753
5068
|
String(hookSpecificOutput?.additionalContext || ""),
|
|
@@ -4833,7 +5148,7 @@ exit 0
|
|
|
4833
5148
|
hookSpecificOutput: {
|
|
4834
5149
|
hookEventName: "PostToolUse",
|
|
4835
5150
|
additionalContext:
|
|
4836
|
-
"Clear MCP transport-death signal detected. Preserve current team/runtime state. Retry via CLI parity with `omx state
|
|
5151
|
+
"Clear MCP transport-death signal detected. Preserve current team/runtime state. Retry via CLI parity with `omx state write --input '{\"mode\":\"team\",\"active\":true}' --json`. OMX MCP servers are plain Node stdio processes, so they still shut down when stdin/transport closes. If this happened during team runtime, inspect first with `omx team status <team>` or `omx team api read-stall-state --input '{\"team_name\":\"<team>\"}' --json`, and only force cleanup after capturing needed state. For root-cause debugging, rerun with `OMX_MCP_TRANSPORT_DEBUG=1` to log why the stdio transport closed.",
|
|
4837
5152
|
},
|
|
4838
5153
|
});
|
|
4839
5154
|
|
|
@@ -6253,6 +6568,41 @@ exit 0
|
|
|
6253
6568
|
}
|
|
6254
6569
|
});
|
|
6255
6570
|
|
|
6571
|
+
it("ignores stale source-root team Stop fallback when OMX_TEAM_STATE_ROOT is authoritative", async () => {
|
|
6572
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-stale-source-root-"));
|
|
6573
|
+
const teamStateRoot = join(cwd, "shared-team-state");
|
|
6574
|
+
const priorTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
6575
|
+
try {
|
|
6576
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
6577
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
6578
|
+
await mkdir(join(teamStateRoot, "team", "stale-source-team"), { recursive: true });
|
|
6579
|
+
await writeJson(join(cwd, ".omx", "state", "team-state.json"), {
|
|
6580
|
+
active: true,
|
|
6581
|
+
team_name: "stale-source-team",
|
|
6582
|
+
current_phase: "team-exec",
|
|
6583
|
+
});
|
|
6584
|
+
await writeJson(join(teamStateRoot, "team", "stale-source-team", "phase.json"), {
|
|
6585
|
+
current_phase: "team-exec",
|
|
6586
|
+
});
|
|
6587
|
+
|
|
6588
|
+
const result = await dispatchCodexNativeHook(
|
|
6589
|
+
{
|
|
6590
|
+
hook_event_name: "Stop",
|
|
6591
|
+
cwd,
|
|
6592
|
+
session_id: "sess-stale-source-team",
|
|
6593
|
+
},
|
|
6594
|
+
{ cwd },
|
|
6595
|
+
);
|
|
6596
|
+
|
|
6597
|
+
assert.equal(result.omxEventName, "stop");
|
|
6598
|
+
assert.equal(result.outputJson, null);
|
|
6599
|
+
} finally {
|
|
6600
|
+
if (typeof priorTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = priorTeamStateRoot;
|
|
6601
|
+
else delete process.env.OMX_TEAM_STATE_ROOT;
|
|
6602
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6603
|
+
}
|
|
6604
|
+
});
|
|
6605
|
+
|
|
6256
6606
|
it("returns Stop continuation output from canonical team state rooted via OMX_TEAM_STATE_ROOT", async () => {
|
|
6257
6607
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-env-root-"));
|
|
6258
6608
|
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
@@ -7288,6 +7638,79 @@ exit 0
|
|
|
7288
7638
|
}
|
|
7289
7639
|
});
|
|
7290
7640
|
|
|
7641
|
+
it("blocks Codex App Stop when Ralph is marked complete without completion-audit evidence", async () => {
|
|
7642
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-missing-"));
|
|
7643
|
+
try {
|
|
7644
|
+
const sessionId = "sess-ralph-complete-missing";
|
|
7645
|
+
const statePath = join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json");
|
|
7646
|
+
await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd });
|
|
7647
|
+
await writeJson(statePath, {
|
|
7648
|
+
active: false,
|
|
7649
|
+
mode: "ralph",
|
|
7650
|
+
current_phase: "complete",
|
|
7651
|
+
session_id: sessionId,
|
|
7652
|
+
completed_at: "2026-05-10T12:00:00.000Z",
|
|
7653
|
+
});
|
|
7654
|
+
|
|
7655
|
+
const result = await dispatchCodexNativeHook(
|
|
7656
|
+
{
|
|
7657
|
+
hook_event_name: "Stop",
|
|
7658
|
+
cwd,
|
|
7659
|
+
session_id: sessionId,
|
|
7660
|
+
last_assistant_message: "Done. Ralph complete.",
|
|
7661
|
+
},
|
|
7662
|
+
{ cwd },
|
|
7663
|
+
);
|
|
7664
|
+
|
|
7665
|
+
assert.equal(result.omxEventName, "stop");
|
|
7666
|
+
assert.match(String(result.outputJson?.reason), /Ralph completion audit is missing required evidence/);
|
|
7667
|
+
assert.equal(result.outputJson?.stopReason, "ralph_completion_audit_missing_completion_audit");
|
|
7668
|
+
const reopened = JSON.parse(await readFile(statePath, "utf-8")) as Record<string, unknown>;
|
|
7669
|
+
assert.equal(reopened.active, true);
|
|
7670
|
+
assert.equal(reopened.current_phase, "verifying");
|
|
7671
|
+
assert.equal(reopened.completion_audit_gate, "blocked");
|
|
7672
|
+
assert.equal(reopened.completion_audit_missing_reason, "missing_completion_audit");
|
|
7673
|
+
assert.equal(typeof reopened.completed_at, "undefined");
|
|
7674
|
+
} finally {
|
|
7675
|
+
await rm(cwd, { recursive: true, force: true });
|
|
7676
|
+
}
|
|
7677
|
+
});
|
|
7678
|
+
|
|
7679
|
+
it("allows Codex App Stop when complete Ralph state carries checklist and verification evidence", async () => {
|
|
7680
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-present-"));
|
|
7681
|
+
try {
|
|
7682
|
+
const sessionId = "sess-ralph-complete-present";
|
|
7683
|
+
await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd });
|
|
7684
|
+
await writeJson(join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json"), {
|
|
7685
|
+
active: false,
|
|
7686
|
+
mode: "ralph",
|
|
7687
|
+
current_phase: "complete",
|
|
7688
|
+
session_id: sessionId,
|
|
7689
|
+
completed_at: "2026-05-10T12:00:00.000Z",
|
|
7690
|
+
completion_audit: {
|
|
7691
|
+
passed: true,
|
|
7692
|
+
prompt_to_artifact_checklist: ["issue #2260 fixed", "tests added"],
|
|
7693
|
+
verification_evidence: ["node --test dist/scripts/__tests__/codex-native-hook.test.js"],
|
|
7694
|
+
},
|
|
7695
|
+
});
|
|
7696
|
+
|
|
7697
|
+
const result = await dispatchCodexNativeHook(
|
|
7698
|
+
{
|
|
7699
|
+
hook_event_name: "Stop",
|
|
7700
|
+
cwd,
|
|
7701
|
+
session_id: sessionId,
|
|
7702
|
+
last_assistant_message: "Done with completion audit evidence recorded.",
|
|
7703
|
+
},
|
|
7704
|
+
{ cwd },
|
|
7705
|
+
);
|
|
7706
|
+
|
|
7707
|
+
assert.equal(result.omxEventName, "stop");
|
|
7708
|
+
assert.equal(result.outputJson, null);
|
|
7709
|
+
} finally {
|
|
7710
|
+
await rm(cwd, { recursive: true, force: true });
|
|
7711
|
+
}
|
|
7712
|
+
});
|
|
7713
|
+
|
|
7291
7714
|
it("returns Stop continuation output while Ralph is active without an explicit session pin", async () => {
|
|
7292
7715
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-"));
|
|
7293
7716
|
try {
|