claude-evolve 1.3.39 → 1.3.41

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.
@@ -55,26 +55,30 @@ show_help() {
55
55
  claude-evolve - AI-powered algorithm evolution tool
56
56
 
57
57
  USAGE:
58
- claude-evolve [--config=PATH] [COMMAND] [OPTIONS]
58
+ claude-evolve [--working-dir=PATH] [COMMAND] [OPTIONS]
59
59
 
60
60
  COMMANDS:
61
- setup Initialize evolution workspace
62
- ideate Generate new algorithm ideas
63
- run Execute evolution candidates
64
- analyze Analyze evolution results
65
- help Show this help message
61
+ setup Initialize evolution workspace
62
+ ideate Generate new algorithm ideas
63
+ run Execute evolution candidates
64
+ analyze Analyze evolution results
65
+ edit Manage candidate statuses by generation
66
+ status Show evolution progress and current leader
67
+ cleanup Clean up unchanged algorithms and descendants
68
+ cleanup-duplicates Alias for cleanup (deprecated)
69
+ help Show this help message
66
70
 
67
71
  GLOBAL OPTIONS:
68
- --config=PATH Use alternate config file (default: evolution/config.yaml)
69
- -h, --help Show help message
70
- -v, --version Show version
72
+ --working-dir=PATH Use alternate working directory (default: evolution/)
73
+ -h, --help Show help message
74
+ -v, --version Show version
71
75
 
72
76
  EXAMPLES:
73
77
  claude-evolve setup
74
78
  claude-evolve ideate 5
75
79
  claude-evolve run --timeout 300
76
80
  claude-evolve analyze --open
77
- claude-evolve --config=evolution-abc/config.yaml run
81
+ claude-evolve --working-dir=evolution-abc run
78
82
 
79
83
  For more information, visit: https://github.com/anthropics/claude-evolve
80
84
  EOF
@@ -91,9 +95,11 @@ show_menu() {
91
95
  echo " 2) ideate - Generate new algorithm ideas"
92
96
  echo " 3) run - Execute evolution candidates"
93
97
  echo " 4) analyze - Analyze evolution results"
94
- echo " 5) config - Manage configuration settings"
95
- echo " 6) help - Show help message"
96
- echo " 7) exit - Exit"
98
+ echo " 5) edit - Manage candidate statuses by generation"
99
+ echo " 6) status - Show evolution progress and current leader"
100
+ echo " 7) config - Manage configuration settings"
101
+ echo " 8) help - Show help message"
102
+ echo " 9) exit - Exit"
97
103
  echo
98
104
 
99
105
  # Show workspace status
@@ -105,19 +111,19 @@ show_menu() {
105
111
  }
106
112
 
107
113
  # Parse global options
108
- CONFIG_FILE=""
114
+ WORKING_DIR=""
109
115
  while [[ $# -gt 0 ]] && [[ "$1" =~ ^-- ]]; do
110
116
  case "$1" in
111
- --config)
117
+ --working-dir)
112
118
  if [[ -z ${2:-} ]]; then
113
- echo -e "${RED}[ERROR] --config requires a file path${NC}" >&2
119
+ echo -e "${RED}[ERROR] --working-dir requires a directory path${NC}" >&2
114
120
  exit 1
115
121
  fi
116
- CONFIG_FILE="$2"
122
+ WORKING_DIR="$2"
117
123
  shift 2
118
124
  ;;
119
- --config=*)
120
- CONFIG_FILE="${1#*=}"
125
+ --working-dir=*)
126
+ WORKING_DIR="${1#*=}"
121
127
  shift
122
128
  ;;
123
129
  *)
@@ -126,9 +132,11 @@ while [[ $# -gt 0 ]] && [[ "$1" =~ ^-- ]]; do
126
132
  esac
127
133
  done
128
134
 
129
- # Export CONFIG_FILE for subcommands
130
- if [[ -n $CONFIG_FILE ]]; then
131
- export CLAUDE_EVOLVE_CONFIG="$CONFIG_FILE"
135
+ # Export config file path for subcommands
136
+ if [[ -n $WORKING_DIR ]]; then
137
+ # Remove trailing slash if present
138
+ WORKING_DIR="${WORKING_DIR%/}"
139
+ export CLAUDE_EVOLVE_CONFIG="$WORKING_DIR/config.yaml"
132
140
  fi
133
141
 
134
142
  # Check for updates (quick, non-blocking)
@@ -137,21 +145,23 @@ check_for_updates
137
145
  # Main logic
138
146
  if [[ $# -eq 0 ]]; then
139
147
  show_menu
140
- read -r -p "Enter your choice (1-7): " choice
148
+ read -r -p "Enter your choice (1-9): " choice
141
149
 
142
150
  case $choice in
143
151
  1) exec "$SCRIPT_DIR/claude-evolve-setup" ;;
144
152
  2) exec "$SCRIPT_DIR/claude-evolve-ideate" ;;
145
- 3) exec "$SCRIPT_DIR/claude-evolve-run-unified" ;;
153
+ 3) exec "$SCRIPT_DIR/claude-evolve-run" ;;
146
154
  4) exec "$SCRIPT_DIR/claude-evolve-analyze" ;;
147
- 5) exec "$SCRIPT_DIR/claude-evolve-config" ;;
148
- 6) show_help ;;
149
- 7)
155
+ 5) exec "$SCRIPT_DIR/claude-evolve-edit" ;;
156
+ 6) exec "$SCRIPT_DIR/claude-evolve-status" ;;
157
+ 7) exec "$SCRIPT_DIR/claude-evolve-config" ;;
158
+ 8) show_help ;;
159
+ 9)
150
160
  echo "Goodbye!"
151
161
  exit 0
152
162
  ;;
153
163
  *)
154
- echo -e "${RED}Invalid choice. Please select 1-7.${NC}"
164
+ echo -e "${RED}Invalid choice. Please select 1-9.${NC}"
155
165
  exit 1
156
166
  ;;
157
167
  esac
@@ -174,12 +184,24 @@ ideate)
174
184
  ;;
175
185
  run)
176
186
  shift
177
- exec "$SCRIPT_DIR/claude-evolve-run-unified" "$@"
187
+ exec "$SCRIPT_DIR/claude-evolve-run" "$@"
178
188
  ;;
179
189
  analyze)
180
190
  shift
181
191
  exec "$SCRIPT_DIR/claude-evolve-analyze" "$@"
182
192
  ;;
193
+ edit)
194
+ shift
195
+ exec "$SCRIPT_DIR/claude-evolve-edit" "$@"
196
+ ;;
197
+ status)
198
+ shift
199
+ exec "$SCRIPT_DIR/claude-evolve-status" "$@"
200
+ ;;
201
+ cleanup-duplicates|cleanup)
202
+ shift
203
+ exec "$SCRIPT_DIR/claude-evolve-cleanup" "$@"
204
+ ;;
183
205
  config)
184
206
  shift
185
207
  exec "$SCRIPT_DIR/claude-evolve-config" "$@"
@@ -236,12 +236,32 @@ cleanup_workers() {
236
236
  # Worker finished
237
237
  if wait "$pid" 2>/dev/null; then
238
238
  echo "[DISPATCHER] Worker $pid completed successfully"
239
+ consecutive_failures=0 # Reset counter on success
239
240
  else
240
241
  local exit_code=$?
241
242
  if [[ $exit_code -eq 2 ]]; then
242
243
  echo "[DISPATCHER] Worker $pid hit rate limit, will retry later"
244
+ # Rate limits don't count as consecutive failures
243
245
  else
244
246
  echo "[DISPATCHER] Worker $pid failed with exit code $exit_code"
247
+ ((consecutive_failures++))
248
+
249
+ # Check if we've hit the failure limit
250
+ if [[ $consecutive_failures -ge $MAX_CONSECUTIVE_FAILURES ]]; then
251
+ echo "" >&2
252
+ echo "⚠️ EVOLUTION PAUSED: Multiple consecutive failures detected" >&2
253
+ echo "⚠️ $consecutive_failures consecutive worker failures - indicates systemic issues" >&2
254
+ echo "⚠️ Possible causes: Claude API issues, evaluator bugs, configuration problems" >&2
255
+ echo "⚠️ Check recent worker logs in logs/ directory for specific error details" >&2
256
+ echo "⚠️ Fix issues before restarting evolution" >&2
257
+ echo "" >&2
258
+
259
+ # Shutdown all workers and exit
260
+ shutdown_workers
261
+ exit 1
262
+ fi
263
+
264
+ echo "[DISPATCHER] Consecutive failures: $consecutive_failures/$MAX_CONSECUTIVE_FAILURES"
245
265
  fi
246
266
  fi
247
267
  fi
@@ -269,9 +289,26 @@ get_csv_stats() {
269
289
  total_rows=$(wc -l < "$csv_path" | tr -d '[:space:]')
270
290
  complete_count=$(grep ',complete' "$csv_path" 2>/dev/null | wc -l | tr -d '[:space:]')
271
291
 
272
- # Count pending: empty status or "pending"
273
- # Handle potential Windows line endings by stripping carriage returns
274
- pending_count=$(awk -F, 'NR>1 {gsub(/\r/, "", $5); if($5=="" || $5=="pending") count++} END {print count+0}' "$csv_path")
292
+ # Count pending using same logic as find_next_pending_with_lock
293
+ # This includes rows with <5 fields AND rows with empty/pending status
294
+ pending_count=$("$PYTHON_CMD" -c "
295
+ import csv
296
+ import sys
297
+
298
+ pending_count = 0
299
+ with open('$csv_path', 'r') as f:
300
+ reader = csv.reader(f)
301
+ rows = list(reader)
302
+
303
+ for i in range(1, len(rows)):
304
+ # Same logic as find_next_pending_with_lock
305
+ if len(rows[i]) < 5:
306
+ pending_count += 1
307
+ elif len(rows[i]) >= 5 and (rows[i][4] == 'pending' or rows[i][4] == ''):
308
+ pending_count += 1
309
+
310
+ print(pending_count)
311
+ ")
275
312
 
276
313
  echo "$total_rows $complete_count $pending_count"
277
314
  }
@@ -279,6 +316,100 @@ get_csv_stats() {
279
316
  echo "[DISPATCHER] Starting unified evolution engine"
280
317
  echo "[DISPATCHER] Configuration: max_workers=$MAX_WORKERS, timeout=${timeout_seconds:-none}"
281
318
 
319
+ # Validate CSV and clean up stuck statuses
320
+ if [[ -f "$FULL_CSV_PATH" ]]; then
321
+ echo "[DISPATCHER] Validating CSV and cleaning up..."
322
+ if ! "$PYTHON_CMD" -c "
323
+ import csv
324
+ import sys
325
+
326
+ csv_file = '$FULL_CSV_PATH'
327
+
328
+ try:
329
+ # Read CSV - let Python's csv module handle all the complexity
330
+ with open(csv_file, 'r') as f:
331
+ reader = csv.reader(f)
332
+ rows = list(reader)
333
+
334
+ if not rows:
335
+ print('[ERROR] CSV is empty')
336
+ sys.exit(1)
337
+
338
+ # Basic sanity checks
339
+ header = rows[0]
340
+ num_fields = len(header)
341
+
342
+ if len(rows) == 1:
343
+ print('[INFO] CSV has no data rows (only header)')
344
+
345
+ # Clean up any stuck 'running' statuses
346
+ changed = 0
347
+ for i in range(1, len(rows)):
348
+ if len(rows[i]) > 4 and rows[i][4] == 'running':
349
+ rows[i][4] = ''
350
+ changed += 1
351
+
352
+ if changed > 0:
353
+ # Write back
354
+ with open(csv_file + '.tmp', 'w', newline='') as f:
355
+ writer = csv.writer(f)
356
+ writer.writerows(rows)
357
+ import os
358
+ os.rename(csv_file + '.tmp', csv_file)
359
+ print(f'[INFO] Reset {changed} stuck running candidates to pending')
360
+
361
+ # Count pending candidates
362
+ pending = 0
363
+ for i in range(1, len(rows)):
364
+ # Row with < 5 fields or empty/pending status in field 5
365
+ if len(rows[i]) < 5 or (len(rows[i]) >= 5 and rows[i][4] in ['', 'pending']):
366
+ pending += 1
367
+
368
+ print(f'[INFO] CSV loaded: {len(rows)-1} total candidates, {pending} pending')
369
+
370
+ except csv.Error as e:
371
+ print(f'[ERROR] CSV parsing error: {e}')
372
+ print('[ERROR] The CSV file appears to be malformed')
373
+ sys.exit(1)
374
+ except Exception as e:
375
+ print(f'[ERROR] Failed to read CSV: {e}')
376
+ sys.exit(1)
377
+ "; then
378
+ echo "[ERROR] CSV validation failed. Please check the error message above."
379
+ exit 1
380
+ fi
381
+ fi
382
+
383
+ # Automatic cleanup detection - check for unchanged algorithms and warn user
384
+ echo "[DISPATCHER] Checking for duplicate/unchanged algorithms..."
385
+ cleanup_output=$("$SCRIPT_DIR/claude-evolve-cleanup" --dry-run 2>&1)
386
+
387
+ # Check if cleanup found any issues (look for "UNCHANGED:" in output)
388
+ if echo "$cleanup_output" | grep -q "📋 UNCHANGED:"; then
389
+ echo "⚠️ WARNING: Issues detected that may need cleanup:"
390
+ echo "$cleanup_output"
391
+ echo ""
392
+ echo "🔧 RECOMMENDATION: Run 'claude-evolve cleanup --force' to fix these issues before continuing"
393
+ echo " This will delete unchanged algorithms and reset their descendants to pending status"
394
+ echo ""
395
+ echo "⏰ Continuing in 10 seconds (Ctrl+C to abort and run cleanup)..."
396
+
397
+ # Give user time to read and potentially abort
398
+ for i in {10..1}; do
399
+ echo -n " $i..."
400
+ sleep 1
401
+ done
402
+ echo ""
403
+ echo "🚀 Proceeding with evolution run..."
404
+ echo ""
405
+ else
406
+ echo "[DISPATCHER] No cleanup issues detected - proceeding with run"
407
+ fi
408
+
409
+ # Consecutive failure tracking
410
+ consecutive_failures=0
411
+ MAX_CONSECUTIVE_FAILURES=5
412
+
282
413
  # Main dispatch loop
283
414
  while true; do
284
415
  # Clean up finished workers
@@ -337,7 +468,7 @@ while true; do
337
468
  done
338
469
 
339
470
  # Brief pause to avoid busy waiting
340
- sleep 2
471
+ sleep 5
341
472
  done
342
473
 
343
474
  # Clean shutdown
@@ -0,0 +1,220 @@
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
+ # Function to show help
24
+ show_help() {
25
+ cat <<EOF
26
+ claude-evolve status - Show evolution progress and current leader
27
+
28
+ USAGE:
29
+ claude-evolve status [OPTIONS]
30
+
31
+ OPTIONS:
32
+ --brief Show only summary stats (no per-generation breakdown)
33
+ --winner Show only the current best performer
34
+ --help Show this help message
35
+
36
+ DESCRIPTION:
37
+ Displays evolution progress by generation including:
38
+ - Candidate counts by status (pending, complete, failed, running)
39
+ - Current best performer across all generations
40
+ - Generation-by-generation breakdown
41
+
42
+ EXAMPLES:
43
+ claude-evolve status # Full status report
44
+ claude-evolve status --brief # Just totals and winner
45
+ claude-evolve status --winner # Just the best performer
46
+ EOF
47
+ }
48
+
49
+ # Parse arguments
50
+ SHOW_BRIEF=false
51
+ SHOW_WINNER_ONLY=false
52
+
53
+ while [[ $# -gt 0 ]]; do
54
+ case $1 in
55
+ --brief)
56
+ SHOW_BRIEF=true
57
+ shift
58
+ ;;
59
+ --winner)
60
+ SHOW_WINNER_ONLY=true
61
+ shift
62
+ ;;
63
+ --help)
64
+ show_help
65
+ exit 0
66
+ ;;
67
+ *)
68
+ echo "[ERROR] Unknown option: $1" >&2
69
+ exit 1
70
+ ;;
71
+ esac
72
+ done
73
+
74
+ # Check if CSV exists
75
+ if [[ ! -f "$FULL_CSV_PATH" ]]; then
76
+ echo "[ERROR] Evolution CSV not found: $FULL_CSV_PATH" >&2
77
+ echo "Run 'claude-evolve setup' first or navigate to the correct directory" >&2
78
+ exit 1
79
+ fi
80
+
81
+ # Determine what we're evolving based on paths
82
+ EVOLUTION_CONTEXT=""
83
+ if [[ -n "$EVOLUTION_DIR" ]]; then
84
+ # Get the evolution directory name (e.g., "evolution-atr" -> "ATR")
85
+ EVOLUTION_NAME=$(basename "$EVOLUTION_DIR")
86
+ EVOLUTION_CONTEXT="${EVOLUTION_NAME#evolution-}"
87
+ EVOLUTION_CONTEXT=$(echo "$EVOLUTION_CONTEXT" | tr '[:lower:]' '[:upper:]')
88
+ fi
89
+
90
+ # If we can't determine from evolution dir, try from algorithm path
91
+ if [[ -z "$EVOLUTION_CONTEXT" && -n "$ALGORITHM_PATH" ]]; then
92
+ # Get parent directory name or algorithm file name
93
+ if [[ -f "$FULL_ALGORITHM_PATH" ]]; then
94
+ ALGO_NAME=$(basename "$FULL_ALGORITHM_PATH" .py)
95
+ EVOLUTION_CONTEXT="$ALGO_NAME"
96
+ fi
97
+ fi
98
+
99
+ # Default if we still can't determine
100
+ if [[ -z "$EVOLUTION_CONTEXT" ]]; then
101
+ EVOLUTION_CONTEXT="Algorithm"
102
+ fi
103
+
104
+ # Main status reporting using Python
105
+ "$PYTHON_CMD" -c "
106
+ import csv
107
+ import sys
108
+
109
+ csv_file = '$FULL_CSV_PATH'
110
+ show_brief = '$SHOW_BRIEF' == 'true'
111
+ show_winner_only = '$SHOW_WINNER_ONLY' == 'true'
112
+ evolution_context = '$EVOLUTION_CONTEXT'
113
+
114
+ try:
115
+ with open(csv_file, 'r') as f:
116
+ reader = csv.reader(f)
117
+ rows = list(reader)
118
+
119
+ if len(rows) <= 1:
120
+ print('No evolution candidates found')
121
+ sys.exit(0)
122
+
123
+ header = rows[0]
124
+
125
+ # Collect all candidates with scores and statuses
126
+ all_candidates = []
127
+ stats_by_gen = {}
128
+ total_stats = {'pending': 0, 'complete': 0, 'failed': 0, 'running': 0}
129
+
130
+ for row in rows[1:]:
131
+ if len(row) >= 1 and row[0]: # Must have an ID
132
+ candidate_id = row[0]
133
+
134
+ # Extract generation (e.g., 'gen03' from 'gen03-001')
135
+ if '-' in candidate_id:
136
+ gen = candidate_id.split('-')[0]
137
+
138
+ # Get status and performance
139
+ status = row[4] if len(row) > 4 and row[4] else 'pending'
140
+ performance = row[3] if len(row) > 3 and row[3] else ''
141
+
142
+ # Track by generation
143
+ if gen not in stats_by_gen:
144
+ stats_by_gen[gen] = {'pending': 0, 'complete': 0, 'failed': 0, 'running': 0}
145
+
146
+ if status in stats_by_gen[gen]:
147
+ stats_by_gen[gen][status] += 1
148
+ total_stats[status] += 1
149
+ else:
150
+ stats_by_gen[gen]['pending'] += 1
151
+ total_stats['pending'] += 1
152
+
153
+ # Collect for winner analysis (only completed with valid scores)
154
+ if status == 'complete' and performance:
155
+ try:
156
+ score = float(performance)
157
+ description = row[2] if len(row) > 2 else 'No description'
158
+ all_candidates.append((candidate_id, description, score))
159
+ except ValueError:
160
+ pass
161
+
162
+ # Find the winner
163
+ winner = None
164
+ if all_candidates:
165
+ winner = max(all_candidates, key=lambda x: x[2])
166
+
167
+ # Show winner only
168
+ if show_winner_only:
169
+ if winner:
170
+ print(f'🏆 CURRENT LEADER: {winner[0]} (score: {winner[2]:.4f})')
171
+ print(f' {winner[1]}')
172
+ else:
173
+ print('No completed candidates found')
174
+ sys.exit(0)
175
+
176
+ # Show header
177
+ print(f'🧬 Evolution Status Report - {evolution_context}')
178
+ print('=' * 50)
179
+
180
+ # Show overall stats
181
+ total_candidates = sum(total_stats.values())
182
+ if total_candidates > 0:
183
+ print(f'📊 OVERALL: {total_candidates} total candidates')
184
+ print(f' • {total_stats[\"pending\"]} pending')
185
+ print(f' • {total_stats[\"complete\"]} complete')
186
+ print(f' • {total_stats[\"failed\"]} failed')
187
+ print(f' • {total_stats[\"running\"]} running')
188
+ print()
189
+
190
+ # Show current winner
191
+ if winner:
192
+ print(f'🏆 CURRENT LEADER: {winner[0]} (score: {winner[2]:.4f})')
193
+ print(f' {winner[1]}')
194
+ print()
195
+ else:
196
+ print('🏆 CURRENT LEADER: None (no completed candidates)')
197
+ print()
198
+
199
+ # Show per-generation breakdown (unless brief mode)
200
+ if not show_brief and stats_by_gen:
201
+ print('📈 BY GENERATION:')
202
+ for gen in sorted(stats_by_gen.keys()):
203
+ data = stats_by_gen[gen]
204
+ total = sum(data.values())
205
+
206
+ # Find best performer in this generation
207
+ gen_candidates = [c for c in all_candidates if c[0].startswith(gen + '-')]
208
+ gen_best = max(gen_candidates, key=lambda x: x[2]) if gen_candidates else None
209
+
210
+ status_str = f'{data[\"pending\"]}p {data[\"complete\"]}c {data[\"failed\"]}f {data[\"running\"]}r'
211
+
212
+ if gen_best:
213
+ print(f' {gen}: {total} total ({status_str}) - best: {gen_best[0]} ({gen_best[2]:.4f})')
214
+ else:
215
+ print(f' {gen}: {total} total ({status_str}) - best: none')
216
+
217
+ except Exception as e:
218
+ print(f'Error reading evolution status: {e}')
219
+ sys.exit(1)
220
+ "