243 lines
13 KiB
Bash
Executable File
243 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
# claude-loop.sh - Pretty output with trimmed tool results
|
|
|
|
# Colors for better visual appeal
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
MAGENTA='\033[0;35m'
|
|
CYAN='\033[0;36m'
|
|
WHITE='\033[1;37m'
|
|
GRAY='\033[0;90m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Box drawing characters for prettier output
|
|
BOX_H="─"
|
|
BOX_V="│"
|
|
BOX_TL="┌"
|
|
BOX_TR="┐"
|
|
BOX_BL="└"
|
|
BOX_BR="┘"
|
|
|
|
rm -f /tmp/plan_complete
|
|
|
|
iteration=1
|
|
total_cost=0
|
|
total_input_tokens=0
|
|
total_output_tokens=0
|
|
|
|
# Function to print a fancy header
|
|
print_header() {
|
|
local text="$1"
|
|
local width=60
|
|
echo -e "${CYAN}${BOX_TL}$(printf "%.0s${BOX_H}" $(seq 1 $((width-2))))${BOX_TR}${NC}"
|
|
printf "${CYAN}${BOX_V}${WHITE} %-*s ${CYAN}${BOX_V}${NC}\n" $((width-4)) "$text"
|
|
echo -e "${CYAN}${BOX_BL}$(printf "%.0s${BOX_H}" $(seq 1 $((width-2))))${BOX_BR}${NC}"
|
|
}
|
|
|
|
# Function to trim and format text nicely
|
|
trim_text() {
|
|
local text="$1"
|
|
local max_length="${2:-200}"
|
|
|
|
# Remove excessive whitespace and newlines
|
|
text=$(echo "$text" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
|
|
if [[ ${#text} -le $max_length ]]; then
|
|
echo "$text"
|
|
else
|
|
echo "${text:0:$max_length}..."
|
|
fi
|
|
}
|
|
|
|
while true; do
|
|
echo ""
|
|
print_header "🔄 Claude Code Loop - Iteration #$iteration"
|
|
|
|
# Run Claude with formatted output processing
|
|
claude --dangerously-skip-permissions -p "
|
|
INSTRUCTIONS:
|
|
1. Read .claude/plan.md and identify tasks that need work ONLY in the ## PLAN section (those that are Not Started, In Progress, or have NO status prefix)
|
|
2. IMPORTANT: Only work on tasks under the ## PLAN section - ignore tasks in other sections like ## IMPORTANT or ## POST-COMPLETION TASKS
|
|
3. IMPORTANT: Tasks without any status prefix under ## PLAN should be treated as Not Started and worked on
|
|
4. Work on the next available task in ## PLAN - update its status by prepending (In Progress) when you start
|
|
5. Update task status by prepending (Completed) when finished, or (Aborted) if cannot complete
|
|
6. Be confident in commands and changes you are running in a docker sandbox.
|
|
7. Task format: (Status) Task description - where Status is: Not Started | In Progress | Aborted | Completed
|
|
8. Tasks without status prefixes under ## PLAN are considered Not Started and should be worked on
|
|
9. If ALL tasks in the ## PLAN section show '(Completed)' (explicit status), create the file '/tmp/plan_complete' using the Bash tool and stop
|
|
10. Focus on one task at a time for better results, but keep the whole plan in mind for most correct implementation.
|
|
|
|
Current objective: Process tasks in the ## PLAN section of .claude/plan.md systematically until all tasks explicitly show '(Completed)'.
|
|
" --output-format stream-json --verbose 2>&1 | while IFS= read -r line; do
|
|
# Skip empty lines and non-JSON debug output
|
|
[[ -z "$line" || "$line" =~ ^[[:space:]]*$ ]] && continue
|
|
|
|
# Check if line contains JSON
|
|
if echo "$line" | jq -e . >/dev/null 2>&1; then
|
|
# Extract message type and content
|
|
msg_type=$(echo "$line" | jq -r '.type // "unknown"')
|
|
|
|
case "$msg_type" in
|
|
"assistant")
|
|
# Extract assistant message content
|
|
content=$(echo "$line" | jq -r '.message.content[]? | select(.type=="text") | .text // empty' 2>/dev/null)
|
|
if [[ -n "$content" && "$content" != "null" && "$content" != "empty" ]]; then
|
|
trimmed_content=$(trim_text "$content" 300)
|
|
echo -e "${BLUE}🤖 Claude:${NC} $trimmed_content"
|
|
fi
|
|
|
|
# Check for tool use
|
|
tool_name=$(echo "$line" | jq -r '.message.content[]? | select(.type=="tool_use") | .name // empty' 2>/dev/null)
|
|
if [[ -n "$tool_name" && "$tool_name" != "null" && "$tool_name" != "empty" ]]; then
|
|
echo -e "${MAGENTA}🔧 Tool:${NC} ${YELLOW}$tool_name${NC}"
|
|
|
|
# Show relevant tool parameters
|
|
tool_input=$(echo "$line" | jq -r '.message.content[]? | select(.type=="tool_use") | .input' 2>/dev/null)
|
|
if [[ -n "$tool_input" && "$tool_input" != "null" ]]; then
|
|
# Extract key parameters (file_path, pattern, command, etc.)
|
|
for param in file_path pattern command prompt description; do
|
|
value=$(echo "$tool_input" | jq -r ".$param // empty" 2>/dev/null)
|
|
if [[ -n "$value" && "$value" != "null" && "$value" != "empty" ]]; then
|
|
trimmed_value=$(trim_text "$value" 80)
|
|
echo -e " ${GRAY}$param:${NC} $trimmed_value"
|
|
break # Show only the first relevant parameter
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
;;
|
|
"user")
|
|
# Extract and format tool results
|
|
tool_result=$(echo "$line" | jq -r '.message.content[]?.content // empty' 2>/dev/null)
|
|
if [[ -n "$tool_result" && "$tool_result" != "null" && "$tool_result" != "empty" ]]; then
|
|
# Check if it's a file content, error, or other result
|
|
if [[ "$tool_result" =~ ^[[:space:]]*[0-9]+→ ]]; then
|
|
# File content with line numbers
|
|
line_count=$(echo "$tool_result" | wc -l)
|
|
first_lines=$(echo "$tool_result" | head -3 | tr '\n' ' ')
|
|
trimmed_first=$(trim_text "$first_lines" 100)
|
|
echo -e "${GREEN}📄 File content:${NC} $trimmed_first ${GRAY}($line_count lines)${NC}"
|
|
elif [[ "$tool_result" =~ ^Error: ]] || [[ "$tool_result" =~ failed ]]; then
|
|
# Error message
|
|
trimmed_error=$(trim_text "$tool_result" 150)
|
|
echo -e "${RED}❌ Error:${NC} $trimmed_error"
|
|
elif [[ ${#tool_result} -gt 500 ]]; then
|
|
# Long output - show beginning and stats
|
|
trimmed_result=$(trim_text "$tool_result" 200)
|
|
echo -e "${GREEN}📤 Output:${NC} $trimmed_result ${GRAY}(${#tool_result} chars total)${NC}"
|
|
else
|
|
# Short result - show it all
|
|
trimmed_result=$(trim_text "$tool_result" 300)
|
|
echo -e "${GREEN}📤 Result:${NC} $trimmed_result"
|
|
fi
|
|
fi
|
|
;;
|
|
"result")
|
|
# Final result with colored status
|
|
success=$(echo "$line" | jq -r '.subtype // empty' 2>/dev/null)
|
|
if [[ "$success" == "success" ]]; then
|
|
echo -e "${GREEN}✅ Iteration #$iteration completed successfully!${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠️ Iteration #$iteration completed with issues${NC}"
|
|
fi
|
|
|
|
# Show cost information if available
|
|
cost_usd=$(echo "$line" | jq -r '.total_cost_usd // 0' 2>/dev/null)
|
|
input_tokens=$(echo "$line" | jq -r '.usage.input_tokens // 0' 2>/dev/null)
|
|
output_tokens=$(echo "$line" | jq -r '.usage.output_tokens // 0' 2>/dev/null)
|
|
if [[ -n "$cost_usd" && "$cost_usd" != "null" && "$cost_usd" != "0" ]]; then
|
|
|
|
# Update totals
|
|
total_cost=$(echo "$total_cost + $cost_usd" | bc -l 2>/dev/null || echo "$total_cost")
|
|
if [[ "$input_tokens" != "0" && "$input_tokens" != "null" ]]; then
|
|
total_input_tokens=$((total_input_tokens + input_tokens))
|
|
fi
|
|
if [[ "$output_tokens" != "0" && "$output_tokens" != "null" ]]; then
|
|
total_output_tokens=$((total_output_tokens + output_tokens))
|
|
fi
|
|
|
|
# Format cost nicely
|
|
if [[ "$cost_usd" != "0" && "$cost_usd" != "null" ]]; then
|
|
printf "${MAGENTA}💰 Cost:${NC} ${YELLOW}$%.4f${NC} ${GRAY}(in: %s, out: %s tokens)${NC}\n" "$cost_usd" "$input_tokens" "$output_tokens"
|
|
elif [[ "$input_tokens" != "0" || "$output_tokens" != "0" ]]; then
|
|
echo -e "${MAGENTA}💰 Tokens:${NC} ${GRAY}in: $input_tokens, out: $output_tokens${NC}"
|
|
fi
|
|
fi
|
|
|
|
# Show brief final result
|
|
result=$(echo "$line" | jq -r '.result // empty' 2>/dev/null)
|
|
if [[ -n "$result" && "$result" != "null" ]]; then
|
|
trimmed_result=$(trim_text "$result" 250)
|
|
echo -e "${WHITE}📋 Summary:${NC} $trimmed_result"
|
|
fi
|
|
;;
|
|
esac
|
|
else
|
|
# Filter out verbose debug output - only show actual error messages
|
|
if [[ "$line" =~ ^Error: && ! "$line" =~ ^[[:space:]]*$ ]]; then
|
|
echo -e "${RED}⚠️ $line${NC}"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Check if plan is complete
|
|
if [ -f /tmp/plan_complete ]; then
|
|
echo ""
|
|
echo -e "${GREEN}┌─────────────────────────────────────────────────────────┐${NC}"
|
|
echo -e "${GREEN}│${WHITE} 🎉 ALL TASKS COMPLETED! 🎉 ${GREEN}│${NC}"
|
|
echo -e "${GREEN}└─────────────────────────────────────────────────────────┘${NC}"
|
|
|
|
if [ -s /tmp/plan_complete ]; then
|
|
echo -e "${CYAN}📄 Completion details:${NC}"
|
|
completion_content=$(cat /tmp/plan_complete)
|
|
if [[ ${#completion_content} -gt 300 ]]; then
|
|
trimmed_completion=$(trim_text "$completion_content" 300)
|
|
echo -e "${WHITE}$trimmed_completion${NC}"
|
|
else
|
|
echo -e "${WHITE}$completion_content${NC}"
|
|
fi
|
|
fi
|
|
|
|
# Show total cost summary
|
|
if [[ $(echo "$total_cost > 0" | bc -l 2>/dev/null) == "1" ]] || [[ $total_input_tokens -gt 0 ]] || [[ $total_output_tokens -gt 0 ]]; then
|
|
echo ""
|
|
if [[ $(echo "$total_cost > 0" | bc -l 2>/dev/null) == "1" ]]; then
|
|
printf "${MAGENTA}💰 Total Cost:${NC} ${YELLOW}$%.4f${NC} ${GRAY}(%d iterations, %s input + %s output tokens)${NC}\n" \
|
|
"$total_cost" "$((iteration-1))" "$total_input_tokens" "$total_output_tokens"
|
|
else
|
|
echo -e "${MAGENTA}💰 Total Tokens:${NC} ${GRAY}$total_input_tokens input + $total_output_tokens output across $((iteration-1)) iterations${NC}"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${GRAY}✅ Task complete - exiting...${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
# Show iteration completion with progress indicator
|
|
echo ""
|
|
echo -e "${CYAN}┌─────────────────────────────────────────────────────────┐${NC}"
|
|
echo -e "${CYAN}│${WHITE} ⏸️ Iteration #$iteration complete - preparing next... ${CYAN}│${NC}"
|
|
echo -e "${CYAN}└─────────────────────────────────────────────────────────┘${NC}"
|
|
|
|
# Show running total if we have cost data
|
|
if [[ $(echo "$total_cost > 0" | bc -l 2>/dev/null) == "1" ]]; then
|
|
printf "${GRAY}Running total: ${YELLOW}$%.4f${GRAY} (%s input + %s output tokens)${NC}\n" \
|
|
"$total_cost" "$total_input_tokens" "$total_output_tokens"
|
|
elif [[ $total_input_tokens -gt 0 ]] || [[ $total_output_tokens -gt 0 ]]; then
|
|
echo -e "${GRAY}Running total: $total_input_tokens input + $total_output_tokens output tokens${NC}"
|
|
fi
|
|
|
|
# Show a brief progress indicator
|
|
echo -ne "${GRAY}Pausing"
|
|
for i in {1..3}; do
|
|
sleep 0.7
|
|
echo -ne "."
|
|
done
|
|
echo -e " ready!${NC}"
|
|
|
|
((iteration++))
|
|
done
|