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