claude-evolve 1.3.12 → 1.3.14
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 +5 -1
- package/bin/claude-evolve-analyze +195 -92
- package/lib/config.sh +28 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -172,7 +172,11 @@ Evolution experiments can fail for various reasons. The system tracks these fail
|
|
|
172
172
|
### Required
|
|
173
173
|
- Node.js >= 14.0.0
|
|
174
174
|
- Python 3.x (for algorithm execution)
|
|
175
|
-
-
|
|
175
|
+
- Automatically detected on all platforms
|
|
176
|
+
- Windows: Uses `python` if it's Python 3
|
|
177
|
+
- macOS/Linux: Prefers `python3`
|
|
178
|
+
- Can override in config.yaml: `python_cmd: "C:\\Python39\\python.exe"`
|
|
179
|
+
- Bash shell (Git Bash on Windows, native on macOS/Linux)
|
|
176
180
|
- [Claude CLI](https://docs.anthropic.com/en/docs/claude-code) (`claude` command)
|
|
177
181
|
|
|
178
182
|
### Optional (but recommended)
|
|
@@ -92,35 +92,69 @@ top_score=""
|
|
|
92
92
|
top_id=""
|
|
93
93
|
top_desc=""
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
95
|
+
# Use Python to parse CSV and generate stats
|
|
96
|
+
eval "$("$PYTHON_CMD" -c "
|
|
97
|
+
import csv
|
|
98
|
+
|
|
99
|
+
# Initialize counters
|
|
100
|
+
total = 0
|
|
101
|
+
completed = 0
|
|
102
|
+
running = 0
|
|
103
|
+
failed = 0
|
|
104
|
+
pending = 0
|
|
105
|
+
total_performance = 0
|
|
106
|
+
count_with_performance = 0
|
|
107
|
+
top_score = None
|
|
108
|
+
top_id = ''
|
|
109
|
+
top_desc = ''
|
|
110
|
+
|
|
111
|
+
with open('$csv_file', 'r') as f:
|
|
112
|
+
reader = csv.reader(f)
|
|
113
|
+
next(reader) # Skip header
|
|
114
|
+
|
|
115
|
+
for row in reader:
|
|
116
|
+
if len(row) < 5:
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
id, _, desc, perf, status = row[:5]
|
|
120
|
+
total += 1
|
|
121
|
+
|
|
122
|
+
if status in ['complete', 'completed']:
|
|
123
|
+
completed += 1
|
|
124
|
+
if perf and perf != '':
|
|
125
|
+
try:
|
|
126
|
+
perf_val = float(perf)
|
|
127
|
+
if perf_val > 0: # Skip zeros (they're errors)
|
|
128
|
+
total_performance += perf_val
|
|
129
|
+
count_with_performance += 1
|
|
130
|
+
|
|
131
|
+
if top_score is None or perf_val > top_score:
|
|
132
|
+
top_score = perf_val
|
|
133
|
+
top_id = id
|
|
134
|
+
top_desc = desc
|
|
135
|
+
except ValueError:
|
|
136
|
+
pass
|
|
137
|
+
elif status == 'running':
|
|
138
|
+
running += 1
|
|
139
|
+
elif status in ['failed', 'timeout', 'interrupted']:
|
|
140
|
+
failed += 1
|
|
141
|
+
else:
|
|
142
|
+
pending += 1
|
|
143
|
+
|
|
144
|
+
# Output shell variable assignments
|
|
145
|
+
print(f'total={total}')
|
|
146
|
+
print(f'completed={completed}')
|
|
147
|
+
print(f'running={running}')
|
|
148
|
+
print(f'failed={failed}')
|
|
149
|
+
print(f'pending={pending}')
|
|
150
|
+
print(f'total_performance={total_performance}')
|
|
151
|
+
print(f'count_with_performance={count_with_performance}')
|
|
152
|
+
print(f'top_score={top_score if top_score is not None else \"\"}')
|
|
153
|
+
print(f'top_id=\"{top_id}\"')
|
|
154
|
+
# Escape special characters in description
|
|
155
|
+
desc_escaped = top_desc.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"').replace('\$', '\\\\\$').replace('\`', '\\\\\`')
|
|
156
|
+
print(f'top_desc=\"{desc_escaped}\"')
|
|
157
|
+
")"
|
|
124
158
|
|
|
125
159
|
# Display summary
|
|
126
160
|
echo "Total Candidates: $total"
|
|
@@ -154,30 +188,44 @@ echo "=== Generation Analysis ==="
|
|
|
154
188
|
gen_stats_file="/tmp/evolution_gen_stats_$$.tmp"
|
|
155
189
|
>"$gen_stats_file"
|
|
156
190
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
191
|
+
# Use Python to write generation stats
|
|
192
|
+
"$PYTHON_CMD" -c "
|
|
193
|
+
import csv
|
|
194
|
+
import re
|
|
195
|
+
|
|
196
|
+
with open('$csv_file', 'r') as f:
|
|
197
|
+
reader = csv.reader(f)
|
|
198
|
+
next(reader) # Skip header
|
|
199
|
+
|
|
200
|
+
with open('$gen_stats_file', 'w') as out:
|
|
201
|
+
for row in reader:
|
|
202
|
+
if len(row) < 5:
|
|
203
|
+
continue
|
|
204
|
+
|
|
205
|
+
id, _, desc, perf, status = row[:5]
|
|
206
|
+
|
|
207
|
+
# Extract generation from ID
|
|
208
|
+
gen = 'gen01' # default for old numeric IDs
|
|
209
|
+
match = re.match(r'^(gen[0-9]+)-', id)
|
|
210
|
+
if match:
|
|
211
|
+
gen = match.group(1)
|
|
212
|
+
elif re.match(r'^[0-9]+$', id):
|
|
213
|
+
gen = 'gen00' # Mark old numeric IDs as gen00
|
|
214
|
+
|
|
215
|
+
# Write generation data
|
|
216
|
+
out.write(gen + ' ')
|
|
217
|
+
if status in ['complete', 'completed'] and perf and perf != '':
|
|
218
|
+
try:
|
|
219
|
+
perf_val = float(perf)
|
|
220
|
+
if perf_val > 0:
|
|
221
|
+
out.write(f'completed {perf}\\n')
|
|
222
|
+
else:
|
|
223
|
+
out.write('error\\n')
|
|
224
|
+
except ValueError:
|
|
225
|
+
out.write('error\\n')
|
|
226
|
+
else:
|
|
227
|
+
out.write('incomplete\\n')
|
|
228
|
+
"
|
|
181
229
|
|
|
182
230
|
# Process generation stats
|
|
183
231
|
for gen in $(cut -d' ' -f1 "$gen_stats_file" | sort -u || echo ""); do
|
|
@@ -206,14 +254,30 @@ rm -f "$gen_stats_file"
|
|
|
206
254
|
|
|
207
255
|
# Count valid performance entries for chart (excluding zeros)
|
|
208
256
|
valid_performance_count=0
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
257
|
+
# Count valid performance entries using Python
|
|
258
|
+
valid_performance_count=$("$PYTHON_CMD" -c "
|
|
259
|
+
import csv
|
|
260
|
+
|
|
261
|
+
count = 0
|
|
262
|
+
with open('$csv_file', 'r') as f:
|
|
263
|
+
reader = csv.reader(f)
|
|
264
|
+
next(reader) # Skip header
|
|
265
|
+
|
|
266
|
+
for row in reader:
|
|
267
|
+
if len(row) < 5:
|
|
268
|
+
continue
|
|
269
|
+
status = row[4]
|
|
270
|
+
perf = row[3]
|
|
271
|
+
|
|
272
|
+
if status in ['complete', 'completed'] and perf and perf != '':
|
|
273
|
+
try:
|
|
274
|
+
if float(perf) > 0:
|
|
275
|
+
count += 1
|
|
276
|
+
except ValueError:
|
|
277
|
+
pass
|
|
278
|
+
|
|
279
|
+
print(count)
|
|
280
|
+
")
|
|
217
281
|
|
|
218
282
|
# Simple chart generation using gnuplot if available
|
|
219
283
|
if command -v gnuplot >/dev/null 2>&1 && [[ $valid_performance_count -gt 0 ]]; then
|
|
@@ -250,40 +314,76 @@ if command -v gnuplot >/dev/null 2>&1 && [[ $valid_performance_count -gt 0 ]]; t
|
|
|
250
314
|
max_row=0
|
|
251
315
|
max_id=""
|
|
252
316
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
317
|
+
# Use Python to generate chart data and capture output
|
|
318
|
+
python_output=$("$PYTHON_CMD" -c "
|
|
319
|
+
import csv
|
|
320
|
+
import re
|
|
321
|
+
|
|
322
|
+
row_num = 0
|
|
323
|
+
max_perf = 0
|
|
324
|
+
max_row = 0
|
|
325
|
+
max_id = ''
|
|
326
|
+
|
|
327
|
+
with open('$csv_file', 'r') as f:
|
|
328
|
+
reader = csv.reader(f)
|
|
329
|
+
next(reader) # Skip header
|
|
256
330
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if [[ $id =~ ^(gen[0-9]+)- ]]; then
|
|
260
|
-
gen="${BASH_REMATCH[1]}"
|
|
261
|
-
fi
|
|
331
|
+
data_lines = []
|
|
332
|
+
gen_temp_lines = []
|
|
262
333
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if [[ $id =~ ^gen([0-9]+)- ]]; then
|
|
270
|
-
gen_num=$((10#${BASH_REMATCH[1]}))
|
|
271
|
-
fi
|
|
334
|
+
for row in reader:
|
|
335
|
+
if len(row) < 5:
|
|
336
|
+
continue
|
|
337
|
+
|
|
338
|
+
row_num += 1
|
|
339
|
+
id, _, desc, perf, status = row[:5]
|
|
272
340
|
|
|
273
|
-
|
|
341
|
+
# Extract generation from ID
|
|
342
|
+
gen = 'gen01' # default
|
|
343
|
+
match = re.match(r'^(gen[0-9]+)-', id)
|
|
344
|
+
if match:
|
|
345
|
+
gen = match.group(1)
|
|
274
346
|
|
|
275
|
-
#
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
347
|
+
# Only include completed algorithms with non-zero performance
|
|
348
|
+
if perf and perf != '' and status in ['complete', 'completed']:
|
|
349
|
+
try:
|
|
350
|
+
perf_val = float(perf)
|
|
351
|
+
if perf_val > 0:
|
|
352
|
+
# Assign generation number for coloring (1-based)
|
|
353
|
+
gen_num = 1
|
|
354
|
+
match = re.match(r'^gen([0-9]+)-', id)
|
|
355
|
+
if match:
|
|
356
|
+
gen_num = int(match.group(1))
|
|
357
|
+
|
|
358
|
+
data_lines.append(f'{row_num} \\\"{id}\\\" {perf} {gen_num}')
|
|
359
|
+
gen_temp_lines.append(f'{gen} {perf}')
|
|
360
|
+
|
|
361
|
+
# Track the winner
|
|
362
|
+
if perf_val > max_perf:
|
|
363
|
+
max_perf = perf_val
|
|
364
|
+
max_row = row_num
|
|
365
|
+
max_id = id
|
|
366
|
+
except ValueError:
|
|
367
|
+
pass
|
|
368
|
+
|
|
369
|
+
# Write data file
|
|
370
|
+
with open('$data_file', 'a') as f:
|
|
371
|
+
for line in data_lines:
|
|
372
|
+
f.write(line + '\\\\n')
|
|
373
|
+
|
|
374
|
+
# Write gen temp file
|
|
375
|
+
with open('$gen_data_temp', 'a') as f:
|
|
376
|
+
for line in gen_temp_lines:
|
|
377
|
+
f.write(line + '\\\\n')
|
|
378
|
+
|
|
379
|
+
# Output max values for shell
|
|
380
|
+
print(f'max_perf={max_perf}')
|
|
381
|
+
print(f'max_row={max_row}')
|
|
382
|
+
print(f'max_id=\\\"{max_id}\\\"')
|
|
383
|
+
")
|
|
384
|
+
|
|
385
|
+
# Evaluate the Python output
|
|
386
|
+
eval "$python_output"
|
|
287
387
|
|
|
288
388
|
# Create generation averages file and track max generation
|
|
289
389
|
gen_index=1
|
|
@@ -308,8 +408,11 @@ if command -v gnuplot >/dev/null 2>&1 && [[ $valid_performance_count -gt 0 ]]; t
|
|
|
308
408
|
done
|
|
309
409
|
|
|
310
410
|
# Create winner data point
|
|
311
|
-
if [[ -n $max_id ]]; then
|
|
411
|
+
if [[ -n $max_id && -n $max_row && -n $max_perf ]]; then
|
|
312
412
|
echo "$max_row \"$max_id\" $max_perf" >"$winner_file"
|
|
413
|
+
else
|
|
414
|
+
# Create empty winner file to avoid gnuplot warning
|
|
415
|
+
echo "0 \"\" 0" >"$winner_file"
|
|
313
416
|
fi
|
|
314
417
|
|
|
315
418
|
# Generate dual plot
|
package/lib/config.sh
CHANGED
|
@@ -9,7 +9,25 @@ DEFAULT_BRIEF_FILE="BRIEF.md"
|
|
|
9
9
|
DEFAULT_EVOLUTION_CSV="evolution.csv"
|
|
10
10
|
DEFAULT_OUTPUT_DIR=""
|
|
11
11
|
DEFAULT_PARENT_SELECTION="best"
|
|
12
|
-
|
|
12
|
+
# Detect Python command based on platform
|
|
13
|
+
detect_python_cmd() {
|
|
14
|
+
# Try python3 first (macOS, Linux)
|
|
15
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
16
|
+
echo "python3"
|
|
17
|
+
# Try python (Windows, some Linux)
|
|
18
|
+
elif command -v python >/dev/null 2>&1; then
|
|
19
|
+
# Verify it's Python 3
|
|
20
|
+
if python -c "import sys; sys.exit(0 if sys.version_info[0] >= 3 else 1)" 2>/dev/null; then
|
|
21
|
+
echo "python"
|
|
22
|
+
else
|
|
23
|
+
echo "python3" # Fallback
|
|
24
|
+
fi
|
|
25
|
+
else
|
|
26
|
+
echo "python3" # Default fallback
|
|
27
|
+
fi
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
DEFAULT_PYTHON_CMD="$(detect_python_cmd)"
|
|
13
31
|
|
|
14
32
|
# Default ideation strategy values
|
|
15
33
|
DEFAULT_TOTAL_IDEAS=15
|
|
@@ -195,7 +213,16 @@ validate_config() {
|
|
|
195
213
|
|
|
196
214
|
if ! command -v "$PYTHON_CMD" >/dev/null 2>&1; then
|
|
197
215
|
echo "[ERROR] Python command not found: $PYTHON_CMD" >&2
|
|
216
|
+
echo "[ERROR] Please install Python 3.x or set python_cmd in config.yaml" >&2
|
|
217
|
+
echo "[ERROR] Examples: python_cmd: \"python\" or python_cmd: \"C:\\Python39\\python.exe\"" >&2
|
|
198
218
|
((errors++))
|
|
219
|
+
else
|
|
220
|
+
# Verify Python version is 3.x
|
|
221
|
+
if ! "$PYTHON_CMD" -c "import sys; sys.exit(0 if sys.version_info[0] >= 3 else 1)" 2>/dev/null; then
|
|
222
|
+
echo "[ERROR] Python 3.x required, but $PYTHON_CMD appears to be Python 2" >&2
|
|
223
|
+
echo "[ERROR] Please set python_cmd in config.yaml to point to Python 3" >&2
|
|
224
|
+
((errors++))
|
|
225
|
+
fi
|
|
199
226
|
fi
|
|
200
227
|
|
|
201
228
|
return $errors
|