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,925 +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, Union
14
- from dataclasses import dataclass
15
- from datetime import datetime
16
- import re
17
-
18
- # 导入编码安全模块
19
- sys.path.append(str(Path(__file__).parent))
20
- from cross_platform_encoding import safe_file_write, safe_file_read
21
-
22
- @dataclass
23
- class CLICallMethod:
24
- """CLI调用方式定义"""
25
- call_patterns: List[str] # 按优先级排序的调用方式
26
- install_command: str # 如果没安装,如何安装
27
- type: str # npm, python, binary
28
- version_check: str # 如何检查版本
29
- api_env: Optional[str] # API密钥环境变量
30
-
31
- class RealCrossCLISystem:
32
- """真实跨CLI调用系统 - 处理所有安装状态"""
33
-
34
- def __init__(self):
35
- # 基于真实研究的CLI调用方式
36
- self.cli_methods = {
37
- # Node.js/npm类型CLI
38
- 'claude': CLICallMethod(
39
- call_patterns=[
40
- 'claude', # 直接命令(如果已安装到PATH)
41
- 'npx @anthropic/claude-code', # npx方式
42
- 'npx claude-code', # 简化npx
43
- '@anthropic/claude-code' # npm包名
44
- ],
45
- install_command='npm install -g @anthropic/claude-code',
46
- type='npm',
47
- version_check='npx @anthropic/claude-code --version',
48
- api_env='ANTHROPIC_API_KEY'
49
- ),
50
-
51
- 'gemini': CLICallMethod(
52
- call_patterns=[
53
- 'gemini-cli',
54
- 'npx @google/gemini-cli',
55
- 'npx gemini-cli',
56
- '@google/gemini-cli'
57
- ],
58
- install_command='npm install -g @google/gemini-cli',
59
- type='npm',
60
- version_check='npx @google/gemini-cli --version',
61
- api_env='GOOGLE_AI_API_KEY'
62
- ),
63
-
64
- 'copilot': CLICallMethod(
65
- call_patterns=[
66
- 'copilot',
67
- 'npx @github/copilot',
68
- 'github-copilot'
69
- ],
70
- install_command='npm install -g @github/copilot',
71
- type='npm',
72
- version_check='copilot --version',
73
- api_env='GITHUB_TOKEN'
74
- ),
75
-
76
- 'iflow': CLICallMethod(
77
- call_patterns=[
78
- 'iflow',
79
- 'npx @iflow-ai/iflow-cli',
80
- 'iflow-cli'
81
- ],
82
- install_command='npm install -g @iflow-ai/iflow-cli',
83
- type='npm',
84
- version_check='iflow --version',
85
- api_env='IFLOW_API_KEY'
86
- ),
87
-
88
- 'codebuddy': CLICallMethod(
89
- call_patterns=[
90
- 'codebuddy',
91
- 'npx @tencent-ai/codebuddy-code',
92
- 'codebuddy-code'
93
- ],
94
- install_command='npm install -g @tencent-ai/codebuddy-code',
95
- type='npm',
96
- version_check='codebuddy --version',
97
- api_env='CODEBUDDY_API_KEY'
98
- ),
99
-
100
- # Python类型CLI
101
- 'qwencode': CLICallMethod(
102
- call_patterns=[
103
- 'qwencode', # 直接命令
104
- 'python -m qwencode', # python -m方式
105
- 'python3 -m qwencode', # python3方式
106
- 'pip show qwencode' # 通过pip检查
107
- ],
108
- install_command='pip install qwencode-cli',
109
- type='python',
110
- version_check='qwencode --version',
111
- api_env='QWEN_API_KEY'
112
- ),
113
-
114
- 'qoder': CLICallMethod(
115
- call_patterns=[
116
- 'qoder',
117
- 'python -m qoder',
118
- 'python3 -m qoder'
119
- ],
120
- install_command='pip install qoder-cli',
121
- type='python',
122
- version_check='qoder --version',
123
- api_env='QODER_API_KEY'
124
- ),
125
-
126
- # 二进制类型CLI
127
- 'codex': CLICallMethod(
128
- call_patterns=[
129
- 'codex',
130
- 'openai-codex',
131
- 'which codex' # 通过which检查
132
- ],
133
- install_command='curl -fsSL https://openai.com/codex-cli/install.sh | bash',
134
- type='binary',
135
- version_check='codex --version',
136
- api_env='OPENAI_API_KEY'
137
- )
138
- }
139
-
140
- # 内存系统
141
- self.memory_dir = Path.home() / '.real_cross_cli_memory'
142
- self.memory_dir.mkdir(exist_ok=True)
143
- self.call_history_file = self.memory_dir / 'call_history.json'
144
- self.install_history_file = self.memory_dir / 'install_history.json'
145
-
146
- def check_cli_status(self, cli_name: str) -> Dict[str, Any]:
147
- """检查CLI状态 - 返回详细的安装和调用信息"""
148
- if cli_name not in self.cli_methods:
149
- return {
150
- 'exists': False,
151
- 'error': f'Unknown CLI: {cli_name}',
152
- 'available_methods': [],
153
- 'best_method': None,
154
- 'needs_install': True
155
- }
156
-
157
- method = self.cli_methods[cli_name]
158
- available_methods = []
159
- best_method = None
160
-
161
- # 按优先级检查每种调用方式
162
- for call_pattern in method.call_patterns:
163
- if self._test_call_pattern(call_pattern, method.type):
164
- available_methods.append(call_pattern)
165
- if not best_method:
166
- best_method = call_pattern
167
-
168
- return {
169
- 'exists': len(available_methods) > 0,
170
- 'cli_name': cli_name,
171
- 'type': method.type,
172
- 'available_methods': available_methods,
173
- 'best_method': best_method,
174
- 'needs_install': len(available_methods) == 0,
175
- 'install_command': method.install_command,
176
- 'api_env': method.api_env,
177
- 'version_info': self._get_version_info(best_method) if best_method else None
178
- }
179
-
180
- def _test_call_pattern(self, call_pattern: str, cli_type: str) -> bool:
181
- """测试特定的调用模式是否可用"""
182
- try:
183
- if cli_type == 'npm':
184
- # npm类型的检查
185
- if call_pattern.startswith('npx '):
186
- # 测试npx是否可用
187
- base_cmd = call_pattern.split()[1] # 去掉npx
188
- if '@' in base_cmd:
189
- # 包名格式,测试npx是否能解析
190
- result = subprocess.run(
191
- f"npx --yes {base_cmd} --version",
192
- shell=True,
193
- capture_output=True,
194
- text=True,
195
- timeout=10
196
- )
197
- else:
198
- result = subprocess.run(
199
- f"{call_pattern} --version",
200
- shell=True,
201
- capture_output=True,
202
- text=True,
203
- timeout=10
204
- )
205
- else:
206
- # 直接命令测试
207
- base_cmd = call_pattern.split()[0]
208
- result = subprocess.run(
209
- f"where {base_cmd}" if os.name == 'nt' else f"which {base_cmd}",
210
- shell=True,
211
- capture_output=True,
212
- text=True,
213
- timeout=5
214
- )
215
-
216
- if result.returncode == 0:
217
- # 进一步测试版本
218
- version_result = subprocess.run(
219
- f"{call_pattern} --version",
220
- shell=True,
221
- capture_output=True,
222
- text=True,
223
- timeout=10
224
- )
225
- result.returncode = version_result.returncode
226
-
227
- elif cli_type == 'python':
228
- # Python类型的检查
229
- if call_pattern.startswith('python -m '):
230
- result = subprocess.run(
231
- f"{call_pattern} --version",
232
- shell=True,
233
- capture_output=True,
234
- text=True,
235
- timeout=10
236
- )
237
- else:
238
- # 直接命令
239
- base_cmd = call_pattern.split()[0]
240
- result = subprocess.run(
241
- f"where {base_cmd}" if os.name == 'nt' else f"which {base_cmd}",
242
- shell=True,
243
- capture_output=True,
244
- text=True,
245
- timeout=5
246
- )
247
-
248
- if result.returncode == 0:
249
- version_result = subprocess.run(
250
- f"{call_pattern} --version",
251
- shell=True,
252
- capture_output=True,
253
- text=True,
254
- timeout=10
255
- )
256
- result.returncode = version_result.returncode
257
-
258
- elif cli_type == 'binary':
259
- # 二进制类型的检查
260
- if 'which' in call_pattern:
261
- # which本身就是检查命令
262
- parts = call_pattern.split()
263
- if len(parts) >= 3:
264
- base_cmd = parts[2] # which <command>中的command
265
- result = subprocess.run(
266
- f"where {base_cmd}" if os.name == 'nt' else f"which {base_cmd}",
267
- shell=True,
268
- capture_output=True,
269
- text=True,
270
- timeout=5
271
- )
272
- else:
273
- result = subprocess.run(['which'], shell=True, capture_output=True, text=True, timeout=5)
274
- result.returncode = 1 # 无效的which命令
275
- else:
276
- base_cmd = call_pattern.split()[0]
277
- result = subprocess.run(
278
- f"where {base_cmd}" if os.name == 'nt' else f"which {base_cmd}",
279
- shell=True,
280
- capture_output=True,
281
- text=True,
282
- timeout=5
283
- )
284
-
285
- if result.returncode == 0:
286
- version_result = subprocess.run(
287
- f"{call_pattern} --version",
288
- shell=True,
289
- capture_output=True,
290
- text=True,
291
- timeout=10
292
- )
293
- result.returncode = version_result.returncode
294
-
295
- else:
296
- result = subprocess.RunResult()
297
- result.returncode = 1
298
-
299
- return result.returncode == 0
300
-
301
- except subprocess.TimeoutExpired:
302
- return False
303
- except Exception:
304
- return False
305
-
306
- def _get_version_info(self, call_pattern: str) -> Optional[str]:
307
- """获取CLI版本信息"""
308
- try:
309
- result = subprocess.run(
310
- f"{call_pattern} --version",
311
- shell=True,
312
- capture_output=True,
313
- text=True,
314
- timeout=10
315
- )
316
-
317
- if result.returncode == 0:
318
- return result.stdout.strip() or result.stderr.strip()
319
- return None
320
-
321
- except:
322
- return None
323
-
324
- def call_cli(self,
325
- source_cli: str,
326
- target_cli: str,
327
- request: str,
328
- context_files: List[str] = None,
329
- working_dir: str = None,
330
- auto_install: bool = False) -> Dict[str, Any]:
331
- """调用CLI - 处理已安装和未安装的所有情况"""
332
-
333
- result = {
334
- 'success': False,
335
- 'response': '',
336
- 'error': '',
337
- 'command_used': '',
338
- 'install_used': False,
339
- 'fallback_used': False,
340
- 'timestamp': datetime.now().isoformat(),
341
- 'execution_time': 0
342
- }
343
-
344
- start_time = time.time()
345
-
346
- # 检查目标CLI状态
347
- status = self.check_cli_status(target_cli)
348
-
349
- if not status['exists']:
350
- # CLI不存在的情况
351
- if auto_install:
352
- # 尝试自动安装
353
- install_result = self._install_cli(target_cli)
354
- if install_result['success']:
355
- # 安装成功,重新检查状态
356
- status = self.check_cli_status(target_cli)
357
- result['install_used'] = True
358
-
359
- if not status['exists']:
360
- # 仍然不存在,提供降级方案
361
- result.update(self._handle_missing_cli(source_cli, target_cli, request, context_files, working_dir, status))
362
- result['execution_time'] = time.time() - start_time
363
- self._record_call(source_cli, target_cli, request, result)
364
- return result
365
-
366
- # CLI存在,构建并执行命令
367
- try:
368
- best_method = status['best_method']
369
- method_info = self.cli_methods[target_cli]
370
-
371
- # 构建完整命令
372
- full_command = self._build_command(best_method, method_info, request, context_files, working_dir)
373
-
374
- # 准备环境
375
- env = self._prepare_environment(method_info)
376
-
377
- # 执行命令
378
- process_result = subprocess.run(
379
- full_command,
380
- shell=True,
381
- capture_output=True,
382
- text=True,
383
- timeout=method_info.api_env and 120 or 60, # 有API密钥的超时时间更长
384
- cwd=working_dir,
385
- env=env
386
- )
387
-
388
- result['execution_time'] = time.time() - start_time
389
- result['command_used'] = full_command
390
-
391
- if process_result.returncode == 0:
392
- result.update({
393
- 'success': True,
394
- 'response': process_result.stdout,
395
- 'stderr': process_result.stderr,
396
- 'method_used': best_method
397
- })
398
- else:
399
- # 执行失败,尝试其他可用方法
400
- result.update(self._handle_execution_failure(source_cli, target_cli, request, context_files, working_dir, status, process_result.stderr))
401
-
402
- except subprocess.TimeoutExpired:
403
- result['execution_time'] = time.time() - start_time
404
- result.update(self._handle_timeout(source_cli, target_cli, request, context_files, working_dir))
405
-
406
- except Exception as e:
407
- result['execution_time'] = time.time() - start_time
408
- result.update(self._handle_exception(source_cli, target_cli, request, context_files, working_dir, str(e)))
409
-
410
- # 记录调用历史
411
- self._record_call(source_cli, target_cli, request, result)
412
-
413
- return result
414
-
415
- def _build_command(self, base_method: str, method_info: CLICallMethod, request: str, context_files: List[str] = None, working_dir: str = None) -> str:
416
- """构建完整的CLI命令"""
417
-
418
- # 基础命令部分
419
- if method_info.type == 'npm' and base_method.startswith('npx '):
420
- # npx命令需要特殊处理
421
- command_parts = base_method.split()
422
- else:
423
- command_parts = [base_method]
424
-
425
- # 根据CLI类型添加请求参数
426
- cli_name = next(k for k, v in self.cli_methods.items() if v == method_info)
427
-
428
- if cli_name in ['claude', 'gemini', 'copilot']:
429
- # npm类型CLI通常直接传递参数或使用 --
430
- if '"' in request or "'" in request or len(request.split()) > 1:
431
- command_parts.extend(['--', request])
432
- else:
433
- command_parts.append(request)
434
-
435
- elif cli_name in ['qwencode', 'qoder']:
436
- # Python类型CLI通常使用 --prompt 或 -p
437
- command_parts.extend(['--prompt', f'"{request}"'])
438
-
439
- elif cli_name in ['iflow', 'codebuddy']:
440
- # 直接传递请求
441
- command_parts.append(f'"{request}"')
442
-
443
- elif cli_name == 'codex':
444
- # Codex CLI特定格式
445
- command_parts.extend(['--request', f'"{request}"'])
446
-
447
- # 添加文件引用
448
- if context_files:
449
- for file_path in context_files:
450
- if os.path.exists(file_path):
451
- command_parts.extend(['--file', file_path])
452
-
453
- # 添加工作目录
454
- if working_dir:
455
- command_parts.extend(['--cwd', working_dir])
456
-
457
- # 构建完整命令
458
- if method_info.type == 'npm' and base_method.startswith('npx '):
459
- # npx命令保持原格式
460
- full_command = f"{command_parts[0]} {' '.join(command_parts[1:])}"
461
- else:
462
- full_command = ' '.join(command_parts)
463
-
464
- return full_command
465
-
466
- def _prepare_environment(self, method_info: CLICallMethod) -> Dict[str, str]:
467
- """准备CLI执行环境"""
468
- env = os.environ.copy()
469
-
470
- # 设置API密钥(如果需要且存在)
471
- if method_info.api_env and method_info.api_env in env:
472
- # API密钥已存在,无需修改
473
- pass
474
-
475
- # 根据CLI类型设置特殊环境
476
- if method_info.type == 'npm':
477
- # npm相关环境
478
- npm_config_prefix = env.get('NPM_CONFIG_PREFIX')
479
- if not npm_config_prefix:
480
- npm_config_prefix = os.path.join(os.path.expanduser('~'), '.npm-global')
481
- env['NPM_CONFIG_PREFIX'] = npm_config_prefix
482
-
483
- # 确保全局npm包在PATH中
484
- global_bin = os.path.join(npm_config_prefix, 'bin')
485
- if global_bin not in env.get('PATH', ''):
486
- env['PATH'] = f"{global_bin}{os.pathsep}{env.get('PATH', '')}"
487
-
488
- elif method_info.type == 'python':
489
- # Python相关环境
490
- user_base = os.path.join(os.path.expanduser('~'), '.local')
491
- user_bin = os.path.join(user_base, 'bin')
492
- if user_bin not in env.get('PATH', ''):
493
- env['PATH'] = f"{user_bin}{os.pathsep}{env.get('PATH', '')}"
494
-
495
- return env
496
-
497
- def _install_cli(self, cli_name: str) -> Dict[str, Any]:
498
- """安装CLI工具"""
499
- if cli_name not in self.cli_methods:
500
- return {'success': False, 'error': f'Unknown CLI: {cli_name}'}
501
-
502
- method_info = self.cli_methods[cli_name]
503
-
504
- try:
505
- print(f"🔧 正在安装 {cli_name}...")
506
-
507
- # 执行安装命令
508
- result = subprocess.run(
509
- method_info.install_command,
510
- shell=True,
511
- capture_output=True,
512
- text=True,
513
- timeout=300 # 5分钟超时
514
- )
515
-
516
- install_result = {
517
- 'success': result.returncode == 0,
518
- 'command': method_info.install_command,
519
- 'stdout': result.stdout,
520
- 'stderr': result.stderr,
521
- 'timestamp': datetime.now().isoformat()
522
- }
523
-
524
- # 记录安装历史
525
- self._record_installation(cli_name, install_result)
526
-
527
- if install_result['success']:
528
- print(f"✅ {cli_name} 安装成功")
529
- else:
530
- print(f"❌ {cli_name} 安装失败: {result.stderr}")
531
-
532
- return install_result
533
-
534
- except subprocess.TimeoutExpired:
535
- error_msg = f"{cli_name} 安装超时"
536
- print(f"⏰ {error_msg}")
537
- return {'success': False, 'error': error_msg}
538
-
539
- except Exception as e:
540
- error_msg = f"{cli_name} 安装异常: {str(e)}"
541
- print(f"💥 {error_msg}")
542
- return {'success': False, 'error': error_msg}
543
-
544
- def _handle_missing_cli(self, source_cli: str, target_cli: str, request: str, context_files: List[str], working_dir: str, status: Dict[str, Any]) -> Dict[str, Any]:
545
- """处理缺失CLI的情况"""
546
- method_info = self.cli_methods[target_cli]
547
-
548
- # Level 1: 提供安装指导
549
- install_guidance = f"""# {method_info.type.title()} CLI 安装指导
550
-
551
- ## 快速安装命令:
552
- ```bash
553
- {status['install_command']}
554
- ```
555
-
556
- ## 验证安装:
557
- 安装后运行以下命令验证:
558
- ```bash
559
- {method_info.version_check}
560
- ```
561
-
562
- ## 使用方式:
563
- 安装后可以使用以下任一方式调用:
564
- """
565
-
566
- for i, call_pattern in enumerate(method_info.call_patterns[:3]):
567
- install_guidance += f"{i+1}. `{call_pattern}`\\n"
568
-
569
- if status['api_env']:
570
- install_guidance += f"""
571
- ## 环境变量设置:
572
- ```bash
573
- export {status['api_env']}='your-api-key-here'
574
- ```
575
- """
576
-
577
- return {
578
- 'success': True,
579
- 'response': install_guidance,
580
- 'fallback_used': True,
581
- 'fallback_level': 'install_guidance',
582
- 'fallback_reason': f'{target_cli} not installed'
583
- }
584
-
585
- def _handle_execution_failure(self, source_cli: str, target_cli: str, request: str, context_files: List[str], working_dir: str, status: Dict[str, Any], error_msg: str) -> Dict[str, Any]:
586
- """处理执行失败的情况"""
587
- method_info = self.cli_methods[target_cli]
588
-
589
- # 尝试其他可用的调用方法
590
- fallback_methods = [m for m in status['available_methods'] if m != status['best_method']]
591
-
592
- if fallback_methods:
593
- # Level 1: 尝试其他调用方法
594
- alt_method = fallback_methods[0]
595
- alt_command = self._build_command(alt_method, method_info, request, context_files, working_dir)
596
-
597
- try:
598
- process_result = subprocess.run(
599
- alt_command,
600
- shell=True,
601
- capture_output=True,
602
- text=True,
603
- timeout=60,
604
- cwd=working_dir,
605
- env=self._prepare_environment(method_info)
606
- )
607
-
608
- if process_result.returncode == 0:
609
- return {
610
- 'success': True,
611
- 'response': process_result.stdout,
612
- 'stderr': process_result.stderr,
613
- 'command_used': alt_command,
614
- 'method_used': alt_method,
615
- 'fallback_used': True,
616
- 'fallback_level': 'alternative_method'
617
- }
618
-
619
- except:
620
- pass # 继续到下一个降级级别
621
-
622
- # Level 2: 提供手动执行指导
623
- manual_guidance = f"""# 手动执行指导
624
-
625
- 原始方法失败,请尝试以下手动方式:
626
-
627
- ## 可用调用方式:
628
- """
629
- for i, method in enumerate(status['available_methods']):
630
- command = self._build_command(method, method_info, request, context_files, working_dir)
631
- manual_guidance += f"### 方式 {i+1}:
632
- ```bash
633
- {command}
634
- ```
635
- \\n"
636
-
637
- manual_guidance += f"""
638
- ## 原始错误信息:
639
- ```
640
- {error_msg}
641
- ```
642
- """
643
-
644
- return {
645
- 'success': True,
646
- 'response': manual_guidance,
647
- 'fallback_used': True,
648
- 'fallback_level': 'manual_guidance',
649
- 'fallback_reason': 'execution_failed'
650
- }
651
-
652
- def _handle_timeout(self, source_cli: str, target_cli: str, request: str, context_files: List[str], working_dir: str) -> Dict[str, Any]:
653
- """处理超时情况"""
654
- timeout_guidance = f"""# 执行超时处理
655
-
656
- {target_cli} 执行超时,可能原因:
657
- 1. 网络连接问题
658
- 2. API密钥配置错误
659
- 3. 请求过于复杂
660
-
661
- ## 建议操作:
662
- 1. 检查网络连接
663
- 2. 验证API密钥配置
664
- 3. 简化请求内容
665
- 4. 尝试手动执行
666
-
667
- ## 手动命令:
668
- ```bash
669
- # 简化版本请求
670
- {target_cli} "简单请求"
671
- ```
672
- """
673
-
674
- return {
675
- 'success': False,
676
- 'response': timeout_guidance,
677
- 'fallback_used': True,
678
- 'fallback_level': 'timeout_guidance',
679
- 'fallback_reason': 'execution_timeout'
680
- }
681
-
682
- def _handle_exception(self, source_cli: str, target_cli: str, request: str, context_files: List[str], working_dir: str, error_msg: str) -> Dict[str, Any]:
683
- """处理异常情况"""
684
- exception_guidance = f"""# 执行异常处理
685
-
686
- {target_cli} 执行出现异常: {error_msg}
687
-
688
- ## 可能解决方案:
689
- 1. 检查CLI是否正确安装
690
- 2. 验证系统环境配置
691
- 3. 检查权限设置
692
- 4. 查看详细错误日志
693
-
694
- ## 故障排除:
695
- ```bash
696
- # 检查CLI状态
697
- {self.cli_methods[target_cli].version_check}
698
-
699
- # 检查环境变量
700
- env | grep -i api
701
- ```
702
- """
703
-
704
- return {
705
- 'success': False,
706
- 'response': exception_guidance,
707
- 'fallback_used': True,
708
- 'fallback_level': 'exception_guidance',
709
- 'fallback_reason': 'execution_exception'
710
- }
711
-
712
- def _record_call(self, source_cli: str, target_cli: str, request: str, result: Dict[str, Any]):
713
- """记录调用历史"""
714
- try:
715
- # 加载现有历史
716
- if self.call_history_file.exists():
717
- history = json.loads(safe_file_read(self.call_history_file))
718
- else:
719
- history = {'calls': [], 'stats': {}}
720
-
721
- # 添加新记录
722
- call_record = {
723
- 'timestamp': result['timestamp'],
724
- 'source_cli': source_cli,
725
- 'target_cli': target_cli,
726
- 'request': request[:200] + '...' if len(request) > 200 else request,
727
- 'success': result['success'],
728
- 'execution_time': result.get('execution_time', 0),
729
- 'command_used': result.get('command_used', ''),
730
- 'install_used': result.get('install_used', False),
731
- 'fallback_used': result.get('fallback_used', False),
732
- 'fallback_level': result.get('fallback_level', ''),
733
- 'method_used': result.get('method_used', '')
734
- }
735
-
736
- history['calls'].append(call_record)
737
-
738
- # 保留最近1000条记录
739
- if len(history['calls']) > 1000:
740
- history['calls'] = history['calls'][-1000:]
741
-
742
- # 更新统计
743
- pattern = f"{source_cli}->{target_cli}"
744
- if pattern not in history['stats']:
745
- history['stats'][pattern] = {
746
- 'total_calls': 0,
747
- 'successful_calls': 0,
748
- 'avg_execution_time': 0,
749
- 'last_call': None
750
- }
751
-
752
- history['stats'][pattern]['total_calls'] += 1
753
- if result['success']:
754
- history['stats'][pattern]['successful_calls'] += 1
755
-
756
- if result.get('execution_time', 0) > 0:
757
- current_avg = history['stats'][pattern]['avg_execution_time']
758
- total_calls = history['stats'][pattern]['total_calls']
759
- history['stats'][pattern]['avg_execution_time'] = (
760
- (current_avg * (total_calls - 1) + result['execution_time']) / total_calls
761
- )
762
-
763
- history['stats'][pattern]['last_call'] = result['timestamp']
764
-
765
- # 保存历史
766
- safe_file_write(self.call_history_file, json.dumps(history, indent=2, ensure_ascii=False))
767
-
768
- except Exception as e:
769
- print(f"Warning: Failed to record call history: {e}")
770
-
771
- def _record_installation(self, cli_name: str, install_result: Dict[str, Any]):
772
- """记录安装历史"""
773
- try:
774
- if self.install_history_file.exists():
775
- history = json.loads(safe_file_read(self.install_history_file))
776
- else:
777
- history = {'installations': []}
778
-
779
- install_record = {
780
- 'timestamp': install_result['timestamp'],
781
- 'cli_name': cli_name,
782
- 'command': install_result['command'],
783
- 'success': install_result['success'],
784
- 'stdout': install_result.get('stdout', '')[:500],
785
- 'stderr': install_result.get('stderr', '')[:500]
786
- }
787
-
788
- history['installations'].append(install_record)
789
-
790
- # 保留最近100条安装记录
791
- if len(history['installations']) > 100:
792
- history['installations'] = history['installations'][-100:]
793
-
794
- safe_file_write(self.install_history_file, json.dumps(history, indent=2, ensure_ascii=False))
795
-
796
- except Exception as e:
797
- print(f"Warning: Failed to record installation history: {e}")
798
-
799
- def get_system_overview(self) -> Dict[str, Any]:
800
- """获取系统概览"""
801
- overview = {
802
- 'timestamp': datetime.now().isoformat(),
803
- 'cli_status': {},
804
- 'total_clis': len(self.cli_methods),
805
- 'available_clis': 0,
806
- 'unavailable_clis': 0,
807
- 'call_statistics': {},
808
- 'recent_installations': []
809
- }
810
-
811
- # 检查所有CLI状态
812
- for cli_name in self.cli_methods:
813
- status = self.check_cli_status(cli_name)
814
- overview['cli_status'][cli_name] = {
815
- 'exists': status['exists'],
816
- 'type': status['type'],
817
- 'best_method': status.get('best_method', ''),
818
- 'available_methods_count': len(status['available_methods']),
819
- 'version_info': status.get('version_info', ''),
820
- 'needs_install': status['needs_install']
821
- }
822
-
823
- if status['exists']:
824
- overview['available_clis'] += 1
825
- else:
826
- overview['unavailable_clis'] += 1
827
-
828
- # 加载调用统计
829
- try:
830
- if self.call_history_file.exists():
831
- history = json.loads(safe_file_read(self.call_history_file))
832
- overview['call_statistics'] = history.get('stats', {})
833
- except:
834
- pass
835
-
836
- # 加载最近安装
837
- try:
838
- if self.install_history_file.exists():
839
- history = json.loads(safe_file_read(self.install_history_file))
840
- overview['recent_installations'] = history.get('installations', [])[-5:]
841
- except:
842
- pass
843
-
844
- return overview
845
-
846
- # 使用示例和测试
847
- if __name__ == '__main__':
848
- system = RealCrossCLISystem()
849
-
850
- print("🔍 真实跨CLI调用系统测试")
851
- print("=" * 50)
852
-
853
- # 1. 系统概览
854
- print("📊 系统概览:")
855
- overview = system.get_system_overview()
856
- print(f" 总CLI数: {overview['total_clis']}")
857
- print(f" 可用CLI: {overview['available_clis']}")
858
- print(f" 不可用CLI: {overview['unavailable_clis']}")
859
-
860
- print("\n📋 CLI状态详情:")
861
- for cli_name, status in overview['cli_status'].items():
862
- status_icon = "✅" if status['exists'] else "❌"
863
- print(f" {status_icon} {cli_name}: {status['type']} - {status['best_method'] or '未安装'}")
864
-
865
- # 2. 测试跨CLI调用
866
- available_clis = [name for name, status in overview['cli_status'].items() if status['exists']]
867
-
868
- if len(available_clis) >= 1:
869
- print(f"\n🚀 测试CLI调用:")
870
-
871
- # 测试直接调用(用同一个CLI)
872
- test_cli = available_clis[0]
873
- print(f" 测试直接调用 {test_cli}...")
874
-
875
- result = system.call_cli(
876
- source_cli='user',
877
- target_cli=test_cli,
878
- request='生成一个简单的Python Hello World程序',
879
- auto_install=False
880
- )
881
-
882
- print(f" 成功: {result['success']}")
883
- print(f" 命令: {result.get('command_used', 'N/A')}")
884
- print(f" 时间: {result.get('execution_time', 0):.2f}s")
885
- if result.get('fallback_used'):
886
- print(f" 降级: {result.get('fallback_level', 'unknown')}")
887
-
888
- if len(available_clis) >= 2:
889
- # 测试跨CLI调用
890
- source_cli = available_clis[0]
891
- target_cli = available_clis[1]
892
-
893
- print(f"\n 测试跨CLI调用 {source_cli} -> {target_cli}...")
894
-
895
- cross_result = system.call_cli(
896
- source_cli=source_cli,
897
- target_cli=target_cli,
898
- request='分析当前目录结构',
899
- auto_install=False
900
- )
901
-
902
- print(f" 成功: {cross_result['success']}")
903
- print(f" 命令: {cross_result.get('command_used', 'N/A')}")
904
- print(f" 时间: {cross_result.get('execution_time', 0):.2f}s")
905
- if cross_result.get('fallback_used'):
906
- print(f" 降级: {cross_result.get('fallback_level', 'unknown')}")
907
-
908
- else:
909
- print("\n⚠️ 没有可用的CLI进行调用测试")
910
- print(" 可以尝试 auto_install=True 来自动安装CLI")
911
-
912
- # 3. 测试未安装CLI的处理
913
- print(f"\n🔧 测试未安装CLI处理:")
914
- missing_result = system.call_cli(
915
- source_cli='user',
916
- target_cli='nonexistent_cli',
917
- request='测试处理',
918
- auto_install=False
919
- )
920
-
921
- print(f" 降级使用: {missing_result.get('fallback_used', False)}")
922
- print(f" 降级级别: {missing_result.get('fallback_level', 'unknown')}")
923
- print(f" 响应长度: {len(missing_result.get('response', ''))}")
924
-
925
- print("\n✅ 测试完成!")