claude-evolve 1.3.43 → 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.
@@ -194,14 +194,9 @@ else
194
194
  fi
195
195
 
196
196
  echo
197
- echo "=== Top Performer ==="
198
- if [[ -n $top_id ]]; then
199
- echo "ID: $top_id"
200
- echo "Performance: $top_score"
201
- echo "Description: $top_desc"
202
- else
203
- echo "No completed candidates yet"
204
- fi
197
+
198
+ # Call status command to show winners and revolutionary improvers
199
+ "$SCRIPT_DIR/claude-evolve-status" --brief
205
200
 
206
201
  # Generation analysis
207
202
  echo
@@ -321,10 +316,12 @@ if command -v gnuplot >/dev/null 2>&1 && [[ $valid_performance_count -gt 0 ]]; t
321
316
  # Create data files for gnuplot
322
317
  data_file="/tmp/evolution_data_$$.dat"
323
318
  winner_file="/tmp/evolution_winner_$$.dat"
319
+ novel_file="/tmp/evolution_novel_$$.dat"
324
320
  gen_avg_file="/tmp/evolution_gen_avg_$$.dat"
325
321
 
326
322
  echo "# Row ID Performance Generation" >"$data_file"
327
323
  echo "# Generation MedianPerformance Color" >"$gen_avg_file"
324
+ echo "# Row ID Performance Generation" >"$novel_file"
328
325
 
329
326
  # Get color by generation number (rotates through 7 colors)
330
327
  get_gen_color() {
@@ -350,14 +347,15 @@ if command -v gnuplot >/dev/null 2>&1 && [[ $valid_performance_count -gt 0 ]]; t
350
347
  max_row=0
351
348
  max_id=""
352
349
 
353
- # Use Python to generate chart data
350
+ # Use Python to generate chart data and identify revolutionary improvers
354
351
  "$PYTHON_CMD" -c "
355
352
  import csv
356
353
  import re
357
354
 
358
355
  with open('$csv_file', 'r') as f:
359
356
  reader = csv.reader(f)
360
- next(reader) # Skip header
357
+ rows = list(reader)
358
+ header = rows[0]
361
359
 
362
360
  completed_order = 0 # Track order of completion
363
361
 
@@ -367,15 +365,19 @@ with open('$csv_file', 'r') as f:
367
365
  with open('$gen_data_temp', 'w') as gen_f:
368
366
  pass # Clear file
369
367
 
368
+ with open('$novel_file', 'w') as novel_f:
369
+ novel_f.write('# Order ID Performance Generation\\n')
370
+
370
371
  max_perf = 0
371
372
  max_id = ''
372
373
  max_order = 0
373
374
 
374
- for row in reader:
375
+
376
+ for row in rows[1:]:
375
377
  if len(row) < 5:
376
378
  continue
377
379
 
378
- id, _, desc, perf, status = row[:5]
380
+ id, parent_id, desc, perf, status = row[0], row[1] if len(row) > 1 else '', row[2] if len(row) > 2 else '', row[3], row[4]
379
381
 
380
382
  # Extract generation from ID
381
383
  gen = 'gen01' # default
@@ -396,6 +398,11 @@ with open('$csv_file', 'r') as f:
396
398
  with open('$data_file', 'a') as f:
397
399
  f.write(f'{completed_order} \"{id}\" {perf} {gen_num}\\n')
398
400
 
401
+ # Write to novel file if this is a novel candidate
402
+ if not parent_id:
403
+ with open('$novel_file', 'a') as f:
404
+ f.write(f'{completed_order} \"{id}\" {perf} {gen_num}\\n')
405
+
399
406
  # Write to gen temp file
400
407
  with open('$gen_data_temp', 'a') as f:
401
408
  f.write(f'{gen} {perf}\\n')
@@ -532,6 +539,15 @@ print(f'max_desc=\"{desc_escaped}\"')
532
539
  fi
533
540
  done
534
541
 
542
+ # Add novel candidates
543
+ if [[ -s "$novel_file" ]] && [[ $(wc -l < "$novel_file") -gt 1 ]]; then
544
+ if [[ $gen_plots_added -gt 0 ]]; then
545
+ plot_cmd="$plot_cmd, \\"$'\n'
546
+ fi
547
+ plot_cmd="${plot_cmd} \"$novel_file\" using 1:3 with points pointtype 8 pointsize $winner_dot_size linecolor rgb \"#ff1493\" title \"Novel Candidates\""
548
+ ((gen_plots_added++))
549
+ fi
550
+
535
551
  # Add winner point
536
552
  if [[ -n $max_id && -s "$winner_file" ]]; then
537
553
  if [[ $gen_plots_added -gt 0 ]]; then
@@ -618,7 +634,7 @@ EOF
618
634
  exit 0
619
635
  fi
620
636
 
621
- rm -f "$data_file" "$winner_file" "$gen_avg_file" "$gen_data_temp"
637
+ rm -f "$data_file" "$winner_file" "$novel_file" "$gen_avg_file" "$gen_data_temp"
622
638
  echo "Chart saved to: $output_file"
623
639
 
624
640
  # Always try to open chart (not just when --open is used)
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Cleanup script to remove invalid entries from evolution CSV files.
4
+ This removes entries with IDs that contain shell constructs or other invalid content.
5
+ """
6
+
7
+ import csv
8
+ import sys
9
+ import os
10
+ import re
11
+ import argparse
12
+
13
+
14
+ def is_invalid_id(candidate_id):
15
+ """Check if a candidate ID contains invalid content."""
16
+ if not candidate_id:
17
+ return True
18
+
19
+ # Check for shell constructs
20
+ invalid_patterns = [
21
+ r'EOF.*<.*null',
22
+ r'<<.*EOF',
23
+ r'<.*dev.*null',
24
+ r'^\s*#', # Comments
25
+ r'^\s*$', # Empty
26
+ r'[<>|&;`$]', # Shell special characters
27
+ r'^\s*\[', # Bash test constructs
28
+ ]
29
+
30
+ for pattern in invalid_patterns:
31
+ if re.search(pattern, candidate_id, re.IGNORECASE):
32
+ return True
33
+
34
+ # Check for excessively long IDs (likely errors)
35
+ if len(candidate_id) > 50:
36
+ return True
37
+
38
+ return False
39
+
40
+
41
+ def clean_csv(csv_file, dry_run=False):
42
+ """Remove invalid entries from CSV file."""
43
+ if not os.path.exists(csv_file):
44
+ print(f"Error: CSV file {csv_file} not found")
45
+ return False
46
+
47
+ invalid_entries = []
48
+ valid_rows = []
49
+
50
+ with open(csv_file, 'r') as f:
51
+ reader = csv.reader(f)
52
+ header = next(reader)
53
+ valid_rows.append(header)
54
+
55
+ for row_num, row in enumerate(reader, start=2):
56
+ if not row or len(row) == 0:
57
+ continue
58
+
59
+ candidate_id = row[0].strip() if row[0] else ''
60
+
61
+ if is_invalid_id(candidate_id):
62
+ invalid_entries.append({
63
+ 'row_num': row_num,
64
+ 'id': candidate_id,
65
+ 'row': row
66
+ })
67
+ else:
68
+ valid_rows.append(row)
69
+
70
+ if not invalid_entries:
71
+ print("No invalid entries found.")
72
+ return True
73
+
74
+ print(f"Found {len(invalid_entries)} invalid entries:")
75
+ for entry in invalid_entries:
76
+ print(f" Row {entry['row_num']}: ID='{entry['id']}'")
77
+ if len(entry['row']) > 4:
78
+ print(f" Status: {entry['row'][4]}")
79
+
80
+ if dry_run:
81
+ print("\nDry run - no changes made.")
82
+ return True
83
+
84
+ # Write cleaned CSV
85
+ backup_file = csv_file + '.backup'
86
+ print(f"\nBacking up to: {backup_file}")
87
+ os.rename(csv_file, backup_file)
88
+
89
+ try:
90
+ with open(csv_file, 'w', newline='') as f:
91
+ writer = csv.writer(f)
92
+ writer.writerows(valid_rows)
93
+
94
+ print(f"Cleaned CSV written to: {csv_file}")
95
+ print(f"Removed {len(invalid_entries)} invalid entries")
96
+ return True
97
+
98
+ except Exception as e:
99
+ print(f"Error writing cleaned CSV: {e}")
100
+ print("Restoring backup...")
101
+ os.rename(backup_file, csv_file)
102
+ return False
103
+
104
+
105
+ def main():
106
+ parser = argparse.ArgumentParser(description='Remove invalid entries from evolution CSV')
107
+ parser.add_argument('csv_file', help='Path to evolution CSV file')
108
+ parser.add_argument('--dry-run', action='store_true', help='Show what would be removed without making changes')
109
+
110
+ args = parser.parse_args()
111
+
112
+ success = clean_csv(args.csv_file, args.dry_run)
113
+ sys.exit(0 if success else 1)
114
+
115
+
116
+ if __name__ == '__main__':
117
+ main()
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Cleanup script to remove duplicate entries from evolution CSV files.
4
+ This fixes the issue where improper CSV parsing caused duplicate IDs.
5
+ """
6
+
7
+ import csv
8
+ import sys
9
+ import os
10
+ import argparse
11
+ from collections import defaultdict
12
+
13
+
14
+ def find_duplicates(csv_file):
15
+ """Find duplicate IDs and return information about them."""
16
+ if not os.path.exists(csv_file):
17
+ print(f"Error: CSV file {csv_file} not found")
18
+ return {}
19
+
20
+ id_entries = defaultdict(list)
21
+
22
+ with open(csv_file, 'r') as f:
23
+ reader = csv.reader(f)
24
+ header = next(reader)
25
+
26
+ for row_num, row in enumerate(reader, start=2):
27
+ if row and len(row) > 0:
28
+ candidate_id = row[0].strip()
29
+ if candidate_id:
30
+ id_entries[candidate_id].append({
31
+ 'row_num': row_num,
32
+ 'row': row,
33
+ 'basedOnId': row[1].strip() if len(row) > 1 else '',
34
+ 'description': row[2].strip() if len(row) > 2 else '',
35
+ 'performance': row[3].strip() if len(row) > 3 else '',
36
+ 'status': row[4].strip() if len(row) > 4 else ''
37
+ })
38
+
39
+ # Find duplicates
40
+ duplicates = {id_val: entries for id_val, entries in id_entries.items() if len(entries) > 1}
41
+
42
+ return duplicates, header
43
+
44
+
45
+ def choose_best_entry(entries):
46
+ """Choose the best entry to keep from duplicates."""
47
+ # Priority order:
48
+ # 1. Completed entries with performance score
49
+ # 2. Entries with empty basedOnId (original entries)
50
+ # 3. Most complete entry
51
+
52
+ completed_entries = [e for e in entries if e['status'] == 'complete' and e['performance']]
53
+
54
+ if completed_entries:
55
+ # Keep the one with the highest performance
56
+ return max(completed_entries, key=lambda x: float(x['performance']) if x['performance'] else 0)
57
+
58
+ # If no completed entries, prefer ones with empty basedOnId (original entries)
59
+ original_entries = [e for e in entries if not e['basedOnId']]
60
+ if original_entries:
61
+ return original_entries[0]
62
+
63
+ # Otherwise, keep the most complete entry
64
+ return max(entries, key=lambda x: len([f for f in x['row'] if f.strip()]))
65
+
66
+
67
+ def cleanup_csv(csv_file, dry_run=True):
68
+ """Clean up duplicate entries in CSV file."""
69
+ duplicates, header = find_duplicates(csv_file)
70
+
71
+ if not duplicates:
72
+ print(f"No duplicates found in {csv_file}")
73
+ return
74
+
75
+ print(f"Found {len(duplicates)} duplicate IDs in {csv_file}:")
76
+
77
+ # Read all rows
78
+ with open(csv_file, 'r') as f:
79
+ reader = csv.reader(f)
80
+ all_rows = list(reader)
81
+
82
+ rows_to_remove = set()
83
+
84
+ for candidate_id, entries in duplicates.items():
85
+ print(f"\n {candidate_id}: {len(entries)} entries")
86
+
87
+ best_entry = choose_best_entry(entries)
88
+
89
+ for entry in entries:
90
+ if entry == best_entry:
91
+ print(f" Row {entry['row_num']}: KEEP - {entry['status']} {entry['performance']}")
92
+ else:
93
+ print(f" Row {entry['row_num']}: REMOVE - {entry['status']} {entry['performance']}")
94
+ rows_to_remove.add(entry['row_num'] - 1) # Convert to 0-based index
95
+
96
+ if dry_run:
97
+ print(f"\nDry run mode: Would remove {len(rows_to_remove)} duplicate rows")
98
+ print("Run with --fix to actually remove duplicates")
99
+ return
100
+
101
+ # Remove duplicate rows
102
+ cleaned_rows = []
103
+ for i, row in enumerate(all_rows):
104
+ if i not in rows_to_remove:
105
+ cleaned_rows.append(row)
106
+
107
+ # Write cleaned CSV
108
+ backup_file = f"{csv_file}.backup.{os.getpid()}"
109
+ os.rename(csv_file, backup_file)
110
+ print(f"\nCreated backup: {backup_file}")
111
+
112
+ with open(csv_file, 'w', newline='') as f:
113
+ writer = csv.writer(f)
114
+ writer.writerows(cleaned_rows)
115
+
116
+ print(f"Removed {len(rows_to_remove)} duplicate rows from {csv_file}")
117
+ print(f"Cleaned CSV has {len(cleaned_rows)} rows (including header)")
118
+
119
+
120
+ def main():
121
+ parser = argparse.ArgumentParser(description='Clean up duplicate entries in evolution CSV files')
122
+ parser.add_argument('csv_file', help='Path to CSV file to clean')
123
+ parser.add_argument('--fix', action='store_true', help='Actually fix the file (default is dry run)')
124
+
125
+ args = parser.parse_args()
126
+
127
+ cleanup_csv(args.csv_file, dry_run=not args.fix)
128
+
129
+
130
+ if __name__ == '__main__':
131
+ main()