claude-evolve 1.5.27 → 1.5.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/claude-evolve-worker +96 -58
- package/lib/ai-cli.sh +8 -3
- package/lib/config.sh +7 -6
- package/package.json +1 -1
- package/templates/config.yaml +2 -1
package/bin/claude-evolve-worker
CHANGED
|
@@ -72,68 +72,106 @@ fi
|
|
|
72
72
|
call_ai_for_evolution() {
|
|
73
73
|
local prompt="$1"
|
|
74
74
|
local candidate_id="$2"
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
# Get target file path from worker context
|
|
77
77
|
local target_file="$FULL_OUTPUT_DIR/evolution_${candidate_id}.py"
|
|
78
|
-
|
|
79
|
-
#
|
|
80
|
-
local
|
|
81
|
-
local
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
id_num=$((10#${BASH_REMATCH[2]}))
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
# Calculate hash for round-robin (combine generation and ID)
|
|
96
|
-
local hash_value=$((gen_num * 1000 + id_num))
|
|
97
|
-
|
|
98
|
-
# Use the centralized AI library for evolution (run command)
|
|
99
|
-
local ai_output
|
|
100
|
-
ai_output=$(call_ai_with_round_robin "$prompt" "run" "$hash_value")
|
|
101
|
-
local ai_exit_code=$?
|
|
102
|
-
|
|
103
|
-
# Handle special exit codes
|
|
104
|
-
if [[ $ai_exit_code -eq 3 ]]; then
|
|
105
|
-
echo "[WORKER-$$] All models hit usage limits" >&2
|
|
106
|
-
echo "[WORKER-$$] Unable to complete evolution due to API limits" >&2
|
|
107
|
-
exit 3
|
|
108
|
-
fi
|
|
109
|
-
|
|
110
|
-
# Check if the target file was actually modified
|
|
111
|
-
local file_was_modified=false
|
|
112
|
-
if [[ -f "$target_file" ]]; then
|
|
113
|
-
local file_hash_after
|
|
114
|
-
local file_mtime_after
|
|
115
|
-
file_hash_after=$(shasum -a 256 "$target_file" 2>/dev/null | cut -d' ' -f1)
|
|
116
|
-
file_mtime_after=$(stat -f %m "$target_file" 2>/dev/null || stat -c %Y "$target_file" 2>/dev/null)
|
|
117
|
-
|
|
118
|
-
if [[ "$file_hash_before" != "$file_hash_after" ]] || [[ "$file_mtime_before" != "$file_mtime_after" ]]; then
|
|
119
|
-
file_was_modified=true
|
|
78
|
+
|
|
79
|
+
# Retry configuration for API limits
|
|
80
|
+
local retry_count=0
|
|
81
|
+
local max_retries=3
|
|
82
|
+
local wait_seconds=300 # Start with 5 minutes
|
|
83
|
+
local max_wait_seconds=1800 # Cap at 30 minutes
|
|
84
|
+
|
|
85
|
+
while true; do
|
|
86
|
+
# Capture file state before AI call
|
|
87
|
+
local file_hash_before=""
|
|
88
|
+
local file_mtime_before=""
|
|
89
|
+
if [[ -f "$target_file" ]]; then
|
|
90
|
+
file_hash_before=$(shasum -a 256 "$target_file" 2>/dev/null | cut -d' ' -f1)
|
|
91
|
+
file_mtime_before=$(stat -f %m "$target_file" 2>/dev/null || stat -c %Y "$target_file" 2>/dev/null)
|
|
120
92
|
fi
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if [[
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
echo "[WORKER-$$] AI succeeded with exit code 0" >&2
|
|
93
|
+
|
|
94
|
+
# Extract generation and ID numbers for round-robin calculation
|
|
95
|
+
local gen_num=0
|
|
96
|
+
local id_num=0
|
|
97
|
+
if [[ $candidate_id =~ ^gen([0-9]+)-([0-9]+)$ ]]; then
|
|
98
|
+
gen_num=$((10#${BASH_REMATCH[1]}))
|
|
99
|
+
id_num=$((10#${BASH_REMATCH[2]}))
|
|
129
100
|
fi
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
101
|
+
|
|
102
|
+
# Calculate hash for round-robin (combine generation and ID)
|
|
103
|
+
local hash_value=$((gen_num * 1000 + id_num))
|
|
104
|
+
|
|
105
|
+
# Use the centralized AI library for evolution (run command)
|
|
106
|
+
local ai_output
|
|
107
|
+
ai_output=$(call_ai_with_round_robin "$prompt" "run" "$hash_value")
|
|
108
|
+
local ai_exit_code=$?
|
|
109
|
+
|
|
110
|
+
# Handle special exit codes
|
|
111
|
+
if [[ $ai_exit_code -eq 3 ]]; then
|
|
112
|
+
# All models hit usage limits
|
|
113
|
+
if [[ $retry_count -lt $max_retries ]]; then
|
|
114
|
+
((retry_count++))
|
|
115
|
+
echo "[WORKER-$$] All models hit usage limits (retry $retry_count/$max_retries)" >&2
|
|
116
|
+
echo "[WORKER-$$] Waiting $wait_seconds seconds ($(($wait_seconds / 60)) minutes) before retrying..." >&2
|
|
117
|
+
|
|
118
|
+
# Sleep with countdown
|
|
119
|
+
local remaining=$wait_seconds
|
|
120
|
+
while [[ $remaining -gt 0 ]]; do
|
|
121
|
+
if [[ $((remaining % 60)) -eq 0 ]]; then
|
|
122
|
+
echo "[WORKER-$$] Retrying in $((remaining / 60)) minutes..." >&2
|
|
123
|
+
fi
|
|
124
|
+
sleep 60
|
|
125
|
+
remaining=$((remaining - 60))
|
|
126
|
+
done
|
|
127
|
+
|
|
128
|
+
echo "[WORKER-$$] Retrying AI call (attempt #$((retry_count + 1)))..." >&2
|
|
129
|
+
|
|
130
|
+
# Exponential backoff: double the wait time, up to max
|
|
131
|
+
wait_seconds=$((wait_seconds * 2))
|
|
132
|
+
if [[ $wait_seconds -gt $max_wait_seconds ]]; then
|
|
133
|
+
wait_seconds=$max_wait_seconds
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Continue to retry
|
|
137
|
+
continue
|
|
138
|
+
else
|
|
139
|
+
# Exhausted retries
|
|
140
|
+
echo "[WORKER-$$] All models hit usage limits after $max_retries retries" >&2
|
|
141
|
+
echo "[WORKER-$$] Unable to complete evolution due to API limits" >&2
|
|
142
|
+
exit 3
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# Check if the target file was actually modified
|
|
147
|
+
local file_was_modified=false
|
|
148
|
+
if [[ -f "$target_file" ]]; then
|
|
149
|
+
local file_hash_after
|
|
150
|
+
local file_mtime_after
|
|
151
|
+
file_hash_after=$(shasum -a 256 "$target_file" 2>/dev/null | cut -d' ' -f1)
|
|
152
|
+
file_mtime_after=$(stat -f %m "$target_file" 2>/dev/null || stat -c %Y "$target_file" 2>/dev/null)
|
|
153
|
+
|
|
154
|
+
if [[ "$file_hash_before" != "$file_hash_after" ]] || [[ "$file_mtime_before" != "$file_mtime_after" ]]; then
|
|
155
|
+
file_was_modified=true
|
|
156
|
+
fi
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
# Success if file was modified OR exit code is 0 (for cases where file validation isn't applicable)
|
|
160
|
+
if [[ "$file_was_modified" == "true" ]] || [[ $ai_exit_code -eq 0 ]]; then
|
|
161
|
+
if [[ "$file_was_modified" == "true" ]]; then
|
|
162
|
+
echo "[WORKER-$$] AI successfully modified $target_file (exit code: $ai_exit_code)" >&2
|
|
163
|
+
else
|
|
164
|
+
echo "[WORKER-$$] AI succeeded with exit code 0" >&2
|
|
165
|
+
fi
|
|
166
|
+
# Output the result for the worker to use
|
|
167
|
+
echo "$ai_output"
|
|
168
|
+
return 0
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# Non-limit failure - don't retry
|
|
172
|
+
echo "[WORKER-$$] AI failed: exit code $ai_exit_code, no file changes detected" >&2
|
|
173
|
+
return 1
|
|
174
|
+
done
|
|
137
175
|
}
|
|
138
176
|
|
|
139
177
|
# Validate paths
|
package/lib/ai-cli.sh
CHANGED
|
@@ -19,7 +19,7 @@ call_ai_model_configured() {
|
|
|
19
19
|
case "$model_name" in
|
|
20
20
|
opus|sonnet)
|
|
21
21
|
local ai_output
|
|
22
|
-
ai_output=$(timeout 300 claude --dangerously-skip-permissions --model "$model_name" -p "$prompt" 2>&1)
|
|
22
|
+
ai_output=$(timeout 300 claude --dangerously-skip-permissions --mcp-config '' --model "$model_name" -p "$prompt" 2>&1)
|
|
23
23
|
local ai_exit_code=$?
|
|
24
24
|
;;
|
|
25
25
|
sonnet-think)
|
|
@@ -28,7 +28,7 @@ call_ai_model_configured() {
|
|
|
28
28
|
local think_prompt="ultrathink
|
|
29
29
|
|
|
30
30
|
$prompt"
|
|
31
|
-
ai_output=$(timeout 600 claude --dangerously-skip-permissions --model sonnet -p "$think_prompt" 2>&1)
|
|
31
|
+
ai_output=$(timeout 600 claude --dangerously-skip-permissions --mcp-config '' --model sonnet -p "$think_prompt" 2>&1)
|
|
32
32
|
local ai_exit_code=$?
|
|
33
33
|
;;
|
|
34
34
|
opus-think)
|
|
@@ -37,7 +37,7 @@ $prompt"
|
|
|
37
37
|
local think_prompt="ultrathink
|
|
38
38
|
|
|
39
39
|
$prompt"
|
|
40
|
-
ai_output=$(timeout 600 claude --dangerously-skip-permissions --model opus -p "$think_prompt" 2>&1)
|
|
40
|
+
ai_output=$(timeout 600 claude --dangerously-skip-permissions --mcp-config '' --model opus -p "$think_prompt" 2>&1)
|
|
41
41
|
local ai_exit_code=$?
|
|
42
42
|
;;
|
|
43
43
|
gpt5high)
|
|
@@ -76,6 +76,11 @@ $prompt"
|
|
|
76
76
|
ai_output=$(timeout 300 cursor-agent opus -p "$prompt" 2>&1)
|
|
77
77
|
local ai_exit_code=$?
|
|
78
78
|
;;
|
|
79
|
+
glm)
|
|
80
|
+
local ai_output
|
|
81
|
+
ai_output=$(timeout 300 opencode -m openrouter/z-ai/glm-4.6 run "$prompt" 2>&1)
|
|
82
|
+
local ai_exit_code=$?
|
|
83
|
+
;;
|
|
79
84
|
*)
|
|
80
85
|
echo "[ERROR] Unknown model: $model_name" >&2
|
|
81
86
|
return 1
|
package/lib/config.sh
CHANGED
|
@@ -54,7 +54,7 @@ DEFAULT_MAX_RETRIES=3
|
|
|
54
54
|
DEFAULT_MEMORY_LIMIT_MB=12288
|
|
55
55
|
|
|
56
56
|
# Default LLM CLI configuration - use simple variables instead of arrays
|
|
57
|
-
DEFAULT_LLM_RUN="sonnet gpt5 cursor-sonnet"
|
|
57
|
+
DEFAULT_LLM_RUN="sonnet gpt5 cursor-sonnet glm"
|
|
58
58
|
DEFAULT_LLM_IDEATE="gemini sonnet-think sonnet-think gpt5high sonnet-think o3high"
|
|
59
59
|
|
|
60
60
|
# Load configuration from config file
|
|
@@ -102,12 +102,13 @@ load_config() {
|
|
|
102
102
|
LLM_CLI_o3high='codex exec --profile o3high --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
|
|
103
103
|
LLM_CLI_codex='codex exec --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
|
|
104
104
|
LLM_CLI_gemini='gemini -y -p "{{PROMPT}}"'
|
|
105
|
-
LLM_CLI_opus='claude --dangerously-skip-permissions --model opus -p "{{PROMPT}}"'
|
|
106
|
-
LLM_CLI_opus_think='claude --dangerously-skip-permissions --model opus -p "ultrathink\n\n{{PROMPT}}"'
|
|
107
|
-
LLM_CLI_sonnet='claude --dangerously-skip-permissions --model sonnet -p "{{PROMPT}}"'
|
|
108
|
-
LLM_CLI_sonnet_think='claude --dangerously-skip-permissions --model sonnet -p "ultrathink\n\n{{PROMPT}}"'
|
|
105
|
+
LLM_CLI_opus='claude --dangerously-skip-permissions --mcp-config "" --model opus -p "{{PROMPT}}"'
|
|
106
|
+
LLM_CLI_opus_think='claude --dangerously-skip-permissions --mcp-config "" --model opus -p "ultrathink\n\n{{PROMPT}}"'
|
|
107
|
+
LLM_CLI_sonnet='claude --dangerously-skip-permissions --mcp-config "" --model sonnet -p "{{PROMPT}}"'
|
|
108
|
+
LLM_CLI_sonnet_think='claude --dangerously-skip-permissions --mcp-config "" --model sonnet -p "ultrathink\n\n{{PROMPT}}"'
|
|
109
109
|
LLM_CLI_cursor_sonnet='cursor-agent sonnet -p "{{PROMPT}}"'
|
|
110
110
|
LLM_CLI_cursor_opus='cursor-agent opus -p "{{PROMPT}}"'
|
|
111
|
+
LLM_CLI_glm='opencode -m openrouter/z-ai/glm-4.6 run "{{PROMPT}}"'
|
|
111
112
|
LLM_RUN="$DEFAULT_LLM_RUN"
|
|
112
113
|
LLM_IDEATE="$DEFAULT_LLM_IDEATE"
|
|
113
114
|
|
|
@@ -327,7 +328,7 @@ show_config() {
|
|
|
327
328
|
echo " Memory limit: ${MEMORY_LIMIT_MB}MB"
|
|
328
329
|
echo " LLM configuration:"
|
|
329
330
|
# Show LLM configurations using dynamic variable names
|
|
330
|
-
for model in gpt5high o3high codex gemini opus opus_think sonnet sonnet_think cursor_sonnet cursor_opus; do
|
|
331
|
+
for model in gpt5high o3high codex gemini opus opus_think sonnet sonnet_think cursor_sonnet cursor_opus glm; do
|
|
331
332
|
var_name="LLM_CLI_${model}"
|
|
332
333
|
var_value=$(eval echo "\$$var_name")
|
|
333
334
|
if [[ -n "$var_value" ]]; then
|
package/package.json
CHANGED
package/templates/config.yaml
CHANGED
|
@@ -72,7 +72,7 @@ llm_cli:
|
|
|
72
72
|
|
|
73
73
|
# commented out because these change over time; if you want to fix them in a particular
|
|
74
74
|
# configuration, uncomment them and set them
|
|
75
|
-
#run: sonnet gpt5 cursor-sonnet
|
|
75
|
+
#run: sonnet gpt5 cursor-sonnet glm
|
|
76
76
|
#ideate: gemini sonnet-think sonnet-think gpt5high sonnet-think o3high
|
|
77
77
|
|
|
78
78
|
# Available models:
|
|
@@ -86,3 +86,4 @@ llm_cli:
|
|
|
86
86
|
# - o3high: O3 via Codex CLI (high reasoning)
|
|
87
87
|
# - cursor-sonnet: Claude 3.5 Sonnet via Cursor Agent CLI
|
|
88
88
|
# - cursor-opus: Claude 3 Opus via Cursor Agent CLI
|
|
89
|
+
# - glm: GLM-4.6 via OpenCode CLI
|