fix(live-mode): crash with text output format (#164)
Live/monitor mode crashed with `stdbuf: unrecognized option '--verbose'` when CLAUDE_OUTPUT_FORMAT="text" because build_claude_command() was only called inside a JSON-only gate, leaving CLAUDE_CMD_ARGS empty. Three coordinated fixes: - Override text→json when live mode is active (stream-json requires JSON) - Always call build_claude_command() regardless of output format - Add safety check for empty CLAUDE_CMD_ARGS before live mode construction Also updates help text for --live and --output-format to document the auto-switch behavior.
This commit is contained in:
parent
204761fa8e
commit
4027ac929a
2 changed files with 115 additions and 12 deletions
|
|
@ -1048,21 +1048,20 @@ execute_claude_code() {
|
|||
session_id=$(init_claude_session)
|
||||
fi
|
||||
|
||||
# Live mode requires JSON output (stream-json) — override text format
|
||||
if [[ "$LIVE_OUTPUT" == "true" && "$CLAUDE_OUTPUT_FORMAT" == "text" ]]; then
|
||||
log_status "WARN" "Live mode requires JSON output format. Overriding text → json for this session."
|
||||
CLAUDE_OUTPUT_FORMAT="json"
|
||||
fi
|
||||
|
||||
# Build the Claude CLI command with modern flags
|
||||
# Note: We use the modern CLI with -p flag when CLAUDE_OUTPUT_FORMAT is "json"
|
||||
# For backward compatibility, fall back to stdin piping for text mode
|
||||
local use_modern_cli=false
|
||||
|
||||
if [[ "$CLAUDE_OUTPUT_FORMAT" == "json" ]]; then
|
||||
# Modern approach: use CLI flags (builds CLAUDE_CMD_ARGS array)
|
||||
if build_claude_command "$PROMPT_FILE" "$loop_context" "$session_id"; then
|
||||
use_modern_cli=true
|
||||
log_status "INFO" "Using modern CLI mode (JSON output)"
|
||||
else
|
||||
log_status "WARN" "Failed to build modern CLI command, falling back to legacy mode"
|
||||
fi
|
||||
if build_claude_command "$PROMPT_FILE" "$loop_context" "$session_id"; then
|
||||
use_modern_cli=true
|
||||
log_status "INFO" "Using modern CLI mode (${CLAUDE_OUTPUT_FORMAT} output)"
|
||||
else
|
||||
log_status "INFO" "Using legacy CLI mode (text output)"
|
||||
log_status "WARN" "Failed to build modern CLI command, falling back to legacy mode"
|
||||
fi
|
||||
|
||||
# Execute Claude Code
|
||||
|
|
@ -1091,6 +1090,14 @@ execute_claude_code() {
|
|||
fi
|
||||
fi
|
||||
|
||||
if [[ "$LIVE_OUTPUT" == "true" ]]; then
|
||||
# Safety check: CLAUDE_CMD_ARGS must be populated for live mode
|
||||
if [[ ${#CLAUDE_CMD_ARGS[@]} -eq 0 ]]; then
|
||||
log_status "ERROR" "CLAUDE_CMD_ARGS is empty — cannot build live mode command. Falling back to background mode."
|
||||
LIVE_OUTPUT=false
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$LIVE_OUTPUT" == "true" ]]; then
|
||||
log_status "INFO" "📺 Live output mode enabled - showing Claude Code streaming..."
|
||||
echo -e "${PURPLE}━━━━━━━━━━━━━━━━ Claude Code Output ━━━━━━━━━━━━━━━━${NC}"
|
||||
|
|
@ -1613,7 +1620,7 @@ Options:
|
|||
-s, --status Show current status and exit
|
||||
-m, --monitor Start with tmux session and live monitor (requires tmux)
|
||||
-v, --verbose Show detailed progress updates during execution
|
||||
-l, --live Show Claude Code output in real-time (streaming mode)
|
||||
-l, --live Show Claude Code output in real-time (auto-switches to JSON output)
|
||||
-t, --timeout MIN Set Claude Code execution timeout in minutes (default: $CLAUDE_TIMEOUT_MINUTES)
|
||||
--reset-circuit Reset circuit breaker to CLOSED state
|
||||
--circuit-status Show circuit breaker status and exit
|
||||
|
|
@ -1622,6 +1629,7 @@ Options:
|
|||
|
||||
Modern CLI Options (Phase 1.1):
|
||||
--output-format FORMAT Set Claude output format: json or text (default: $CLAUDE_OUTPUT_FORMAT)
|
||||
Note: --live mode requires JSON and will auto-switch
|
||||
--allowed-tools TOOLS Comma-separated list of allowed tools (default: $CLAUDE_ALLOWED_TOOLS)
|
||||
--no-continue Disable session continuity across loops
|
||||
--session-expiry HOURS Set session expiration time in hours (default: $CLAUDE_SESSION_EXPIRY_HOURS)
|
||||
|
|
|
|||
|
|
@ -667,3 +667,98 @@ EOF
|
|||
[[ "$output" == *'_env_MAX_CALLS_PER_HOUR'* ]]
|
||||
[[ "$output" == *'_env_CLAUDE_TIMEOUT_MINUTES'* ]]
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# LIVE MODE + TEXT FORMAT FIX TESTS (Issue #164)
|
||||
# Tests for: live mode format override, always-call build_claude_command,
|
||||
# and safety check for empty CLAUDE_CMD_ARGS
|
||||
# =============================================================================
|
||||
|
||||
@test "build_claude_command works for text format (populates CLAUDE_CMD_ARGS)" {
|
||||
export CLAUDE_CODE_CMD="claude"
|
||||
export CLAUDE_OUTPUT_FORMAT="text"
|
||||
export CLAUDE_ALLOWED_TOOLS="Write,Read"
|
||||
export CLAUDE_USE_CONTINUE="false"
|
||||
|
||||
echo "Test prompt content" > "$PROMPT_FILE"
|
||||
|
||||
build_claude_command "$PROMPT_FILE" "" ""
|
||||
|
||||
# CLAUDE_CMD_ARGS should be populated even in text mode
|
||||
[[ ${#CLAUDE_CMD_ARGS[@]} -gt 0 ]]
|
||||
|
||||
local cmd_string="${CLAUDE_CMD_ARGS[*]}"
|
||||
|
||||
# Should contain claude command and -p flag
|
||||
[[ "$cmd_string" == *"claude"* ]]
|
||||
[[ "$cmd_string" == *"-p"* ]]
|
||||
[[ "$cmd_string" == *"Test prompt content"* ]]
|
||||
|
||||
# Should NOT contain --output-format (text mode omits it)
|
||||
[[ "$cmd_string" != *"--output-format"* ]]
|
||||
|
||||
# Should still include allowed tools
|
||||
[[ "$cmd_string" == *"--allowedTools"* ]]
|
||||
[[ "$cmd_string" == *"Write"* ]]
|
||||
}
|
||||
|
||||
@test "build_claude_command works for json format (includes --output-format json)" {
|
||||
export CLAUDE_CODE_CMD="claude"
|
||||
export CLAUDE_OUTPUT_FORMAT="json"
|
||||
export CLAUDE_ALLOWED_TOOLS=""
|
||||
export CLAUDE_USE_CONTINUE="false"
|
||||
|
||||
echo "Test prompt" > "$PROMPT_FILE"
|
||||
|
||||
build_claude_command "$PROMPT_FILE" "" ""
|
||||
|
||||
local cmd_string="${CLAUDE_CMD_ARGS[*]}"
|
||||
|
||||
# Should contain --output-format json
|
||||
[[ "$cmd_string" == *"--output-format"* ]]
|
||||
[[ "$cmd_string" == *"json"* ]]
|
||||
[[ "$cmd_string" == *"-p"* ]]
|
||||
}
|
||||
|
||||
@test "live mode overrides text to json format in ralph_loop.sh" {
|
||||
# Verify ralph_loop.sh contains the live mode format override logic
|
||||
run grep -A3 'LIVE_OUTPUT.*true.*CLAUDE_OUTPUT_FORMAT.*text' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
||||
|
||||
# Should find the override block
|
||||
[[ "$output" == *"CLAUDE_OUTPUT_FORMAT"* ]]
|
||||
[[ "$output" == *"json"* ]]
|
||||
}
|
||||
|
||||
@test "live mode format override preserves json format unchanged" {
|
||||
# The override should only trigger when format is "text", not "json"
|
||||
# Verify the condition checks for text specifically
|
||||
run grep 'CLAUDE_OUTPUT_FORMAT.*text' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
||||
|
||||
# Should check specifically for "text" (not a blanket override)
|
||||
[[ "$output" == *'"text"'* ]]
|
||||
}
|
||||
|
||||
@test "safety check prevents live mode with empty CLAUDE_CMD_ARGS" {
|
||||
# Verify ralph_loop.sh has the safety check for empty CLAUDE_CMD_ARGS
|
||||
run grep -A3 'CLAUDE_CMD_ARGS.*-eq 0' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
||||
|
||||
# Should find safety check that falls back to background mode
|
||||
[[ "$output" == *"LIVE_OUTPUT"* ]] || [[ "$output" == *"empty"* ]] || [[ "$output" == *"background"* ]]
|
||||
}
|
||||
|
||||
@test "build_claude_command is called regardless of output format in ralph_loop.sh" {
|
||||
# Verify that build_claude_command is NOT gated behind JSON-only check
|
||||
# The old pattern was: if [[ "$CLAUDE_OUTPUT_FORMAT" == "json" ]]; then build_claude_command...
|
||||
# The new pattern should call build_claude_command unconditionally
|
||||
|
||||
# Check that build_claude_command call is NOT inside a JSON-only conditional
|
||||
# Look for the actual call site (not the function definition or comments)
|
||||
local script="${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
||||
|
||||
# The old pattern: "json" check immediately followed by build_claude_command
|
||||
# should no longer exist as a gate
|
||||
run bash -c "sed -n '/# Build the Claude CLI command/,/execute Claude Code/p' '$script' | grep -c 'CLAUDE_OUTPUT_FORMAT.*json.*build_claude_command'"
|
||||
|
||||
# Should find 0 matches (the gate has been removed)
|
||||
[[ "$output" == "0" ]]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue