claude-evolve 1.8.19 → 1.8.21

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.
@@ -36,6 +36,24 @@ if [[ -n "${FULL_EVOLUTION_DIR:-}" ]]; then
36
36
  echo "[IDEATE-$$] Logging to: $LOG_FILE"
37
37
  fi
38
38
 
39
+ # AIDEV-NOTE: Directory restoration helper to prevent working directory corruption
40
+ # Critical for preventing the bug where Ctrl+C during ideation leaves the shell in wrong directory
41
+ # Helper to safely change directory with automatic restoration
42
+ safe_pushd() {
43
+ SAFE_PUSHD_ORIGINAL_PWD=$(pwd)
44
+ cd "$1" || return 1
45
+ # Set trap to restore directory on any exit, error, or interrupt
46
+ trap 'cd "$SAFE_PUSHD_ORIGINAL_PWD" 2>/dev/null || true' EXIT INT TERM ERR
47
+ }
48
+
49
+ safe_popd() {
50
+ if [[ -n "${SAFE_PUSHD_ORIGINAL_PWD:-}" ]]; then
51
+ cd "$SAFE_PUSHD_ORIGINAL_PWD" || true
52
+ # Clear the trap since we're explicitly returning
53
+ trap - EXIT INT TERM ERR
54
+ fi
55
+ }
56
+
39
57
  # Helper function to call AI with limit check
40
58
  call_ai_with_limit_check() {
41
59
  local prompt="$1"
@@ -1104,12 +1122,15 @@ CRITICAL: Do NOT use any git commands (git add, git commit, git reset, etc.). On
1104
1122
 
1105
1123
  # Debug prompt size and context (removed - no longer needed)
1106
1124
 
1107
- # Change to evolution directory so AI can access files
1108
- local original_pwd=$(pwd)
1109
- cd "$FULL_EVOLUTION_DIR"
1110
-
1125
+ # Change to evolution directory so AI can access files (with safe restoration on interrupt)
1126
+ safe_pushd "$FULL_EVOLUTION_DIR" || {
1127
+ echo "[ERROR] Failed to change to evolution directory" >&2
1128
+ rm -f "$temp_csv"
1129
+ return 1
1130
+ }
1131
+
1111
1132
  # Debug: Show files in evolution directory
1112
-
1133
+
1113
1134
  # Get AI to directly edit the CSV file
1114
1135
  local ai_response
1115
1136
  local stderr_file="stderr-$$.txt"
@@ -1117,14 +1138,14 @@ CRITICAL: Do NOT use any git commands (git add, git commit, git reset, etc.). On
1117
1138
  if ! ai_response=$(call_ai_for_ideation "$prompt" "$CURRENT_GENERATION" "$count" "$temp_csv_basename"); then
1118
1139
  echo "[ERROR] All AI models failed to generate novel ideas" >&2
1119
1140
  cat "$stderr_file" >&2
1120
- cd "$original_pwd"
1141
+ safe_popd
1121
1142
  rm -f "$temp_csv" "$stderr_file"
1122
1143
  return 1
1123
1144
  fi
1124
1145
  rm -f "$stderr_file"
1125
-
1146
+
1126
1147
  # Restore working directory
1127
- cd "$original_pwd"
1148
+ safe_popd
1128
1149
 
1129
1150
  # Validate that the CSV file was actually modified with correct IDs
1130
1151
  # Pass original_csv_lines to prevent race conditions
@@ -1255,10 +1276,13 @@ CRITICAL INSTRUCTIONS:
1255
1276
  8. CRITICAL: When editing, preserve the CSV formatting with proper quoting
1256
1277
  9. DO NOT use any git commands (git add, git commit, git reset, etc.). Only modify the file directly."
1257
1278
 
1258
- # Change to evolution directory so AI can access files
1259
- local original_pwd=$(pwd)
1260
- cd "$FULL_EVOLUTION_DIR"
1261
-
1279
+ # Change to evolution directory so AI can access files (with safe restoration on interrupt)
1280
+ safe_pushd "$FULL_EVOLUTION_DIR" || {
1281
+ echo "[ERROR] Failed to change to evolution directory" >&2
1282
+ rm -f "$temp_csv"
1283
+ return 1
1284
+ }
1285
+
1262
1286
  # Get AI to directly edit the CSV file
1263
1287
  local ai_response
1264
1288
  local stderr_file="stderr-$$.txt"
@@ -1266,14 +1290,14 @@ CRITICAL INSTRUCTIONS:
1266
1290
  if ! ai_response=$(call_ai_for_ideation "$prompt" "$CURRENT_GENERATION" "$count" "$temp_csv_basename"); then
1267
1291
  echo "[ERROR] All AI models failed to generate hill climbing ideas" >&2
1268
1292
  cat "$stderr_file" >&2
1269
- cd "$original_pwd"
1293
+ safe_popd
1270
1294
  rm -f "$temp_csv" "$stderr_file"
1271
1295
  return 1
1272
1296
  fi
1273
1297
  rm -f "$stderr_file"
1274
-
1298
+
1275
1299
  # Restore working directory
1276
- cd "$original_pwd"
1300
+ safe_popd
1277
1301
 
1278
1302
  # Validate that the CSV file was actually modified with correct IDs
1279
1303
  # Pass original_csv_lines to prevent race conditions
@@ -1394,10 +1418,13 @@ CRITICAL INSTRUCTIONS:
1394
1418
  8. CRITICAL: When editing, preserve the CSV formatting with proper quoting
1395
1419
  9. DO NOT use any git commands (git add, git commit, git reset, etc.). Only modify the file directly."
1396
1420
 
1397
- # Change to evolution directory so AI can access files
1398
- local original_pwd=$(pwd)
1399
- cd "$FULL_EVOLUTION_DIR"
1400
-
1421
+ # Change to evolution directory so AI can access files (with safe restoration on interrupt)
1422
+ safe_pushd "$FULL_EVOLUTION_DIR" || {
1423
+ echo "[ERROR] Failed to change to evolution directory" >&2
1424
+ rm -f "$temp_csv"
1425
+ return 1
1426
+ }
1427
+
1401
1428
  # Get AI to directly edit the CSV file
1402
1429
  local ai_response
1403
1430
  local stderr_file="stderr-$$.txt"
@@ -1405,14 +1432,14 @@ CRITICAL INSTRUCTIONS:
1405
1432
  if ! ai_response=$(call_ai_for_ideation "$prompt" "$CURRENT_GENERATION" "$count" "$temp_csv_basename"); then
1406
1433
  echo "[ERROR] All AI models failed to generate structural mutation ideas" >&2
1407
1434
  cat "$stderr_file" >&2
1408
- cd "$original_pwd"
1435
+ safe_popd
1409
1436
  rm -f "$temp_csv" "$stderr_file"
1410
1437
  return 1
1411
1438
  fi
1412
1439
  rm -f "$stderr_file"
1413
-
1440
+
1414
1441
  # Restore working directory
1415
- cd "$original_pwd"
1442
+ safe_popd
1416
1443
 
1417
1444
  # Validate that the CSV file was actually modified with correct IDs
1418
1445
  # Pass original_csv_lines to prevent race conditions
@@ -1533,10 +1560,13 @@ CRITICAL INSTRUCTIONS:
1533
1560
  8. CRITICAL: When editing, preserve the CSV formatting with proper quoting
1534
1561
  9. DO NOT use any git commands (git add, git commit, git reset, etc.). Only modify the file directly."
1535
1562
 
1536
- # Change to evolution directory so AI can access files
1537
- local original_pwd=$(pwd)
1538
- cd "$FULL_EVOLUTION_DIR"
1539
-
1563
+ # Change to evolution directory so AI can access files (with safe restoration on interrupt)
1564
+ safe_pushd "$FULL_EVOLUTION_DIR" || {
1565
+ echo "[ERROR] Failed to change to evolution directory" >&2
1566
+ rm -f "$temp_csv"
1567
+ return 1
1568
+ }
1569
+
1540
1570
  # Get AI to directly edit the CSV file
1541
1571
  local ai_response
1542
1572
  local stderr_file="stderr-$$.txt"
@@ -1544,14 +1574,14 @@ CRITICAL INSTRUCTIONS:
1544
1574
  if ! ai_response=$(call_ai_for_ideation "$prompt" "$CURRENT_GENERATION" "$count" "$temp_csv_basename"); then
1545
1575
  echo "[ERROR] All AI models failed to generate crossover hybrid ideas" >&2
1546
1576
  cat "$stderr_file" >&2
1547
- cd "$original_pwd"
1577
+ safe_popd
1548
1578
  rm -f "$temp_csv" "$stderr_file"
1549
1579
  return 1
1550
1580
  fi
1551
1581
  rm -f "$stderr_file"
1552
-
1582
+
1553
1583
  # Restore working directory
1554
- cd "$original_pwd"
1584
+ safe_popd
1555
1585
 
1556
1586
  # Validate that the CSV file was actually modified with correct IDs
1557
1587
  # Pass original_csv_lines to prevent race conditions
@@ -1650,24 +1680,27 @@ IMPORTANT: You must APPEND new rows to the existing CSV file. DO NOT replace the
1650
1680
  CRITICAL: You must use your file editing tools (Edit/MultiEdit) to modify the CSV file. DO NOT return CSV text - use your tools to edit the file directly.
1651
1681
  CRITICAL: Do NOT use any git commands (git add, git commit, git reset, etc.). Only modify the file directly."
1652
1682
 
1653
- # Change to evolution directory so AI can access files
1654
- local original_pwd=$(pwd)
1655
- cd "$FULL_EVOLUTION_DIR"
1656
-
1683
+ # Change to evolution directory so AI can access files (with safe restoration on interrupt)
1684
+ safe_pushd "$FULL_EVOLUTION_DIR" || {
1685
+ echo "[ERROR] Failed to change to evolution directory" >&2
1686
+ rm -f "$temp_csv"
1687
+ return 1
1688
+ }
1689
+
1657
1690
  # Get AI to directly edit the CSV file
1658
1691
  local ai_response
1659
1692
  local stderr_file="stderr-$$.txt"
1660
1693
  if ! ai_response=$(call_ai_for_ideation "$prompt" "$CURRENT_GENERATION" "$TOTAL_IDEAS" "$temp_csv_basename" 2>"$stderr_file"); then
1661
1694
  echo "[ERROR] All AI models failed to generate ideas" >&2
1662
1695
  cat "$stderr_file" >&2
1663
- cd "$original_pwd"
1696
+ safe_popd
1664
1697
  rm -f "$temp_csv" "$stderr_file"
1665
1698
  return 1
1666
1699
  fi
1667
1700
  rm -f "$stderr_file"
1668
-
1701
+
1669
1702
  # Restore working directory
1670
- cd "$original_pwd"
1703
+ safe_popd
1671
1704
 
1672
1705
  # Validate that the CSV file was actually modified
1673
1706
  # Pass original_csv_lines to prevent race conditions
@@ -646,8 +646,8 @@ except Exception as e:
646
646
  if [[ $pending_count -eq 0 && $active_workers -eq 0 ]]; then
647
647
  echo "[DISPATCHER] No pending candidates found."
648
648
 
649
- # Before blocking, do final check for stuck work (immediate, not periodic)
650
- echo "[DISPATCHER] Performing final verification for stuck candidates..."
649
+ # Before triggering ideation, do final check AND RESET stuck work (immediate, not periodic)
650
+ echo "[DISPATCHER] Performing final verification and cleanup for stuck candidates..."
651
651
  stuck_work_count=$("$PYTHON_CMD" -c "
652
652
  import sys
653
653
  sys.path.insert(0, '$SCRIPT_DIR/..')
@@ -655,18 +655,25 @@ from lib.evolution_csv import EvolutionCSV
655
655
 
656
656
  try:
657
657
  with EvolutionCSV('$FULL_CSV_PATH') as csv:
658
+ # First count stuck candidates
658
659
  stuck = csv.count_stuck_candidates()
660
+
661
+ # If any found, reset them immediately
662
+ if stuck > 0:
663
+ print(f'[DISPATCHER] Found {stuck} stuck candidates, resetting to pending...', file=sys.stderr)
664
+ reset_count = csv.reset_stuck_candidates()
665
+ print(f'[DISPATCHER] Reset {reset_count} stuck candidates to pending', file=sys.stderr)
666
+
659
667
  print(stuck)
660
668
  except Exception as e:
661
- print(f'[ERROR] Failed to count stuck candidates: {e}', file=sys.stderr)
669
+ print(f'[ERROR] Failed to check/reset stuck candidates: {e}', file=sys.stderr)
662
670
  print('0') # Default to 0 on error
663
671
  " 2>/dev/null || echo "0")
664
672
 
665
673
  if [[ $stuck_work_count -gt 0 ]]; then
666
- echo "[DISPATCHER] Found $stuck_work_count stuck candidates - running cleanup cycle..."
667
- # Force immediate cleanup
668
- loop_iteration=$((last_stuck_check + 5))
669
- continue # Go back to top of loop to trigger cleanup
674
+ echo "[DISPATCHER] Reset $stuck_work_count stuck candidates to pending"
675
+ # Don't trigger ideation - go back to check pending count again
676
+ continue # Go back to top of loop to re-count pending
670
677
  fi
671
678
 
672
679
  # Check if auto ideation is enabled
@@ -18,6 +18,24 @@ if [[ -n "${FULL_EVOLUTION_DIR:-}" ]]; then
18
18
  echo "[WORKER-$$] Logging to: $LOG_FILE"
19
19
  fi
20
20
 
21
+ # AIDEV-NOTE: Directory restoration helper to prevent working directory corruption
22
+ # Critical for preventing the bug where Ctrl+C during worker leaves the shell in wrong directory
23
+ # Helper to safely change directory with automatic restoration
24
+ safe_pushd() {
25
+ SAFE_PUSHD_ORIGINAL_PWD=$(pwd)
26
+ cd "$1" || return 1
27
+ # Set trap to restore directory on any exit, error, or interrupt
28
+ trap 'cd "$SAFE_PUSHD_ORIGINAL_PWD" 2>/dev/null || true' EXIT INT TERM ERR
29
+ }
30
+
31
+ safe_popd() {
32
+ if [[ -n "${SAFE_PUSHD_ORIGINAL_PWD:-}" ]]; then
33
+ cd "$SAFE_PUSHD_ORIGINAL_PWD" || true
34
+ # Clear the trap since we're explicitly returning
35
+ trap - EXIT INT TERM ERR
36
+ fi
37
+ }
38
+
21
39
  # Track current candidate for cleanup
22
40
  CURRENT_CANDIDATE_ID=""
23
41
  TERMINATION_SIGNAL=""
@@ -343,23 +361,26 @@ This is especially important for models with smaller context windows (like GLM).
343
361
  CRITICAL: If you do not know how to implement what was asked for, or if the requested change is unclear or not feasible, you MUST refuse to make any changes. DO NOT modify the code if you are uncertain about the implementation. Simply respond that you cannot implement the requested change and explain why. It is better to refuse than to make incorrect or random changes.
344
362
 
345
363
  CRITICAL: Do NOT use any git commands (git add, git commit, git reset, etc.). Only modify the file directly."
346
-
364
+
347
365
  if [[ "$is_baseline" != "true" ]]; then
348
- # Change to evolution directory so AI can access files
349
- local original_pwd=$(pwd)
350
- cd "$FULL_EVOLUTION_DIR"
351
-
366
+ # Change to evolution directory so AI can access files (with safe restoration on interrupt)
367
+ safe_pushd "$FULL_EVOLUTION_DIR" || {
368
+ echo "[WORKER-$$] ERROR: Failed to change to evolution directory" >&2
369
+ rm -f "$target_file"
370
+ return 1
371
+ }
372
+
352
373
  # Try AI models with round-robin based on candidate ID
353
374
  if ! call_ai_for_evolution "$evolution_prompt" "$candidate_id"; then
354
375
  echo "[WORKER-$$] ERROR: All AI models failed to generate code - leaving as pending for retry" >&2
355
- cd "$original_pwd"
376
+ safe_popd
356
377
  rm -f "$target_file" # Clean up on failure
357
378
  # Return with special code to indicate AI failure (should remain pending)
358
379
  return 77
359
380
  fi
360
-
381
+
361
382
  # Restore working directory
362
- cd "$original_pwd"
383
+ safe_popd
363
384
 
364
385
  echo "[WORKER-$$] Evolution applied successfully"
365
386
 
package/lib/ai-cli.sh CHANGED
@@ -1,5 +1,13 @@
1
1
  #!/bin/bash
2
2
  # Centralized AI CLI invocation library for claude-evolve
3
+ #
4
+ # AIDEV-NOTE: All timeout commands use -k flag to ensure process termination
5
+ # The -k flag sends SIGKILL if the process doesn't respond to SIGTERM within
6
+ # the grace period (30 seconds). This prevents AI CLI processes from hanging
7
+ # indefinitely when they ignore the initial SIGTERM signal.
8
+ # Example: timeout -k 30 600 means:
9
+ # - Wait 600 seconds, then send SIGTERM
10
+ # - If still running after 30 more seconds, send SIGKILL (force kill)
3
11
 
4
12
  # Source config to get LLM_CLI array and model lists
5
13
  # This will be sourced after config.sh in the main scripts
@@ -19,12 +27,12 @@ call_ai_model_configured() {
19
27
  case "$model_name" in
20
28
  opus)
21
29
  local ai_output
22
- ai_output=$(timeout 300 claude --dangerously-skip-permissions --mcp-config '' --model opus -p "$prompt" 2>&1)
30
+ ai_output=$(timeout -k 30 300 claude --dangerously-skip-permissions --mcp-config '' --model opus -p "$prompt" 2>&1)
23
31
  local ai_exit_code=$?
24
32
  ;;
25
33
  sonnet)
26
34
  local ai_output
27
- ai_output=$(timeout 300 claude --dangerously-skip-permissions --mcp-config '' --model sonnet -p "$prompt" 2>&1)
35
+ ai_output=$(timeout -k 30 300 claude --dangerously-skip-permissions --mcp-config '' --model sonnet -p "$prompt" 2>&1)
28
36
  local ai_exit_code=$?
29
37
  ;;
30
38
  sonnet-think)
@@ -33,7 +41,7 @@ call_ai_model_configured() {
33
41
  local think_prompt="ultrathink
34
42
 
35
43
  $prompt"
36
- ai_output=$(timeout 600 claude --dangerously-skip-permissions --mcp-config '' --model sonnet -p "$think_prompt" 2>&1)
44
+ ai_output=$(timeout -k 30 600 claude --dangerously-skip-permissions --mcp-config '' --model sonnet -p "$think_prompt" 2>&1)
37
45
  local ai_exit_code=$?
38
46
  ;;
39
47
  opus-think)
@@ -42,97 +50,103 @@ $prompt"
42
50
  local think_prompt="ultrathink
43
51
 
44
52
  $prompt"
45
- ai_output=$(timeout 600 claude --dangerously-skip-permissions --mcp-config '' --model opus -p "$think_prompt" 2>&1)
53
+ ai_output=$(timeout -k 30 600 claude --dangerously-skip-permissions --mcp-config '' --model opus -p "$think_prompt" 2>&1)
46
54
  local ai_exit_code=$?
47
55
  ;;
48
56
  haiku)
49
57
  local ai_output
50
- ai_output=$(timeout 300 claude --dangerously-skip-permissions --mcp-config '' --model haiku -p "$prompt" 2>&1)
58
+ ai_output=$(timeout -k 30 300 claude --dangerously-skip-permissions --mcp-config '' --model haiku -p "$prompt" 2>&1)
51
59
  local ai_exit_code=$?
52
60
  ;;
53
61
  gpt5high)
54
62
  local ai_output
55
- ai_output=$(timeout 600 codex exec -m gpt-5 -c model_reasoning_effort="high" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
63
+ ai_output=$(timeout -k 30 600 codex exec -m gpt-5 -c model_reasoning_effort="high" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
56
64
  local ai_exit_code=$?
57
65
  ;;
58
66
  gpt5)
59
67
  local ai_output
60
- ai_output=$(timeout 600 codex exec -m gpt-5 --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
68
+ ai_output=$(timeout -k 30 600 codex exec -m gpt-5 --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
61
69
  local ai_exit_code=$?
62
70
  ;;
63
71
  o3high)
64
72
  local ai_output
65
- ai_output=$(timeout 600 codex exec -m o3-mini -c model_reasoning_effort="high" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
73
+ ai_output=$(timeout -k 30 600 codex exec -m o3-mini -c model_reasoning_effort="high" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
66
74
  local ai_exit_code=$?
67
75
  ;;
68
76
  gemini-pro)
69
77
  local ai_output
70
78
  # Gemini needs longer timeout as it streams output while working (20 minutes)
71
- ai_output=$(timeout 1800 gemini -y -m gemini-2.5-pro -p "$prompt" 2>&1)
79
+ ai_output=$(timeout -k 30 1800 gemini -y -m gemini-2.5-pro -p "$prompt" 2>&1)
72
80
  local ai_exit_code=$?
73
81
  ;;
74
82
  gemini-flash)
75
83
  local ai_output
76
84
  # Gemini needs longer timeout as it streams output while working (20 minutes)
77
- ai_output=$(timeout 1200 gemini -y -m gemini-2.5-flash -p "$prompt" 2>&1)
85
+ ai_output=$(timeout -k 30 1200 gemini -y -m gemini-2.5-flash -p "$prompt" 2>&1)
86
+ local ai_exit_code=$?
87
+ ;;
88
+ gemini-3-pro-preview)
89
+ local ai_output
90
+ # Gemini v3 Pro Preview via OpenRouter (30 minute timeout)
91
+ ai_output=$(timeout -k 30 1800 opencode -m openrouter/google/gemini-3-pro-preview run "$prompt" 2>&1)
78
92
  local ai_exit_code=$?
79
93
  ;;
80
94
  cursor-sonnet)
81
95
  local ai_output
82
- ai_output=$(timeout 600 cursor-agent sonnet-4.5 -p "$prompt" 2>&1)
96
+ ai_output=$(timeout -k 30 600 cursor-agent sonnet-4.5 -p "$prompt" 2>&1)
83
97
  local ai_exit_code=$?
84
98
  ;;
85
99
  cursor-opus)
86
100
  local ai_output
87
- ai_output=$(timeout 600 cursor-agent opus -p "$prompt" 2>&1)
101
+ ai_output=$(timeout -k 30 600 cursor-agent opus -p "$prompt" 2>&1)
88
102
  local ai_exit_code=$?
89
103
  ;;
90
104
  glm-openrouter)
91
105
  local ai_output
92
- ai_output=$(timeout 600 opencode -m openrouter/z-ai/glm-4.6 run "$prompt" 2>&1)
106
+ ai_output=$(timeout -k 30 600 opencode -m openrouter/z-ai/glm-4.6 run "$prompt" 2>&1)
93
107
  local ai_exit_code=$?
94
108
  ;;
95
109
  glm-zai)
96
110
  # GLM -- can be slow sometimes
97
111
  local ai_output
98
- ai_output=$(timeout 1800 opencode -m zai-coding-plan/glm-4.6 run "$prompt" 2>&1)
112
+ ai_output=$(timeout -k 30 1800 opencode -m zai-coding-plan/glm-4.6 run "$prompt" 2>&1)
99
113
  local ai_exit_code=$?
100
114
  ;;
101
115
  deepseek-openrouter)
102
116
  local ai_output
103
- ai_output=$(timeout 600 opencode -m openrouter/deepseek/deepseek-v3.1-terminus run "$prompt" 2>&1)
117
+ ai_output=$(timeout -k 30 600 opencode -m openrouter/deepseek/deepseek-v3.1-terminus run "$prompt" 2>&1)
104
118
  local ai_exit_code=$?
105
119
  ;;
106
120
  grok-code-fast-openrouter)
107
121
  local ai_output
108
- ai_output=$(timeout 600 opencode -m openrouter/x-ai/grok-code-fast-1 run "$prompt" 2>&1)
122
+ ai_output=$(timeout -k 30 600 opencode -m openrouter/x-ai/grok-code-fast-1 run "$prompt" 2>&1)
109
123
  local ai_exit_code=$?
110
124
  ;;
111
125
  grok-4-openrouter)
112
126
  local ai_output
113
- ai_output=$(timeout 600 opencode -m openrouter/x-ai/grok-4 run "$prompt" 2>&1)
127
+ ai_output=$(timeout -k 30 600 opencode -m openrouter/x-ai/grok-4 run "$prompt" 2>&1)
114
128
  local ai_exit_code=$?
115
129
  ;;
116
130
  opus-openrouter)
117
131
  local ai_output
118
- ai_output=$(timeout 600 opencode -m openrouter/anthropic/claude-opus-4.1 run "$prompt" 2>&1)
132
+ ai_output=$(timeout -k 30 600 opencode -m openrouter/anthropic/claude-opus-4.1 run "$prompt" 2>&1)
119
133
  local ai_exit_code=$?
120
134
  ;;
121
135
  kimi-k2-think-moonshot)
122
136
  local ai_output
123
- ai_output=$(timeout 600 opencode -m moonshotai/kimi-k2-thinking run "$prompt" 2>&1)
137
+ ai_output=$(timeout -k 30 600 opencode -m moonshotai/kimi-k2-thinking run "$prompt" 2>&1)
124
138
  local ai_exit_code=$?
125
139
  ;;
126
140
  codex-oss-local)
127
141
  # Codex-OSS via Codex CLI with Ollama backend
128
142
  local ai_output
129
- ai_output=$(timeout 2400 codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --oss "$prompt" 2>&1)
143
+ ai_output=$(timeout -k 30 2400 codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --oss "$prompt" 2>&1)
130
144
  local ai_exit_code=$?
131
145
  ;;
132
146
  deepseek-v3-llamacloud)
133
147
  # Deepseek via Codex CLI with Ollama cloud backend
134
148
  local ai_output
135
- ai_output=$(timeout 600 codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --oss -m deepseek-v3.1:671b-cloud "$prompt" 2>&1)
149
+ ai_output=$(timeout -k 30 600 codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --oss -m deepseek-v3.1:671b-cloud "$prompt" 2>&1)
136
150
  local ai_exit_code=$?
137
151
  ;;
138
152
  esac
package/lib/config.sh CHANGED
@@ -58,9 +58,9 @@ DEFAULT_MEMORY_LIMIT_MB=12288
58
58
  DEFAULT_WORKER_MAX_CANDIDATES=3
59
59
 
60
60
  # Default LLM CLI configuration
61
- DEFAULT_LLM_RUN="glm-zai glm-zai glm-zai glm-zai glm-zai codex-oss-local gemini-flash haiku"
61
+ DEFAULT_LLM_RUN="glm-zai glm-zai glm-zai glm-zai glm-zai codex-oss-local haiku"
62
62
  # Ideate: Commercial models for idea generation + local fallback
63
- DEFAULT_LLM_IDEATE="opus-openrouter kimi-k2-think-moonshot gemini-pro sonnet-think gpt5high grok-4-openrouter deepseek-openrouter glm-zai"
63
+ DEFAULT_LLM_IDEATE="opus-openrouter kimi-k2-think-moonshot gemini-3-pro-preview sonnet-think gpt5high grok-4-openrouter deepseek-openrouter glm-zai"
64
64
 
65
65
  # Load configuration from a YAML file and update variables
66
66
  _load_yaml_config() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.8.19",
3
+ "version": "1.8.21",
4
4
  "bin": {
5
5
  "claude-evolve": "./bin/claude-evolve",
6
6
  "claude-evolve-main": "./bin/claude-evolve-main",
@@ -1,337 +0,0 @@
1
- #!/bin/bash
2
- # Configuration loader for claude-evolve
3
-
4
- # Default configuration values
5
- DEFAULT_EVOLUTION_DIR="evolution"
6
- DEFAULT_ALGORITHM_FILE="algorithm.py"
7
- DEFAULT_EVALUATOR_FILE="evaluator.py"
8
- DEFAULT_BRIEF_FILE="BRIEF.md"
9
- DEFAULT_EVOLUTION_CSV="evolution.csv"
10
- DEFAULT_OUTPUT_DIR=""
11
- DEFAULT_PARENT_SELECTION="best"
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)"
31
-
32
- # Default ideation strategy values
33
- DEFAULT_TOTAL_IDEAS=15
34
- DEFAULT_NOVEL_EXPLORATION=3
35
- DEFAULT_HILL_CLIMBING=5
36
- DEFAULT_STRUCTURAL_MUTATION=3
37
- DEFAULT_CROSSOVER_HYBRID=4
38
- DEFAULT_NUM_ELITES=3
39
- DEFAULT_NUM_REVOLUTION=2 # Number of top novel candidates to include
40
-
41
- # Default parallel execution values
42
- DEFAULT_PARALLEL_ENABLED=false
43
- DEFAULT_MAX_WORKERS=4
44
- DEFAULT_LOCK_TIMEOUT=10
45
-
46
- # Default auto ideation value
47
- DEFAULT_AUTO_IDEATE=true
48
-
49
- # Default retry value
50
- DEFAULT_MAX_RETRIES=3
51
-
52
- # Default memory limit (in MB, 0 means no limit)
53
- # Set to reasonable limit for ML workloads - about half of available system RAM
54
- DEFAULT_MEMORY_LIMIT_MB=12288
55
-
56
- # Default LLM CLI configuration - use simple variables instead of arrays
57
- # Run: 100% local with qwen3 via Codex+Ollama (more reliable than aider)
58
- DEFAULT_LLM_RUN="codex-qwen3 codex-oss gemini-flash"
59
- # Ideate: Commercial models for idea generation + local fallback
60
- DEFAULT_LLM_IDEATE="gemini sonnet-think gpt5high glm grok-4 codex-qwen3 codex-oss"
61
-
62
- # Load configuration from a YAML file and update variables
63
- _load_yaml_config() {
64
- local config_file="$1"
65
- if [[ ! -f "$config_file" ]]; then
66
- return 0 # File does not exist, nothing to load
67
- fi
68
-
69
- echo "[DEBUG] Loading configuration from: $config_file" >&2
70
-
71
- local in_ideation_section=false
72
- local in_parallel_section=false
73
- local in_llm_cli_section=false
74
- local llm_cli_subsection=""
75
-
76
- while IFS='' read -r line; do
77
- [[ $line =~ ^[[:space:]]*# ]] || [[ -z $line ]] && continue
78
-
79
- if [[ ! $line =~ ^([^:]+):(.*)$ ]]; then
80
- continue
81
- fi
82
- local key="${BASH_REMATCH[1]}"
83
- local value="${BASH_REMATCH[2]}"
84
-
85
- local is_indented=false
86
- [[ $key =~ ^[[:space:]]+ ]] && is_indented=true
87
-
88
- key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
89
- value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
90
-
91
- if [[ "${DEBUG_CONFIG:-}" == "true" ]]; then
92
- echo "[CONFIG DEBUG] Before comment removal: key='$key' value='$value'" >&2
93
- fi
94
-
95
- value=$(echo "$value" | sed 's/[[:space:]]*#.*$//')
96
- value=$(echo "$value" | sed 's/^"//;s/"$//')
97
-
98
- if [[ $key == "ideation_strategies" ]]; then
99
- in_ideation_section=true
100
- in_parallel_section=false
101
- in_llm_cli_section=false
102
- continue
103
- elif [[ $key == "parallel" ]]; then
104
- in_parallel_section=true
105
- in_ideation_section=false
106
- in_llm_cli_section=false
107
- continue
108
- elif [[ $key == "llm_cli" ]]; then
109
- in_llm_cli_section=true
110
- in_ideation_section=false
111
- in_parallel_section=false
112
- llm_cli_subsection=""
113
- continue
114
- elif [[ $is_indented == false ]] && [[ $in_ideation_section == true || $in_parallel_section == true || $in_llm_cli_section == true ]]; then
115
- in_ideation_section=false
116
- in_parallel_section=false
117
- in_llm_cli_section=false
118
- llm_cli_subsection=""
119
- fi
120
-
121
- if [[ $in_ideation_section == true ]]; then
122
- case $key in
123
- total_ideas) TOTAL_IDEAS="$value" ;;
124
- novel_exploration) NOVEL_EXPLORATION="$value" ;;
125
- hill_climbing) HILL_CLIMBING="$value" ;;
126
- structural_mutation) STRUCTURAL_MUTATION="$value" ;;
127
- crossover_hybrid) CROSSOVER_HYBRID="$value" ;;
128
- num_elites) NUM_ELITES="$value" ;;
129
- num_revolution) NUM_REVOLUTION="$value" ;;
130
- esac
131
- elif [[ $in_parallel_section == true ]]; then
132
- case $key in
133
- enabled) PARALLEL_ENABLED="$value" ;;
134
- max_workers) MAX_WORKERS="$value" ;;
135
- lock_timeout) LOCK_TIMEOUT="$value" ;;
136
- esac
137
- elif [[ $in_llm_cli_section == true ]]; then
138
- if [[ $key == "run" || $key == "ideate" ]]; then
139
- case $key in
140
- run) LLM_RUN="$value" ;;
141
- ideate) LLM_IDEATE="$value" ;;
142
- esac
143
- else
144
- value=$(echo "$value" | sed "s/^'//;s/'$//")
145
- local var_key=$(echo "$key" | sed 's/-/_/g')
146
- if [[ "${DEBUG_CONFIG:-}" == "true" ]]; then
147
- echo "[CONFIG DEBUG] Setting LLM_CLI_${var_key} = '$value'" >&2
148
- fi
149
- eval "LLM_CLI_${var_key}=\"$value\""
150
- fi
151
- else
152
- case $key in
153
- algorithm_file) ALGORITHM_FILE="$value" ;;
154
- evaluator_file) EVALUATOR_FILE="$value" ;;
155
- brief_file) BRIEF_FILE="$value" ;;
156
- evolution_csv) EVOLUTION_CSV="$value" ;;
157
- output_dir) OUTPUT_DIR="$value" ;;
158
- parent_selection) PARENT_SELECTION="$value" ;;
159
- python_cmd) PYTHON_CMD="$value" ;;
160
- auto_ideate) AUTO_IDEATE="$value" ;;
161
- max_retries) MAX_RETRIES="$value" ;;
162
- memory_limit_mb) MEMORY_LIMIT_MB="$value" ;;
163
- evolution_dir):
164
- echo "[WARN] evolution_dir in config is ignored - automatically inferred from config file location" >&2
165
- ;;
166
- esac
167
- fi
168
- done < "$config_file"
169
- # Keep track of the last config file loaded to infer evolution_dir
170
- LAST_CONFIG_FILE_LOADED="$config_file"
171
- }
172
-
173
- load_config() {
174
- echo "[DEBUG] $1 at start of load_config: '$1'" >&2
175
- echo "[DEBUG] DEFAULT_EVOLUTION_DIR: $DEFAULT_EVOLUTION_DIR" >&2
176
- # Set defaults first
177
- EVOLUTION_DIR="$DEFAULT_EVOLUTION_DIR" # Initialize with default
178
- ALGORITHM_FILE="$DEFAULT_ALGORITHM_FILE"
179
- EVALUATOR_FILE="$DEFAULT_EVALUATOR_FILE"
180
- BRIEF_FILE="$DEFAULT_BRIEF_FILE"
181
- EVOLUTION_CSV="$DEFAULT_EVOLUTION_CSV"
182
- OUTPUT_DIR="$DEFAULT_OUTPUT_DIR"
183
- PARENT_SELECTION="$DEFAULT_PARENT_SELECTION"
184
- PYTHON_CMD="$DEFAULT_PYTHON_CMD"
185
-
186
- # Determine EVOLUTION_DIR based on specified logic, overriding default if found
187
- if [[ -n "$CLAUDE_EVOLVE_WORKING_DIR" ]]; then
188
- echo "[DEBUG] EVOLUTION_DIR set by CLAUDE_EVOLVE_WORKING_DIR: $CLAUDE_EVOLVE_WORKING_DIR" >&2
189
- EVOLUTION_DIR="$CLAUDE_EVOLVE_WORKING_DIR"
190
- elif [[ -f "evolution/evolution.csv" ]]; then
191
- echo "[DEBUG] EVOLUTION_DIR set by evolution/evolution.csv: evolution" >&2
192
- EVOLUTION_DIR="evolution"
193
- elif [[ -f "./evolution.csv" ]]; then
194
- echo "[DEBUG] EVOLUTION_DIR set by ./evolution.csv: ." >&2
195
- EVOLUTION_DIR="."
196
- else
197
- echo "[DEBUG] EVOLUTION_DIR defaulting to: $DEFAULT_EVOLUTION_DIR" >&2
198
- fi
199
- echo "[DEBUG] EVOLUTION_DIR after initial determination: $EVOLUTION_DIR" >&2
200
-
201
- TOTAL_IDEAS="$DEFAULT_TOTAL_IDEAS"
202
- NOVEL_EXPLORATION="$DEFAULT_NOVEL_EXPLORATION"
203
- HILL_CLIMBING="$DEFAULT_HILL_CLIMBING"
204
- STRUCTURAL_MUTATION="$DEFAULT_STRUCTURAL_MUTATION"
205
- CROSSOVER_HYBRID="$DEFAULT_CROSSOVER_HYBRID"
206
- NUM_ELITES="$DEFAULT_NUM_ELITES"
207
- NUM_REVOLUTION="$DEFAULT_NUM_REVOLUTION"
208
-
209
- PARALLEL_ENABLED="$DEFAULT_PARALLEL_ENABLED"
210
- MAX_WORKERS="$DEFAULT_MAX_WORKERS"
211
- LOCK_TIMEOUT="$DEFAULT_LOCK_TIMEOUT"
212
-
213
- AUTO_IDEATE="$DEFAULT_AUTO_IDEATE"
214
- MAX_RETRIES="$DEFAULT_MAX_RETRIES"
215
- MEMORY_LIMIT_MB="$DEFAULT_MEMORY_LIMIT_MB"
216
-
217
- LLM_CLI_gpt5high='codex exec --profile gpt5high --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
218
- LLM_CLI_o3high='codex exec --profile o3high --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
219
- LLM_CLI_codex='codex exec --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
220
- LLM_CLI_gemini='gemini -y -p "{{PROMPT}}"'
221
- LLM_CLI_gemini_flash='gemini -y -p "{{PROMPT}}" --model gemini-2.5-flash'
222
- LLM_CLI_opus='claude --dangerously-skip-permissions --mcp-config "" --model opus -p "{{PROMPT}}"'
223
- LLM_CLI_opus_think='claude --dangerously-skip-permissions --mcp-config "" --model opus -p "ultrathink\n\n{{PROMPT}}"'
224
- LLM_CLI_sonnet='claude --dangerously-skip-permissions --mcp-config "" --model sonnet -p "{{PROMPT}}"'
225
- LLM_CLI_sonnet_think='claude --dangerously-skip-permissions --mcp-config "" --model sonnet -p "ultrathink\n\n{{PROMPT}}"'
226
- LLM_CLI_cursor_sonnet='cursor-agent sonnet -p "{{PROMPT}}"'
227
- LLM_CLI_cursor_opus='cursor-agent opus -p "{{PROMPT}}"'
228
- LLM_CLI_glm='opencode -m openrouter/z-ai/glm-4.6 run "{{PROMPT}}"'
229
- LLM_CLI_deepseek='opencode -m openrouter/deepseek/deepseek-v3.1-terminus run "{{PROMPT}}"'
230
- LLM_CLI_ollama-cloud-gpt-oss='codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --oss --model gpt-oss:20b-cloud "{{PROMPT}}"' LLM_CLI_ollama-cloud-kimi-k2='codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --oss --model kimi-k2:1t-cloud "{{PROMPT}}"' LLM_RUN="$DEFAULT_LLM_RUN"
231
- LLM_IDEATE="$DEFAULT_LLM_IDEATE"
232
-
233
- # Determine local config file path relative to EVOLUTION_DIR
234
- local local_config_file="$EVOLUTION_DIR/config.yaml"
235
-
236
- # Load local config
237
- _load_yaml_config "$local_config_file"
238
-
239
- # Load global config (overrides local config)
240
- local global_config_file="$HOME/.config/claude-evolve/config.yaml"
241
- _load_yaml_config "$global_config_file"
242
-
243
- echo "[DEBUG] EVOLUTION_DIR before FULL_EVOLUTION_DIR calculation: $EVOLUTION_DIR" >&2
244
-
245
- # Create full paths - ALL paths are relative to EVOLUTION_DIR
246
- # Make EVOLUTION_DIR absolute if it\'s relative
247
- if [[ "$EVOLUTION_DIR" = /* ]]; then
248
- FULL_EVOLUTION_DIR="$EVOLUTION_DIR"
249
- else
250
- FULL_EVOLUTION_DIR="$(cd "$EVOLUTION_DIR" 2>/dev/null && pwd)" || FULL_EVOLUTION_DIR="$EVOLUTION_DIR"
251
- fi
252
-
253
- FULL_ALGORITHM_PATH="$FULL_EVOLUTION_DIR/$ALGORITHM_FILE"
254
- FULL_EVALUATOR_PATH="$FULL_EVOLUTION_DIR/$EVALUATOR_FILE"
255
- FULL_BRIEF_PATH="$FULL_EVOLUTION_DIR/$BRIEF_FILE"
256
- FULL_CSV_PATH="$FULL_EVOLUTION_DIR/$EVOLUTION_CSV"
257
-
258
- if [[ -n $OUTPUT_DIR ]]; then
259
- FULL_OUTPUT_DIR="$FULL_EVOLUTION_DIR/$OUTPUT_DIR"
260
- else
261
- FULL_OUTPUT_DIR="$FULL_EVOLUTION_DIR"
262
- fi
263
- echo "[DEBUG] FULL_EVOLUTION_DIR at end of load_config: $FULL_EVOLUTION_DIR" >&2
264
- }
265
-
266
- # Validate configuration
267
- validate_config() {
268
- local errors=0
269
-
270
- if [[ ! -d "$FULL_EVOLUTION_DIR" ]]; then
271
- echo "[ERROR] Evolution directory not found: $FULL_EVOLUTION_DIR" >&2
272
- ((errors++))
273
- fi
274
-
275
- if [[ ! -f "$FULL_ALGORITHM_PATH" ]]; then
276
- echo "[ERROR] Algorithm file not found: $FULL_ALGORITHM_PATH" >&2
277
- ((errors++))
278
- fi
279
-
280
- if [[ ! -f "$FULL_EVALUATOR_PATH" ]]; then
281
- echo "[ERROR] Evaluator file not found: $FULL_EVALUATOR_PATH" >&2
282
- ((errors++))
283
- fi
284
-
285
- if [[ ! -f "$FULL_BRIEF_PATH" ]]; then
286
- echo "[ERROR] Brief file not found: $FULL_BRIEF_PATH" >&2
287
- ((errors++))
288
- fi
289
-
290
- if ! command -v "$PYTHON_CMD" >/dev/null 2>&1; then
291
- echo "[ERROR] Python command not found: $PYTHON_CMD" >&2
292
- echo "[ERROR] Please install Python 3.x or set python_cmd in config.yaml" >&2
293
- echo "[ERROR] Examples: python_cmd: \"python\" or python_cmd: \"C:\\Python39\\python.exe\"" >&2
294
- ((errors++))
295
- else
296
- # Verify Python version is 3.x
297
- if ! "$PYTHON_CMD" -c "import sys; sys.exit(0 if sys.version_info[0] >= 3 else 1)" 2>/dev/null; then
298
- echo "[ERROR] Python 3.x required, but $PYTHON_CMD appears to be Python 2" >&2
299
- echo "[ERROR] Please set python_cmd in config.yaml to point to Python 3" >&2
300
- ((errors++))
301
- fi
302
- fi
303
-
304
- return $errors
305
- }
306
-
307
- # Show current configuration
308
- show_config() {
309
- echo "Current claude-evolve configuration:"
310
- echo " Evolution directory: $FULL_EVOLUTION_DIR"
311
- echo " Algorithm file: $FULL_ALGORITHM_PATH"
312
- echo " Evaluator file: $FULL_EVALUATOR_PATH"
313
- echo " Brief file: $FULL_BRIEF_PATH"
314
- echo " CSV file: $FULL_CSV_PATH"
315
- echo " Output directory: $FULL_OUTPUT_DIR"
316
- echo " Parent selection: $PARENT_SELECTION"
317
- echo " Python command: $PYTHON_CMD"
318
- echo " Parallel enabled: $PARALLEL_ENABLED"
319
- echo " Max workers: $MAX_WORKERS"
320
- echo " Lock timeout: $LOCK_TIMEOUT"
321
- echo " Auto ideate: $AUTO_IDEATE"
322
- echo " Max retries: $MAX_RETRIES"
323
- echo " Memory limit: ${MEMORY_LIMIT_MB}MB"
324
- echo " LLM configuration:"
325
- # Show LLM configurations using dynamic variable names
326
- for model in gpt5high o3high codex gemini opus opus_think sonnet sonnet_think cursor_sonnet cursor_opus glm deepseek; do
327
- var_name="LLM_CLI_${model}"
328
- var_value=$(eval echo "\$$var_name")
329
- if [[ -n "$var_value" ]]; then
330
- # Convert underscore back to dash for display
331
- display_name=$(echo "$model" | sed 's/_/-/g')
332
- echo " $display_name: $var_value"
333
- fi
334
- done
335
- echo " LLM for run: $LLM_RUN"
336
- echo " LLM for ideate: $LLM_IDEATE"
337
- }