From 028eccf5447b1f226f5c98a575dad6ee704b2bca Mon Sep 17 00:00:00 2001 From: sfishman Date: Wed, 4 Mar 2026 00:25:42 +0000 Subject: [PATCH] address review: bound grep to tail -n 100; restore explicit error paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Split pipeline into two steps (extract lines, then parse) mirroring the original structure. - set +e around the jq call so failures reach the $? check instead of aborting under set -e. - The "no text content" branch remains removed (that was the original bug — all-tool-use turns now correctly yield empty text and the loop continues). --- plugins/ralph-loop/hooks/stop-hook.sh | 34 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/plugins/ralph-loop/hooks/stop-hook.sh b/plugins/ralph-loop/hooks/stop-hook.sh index ef83af4..7edf13b 100755 --- a/plugins/ralph-loop/hooks/stop-hook.sh +++ b/plugins/ralph-loop/hooks/stop-hook.sh @@ -90,16 +90,40 @@ fi # Extract the most recent assistant text block. # # Claude Code writes each content block (text/tool_use/thinking) as its own -# JSONL line, all with role=assistant. `tail -1` alone would often grab a -# tool_use or thinking block, leaving no text to check. Instead, slurp all -# assistant lines, flatten to text blocks only, and take the last one. +# JSONL line, all with role=assistant. So slurp the last N assistant lines, +# flatten to text blocks only, and take the last one. # +# Capped at the last 100 assistant lines to keep jq's slurp input bounded +# for long-running sessions. +LAST_LINES=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -n 100) +if [[ -z "$LAST_LINES" ]]; then + echo "⚠️ Ralph loop: Failed to extract assistant messages" >&2 + echo " Ralph loop is stopping." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi + +# Parse the recent lines and pull out the final text block. # `last // ""` yields empty string when no text blocks exist (e.g. a turn # that is all tool calls). That's fine: empty text means no tag, # so the loop simply continues. -LAST_OUTPUT=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | jq -rs ' +# (Briefly disable errexit so a jq failure can be caught by the $? check.) +set +e +LAST_OUTPUT=$(echo "$LAST_LINES" | jq -rs ' map(.message.content[]? | select(.type == "text") | .text) | last // "" -' 2>/dev/null) || LAST_OUTPUT="" +' 2>&1) +JQ_EXIT=$? +set -e + +# Check if jq succeeded +if [[ $JQ_EXIT -ne 0 ]]; then + echo "⚠️ Ralph loop: Failed to parse assistant message JSON" >&2 + echo " Error: $LAST_OUTPUT" >&2 + echo " This may indicate a transcript format issue." >&2 + echo " Ralph loop is stopping." >&2 + rm "$RALPH_STATE_FILE" + exit 0 +fi # Check for completion promise (only if set) if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then