claude-evolve 1.5.2 → 1.5.4
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/README.md +16 -1
- package/bin/claude-evolve-analyze +42 -15
- package/bin/claude-evolve-autostatus +2 -2
- package/bin/claude-evolve-edit +182 -17
- package/bin/claude-evolve-ideate +105 -32
- package/bin/claude-evolve-run +71 -1
- package/bin/claude-evolve-status +3 -1
- package/bin/claude-evolve-worker +96 -39
- package/lib/ai-cli.sh +7 -2
- package/lib/config.sh +22 -16
- package/lib/csv_fixer.py +35 -0
- package/lib/memory_limit_wrapper.py +192 -0
- package/package.json +1 -1
- package/templates/config.yaml +10 -10
package/README.md
CHANGED
|
@@ -30,14 +30,29 @@ Evolution runs indefinitely until you stop it. Perfect for overnight optimizatio
|
|
|
30
30
|
|
|
31
31
|
## Commands
|
|
32
32
|
|
|
33
|
+
### Core Commands
|
|
33
34
|
```bash
|
|
34
35
|
claude-evolve # Interactive menu
|
|
35
36
|
claude-evolve setup # Initialize workspace
|
|
36
37
|
claude-evolve ideate # Generate new algorithm ideas
|
|
37
38
|
claude-evolve run # Start evolution loop (runs forever)
|
|
38
|
-
claude-evolve analyze # View results and progress
|
|
39
|
+
claude-evolve analyze # View results and progress with charts
|
|
39
40
|
claude-evolve status # Quick progress overview
|
|
41
|
+
claude-evolve autostatus # Live auto-updating status display
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Management Commands
|
|
45
|
+
```bash
|
|
40
46
|
claude-evolve edit # Manage candidate statuses
|
|
47
|
+
claude-evolve config # View/edit configuration
|
|
48
|
+
claude-evolve cleanup # Clean up old lock files
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Maintenance Commands
|
|
52
|
+
```bash
|
|
53
|
+
claude-evolve clean-invalid # Remove invalid candidates
|
|
54
|
+
claude-evolve cleanup-duplicates # Remove duplicate entries
|
|
55
|
+
claude-evolve csv-fix # Fix CSV formatting issues
|
|
41
56
|
```
|
|
42
57
|
|
|
43
58
|
## Working with Multiple Projects
|
|
@@ -246,7 +246,8 @@ with open('$csv_file', 'r') as f:
|
|
|
246
246
|
"
|
|
247
247
|
|
|
248
248
|
# Process generation stats
|
|
249
|
-
|
|
249
|
+
# Sort generations numerically
|
|
250
|
+
for gen in $(cut -d' ' -f1 "$gen_stats_file" | sort -u | awk '{print substr($0,4), $0}' | sort -n | cut -d' ' -f2 || echo ""); do
|
|
250
251
|
[[ -z "$gen" ]] && continue
|
|
251
252
|
total_in_gen=$(grep -c "^$gen " "$gen_stats_file" 2>/dev/null || echo "0")
|
|
252
253
|
completed_in_gen=$(grep -c "^$gen completed" "$gen_stats_file" 2>/dev/null || echo "0")
|
|
@@ -461,7 +462,8 @@ print(f'max_desc=\"{desc_escaped}\"')
|
|
|
461
462
|
# Create generation averages file and track max generation
|
|
462
463
|
gen_index=1
|
|
463
464
|
max_gen_num=0
|
|
464
|
-
|
|
465
|
+
# Sort generations numerically for graph
|
|
466
|
+
for gen in $(cut -d' ' -f1 "$gen_data_temp" | sort -u | awk '{print substr($0,4), $0}' | sort -n | cut -d' ' -f2); do
|
|
465
467
|
if grep -q "^$gen " "$gen_data_temp"; then
|
|
466
468
|
# Calculate median for this generation
|
|
467
469
|
# AIDEV-NOTE: Changed from mean to median to be more robust to outliers
|
|
@@ -511,6 +513,9 @@ print(f'max_desc=\"{desc_escaped}\"')
|
|
|
511
513
|
# Calculate total data points for dynamic sizing
|
|
512
514
|
total_data_points=$(awk 'END {print NR-1}' "$data_file") # Subtract header row
|
|
513
515
|
|
|
516
|
+
# Count unique generations
|
|
517
|
+
unique_generations=$(awk '{if(NR>1) print $4}' "$data_file" | sort -nu | wc -l)
|
|
518
|
+
|
|
514
519
|
# AIDEV-NOTE: Dynamic dot sizing based on data point count
|
|
515
520
|
# Use significantly larger dots when there are fewer data points for better visibility
|
|
516
521
|
if [[ $total_data_points -lt 35 ]]; then
|
|
@@ -528,16 +533,24 @@ print(f'max_desc=\"{desc_escaped}\"')
|
|
|
528
533
|
# Find all generations that have data
|
|
529
534
|
generations=($(awk '{if(NR>1) print $4}' "$data_file" | sort -n | uniq))
|
|
530
535
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
+
# If too many generations (>10), use a simplified plot without individual generation legends
|
|
537
|
+
if [[ $unique_generations -gt 10 ]]; then
|
|
538
|
+
# Single plot with color gradient based on generation number
|
|
539
|
+
plot_cmd="\"$data_file\" using 1:3:(\$4) with points palette pointsize $regular_dot_size notitle"
|
|
540
|
+
gen_plots_added=1
|
|
541
|
+
else
|
|
542
|
+
# Original plotting with individual generation legends
|
|
543
|
+
for gen_num in "${generations[@]}"; do
|
|
544
|
+
if [[ -n $gen_num ]]; then
|
|
545
|
+
color=$(get_gen_color "$gen_num")
|
|
546
|
+
if [[ $gen_plots_added -gt 0 ]]; then
|
|
547
|
+
plot_cmd="$plot_cmd, \\"$'\n'
|
|
548
|
+
fi
|
|
549
|
+
plot_cmd="${plot_cmd} \"$data_file\" using (\$4==$gen_num?\$1:1/0):3 with points linecolor rgb \"$color\" pointsize $regular_dot_size title \"Gen $gen_num\""
|
|
550
|
+
((gen_plots_added++))
|
|
536
551
|
fi
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
fi
|
|
540
|
-
done
|
|
552
|
+
done
|
|
553
|
+
fi
|
|
541
554
|
|
|
542
555
|
# Add novel candidates
|
|
543
556
|
if [[ -s "$novel_file" ]] && [[ $(wc -l < "$novel_file") -gt 1 ]]; then
|
|
@@ -568,7 +581,8 @@ print(f'max_desc=\"{desc_escaped}\"')
|
|
|
568
581
|
# Build x-axis labels for generation chart (include all generations from data)
|
|
569
582
|
xtics_labels=""
|
|
570
583
|
label_index=1
|
|
571
|
-
|
|
584
|
+
# Sort generations numerically for graph
|
|
585
|
+
for gen in $(cut -d' ' -f1 "$gen_data_temp" | sort -u | awk '{print substr($0,4), $0}' | sort -n | cut -d' ' -f2); do
|
|
572
586
|
if [[ -n $gen ]]; then
|
|
573
587
|
gen_display=$(echo "$gen" | sed 's/gen0*//')
|
|
574
588
|
if [[ -n $xtics_labels ]]; then
|
|
@@ -583,6 +597,9 @@ print(f'max_desc=\"{desc_escaped}\"')
|
|
|
583
597
|
set terminal png size 1200,800
|
|
584
598
|
set output "$output_file"
|
|
585
599
|
|
|
600
|
+
# Define unique generations count
|
|
601
|
+
unique_gens = $unique_generations
|
|
602
|
+
|
|
586
603
|
# Set up multiplot with proper spacing
|
|
587
604
|
set multiplot layout 2,1 margins 0.08,0.82,0.15,0.95 spacing 0.1,0.15
|
|
588
605
|
|
|
@@ -592,7 +609,9 @@ set title "$EVOLUTION_CONTEXT Algorithm Evolution Performance Over Time" font ",
|
|
|
592
609
|
unset xlabel
|
|
593
610
|
set ylabel "Performance Score"
|
|
594
611
|
set grid y # Only show horizontal grid lines
|
|
595
|
-
|
|
612
|
+
|
|
613
|
+
# Show legend only if 10 or fewer generations
|
|
614
|
+
if (unique_gens <= 10) set key outside right; else unset key
|
|
596
615
|
|
|
597
616
|
# AIDEV-NOTE: Remove x-axis entirely to avoid tick problems with large datasets
|
|
598
617
|
unset xtics
|
|
@@ -600,6 +619,9 @@ set autoscale
|
|
|
600
619
|
set yrange [*:*] # Auto-scale y-axis only
|
|
601
620
|
|
|
602
621
|
# Define colors for generations
|
|
622
|
+
# Use palette for many generations
|
|
623
|
+
if (unique_gens > 10) set palette model RGB defined (0 "#1f77b4", 1 "#ff7f0e", 2 "#2ca02c", 3 "#d62728", 4 "#9467bd", 5 "#8c564b", 6 "#e377c2")
|
|
624
|
+
|
|
603
625
|
plot $plot_cmd
|
|
604
626
|
|
|
605
627
|
#=================== BOTTOM PLOT: Generation Medians ===================
|
|
@@ -611,8 +633,13 @@ set boxwidth 0.6
|
|
|
611
633
|
unset key
|
|
612
634
|
set grid y
|
|
613
635
|
|
|
614
|
-
# Set custom x-axis labels
|
|
615
|
-
|
|
636
|
+
# Set custom x-axis labels (but hide if too many generations)
|
|
637
|
+
if (unique_gens > 10) {
|
|
638
|
+
set xtics auto
|
|
639
|
+
set xtics rotate by -45
|
|
640
|
+
} else {
|
|
641
|
+
set xtics ($xtics_labels)
|
|
642
|
+
}
|
|
616
643
|
|
|
617
644
|
# Auto-scale for generation plot too
|
|
618
645
|
set autoscale
|
|
@@ -246,8 +246,8 @@ class AutoStatus:
|
|
|
246
246
|
print("-" * min(self.display.cols, len(header_fmt)))
|
|
247
247
|
row += 1
|
|
248
248
|
|
|
249
|
-
# Sort generations
|
|
250
|
-
sorted_gens = sorted(generations.keys())
|
|
249
|
+
# Sort generations numerically by extracting the number after 'gen'
|
|
250
|
+
sorted_gens = sorted(generations.keys(), key=lambda g: int(g[3:]) if g.startswith("gen") and g[3:].isdigit() else 0)
|
|
251
251
|
|
|
252
252
|
# Calculate how many generations we can show
|
|
253
253
|
available_rows = self.display.rows - row - 1 # Leave room at bottom
|
package/bin/claude-evolve-edit
CHANGED
|
@@ -20,7 +20,10 @@ show_help() {
|
|
|
20
20
|
claude-evolve edit - Manage evolution candidate statuses by generation or status
|
|
21
21
|
|
|
22
22
|
USAGE:
|
|
23
|
-
claude-evolve edit <selector> <action>
|
|
23
|
+
claude-evolve edit [--recent-generations=N] <selector> <action>
|
|
24
|
+
|
|
25
|
+
OPTIONS:
|
|
26
|
+
--recent-generations=N Limit operations to the most recent N generations only
|
|
24
27
|
|
|
25
28
|
SELECTORS:
|
|
26
29
|
gen01, gen02, etc. Target specific generation
|
|
@@ -41,29 +44,56 @@ ACTIONS:
|
|
|
41
44
|
delete Delete candidates from CSV and remove .py files (asks confirmation)
|
|
42
45
|
|
|
43
46
|
EXAMPLES:
|
|
44
|
-
claude-evolve edit gen03 failed
|
|
45
|
-
claude-evolve edit failed pending
|
|
46
|
-
claude-evolve edit
|
|
47
|
-
claude-evolve edit complete failed
|
|
48
|
-
claude-evolve edit
|
|
49
|
-
claude-evolve edit
|
|
50
|
-
claude-evolve edit
|
|
47
|
+
claude-evolve edit gen03 failed # Mark all gen03 as failed
|
|
48
|
+
claude-evolve edit failed pending # Reset all failed candidates to pending
|
|
49
|
+
claude-evolve edit --recent-generations=15 failed pending # Reset only recent 15 gen failures
|
|
50
|
+
claude-evolve edit --recent-generations=5 complete failed # Re-run recent 5 gen completions
|
|
51
|
+
claude-evolve edit failed failed-retry1 # Convert failed to retry status (bug fixing)
|
|
52
|
+
claude-evolve edit complete failed # Mark all complete as failed for re-run
|
|
53
|
+
claude-evolve edit all pending # Mark everything as pending for re-run
|
|
54
|
+
claude-evolve edit gen02 reboot # Full reset of gen02 (delete files + clear data)
|
|
55
|
+
claude-evolve edit gen02 delete # Delete gen02 from CSV and remove .py files
|
|
51
56
|
|
|
52
57
|
DESCRIPTION:
|
|
53
58
|
This command helps manage evolution runs when you need to re-evaluate candidates.
|
|
54
59
|
Use status selectors (failed, complete, etc.) to bulk-change candidates by status.
|
|
55
60
|
Use 'reboot' for complete reset including file deletion.
|
|
61
|
+
Use --recent-generations to limit operations to recent work only, useful for large systems.
|
|
56
62
|
EOF
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
# Parse arguments
|
|
60
|
-
|
|
66
|
+
recent_generations=""
|
|
67
|
+
args=()
|
|
68
|
+
|
|
69
|
+
while [[ $# -gt 0 ]]; do
|
|
70
|
+
case "$1" in
|
|
71
|
+
--recent-generations=*)
|
|
72
|
+
recent_generations="${1#*=}"
|
|
73
|
+
if [[ ! "$recent_generations" =~ ^[1-9][0-9]*$ ]]; then
|
|
74
|
+
echo "[ERROR] --recent-generations must be a positive integer" >&2
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
shift
|
|
78
|
+
;;
|
|
79
|
+
--help|-h)
|
|
80
|
+
show_help
|
|
81
|
+
exit 0
|
|
82
|
+
;;
|
|
83
|
+
*)
|
|
84
|
+
args+=("$1")
|
|
85
|
+
shift
|
|
86
|
+
;;
|
|
87
|
+
esac
|
|
88
|
+
done
|
|
89
|
+
|
|
90
|
+
if [[ ${#args[@]} -ne 2 ]]; then
|
|
61
91
|
show_help
|
|
62
92
|
exit 1
|
|
63
93
|
fi
|
|
64
94
|
|
|
65
|
-
SELECTOR="$
|
|
66
|
-
ACTION="$
|
|
95
|
+
SELECTOR="${args[0]}"
|
|
96
|
+
ACTION="${args[1]}"
|
|
67
97
|
|
|
68
98
|
# Validate configuration
|
|
69
99
|
if ! validate_config; then
|
|
@@ -99,7 +129,11 @@ update_candidates_status() {
|
|
|
99
129
|
local new_status="$2"
|
|
100
130
|
local clear_scores="$3"
|
|
101
131
|
|
|
102
|
-
|
|
132
|
+
local filter_msg=""
|
|
133
|
+
if [[ -n "$recent_generations" ]]; then
|
|
134
|
+
filter_msg=" (limited to recent $recent_generations generations)"
|
|
135
|
+
fi
|
|
136
|
+
echo "[INFO] Updating candidates matching '$selector' to status: $new_status${filter_msg}"
|
|
103
137
|
|
|
104
138
|
# Use Python to safely edit the CSV
|
|
105
139
|
"$PYTHON_CMD" -c "
|
|
@@ -112,6 +146,7 @@ csv_file = '$FULL_CSV_PATH'
|
|
|
112
146
|
selector = '$selector'
|
|
113
147
|
new_status = '$new_status'
|
|
114
148
|
clear_scores = '$clear_scores' == 'true'
|
|
149
|
+
recent_generations = '$recent_generations'
|
|
115
150
|
|
|
116
151
|
|
|
117
152
|
try:
|
|
@@ -127,6 +162,31 @@ try:
|
|
|
127
162
|
header = rows[0]
|
|
128
163
|
updated_count = 0
|
|
129
164
|
|
|
165
|
+
# If recent_generations is specified, determine which generations to include
|
|
166
|
+
recent_gen_set = set()
|
|
167
|
+
if recent_generations and recent_generations.isdigit():
|
|
168
|
+
n_recent = int(recent_generations)
|
|
169
|
+
|
|
170
|
+
# Find all generation numbers from candidate IDs
|
|
171
|
+
all_generations = set()
|
|
172
|
+
for i in range(1, len(rows)):
|
|
173
|
+
row = rows[i]
|
|
174
|
+
if len(row) < 1:
|
|
175
|
+
continue
|
|
176
|
+
candidate_id = row[0]
|
|
177
|
+
|
|
178
|
+
# Extract generation number from candidate_id (e.g., gen01-001 -> 1)
|
|
179
|
+
match = re.match(r'^gen(\d+)-', candidate_id)
|
|
180
|
+
if match:
|
|
181
|
+
gen_num = int(match.group(1))
|
|
182
|
+
all_generations.add(gen_num)
|
|
183
|
+
|
|
184
|
+
# Get the most recent N generations
|
|
185
|
+
if all_generations:
|
|
186
|
+
sorted_generations = sorted(all_generations, reverse=True)
|
|
187
|
+
recent_gen_set = set(sorted_generations[:n_recent])
|
|
188
|
+
print(f'[INFO] Filtering to recent generations: {sorted(recent_gen_set)}', file=sys.stderr)
|
|
189
|
+
|
|
130
190
|
# Update matching rows
|
|
131
191
|
for i in range(1, len(rows)):
|
|
132
192
|
row = rows[i]
|
|
@@ -152,6 +212,18 @@ try:
|
|
|
152
212
|
else:
|
|
153
213
|
matches = current_status == selector
|
|
154
214
|
|
|
215
|
+
# Apply recent generations filter if specified
|
|
216
|
+
if matches and recent_gen_set:
|
|
217
|
+
# Extract generation number from candidate_id
|
|
218
|
+
gen_match = re.match(r'^gen(\d+)-', candidate_id)
|
|
219
|
+
if gen_match:
|
|
220
|
+
candidate_gen = int(gen_match.group(1))
|
|
221
|
+
if candidate_gen not in recent_gen_set:
|
|
222
|
+
matches = False # Filter out this candidate
|
|
223
|
+
else:
|
|
224
|
+
# Non-generation candidates (like baseline) - exclude when filtering by recent generations
|
|
225
|
+
matches = False
|
|
226
|
+
|
|
155
227
|
if matches:
|
|
156
228
|
if clear_scores:
|
|
157
229
|
# Clear everything after description (keep id, basedOnId, description)
|
|
@@ -192,6 +264,12 @@ delete_evolution_files() {
|
|
|
192
264
|
return
|
|
193
265
|
fi
|
|
194
266
|
|
|
267
|
+
local filter_msg=""
|
|
268
|
+
if [[ -n "$recent_generations" ]]; then
|
|
269
|
+
filter_msg=" (limited to recent $recent_generations generations)"
|
|
270
|
+
fi
|
|
271
|
+
echo "[INFO] Deleting evolution files for '$selector'${filter_msg}..."
|
|
272
|
+
|
|
195
273
|
local deleted_count=0
|
|
196
274
|
|
|
197
275
|
if [[ "$selector" == "all" ]]; then
|
|
@@ -225,15 +303,46 @@ import re
|
|
|
225
303
|
|
|
226
304
|
csv_file = '$FULL_CSV_PATH'
|
|
227
305
|
selector = '$selector'
|
|
306
|
+
recent_generations = '$recent_generations'
|
|
228
307
|
|
|
229
308
|
|
|
230
309
|
try:
|
|
231
310
|
with open(csv_file, 'r') as f:
|
|
232
311
|
reader = csv.reader(f)
|
|
233
|
-
|
|
312
|
+
rows = list(reader)
|
|
313
|
+
|
|
314
|
+
if not rows:
|
|
315
|
+
print('')
|
|
316
|
+
sys.exit(0)
|
|
317
|
+
|
|
318
|
+
# Skip header if present
|
|
319
|
+
start_idx = 1 if rows and rows[0] and rows[0][0].lower() == 'id' else 0
|
|
320
|
+
|
|
321
|
+
# Determine recent generations if filtering is requested
|
|
322
|
+
recent_gen_set = set()
|
|
323
|
+
if recent_generations and recent_generations.isdigit():
|
|
324
|
+
n_recent = int(recent_generations)
|
|
325
|
+
|
|
326
|
+
# Find all generation numbers from candidate IDs
|
|
327
|
+
all_generations = set()
|
|
328
|
+
for row in rows[start_idx:]:
|
|
329
|
+
if len(row) < 1:
|
|
330
|
+
continue
|
|
331
|
+
candidate_id = row[0]
|
|
332
|
+
|
|
333
|
+
# Extract generation number from candidate_id (e.g., gen01-001 -> 1)
|
|
334
|
+
match = re.match(r'^gen(\d+)-', candidate_id)
|
|
335
|
+
if match:
|
|
336
|
+
gen_num = int(match.group(1))
|
|
337
|
+
all_generations.add(gen_num)
|
|
338
|
+
|
|
339
|
+
# Get the most recent N generations
|
|
340
|
+
if all_generations:
|
|
341
|
+
sorted_generations = sorted(all_generations, reverse=True)
|
|
342
|
+
recent_gen_set = set(sorted_generations[:n_recent])
|
|
234
343
|
|
|
235
344
|
candidates = []
|
|
236
|
-
for row in
|
|
345
|
+
for row in rows[start_idx:]:
|
|
237
346
|
if len(row) < 1:
|
|
238
347
|
continue
|
|
239
348
|
|
|
@@ -249,6 +358,18 @@ try:
|
|
|
249
358
|
else:
|
|
250
359
|
matches = current_status == selector
|
|
251
360
|
|
|
361
|
+
# Apply recent generations filter if specified
|
|
362
|
+
if matches and recent_gen_set:
|
|
363
|
+
# Extract generation number from candidate_id
|
|
364
|
+
gen_match = re.match(r'^gen(\d+)-', candidate_id)
|
|
365
|
+
if gen_match:
|
|
366
|
+
candidate_gen = int(gen_match.group(1))
|
|
367
|
+
if candidate_gen not in recent_gen_set:
|
|
368
|
+
matches = False # Filter out this candidate
|
|
369
|
+
else:
|
|
370
|
+
# Non-generation candidates (like baseline) - exclude when filtering by recent generations
|
|
371
|
+
matches = False
|
|
372
|
+
|
|
252
373
|
if matches:
|
|
253
374
|
candidates.append(candidate_id)
|
|
254
375
|
|
|
@@ -284,7 +405,11 @@ except Exception as e:
|
|
|
284
405
|
delete_candidates_from_csv() {
|
|
285
406
|
local selector="$1"
|
|
286
407
|
|
|
287
|
-
|
|
408
|
+
local filter_msg=""
|
|
409
|
+
if [[ -n "$recent_generations" ]]; then
|
|
410
|
+
filter_msg=" (limited to recent $recent_generations generations)"
|
|
411
|
+
fi
|
|
412
|
+
echo "[INFO] Deleting candidates matching '$selector' from CSV${filter_msg}..."
|
|
288
413
|
|
|
289
414
|
"$PYTHON_CMD" -c "
|
|
290
415
|
import sys
|
|
@@ -293,6 +418,7 @@ from lib.evolution_csv import EvolutionCSV
|
|
|
293
418
|
import re
|
|
294
419
|
|
|
295
420
|
selector = '$selector'
|
|
421
|
+
recent_generations = '$recent_generations'
|
|
296
422
|
deleted_count = 0
|
|
297
423
|
|
|
298
424
|
with EvolutionCSV('$FULL_CSV_PATH') as csv:
|
|
@@ -306,6 +432,29 @@ with EvolutionCSV('$FULL_CSV_PATH') as csv:
|
|
|
306
432
|
has_header = rows and rows[0] and rows[0][0].lower() == 'id'
|
|
307
433
|
start_idx = 1 if has_header else 0
|
|
308
434
|
|
|
435
|
+
# Determine recent generations if filtering is requested
|
|
436
|
+
recent_gen_set = set()
|
|
437
|
+
if recent_generations and recent_generations.isdigit():
|
|
438
|
+
n_recent = int(recent_generations)
|
|
439
|
+
|
|
440
|
+
# Find all generation numbers from candidate IDs
|
|
441
|
+
all_generations = set()
|
|
442
|
+
for row in rows[start_idx:]:
|
|
443
|
+
if not row or not row[0].strip():
|
|
444
|
+
continue
|
|
445
|
+
candidate_id = row[0].strip()
|
|
446
|
+
|
|
447
|
+
# Extract generation number from candidate_id (e.g., gen01-001 -> 1)
|
|
448
|
+
match = re.match(r'^gen(\d+)-', candidate_id)
|
|
449
|
+
if match:
|
|
450
|
+
gen_num = int(match.group(1))
|
|
451
|
+
all_generations.add(gen_num)
|
|
452
|
+
|
|
453
|
+
# Get the most recent N generations
|
|
454
|
+
if all_generations:
|
|
455
|
+
sorted_generations = sorted(all_generations, reverse=True)
|
|
456
|
+
recent_gen_set = set(sorted_generations[:n_recent])
|
|
457
|
+
|
|
309
458
|
for row in rows[start_idx:]:
|
|
310
459
|
if not row or not row[0].strip():
|
|
311
460
|
continue
|
|
@@ -327,6 +476,18 @@ with EvolutionCSV('$FULL_CSV_PATH') as csv:
|
|
|
327
476
|
else:
|
|
328
477
|
matches = current_status == selector
|
|
329
478
|
|
|
479
|
+
# Apply recent generations filter if specified
|
|
480
|
+
if matches and recent_gen_set:
|
|
481
|
+
# Extract generation number from candidate_id
|
|
482
|
+
gen_match = re.match(r'^gen(\d+)-', candidate_id)
|
|
483
|
+
if gen_match:
|
|
484
|
+
candidate_gen = int(gen_match.group(1))
|
|
485
|
+
if candidate_gen not in recent_gen_set:
|
|
486
|
+
matches = False # Filter out this candidate
|
|
487
|
+
else:
|
|
488
|
+
# Non-generation candidates (like baseline) - exclude when filtering by recent generations
|
|
489
|
+
matches = False
|
|
490
|
+
|
|
330
491
|
if matches:
|
|
331
492
|
candidates_to_delete.append(candidate_id)
|
|
332
493
|
|
|
@@ -341,7 +502,11 @@ with EvolutionCSV('$FULL_CSV_PATH') as csv:
|
|
|
341
502
|
}
|
|
342
503
|
|
|
343
504
|
# Main execution
|
|
344
|
-
|
|
505
|
+
info_msg="Processing '$SELECTOR' with action: $ACTION"
|
|
506
|
+
if [[ -n "$recent_generations" ]]; then
|
|
507
|
+
info_msg="$info_msg (limited to recent $recent_generations generations)"
|
|
508
|
+
fi
|
|
509
|
+
echo "[INFO] $info_msg"
|
|
345
510
|
|
|
346
511
|
case "$ACTION" in
|
|
347
512
|
failed)
|
|
@@ -387,4 +552,4 @@ echo "[INFO] Edit operation complete"
|
|
|
387
552
|
|
|
388
553
|
# Call status command to show current state
|
|
389
554
|
echo ""
|
|
390
|
-
"$SCRIPT_DIR/claude-evolve-status" --brief
|
|
555
|
+
"$SCRIPT_DIR/claude-evolve-status" --brief
|