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.
@@ -0,0 +1,297 @@
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
+ # Function to show help
18
+ show_help() {
19
+ cat <<EOF
20
+ claude-evolve cleanup - Clean up unchanged algorithms and their descendants
21
+
22
+ USAGE:
23
+ claude-evolve cleanup [OPTIONS]
24
+
25
+ OPTIONS:
26
+ --dry-run Show what would be done without making changes
27
+ --force Actually perform the cleanup (required for real changes)
28
+ --help Show this help message
29
+
30
+ DESCRIPTION:
31
+ This tool finds algorithm files that are identical to their parent and:
32
+ 1. Deletes the unchanged .py files
33
+ 2. Resets those candidates to pending status in CSV
34
+ 3. Finds and cleans up any descendants that inherited from the bad copies
35
+
36
+ Use --dry-run first to see what would be affected.
37
+
38
+ EXAMPLES:
39
+ claude-evolve cleanup --dry-run # Preview changes
40
+ claude-evolve cleanup --force # Actually clean up
41
+ EOF
42
+ }
43
+
44
+ # Parse arguments
45
+ DRY_RUN=true
46
+ FORCE=false
47
+
48
+ while [[ $# -gt 0 ]]; do
49
+ case $1 in
50
+ --dry-run)
51
+ DRY_RUN=true
52
+ shift
53
+ ;;
54
+ --force)
55
+ FORCE=true
56
+ DRY_RUN=false
57
+ shift
58
+ ;;
59
+ --help)
60
+ show_help
61
+ exit 0
62
+ ;;
63
+ *)
64
+ echo "[ERROR] Unknown option: $1" >&2
65
+ exit 1
66
+ ;;
67
+ esac
68
+ done
69
+
70
+ if [[ $FORCE == false ]]; then
71
+ DRY_RUN=true
72
+ fi
73
+
74
+ # Validate configuration
75
+ if ! validate_config; then
76
+ echo "[ERROR] Configuration validation failed" >&2
77
+ exit 1
78
+ fi
79
+
80
+ # Check if CSV exists
81
+ if [[ ! -f "$FULL_CSV_PATH" ]]; then
82
+ echo "[ERROR] Evolution CSV not found: $FULL_CSV_PATH" >&2
83
+ exit 1
84
+ fi
85
+
86
+ echo "๐Ÿงน Claude-Evolve Duplicate Cleanup Tool"
87
+ echo "========================================"
88
+ echo "Evolution directory: $FULL_EVOLUTION_DIR"
89
+ echo "CSV file: $FULL_CSV_PATH"
90
+ echo "Mode: $(if [[ $DRY_RUN == true ]]; then echo "DRY RUN (preview only)"; else echo "FORCE (will make changes)"; fi)"
91
+ echo ""
92
+
93
+ # Use Python to analyze and clean up duplicates
94
+ "$PYTHON_CMD" -c "
95
+ import csv
96
+ import os
97
+ import sys
98
+ import shutil
99
+ from pathlib import Path
100
+
101
+ csv_file = '$FULL_CSV_PATH'
102
+ evolution_dir = '$FULL_EVOLUTION_DIR'
103
+ dry_run = '$DRY_RUN' == 'true'
104
+ algorithm_file = '$FULL_ALGORITHM_PATH'
105
+
106
+ def files_identical(file1, file2):
107
+ \"\"\"Check if two files have identical content.\"\"\"
108
+ if not os.path.exists(file1) or not os.path.exists(file2):
109
+ return False
110
+
111
+ try:
112
+ with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
113
+ return f1.read() == f2.read()
114
+ except Exception:
115
+ return False
116
+
117
+ def get_algorithm_file_path(candidate_id, base_algorithm):
118
+ \"\"\"Get the file path for a candidate's algorithm.\"\"\"
119
+ # Handle both old and new format IDs
120
+ if candidate_id.isdigit():
121
+ filename = f'evolution_id{candidate_id}.py'
122
+ else:
123
+ filename = f'evolution_{candidate_id}.py'
124
+
125
+ return os.path.join(evolution_dir, filename)
126
+
127
+ def get_parent_file_path(based_on_id, base_algorithm):
128
+ \"\"\"Get the file path for a parent algorithm.\"\"\"
129
+ if not based_on_id or based_on_id == '0' or based_on_id == '\"\"':
130
+ return base_algorithm
131
+
132
+ # Handle both old and new format IDs
133
+ if based_on_id.isdigit():
134
+ filename = f'evolution_id{based_on_id}.py'
135
+ else:
136
+ filename = f'evolution_{based_on_id}.py'
137
+
138
+ return os.path.join(evolution_dir, filename)
139
+
140
+ try:
141
+ # Read CSV
142
+ with open(csv_file, 'r') as f:
143
+ reader = csv.reader(f)
144
+ rows = list(reader)
145
+
146
+ if len(rows) <= 1:
147
+ print('No candidates found in CSV')
148
+ sys.exit(0)
149
+
150
+ header = rows[0]
151
+ candidates = {}
152
+
153
+ # Build candidate map
154
+ for i, row in enumerate(rows[1:], 1):
155
+ if len(row) >= 3:
156
+ candidate_id = row[0]
157
+ based_on_id = row[1] if len(row) > 1 else ''
158
+ description = row[2] if len(row) > 2 else ''
159
+ performance = row[3] if len(row) > 3 else ''
160
+ status = row[4] if len(row) > 4 else ''
161
+
162
+ candidates[candidate_id] = {
163
+ 'row_index': i,
164
+ 'based_on_id': based_on_id,
165
+ 'description': description,
166
+ 'performance': performance,
167
+ 'status': status,
168
+ 'file_path': get_algorithm_file_path(candidate_id, algorithm_file)
169
+ }
170
+
171
+ print(f'Found {len(candidates)} candidates to analyze')
172
+ print('')
173
+
174
+ # Find unchanged candidates
175
+ unchanged_candidates = []
176
+
177
+ for candidate_id, info in candidates.items():
178
+ if not info['based_on_id'] or info['based_on_id'] == '0' or info['based_on_id'] == '\"\"':
179
+ # Skip root candidates (no parent)
180
+ continue
181
+
182
+ parent_file = get_parent_file_path(info['based_on_id'], algorithm_file)
183
+ candidate_file = info['file_path']
184
+
185
+ if os.path.exists(candidate_file) and files_identical(candidate_file, parent_file):
186
+ unchanged_candidates.append(candidate_id)
187
+ print(f'๐Ÿ“‹ UNCHANGED: {candidate_id} is identical to parent {info[\"based_on_id\"]}')
188
+ print(f' File: {os.path.basename(candidate_file)}')
189
+ print(f' Description: {info[\"description\"]}')
190
+ print(f' Status: {info[\"status\"]}')
191
+ print('')
192
+
193
+ if not unchanged_candidates:
194
+ print('โœ… No unchanged candidates found - all algorithms appear to be properly mutated!')
195
+ sys.exit(0)
196
+
197
+ print(f'Found {len(unchanged_candidates)} unchanged candidates')
198
+ print('')
199
+
200
+ # Find descendants of unchanged candidates
201
+ def find_descendants(bad_parent_id, all_candidates, found=None):
202
+ if found is None:
203
+ found = set()
204
+
205
+ for cand_id, info in all_candidates.items():
206
+ if info['based_on_id'] == bad_parent_id and cand_id not in found:
207
+ found.add(cand_id)
208
+ # Recursively find descendants of this candidate
209
+ find_descendants(cand_id, all_candidates, found)
210
+
211
+ return found
212
+
213
+ all_affected = set(unchanged_candidates)
214
+
215
+ # Find all descendants
216
+ for unchanged_id in unchanged_candidates:
217
+ descendants = find_descendants(unchanged_id, candidates)
218
+ all_affected.update(descendants)
219
+
220
+ if descendants:
221
+ print(f'๐Ÿ”— DESCENDANTS of {unchanged_id}: {sorted(descendants)}')
222
+
223
+ print('')
224
+ print(f'๐Ÿ“Š SUMMARY:')
225
+ print(f' โ€ข {len(unchanged_candidates)} unchanged candidates')
226
+ print(f' โ€ข {len(all_affected) - len(unchanged_candidates)} descendants affected')
227
+ print(f' โ€ข {len(all_affected)} total candidates to clean up')
228
+ print('')
229
+
230
+ if dry_run:
231
+ print('๐Ÿ” DRY RUN - Showing what would be done:')
232
+ print('')
233
+
234
+ for candidate_id in sorted(all_affected):
235
+ info = candidates[candidate_id]
236
+ action = 'DELETE FILE & RESET' if candidate_id in unchanged_candidates else 'RESET (descendant)'
237
+ print(f' {action}: {candidate_id}')
238
+ print(f' File: {os.path.basename(info[\"file_path\"])}')
239
+ print(f' Description: {info[\"description\"]}')
240
+ print('')
241
+
242
+ print('To actually perform cleanup, run with --force')
243
+ else:
244
+ print('๐Ÿงน PERFORMING CLEANUP:')
245
+ print('')
246
+
247
+ # Delete files and update CSV
248
+ files_deleted = 0
249
+ rows_updated = 0
250
+
251
+ for candidate_id in sorted(all_affected):
252
+ info = candidates[candidate_id]
253
+
254
+ # Delete file if it exists (for unchanged candidates)
255
+ if candidate_id in unchanged_candidates and os.path.exists(info['file_path']):
256
+ try:
257
+ os.remove(info['file_path'])
258
+ files_deleted += 1
259
+ print(f' โœ… DELETED: {os.path.basename(info[\"file_path\"])}')
260
+ except Exception as e:
261
+ print(f' โŒ FAILED to delete {os.path.basename(info[\"file_path\"])}: {e}')
262
+
263
+ # Reset CSV row (clear performance and status, keep description)
264
+ row_idx = info['row_index']
265
+ if len(rows[row_idx]) >= 5:
266
+ # Clear performance (column 3) and status (column 4), but keep first 3 columns
267
+ rows[row_idx] = rows[row_idx][:3] + ['', ''] + rows[row_idx][5:]
268
+ rows_updated += 1
269
+ print(f' โœ… RESET CSV: {candidate_id} -> pending')
270
+
271
+ # Write updated CSV
272
+ try:
273
+ with open(csv_file + '.tmp', 'w', newline='') as f:
274
+ writer = csv.writer(f)
275
+ writer.writerows(rows)
276
+
277
+ # Atomic replace
278
+ os.rename(csv_file + '.tmp', csv_file)
279
+ print('')
280
+ print(f'โœ… CLEANUP COMPLETE:')
281
+ print(f' โ€ข {files_deleted} files deleted')
282
+ print(f' โ€ข {rows_updated} CSV rows reset to pending')
283
+ print(f' โ€ข CSV updated successfully')
284
+
285
+ except Exception as e:
286
+ print(f'โŒ FAILED to update CSV: {e}')
287
+ sys.exit(1)
288
+
289
+ except Exception as e:
290
+ print(f'Error: {e}')
291
+ sys.exit(1)
292
+ "
293
+
294
+ echo ""
295
+ if [[ $DRY_RUN == true ]]; then
296
+ echo "๐Ÿ’ก TIP: Run with --force to actually perform the cleanup"
297
+ fi
@@ -0,0 +1,293 @@
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
+ # Function to show help
18
+ show_help() {
19
+ cat <<EOF
20
+ claude-evolve edit - Manage evolution candidate statuses by generation or status
21
+
22
+ USAGE:
23
+ claude-evolve edit <selector> <action>
24
+
25
+ SELECTORS:
26
+ gen01, gen02, etc. Target specific generation
27
+ all Target all generations
28
+ failed Target all candidates with failed status
29
+ complete Target all candidates with complete status
30
+ pending Target all candidates with pending status
31
+ running Target all candidates with running status
32
+
33
+ ACTIONS:
34
+ failed Mark candidates as failed (keeps scores)
35
+ complete Mark candidates as complete (keeps scores)
36
+ pending Mark candidates as pending (keeps scores)
37
+ reboot Reset completely (delete .py files, clear scores, set pending)
38
+
39
+ EXAMPLES:
40
+ claude-evolve edit gen03 failed # Mark all gen03 as failed
41
+ claude-evolve edit failed pending # Reset all failed candidates to pending
42
+ claude-evolve edit complete failed # Mark all complete as failed for re-run
43
+ claude-evolve edit all pending # Mark everything as pending for re-run
44
+ claude-evolve edit gen02 reboot # Full reset of gen02 (delete files + clear data)
45
+
46
+ DESCRIPTION:
47
+ This command helps manage evolution runs when you need to re-evaluate candidates.
48
+ Use status selectors (failed, complete, etc.) to bulk-change candidates by status.
49
+ Use 'reboot' for complete reset including file deletion.
50
+ EOF
51
+ }
52
+
53
+ # Parse arguments
54
+ if [[ $# -ne 2 ]]; then
55
+ show_help
56
+ exit 1
57
+ fi
58
+
59
+ SELECTOR="$1"
60
+ ACTION="$2"
61
+
62
+ # Validate configuration
63
+ if ! validate_config; then
64
+ echo "[ERROR] Configuration validation failed" >&2
65
+ exit 1
66
+ fi
67
+
68
+ # Validate selector format
69
+ if [[ "$SELECTOR" != "all" && ! "$SELECTOR" =~ ^gen[0-9]+$ && "$SELECTOR" != "failed" && "$SELECTOR" != "complete" && "$SELECTOR" != "pending" && "$SELECTOR" != "running" ]]; then
70
+ echo "[ERROR] Selector must be 'all', 'genXX' (e.g., gen01), or status ('failed', 'complete', 'pending', 'running')" >&2
71
+ exit 1
72
+ fi
73
+
74
+ # Validate action
75
+ case "$ACTION" in
76
+ failed|complete|pending|reboot) ;;
77
+ *)
78
+ echo "[ERROR] Action must be one of: failed, complete, pending, reboot" >&2
79
+ exit 1
80
+ ;;
81
+ esac
82
+
83
+ # Check if CSV exists
84
+ if [[ ! -f "$FULL_CSV_PATH" ]]; then
85
+ echo "[ERROR] Evolution CSV not found: $FULL_CSV_PATH" >&2
86
+ echo "Run 'claude-evolve setup' first or navigate to the correct directory" >&2
87
+ exit 1
88
+ fi
89
+
90
+ # Function to update CSV status for specific selector
91
+ update_candidates_status() {
92
+ local selector="$1"
93
+ local new_status="$2"
94
+ local clear_scores="$3"
95
+
96
+ echo "[INFO] Updating candidates matching '$selector' to status: $new_status"
97
+
98
+ # Use Python to safely edit the CSV
99
+ "$PYTHON_CMD" -c "
100
+ import csv
101
+ import sys
102
+ import os
103
+
104
+ csv_file = '$FULL_CSV_PATH'
105
+ selector = '$selector'
106
+ new_status = '$new_status'
107
+ clear_scores = '$clear_scores' == 'true'
108
+
109
+ try:
110
+ # Read CSV
111
+ with open(csv_file, 'r') as f:
112
+ reader = csv.reader(f)
113
+ rows = list(reader)
114
+
115
+ if not rows:
116
+ print('[ERROR] CSV is empty')
117
+ sys.exit(1)
118
+
119
+ header = rows[0]
120
+ updated_count = 0
121
+
122
+ # Update matching rows
123
+ for i in range(1, len(rows)):
124
+ row = rows[i]
125
+ if len(row) < 1:
126
+ continue
127
+
128
+ candidate_id = row[0]
129
+ current_status = row[4] if len(row) > 4 else ''
130
+
131
+ # Check if this row matches selector
132
+ matches = False
133
+ if selector == 'all':
134
+ matches = True
135
+ elif selector.startswith('gen') and '-' in candidate_id:
136
+ # Generation selector (e.g., gen01, gen02)
137
+ matches = candidate_id.startswith(selector + '-')
138
+ elif selector in ['failed', 'complete', 'pending', 'running']:
139
+ # Status selector
140
+ if selector == 'pending':
141
+ matches = current_status == '' or current_status == 'pending'
142
+ else:
143
+ matches = current_status == selector
144
+
145
+ if matches:
146
+ if clear_scores:
147
+ # Reboot: clear everything after description (keep id, basedOnId, description)
148
+ if len(row) >= 3:
149
+ rows[i] = [row[0], row[1], row[2], '', ''] # id, basedOnId, description, empty performance, empty status
150
+ updated_count += 1
151
+ else:
152
+ # Just update status (preserve other fields)
153
+ # Ensure row has at least 5 fields
154
+ while len(row) < 5:
155
+ row.append('')
156
+ row[4] = new_status # Update status field
157
+ updated_count += 1
158
+
159
+ # Write back to CSV
160
+ with open(csv_file + '.tmp', 'w', newline='') as f:
161
+ writer = csv.writer(f)
162
+ writer.writerows(rows)
163
+
164
+ # Atomic replace
165
+ os.rename(csv_file + '.tmp', csv_file)
166
+
167
+ print(f'[INFO] Updated {updated_count} candidates')
168
+
169
+ except Exception as e:
170
+ print(f'[ERROR] Failed to update CSV: {e}')
171
+ sys.exit(1)
172
+ "
173
+ }
174
+
175
+ # Function to delete evolution files for selector
176
+ delete_evolution_files() {
177
+ local selector="$1"
178
+
179
+ if [[ ! -d "$FULL_EVOLUTION_DIR" ]]; then
180
+ echo "[WARN] Evolution directory not found: $FULL_EVOLUTION_DIR"
181
+ return
182
+ fi
183
+
184
+ local deleted_count=0
185
+
186
+ if [[ "$selector" == "all" ]]; then
187
+ echo "[INFO] Deleting all evolution_*.py files..."
188
+ for file in "$FULL_EVOLUTION_DIR"/evolution_*.py; do
189
+ if [[ -f "$file" ]]; then
190
+ rm "$file"
191
+ ((deleted_count++))
192
+ echo "[INFO] Deleted: $(basename "$file")"
193
+ fi
194
+ done
195
+ elif [[ "$selector" =~ ^gen[0-9]+$ ]]; then
196
+ echo "[INFO] Deleting evolution files for $selector..."
197
+ for file in "$FULL_EVOLUTION_DIR"/evolution_${selector}-*.py; do
198
+ if [[ -f "$file" ]]; then
199
+ rm "$file"
200
+ ((deleted_count++))
201
+ echo "[INFO] Deleted: $(basename "$file")"
202
+ fi
203
+ done
204
+ else
205
+ # Status-based selector - need to query CSV for candidate IDs
206
+ echo "[INFO] Finding files to delete for status '$selector'..."
207
+
208
+ # Use Python to get list of candidate IDs matching the status
209
+ local candidates_to_delete
210
+ candidates_to_delete=$("$PYTHON_CMD" -c "
211
+ import csv
212
+ import sys
213
+
214
+ csv_file = '$FULL_CSV_PATH'
215
+ selector = '$selector'
216
+
217
+ try:
218
+ with open(csv_file, 'r') as f:
219
+ reader = csv.reader(f)
220
+ next(reader) # Skip header
221
+
222
+ candidates = []
223
+ for row in reader:
224
+ if len(row) < 1:
225
+ continue
226
+
227
+ candidate_id = row[0]
228
+ current_status = row[4] if len(row) > 4 else ''
229
+
230
+ # Check if matches status selector
231
+ matches = False
232
+ if selector == 'pending':
233
+ matches = current_status == '' or current_status == 'pending'
234
+ else:
235
+ matches = current_status == selector
236
+
237
+ if matches:
238
+ candidates.append(candidate_id)
239
+
240
+ print(' '.join(candidates))
241
+
242
+ except Exception as e:
243
+ print('', file=sys.stderr) # Empty output on error
244
+ sys.exit(1)
245
+ ")
246
+
247
+ if [[ -n "$candidates_to_delete" ]]; then
248
+ for candidate_id in $candidates_to_delete; do
249
+ # Determine file format
250
+ if [[ "$candidate_id" =~ ^[0-9]+$ ]]; then
251
+ file="$FULL_EVOLUTION_DIR/evolution_id${candidate_id}.py"
252
+ else
253
+ file="$FULL_EVOLUTION_DIR/evolution_${candidate_id}.py"
254
+ fi
255
+
256
+ if [[ -f "$file" ]]; then
257
+ rm "$file"
258
+ ((deleted_count++))
259
+ echo "[INFO] Deleted: $(basename "$file")"
260
+ fi
261
+ done
262
+ fi
263
+ fi
264
+
265
+ echo "[INFO] Deleted $deleted_count evolution files"
266
+ }
267
+
268
+ # Main execution
269
+ echo "[INFO] Processing '$SELECTOR' with action: $ACTION"
270
+
271
+ case "$ACTION" in
272
+ failed)
273
+ update_candidates_status "$SELECTOR" "failed" "false"
274
+ ;;
275
+ complete)
276
+ update_candidates_status "$SELECTOR" "complete" "false"
277
+ ;;
278
+ pending)
279
+ update_candidates_status "$SELECTOR" "" "false" # Empty status means pending
280
+ ;;
281
+ reboot)
282
+ echo "[INFO] Performing full reboot of '$SELECTOR'..."
283
+ delete_evolution_files "$SELECTOR"
284
+ update_candidates_status "$SELECTOR" "" "true" # Clear scores and set pending
285
+ echo "[INFO] Reboot complete: files deleted, scores cleared, status set to pending"
286
+ ;;
287
+ esac
288
+
289
+ echo "[INFO] Edit operation complete"
290
+
291
+ # Call status command to show current state
292
+ echo ""
293
+ "$SCRIPT_DIR/claude-evolve-status" --brief
@@ -30,7 +30,7 @@ get_model_for_generation() {
30
30
  if (( gen_num % 2 == 1 )); then
31
31
  echo "opus" # Odd generations: use Opus for exploration
32
32
  else
33
- echo "o3-pro" # Even generations: use o3-pro for refinement
33
+ echo "o3" # Even generations: use o3 for refinement
34
34
  fi
35
35
  }
36
36
 
@@ -46,19 +46,19 @@ call_ai_with_limit_check() {
46
46
  echo "[INFO] Generation $generation: Using $preferred_model" >&2
47
47
 
48
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
49
+ if [[ "$preferred_model" == "o3" ]] && command -v codex >/dev/null 2>&1; then
50
+ echo "[INFO] Using codex o3 for ideation" >&2
51
51
 
52
- # Call codex with o3-pro model using -q flag and --full-auto
52
+ # Call codex with o3 model using -q flag and --full-auto
53
53
  local ai_output
54
- ai_output=$(codex -m o3-pro --full-auto -q "$prompt" 2>&1)
54
+ ai_output=$(codex -m o3 --full-auto -q "$prompt" 2>&1)
55
55
  local ai_exit_code=$?
56
56
 
57
57
  if [[ $ai_exit_code -eq 0 ]]; then
58
58
  echo "$ai_output"
59
59
  return 0
60
60
  else
61
- echo "[WARN] Codex o3-pro failed, falling back to Claude Opus" >&2
61
+ echo "[WARN] Codex o3 failed, falling back to Claude Opus" >&2
62
62
  preferred_model="opus"
63
63
  fi
64
64
  fi