claude-evolve 1.11.13 → 1.11.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/lib/ai-cli.sh CHANGED
@@ -1,13 +1,11 @@
1
1
  #!/bin/bash
2
2
  # Centralized AI CLI invocation library for claude-evolve
3
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)
4
+ # AIDEV-NOTE: Timeouts are now handled by the Python caller (ai_cli.py), not by
5
+ # bash timeout commands. This allows for better control and monitoring of AI CLI
6
+ # processes from the Python layer, including graceful timeout handling and
7
+ # proper error recovery. The bash functions here focus on clean command execution
8
+ # without timeout wrapping.
11
9
 
12
10
  # Source config to get LLM_CLI array and model lists
13
11
  # This will be sourced after config.sh in the main scripts
@@ -64,183 +62,183 @@ call_ai_model_configured() {
64
62
  case "$model_name" in
65
63
  opus)
66
64
  local ai_output
67
- ai_output=$(timeout -k 30 300 claude --dangerously-skip-permissions --mcp-config '' --model opus -p "$prompt" 2>&1)
65
+ ai_output=$(claude --dangerously-skip-permissions --mcp-config '' --model opus -p "$prompt" 2>&1)
68
66
  local ai_exit_code=$?
69
67
  ;;
70
68
  sonnet)
71
69
  local ai_output
72
- ai_output=$(timeout -k 30 300 claude --dangerously-skip-permissions --mcp-config '' --model sonnet -p "$prompt" 2>&1)
70
+ ai_output=$(claude --dangerously-skip-permissions --mcp-config '' --model sonnet -p "$prompt" 2>&1)
73
71
  local ai_exit_code=$?
74
72
  ;;
75
73
  sonnet-think)
76
74
  local ai_output
77
75
  # Use extended thinking with sonnet 4.5 - prepend ultrathink instruction
78
- # AIDEV-NOTE: Extended thinking needs 30 min timeout - can take long for complex ideation
76
+ # AIDEV-NOTE: Extended thinking can take long for complex ideation
79
77
  local think_prompt="ultrathink
80
78
 
81
79
  $prompt"
82
- ai_output=$(timeout -k 30 1800 claude --dangerously-skip-permissions --mcp-config '' --model sonnet -p "$think_prompt" 2>&1)
80
+ ai_output=$(claude --dangerously-skip-permissions --mcp-config '' --model sonnet -p "$think_prompt" 2>&1)
83
81
  local ai_exit_code=$?
84
82
  ;;
85
83
  opus-think)
86
84
  local ai_output
87
85
  # Use extended thinking with opus - prepend ultrathink instruction
88
- # AIDEV-NOTE: Extended thinking needs 30 min timeout - can take long for complex ideation
86
+ # AIDEV-NOTE: Extended thinking can take long for complex ideation
89
87
  local think_prompt="ultrathink
90
88
 
91
89
  $prompt"
92
- ai_output=$(timeout -k 30 1800 claude --dangerously-skip-permissions --mcp-config '' --model opus -p "$think_prompt" 2>&1)
90
+ ai_output=$(claude --dangerously-skip-permissions --mcp-config '' --model opus -p "$think_prompt" 2>&1)
93
91
  local ai_exit_code=$?
94
92
  ;;
95
93
  haiku)
96
94
  local ai_output
97
- ai_output=$(timeout -k 30 300 claude --dangerously-skip-permissions --mcp-config '' --model haiku -p "$prompt" 2>&1)
95
+ ai_output=$(claude --dangerously-skip-permissions --mcp-config '' --model haiku -p "$prompt" 2>&1)
98
96
  local ai_exit_code=$?
99
97
  ;;
100
98
  gpt5high)
101
99
  local ai_output
102
- ai_output=$(timeout -k 30 600 codex exec -m "$codex_gpt5_model" -c model_reasoning_effort="high" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
100
+ ai_output=$(codex exec -m "$codex_gpt5_model" -c model_reasoning_effort="high" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
103
101
  local ai_exit_code=$?
104
102
  ;;
105
103
  gpt5)
106
104
  local ai_output
107
- ai_output=$(timeout -k 30 600 codex exec -m "$codex_gpt5_model" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
105
+ ai_output=$(codex exec -m "$codex_gpt5_model" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
108
106
  local ai_exit_code=$?
109
107
  ;;
110
108
  o3high)
111
109
  local ai_output
112
- ai_output=$(timeout -k 30 600 codex exec -m o3-mini -c model_reasoning_effort="high" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
110
+ ai_output=$(codex exec -m o3-mini -c model_reasoning_effort="high" --dangerously-bypass-approvals-and-sandbox "$prompt" 2>&1)
113
111
  local ai_exit_code=$?
114
112
  ;;
115
113
  gemini-pro)
116
114
  local ai_output
117
- # Gemini needs longer timeout as it streams output while working (20 minutes)
118
- ai_output=$(timeout -k 30 1800 gemini -y -m gemini-3-pro-preview -p "$prompt" 2>&1)
115
+ # Gemini streams output while working
116
+ ai_output=$(gemini -y -m gemini-3-pro-preview -p "$prompt" 2>&1)
119
117
  local ai_exit_code=$?
120
118
  ;;
121
119
  gemini-flash)
122
120
  local ai_output
123
- # Gemini needs longer timeout as it streams output while working (20 minutes)
124
- ai_output=$(timeout -k 30 1200 gemini -y -m gemini-2.5-flash -p "$prompt" 2>&1)
121
+ # Gemini streams output while working
122
+ ai_output=$(gemini -y -m gemini-2.5-flash -p "$prompt" 2>&1)
125
123
  local ai_exit_code=$?
126
124
  ;;
127
125
  gemini-3-pro-preview)
128
126
  local ai_output
129
- # Gemini v3 Pro Preview via OpenRouter (30 minute timeout) - EXPENSIVE
130
- ai_output=$(timeout -k 30 1800 opencode -m openrouter/google/gemini-3-pro-preview run "$prompt" 2>&1)
127
+ # Gemini v3 Pro Preview via OpenRouter - EXPENSIVE
128
+ ai_output=$(opencode -m openrouter/google/gemini-3-pro-preview run "$prompt" 2>&1)
131
129
  local ai_exit_code=$?
132
130
  ;;
133
131
  gemini-3-flash)
134
132
  local ai_output
135
133
  # Gemini 3 Flash - fast, cheap, strong thinker
136
- ai_output=$(timeout -k 30 600 opencode -m openrouter/google/gemini-3-flash-preview run "$prompt" 2>&1)
134
+ ai_output=$(opencode -m openrouter/google/gemini-3-flash-preview run "$prompt" 2>&1)
137
135
  local ai_exit_code=$?
138
136
  ;;
139
137
  cursor-sonnet)
140
138
  local ai_output
141
- ai_output=$(timeout -k 30 600 cursor-agent sonnet-4.5 -p "$prompt" 2>&1)
139
+ ai_output=$(cursor-agent sonnet-4.5 -p "$prompt" 2>&1)
142
140
  local ai_exit_code=$?
143
141
  ;;
144
142
  cursor-opus)
145
143
  local ai_output
146
- ai_output=$(timeout -k 30 600 cursor-agent opus -p "$prompt" 2>&1)
144
+ ai_output=$(cursor-agent opus -p "$prompt" 2>&1)
147
145
  local ai_exit_code=$?
148
146
  ;;
149
147
  glm-openrouter)
150
148
  local ai_output
151
- ai_output=$(timeout -k 30 600 opencode -m openrouter/z-ai/glm-4.7 run "$prompt" 2>&1)
149
+ ai_output=$(opencode -m openrouter/z-ai/glm-4.7 run "$prompt" 2>&1)
152
150
  local ai_exit_code=$?
153
151
  ;;
154
152
  glm-5)
155
153
  local ai_output
156
154
  # GLM-5: 744B MoE model, very cheap ($0.80/$2.56 per 1M tokens), 200K context
157
155
  # Released Feb 2026 - scores 77.8% SWE-bench, MIT license
158
- ai_output=$(timeout -k 30 600 opencode -m openrouter/z-ai/glm-5 run "$prompt" 2>&1)
156
+ ai_output=$(opencode -m openrouter/z-ai/glm-5 run "$prompt" 2>&1)
159
157
  local ai_exit_code=$?
160
158
  ;;
161
159
  glm-zai)
162
160
  # GLM 4.7 via Z.AI agentic mode -- can be slow sometimes
163
161
  local ai_output
164
- ai_output=$(timeout -k 30 1800 opencode -m zai-coding-plan/glm-4.7 run "$prompt" 2>&1)
162
+ ai_output=$(opencode -m zai-coding-plan/glm-4.7 run "$prompt" 2>&1)
165
163
  local ai_exit_code=$?
166
164
  ;;
167
165
  glm-5-zai)
168
166
  # GLM-5 via Z.AI agentic mode - supports file editing for ideation
169
167
  # 744B MoE, strong reasoning, can edit files
170
168
  local ai_output
171
- ai_output=$(timeout -k 30 1800 opencode -m zai-coding-plan/glm-5 run "$prompt" 2>&1)
169
+ ai_output=$(opencode -m zai-coding-plan/glm-5 run "$prompt" 2>&1)
172
170
  local ai_exit_code=$?
173
171
  ;;
174
172
  deepseek-openrouter)
175
173
  local ai_output
176
- ai_output=$(timeout -k 30 600 opencode -m openrouter/deepseek/deepseek-v3.2 run "$prompt" 2>&1)
174
+ ai_output=$(opencode -m openrouter/deepseek/deepseek-v3.2 run "$prompt" 2>&1)
177
175
  local ai_exit_code=$?
178
176
  ;;
179
177
  grok-code-fast-openrouter)
180
178
  local ai_output
181
- ai_output=$(timeout -k 30 600 opencode -m openrouter/x-ai/grok-code-fast-1 run "$prompt" 2>&1)
179
+ ai_output=$(opencode -m openrouter/x-ai/grok-code-fast-1 run "$prompt" 2>&1)
182
180
  local ai_exit_code=$?
183
181
  ;;
184
182
  grok-4-openrouter)
185
183
  local ai_output
186
184
  # EXPENSIVE - consider grok-4.1-fast instead
187
- ai_output=$(timeout -k 30 600 opencode -m openrouter/x-ai/grok-4 run "$prompt" 2>&1)
185
+ ai_output=$(opencode -m openrouter/x-ai/grok-4 run "$prompt" 2>&1)
188
186
  local ai_exit_code=$?
189
187
  ;;
190
188
  grok-4.1-fast)
191
189
  local ai_output
192
190
  # Grok 4.1 Fast - close to Grok 4 quality, much cheaper
193
- ai_output=$(timeout -k 30 600 opencode -m openrouter/x-ai/grok-4.1-fast run "$prompt" 2>&1)
191
+ ai_output=$(opencode -m openrouter/x-ai/grok-4.1-fast run "$prompt" 2>&1)
194
192
  local ai_exit_code=$?
195
193
  ;;
196
194
  opus-openrouter)
197
195
  local ai_output
198
- ai_output=$(timeout -k 30 600 opencode -m openrouter/anthropic/claude-opus-4.1 run "$prompt" 2>&1)
196
+ ai_output=$(opencode -m openrouter/anthropic/claude-opus-4.1 run "$prompt" 2>&1)
199
197
  local ai_exit_code=$?
200
198
  ;;
201
199
  kimi-k2-openrouter)
202
200
  local ai_output
203
201
  # Kimi K2 Thinking via OpenRouter (no separate auth needed)
204
- ai_output=$(timeout -k 30 600 opencode -m openrouter/moonshotai/kimi-k2-thinking run "$prompt" 2>&1)
202
+ ai_output=$(opencode -m openrouter/moonshotai/kimi-k2-thinking run "$prompt" 2>&1)
205
203
  local ai_exit_code=$?
206
204
  ;;
207
205
  kimi-k2-think-moonshot)
208
206
  local ai_output
209
207
  # Use kimi CLI directly (assumes kimi is installed and configured)
210
- ai_output=$(timeout -k 30 600 kimi --print -c "$prompt" 2>&1)
208
+ ai_output=$(kimi --print -c "$prompt" 2>&1)
211
209
  local ai_exit_code=$?
212
210
  ;;
213
211
  kimi-coder)
214
212
  local ai_output
215
213
  # Kimi for Coding model via kimi CLI (fast coding-focused model)
216
214
  # Use --print to see agent actions while still allowing file modifications
217
- ai_output=$(timeout -k 30 600 kimi --print -y -m kimi-for-coding -c "$prompt" 2>&1)
215
+ ai_output=$(kimi --print -y -m kimi-for-coding -c "$prompt" 2>&1)
218
216
  local ai_exit_code=$?
219
217
  ;;
220
218
  kimi-k2.5)
221
219
  local ai_output
222
220
  # Kimi K2.5 - Moonshot's most powerful model (Jan 2025)
223
221
  # Native multimodal agentic model, stronger than GLM-4.7
224
- ai_output=$(timeout -k 30 600 opencode -m openrouter/moonshotai/kimi-k2.5 run "$prompt" 2>&1)
222
+ ai_output=$(opencode -m openrouter/moonshotai/kimi-k2.5 run "$prompt" 2>&1)
225
223
  local ai_exit_code=$?
226
224
  ;;
227
225
  qwen)
228
226
  local ai_output
229
227
  # Qwen latest - Alibaba's flagship model (currently qwen3.5-plus)
230
228
  # Linear attention + sparse MoE, strong multimodal capabilities
231
- ai_output=$(timeout -k 30 600 opencode -m openrouter/qwen/qwen3.5-plus-02-15 run "$prompt" 2>&1)
229
+ ai_output=$(opencode -m openrouter/qwen/qwen3.5-plus-02-15 run "$prompt" 2>&1)
232
230
  local ai_exit_code=$?
233
231
  ;;
234
232
  codex-oss-local)
235
233
  # Codex-OSS via Codex CLI with Ollama backend
236
234
  local ai_output
237
- ai_output=$(timeout -k 30 2400 codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --oss --local-provider=ollama "$prompt" 2>&1)
235
+ ai_output=$(codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --oss --local-provider=ollama "$prompt" 2>&1)
238
236
  local ai_exit_code=$?
239
237
  ;;
240
238
  deepseek-v3-llamacloud)
241
239
  # Deepseek via Codex CLI with Ollama cloud backend
242
240
  local ai_output
243
- 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)
241
+ ai_output=$(codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --oss -m deepseek-v3.1:671b-cloud "$prompt" 2>&1)
244
242
  local ai_exit_code=$?
245
243
  ;;
246
244
  esac
package/lib/ai_cli.py CHANGED
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Python wrapper around ai-cli.sh for AI model invocation.
4
- AIDEV-NOTE: This keeps ai-cli.sh as the source of truth for model configs and timeouts.
4
+ AIDEV-NOTE: ai-cli.sh handles model commands, Python handles timeouts.
5
+ Timeouts were moved from bash to Python because the bash `timeout` command
6
+ causes claude CLI and gemini CLI to hang in nested subprocess contexts.
5
7
  """
6
8
 
7
9
  import os
8
10
  import random
11
+ import signal
9
12
  import subprocess
10
13
  import sys
11
14
  import tempfile
@@ -234,6 +237,29 @@ def get_fallback_models_for_command(command: str) -> List[str]:
234
237
  return model_list.split()
235
238
 
236
239
 
240
+ # AIDEV-NOTE: Model timeout lookup. Timeouts are handled here in Python, not
241
+ # in bash, because the bash `timeout` command causes claude CLI (and sometimes
242
+ # gemini CLI) to hang when called from nested subprocess contexts.
243
+ MODEL_TIMEOUTS = {
244
+ # Claude models - 5 minutes for standard, 30 minutes for thinking
245
+ 'opus': 300, 'sonnet': 300, 'haiku': 300,
246
+ 'opus-think': 1800, 'sonnet-think': 1800,
247
+ # Gemini - 30 min for pro (streams while working), 20 min for flash
248
+ 'gemini-pro': 1800, 'gemini-flash': 1200, 'gemini-3-flash': 600,
249
+ 'gemini-3-pro-preview': 1800,
250
+ # Z.AI agentic modes - 30 min (can be slow)
251
+ 'glm-zai': 1800, 'glm-5-zai': 1800,
252
+ # Codex local - 40 min (local inference can be slow)
253
+ 'codex-oss-local': 2400,
254
+ }
255
+ DEFAULT_MODEL_TIMEOUT = 600 # 10 minutes for everything else
256
+
257
+
258
+ def get_model_timeout(model_name: str) -> int:
259
+ """Get the timeout in seconds for a given model."""
260
+ return MODEL_TIMEOUTS.get(model_name, DEFAULT_MODEL_TIMEOUT)
261
+
262
+
237
263
  def call_ai_model(
238
264
  prompt: str,
239
265
  model_name: str,
@@ -243,6 +269,10 @@ def call_ai_model(
243
269
  """
244
270
  Call a specific AI model.
245
271
 
272
+ AIDEV-NOTE: Timeouts are handled here in Python using subprocess.Popen +
273
+ process group kill. The bash timeout command was removed because it causes
274
+ claude CLI and gemini CLI to hang in nested subprocess contexts.
275
+
246
276
  Args:
247
277
  prompt: The prompt to send to the AI
248
278
  model_name: The specific model to use
@@ -268,18 +298,34 @@ def call_ai_model(
268
298
  if env_vars:
269
299
  env.update(env_vars)
270
300
 
301
+ timeout_secs = get_model_timeout(model_name)
302
+
271
303
  try:
272
- result = subprocess.run(
304
+ # Use Popen with process group so we can kill all children on timeout
305
+ proc = subprocess.Popen(
273
306
  ["bash", "-c", bash_script, "bash", model_name, prompt],
274
- capture_output=True,
307
+ stdout=subprocess.PIPE,
308
+ stderr=subprocess.PIPE,
275
309
  text=True,
276
310
  cwd=working_dir,
277
- env=env
311
+ env=env,
312
+ start_new_session=True, # Creates new process group
278
313
  )
279
314
 
280
- output = result.stdout
281
- stderr = result.stderr
282
- exit_code = result.returncode
315
+ try:
316
+ output, stderr = proc.communicate(timeout=timeout_secs)
317
+ exit_code = proc.returncode
318
+ except subprocess.TimeoutExpired:
319
+ # Kill the entire process group
320
+ try:
321
+ os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
322
+ time.sleep(2)
323
+ os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
324
+ except (ProcessLookupError, OSError):
325
+ pass
326
+ proc.kill()
327
+ proc.wait()
328
+ raise TimeoutError(f"AI call timed out (model: {model_name})")
283
329
 
284
330
  # Print stderr (contains debug info)
285
331
  if stderr:
@@ -299,7 +345,9 @@ def call_ai_model(
299
345
 
300
346
  return output, model_name
301
347
 
302
- except subprocess.SubprocessError as e:
348
+ except (subprocess.SubprocessError, OSError) as e:
349
+ if isinstance(e, subprocess.TimeoutExpired):
350
+ raise TimeoutError(f"AI call timed out (model: {model_name})")
303
351
  raise AIError(f"Failed to call AI: {e}")
304
352
 
305
353
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.11.13",
3
+ "version": "1.11.14",
4
4
  "bin": {
5
5
  "claude-evolve": "bin/claude-evolve",
6
6
  "claude-evolve-main": "bin/claude-evolve-main",