stigmergy 1.0.68 → 1.0.70
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/README.en.md +306 -300
- package/README.md +469 -301
- package/package.json +97 -81
- package/scripts/publish.js +268 -0
- package/scripts/simple-publish.js +59 -0
- package/src/index.js +12 -0
- package/test/enhanced-main-alignment.test.js +298 -0
- package/test/hook-system-integration-test.js +307 -0
- package/test/natural-language-skills-test.js +320 -0
- package/test/nl-integration-test.js +179 -0
- package/test/parameter-parsing-test.js +143 -0
- package/test/real-test.js +435 -0
- package/test/system-compatibility-test.js +447 -0
- package/test/tdd-fixes-test.js +211 -0
- package/test/third-party-skills-test.js +321 -0
- package/test/tool-selection-integration-test.js +157 -0
- package/test/unit/cli-scanner.test.js +291 -0
- package/test/unit/cross-cli-executor.test.js +399 -0
- package/src/adapters/claude/__init__.py +0 -13
- package/src/adapters/claude/claude_skills_integration.py +0 -609
- package/src/adapters/claude/hook_adapter.py +0 -663
- package/src/adapters/claude/install_claude_integration.py +0 -265
- package/src/adapters/claude/skills_hook_adapter.py +0 -841
- package/src/adapters/claude/standalone_claude_adapter.py +0 -384
- package/src/adapters/cline/__init__.py +0 -20
- package/src/adapters/cline/config.py +0 -108
- package/src/adapters/cline/install_cline_integration.py +0 -617
- package/src/adapters/cline/mcp_server.py +0 -713
- package/src/adapters/cline/standalone_cline_adapter.py +0 -459
- package/src/adapters/codebuddy/__init__.py +0 -13
- package/src/adapters/codebuddy/buddy_adapter.py +0 -1125
- package/src/adapters/codebuddy/install_codebuddy_integration.py +0 -279
- package/src/adapters/codebuddy/skills_hook_adapter.py +0 -672
- package/src/adapters/codebuddy/skills_integration.py +0 -395
- package/src/adapters/codebuddy/standalone_codebuddy_adapter.py +0 -403
- package/src/adapters/codex/__init__.py +0 -11
- package/src/adapters/codex/base.py +0 -46
- package/src/adapters/codex/install_codex_integration.py +0 -311
- package/src/adapters/codex/mcp_server.py +0 -493
- package/src/adapters/codex/natural_language_parser.py +0 -82
- package/src/adapters/codex/slash_command_adapter.py +0 -326
- package/src/adapters/codex/standalone_codex_adapter.py +0 -362
- package/src/adapters/copilot/__init__.py +0 -13
- package/src/adapters/copilot/install_copilot_integration.py +0 -564
- package/src/adapters/copilot/mcp_adapter.py +0 -772
- package/src/adapters/copilot/mcp_server.py +0 -168
- package/src/adapters/copilot/standalone_copilot_adapter.py +0 -114
- package/src/adapters/gemini/__init__.py +0 -13
- package/src/adapters/gemini/extension_adapter.py +0 -690
- package/src/adapters/gemini/install_gemini_integration.py +0 -257
- package/src/adapters/gemini/standalone_gemini_adapter.py +0 -366
- package/src/adapters/iflow/__init__.py +0 -7
- package/src/adapters/iflow/hook_adapter.py +0 -1038
- package/src/adapters/iflow/hook_installer.py +0 -536
- package/src/adapters/iflow/install_iflow_integration.py +0 -271
- package/src/adapters/iflow/official_hook_adapter.py +0 -1272
- package/src/adapters/iflow/standalone_iflow_adapter.py +0 -48
- package/src/adapters/iflow/workflow_adapter.py +0 -793
- package/src/adapters/qoder/hook_installer.py +0 -732
- package/src/adapters/qoder/install_qoder_integration.py +0 -265
- package/src/adapters/qoder/notification_hook_adapter.py +0 -863
- package/src/adapters/qoder/standalone_qoder_adapter.py +0 -48
- package/src/adapters/qwen/__init__.py +0 -17
- package/src/adapters/qwencode/__init__.py +0 -13
- package/src/adapters/qwencode/inheritance_adapter.py +0 -818
- package/src/adapters/qwencode/install_qwencode_integration.py +0 -276
- package/src/adapters/qwencode/standalone_qwencode_adapter.py +0 -399
- package/src/atomic_collaboration_handler.py +0 -461
- package/src/cli_collaboration_agent.py +0 -697
- package/src/collaboration/hooks.py +0 -315
- package/src/core/__init__.py +0 -21
- package/src/core/ai_environment_scanner.py +0 -331
- package/src/core/base_adapter.py +0 -220
- package/src/core/cli_hook_integration.py +0 -406
- package/src/core/cross_cli_executor.py +0 -713
- package/src/core/cross_cli_mapping.py +0 -1165
- package/src/core/cross_platform_encoding.py +0 -365
- package/src/core/cross_platform_safe_cli.py +0 -894
- package/src/core/direct_cli_executor.py +0 -805
- package/src/core/direct_cli_hook_system.py +0 -958
- package/src/core/enhanced_init_processor.py +0 -467
- package/src/core/graceful_cli_executor.py +0 -912
- package/src/core/md_enhancer.py +0 -342
- package/src/core/md_generator.py +0 -619
- package/src/core/models.py +0 -218
- package/src/core/parser.py +0 -108
- package/src/core/real_cli_hook_system.py +0 -852
- package/src/core/real_cross_cli_system.py +0 -925
- package/src/core/verified_cross_cli_system.py +0 -961
- package/src/deploy.js +0 -737
- package/src/enhanced-main.js +0 -626
- package/src/enhanced_deploy.js +0 -303
- package/src/enhanced_universal_cli_setup.py +0 -930
- package/src/kimi_wrapper.py +0 -104
- package/src/main.js +0 -1309
- package/src/shell_integration.py +0 -398
- package/src/simple-main.js +0 -315
- package/src/smart_router_creator.py +0 -323
- package/src/universal_cli_setup.py +0 -1289
- package/src/utils/__init__.py +0 -12
- package/src/utils/cli_detector.py +0 -445
- package/src/utils/file_utils.py +0 -246
|
@@ -1,805 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
直接跨CLI执行器 - 针对已全局安装CLI的协作系统
|
|
3
|
-
严格基于真实研究,严禁推测
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
import sys
|
|
8
|
-
import json
|
|
9
|
-
import subprocess
|
|
10
|
-
import shutil
|
|
11
|
-
import time
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Dict, List, Optional, Tuple, Any
|
|
14
|
-
from dataclasses import dataclass, asdict
|
|
15
|
-
from datetime import datetime
|
|
16
|
-
import tempfile
|
|
17
|
-
import re
|
|
18
|
-
|
|
19
|
-
# 导入编码安全模块
|
|
20
|
-
sys.path.append(str(Path(__file__).parent))
|
|
21
|
-
from cross_platform_encoding import safe_file_write, safe_file_read
|
|
22
|
-
from cross_platform_safe_cli import safe_cli_execute
|
|
23
|
-
|
|
24
|
-
class DirectCLIExecutor:
|
|
25
|
-
"""直接CLI执行器 - 基于已安装CLI的协作系统"""
|
|
26
|
-
|
|
27
|
-
def __init__(self, memory_dir: str = None):
|
|
28
|
-
self.memory_dir = Path(memory_dir or Path.home() / '.stigmergy_direct_cli')
|
|
29
|
-
self.memory_dir.mkdir(parents=True, exist_ok=True)
|
|
30
|
-
|
|
31
|
-
# 基于真实架构的CLI调用命令(不需要安装)
|
|
32
|
-
self.cli_commands = {
|
|
33
|
-
'claude': {
|
|
34
|
-
'name': 'Claude Code CLI',
|
|
35
|
-
'commands': [
|
|
36
|
-
'npx @anthropic/claude-code',
|
|
37
|
-
'claude-code', # 如果已安装到PATH
|
|
38
|
-
'@anthropic/claude-code'
|
|
39
|
-
],
|
|
40
|
-
'type': 'npm',
|
|
41
|
-
'api_key_env': 'ANTHROPIC_API_KEY',
|
|
42
|
-
'timeout': 120
|
|
43
|
-
},
|
|
44
|
-
'gemini': {
|
|
45
|
-
'name': 'Gemini CLI',
|
|
46
|
-
'commands': [
|
|
47
|
-
'npx @google/gemini-cli',
|
|
48
|
-
'gemini-cli',
|
|
49
|
-
'@google/gemini-cli'
|
|
50
|
-
],
|
|
51
|
-
'type': 'npm',
|
|
52
|
-
'api_key_env': 'GOOGLE_AI_API_KEY',
|
|
53
|
-
'timeout': 120
|
|
54
|
-
},
|
|
55
|
-
'qwencode': {
|
|
56
|
-
'name': 'Qwen Code CLI',
|
|
57
|
-
'commands': [
|
|
58
|
-
'qwencode',
|
|
59
|
-
'python -m qwencode'
|
|
60
|
-
],
|
|
61
|
-
'type': 'python',
|
|
62
|
-
'api_key_env': 'QWEN_API_KEY',
|
|
63
|
-
'timeout': 120
|
|
64
|
-
},
|
|
65
|
-
'copilot': {
|
|
66
|
-
'name': 'GitHub Copilot CLI',
|
|
67
|
-
'commands': [
|
|
68
|
-
'copilot',
|
|
69
|
-
'github-copilot'
|
|
70
|
-
],
|
|
71
|
-
'type': 'npm',
|
|
72
|
-
'api_key_env': 'GITHUB_TOKEN',
|
|
73
|
-
'timeout': 120
|
|
74
|
-
},
|
|
75
|
-
'iflow': {
|
|
76
|
-
'name': 'iFlow CLI',
|
|
77
|
-
'commands': [
|
|
78
|
-
'iflow',
|
|
79
|
-
'iflow-cli'
|
|
80
|
-
],
|
|
81
|
-
'type': 'npm',
|
|
82
|
-
'api_key_env': 'IFLOW_API_KEY',
|
|
83
|
-
'timeout': 120
|
|
84
|
-
},
|
|
85
|
-
'qoder': {
|
|
86
|
-
'name': 'Qoder CLI',
|
|
87
|
-
'commands': [
|
|
88
|
-
'qoder',
|
|
89
|
-
'python -m qoder'
|
|
90
|
-
],
|
|
91
|
-
'type': 'python',
|
|
92
|
-
'api_key_env': 'QODER_API_KEY',
|
|
93
|
-
'timeout': 120
|
|
94
|
-
},
|
|
95
|
-
'codebuddy': {
|
|
96
|
-
'name': 'CodeBuddy CLI',
|
|
97
|
-
'commands': [
|
|
98
|
-
'codebuddy',
|
|
99
|
-
'codebuddy-code'
|
|
100
|
-
],
|
|
101
|
-
'type': 'npm',
|
|
102
|
-
'api_key_env': 'CODEBUDDY_API_KEY',
|
|
103
|
-
'timeout': 120
|
|
104
|
-
},
|
|
105
|
-
'codex': {
|
|
106
|
-
'name': 'OpenAI Codex CLI',
|
|
107
|
-
'commands': [
|
|
108
|
-
'codex',
|
|
109
|
-
'openai-codex'
|
|
110
|
-
],
|
|
111
|
-
'type': 'binary',
|
|
112
|
-
'api_key_env': 'OPENAI_API_KEY',
|
|
113
|
-
'timeout': 120
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
# 初始化记忆文件
|
|
118
|
-
self.global_memory_file = self.memory_dir / 'global_cli_memory.json'
|
|
119
|
-
self.success_patterns_file = self.memory_dir / 'success_patterns.json'
|
|
120
|
-
self.cli_status_file = self.memory_dir / 'cli_status.json'
|
|
121
|
-
self.command_preference_file = self.memory_dir / 'command_preferences.json'
|
|
122
|
-
|
|
123
|
-
self._initialize_memory()
|
|
124
|
-
|
|
125
|
-
def _initialize_memory(self):
|
|
126
|
-
"""初始化记忆系统"""
|
|
127
|
-
if not self.global_memory_file.exists():
|
|
128
|
-
safe_file_write(self.global_memory_file, json.dumps({
|
|
129
|
-
'last_updated': datetime.now().isoformat(),
|
|
130
|
-
'cli_knowledge': {},
|
|
131
|
-
'collaboration_patterns': {},
|
|
132
|
-
'version': '1.0.0'
|
|
133
|
-
}, indent=2, ensure_ascii=False))
|
|
134
|
-
|
|
135
|
-
if not self.success_patterns_file.exists():
|
|
136
|
-
safe_file_write(self.success_patterns_file, json.dumps({
|
|
137
|
-
'successful_calls': [],
|
|
138
|
-
'failed_calls': [],
|
|
139
|
-
'optimal_patterns': {},
|
|
140
|
-
'version': '1.0.0'
|
|
141
|
-
}, indent=2, ensure_ascii=False))
|
|
142
|
-
|
|
143
|
-
if not self.cli_status_file.exists():
|
|
144
|
-
safe_file_write(self.cli_status_file, json.dumps({
|
|
145
|
-
'available_clis': {},
|
|
146
|
-
'last_check': None,
|
|
147
|
-
'version': '1.0.0'
|
|
148
|
-
}, indent=2, ensure_ascii=False))
|
|
149
|
-
|
|
150
|
-
if not self.command_preference_file.exists():
|
|
151
|
-
safe_file_write(self.command_preference_file, json.dumps({
|
|
152
|
-
'preferred_commands': {},
|
|
153
|
-
'fallback_commands': {},
|
|
154
|
-
'version': '1.0.0'
|
|
155
|
-
}, indent=2, ensure_ascii=False))
|
|
156
|
-
|
|
157
|
-
def check_cli_availability(self, cli_name: str) -> Tuple[bool, str, str]:
|
|
158
|
-
"""检查CLI可用性并返回最佳命令"""
|
|
159
|
-
if cli_name not in self.cli_commands:
|
|
160
|
-
return False, f"Unknown CLI: {cli_name}", ""
|
|
161
|
-
|
|
162
|
-
cli_info = self.cli_commands[cli_name]
|
|
163
|
-
preferred_commands = self._get_preferred_commands(cli_name)
|
|
164
|
-
|
|
165
|
-
# 按优先级检查命令
|
|
166
|
-
for command in preferred_commands + cli_info['commands']:
|
|
167
|
-
try:
|
|
168
|
-
# 提取基础命令进行测试
|
|
169
|
-
base_command = command.split()[0]
|
|
170
|
-
result = subprocess.run(
|
|
171
|
-
f"where {base_command}" if os.name == 'nt' else f"which {base_command}",
|
|
172
|
-
shell=True,
|
|
173
|
-
capture_output=True,
|
|
174
|
-
text=True,
|
|
175
|
-
timeout=5
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
if result.returncode == 0:
|
|
179
|
-
# 进一步验证命令完整性
|
|
180
|
-
try:
|
|
181
|
-
test_result = subprocess.run(
|
|
182
|
-
f"{command} --version",
|
|
183
|
-
shell=True,
|
|
184
|
-
capture_output=True,
|
|
185
|
-
text=True,
|
|
186
|
-
timeout=10
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
# 记录成功的命令作为偏好
|
|
190
|
-
self._set_preferred_command(cli_name, command)
|
|
191
|
-
|
|
192
|
-
if test_result.returncode == 0:
|
|
193
|
-
return True, command, f"Available (v{test_result.stdout.strip()})"
|
|
194
|
-
else:
|
|
195
|
-
return True, command, "Available (version check failed but command exists)"
|
|
196
|
-
except:
|
|
197
|
-
# 即使版本检查失败,命令也存在
|
|
198
|
-
self._set_preferred_command(cli_name, command)
|
|
199
|
-
return True, command, "Available"
|
|
200
|
-
|
|
201
|
-
except subprocess.TimeoutExpired:
|
|
202
|
-
continue
|
|
203
|
-
except:
|
|
204
|
-
continue
|
|
205
|
-
|
|
206
|
-
return False, "", f"{cli_info['name']} not found in system PATH"
|
|
207
|
-
|
|
208
|
-
def _get_preferred_commands(self, cli_name: str) -> List[str]:
|
|
209
|
-
"""获取用户偏好的命令"""
|
|
210
|
-
try:
|
|
211
|
-
preferences = json.loads(safe_file_read(self.command_preference_file))
|
|
212
|
-
return preferences.get('preferred_commands', {}).get(cli_name, [])
|
|
213
|
-
except:
|
|
214
|
-
return []
|
|
215
|
-
|
|
216
|
-
def _set_preferred_command(self, cli_name: str, command: str):
|
|
217
|
-
"""设置用户偏好的命令"""
|
|
218
|
-
try:
|
|
219
|
-
preferences = json.loads(safe_file_read(self.command_preference_file))
|
|
220
|
-
if cli_name not in preferences['preferred_commands']:
|
|
221
|
-
preferences['preferred_commands'][cli_name] = []
|
|
222
|
-
|
|
223
|
-
# 将成功的命令移到第一位
|
|
224
|
-
if command in preferences['preferred_commands'][cli_name]:
|
|
225
|
-
preferences['preferred_commands'][cli_name].remove(command)
|
|
226
|
-
preferences['preferred_commands'][cli_name].insert(0, command)
|
|
227
|
-
|
|
228
|
-
# 只保留前5个偏好命令
|
|
229
|
-
preferences['preferred_commands'][cli_name] = preferences['preferred_commands'][cli_name][:5]
|
|
230
|
-
|
|
231
|
-
safe_file_write(self.command_preference_file, json.dumps(preferences, indent=2, ensure_ascii=False))
|
|
232
|
-
except Exception as e:
|
|
233
|
-
print(f"Warning: Failed to set preferred command: {e}")
|
|
234
|
-
|
|
235
|
-
def _update_cli_status(self, cli_name: str, available: bool, command: str, message: str):
|
|
236
|
-
"""更新CLI状态"""
|
|
237
|
-
try:
|
|
238
|
-
status_data = json.loads(safe_file_read(self.cli_status_file))
|
|
239
|
-
status_data['available_clis'][cli_name] = {
|
|
240
|
-
'available': available,
|
|
241
|
-
'command': command,
|
|
242
|
-
'message': message,
|
|
243
|
-
'last_check': datetime.now().isoformat()
|
|
244
|
-
}
|
|
245
|
-
status_data['last_check'] = datetime.now().isoformat()
|
|
246
|
-
|
|
247
|
-
safe_file_write(self.cli_status_file, json.dumps(status_data, indent=2, ensure_ascii=False))
|
|
248
|
-
except Exception as e:
|
|
249
|
-
print(f"Warning: Failed to update CLI status: {e}")
|
|
250
|
-
|
|
251
|
-
def execute_direct_cli_call(self,
|
|
252
|
-
source_cli: str,
|
|
253
|
-
target_cli: str,
|
|
254
|
-
request: str,
|
|
255
|
-
context_files: List[str] = None,
|
|
256
|
-
working_dir: str = None,
|
|
257
|
-
use_fallback: bool = True) -> Dict[str, Any]:
|
|
258
|
-
"""执行直接CLI调用 - 无需安装"""
|
|
259
|
-
|
|
260
|
-
result = {
|
|
261
|
-
'success': False,
|
|
262
|
-
'response': '',
|
|
263
|
-
'error': '',
|
|
264
|
-
'command_used': '',
|
|
265
|
-
'fallback_used': False,
|
|
266
|
-
'timestamp': datetime.now().isoformat(),
|
|
267
|
-
'execution_time': 0
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
start_time = time.time()
|
|
271
|
-
|
|
272
|
-
# 检查目标CLI可用性
|
|
273
|
-
available, command, message = self.check_cli_availability(target_cli)
|
|
274
|
-
if not available:
|
|
275
|
-
if use_fallback:
|
|
276
|
-
return self._graceful_fallback(source_cli, target_cli, request, context_files, working_dir, f"CLI not available: {message}")
|
|
277
|
-
else:
|
|
278
|
-
result['error'] = f"CLI not available: {message}"
|
|
279
|
-
return result
|
|
280
|
-
|
|
281
|
-
cli_info = self.cli_commands[target_cli]
|
|
282
|
-
|
|
283
|
-
try:
|
|
284
|
-
# 构建执行命令
|
|
285
|
-
execution_command = self._build_direct_execution_command(command, cli_info, request, context_files, working_dir)
|
|
286
|
-
|
|
287
|
-
# 设置环境变量
|
|
288
|
-
env_vars = self._prepare_environment(target_cli)
|
|
289
|
-
|
|
290
|
-
# 执行命令
|
|
291
|
-
process_result = safe_cli_execute(
|
|
292
|
-
execution_command,
|
|
293
|
-
timeout=cli_info['timeout'],
|
|
294
|
-
work_dir=working_dir,
|
|
295
|
-
env_vars=env_vars
|
|
296
|
-
)
|
|
297
|
-
|
|
298
|
-
result['execution_time'] = time.time() - start_time
|
|
299
|
-
|
|
300
|
-
if process_result['success']:
|
|
301
|
-
result.update({
|
|
302
|
-
'success': True,
|
|
303
|
-
'response': process_result['stdout'],
|
|
304
|
-
'command_used': execution_command,
|
|
305
|
-
'stderr': process_result.get('stderr', '')
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
# 记录成功模式
|
|
309
|
-
self._record_success_pattern(source_cli, target_cli, request, execution_command, result['execution_time'])
|
|
310
|
-
else:
|
|
311
|
-
if use_fallback:
|
|
312
|
-
return self._graceful_fallback(source_cli, target_cli, request, context_files, working_dir, f"Execution failed: {process_result['stderr']}")
|
|
313
|
-
else:
|
|
314
|
-
result['error'] = process_result['stderr']
|
|
315
|
-
result['command_used'] = execution_command
|
|
316
|
-
|
|
317
|
-
except Exception as e:
|
|
318
|
-
result['execution_time'] = time.time() - start_time
|
|
319
|
-
if use_fallback:
|
|
320
|
-
return self._graceful_fallback(source_cli, target_cli, request, context_files, working_dir, f"Execution error: {str(e)}")
|
|
321
|
-
else:
|
|
322
|
-
result['error'] = f"Execution error: {str(e)}"
|
|
323
|
-
|
|
324
|
-
return result
|
|
325
|
-
|
|
326
|
-
def _build_direct_execution_command(self, base_command: str, cli_info: Dict[str, Any], request: str, context_files: List[str] = None, working_dir: str = None) -> str:
|
|
327
|
-
"""基于已安装CLI构建执行命令"""
|
|
328
|
-
|
|
329
|
-
# 构建命令部分
|
|
330
|
-
if cli_info['type'] == 'npm' and 'npx' in base_command:
|
|
331
|
-
# npx命令
|
|
332
|
-
command_parts = base_command.split()
|
|
333
|
-
else:
|
|
334
|
-
# 其他直接命令
|
|
335
|
-
command_parts = [base_command]
|
|
336
|
-
|
|
337
|
-
# 根据不同CLI的参数格式添加请求
|
|
338
|
-
cli_name = next(k for k, v in self.cli_commands.items() if v['name'] == cli_info['name'])
|
|
339
|
-
|
|
340
|
-
if cli_name in ['claude', 'gemini', 'copilot']:
|
|
341
|
-
# npm类型的CLI通常使用 -- 或直接传递参数
|
|
342
|
-
if '"' in request:
|
|
343
|
-
command_parts.extend(['--', request])
|
|
344
|
-
else:
|
|
345
|
-
command_parts.append(request)
|
|
346
|
-
elif cli_name in ['qwencode', 'qoder']:
|
|
347
|
-
# Python类型的CLI通常使用 --prompt 或 -p
|
|
348
|
-
command_parts.extend(['--prompt', request])
|
|
349
|
-
elif cli_name == 'iflow':
|
|
350
|
-
# iFlow直接接受自然语言
|
|
351
|
-
command_parts.append(request)
|
|
352
|
-
elif cli_name == 'codebuddy':
|
|
353
|
-
# CodeBuddy使用自然语言
|
|
354
|
-
command_parts.append(request)
|
|
355
|
-
elif cli_name == 'codex':
|
|
356
|
-
# Codex CLI参数格式
|
|
357
|
-
command_parts.extend(['--request', request])
|
|
358
|
-
|
|
359
|
-
# 添加文件引用
|
|
360
|
-
if context_files:
|
|
361
|
-
for file_path in context_files:
|
|
362
|
-
if os.path.exists(file_path):
|
|
363
|
-
command_parts.extend(['--file', file_path])
|
|
364
|
-
|
|
365
|
-
# 添加工作目录
|
|
366
|
-
if working_dir:
|
|
367
|
-
command_parts.extend(['--cwd', working_dir])
|
|
368
|
-
|
|
369
|
-
# 构建完整命令
|
|
370
|
-
if cli_info['type'] == 'npm' and 'npx' in base_command:
|
|
371
|
-
# npx命令需要特殊处理
|
|
372
|
-
full_command = f"{command_parts[0]} {' '.join(command_parts[1:])}"
|
|
373
|
-
else:
|
|
374
|
-
full_command = ' '.join(command_parts)
|
|
375
|
-
|
|
376
|
-
return full_command
|
|
377
|
-
|
|
378
|
-
def _prepare_environment(self, cli_name: str) -> Dict[str, str]:
|
|
379
|
-
"""准备CLI执行环境"""
|
|
380
|
-
env = os.environ.copy()
|
|
381
|
-
|
|
382
|
-
cli_info = self.cli_commands[cli_name]
|
|
383
|
-
|
|
384
|
-
# 设置API密钥环境变量(如果存在)
|
|
385
|
-
if cli_info.get('api_key_env') and cli_info['api_key_env'] in env:
|
|
386
|
-
pass # 已存在,无需修改
|
|
387
|
-
|
|
388
|
-
# 根据CLI类型设置特殊环境
|
|
389
|
-
if cli_info['type'] == 'npm':
|
|
390
|
-
# 确保Node.js相关环境
|
|
391
|
-
node_path = env.get('NODE_PATH', '')
|
|
392
|
-
npm_config_prefix = env.get('NPM_CONFIG_PREFIX', '')
|
|
393
|
-
|
|
394
|
-
# 确保npm全局包可执行
|
|
395
|
-
if not npm_config_prefix:
|
|
396
|
-
npm_config_prefix = os.path.join(os.path.expanduser('~'), '.npm-global')
|
|
397
|
-
env['NPM_CONFIG_PREFIX'] = npm_config_prefix
|
|
398
|
-
|
|
399
|
-
# 添加到PATH
|
|
400
|
-
global_bin_path = os.path.join(npm_config_prefix, 'bin')
|
|
401
|
-
if global_bin_path not in env.get('PATH', ''):
|
|
402
|
-
env['PATH'] = f"{global_bin_path}:{env.get('PATH', '')}"
|
|
403
|
-
|
|
404
|
-
elif cli_info['type'] == 'python':
|
|
405
|
-
# Python相关环境
|
|
406
|
-
python_path = env.get('PYTHONPATH', '')
|
|
407
|
-
user_base = os.path.join(os.path.expanduser('~'), '.local')
|
|
408
|
-
|
|
409
|
-
# 确保用户site-packages在PATH中
|
|
410
|
-
user_bin = os.path.join(user_base, 'bin')
|
|
411
|
-
if user_bin not in env.get('PATH', ''):
|
|
412
|
-
env['PATH'] = f"{user_bin}:{env.get('PATH', '')}"
|
|
413
|
-
|
|
414
|
-
return env
|
|
415
|
-
|
|
416
|
-
def _graceful_fallback(self, source_cli: str, target_cli: str, request: str, context_files: List[str] = None, working_dir: str = None, error_reason: str = "") -> Dict[str, Any]:
|
|
417
|
-
"""优雅降级 - 4级回退金字塔"""
|
|
418
|
-
|
|
419
|
-
result = {
|
|
420
|
-
'success': False,
|
|
421
|
-
'response': '',
|
|
422
|
-
'error': error_reason,
|
|
423
|
-
'fallback_used': True,
|
|
424
|
-
'timestamp': datetime.now().isoformat()
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
# Level 1: 生成等价命令(基于已安装CLI的模式)
|
|
428
|
-
try:
|
|
429
|
-
available_clis = self._get_available_clis()
|
|
430
|
-
cli_info = self.cli_commands[target_cli]
|
|
431
|
-
|
|
432
|
-
equivalent_command = self._build_direct_execution_command(
|
|
433
|
-
cli_info['commands'][0], # 使用第一个可能的命令
|
|
434
|
-
cli_info,
|
|
435
|
-
request,
|
|
436
|
-
context_files,
|
|
437
|
-
working_dir
|
|
438
|
-
)
|
|
439
|
-
|
|
440
|
-
result.update({
|
|
441
|
-
'success': True,
|
|
442
|
-
'response': f"Cannot execute {cli_info['name']} directly, but here's the equivalent command you can run manually:\\n\\n```bash\\n{equivalent_command}\\n```\\n\\nMake sure {cli_info['name']} is installed and accessible in your PATH.",
|
|
443
|
-
'execution_method': 'command_generation',
|
|
444
|
-
'fallback_level': 1,
|
|
445
|
-
'suggested_command': equivalent_command
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
self._record_fallback_pattern(source_cli, target_cli, request, equivalent_command, 'command_generation')
|
|
449
|
-
return result
|
|
450
|
-
|
|
451
|
-
except Exception as e:
|
|
452
|
-
error_reason = f"Command generation failed: {str(e)}"
|
|
453
|
-
|
|
454
|
-
# Level 2: 提供手动指导
|
|
455
|
-
try:
|
|
456
|
-
cli_info = self.cli_commands[target_cli]
|
|
457
|
-
guidance = self._generate_direct_manual_guidance(cli_info, request, context_files, working_dir)
|
|
458
|
-
|
|
459
|
-
result.update({
|
|
460
|
-
'success': True,
|
|
461
|
-
'response': guidance,
|
|
462
|
-
'execution_method': 'manual_guidance',
|
|
463
|
-
'fallback_level': 2
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
self._record_fallback_pattern(source_cli, target_cli, request, guidance, 'manual_guidance')
|
|
467
|
-
return result
|
|
468
|
-
|
|
469
|
-
except Exception as e:
|
|
470
|
-
error_reason = f"Manual guidance failed: {str(e)}"
|
|
471
|
-
|
|
472
|
-
# Level 3: 建议替代方案(基于可用的CLI)
|
|
473
|
-
try:
|
|
474
|
-
available_clis = self._get_available_clis()
|
|
475
|
-
alternatives = self._suggest_available_alternatives(source_cli, target_cli, request, available_clis)
|
|
476
|
-
|
|
477
|
-
result.update({
|
|
478
|
-
'success': True,
|
|
479
|
-
'response': alternatives,
|
|
480
|
-
'execution_method': 'alternatives',
|
|
481
|
-
'fallback_level': 3
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
self._record_fallback_pattern(source_cli, target_cli, request, alternatives, 'alternatives')
|
|
485
|
-
return result
|
|
486
|
-
|
|
487
|
-
except Exception as e:
|
|
488
|
-
error_reason = f"Alternatives failed: {str(e)}"
|
|
489
|
-
|
|
490
|
-
# Level 4: 错误信息和最低保障
|
|
491
|
-
result.update({
|
|
492
|
-
'success': False,
|
|
493
|
-
'response': f"Unable to process request with {target_cli}. Error: {error_reason}",
|
|
494
|
-
'execution_method': 'error_fallback',
|
|
495
|
-
'fallback_level': 4,
|
|
496
|
-
'error': error_reason
|
|
497
|
-
})
|
|
498
|
-
|
|
499
|
-
self._record_failure_pattern(source_cli, target_cli, request, error_reason)
|
|
500
|
-
return result
|
|
501
|
-
|
|
502
|
-
def _generate_direct_manual_guidance(self, cli_info: Dict[str, Any], request: str, context_files: List[str] = None, working_dir: str = None) -> str:
|
|
503
|
-
"""生成直接使用指导"""
|
|
504
|
-
|
|
505
|
-
cli_name = next(k for k, v in self.cli_commands.items() if v['name'] == cli_info['name'])
|
|
506
|
-
|
|
507
|
-
guidance = f"""# Manual Usage Guide for {cli_info['name']}
|
|
508
|
-
|
|
509
|
-
## Status Check:
|
|
510
|
-
Run one of these commands to check if the CLI is available:
|
|
511
|
-
"""
|
|
512
|
-
|
|
513
|
-
for command in cli_info['commands']:
|
|
514
|
-
guidance += f"```bash\\n{command} --version\\n```\\n\\n"
|
|
515
|
-
|
|
516
|
-
guidance += f"""
|
|
517
|
-
## Direct Command:
|
|
518
|
-
```bash
|
|
519
|
-
{self._build_direct_execution_command(cli_info['commands'][0], cli_info, request, context_files, working_dir)}
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
## Alternative Commands:
|
|
523
|
-
Try these if the main command doesn't work:
|
|
524
|
-
"""
|
|
525
|
-
|
|
526
|
-
for command in cli_info['commands'][1:3]: # 显示前2个替代命令
|
|
527
|
-
alt_command = self._build_direct_execution_command(command, cli_info, request, context_files, working_dir)
|
|
528
|
-
guidance += f"```bash\\n{alt_command}\\n```\\n\\n"
|
|
529
|
-
|
|
530
|
-
guidance += f"""
|
|
531
|
-
## API Key Setup (if required):
|
|
532
|
-
"""
|
|
533
|
-
|
|
534
|
-
if cli_info.get('api_key_env'):
|
|
535
|
-
guidance += f"Set environment variable: `{cli_info['api_key_env']}`\\n\\n"
|
|
536
|
-
guidance += f"```bash\\nexport {cli_info['api_key_env']}='your-api-key-here'\\n```\\n\\n"
|
|
537
|
-
|
|
538
|
-
guidance += """
|
|
539
|
-
## Troubleshooting:
|
|
540
|
-
1. Check if the CLI is in your system PATH
|
|
541
|
-
2. Verify API key configuration
|
|
542
|
-
3. Test with a simple command first
|
|
543
|
-
4. Check network connectivity
|
|
544
|
-
"""
|
|
545
|
-
|
|
546
|
-
return guidance
|
|
547
|
-
|
|
548
|
-
def _get_available_clis(self) -> Dict[str, str]:
|
|
549
|
-
"""获取所有可用的CLI"""
|
|
550
|
-
available = {}
|
|
551
|
-
|
|
552
|
-
for cli_name in self.cli_commands:
|
|
553
|
-
available, command, message = self.check_cli_availability(cli_name)
|
|
554
|
-
if available:
|
|
555
|
-
available[cli_name] = command
|
|
556
|
-
|
|
557
|
-
return available
|
|
558
|
-
|
|
559
|
-
def _suggest_available_alternatives(self, source_cli: str, target_cli: str, request: str, available_clis: Dict[str, str]) -> str:
|
|
560
|
-
"""建议可用的替代方案"""
|
|
561
|
-
|
|
562
|
-
alternatives = f"# Alternative Solutions for {self.cli_commands[target_cli]['name']}\\n\\n"
|
|
563
|
-
|
|
564
|
-
# 分析请求类型
|
|
565
|
-
request_lower = request.lower()
|
|
566
|
-
|
|
567
|
-
if 'code' in request_lower or 'programming' in request_lower:
|
|
568
|
-
alternatives += "## Available Code Generation CLIs:\\n\\n"
|
|
569
|
-
|
|
570
|
-
if available_clis:
|
|
571
|
-
for cli_name, command in available_clis.items():
|
|
572
|
-
if cli_name != target_cli:
|
|
573
|
-
cli_info = self.cli_commands[cli_name]
|
|
574
|
-
alternatives += f"### {cli_info['name']}\\n"
|
|
575
|
-
alternatives += f"- Command: `{cli_name}`\\n"
|
|
576
|
-
alternatives += f"- Usage: `{cli_name} \\\"your request\\\"`\\n\\n"
|
|
577
|
-
else:
|
|
578
|
-
alternatives += "- No alternative CLIs are currently available\\n"
|
|
579
|
-
alternatives += "- Consider installing an alternative CLI tool\\n"
|
|
580
|
-
alternatives += "- Use web-based code editors or IDE plugins\\n\\n"
|
|
581
|
-
|
|
582
|
-
elif 'file' in request_lower or 'analyze' in request_lower:
|
|
583
|
-
alternatives += "## Available Analysis Options:\\n\\n"
|
|
584
|
-
|
|
585
|
-
if available_clis:
|
|
586
|
-
for cli_name, command in available_clis.items():
|
|
587
|
-
if cli_name != target_cli:
|
|
588
|
-
cli_info = self.cli_commands[cli_name]
|
|
589
|
-
alternatives += f"### {cli_info['name']}\\n"
|
|
590
|
-
alternatives += f"- Command: `{cli_name} \\\"analyze this file\\\"`\\n\\n"
|
|
591
|
-
|
|
592
|
-
alternatives += "- Use built-in system commands: `cat`, `grep`, `find`\\n"
|
|
593
|
-
alternatives += "- Use IDE code analysis features\\n"
|
|
594
|
-
|
|
595
|
-
else:
|
|
596
|
-
alternatives += "## General Alternatives:\\n\\n"
|
|
597
|
-
|
|
598
|
-
if available_clis:
|
|
599
|
-
alternatives += "### Available CLIs:\\n\\n"
|
|
600
|
-
for cli_name, command in available_clis.items():
|
|
601
|
-
if cli_name != target_cli:
|
|
602
|
-
cli_info = self.cli_commands[cli_name]
|
|
603
|
-
alternatives += f"- **{cli_info['name']}**: `{cli_name}`\\n"
|
|
604
|
-
|
|
605
|
-
alternatives += "- Use web-based AI assistants\\n"
|
|
606
|
-
alternatives += "- Try desktop applications\\n"
|
|
607
|
-
|
|
608
|
-
alternatives += f"\\n### Original Request:\\n```\\n{request}\\n```\\n"
|
|
609
|
-
|
|
610
|
-
return alternatives
|
|
611
|
-
|
|
612
|
-
def _record_success_pattern(self, source_cli: str, target_cli: str, request: str, command: str, execution_time: float):
|
|
613
|
-
"""记录成功模式"""
|
|
614
|
-
try:
|
|
615
|
-
patterns_data = json.loads(safe_file_read(self.success_patterns_file))
|
|
616
|
-
|
|
617
|
-
success_entry = {
|
|
618
|
-
'timestamp': datetime.now().isoformat(),
|
|
619
|
-
'source_cli': source_cli,
|
|
620
|
-
'target_cli': target_cli,
|
|
621
|
-
'request_type': self._classify_request(request),
|
|
622
|
-
'request_sample': request[:100] + '...' if len(request) > 100 else request,
|
|
623
|
-
'successful_command': command,
|
|
624
|
-
'execution_time': execution_time,
|
|
625
|
-
'pattern_id': f"{source_cli}->{target_cli}:{self._classify_request(request)}"
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
patterns_data['successful_calls'].append(success_entry)
|
|
629
|
-
|
|
630
|
-
# 更新最优模式
|
|
631
|
-
pattern_key = f"{source_cli}->{target_cli}:{self._classify_request(request)}"
|
|
632
|
-
if pattern_key not in patterns_data['optimal_patterns']:
|
|
633
|
-
patterns_data['optimal_patterns'][pattern_key] = {
|
|
634
|
-
'usage_count': 0,
|
|
635
|
-
'last_used': None,
|
|
636
|
-
'success_rate': 0.0,
|
|
637
|
-
'avg_execution_time': 0.0,
|
|
638
|
-
'commands': []
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
patterns_data['optimal_patterns'][pattern_key]['usage_count'] += 1
|
|
642
|
-
patterns_data['optimal_patterns'][pattern_key]['last_used'] = datetime.now().isoformat()
|
|
643
|
-
patterns_data['optimal_patterns'][pattern_key]['commands'].append(command)
|
|
644
|
-
|
|
645
|
-
# 计算成功率和平均执行时间
|
|
646
|
-
all_calls = patterns_data['successful_calls'] + patterns_data['failed_calls']
|
|
647
|
-
total_attempts = len([c for c in all_calls if c['pattern_id'] == pattern_key])
|
|
648
|
-
successful_attempts = len([c for c in patterns_data['successful_calls'] if c['pattern_id'] == pattern_key])
|
|
649
|
-
|
|
650
|
-
if total_attempts > 0:
|
|
651
|
-
patterns_data['optimal_patterns'][pattern_key]['success_rate'] = successful_attempts / total_attempts
|
|
652
|
-
|
|
653
|
-
# 更新平均执行时间
|
|
654
|
-
pattern_calls = [c for c in patterns_data['successful_calls'] if c['pattern_id'] == pattern_key and 'execution_time' in c]
|
|
655
|
-
if pattern_calls:
|
|
656
|
-
patterns_data['optimal_patterns'][pattern_key]['avg_execution_time'] = sum(c['execution_time'] for c in pattern_calls) / len(pattern_calls)
|
|
657
|
-
|
|
658
|
-
safe_file_write(self.success_patterns_file, json.dumps(patterns_data, indent=2, ensure_ascii=False))
|
|
659
|
-
|
|
660
|
-
except Exception as e:
|
|
661
|
-
print(f"Warning: Failed to record success pattern: {e}")
|
|
662
|
-
|
|
663
|
-
def _record_fallback_pattern(self, source_cli: str, target_cli: str, request: str, fallback_result: str, fallback_type: str):
|
|
664
|
-
"""记录回退模式"""
|
|
665
|
-
try:
|
|
666
|
-
patterns_data = json.loads(safe_file_read(self.success_patterns_file))
|
|
667
|
-
|
|
668
|
-
fallback_entry = {
|
|
669
|
-
'timestamp': datetime.now().isoformat(),
|
|
670
|
-
'source_cli': source_cli,
|
|
671
|
-
'target_cli': target_cli,
|
|
672
|
-
'request_type': self._classify_request(request),
|
|
673
|
-
'request_sample': request[:100] + '...' if len(request) > 100 else request,
|
|
674
|
-
'fallback_type': fallback_type,
|
|
675
|
-
'fallback_result': fallback_result[:200] + '...' if len(fallback_result) > 200 else fallback_result,
|
|
676
|
-
'pattern_id': f"{source_cli}->{target_cli}:{self._classify_request(request)}:fallback"
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
if 'fallback_calls' not in patterns_data:
|
|
680
|
-
patterns_data['fallback_calls'] = []
|
|
681
|
-
|
|
682
|
-
patterns_data['fallback_calls'].append(fallback_entry)
|
|
683
|
-
|
|
684
|
-
safe_file_write(self.success_patterns_file, json.dumps(patterns_data, indent=2, ensure_ascii=False))
|
|
685
|
-
|
|
686
|
-
except Exception as e:
|
|
687
|
-
print(f"Warning: Failed to record fallback pattern: {e}")
|
|
688
|
-
|
|
689
|
-
def _record_failure_pattern(self, source_cli: str, target_cli: str, request: str, error_reason: str):
|
|
690
|
-
"""记录失败模式"""
|
|
691
|
-
try:
|
|
692
|
-
patterns_data = json.loads(safe_file_read(self.success_patterns_file))
|
|
693
|
-
|
|
694
|
-
failure_entry = {
|
|
695
|
-
'timestamp': datetime.now().isoformat(),
|
|
696
|
-
'source_cli': source_cli,
|
|
697
|
-
'target_cli': target_cli,
|
|
698
|
-
'request_type': self._classify_request(request),
|
|
699
|
-
'request_sample': request[:100] + '...' if len(request) > 100 else request,
|
|
700
|
-
'error_reason': error_reason,
|
|
701
|
-
'pattern_id': f"{source_cli}->{target_cli}:{self._classify_request(request)}"
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
patterns_data['failed_calls'].append(failure_entry)
|
|
705
|
-
|
|
706
|
-
safe_file_write(self.success_patterns_file, json.dumps(patterns_data, indent=2, ensure_ascii=False))
|
|
707
|
-
|
|
708
|
-
except Exception as e:
|
|
709
|
-
print(f"Warning: Failed to record failure pattern: {e}")
|
|
710
|
-
|
|
711
|
-
def _classify_request(self, request: str) -> str:
|
|
712
|
-
"""分类请求类型"""
|
|
713
|
-
request_lower = request.lower()
|
|
714
|
-
|
|
715
|
-
if any(word in request_lower for word in ['generate', 'create', 'write', 'build']):
|
|
716
|
-
return 'generation'
|
|
717
|
-
elif any(word in request_lower for word in ['analyze', 'review', 'check', 'examine']):
|
|
718
|
-
return 'analysis'
|
|
719
|
-
elif any(word in request_lower for word in ['fix', 'debug', 'repair', 'resolve']):
|
|
720
|
-
return 'debugging'
|
|
721
|
-
elif any(word in request_lower for word in ['test', 'validate', 'verify']):
|
|
722
|
-
return 'testing'
|
|
723
|
-
elif any(word in request_lower for word in ['document', 'explain', 'describe']):
|
|
724
|
-
return 'documentation'
|
|
725
|
-
elif any(word in request_lower for word in ['refactor', 'improve', 'optimize']):
|
|
726
|
-
return 'optimization'
|
|
727
|
-
else:
|
|
728
|
-
return 'general'
|
|
729
|
-
|
|
730
|
-
def get_system_status(self) -> Dict[str, Any]:
|
|
731
|
-
"""获取系统状态"""
|
|
732
|
-
status = {
|
|
733
|
-
'available_clis': {},
|
|
734
|
-
'total_clis': len(self.cli_commands),
|
|
735
|
-
'success_patterns': 0,
|
|
736
|
-
'last_updated': datetime.now().isoformat()
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
# 检查所有CLI
|
|
740
|
-
for cli_name in self.cli_commands:
|
|
741
|
-
available, command, message = self.check_cli_availability(cli_name)
|
|
742
|
-
status['available_clis'][cli_name] = {
|
|
743
|
-
'name': self.cli_commands[cli_name]['name'],
|
|
744
|
-
'available': available,
|
|
745
|
-
'command': command,
|
|
746
|
-
'message': message,
|
|
747
|
-
'type': self.cli_commands[cli_name]['type']
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
# 统计成功模式
|
|
751
|
-
try:
|
|
752
|
-
patterns_data = json.loads(safe_file_read(self.success_patterns_file))
|
|
753
|
-
status['success_patterns'] = len(patterns_data.get('successful_calls', []))
|
|
754
|
-
status['failed_patterns'] = len(patterns_data.get('failed_calls', []))
|
|
755
|
-
except:
|
|
756
|
-
pass
|
|
757
|
-
|
|
758
|
-
return status
|
|
759
|
-
|
|
760
|
-
# 使用示例
|
|
761
|
-
if __name__ == '__main__':
|
|
762
|
-
import time
|
|
763
|
-
|
|
764
|
-
executor = DirectCLIExecutor()
|
|
765
|
-
|
|
766
|
-
# 检查所有CLI状态
|
|
767
|
-
print("=== CLI Availability Status ===")
|
|
768
|
-
status = executor.get_system_status()
|
|
769
|
-
print(f"Total CLIs: {status['total_clis']}")
|
|
770
|
-
print(f"Available CLIs: {len([c for c in status['available_clis'].values() if c['available']])}")
|
|
771
|
-
|
|
772
|
-
print("\\n=== Available CLI Details ===")
|
|
773
|
-
for cli_name, info in status['available_clis'].items():
|
|
774
|
-
if info['available']:
|
|
775
|
-
print(f"✓ {info['name']}: {info['command']} ({info['type']})")
|
|
776
|
-
else:
|
|
777
|
-
print(f"✗ {info['name']}: {info['message']}")
|
|
778
|
-
|
|
779
|
-
# 测试跨CLI调用
|
|
780
|
-
print("\\n=== Direct Cross-CLI Execution Test ===")
|
|
781
|
-
available_clis = [name for name, info in status['available_clis'].items() if info['available']]
|
|
782
|
-
|
|
783
|
-
if len(available_clis) >= 2:
|
|
784
|
-
source_cli = available_clis[0]
|
|
785
|
-
target_cli = available_clis[1] if len(available_clis) > 1 else available_clis[0]
|
|
786
|
-
|
|
787
|
-
print(f"Testing: {source_cli} -> {target_cli}")
|
|
788
|
-
|
|
789
|
-
result = executor.execute_direct_cli_call(
|
|
790
|
-
source_cli=source_cli,
|
|
791
|
-
target_cli=target_cli,
|
|
792
|
-
request='分析这个项目的结构并生成改进建议',
|
|
793
|
-
context_files=['./README.md'] if os.path.exists('./README.md') else None,
|
|
794
|
-
working_dir=os.getcwd()
|
|
795
|
-
)
|
|
796
|
-
|
|
797
|
-
print(f"Success: {result['success']}")
|
|
798
|
-
print(f"Command: {result.get('command_used', 'N/A')}")
|
|
799
|
-
print(f"Execution time: {result.get('execution_time', 0):.2f}s")
|
|
800
|
-
print(f"Response: {result['response'][:200]}...")
|
|
801
|
-
|
|
802
|
-
if result.get('fallback_used'):
|
|
803
|
-
print(f"Fallback Level: {result.get('fallback_level', 'unknown')}")
|
|
804
|
-
else:
|
|
805
|
-
print("Need at least 2 available CLIs to test cross-CLI execution")
|