oh-my-codex 0.18.13 → 0.18.15
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 +6 -6
- package/Cargo.toml +1 -1
- package/README.md +41 -18
- package/dist/agents/__tests__/native-config.test.js +62 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +15 -2
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/fsm.test.js +7 -0
- package/dist/autopilot/__tests__/fsm.test.js.map +1 -1
- package/dist/autopilot/__tests__/planner-routing.test.d.ts +2 -0
- package/dist/autopilot/__tests__/planner-routing.test.d.ts.map +1 -0
- package/dist/autopilot/__tests__/planner-routing.test.js +74 -0
- package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js +315 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/completion-gate.js +1 -1
- package/dist/autopilot/completion-gate.js.map +1 -1
- package/dist/autopilot/fsm.d.ts +2 -2
- package/dist/autopilot/fsm.d.ts.map +1 -1
- package/dist/autopilot/fsm.js +3 -0
- package/dist/autopilot/fsm.js.map +1 -1
- package/dist/autopilot/planner-routing.d.ts +21 -0
- package/dist/autopilot/planner-routing.d.ts.map +1 -0
- package/dist/autopilot/planner-routing.js +59 -0
- package/dist/autopilot/planner-routing.js.map +1 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +30 -0
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/autoresearch/goal.d.ts.map +1 -1
- package/dist/autoresearch/goal.js +10 -1
- package/dist/autoresearch/goal.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-goal.test.js +33 -0
- package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +3 -0
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +30 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js +158 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js +33 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-spark-routing.test.js +13 -0
- package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +132 -2
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +5 -0
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +40 -0
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/nested-help-routing.test.js +4 -0
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +7 -1
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/performance-goal.test.js +22 -0
- package/dist/cli/__tests__/performance-goal.test.js.map +1 -1
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts +2 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts.map +1 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +64 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -0
- package/dist/cli/__tests__/resume.test.js +96 -0
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +162 -0
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/session-search.test.js +36 -0
- package/dist/cli/__tests__/session-search.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +70 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +33 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +17 -3
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +37 -0
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +81 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/url.test.d.ts +2 -0
- package/dist/cli/__tests__/url.test.d.ts.map +1 -0
- package/dist/cli/__tests__/url.test.js +82 -0
- package/dist/cli/__tests__/url.test.js.map +1 -0
- package/dist/cli/autoresearch-goal.d.ts.map +1 -1
- package/dist/cli/autoresearch-goal.js +2 -1
- package/dist/cli/autoresearch-goal.js.map +1 -1
- package/dist/cli/autoresearch-intake.d.ts.map +1 -1
- package/dist/cli/autoresearch-intake.js +2 -0
- package/dist/cli/autoresearch-intake.js.map +1 -1
- package/dist/cli/doctor.d.ts +39 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +460 -40
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +71 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/performance-goal.d.ts.map +1 -1
- package/dist/cli/performance-goal.js +4 -2
- package/dist/cli/performance-goal.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +1 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +88 -67
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/project-runtime-codex-homes.d.ts +2 -0
- package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -1
- package/dist/cli/project-runtime-codex-homes.js +112 -5
- package/dist/cli/project-runtime-codex-homes.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +15 -6
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +5 -1
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +23 -4
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/url.d.ts +8 -0
- package/dist/cli/url.d.ts.map +1 -0
- package/dist/cli/url.js +69 -0
- package/dist/cli/url.js.map +1 -0
- package/dist/config/__tests__/codex-hooks.test.js +20 -4
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +21 -1
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +1 -4
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/models.d.ts +5 -0
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +23 -0
- package/dist/config/models.js.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts +2 -0
- package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.js +10 -0
- package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +7 -0
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +46 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +100 -0
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +75 -0
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +225 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +75 -0
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +563 -0
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +187 -12
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +225 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +75 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +4 -0
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +137 -2
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +107 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +17 -0
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +21 -7
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +9 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +43 -5
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/types.d.ts +6 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +24 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +53 -0
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +2 -0
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +32 -6
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +11 -5
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/__tests__/base-autopilot-gates.test.js +48 -0
- package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +1 -0
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/http-client.test.js +9 -0
- package/dist/notifications/__tests__/http-client.test.js.map +1 -1
- package/dist/notifications/http-client.js +1 -1
- package/dist/notifications/http-client.js.map +1 -1
- package/dist/performance-goal/artifacts.d.ts.map +1 -1
- package/dist/performance-goal/artifacts.js +10 -1
- package/dist/performance-goal/artifacts.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +112 -0
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +23 -0
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +133 -21
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +1251 -13
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +601 -24
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts +1 -0
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +279 -86
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +87 -8
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/run-compiled-ci.d.ts +2 -0
- package/dist/scripts/run-compiled-ci.d.ts.map +1 -0
- package/dist/scripts/run-compiled-ci.js +77 -0
- package/dist/scripts/run-compiled-ci.js.map +1 -0
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +1 -0
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/session-history/search.d.ts.map +1 -1
- package/dist/session-history/search.js +24 -9
- package/dist/session-history/search.js.map +1 -1
- package/dist/sidecar/__tests__/args.test.d.ts +2 -0
- package/dist/sidecar/__tests__/args.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/args.test.js +18 -0
- package/dist/sidecar/__tests__/args.test.js.map +1 -0
- package/dist/sidecar/index.d.ts.map +1 -1
- package/dist/sidecar/index.js +12 -4
- package/dist/sidecar/index.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +107 -3
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +57 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +22 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +57 -1
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +1468 -110
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +63 -0
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +24 -1
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +512 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +42 -9
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/model-contract.d.ts +16 -0
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +41 -1
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +116 -30
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +9 -1
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/types.d.ts +2 -0
- package/dist/team/state/types.d.ts.map +1 -1
- package/dist/team/state/types.js.map +1 -1
- package/dist/team/state.d.ts +3 -0
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +20 -0
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts +38 -3
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +275 -36
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +26 -1
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +388 -0
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +3 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +23 -0
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +275 -12
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/url-reader/__tests__/url-reader.test.d.ts +2 -0
- package/dist/url-reader/__tests__/url-reader.test.d.ts.map +1 -0
- package/dist/url-reader/__tests__/url-reader.test.js +433 -0
- package/dist/url-reader/__tests__/url-reader.test.js.map +1 -0
- package/dist/url-reader/index.d.ts +4 -0
- package/dist/url-reader/index.d.ts.map +1 -0
- package/dist/url-reader/index.js +522 -0
- package/dist/url-reader/index.js.map +1 -0
- package/dist/url-reader/types.d.ts +41 -0
- package/dist/url-reader/types.d.ts.map +1 -0
- package/dist/url-reader/types.js +2 -0
- package/dist/url-reader/types.js.map +1 -0
- package/dist/utils/__tests__/agents-md.test.js +39 -1
- package/dist/utils/__tests__/agents-md.test.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +28 -0
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/agents-md.d.ts +4 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +28 -0
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/agents-model-table.d.ts +7 -3
- package/dist/utils/agents-model-table.d.ts.map +1 -1
- package/dist/utils/agents-model-table.js +16 -8
- package/dist/utils/agents-model-table.js.map +1 -1
- package/dist/utils/platform-command.d.ts +7 -1
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +12 -1
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +4 -0
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/vscode/__tests__/index.test.d.ts +2 -0
- package/dist/vscode/__tests__/index.test.d.ts.map +1 -0
- package/dist/vscode/__tests__/index.test.js +236 -0
- package/dist/vscode/__tests__/index.test.js.map +1 -0
- package/dist/vscode/index.d.ts +76 -0
- package/dist/vscode/index.d.ts.map +1 -0
- package/dist/vscode/index.js +196 -0
- package/dist/vscode/index.js.map +1 -0
- package/package.json +3 -3
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +59 -8
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +9 -5
- package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +3 -0
- package/plugins/oh-my-codex/skills/performance-goal/SKILL.md +3 -0
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +22 -4
- package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +61 -0
- package/skills/autopilot/SKILL.md +9 -5
- package/skills/autoresearch-goal/SKILL.md +3 -0
- package/skills/ecomode/SKILL.md +1 -1
- package/skills/ecomode/references/agent-tiers.md +61 -0
- package/skills/performance-goal/SKILL.md +3 -0
- package/skills/ralplan/SKILL.md +1 -1
- package/skills/ultragoal/SKILL.md +22 -4
- package/skills/ultrawork/SKILL.md +2 -2
- package/skills/ultrawork/references/agent-tiers.md +61 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +1560 -181
- package/src/scripts/codex-native-hook.ts +663 -24
- package/src/scripts/notify-hook/team-leader-nudge.ts +298 -91
- package/src/scripts/notify-hook/team-tmux-guard.ts +92 -8
- package/src/scripts/notify-hook/tmux-injection.ts +1 -1
- package/src/scripts/run-compiled-ci.ts +85 -0
- package/src/scripts/sync-plugin-mirror.ts +1 -0
- package/templates/AGENTS.md +6 -0
package/dist/cli/doctor.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* omx doctor - Validate oh-my-codex installation
|
|
3
3
|
*/
|
|
4
|
-
import { existsSync, readFileSync } from "fs";
|
|
5
|
-
import { mkdtemp, readdir, readFile, rm } from "fs/promises";
|
|
4
|
+
import { constants, existsSync, readFileSync } from "fs";
|
|
5
|
+
import { access, chown, lstat, mkdtemp, readdir, readFile, rm } from "fs/promises";
|
|
6
6
|
import { spawnSync } from "child_process";
|
|
7
|
-
import { basename, join } from "path";
|
|
7
|
+
import { basename, join, relative } from "path";
|
|
8
8
|
import { tmpdir } from "os";
|
|
9
9
|
import { codexHome, codexConfigPath, codexPromptsDir, userSkillsDir, projectSkillsDir, omxStateDir, detectLegacySkillRootOverlap, codexAgentsDir, } from "../utils/paths.js";
|
|
10
10
|
import { classifySpawnError, spawnPlatformCommandSync, } from "../utils/platform-command.js";
|
|
@@ -13,7 +13,7 @@ import { parse as parseToml } from "@iarna/toml";
|
|
|
13
13
|
import { getBuiltinExploreHarnessUnsupportedReason, resolvePackagedExploreHarnessCommand, EXPLORE_BIN_ENV, } from "./explore.js";
|
|
14
14
|
import { getPackageRoot } from "../utils/package.js";
|
|
15
15
|
import { hasLegacyOmxTeamRunTable, getModelContextRecommendation, } from "../config/generator.js";
|
|
16
|
-
import { MANAGED_HOOK_EVENTS, buildManagedCodexNativeHookCommand, discoverCodexHookConfigPaths, getManagedCodexHookCommandsForEvent, getMissingManagedCodexHookEvents, hasCodexHooksJsonTopLevelState, } from "../config/codex-hooks.js";
|
|
16
|
+
import { MANAGED_HOOK_EVENTS, buildManagedCodexNativeHookCommand, discoverCodexHookConfigPaths, resolveWindowsPowerShellPath, getManagedCodexHookCommandsForEvent, getMissingManagedCodexHookEvents, hasCodexHooksJsonTopLevelState, } from "../config/codex-hooks.js";
|
|
17
17
|
import { OMX_FIRST_PARTY_MCP_SERVER_NAMES } from "../config/omx-first-party-mcp.js";
|
|
18
18
|
import { getDefaultBridge, isBridgeEnabled } from "../runtime/bridge.js";
|
|
19
19
|
import { OMX_EXPLORE_CMD_ENV, isExploreCommandRoutingEnabled, } from "../hooks/explore-routing.js";
|
|
@@ -24,10 +24,11 @@ import { readTriageConfig } from "../hooks/triage-config.js";
|
|
|
24
24
|
import { readPersistedSetupPreferences, } from "./setup-preferences.js";
|
|
25
25
|
import { OMX_LOCAL_MARKETPLACE_NAME, OMX_LOCAL_PLUGIN_CONFIG_KEY, discoverOmxPluginCacheDirs, expectedPackagedOmxSkillNames, packagedOmxPluginVersion, pluginHookCacheMatchesPackaged, readOmxPluginCacheState, resolvePackagedOmxMarketplace, } from "./plugin-marketplace.js";
|
|
26
26
|
import { hasOmxAgentsContract } from "../utils/agents-md.js";
|
|
27
|
-
import { OMX_DEFAULT_SPARK_MODEL_ENV, OMX_SPARK_MODEL_ENV, getCodexConfigRootModelProvider, getEnvConfiguredSparkDefaultModel, getMainDefaultModel, getSparkDefaultModel, getStandardDefaultModel, } from "../config/models.js";
|
|
27
|
+
import { OMX_DEFAULT_SPARK_MODEL_ENV, OMX_SPARK_MODEL_ENV, getAgentModelOverride, getCodexConfigRootModelProvider, getEnvConfiguredSparkDefaultModel, getMainDefaultModel, getSparkDefaultModel, getStandardDefaultModel, } from "../config/models.js";
|
|
28
28
|
import { AGENT_DEFINITIONS } from "../agents/definitions.js";
|
|
29
29
|
import { getInstallableNativeAgentNames } from "../agents/policy.js";
|
|
30
30
|
import { readCatalogManifest } from "../catalog/reader.js";
|
|
31
|
+
const REPO_ARTIFACT_DIRS = [".omx", ".beads"];
|
|
31
32
|
async function resolveDoctorScope(cwd) {
|
|
32
33
|
const persisted = await readPersistedSetupPreferences(cwd);
|
|
33
34
|
if (persisted?.scope) {
|
|
@@ -128,6 +129,24 @@ export async function doctor(options = {}) {
|
|
|
128
129
|
: scopeResolution.source === "config"
|
|
129
130
|
? " (inferred from Codex plugin config)"
|
|
130
131
|
: "";
|
|
132
|
+
if (options.force) {
|
|
133
|
+
if (options.dryRun) {
|
|
134
|
+
const check = await checkRepoArtifactOwnership(cwd);
|
|
135
|
+
if (check.status !== "pass") {
|
|
136
|
+
console.log(`Dry run: ${check.message}`);
|
|
137
|
+
console.log();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const repair = await repairRepoArtifactOwnership(cwd);
|
|
142
|
+
if (repair.repaired > 0 || repair.skipped.length > 0) {
|
|
143
|
+
console.log(`Repo artifact ownership repair: ${repair.repaired} path(s) repaired${repair.skipped.length > 0
|
|
144
|
+
? `, ${repair.skipped.length} skipped (${repair.skipped.join("; ")})`
|
|
145
|
+
: ""}`);
|
|
146
|
+
console.log();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
131
150
|
console.log("oh-my-codex doctor");
|
|
132
151
|
console.log("==================\n");
|
|
133
152
|
console.log(`Resolved setup scope: ${scopeResolution.scope}${scopeSourceMessage}`);
|
|
@@ -144,7 +163,10 @@ export async function doctor(options = {}) {
|
|
|
144
163
|
// Check 2: Node.js version
|
|
145
164
|
checks.push(checkNodeVersion());
|
|
146
165
|
// Check 2.5: Explore harness readiness
|
|
147
|
-
|
|
166
|
+
const exploreRoutingState = await resolveExploreRoutingState(paths.configPath);
|
|
167
|
+
checks.push(checkExploreHarness(process.platform, process.env, {
|
|
168
|
+
exploreRoutingEnabled: exploreRoutingState.enabled,
|
|
169
|
+
}));
|
|
148
170
|
// Check 3: Codex home directory
|
|
149
171
|
checks.push(checkDirectory("Codex home", paths.codexHomeDir));
|
|
150
172
|
// Check 4: Config file
|
|
@@ -168,13 +190,20 @@ export async function doctor(options = {}) {
|
|
|
168
190
|
if (runtimeMirrorCheck)
|
|
169
191
|
checks.push(runtimeMirrorCheck);
|
|
170
192
|
// Check 4.5: Explore routing default
|
|
171
|
-
checks.push(
|
|
193
|
+
checks.push(checkExploreRoutingFromState(exploreRoutingState));
|
|
172
194
|
// Check 4.6: Lore commit guard default
|
|
173
195
|
checks.push(await checkLoreCommitGuard(paths.configPath));
|
|
196
|
+
// Check 4.7: External process guards
|
|
197
|
+
const externalProcessGuardCheck = await checkExternalCodexProcessGuards();
|
|
198
|
+
if (externalProcessGuardCheck)
|
|
199
|
+
checks.push(externalProcessGuardCheck);
|
|
174
200
|
// Check 5: Prompts installed
|
|
175
201
|
checks.push(await checkPrompts(paths.promptsDir, scopeResolution.installMode));
|
|
176
202
|
// Check 6: Skills installed
|
|
177
203
|
checks.push(await checkSkills(paths, scopeResolution.installMode));
|
|
204
|
+
if (scopeResolution.installMode === "plugin") {
|
|
205
|
+
checks.push(await checkPluginVersionDiagnostics(paths.codexHomeDir));
|
|
206
|
+
}
|
|
178
207
|
// Check 6.25: Native reviewer roles required by RALPLAN/Autopilot
|
|
179
208
|
const nativeReviewerRolesCheck = checkNativeReviewerRoles(paths, scopeResolution.installMode);
|
|
180
209
|
if (nativeReviewerRolesCheck)
|
|
@@ -189,6 +218,7 @@ export async function doctor(options = {}) {
|
|
|
189
218
|
checks.push(checkAgentsMd(scopeResolution.scope, paths.codexHomeDir, scopeResolution.installMode));
|
|
190
219
|
// Check 8: State directory
|
|
191
220
|
checks.push(checkDirectory("State dir", paths.stateDir));
|
|
221
|
+
checks.push(await checkRepoArtifactOwnership(cwd));
|
|
192
222
|
// Check 9: MCP servers configured
|
|
193
223
|
checks.push(await checkMcpServers(paths.configPath, scopeResolution.installMode, scopeResolution.mcpMode));
|
|
194
224
|
// Check 10: Prompt triage
|
|
@@ -559,7 +589,16 @@ function checkNodeVersion() {
|
|
|
559
589
|
message: `v${process.versions.node} (need >= 20)`,
|
|
560
590
|
};
|
|
561
591
|
}
|
|
562
|
-
export function checkExploreHarness(platform = process.platform, env = process.env) {
|
|
592
|
+
export function checkExploreHarness(platform = process.platform, env = process.env, options = {}) {
|
|
593
|
+
const override = env[EXPLORE_BIN_ENV]?.trim();
|
|
594
|
+
const exploreRoutingEnabled = options.exploreRoutingEnabled ?? isExploreCommandRoutingEnabled(env);
|
|
595
|
+
if (!override && !exploreRoutingEnabled) {
|
|
596
|
+
return {
|
|
597
|
+
name: "Explore Harness",
|
|
598
|
+
status: "pass",
|
|
599
|
+
message: "skipped: omx explore is hard-deprecated and explore routing is disabled by default; use omx sparkshell for shell-native read-only evidence",
|
|
600
|
+
};
|
|
601
|
+
}
|
|
563
602
|
const packageRoot = getPackageRoot();
|
|
564
603
|
const manifestPath = join(packageRoot, "crates", "omx-explore", "Cargo.toml");
|
|
565
604
|
if (!existsSync(manifestPath)) {
|
|
@@ -569,7 +608,6 @@ export function checkExploreHarness(platform = process.platform, env = process.e
|
|
|
569
608
|
message: "Rust harness sources not found in this install (omx explore unavailable until packaged or OMX_EXPLORE_BIN is set)",
|
|
570
609
|
};
|
|
571
610
|
}
|
|
572
|
-
const override = env[EXPLORE_BIN_ENV]?.trim();
|
|
573
611
|
if (override) {
|
|
574
612
|
const resolved = join(packageRoot, override);
|
|
575
613
|
if (existsSync(override) || existsSync(resolved)) {
|
|
@@ -640,6 +678,166 @@ function checkDirectory(name, path) {
|
|
|
640
678
|
}
|
|
641
679
|
return { name, status: "warn", message: `${path} (not created yet)` };
|
|
642
680
|
}
|
|
681
|
+
function currentProcessUid() {
|
|
682
|
+
return typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
683
|
+
}
|
|
684
|
+
function currentProcessGid() {
|
|
685
|
+
return typeof process.getgid === "function" ? process.getgid() : undefined;
|
|
686
|
+
}
|
|
687
|
+
function remediationCommand(repoRoot) {
|
|
688
|
+
return `sudo chown -R $(id -u):$(id -g) ${JSON.stringify(repoRoot)}`;
|
|
689
|
+
}
|
|
690
|
+
function formatArtifactPath(repoRoot, path) {
|
|
691
|
+
const rel = relative(repoRoot, path);
|
|
692
|
+
return rel === "" ? "." : rel;
|
|
693
|
+
}
|
|
694
|
+
function formatArtifactIssue(repoRoot, issue) {
|
|
695
|
+
const owner = typeof issue.uid === "number" && typeof issue.gid === "number"
|
|
696
|
+
? ` uid=${issue.uid} gid=${issue.gid}`
|
|
697
|
+
: "";
|
|
698
|
+
return `${formatArtifactPath(repoRoot, issue.path)} (${issue.reason}${owner})`;
|
|
699
|
+
}
|
|
700
|
+
function shouldReportOwnerMismatch(uid, currentUid) {
|
|
701
|
+
if (typeof uid !== "number")
|
|
702
|
+
return false;
|
|
703
|
+
if (uid === 0)
|
|
704
|
+
return true;
|
|
705
|
+
return typeof currentUid === "number" && uid !== currentUid;
|
|
706
|
+
}
|
|
707
|
+
function isOwnershipIssue(issue) {
|
|
708
|
+
return issue.type === "ownership";
|
|
709
|
+
}
|
|
710
|
+
async function collectRepoArtifactOwnershipIssues(repoRoot, options = {}) {
|
|
711
|
+
if (process.platform === "win32")
|
|
712
|
+
return [];
|
|
713
|
+
const currentUid = options.currentUid ?? currentProcessUid();
|
|
714
|
+
const maxExamples = options.maxExamples ?? 20;
|
|
715
|
+
const statPath = options.statPath ?? lstat;
|
|
716
|
+
const readDir = options.readDir ?? readdir;
|
|
717
|
+
const accessPath = options.accessPath ?? access;
|
|
718
|
+
const issues = [];
|
|
719
|
+
const visited = new Set();
|
|
720
|
+
async function visit(path) {
|
|
721
|
+
if (issues.length >= maxExamples)
|
|
722
|
+
return;
|
|
723
|
+
let info;
|
|
724
|
+
try {
|
|
725
|
+
info = await statPath(path);
|
|
726
|
+
}
|
|
727
|
+
catch {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
if (info.isSymbolicLink())
|
|
731
|
+
return;
|
|
732
|
+
const uid = typeof info.uid === "number" ? info.uid : undefined;
|
|
733
|
+
const gid = typeof info.gid === "number" ? info.gid : undefined;
|
|
734
|
+
let reason = null;
|
|
735
|
+
if (uid === 0)
|
|
736
|
+
reason = "root-owned";
|
|
737
|
+
else if (shouldReportOwnerMismatch(uid, currentUid))
|
|
738
|
+
reason = "owner-mismatch";
|
|
739
|
+
else {
|
|
740
|
+
try {
|
|
741
|
+
await accessPath(path, constants.W_OK);
|
|
742
|
+
}
|
|
743
|
+
catch {
|
|
744
|
+
reason = "not-writable";
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (reason) {
|
|
748
|
+
issues.push({
|
|
749
|
+
path,
|
|
750
|
+
reason,
|
|
751
|
+
type: reason === "not-writable" ? "writability" : "ownership",
|
|
752
|
+
uid,
|
|
753
|
+
gid,
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
if (issues.length >= maxExamples || !info.isDirectory())
|
|
757
|
+
return;
|
|
758
|
+
if (visited.has(path))
|
|
759
|
+
return;
|
|
760
|
+
visited.add(path);
|
|
761
|
+
let entries;
|
|
762
|
+
try {
|
|
763
|
+
entries = await readDir(path);
|
|
764
|
+
}
|
|
765
|
+
catch {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
for (const entry of entries) {
|
|
769
|
+
await visit(join(path, entry));
|
|
770
|
+
if (issues.length >= maxExamples)
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
for (const dir of REPO_ARTIFACT_DIRS) {
|
|
775
|
+
const root = join(repoRoot, dir);
|
|
776
|
+
if (existsSync(root))
|
|
777
|
+
await visit(root);
|
|
778
|
+
}
|
|
779
|
+
return issues;
|
|
780
|
+
}
|
|
781
|
+
export async function checkRepoArtifactOwnership(repoRoot, options = {}) {
|
|
782
|
+
const issues = await collectRepoArtifactOwnershipIssues(repoRoot, options);
|
|
783
|
+
if (issues.length === 0) {
|
|
784
|
+
return {
|
|
785
|
+
name: "Repo artifact ownership",
|
|
786
|
+
status: "pass",
|
|
787
|
+
message: "repo-local .omx/.beads artifacts are writable by the current user",
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
const examples = issues
|
|
791
|
+
.slice(0, options.maxExamples ?? 5)
|
|
792
|
+
.map((issue) => formatArtifactIssue(repoRoot, issue))
|
|
793
|
+
.join("; ");
|
|
794
|
+
const repair = remediationCommand(repoRoot);
|
|
795
|
+
return {
|
|
796
|
+
name: "Repo artifact ownership",
|
|
797
|
+
status: "warn",
|
|
798
|
+
message: `${issues.length} root-owned, owner-mismatched, or non-writable repo artifact(s): ${examples}. Safe remediation: ${repair}. Automatic repair is only run by \"omx doctor --force\" when the repo root is owned by the current user.`,
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
export async function repairRepoArtifactOwnership(repoRoot, options = {}) {
|
|
802
|
+
if (process.platform === "win32")
|
|
803
|
+
return { repaired: 0, skipped: [] };
|
|
804
|
+
const currentUid = options.currentUid ?? currentProcessUid();
|
|
805
|
+
const currentGid = options.currentGid ?? currentProcessGid();
|
|
806
|
+
if (typeof currentUid !== "number" || typeof currentGid !== "number") {
|
|
807
|
+
return { repaired: 0, skipped: ["current uid/gid unavailable"] };
|
|
808
|
+
}
|
|
809
|
+
const statPath = options.statPath ?? lstat;
|
|
810
|
+
const repoInfo = await statPath(repoRoot);
|
|
811
|
+
if (repoInfo.uid !== currentUid) {
|
|
812
|
+
return { repaired: 0, skipped: ["repo root is not owned by the current user"] };
|
|
813
|
+
}
|
|
814
|
+
const issues = await collectRepoArtifactOwnershipIssues(repoRoot, {
|
|
815
|
+
...options,
|
|
816
|
+
currentUid,
|
|
817
|
+
currentGid,
|
|
818
|
+
maxExamples: Number.MAX_SAFE_INTEGER,
|
|
819
|
+
statPath,
|
|
820
|
+
});
|
|
821
|
+
const ownershipIssues = issues.filter(isOwnershipIssue);
|
|
822
|
+
const writabilityIssues = issues.filter((issue) => !isOwnershipIssue(issue));
|
|
823
|
+
const chownPath = options.chownPath ?? chown;
|
|
824
|
+
let repaired = 0;
|
|
825
|
+
const skipped = [];
|
|
826
|
+
for (const issue of writabilityIssues) {
|
|
827
|
+
skipped.push(`${formatArtifactPath(repoRoot, issue.path)}: not writable by current user`);
|
|
828
|
+
}
|
|
829
|
+
for (const issue of ownershipIssues) {
|
|
830
|
+
try {
|
|
831
|
+
await chownPath(issue.path, currentUid, currentGid);
|
|
832
|
+
repaired++;
|
|
833
|
+
}
|
|
834
|
+
catch (error) {
|
|
835
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
836
|
+
skipped.push(`${formatArtifactPath(repoRoot, issue.path)}: ${message}`);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return { repaired, skipped };
|
|
840
|
+
}
|
|
643
841
|
function validateToml(content) {
|
|
644
842
|
try {
|
|
645
843
|
parseToml(content);
|
|
@@ -738,10 +936,36 @@ async function checkModelContextRecommendation(configPath) {
|
|
|
738
936
|
return null;
|
|
739
937
|
}
|
|
740
938
|
}
|
|
741
|
-
async function
|
|
742
|
-
const envValue =
|
|
939
|
+
async function resolveExploreRoutingState(configPath, env = process.env) {
|
|
940
|
+
const envValue = env[OMX_EXPLORE_CMD_ENV];
|
|
743
941
|
if (typeof envValue === "string") {
|
|
744
|
-
|
|
942
|
+
return { source: "env", enabled: isExploreCommandRoutingEnabled(env) };
|
|
943
|
+
}
|
|
944
|
+
if (!existsSync(configPath)) {
|
|
945
|
+
return { source: "default", enabled: false, reason: "missing-config" };
|
|
946
|
+
}
|
|
947
|
+
try {
|
|
948
|
+
const content = await readFile(configPath, "utf-8");
|
|
949
|
+
const parsed = parseToml(content);
|
|
950
|
+
const configuredValue = parsed?.shell_environment_policy?.set?.USE_OMX_EXPLORE_CMD ??
|
|
951
|
+
parsed?.env?.USE_OMX_EXPLORE_CMD;
|
|
952
|
+
if (typeof configuredValue === "string") {
|
|
953
|
+
return {
|
|
954
|
+
source: "config",
|
|
955
|
+
enabled: isExploreCommandRoutingEnabled({
|
|
956
|
+
USE_OMX_EXPLORE_CMD: configuredValue,
|
|
957
|
+
}),
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
return { source: "default", enabled: false, reason: "unset" };
|
|
961
|
+
}
|
|
962
|
+
catch {
|
|
963
|
+
return { source: "unreadable", enabled: false };
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
function checkExploreRoutingFromState(state) {
|
|
967
|
+
if (state.source === "env") {
|
|
968
|
+
if (state.enabled) {
|
|
745
969
|
return {
|
|
746
970
|
name: "Explore routing",
|
|
747
971
|
status: "warn",
|
|
@@ -754,47 +978,34 @@ async function checkExploreRouting(configPath) {
|
|
|
754
978
|
message: "deprecated compatibility routing disabled by environment override (recommended)",
|
|
755
979
|
};
|
|
756
980
|
}
|
|
757
|
-
if (
|
|
758
|
-
|
|
759
|
-
name: "Explore routing",
|
|
760
|
-
status: "pass",
|
|
761
|
-
message: "deprecated by default (config.toml not found yet)",
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
try {
|
|
765
|
-
const content = await readFile(configPath, "utf-8");
|
|
766
|
-
const parsed = parseToml(content);
|
|
767
|
-
const configuredValue = parsed?.shell_environment_policy?.set?.USE_OMX_EXPLORE_CMD ??
|
|
768
|
-
parsed?.env?.USE_OMX_EXPLORE_CMD;
|
|
769
|
-
if (typeof configuredValue === "string") {
|
|
770
|
-
if (isExploreCommandRoutingEnabled({
|
|
771
|
-
USE_OMX_EXPLORE_CMD: configuredValue,
|
|
772
|
-
})) {
|
|
773
|
-
return {
|
|
774
|
-
name: "Explore routing",
|
|
775
|
-
status: "warn",
|
|
776
|
-
message: 'deprecated compatibility routing enabled in config.toml; set USE_OMX_EXPLORE_CMD = "0" under [shell_environment_policy.set] and use normal Codex repo inspection or omx sparkshell instead',
|
|
777
|
-
};
|
|
778
|
-
}
|
|
981
|
+
if (state.source === "config") {
|
|
982
|
+
if (state.enabled) {
|
|
779
983
|
return {
|
|
780
984
|
name: "Explore routing",
|
|
781
|
-
status: "
|
|
782
|
-
message:
|
|
985
|
+
status: "warn",
|
|
986
|
+
message: 'deprecated compatibility routing enabled in config.toml; set USE_OMX_EXPLORE_CMD = "0" under [shell_environment_policy.set] and use normal Codex repo inspection or omx sparkshell instead',
|
|
783
987
|
};
|
|
784
988
|
}
|
|
785
989
|
return {
|
|
786
990
|
name: "Explore routing",
|
|
787
991
|
status: "pass",
|
|
788
|
-
message: "deprecated
|
|
992
|
+
message: "deprecated compatibility routing disabled in config.toml (recommended)",
|
|
789
993
|
};
|
|
790
994
|
}
|
|
791
|
-
|
|
995
|
+
if (state.source === "unreadable") {
|
|
792
996
|
return {
|
|
793
997
|
name: "Explore routing",
|
|
794
998
|
status: "fail",
|
|
795
999
|
message: "cannot read config.toml for explore routing check",
|
|
796
1000
|
};
|
|
797
1001
|
}
|
|
1002
|
+
return {
|
|
1003
|
+
name: "Explore routing",
|
|
1004
|
+
status: "pass",
|
|
1005
|
+
message: state.reason === "missing-config"
|
|
1006
|
+
? "deprecated by default (config.toml not found yet)"
|
|
1007
|
+
: "deprecated by default",
|
|
1008
|
+
};
|
|
798
1009
|
}
|
|
799
1010
|
const LORE_COMMIT_GUARD_EXPLICIT_OPT_OUT_VALUES = new Set([
|
|
800
1011
|
"0",
|
|
@@ -877,6 +1088,117 @@ async function checkLoreCommitGuard(configPath) {
|
|
|
877
1088
|
function isExplicitLoreCommitGuardOptOut(value) {
|
|
878
1089
|
return LORE_COMMIT_GUARD_EXPLICIT_OPT_OUT_VALUES.has(value.trim().toLowerCase());
|
|
879
1090
|
}
|
|
1091
|
+
function decodeBasicXmlEntities(value) {
|
|
1092
|
+
return value
|
|
1093
|
+
.replaceAll("&", "&")
|
|
1094
|
+
.replaceAll("<", "<")
|
|
1095
|
+
.replaceAll(">", ">")
|
|
1096
|
+
.replaceAll(""", '"')
|
|
1097
|
+
.replaceAll("'", "'");
|
|
1098
|
+
}
|
|
1099
|
+
function extractPlistStrings(content) {
|
|
1100
|
+
return [...content.matchAll(/<string>([^<]+)<\/string>/g)].map((match) => decodeBasicXmlEntities(match[1]?.trim() ?? ""));
|
|
1101
|
+
}
|
|
1102
|
+
function extractPlistLabel(content, fallback) {
|
|
1103
|
+
const labelMatch = content.match(/<key>Label<\/key>\s*<string>([^<]+)<\/string>/);
|
|
1104
|
+
return decodeBasicXmlEntities(labelMatch?.[1]?.trim() || fallback);
|
|
1105
|
+
}
|
|
1106
|
+
async function readExistingTextFile(path) {
|
|
1107
|
+
try {
|
|
1108
|
+
return await readFile(path, "utf-8");
|
|
1109
|
+
}
|
|
1110
|
+
catch {
|
|
1111
|
+
return "";
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
function expandLaunchAgentPath(value, homeDir) {
|
|
1115
|
+
if (value === "~")
|
|
1116
|
+
return homeDir;
|
|
1117
|
+
if (value.startsWith("~/"))
|
|
1118
|
+
return join(homeDir, value.slice(2));
|
|
1119
|
+
if (value === "$HOME")
|
|
1120
|
+
return homeDir;
|
|
1121
|
+
if (value.startsWith("$HOME/"))
|
|
1122
|
+
return join(homeDir, value.slice(6));
|
|
1123
|
+
if (value === "${HOME}")
|
|
1124
|
+
return homeDir;
|
|
1125
|
+
if (value.startsWith("${HOME}/"))
|
|
1126
|
+
return join(homeDir, value.slice(8));
|
|
1127
|
+
return value;
|
|
1128
|
+
}
|
|
1129
|
+
function classifyExternalCodexProcessGuard(plistContent, combinedContent) {
|
|
1130
|
+
const lowerPlist = plistContent.toLowerCase();
|
|
1131
|
+
const lower = combinedContent.toLowerCase();
|
|
1132
|
+
if (lower.includes("codex-mcp-child-guard") ||
|
|
1133
|
+
lower.includes("codex_mcp_child_guard") ||
|
|
1134
|
+
combinedContent.includes("CODEX_MCP_GUARD_DEDUPE_APP_CHILDREN") ||
|
|
1135
|
+
(lower.includes("codex app-server") &&
|
|
1136
|
+
lower.includes("pgrep -p") &&
|
|
1137
|
+
lower.includes("kill"))) {
|
|
1138
|
+
return "Codex app-server MCP child dedupe";
|
|
1139
|
+
}
|
|
1140
|
+
if ((lowerPlist.includes("codex-xcode-mcp-guard") ||
|
|
1141
|
+
lowerPlist.includes("codex_xcode_mcp_guard")) &&
|
|
1142
|
+
lower.includes("xcodebuildmcp") &&
|
|
1143
|
+
lower.includes("kill")) {
|
|
1144
|
+
return "XcodeBuildMCP cleanup";
|
|
1145
|
+
}
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
export async function checkExternalCodexProcessGuards(options = {}) {
|
|
1149
|
+
const platform = options.platform ?? process.platform;
|
|
1150
|
+
if (platform !== "darwin")
|
|
1151
|
+
return null;
|
|
1152
|
+
const homeDir = options.homeDir ?? process.env.HOME;
|
|
1153
|
+
if (!homeDir)
|
|
1154
|
+
return null;
|
|
1155
|
+
const launchAgentsDir = join(homeDir, "Library", "LaunchAgents");
|
|
1156
|
+
if (!existsSync(launchAgentsDir))
|
|
1157
|
+
return null;
|
|
1158
|
+
let entries;
|
|
1159
|
+
try {
|
|
1160
|
+
entries = await readdir(launchAgentsDir);
|
|
1161
|
+
}
|
|
1162
|
+
catch {
|
|
1163
|
+
return null;
|
|
1164
|
+
}
|
|
1165
|
+
const findings = [];
|
|
1166
|
+
for (const entry of entries) {
|
|
1167
|
+
if (!entry.endsWith(".plist"))
|
|
1168
|
+
continue;
|
|
1169
|
+
const plistPath = join(launchAgentsDir, entry);
|
|
1170
|
+
const plistContent = await readExistingTextFile(plistPath);
|
|
1171
|
+
if (plistContent.trim() === "")
|
|
1172
|
+
continue;
|
|
1173
|
+
const loweredPlist = plistContent.toLowerCase();
|
|
1174
|
+
if (!loweredPlist.includes("codex") &&
|
|
1175
|
+
!loweredPlist.includes("mcp") &&
|
|
1176
|
+
!loweredPlist.includes("omx")) {
|
|
1177
|
+
continue;
|
|
1178
|
+
}
|
|
1179
|
+
let combinedContent = plistContent;
|
|
1180
|
+
for (const value of extractPlistStrings(plistContent)) {
|
|
1181
|
+
const expandedValue = expandLaunchAgentPath(value, homeDir);
|
|
1182
|
+
if (!expandedValue.startsWith("/"))
|
|
1183
|
+
continue;
|
|
1184
|
+
if (!existsSync(expandedValue))
|
|
1185
|
+
continue;
|
|
1186
|
+
combinedContent += `\n${await readExistingTextFile(expandedValue)}`;
|
|
1187
|
+
}
|
|
1188
|
+
const reason = classifyExternalCodexProcessGuard(plistContent, combinedContent);
|
|
1189
|
+
if (!reason)
|
|
1190
|
+
continue;
|
|
1191
|
+
const label = extractPlistLabel(plistContent, entry);
|
|
1192
|
+
findings.push(`${label} (${reason})`);
|
|
1193
|
+
}
|
|
1194
|
+
if (findings.length === 0)
|
|
1195
|
+
return null;
|
|
1196
|
+
return {
|
|
1197
|
+
name: "External process guards",
|
|
1198
|
+
status: "warn",
|
|
1199
|
+
message: `external LaunchAgent(s) may terminate Codex/MCP helper processes outside OMX setup ownership: ${findings.join(", ")}; unload/remove them before attributing SIGTERM or transport resets to standard OMX MCP configuration`,
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
880
1202
|
function isEnabledTomlValue(value) {
|
|
881
1203
|
return value === true || (typeof value === "string" && ["1", "true", "yes", "on"].includes(value.trim().toLowerCase()));
|
|
882
1204
|
}
|
|
@@ -1222,6 +1544,27 @@ export function classifyPostCompactHookStdout(stdout) {
|
|
|
1222
1544
|
};
|
|
1223
1545
|
}
|
|
1224
1546
|
}
|
|
1547
|
+
export function buildPostCompactSmokeSpawnInvocation(expectedCommand, options = {}) {
|
|
1548
|
+
const platform = options.platform ?? process.platform;
|
|
1549
|
+
if (platform === "win32") {
|
|
1550
|
+
return {
|
|
1551
|
+
command: resolveWindowsPowerShellPath(options.env),
|
|
1552
|
+
args: [
|
|
1553
|
+
"-NoProfile",
|
|
1554
|
+
"-ExecutionPolicy",
|
|
1555
|
+
"Bypass",
|
|
1556
|
+
"-Command",
|
|
1557
|
+
expectedCommand,
|
|
1558
|
+
],
|
|
1559
|
+
shell: false,
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
return {
|
|
1563
|
+
command: expectedCommand,
|
|
1564
|
+
args: [],
|
|
1565
|
+
shell: true,
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1225
1568
|
async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
|
|
1226
1569
|
if (!existsSync(hooksPath))
|
|
1227
1570
|
return null;
|
|
@@ -1254,7 +1597,8 @@ async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
|
|
|
1254
1597
|
cwd: smokeCwd,
|
|
1255
1598
|
session_id: "omx-doctor-postcompact-smoke",
|
|
1256
1599
|
});
|
|
1257
|
-
const
|
|
1600
|
+
const smokeInvocation = buildPostCompactSmokeSpawnInvocation(expectedCommand);
|
|
1601
|
+
const result = spawnSync(smokeInvocation.command, smokeInvocation.args, {
|
|
1258
1602
|
cwd,
|
|
1259
1603
|
encoding: "utf-8",
|
|
1260
1604
|
env: {
|
|
@@ -1262,7 +1606,7 @@ async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
|
|
|
1262
1606
|
OMX_NATIVE_HOOK_DOCTOR_SMOKE: "1",
|
|
1263
1607
|
},
|
|
1264
1608
|
input: payload,
|
|
1265
|
-
shell:
|
|
1609
|
+
shell: smokeInvocation.shell,
|
|
1266
1610
|
timeout: 5_000,
|
|
1267
1611
|
});
|
|
1268
1612
|
if (result.error) {
|
|
@@ -1490,6 +1834,70 @@ async function checkPluginMarketplaceRegistration(configPath, codexHomeDir) {
|
|
|
1490
1834
|
};
|
|
1491
1835
|
}
|
|
1492
1836
|
}
|
|
1837
|
+
async function readDoctorInstallStamp(codexHomeDir) {
|
|
1838
|
+
try {
|
|
1839
|
+
const parsed = JSON.parse(await readFile(join(codexHomeDir, ".omx", "install-state.json"), "utf-8"));
|
|
1840
|
+
return {
|
|
1841
|
+
...(typeof parsed.install_channel === "string" ? { install_channel: parsed.install_channel } : {}),
|
|
1842
|
+
...(typeof parsed.dev_base_version === "string" ? { dev_base_version: parsed.dev_base_version } : {}),
|
|
1843
|
+
...(typeof parsed.install_revision === "string" ? { install_revision: parsed.install_revision } : {}),
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
catch {
|
|
1847
|
+
return null;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
async function checkPluginVersionDiagnostics(codexHomeDir) {
|
|
1851
|
+
const packagedMarketplace = await resolvePackagedOmxMarketplace(getPackageRoot());
|
|
1852
|
+
if (!packagedMarketplace) {
|
|
1853
|
+
return {
|
|
1854
|
+
name: "Plugin versions",
|
|
1855
|
+
status: "warn",
|
|
1856
|
+
message: `packaged ${OMX_LOCAL_MARKETPLACE_NAME} metadata was not found; reinstall oh-my-codex`,
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
const [manifestVersion, stamp] = await Promise.all([
|
|
1860
|
+
packagedOmxPluginVersion(packagedMarketplace),
|
|
1861
|
+
readDoctorInstallStamp(codexHomeDir),
|
|
1862
|
+
]);
|
|
1863
|
+
if (!manifestVersion) {
|
|
1864
|
+
return {
|
|
1865
|
+
name: "Plugin versions",
|
|
1866
|
+
status: "warn",
|
|
1867
|
+
message: "packaged plugin manifest has no version; reinstall oh-my-codex",
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
const cacheDir = join(codexHomeDir, "plugins", "cache", OMX_LOCAL_MARKETPLACE_NAME, "oh-my-codex", manifestVersion);
|
|
1871
|
+
const cacheState = await readOmxPluginCacheState(cacheDir);
|
|
1872
|
+
if (cacheState?.manifestVersion !== manifestVersion) {
|
|
1873
|
+
return {
|
|
1874
|
+
name: "Plugin versions",
|
|
1875
|
+
status: "warn",
|
|
1876
|
+
message: `expected cache directory ${cacheDir} is not materialized with packaged plugin manifest version ${manifestVersion}; run "omx setup --plugin --force" to refresh the plugin cache`,
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
if (stamp?.install_channel === "dev") {
|
|
1880
|
+
const devDisplay = stamp.dev_base_version && stamp.install_revision
|
|
1881
|
+
? `v${stamp.dev_base_version}-dev-${stamp.install_revision}`
|
|
1882
|
+
: null;
|
|
1883
|
+
const stampDetail = [
|
|
1884
|
+
`package/plugin manifest version ${manifestVersion}`,
|
|
1885
|
+
devDisplay ? `dev display version ${devDisplay}` : null,
|
|
1886
|
+
stamp.dev_base_version ? `dev_base_version ${stamp.dev_base_version}` : null,
|
|
1887
|
+
stamp.install_revision ? `install_revision ${stamp.install_revision}` : null,
|
|
1888
|
+
].filter(Boolean).join("; ");
|
|
1889
|
+
return {
|
|
1890
|
+
name: "Plugin versions",
|
|
1891
|
+
status: "pass",
|
|
1892
|
+
message: `${stampDetail}; Codex may keep current-session plugin skill metadata until a new Codex session starts`,
|
|
1893
|
+
};
|
|
1894
|
+
}
|
|
1895
|
+
return {
|
|
1896
|
+
name: "Plugin versions",
|
|
1897
|
+
status: "pass",
|
|
1898
|
+
message: `cache directory version matches packaged plugin manifest version ${manifestVersion}`,
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1493
1901
|
const REQUIRED_NATIVE_REVIEWER_ROLES = ["architect", "critic"];
|
|
1494
1902
|
const ADVISORY_NATIVE_REVIEWER_ROLES = ["scholastic"];
|
|
1495
1903
|
function getParsedAgentTables(configPath) {
|
|
@@ -1633,6 +2041,9 @@ export function checkSparkRouting(paths) {
|
|
|
1633
2041
|
const standardModel = getStandardDefaultModel(codexHomeOverride);
|
|
1634
2042
|
const sparkSource = resolveSparkModelSource(codexHomeOverride);
|
|
1635
2043
|
const rootProvider = getCodexConfigRootModelProvider(codexHomeOverride);
|
|
2044
|
+
const explicitSparkAgentOverrides = new Map(getInstallableSparkLaneAgentNames()
|
|
2045
|
+
.map((agentName) => [agentName, getAgentModelOverride(agentName, codexHomeOverride)])
|
|
2046
|
+
.filter((entry) => typeof entry[1] === "string"));
|
|
1636
2047
|
const laneSummary = `lanes: frontier=\`${frontierModel}\`, standard=\`${standardModel}\`, ` +
|
|
1637
2048
|
`spark=\`${sparkModel}\` (source: ${sparkSource})`;
|
|
1638
2049
|
const sparkAgents = getInstallableSparkLaneAgentNames();
|
|
@@ -1656,6 +2067,15 @@ export function checkSparkRouting(paths) {
|
|
|
1656
2067
|
problems.push(`${agentName}.toml has no model field (stale install; run \`omx setup --force\`)`);
|
|
1657
2068
|
continue;
|
|
1658
2069
|
}
|
|
2070
|
+
const explicitOverride = explicitSparkAgentOverrides.get(agentName);
|
|
2071
|
+
if (explicitOverride) {
|
|
2072
|
+
if (info.model !== explicitOverride) {
|
|
2073
|
+
problems.push(`${agentName}.toml model is \`${info.model}\` but agentModels.${agentName} explicitly resolves to \`${explicitOverride}\` (stale install; run \`omx setup --force\`)`);
|
|
2074
|
+
continue;
|
|
2075
|
+
}
|
|
2076
|
+
wired.push(`${agentName} -> \`${info.model}\` (agentModels override)${info.modelProvider ? ` (provider: ${info.modelProvider})` : ""}`);
|
|
2077
|
+
continue;
|
|
2078
|
+
}
|
|
1659
2079
|
if (info.model !== sparkModel) {
|
|
1660
2080
|
problems.push(`${agentName}.toml model is \`${info.model}\` but the resolved Spark model is \`${sparkModel}\` (stale install; run \`omx setup --force\`)`);
|
|
1661
2081
|
continue;
|