claude-evolve 1.3.44 → 1.4.0
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-analyze +29 -13
- package/bin/claude-evolve-clean-invalid +117 -0
- package/bin/claude-evolve-cleanup-duplicates +131 -0
- package/bin/claude-evolve-ideate +433 -310
- package/bin/claude-evolve-run +79 -30
- package/bin/claude-evolve-status +23 -0
- package/bin/claude-evolve-worker +24 -24
- package/lib/__pycache__/evolution_csv.cpython-311.pyc +0 -0
- package/lib/__pycache__/evolution_csv.cpython-313.pyc +0 -0
- package/lib/config.sh +3 -0
- package/lib/evolution_csv.py +349 -0
- package/package.json +1 -1
package/bin/claude-evolve-run
CHANGED
|
@@ -251,10 +251,9 @@ cleanup_workers() {
|
|
|
251
251
|
worker_pids=("${new_pids[@]}")
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
# Function to count pending candidates
|
|
254
|
+
# Function to count pending candidates - UNIFIED LOGIC
|
|
255
255
|
count_pending_candidates() {
|
|
256
|
-
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/
|
|
257
|
-
echo $? # 0 if found, 1 if not found
|
|
256
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" count
|
|
258
257
|
}
|
|
259
258
|
|
|
260
259
|
# Function to get CSV stats
|
|
@@ -271,26 +270,8 @@ get_csv_stats() {
|
|
|
271
270
|
total_rows=$(wc -l < "$csv_path" | tr -d '[:space:]')
|
|
272
271
|
complete_count=$(grep ',complete' "$csv_path" 2>/dev/null | wc -l | tr -d '[:space:]')
|
|
273
272
|
|
|
274
|
-
# Count pending using
|
|
275
|
-
|
|
276
|
-
pending_count=$("$PYTHON_CMD" -c "
|
|
277
|
-
import csv
|
|
278
|
-
import sys
|
|
279
|
-
|
|
280
|
-
pending_count = 0
|
|
281
|
-
with open('$csv_path', 'r') as f:
|
|
282
|
-
reader = csv.reader(f)
|
|
283
|
-
rows = list(reader)
|
|
284
|
-
|
|
285
|
-
for i in range(1, len(rows)):
|
|
286
|
-
# Same logic as find_next_pending_with_lock
|
|
287
|
-
if len(rows[i]) < 5:
|
|
288
|
-
pending_count += 1
|
|
289
|
-
elif len(rows[i]) >= 5 and (rows[i][4] == 'pending' or rows[i][4] == ''):
|
|
290
|
-
pending_count += 1
|
|
291
|
-
|
|
292
|
-
print(pending_count)
|
|
293
|
-
")
|
|
273
|
+
# Count pending using UNIFIED CSV logic
|
|
274
|
+
pending_count=$("$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$csv_path" count)
|
|
294
275
|
|
|
295
276
|
echo "$total_rows $complete_count $pending_count"
|
|
296
277
|
}
|
|
@@ -298,9 +279,45 @@ print(pending_count)
|
|
|
298
279
|
echo "[DISPATCHER] Starting unified evolution engine"
|
|
299
280
|
echo "[DISPATCHER] Configuration: max_workers=$MAX_WORKERS, timeout=${timeout_seconds:-none}"
|
|
300
281
|
|
|
301
|
-
# Validate CSV and clean up stuck statuses
|
|
282
|
+
# Validate CSV and clean up stuck statuses and duplicates
|
|
302
283
|
if [[ -f "$FULL_CSV_PATH" ]]; then
|
|
303
284
|
echo "[DISPATCHER] Validating CSV and cleaning up..."
|
|
285
|
+
|
|
286
|
+
# First check for and clean up duplicates
|
|
287
|
+
echo "[DISPATCHER] Checking for duplicate entries..."
|
|
288
|
+
duplicate_check_output=$("$PYTHON_CMD" "$SCRIPT_DIR/claude-evolve-cleanup-duplicates" "$FULL_CSV_PATH" 2>&1)
|
|
289
|
+
if echo "$duplicate_check_output" | grep -q "Found.*duplicate"; then
|
|
290
|
+
echo "[DISPATCHER] WARNING: Duplicate entries detected in CSV!"
|
|
291
|
+
echo "$duplicate_check_output"
|
|
292
|
+
echo "[DISPATCHER] Automatically cleaning up duplicates..."
|
|
293
|
+
if "$PYTHON_CMD" "$SCRIPT_DIR/claude-evolve-cleanup-duplicates" "$FULL_CSV_PATH" --fix; then
|
|
294
|
+
echo "[DISPATCHER] Duplicates cleaned up successfully"
|
|
295
|
+
else
|
|
296
|
+
echo "[ERROR] Failed to clean up duplicates" >&2
|
|
297
|
+
exit 1
|
|
298
|
+
fi
|
|
299
|
+
else
|
|
300
|
+
echo "[DISPATCHER] No duplicates found"
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
# Check for and clean up invalid entries
|
|
304
|
+
echo "[DISPATCHER] Checking for invalid entries..."
|
|
305
|
+
invalid_check_output=$("$PYTHON_CMD" "$SCRIPT_DIR/claude-evolve-clean-invalid" "$FULL_CSV_PATH" --dry-run 2>&1)
|
|
306
|
+
if echo "$invalid_check_output" | grep -q "Found.*invalid"; then
|
|
307
|
+
echo "[DISPATCHER] WARNING: Invalid entries detected in CSV!"
|
|
308
|
+
echo "$invalid_check_output"
|
|
309
|
+
echo "[DISPATCHER] Automatically cleaning up invalid entries..."
|
|
310
|
+
if "$PYTHON_CMD" "$SCRIPT_DIR/claude-evolve-clean-invalid" "$FULL_CSV_PATH"; then
|
|
311
|
+
echo "[DISPATCHER] Invalid entries cleaned up successfully"
|
|
312
|
+
else
|
|
313
|
+
echo "[ERROR] Failed to clean up invalid entries" >&2
|
|
314
|
+
exit 1
|
|
315
|
+
fi
|
|
316
|
+
else
|
|
317
|
+
echo "[DISPATCHER] No invalid entries found"
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
# Then validate and clean stuck statuses
|
|
304
321
|
if ! "$PYTHON_CMD" -c "
|
|
305
322
|
import csv
|
|
306
323
|
import sys
|
|
@@ -340,12 +357,13 @@ try:
|
|
|
340
357
|
os.rename(csv_file + '.tmp', csv_file)
|
|
341
358
|
print(f'[INFO] Reset {changed} stuck running candidates to pending')
|
|
342
359
|
|
|
343
|
-
# Count pending candidates
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
360
|
+
# Count pending candidates using UNIFIED logic
|
|
361
|
+
import sys
|
|
362
|
+
sys.path.append('$SCRIPT_DIR/../lib')
|
|
363
|
+
from evolution_csv import EvolutionCSV
|
|
364
|
+
|
|
365
|
+
with EvolutionCSV(csv_file) as csv_ops:
|
|
366
|
+
pending = csv_ops.count_pending_candidates()
|
|
349
367
|
|
|
350
368
|
print(f'[INFO] CSV loaded: {len(rows)-1} total candidates, {pending} pending')
|
|
351
369
|
|
|
@@ -388,6 +406,37 @@ else
|
|
|
388
406
|
echo "[DISPATCHER] No cleanup issues detected - proceeding with run"
|
|
389
407
|
fi
|
|
390
408
|
|
|
409
|
+
# Ensure baseline algorithm performance is recorded
|
|
410
|
+
ensure_baseline_entry() {
|
|
411
|
+
# Check if baseline already exists
|
|
412
|
+
if "$PYTHON_CMD" -c "
|
|
413
|
+
import csv
|
|
414
|
+
with open('$FULL_CSV_PATH', 'r') as f:
|
|
415
|
+
reader = csv.reader(f)
|
|
416
|
+
next(reader, None) # Skip header
|
|
417
|
+
for row in reader:
|
|
418
|
+
if len(row) >= 2:
|
|
419
|
+
candidate_id = row[0]
|
|
420
|
+
parent_id = row[1] if len(row) > 1 else ''
|
|
421
|
+
# Check for baseline entry (empty parent and baseline-like ID)
|
|
422
|
+
if not parent_id and ('baseline' in candidate_id.lower() or candidate_id.startswith('000') or candidate_id == '0'):
|
|
423
|
+
print('found')
|
|
424
|
+
exit(0)
|
|
425
|
+
exit(1)
|
|
426
|
+
"; then
|
|
427
|
+
echo "[DISPATCHER] Baseline performance already recorded"
|
|
428
|
+
else
|
|
429
|
+
echo "[DISPATCHER] No baseline found, adding baseline-000 for evaluation..."
|
|
430
|
+
|
|
431
|
+
# Add baseline entry as pending
|
|
432
|
+
echo "baseline-000,,Original algorithm.py performance,,pending" >> "$FULL_CSV_PATH"
|
|
433
|
+
echo "[DISPATCHER] Added baseline-000 to evaluation queue"
|
|
434
|
+
fi
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
# Check for baseline before starting main loop
|
|
438
|
+
ensure_baseline_entry
|
|
439
|
+
|
|
391
440
|
# With retry mechanism, we don't need consecutive failure tracking
|
|
392
441
|
# Failures are handled gracefully through the retry system
|
|
393
442
|
|
package/bin/claude-evolve-status
CHANGED
|
@@ -111,6 +111,7 @@ csv_file = '$FULL_CSV_PATH'
|
|
|
111
111
|
show_brief = '$SHOW_BRIEF' == 'true'
|
|
112
112
|
show_winner_only = '$SHOW_WINNER_ONLY' == 'true'
|
|
113
113
|
evolution_context = '$EVOLUTION_CONTEXT'
|
|
114
|
+
num_novel_to_show = int('${NUM_REVOLUTION:-2}')
|
|
114
115
|
|
|
115
116
|
def normalize_status(status):
|
|
116
117
|
'''Convert retry statuses to base status for counting.'''
|
|
@@ -183,6 +184,7 @@ try:
|
|
|
183
184
|
if all_candidates:
|
|
184
185
|
winner = max(all_candidates, key=lambda x: x[2])
|
|
185
186
|
|
|
187
|
+
|
|
186
188
|
# Show winner only
|
|
187
189
|
if show_winner_only:
|
|
188
190
|
if winner:
|
|
@@ -218,6 +220,27 @@ try:
|
|
|
218
220
|
print('🏆 CURRENT LEADER: None (no completed candidates)')
|
|
219
221
|
print()
|
|
220
222
|
|
|
223
|
+
# Show top novel candidates
|
|
224
|
+
novel_candidates = []
|
|
225
|
+
for row in rows[1:]:
|
|
226
|
+
if len(row) >= 5 and row[3] and row[4] == 'complete' and not row[1]:
|
|
227
|
+
try:
|
|
228
|
+
candidate_id = row[0]
|
|
229
|
+
description = row[2] if len(row) > 2 else ''
|
|
230
|
+
score = float(row[3])
|
|
231
|
+
novel_candidates.append((candidate_id, description, score))
|
|
232
|
+
except ValueError:
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
if novel_candidates:
|
|
236
|
+
novel_candidates.sort(key=lambda x: x[2], reverse=True)
|
|
237
|
+
print(f'🌟 TOP NOVEL CANDIDATES:')
|
|
238
|
+
# Use the num_novel_to_show variable set at the top
|
|
239
|
+
for i, (candidate_id, description, score) in enumerate(novel_candidates[:num_novel_to_show]):
|
|
240
|
+
print(f' {i+1}. {candidate_id} (score: {score:.4f})')
|
|
241
|
+
print(f' {description}')
|
|
242
|
+
print()
|
|
243
|
+
|
|
221
244
|
# Show per-generation breakdown (unless brief mode)
|
|
222
245
|
if not show_brief and stats_by_gen:
|
|
223
246
|
print('📈 BY GENERATION:')
|
package/bin/claude-evolve-worker
CHANGED
|
@@ -31,26 +31,26 @@ handle_failure() {
|
|
|
31
31
|
|
|
32
32
|
if [[ $new_retry_num -le $MAX_RETRIES ]]; then
|
|
33
33
|
local new_status="failed-retry${new_retry_num}"
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "$new_status"
|
|
35
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" perf "$candidate_id" "$performance"
|
|
36
36
|
echo "[WORKER-$$] ✗ Retry $retry_num failed, marked as $new_status"
|
|
37
37
|
exit 1
|
|
38
38
|
else
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "failed"
|
|
40
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" perf "$candidate_id" "$performance"
|
|
41
41
|
echo "[WORKER-$$] ✗ Max retries ($MAX_RETRIES) exceeded, marking as permanently failed"
|
|
42
42
|
exit 1
|
|
43
43
|
fi
|
|
44
44
|
elif [[ $current_status == "failed" ]]; then
|
|
45
45
|
# Initial failure, convert to retry1 if retries are enabled
|
|
46
46
|
if [[ $MAX_RETRIES -gt 0 ]]; then
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "failed-retry1"
|
|
48
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" perf "$candidate_id" "$performance"
|
|
49
49
|
echo "[WORKER-$$] ✗ Initial failure, marked as failed-retry1 for retry"
|
|
50
50
|
exit 1
|
|
51
51
|
else
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "failed"
|
|
53
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" perf "$candidate_id" "$performance"
|
|
54
54
|
echo "[WORKER-$$] ✗ Failed (retries disabled)"
|
|
55
55
|
# Use exit code 1 - systemic issue since retries are disabled
|
|
56
56
|
exit 1
|
|
@@ -58,13 +58,13 @@ handle_failure() {
|
|
|
58
58
|
else
|
|
59
59
|
# Not a failure scenario, convert to retry1 if retries enabled
|
|
60
60
|
if [[ $MAX_RETRIES -gt 0 ]]; then
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "failed-retry1"
|
|
62
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" perf "$candidate_id" "$performance"
|
|
63
63
|
echo "[WORKER-$$] ✗ Evaluation failed, marked as failed-retry1 for retry"
|
|
64
64
|
exit 1
|
|
65
65
|
else
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "failed"
|
|
67
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" perf "$candidate_id" "$performance"
|
|
68
68
|
echo "[WORKER-$$] ✗ Evaluation failed (retries disabled)"
|
|
69
69
|
exit 1
|
|
70
70
|
fi
|
|
@@ -110,9 +110,9 @@ while [[ $# -gt 0 ]]; do
|
|
|
110
110
|
esac
|
|
111
111
|
done
|
|
112
112
|
|
|
113
|
-
# If no ID provided, find next pending
|
|
113
|
+
# If no ID provided, find next pending using UNIFIED LOGIC
|
|
114
114
|
if [[ -z $candidate_id ]]; then
|
|
115
|
-
candidate_result=$(
|
|
115
|
+
candidate_result=$("$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" next)
|
|
116
116
|
if [[ -z $candidate_result ]]; then
|
|
117
117
|
echo "[DEBUG] No pending candidates found" >&2
|
|
118
118
|
exit 0
|
|
@@ -128,8 +128,8 @@ if [[ -z $candidate_id ]]; then
|
|
|
128
128
|
original_candidate_status=""
|
|
129
129
|
fi
|
|
130
130
|
else
|
|
131
|
-
# Mark specified candidate as running
|
|
132
|
-
|
|
131
|
+
# Mark specified candidate as running using UNIFIED LOGIC
|
|
132
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "running"
|
|
133
133
|
original_candidate_status="" # Unknown for manually specified candidates
|
|
134
134
|
fi
|
|
135
135
|
|
|
@@ -334,7 +334,7 @@ The file currently contains the parent algorithm. Modify it according to the des
|
|
|
334
334
|
echo "[WORKER-$$] Cleaned up temp file due to rate limit" >&2
|
|
335
335
|
fi
|
|
336
336
|
# Reset to pending so it can be retried later
|
|
337
|
-
|
|
337
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "pending"
|
|
338
338
|
exit 2 # Special exit code for rate limit
|
|
339
339
|
fi
|
|
340
340
|
|
|
@@ -391,7 +391,7 @@ if [[ -n $timeout_seconds ]]; then
|
|
|
391
391
|
eval_exit_code=$?
|
|
392
392
|
if [[ $eval_exit_code -eq 124 ]]; then
|
|
393
393
|
echo "[ERROR] Evaluation timed out" >&2
|
|
394
|
-
|
|
394
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "timeout"
|
|
395
395
|
exit 1
|
|
396
396
|
fi
|
|
397
397
|
fi
|
|
@@ -421,8 +421,8 @@ if [[ $eval_exit_code -eq 0 ]]; then
|
|
|
421
421
|
handle_failure "$candidate_id" "$retry_status" "$score"
|
|
422
422
|
exit 1
|
|
423
423
|
else
|
|
424
|
-
|
|
425
|
-
|
|
424
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" perf "$candidate_id" "$score"
|
|
425
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "complete"
|
|
426
426
|
echo "[WORKER-$$] ✓ Evaluation complete, score: $score"
|
|
427
427
|
exit 0
|
|
428
428
|
fi
|
|
@@ -461,8 +461,8 @@ if [[ $eval_exit_code -eq 0 ]]; then
|
|
|
461
461
|
handle_failure "$candidate_id" "$retry_status" "$score"
|
|
462
462
|
exit 1
|
|
463
463
|
else
|
|
464
|
-
|
|
465
|
-
|
|
464
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" perf "$candidate_id" "$score"
|
|
465
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "complete"
|
|
466
466
|
echo "[WORKER-$$] ✓ Evaluation complete, score: $score"
|
|
467
467
|
exit 0
|
|
468
468
|
fi
|
|
@@ -476,8 +476,8 @@ if [[ $eval_exit_code -eq 0 ]]; then
|
|
|
476
476
|
handle_failure "$candidate_id" "$retry_status" "$score"
|
|
477
477
|
exit 1
|
|
478
478
|
else
|
|
479
|
-
|
|
480
|
-
|
|
479
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" perf "$candidate_id" "$score"
|
|
480
|
+
"$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_csv.py" "$FULL_CSV_PATH" update "$candidate_id" "complete"
|
|
481
481
|
echo "[WORKER-$$] ✓ Evaluation complete, score: $score"
|
|
482
482
|
exit 0
|
|
483
483
|
fi
|
|
Binary file
|
|
Binary file
|
package/lib/config.sh
CHANGED
|
@@ -36,6 +36,7 @@ DEFAULT_HILL_CLIMBING=5
|
|
|
36
36
|
DEFAULT_STRUCTURAL_MUTATION=3
|
|
37
37
|
DEFAULT_CROSSOVER_HYBRID=4
|
|
38
38
|
DEFAULT_NUM_ELITES=3
|
|
39
|
+
DEFAULT_NUM_REVOLUTION=2 # Number of top novel candidates to include
|
|
39
40
|
|
|
40
41
|
# Default parallel execution values
|
|
41
42
|
DEFAULT_PARALLEL_ENABLED=false
|
|
@@ -70,6 +71,7 @@ load_config() {
|
|
|
70
71
|
STRUCTURAL_MUTATION="$DEFAULT_STRUCTURAL_MUTATION"
|
|
71
72
|
CROSSOVER_HYBRID="$DEFAULT_CROSSOVER_HYBRID"
|
|
72
73
|
NUM_ELITES="$DEFAULT_NUM_ELITES"
|
|
74
|
+
NUM_REVOLUTION="$DEFAULT_NUM_REVOLUTION"
|
|
73
75
|
|
|
74
76
|
# Set parallel execution defaults
|
|
75
77
|
PARALLEL_ENABLED="$DEFAULT_PARALLEL_ENABLED"
|
|
@@ -138,6 +140,7 @@ load_config() {
|
|
|
138
140
|
structural_mutation) STRUCTURAL_MUTATION="$value" ;;
|
|
139
141
|
crossover_hybrid) CROSSOVER_HYBRID="$value" ;;
|
|
140
142
|
num_elites) NUM_ELITES="$value" ;;
|
|
143
|
+
num_revolution) NUM_REVOLUTION="$value" ;;
|
|
141
144
|
esac
|
|
142
145
|
elif [[ $in_parallel_section == true ]]; then
|
|
143
146
|
# Handle indented keys in parallel section
|