claude-evolve 1.3.44 → 1.4.1
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 +29 -1
- package/bin/claude-evolve-worker +138 -459
- 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:')
|
|
@@ -232,7 +255,12 @@ try:
|
|
|
232
255
|
status_str = f'{data[\"pending\"]}p {data[\"complete\"]}c {data[\"failed\"]}f {data[\"running\"]}r'
|
|
233
256
|
|
|
234
257
|
if gen_best:
|
|
235
|
-
|
|
258
|
+
# Check if this generation's best is the overall winner
|
|
259
|
+
if winner and gen_best[0] == winner[0]:
|
|
260
|
+
# Highlight in green if it's the overall winner
|
|
261
|
+
print(f' {gen}: {total} total ({status_str}) - best: \033[32m{gen_best[0]} ({gen_best[2]:.4f})\033[0m')
|
|
262
|
+
else:
|
|
263
|
+
print(f' {gen}: {total} total ({status_str}) - best: {gen_best[0]} ({gen_best[2]:.4f})')
|
|
236
264
|
else:
|
|
237
265
|
print(f' {gen}: {total} total ({status_str}) - best: none')
|
|
238
266
|
|