stigmergy 1.0.68 → 1.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +306 -300
- package/README.md +469 -301
- package/package.json +97 -81
- package/scripts/publish.js +268 -0
- package/scripts/simple-publish.js +59 -0
- package/src/index.js +12 -0
- package/test/enhanced-main-alignment.test.js +298 -0
- package/test/hook-system-integration-test.js +307 -0
- package/test/natural-language-skills-test.js +320 -0
- package/test/nl-integration-test.js +179 -0
- package/test/parameter-parsing-test.js +143 -0
- package/test/real-test.js +435 -0
- package/test/system-compatibility-test.js +447 -0
- package/test/tdd-fixes-test.js +211 -0
- package/test/third-party-skills-test.js +321 -0
- package/test/tool-selection-integration-test.js +157 -0
- package/test/unit/cli-scanner.test.js +291 -0
- package/test/unit/cross-cli-executor.test.js +399 -0
- package/src/adapters/claude/__init__.py +0 -13
- package/src/adapters/claude/claude_skills_integration.py +0 -609
- package/src/adapters/claude/hook_adapter.py +0 -663
- package/src/adapters/claude/install_claude_integration.py +0 -265
- package/src/adapters/claude/skills_hook_adapter.py +0 -841
- package/src/adapters/claude/standalone_claude_adapter.py +0 -384
- package/src/adapters/cline/__init__.py +0 -20
- package/src/adapters/cline/config.py +0 -108
- package/src/adapters/cline/install_cline_integration.py +0 -617
- package/src/adapters/cline/mcp_server.py +0 -713
- package/src/adapters/cline/standalone_cline_adapter.py +0 -459
- package/src/adapters/codebuddy/__init__.py +0 -13
- package/src/adapters/codebuddy/buddy_adapter.py +0 -1125
- package/src/adapters/codebuddy/install_codebuddy_integration.py +0 -279
- package/src/adapters/codebuddy/skills_hook_adapter.py +0 -672
- package/src/adapters/codebuddy/skills_integration.py +0 -395
- package/src/adapters/codebuddy/standalone_codebuddy_adapter.py +0 -403
- package/src/adapters/codex/__init__.py +0 -11
- package/src/adapters/codex/base.py +0 -46
- package/src/adapters/codex/install_codex_integration.py +0 -311
- package/src/adapters/codex/mcp_server.py +0 -493
- package/src/adapters/codex/natural_language_parser.py +0 -82
- package/src/adapters/codex/slash_command_adapter.py +0 -326
- package/src/adapters/codex/standalone_codex_adapter.py +0 -362
- package/src/adapters/copilot/__init__.py +0 -13
- package/src/adapters/copilot/install_copilot_integration.py +0 -564
- package/src/adapters/copilot/mcp_adapter.py +0 -772
- package/src/adapters/copilot/mcp_server.py +0 -168
- package/src/adapters/copilot/standalone_copilot_adapter.py +0 -114
- package/src/adapters/gemini/__init__.py +0 -13
- package/src/adapters/gemini/extension_adapter.py +0 -690
- package/src/adapters/gemini/install_gemini_integration.py +0 -257
- package/src/adapters/gemini/standalone_gemini_adapter.py +0 -366
- package/src/adapters/iflow/__init__.py +0 -7
- package/src/adapters/iflow/hook_adapter.py +0 -1038
- package/src/adapters/iflow/hook_installer.py +0 -536
- package/src/adapters/iflow/install_iflow_integration.py +0 -271
- package/src/adapters/iflow/official_hook_adapter.py +0 -1272
- package/src/adapters/iflow/standalone_iflow_adapter.py +0 -48
- package/src/adapters/iflow/workflow_adapter.py +0 -793
- package/src/adapters/qoder/hook_installer.py +0 -732
- package/src/adapters/qoder/install_qoder_integration.py +0 -265
- package/src/adapters/qoder/notification_hook_adapter.py +0 -863
- package/src/adapters/qoder/standalone_qoder_adapter.py +0 -48
- package/src/adapters/qwen/__init__.py +0 -17
- package/src/adapters/qwencode/__init__.py +0 -13
- package/src/adapters/qwencode/inheritance_adapter.py +0 -818
- package/src/adapters/qwencode/install_qwencode_integration.py +0 -276
- package/src/adapters/qwencode/standalone_qwencode_adapter.py +0 -399
- package/src/atomic_collaboration_handler.py +0 -461
- package/src/cli_collaboration_agent.py +0 -697
- package/src/collaboration/hooks.py +0 -315
- package/src/core/__init__.py +0 -21
- package/src/core/ai_environment_scanner.py +0 -331
- package/src/core/base_adapter.py +0 -220
- package/src/core/cli_hook_integration.py +0 -406
- package/src/core/cross_cli_executor.py +0 -713
- package/src/core/cross_cli_mapping.py +0 -1165
- package/src/core/cross_platform_encoding.py +0 -365
- package/src/core/cross_platform_safe_cli.py +0 -894
- package/src/core/direct_cli_executor.py +0 -805
- package/src/core/direct_cli_hook_system.py +0 -958
- package/src/core/enhanced_init_processor.py +0 -467
- package/src/core/graceful_cli_executor.py +0 -912
- package/src/core/md_enhancer.py +0 -342
- package/src/core/md_generator.py +0 -619
- package/src/core/models.py +0 -218
- package/src/core/parser.py +0 -108
- package/src/core/real_cli_hook_system.py +0 -852
- package/src/core/real_cross_cli_system.py +0 -925
- package/src/core/verified_cross_cli_system.py +0 -961
- package/src/deploy.js +0 -737
- package/src/enhanced-main.js +0 -626
- package/src/enhanced_deploy.js +0 -303
- package/src/enhanced_universal_cli_setup.py +0 -930
- package/src/kimi_wrapper.py +0 -104
- package/src/main.js +0 -1309
- package/src/shell_integration.py +0 -398
- package/src/simple-main.js +0 -315
- package/src/smart_router_creator.py +0 -323
- package/src/universal_cli_setup.py +0 -1289
- package/src/utils/__init__.py +0 -12
- package/src/utils/cli_detector.py +0 -445
- package/src/utils/file_utils.py +0 -246
|
@@ -1,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()
|