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.
@@ -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 "[INFO] No pending candidates found"
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" "$output_file"
148
- echo "[WORKER-$$] Copied parent to: $output_file"
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 $output_file to implement this specific change: $description
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 "Claude AI usage limit reached"; then
194
- echo "[ERROR] Claude API rate limit reached" >&2
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 "[ERROR] Claude failed to mutate algorithm" >&2
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 (( $(echo "$score == 0" | bc -l) )); then
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 (( $(echo "$score == 0" | bc -l) )); then
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 (( $(echo "$score == 0" | bc -l) )); then
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 "[INFO] Loading configuration from: $config_file"
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 "[INFO] Using evolution directory from config path: $EVOLUTION_DIR"
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
- local csv_file="${EVOLUTION_DIR:-evolution}/evolution.csv"
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
- local csv_file="${EVOLUTION_DIR:-evolution}/evolution.csv"
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
- local csv_file="${FULL_CSV_PATH:-${EVOLUTION_DIR:-evolution}/evolution.csv}"
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
- local csv_file="${FULL_CSV_PATH:-${EVOLUTION_DIR:-evolution}/evolution.csv}"
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 > 0 else 'failed'}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.3.39",
3
+ "version": "1.3.41",
4
4
  "bin": {
5
5
  "claude-evolve": "./bin/claude-evolve",
6
6
  "claude-evolve-main": "./bin/claude-evolve-main",
@@ -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
- # Working directory for evolution files
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 evolution_dir)
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 evolution_dir)
16
- # Leave empty to use evolution_dir directly
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