diff --git a/lib/circuit_breaker.sh b/lib/circuit_breaker.sh index e681cae..3e11460 100644 --- a/lib/circuit_breaker.sh +++ b/lib/circuit_breaker.sh @@ -3,6 +3,9 @@ # Prevents runaway token consumption by detecting stagnation # Based on Michael Nygard's "Release It!" pattern +# Source date utilities for cross-platform compatibility +source "$(dirname "${BASH_SOURCE[0]}")/date_utils.sh" + # Circuit Breaker States CB_STATE_CLOSED="CLOSED" # Normal operation, progress detected CB_STATE_HALF_OPEN="HALF_OPEN" # Monitoring mode, checking for recovery @@ -36,7 +39,7 @@ init_circuit_breaker() { cat > "$CB_STATE_FILE" << EOF { "state": "$CB_STATE_CLOSED", - "last_change": "$(date -Iseconds)", + "last_change": "$(get_iso_timestamp)", "consecutive_no_progress": 0, "consecutive_same_error": 0, "last_progress_loop": 0, @@ -164,7 +167,7 @@ record_loop_result() { cat > "$CB_STATE_FILE" << EOF { "state": "$new_state", - "last_change": "$(date -Iseconds)", + "last_change": "$(get_iso_timestamp)", "consecutive_no_progress": $consecutive_no_progress, "consecutive_same_error": $consecutive_same_error, "last_progress_loop": $last_progress_loop, @@ -196,7 +199,7 @@ log_circuit_transition() { local history=$(cat "$CB_HISTORY_FILE") local transition="{ - \"timestamp\": \"$(date -Iseconds)\", + \"timestamp\": \"$(get_iso_timestamp)\", \"loop\": $loop_number, \"from_state\": \"$from_state\", \"to_state\": \"$to_state\", @@ -272,7 +275,7 @@ reset_circuit_breaker() { cat > "$CB_STATE_FILE" << EOF { "state": "$CB_STATE_CLOSED", - "last_change": "$(date -Iseconds)", + "last_change": "$(get_iso_timestamp)", "consecutive_no_progress": 0, "consecutive_same_error": 0, "last_progress_loop": 0, diff --git a/lib/date_utils.sh b/lib/date_utils.sh new file mode 100644 index 0000000..1e92b3b --- /dev/null +++ b/lib/date_utils.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# date_utils.sh - Cross-platform date utility functions +# Provides consistent date formatting and arithmetic across GNU (Linux) and BSD (macOS) systems + +# Get current timestamp in ISO 8601 format with seconds precision +# Returns: YYYY-MM-DDTHH:MM:SS+00:00 format +get_iso_timestamp() { + local os_type + os_type=$(uname) + + if [[ "$os_type" == "Darwin" ]]; then + # macOS (BSD date) + # Use manual formatting and add colon to timezone offset + date -u +"%Y-%m-%dT%H:%M:%S%z" | sed 's/\(..\)$/:\1/' + else + # Linux (GNU date) - use -u flag for UTC + date -u -Iseconds + fi +} + +# Get time component (HH:MM:SS) for one hour from now +# Returns: HH:MM:SS format +get_next_hour_time() { + local os_type + os_type=$(uname) + + if [[ "$os_type" == "Darwin" ]]; then + # macOS (BSD date) - use -v flag for date arithmetic + date -v+1H '+%H:%M:%S' + else + # Linux (GNU date) - use -d flag for date arithmetic + date -d '+1 hour' '+%H:%M:%S' + fi +} + +# Get current timestamp in a basic format (fallback) +# Returns: YYYY-MM-DD HH:MM:SS format +get_basic_timestamp() { + date '+%Y-%m-%d %H:%M:%S' +} diff --git a/lib/response_analyzer.sh b/lib/response_analyzer.sh index 30f8f03..df729e0 100644 --- a/lib/response_analyzer.sh +++ b/lib/response_analyzer.sh @@ -2,6 +2,9 @@ # Response Analyzer Component for Ralph # Analyzes Claude Code output to detect completion signals, test-only loops, and progress +# Source date utilities for cross-platform compatibility +source "$(dirname "${BASH_SOURCE[0]}")/date_utils.sh" + # Response Analysis Functions # Based on expert recommendations from Martin Fowler, Michael Nygard, Sam Newman @@ -152,7 +155,7 @@ analyze_response() { cat > "$analysis_result_file" << EOF { "loop_number": $loop_number, - "timestamp": "$(date -Iseconds)", + "timestamp": "$(get_iso_timestamp)", "output_file": "$output_file", "analysis": { "has_completion_signal": $has_completion_signal, diff --git a/ralph_loop.sh b/ralph_loop.sh index 6881366..7cb03c4 100755 --- a/ralph_loop.sh +++ b/ralph_loop.sh @@ -7,6 +7,7 @@ set -e # Exit on any error # Source library components SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/lib/date_utils.sh" source "$SCRIPT_DIR/lib/response_analyzer.sh" source "$SCRIPT_DIR/lib/circuit_breaker.sh" @@ -164,14 +165,14 @@ update_status() { cat > "$STATUS_FILE" << STATUSEOF { - "timestamp": "$(date -Iseconds)", + "timestamp": "$(get_iso_timestamp)", "loop_count": $loop_count, "calls_made_this_hour": $calls_made, "max_calls_per_hour": $MAX_CALLS_PER_HOUR, "last_action": "$last_action", "status": "$status", "exit_reason": "$exit_reason", - "next_reset": "$(date -d '+1 hour' -Iseconds | cut -d'T' -f2 | cut -d'+' -f1)" + "next_reset": "$(get_next_hour_time)" } STATUSEOF }