claude-evolve 1.3.37 → 1.3.38

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.
@@ -165,7 +165,7 @@ echo "Pending: $pending"
165
165
 
166
166
  if [[ $count_with_performance -gt 0 ]]; then
167
167
  avg_performance=$(echo "scale=4; $total_performance / $count_with_performance" | bc -l 2>/dev/null || echo "0")
168
- echo "Average Performance: $avg_performance"
168
+ echo "Average Performance: $avg_performance" # Still showing mean for overall stats
169
169
  else
170
170
  echo "Average Performance: N/A"
171
171
  fi
@@ -238,13 +238,24 @@ for gen in $(cut -d' ' -f1 "$gen_stats_file" | sort -u || echo ""); do
238
238
  echo -n "$gen: $total_in_gen candidates"
239
239
 
240
240
  if [[ "$completed_in_gen" -gt 0 ]]; then
241
- # Calculate average performance for this generation
242
- sum="0"
241
+ # Calculate median performance for this generation
242
+ # AIDEV-NOTE: Changed from mean to median to be more robust to outliers
243
+ median="0"
243
244
  if grep -q "^$gen completed" "$gen_stats_file"; then
244
- sum=$(grep "^$gen completed" "$gen_stats_file" | awk '{s+=$3} END {printf "%.6f", s}' 2>/dev/null || echo "0")
245
+ median=$(grep "^$gen completed" "$gen_stats_file" | awk '{print $3}' | sort -n | awk '{
246
+ a[NR] = $0
247
+ }
248
+ END {
249
+ if (NR % 2) {
250
+ # Odd number of elements
251
+ print a[(NR + 1) / 2]
252
+ } else {
253
+ # Even number of elements - average of two middle values
254
+ printf "%.4f", (a[NR/2] + a[NR/2 + 1]) / 2.0
255
+ }
256
+ }' 2>/dev/null || echo "0")
245
257
  fi
246
- avg=$(echo "scale=4; $sum / $completed_in_gen" | bc -l 2>/dev/null || echo "0")
247
- echo " ($completed_in_gen completed, avg: $avg)"
258
+ echo " ($completed_in_gen completed, median: $median)"
248
259
  else
249
260
  echo " (0 completed)"
250
261
  fi
@@ -290,7 +301,7 @@ if command -v gnuplot >/dev/null 2>&1 && [[ $valid_performance_count -gt 0 ]]; t
290
301
  gen_avg_file="/tmp/evolution_gen_avg_$$.dat"
291
302
 
292
303
  echo "# Row ID Performance Generation" >"$data_file"
293
- echo "# Generation AvgPerformance Color" >"$gen_avg_file"
304
+ echo "# Generation MedianPerformance Color" >"$gen_avg_file"
294
305
 
295
306
  # Get color by generation number (rotates through 7 colors)
296
307
  get_gen_color() {
@@ -417,18 +428,31 @@ print(f'max_id=\"{max_id}\"')
417
428
  max_gen_num=0
418
429
  for gen in $(cut -d' ' -f1 "$gen_data_temp" | sort -u); do
419
430
  if grep -q "^$gen " "$gen_data_temp"; then
420
- # Calculate average for this generation
421
- sum=$(grep "^$gen " "$gen_data_temp" | awk '{s+=$2} END {printf "%.6f", s}' 2>/dev/null || echo "0")
431
+ # Calculate median for this generation
432
+ # AIDEV-NOTE: Changed from mean to median to be more robust to outliers
433
+ # Extract all performance values for this generation and sort them
434
+ median=$(grep "^$gen " "$gen_data_temp" | awk '{print $2}' | sort -n | awk '{
435
+ a[NR] = $0
436
+ }
437
+ END {
438
+ if (NR % 2) {
439
+ # Odd number of elements
440
+ print a[(NR + 1) / 2]
441
+ } else {
442
+ # Even number of elements - average of two middle values
443
+ print (a[NR/2] + a[NR/2 + 1]) / 2.0
444
+ }
445
+ }' 2>/dev/null || echo "0")
422
446
  count=$(grep -c "^$gen " "$gen_data_temp")
423
447
  if [[ $count -gt 0 ]]; then
424
- avg=$(echo "scale=4; $sum / $count" | bc -l 2>/dev/null || echo "0")
448
+ avg=$median # Using median instead of mean
425
449
  gen_num=$(echo "$gen" | sed 's/gen0*//')
426
450
  # Track max generation number
427
451
  if [[ $gen_num =~ ^[0-9]+$ ]] && [[ $gen_num -gt $max_gen_num ]]; then
428
452
  max_gen_num=$gen_num
429
453
  fi
430
454
  color=$(get_gen_color "$gen_num")
431
- echo "$gen_index \"Gen$gen_num\" $avg \"$color\"" >>"$gen_avg_file"
455
+ echo "$gen_index \"Gen$gen_num\" $avg \"$color\"" >>"$gen_avg_file" # avg is now median
432
456
  ((gen_index++))
433
457
  fi
434
458
  fi
@@ -449,6 +473,19 @@ print(f'max_id=\"{max_id}\"')
449
473
  # cat "$data_file"
450
474
  # echo "DEBUG: max_gen_num=$max_gen_num"
451
475
 
476
+ # Calculate total data points for dynamic sizing
477
+ total_data_points=$(awk 'END {print NR-1}' "$data_file") # Subtract header row
478
+
479
+ # AIDEV-NOTE: Dynamic dot sizing based on data point count
480
+ # Use significantly larger dots when there are fewer data points for better visibility
481
+ if [[ $total_data_points -lt 35 ]]; then
482
+ regular_dot_size="1.8"
483
+ winner_dot_size="3.0"
484
+ else
485
+ regular_dot_size="0.6"
486
+ winner_dot_size="1.5"
487
+ fi
488
+
452
489
  # Plot all algorithms in order of completion, colored by generation
453
490
  plot_cmd=""
454
491
  gen_plots_added=0
@@ -462,7 +499,7 @@ print(f'max_id=\"{max_id}\"')
462
499
  if [[ $gen_plots_added -gt 0 ]]; then
463
500
  plot_cmd="$plot_cmd, \\"$'\n'
464
501
  fi
465
- plot_cmd="${plot_cmd} \"$data_file\" using (\$4==$gen_num?\$1:1/0):3 with linespoints linewidth 2 linecolor rgb \"$color\" pointsize 1.2 title \"Gen $gen_num\""
502
+ 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\""
466
503
  ((gen_plots_added++))
467
504
  fi
468
505
  done
@@ -472,15 +509,15 @@ print(f'max_id=\"{max_id}\"')
472
509
  if [[ $gen_plots_added -gt 0 ]]; then
473
510
  plot_cmd="$plot_cmd, \\"$'\n'
474
511
  fi
475
- plot_cmd="${plot_cmd} \"$winner_file\" using 1:3 with points pointtype 7 pointsize 3 linecolor rgb \"gold\" title \"Best ($max_id)\""
512
+ plot_cmd="${plot_cmd} \"$winner_file\" using 1:3 with points pointtype 7 pointsize $winner_dot_size linecolor rgb \"gold\" title \"Best ($max_id)\""
476
513
  fi
477
514
 
478
515
  # Fallback if no generation-specific plots
479
516
  if [[ $gen_plots_added -eq 0 ]]; then
480
- plot_cmd="\"$data_file\" using 1:3 with linespoints linewidth 2 linecolor rgb \"#1f77b4\" pointsize 1.2 title \"Evolution Progress\""
517
+ plot_cmd="\"$data_file\" using 1:3 with points linecolor rgb \"#1f77b4\" pointsize $regular_dot_size title \"Evolution Progress\""
481
518
  if [[ -n $max_id && -s "$winner_file" ]]; then
482
519
  plot_cmd="$plot_cmd, \\"$'\n'
483
- plot_cmd="${plot_cmd} \"$winner_file\" using 1:3 with points pointtype 7 pointsize 3 linecolor rgb \"gold\" title \"Best ($max_id)\""
520
+ plot_cmd="${plot_cmd} \"$winner_file\" using 1:3 with points pointtype 7 pointsize $winner_dot_size linecolor rgb \"gold\" title \"Best ($max_id)\""
484
521
  fi
485
522
  fi
486
523
 
@@ -506,21 +543,25 @@ set output "$output_file"
506
543
  set multiplot layout 2,1 margins 0.08,0.82,0.15,0.95 spacing 0.1,0.15
507
544
 
508
545
  #=================== TOP PLOT: Performance Over Time ===================
546
+ # AIDEV-NOTE: Removed x-axis to eliminate tick overlap and formatting issues
509
547
  set title "Algorithm Evolution Performance Over Time" font ",14"
510
- set xlabel "Completion Order (Algorithm #)"
548
+ unset xlabel
511
549
  set ylabel "Performance Score"
512
- set grid
550
+ set grid y # Only show horizontal grid lines
513
551
  set key outside right
514
- set xtics 1
515
- set autoscale y
552
+
553
+ # AIDEV-NOTE: Remove x-axis entirely to avoid tick problems with large datasets
554
+ unset xtics
555
+ set autoscale
556
+ set yrange [*:*] # Auto-scale y-axis only
516
557
 
517
558
  # Define colors for generations
518
559
  plot $plot_cmd
519
560
 
520
- #=================== BOTTOM PLOT: Generation Averages ===================
521
- set title "Average Performance by Generation" font ",14"
561
+ #=================== BOTTOM PLOT: Generation Medians ===================
562
+ set title "Median Performance by Generation" font ",14"
522
563
  set xlabel "Generation"
523
- set ylabel "Avg Performance"
564
+ set ylabel "Median Performance"
524
565
  set style fill solid 0.8
525
566
  set boxwidth 0.6
526
567
  unset key
@@ -529,6 +570,10 @@ set grid y
529
570
  # Set custom x-axis labels
530
571
  set xtics ($xtics_labels)
531
572
 
573
+ # Auto-scale for generation plot too
574
+ set autoscale
575
+ set yrange [*:*]
576
+
532
577
  plot "$gen_avg_file" using 1:3 with boxes linecolor rgb "#4CAF50" notitle
533
578
 
534
579
  unset multiplot
@@ -14,14 +14,40 @@ else
14
14
  load_config
15
15
  fi
16
16
 
17
- # Helper function to call AI model (codex o3-pro if available, else Claude)
17
+ # Function to determine which model to use based on generation
18
+ get_model_for_generation() {
19
+ local generation="$1"
20
+ local gen_num
21
+
22
+ # Extract numeric part of generation (e.g., "05" from gen05)
23
+ if [[ $generation =~ ^0*([0-9]+)$ ]]; then
24
+ gen_num=$((10#${BASH_REMATCH[1]}))
25
+ else
26
+ gen_num=1 # Default for malformed input
27
+ fi
28
+
29
+ # Alternate between models by generation
30
+ if (( gen_num % 2 == 1 )); then
31
+ echo "opus" # Odd generations: use Opus for exploration
32
+ else
33
+ echo "o3-pro" # Even generations: use o3-pro for refinement
34
+ fi
35
+ }
36
+
37
+ # Helper function to call AI model (alternating based on generation)
18
38
  call_ai_with_limit_check() {
19
39
  local prompt="$1"
20
- local fallback_model="${2:-opus}"
40
+ local generation="${2:-01}" # Default to generation 01 if not provided
41
+
42
+ # Determine which model to use for this generation
43
+ local preferred_model
44
+ preferred_model=$(get_model_for_generation "$generation")
45
+
46
+ echo "[INFO] Generation $generation: Using $preferred_model" >&2
21
47
 
22
- # Check if codex is available
23
- if command -v codex >/dev/null 2>&1; then
24
- echo "[INFO] Using codex o3-pro for ideation (smartest available model)" >&2
48
+ # Try preferred model first
49
+ if [[ "$preferred_model" == "o3-pro" ]] && command -v codex >/dev/null 2>&1; then
50
+ echo "[INFO] Using codex o3-pro for ideation" >&2
25
51
 
26
52
  # Call codex with o3-pro model using -q flag and --full-auto
27
53
  local ai_output
@@ -32,16 +58,17 @@ call_ai_with_limit_check() {
32
58
  echo "$ai_output"
33
59
  return 0
34
60
  else
35
- echo "[WARN] Codex failed, falling back to Claude" >&2
61
+ echo "[WARN] Codex o3-pro failed, falling back to Claude Opus" >&2
62
+ preferred_model="opus"
36
63
  fi
37
64
  fi
38
65
 
39
- # Fall back to Claude
40
- echo "[INFO] Using Claude $fallback_model for ideation" >&2
66
+ # Use Claude with preferred model (or fallback)
67
+ echo "[INFO] Using Claude $preferred_model for ideation" >&2
41
68
 
42
69
  # Call Claude and capture output
43
70
  local claude_output
44
- claude_output=$(echo "$prompt" | claude --dangerously-skip-permissions --model "$fallback_model" -p 2>&1)
71
+ claude_output=$(echo "$prompt" | claude --dangerously-skip-permissions --model "$preferred_model" -p 2>&1)
45
72
  local claude_exit_code=$?
46
73
 
47
74
  # Check for usage limit
@@ -309,7 +336,7 @@ Example descriptions:
309
336
  Add exactly $count rows to the CSV file now."
310
337
 
311
338
  echo "[INFO] Generating $count novel exploration ideas..."
312
- if ! call_ai_with_limit_check "$prompt" "opus"; then
339
+ if ! call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION"; then
313
340
  echo "[WARN] AI failed to generate novel ideas" >&2
314
341
  return 1
315
342
  fi
@@ -372,7 +399,7 @@ Example descriptions:
372
399
  Add exactly $count parameter tuning rows to the CSV file now."
373
400
 
374
401
  echo "[INFO] Generating $count hill climbing ideas..."
375
- if ! call_ai_with_limit_check "$prompt" "opus"; then
402
+ if ! call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION"; then
376
403
  echo "[WARN] AI failed to generate hill climbing ideas" >&2
377
404
  return 1
378
405
  fi
@@ -435,7 +462,7 @@ Example descriptions:
435
462
  Add exactly $count structural modification rows to the CSV file now."
436
463
 
437
464
  echo "[INFO] Generating $count structural mutation ideas..."
438
- if ! call_ai_with_limit_check "$prompt" "opus"; then
465
+ if ! call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION"; then
439
466
  echo "[WARN] AI failed to generate structural mutation ideas" >&2
440
467
  return 1
441
468
  fi
@@ -498,7 +525,7 @@ Example descriptions:
498
525
  Add exactly $count hybrid combination rows to the CSV file now."
499
526
 
500
527
  echo "[INFO] Generating $count crossover hybrid ideas..."
501
- if ! call_ai_with_limit_check "$prompt" "opus"; then
528
+ if ! call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION"; then
502
529
  echo "[WARN] AI failed to generate crossover ideas" >&2
503
530
  return 1
504
531
  fi
@@ -567,7 +594,7 @@ CRITICAL CSV FORMAT RULES:
567
594
  Add exactly $TOTAL_IDEAS algorithm variation rows to the CSV file now."
568
595
 
569
596
  echo "[INFO] Generating $TOTAL_IDEAS ideas (legacy mode)..."
570
- if ! call_ai_with_limit_check "$prompt" "opus"; then
597
+ if ! call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION"; then
571
598
  echo "[WARN] AI failed to generate ideas" >&2
572
599
  return 1
573
600
  fi
@@ -142,7 +142,7 @@ if [[ $# -eq 0 ]]; then
142
142
  case $choice in
143
143
  1) exec "$SCRIPT_DIR/claude-evolve-setup" ;;
144
144
  2) exec "$SCRIPT_DIR/claude-evolve-ideate" ;;
145
- 3) exec "$SCRIPT_DIR/claude-evolve-run" ;;
145
+ 3) exec "$SCRIPT_DIR/claude-evolve-run-unified" ;;
146
146
  4) exec "$SCRIPT_DIR/claude-evolve-analyze" ;;
147
147
  5) exec "$SCRIPT_DIR/claude-evolve-config" ;;
148
148
  6) show_help ;;
@@ -174,7 +174,7 @@ ideate)
174
174
  ;;
175
175
  run)
176
176
  shift
177
- exec "$SCRIPT_DIR/claude-evolve-run" "$@"
177
+ exec "$SCRIPT_DIR/claude-evolve-run-unified" "$@"
178
178
  ;;
179
179
  analyze)
180
180
  shift
@@ -297,12 +297,39 @@ print(count)
297
297
  echo "[DISPATCHER] CSV has $((total_rows-1)) total candidates, $complete_count complete"
298
298
  fi
299
299
 
300
- # If no pending work and no active workers, we're done
300
+ # If no pending work and no active workers, check for auto-ideation
301
301
  if [[ $pending_count -eq 0 && $active_workers -eq 0 ]]; then
302
- echo "[DISPATCHER] No pending candidates found. Evolution complete."
303
- echo "[DISPATCHER] Run 'claude-evolve ideate' to generate more candidates."
304
- echo "[DISPATCHER] Exiting main loop: no work remaining" >&2
305
- break
302
+ echo "[DISPATCHER] No pending candidates found."
303
+
304
+ # Check if auto ideation is enabled
305
+ echo "[DEBUG] AUTO_IDEATE value: '$AUTO_IDEATE'"
306
+ if [[ "$AUTO_IDEATE" == "true" || "$AUTO_IDEATE" == "1" ]]; then
307
+ echo "[DISPATCHER] Auto ideation is enabled. Generating new ideas..."
308
+
309
+ # Check if claude-evolve-ideate exists
310
+ ideate_script="$SCRIPT_DIR/claude-evolve-ideate"
311
+ if [[ ! -f "$ideate_script" ]]; then
312
+ echo "[ERROR] claude-evolve-ideate script not found: $ideate_script" >&2
313
+ echo "[DISPATCHER] Evolution complete - no way to generate more ideas."
314
+ break
315
+ fi
316
+
317
+ # Generate new ideas using the multi-strategy approach
318
+ echo "[DISPATCHER] Calling claude-evolve-ideate to generate new candidates..."
319
+ if ! "$ideate_script"; then
320
+ echo "[ERROR] Failed to generate new ideas" >&2
321
+ echo "[DISPATCHER] Evolution complete - ideation failed."
322
+ break
323
+ fi
324
+
325
+ echo "[DISPATCHER] New ideas generated successfully. Continuing evolution..."
326
+ continue # Go back to start of loop to find the new candidates
327
+ else
328
+ echo "[DISPATCHER] Auto ideation is disabled. Evolution complete."
329
+ echo "[DISPATCHER] Run 'claude-evolve ideate' to generate more candidates."
330
+ echo "[DISPATCHER] Exiting main loop: no work remaining" >&2
331
+ break
332
+ fi
306
333
  fi
307
334
 
308
335
  # Start workers if we have pending work and capacity
@@ -0,0 +1,342 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ # Load configuration
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ # shellcheck source=../lib/config.sh
8
+ source "$SCRIPT_DIR/../lib/config.sh"
9
+
10
+ # Use CLAUDE_EVOLVE_CONFIG if set, otherwise default
11
+ if [[ -n ${CLAUDE_EVOLVE_CONFIG:-} ]]; then
12
+ load_config "$CLAUDE_EVOLVE_CONFIG"
13
+ else
14
+ load_config
15
+ fi
16
+
17
+ # Validate configuration
18
+ if ! validate_config; then
19
+ echo "[ERROR] Configuration validation failed" >&2
20
+ exit 1
21
+ fi
22
+
23
+ # AIDEV-NOTE: Unified execution engine that handles both sequential and parallel modes
24
+ # Sequential mode is just parallel mode with max_workers=1
25
+
26
+ # Default values
27
+ timeout_seconds=""
28
+ force_parallel=""
29
+ force_sequential=""
30
+ use_caffeinate="false"
31
+
32
+ # Parse command line arguments
33
+ while [[ $# -gt 0 ]]; do
34
+ case $1 in
35
+ --timeout)
36
+ if [[ -z ${2:-} ]] || [[ ! $2 =~ ^[0-9]+$ ]] || [[ $2 -eq 0 ]]; then
37
+ echo "[ERROR] --timeout requires a positive integer (seconds)" >&2
38
+ exit 1
39
+ fi
40
+ timeout_seconds="$2"
41
+ shift 2
42
+ ;;
43
+ --parallel)
44
+ force_parallel="true"
45
+ shift
46
+ ;;
47
+ --sequential)
48
+ force_sequential="true"
49
+ shift
50
+ ;;
51
+ --keep-awake|--caffeinate)
52
+ use_caffeinate="true"
53
+ shift
54
+ ;;
55
+ --help)
56
+ cat <<EOF
57
+ claude-evolve run - Execute evolution candidates
58
+
59
+ USAGE:
60
+ claude-evolve run [OPTIONS]
61
+
62
+ OPTIONS:
63
+ --timeout N Timeout in seconds for each evaluation
64
+ --parallel Force parallel execution mode
65
+ --sequential Force sequential execution mode (max_workers=1)
66
+ --keep-awake Keep system awake during execution (macOS only)
67
+ --caffeinate Alias for --keep-awake
68
+ --help Show this help message
69
+
70
+ DESCRIPTION:
71
+ Continuously processes evolution candidates by:
72
+ 1. Finding pending candidates in CSV
73
+ 2. Calling Claude to mutate algorithms
74
+ 3. Updating CSV with performance score and completion status
75
+ 4. Auto-generating new ideas when no pending candidates remain
76
+
77
+ Use --timeout to prevent runaway evaluations from blocking progress.
78
+ EOF
79
+ exit 0
80
+ ;;
81
+ *)
82
+ echo "[ERROR] Unknown option: $1" >&2
83
+ exit 1
84
+ ;;
85
+ esac
86
+ done
87
+
88
+ # Check if caffeinate should be used
89
+ if [[ "$use_caffeinate" == "true" ]] && command -v caffeinate >/dev/null 2>&1; then
90
+ echo "[INFO] Using caffeinate to prevent system sleep"
91
+ # Re-run this script with caffeinate
92
+ exec caffeinate -dims "$0" "$@"
93
+ fi
94
+
95
+ # Determine execution mode and worker count
96
+ if [[ "$force_sequential" == "true" ]]; then
97
+ MAX_WORKERS=1
98
+ echo "[INFO] Using sequential mode (forced via --sequential, max_workers=1)"
99
+ elif [[ "$force_parallel" == "true" ]]; then
100
+ echo "[INFO] Using parallel mode (forced via --parallel, max_workers=$MAX_WORKERS)"
101
+ elif [[ "$PARALLEL_ENABLED" == "true" || "$PARALLEL_ENABLED" == "1" ]]; then
102
+ echo "[INFO] Using parallel mode (enabled in config, max_workers=$MAX_WORKERS)"
103
+ else
104
+ MAX_WORKERS=1
105
+ echo "[INFO] Using sequential mode (default, max_workers=1)"
106
+ fi
107
+
108
+ # Start unified execution engine
109
+ echo "[INFO] Starting evolution run with up to $MAX_WORKERS workers"
110
+
111
+ # Rest of the logic is the same as claude-evolve-run-parallel but with dynamic MAX_WORKERS
112
+ # and the updated auto-ideation logic we just added
113
+
114
+ # Worker management
115
+ declare -a worker_pids=()
116
+
117
+ # Graceful shutdown function
118
+ shutdown_workers() {
119
+ if [[ ${#worker_pids[@]} -eq 0 ]]; then
120
+ echo "[DISPATCHER] No workers to shutdown"
121
+ return 0
122
+ fi
123
+
124
+ echo "[DISPATCHER] Shutting down workers..."
125
+ for pid in "${worker_pids[@]}"; do
126
+ if kill -0 "$pid" 2>/dev/null; then
127
+ echo "[DISPATCHER] Stopping worker $pid"
128
+ kill -TERM "$pid" 2>/dev/null || true
129
+ fi
130
+ done
131
+
132
+ # Wait for workers to exit
133
+ local timeout=10
134
+ while [[ ${#worker_pids[@]} -gt 0 && $timeout -gt 0 ]]; do
135
+ sleep 1
136
+ ((timeout--))
137
+
138
+ local new_pids=()
139
+ for pid in "${worker_pids[@]}"; do
140
+ if kill -0 "$pid" 2>/dev/null; then
141
+ new_pids+=($pid)
142
+ fi
143
+ done
144
+ worker_pids=("${new_pids[@]}")
145
+ done
146
+
147
+ # Force kill remaining workers
148
+ for pid in "${worker_pids[@]}"; do
149
+ if kill -0 "$pid" 2>/dev/null; then
150
+ echo "[DISPATCHER] Force killing worker $pid"
151
+ kill -KILL "$pid" 2>/dev/null || true
152
+ fi
153
+ done
154
+
155
+ echo "[DISPATCHER] Shutdown complete"
156
+ exit 0
157
+ }
158
+
159
+ # Signal handling - graceful shutdown with force option
160
+ handle_signal() {
161
+ local signal="$1"
162
+ echo "[DISPATCHER] Received signal: $signal" >&2
163
+ echo "[DISPATCHER] Active workers: ${#worker_pids[@]}" >&2
164
+
165
+ # For expensive workers, give option to force shutdown
166
+ if [[ ${#worker_pids[@]} -gt 0 ]]; then
167
+ echo "[DISPATCHER] Warning: ${#worker_pids[@]} expensive workers are still running!" >&2
168
+ echo "[DISPATCHER] Press Ctrl+C again to force shutdown immediately, or wait for graceful shutdown..." >&2
169
+
170
+ # Give a few seconds for force shutdown option
171
+ local count=3
172
+ while [[ $count -gt 0 && "$force_shutdown_requested" != "true" ]]; do
173
+ sleep 1
174
+ ((count--))
175
+ done
176
+
177
+ if [[ "$force_shutdown_requested" == "true" ]]; then
178
+ echo "[DISPATCHER] Force shutdown in progress..." >&2
179
+ return # shutdown_workers already called
180
+ fi
181
+
182
+ echo "[DISPATCHER] Proceeding with graceful shutdown..." >&2
183
+ fi
184
+
185
+ shutdown_workers
186
+ }
187
+
188
+ # Track signals for force shutdown
189
+ signal_count=0
190
+ force_shutdown_requested=false
191
+
192
+ # Immediate signal handler for force shutdown detection
193
+ signal_handler() {
194
+ ((signal_count++))
195
+
196
+ if [[ $signal_count -eq 1 ]]; then
197
+ # First signal - start graceful shutdown
198
+ handle_signal "SIGINT"
199
+ else
200
+ # Second+ signal - force shutdown immediately
201
+ echo "[DISPATCHER] Force shutdown requested!" >&2
202
+ force_shutdown_requested=true
203
+ shutdown_workers
204
+ fi
205
+ }
206
+
207
+ # Set up signal handlers
208
+ trap 'signal_handler' INT
209
+ trap 'handle_signal SIGTERM' TERM
210
+
211
+ # Function to start a worker
212
+ start_worker() {
213
+ local worker_script="$SCRIPT_DIR/claude-evolve-worker"
214
+ if [[ ! -f "$worker_script" ]]; then
215
+ echo "[ERROR] Worker script not found: $worker_script" >&2
216
+ exit 1
217
+ fi
218
+
219
+ local worker_args=()
220
+ [[ -n $timeout_seconds ]] && worker_args+=(--timeout "$timeout_seconds")
221
+
222
+ echo "[DISPATCHER] Starting worker..."
223
+ "$worker_script" "${worker_args[@]}" &
224
+ local worker_pid=$!
225
+ worker_pids+=($worker_pid)
226
+ echo "[DISPATCHER] Worker started with PID: $worker_pid"
227
+ }
228
+
229
+ # Function to clean up finished workers
230
+ cleanup_workers() {
231
+ local new_pids=()
232
+ for pid in "${worker_pids[@]}"; do
233
+ if kill -0 "$pid" 2>/dev/null; then
234
+ new_pids+=($pid)
235
+ else
236
+ # Worker finished
237
+ if wait "$pid" 2>/dev/null; then
238
+ echo "[DISPATCHER] Worker $pid completed successfully"
239
+ else
240
+ local exit_code=$?
241
+ if [[ $exit_code -eq 2 ]]; then
242
+ echo "[DISPATCHER] Worker $pid hit rate limit, will retry later"
243
+ else
244
+ echo "[DISPATCHER] Worker $pid failed with exit code $exit_code"
245
+ fi
246
+ fi
247
+ fi
248
+ done
249
+ worker_pids=("${new_pids[@]}")
250
+ }
251
+
252
+ # Function to count pending candidates
253
+ count_pending_candidates() {
254
+ "$PYTHON_CMD" "$SCRIPT_DIR/../lib/csv_helper.py" find_pending "$FULL_CSV_PATH" >/dev/null 2>&1
255
+ echo $? # 0 if found, 1 if not found
256
+ }
257
+
258
+ # Function to get CSV stats
259
+ get_csv_stats() {
260
+ if [[ ! -f "$FULL_CSV_PATH" ]]; then
261
+ echo "0 0 0"
262
+ return
263
+ fi
264
+
265
+ local total_rows complete_count pending_count
266
+ total_rows=$(wc -l < "$FULL_CSV_PATH" | tr -d '[:space:]')
267
+ complete_count=$(grep -c ',complete' "$FULL_CSV_PATH" || echo "0")
268
+
269
+ # Count pending: empty status or "pending"
270
+ # Handle potential Windows line endings by stripping carriage returns
271
+ pending_count=$(awk -F, 'NR>1 {gsub(/\r/, "", $5); if($5=="" || $5=="pending") count++} END {print count+0}' "$FULL_CSV_PATH")
272
+
273
+ echo "$total_rows $complete_count $pending_count"
274
+ }
275
+
276
+ echo "[DISPATCHER] Starting unified evolution engine"
277
+ echo "[DISPATCHER] Configuration: max_workers=$MAX_WORKERS, timeout=${timeout_seconds:-none}"
278
+
279
+ # Main dispatch loop
280
+ while true; do
281
+ # Clean up finished workers
282
+ cleanup_workers
283
+
284
+ # Get current status
285
+ read -r total_rows complete_count pending_count <<< "$(get_csv_stats)"
286
+ active_workers=${#worker_pids[@]}
287
+
288
+ # Status reporting
289
+ if [[ $total_rows -gt 1 ]]; then
290
+ echo "[DISPATCHER] Status: $pending_count pending, $active_workers active workers"
291
+ echo "[DISPATCHER] CSV has $((total_rows-1)) total candidates, $complete_count complete"
292
+ fi
293
+
294
+ # If no pending work and no active workers, check for auto-ideation
295
+ if [[ $pending_count -eq 0 && $active_workers -eq 0 ]]; then
296
+ echo "[DISPATCHER] No pending candidates found."
297
+
298
+ # Check if auto ideation is enabled
299
+ if [[ "$AUTO_IDEATE" == "true" || "$AUTO_IDEATE" == "1" ]]; then
300
+ echo "[DISPATCHER] Auto ideation is enabled. Generating new ideas..."
301
+
302
+ # Check if claude-evolve-ideate exists
303
+ ideate_script="$SCRIPT_DIR/claude-evolve-ideate"
304
+ if [[ ! -f "$ideate_script" ]]; then
305
+ echo "[ERROR] claude-evolve-ideate script not found: $ideate_script" >&2
306
+ echo "[DISPATCHER] Evolution complete - no way to generate more ideas."
307
+ break
308
+ fi
309
+
310
+ # Generate new ideas using the multi-strategy approach
311
+ echo "[DISPATCHER] Calling claude-evolve-ideate to generate new candidates..."
312
+ if ! "$ideate_script"; then
313
+ echo "[ERROR] Failed to generate new ideas" >&2
314
+ echo "[DISPATCHER] Evolution complete - ideation failed."
315
+ break
316
+ fi
317
+
318
+ echo "[DISPATCHER] New ideas generated successfully. Continuing evolution..."
319
+ continue # Go back to start of loop to find the new candidates
320
+ else
321
+ echo "[DISPATCHER] Auto ideation is disabled. Evolution complete."
322
+ echo "[DISPATCHER] Run 'claude-evolve ideate' to generate more candidates."
323
+ echo "[DISPATCHER] Exiting main loop: no work remaining" >&2
324
+ break
325
+ fi
326
+ fi
327
+
328
+ # Start workers if we have pending work and capacity
329
+ while [[ $pending_count -gt 0 && $active_workers -lt $MAX_WORKERS ]]; do
330
+ start_worker
331
+ active_workers=${#worker_pids[@]}
332
+ ((pending_count--)) # Optimistically assume this will be picked up
333
+ done
334
+
335
+ # Brief pause to avoid busy waiting
336
+ sleep 2
337
+ done
338
+
339
+ # Clean shutdown
340
+ shutdown_workers
341
+ echo "[DISPATCHER] Evolution run complete"
342
+ echo "[DISPATCHER] Exiting with code 0"
@@ -233,6 +233,7 @@ while true; do
233
233
  echo "[INFO] No more pending candidates found."
234
234
 
235
235
  # Check if auto ideation is enabled
236
+ echo "[DEBUG] AUTO_IDEATE value: '$AUTO_IDEATE'"
236
237
  if [[ "$AUTO_IDEATE" == "true" || "$AUTO_IDEATE" == "1" ]]; then
237
238
  echo "[INFO] Auto ideation is enabled. Generating new ideas..."
238
239
 
@@ -267,7 +268,7 @@ while true; do
267
268
  eval "$("$PYTHON_CMD" "$SCRIPT_DIR/../lib/csv_helper.py" get_row "$FULL_CSV_PATH" "$row_num")"
268
269
 
269
270
  # Variables are now set: id, basedOnId, description, performance, status
270
- based_on_id="$basedOnId" # Convert to expected variable name
271
+ # based_on_id is already set correctly by csv_helper.py
271
272
 
272
273
  # Check if ID is empty
273
274
  if [[ -z $id ]]; then
@@ -282,18 +283,18 @@ echo "[INFO] Based on ID: $based_on_id"
282
283
  # Set interrupt handler - just exit without updating CSV status
283
284
  trap 'echo "[INFO] Evolution interrupted"; exit 130' INT
284
285
 
285
- # Determine parent algorithm
286
+ # AIDEV-NOTE: Using common evolution processor logic to determine parent/output files
287
+ # and check if processing should be skipped (handles self-parent detection)
288
+
289
+ # Determine parent algorithm path
286
290
  if [[ -z $based_on_id || $based_on_id == "0" || $based_on_id == '""' ]]; then
287
- # Empty or zero basedonID means use the base algorithm
288
291
  parent_file="$FULL_ALGORITHM_PATH"
289
292
  echo "[INFO] Using base algorithm (basedonID is empty or 0)"
290
293
  else
291
294
  # Handle both old format (numeric) and new format (genXX-XXX)
292
295
  if [[ $based_on_id =~ ^[0-9]+$ ]]; then
293
- # Old numeric format
294
296
  parent_file="$FULL_OUTPUT_DIR/evolution_id${based_on_id}.py"
295
297
  else
296
- # New generation format
297
298
  parent_file="$FULL_OUTPUT_DIR/evolution_${based_on_id}.py"
298
299
  fi
299
300
 
@@ -310,20 +311,24 @@ fi
310
311
 
311
312
  echo "[INFO] Using parent algorithm: $parent_file"
312
313
 
313
- # Generate mutation output file
314
- # Handle both old format (numeric) and new format (genXX-XXX)
314
+ # Generate output file path
315
315
  if [[ $id =~ ^[0-9]+$ ]]; then
316
- # Old numeric format
317
316
  output_file="$FULL_OUTPUT_DIR/evolution_id${id}.py"
318
317
  else
319
- # New generation format
320
318
  output_file="$FULL_OUTPUT_DIR/evolution_${id}.py"
321
319
  fi
322
320
  echo "[INFO] Generating algorithm mutation..."
323
321
 
324
- # Copy parent algorithm to output file first
325
- cp "$parent_file" "$output_file"
326
- echo "[INFO] Copied parent algorithm to: $output_file"
322
+ # Check if processing should be skipped using common logic
323
+ eval "$("$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_processor.py" "$id" "$based_on_id" "$FULL_OUTPUT_DIR" "$ROOT_DIR" "$parent_file" "$output_file")"
324
+
325
+ # Handle copy operation
326
+ if [[ "$skip_copy" == "True" ]]; then
327
+ echo "[INFO] ⚠️ Skipping copy - $reason"
328
+ else
329
+ cp "$parent_file" "$output_file"
330
+ echo "[INFO] Copied parent algorithm to: $output_file"
331
+ fi
327
332
 
328
333
  # Check for claude CLI
329
334
  claude_cmd="${CLAUDE_CMD:-claude}"
@@ -354,9 +359,10 @@ Requirements:
354
359
 
355
360
  The file currently contains the parent algorithm. Modify it according to the description above while adhering to all guidelines from the CLAUDE.md files."
356
361
 
357
- # Generate mutation (skip for baseline)
358
- if [[ $id == "000" || $id == "0" ]]; then
359
- echo "[INFO] Baseline algorithm - skipping mutation, using original"
362
+ # AIDEV-NOTE: Using common evolution processor logic for Claude processing decisions
363
+ # Handle Claude mutation based on skip flags
364
+ if [[ "$skip_claude" == "True" ]]; then
365
+ echo "[INFO] ⚠️ Skipping Claude processing - $reason"
360
366
  else
361
367
  echo "[INFO] Calling Claude $CLAUDE_MODEL to apply mutation..."
362
368
  echo "[INFO] Claude will edit: $output_file"
@@ -110,6 +110,7 @@ fi
110
110
  echo "[WORKER-$$] Description: $description"
111
111
  echo "[WORKER-$$] Based on ID: $based_on_id"
112
112
 
113
+ # AIDEV-NOTE: Using common evolution processor logic for consistent handling
113
114
  # Determine parent algorithm
114
115
  if [[ -z $based_on_id || $based_on_id == "0" || $based_on_id == '""' ]]; then
115
116
  parent_file="$FULL_ALGORITHM_PATH"
@@ -136,19 +137,20 @@ else
136
137
  output_file="$FULL_OUTPUT_DIR/evolution_${id}.py"
137
138
  fi
138
139
 
139
- # Copy parent to output (skip if destination already exists)
140
- if [[ -f "$output_file" ]]; then
141
- echo "[WORKER-$$] ⚠️ Skipping copy - child algorithm already exists: $output_file"
140
+ # Check if processing should be skipped using common logic
141
+ eval "$("$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_processor.py" "$id" "$based_on_id" "$FULL_OUTPUT_DIR" "$ROOT_DIR" "$parent_file" "$output_file")"
142
+
143
+ # Handle copy operation
144
+ if [[ "$skip_copy" == "True" ]]; then
145
+ echo "[WORKER-$$] ⚠️ Skipping copy - $reason"
142
146
  else
143
147
  cp "$parent_file" "$output_file"
144
148
  echo "[WORKER-$$] Copied parent to: $output_file"
145
149
  fi
146
150
 
147
- # Generate mutation (skip for baseline or self-parent)
148
- if [[ $id == "000" || $id == "0" || $id == "gen00-000" ]]; then
149
- echo "[WORKER-$$] Baseline algorithm - skipping mutation"
150
- elif [[ "$parent_file" == "$output_file" ]]; then
151
- echo "[WORKER-$$] ⚠️ Self-parent detected - skipping mutation to preserve existing code"
151
+ # Handle Claude mutation based on skip flags
152
+ if [[ "$skip_claude" == "True" ]]; then
153
+ echo "[WORKER-$$] ⚠️ Skipping Claude processing - $reason"
152
154
  else
153
155
  # Check for claude CLI
154
156
  claude_cmd="${CLAUDE_CMD:-claude}"
package/lib/config.sh CHANGED
@@ -40,7 +40,7 @@ DEFAULT_NUM_ELITES=3
40
40
  # Default parallel execution values
41
41
  DEFAULT_PARALLEL_ENABLED=false
42
42
  DEFAULT_MAX_WORKERS=4
43
- DEFAULT_LOCK_TIMEOUT=30
43
+ DEFAULT_LOCK_TIMEOUT=10
44
44
 
45
45
  # Default auto ideation value
46
46
  DEFAULT_AUTO_IDEATE=true
package/lib/csv-lock.sh CHANGED
@@ -4,36 +4,95 @@
4
4
  # Lock file location
5
5
  CSV_LOCKFILE="${EVOLUTION_DIR:-evolution}/.evolution.csv.lock"
6
6
 
7
- # Acquire exclusive lock on CSV file
7
+ # Acquire exclusive lock on CSV file with automatic stale lock cleanup
8
8
  # Usage: acquire_csv_lock [timeout_seconds]
9
9
  acquire_csv_lock() {
10
- local timeout="${1:-30}"
10
+ local timeout="${1:-${LOCK_TIMEOUT:-10}}" # Reduced default timeout
11
11
  local lockdir="$(dirname "$CSV_LOCKFILE")"
12
12
 
13
13
  # Ensure lock directory exists
14
14
  mkdir -p "$lockdir"
15
15
 
16
- # Try to acquire lock with timeout
17
- if command -v flock >/dev/null 2>&1; then
18
- # Use flock if available (Linux)
19
- exec 200>"$CSV_LOCKFILE"
20
- if ! flock -w "$timeout" -x 200; then
21
- echo "ERROR: Failed to acquire CSV lock within $timeout seconds" >&2
22
- return 1
23
- fi
24
- else
25
- # Fallback for systems without flock (macOS)
26
- local start_time=$(date +%s)
27
- while ! (set -C; echo $$ > "$CSV_LOCKFILE") 2>/dev/null; do
28
- local current_time=$(date +%s)
29
- if [ $((current_time - start_time)) -ge $timeout ]; then
30
- echo "ERROR: Failed to acquire CSV lock within $timeout seconds" >&2
31
- return 1
16
+ # AIDEV-NOTE: Robust locking with automatic stale lock detection and cleanup
17
+ # CSV operations should be fast (<100ms), so long timeouts indicate problems
18
+
19
+ # Clean up stale locks first
20
+ cleanup_stale_locks
21
+
22
+ # Try to acquire lock with short timeout and fast retry
23
+ local end_time=$(($(date +%s) + timeout))
24
+ local sleep_time=0.01 # Start with 10ms sleep
25
+
26
+ while [ $(date +%s) -lt $end_time ]; do
27
+ if command -v flock >/dev/null 2>&1; then
28
+ # Use flock if available (Linux) - this should be instant
29
+ exec 200>"$CSV_LOCKFILE"
30
+ if flock -n -x 200; then
31
+ return 0
32
+ fi
33
+ else
34
+ # Fallback for systems without flock (macOS)
35
+ if (set -C; echo $$ > "$CSV_LOCKFILE") 2>/dev/null; then
36
+ return 0
37
+ fi
38
+
39
+ # Check if existing lock is stale and clean it up
40
+ if [ -f "$CSV_LOCKFILE" ]; then
41
+ local lock_pid=$(cat "$CSV_LOCKFILE" 2>/dev/null)
42
+ if [[ "$lock_pid" =~ ^[0-9]+$ ]] && ! kill -0 "$lock_pid" 2>/dev/null; then
43
+ echo "[DEBUG] Removing stale lock from dead process $lock_pid" >&2
44
+ rm -f "$CSV_LOCKFILE"
45
+ continue # Try again immediately
46
+ fi
32
47
  fi
33
- sleep 0.1
34
- done
48
+ fi
49
+
50
+ # Brief sleep with exponential backoff (cap at 100ms)
51
+ sleep "$sleep_time"
52
+ sleep_time=$(echo "$sleep_time * 1.5" | bc -l 2>/dev/null | head -c 10)
53
+ if (( $(echo "$sleep_time > 0.1" | bc -l 2>/dev/null || echo 0) )); then
54
+ sleep_time=0.1
55
+ fi
56
+ done
57
+
58
+ echo "ERROR: Failed to acquire CSV lock within $timeout seconds" >&2
59
+ echo "ERROR: This indicates a serious problem - CSV operations should be fast" >&2
60
+
61
+ # As a last resort, if lock is very old, break it
62
+ if [ -f "$CSV_LOCKFILE" ]; then
63
+ local lock_age=$(($(date +%s) - $(stat -f %m "$CSV_LOCKFILE" 2>/dev/null || stat -c %Y "$CSV_LOCKFILE" 2>/dev/null || echo $(date +%s))))
64
+ if [ $lock_age -gt 60 ]; then # Lock older than 1 minute is definitely stale
65
+ echo "[WARN] Breaking very old lock file (${lock_age}s old)" >&2
66
+ rm -f "$CSV_LOCKFILE"
67
+ return 1 # Still return error to trigger retry
68
+ fi
69
+ fi
70
+
71
+ return 1
72
+ }
73
+
74
+ # Clean up stale lock files
75
+ cleanup_stale_locks() {
76
+ if [ ! -f "$CSV_LOCKFILE" ]; then
77
+ return 0
78
+ fi
79
+
80
+ # Check file age - any lock older than 10 seconds is definitely stale
81
+ local lock_age=$(($(date +%s) - $(stat -f %m "$CSV_LOCKFILE" 2>/dev/null || stat -c %Y "$CSV_LOCKFILE" 2>/dev/null || echo $(date +%s))))
82
+ if [ $lock_age -gt 10 ]; then
83
+ echo "[DEBUG] Removing stale lock file (${lock_age}s old)" >&2
84
+ rm -f "$CSV_LOCKFILE"
85
+ return 0
86
+ fi
87
+
88
+ # Check if process is still alive (macOS fallback mode only)
89
+ if ! command -v flock >/dev/null 2>&1; then
90
+ local lock_pid=$(cat "$CSV_LOCKFILE" 2>/dev/null)
91
+ if [[ "$lock_pid" =~ ^[0-9]+$ ]] && ! kill -0 "$lock_pid" 2>/dev/null; then
92
+ echo "[DEBUG] Removing lock from dead process $lock_pid" >&2
93
+ rm -f "$CSV_LOCKFILE"
94
+ fi
35
95
  fi
36
- return 0
37
96
  }
38
97
 
39
98
  # Release CSV lock
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Evolution processor - common logic for processing evolution candidates.
4
+ Used by both sequential (claude-evolve-run) and parallel (claude-evolve-worker) modes.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import subprocess
10
+ import re
11
+ from pathlib import Path
12
+
13
+ def should_skip_processing(id_val, based_on_id, parent_file, output_file):
14
+ """
15
+ Determine if evolution processing should be skipped.
16
+
17
+ Simple rule: If file exists, skip everything. This handles all edge cases cleanly.
18
+
19
+ Returns tuple: (skip_copy, skip_claude, reason)
20
+ """
21
+ # Baseline algorithm check
22
+ if id_val in ["000", "0", "gen00-000"]:
23
+ return True, True, "Baseline algorithm - no processing needed"
24
+
25
+ # File existence check - if file exists, skip both copy and Claude
26
+ # This automatically handles self-parent cases and re-runs
27
+ if os.path.exists(output_file):
28
+ return True, True, "File already exists - skipping all processing"
29
+
30
+ # File doesn't exist - proceed with copy and Claude
31
+ return False, False, None
32
+
33
+ def get_parent_file_path(based_on_id, output_dir, root_dir):
34
+ """Get the parent file path based on based_on_id."""
35
+ if not based_on_id or based_on_id in ["0", '""']:
36
+ # Use base algorithm
37
+ return os.path.join(root_dir, "algorithm.py")
38
+
39
+ # Handle both old format (numeric) and new format (genXX-XXX)
40
+ if re.match(r'^[0-9]+$', based_on_id):
41
+ # Old numeric format
42
+ return os.path.join(output_dir, f"evolution_id{based_on_id}.py")
43
+ else:
44
+ # New generation format
45
+ return os.path.join(output_dir, f"evolution_{based_on_id}.py")
46
+
47
+ def get_output_file_path(id_val, output_dir):
48
+ """Get the output file path based on id."""
49
+ # Handle both old format (numeric) and new format (genXX-XXX)
50
+ if re.match(r'^[0-9]+$', id_val):
51
+ # Old numeric format
52
+ return os.path.join(output_dir, f"evolution_id{id_val}.py")
53
+ else:
54
+ # New generation format
55
+ return os.path.join(output_dir, f"evolution_{id_val}.py")
56
+
57
+ def main():
58
+ """Main entry point for standalone testing."""
59
+ if len(sys.argv) < 7:
60
+ print("Usage: evolution_processor.py <id> <based_on_id> <output_dir> <root_dir> <parent_file> <output_file>")
61
+ sys.exit(1)
62
+
63
+ id_val = sys.argv[1]
64
+ based_on_id = sys.argv[2]
65
+ output_dir = sys.argv[3]
66
+ root_dir = sys.argv[4]
67
+ parent_file = sys.argv[5]
68
+ output_file = sys.argv[6]
69
+
70
+ skip_copy, skip_claude, reason = should_skip_processing(id_val, based_on_id, parent_file, output_file)
71
+
72
+ print(f"skip_copy={skip_copy}")
73
+ print(f"skip_claude={skip_claude}")
74
+ print(f'reason="{reason or ""}"')
75
+
76
+ if __name__ == "__main__":
77
+ main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.3.37",
3
+ "version": "1.3.38",
4
4
  "bin": {
5
5
  "claude-evolve": "./bin/claude-evolve",
6
6
  "claude-evolve-main": "./bin/claude-evolve-main",