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
|
@@ -9,12 +9,13 @@ import { afterEach, beforeEach, describe, it } from "node:test";
|
|
|
9
9
|
import { buildManagedCodexHooksConfig } from "../../config/codex-hooks.js";
|
|
10
10
|
import { DOCUMENT_REFRESH_EXEMPTION_PREFIX } from "../../document-refresh/enforcer.js";
|
|
11
11
|
import { initTeamState, readTeamLeaderAttention, readTeamPhase, writeTeamLeaderAttention, } from "../../team/state.js";
|
|
12
|
-
import { dispatchCodexNativeHook, isCodexNativeHookMainModule, mapCodexHookEventToOmxEvent, resolveSessionOwnerPidFromAncestry, } from "../codex-native-hook.js";
|
|
12
|
+
import { dispatchCodexNativeHook, isCodexNativeHookMainModule, looksLikeGoalCompletionPrompt, mapCodexHookEventToOmxEvent, resolveSessionOwnerPidFromAncestry, } from "../codex-native-hook.js";
|
|
13
13
|
import { writeSessionStart } from "../../hooks/session.js";
|
|
14
14
|
import { resetTriageConfigCache } from "../../hooks/triage-config.js";
|
|
15
15
|
import { executeStateOperation } from "../../state/operations.js";
|
|
16
16
|
import { OMX_TMUX_HUD_OWNER_ENV } from "../../hud/reconcile.js";
|
|
17
|
-
import {
|
|
17
|
+
import { readAllState } from "../../hud/state.js";
|
|
18
|
+
import { getLegacyWikiDir, serializePage, writePage } from "../../wiki/storage.js";
|
|
18
19
|
import { WIKI_SCHEMA_VERSION } from "../../wiki/types.js";
|
|
19
20
|
function nativeHookScriptPath() {
|
|
20
21
|
return join(process.cwd(), "dist", "scripts", "codex-native-hook.js");
|
|
@@ -183,7 +184,7 @@ describe("codex native hook config", () => {
|
|
|
183
184
|
"Stop",
|
|
184
185
|
]);
|
|
185
186
|
const sessionStart = config.hooks.SessionStart[0];
|
|
186
|
-
assert.equal(sessionStart.matcher, "startup|resume");
|
|
187
|
+
assert.equal(sessionStart.matcher, "startup|resume|clear");
|
|
187
188
|
assert.equal(sessionStart.hooks?.[0]?.statusMessage, undefined);
|
|
188
189
|
const preToolUse = config.hooks.PreToolUse[0];
|
|
189
190
|
assert.equal(preToolUse.matcher, "Bash");
|
|
@@ -858,6 +859,61 @@ describe("codex native hook dispatch", () => {
|
|
|
858
859
|
await rm(cwd, { recursive: true, force: true });
|
|
859
860
|
}
|
|
860
861
|
});
|
|
862
|
+
it("prefers repository project-memory.json during SessionStart while preserving legacy wiki guidance", async () => {
|
|
863
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-root-memory-legacy-wiki-"));
|
|
864
|
+
try {
|
|
865
|
+
const now = new Date().toISOString();
|
|
866
|
+
const legacyWikiDir = getLegacyWikiDir(cwd);
|
|
867
|
+
await mkdir(legacyWikiDir, { recursive: true });
|
|
868
|
+
await writeFile(join(legacyWikiDir, "legacy.md"), serializePage({
|
|
869
|
+
filename: "legacy.md",
|
|
870
|
+
frontmatter: {
|
|
871
|
+
title: "Legacy",
|
|
872
|
+
tags: ["legacy"],
|
|
873
|
+
created: now,
|
|
874
|
+
updated: now,
|
|
875
|
+
sources: [],
|
|
876
|
+
links: [],
|
|
877
|
+
category: "reference",
|
|
878
|
+
confidence: "medium",
|
|
879
|
+
schemaVersion: WIKI_SCHEMA_VERSION,
|
|
880
|
+
},
|
|
881
|
+
content: "\n# Legacy\n\nLegacy wiki context must remain visible.\n",
|
|
882
|
+
}));
|
|
883
|
+
await writeJson(join(cwd, ".omx", "project-memory.json"), {
|
|
884
|
+
techStack: "Legacy runtime memory should not win",
|
|
885
|
+
notes: [{ category: "legacy", content: "stale legacy note", timestamp: now }],
|
|
886
|
+
});
|
|
887
|
+
await writeJson(join(cwd, "project-memory.json"), {
|
|
888
|
+
techStack: "Canonical root memory",
|
|
889
|
+
build: "npm run build && node --test dist/scripts/__tests__/codex-native-hook.test.js",
|
|
890
|
+
conventions: "prefer repository-visible project memory at startup",
|
|
891
|
+
directives: [
|
|
892
|
+
{ directive: "Load root project-memory.json before legacy .omx memory.", priority: "high", timestamp: now },
|
|
893
|
+
],
|
|
894
|
+
notes: [
|
|
895
|
+
{ category: "issue", content: "Regression fixture for issue #2273.", timestamp: now },
|
|
896
|
+
],
|
|
897
|
+
});
|
|
898
|
+
const result = await dispatchCodexNativeHook({
|
|
899
|
+
hook_event_name: "SessionStart",
|
|
900
|
+
cwd,
|
|
901
|
+
session_id: "sess-root-memory-legacy-wiki",
|
|
902
|
+
}, { cwd, sessionOwnerPid: 43210 });
|
|
903
|
+
const additionalContext = String(result.outputJson?.hookSpecificOutput?.additionalContext ?? "");
|
|
904
|
+
assert.match(additionalContext, /\[Project memory\]/);
|
|
905
|
+
assert.match(additionalContext, /source: project-memory\.json/);
|
|
906
|
+
assert.match(additionalContext, /Canonical root memory/);
|
|
907
|
+
assert.match(additionalContext, /Load root project-memory\.json before legacy \.omx memory\./);
|
|
908
|
+
assert.match(additionalContext, /Regression fixture for issue #2273\./);
|
|
909
|
+
assert.doesNotMatch(additionalContext, /Legacy runtime memory should not win/);
|
|
910
|
+
assert.match(additionalContext, /legacy pages at \.omx\/wiki\//);
|
|
911
|
+
assert.match(additionalContext, /Legacy wiki fallback is read-only/);
|
|
912
|
+
}
|
|
913
|
+
finally {
|
|
914
|
+
await rm(cwd, { recursive: true, force: true });
|
|
915
|
+
}
|
|
916
|
+
});
|
|
861
917
|
it("starts a fresh native session without inheriting stale task-scoped context", async () => {
|
|
862
918
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-isolation-"));
|
|
863
919
|
try {
|
|
@@ -962,7 +1018,7 @@ describe("codex native hook dispatch", () => {
|
|
|
962
1018
|
assert.equal(result.omxEventName, "keyword-detector");
|
|
963
1019
|
assert.equal(result.skillState?.skill, "ralplan");
|
|
964
1020
|
assert.ok(result.outputJson, "UserPromptSubmit should emit developer context");
|
|
965
|
-
assert.match(JSON.stringify(result.outputJson), /
|
|
1021
|
+
assert.match(JSON.stringify(result.outputJson), /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
966
1022
|
assert.equal(existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, "session-scoped keyword activation should not write root skill-active-state.json");
|
|
967
1023
|
const statePath = join(cwd, ".omx", "state", "sessions", "sess-1", "skill-active-state.json");
|
|
968
1024
|
assert.equal(existsSync(statePath), true);
|
|
@@ -976,6 +1032,141 @@ describe("codex native hook dispatch", () => {
|
|
|
976
1032
|
await rm(cwd, { recursive: true, force: true });
|
|
977
1033
|
}
|
|
978
1034
|
});
|
|
1035
|
+
it("records boxed keyword activation mode detail and skill state under OMX_ROOT", async () => {
|
|
1036
|
+
const root = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-"));
|
|
1037
|
+
const cwd = join(root, "source");
|
|
1038
|
+
const omxRoot = join(root, "box");
|
|
1039
|
+
const sessionId = "sess-boxed-ralplan";
|
|
1040
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
1041
|
+
const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
1042
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
1043
|
+
const previousOmxSessionId = process.env.OMX_SESSION_ID;
|
|
1044
|
+
try {
|
|
1045
|
+
await mkdir(cwd, { recursive: true });
|
|
1046
|
+
process.env.OMX_ROOT = omxRoot;
|
|
1047
|
+
delete process.env.OMX_STATE_ROOT;
|
|
1048
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
1049
|
+
process.env.OMX_SESSION_ID = sessionId;
|
|
1050
|
+
const result = await dispatchCodexNativeHook({
|
|
1051
|
+
hook_event_name: "UserPromptSubmit",
|
|
1052
|
+
cwd,
|
|
1053
|
+
session_id: sessionId,
|
|
1054
|
+
thread_id: "thread-boxed",
|
|
1055
|
+
turn_id: "turn-boxed",
|
|
1056
|
+
prompt: "$ralplan implement issue #1307",
|
|
1057
|
+
}, { cwd });
|
|
1058
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
1059
|
+
assert.equal(result.skillState?.skill, "ralplan");
|
|
1060
|
+
const boxedSessionDir = join(omxRoot, ".omx", "state", "sessions", sessionId);
|
|
1061
|
+
assert.equal(existsSync(join(boxedSessionDir, "skill-active-state.json")), true);
|
|
1062
|
+
assert.equal(existsSync(join(boxedSessionDir, "ralplan-state.json")), true);
|
|
1063
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json")), false);
|
|
1064
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json")), false);
|
|
1065
|
+
const hudState = await readAllState(cwd);
|
|
1066
|
+
assert.equal(hudState.ralplan?.active, true);
|
|
1067
|
+
assert.equal(hudState.ralplan?.current_phase, "planning");
|
|
1068
|
+
}
|
|
1069
|
+
finally {
|
|
1070
|
+
if (typeof previousOmxRoot === "string")
|
|
1071
|
+
process.env.OMX_ROOT = previousOmxRoot;
|
|
1072
|
+
else
|
|
1073
|
+
delete process.env.OMX_ROOT;
|
|
1074
|
+
if (typeof previousOmxStateRoot === "string")
|
|
1075
|
+
process.env.OMX_STATE_ROOT = previousOmxStateRoot;
|
|
1076
|
+
else
|
|
1077
|
+
delete process.env.OMX_STATE_ROOT;
|
|
1078
|
+
if (typeof previousTeamStateRoot === "string")
|
|
1079
|
+
process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
1080
|
+
else
|
|
1081
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
1082
|
+
if (typeof previousOmxSessionId === "string")
|
|
1083
|
+
process.env.OMX_SESSION_ID = previousOmxSessionId;
|
|
1084
|
+
else
|
|
1085
|
+
delete process.env.OMX_SESSION_ID;
|
|
1086
|
+
await rm(root, { recursive: true, force: true });
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
it("records native keyword activation mode detail and skill state under OMX_TEAM_STATE_ROOT", async () => {
|
|
1090
|
+
const root = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-"));
|
|
1091
|
+
const cwd = join(root, "source");
|
|
1092
|
+
const teamStateRoot = join(root, "team-state");
|
|
1093
|
+
const sessionId = "sess-team-root-ralplan";
|
|
1094
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
1095
|
+
const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
1096
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
1097
|
+
const previousOmxSessionId = process.env.OMX_SESSION_ID;
|
|
1098
|
+
try {
|
|
1099
|
+
await mkdir(cwd, { recursive: true });
|
|
1100
|
+
delete process.env.OMX_ROOT;
|
|
1101
|
+
delete process.env.OMX_STATE_ROOT;
|
|
1102
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
1103
|
+
process.env.OMX_SESSION_ID = sessionId;
|
|
1104
|
+
const result = await dispatchCodexNativeHook({
|
|
1105
|
+
hook_event_name: "UserPromptSubmit",
|
|
1106
|
+
cwd,
|
|
1107
|
+
session_id: sessionId,
|
|
1108
|
+
thread_id: "thread-team-root",
|
|
1109
|
+
turn_id: "turn-team-root",
|
|
1110
|
+
prompt: "$ralplan implement issue #1307",
|
|
1111
|
+
}, { cwd });
|
|
1112
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
1113
|
+
assert.equal(result.skillState?.skill, "ralplan");
|
|
1114
|
+
const teamSessionDir = join(teamStateRoot, "sessions", sessionId);
|
|
1115
|
+
assert.equal(existsSync(join(teamSessionDir, "skill-active-state.json")), true);
|
|
1116
|
+
assert.equal(existsSync(join(teamSessionDir, "ralplan-state.json")), true);
|
|
1117
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json")), false);
|
|
1118
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json")), false);
|
|
1119
|
+
const hudState = await readAllState(cwd);
|
|
1120
|
+
assert.equal(hudState.ralplan?.active, true);
|
|
1121
|
+
assert.equal(hudState.ralplan?.current_phase, "planning");
|
|
1122
|
+
}
|
|
1123
|
+
finally {
|
|
1124
|
+
if (typeof previousOmxRoot === "string")
|
|
1125
|
+
process.env.OMX_ROOT = previousOmxRoot;
|
|
1126
|
+
else
|
|
1127
|
+
delete process.env.OMX_ROOT;
|
|
1128
|
+
if (typeof previousOmxStateRoot === "string")
|
|
1129
|
+
process.env.OMX_STATE_ROOT = previousOmxStateRoot;
|
|
1130
|
+
else
|
|
1131
|
+
delete process.env.OMX_STATE_ROOT;
|
|
1132
|
+
if (typeof previousTeamStateRoot === "string")
|
|
1133
|
+
process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
1134
|
+
else
|
|
1135
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
1136
|
+
if (typeof previousOmxSessionId === "string")
|
|
1137
|
+
process.env.OMX_SESSION_ID = previousOmxSessionId;
|
|
1138
|
+
else
|
|
1139
|
+
delete process.env.OMX_SESSION_ID;
|
|
1140
|
+
await rm(root, { recursive: true, force: true });
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
it("classifies only actionable goal completion wording", () => {
|
|
1144
|
+
const actionable = [
|
|
1145
|
+
"complete this goal now",
|
|
1146
|
+
"Performance goal complete; next call update_goal({status: \"complete\"}).",
|
|
1147
|
+
"get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.",
|
|
1148
|
+
"omx ultragoal checkpoint --goal-id G001-demo --status complete --codex-goal-json goal.json",
|
|
1149
|
+
"Call update_goal({status: \"complete\"}) after verification.",
|
|
1150
|
+
"Goal complete.",
|
|
1151
|
+
"The goal is complete.",
|
|
1152
|
+
"Goal complete: verified with tests.",
|
|
1153
|
+
"Goal complete — verified with tests.",
|
|
1154
|
+
"The goal is complete: verified.",
|
|
1155
|
+
"The goal is complete — verified.",
|
|
1156
|
+
];
|
|
1157
|
+
const ordinary = [
|
|
1158
|
+
"my goal is to complete the migration without regressions",
|
|
1159
|
+
"Our goal is to finish this carefully after tests pass.",
|
|
1160
|
+
"The goal of this patch is to close a review gap.",
|
|
1161
|
+
"A goal can be complete only after a human review.",
|
|
1162
|
+
];
|
|
1163
|
+
for (const text of actionable) {
|
|
1164
|
+
assert.equal(looksLikeGoalCompletionPrompt(text), true, text);
|
|
1165
|
+
}
|
|
1166
|
+
for (const text of ordinary) {
|
|
1167
|
+
assert.equal(looksLikeGoalCompletionPrompt(text), false, text);
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
979
1170
|
it("warns completion-like prompts when active goal workflows need Codex snapshot reconciliation", async () => {
|
|
980
1171
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-goal-warning-"));
|
|
981
1172
|
try {
|
|
@@ -1025,6 +1216,109 @@ describe("codex native hook dispatch", () => {
|
|
|
1025
1216
|
await rm(cwd, { recursive: true, force: true });
|
|
1026
1217
|
}
|
|
1027
1218
|
});
|
|
1219
|
+
it("blocks ultragoal Stop for concise generic goal completion claims", async () => {
|
|
1220
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-generic-complete-stop-"));
|
|
1221
|
+
try {
|
|
1222
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1223
|
+
version: 1,
|
|
1224
|
+
activeGoalId: "G001-demo",
|
|
1225
|
+
goals: [{ id: "G001-demo", status: "in_progress", objective: "Demo goal" }],
|
|
1226
|
+
});
|
|
1227
|
+
const result = await dispatchCodexNativeHook({
|
|
1228
|
+
hook_event_name: "Stop",
|
|
1229
|
+
cwd,
|
|
1230
|
+
session_id: "sess-ultragoal-generic-complete-stop",
|
|
1231
|
+
thread_id: "thread-ultragoal-generic-complete-stop",
|
|
1232
|
+
last_assistant_message: "Goal complete.",
|
|
1233
|
+
}, { cwd });
|
|
1234
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
1235
|
+
assert.match(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
1236
|
+
}
|
|
1237
|
+
finally {
|
|
1238
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
it("does not block ultragoal Stop for ordinary prose about a goal to complete work", async () => {
|
|
1242
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-ordinary-stop-"));
|
|
1243
|
+
try {
|
|
1244
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1245
|
+
version: 1,
|
|
1246
|
+
activeGoalId: "G001-demo",
|
|
1247
|
+
goals: [{ id: "G001-demo", status: "in_progress", objective: "Demo goal" }],
|
|
1248
|
+
});
|
|
1249
|
+
const result = await dispatchCodexNativeHook({
|
|
1250
|
+
hook_event_name: "Stop",
|
|
1251
|
+
cwd,
|
|
1252
|
+
session_id: "sess-ultragoal-ordinary-stop",
|
|
1253
|
+
thread_id: "thread-ultragoal-ordinary-stop",
|
|
1254
|
+
last_assistant_message: "My goal is to complete the migration without regressions, so I will keep testing.",
|
|
1255
|
+
}, { cwd });
|
|
1256
|
+
assert.notEqual(result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required");
|
|
1257
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
1258
|
+
}
|
|
1259
|
+
finally {
|
|
1260
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
it("blocks ultragoal Stop with blocked checkpoint and fresh-thread remediation for completed legacy snapshots", async () => {
|
|
1264
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-legacy-stop-"));
|
|
1265
|
+
try {
|
|
1266
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1267
|
+
version: 1,
|
|
1268
|
+
activeGoalId: "G001-demo",
|
|
1269
|
+
goals: [{ id: "G001-demo", status: "in_progress", objective: "Demo goal" }],
|
|
1270
|
+
});
|
|
1271
|
+
const result = await dispatchCodexNativeHook({
|
|
1272
|
+
hook_event_name: "Stop",
|
|
1273
|
+
cwd,
|
|
1274
|
+
session_id: "sess-ultragoal-legacy-stop",
|
|
1275
|
+
thread_id: "thread-ultragoal-legacy-stop",
|
|
1276
|
+
last_assistant_message: "get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.",
|
|
1277
|
+
}, { cwd });
|
|
1278
|
+
const output = JSON.stringify(result.outputJson);
|
|
1279
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
1280
|
+
assert.match(output, /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
1281
|
+
assert.match(output, /--status blocked/);
|
|
1282
|
+
assert.match(output, /fresh Codex thread/);
|
|
1283
|
+
assert.match(output, /Hooks must not mutate Codex goal state/);
|
|
1284
|
+
}
|
|
1285
|
+
finally {
|
|
1286
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
it("does not block ultragoal Stop after task-scoped reconciliation finishes exploded bookkeeping", async () => {
|
|
1290
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-reconciled-stop-"));
|
|
1291
|
+
try {
|
|
1292
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
1293
|
+
version: 1,
|
|
1294
|
+
codexGoalMode: "aggregate",
|
|
1295
|
+
codexObjective: "Complete all ultragoal stories in .omx/ultragoal/goals.json: many micro goals",
|
|
1296
|
+
activeGoalId: "G001-micro",
|
|
1297
|
+
aggregateCompletion: {
|
|
1298
|
+
status: "complete",
|
|
1299
|
+
completedAt: "2026-05-04T10:04:00.000Z",
|
|
1300
|
+
evidence: "planned work done; validation complete; reviews clean",
|
|
1301
|
+
},
|
|
1302
|
+
goals: Array.from({ length: 136 }, (_, index) => ({
|
|
1303
|
+
id: `G${String(index + 1).padStart(3, "0")}-micro`,
|
|
1304
|
+
status: index === 0 ? "in_progress" : "pending",
|
|
1305
|
+
objective: `Synthetic slice ${index + 1}.`,
|
|
1306
|
+
})),
|
|
1307
|
+
});
|
|
1308
|
+
const result = await dispatchCodexNativeHook({
|
|
1309
|
+
hook_event_name: "Stop",
|
|
1310
|
+
cwd,
|
|
1311
|
+
session_id: "sess-ultragoal-reconciled-stop",
|
|
1312
|
+
thread_id: "thread-ultragoal-reconciled-stop",
|
|
1313
|
+
last_assistant_message: "Yes — planned implementation work is done; ultragoal bookkeeping reconciled complete.",
|
|
1314
|
+
}, { cwd });
|
|
1315
|
+
assert.notEqual(result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required");
|
|
1316
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id/);
|
|
1317
|
+
}
|
|
1318
|
+
finally {
|
|
1319
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1320
|
+
}
|
|
1321
|
+
});
|
|
1028
1322
|
it("does not block Stop for non-passing autoresearch-goal professor-critic verdicts", async () => {
|
|
1029
1323
|
for (const verdict of ["blocked", "fail", "failed"]) {
|
|
1030
1324
|
const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-autoresearch-${verdict}-stop-`));
|
|
@@ -1188,7 +1482,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1188
1482
|
assert.equal(result.skillState?.skill, "ralplan");
|
|
1189
1483
|
const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
|
|
1190
1484
|
assert.match(message, /\$oh-my-codex:ralplan" -> ralplan/);
|
|
1191
|
-
assert.match(message, /
|
|
1485
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1192
1486
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-plugin-1", "ralplan-state.json")), true);
|
|
1193
1487
|
}
|
|
1194
1488
|
finally {
|
|
@@ -1362,7 +1656,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1362
1656
|
assert.equal(result.skillState?.skill, "ralph");
|
|
1363
1657
|
const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
|
|
1364
1658
|
assert.match(message, /\$ralph" -> ralph/);
|
|
1365
|
-
assert.match(message, /
|
|
1659
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1366
1660
|
assert.match(message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./);
|
|
1367
1661
|
assert.match(message, /Use `omx ralph --prd \.\.\.` only when you explicitly want the PRD-gated CLI startup path\./);
|
|
1368
1662
|
}
|
|
@@ -1386,7 +1680,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1386
1680
|
assert.equal(result.skillState?.skill, "ralph");
|
|
1387
1681
|
const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
|
|
1388
1682
|
assert.match(message, /\$oh-my-codex:ralph" -> ralph/);
|
|
1389
|
-
assert.match(message, /
|
|
1683
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1390
1684
|
assert.match(message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./);
|
|
1391
1685
|
}
|
|
1392
1686
|
finally {
|
|
@@ -1455,7 +1749,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1455
1749
|
assert.equal(result.skillState?.skill, "deep-interview");
|
|
1456
1750
|
const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
|
|
1457
1751
|
assert.match(message, /\$deep-interview" -> deep-interview/);
|
|
1458
|
-
assert.match(message, /
|
|
1752
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1459
1753
|
assert.match(message, /Deep-interview is active, but this session is not attached to tmux/);
|
|
1460
1754
|
assert.match(message, /Do not invoke `omx question`, `omx hud`, or `omx team`/);
|
|
1461
1755
|
assert.match(message, /native structured question tool when available/);
|
|
@@ -1845,8 +2139,11 @@ export async function onHookEvent(event) {
|
|
|
1845
2139
|
}, { cwd });
|
|
1846
2140
|
assert.match(JSON.stringify(denied.outputJson), /denied workflow keyword/i);
|
|
1847
2141
|
assert.match(JSON.stringify(denied.outputJson), /Unsupported workflow overlap: team \+ autopilot\./);
|
|
1848
|
-
assert.match(JSON.stringify(denied.outputJson),
|
|
1849
|
-
assert.match(JSON.stringify(denied.outputJson),
|
|
2142
|
+
assert.match(JSON.stringify(denied.outputJson), /omx state clear --input/);
|
|
2143
|
+
assert.match(JSON.stringify(denied.outputJson), /mode\\":\\"<mode>/);
|
|
2144
|
+
assert.match(JSON.stringify(denied.outputJson), /--json/);
|
|
2145
|
+
assert.match(JSON.stringify(denied.outputJson), /explicit MCP compatibility is enabled/);
|
|
2146
|
+
assert.match(JSON.stringify(denied.outputJson), /`omx_state\.\*` tools/);
|
|
1850
2147
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-deny-1", "autopilot-state.json")), false);
|
|
1851
2148
|
}
|
|
1852
2149
|
finally {
|
|
@@ -1905,7 +2202,7 @@ export async function onHookEvent(event) {
|
|
|
1905
2202
|
assert.match(message, /\$ralph" -> ralph/);
|
|
1906
2203
|
assert.doesNotMatch(message, /mode transiting:/);
|
|
1907
2204
|
assert.match(message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./);
|
|
1908
|
-
assert.match(message, /
|
|
2205
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1909
2206
|
assert.doesNotMatch(message, /Use the durable OMX team runtime via `omx team \.\.\.`/);
|
|
1910
2207
|
}
|
|
1911
2208
|
finally {
|
|
@@ -1930,7 +2227,7 @@ export async function onHookEvent(event) {
|
|
|
1930
2227
|
assert.match(message, /\$oh-my-codex:ralph" -> ralph/);
|
|
1931
2228
|
assert.doesNotMatch(message, /mode transiting:/);
|
|
1932
2229
|
assert.match(message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./);
|
|
1933
|
-
assert.match(message, /
|
|
2230
|
+
assert.match(message, /use CLI-first state updates via `omx state write\/read\/clear --input '<json>' --json`/);
|
|
1934
2231
|
}
|
|
1935
2232
|
finally {
|
|
1936
2233
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -3700,7 +3997,7 @@ exit 0
|
|
|
3700
3997
|
assert.equal(output?.decision, "block");
|
|
3701
3998
|
assert.equal(output?.reason, "The MCP tool appears to have lost its transport/server connection. Preserve state, debug the transport failure, and use OMX CLI/file-backed fallbacks instead of retrying blindly.");
|
|
3702
3999
|
const additionalContext = String(output?.hookSpecificOutput?.additionalContext ?? "");
|
|
3703
|
-
assert.match(additionalContext, /omx state
|
|
4000
|
+
assert.match(additionalContext, /omx state write --input/);
|
|
3704
4001
|
assert.match(additionalContext, /plain Node stdio processes/i);
|
|
3705
4002
|
assert.match(additionalContext, /read-stall-state/);
|
|
3706
4003
|
assert.match(additionalContext, /OMX_MCP_TRANSPORT_DEBUG=1/);
|
|
@@ -3870,7 +4167,7 @@ exit 0
|
|
|
3870
4167
|
assert.match(String(result.outputJson?.reason || ""), /lost its transport\/server connection/);
|
|
3871
4168
|
const hookSpecificOutput = result.outputJson?.hookSpecificOutput;
|
|
3872
4169
|
assert.equal(hookSpecificOutput?.hookEventName, "PostToolUse");
|
|
3873
|
-
assert.match(String(hookSpecificOutput?.additionalContext || ""), /Retry via CLI parity with `omx state
|
|
4170
|
+
assert.match(String(hookSpecificOutput?.additionalContext || ""), /Retry via CLI parity with `omx state write --input '\{\}' --json`\./);
|
|
3874
4171
|
assert.match(String(hookSpecificOutput?.additionalContext || ""), /omx team api read-stall-state/);
|
|
3875
4172
|
const phase = JSON.parse(await readFile(join(cwd, ".omx", "state", "team", "mcp-transport-dead-team", "phase.json"), "utf-8"));
|
|
3876
4173
|
assert.equal(phase.current_phase, "failed");
|
|
@@ -3928,7 +4225,7 @@ exit 0
|
|
|
3928
4225
|
reason: "The MCP tool appears to have lost its transport/server connection. Preserve state, debug the transport failure, and use OMX CLI/file-backed fallbacks instead of retrying blindly.",
|
|
3929
4226
|
hookSpecificOutput: {
|
|
3930
4227
|
hookEventName: "PostToolUse",
|
|
3931
|
-
additionalContext: "Clear MCP transport-death signal detected. Preserve current team/runtime state. Retry via CLI parity with `omx state
|
|
4228
|
+
additionalContext: "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.",
|
|
3932
4229
|
},
|
|
3933
4230
|
});
|
|
3934
4231
|
const phase = await readTeamPhase("transport-team", cwd);
|
|
@@ -5093,6 +5390,38 @@ exit 0
|
|
|
5093
5390
|
await rm(cwd, { recursive: true, force: true });
|
|
5094
5391
|
}
|
|
5095
5392
|
});
|
|
5393
|
+
it("ignores stale source-root team Stop fallback when OMX_TEAM_STATE_ROOT is authoritative", async () => {
|
|
5394
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-stale-source-root-"));
|
|
5395
|
+
const teamStateRoot = join(cwd, "shared-team-state");
|
|
5396
|
+
const priorTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
5397
|
+
try {
|
|
5398
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
5399
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
5400
|
+
await mkdir(join(teamStateRoot, "team", "stale-source-team"), { recursive: true });
|
|
5401
|
+
await writeJson(join(cwd, ".omx", "state", "team-state.json"), {
|
|
5402
|
+
active: true,
|
|
5403
|
+
team_name: "stale-source-team",
|
|
5404
|
+
current_phase: "team-exec",
|
|
5405
|
+
});
|
|
5406
|
+
await writeJson(join(teamStateRoot, "team", "stale-source-team", "phase.json"), {
|
|
5407
|
+
current_phase: "team-exec",
|
|
5408
|
+
});
|
|
5409
|
+
const result = await dispatchCodexNativeHook({
|
|
5410
|
+
hook_event_name: "Stop",
|
|
5411
|
+
cwd,
|
|
5412
|
+
session_id: "sess-stale-source-team",
|
|
5413
|
+
}, { cwd });
|
|
5414
|
+
assert.equal(result.omxEventName, "stop");
|
|
5415
|
+
assert.equal(result.outputJson, null);
|
|
5416
|
+
}
|
|
5417
|
+
finally {
|
|
5418
|
+
if (typeof priorTeamStateRoot === "string")
|
|
5419
|
+
process.env.OMX_TEAM_STATE_ROOT = priorTeamStateRoot;
|
|
5420
|
+
else
|
|
5421
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
5422
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5423
|
+
}
|
|
5424
|
+
});
|
|
5096
5425
|
it("returns Stop continuation output from canonical team state rooted via OMX_TEAM_STATE_ROOT", async () => {
|
|
5097
5426
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-env-root-"));
|
|
5098
5427
|
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
@@ -5984,6 +6313,69 @@ exit 0
|
|
|
5984
6313
|
await rm(cwd, { recursive: true, force: true });
|
|
5985
6314
|
}
|
|
5986
6315
|
});
|
|
6316
|
+
it("blocks Codex App Stop when Ralph is marked complete without completion-audit evidence", async () => {
|
|
6317
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-missing-"));
|
|
6318
|
+
try {
|
|
6319
|
+
const sessionId = "sess-ralph-complete-missing";
|
|
6320
|
+
const statePath = join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json");
|
|
6321
|
+
await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd });
|
|
6322
|
+
await writeJson(statePath, {
|
|
6323
|
+
active: false,
|
|
6324
|
+
mode: "ralph",
|
|
6325
|
+
current_phase: "complete",
|
|
6326
|
+
session_id: sessionId,
|
|
6327
|
+
completed_at: "2026-05-10T12:00:00.000Z",
|
|
6328
|
+
});
|
|
6329
|
+
const result = await dispatchCodexNativeHook({
|
|
6330
|
+
hook_event_name: "Stop",
|
|
6331
|
+
cwd,
|
|
6332
|
+
session_id: sessionId,
|
|
6333
|
+
last_assistant_message: "Done. Ralph complete.",
|
|
6334
|
+
}, { cwd });
|
|
6335
|
+
assert.equal(result.omxEventName, "stop");
|
|
6336
|
+
assert.match(String(result.outputJson?.reason), /Ralph completion audit is missing required evidence/);
|
|
6337
|
+
assert.equal(result.outputJson?.stopReason, "ralph_completion_audit_missing_completion_audit");
|
|
6338
|
+
const reopened = JSON.parse(await readFile(statePath, "utf-8"));
|
|
6339
|
+
assert.equal(reopened.active, true);
|
|
6340
|
+
assert.equal(reopened.current_phase, "verifying");
|
|
6341
|
+
assert.equal(reopened.completion_audit_gate, "blocked");
|
|
6342
|
+
assert.equal(reopened.completion_audit_missing_reason, "missing_completion_audit");
|
|
6343
|
+
assert.equal(typeof reopened.completed_at, "undefined");
|
|
6344
|
+
}
|
|
6345
|
+
finally {
|
|
6346
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6347
|
+
}
|
|
6348
|
+
});
|
|
6349
|
+
it("allows Codex App Stop when complete Ralph state carries checklist and verification evidence", async () => {
|
|
6350
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-present-"));
|
|
6351
|
+
try {
|
|
6352
|
+
const sessionId = "sess-ralph-complete-present";
|
|
6353
|
+
await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd });
|
|
6354
|
+
await writeJson(join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json"), {
|
|
6355
|
+
active: false,
|
|
6356
|
+
mode: "ralph",
|
|
6357
|
+
current_phase: "complete",
|
|
6358
|
+
session_id: sessionId,
|
|
6359
|
+
completed_at: "2026-05-10T12:00:00.000Z",
|
|
6360
|
+
completion_audit: {
|
|
6361
|
+
passed: true,
|
|
6362
|
+
prompt_to_artifact_checklist: ["issue #2260 fixed", "tests added"],
|
|
6363
|
+
verification_evidence: ["node --test dist/scripts/__tests__/codex-native-hook.test.js"],
|
|
6364
|
+
},
|
|
6365
|
+
});
|
|
6366
|
+
const result = await dispatchCodexNativeHook({
|
|
6367
|
+
hook_event_name: "Stop",
|
|
6368
|
+
cwd,
|
|
6369
|
+
session_id: sessionId,
|
|
6370
|
+
last_assistant_message: "Done with completion audit evidence recorded.",
|
|
6371
|
+
}, { cwd });
|
|
6372
|
+
assert.equal(result.omxEventName, "stop");
|
|
6373
|
+
assert.equal(result.outputJson, null);
|
|
6374
|
+
}
|
|
6375
|
+
finally {
|
|
6376
|
+
await rm(cwd, { recursive: true, force: true });
|
|
6377
|
+
}
|
|
6378
|
+
});
|
|
5987
6379
|
it("returns Stop continuation output while Ralph is active without an explicit session pin", async () => {
|
|
5988
6380
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-"));
|
|
5989
6381
|
try {
|