-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathclaude-loop.sh
More file actions
executable file
Β·242 lines (215 loc) Β· 12.6 KB
/
claude-loop.sh
File metadata and controls
executable file
Β·242 lines (215 loc) Β· 12.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#!/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