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,894 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- 跨平台安全CLI调用系统
5
- 解决权限、编码、跨平台兼容性问题
6
- 支持所有主流AI CLI工具的安全调用
7
- """
8
-
9
- import os
10
- import sys
11
- import json
12
- import subprocess
13
- import platform
14
- import shutil
15
- import stat
16
- import tempfile
17
- import signal
18
- import logging
19
- from pathlib import Path
20
- from typing import Dict, List, Optional, Tuple, Any, Union
21
- from dataclasses import dataclass
22
- from datetime import datetime
23
- from enum import Enum
24
-
25
- # 导入跨平台编码库
26
- sys.path.insert(0, str(Path(__file__).parent / 'src' / 'core'))
27
- try:
28
- from cross_platform_encoding import get_cross_platform_installer, encoding_safe
29
- except ImportError:
30
- # 如果无法导入,创建基础版本
31
- def get_cross_platform_installer():
32
- class BasicInstaller:
33
- def print_system_info(self):
34
- print("基础安装器模式")
35
- return BasicInstaller()
36
-
37
- def encoding_safe(func):
38
- return func
39
-
40
- class PermissionLevel(Enum):
41
- """权限级别枚举"""
42
- MINIMAL = "minimal" # 最小权限
43
- USER = "user" # 用户权限
44
- ELEVATED = "elevated" # 提升权限
45
- ADMIN = "admin" # 管理员权限
46
- SYSTEM = "system" # 系统权限
47
-
48
- class CLIStatus(Enum):
49
- """CLI状态枚举"""
50
- AVAILABLE = "available" # 可用
51
- INSTALLED = "installed" # 已安装
52
- CONFIGURED = "configured" # 已配置
53
- AUTHENTICATED = "authenticated" # 已认证
54
- ERROR = "error" # 错误
55
- NOT_FOUND = "not_found" # 未找到
56
-
57
- @dataclass
58
- class CLIConfig:
59
- """CLI配置数据类"""
60
- name: str
61
- display_name: str
62
- command: str
63
- description: str
64
- required_env_vars: List[str]
65
- optional_env_vars: List[str]
66
- config_files: List[str]
67
- auth_method: str
68
- supported_file_types: List[str]
69
- input_format: str
70
- output_format: str
71
- permission_level: PermissionLevel
72
- version_check_command: Optional[str] = None
73
- auth_command: Optional[str] = None
74
- help_command: Optional[str] = None
75
-
76
- @dataclass
77
- class CLICommand:
78
- """CLI命令数据类"""
79
- cli_name: str
80
- command_type: str # 'prompt', 'file', 'config', 'auth', 'help'
81
- command: str
82
- description: str
83
- parameters: Dict[str, Any]
84
- input_files: List[str]
85
- output_files: List[str]
86
- working_dir: Optional[str] = None
87
- timeout: int = 300
88
- required_permission: PermissionLevel = PermissionLevel.USER
89
-
90
- @dataclass
91
- class ExecutionResult:
92
- """执行结果数据类"""
93
- success: bool
94
- exit_code: int
95
- stdout: str
96
- stderr: str
97
- execution_time: float
98
- command_executed: str
99
- cli_name: str
100
- files_created: List[str]
101
- files_modified: List[str]
102
- error_message: Optional[str] = None
103
- output_files: Dict[str, Path] = None
104
-
105
- class PermissionManager:
106
- """权限管理器"""
107
-
108
- def __init__(self):
109
- self.system = platform.system().lower()
110
- self.current_user = self._get_current_user()
111
-
112
- def _get_current_user(self) -> str:
113
- """获取当前用户"""
114
- try:
115
- if self.system == 'windows':
116
- return os.environ.get('USERNAME', 'unknown')
117
- else:
118
- return os.environ.get('USER', 'unknown')
119
- except:
120
- return 'unknown'
121
-
122
- def check_permission_level(self, cli_config: CLIConfig) -> PermissionLevel:
123
- """检查CLI所需权限级别"""
124
- required_level = cli_config.permission_level
125
-
126
- # Windows权限检查
127
- if self.system == 'windows':
128
- return self._check_windows_permission(required_level)
129
- else:
130
- return self._check_unix_permission(required_level)
131
-
132
- def _check_windows_permission(self, required_level: PermissionLevel) -> PermissionLevel:
133
- """Windows权限检查"""
134
- try:
135
- import ctypes
136
- import win32api
137
- import win32con
138
- import win32security
139
-
140
- # 检查是否管理员
141
- is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
142
-
143
- if is_admin:
144
- return PermissionLevel.ADMIN
145
- else:
146
- return PermissionLevel.USER
147
- except ImportError:
148
- # 如果没有win32库,使用基础检查
149
- try:
150
- # 尝试写入系统目录检查权限
151
- test_path = Path(os.environ.get('WINDIR', 'C:\\Windows'))
152
- test_file = test_path / 'stigmergy_permission_test.tmp'
153
- try:
154
- test_file.write_text('test')
155
- test_file.unlink()
156
- return PermissionLevel.ADMIN
157
- except:
158
- return PermissionLevel.USER
159
- except:
160
- return PermissionLevel.USER
161
-
162
- def _check_unix_permission(self, required_level: PermissionLevel) -> PermissionLevel:
163
- """Unix权限检查"""
164
- try:
165
- # 检查是否root
166
- if os.geteuid() == 0:
167
- return PermissionLevel.SYSTEM
168
- # 检查是否在sudo组
169
- elif self._check_sudo_access():
170
- return PermissionLevel.ADMIN
171
- else:
172
- return PermissionLevel.USER
173
- except:
174
- return PermissionLevel.USER
175
-
176
- def _check_sudo_access(self) -> bool:
177
- """检查sudo访问权限"""
178
- try:
179
- # 尝试执行一个需要sudo权限的命令
180
- result = subprocess.run(
181
- ['sudo', '-n', 'true'],
182
- capture_output=True,
183
- timeout=5
184
- )
185
- return result.returncode == 0
186
- except:
187
- return False
188
-
189
- def elevate_privileges(self, required_level: PermissionLevel) -> Tuple[bool, str]:
190
- """提升权限"""
191
- if required_level == PermissionLevel.USER:
192
- return True, "用户权限足够"
193
-
194
- if self.system == 'windows':
195
- return self._elevate_windows(required_level)
196
- else:
197
- return self._elevate_unix(required_level)
198
-
199
- def _elevate_windows(self, required_level: PermissionLevel) -> Tuple[bool, str]:
200
- """Windows权限提升"""
201
- try:
202
- if required_level in [PermissionLevel.ADMIN, PermissionLevel.SYSTEM]:
203
- # 请求管理员权限
204
- ctypes.windll.shell32.ShellExecuteW(
205
- None, "runas", sys.executable, " ".join(sys.argv), None, 1
206
- )
207
- return True, "已请求管理员权限"
208
- except Exception as e:
209
- return False, f"权限提升失败: {e}"
210
-
211
- return False, "无法提升权限"
212
-
213
- def _elevate_unix(self, required_level: PermissionLevel) -> Tuple[bool, str]:
214
- """Unix权限提升"""
215
- try:
216
- if required_level in [PermissionLevel.ADMIN, PermissionLevel.SYSTEM]:
217
- # 使用sudo重新执行
218
- sudo_command = ['sudo'] + sys.argv
219
- result = subprocess.run(sudo_command)
220
- return result.returncode == 0, "权限提升完成"
221
- except Exception as e:
222
- return False, f"权限提升失败: {e}"
223
-
224
- return False, "无法提升权限"
225
-
226
- class CrossPlatformSafeCLI:
227
- """跨平台安全CLI调用器"""
228
-
229
- def __init__(self):
230
- self.system = platform.system().lower()
231
- self.temp_dir = Path(tempfile.gettempdir()) / 'stigmergy_cli_safe'
232
- self.temp_dir.mkdir(exist_ok=True)
233
-
234
- # 设置日志
235
- self.logger = self._setup_logging()
236
-
237
- # 初始化权限管理器
238
- self.permission_manager = PermissionManager()
239
-
240
- # 获取编码安全安装器
241
- self.encoding_installer = get_cross_platform_installer()
242
-
243
- # CLI配置映射
244
- self.cli_configs = self._load_cli_configs()
245
-
246
- # 权限缓存
247
- self._permission_cache = {}
248
-
249
- def _setup_logging(self) -> logging.Logger:
250
- """设置日志"""
251
- logger = logging.getLogger('CrossPlatformSafeCLI')
252
- logger.setLevel(logging.INFO)
253
-
254
- # 确保日志目录存在
255
- log_dir = self.temp_dir / 'logs'
256
- log_dir.mkdir(exist_ok=True)
257
-
258
- # 文件处理器
259
- log_file = log_dir / f'cli_execution_{datetime.now().strftime("%Y%m%d")}.log'
260
- file_handler = logging.FileHandler(log_file, encoding='utf-8')
261
- file_handler.setLevel(logging.INFO)
262
-
263
- # 控制台处理器
264
- console_handler = logging.StreamHandler()
265
- console_handler.setLevel(logging.WARNING)
266
-
267
- # 格式化器
268
- formatter = logging.Formatter(
269
- '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
270
- )
271
- file_handler.setFormatter(formatter)
272
- console_handler.setFormatter(formatter)
273
-
274
- logger.addHandler(file_handler)
275
- logger.addHandler(console_handler)
276
-
277
- return logger
278
-
279
- def _load_cli_configs(self) -> Dict[str, CLIConfig]:
280
- """加载CLI配置"""
281
- configs = {}
282
-
283
- # Claude CLI配置
284
- configs['claude'] = CLIConfig(
285
- name='claude',
286
- display_name='Claude CLI',
287
- command='claude',
288
- description='Anthropic Claude CLI工具',
289
- required_env_vars=['ANTHROPIC_API_KEY'],
290
- optional_env_vars=['ANTHROPIC_BASE_URL', 'ANTHROPIC_API_URL'],
291
- config_files=['~/.claude/config.json', '~/.config/claude/config.json'],
292
- auth_method='api_key',
293
- supported_file_types=['.txt', '.md', '.py', '.js', '.ts', '.java', '.cpp', '.c', '.h'],
294
- input_format='text',
295
- output_format='text',
296
- permission_level=PermissionLevel.USER,
297
- version_check_command='claude --version',
298
- auth_command='claude auth',
299
- help_command='claude --help'
300
- )
301
-
302
- # Gemini CLI配置
303
- configs['gemini'] = CLIConfig(
304
- name='gemini',
305
- display_name='Gemini CLI',
306
- command='gemini',
307
- description='Google Gemini CLI工具',
308
- required_env_vars=['GEMINI_API_KEY'],
309
- optional_env_vars=['GOOGLE_CLOUD_PROJECT', 'GOOGLE_APPLICATION_CREDENTIALS'],
310
- config_files=['~/.gemini/config.json', '~/.config/gemini/config.json'],
311
- auth_method='api_key',
312
- supported_file_types=['.txt', '.md', '.py', '.js', '.ts', '.java', '.cpp', '.c', '.h', '.png', '.jpg', '.jpeg'],
313
- input_format='text_or_image',
314
- output_format='text',
315
- permission_level=PermissionLevel.USER,
316
- version_check_command='gemini --version',
317
- auth_command='gemini auth',
318
- help_command='gemini --help'
319
- )
320
-
321
- # QwenCode CLI配置
322
- configs['qwencode'] = CLIConfig(
323
- name='qwencode',
324
- display_name='QwenCode CLI',
325
- command='qwencode',
326
- description='阿里云QwenCode CLI工具',
327
- required_env_vars=['QWEN_API_KEY'],
328
- optional_env_vars=['QWEN_BASE_URL', 'QWEN_MODEL'],
329
- config_files=['~/.qwencode/config.json', '~/.config/qwencode/config.json'],
330
- auth_method='api_key',
331
- supported_file_types=['.txt', '.md', '.py', '.js', '.ts', '.java', '.cpp', '.c', '.h'],
332
- input_format='text',
333
- output_format='text',
334
- permission_level=PermissionLevel.USER,
335
- version_check_command='qwencode --version',
336
- auth_command='qwencode auth',
337
- help_command='qwencode --help'
338
- )
339
-
340
- # iFlow CLI配置
341
- configs['iflow'] = CLIConfig(
342
- name='iflow',
343
- display_name='iFlow CLI',
344
- command='iflow',
345
- description='iFlow工作流CLI工具',
346
- required_env_vars=['IFLOW_API_KEY'],
347
- optional_env_vars=['IFLOW_BASE_URL', 'IFLOW_WORKSPACE'],
348
- config_files=['~/.iflow/config.json', '~/.config/iflow/config.json'],
349
- auth_method='api_key',
350
- supported_file_types=['.yml', '.yaml', '.json', '.txt', '.md'],
351
- input_format='workflow',
352
- output_format='workflow',
353
- permission_level=PermissionLevel.USER,
354
- version_check_command='iflow --version',
355
- help_command='iflow --help'
356
- )
357
-
358
- # Qoder CLI配置
359
- configs['qoder'] = CLIConfig(
360
- name='qoder',
361
- display_name='Qoder CLI',
362
- command='qoder',
363
- description='Qoder代码生成CLI工具',
364
- required_env_vars=['QODER_API_KEY'],
365
- optional_env_vars=['QODER_BASE_URL', 'QODER_MODEL'],
366
- config_files=['~/.qoder/config.json', '~/.config/qoder/config.json'],
367
- auth_method='api_key',
368
- supported_file_types=['.txt', '.md', '.py', '.js', '.ts', '.java', '.cpp', '.c', '.h'],
369
- input_format='text',
370
- output_format='code',
371
- permission_level=PermissionLevel.USER,
372
- version_check_command='qoder --version',
373
- help_command='qoder --help'
374
- )
375
-
376
- # CodeBuddy CLI配置
377
- configs['codebuddy'] = CLIConfig(
378
- name='codebuddy',
379
- display_name='CodeBuddy CLI',
380
- command='codebuddy',
381
- description='CodeBuddy编程助手CLI工具',
382
- required_env_vars=['CODEBUDDY_API_KEY'],
383
- optional_env_vars=['CODEBUDDY_BASE_URL', 'CODEBUDDY_MODEL'],
384
- config_files=['~/.codebuddy/config.json', '~/.config/codebuddy/config.json'],
385
- auth_method='api_key',
386
- supported_file_types=['.txt', '.md', '.py', '.js', '.ts', '.java', '.cpp', '.c', '.h'],
387
- input_format='text',
388
- output_format='text',
389
- permission_level=PermissionLevel.USER,
390
- version_check_command='codebuddy --version',
391
- help_command='codebuddy --help'
392
- )
393
-
394
- # Copilot CLI配置
395
- configs['copilot'] = CLIConfig(
396
- name='copilot',
397
- display_name='GitHub Copilot CLI',
398
- command='copilot',
399
- description='GitHub Copilot CLI工具',
400
- required_env_vars=['GITHUB_TOKEN'],
401
- optional_env_vars=['COPILOT_API_KEY', 'COPILOT_MODEL'],
402
- config_files=['~/.config/copilot/config.json', '~/.config/github/copilot/config.json'],
403
- auth_method='oauth_or_token',
404
- supported_file_types=['.txt', '.md', '.py', '.js', '.ts', '.java', '.cpp', '.c', '.h'],
405
- input_format='text',
406
- output_format='text',
407
- permission_level=PermissionLevel.USER,
408
- version_check_command='copilot --version',
409
- auth_command='copilot auth',
410
- help_command='copilot --help'
411
- )
412
-
413
- # Codex CLI配置
414
- configs['codex'] = CLIConfig(
415
- name='codex',
416
- display_name='Codex CLI',
417
- command='codex',
418
- description='OpenAI Codex代码分析CLI工具',
419
- required_env_vars=['OPENAI_API_KEY'],
420
- optional_env_vars=['OPENAI_ORGANIZATION', 'OPENAI_BASE_URL'],
421
- config_files=['~/.codex/config.json', '~/.config/codex/config.json'],
422
- auth_method='api_key',
423
- supported_file_types=['.txt', '.md', '.py', '.js', '.ts', '.java', '.cpp', '.c', '.h'],
424
- input_format='text',
425
- output_format='text',
426
- permission_level=PermissionLevel.USER,
427
- version_check_command='codex --version',
428
- help_command='codex --help'
429
- )
430
-
431
- # Cline CLI配置
432
- configs['cline'] = CLIConfig(
433
- name='cline',
434
- display_name='Cline CLI',
435
- command='cline',
436
- description='Cline AI助手CLI工具 - 支持任务生命周期钩子和多智能体编排',
437
- required_env_vars=['ANTHROPIC_API_KEY'], # Cline支持多种API提供商
438
- optional_env_vars=['OPENAI_API_KEY', 'GEMINI_API_KEY', 'OPENROUTER_API_KEY', 'CLINE_MODEL', 'CLINE_PROVIDER'],
439
- config_files=['~/.cline/config.json', '~/.config/cline/config.json'],
440
- auth_method='api_key_or_oauth',
441
- supported_file_types=['.txt', '.md', '.py', '.js', '.ts', '.java', '.cpp', '.c', '.h', '.json', '.yml', '.yaml'],
442
- input_format='text',
443
- output_format='text',
444
- permission_level=PermissionLevel.USER,
445
- version_check_command='cline --version',
446
- auth_command='cline auth',
447
- help_command='cline --help'
448
- )
449
-
450
- return configs
451
-
452
- def check_cli_status(self, cli_name: str) -> Tuple[CLIStatus, str]:
453
- """检查CLI状态"""
454
- if cli_name not in self.cli_configs:
455
- return CLIStatus.NOT_FOUND, f"CLI工具 '{cli_name}' 不支持"
456
-
457
- config = self.cli_configs[cli_name]
458
-
459
- # 检查是否安装
460
- try:
461
- result = subprocess.run(
462
- [config.command, '--version'],
463
- capture_output=True,
464
- text=True,
465
- timeout=10
466
- )
467
- if result.returncode != 0:
468
- return CLIStatus.NOT_FOUND, f"CLI工具 '{config.display_name}' 未安装"
469
- except (subprocess.TimeoutExpired, FileNotFoundError):
470
- return CLIStatus.NOT_FOUND, f"CLI工具 '{config.display_name}' 未安装"
471
-
472
- # 检查环境变量
473
- missing_vars = []
474
- for var in config.required_env_vars:
475
- if not os.environ.get(var):
476
- missing_vars.append(var)
477
-
478
- if missing_vars:
479
- return CLIStatus.ERROR, f"缺少环境变量: {', '.join(missing_vars)}"
480
-
481
- # 检查认证
482
- if config.auth_method == 'api_key':
483
- return CLIStatus.AUTHENTICATED, f"{config.display_name} 已认证"
484
- elif config.auth_command:
485
- # 尝试运行认证检查命令
486
- try:
487
- auth_result = subprocess.run(
488
- config.auth_command.split(),
489
- capture_output=True,
490
- text=True,
491
- timeout=10
492
- )
493
- if auth_result.returncode == 0:
494
- return CLIStatus.AUTHENTICATED, f"{config.display_name} 已认证"
495
- else:
496
- return CLIStatus.CONFIGURED, f"{config.display_name} 需要重新认证"
497
- except:
498
- return CLIStatus.CONFIGURED, f"{config.display_name} 认证状态未知"
499
-
500
- return CLIStatus.CONFIGURED, f"{config.display_name} 已配置"
501
-
502
- @encoding_safe
503
- def execute_cli_command(self, command: CLICommand) -> ExecutionResult:
504
- """安全执行CLI命令"""
505
- start_time = datetime.now()
506
-
507
- # 检查权限
508
- required_permission = command.required_permission
509
- current_permission = self.permission_manager.check_permission_level(
510
- self.cli_configs[command.cli_name]
511
- )
512
-
513
- # 权限不足时尝试提升
514
- if self._compare_permission_levels(current_permission, required_permission) < 0:
515
- success, message = self.permission_manager.elevate_privileges(required_permission)
516
- if not success:
517
- return ExecutionResult(
518
- success=False,
519
- exit_code=-1,
520
- stdout="",
521
- stderr=f"权限不足: {message}",
522
- execution_time=0,
523
- command_executed=command.command,
524
- cli_name=command.cli_name,
525
- files_created=[],
526
- files_modified=[],
527
- error_message=f"权限提升失败: {message}"
528
- )
529
-
530
- # 准备执行环境
531
- env = self._prepare_execution_environment(command.cli_name)
532
- working_dir = Path(command.working_dir) if command.working_dir else Path.cwd()
533
-
534
- # 准备命令
535
- full_command = self._build_command(command)
536
-
537
- try:
538
- # 执行命令
539
- self.logger.info(f"执行命令: {full_command} 在目录: {working_dir}")
540
-
541
- # 记录执行前的文件状态
542
- before_files = set(working_dir.rglob('*')) if working_dir.exists() else set()
543
-
544
- result = subprocess.run(
545
- full_command,
546
- cwd=working_dir,
547
- env=env,
548
- capture_output=True,
549
- text=True,
550
- timeout=command.timeout
551
- )
552
-
553
- # 记录执行后的文件状态
554
- after_files = set(working_dir.rglob('*')) if working_dir.exists() else set()
555
- files_created = list(after_files - before_files)
556
- files_modified = [f for f in before_files.intersection(after_files) if f.is_file()]
557
-
558
- # 分析输出文件
559
- output_files = self._analyze_output_files(
560
- result.stdout,
561
- command.output_files,
562
- working_dir
563
- )
564
-
565
- execution_time = (datetime.now() - start_time).total_seconds()
566
-
567
- # 记录结果
568
- self.logger.info(f"命令执行完成: 退出码={result.returncode}, 耗时={execution_time}秒")
569
-
570
- return ExecutionResult(
571
- success=result.returncode == 0,
572
- exit_code=result.returncode,
573
- stdout=result.stdout,
574
- stderr=result.stderr,
575
- execution_time=execution_time,
576
- command_executed=full_command,
577
- cli_name=command.cli_name,
578
- files_created=[str(f) for f in files_created],
579
- files_modified=[str(f) for f in files_modified],
580
- output_files=output_files
581
- )
582
-
583
- except subprocess.TimeoutExpired:
584
- return ExecutionResult(
585
- success=False,
586
- exit_code=-1,
587
- stdout="",
588
- stderr=f"命令执行超时 ({command.timeout}秒)",
589
- execution_time=command.timeout,
590
- command_executed=full_command,
591
- cli_name=command.cli_name,
592
- files_created=[],
593
- files_modified=[],
594
- error_message="执行超时"
595
- )
596
- except Exception as e:
597
- return ExecutionResult(
598
- success=False,
599
- exit_code=-1,
600
- stdout="",
601
- stderr=str(e),
602
- execution_time=(datetime.now() - start_time).total_seconds(),
603
- command_executed=full_command,
604
- cli_name=command.cli_name,
605
- files_created=[],
606
- files_modified=[],
607
- error_message=str(e)
608
- )
609
-
610
- def _compare_permission_levels(self, current: PermissionLevel, required: PermissionLevel) -> int:
611
- """比较权限级别"""
612
- levels = {
613
- PermissionLevel.MINIMAL: 0,
614
- PermissionLevel.USER: 1,
615
- PermissionLevel.ELEVATED: 2,
616
- PermissionLevel.ADMIN: 3,
617
- PermissionLevel.SYSTEM: 4
618
- }
619
- return levels.get(current, 0) - levels.get(required, 0)
620
-
621
- def _prepare_execution_environment(self, cli_name: str) -> Dict[str, str]:
622
- """准备执行环境"""
623
- env = os.environ.copy()
624
-
625
- # 设置编码环境
626
- env['PYTHONIOENCODING'] = 'utf-8'
627
- if self.system == 'windows':
628
- env['PYTHONLEGACYWINDOWSSTDIO'] = 'utf-8'
629
-
630
- # 设置CLI特定环境变量
631
- config = self.cli_configs[cli_name]
632
- for var in config.required_env_vars + config.optional_env_vars:
633
- if var not in env:
634
- env[var] = os.environ.get(var, '')
635
-
636
- return env
637
-
638
- def _build_command(self, command: CLICommand) -> List[str]:
639
- """构建执行命令"""
640
- config = self.cli_configs[command.cli_name]
641
- cmd_parts = [config.command]
642
-
643
- # 添加命令特定参数
644
- if command.command_type == 'prompt':
645
- # 处理提示词命令
646
- if isinstance(command.command, str):
647
- cmd_parts.extend(command.command.split())
648
- else:
649
- cmd_parts.extend(command.command)
650
- elif command.command_type == 'file':
651
- # 处理文件命令
652
- for file_path in command.input_files:
653
- cmd_parts.extend(['--file', str(file_path)])
654
- elif command.command_type == 'config':
655
- # 处理配置命令
656
- cmd_parts.extend(['config'] + command.command.split())
657
- elif command.command_type == 'auth':
658
- # 处理认证命令
659
- cmd_parts.extend(['auth'] + command.command.split())
660
- elif command.command_type == 'help':
661
- # 处理帮助命令
662
- cmd_parts.append('--help')
663
-
664
- return cmd_parts
665
-
666
- def _analyze_output_files(self, stdout: str, expected_patterns: List[str],
667
- working_dir: Path) -> Dict[str, Path]:
668
- """分析输出文件"""
669
- output_files = {}
670
-
671
- # 从输出中查找文件路径
672
- import re
673
-
674
- # 匹配文件路径的正则表达式
675
- file_patterns = [
676
- r'输出文件[::]\s*([^\s\n]+)',
677
- r'output[::]\s*([^\s\n]+)',
678
- r'result[::]\s*([^\s\n]+)',
679
- r'文件已保存到[::]\s*([^\s\n]+)',
680
- r'file saved to[::]\s*([^\s\n]+)'
681
- ]
682
-
683
- for pattern in file_patterns:
684
- matches = re.findall(pattern, stdout, re.IGNORECASE)
685
- for match in matches:
686
- file_path = Path(match)
687
- if not file_path.is_absolute():
688
- file_path = working_dir / file_path
689
-
690
- if file_path.exists():
691
- output_files[f'output_{len(output_files)}'] = file_path
692
-
693
- # 检查预期的输出文件
694
- for pattern in expected_patterns:
695
- matching_files = working_dir.glob(pattern)
696
- for file_path in matching_files:
697
- output_files[f'expected_{file_path.name}'] = file_path
698
-
699
- return output_files
700
-
701
- def get_cli_help(self, cli_name: str) -> Optional[str]:
702
- """获取CLI帮助信息"""
703
- if cli_name not in self.cli_configs:
704
- return None
705
-
706
- config = self.cli_configs[cli_name]
707
-
708
- try:
709
- result = subprocess.run(
710
- [config.command, '--help'],
711
- capture_output=True,
712
- text=True,
713
- timeout=10
714
- )
715
- return result.stdout if result.returncode == 0 else None
716
- except:
717
- return None
718
-
719
- def create_global_memory_document(self, cli_name: str) -> Optional[Dict[str, Any]]:
720
- """创建全局记忆文档"""
721
- if cli_name not in self.cli_configs:
722
- return None
723
-
724
- config = self.cli_configs[cli_name]
725
- status, message = self.check_cli_status(cli_name)
726
- help_text = self.get_cli_help(cli_name)
727
-
728
- memory_doc = {
729
- 'cli_name': cli_name,
730
- 'display_name': config.display_name,
731
- 'description': config.description,
732
- 'command': config.command,
733
- 'status': status.value,
734
- 'status_message': message,
735
- 'auth_method': config.auth_method,
736
- 'supported_file_types': config.supported_file_types,
737
- 'input_format': config.input_format,
738
- 'output_format': config.output_format,
739
- 'required_env_vars': config.required_env_vars,
740
- 'optional_env_vars': config.optional_env_vars,
741
- 'config_files': config.config_files,
742
- 'permission_level': config.permission_level.value,
743
- 'version_check_command': config.version_check_command,
744
- 'auth_command': config.auth_command,
745
- 'help_command': config.help_command,
746
- 'help_text': help_text,
747
- 'usage_examples': self._generate_usage_examples(config),
748
- 'integration_capabilities': self._get_integration_capabilities(config),
749
- 'cross_cli_collaboration': self._get_cross_cli_collaboration_info(config),
750
- 'last_updated': datetime.now().isoformat()
751
- }
752
-
753
- return memory_doc
754
-
755
- def _generate_usage_examples(self, config: CLIConfig) -> List[Dict[str, str]]:
756
- """生成使用示例"""
757
- examples = []
758
-
759
- # 基本提示词示例
760
- examples.append({
761
- 'type': 'prompt',
762
- 'description': f'{config.display_name} 基本提示词',
763
- 'command': f'{config.command} "请解释这段代码的功能"',
764
- 'example': f'{config.command} "请解释这段Python代码的功能"'
765
- })
766
-
767
- # 文件处理示例
768
- if config.supported_file_types:
769
- examples.append({
770
- 'type': 'file',
771
- 'description': f'{config.display_name} 处理文件',
772
- 'command': f'{config.command} --file example.py',
773
- 'example': f'{config.command} --file ./src/main.py'
774
- })
775
-
776
- # 跨CLI协作示例
777
- examples.append({
778
- 'type': 'cross_cli',
779
- 'description': f'{config.display_name} 跨CLI协作',
780
- 'command': f'{config.command} "请用gemini帮我优化这段代码"',
781
- 'example': f'{config.command} "请用claude帮我分析这个算法"'
782
- })
783
-
784
- return examples
785
-
786
- def _get_integration_capabilities(self, config: CLIConfig) -> Dict[str, Any]:
787
- """获取集成能力"""
788
- return {
789
- 'can_process_files': len(config.supported_file_types) > 0,
790
- 'supports_images': 'image' in config.input_format.lower(),
791
- 'supports_workflows': config.input_format == 'workflow',
792
- 'can_generate_code': config.output_format == 'code',
793
- 'requires_authentication': config.auth_method in ['api_key', 'oauth'],
794
- 'supports_batch_processing': 'batch' in config.command.lower(),
795
- 'supports_streaming': 'stream' in config.command.lower()
796
- }
797
-
798
- def _get_cross_cli_collaboration_info(self, config: CLIConfig) -> Dict[str, Any]:
799
- """获取跨CLI协作信息"""
800
- all_clis = list(self.cli_configs.keys())
801
- other_clis = [cli for cli in all_clis if cli != config.name]
802
-
803
- return {
804
- 'can_call_other_clis': True,
805
- 'supported_target_clis': other_clis,
806
- 'collaboration_patterns': [
807
- {
808
- 'pattern': f'请用{{target_cli}}帮我{{action}}',
809
- 'description': '标准跨CLI调用模式',
810
- 'example': f'请用claude帮我审查代码'
811
- },
812
- {
813
- 'pattern': f'use {{target_cli}} to {{action}}',
814
- 'description': '英文跨CLI调用模式',
815
- 'example': f'use gemini to analyze this code'
816
- },
817
- {
818
- 'pattern': f'调用{{target_cli}}{{action}}',
819
- 'description': '中文跨CLI调用模式',
820
- 'example': f'调用qwen生成测试用例'
821
- }
822
- ],
823
- 'supported_collaborations': {
824
- 'claude': ['code_review', 'analysis', 'documentation'],
825
- 'gemini': ['translation', 'optimization', 'refactoring'],
826
- 'qwencode': ['code_generation', 'debugging', 'testing'],
827
- 'iflow': ['workflow_creation', 'automation', 'integration'],
828
- 'qoder': ['code_completion', 'snippet_generation', 'template_creation'],
829
- 'codebuddy': ['learning', 'tutorial', 'explanation'],
830
- 'copilot': ['pair_programming', 'suggestion', 'completion'],
831
- 'codex': ['code_analysis', 'pattern_detection', 'optimization']
832
- }.get(config.name, [])
833
- }
834
-
835
- # 全局实例
836
- _cli_executor = None
837
-
838
- def get_cli_executor() -> CrossPlatformSafeCLI:
839
- """获取CLI执行器实例"""
840
- global _cli_executor
841
- if _cli_executor is None:
842
- _cli_executor = CrossPlatformSafeCLI()
843
- return _cli_executor
844
-
845
- @encoding_safe
846
- def create_all_global_memory_documents() -> Dict[str, Dict[str, Any]]:
847
- """创建所有CLI的全局记忆文档"""
848
- executor = get_cli_executor()
849
- memory_docs = {}
850
-
851
- for cli_name in executor.cli_configs.keys():
852
- doc = executor.create_global_memory_document(cli_name)
853
- if doc:
854
- memory_docs[cli_name] = doc
855
-
856
- return memory_docs
857
-
858
- def main():
859
- """主函数"""
860
- executor = get_cli_executor()
861
-
862
- print("🔧 跨平台安全CLI调用系统")
863
- print("=" * 50)
864
- executor.encoding_installer.print_system_info()
865
-
866
- # 检查所有CLI状态
867
- print("\n📋 CLI工具状态检查:")
868
- for cli_name in executor.cli_configs.keys():
869
- status, message = executor.check_cli_status(cli_name)
870
- status_icon = "✅" if status in [CLIStatus.AVAILABLE, CLIStatus.INSTALLED, CLIStatus.CONFIGURED, CLIStatus.AUTHENTICATED] else "❌"
871
- print(f" {status_icon} {executor.cli_configs[cli_name].display_name:<20} {status.value}: {message}")
872
-
873
- # 创建全局记忆文档
874
- print("\n📚 创建全局记忆文档...")
875
- memory_docs = create_all_global_memory_documents()
876
-
877
- # 保存到文件
878
- memory_dir = Path('.') / 'global_memory'
879
- memory_dir.mkdir(exist_ok=True)
880
-
881
- for cli_name, doc in memory_docs.items():
882
- memory_file = memory_dir / f'{cli_name}_memory.json'
883
- try:
884
- with open(memory_file, 'w', encoding='utf-8') as f:
885
- json.dump(doc, f, indent=2, ensure_ascii=False)
886
- print(f" ✅ {doc['display_name']} 记忆文档已创建")
887
- except Exception as e:
888
- print(f" ❌ {doc['display_name']} 记忆文档创建失败: {e}")
889
-
890
- print(f"\n📁 全局记忆文档已保存到: {memory_dir}")
891
- print("\n🎯 CLI调用系统就绪!")
892
-
893
- if __name__ == "__main__":
894
- main()