claude-evolve 1.5.1 → 1.5.2

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.
@@ -518,10 +518,22 @@ while true; do
518
518
 
519
519
  # Generate new ideas using the multi-strategy approach
520
520
  echo "[DISPATCHER] Calling claude-evolve-ideate to generate new candidates..."
521
+ # Export config path for ideate script
522
+ if [[ -n $WORKER_CONFIG_PATH ]]; then
523
+ export CLAUDE_EVOLVE_CONFIG="$WORKER_CONFIG_PATH"
524
+ fi
521
525
  if ! "$ideate_script"; then
522
- echo "[ERROR] Failed to generate new ideas" >&2
523
- echo "[DISPATCHER] Evolution complete - ideation failed."
524
- break
526
+ echo "[WARN] Ideation failed to generate new ideas" >&2
527
+ if [[ "${CONTINUE_ON_IDEATION_FAILURE:-false}" == "true" ]]; then
528
+ echo "[DISPATCHER] Continuing despite ideation failure (CONTINUE_ON_IDEATION_FAILURE=true)" >&2
529
+ echo "[DISPATCHER] Waiting 60 seconds before retry..." >&2
530
+ sleep 60
531
+ continue
532
+ else
533
+ echo "[DISPATCHER] Evolution complete - ideation failed."
534
+ echo "[DISPATCHER] Set CONTINUE_ON_IDEATION_FAILURE=true to continue despite failures"
535
+ break
536
+ fi
525
537
  fi
526
538
 
527
539
  echo "[DISPATCHER] New ideas generated successfully. Continuing evolution..."
@@ -5,6 +5,7 @@ set -e
5
5
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
6
6
  source "$SCRIPT_DIR/../lib/config.sh"
7
7
  source "$SCRIPT_DIR/../lib/csv-lock.sh"
8
+ source "$SCRIPT_DIR/../lib/ai-cli.sh"
8
9
 
9
10
  # Track current candidate for cleanup
10
11
  CURRENT_CANDIDATE_ID=""
@@ -79,148 +80,6 @@ else
79
80
  load_config
80
81
  fi
81
82
 
82
- # Call an AI model with a prompt - handles model-specific invocation
83
- call_ai_model() {
84
- local model="$1"
85
- local prompt="$2"
86
- local ai_output
87
- local ai_exit_code
88
-
89
- case "$model" in
90
- "claude")
91
- # Pass prompt as argument, not via stdin
92
- ai_output=$(timeout 300 claude --dangerously-skip-permissions -p "$prompt" 2>&1)
93
- ai_exit_code=$?
94
- ;;
95
-
96
- "gemini")
97
- # Pass prompt as argument, not via stdin
98
- ai_output=$(timeout 300 gemini -y -p "$prompt" 2>&1)
99
- ai_exit_code=$?
100
- ;;
101
-
102
- "codex")
103
- # Pass prompt as argument, not via stdin
104
- ai_output=$(timeout 300 codex exec --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
105
- ai_exit_code=$?
106
- ;;
107
-
108
- *)
109
- echo "[WORKER-$$] ERROR: Unknown AI model: $model" >&2
110
- return 1
111
- ;;
112
- esac
113
-
114
- # Return output via stdout
115
- echo "$ai_output"
116
- return $ai_exit_code
117
- }
118
-
119
- # Check if AI output indicates a usage limit was hit
120
- is_usage_limit_error() {
121
- local output="$1"
122
- local model="$2"
123
-
124
- case "$model" in
125
- "claude")
126
- echo "$output" | grep -q "Claude AI usage limit reached"
127
- ;;
128
- "gemini")
129
- echo "$output" | grep -q "Quota exceeded.*Gemini"
130
- ;;
131
- "codex")
132
- # Add codex-specific limit patterns if they exist
133
- false
134
- ;;
135
- *)
136
- false
137
- ;;
138
- esac
139
- }
140
-
141
- # Validate if AI output is successful
142
- is_valid_ai_output() {
143
- local output="$1"
144
- local model="$2"
145
- local exit_code="$3"
146
-
147
- # First check exit code
148
- [[ $exit_code -ne 0 ]] && return 1
149
-
150
- # Model-specific validation
151
- case "$model" in
152
- "claude")
153
- # Claude is straightforward - exit code 0 means success
154
- return 0
155
- ;;
156
-
157
- "gemini")
158
- # Gemini needs extra validation for auth messages
159
- if echo "$output" | grep -q "Attempting to authenticate\|Authenticating\|Loading\|Initializing"; then
160
- return 1
161
- fi
162
- # Also check for minimal output
163
- if [[ -z "$output" ]] || [[ $(echo "$output" | wc -l) -lt 2 ]]; then
164
- return 1
165
- fi
166
- return 0
167
- ;;
168
-
169
- "codex")
170
- # Codex might return JSON that needs extraction
171
- if echo "$output" | grep -q '"content"'; then
172
- # Will be cleaned later, just check it's not an error
173
- if echo "$output" | grep -q "error\|failed\|exception"; then
174
- return 1
175
- fi
176
- fi
177
- [[ -n "$output" ]]
178
- ;;
179
-
180
- *)
181
- return 1
182
- ;;
183
- esac
184
- }
185
-
186
- # Clean AI output if needed (e.g., extract from JSON)
187
- clean_ai_output() {
188
- local output="$1"
189
- local model="$2"
190
-
191
- case "$model" in
192
- "codex")
193
- # Clean codex output - extract content between "codex" marker and "tokens used"
194
- if echo "$output" | grep -q "^\[.*\] codex$"; then
195
- # Extract content between "codex" line and "tokens used" line
196
- output=$(echo "$output" | awk '/\] codex$/{flag=1;next}/\] tokens used/{flag=0}flag')
197
- elif echo "$output" | grep -q '"content"'; then
198
- # Old JSON format
199
- output=$(echo "$output" | python3 -c "
200
- import sys
201
- import json
202
- try:
203
- data = json.load(sys.stdin)
204
- if 'content' in data:
205
- print(data['content'])
206
- elif 'response' in data:
207
- print(data['response'])
208
- elif 'text' in data:
209
- print(data['text'])
210
- else:
211
- print(json.dumps(data))
212
- except:
213
- print(sys.stdin.read())
214
- " 2>/dev/null || echo "$output")
215
- fi
216
- # Trim whitespace
217
- output=$(echo "$output" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
218
- ;;
219
- esac
220
-
221
- echo "$output"
222
- }
223
-
224
83
  # AI round-robin with fallback function for code evolution
225
84
  call_ai_for_evolution() {
226
85
  local prompt="$1"
@@ -237,83 +96,26 @@ call_ai_for_evolution() {
237
96
  # Calculate hash for round-robin (combine generation and ID)
238
97
  local hash_value=$((gen_num * 1000 + id_num))
239
98
 
240
- # Check which AI tools are available
241
- local available_models=()
242
- if command -v claude >/dev/null 2>&1; then
243
- available_models+=("claude")
244
- fi
245
- if command -v gemini >/dev/null 2>&1; then
246
- available_models+=("gemini")
247
- fi
248
- if command -v codex >/dev/null 2>&1; then
249
- available_models+=("codex")
250
- fi
251
-
252
- if [[ ${#available_models[@]} -eq 0 ]]; then
253
- echo "[WORKER-$$] ERROR: No AI models available!" >&2
254
- return 1
255
- fi
256
-
257
- # Create ordered list based on round-robin for this candidate
258
- local num_models=${#available_models[@]}
259
- local start_index=$((hash_value % num_models))
260
- local models=()
261
-
262
- # Add models in round-robin order starting from the calculated index
263
- for ((i=0; i<num_models; i++)); do
264
- local idx=$(((start_index + i) % num_models))
265
- models+=("${available_models[$idx]}")
266
- done
267
-
268
- echo "[WORKER-$$] Model order for $candidate_id (round-robin): ${models[*]}" >&2
269
-
270
- # Track if any model hit usage limits
271
- local hit_usage_limit=false
272
- local limited_models=()
273
-
274
- # Try each model in the ordered sequence
275
- for model in "${models[@]}"; do
276
- echo "[WORKER-$$] Attempting code evolution with $model" >&2
277
-
278
- # Call the AI model
279
- local ai_output
280
- ai_output=$(call_ai_model "$model" "$prompt")
281
- local ai_exit_code=$?
282
-
283
- # Check for usage limits
284
- if is_usage_limit_error "$ai_output" "$model"; then
285
- echo "[WORKER-$$] $model hit usage limit - trying next model" >&2
286
- hit_usage_limit=true
287
- limited_models+=("$model")
288
- continue
289
- fi
290
-
291
- # Validate output
292
- if is_valid_ai_output "$ai_output" "$model" "$ai_exit_code"; then
293
- # Clean output if needed
294
- ai_output=$(clean_ai_output "$ai_output" "$model")
295
- echo "[WORKER-$$] $model succeeded" >&2
296
- # Output the cleaned result for the worker to use
297
- echo "$ai_output"
298
- return 0
299
- fi
300
-
301
- echo "[WORKER-$$] $model failed (exit code $ai_exit_code), trying next model..." >&2
302
- if [[ -n "$ai_output" ]]; then
303
- echo "[WORKER-$$] $model error: $(echo "$ai_output" | head -5)" >&2
304
- fi
305
- done
306
-
307
- # All models have been tried
308
- echo "[WORKER-$$] All AI models failed for code evolution" >&2
99
+ # Use the centralized AI library for evolution (run command)
100
+ local ai_output
101
+ ai_output=$(call_ai_with_round_robin "$prompt" "run" "$hash_value")
102
+ local ai_exit_code=$?
309
103
 
310
- # If any model hit usage limits and we couldn't complete the task
311
- if [[ "$hit_usage_limit" == "true" ]]; then
312
- echo "[WORKER-$$] Models hit usage limits: ${limited_models[*]}" >&2
104
+ # Handle special exit codes
105
+ if [[ $ai_exit_code -eq 3 ]]; then
106
+ echo "[WORKER-$$] All models hit usage limits" >&2
313
107
  echo "[WORKER-$$] Unable to complete evolution due to API limits" >&2
314
108
  exit 3
315
109
  fi
316
110
 
111
+ if [[ $ai_exit_code -eq 0 ]]; then
112
+ echo "[WORKER-$$] AI succeeded" >&2
113
+ # Output the result for the worker to use
114
+ echo "$ai_output"
115
+ return 0
116
+ fi
117
+
118
+ echo "[WORKER-$$] All AI models failed" >&2
317
119
  return 1
318
120
  }
319
121
 
@@ -400,7 +202,9 @@ with EvolutionCSV('$FULL_CSV_PATH') as csv:
400
202
 
401
203
  The modification should be substantial and follow the description exactly. Make sure the algorithm still follows all interface requirements and can run properly.
402
204
 
403
- Important: Make meaningful changes that match the description. Don't just add comments or make trivial adjustments."
205
+ Important: Make meaningful changes that match the description. Don't just add comments or make trivial adjustments.
206
+
207
+ CRITICAL: Do NOT use any git commands (git add, git commit, git reset, etc.). Only modify the file directly."
404
208
 
405
209
  if [[ "$is_baseline" != "true" ]]; then
406
210
  # Change to evolution directory so AI can access files
package/lib/ai-cli.sh ADDED
@@ -0,0 +1,221 @@
1
+ #!/bin/bash
2
+ # Centralized AI CLI invocation library for claude-evolve
3
+
4
+ # Source config to get LLM_CLI array and model lists
5
+ # This will be sourced after config.sh in the main scripts
6
+
7
+ # Call an AI model using the configured command template
8
+ # Usage: call_ai_model_configured <model_name> <prompt>
9
+ # Returns: 0 on success, non-zero on failure
10
+ # Output: AI response on stdout
11
+ call_ai_model_configured() {
12
+ local model_name="$1"
13
+ local prompt="$2"
14
+
15
+ # Build command directly based on model
16
+ case "$model_name" in
17
+ opus|sonnet)
18
+ local ai_output
19
+ ai_output=$(timeout 300 claude --dangerously-skip-permissions --model "$model_name" -p "$prompt" 2>&1)
20
+ local ai_exit_code=$?
21
+ ;;
22
+ o3)
23
+ local ai_output
24
+ ai_output=$(timeout 300 codex exec -m o3 --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
25
+ local ai_exit_code=$?
26
+ ;;
27
+ codex)
28
+ local ai_output
29
+ ai_output=$(timeout 300 codex exec --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
30
+ local ai_exit_code=$?
31
+ ;;
32
+ gemini)
33
+ # Debug: Show exact command
34
+ echo "[DEBUG] Running: timeout 300 gemini -y -p <prompt>" >&2
35
+ echo "[DEBUG] Working directory: $(pwd)" >&2
36
+ echo "[DEBUG] Files in current dir:" >&2
37
+ ls -la temp-csv-*.csv 2>&1 | head -5 >&2
38
+ local ai_output
39
+ ai_output=$(timeout 300 gemini -y -p "$prompt" 2>&1)
40
+ local ai_exit_code=$?
41
+ ;;
42
+ *)
43
+ echo "[ERROR] Unknown model: $model_name" >&2
44
+ return 1
45
+ ;;
46
+ esac
47
+
48
+ # Debug: log model and prompt size
49
+ echo "[DEBUG] Calling $model_name with prompt of ${#prompt} characters" >&2
50
+
51
+ # Always log basic info
52
+ echo "[AI] $model_name exit code: $ai_exit_code, output length: ${#ai_output} chars" >&2
53
+
54
+ # Show detailed output if verbose or if there was an error
55
+ if [[ "${VERBOSE_AI_OUTPUT:-false}" == "true" ]] || [[ $ai_exit_code -ne 0 ]]; then
56
+ echo "[AI] Raw output from $model_name:" >&2
57
+ echo "----------------------------------------" >&2
58
+ if [[ ${#ai_output} -gt 2000 ]]; then
59
+ echo "$ai_output" | head -50 >&2
60
+ echo "... (truncated from ${#ai_output} characters to first 50 lines) ..." >&2
61
+ else
62
+ echo "$ai_output" >&2
63
+ fi
64
+ echo "----------------------------------------" >&2
65
+ fi
66
+
67
+ # Debug: save full output if debugging is enabled
68
+ if [[ "${DEBUG_AI_CALLS:-}" == "true" ]]; then
69
+ local debug_file="/tmp/claude-evolve-ai-${model_name}-$$.log"
70
+ echo "Model: $model_name" > "$debug_file"
71
+ echo "Exit code: $ai_exit_code" >> "$debug_file"
72
+ echo "Prompt length: ${#prompt}" >> "$debug_file"
73
+ echo "Output:" >> "$debug_file"
74
+ echo "$ai_output" >> "$debug_file"
75
+ echo "[DEBUG] Full output saved to: $debug_file" >&2
76
+ fi
77
+
78
+ # Output the result
79
+ echo "$ai_output"
80
+ return $ai_exit_code
81
+ }
82
+
83
+ # DEPRECATED - Keep for compatibility but always return false
84
+ is_usage_limit_error() {
85
+ return 1
86
+ }
87
+
88
+ # DEPRECATED - Just check exit code now
89
+ is_valid_ai_output() {
90
+ local output="$1"
91
+ local exit_code="$2"
92
+
93
+ # Only check exit code - let the caller verify file changes
94
+ return $exit_code
95
+ }
96
+
97
+ # Clean AI output if needed (e.g., extract from JSON)
98
+ clean_ai_output() {
99
+ local output="$1"
100
+ local model_name="$2"
101
+
102
+ # Handle codex-specific output format
103
+ if [[ "$model_name" == "codex" || "$model_name" == "o3" ]]; then
104
+ # Clean codex output - extract content between "codex" marker and "tokens used"
105
+ if echo "$output" | grep -q "^\[.*\] codex$"; then
106
+ # Extract content between "codex" line and "tokens used" line
107
+ output=$(echo "$output" | awk '/\] codex$/{flag=1;next}/\] tokens used/{flag=0}flag')
108
+ fi
109
+ fi
110
+
111
+ # Trim whitespace
112
+ output=$(echo "$output" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
113
+
114
+ echo "$output"
115
+ }
116
+
117
+ # Get models for a specific command (run or ideate)
118
+ # Usage: get_models_for_command <command>
119
+ # Returns: Array of model names
120
+ get_models_for_command() {
121
+ local command="$1"
122
+ local model_list=""
123
+
124
+ case "$command" in
125
+ run)
126
+ model_list="$LLM_RUN"
127
+ ;;
128
+ ideate)
129
+ model_list="$LLM_IDEATE"
130
+ ;;
131
+ *)
132
+ echo "[ERROR] Unknown command: $command" >&2
133
+ return 1
134
+ ;;
135
+ esac
136
+
137
+ # Convert space-separated list to array
138
+ echo "$model_list"
139
+ }
140
+
141
+ # Call AI with round-robin and fallback support
142
+ # Usage: call_ai_with_round_robin <prompt> <command> <hash_value>
143
+ # command: "run" or "ideate"
144
+ # hash_value: numeric value for round-robin selection (e.g., candidate ID hash)
145
+ call_ai_with_round_robin() {
146
+ local prompt="$1"
147
+ local command="$2"
148
+ local hash_value="${3:-0}"
149
+
150
+ # Get model list for this command
151
+ local model_list
152
+ model_list=$(get_models_for_command "$command")
153
+ if [[ -z "$model_list" ]]; then
154
+ echo "[ERROR] No models configured for command: $command" >&2
155
+ return 1
156
+ fi
157
+
158
+ # Convert to array
159
+ local models=()
160
+ read -ra models <<< "$model_list"
161
+
162
+ if [[ ${#models[@]} -eq 0 ]]; then
163
+ echo "[ERROR] No models available for $command" >&2
164
+ return 1
165
+ fi
166
+
167
+ # Calculate starting index for round-robin
168
+ local num_models=${#models[@]}
169
+ local start_index=$((hash_value % num_models))
170
+
171
+ # Create ordered list based on round-robin
172
+ local ordered_models=()
173
+ for ((i=0; i<num_models; i++)); do
174
+ local idx=$(((start_index + i) % num_models))
175
+ ordered_models+=("${models[$idx]}")
176
+ done
177
+
178
+ echo "[AI] Model order for $command (round-robin): ${ordered_models[*]}" >&2
179
+
180
+ # Track models that hit usage limits
181
+ local limited_models=()
182
+ local tried_models=()
183
+
184
+ # Try each model in order
185
+ for model in "${ordered_models[@]}"; do
186
+ echo "[AI] Attempting $command with $model" >&2
187
+ tried_models+=("$model")
188
+
189
+ # Call the AI model
190
+ local ai_output
191
+ ai_output=$(call_ai_model_configured "$model" "$prompt")
192
+ local ai_exit_code=$?
193
+
194
+ # Just check exit code - no interpretation
195
+ if [[ $ai_exit_code -eq 0 ]]; then
196
+ # Clean output if needed
197
+ ai_output=$(clean_ai_output "$ai_output" "$model")
198
+ echo "[AI] $model returned exit code 0" >&2
199
+ # Debug: log what AI returned on success
200
+ if [[ "${DEBUG_AI_SUCCESS:-}" == "true" ]]; then
201
+ echo "[AI] $model success output preview:" >&2
202
+ echo "$ai_output" | head -10 >&2
203
+ echo "[AI] (truncated to first 10 lines)" >&2
204
+ fi
205
+ # Output the cleaned result
206
+ echo "$ai_output"
207
+ return 0
208
+ fi
209
+
210
+ echo "[AI] $model returned exit code $ai_exit_code, trying next model..." >&2
211
+ done
212
+
213
+ # All models have been tried
214
+ echo "[AI] All models have been tried without success" >&2
215
+ return 1
216
+ }
217
+
218
+ # Legacy function name for compatibility
219
+ call_ai_with_fallbacks() {
220
+ call_ai_with_round_robin "$@"
221
+ }
package/lib/config.sh CHANGED
@@ -49,6 +49,18 @@ DEFAULT_AUTO_IDEATE=true
49
49
  # Default retry value
50
50
  DEFAULT_MAX_RETRIES=3
51
51
 
52
+ # Default LLM CLI configuration (using eval for compatibility)
53
+ declare -a DEFAULT_LLM_CLI_KEYS
54
+ declare -a DEFAULT_LLM_CLI_VALUES
55
+ DEFAULT_LLM_CLI_KEYS=(o3 codex gemini opus sonnet)
56
+ DEFAULT_LLM_CLI_VALUES[0]='codex exec -m o3 --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
57
+ DEFAULT_LLM_CLI_VALUES[1]='codex exec --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
58
+ DEFAULT_LLM_CLI_VALUES[2]='gemini -y -p "{{PROMPT}}"'
59
+ DEFAULT_LLM_CLI_VALUES[3]='claude --dangerously-skip-permissions --model opus -p "{{PROMPT}}"'
60
+ DEFAULT_LLM_CLI_VALUES[4]='claude --dangerously-skip-permissions --model sonnet -p "{{PROMPT}}"'
61
+ DEFAULT_LLM_RUN="sonnet gemini"
62
+ DEFAULT_LLM_IDEATE="opus o3"
63
+
52
64
  # Load configuration from config file
53
65
  load_config() {
54
66
  # Accept config file path as parameter
@@ -84,12 +96,25 @@ load_config() {
84
96
  # Set retry default
85
97
  MAX_RETRIES="$DEFAULT_MAX_RETRIES"
86
98
 
99
+ # Set LLM CLI defaults (compatibility for older bash)
100
+ # Initialize associative array for LLM commands
101
+ # Use simpler approach for compatibility
102
+ LLM_CLI_o3='codex exec -m o3 --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
103
+ LLM_CLI_codex='codex exec --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
104
+ LLM_CLI_gemini='gemini -y -p "{{PROMPT}}"'
105
+ LLM_CLI_opus='claude --dangerously-skip-permissions --model opus -p "{{PROMPT}}"'
106
+ LLM_CLI_sonnet='claude --dangerously-skip-permissions --model sonnet -p "{{PROMPT}}"'
107
+ LLM_RUN="$DEFAULT_LLM_RUN"
108
+ LLM_IDEATE="$DEFAULT_LLM_IDEATE"
109
+
87
110
  # Load config if found
88
111
  if [[ -f "$config_file" ]]; then
89
112
  echo "[DEBUG] Loading configuration from: $config_file" >&2
90
113
  # Simple YAML parsing for key: value pairs and nested structures
91
114
  local in_ideation_section=false
92
115
  local in_parallel_section=false
116
+ local in_llm_cli_section=false
117
+ local llm_cli_subsection=""
93
118
  while IFS='' read -r line; do
94
119
  # Skip comments and empty lines
95
120
  [[ $line =~ ^[[:space:]]*# ]] || [[ -z $line ]] && continue
@@ -110,6 +135,11 @@ load_config() {
110
135
  key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
111
136
  value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
112
137
 
138
+ # Debug before comment removal
139
+ if [[ "${DEBUG_CONFIG:-}" == "true" ]]; then
140
+ echo "[CONFIG DEBUG] Before comment removal: key='$key' value='$value'" >&2
141
+ fi
142
+
113
143
  # Remove inline comments from value
114
144
  value=$(echo "$value" | sed 's/[[:space:]]*#.*$//')
115
145
 
@@ -120,15 +150,25 @@ load_config() {
120
150
  if [[ $key == "ideation_strategies" ]]; then
121
151
  in_ideation_section=true
122
152
  in_parallel_section=false
153
+ in_llm_cli_section=false
123
154
  continue
124
155
  elif [[ $key == "parallel" ]]; then
125
156
  in_parallel_section=true
126
157
  in_ideation_section=false
158
+ in_llm_cli_section=false
127
159
  continue
128
- elif [[ $is_indented == false ]] && [[ $in_ideation_section == true || $in_parallel_section == true ]]; then
160
+ elif [[ $key == "llm_cli" ]]; then
161
+ in_llm_cli_section=true
162
+ in_ideation_section=false
163
+ in_parallel_section=false
164
+ llm_cli_subsection=""
165
+ continue
166
+ elif [[ $is_indented == false ]] && [[ $in_ideation_section == true || $in_parallel_section == true || $in_llm_cli_section == true ]]; then
129
167
  # Non-indented key found while in a section, exit nested sections
130
168
  in_ideation_section=false
131
169
  in_parallel_section=false
170
+ in_llm_cli_section=false
171
+ llm_cli_subsection=""
132
172
  fi
133
173
 
134
174
  if [[ $in_ideation_section == true ]]; then
@@ -149,6 +189,26 @@ load_config() {
149
189
  max_workers) MAX_WORKERS="$value" ;;
150
190
  lock_timeout) LOCK_TIMEOUT="$value" ;;
151
191
  esac
192
+ elif [[ $in_llm_cli_section == true ]]; then
193
+ # Handle indented keys in llm_cli section
194
+ # Check if this is a model definition (o3, codex, gemini, etc.) or a command list (run, ideate)
195
+ if [[ $key == "run" || $key == "ideate" ]]; then
196
+ # Command list - value is a space-separated list of models
197
+ case $key in
198
+ run) LLM_RUN="$value" ;;
199
+ ideate) LLM_IDEATE="$value" ;;
200
+ esac
201
+ else
202
+ # Model definition - key is model name, value is command template
203
+ # Remove single quotes from value if present
204
+ value=$(echo "$value" | sed "s/^'//;s/'$//")
205
+ # Debug config loading
206
+ if [[ "${DEBUG_CONFIG:-}" == "true" ]]; then
207
+ echo "[CONFIG DEBUG] Setting LLM_CLI_${key} = '$value'" >&2
208
+ fi
209
+ # Use dynamic variable name for compatibility
210
+ eval "LLM_CLI_${key}=\"$value\""
211
+ fi
152
212
  else
153
213
  # Handle top-level keys
154
214
  case $key in
@@ -256,4 +316,14 @@ show_config() {
256
316
  echo " Lock timeout: $LOCK_TIMEOUT"
257
317
  echo " Auto ideate: $AUTO_IDEATE"
258
318
  echo " Max retries: $MAX_RETRIES"
319
+ echo " LLM configuration:"
320
+ # Show LLM configurations using dynamic variable names
321
+ for model in o3 codex gemini opus sonnet; do
322
+ var_name="LLM_CLI_${model}"
323
+ if [[ -n "${!var_name}" ]]; then
324
+ echo " $model: ${!var_name}"
325
+ fi
326
+ done
327
+ echo " LLM for run: $LLM_RUN"
328
+ echo " LLM for ideate: $LLM_IDEATE"
259
329
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "bin": {
5
5
  "claude-evolve": "./bin/claude-evolve",
6
6
  "claude-evolve-main": "./bin/claude-evolve-main",
@@ -55,4 +55,20 @@ parallel:
55
55
  max_workers: 4
56
56
 
57
57
  # Timeout in seconds when waiting for CSV locks
58
- lock_timeout: 30
58
+ lock_timeout: 30
59
+
60
+ # LLM/AI CLI configuration
61
+ llm_cli:
62
+ # How to run each CLI for each LLM option
63
+ # {{PROMPT}} will be replaced with the actual prompt text
64
+ o3: 'codex exec -m o3 --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
65
+ codex: 'codex exec --dangerously-bypass-approvals-and-sandbox "{{PROMPT}}"'
66
+ gemini: 'gemini -y -p "{{PROMPT}}"'
67
+ opus: 'claude --dangerously-skip-permissions --model opus -p "{{PROMPT}}"'
68
+ sonnet: 'claude --dangerously-skip-permissions --model sonnet -p "{{PROMPT}}"'
69
+
70
+ # What to run for each sub-command
71
+ # Models are tried in order, with round-robin distribution across candidates
72
+ # You can repeat models for weighted selection (e.g., "sonnet sonnet gemini" for 2:1 ratio)
73
+ run: sonnet gemini
74
+ ideate: opus o3