diff --git a/codex/SKILL.md b/codex/SKILL.md index 826e50511..d74a52588 100644 --- a/codex/SKILL.md +++ b/codex/SKILL.md @@ -814,7 +814,7 @@ assumptions, catches things you might miss. Present its output faithfully, not s ## Step 0.4: Check codex binary ```bash -CODEX_BIN=$(which codex 2>/dev/null || echo "") +CODEX_BIN=$(command -v codex || echo "") [ -z "$CODEX_BIN" ] && echo "NOT_FOUND" || echo "FOUND: $CODEX_BIN" ``` diff --git a/codex/SKILL.md.tmpl b/codex/SKILL.md.tmpl index 108fca055..6cda8a5a2 100644 --- a/codex/SKILL.md.tmpl +++ b/codex/SKILL.md.tmpl @@ -42,7 +42,7 @@ assumptions, catches things you might miss. Present its output faithfully, not s ## Step 0.4: Check codex binary ```bash -CODEX_BIN=$(which codex 2>/dev/null || echo "") +CODEX_BIN=$(command -v codex || echo "") [ -z "$CODEX_BIN" ] && echo "NOT_FOUND" || echo "FOUND: $CODEX_BIN" ``` diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index 00a5f0f2e..bc52edc10 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -1090,7 +1090,7 @@ If user chooses B, skip this step and continue. **Check Codex availability:** ```bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" ``` **If Codex is available**, launch both voices simultaneously: diff --git a/design-review/SKILL.md b/design-review/SKILL.md index 91603dd2e..b584ada8f 100644 --- a/design-review/SKILL.md +++ b/design-review/SKILL.md @@ -1687,7 +1687,7 @@ Record baseline design score and AI slop score at end of Phase 6. **Check Codex availability:** ```bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" ``` **If Codex is available**, launch both voices simultaneously: diff --git a/office-hours/SKILL.md b/office-hours/SKILL.md index c4acb9ea8..b8b6fe1f9 100644 --- a/office-hours/SKILL.md +++ b/office-hours/SKILL.md @@ -1219,7 +1219,7 @@ Use AskUserQuestion to confirm. If the user disagrees with a premise, revise und **Binary check first:** ```bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" ``` Use AskUserQuestion (regardless of codex availability): @@ -1491,7 +1491,7 @@ The screenshot file at `/tmp/gstack-sketch.png` can be referenced by downstream After the wireframe is approved, offer outside design perspectives: ```bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" ``` If Codex is available, use AskUserQuestion: diff --git a/plan-ceo-review/SKILL.md b/plan-ceo-review/SKILL.md index 91c1cfc79..a0b24ef99 100644 --- a/plan-ceo-review/SKILL.md +++ b/plan-ceo-review/SKILL.md @@ -1613,7 +1613,7 @@ thorough review. **Check tool availability:** ```bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" ``` Use AskUserQuestion: diff --git a/plan-design-review/SKILL.md b/plan-design-review/SKILL.md index 580268767..45b56bf4d 100644 --- a/plan-design-review/SKILL.md +++ b/plan-design-review/SKILL.md @@ -1241,7 +1241,7 @@ If user chooses B, skip this step and continue. **Check Codex availability:** ```bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" ``` **If Codex is available**, launch both voices simultaneously: diff --git a/plan-devex-review/SKILL.md b/plan-devex-review/SKILL.md index 29014b4a4..371d07a75 100644 --- a/plan-devex-review/SKILL.md +++ b/plan-devex-review/SKILL.md @@ -1585,7 +1585,7 @@ thorough review. **Check tool availability:** ```bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" ``` Use AskUserQuestion: diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index 1dbc3c96e..925daab13 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -1214,7 +1214,7 @@ thorough review. **Check tool availability:** ```bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" ``` Use AskUserQuestion: diff --git a/review/SKILL.md b/review/SKILL.md index ee065f032..d7e84cbaa 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -1578,7 +1578,7 @@ DIFF_BASE=$(git merge-base origin/ HEAD) DIFF_INS=$(git diff "$DIFF_BASE" --stat | tail -1 | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo "0") DIFF_DEL=$(git diff "$DIFF_BASE" --stat | tail -1 | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+' || echo "0") DIFF_TOTAL=$((DIFF_INS + DIFF_DEL)) -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" # Legacy opt-out — only gates Codex passes, Claude always runs OLD_CFG=$(~/.claude/skills/gstack/bin/gstack-config get codex_reviews 2>/dev/null || true) echo "DIFF_SIZE: $DIFF_TOTAL" diff --git a/scripts/resolvers/design.ts b/scripts/resolvers/design.ts index fc6d6ecee..33247aab5 100644 --- a/scripts/resolvers/design.ts +++ b/scripts/resolvers/design.ts @@ -10,7 +10,7 @@ export function generateDesignReviewLite(ctx: TemplateContext): string { 7. **Codex design voice** (optional, automatic if available): \`\`\`bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" \`\`\` If Codex is available, run a lightweight design check on the diff: @@ -512,7 +512,7 @@ The screenshot file at \`/tmp/gstack-sketch.png\` can be referenced by downstrea After the wireframe is approved, offer outside design perspectives: \`\`\`bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" \`\`\` If Codex is available, use AskUserQuestion: @@ -688,7 +688,7 @@ ${optInSection} **Check Codex availability:** \`\`\`bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" \`\`\` **If Codex is available**, launch both voices simultaneously: diff --git a/scripts/resolvers/review.ts b/scripts/resolvers/review.ts index 9839a2023..0c7cb8230 100644 --- a/scripts/resolvers/review.ts +++ b/scripts/resolvers/review.ts @@ -311,7 +311,7 @@ export function generateCodexSecondOpinion(ctx: TemplateContext): string { **Binary check first:** \`\`\`bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" \`\`\` Use AskUserQuestion (regardless of codex availability): @@ -471,7 +471,7 @@ DIFF_BASE=$(git merge-base origin/ HEAD) DIFF_INS=$(git diff "$DIFF_BASE" --stat | tail -1 | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo "0") DIFF_DEL=$(git diff "$DIFF_BASE" --stat | tail -1 | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+' || echo "0") DIFF_TOTAL=$((DIFF_INS + DIFF_DEL)) -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" # Legacy opt-out — only gates Codex passes, Claude always runs OLD_CFG=$(~/.claude/skills/gstack/bin/gstack-config get codex_reviews 2>/dev/null || true) echo "DIFF_SIZE: $DIFF_TOTAL" @@ -600,7 +600,7 @@ thorough review. **Check tool availability:** \`\`\`bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" \`\`\` Use AskUserQuestion: diff --git a/ship/SKILL.md b/ship/SKILL.md index 0cb0fe529..481f1bfd4 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -1962,7 +1962,7 @@ Substitute: TIMESTAMP = ISO 8601 datetime, STATUS = "clean" if 0 findings or "is 7. **Codex design voice** (optional, automatic if available): ```bash -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" ``` If Codex is available, run a lightweight design check on the diff: @@ -2317,7 +2317,7 @@ DIFF_BASE=$(git merge-base origin/ HEAD) DIFF_INS=$(git diff "$DIFF_BASE" --stat | tail -1 | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo "0") DIFF_DEL=$(git diff "$DIFF_BASE" --stat | tail -1 | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+' || echo "0") DIFF_TOTAL=$((DIFF_INS + DIFF_DEL)) -which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" # Legacy opt-out — only gates Codex passes, Claude always runs OLD_CFG=$(~/.claude/skills/gstack/bin/gstack-config get codex_reviews 2>/dev/null || true) echo "DIFF_SIZE: $DIFF_TOTAL" diff --git a/test/skill-e2e-plan.test.ts b/test/skill-e2e-plan.test.ts index cb630ca97..d6f58416e 100644 --- a/test/skill-e2e-plan.test.ts +++ b/test/skill-e2e-plan.test.ts @@ -775,8 +775,8 @@ Write your summary to ${testDir}/${testName}-summary.md`, expect(fs.existsSync(summaryPath)).toBe(true); const summary = fs.readFileSync(summaryPath, 'utf-8').toLowerCase(); - // All skills should have codex availability check - expect(summary).toMatch(/which codex/); + // All skills should have codex availability check (command -v per #1197) + expect(summary).toMatch(/command -v codex/); // All skills should have fallback behavior expect(summary).toMatch(/fallback|subagent|unavailable|not available|skip/); // All skills should show it's optional/non-blocking diff --git a/test/skill-validation.test.ts b/test/skill-validation.test.ts index ed3c32611..7df535552 100644 --- a/test/skill-validation.test.ts +++ b/test/skill-validation.test.ts @@ -1325,10 +1325,14 @@ describe('Codex skill', () => { expect(content).toContain('gstack-review-log'); }); - test('codex/SKILL.md uses which for binary discovery, not hardcoded path', () => { + test('codex/SKILL.md uses command -v for binary discovery, not hardcoded path', () => { const content = fs.readFileSync(path.join(ROOT, 'codex', 'SKILL.md'), 'utf-8'); - expect(content).toContain('which codex'); + expect(content).toContain('command -v codex'); expect(content).not.toContain('/opt/homebrew/bin/codex'); + // Defensive: catch any future regression that reintroduces `which codex`, + // which fails in environments where `which` isn't on PATH (some Windows + // shells, BusyBox-only containers). #1197. + expect(content).not.toContain('which codex'); }); test('codex/SKILL.md contains error handling for missing binary and auth', () => {