oh-my-codex 0.13.2 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +5 -5
- package/Cargo.toml +1 -1
- package/README.md +14 -8
- package/crates/omx-explore/src/main.rs +94 -1
- package/crates/omx-sparkshell/src/codex_bridge.rs +59 -12
- package/crates/omx-sparkshell/tests/execution.rs +48 -0
- package/dist/autoresearch/__tests__/skill-validation.test.d.ts +2 -0
- package/dist/autoresearch/__tests__/skill-validation.test.d.ts.map +1 -0
- package/dist/autoresearch/__tests__/skill-validation.test.js +91 -0
- package/dist/autoresearch/__tests__/skill-validation.test.js.map +1 -0
- package/dist/autoresearch/skill-validation.d.ts +13 -0
- package/dist/autoresearch/skill-validation.d.ts.map +1 -0
- package/dist/autoresearch/skill-validation.js +165 -0
- package/dist/autoresearch/skill-validation.js.map +1 -0
- package/dist/catalog/__tests__/schema.test.js +6 -0
- package/dist/catalog/__tests__/schema.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +236 -273
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch.test.js +64 -653
- package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +33 -1
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +18 -2
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/nested-help-routing.test.js +2 -1
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +5 -0
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.d.ts +2 -0
- package/dist/cli/__tests__/question.test.d.ts.map +1 -0
- package/dist/cli/__tests__/question.test.js +166 -0
- package/dist/cli/__tests__/question.test.js.map +1 -0
- package/dist/cli/__tests__/session-search-help.test.js +1 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +32 -7
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +8 -6
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -0
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +23 -0
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +65 -5
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +360 -26
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/autoresearch-guided.d.ts +24 -7
- package/dist/cli/autoresearch-guided.d.ts.map +1 -1
- package/dist/cli/autoresearch-guided.js +189 -130
- package/dist/cli/autoresearch-guided.js.map +1 -1
- package/dist/cli/autoresearch.d.ts +3 -2
- package/dist/cli/autoresearch.d.ts.map +1 -1
- package/dist/cli/autoresearch.js +29 -305
- package/dist/cli/autoresearch.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +43 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +18 -3
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +15 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/question.d.ts +3 -0
- package/dist/cli/question.d.ts.map +1 -0
- package/dist/cli/question.js +182 -0
- package/dist/cli/question.js.map +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +25 -3
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/sparkshell.d.ts.map +1 -1
- package/dist/cli/sparkshell.js +11 -1
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +159 -394
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +3 -1
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/cli/update.d.ts +37 -9
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +204 -26
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +51 -14
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +35 -10
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +1 -0
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +61 -7
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/analyze-routing-contract.test.js +22 -13
- package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/anti-slop-workflow.test.js +3 -3
- package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +56 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +2 -2
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +51 -5
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js +43 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js +38 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +2 -2
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +308 -17
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +570 -2
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +717 -16
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +25 -0
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +894 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +34 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +132 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js +22 -4
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +4 -2
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts +1 -0
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js +19 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +28 -0
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js +5 -4
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-team-routing.test.js +2 -2
- package/dist/hooks/__tests__/prompt-team-routing.test.js.map +1 -1
- package/dist/hooks/__tests__/triage-config.test.d.ts +2 -0
- package/dist/hooks/__tests__/triage-config.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/triage-config.test.js +211 -0
- package/dist/hooks/__tests__/triage-config.test.js.map +1 -0
- package/dist/hooks/__tests__/triage-heuristic.test.d.ts +2 -0
- package/dist/hooks/__tests__/triage-heuristic.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/triage-heuristic.test.js +230 -0
- package/dist/hooks/__tests__/triage-heuristic.test.js.map +1 -0
- package/dist/hooks/__tests__/triage-state.test.d.ts +2 -0
- package/dist/hooks/__tests__/triage-state.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/triage-state.test.js +426 -0
- package/dist/hooks/__tests__/triage-state.test.js.map +1 -0
- package/dist/hooks/keyword-detector.d.ts +26 -7
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +97 -26
- 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 +16 -9
- 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 +28 -1
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/triage-config.d.ts +33 -0
- package/dist/hooks/triage-config.d.ts.map +1 -0
- package/dist/hooks/triage-config.js +87 -0
- package/dist/hooks/triage-config.js.map +1 -0
- package/dist/hooks/triage-heuristic.d.ts +20 -0
- package/dist/hooks/triage-heuristic.d.ts.map +1 -0
- package/dist/hooks/triage-heuristic.js +210 -0
- package/dist/hooks/triage-heuristic.js.map +1 -0
- package/dist/hooks/triage-state.d.ts +63 -0
- package/dist/hooks/triage-state.d.ts.map +1 -0
- package/dist/hooks/triage-state.js +138 -0
- package/dist/hooks/triage-state.js.map +1 -0
- package/dist/hud/__tests__/reconcile.test.js +20 -0
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/reconcile.d.ts +1 -0
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +2 -1
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +5 -24
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +127 -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 +3 -11
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-server.d.ts +25 -0
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +41 -0
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/modes/__tests__/base-ralph-contract.test.js +15 -0
- package/dist/modes/__tests__/base-ralph-contract.test.js.map +1 -1
- package/dist/modes/base.d.ts +1 -0
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +22 -6
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/index.test.js +75 -0
- package/dist/notifications/__tests__/index.test.js.map +1 -1
- package/dist/notifications/__tests__/session-status.test.js +90 -0
- package/dist/notifications/__tests__/session-status.test.js.map +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +39 -22
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/session-status.d.ts +2 -0
- package/dist/notifications/session-status.d.ts.map +1 -1
- package/dist/notifications/session-status.js +19 -4
- package/dist/notifications/session-status.js.map +1 -1
- package/dist/openclaw/index.d.ts +5 -3
- package/dist/openclaw/index.d.ts.map +1 -1
- package/dist/openclaw/index.js +5 -3
- package/dist/openclaw/index.js.map +1 -1
- package/dist/question/__tests__/client.test.d.ts +2 -0
- package/dist/question/__tests__/client.test.d.ts.map +1 -0
- package/dist/question/__tests__/client.test.js +70 -0
- package/dist/question/__tests__/client.test.js.map +1 -0
- package/dist/question/__tests__/deep-interview.test.d.ts +2 -0
- package/dist/question/__tests__/deep-interview.test.d.ts.map +1 -0
- package/dist/question/__tests__/deep-interview.test.js +118 -0
- package/dist/question/__tests__/deep-interview.test.js.map +1 -0
- package/dist/question/__tests__/policy.test.d.ts +2 -0
- package/dist/question/__tests__/policy.test.d.ts.map +1 -0
- package/dist/question/__tests__/policy.test.js +107 -0
- package/dist/question/__tests__/policy.test.js.map +1 -0
- package/dist/question/__tests__/renderer.test.d.ts +2 -0
- package/dist/question/__tests__/renderer.test.d.ts.map +1 -0
- package/dist/question/__tests__/renderer.test.js +238 -0
- package/dist/question/__tests__/renderer.test.js.map +1 -0
- package/dist/question/__tests__/state.test.d.ts +2 -0
- package/dist/question/__tests__/state.test.d.ts.map +1 -0
- package/dist/question/__tests__/state.test.js +75 -0
- package/dist/question/__tests__/state.test.js.map +1 -0
- package/dist/question/__tests__/types.test.d.ts +2 -0
- package/dist/question/__tests__/types.test.d.ts.map +1 -0
- package/dist/question/__tests__/types.test.js +44 -0
- package/dist/question/__tests__/types.test.js.map +1 -0
- package/dist/question/__tests__/ui.test.d.ts +2 -0
- package/dist/question/__tests__/ui.test.d.ts.map +1 -0
- package/dist/question/__tests__/ui.test.js +169 -0
- package/dist/question/__tests__/ui.test.js.map +1 -0
- package/dist/question/client.d.ts +54 -0
- package/dist/question/client.d.ts.map +1 -0
- package/dist/question/client.js +77 -0
- package/dist/question/client.js.map +1 -0
- package/dist/question/deep-interview.d.ts +30 -0
- package/dist/question/deep-interview.d.ts.map +1 -0
- package/dist/question/deep-interview.js +118 -0
- package/dist/question/deep-interview.js.map +1 -0
- package/dist/question/policy.d.ts +18 -0
- package/dist/question/policy.d.ts.map +1 -0
- package/dist/question/policy.js +77 -0
- package/dist/question/policy.js.map +1 -0
- package/dist/question/renderer.d.ts +20 -0
- package/dist/question/renderer.d.ts.map +1 -0
- package/dist/question/renderer.js +190 -0
- package/dist/question/renderer.js.map +1 -0
- package/dist/question/state.d.ts +19 -0
- package/dist/question/state.d.ts.map +1 -0
- package/dist/question/state.js +108 -0
- package/dist/question/state.js.map +1 -0
- package/dist/question/types.d.ts +66 -0
- package/dist/question/types.d.ts.map +1 -0
- package/dist/question/types.js +82 -0
- package/dist/question/types.js.map +1 -0
- package/dist/question/ui.d.ts +38 -0
- package/dist/question/ui.d.ts.map +1 -0
- package/dist/question/ui.js +321 -0
- package/dist/question/ui.js.map +1 -0
- package/dist/ralph/contract.d.ts +1 -1
- package/dist/ralph/contract.d.ts.map +1 -1
- package/dist/ralph/contract.js +4 -1
- package/dist/ralph/contract.js.map +1 -1
- package/dist/ralplan/runtime.js +1 -1
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/__tests__/run-loop.test.d.ts +2 -0
- package/dist/runtime/__tests__/run-loop.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/run-loop.test.js +35 -0
- package/dist/runtime/__tests__/run-loop.test.js.map +1 -0
- package/dist/runtime/__tests__/run-outcome.test.d.ts +2 -0
- package/dist/runtime/__tests__/run-outcome.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/run-outcome.test.js +102 -0
- package/dist/runtime/__tests__/run-outcome.test.js.map +1 -0
- package/dist/runtime/__tests__/run-state.test.d.ts +2 -0
- package/dist/runtime/__tests__/run-state.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/run-state.test.js +37 -0
- package/dist/runtime/__tests__/run-state.test.js.map +1 -0
- package/dist/runtime/run-loop.d.ts +45 -0
- package/dist/runtime/run-loop.d.ts.map +1 -0
- package/dist/runtime/run-loop.js +51 -0
- package/dist/runtime/run-loop.js.map +1 -0
- package/dist/runtime/run-outcome.d.ts +46 -0
- package/dist/runtime/run-outcome.d.ts.map +1 -0
- package/dist/runtime/run-outcome.js +285 -0
- package/dist/runtime/run-outcome.js.map +1 -0
- package/dist/runtime/run-state.d.ts +40 -0
- package/dist/runtime/run-state.d.ts.map +1 -0
- package/dist/runtime/run-state.js +120 -0
- package/dist/runtime/run-state.js.map +1 -0
- package/dist/runtime/terminal-lifecycle.d.ts +11 -0
- package/dist/runtime/terminal-lifecycle.d.ts.map +1 -0
- package/dist/runtime/terminal-lifecycle.js +52 -0
- package/dist/runtime/terminal-lifecycle.js.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +1459 -126
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/postinstall.test.d.ts +2 -0
- package/dist/scripts/__tests__/postinstall.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/postinstall.test.js +178 -0
- package/dist/scripts/__tests__/postinstall.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts +3 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +308 -61
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +81 -2
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts +27 -0
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +83 -20
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.js +64 -38
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook.js +15 -5
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/postinstall.d.ts +22 -0
- package/dist/scripts/postinstall.d.ts.map +1 -0
- package/dist/scripts/postinstall.js +105 -0
- package/dist/scripts/postinstall.js.map +1 -0
- package/dist/scripts/sync-prompt-guidance-fragments.js +5 -0
- package/dist/scripts/sync-prompt-guidance-fragments.js.map +1 -1
- package/dist/state/__tests__/operations-ralph-phase.test.js +21 -0
- package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +18 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +11 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +15 -0
- package/dist/state/operations.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +14 -1
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +3 -1
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/team/__tests__/followup-planner.test.js +15 -0
- package/dist/team/__tests__/followup-planner.test.js.map +1 -1
- package/dist/team/__tests__/role-router.test.js +47 -0
- package/dist/team/__tests__/role-router.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +108 -2
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/followup-planner.d.ts.map +1 -1
- package/dist/team/followup-planner.js +31 -9
- package/dist/team/followup-planner.js.map +1 -1
- package/dist/team/role-router.d.ts.map +1 -1
- package/dist/team/role-router.js +73 -0
- package/dist/team/role-router.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +18 -4
- package/dist/team/runtime.js.map +1 -1
- package/dist/utils/__tests__/dep-versions.test.js +25 -8
- package/dist/utils/__tests__/dep-versions.test.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +45 -0
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +22 -7
- package/dist/utils/paths.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/package.json +4 -2
- package/prompts/architect.md +4 -0
- package/prompts/code-reviewer.md +3 -0
- package/prompts/dependency-expert.md +3 -0
- package/prompts/executor.md +5 -0
- package/prompts/explore.md +2 -0
- package/prompts/planner.md +5 -0
- package/prompts/product-analyst.md +8 -8
- package/prompts/researcher.md +78 -30
- package/prompts/verifier.md +4 -0
- package/skills/autoresearch/SKILL.md +68 -0
- package/skills/code-review/SKILL.md +94 -28
- package/skills/deep-interview/SKILL.md +100 -9
- package/skills/help/SKILL.md +3 -1
- package/skills/ralplan/SKILL.md +1 -0
- package/skills/team/SKILL.md +1 -0
- package/skills/ultrawork/SKILL.md +1 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +2373 -692
- package/src/scripts/__tests__/postinstall.test.ts +210 -0
- package/src/scripts/codex-native-hook.ts +365 -66
- package/src/scripts/notify-fallback-watcher.ts +92 -2
- package/src/scripts/notify-hook/auto-nudge.ts +89 -20
- package/src/scripts/notify-hook/managed-tmux.ts +70 -31
- package/src/scripts/notify-hook/ralph-session-resume.ts +1 -1
- package/src/scripts/notify-hook.ts +23 -5
- package/src/scripts/postinstall-bootstrap.js +23 -0
- package/src/scripts/postinstall.ts +161 -0
- package/src/scripts/sync-prompt-guidance-fragments.ts +4 -0
- package/templates/AGENTS.md +48 -37
- package/templates/catalog-manifest.json +7 -0
- package/templates/model-instructions/explore-lightweight-AGENTS.md +11 -0
- package/templates/model-instructions/sparkshell-lightweight-AGENTS.md +10 -0
|
@@ -122,7 +122,7 @@ if [[ "\$cmd" == "list-panes" ]]; then
|
|
|
122
122
|
esac
|
|
123
123
|
done
|
|
124
124
|
if [[ -n "\$target" && "\$target" == "${'${OMX_TEST_TMUX_SESSION_NAME:-devsess}'}" ]]; then
|
|
125
|
-
printf '%%99\tnode\tcodex --model gpt-5\n'
|
|
125
|
+
printf '%%99\t1\tnode\tcodex --model gpt-5\n'
|
|
126
126
|
exit 0
|
|
127
127
|
fi
|
|
128
128
|
echo "%1 12345"
|
|
@@ -375,6 +375,7 @@ describe('notify-hook auto-nudge', () => {
|
|
|
375
375
|
await withTempWorkingDir(async (cwd) => {
|
|
376
376
|
const omxDir = join(cwd, '.omx');
|
|
377
377
|
const stateDir = join(omxDir, 'state');
|
|
378
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
378
379
|
const logsDir = join(omxDir, 'logs');
|
|
379
380
|
const codexHome = join(cwd, 'codex-home');
|
|
380
381
|
const fakeBinDir = join(cwd, 'fake-bin');
|
|
@@ -382,6 +383,7 @@ describe('notify-hook auto-nudge', () => {
|
|
|
382
383
|
const managedSessionName = buildTmuxSessionName(cwd, 'sess-managed');
|
|
383
384
|
await mkdir(logsDir, { recursive: true });
|
|
384
385
|
await mkdir(stateDir, { recursive: true });
|
|
386
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
385
387
|
await mkdir(codexHome, { recursive: true });
|
|
386
388
|
await mkdir(fakeBinDir, { recursive: true });
|
|
387
389
|
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
@@ -506,6 +508,7 @@ describe('notify-hook auto-nudge', () => {
|
|
|
506
508
|
await withTempWorkingDir(async (cwd) => {
|
|
507
509
|
const omxDir = join(cwd, '.omx');
|
|
508
510
|
const stateDir = join(omxDir, 'state');
|
|
511
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
509
512
|
const logsDir = join(omxDir, 'logs');
|
|
510
513
|
const codexHome = join(cwd, 'codex-home');
|
|
511
514
|
const fakeBinDir = join(cwd, 'fake-bin');
|
|
@@ -513,13 +516,14 @@ describe('notify-hook auto-nudge', () => {
|
|
|
513
516
|
const managedSessionName = buildTmuxSessionName(cwd, 'sess-managed');
|
|
514
517
|
await mkdir(logsDir, { recursive: true });
|
|
515
518
|
await mkdir(stateDir, { recursive: true });
|
|
519
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
516
520
|
await mkdir(codexHome, { recursive: true });
|
|
517
521
|
await mkdir(fakeBinDir, { recursive: true });
|
|
518
522
|
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
519
523
|
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
520
524
|
});
|
|
521
525
|
await writeManagedSessionState(stateDir, cwd);
|
|
522
|
-
await writeJson(join(
|
|
526
|
+
await writeJson(join(sessionStateDir, 'ralph-state.json'), {
|
|
523
527
|
active: true,
|
|
524
528
|
tmux_pane_id: '%99',
|
|
525
529
|
});
|
|
@@ -569,7 +573,7 @@ if [[ "$cmd" == "list-panes" ]]; then
|
|
|
569
573
|
esac
|
|
570
574
|
done
|
|
571
575
|
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
572
|
-
printf "%%99\tsh\tbash\n%%100\tnode\tcodex --model gpt-5\n"
|
|
576
|
+
printf "%%99\t0\tsh\tbash\n%%100\t1\tnode\tcodex --model gpt-5\n"
|
|
573
577
|
exit 0
|
|
574
578
|
fi
|
|
575
579
|
echo "%1 12345"
|
|
@@ -587,23 +591,420 @@ exit 0
|
|
|
587
591
|
await writeFile(join(fakeBinDir, 'tmux'), fakeTmux);
|
|
588
592
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
589
593
|
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
590
|
-
'last-assistant-message': 'Keep going and finish the cleanup from here.',
|
|
594
|
+
'last-assistant-message': 'Keep going and finish the cleanup from here.',
|
|
595
|
+
}, {
|
|
596
|
+
TMUX_PANE: '',
|
|
597
|
+
});
|
|
598
|
+
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
599
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
600
|
+
assert.match(tmuxLog, defaultAutoNudgePattern('%100'), 'should upgrade anchored shell pane to sibling codex pane');
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
it('keeps a verified codex anchor from active mode state even when a sibling codex pane is focused', async () => {
|
|
604
|
+
await withTempWorkingDir(async (cwd) => {
|
|
605
|
+
const omxDir = join(cwd, '.omx');
|
|
606
|
+
const stateDir = join(omxDir, 'state');
|
|
607
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
608
|
+
const logsDir = join(omxDir, 'logs');
|
|
609
|
+
const codexHome = join(cwd, 'codex-home');
|
|
610
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
611
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
612
|
+
const managedSessionName = buildTmuxSessionName(cwd, 'sess-managed');
|
|
613
|
+
await mkdir(logsDir, { recursive: true });
|
|
614
|
+
await mkdir(stateDir, { recursive: true });
|
|
615
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
616
|
+
await mkdir(codexHome, { recursive: true });
|
|
617
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
618
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
619
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
620
|
+
});
|
|
621
|
+
await writeManagedSessionState(stateDir, cwd);
|
|
622
|
+
await writeJson(join(sessionStateDir, 'ralph-state.json'), {
|
|
623
|
+
active: true,
|
|
624
|
+
tmux_pane_id: '%99',
|
|
625
|
+
});
|
|
626
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
627
|
+
set -eu
|
|
628
|
+
echo "$@" >> "${tmuxLogPath}"
|
|
629
|
+
cmd="$1"
|
|
630
|
+
shift || true
|
|
631
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
632
|
+
target=""
|
|
633
|
+
format=""
|
|
634
|
+
while [[ "$#" -gt 0 ]]; do
|
|
635
|
+
case "$1" in
|
|
636
|
+
-p) shift ;;
|
|
637
|
+
-t) target="$2"; shift 2 ;;
|
|
638
|
+
*) format="$1"; shift ;;
|
|
639
|
+
esac
|
|
640
|
+
done
|
|
641
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%99" ]]; then
|
|
642
|
+
echo "codex"
|
|
643
|
+
exit 0
|
|
644
|
+
fi
|
|
645
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%99" ]]; then
|
|
646
|
+
echo "codex"
|
|
647
|
+
exit 0
|
|
648
|
+
fi
|
|
649
|
+
if [[ "$format" == "#{pane_in_mode}" && "$target" == "%99" ]]; then
|
|
650
|
+
echo "0"
|
|
651
|
+
exit 0
|
|
652
|
+
fi
|
|
653
|
+
if [[ "$format" == "#S" && "$target" == "%99" ]]; then
|
|
654
|
+
echo "${managedSessionName}"
|
|
655
|
+
exit 0
|
|
656
|
+
fi
|
|
657
|
+
exit 0
|
|
658
|
+
fi
|
|
659
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
660
|
+
target=""
|
|
661
|
+
while [[ "$#" -gt 0 ]]; do
|
|
662
|
+
case "$1" in
|
|
663
|
+
-t) target="$2"; shift 2 ;;
|
|
664
|
+
*) shift ;;
|
|
665
|
+
esac
|
|
666
|
+
done
|
|
667
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
668
|
+
printf "%%99\t0\tcodex\tcodex\\n%%100\t1\tcodex\tcodex\\n"
|
|
669
|
+
exit 0
|
|
670
|
+
fi
|
|
671
|
+
echo "%1 12345"
|
|
672
|
+
exit 0
|
|
673
|
+
fi
|
|
674
|
+
if [[ "$cmd" == "capture-pane" ]]; then
|
|
675
|
+
printf "How can I help?\\n› "
|
|
676
|
+
exit 0
|
|
677
|
+
fi
|
|
678
|
+
if [[ "$cmd" == "send-keys" ]]; then
|
|
679
|
+
exit 0
|
|
680
|
+
fi
|
|
681
|
+
exit 0
|
|
682
|
+
`;
|
|
683
|
+
await writeFile(join(fakeBinDir, 'tmux'), fakeTmux);
|
|
684
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
685
|
+
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
686
|
+
'last-assistant-message': 'Keep going and finish the cleanup from here.',
|
|
687
|
+
}, {
|
|
688
|
+
TMUX_PANE: '',
|
|
689
|
+
});
|
|
690
|
+
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
691
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
692
|
+
assert.match(tmuxLog, defaultAutoNudgePattern('%99'), 'should keep the verified codex anchor');
|
|
693
|
+
assert.doesNotMatch(tmuxLog, defaultAutoNudgePattern('%100'), 'should not jump to the focused sibling codex pane');
|
|
694
|
+
});
|
|
695
|
+
});
|
|
696
|
+
it('upgrades a node shell anchor from active mode state to the sibling codex pane', async () => {
|
|
697
|
+
await withTempWorkingDir(async (cwd) => {
|
|
698
|
+
const omxDir = join(cwd, '.omx');
|
|
699
|
+
const stateDir = join(omxDir, 'state');
|
|
700
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
701
|
+
const logsDir = join(omxDir, 'logs');
|
|
702
|
+
const codexHome = join(cwd, 'codex-home');
|
|
703
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
704
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
705
|
+
const managedSessionName = buildTmuxSessionName(cwd, 'sess-managed');
|
|
706
|
+
await mkdir(logsDir, { recursive: true });
|
|
707
|
+
await mkdir(stateDir, { recursive: true });
|
|
708
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
709
|
+
await mkdir(codexHome, { recursive: true });
|
|
710
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
711
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
712
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
713
|
+
});
|
|
714
|
+
await writeManagedSessionState(stateDir, cwd);
|
|
715
|
+
await writeJson(join(sessionStateDir, 'ralph-state.json'), {
|
|
716
|
+
active: true,
|
|
717
|
+
tmux_pane_id: '%99',
|
|
718
|
+
});
|
|
719
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
720
|
+
set -eu
|
|
721
|
+
echo "$@" >> "${tmuxLogPath}"
|
|
722
|
+
cmd="$1"
|
|
723
|
+
shift || true
|
|
724
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
725
|
+
target=""
|
|
726
|
+
format=""
|
|
727
|
+
while [[ "$#" -gt 0 ]]; do
|
|
728
|
+
case "$1" in
|
|
729
|
+
-p) shift ;;
|
|
730
|
+
-t) target="$2"; shift 2 ;;
|
|
731
|
+
*) format="$1"; shift ;;
|
|
732
|
+
esac
|
|
733
|
+
done
|
|
734
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%99" ]]; then
|
|
735
|
+
echo "node"
|
|
736
|
+
exit 0
|
|
737
|
+
fi
|
|
738
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%99" ]]; then
|
|
739
|
+
echo "bash"
|
|
740
|
+
exit 0
|
|
741
|
+
fi
|
|
742
|
+
if [[ "$format" == "#{pane_in_mode}" && "$target" == "%100" ]]; then
|
|
743
|
+
echo "0"
|
|
744
|
+
exit 0
|
|
745
|
+
fi
|
|
746
|
+
if [[ "$format" == "#S" && "$target" == "%99" ]]; then
|
|
747
|
+
echo "${managedSessionName}"
|
|
748
|
+
exit 0
|
|
749
|
+
fi
|
|
750
|
+
exit 0
|
|
751
|
+
fi
|
|
752
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
753
|
+
target=""
|
|
754
|
+
while [[ "$#" -gt 0 ]]; do
|
|
755
|
+
case "$1" in
|
|
756
|
+
-t) target="$2"; shift 2 ;;
|
|
757
|
+
*) shift ;;
|
|
758
|
+
esac
|
|
759
|
+
done
|
|
760
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
761
|
+
printf "%%99\t0\tnode\tbash\\n%%100\t1\tnode\tcodex --model gpt-5\\n"
|
|
762
|
+
exit 0
|
|
763
|
+
fi
|
|
764
|
+
echo "%1 12345"
|
|
765
|
+
exit 0
|
|
766
|
+
fi
|
|
767
|
+
if [[ "$cmd" == "capture-pane" ]]; then
|
|
768
|
+
printf "How can I help?\\n› "
|
|
769
|
+
exit 0
|
|
770
|
+
fi
|
|
771
|
+
if [[ "$cmd" == "send-keys" ]]; then
|
|
772
|
+
exit 0
|
|
773
|
+
fi
|
|
774
|
+
exit 0
|
|
775
|
+
`;
|
|
776
|
+
await writeFile(join(fakeBinDir, 'tmux'), fakeTmux);
|
|
777
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
778
|
+
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
779
|
+
'last-assistant-message': 'Keep going and finish the cleanup from here.',
|
|
780
|
+
}, {
|
|
781
|
+
TMUX_PANE: '',
|
|
782
|
+
});
|
|
783
|
+
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
784
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
785
|
+
assert.match(tmuxLog, defaultAutoNudgePattern('%100'), 'should upgrade the node shell anchor to the sibling codex pane');
|
|
786
|
+
assert.doesNotMatch(tmuxLog, defaultAutoNudgePattern('%99'), 'node shell anchor should not be retained');
|
|
787
|
+
});
|
|
788
|
+
});
|
|
789
|
+
it('upgrades a shell-degraded codex anchor from active mode state to the sibling codex pane', async () => {
|
|
790
|
+
await withTempWorkingDir(async (cwd) => {
|
|
791
|
+
const omxDir = join(cwd, '.omx');
|
|
792
|
+
const stateDir = join(omxDir, 'state');
|
|
793
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
794
|
+
const logsDir = join(omxDir, 'logs');
|
|
795
|
+
const codexHome = join(cwd, 'codex-home');
|
|
796
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
797
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
798
|
+
const managedSessionName = buildTmuxSessionName(cwd, 'sess-managed');
|
|
799
|
+
await mkdir(logsDir, { recursive: true });
|
|
800
|
+
await mkdir(stateDir, { recursive: true });
|
|
801
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
802
|
+
await mkdir(codexHome, { recursive: true });
|
|
803
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
804
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
805
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
806
|
+
});
|
|
807
|
+
await writeManagedSessionState(stateDir, cwd);
|
|
808
|
+
await writeJson(join(sessionStateDir, 'ralph-state.json'), {
|
|
809
|
+
active: true,
|
|
810
|
+
tmux_pane_id: '%99',
|
|
811
|
+
});
|
|
812
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
813
|
+
set -eu
|
|
814
|
+
echo "$@" >> "${tmuxLogPath}"
|
|
815
|
+
cmd="$1"
|
|
816
|
+
shift || true
|
|
817
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
818
|
+
target=""
|
|
819
|
+
format=""
|
|
820
|
+
while [[ "$#" -gt 0 ]]; do
|
|
821
|
+
case "$1" in
|
|
822
|
+
-p) shift ;;
|
|
823
|
+
-t) target="$2"; shift 2 ;;
|
|
824
|
+
*) format="$1"; shift ;;
|
|
825
|
+
esac
|
|
826
|
+
done
|
|
827
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%99" ]]; then
|
|
828
|
+
echo "bash"
|
|
829
|
+
exit 0
|
|
830
|
+
fi
|
|
831
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%99" ]]; then
|
|
832
|
+
echo "codex --model gpt-5"
|
|
833
|
+
exit 0
|
|
834
|
+
fi
|
|
835
|
+
if [[ "$format" == "#{pane_in_mode}" && "$target" == "%100" ]]; then
|
|
836
|
+
echo "0"
|
|
837
|
+
exit 0
|
|
838
|
+
fi
|
|
839
|
+
if [[ "$format" == "#S" && "$target" == "%99" ]]; then
|
|
840
|
+
echo "${managedSessionName}"
|
|
841
|
+
exit 0
|
|
842
|
+
fi
|
|
843
|
+
exit 0
|
|
844
|
+
fi
|
|
845
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
846
|
+
target=""
|
|
847
|
+
while [[ "$#" -gt 0 ]]; do
|
|
848
|
+
case "$1" in
|
|
849
|
+
-t) target="$2"; shift 2 ;;
|
|
850
|
+
*) shift ;;
|
|
851
|
+
esac
|
|
852
|
+
done
|
|
853
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
854
|
+
printf "%%99\t1\tbash\tcodex --model gpt-5\\n%%100\t0\tnode\tcodex --model gpt-5\\n"
|
|
855
|
+
exit 0
|
|
856
|
+
fi
|
|
857
|
+
echo "%1 12345"
|
|
858
|
+
exit 0
|
|
859
|
+
fi
|
|
860
|
+
if [[ "$cmd" == "capture-pane" ]]; then
|
|
861
|
+
printf "How can I help?\\n› "
|
|
862
|
+
exit 0
|
|
863
|
+
fi
|
|
864
|
+
if [[ "$cmd" == "send-keys" ]]; then
|
|
865
|
+
exit 0
|
|
866
|
+
fi
|
|
867
|
+
exit 0
|
|
868
|
+
`;
|
|
869
|
+
await writeFile(join(fakeBinDir, 'tmux'), fakeTmux);
|
|
870
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
871
|
+
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
872
|
+
'last-assistant-message': 'Keep going and finish the cleanup from here.',
|
|
873
|
+
}, {
|
|
874
|
+
TMUX_PANE: '',
|
|
875
|
+
});
|
|
876
|
+
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
877
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
878
|
+
assert.match(tmuxLog, defaultAutoNudgePattern('%100'), 'should upgrade the shell-degraded codex anchor to the sibling codex pane');
|
|
879
|
+
assert.doesNotMatch(tmuxLog, defaultAutoNudgePattern('%99'), 'shell-degraded codex anchor should not be retained');
|
|
880
|
+
});
|
|
881
|
+
});
|
|
882
|
+
it('fails closed when a shell-degraded codex anchor has no live sibling pane', async () => {
|
|
883
|
+
await withTempWorkingDir(async (cwd) => {
|
|
884
|
+
const omxDir = join(cwd, '.omx');
|
|
885
|
+
const stateDir = join(omxDir, 'state');
|
|
886
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
887
|
+
const logsDir = join(omxDir, 'logs');
|
|
888
|
+
const codexHome = join(cwd, 'codex-home');
|
|
889
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
890
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
891
|
+
const managedSessionName = buildTmuxSessionName(cwd, 'sess-managed');
|
|
892
|
+
await mkdir(logsDir, { recursive: true });
|
|
893
|
+
await mkdir(stateDir, { recursive: true });
|
|
894
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
895
|
+
await mkdir(codexHome, { recursive: true });
|
|
896
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
897
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
898
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
899
|
+
});
|
|
900
|
+
await writeManagedSessionState(stateDir, cwd);
|
|
901
|
+
await writeJson(join(sessionStateDir, 'ralph-state.json'), {
|
|
902
|
+
active: true,
|
|
903
|
+
tmux_pane_id: '%99',
|
|
904
|
+
});
|
|
905
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
906
|
+
set -eu
|
|
907
|
+
echo "$@" >> "${tmuxLogPath}"
|
|
908
|
+
cmd="$1"
|
|
909
|
+
shift || true
|
|
910
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
911
|
+
target=""
|
|
912
|
+
format=""
|
|
913
|
+
while [[ "$#" -gt 0 ]]; do
|
|
914
|
+
case "$1" in
|
|
915
|
+
-p) shift ;;
|
|
916
|
+
-t) target="$2"; shift 2 ;;
|
|
917
|
+
*) format="$1"; shift ;;
|
|
918
|
+
esac
|
|
919
|
+
done
|
|
920
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%99" ]]; then
|
|
921
|
+
echo "bash"
|
|
922
|
+
exit 0
|
|
923
|
+
fi
|
|
924
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%99" ]]; then
|
|
925
|
+
echo "codex --model gpt-5"
|
|
926
|
+
exit 0
|
|
927
|
+
fi
|
|
928
|
+
if [[ "$format" == "#S" && "$target" == "%99" ]]; then
|
|
929
|
+
echo "${managedSessionName}"
|
|
930
|
+
exit 0
|
|
931
|
+
fi
|
|
932
|
+
exit 0
|
|
933
|
+
fi
|
|
934
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
935
|
+
target=""
|
|
936
|
+
while [[ "$#" -gt 0 ]]; do
|
|
937
|
+
case "$1" in
|
|
938
|
+
-t) target="$2"; shift 2 ;;
|
|
939
|
+
*) shift ;;
|
|
940
|
+
esac
|
|
941
|
+
done
|
|
942
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
943
|
+
printf "%%99\t1\tbash\tcodex --model gpt-5\\n%%100\t0\tbash\tbash\\n"
|
|
944
|
+
exit 0
|
|
945
|
+
fi
|
|
946
|
+
echo "%1 12345"
|
|
947
|
+
exit 0
|
|
948
|
+
fi
|
|
949
|
+
if [[ "$cmd" == "capture-pane" ]]; then
|
|
950
|
+
printf "How can I help?\\n› "
|
|
951
|
+
exit 0
|
|
952
|
+
fi
|
|
953
|
+
if [[ "$cmd" == "send-keys" ]]; then
|
|
954
|
+
exit 0
|
|
955
|
+
fi
|
|
956
|
+
exit 0
|
|
957
|
+
`;
|
|
958
|
+
await writeFile(join(fakeBinDir, 'tmux'), fakeTmux);
|
|
959
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
960
|
+
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
961
|
+
'last-assistant-message': 'Keep going and finish the cleanup from here.',
|
|
962
|
+
}, {
|
|
963
|
+
TMUX_PANE: '',
|
|
964
|
+
});
|
|
965
|
+
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
966
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
967
|
+
assert.doesNotMatch(tmuxLog, defaultAutoNudgePattern('%99'), 'shell-degraded codex anchor should not be retained without a live sibling');
|
|
968
|
+
assert.doesNotMatch(tmuxLog, defaultAutoNudgePattern('%100'), 'no live sibling should keep auto-nudge from sending input');
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
it('still auto-nudges in team-worker context using the worker state root', async () => {
|
|
972
|
+
await withTempWorkingDir(async (cwd) => {
|
|
973
|
+
const workerStateRoot = join(cwd, 'leader-state-root');
|
|
974
|
+
const logsDir = join(cwd, '.omx', 'logs');
|
|
975
|
+
const codexHome = join(cwd, 'codex-home');
|
|
976
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
977
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
978
|
+
const managedSessionName = buildTmuxSessionName(cwd, 'sess-managed');
|
|
979
|
+
await mkdir(logsDir, { recursive: true });
|
|
980
|
+
await mkdir(workerStateRoot, { recursive: true });
|
|
981
|
+
await mkdir(codexHome, { recursive: true });
|
|
982
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
983
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
984
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
985
|
+
});
|
|
986
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
987
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
988
|
+
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
989
|
+
'last-assistant-message': 'I can continue with the worker follow-up from here.',
|
|
591
990
|
}, {
|
|
592
|
-
|
|
991
|
+
OMX_TEAM_WORKER: 'auto-nudge/worker-1',
|
|
992
|
+
OMX_TEAM_STATE_ROOT: workerStateRoot,
|
|
593
993
|
});
|
|
594
994
|
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
595
995
|
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
596
|
-
assert.match(tmuxLog, defaultAutoNudgePattern('%
|
|
996
|
+
assert.match(tmuxLog, defaultAutoNudgePattern('%99'), 'team-worker context should still send auto-nudge');
|
|
997
|
+
const nudgeStatePath = join(workerStateRoot, 'auto-nudge-state.json');
|
|
998
|
+
assert.ok(existsSync(nudgeStatePath), 'worker state root should receive auto-nudge state');
|
|
597
999
|
});
|
|
598
1000
|
});
|
|
599
|
-
it('still auto-nudges
|
|
1001
|
+
it('still auto-nudges from the stored worker pane when TMUX_PANE is missing and the worker pane looks shell-degraded', async () => {
|
|
600
1002
|
await withTempWorkingDir(async (cwd) => {
|
|
601
1003
|
const workerStateRoot = join(cwd, 'leader-state-root');
|
|
602
1004
|
const logsDir = join(cwd, '.omx', 'logs');
|
|
603
1005
|
const codexHome = join(cwd, 'codex-home');
|
|
604
1006
|
const fakeBinDir = join(cwd, 'fake-bin');
|
|
605
1007
|
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
606
|
-
const managedSessionName = buildTmuxSessionName(cwd, 'sess-managed');
|
|
607
1008
|
await mkdir(logsDir, { recursive: true });
|
|
608
1009
|
await mkdir(workerStateRoot, { recursive: true });
|
|
609
1010
|
await mkdir(codexHome, { recursive: true });
|
|
@@ -611,19 +1012,64 @@ exit 0
|
|
|
611
1012
|
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
612
1013
|
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
613
1014
|
});
|
|
614
|
-
await
|
|
1015
|
+
await writeJson(join(workerStateRoot, 'ralph-state.json'), {
|
|
1016
|
+
active: true,
|
|
1017
|
+
tmux_pane_id: '%99',
|
|
1018
|
+
});
|
|
1019
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
1020
|
+
set -eu
|
|
1021
|
+
echo "$@" >> "${tmuxLogPath}"
|
|
1022
|
+
cmd="$1"
|
|
1023
|
+
shift || true
|
|
1024
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
1025
|
+
target=""
|
|
1026
|
+
format=""
|
|
1027
|
+
while [[ "$#" -gt 0 ]]; do
|
|
1028
|
+
case "$1" in
|
|
1029
|
+
-p) shift ;;
|
|
1030
|
+
-t) target="$2"; shift 2 ;;
|
|
1031
|
+
*) format="$1"; shift ;;
|
|
1032
|
+
esac
|
|
1033
|
+
done
|
|
1034
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%99" ]]; then
|
|
1035
|
+
echo "bash"
|
|
1036
|
+
exit 0
|
|
1037
|
+
fi
|
|
1038
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%99" ]]; then
|
|
1039
|
+
echo "codex --model gpt-5"
|
|
1040
|
+
exit 0
|
|
1041
|
+
fi
|
|
1042
|
+
if [[ "$format" == "#{pane_in_mode}" && "$target" == "%99" ]]; then
|
|
1043
|
+
echo "0"
|
|
1044
|
+
exit 0
|
|
1045
|
+
fi
|
|
1046
|
+
exit 0
|
|
1047
|
+
fi
|
|
1048
|
+
if [[ "$cmd" == "capture-pane" ]]; then
|
|
1049
|
+
printf "How can I help?\\n› "
|
|
1050
|
+
exit 0
|
|
1051
|
+
fi
|
|
1052
|
+
if [[ "$cmd" == "send-keys" ]]; then
|
|
1053
|
+
exit 0
|
|
1054
|
+
fi
|
|
1055
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
1056
|
+
echo "%1 12345"
|
|
1057
|
+
exit 0
|
|
1058
|
+
fi
|
|
1059
|
+
exit 0
|
|
1060
|
+
`;
|
|
1061
|
+
await writeFile(join(fakeBinDir, 'tmux'), fakeTmux);
|
|
615
1062
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
616
1063
|
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
617
1064
|
'last-assistant-message': 'I can continue with the worker follow-up from here.',
|
|
618
1065
|
}, {
|
|
619
1066
|
OMX_TEAM_WORKER: 'auto-nudge/worker-1',
|
|
620
1067
|
OMX_TEAM_STATE_ROOT: workerStateRoot,
|
|
1068
|
+
TMUX_PANE: '',
|
|
621
1069
|
});
|
|
622
1070
|
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
623
1071
|
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
624
|
-
assert.match(tmuxLog, defaultAutoNudgePattern('%99'), '
|
|
625
|
-
const nudgeStatePath = join(workerStateRoot, 'auto-nudge-state.json');
|
|
626
|
-
assert.ok(existsSync(nudgeStatePath), 'worker state root should receive auto-nudge state');
|
|
1072
|
+
assert.match(tmuxLog, defaultAutoNudgePattern('%99'), 'worker fallback should keep using the stored pane when TMUX_PANE is absent');
|
|
627
1073
|
});
|
|
628
1074
|
});
|
|
629
1075
|
it('does not nudge when no stall pattern is present', async () => {
|
|
@@ -790,7 +1236,7 @@ if [[ "$cmd" == "list-panes" ]]; then
|
|
|
790
1236
|
esac
|
|
791
1237
|
done
|
|
792
1238
|
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
793
|
-
printf "%%99\t1\tsh\\n%%100\t0\tcodex --model gpt-5\\n"
|
|
1239
|
+
printf "%%99\t1\tsh\tbash\\n%%100\t0\tnode\tcodex --model gpt-5\\n"
|
|
794
1240
|
exit 0
|
|
795
1241
|
fi
|
|
796
1242
|
echo "%1 12345"
|
|
@@ -1152,7 +1598,7 @@ exit 0
|
|
|
1152
1598
|
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(join(cwd, 'tmux.log')));
|
|
1153
1599
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
1154
1600
|
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
1155
|
-
'input-messages': ['please use autopilot for this task'],
|
|
1601
|
+
'input-messages': ['please use $autopilot for this task'],
|
|
1156
1602
|
'last-assistant-message': 'Here is the plan I will follow.',
|
|
1157
1603
|
});
|
|
1158
1604
|
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
@@ -1246,6 +1692,7 @@ exit 0
|
|
|
1246
1692
|
const logsDir = join(omxDir, 'logs');
|
|
1247
1693
|
const codexHome = join(cwd, 'codex-home');
|
|
1248
1694
|
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1695
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
1249
1696
|
await mkdir(logsDir, { recursive: true });
|
|
1250
1697
|
await mkdir(stateDir, { recursive: true });
|
|
1251
1698
|
await mkdir(codexHome, { recursive: true });
|
|
@@ -1265,13 +1712,55 @@ exit 0
|
|
|
1265
1712
|
assert.equal(skillState.input_lock?.active, true);
|
|
1266
1713
|
assert.deepEqual(skillState.input_lock?.blocked_inputs, DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS);
|
|
1267
1714
|
assert.match(skillState.input_lock?.message || '', /Deep interview is active/i);
|
|
1268
|
-
const modeState = JSON.parse(await readFile(join(
|
|
1715
|
+
const modeState = JSON.parse(await readFile(join(sessionStateDir, 'deep-interview-state.json'), 'utf-8'));
|
|
1269
1716
|
assert.equal(modeState.active, true);
|
|
1270
1717
|
assert.equal(modeState.mode, 'deep-interview');
|
|
1271
1718
|
assert.equal(modeState.current_phase, 'intent-first');
|
|
1272
1719
|
assert.equal(modeState.input_lock?.active, true);
|
|
1273
1720
|
});
|
|
1274
1721
|
});
|
|
1722
|
+
it('releases deep-interview mode state on normal completion without waiting for later keyword input', async () => {
|
|
1723
|
+
await withTempWorkingDir(async (cwd) => {
|
|
1724
|
+
const omxDir = join(cwd, '.omx');
|
|
1725
|
+
const stateDir = join(omxDir, 'state');
|
|
1726
|
+
const logsDir = join(omxDir, 'logs');
|
|
1727
|
+
const codexHome = join(cwd, 'codex-home');
|
|
1728
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1729
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
1730
|
+
await mkdir(logsDir, { recursive: true });
|
|
1731
|
+
await mkdir(stateDir, { recursive: true });
|
|
1732
|
+
await mkdir(codexHome, { recursive: true });
|
|
1733
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
1734
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
1735
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
1736
|
+
});
|
|
1737
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(join(cwd, 'tmux.log')));
|
|
1738
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
1739
|
+
const activated = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
1740
|
+
'input-messages': ['please run a deep interview first'],
|
|
1741
|
+
'last-assistant-message': 'Round 1 | Target: Goal Clarity',
|
|
1742
|
+
});
|
|
1743
|
+
assert.equal(activated.status, 0, `activation hook failed: ${activated.stderr || activated.stdout}`);
|
|
1744
|
+
const completed = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
1745
|
+
'input-messages': ['continue'],
|
|
1746
|
+
'last-assistant-message': 'Interview completed. Final summary ready.',
|
|
1747
|
+
});
|
|
1748
|
+
assert.equal(completed.status, 0, `completion hook failed: ${completed.stderr || completed.stdout}`);
|
|
1749
|
+
const skillState = JSON.parse(await readFile(join(sessionStateDir, 'skill-active-state.json'), 'utf-8'));
|
|
1750
|
+
assert.equal(skillState.active, false);
|
|
1751
|
+
assert.equal(skillState.phase, 'completing');
|
|
1752
|
+
assert.equal(skillState.input_lock?.active, false);
|
|
1753
|
+
assert.equal(skillState.input_lock?.exit_reason, 'success');
|
|
1754
|
+
assert.ok(skillState.input_lock?.released_at);
|
|
1755
|
+
const modeState = JSON.parse(await readFile(join(sessionStateDir, 'deep-interview-state.json'), 'utf-8'));
|
|
1756
|
+
assert.equal(modeState.active, false);
|
|
1757
|
+
assert.equal(modeState.current_phase, 'completing');
|
|
1758
|
+
assert.ok(modeState.completed_at);
|
|
1759
|
+
assert.equal(modeState.input_lock?.active, false);
|
|
1760
|
+
assert.equal(modeState.input_lock?.exit_reason, 'success');
|
|
1761
|
+
assert.ok(modeState.input_lock?.released_at);
|
|
1762
|
+
});
|
|
1763
|
+
});
|
|
1275
1764
|
for (const blockedResponse of ['yes', 'y', 'proceed', 'continue', 'ok', 'sure', 'go ahead']) {
|
|
1276
1765
|
it(`blocks deep-interview auto-approval injection for "${blockedResponse}"`, async () => {
|
|
1277
1766
|
await withTempWorkingDir(async (cwd) => {
|
|
@@ -1402,6 +1891,156 @@ exit 0
|
|
|
1402
1891
|
assert.equal(tmuxLog.includes(`send-keys -t %99 -l ${NEXT_I_SHOULD_RESPONSE} [OMX_TMUX_INJECT]`), false);
|
|
1403
1892
|
});
|
|
1404
1893
|
});
|
|
1894
|
+
it('allows non-blocked custom deep-interview auto-nudge responses to continue', async () => {
|
|
1895
|
+
await withTempWorkingDir(async (cwd) => {
|
|
1896
|
+
const omxDir = join(cwd, '.omx');
|
|
1897
|
+
const stateDir = join(omxDir, 'state');
|
|
1898
|
+
const logsDir = join(omxDir, 'logs');
|
|
1899
|
+
const codexHome = join(cwd, 'codex-home');
|
|
1900
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1901
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1902
|
+
const capturePath = join(cwd, 'capture.txt');
|
|
1903
|
+
const customResponse = 'advance with the next interview question';
|
|
1904
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
1905
|
+
await mkdir(logsDir, { recursive: true });
|
|
1906
|
+
await mkdir(stateDir, { recursive: true });
|
|
1907
|
+
await mkdir(codexHome, { recursive: true });
|
|
1908
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
1909
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
1910
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0, response: customResponse },
|
|
1911
|
+
});
|
|
1912
|
+
await writeManagedSessionState(stateDir, cwd);
|
|
1913
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
1914
|
+
await writeJson(join(sessionStateDir, 'skill-active-state.json'), {
|
|
1915
|
+
version: 1,
|
|
1916
|
+
active: true,
|
|
1917
|
+
skill: 'deep-interview',
|
|
1918
|
+
keyword: 'deep interview',
|
|
1919
|
+
phase: 'executing',
|
|
1920
|
+
activated_at: '2026-02-25T00:00:00.000Z',
|
|
1921
|
+
updated_at: '2026-02-25T00:00:00.000Z',
|
|
1922
|
+
source: 'keyword-detector',
|
|
1923
|
+
input_lock: {
|
|
1924
|
+
active: true,
|
|
1925
|
+
scope: 'deep-interview-auto-approval',
|
|
1926
|
+
acquired_at: '2026-02-25T00:00:00.000Z',
|
|
1927
|
+
blocked_inputs: DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS,
|
|
1928
|
+
message: 'Deep interview is active; auto-approval shortcuts are blocked until the interview finishes.',
|
|
1929
|
+
},
|
|
1930
|
+
});
|
|
1931
|
+
await writeFile(capturePath, 'OpenAI Codex\n› ');
|
|
1932
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
1933
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
1934
|
+
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
1935
|
+
'last-assistant-message': 'Keep going and finish the cleanup.',
|
|
1936
|
+
}, {
|
|
1937
|
+
OMX_TEST_CAPTURE_FILE: capturePath,
|
|
1938
|
+
});
|
|
1939
|
+
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
1940
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1941
|
+
assert.match(tmuxLog, new RegExp(`send-keys -t %99 -l ${customResponse} \\[OMX_TMUX_INJECT\\]`), 'should allow a non-blocked continuation response during deep interview');
|
|
1942
|
+
});
|
|
1943
|
+
});
|
|
1944
|
+
it('keeps autoresearch active when assistant claims completion without validator evidence', async () => {
|
|
1945
|
+
await withTempWorkingDir(async (cwd) => {
|
|
1946
|
+
const omxDir = join(cwd, '.omx');
|
|
1947
|
+
const stateDir = join(omxDir, 'state');
|
|
1948
|
+
const logsDir = join(omxDir, 'logs');
|
|
1949
|
+
const codexHome = join(cwd, 'codex-home');
|
|
1950
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1951
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1952
|
+
await mkdir(logsDir, { recursive: true });
|
|
1953
|
+
await mkdir(stateDir, { recursive: true });
|
|
1954
|
+
await mkdir(codexHome, { recursive: true });
|
|
1955
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
1956
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
1957
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
1958
|
+
});
|
|
1959
|
+
await writeManagedSessionState(stateDir, cwd);
|
|
1960
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
1961
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
1962
|
+
await writeJson(join(sessionStateDir, 'skill-active-state.json'), {
|
|
1963
|
+
active: true,
|
|
1964
|
+
skill: 'autoresearch',
|
|
1965
|
+
keyword: '$autoresearch',
|
|
1966
|
+
phase: 'executing',
|
|
1967
|
+
source: 'keyword-detector',
|
|
1968
|
+
session_id: 'sess-managed',
|
|
1969
|
+
});
|
|
1970
|
+
await writeJson(join(sessionStateDir, 'autoresearch-state.json'), {
|
|
1971
|
+
active: true,
|
|
1972
|
+
mode: 'autoresearch',
|
|
1973
|
+
current_phase: 'executing',
|
|
1974
|
+
session_id: 'sess-managed',
|
|
1975
|
+
validation_mode: 'mission-validator-script',
|
|
1976
|
+
mission_validator_command: 'node scripts/validate.js',
|
|
1977
|
+
completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json',
|
|
1978
|
+
});
|
|
1979
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
1980
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
1981
|
+
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
1982
|
+
'last-assistant-message': 'All tests pass. Completed with summary.',
|
|
1983
|
+
});
|
|
1984
|
+
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
1985
|
+
const skillState = JSON.parse(await readFile(join(sessionStateDir, 'skill-active-state.json'), 'utf-8'));
|
|
1986
|
+
assert.equal(skillState.active, true);
|
|
1987
|
+
assert.equal(skillState.phase, 'executing');
|
|
1988
|
+
assert.equal(skillState.autoresearch_completion_reason, 'missing_or_invalid_completion_artifact');
|
|
1989
|
+
});
|
|
1990
|
+
});
|
|
1991
|
+
it('completes autoresearch when validator artifact passes', async () => {
|
|
1992
|
+
await withTempWorkingDir(async (cwd) => {
|
|
1993
|
+
const omxDir = join(cwd, '.omx');
|
|
1994
|
+
const stateDir = join(omxDir, 'state');
|
|
1995
|
+
const logsDir = join(omxDir, 'logs');
|
|
1996
|
+
const codexHome = join(cwd, 'codex-home');
|
|
1997
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1998
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1999
|
+
const specDir = join(cwd, '.omx', 'specs', 'autoresearch-demo');
|
|
2000
|
+
await mkdir(logsDir, { recursive: true });
|
|
2001
|
+
await mkdir(stateDir, { recursive: true });
|
|
2002
|
+
await mkdir(codexHome, { recursive: true });
|
|
2003
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
2004
|
+
await mkdir(specDir, { recursive: true });
|
|
2005
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
2006
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
2007
|
+
});
|
|
2008
|
+
await writeManagedSessionState(stateDir, cwd);
|
|
2009
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
2010
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
2011
|
+
await writeJson(join(sessionStateDir, 'skill-active-state.json'), {
|
|
2012
|
+
active: true,
|
|
2013
|
+
skill: 'autoresearch',
|
|
2014
|
+
keyword: '$autoresearch',
|
|
2015
|
+
phase: 'reviewing',
|
|
2016
|
+
source: 'keyword-detector',
|
|
2017
|
+
session_id: 'sess-managed',
|
|
2018
|
+
});
|
|
2019
|
+
await writeJson(join(sessionStateDir, 'autoresearch-state.json'), {
|
|
2020
|
+
active: true,
|
|
2021
|
+
mode: 'autoresearch',
|
|
2022
|
+
current_phase: 'reviewing',
|
|
2023
|
+
session_id: 'sess-managed',
|
|
2024
|
+
validation_mode: 'mission-validator-script',
|
|
2025
|
+
mission_validator_command: 'node scripts/validate.js',
|
|
2026
|
+
completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json',
|
|
2027
|
+
});
|
|
2028
|
+
await writeJson(join(specDir, 'completion.json'), {
|
|
2029
|
+
status: 'passed',
|
|
2030
|
+
passed: true,
|
|
2031
|
+
});
|
|
2032
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
2033
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2034
|
+
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
2035
|
+
'last-assistant-message': 'Completed with final summary after validator pass.',
|
|
2036
|
+
});
|
|
2037
|
+
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
2038
|
+
const skillState = JSON.parse(await readFile(join(sessionStateDir, 'skill-active-state.json'), 'utf-8'));
|
|
2039
|
+
assert.equal(skillState.active, false);
|
|
2040
|
+
assert.equal(skillState.phase, 'completing');
|
|
2041
|
+
assert.equal(skillState.autoresearch_completion_reason, 'validator_passed');
|
|
2042
|
+
});
|
|
2043
|
+
});
|
|
1405
2044
|
it('releases the deep-interview input lock on success', async () => {
|
|
1406
2045
|
await withTempWorkingDir(async (cwd) => {
|
|
1407
2046
|
const omxDir = join(cwd, '.omx');
|
|
@@ -1450,6 +2089,65 @@ exit 0
|
|
|
1450
2089
|
assert.equal(skillState.input_lock?.exit_reason, 'success');
|
|
1451
2090
|
});
|
|
1452
2091
|
});
|
|
2092
|
+
it('does not release deep-interview state from generic progress prose', async () => {
|
|
2093
|
+
await withTempWorkingDir(async (cwd) => {
|
|
2094
|
+
const omxDir = join(cwd, '.omx');
|
|
2095
|
+
const stateDir = join(omxDir, 'state');
|
|
2096
|
+
const logsDir = join(omxDir, 'logs');
|
|
2097
|
+
const codexHome = join(cwd, 'codex-home');
|
|
2098
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2099
|
+
await mkdir(logsDir, { recursive: true });
|
|
2100
|
+
await mkdir(stateDir, { recursive: true });
|
|
2101
|
+
await mkdir(codexHome, { recursive: true });
|
|
2102
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
2103
|
+
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
2104
|
+
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
2105
|
+
});
|
|
2106
|
+
await writeManagedSessionState(stateDir, cwd);
|
|
2107
|
+
const sessionStateDir = join(stateDir, 'sessions', 'sess-managed');
|
|
2108
|
+
await mkdir(sessionStateDir, { recursive: true });
|
|
2109
|
+
await writeJson(join(sessionStateDir, 'skill-active-state.json'), {
|
|
2110
|
+
version: 1,
|
|
2111
|
+
active: true,
|
|
2112
|
+
skill: 'deep-interview',
|
|
2113
|
+
keyword: 'deep interview',
|
|
2114
|
+
phase: 'executing',
|
|
2115
|
+
activated_at: '2026-02-25T00:00:00.000Z',
|
|
2116
|
+
updated_at: '2026-02-25T00:00:00.000Z',
|
|
2117
|
+
source: 'keyword-detector',
|
|
2118
|
+
input_lock: {
|
|
2119
|
+
active: true,
|
|
2120
|
+
scope: 'deep-interview-auto-approval',
|
|
2121
|
+
acquired_at: '2026-02-25T00:00:00.000Z',
|
|
2122
|
+
blocked_inputs: DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS,
|
|
2123
|
+
message: 'Deep interview is active; auto-approval shortcuts are blocked until the interview finishes.',
|
|
2124
|
+
},
|
|
2125
|
+
});
|
|
2126
|
+
await writeJson(join(sessionStateDir, 'deep-interview-state.json'), {
|
|
2127
|
+
active: true,
|
|
2128
|
+
mode: 'deep-interview',
|
|
2129
|
+
current_phase: 'intent-first',
|
|
2130
|
+
started_at: '2026-02-25T00:00:00.000Z',
|
|
2131
|
+
updated_at: '2026-02-25T00:00:00.000Z',
|
|
2132
|
+
});
|
|
2133
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(join(cwd, 'tmux.log')));
|
|
2134
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2135
|
+
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
2136
|
+
'last-assistant-message': 'Summary so far: done with the first round of questions.',
|
|
2137
|
+
});
|
|
2138
|
+
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
2139
|
+
const skillState = JSON.parse(await readFile(join(sessionStateDir, 'skill-active-state.json'), 'utf-8'));
|
|
2140
|
+
assert.equal(skillState.active, true);
|
|
2141
|
+
assert.notEqual(skillState.phase, 'completing');
|
|
2142
|
+
assert.equal(skillState.input_lock?.active, true);
|
|
2143
|
+
assert.equal(skillState.input_lock?.released_at || '', '');
|
|
2144
|
+
assert.equal(skillState.input_lock?.exit_reason || '', '');
|
|
2145
|
+
const modeState = JSON.parse(await readFile(join(sessionStateDir, 'deep-interview-state.json'), 'utf-8'));
|
|
2146
|
+
assert.equal(modeState.active, true);
|
|
2147
|
+
assert.equal(modeState.current_phase, 'intent-first');
|
|
2148
|
+
assert.equal(modeState.completed_at || '', '');
|
|
2149
|
+
});
|
|
2150
|
+
});
|
|
1453
2151
|
it('releases the deep-interview input lock on error', async () => {
|
|
1454
2152
|
await withTempWorkingDir(async (cwd) => {
|
|
1455
2153
|
const omxDir = join(cwd, '.omx');
|
|
@@ -1627,6 +2325,7 @@ exit 0
|
|
|
1627
2325
|
const codexHome = join(cwd, 'codex-home');
|
|
1628
2326
|
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1629
2327
|
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2328
|
+
const captureFile = join(cwd, 'capture-output.txt');
|
|
1630
2329
|
await mkdir(logsDir, { recursive: true });
|
|
1631
2330
|
await mkdir(stateDir, { recursive: true });
|
|
1632
2331
|
await mkdir(codexHome, { recursive: true });
|
|
@@ -1634,13 +2333,15 @@ exit 0
|
|
|
1634
2333
|
await writeJson(join(codexHome, '.omx-config.json'), {
|
|
1635
2334
|
autoNudge: { enabled: true, delaySec: 0, stallMs: 0 },
|
|
1636
2335
|
});
|
|
2336
|
+
await writeFile(captureFile, 'Here are the results.\nKeep going and finish the implementation.\n› ');
|
|
1637
2337
|
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
1638
2338
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
1639
2339
|
const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
|
|
1640
|
-
'last-assistant-message': '
|
|
2340
|
+
'last-assistant-message': 'clean output with no stall',
|
|
1641
2341
|
}, {
|
|
1642
2342
|
TMUX_PANE: '', // No pane available
|
|
1643
2343
|
TMUX: '',
|
|
2344
|
+
OMX_TEST_CAPTURE_FILE: captureFile,
|
|
1644
2345
|
});
|
|
1645
2346
|
assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
|
|
1646
2347
|
if (existsSync(tmuxLogPath)) {
|