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/__pycache__/ai_cli.cpython-314.pyc +0 -0
- package/lib/ai-cli.sh +40 -42
- package/lib/ai_cli.py +56 -8
- package/package.json +1 -1
|
Binary file
|
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:
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
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=$(
|
|
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=$(
|
|
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
|
|
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=$(
|
|
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
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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
|
|
118
|
-
ai_output=$(
|
|
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
|
|
124
|
-
ai_output=$(
|
|
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
|
|
130
|
-
ai_output=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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=$(
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
|