claude-evolve 1.8.42 → 1.8.44
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-ideate +31 -12
- package/bin/claude-evolve-worker +48 -7
- package/lib/ai-cli.sh +8 -3
- package/lib/config.sh +2 -2
- package/package.json +1 -1
package/bin/claude-evolve-ideate
CHANGED
|
@@ -236,10 +236,11 @@ if [[ $use_strategies == true ]]; then
|
|
|
236
236
|
fi
|
|
237
237
|
|
|
238
238
|
# Get next generation number that doesn't have existing Python files
|
|
239
|
+
# AIDEV-NOTE: Returns zero-padded generation number (e.g., "02" not "2")
|
|
239
240
|
get_next_generation() {
|
|
240
241
|
# Start with generation 1 if no CSV exists
|
|
241
242
|
local start_gen=1
|
|
242
|
-
|
|
243
|
+
|
|
243
244
|
if [[ -f "$FULL_CSV_PATH" ]]; then
|
|
244
245
|
# Use Python for proper CSV parsing to find max generation
|
|
245
246
|
local max_gen
|
|
@@ -264,21 +265,25 @@ print(max_gen)
|
|
|
264
265
|
# Start checking from the next generation after max
|
|
265
266
|
start_gen=$((max_gen + 1))
|
|
266
267
|
fi
|
|
267
|
-
|
|
268
|
+
|
|
268
269
|
# Keep incrementing until we find a generation with no Python files
|
|
269
270
|
local candidate_gen=$start_gen
|
|
270
271
|
while true; do
|
|
271
|
-
#
|
|
272
|
-
local gen_formatted
|
|
272
|
+
# Zero-pad to 2 digits for consistency (gen01, gen02, etc.)
|
|
273
|
+
local gen_formatted
|
|
274
|
+
gen_formatted=$(printf "%02d" "$candidate_gen")
|
|
273
275
|
|
|
274
|
-
# Check if any Python files exist for this generation
|
|
276
|
+
# Check if any Python files exist for this generation (check both padded and unpadded)
|
|
275
277
|
local py_files_exist=false
|
|
276
278
|
if ls "$FULL_OUTPUT_DIR"/evolution_gen${gen_formatted}-*.py >/dev/null 2>&1; then
|
|
277
279
|
py_files_exist=true
|
|
280
|
+
elif ls "$FULL_OUTPUT_DIR"/evolution_gen${candidate_gen}-*.py >/dev/null 2>&1; then
|
|
281
|
+
# Also check unpadded format for legacy compatibility
|
|
282
|
+
py_files_exist=true
|
|
278
283
|
fi
|
|
279
284
|
|
|
280
285
|
if [[ "$py_files_exist" == "false" ]]; then
|
|
281
|
-
# This generation is safe to use
|
|
286
|
+
# This generation is safe to use - return padded format
|
|
282
287
|
echo "$gen_formatted"
|
|
283
288
|
return
|
|
284
289
|
else
|
|
@@ -1710,30 +1715,44 @@ count_generation_pending() {
|
|
|
1710
1715
|
}
|
|
1711
1716
|
|
|
1712
1717
|
# Find the right generation to use
|
|
1718
|
+
# AIDEV-NOTE: Generation numbers must be zero-padded to 2 digits (gen01, gen02, etc.)
|
|
1719
|
+
# to maintain consistency with existing CSV data format
|
|
1713
1720
|
highest_gen=$(get_highest_generation)
|
|
1714
1721
|
if [[ $highest_gen -eq 0 ]]; then
|
|
1715
|
-
# No generations yet, start with
|
|
1716
|
-
CURRENT_GENERATION=
|
|
1717
|
-
echo "[INFO] No existing generations, starting with generation
|
|
1722
|
+
# No generations yet, start with 01
|
|
1723
|
+
CURRENT_GENERATION="01"
|
|
1724
|
+
echo "[INFO] No existing generations, starting with generation 01"
|
|
1718
1725
|
else
|
|
1719
1726
|
# Check if highest generation needs more ideas
|
|
1727
|
+
# Use unpadded for grep patterns (matches both gen1- and gen01-)
|
|
1720
1728
|
existing_ideas=$(count_generation_ideas "$highest_gen")
|
|
1721
1729
|
pending_ideas=$(count_generation_pending "$highest_gen")
|
|
1730
|
+
# Also check padded format
|
|
1731
|
+
padded_gen=$(printf "%02d" "$highest_gen")
|
|
1732
|
+
existing_ideas_padded=$(count_generation_ideas "$padded_gen")
|
|
1733
|
+
pending_ideas_padded=$(count_generation_pending "$padded_gen")
|
|
1734
|
+
# Use whichever found more (handles both gen1 and gen01 formats)
|
|
1735
|
+
if [[ $existing_ideas_padded -gt $existing_ideas ]]; then
|
|
1736
|
+
existing_ideas=$existing_ideas_padded
|
|
1737
|
+
pending_ideas=$pending_ideas_padded
|
|
1738
|
+
fi
|
|
1722
1739
|
|
|
1723
1740
|
echo "[INFO] Highest generation: $highest_gen with $existing_ideas total ideas ($pending_ideas pending)"
|
|
1724
1741
|
|
|
1725
1742
|
if [[ $existing_ideas -ge $TOTAL_IDEAS ]]; then
|
|
1726
1743
|
# Highest generation is full, create a new one
|
|
1727
1744
|
CURRENT_GENERATION=$(get_next_generation)
|
|
1745
|
+
# Ensure it's zero-padded
|
|
1746
|
+
CURRENT_GENERATION=$(printf "%02d" "$CURRENT_GENERATION")
|
|
1728
1747
|
echo "[INFO] Generation $highest_gen is full ($existing_ideas >= $TOTAL_IDEAS), creating generation $CURRENT_GENERATION"
|
|
1729
1748
|
elif [[ $pending_ideas -ge $TOTAL_IDEAS ]]; then
|
|
1730
1749
|
# Already have enough pending, skip ideation
|
|
1731
|
-
echo "[INFO] Generation $
|
|
1750
|
+
echo "[INFO] Generation $padded_gen already has $pending_ideas pending ideas (target: $TOTAL_IDEAS)"
|
|
1732
1751
|
echo "[INFO] Skipping ideation - workers will process existing ideas"
|
|
1733
1752
|
exit 0
|
|
1734
1753
|
else
|
|
1735
|
-
# Continue filling the current highest generation
|
|
1736
|
-
CURRENT_GENERATION=$highest_gen
|
|
1754
|
+
# Continue filling the current highest generation - use padded format
|
|
1755
|
+
CURRENT_GENERATION=$(printf "%02d" "$highest_gen")
|
|
1737
1756
|
echo "[INFO] Continuing generation $CURRENT_GENERATION (need $((TOTAL_IDEAS - existing_ideas)) more ideas)"
|
|
1738
1757
|
fi
|
|
1739
1758
|
fi
|
package/bin/claude-evolve-worker
CHANGED
|
@@ -111,6 +111,8 @@ else
|
|
|
111
111
|
fi
|
|
112
112
|
|
|
113
113
|
# AI random selection function for code evolution
|
|
114
|
+
# AIDEV-NOTE: This function reads the model name from a temp file written by call_ai_random
|
|
115
|
+
# because exports don't propagate from subshells. The model is stored in SUCCESSFUL_RUN_MODEL.
|
|
114
116
|
call_ai_for_evolution() {
|
|
115
117
|
local prompt="$1"
|
|
116
118
|
local candidate_id="$2"
|
|
@@ -131,6 +133,17 @@ call_ai_for_evolution() {
|
|
|
131
133
|
ai_output=$(call_ai_random "$prompt" "run")
|
|
132
134
|
local ai_exit_code=$?
|
|
133
135
|
|
|
136
|
+
# Read the model name from temp file (written by call_ai_random in subshell)
|
|
137
|
+
local model_file="/tmp/.claude-evolve-model-$$"
|
|
138
|
+
if [[ -f "$model_file" ]]; then
|
|
139
|
+
SUCCESSFUL_RUN_MODEL=$(cat "$model_file")
|
|
140
|
+
rm -f "$model_file"
|
|
141
|
+
echo "[WORKER-$$] AI model used: $SUCCESSFUL_RUN_MODEL" >&2
|
|
142
|
+
else
|
|
143
|
+
echo "[WORKER-$$] Warning: Could not determine which AI model was used" >&2
|
|
144
|
+
SUCCESSFUL_RUN_MODEL=""
|
|
145
|
+
fi
|
|
146
|
+
|
|
134
147
|
# Check if the target file was actually modified
|
|
135
148
|
local file_was_modified=false
|
|
136
149
|
if [[ -f "$target_file" ]]; then
|
|
@@ -310,20 +323,48 @@ CRITICAL: If you do not know how to implement what was asked for, or if the requ
|
|
|
310
323
|
return 1
|
|
311
324
|
}
|
|
312
325
|
|
|
313
|
-
#
|
|
314
|
-
|
|
315
|
-
|
|
326
|
+
# AIDEV-NOTE: Retry logic for AI code generation
|
|
327
|
+
# Try up to 3 times with different random models before giving up
|
|
328
|
+
# This handles transient failures like rate limits or capacity issues
|
|
329
|
+
local max_retries=3
|
|
330
|
+
local retry_count=0
|
|
331
|
+
local ai_success=false
|
|
332
|
+
|
|
333
|
+
while [[ $retry_count -lt $max_retries ]]; do
|
|
334
|
+
((retry_count++))
|
|
335
|
+
echo "[WORKER-$$] AI attempt $retry_count/$max_retries for $candidate_id" >&2
|
|
336
|
+
|
|
337
|
+
# Re-copy source file if this is a retry (previous attempt may have corrupted it)
|
|
338
|
+
if [[ $retry_count -gt 1 ]]; then
|
|
339
|
+
echo "[WORKER-$$] Re-copying source file for retry attempt" >&2
|
|
340
|
+
cp "$source_file" "$target_file"
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
if call_ai_for_evolution "$evolution_prompt" "$candidate_id"; then
|
|
344
|
+
ai_success=true
|
|
345
|
+
break
|
|
346
|
+
else
|
|
347
|
+
echo "[WORKER-$$] AI attempt $retry_count failed" >&2
|
|
348
|
+
if [[ $retry_count -lt $max_retries ]]; then
|
|
349
|
+
echo "[WORKER-$$] Will retry with a different model..." >&2
|
|
350
|
+
sleep 2 # Brief pause before retry
|
|
351
|
+
fi
|
|
352
|
+
fi
|
|
353
|
+
done
|
|
354
|
+
|
|
355
|
+
if [[ "$ai_success" != "true" ]]; then
|
|
356
|
+
echo "[WORKER-$$] ERROR: All $max_retries AI attempts failed for $candidate_id" >&2
|
|
316
357
|
safe_popd
|
|
317
358
|
rm -f "$target_file" # Clean up on failure
|
|
318
|
-
# Return with special code to indicate AI failure
|
|
359
|
+
# Return with special code to indicate AI failure after all retries exhausted
|
|
319
360
|
return 77
|
|
320
361
|
fi
|
|
321
362
|
|
|
322
363
|
# Restore working directory
|
|
323
364
|
safe_popd
|
|
324
|
-
|
|
325
|
-
echo "[WORKER-$$] Evolution applied successfully"
|
|
326
|
-
|
|
365
|
+
|
|
366
|
+
echo "[WORKER-$$] Evolution applied successfully (attempt $retry_count/$max_retries)"
|
|
367
|
+
|
|
327
368
|
# Record which AI model generated the code (regardless of evaluation outcome)
|
|
328
369
|
if [[ -n "${SUCCESSFUL_RUN_MODEL:-}" ]]; then
|
|
329
370
|
echo "[WORKER-$$] Recording that $SUCCESSFUL_RUN_MODEL generated the code" >&2
|
package/lib/ai-cli.sh
CHANGED
|
@@ -302,6 +302,9 @@ get_models_for_command() {
|
|
|
302
302
|
# Usage: call_ai_random <prompt> <command>
|
|
303
303
|
# command: "run" or "ideate"
|
|
304
304
|
# Picks one random model from the list and uses it
|
|
305
|
+
# AIDEV-NOTE: This function writes the selected model to a temp file because
|
|
306
|
+
# export doesn't work from subshells (command substitution creates a subshell).
|
|
307
|
+
# The parent process should read /tmp/.claude-evolve-model-$$ to get the model name.
|
|
305
308
|
call_ai_random() {
|
|
306
309
|
local prompt="$1"
|
|
307
310
|
local command="$2"
|
|
@@ -330,6 +333,11 @@ call_ai_random() {
|
|
|
330
333
|
|
|
331
334
|
echo "[AI] Selected $model for $command (random from $num_models models)" >&2
|
|
332
335
|
|
|
336
|
+
# Write model to temp file so parent can read it
|
|
337
|
+
# (exports don't propagate from subshells created by $(...) command substitution)
|
|
338
|
+
local model_file="/tmp/.claude-evolve-model-$$"
|
|
339
|
+
echo "$model" > "$model_file"
|
|
340
|
+
|
|
333
341
|
# Call the AI model
|
|
334
342
|
local ai_output
|
|
335
343
|
ai_output=$(call_ai_model_configured "$model" "$prompt")
|
|
@@ -338,9 +346,6 @@ call_ai_random() {
|
|
|
338
346
|
# Clean output if needed
|
|
339
347
|
ai_output=$(clean_ai_output "$ai_output" "$model")
|
|
340
348
|
|
|
341
|
-
# Export the model used for tracking (used by worker)
|
|
342
|
-
export SUCCESSFUL_RUN_MODEL="$model"
|
|
343
|
-
|
|
344
349
|
# Log result
|
|
345
350
|
if [[ $ai_exit_code -eq 0 ]]; then
|
|
346
351
|
echo "[AI] $model returned exit code 0" >&2
|
package/lib/config.sh
CHANGED
|
@@ -58,9 +58,9 @@ DEFAULT_MEMORY_LIMIT_MB=12288
|
|
|
58
58
|
DEFAULT_WORKER_MAX_CANDIDATES=3
|
|
59
59
|
|
|
60
60
|
# Default LLM CLI configuration
|
|
61
|
-
DEFAULT_LLM_RUN="glm-zai
|
|
61
|
+
DEFAULT_LLM_RUN="glm-zai glm-zai glm-zai glm-zai glm-zai kimi-coder codex-oss-local haiku"
|
|
62
62
|
# Ideate: Commercial models for idea generation + local fallback
|
|
63
|
-
DEFAULT_LLM_IDEATE="opus-think kimi-k2-openrouter gemini-3-pro-preview gpt5high grok-4-openrouter deepseek-openrouter glm-zai
|
|
63
|
+
DEFAULT_LLM_IDEATE="opus-think kimi-k2-openrouter gemini-3-pro-preview gpt5high grok-4-openrouter deepseek-openrouter glm-zai"
|
|
64
64
|
|
|
65
65
|
# Load configuration from a YAML file and update variables
|
|
66
66
|
_load_yaml_config() {
|