-
-
Save trkaplan/f4a618515346f025aa94c2392658f728 to your computer and use it in GitHub Desktop.
Autonomous AI Task Processor - Claude Code automation loop script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
################################################################################ | |
# AUTONOMOUS AI TASK PROCESSOR | |
# https://gist.github.com/nibzard/a97ef0a1919328bcbc6a224a5d2cfc78 | |
################################################################################ | |
# | |
# PURPOSE: | |
# Runs Claude Code in a fully autonomous loop to process tasks from a todo | |
# file without human intervention. The AI agent will continuously select, | |
# implement, and commit tasks until reaching limits or completing all work. | |
# | |
# SETUP: | |
# Copy this script and save it as loop.sh wherever you do your work. | |
# Make it executable: chmod +x loop.sh | |
# | |
# USAGE: | |
# ./loop.sh [-z] [TODO_FILE] | |
# | |
# OPTIONS: | |
# -z Use claude-zhipu CLI instead of claude | |
# | |
# ARGUMENTS: | |
# TODO_FILE Path to the todo file to process (default: todo.md) | |
# | |
# EXAMPLES: | |
# ./loop.sh # Process todo.md with claude | |
# ./loop.sh -z # Process todo.md with claude-zhipu | |
# ./loop.sh refactor-todo.md # Process specific todo file with claude | |
# ./loop.sh -z refactor-todo.md # Process specific todo file with claude-zhipu | |
# ./loop.sh ~/projects/tasks.md # Process todo file from any path | |
# | |
# ENVIRONMENT VARIABLES: | |
# MAX_ITERATIONS Maximum number of tasks to process (default: 50) | |
# VERBOSE Set to 1 to show raw JSON stream debug info (default: 0) | |
# | |
# EXAMPLE WITH ENV VARS: | |
# MAX_ITERATIONS=100 VERBOSE=1 ./loop.sh | |
# | |
# FEATURES: | |
# - Fully autonomous operation with --dangerously-skip-permissions | |
# - Fresh context for each iteration ensuring clean task execution | |
# - Real-time progress display with live stream parsing | |
# - Task status tracking and progress reporting | |
# - Git commits after each completed task | |
# - Stream-JSON output parsing with tool usage indicators | |
# - Cost and performance tracking per iteration | |
# | |
# REQUIREMENTS: | |
# - Claude Code CLI installed and in PATH (or claude-zhipu with -z flag) | |
# - jq command-line JSON processor for parsing stream output | |
# - Two subagents configured: | |
# * task-master: For todo file management (~/.claude/agents/task-master.md) | |
# * git-master: For git operations (~/.claude/agents/git-master.md) | |
# | |
# HOW IT WORKS: | |
# 1. Validates environment and checks for required tools (claude, jq) | |
# 2. For each iteration: | |
# a. Uses task-master subagent to select next priority task | |
# b. Implements the selected task autonomously | |
# c. Uses git-master subagent to commit completed work | |
# d. Updates task status in todo file | |
# e. Streams real-time progress via JSON parsing with live updates | |
# 3. Stops when: | |
# - Maximum iterations reached (default 50) | |
# - User interrupts with Ctrl+C | |
# | |
# SUBAGENTS: | |
# The script relies on two specialized subagents: | |
# - task-master: Handles todo file parsing, task prioritization, status updates | |
# Download: https://gist.github.com/nibzard/d4f97d0cade5b7204afe5ed862e42ae4 | |
# - git-master: Manages git operations, creates meaningful commits | |
# Download: https://gist.github.com/nibzard/1e5266b86c75418ce836106c607e21de | |
# | |
# OUTPUT: | |
# - Shows iteration progress and timestamps | |
# - Real-time display of AI messages, tool usage, and status updates | |
# - Fresh execution status for each iteration | |
# - Task completion status with timing and cost information | |
# - Color-coded progress indicators (green=success, red=error, cyan=info) | |
# - Verbose mode shows raw JSON stream for debugging | |
# | |
# EXIT CODES: | |
# 0 - Normal completion (reached max iterations) | |
# 1 - Missing required tools (claude or jq) | |
# 130 - User interruption (Ctrl+C) | |
# | |
################################################################################ | |
# Color codes for output | |
GREEN=$'\033[0;32m' | |
YELLOW=$'\033[1;33m' | |
RED=$'\033[0;31m' | |
BLUE=$'\033[0;34m' | |
CYAN=$'\033[0;36m' | |
NC=$'\033[0m' # No Color | |
# Parse command line arguments | |
USE_ZHIPU=0 | |
while getopts "z" opt; do | |
case $opt in | |
z) | |
USE_ZHIPU=1 | |
;; | |
\?) | |
echo "Invalid option: -$OPTARG" >&2 | |
exit 1 | |
;; | |
esac | |
done | |
shift $((OPTIND-1)) | |
# Counter for iterations | |
iteration=0 | |
# Maximum iterations before stopping (default 50) | |
MAX_ITERATIONS=${MAX_ITERATIONS:-50} | |
# Verbose mode (set to 1 for detailed output) | |
VERBOSE=${VERBOSE:-0} | |
# Removed: Session ID tracking (each iteration uses fresh context) | |
# Reset timestamp for rate limit handling (passed between functions via file) | |
RESET_TIMESTAMP="" | |
TIMESTAMP_FILE="" | |
# PID of current claude process | |
CLAUDE_PID="" | |
# Consecutive failure tracking | |
consecutive_failures=0 | |
consecutive_quick_failures=0 | |
last_failure_was_rate_limit=false | |
# Todo file to process (can be passed as first argument, defaults to todo.md) | |
TODO_FILE=${1:-todo.md} | |
# Determine which CLI to use | |
if [ $USE_ZHIPU -eq 1 ]; then | |
CLAUDE_CLI="claude-zhipu" | |
else | |
CLAUDE_CLI="claude" | |
fi | |
# Function to handle script interruption | |
cleanup() { | |
echo -e "\n${YELLOW}Script interrupted. Exiting...${NC}" | |
# Kill any remaining claude processes started by this script | |
pkill -f "claude.*--dangerously-skip-permissions.*$TODO_FILE" 2>/dev/null || true | |
exit 130 | |
} | |
# Set up trap to catch Ctrl+C and other signals | |
trap cleanup INT TERM | |
# Function to calculate wait time until rate limit resets | |
calculate_wait_time() { | |
local reset_time="$1" | |
if [ -z "$reset_time" ]; then | |
return 0 | |
fi | |
# Convert reset time to 24-hour format and calculate seconds to wait | |
local current_hour=$(date +%H) | |
local current_minute=$(date +%M) | |
local current_seconds=$((current_hour * 3600 + current_minute * 60)) | |
# Parse reset time (e.g., "2pm" -> 14:00) | |
local reset_hour | |
if [[ "$reset_time" =~ ([0-9]+)pm ]]; then | |
reset_hour=$((${BASH_REMATCH[1]} + 12)) | |
[ "$reset_hour" -eq 24 ] && reset_hour=12 # Handle 12pm | |
elif [[ "$reset_time" =~ ([0-9]+)am ]]; then | |
reset_hour=${BASH_REMATCH[1]} | |
[ "$reset_hour" -eq 12 ] && reset_hour=0 # Handle 12am | |
else | |
return 0 # Unable to parse, return 0 | |
fi | |
local reset_seconds=$((reset_hour * 3600)) | |
local wait_seconds=$((reset_seconds - current_seconds)) | |
# If reset time is in the past, add 24 hours | |
if [ $wait_seconds -le 0 ]; then | |
wait_seconds=$((wait_seconds + 86400)) | |
fi | |
echo $wait_seconds | |
} | |
# Function to calculate wait time from zhipu timestamp (Beijing time) | |
calculate_wait_from_timestamp() { | |
local reset_timestamp="$1" | |
if [ -z "$reset_timestamp" ]; then | |
return 0 | |
fi | |
# Convert Beijing time (UTC+8) to UTC seconds since epoch | |
# Input format: "2025-09-24 05:17:01" (Beijing time) | |
local reset_utc_seconds | |
if command -v gdate >/dev/null 2>&1; then | |
# macOS with GNU date installed | |
reset_utc_seconds=$(gdate -d "$reset_timestamp UTC+8" +%s 2>/dev/null) | |
else | |
# Linux date - convert manually by adding 8 hours to Beijing time to get UTC | |
# Beijing time is UTC+8, so to convert Beijing time to UTC, we need to subtract 8 hours from the Beijing timestamp | |
# But date -d parses the timestamp as local time, so we need to add the difference between Beijing and local | |
local local_offset_hours=$(date +%z | sed 's/[+-]0*//' | sed 's/..$//') | |
local beijing_offset_hours=8 | |
local offset_diff=$(( (beijing_offset_hours - local_offset_hours) * 3600 )) | |
local beijing_seconds=$(date -d "$reset_timestamp" +%s 2>/dev/null) | |
if [ $? -eq 0 ]; then | |
reset_utc_seconds=$((beijing_seconds - offset_diff)) | |
else | |
return 0 # Unable to parse timestamp | |
fi | |
fi | |
if [ -z "$reset_utc_seconds" ] || [ "$reset_utc_seconds" -eq 0 ]; then | |
return 0 | |
fi | |
# Get current time in UTC seconds | |
local current_utc_seconds=$(date -u +%s) | |
# Calculate wait time | |
local wait_seconds=$((reset_utc_seconds - current_utc_seconds)) | |
# Return reasonable wait times (0 to 8 hours) | |
if [ $wait_seconds -le 0 ]; then | |
echo 0 | |
elif [ $wait_seconds -gt 28800 ]; then # 8 hours | |
echo 0 | |
else | |
echo $wait_seconds | |
fi | |
} | |
# Function to wait with countdown display | |
wait_with_countdown() { | |
local total_seconds=$1 | |
local reason="$2" | |
if [ $total_seconds -le 0 ]; then | |
return 0 | |
fi | |
echo -e "${YELLOW}Waiting for rate limit to reset... (Press Ctrl+C to interrupt)${NC}" | |
if [ -n "$reason" ]; then | |
echo -e "${YELLOW}Reason: $reason${NC}" | |
fi | |
local remaining=$total_seconds | |
while [ $remaining -gt 0 ]; do | |
local hours=$((remaining / 3600)) | |
local minutes=$(((remaining % 3600) / 60)) | |
local seconds=$((remaining % 60)) | |
printf "\r${CYAN}Time remaining: %02d:%02d:%02d${NC}" $hours $minutes $seconds | |
sleep 1 | |
((remaining--)) | |
done | |
echo -e "\n${GREEN}Rate limit should be reset now. Continuing...${NC}" | |
} | |
# Validate required commands exist | |
if ! command -v $CLAUDE_CLI &> /dev/null; then | |
echo -e "${RED}Error: '$CLAUDE_CLI' command not found in PATH${NC}" | |
exit 1 | |
fi | |
if ! command -v jq &> /dev/null; then | |
echo -e "${RED}Error: 'jq' command not found in PATH${NC}" | |
echo -e "${YELLOW}jq is required for parsing Claude's JSON output. Install with:${NC}" | |
echo " sudo apt-get install jq # Ubuntu/Debian" | |
echo " brew install jq # macOS" | |
exit 1 | |
fi | |
# Function to process Claude's stream-json output in real-time | |
stream_progress() { | |
local current_task="" | |
local tool_count=0 | |
local rate_limited=false | |
local reset_time="" | |
while IFS= read -r line; do | |
# Skip empty lines | |
[ -z "$line" ] && continue | |
# Skip processing special timestamp lines (they're handled via file now) | |
if [[ "$line" =~ ^(ZHIPU_RESET_TIMESTAMP|CLAUDE_RESET_TIME): ]]; then | |
continue | |
fi | |
# Parse JSON line by line | |
local msg_type=$(echo "$line" | jq -r '.type // empty' 2>/dev/null) | |
case "$msg_type" in | |
"system") | |
local subtype=$(echo "$line" | jq -r '.subtype // empty' 2>/dev/null) | |
if [ "$subtype" = "init" ]; then | |
echo -e "${CYAN}→ Starting fresh task execution${NC}" | |
fi | |
;; | |
"assistant") | |
# Extract assistant message content | |
local content=$(echo "$line" | jq -r '.message.content[0].text // empty' 2>/dev/null) | |
if [ -n "$content" ] && [ "$content" != "null" ]; then | |
# Check for rate limiting messages (regular claude and claude-zhipu formats) | |
if [[ "$content" =~ "5-hour limit reached" ]] || [[ "$content" =~ "rate limit" ]] || [[ "$content" =~ "usage limit" ]] || [[ "$content" =~ "API Error: 429" ]] || [[ "$content" =~ "\"type\":\"1308\"" ]] || [[ "$content" =~ "Usage limit reached for 5 hour" ]]; then | |
rate_limited=true | |
# Handle claude-zhipu API Error 429 format | |
if [[ "$content" =~ API\ Error:\ 429 ]] || [[ "$content" =~ "\"type\":\"1308\"" ]]; then | |
# Extract JSON error from the content (after "API Error: 429 ") | |
local error_json=$(echo "$content" | sed -n 's/.*API Error: 429 \(.*\)/\1/p') | |
if [ -n "$error_json" ]; then | |
local error_message=$(echo "$error_json" | jq -r '.error.message // empty' 2>/dev/null) | |
local error_type=$(echo "$error_json" | jq -r '.error.type // empty' 2>/dev/null) | |
if [ -n "$error_message" ] && [ "$error_message" != "null" ]; then | |
echo -e "${RED}⚠ Rate limit detected (claude-zhipu): ${error_message:0:100}${NC}" | |
# Extract timestamp from zhipu error message (Beijing time) | |
if [[ "$error_message" =~ reset\ at\ ([0-9]{4}-[0-9]{2}-[0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}) ]]; then | |
RESET_TIMESTAMP="${BASH_REMATCH[1]}" | |
fi | |
elif [ "$error_type" = "1308" ]; then | |
echo -e "${RED}⚠ Rate limit detected (claude-zhipu): Usage limit reached for 5 hour${NC}" | |
else | |
echo -e "${RED}⚠ Rate limit detected (claude-zhipu): API Error 429${NC}" | |
fi | |
# Handle case where error is directly in content without "API Error: 429" prefix | |
elif [[ "$content" =~ "Usage limit reached for 5 hour" ]]; then | |
echo -e "${RED}⚠ Rate limit detected (claude-zhipu): Usage limit reached for 5 hour${NC}" | |
# Also try to extract timestamp from direct content | |
if [[ "$content" =~ reset\ at\ ([0-9]{4}-[0-9]{2}-[0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}) ]]; then | |
RESET_TIMESTAMP="${BASH_REMATCH[1]}" | |
fi | |
else | |
echo -e "${RED}⚠ Rate limit detected (claude-zhipu): API Error 429${NC}" | |
fi | |
else | |
# Extract reset time if present (e.g., "resets 2pm") | |
if [[ "$content" =~ resets[[:space:]]+([0-9]+[ap]m) ]]; then | |
reset_time="${BASH_REMATCH[1]}" | |
fi | |
echo -e "${RED}⚠ Rate limit detected: ${content:0:100}${NC}" | |
fi | |
else | |
# Show first 100 chars of assistant response | |
echo -e "${BLUE}AI:${NC} ${content:0:100}$([ ${#content} -gt 100 ] && echo '...')" | |
fi | |
fi | |
;; | |
"tool_use") | |
((tool_count++)) | |
local tool_name=$(echo "$line" | jq -r '.tool_name // empty' 2>/dev/null) | |
if [ -n "$tool_name" ] && [ "$tool_name" != "null" ]; then | |
echo -e "${CYAN}→ Tool[$tool_count]: ${tool_name}${NC}" | |
fi | |
;; | |
"tool_result") | |
local is_error=$(echo "$line" | jq -r '.is_error // false' 2>/dev/null) | |
if [ "$is_error" = "true" ]; then | |
echo -e "${RED}✗ Tool failed${NC}" | |
else | |
echo -e "${GREEN}✓ Tool completed${NC}" | |
fi | |
;; | |
"result") | |
local is_error=$(echo "$line" | jq -r '.is_error // false' 2>/dev/null) | |
local duration=$(echo "$line" | jq -r '.duration_ms // 0' 2>/dev/null) | |
local cost=$(echo "$line" | jq -r '.total_cost_usd // 0' 2>/dev/null) | |
if [ "$rate_limited" = "true" ]; then | |
echo -e "${YELLOW}⚠ Rate limited (${duration}ms, \$${cost})${NC}" | |
if [ -n "$reset_time" ]; then | |
echo -e "${YELLOW} Limit resets at: $reset_time${NC}" | |
fi | |
# Write timestamp info to file for main script to read | |
if [ -n "$RESET_TIMESTAMP" ] && [ -n "$TIMESTAMP_FILE" ]; then | |
echo "ZHIPU_RESET_TIMESTAMP:$RESET_TIMESTAMP" >> "$TIMESTAMP_FILE" | |
fi | |
if [ -n "$reset_time" ] && [ -n "$TIMESTAMP_FILE" ]; then | |
echo "CLAUDE_RESET_TIME:$reset_time" >> "$TIMESTAMP_FILE" | |
fi | |
return 2 # Special return code for rate limiting | |
elif [ "$is_error" = "true" ]; then | |
echo -e "${RED}✗ Task failed (${duration}ms, \$${cost})${NC}" | |
return 1 | |
else | |
echo -e "${GREEN}✓ Task completed (${duration}ms, \$${cost})${NC}" | |
return 0 | |
fi | |
;; | |
esac | |
# Show verbose output if enabled | |
if [ "$VERBOSE" -eq 1 ]; then | |
echo -e "${CYAN}[JSON]${NC} $line" | |
fi | |
done | |
# If we exit the loop, the stream ended naturally (EOF) | |
echo -e "${YELLOW}→ Stream ended${NC}" | |
return 0 | |
} | |
echo -e "${GREEN}Starting $CLAUDE_CLI task processing loop${NC}" | |
echo "Press Ctrl+C to stop the loop" | |
echo "Processing file: ${YELLOW}$TODO_FILE${NC}" | |
echo "Max iterations: $MAX_ITERATIONS | Verbose: $VERBOSE" | |
echo "----------------------------------------" | |
while true; do | |
((iteration++)) | |
# Check if we've reached the maximum iterations | |
if [ $iteration -gt $MAX_ITERATIONS ]; then | |
echo -e "${YELLOW}Reached maximum iterations ($MAX_ITERATIONS). Exiting...${NC}" | |
break | |
fi | |
echo -e "\n${YELLOW}[Iteration $iteration/$MAX_ITERATIONS - $(date '+%Y-%m-%d %H:%M:%S')]${NC}" | |
# Reset rate limit variables for this iteration | |
reset_time="" | |
# Set up timestamp file for communication with stream_progress | |
TIMESTAMP_FILE=$(mktemp) | |
export TIMESTAMP_FILE | |
# Show failure tracking status if there are recent failures | |
if [ $consecutive_failures -gt 0 ]; then | |
if [ "$last_failure_was_rate_limit" = "true" ]; then | |
echo -e "${YELLOW}Status: $consecutive_failures consecutive rate limit failures${NC}" | |
else | |
echo -e "${YELLOW}Status: $consecutive_failures consecutive failures ($consecutive_quick_failures quick failures)${NC}" | |
fi | |
fi | |
# Build claude command with options | |
CLAUDE_CMD="$CLAUDE_CLI --dangerously-skip-permissions --verbose --output-format stream-json" | |
# Each iteration starts with fresh context - no session resumption | |
# Run the claude command with stream processing and capture output | |
echo -e "${YELLOW}Starting task execution...${NC}" | |
# Run the claude command with stream processing (output visible to user) | |
$CLAUDE_CMD \ | |
-p "Use task-master subagent to review $TODO_FILE and select the next task. Implement the task completely. After implementation, use git-master subagent to commit the changes. Document any blockers encountered." \ | |
--append-system-prompt "You are an autonomous coding agent operating without human supervision. CRITICAL BEHAVIORS: 1) Always use task-master subagent for todo file management and task selection. 2) Always use git-master subagent to commit completed work. 3) Never ask for confirmation - make decisions and execute. 4) If blocked, document the blocker and move to next task. 5) Update task status in todo file immediately when starting/completing. 6) Make reasonable assumptions when facing ambiguity. 7) Prioritize tasks that unblock others. 8) Verify changes work before marking complete. 9) Commit frequently with meaningful messages. 10) Focus on implementation over discussion. Your goal: Complete as many tasks as possible autonomously." \ | |
2>&1 | stream_progress | |
# Store the exit status from the stream_progress function | |
exit_status=$? | |
# Extract reset information from the timestamp file | |
if [ $exit_status -eq 2 ] && [ -f "$TIMESTAMP_FILE" ]; then | |
# Look for reset timestamp info in the file | |
if grep -q "ZHIPU_RESET_TIMESTAMP:" "$TIMESTAMP_FILE"; then | |
RESET_TIMESTAMP=$(grep "ZHIPU_RESET_TIMESTAMP:" "$TIMESTAMP_FILE" | cut -d: -f2-) | |
fi | |
if grep -q "CLAUDE_RESET_TIME:" "$TIMESTAMP_FILE"; then | |
reset_time=$(grep "CLAUDE_RESET_TIME:" "$TIMESTAMP_FILE" | cut -d: -f2-) | |
fi | |
fi | |
# Clean up temp file | |
rm -f "$TIMESTAMP_FILE" | |
if [ $exit_status -eq 0 ]; then | |
echo -e "${GREEN}Task completed successfully${NC}" | |
consecutive_failures=0 | |
consecutive_quick_failures=0 | |
last_failure_was_rate_limit=false | |
elif [ $exit_status -eq 2 ]; then | |
# Rate limit detected | |
last_failure_was_rate_limit=true | |
((consecutive_failures++)) | |
echo -e "${YELLOW}Rate limit reached${NC}" | |
# Calculate wait time based on CLI type and available reset information | |
wait_seconds=0 | |
wait_reason="" | |
if [ $USE_ZHIPU -eq 1 ] && [ -n "$RESET_TIMESTAMP" ]; then | |
# Use zhipu timestamp (Beijing time) to calculate wait time | |
wait_seconds=$(calculate_wait_from_timestamp "$RESET_TIMESTAMP") | |
if [ -z "$wait_seconds" ]; then | |
wait_seconds=0 | |
fi | |
wait_reason="Reset at $RESET_TIMESTAMP (Beijing time)" | |
# Clear the timestamp for next iteration | |
RESET_TIMESTAMP="" | |
elif [ $USE_ZHIPU -eq 0 ] && [ -n "$reset_time" ]; then | |
# Use regular claude reset time (local time) | |
wait_seconds=$(calculate_wait_time "$reset_time") | |
if [ -z "$wait_seconds" ]; then | |
wait_seconds=0 | |
fi | |
wait_reason="Reset at $reset_time (local time)" | |
fi | |
# Decide whether to wait or exit | |
if [ $wait_seconds -gt 0 ] && [ $wait_seconds -le 28800 ]; then # Wait up to 8 hours | |
wait_hours=$((wait_seconds / 3600)) | |
wait_minutes=$(((wait_seconds % 3600) / 60)) | |
wait_secs=$((wait_seconds % 60)) | |
echo -e "${YELLOW}Calculated wait time: ${wait_hours}h ${wait_minutes}m ${wait_secs}s${NC}" | |
wait_with_countdown $wait_seconds "$wait_reason" | |
# Reset failure counters after successful wait | |
consecutive_failures=0 | |
consecutive_quick_failures=0 | |
last_failure_was_rate_limit=false | |
else | |
# Check if we should exit after multiple failures | |
if [ $consecutive_failures -ge 3 ]; then | |
echo -e "${RED}Multiple consecutive rate limit failures detected${NC}" | |
echo -e "${YELLOW}Consider running the loop again later when the rate limit resets${NC}" | |
echo -e "${YELLOW}You can restart with: $0 $TODO_FILE${NC}" | |
break | |
fi | |
# Skip to next iteration for rate limits without wait time | |
echo -e "${YELLOW}No wait time available - skipping to next iteration${NC}" | |
fi | |
else | |
# Regular failure | |
((consecutive_failures++)) | |
# Check if this was a quick failure (< 5 seconds, $0 cost) | |
# This pattern often indicates persistent issues like rate limits not caught above | |
if [ "$exit_status" -eq 1 ]; then | |
((consecutive_quick_failures++)) | |
else | |
consecutive_quick_failures=0 | |
fi | |
echo -e "${RED}Task failed with exit code: $exit_status${NC}" | |
# If we have multiple quick failures, it might be a persistent issue | |
if [ $consecutive_quick_failures -ge 3 ]; then | |
echo -e "${RED}Multiple consecutive quick failures detected${NC}" | |
echo -e "${YELLOW}This often indicates rate limiting or other persistent issues${NC}" | |
echo -e "${YELLOW}Consider checking Claude CLI status or waiting before retrying${NC}" | |
# Add a small delay before continuing | |
echo -e "${YELLOW}Waiting 30 seconds before next attempt...${NC}" | |
sleep 30 | |
fi | |
echo -e "${YELLOW}Continuing despite error${NC}" | |
last_failure_was_rate_limit=false | |
fi | |
# Optional: Add a delay between iterations (uncomment and adjust as needed) | |
# echo "Waiting 5 seconds before next iteration..." | |
# sleep 5 | |
echo "----------------------------------------" | |
done | |
echo -e "${GREEN}Loop completed. Total iterations: $iteration${NC}" | |
# Final status summary | |
if [ $consecutive_failures -gt 0 ]; then | |
if [ "$last_failure_was_rate_limit" = "true" ]; then | |
echo -e "${YELLOW}Final status: Stopped due to rate limiting${NC}" | |
echo -e "${YELLOW}Recommendation: Wait for rate limit to reset and run: $0 $TODO_FILE${NC}" | |
else | |
echo -e "${YELLOW}Final status: $consecutive_failures consecutive failures${NC}" | |
echo -e "${YELLOW}Recommendation: Check error logs and todo file status${NC}" | |
fi | |
else | |
echo -e "${GREEN}Final status: Clean completion${NC}" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment