Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to the claude-plugins project will be documented in this fil

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Entries are listed newest-first; each plugin section is treated as released when merged to `main`.

### code v1.16.4

#### Fixed
- `codex-review`'s `run_codex_review.sh` no longer passes `--full-auto`, which codex-cli 0.147 removed, so every review round exited 2. It now passes `-c sandbox_mode=read-only`, which both `codex exec` and `codex exec resume` accept; `-s read-only` would have broken every resumed round. The reviewer's sandbox narrows from `--full-auto`'s workspace-write to read-only.
- `run_codex_review.sh` keeps codex's stderr instead of discarding it. `CODEX_FAILED` now carries one line after the exit code saying why codex failed: the last `turn.failed` (else `error`) message from the JSON stream, otherwise the first stderr line starting with `error`, otherwise the last stderr line, skipping the `Reading ... from stdin...` banner. The full stderr goes to the script's stderr, and a failed session resume names its cause before falling back to a fresh session.
- `debate-loop.sh` prints the `CODEX_FAILED` reason with `printf '%s'` instead of `echo -e`, so backslashes in codex's message are printed as-is instead of being read as escapes.
- `run_codex_review.sh` also passes `-c approval_policy=never`. `codex exec` defaults approval to `never` but drops that default when the user's config sets `approvals_reviewer = "auto_review"`, which left the user's `approval_policy` in effect.
- `run_codex_review.sh` writes codex's stderr to the script's stderr when it reports `CODEX_EMPTY`, as it already does for `CODEX_FAILED`.
- `hooks/plan-review.sh` passes `-c sandbox_mode=read-only -c approval_policy=never` instead of `--full-auto`, and appends codex's stderr to its debug log instead of discarding it.

### code-review v3.10.3

#### Fixed
Expand Down
2 changes: 1 addition & 1 deletion plugins/code/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "code",
"description": "Code and planning framework plugin",
"version": "1.16.3",
"version": "1.16.4",
"author": {
"name": "ClosedLoop",
"email": "support@closedloop.ai"
Expand Down
2 changes: 1 addition & 1 deletion plugins/code/hooks/plan-review.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ EOF

# Get Codex's review using stdin to avoid shell escaping issues
log "Calling codex exec..."
REVIEW=$(codex exec --full-auto -m "gpt-5.3-codex-spark" < "$TMPFILE" 2>/dev/null)
REVIEW=$(codex exec -c sandbox_mode=read-only -c approval_policy=never -m "gpt-5.3-codex-spark" < "$TMPFILE" 2>>"$LOG_FILE")

# If codex failed, exit silently
if [ -z "$REVIEW" ]; then
Expand Down
2 changes: 1 addition & 1 deletion plugins/code/scripts/debate-loop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ while [[ $round -le $MAX_ROUNDS ]]; do

# Handle failures and empty responses
if [[ "$CODEX_VERDICT" == FAILED:* ]]; then
echo -e "${RED}Error: Codex failed: ${CODEX_VERDICT#FAILED:}${NC}" >&2
printf '%bError: Codex failed: %s%b\n' "$RED" "${CODEX_VERDICT#FAILED:}" "$NC" >&2
exit 1
fi

Expand Down
51 changes: 47 additions & 4 deletions plugins/code/skills/codex-review/scripts/run_codex_review.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT

codex_json="$tmp_dir/codex_output.json"
codex_stderr="$tmp_dir/codex_stderr.txt"
prompt_file="$tmp_dir/prompt.txt"

# ── Build the review prompt ──────────────────────────────────────────────────
Expand Down Expand Up @@ -256,20 +257,57 @@ sys.stdout.write('\n'.join(lines))
" "$json_file" > "$output_file" 2>/dev/null
}

# Extract the failure message from the JSON stream: the last turn.failed error,
# else the last top-level error event. Prints it on one line, or nothing.
parse_failure_message() {
python3 -c "
import json, sys
turn = err = ''
for line in open(sys.argv[1]):
try:
e = json.loads(line.strip())
if e.get('type') == 'turn.failed':
turn = e['error']['message'] or turn
elif e.get('type') == 'error':
err = e['message'] or err
except Exception:
pass
print(' '.join(str(turn or err).split()))
" "$1" 2>/dev/null || true
}

# ── Run codex ────────────────────────────────────────────────────────────────

run_codex_cmd() {
local json_out="$1"; shift
# Log round header
printf '\n--- Round %s | %s ---\n' "$ROUND" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$LOG_FILE"
# Tee raw JSON stream to both the capture file and the persistent log
codex "$@" 2>/dev/null | tee -a "$LOG_FILE" > "$json_out"
codex "$@" 2>"$codex_stderr" | tee -a "$LOG_FILE" > "$json_out"
}

# One line saying why the last codex run failed. Turn failures arrive in the JSON
# stream; CLI and config errors only on stderr, where the first line starting with
# "error" wins, else the last line, skipping the "Reading ... stdin..." banner.
codex_failure_reason() {
local reason lines
reason=$(parse_failure_message "$codex_json")
if [[ -n "$reason" ]]; then
echo "$reason"
return
fi
lines=$(tr -d '\r' < "$codex_stderr" 2>/dev/null | grep -v -e '^[[:space:]]*$' -e '^Reading .*stdin\.\.\.$') || true
grep -i -m1 '^error' <<<"$lines" || tail -n1 <<<"$lines"
}

effective_session_id="$SESSION_ID"
codex_exit=0

base_args=(--full-auto --json -m "$CODEX_MODEL" -c model_reasoning_effort=high)
# `-c sandbox_mode=` rather than `--full-auto` (removed in codex-cli 0.147) or
Comment thread
peterulsteen marked this conversation as resolved.
# `-s` (rejected by `codex exec resume`), so one arg set serves both calls.
# approval_policy is explicit because exec drops its own `never` default when the
# user's config sets `approvals_reviewer = "auto_review"`.
base_args=(--json -m "$CODEX_MODEL" -c sandbox_mode=read-only -c approval_policy=never -c model_reasoning_effort=high)
prompt_content=$(cat "$prompt_file")

# Attempt session resume if we have a prior session ID
Expand All @@ -292,7 +330,8 @@ if [[ -n "$SESSION_ID" ]]; then
# Resume succeeded -- skip to verdict extraction
:
else
echo "Codex session resume failed, starting fresh session..." >&2
resume_reason=$(codex_failure_reason)
echo "Codex session resume failed${resume_reason:+ ($resume_reason)}, starting fresh session..." >&2
effective_session_id=""
rm -f "$codex_json"

Expand Down Expand Up @@ -330,14 +369,17 @@ feedback_content=$(cat "$FEEDBACK_FILE" 2>/dev/null || echo "")

# Handle failures
if [[ $codex_exit -ne 0 ]] && [[ -z "$feedback_content" ]]; then
echo "CODEX_FAILED:codex exited with code $codex_exit"
cat "$codex_stderr" >&2 2>/dev/null || true
Comment thread
peterulsteen marked this conversation as resolved.
reason=$(codex_failure_reason)
echo "CODEX_FAILED:codex exited with code $codex_exit${reason:+: $reason}"
echo "CODEX_SESSION:${effective_session_id:-none}"
echo "LOG_ID:$LOG_ID"
exit 0
fi

# Handle empty response
if [[ -z "$feedback_content" ]]; then
cat "$codex_stderr" >&2 2>/dev/null || true
echo "CODEX_EMPTY"
echo "CODEX_SESSION:${effective_session_id:-none}"
echo "LOG_ID:$LOG_ID"
Expand All @@ -354,6 +396,7 @@ elif echo "$feedback_content" | grep -q "^### Finding"; then
echo "VERDICT:NEEDS_CHANGES"
else
# No verdict AND no findings -- likely truncated response, not a real review
cat "$codex_stderr" >&2 2>/dev/null || true
echo "CODEX_EMPTY"
fi

Expand Down
Loading
Loading