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.
- package/README.md +80 -317
- package/bin/claude-evolve-analyze +34 -5
- package/bin/claude-evolve-cleanup +297 -0
- package/bin/claude-evolve-edit +293 -0
- package/bin/claude-evolve-ideate +6 -6
- package/bin/claude-evolve-main +51 -29
- package/bin/{claude-evolve-run-unified โ claude-evolve-run} +135 -4
- package/bin/claude-evolve-status +220 -0
- package/bin/claude-evolve-worker +76 -13
- package/lib/config.sh +5 -3
- package/lib/csv-lock.sh +26 -4
- package/lib/csv_helper.py +1 -1
- package/package.json +1 -1
- package/templates/config.yaml +8 -7
- package/bin/claude-evolve-run-parallel.OLD +0 -389
- package/bin/claude-evolve-run.OLD +0 -662
|
@@ -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
|
package/bin/claude-evolve-ideate
CHANGED
|
@@ -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
|
|
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
|
|
50
|
-
echo "[INFO] Using codex o3
|
|
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
|
|
52
|
+
# Call codex with o3 model using -q flag and --full-auto
|
|
53
53
|
local ai_output
|
|
54
|
-
ai_output=$(codex -m o3
|
|
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
|
|
61
|
+
echo "[WARN] Codex o3 failed, falling back to Claude Opus" >&2
|
|
62
62
|
preferred_model="opus"
|
|
63
63
|
fi
|
|
64
64
|
fi
|