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.
Files changed (102) hide show
  1. package/README.en.md +306 -300
  2. package/README.md +469 -301
  3. package/package.json +97 -81
  4. package/scripts/publish.js +268 -0
  5. package/scripts/simple-publish.js +59 -0
  6. package/src/index.js +12 -0
  7. package/test/enhanced-main-alignment.test.js +298 -0
  8. package/test/hook-system-integration-test.js +307 -0
  9. package/test/natural-language-skills-test.js +320 -0
  10. package/test/nl-integration-test.js +179 -0
  11. package/test/parameter-parsing-test.js +143 -0
  12. package/test/real-test.js +435 -0
  13. package/test/system-compatibility-test.js +447 -0
  14. package/test/tdd-fixes-test.js +211 -0
  15. package/test/third-party-skills-test.js +321 -0
  16. package/test/tool-selection-integration-test.js +157 -0
  17. package/test/unit/cli-scanner.test.js +291 -0
  18. package/test/unit/cross-cli-executor.test.js +399 -0
  19. package/src/adapters/claude/__init__.py +0 -13
  20. package/src/adapters/claude/claude_skills_integration.py +0 -609
  21. package/src/adapters/claude/hook_adapter.py +0 -663
  22. package/src/adapters/claude/install_claude_integration.py +0 -265
  23. package/src/adapters/claude/skills_hook_adapter.py +0 -841
  24. package/src/adapters/claude/standalone_claude_adapter.py +0 -384
  25. package/src/adapters/cline/__init__.py +0 -20
  26. package/src/adapters/cline/config.py +0 -108
  27. package/src/adapters/cline/install_cline_integration.py +0 -617
  28. package/src/adapters/cline/mcp_server.py +0 -713
  29. package/src/adapters/cline/standalone_cline_adapter.py +0 -459
  30. package/src/adapters/codebuddy/__init__.py +0 -13
  31. package/src/adapters/codebuddy/buddy_adapter.py +0 -1125
  32. package/src/adapters/codebuddy/install_codebuddy_integration.py +0 -279
  33. package/src/adapters/codebuddy/skills_hook_adapter.py +0 -672
  34. package/src/adapters/codebuddy/skills_integration.py +0 -395
  35. package/src/adapters/codebuddy/standalone_codebuddy_adapter.py +0 -403
  36. package/src/adapters/codex/__init__.py +0 -11
  37. package/src/adapters/codex/base.py +0 -46
  38. package/src/adapters/codex/install_codex_integration.py +0 -311
  39. package/src/adapters/codex/mcp_server.py +0 -493
  40. package/src/adapters/codex/natural_language_parser.py +0 -82
  41. package/src/adapters/codex/slash_command_adapter.py +0 -326
  42. package/src/adapters/codex/standalone_codex_adapter.py +0 -362
  43. package/src/adapters/copilot/__init__.py +0 -13
  44. package/src/adapters/copilot/install_copilot_integration.py +0 -564
  45. package/src/adapters/copilot/mcp_adapter.py +0 -772
  46. package/src/adapters/copilot/mcp_server.py +0 -168
  47. package/src/adapters/copilot/standalone_copilot_adapter.py +0 -114
  48. package/src/adapters/gemini/__init__.py +0 -13
  49. package/src/adapters/gemini/extension_adapter.py +0 -690
  50. package/src/adapters/gemini/install_gemini_integration.py +0 -257
  51. package/src/adapters/gemini/standalone_gemini_adapter.py +0 -366
  52. package/src/adapters/iflow/__init__.py +0 -7
  53. package/src/adapters/iflow/hook_adapter.py +0 -1038
  54. package/src/adapters/iflow/hook_installer.py +0 -536
  55. package/src/adapters/iflow/install_iflow_integration.py +0 -271
  56. package/src/adapters/iflow/official_hook_adapter.py +0 -1272
  57. package/src/adapters/iflow/standalone_iflow_adapter.py +0 -48
  58. package/src/adapters/iflow/workflow_adapter.py +0 -793
  59. package/src/adapters/qoder/hook_installer.py +0 -732
  60. package/src/adapters/qoder/install_qoder_integration.py +0 -265
  61. package/src/adapters/qoder/notification_hook_adapter.py +0 -863
  62. package/src/adapters/qoder/standalone_qoder_adapter.py +0 -48
  63. package/src/adapters/qwen/__init__.py +0 -17
  64. package/src/adapters/qwencode/__init__.py +0 -13
  65. package/src/adapters/qwencode/inheritance_adapter.py +0 -818
  66. package/src/adapters/qwencode/install_qwencode_integration.py +0 -276
  67. package/src/adapters/qwencode/standalone_qwencode_adapter.py +0 -399
  68. package/src/atomic_collaboration_handler.py +0 -461
  69. package/src/cli_collaboration_agent.py +0 -697
  70. package/src/collaboration/hooks.py +0 -315
  71. package/src/core/__init__.py +0 -21
  72. package/src/core/ai_environment_scanner.py +0 -331
  73. package/src/core/base_adapter.py +0 -220
  74. package/src/core/cli_hook_integration.py +0 -406
  75. package/src/core/cross_cli_executor.py +0 -713
  76. package/src/core/cross_cli_mapping.py +0 -1165
  77. package/src/core/cross_platform_encoding.py +0 -365
  78. package/src/core/cross_platform_safe_cli.py +0 -894
  79. package/src/core/direct_cli_executor.py +0 -805
  80. package/src/core/direct_cli_hook_system.py +0 -958
  81. package/src/core/enhanced_init_processor.py +0 -467
  82. package/src/core/graceful_cli_executor.py +0 -912
  83. package/src/core/md_enhancer.py +0 -342
  84. package/src/core/md_generator.py +0 -619
  85. package/src/core/models.py +0 -218
  86. package/src/core/parser.py +0 -108
  87. package/src/core/real_cli_hook_system.py +0 -852
  88. package/src/core/real_cross_cli_system.py +0 -925
  89. package/src/core/verified_cross_cli_system.py +0 -961
  90. package/src/deploy.js +0 -737
  91. package/src/enhanced-main.js +0 -626
  92. package/src/enhanced_deploy.js +0 -303
  93. package/src/enhanced_universal_cli_setup.py +0 -930
  94. package/src/kimi_wrapper.py +0 -104
  95. package/src/main.js +0 -1309
  96. package/src/shell_integration.py +0 -398
  97. package/src/simple-main.js +0 -315
  98. package/src/smart_router_creator.py +0 -323
  99. package/src/universal_cli_setup.py +0 -1289
  100. package/src/utils/__init__.py +0 -12
  101. package/src/utils/cli_detector.py +0 -445
  102. 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")