From c1200b8247a6b7bcbe21f22f4043bc54278ba2c6 Mon Sep 17 00:00:00 2001 From: Jayesh Betala Date: Mon, 4 May 2026 12:17:19 +0530 Subject: [PATCH] fix(codex): resolve python for JSON parser --- codex/SKILL.md | 21 ++++++++++++++++++--- codex/SKILL.md.tmpl | 21 ++++++++++++++++++--- test/skill-validation.test.ts | 11 +++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/codex/SKILL.md b/codex/SKILL.md index 60820dd20a..594693b419 100644 --- a/codex/SKILL.md +++ b/codex/SKILL.md @@ -1055,10 +1055,15 @@ If the user passed `--xhigh`, use `"xhigh"` instead of `"high"`. ```bash _REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; } +PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true) +if [ -z "$PYTHON_CMD" ]; then + echo "ERROR: Python 3 is required to parse Codex JSON output. Install python3 or python and retry." >&2 + exit 1 +fi # Fix 1+2: wrap with timeout (gtimeout/timeout fallback chain via probe helper), # capture stderr to $TMPERR for auth error detection (was: 2>/dev/null). TMPERR=${TMPERR:-$(mktemp "$TMP_ROOT/codex-err-XXXXXX.txt")} -_gstack_codex_timeout_wrapper 600 codex exec "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 python3 -u -c " +_gstack_codex_timeout_wrapper 600 codex exec "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c " import sys, json turn_completed_count = 0 for line in sys.stdin: @@ -1201,8 +1206,13 @@ If the user passed `--xhigh`, use `"xhigh"` instead of `"medium"`. For a **new session:** ```bash _REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; } +PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true) +if [ -z "$PYTHON_CMD" ]; then + echo "ERROR: Python 3 is required to parse Codex JSON output. Install python3 or python and retry." >&2 + exit 1 +fi # Fix 1: wrap with timeout (gtimeout/timeout fallback chain via probe helper) -_gstack_codex_timeout_wrapper 600 codex exec "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 python3 -u -c " +_gstack_codex_timeout_wrapper 600 codex exec "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c " import sys, json for line in sys.stdin: line = line.strip() @@ -1243,8 +1253,13 @@ fi For a **resumed session** (user chose "Continue"): ```bash _REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; } +PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true) +if [ -z "$PYTHON_CMD" ]; then + echo "ERROR: Python 3 is required to parse Codex JSON output. Install python3 or python and retry." >&2 + exit 1 +fi # Fix 1: wrap with timeout (gtimeout/timeout fallback chain via probe helper) -_gstack_codex_timeout_wrapper 600 codex exec resume "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 python3 -u -c " +_gstack_codex_timeout_wrapper 600 codex exec resume "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c " " # Fix 1: same hang detection pattern as new-session block diff --git a/codex/SKILL.md.tmpl b/codex/SKILL.md.tmpl index 1b849a82d9..53d4aa756e 100644 --- a/codex/SKILL.md.tmpl +++ b/codex/SKILL.md.tmpl @@ -284,10 +284,15 @@ If the user passed `--xhigh`, use `"xhigh"` instead of `"high"`. ```bash _REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; } +PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true) +if [ -z "$PYTHON_CMD" ]; then + echo "ERROR: Python 3 is required to parse Codex JSON output. Install python3 or python and retry." >&2 + exit 1 +fi # Fix 1+2: wrap with timeout (gtimeout/timeout fallback chain via probe helper), # capture stderr to $TMPERR for auth error detection (was: 2>/dev/null). TMPERR=${TMPERR:-$(mktemp "$TMP_ROOT/codex-err-XXXXXX.txt")} -_gstack_codex_timeout_wrapper 600 codex exec "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 python3 -u -c " +_gstack_codex_timeout_wrapper 600 codex exec "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c " import sys, json turn_completed_count = 0 for line in sys.stdin: @@ -430,8 +435,13 @@ If the user passed `--xhigh`, use `"xhigh"` instead of `"medium"`. For a **new session:** ```bash _REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; } +PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true) +if [ -z "$PYTHON_CMD" ]; then + echo "ERROR: Python 3 is required to parse Codex JSON output. Install python3 or python and retry." >&2 + exit 1 +fi # Fix 1: wrap with timeout (gtimeout/timeout fallback chain via probe helper) -_gstack_codex_timeout_wrapper 600 codex exec "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 python3 -u -c " +_gstack_codex_timeout_wrapper 600 codex exec "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c " import sys, json for line in sys.stdin: line = line.strip() @@ -472,8 +482,13 @@ fi For a **resumed session** (user chose "Continue"): ```bash _REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; } +PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true) +if [ -z "$PYTHON_CMD" ]; then + echo "ERROR: Python 3 is required to parse Codex JSON output. Install python3 or python and retry." >&2 + exit 1 +fi # Fix 1: wrap with timeout (gtimeout/timeout fallback chain via probe helper) -_gstack_codex_timeout_wrapper 600 codex exec resume "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 python3 -u -c " +_gstack_codex_timeout_wrapper 600 codex exec resume "" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c " " # Fix 1: same hang detection pattern as new-session block diff --git a/test/skill-validation.test.ts b/test/skill-validation.test.ts index b46028b518..701cf8fd42 100644 --- a/test/skill-validation.test.ts +++ b/test/skill-validation.test.ts @@ -1332,6 +1332,17 @@ describe('Codex skill', () => { expect(content).toContain('mktemp'); }); + test('codex JSON stream parser uses portable Python discovery', () => { + const files = ['codex/SKILL.md.tmpl', 'codex/SKILL.md']; + + for (const rel of files) { + const content = fs.readFileSync(path.join(ROOT, rel), 'utf-8'); + expect(content).toContain('PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true)'); + expect(content).toContain('PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c'); + expect(content).not.toContain('PYTHONUNBUFFERED=1 python3 -u -c'); + } + }); + test('adversarial review in /review always runs both passes', () => { const content = fs.readFileSync(path.join(ROOT, 'review', 'SKILL.md'), 'utf-8'); expect(content).toContain('Adversarial review (always-on)');