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
package/bin/claude-evolve-worker
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
set -e
|
|
6
6
|
|
|
7
|
+
# Track temp file for cleanup
|
|
8
|
+
temp_file=""
|
|
9
|
+
|
|
10
|
+
# Cleanup function for temp files
|
|
11
|
+
cleanup_temp() {
|
|
12
|
+
if [[ -n "$temp_file" && -f "$temp_file" ]]; then
|
|
13
|
+
rm -f "$temp_file"
|
|
14
|
+
echo "[WORKER-$$] Cleaned up temp file: $temp_file" >&2
|
|
15
|
+
fi
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# Set trap to clean up temp files on exit
|
|
19
|
+
trap cleanup_temp EXIT INT TERM
|
|
20
|
+
|
|
7
21
|
# Load configuration
|
|
8
22
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
23
|
# shellcheck source=../lib/config.sh
|
|
@@ -47,7 +61,7 @@ done
|
|
|
47
61
|
if [[ -z $candidate_id ]]; then
|
|
48
62
|
candidate_id=$(find_next_pending_with_lock)
|
|
49
63
|
if [[ -z $candidate_id ]]; then
|
|
50
|
-
echo "[
|
|
64
|
+
echo "[DEBUG] No pending candidates found" >&2
|
|
51
65
|
exit 0
|
|
52
66
|
fi
|
|
53
67
|
else
|
|
@@ -130,27 +144,35 @@ else
|
|
|
130
144
|
fi
|
|
131
145
|
fi
|
|
132
146
|
|
|
133
|
-
# Generate output file
|
|
147
|
+
# Generate output file path
|
|
134
148
|
if [[ $id =~ ^[0-9]+$ ]]; then
|
|
135
149
|
output_file="$FULL_OUTPUT_DIR/evolution_id${id}.py"
|
|
136
150
|
else
|
|
137
151
|
output_file="$FULL_OUTPUT_DIR/evolution_${id}.py"
|
|
138
152
|
fi
|
|
139
153
|
|
|
154
|
+
# Use temp file for mutations to avoid partial/failed edits
|
|
155
|
+
temp_file="${output_file}.tmp$$"
|
|
156
|
+
|
|
140
157
|
# Check if processing should be skipped using common logic
|
|
141
158
|
eval "$("$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_processor.py" "$id" "$based_on_id" "$FULL_OUTPUT_DIR" "$ROOT_DIR" "$parent_file" "$output_file")"
|
|
142
159
|
|
|
143
|
-
# Handle copy operation
|
|
160
|
+
# Handle copy operation to temp file
|
|
144
161
|
if [[ "$skip_copy" == "True" ]]; then
|
|
145
162
|
echo "[WORKER-$$] ⚠️ Skipping copy - $reason"
|
|
146
163
|
else
|
|
147
|
-
cp "$parent_file" "$
|
|
148
|
-
echo "[WORKER-$$] Copied parent to: $
|
|
164
|
+
cp "$parent_file" "$temp_file"
|
|
165
|
+
echo "[WORKER-$$] Copied parent to temp file: $temp_file"
|
|
149
166
|
fi
|
|
150
167
|
|
|
151
168
|
# Handle Claude mutation based on skip flags
|
|
152
169
|
if [[ "$skip_claude" == "True" ]]; then
|
|
153
170
|
echo "[WORKER-$$] ⚠️ Skipping Claude processing - $reason"
|
|
171
|
+
# If we have a temp file but are skipping Claude, move it to final location
|
|
172
|
+
if [[ -f "$temp_file" ]]; then
|
|
173
|
+
mv "$temp_file" "$output_file"
|
|
174
|
+
echo "[WORKER-$$] Moved temp file to final location (no Claude processing)"
|
|
175
|
+
fi
|
|
154
176
|
else
|
|
155
177
|
# Check for claude CLI
|
|
156
178
|
claude_cmd="${CLAUDE_CMD:-claude}"
|
|
@@ -164,7 +186,7 @@ else
|
|
|
164
186
|
echo "[WORKER-$$] Using Claude $CLAUDE_MODEL for mutation"
|
|
165
187
|
|
|
166
188
|
# Create mutation prompt
|
|
167
|
-
prompt="Edit the file $
|
|
189
|
+
prompt="Edit the file $temp_file to implement this specific change: $description
|
|
168
190
|
|
|
169
191
|
Requirements:
|
|
170
192
|
- Edit the file directly (don't just provide comments or suggestions)
|
|
@@ -189,19 +211,58 @@ The file currently contains the parent algorithm. Modify it according to the des
|
|
|
189
211
|
claude_output=$(echo "$prompt" | "$claude_cmd" --dangerously-skip-permissions --model $CLAUDE_MODEL -p 2>&1 | tee -a "$LOGFILE")
|
|
190
212
|
claude_exit_code=${PIPESTATUS[1]}
|
|
191
213
|
|
|
192
|
-
# Check for rate limit
|
|
193
|
-
if echo "$claude_output" | grep -q "
|
|
194
|
-
echo "
|
|
214
|
+
# Check for rate limit (multiple possible messages)
|
|
215
|
+
if echo "$claude_output" | grep -q -E "(usage limit|rate limit|limit reached|too many requests)"; then
|
|
216
|
+
echo "⚠️ Claude API rate limit reached" >&2
|
|
217
|
+
echo "⚠️ Claude output:" >&2
|
|
218
|
+
echo "$claude_output" >&2
|
|
219
|
+
# Clean up the temp file
|
|
220
|
+
if [[ -f "$temp_file" ]]; then
|
|
221
|
+
rm "$temp_file"
|
|
222
|
+
echo "[WORKER-$$] Cleaned up temp file due to rate limit" >&2
|
|
223
|
+
fi
|
|
195
224
|
# Reset to pending so it can be retried later
|
|
196
225
|
update_csv_row_with_lock "$candidate_id" "status" "pending"
|
|
197
226
|
exit 2 # Special exit code for rate limit
|
|
198
227
|
fi
|
|
199
228
|
|
|
200
229
|
if [[ $claude_exit_code -ne 0 ]]; then
|
|
201
|
-
echo "
|
|
230
|
+
echo "⚠️ Claude failed to mutate algorithm (exit code: $claude_exit_code)" >&2
|
|
231
|
+
echo "⚠️ Claude output:" >&2
|
|
232
|
+
echo "$claude_output" >&2
|
|
233
|
+
# Clean up the temp file
|
|
234
|
+
if [[ -f "$temp_file" ]]; then
|
|
235
|
+
rm "$temp_file"
|
|
236
|
+
echo "[WORKER-$$] Cleaned up temp file due to Claude failure" >&2
|
|
237
|
+
fi
|
|
202
238
|
update_csv_row_with_lock "$candidate_id" "status" "failed"
|
|
203
239
|
exit 1
|
|
204
240
|
fi
|
|
241
|
+
|
|
242
|
+
# Verify that Claude actually modified the file
|
|
243
|
+
if [[ -f "$temp_file" && -f "$parent_file" ]]; then
|
|
244
|
+
if cmp -s "$temp_file" "$parent_file"; then
|
|
245
|
+
echo "⚠️ Unchanged algorithm detected - Claude didn't modify the file" >&2
|
|
246
|
+
echo "⚠️ Description was: $description" >&2
|
|
247
|
+
echo "⚠️ Claude's response:" >&2
|
|
248
|
+
echo "$claude_output" >&2
|
|
249
|
+
|
|
250
|
+
# Clean up temp file and mark as failed
|
|
251
|
+
rm "$temp_file"
|
|
252
|
+
update_csv_row_with_lock "$candidate_id" "status" "failed"
|
|
253
|
+
exit 1
|
|
254
|
+
else
|
|
255
|
+
# Changes were made - move temp file to final location
|
|
256
|
+
mv "$temp_file" "$output_file"
|
|
257
|
+
echo "[WORKER-$$] Changes detected - moved to: $output_file"
|
|
258
|
+
fi
|
|
259
|
+
else
|
|
260
|
+
# If we can't compare, assume it's okay and move the file
|
|
261
|
+
if [[ -f "$temp_file" ]]; then
|
|
262
|
+
mv "$temp_file" "$output_file"
|
|
263
|
+
echo "[WORKER-$$] Moved temp file to: $output_file"
|
|
264
|
+
fi
|
|
265
|
+
fi
|
|
205
266
|
fi
|
|
206
267
|
|
|
207
268
|
# Run evaluator
|
|
@@ -244,7 +305,7 @@ if [[ $eval_exit_code -eq 0 ]]; then
|
|
|
244
305
|
# First, check if output is just a plain number
|
|
245
306
|
if [[ $eval_output =~ ^[[:space:]]*-?[0-9]+\.?[0-9]*[[:space:]]*$ ]]; then
|
|
246
307
|
score=$(echo "$eval_output" | tr -d ' ')
|
|
247
|
-
if
|
|
308
|
+
if [[ $(echo "$score == 0" | bc -l) == "1" ]]; then
|
|
248
309
|
update_csv_row_with_lock "$candidate_id" "status" "failed"
|
|
249
310
|
update_csv_row_with_lock "$candidate_id" "performance" "$score"
|
|
250
311
|
echo "[WORKER-$$] ✗ Evaluation failed with score 0"
|
|
@@ -286,7 +347,7 @@ if [[ $eval_exit_code -eq 0 ]]; then
|
|
|
286
347
|
# Fallback: Try simple grep for score/performance fields
|
|
287
348
|
if score=$(echo "$eval_output" | grep -o '"score"[[:space:]]*:[[:space:]]*[0-9.]*' | cut -d: -f2 | tr -d ' '); then
|
|
288
349
|
if [[ -n $score ]]; then
|
|
289
|
-
if
|
|
350
|
+
if [[ $(echo "$score == 0" | bc -l) == "1" ]]; then
|
|
290
351
|
update_csv_row_with_lock "$candidate_id" "status" "failed"
|
|
291
352
|
update_csv_row_with_lock "$candidate_id" "performance" "$score"
|
|
292
353
|
echo "[WORKER-$$] ✗ Evaluation failed with score 0"
|
|
@@ -303,7 +364,7 @@ if [[ $eval_exit_code -eq 0 ]]; then
|
|
|
303
364
|
# Try "performance" field
|
|
304
365
|
if score=$(echo "$eval_output" | grep -o '"performance"[[:space:]]*:[[:space:]]*[0-9.]*' | cut -d: -f2 | tr -d ' '); then
|
|
305
366
|
if [[ -n $score ]]; then
|
|
306
|
-
if
|
|
367
|
+
if [[ $(echo "$score == 0" | bc -l) == "1" ]]; then
|
|
307
368
|
update_csv_row_with_lock "$candidate_id" "status" "failed"
|
|
308
369
|
update_csv_row_with_lock "$candidate_id" "performance" "$score"
|
|
309
370
|
echo "[WORKER-$$] ✗ Evaluation failed with score 0"
|
|
@@ -319,6 +380,8 @@ if [[ $eval_exit_code -eq 0 ]]; then
|
|
|
319
380
|
|
|
320
381
|
echo "[ERROR] No score found in evaluator output" >&2
|
|
321
382
|
echo "[ERROR] Expected: plain number (e.g., 1.23) or JSON with 'score' or 'performance' field" >&2
|
|
383
|
+
echo "[ERROR] Actual evaluator output was:" >&2
|
|
384
|
+
echo "$eval_output" >&2
|
|
322
385
|
update_csv_row_with_lock "$candidate_id" "status" "failed"
|
|
323
386
|
exit 1
|
|
324
387
|
else
|
package/lib/config.sh
CHANGED
|
@@ -78,7 +78,7 @@ load_config() {
|
|
|
78
78
|
|
|
79
79
|
# Load config if found
|
|
80
80
|
if [[ -f "$config_file" ]]; then
|
|
81
|
-
echo "[
|
|
81
|
+
echo "[DEBUG] Loading configuration from: $config_file" >&2
|
|
82
82
|
# Simple YAML parsing for key: value pairs and nested structures
|
|
83
83
|
local in_ideation_section=false
|
|
84
84
|
local in_parallel_section=false
|
|
@@ -143,7 +143,6 @@ load_config() {
|
|
|
143
143
|
else
|
|
144
144
|
# Handle top-level keys
|
|
145
145
|
case $key in
|
|
146
|
-
evolution_dir) EVOLUTION_DIR="$value" ;;
|
|
147
146
|
algorithm_file) ALGORITHM_FILE="$value" ;;
|
|
148
147
|
evaluator_file) EVALUATOR_FILE="$value" ;;
|
|
149
148
|
brief_file) BRIEF_FILE="$value" ;;
|
|
@@ -152,6 +151,9 @@ load_config() {
|
|
|
152
151
|
parent_selection) PARENT_SELECTION="$value" ;;
|
|
153
152
|
python_cmd) PYTHON_CMD="$value" ;;
|
|
154
153
|
auto_ideate) AUTO_IDEATE="$value" ;;
|
|
154
|
+
evolution_dir)
|
|
155
|
+
echo "[WARN] evolution_dir in config is ignored - automatically inferred from config file location" >&2
|
|
156
|
+
;;
|
|
155
157
|
esac
|
|
156
158
|
fi
|
|
157
159
|
done < "$config_file"
|
|
@@ -163,7 +165,7 @@ load_config() {
|
|
|
163
165
|
local config_dir=$(dirname "$config_file")
|
|
164
166
|
if [[ "$config_dir" != "." && "$config_dir" != "" ]]; then
|
|
165
167
|
EVOLUTION_DIR="$config_dir"
|
|
166
|
-
echo "[
|
|
168
|
+
echo "[DEBUG] Using evolution directory from config path: $EVOLUTION_DIR" >&2
|
|
167
169
|
fi
|
|
168
170
|
fi
|
|
169
171
|
|
package/lib/csv-lock.sh
CHANGED
|
@@ -110,7 +110,13 @@ release_csv_lock() {
|
|
|
110
110
|
# Usage: read_csv_with_lock <variable_name>
|
|
111
111
|
read_csv_with_lock() {
|
|
112
112
|
local var_name="$1"
|
|
113
|
-
|
|
113
|
+
|
|
114
|
+
# Ensure we have the full CSV path set
|
|
115
|
+
if [[ -z "$FULL_CSV_PATH" ]]; then
|
|
116
|
+
echo "[ERROR] FULL_CSV_PATH not set in read_csv_with_lock" >&2
|
|
117
|
+
return 1
|
|
118
|
+
fi
|
|
119
|
+
local csv_file="$FULL_CSV_PATH"
|
|
114
120
|
|
|
115
121
|
if ! acquire_csv_lock; then
|
|
116
122
|
return 1
|
|
@@ -130,7 +136,12 @@ read_csv_with_lock() {
|
|
|
130
136
|
# Write CSV with lock
|
|
131
137
|
# Usage: echo "content" | write_csv_with_lock
|
|
132
138
|
write_csv_with_lock() {
|
|
133
|
-
|
|
139
|
+
# Ensure we have the full CSV path set
|
|
140
|
+
if [[ -z "$FULL_CSV_PATH" ]]; then
|
|
141
|
+
echo "[ERROR] FULL_CSV_PATH not set in write_csv_with_lock" >&2
|
|
142
|
+
return 1
|
|
143
|
+
fi
|
|
144
|
+
local csv_file="$FULL_CSV_PATH"
|
|
134
145
|
local temp_file="${csv_file}.tmp.$$"
|
|
135
146
|
|
|
136
147
|
if ! acquire_csv_lock; then
|
|
@@ -153,7 +164,13 @@ update_csv_row_with_lock() {
|
|
|
153
164
|
local target_id="$1"
|
|
154
165
|
local field="$2"
|
|
155
166
|
local value="$3"
|
|
156
|
-
|
|
167
|
+
|
|
168
|
+
# Ensure we have the full CSV path set
|
|
169
|
+
if [[ -z "$FULL_CSV_PATH" ]]; then
|
|
170
|
+
echo "[ERROR] FULL_CSV_PATH not set in update_csv_row_with_lock" >&2
|
|
171
|
+
return 1
|
|
172
|
+
fi
|
|
173
|
+
local csv_file="$FULL_CSV_PATH"
|
|
157
174
|
|
|
158
175
|
if ! acquire_csv_lock; then
|
|
159
176
|
return 1
|
|
@@ -202,7 +219,12 @@ with open('${csv_file}.tmp', 'w', newline='') as f:
|
|
|
202
219
|
# Find next pending candidate with lock
|
|
203
220
|
# Usage: next_pending=$(find_next_pending_with_lock)
|
|
204
221
|
find_next_pending_with_lock() {
|
|
205
|
-
|
|
222
|
+
# Ensure we have the full CSV path set
|
|
223
|
+
if [[ -z "$FULL_CSV_PATH" ]]; then
|
|
224
|
+
echo "[ERROR] FULL_CSV_PATH not set in find_next_pending_with_lock" >&2
|
|
225
|
+
return 1
|
|
226
|
+
fi
|
|
227
|
+
local csv_file="$FULL_CSV_PATH"
|
|
206
228
|
|
|
207
229
|
if ! acquire_csv_lock; then
|
|
208
230
|
return 1
|
package/lib/csv_helper.py
CHANGED
|
@@ -91,7 +91,7 @@ def main():
|
|
|
91
91
|
performance = data.get('performance') or data.get('score', 0)
|
|
92
92
|
|
|
93
93
|
# Build fields to update
|
|
94
|
-
fields = {'performance': performance, 'status': 'complete' if performance
|
|
94
|
+
fields = {'performance': performance, 'status': 'complete' if performance != 0 else 'failed'}
|
|
95
95
|
|
|
96
96
|
# Add all other fields from the JSON
|
|
97
97
|
for key, value in data.items():
|
package/package.json
CHANGED
package/templates/config.yaml
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# claude-evolve configuration file
|
|
2
2
|
# This file defines paths and settings for the evolution process
|
|
3
|
+
#
|
|
4
|
+
# NOTE: The evolution directory is automatically inferred from this config file's location.
|
|
5
|
+
# For example, if this file is at /path/to/my-experiment/config.yaml,
|
|
6
|
+
# then the evolution directory will be /path/to/my-experiment/
|
|
3
7
|
|
|
4
|
-
#
|
|
5
|
-
evolution_dir: "evolution"
|
|
6
|
-
|
|
7
|
-
# Algorithm and evaluator file paths (relative to evolution_dir)
|
|
8
|
+
# Algorithm and evaluator file paths (relative to evolution directory)
|
|
8
9
|
algorithm_file: "algorithm.py"
|
|
9
10
|
evaluator_file: "evaluator.py"
|
|
10
11
|
brief_file: "BRIEF.md"
|
|
11
12
|
|
|
12
|
-
# CSV file for tracking evolution (relative to
|
|
13
|
+
# CSV file for tracking evolution (relative to evolution directory)
|
|
13
14
|
evolution_csv: "evolution.csv"
|
|
14
15
|
|
|
15
|
-
# Output directory for generated algorithms (relative to
|
|
16
|
-
# Leave empty to use
|
|
16
|
+
# Output directory for generated algorithms (relative to evolution directory)
|
|
17
|
+
# Leave empty to use evolution directory directly
|
|
17
18
|
output_dir: ""
|
|
18
19
|
|
|
19
20
|
# Parent algorithm selection strategy
|