mirror of
https://github.com/anthropics/claude-plugins-official.git
synced 2026-03-16 22:23:07 +00:00
fix(ralph-loop): stop hook fails when last assistant block is tool_use
Claude Code writes each assistant content block (text/tool_use/thinking) as its own JSONL line. The hook's `grep role:assistant | tail -1` would grab whichever block happened to be last — often tool_use — then jq's text filter returned empty string, triggering the 'no text content' path which deletes the state file and exits without blocking. Net effect: the loop silently never fires. In one observed session, 62% of assistant lines were tool_use-only; the hook deleted state on the very first Stop event every time. Fix: slurp all assistant lines with jq -rs, flatten to text blocks only, take the last. Empty result is now non-fatal — no text means no <promise> tag, so the loop continues. Also absorbs jq parse errors (control chars in text) via || fallback instead of aborting under set -e.
This commit is contained in:
@@ -77,39 +77,19 @@ if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH"; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract last assistant message with explicit error handling
|
# Extract the most recent assistant text block.
|
||||||
LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1)
|
#
|
||||||
if [[ -z "$LAST_LINE" ]]; then
|
# Claude Code writes each content block (text/tool_use/thinking) as its own
|
||||||
echo "⚠️ Ralph loop: Failed to extract last assistant message" >&2
|
# JSONL line, all with role=assistant. `tail -1` alone would often grab a
|
||||||
echo " Ralph loop is stopping." >&2
|
# tool_use or thinking block, leaving no text to check. Instead, slurp all
|
||||||
rm "$RALPH_STATE_FILE"
|
# assistant lines, flatten to text blocks only, and take the last one.
|
||||||
exit 0
|
#
|
||||||
fi
|
# `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 <promise> tag,
|
||||||
# Parse JSON with error handling
|
# so the loop simply continues.
|
||||||
LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r '
|
LAST_OUTPUT=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | jq -rs '
|
||||||
.message.content |
|
map(.message.content[]? | select(.type == "text") | .text) | last // ""
|
||||||
map(select(.type == "text")) |
|
' 2>/dev/null) || LAST_OUTPUT=""
|
||||||
map(.text) |
|
|
||||||
join("\n")
|
|
||||||
' 2>&1)
|
|
||||||
|
|
||||||
# Check if jq succeeded
|
|
||||||
if [[ $? -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
|
|
||||||
|
|
||||||
if [[ -z "$LAST_OUTPUT" ]]; then
|
|
||||||
echo "⚠️ Ralph loop: Assistant message contained no text content" >&2
|
|
||||||
echo " Ralph loop is stopping." >&2
|
|
||||||
rm "$RALPH_STATE_FILE"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check for completion promise (only if set)
|
# Check for completion promise (only if set)
|
||||||
if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
|
if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user