oh-my-codex 0.16.3 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +5 -5
- package/Cargo.toml +1 -1
- package/README.md +3 -3
- package/dist/catalog/__tests__/generator.test.js +2 -0
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +9 -0
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
- package/dist/cli/__tests__/cleanup.test.js +27 -0
- package/dist/cli/__tests__/cleanup.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -5
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +175 -7
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +147 -12
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-serve.test.js +4 -0
- package/dist/cli/__tests__/mcp-serve.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +2 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +47 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +10 -5
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +299 -27
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +85 -3
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +1 -1
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +108 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +91 -0
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +54 -8
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/cleanup.d.ts.map +1 -1
- package/dist/cli/cleanup.js +8 -4
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/codex-feature-probe.d.ts +9 -0
- package/dist/cli/codex-feature-probe.d.ts.map +1 -0
- package/dist/cli/codex-feature-probe.js +28 -0
- package/dist/cli/codex-feature-probe.js.map +1 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +214 -23
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +17 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +152 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.js +8 -8
- package/dist/cli/mcp-parity.js.map +1 -1
- package/dist/cli/mcp-serve.d.ts.map +1 -1
- package/dist/cli/mcp-serve.js +4 -0
- package/dist/cli/mcp-serve.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +23 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +203 -1
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +21 -0
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +4 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +7 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +5 -3
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +140 -51
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/ultragoal.d.ts +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +70 -5
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/uninstall.d.ts +2 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +12 -3
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/codex-feature-flags.test.d.ts +2 -0
- package/dist/config/__tests__/codex-feature-flags.test.d.ts.map +1 -0
- package/dist/config/__tests__/codex-feature-flags.test.js +35 -0
- package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -0
- package/dist/config/__tests__/codex-hooks.test.js +143 -9
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +85 -9
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +116 -11
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/wiki-config-contract.test.js +6 -3
- package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -1
- package/dist/config/codex-feature-flags.d.ts +21 -0
- package/dist/config/codex-feature-flags.d.ts.map +1 -0
- package/dist/config/codex-feature-flags.js +56 -0
- package/dist/config/codex-feature-flags.js.map +1 -0
- package/dist/config/codex-hooks.d.ts +14 -13
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +108 -8
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/generator.d.ts +15 -3
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +233 -129
- package/dist/config/generator.js.map +1 -1
- package/dist/config/omx-first-party-mcp.d.ts +3 -1
- package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
- package/dist/config/omx-first-party-mcp.js +9 -2
- package/dist/config/omx-first-party-mcp.js.map +1 -1
- package/dist/hooks/__tests__/design-skill.test.d.ts +2 -0
- package/dist/hooks/__tests__/design-skill.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/design-skill.test.js +55 -0
- package/dist/hooks/__tests__/design-skill.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +92 -2
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +125 -1
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +265 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts +2 -0
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.js +84 -0
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +41 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
- package/dist/hooks/agents-overlay.js +2 -2
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +1 -0
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +12 -6
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +2 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +47 -2
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +164 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +4 -5
- package/dist/hud/state.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +3 -0
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/hermes-bridge.test.d.ts +2 -0
- package/dist/mcp/__tests__/hermes-bridge.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/hermes-bridge.test.js +374 -0
- package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -0
- package/dist/mcp/__tests__/state-paths.test.js +155 -11
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +166 -0
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +2 -0
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/hermes-bridge.d.ts +81 -0
- package/dist/mcp/hermes-bridge.d.ts.map +1 -0
- package/dist/mcp/hermes-bridge.js +400 -0
- package/dist/mcp/hermes-bridge.js.map +1 -0
- package/dist/mcp/hermes-server.d.ts +269 -0
- package/dist/mcp/hermes-server.d.ts.map +1 -0
- package/dist/mcp/hermes-server.js +121 -0
- package/dist/mcp/hermes-server.js.map +1 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +64 -11
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/__tests__/base-session-scope.test.js +22 -0
- package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +88 -27
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +5 -0
- package/dist/modes/base.js.map +1 -1
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts +2 -0
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts.map +1 -0
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js +316 -0
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts +2 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts.map +1 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js +481 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js.map +1 -0
- package/dist/planning/__tests__/artifacts.test.js +533 -4
- package/dist/planning/__tests__/artifacts.test.js.map +1 -1
- package/dist/planning/__tests__/context-pack-status.test.js +524 -0
- package/dist/planning/__tests__/context-pack-status.test.js.map +1 -1
- package/dist/planning/__tests__/markdown-structure.test.d.ts +2 -0
- package/dist/planning/__tests__/markdown-structure.test.d.ts.map +1 -0
- package/dist/planning/__tests__/markdown-structure.test.js +459 -0
- package/dist/planning/__tests__/markdown-structure.test.js.map +1 -0
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js +523 -1
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js.map +1 -1
- package/dist/planning/artifacts.d.ts +1 -1
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +227 -28
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/planning/context-pack-status.d.ts +25 -0
- package/dist/planning/context-pack-status.d.ts.map +1 -1
- package/dist/planning/context-pack-status.js +272 -31
- package/dist/planning/context-pack-status.js.map +1 -1
- package/dist/planning/markdown-structure.d.ts +20 -0
- package/dist/planning/markdown-structure.d.ts.map +1 -0
- package/dist/planning/markdown-structure.js +137 -0
- package/dist/planning/markdown-structure.js.map +1 -0
- package/dist/ralph/__tests__/completion-audit.test.d.ts +2 -0
- package/dist/ralph/__tests__/completion-audit.test.d.ts.map +1 -0
- package/dist/ralph/__tests__/completion-audit.test.js +121 -0
- package/dist/ralph/__tests__/completion-audit.test.js.map +1 -0
- package/dist/ralph/completion-audit.d.ts +8 -0
- package/dist/ralph/completion-audit.d.ts.map +1 -0
- package/dist/ralph/completion-audit.js +99 -0
- package/dist/ralph/completion-audit.js.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +407 -15
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-dispatcher.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.js +126 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +177 -71
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +4 -2
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/notify-dispatcher.js +30 -1
- package/dist/scripts/notify-dispatcher.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +91 -2
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +3 -1
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +102 -27
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/mode-state-context.d.ts +2 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +21 -0
- package/dist/state/mode-state-context.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +9 -3
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +7 -0
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +25 -8
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts +1 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +22 -15
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.js +3 -3
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/team/__tests__/approved-execution.test.js +39 -0
- package/dist/team/__tests__/approved-execution.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +5 -0
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +497 -2
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state-root.test.js +1 -1
- package/dist/team/__tests__/state-root.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +8 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/approved-execution.d.ts.map +1 -1
- package/dist/team/approved-execution.js +3 -0
- package/dist/team/approved-execution.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +43 -0
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state-root.d.ts.map +1 -1
- package/dist/team/state-root.js +4 -0
- package/dist/team/state-root.js.map +1 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +2 -6
- package/dist/team/state.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +245 -1
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +21 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +52 -2
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +301 -15
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +31 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +18 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/wiki/lifecycle.js +4 -4
- package/dist/wiki/lifecycle.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/.mcp.json +13 -5
- package/plugins/oh-my-codex/skills/analyze/SKILL.md +0 -2
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -3
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +5 -7
- package/plugins/oh-my-codex/skills/design/SKILL.md +180 -0
- package/plugins/oh-my-codex/skills/doctor/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/pipeline/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/plan/SKILL.md +3 -6
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +9 -10
- package/plugins/oh-my-codex/skills/skill/SKILL.md +2 -1
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +36 -3
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +175 -64
- package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +8 -8
- package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/wiki/SKILL.md +13 -13
- package/skills/analyze/SKILL.md +0 -2
- package/skills/ask-claude/SKILL.md +5 -3
- package/skills/ask-gemini/SKILL.md +5 -3
- package/skills/autopilot/SKILL.md +2 -2
- package/skills/code-review/SKILL.md +1 -3
- package/skills/deep-interview/SKILL.md +5 -7
- package/skills/design/SKILL.md +180 -0
- package/skills/doctor/SKILL.md +2 -2
- package/skills/ecomode/SKILL.md +105 -1
- package/skills/frontend-ui-ux/SKILL.md +6 -24
- package/skills/git-master/SKILL.md +2 -4
- package/skills/omx-setup/SKILL.md +3 -3
- package/skills/pipeline/SKILL.md +3 -3
- package/skills/plan/SKILL.md +3 -6
- package/skills/ralph/SKILL.md +9 -10
- package/skills/skill/SKILL.md +2 -1
- package/skills/swarm/SKILL.md +5 -3
- package/skills/tdd/SKILL.md +95 -1
- package/skills/ultragoal/SKILL.md +36 -3
- package/skills/ultraqa/SKILL.md +175 -64
- package/skills/ultrawork/SKILL.md +8 -8
- package/skills/visual-ralph/SKILL.md +2 -2
- package/skills/web-clone/SKILL.md +348 -1
- package/skills/wiki/SKILL.md +13 -13
- package/src/scripts/__tests__/codex-native-hook.test.ts +437 -14
- package/src/scripts/__tests__/notify-dispatcher.test.ts +153 -0
- package/src/scripts/codex-native-hook.ts +205 -61
- package/src/scripts/codex-native-pre-post.ts +4 -1
- package/src/scripts/notify-dispatcher.ts +40 -1
- package/src/scripts/notify-hook/tmux-injection.ts +110 -3
- package/src/scripts/notify-hook.ts +3 -1
- package/templates/catalog-manifest.json +9 -2
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, it } from 'node:test';
|
|
1
|
+
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
4
|
+
import fs, { existsSync } from 'node:fs';
|
|
5
5
|
import { createHash } from 'node:crypto';
|
|
6
|
+
import { syncBuiltinESMExports } from 'node:module';
|
|
6
7
|
import { tmpdir } from 'node:os';
|
|
7
|
-
import { join, relative } from 'node:path';
|
|
8
|
+
import { basename, join, relative } from 'node:path';
|
|
8
9
|
import { decodeApprovedExecutionQuotedValue, isPlanningComplete, readApprovedExecutionLaunchHint, readApprovedExecutionLaunchHintOutcome, readLatestPlanningArtifacts, readPlanningArtifacts, readTeamDagArtifactResolution, } from '../artifacts.js';
|
|
9
10
|
import { readTeamDagHandoffForLatestPlan } from '../../team/dag-schema.js';
|
|
10
11
|
let tempDir;
|
|
@@ -56,6 +57,28 @@ async function writeContextPack(slug, prdPath, testSpecPath, roles) {
|
|
|
56
57
|
}, null, 2));
|
|
57
58
|
return packPath;
|
|
58
59
|
}
|
|
60
|
+
async function writeContextPackWithEntries(slug, prdPath, testSpecPath, entries) {
|
|
61
|
+
const contextDir = join(tempDir, '.omx', 'context');
|
|
62
|
+
await mkdir(contextDir, { recursive: true });
|
|
63
|
+
const packPath = join(tempDir, canonicalContextPackRelativePath(slug));
|
|
64
|
+
const prdContent = await readFile(prdPath, 'utf-8');
|
|
65
|
+
const testSpecContent = await readFile(testSpecPath, 'utf-8');
|
|
66
|
+
await writeFile(packPath, JSON.stringify({
|
|
67
|
+
slug,
|
|
68
|
+
basis: {
|
|
69
|
+
prd: {
|
|
70
|
+
path: relativeToRepo(prdPath),
|
|
71
|
+
sha1: computeGitBlobSha1(prdContent),
|
|
72
|
+
},
|
|
73
|
+
testSpecs: [{
|
|
74
|
+
path: relativeToRepo(testSpecPath),
|
|
75
|
+
sha1: computeGitBlobSha1(testSpecContent),
|
|
76
|
+
}],
|
|
77
|
+
},
|
|
78
|
+
entries,
|
|
79
|
+
}, null, 2));
|
|
80
|
+
return packPath;
|
|
81
|
+
}
|
|
59
82
|
async function setup() {
|
|
60
83
|
tempDir = await mkdtemp(join(tmpdir(), 'omx-planning-artifacts-'));
|
|
61
84
|
}
|
|
@@ -66,7 +89,11 @@ async function cleanup() {
|
|
|
66
89
|
}
|
|
67
90
|
describe('planning artifacts', () => {
|
|
68
91
|
beforeEach(async () => { await setup(); });
|
|
69
|
-
afterEach(async () => {
|
|
92
|
+
afterEach(async () => {
|
|
93
|
+
mock.restoreAll();
|
|
94
|
+
syncBuiltinESMExports();
|
|
95
|
+
await cleanup();
|
|
96
|
+
});
|
|
70
97
|
it('round-trips single-quoted approved execution tasks with only escaped apostrophes normalized', () => {
|
|
71
98
|
const task = String.raw `Fix Bob's regression in C:\\tmp`;
|
|
72
99
|
assert.equal(decodeApprovedExecutionQuotedValue(encodeApprovedExecutionTask(task, 'single')), task);
|
|
@@ -273,6 +300,112 @@ describe('planning artifacts', () => {
|
|
|
273
300
|
assert.deepEqual(hint?.missingRequiredContextPackRoles, []);
|
|
274
301
|
assert.deepEqual(hint?.contextPackIssues, []);
|
|
275
302
|
});
|
|
303
|
+
it('keeps approved hint role refs unchanged when ready packs carry private entry metadata', async () => {
|
|
304
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
305
|
+
await mkdir(plansDir, { recursive: true });
|
|
306
|
+
const prdPath = join(plansDir, 'prd-context-ready-private.md');
|
|
307
|
+
const testSpecPath = join(plansDir, 'test-spec-context-ready-private.md');
|
|
308
|
+
await writeFile(prdPath, [
|
|
309
|
+
'# PRD',
|
|
310
|
+
'',
|
|
311
|
+
buildContextPackOutcome(canonicalContextPackRelativePath('context-ready-private')),
|
|
312
|
+
'',
|
|
313
|
+
'Launch via omx ralph "Execute context-ready private handoff"',
|
|
314
|
+
].join('\n'));
|
|
315
|
+
await writeFile(testSpecPath, '# Test Spec\n');
|
|
316
|
+
const packPath = await writeContextPackWithEntries('context-ready-private', prdPath, testSpecPath, [
|
|
317
|
+
{
|
|
318
|
+
path: 'src/scope-ready.ts',
|
|
319
|
+
roles: ['scope'],
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
path: 'src/build-ready.ts',
|
|
323
|
+
roles: ['build'],
|
|
324
|
+
label: 'Build Focus',
|
|
325
|
+
tags: ['runtime', 'build'],
|
|
326
|
+
selector: { type: 'heading', value: ' ## Build Focus ', maxWords: 120 },
|
|
327
|
+
relationPath: [
|
|
328
|
+
{ tag: 'Plan', target: ' context-ready-private ' },
|
|
329
|
+
{ tag: 'Implements', target: ' src/build-ready.ts#build-focus ' },
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
path: 'src/verify-ready.ts',
|
|
334
|
+
roles: ['verify'],
|
|
335
|
+
relationPath: [
|
|
336
|
+
{ tag: 'Plan', target: ' context-ready-private ' },
|
|
337
|
+
{ tag: 'Verifies', target: ' src/verify-ready.ts ' },
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
]);
|
|
341
|
+
const selection = readLatestPlanningArtifacts(tempDir);
|
|
342
|
+
const hint = readApprovedExecutionLaunchHint(tempDir, 'ralph');
|
|
343
|
+
assert.deepEqual(selection.contextPack, { path: packPath });
|
|
344
|
+
assert.equal(selection.contextPackStatus, 'ready');
|
|
345
|
+
assert.deepEqual(selection.contextPackRoleRefs, {
|
|
346
|
+
scope: ['src/scope-ready.ts'],
|
|
347
|
+
build: ['src/build-ready.ts'],
|
|
348
|
+
verify: ['src/verify-ready.ts'],
|
|
349
|
+
});
|
|
350
|
+
assert.ok(hint);
|
|
351
|
+
assert.deepEqual(hint?.contextPack, { path: packPath });
|
|
352
|
+
assert.equal(hint?.contextPackStatus, 'ready');
|
|
353
|
+
assert.deepEqual(hint?.contextPackRoleRefs, {
|
|
354
|
+
scope: ['src/scope-ready.ts'],
|
|
355
|
+
build: ['src/build-ready.ts'],
|
|
356
|
+
verify: ['src/verify-ready.ts'],
|
|
357
|
+
});
|
|
358
|
+
assert.deepEqual(hint?.missingRequiredContextPackRoles, []);
|
|
359
|
+
assert.deepEqual(hint?.contextPackIssues, []);
|
|
360
|
+
});
|
|
361
|
+
it('keeps approved hint role refs unchanged when ready packs carry malformed private metadata', async () => {
|
|
362
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
363
|
+
await mkdir(plansDir, { recursive: true });
|
|
364
|
+
const prdPath = join(plansDir, 'prd-context-ready-private-counterfactual.md');
|
|
365
|
+
const testSpecPath = join(plansDir, 'test-spec-context-ready-private-counterfactual.md');
|
|
366
|
+
await writeFile(prdPath, [
|
|
367
|
+
'# PRD',
|
|
368
|
+
'',
|
|
369
|
+
buildContextPackOutcome(canonicalContextPackRelativePath('context-ready-private-counterfactual')),
|
|
370
|
+
'',
|
|
371
|
+
'Launch via omx ralph "Execute context-ready private counterfactual handoff"',
|
|
372
|
+
].join('\n'));
|
|
373
|
+
await writeFile(testSpecPath, '# Test Spec\n');
|
|
374
|
+
const packPath = await writeContextPackWithEntries('context-ready-private-counterfactual', prdPath, testSpecPath, [
|
|
375
|
+
{
|
|
376
|
+
path: 'src/scope-counterfactual.ts',
|
|
377
|
+
roles: ['scope'],
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
path: 'src/build-counterfactual.ts',
|
|
381
|
+
roles: ['build'],
|
|
382
|
+
selector: { type: 'heading', value: 'Build Focus', maxWords: 20 },
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
path: 'src/verify-counterfactual.ts',
|
|
386
|
+
roles: ['verify'],
|
|
387
|
+
},
|
|
388
|
+
]);
|
|
389
|
+
const selection = readLatestPlanningArtifacts(tempDir);
|
|
390
|
+
const hint = readApprovedExecutionLaunchHint(tempDir, 'ralph');
|
|
391
|
+
assert.deepEqual(selection.contextPack, { path: packPath });
|
|
392
|
+
assert.equal(selection.contextPackStatus, 'ready');
|
|
393
|
+
assert.deepEqual(selection.contextPackRoleRefs, {
|
|
394
|
+
scope: ['src/scope-counterfactual.ts'],
|
|
395
|
+
build: ['src/build-counterfactual.ts'],
|
|
396
|
+
verify: ['src/verify-counterfactual.ts'],
|
|
397
|
+
});
|
|
398
|
+
assert.ok(hint);
|
|
399
|
+
assert.deepEqual(hint?.contextPack, { path: packPath });
|
|
400
|
+
assert.equal(hint?.contextPackStatus, 'ready');
|
|
401
|
+
assert.deepEqual(hint?.contextPackRoleRefs, {
|
|
402
|
+
scope: ['src/scope-counterfactual.ts'],
|
|
403
|
+
build: ['src/build-counterfactual.ts'],
|
|
404
|
+
verify: ['src/verify-counterfactual.ts'],
|
|
405
|
+
});
|
|
406
|
+
assert.deepEqual(hint?.missingRequiredContextPackRoles, []);
|
|
407
|
+
assert.deepEqual(hint?.contextPackIssues, []);
|
|
408
|
+
});
|
|
276
409
|
it('preserves invalid context-pack issues on approved hints without widening them into missing roles', async () => {
|
|
277
410
|
const plansDir = join(tempDir, '.omx', 'plans');
|
|
278
411
|
await mkdir(plansDir, { recursive: true });
|
|
@@ -437,6 +570,49 @@ describe('planning artifacts', () => {
|
|
|
437
570
|
assert.equal(hint?.sourcePath, alphaPrdPath);
|
|
438
571
|
assert.deepEqual(hint?.testSpecPaths, [join(plansDir, 'test-spec-alpha.md')]);
|
|
439
572
|
});
|
|
573
|
+
it('binds approved launch hints through canonical-equivalent requested PRD aliases', async () => {
|
|
574
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
575
|
+
await mkdir(plansDir, { recursive: true });
|
|
576
|
+
const alphaPrdPath = join(plansDir, 'prd-alpha.md');
|
|
577
|
+
await writeFile(alphaPrdPath, '# Alpha\n\nLaunch via omx ralph "Execute alpha"\n');
|
|
578
|
+
await writeFile(join(plansDir, 'test-spec-alpha.md'), '# Alpha Test Spec\n');
|
|
579
|
+
await writeFile(join(plansDir, 'prd-zeta.md'), '# Zeta\n\nLaunch via omx ralph "Execute zeta"\n');
|
|
580
|
+
await writeFile(join(plansDir, 'test-spec-zeta.md'), '# Zeta Test Spec\n');
|
|
581
|
+
const aliases = [
|
|
582
|
+
'.omx/plans/prd-alpha.md',
|
|
583
|
+
'prd-alpha.md',
|
|
584
|
+
alphaPrdPath,
|
|
585
|
+
'.omx/plans/../plans/prd-alpha.md',
|
|
586
|
+
'../plans/prd-alpha.md',
|
|
587
|
+
join(tempDir, '.omx', 'plans', '..', 'plans', 'prd-alpha.md'),
|
|
588
|
+
];
|
|
589
|
+
for (const prdPath of aliases) {
|
|
590
|
+
const outcome = readApprovedExecutionLaunchHintOutcome(tempDir, 'ralph', { prdPath });
|
|
591
|
+
assert.equal(outcome.status, 'resolved');
|
|
592
|
+
if (outcome.status !== 'resolved') {
|
|
593
|
+
throw new Error(`expected resolved hint for alias ${prdPath}`);
|
|
594
|
+
}
|
|
595
|
+
assert.equal(outcome.hint.task, 'Execute alpha');
|
|
596
|
+
assert.equal(outcome.hint.sourcePath, alphaPrdPath);
|
|
597
|
+
assert.deepEqual(outcome.hint.testSpecPaths, [join(plansDir, 'test-spec-alpha.md')]);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
it('does not bind requested PRD aliases that do not resolve to a discovered canonical PRD', async () => {
|
|
601
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
602
|
+
await mkdir(plansDir, { recursive: true });
|
|
603
|
+
await writeFile(join(plansDir, 'prd-alpha.md'), '# Alpha\n\nLaunch via omx ralph "Execute alpha"\n');
|
|
604
|
+
await writeFile(join(plansDir, 'test-spec-alpha.md'), '# Alpha Test Spec\n');
|
|
605
|
+
const rejectedAliases = [
|
|
606
|
+
'.omx/plans/prd-missing.md',
|
|
607
|
+
'../prd-alpha.md',
|
|
608
|
+
join('..', basename(tempDir), '.omx', 'plans', 'prd-alpha.md'),
|
|
609
|
+
join(tempDir, '.omx', 'prd-alpha.md'),
|
|
610
|
+
relative(process.cwd(), join(plansDir, 'prd-alpha.md')),
|
|
611
|
+
];
|
|
612
|
+
for (const prdPath of rejectedAliases) {
|
|
613
|
+
assert.equal(readApprovedExecutionLaunchHintOutcome(tempDir, 'ralph', { prdPath }).status, 'absent');
|
|
614
|
+
}
|
|
615
|
+
});
|
|
440
616
|
it('honors the requested Ralph task when a single plan lists multiple Ralph launch hints', async () => {
|
|
441
617
|
const plansDir = join(tempDir, '.omx', 'plans');
|
|
442
618
|
await mkdir(plansDir, { recursive: true });
|
|
@@ -452,6 +628,30 @@ describe('planning artifacts', () => {
|
|
|
452
628
|
assert.equal(hint?.task, 'Execute alpha');
|
|
453
629
|
assert.equal(hint?.command, 'omx ralph "Execute alpha"');
|
|
454
630
|
});
|
|
631
|
+
it('reuses one planning artifact scan while task lookup checks older same-lineage PRDs', async () => {
|
|
632
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
633
|
+
const task = 'Execute cached planning artifact lineage lookup';
|
|
634
|
+
await mkdir(plansDir, { recursive: true });
|
|
635
|
+
await writeFile(join(plansDir, 'prd-alpha.md'), `# Alpha\n\nLaunch via omx ralph ${JSON.stringify(task)}\n`);
|
|
636
|
+
await writeFile(join(plansDir, 'test-spec-alpha.md'), '# Alpha Test Spec\n');
|
|
637
|
+
await writeFile(join(plansDir, 'prd-beta.md'), `# Beta\n\nLaunch via omx ralph ${JSON.stringify(task)}\n`);
|
|
638
|
+
await writeFile(join(plansDir, 'prd-gamma.md'), `# Gamma\n\nLaunch via omx ralph ${JSON.stringify(task)}\n`);
|
|
639
|
+
await writeFile(join(plansDir, 'prd-zeta.md'), `# Zeta\n\nLaunch via omx ralph ${JSON.stringify(task)}\n`);
|
|
640
|
+
let readdirCount = 0;
|
|
641
|
+
const originalReaddirSync = fs.readdirSync;
|
|
642
|
+
mock.method(fs, 'readdirSync', ((...args) => {
|
|
643
|
+
readdirCount += 1;
|
|
644
|
+
return Reflect.apply(originalReaddirSync, fs, args);
|
|
645
|
+
}));
|
|
646
|
+
syncBuiltinESMExports();
|
|
647
|
+
const outcome = readApprovedExecutionLaunchHintOutcome(tempDir, 'ralph', { task });
|
|
648
|
+
assert.equal(outcome.status, 'resolved');
|
|
649
|
+
if (outcome.status !== 'resolved') {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
assert.equal(outcome.hint.sourcePath, join(plansDir, 'prd-alpha.md'));
|
|
653
|
+
assert.equal(readdirCount, 2);
|
|
654
|
+
});
|
|
455
655
|
it('fails closed for bare Ralph lookups when a single plan lists multiple Ralph launch hints', async () => {
|
|
456
656
|
const plansDir = join(tempDir, '.omx', 'plans');
|
|
457
657
|
await mkdir(plansDir, { recursive: true });
|
|
@@ -465,6 +665,335 @@ describe('planning artifacts', () => {
|
|
|
465
665
|
const hint = readApprovedExecutionLaunchHint(tempDir, 'ralph');
|
|
466
666
|
assert.equal(hint, null);
|
|
467
667
|
});
|
|
668
|
+
it('ignores Ralph launch hints that appear only inside indented code blocks', async () => {
|
|
669
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
670
|
+
const task = 'Execute hidden indented ralph plan';
|
|
671
|
+
await mkdir(plansDir, { recursive: true });
|
|
672
|
+
await writeFile(join(plansDir, 'prd-hidden-indented-ralph.md'), [
|
|
673
|
+
'# PRD',
|
|
674
|
+
'',
|
|
675
|
+
` omx ralph ${JSON.stringify(task)}`,
|
|
676
|
+
].join('\n'));
|
|
677
|
+
await writeFile(join(plansDir, 'test-spec-hidden-indented-ralph.md'), '# Test Spec\n');
|
|
678
|
+
const outcome = readApprovedExecutionLaunchHintOutcome(tempDir, 'ralph', { task });
|
|
679
|
+
assert.equal(outcome.status, 'absent');
|
|
680
|
+
});
|
|
681
|
+
it('resolves Ralph launch hints wrapped across visible markdown lines', async () => {
|
|
682
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
683
|
+
const task = 'Execute wrapped visible ralph plan';
|
|
684
|
+
const command = `omx ralph ${JSON.stringify(task)}`;
|
|
685
|
+
await mkdir(plansDir, { recursive: true });
|
|
686
|
+
await writeFile(join(plansDir, 'prd-wrapped-visible-ralph.md'), [
|
|
687
|
+
'# PRD',
|
|
688
|
+
'',
|
|
689
|
+
'Launch via omx ralph',
|
|
690
|
+
JSON.stringify(task),
|
|
691
|
+
].join('\n'));
|
|
692
|
+
await writeFile(join(plansDir, 'test-spec-wrapped-visible-ralph.md'), '# Test Spec\n');
|
|
693
|
+
const hint = readApprovedExecutionLaunchHint(tempDir, 'ralph', { task });
|
|
694
|
+
assert.ok(hint);
|
|
695
|
+
assert.equal(hint?.task, task);
|
|
696
|
+
assert.equal(hint?.command, command);
|
|
697
|
+
assert.equal(hint?.contextPackStatus, 'plan-only');
|
|
698
|
+
const exactHint = readApprovedExecutionLaunchHint(tempDir, 'ralph', { command });
|
|
699
|
+
assert.ok(exactHint);
|
|
700
|
+
assert.equal(exactHint?.command, command);
|
|
701
|
+
});
|
|
702
|
+
it('does not let Ralph launch hints span hidden markdown gaps', async () => {
|
|
703
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
704
|
+
await mkdir(plansDir, { recursive: true });
|
|
705
|
+
const scenarios = [
|
|
706
|
+
{
|
|
707
|
+
name: 'fenced',
|
|
708
|
+
buildHiddenLines: (task) => [
|
|
709
|
+
'```md',
|
|
710
|
+
JSON.stringify(task),
|
|
711
|
+
'```',
|
|
712
|
+
],
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
name: 'commented',
|
|
716
|
+
buildHiddenLines: (task) => [
|
|
717
|
+
'<!--',
|
|
718
|
+
JSON.stringify(task),
|
|
719
|
+
'-->',
|
|
720
|
+
],
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
name: 'indented',
|
|
724
|
+
buildHiddenLines: (task) => [
|
|
725
|
+
` ${JSON.stringify(task)}`,
|
|
726
|
+
],
|
|
727
|
+
},
|
|
728
|
+
];
|
|
729
|
+
for (const scenario of scenarios) {
|
|
730
|
+
const task = `Execute hidden-gap ${scenario.name} ralph plan`;
|
|
731
|
+
await writeFile(join(plansDir, `prd-hidden-gap-${scenario.name}-ralph.md`), [
|
|
732
|
+
'# PRD',
|
|
733
|
+
'',
|
|
734
|
+
'Launch via omx ralph',
|
|
735
|
+
...scenario.buildHiddenLines(task),
|
|
736
|
+
].join('\n'));
|
|
737
|
+
await writeFile(join(plansDir, `test-spec-hidden-gap-${scenario.name}-ralph.md`), '# Test Spec\n');
|
|
738
|
+
const outcome = readApprovedExecutionLaunchHintOutcome(tempDir, 'ralph', { task });
|
|
739
|
+
assert.equal(outcome.status, 'absent', scenario.name);
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
it('resolves Team launch hints wrapped across visible markdown lines', async () => {
|
|
743
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
744
|
+
const task = 'Execute wrapped visible team plan';
|
|
745
|
+
const command = `omx team 2:executor ${JSON.stringify(task)}`;
|
|
746
|
+
await mkdir(plansDir, { recursive: true });
|
|
747
|
+
await writeFile(join(plansDir, 'prd-wrapped-visible-team.md'), [
|
|
748
|
+
'# PRD',
|
|
749
|
+
'',
|
|
750
|
+
'Launch via omx team',
|
|
751
|
+
'2:executor',
|
|
752
|
+
JSON.stringify(task),
|
|
753
|
+
].join('\n'));
|
|
754
|
+
await writeFile(join(plansDir, 'test-spec-wrapped-visible-team.md'), '# Test Spec\n');
|
|
755
|
+
const hint = readApprovedExecutionLaunchHint(tempDir, 'team', { task });
|
|
756
|
+
assert.ok(hint);
|
|
757
|
+
assert.equal(hint?.task, task);
|
|
758
|
+
assert.equal(hint?.workerCount, 2);
|
|
759
|
+
assert.equal(hint?.agentType, 'executor');
|
|
760
|
+
assert.equal(hint?.command, command);
|
|
761
|
+
assert.equal(hint?.contextPackStatus, 'plan-only');
|
|
762
|
+
const exactHint = readApprovedExecutionLaunchHint(tempDir, 'team', { command });
|
|
763
|
+
assert.ok(exactHint);
|
|
764
|
+
assert.equal(exactHint?.command, command);
|
|
765
|
+
});
|
|
766
|
+
it('normalizes wrapped linked-Ralph team launch hints for exact command matching', async () => {
|
|
767
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
768
|
+
const task = 'Execute wrapped linked ralph team plan';
|
|
769
|
+
const command = `$team ralph 5:debugger ${JSON.stringify(task)}`;
|
|
770
|
+
await mkdir(plansDir, { recursive: true });
|
|
771
|
+
await writeFile(join(plansDir, 'prd-wrapped-visible-team-linked-ralph.md'), [
|
|
772
|
+
'# PRD',
|
|
773
|
+
'',
|
|
774
|
+
'Launch via $team',
|
|
775
|
+
'ralph',
|
|
776
|
+
'5:debugger',
|
|
777
|
+
JSON.stringify(task),
|
|
778
|
+
].join('\n'));
|
|
779
|
+
await writeFile(join(plansDir, 'test-spec-wrapped-visible-team-linked-ralph.md'), '# Test Spec\n');
|
|
780
|
+
const hint = readApprovedExecutionLaunchHint(tempDir, 'team', { command });
|
|
781
|
+
assert.ok(hint);
|
|
782
|
+
assert.equal(hint?.command, command);
|
|
783
|
+
assert.equal(hint?.workerCount, 5);
|
|
784
|
+
assert.equal(hint?.agentType, 'debugger');
|
|
785
|
+
assert.equal(hint?.linkedRalph, true);
|
|
786
|
+
});
|
|
787
|
+
it('keeps exact-command normalization bounded to visible whitespace-only variants', async () => {
|
|
788
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
789
|
+
await mkdir(plansDir, { recursive: true });
|
|
790
|
+
const task = 'Execute exact-command normalization state table plan';
|
|
791
|
+
const command = `omx team 2:executor ${JSON.stringify(task)}`;
|
|
792
|
+
const cases = [
|
|
793
|
+
{
|
|
794
|
+
name: 'visible-whitespace-only-variant',
|
|
795
|
+
prdPath: 'prd-exact-command-visible.md',
|
|
796
|
+
content: [
|
|
797
|
+
'# PRD',
|
|
798
|
+
'',
|
|
799
|
+
'Launch via omx team',
|
|
800
|
+
'2:executor',
|
|
801
|
+
JSON.stringify(task),
|
|
802
|
+
].join('\n'),
|
|
803
|
+
expectedStatus: 'resolved',
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
name: 'hidden-gap-counterfactual',
|
|
807
|
+
prdPath: 'prd-exact-command-hidden-gap.md',
|
|
808
|
+
content: [
|
|
809
|
+
'# PRD',
|
|
810
|
+
'',
|
|
811
|
+
'Launch via omx team',
|
|
812
|
+
'```md',
|
|
813
|
+
'hidden',
|
|
814
|
+
'```',
|
|
815
|
+
'2:executor',
|
|
816
|
+
JSON.stringify(task),
|
|
817
|
+
].join('\n'),
|
|
818
|
+
expectedStatus: 'absent',
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
name: 'formatting-only-duplicate',
|
|
822
|
+
prdPath: 'prd-exact-command-duplicate.md',
|
|
823
|
+
content: [
|
|
824
|
+
'# PRD',
|
|
825
|
+
'',
|
|
826
|
+
`Launch via ${command}`,
|
|
827
|
+
'Launch via omx team',
|
|
828
|
+
'2:executor',
|
|
829
|
+
JSON.stringify(task),
|
|
830
|
+
].join('\n'),
|
|
831
|
+
expectedStatus: 'ambiguous',
|
|
832
|
+
},
|
|
833
|
+
];
|
|
834
|
+
for (const scenario of cases) {
|
|
835
|
+
await writeFile(join(plansDir, scenario.prdPath), scenario.content);
|
|
836
|
+
await writeFile(join(plansDir, scenario.prdPath.replace(/^prd-/, 'test-spec-')), '# Test Spec\n');
|
|
837
|
+
const outcome = readApprovedExecutionLaunchHintOutcome(tempDir, 'team', { command });
|
|
838
|
+
assert.equal(outcome.status, scenario.expectedStatus, scenario.name);
|
|
839
|
+
await rm(join(plansDir, scenario.prdPath), { force: true });
|
|
840
|
+
await rm(join(plansDir, scenario.prdPath.replace(/^prd-/, 'test-spec-')), { force: true });
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
it('does not normalize whitespace that changes the quoted task payload', async () => {
|
|
844
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
845
|
+
await mkdir(plansDir, { recursive: true });
|
|
846
|
+
const task = 'Execute embedded\nnewline task';
|
|
847
|
+
const exactCommand = [
|
|
848
|
+
'omx ralph "Execute embedded',
|
|
849
|
+
'newline task"',
|
|
850
|
+
].join('\n');
|
|
851
|
+
const collapsedCommand = 'omx ralph "Execute embedded newline task"';
|
|
852
|
+
await writeFile(join(plansDir, 'prd-exact-command-embedded-newline-task.md'), [
|
|
853
|
+
'# PRD',
|
|
854
|
+
'',
|
|
855
|
+
'Launch via omx ralph "Execute embedded',
|
|
856
|
+
'newline task"',
|
|
857
|
+
].join('\n'));
|
|
858
|
+
await writeFile(join(plansDir, 'test-spec-exact-command-embedded-newline-task.md'), '# Test Spec\n');
|
|
859
|
+
const exactOutcome = readApprovedExecutionLaunchHintOutcome(tempDir, 'ralph', { command: exactCommand });
|
|
860
|
+
assert.equal(exactOutcome.status, 'resolved');
|
|
861
|
+
if (exactOutcome.status !== 'resolved') {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
assert.equal(exactOutcome.hint.command, exactCommand);
|
|
865
|
+
assert.equal(exactOutcome.hint.task, task);
|
|
866
|
+
assert.equal(readApprovedExecutionLaunchHintOutcome(tempDir, 'ralph', { command: collapsedCommand }).status, 'absent');
|
|
867
|
+
});
|
|
868
|
+
it('ignores Team launch hints inside fenced code blocks', async () => {
|
|
869
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
870
|
+
const task = 'Execute approved issue 1314 fenced team plan';
|
|
871
|
+
await mkdir(plansDir, { recursive: true });
|
|
872
|
+
await writeFile(join(plansDir, 'prd-issue-1314-fenced-team.md'), [
|
|
873
|
+
'# PRD',
|
|
874
|
+
'',
|
|
875
|
+
'```sh',
|
|
876
|
+
`omx team 5:reviewer ${JSON.stringify(task)}`,
|
|
877
|
+
'```',
|
|
878
|
+
'',
|
|
879
|
+
`Launch via omx team 2:executor ${JSON.stringify(task)}`,
|
|
880
|
+
].join('\n'));
|
|
881
|
+
await writeFile(join(plansDir, 'test-spec-issue-1314-fenced-team.md'), '# Test Spec\n');
|
|
882
|
+
const hint = readApprovedExecutionLaunchHint(tempDir, 'team');
|
|
883
|
+
assert.ok(hint);
|
|
884
|
+
assert.equal(hint?.task, task);
|
|
885
|
+
assert.equal(hint?.workerCount, 2);
|
|
886
|
+
assert.equal(hint?.agentType, 'executor');
|
|
887
|
+
assert.equal(hint?.command, `omx team 2:executor ${JSON.stringify(task)}`);
|
|
888
|
+
assert.equal(hint?.contextPackStatus, 'plan-only');
|
|
889
|
+
});
|
|
890
|
+
it('ignores Team launch hints that appear only inside fenced code blocks', async () => {
|
|
891
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
892
|
+
const task = 'Execute hidden fenced team plan';
|
|
893
|
+
await mkdir(plansDir, { recursive: true });
|
|
894
|
+
await writeFile(join(plansDir, 'prd-hidden-fenced-team.md'), [
|
|
895
|
+
'# PRD',
|
|
896
|
+
'',
|
|
897
|
+
'```sh',
|
|
898
|
+
`omx team 2:executor ${JSON.stringify(task)}`,
|
|
899
|
+
'```',
|
|
900
|
+
].join('\n'));
|
|
901
|
+
await writeFile(join(plansDir, 'test-spec-hidden-fenced-team.md'), '# Test Spec\n');
|
|
902
|
+
const outcome = readApprovedExecutionLaunchHintOutcome(tempDir, 'team', { task });
|
|
903
|
+
assert.equal(outcome.status, 'absent');
|
|
904
|
+
});
|
|
905
|
+
it('ignores adversarial hidden Team launch hints that try to break out with deceptive fence lines', async () => {
|
|
906
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
907
|
+
const task = 'Execute adversarial fenced team plan';
|
|
908
|
+
await mkdir(plansDir, { recursive: true });
|
|
909
|
+
await writeFile(join(plansDir, 'prd-adversarial-fenced-team.md'), [
|
|
910
|
+
'# PRD',
|
|
911
|
+
'',
|
|
912
|
+
'```md',
|
|
913
|
+
`omx team 9:reviewer ${JSON.stringify(task)}`,
|
|
914
|
+
' ```',
|
|
915
|
+
'```still-open',
|
|
916
|
+
'~~~',
|
|
917
|
+
`omx team 7:critic ${JSON.stringify(task)}`,
|
|
918
|
+
'```',
|
|
919
|
+
'',
|
|
920
|
+
`Launch via omx team 2:executor ${JSON.stringify(task)}`,
|
|
921
|
+
].join('\n'));
|
|
922
|
+
await writeFile(join(plansDir, 'test-spec-adversarial-fenced-team.md'), '# Test Spec\n');
|
|
923
|
+
const hint = readApprovedExecutionLaunchHint(tempDir, 'team');
|
|
924
|
+
assert.ok(hint);
|
|
925
|
+
assert.equal(hint?.task, task);
|
|
926
|
+
assert.equal(hint?.workerCount, 2);
|
|
927
|
+
assert.equal(hint?.agentType, 'executor');
|
|
928
|
+
assert.equal(hint?.command, `omx team 2:executor ${JSON.stringify(task)}`);
|
|
929
|
+
assert.equal(hint?.contextPackStatus, 'plan-only');
|
|
930
|
+
});
|
|
931
|
+
it('ignores Team launch hints inside nested commented blocks with inner fence lookalikes', async () => {
|
|
932
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
933
|
+
const task = 'Execute commented nested team plan';
|
|
934
|
+
await mkdir(plansDir, { recursive: true });
|
|
935
|
+
await writeFile(join(plansDir, 'prd-commented-nested-team.md'), [
|
|
936
|
+
'# PRD',
|
|
937
|
+
'',
|
|
938
|
+
'<!--',
|
|
939
|
+
`Launch via omx team 9:reviewer ${JSON.stringify(task)}`,
|
|
940
|
+
'<!--',
|
|
941
|
+
`omx team 7:critic ${JSON.stringify(task)}`,
|
|
942
|
+
'```md',
|
|
943
|
+
`Launch via omx team 6:debugger ${JSON.stringify(task)}`,
|
|
944
|
+
'-->',
|
|
945
|
+
'~~~',
|
|
946
|
+
'-->',
|
|
947
|
+
'',
|
|
948
|
+
`Launch via omx team 2:executor ${JSON.stringify(task)}`,
|
|
949
|
+
].join('\n'));
|
|
950
|
+
await writeFile(join(plansDir, 'test-spec-commented-nested-team.md'), '# Test Spec\n');
|
|
951
|
+
const hint = readApprovedExecutionLaunchHint(tempDir, 'team');
|
|
952
|
+
assert.ok(hint);
|
|
953
|
+
assert.equal(hint?.task, task);
|
|
954
|
+
assert.equal(hint?.workerCount, 2);
|
|
955
|
+
assert.equal(hint?.agentType, 'executor');
|
|
956
|
+
assert.equal(hint?.command, `omx team 2:executor ${JSON.stringify(task)}`);
|
|
957
|
+
assert.equal(hint?.contextPackStatus, 'plan-only');
|
|
958
|
+
});
|
|
959
|
+
it('ignores Ralph launch hints inside indented code blocks', async () => {
|
|
960
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
961
|
+
const task = 'Execute approved issue 1314 indented ralph plan';
|
|
962
|
+
await mkdir(plansDir, { recursive: true });
|
|
963
|
+
await writeFile(join(plansDir, 'prd-issue-1314-indented-ralph.md'), [
|
|
964
|
+
'# PRD',
|
|
965
|
+
'',
|
|
966
|
+
` omx ralph ${JSON.stringify(task)}`,
|
|
967
|
+
'',
|
|
968
|
+
`Launch via omx ralph ${JSON.stringify(task)}`,
|
|
969
|
+
].join('\n'));
|
|
970
|
+
await writeFile(join(plansDir, 'test-spec-issue-1314-indented-ralph.md'), '# Test Spec\n');
|
|
971
|
+
const hint = readApprovedExecutionLaunchHint(tempDir, 'ralph');
|
|
972
|
+
assert.ok(hint);
|
|
973
|
+
assert.equal(hint?.task, task);
|
|
974
|
+
assert.equal(hint?.command, `omx ralph ${JSON.stringify(task)}`);
|
|
975
|
+
assert.equal(hint?.contextPackStatus, 'plan-only');
|
|
976
|
+
});
|
|
977
|
+
it('ignores Ralph launch hints that appear only inside nested commented blocks', async () => {
|
|
978
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
979
|
+
const task = 'Execute hidden commented ralph plan';
|
|
980
|
+
await mkdir(plansDir, { recursive: true });
|
|
981
|
+
await writeFile(join(plansDir, 'prd-hidden-commented-ralph.md'), [
|
|
982
|
+
'# PRD',
|
|
983
|
+
'',
|
|
984
|
+
'<!--',
|
|
985
|
+
`Launch via omx ralph ${JSON.stringify(task)}`,
|
|
986
|
+
'<!--',
|
|
987
|
+
`omx ralph ${JSON.stringify(task)}`,
|
|
988
|
+
'```md',
|
|
989
|
+
`Launch via omx ralph ${JSON.stringify(task)}`,
|
|
990
|
+
'-->',
|
|
991
|
+
'-->',
|
|
992
|
+
].join('\n'));
|
|
993
|
+
await writeFile(join(plansDir, 'test-spec-hidden-commented-ralph.md'), '# Test Spec\n');
|
|
994
|
+
const outcome = readApprovedExecutionLaunchHintOutcome(tempDir, 'ralph', { task });
|
|
995
|
+
assert.equal(outcome.status, 'absent');
|
|
996
|
+
});
|
|
468
997
|
it('honors the requested team task when a single plan lists multiple team launch hints', async () => {
|
|
469
998
|
const plansDir = join(tempDir, '.omx', 'plans');
|
|
470
999
|
await mkdir(plansDir, { recursive: true });
|