stigmergy 1.0.57
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/LICENSE +19 -0
- package/README.de.md +301 -0
- package/README.en.md +301 -0
- package/README.es.md +301 -0
- package/README.fr.md +301 -0
- package/README.ja.md +301 -0
- package/README.ko.md +301 -0
- package/README.md +301 -0
- package/README.ru.md +301 -0
- package/README.zh.md +301 -0
- package/package.json +82 -0
- package/src/adapters/claude/__init__.py +13 -0
- package/src/adapters/claude/claude_skills_integration.py +609 -0
- package/src/adapters/claude/hook_adapter.py +663 -0
- package/src/adapters/claude/install_claude_integration.py +265 -0
- package/src/adapters/claude/skills_hook_adapter.py +841 -0
- package/src/adapters/claude/standalone_claude_adapter.py +384 -0
- package/src/adapters/cline/__init__.py +20 -0
- package/src/adapters/cline/config.py +108 -0
- package/src/adapters/cline/install_cline_integration.py +617 -0
- package/src/adapters/cline/mcp_server.py +713 -0
- package/src/adapters/cline/standalone_cline_adapter.py +459 -0
- package/src/adapters/codebuddy/__init__.py +13 -0
- package/src/adapters/codebuddy/buddy_adapter.py +1125 -0
- package/src/adapters/codebuddy/install_codebuddy_integration.py +279 -0
- package/src/adapters/codebuddy/skills_hook_adapter.py +672 -0
- package/src/adapters/codebuddy/skills_integration.py +395 -0
- package/src/adapters/codebuddy/standalone_codebuddy_adapter.py +403 -0
- package/src/adapters/codex/__init__.py +11 -0
- package/src/adapters/codex/base.py +46 -0
- package/src/adapters/codex/install_codex_integration.py +311 -0
- package/src/adapters/codex/mcp_server.py +493 -0
- package/src/adapters/codex/natural_language_parser.py +82 -0
- package/src/adapters/codex/slash_command_adapter.py +326 -0
- package/src/adapters/codex/standalone_codex_adapter.py +362 -0
- package/src/adapters/copilot/__init__.py +13 -0
- package/src/adapters/copilot/install_copilot_integration.py +564 -0
- package/src/adapters/copilot/mcp_adapter.py +772 -0
- package/src/adapters/copilot/mcp_server.py +168 -0
- package/src/adapters/copilot/standalone_copilot_adapter.py +114 -0
- package/src/adapters/gemini/__init__.py +13 -0
- package/src/adapters/gemini/extension_adapter.py +690 -0
- package/src/adapters/gemini/install_gemini_integration.py +257 -0
- package/src/adapters/gemini/standalone_gemini_adapter.py +366 -0
- package/src/adapters/iflow/__init__.py +7 -0
- package/src/adapters/iflow/hook_adapter.py +1038 -0
- package/src/adapters/iflow/hook_installer.py +536 -0
- package/src/adapters/iflow/install_iflow_integration.py +271 -0
- package/src/adapters/iflow/official_hook_adapter.py +1272 -0
- package/src/adapters/iflow/standalone_iflow_adapter.py +48 -0
- package/src/adapters/iflow/workflow_adapter.py +793 -0
- package/src/adapters/qoder/hook_installer.py +732 -0
- package/src/adapters/qoder/install_qoder_integration.py +265 -0
- package/src/adapters/qoder/notification_hook_adapter.py +863 -0
- package/src/adapters/qoder/standalone_qoder_adapter.py +48 -0
- package/src/adapters/qwen/__init__.py +17 -0
- package/src/adapters/qwencode/__init__.py +13 -0
- package/src/adapters/qwencode/inheritance_adapter.py +818 -0
- package/src/adapters/qwencode/install_qwencode_integration.py +276 -0
- package/src/adapters/qwencode/standalone_qwencode_adapter.py +399 -0
- package/src/atomic_collaboration_handler.py +461 -0
- package/src/cli_collaboration_agent.py +697 -0
- package/src/collaboration/hooks.py +315 -0
- package/src/core/__init__.py +21 -0
- package/src/core/ai_environment_scanner.py +331 -0
- package/src/core/base_adapter.py +220 -0
- package/src/core/cli_hook_integration.py +406 -0
- package/src/core/cross_cli_executor.py +713 -0
- package/src/core/cross_cli_mapping.py +1163 -0
- package/src/core/cross_platform_encoding.py +365 -0
- package/src/core/cross_platform_safe_cli.py +894 -0
- package/src/core/direct_cli_executor.py +805 -0
- package/src/core/direct_cli_hook_system.py +958 -0
- package/src/core/enhanced_init_processor.py +427 -0
- package/src/core/graceful_cli_executor.py +912 -0
- package/src/core/md_enhancer.py +342 -0
- package/src/core/md_generator.py +619 -0
- package/src/core/models.py +218 -0
- package/src/core/parser.py +108 -0
- package/src/core/real_cli_hook_system.py +852 -0
- package/src/core/real_cross_cli_system.py +925 -0
- package/src/core/verified_cross_cli_system.py +961 -0
- package/src/deploy.js +737 -0
- package/src/enhanced_deploy.js +303 -0
- package/src/enhanced_universal_cli_setup.py +930 -0
- package/src/kimi_wrapper.py +104 -0
- package/src/main.js +1309 -0
- package/src/shell_integration.py +398 -0
- package/src/simple-main.js +315 -0
- package/src/smart_router_creator.py +323 -0
- package/src/universal_cli_setup.py +1289 -0
- package/src/utils/__init__.py +12 -0
- package/src/utils/cli_detector.py +445 -0
- package/src/utils/file_utils.py +246 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI CLI Universal Integration System - 工具模块
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .cli_detector import CLIDetector, get_cli_detector, detect_claude_cli_status, generate_environment_report
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'CLIDetector',
|
|
9
|
+
'get_cli_detector',
|
|
10
|
+
'detect_claude_cli_status',
|
|
11
|
+
'generate_environment_report'
|
|
12
|
+
]
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI Detector - CLI工具检测器
|
|
3
|
+
检测系统中可用的AI CLI工具及其扩展机制
|
|
4
|
+
|
|
5
|
+
这是TDD驱动的实现,用于实际环境检测
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import subprocess
|
|
11
|
+
import json
|
|
12
|
+
import shutil
|
|
13
|
+
from typing import Dict, List, Optional, Tuple, Any
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CLIDetector:
|
|
21
|
+
"""CLI工具检测器"""
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
"""初始化检测器"""
|
|
25
|
+
self.cli_tools = {
|
|
26
|
+
'claude': {
|
|
27
|
+
'commands': ['claude-cli', 'claude'],
|
|
28
|
+
'version_flag': '--version',
|
|
29
|
+
'config_dir': '~/.config/claude',
|
|
30
|
+
'hooks_file': '~/.config/claude/hooks.json',
|
|
31
|
+
'extension_mechanism': 'hook_system'
|
|
32
|
+
},
|
|
33
|
+
'gemini': {
|
|
34
|
+
'commands': ['gemini-cli', 'gemini'],
|
|
35
|
+
'version_flag': '--version',
|
|
36
|
+
'config_dir': '~/.config/gemini',
|
|
37
|
+
'extensions_file': '~/.config/gemini/extensions.json',
|
|
38
|
+
'extension_mechanism': 'extension_system'
|
|
39
|
+
},
|
|
40
|
+
'qwencode': {
|
|
41
|
+
'commands': ['qwencode-cli', 'qwencode', 'qwen-code'],
|
|
42
|
+
'version_flag': '--version',
|
|
43
|
+
'config_dir': '~/.config/qwencode',
|
|
44
|
+
'config_file': '~/.config/qwencode/config.yml',
|
|
45
|
+
'extension_mechanism': 'class_inheritance'
|
|
46
|
+
},
|
|
47
|
+
'iflow': {
|
|
48
|
+
'commands': ['iflow-cli', 'iflow'],
|
|
49
|
+
'version_flag': '--version',
|
|
50
|
+
'config_dir': '~/.config/iflow',
|
|
51
|
+
'workflows_dir': '~/.config/iflow/workflows',
|
|
52
|
+
'extension_mechanism': 'workflow_nodes'
|
|
53
|
+
},
|
|
54
|
+
'qoder': {
|
|
55
|
+
'commands': ['qoder-cli', 'qoder'],
|
|
56
|
+
'version_flag': '--version',
|
|
57
|
+
'config_dir': '~/.config/qoder',
|
|
58
|
+
'plugins_file': '~/.config/qoder/plugins.json',
|
|
59
|
+
'extension_mechanism': 'plugin_system'
|
|
60
|
+
},
|
|
61
|
+
'codebuddy': {
|
|
62
|
+
'commands': ['codebuddy', 'codebuddy-cli'],
|
|
63
|
+
'version_flag': '--version',
|
|
64
|
+
'config_dir': '~/.config/codebuddy',
|
|
65
|
+
'buddies_file': '~/.config/codebuddy/buddies.yml',
|
|
66
|
+
'extension_mechanism': 'buddy_system'
|
|
67
|
+
},
|
|
68
|
+
'codex': {
|
|
69
|
+
'commands': ['codex-cli', 'codex'],
|
|
70
|
+
'version_flag': '--version',
|
|
71
|
+
'config_dir': '~/.config/codex',
|
|
72
|
+
'extensions_file': '~/.config/codex/extensions.json',
|
|
73
|
+
'extension_mechanism': 'extension_system'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
def detect_all_cli_tools(self) -> Dict[str, Dict[str, Any]]:
|
|
78
|
+
"""
|
|
79
|
+
检测所有CLI工具的可用性和状态
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Dict[str, Dict[str, Any]]: 检测结果
|
|
83
|
+
"""
|
|
84
|
+
results = {}
|
|
85
|
+
|
|
86
|
+
for cli_name, cli_info in self.cli_tools.items():
|
|
87
|
+
results[cli_name] = self._detect_single_cli(cli_name, cli_info)
|
|
88
|
+
|
|
89
|
+
return results
|
|
90
|
+
|
|
91
|
+
def detect_claude_cli(self) -> Dict[str, Any]:
|
|
92
|
+
"""
|
|
93
|
+
专门检测Claude CLI工具
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Dict[str, Any]: Claude CLI检测结果
|
|
97
|
+
"""
|
|
98
|
+
if 'claude' not in self.cli_tools:
|
|
99
|
+
return {'error': 'Claude CLI not configured'}
|
|
100
|
+
|
|
101
|
+
return self._detect_single_cli('claude', self.cli_tools['claude'])
|
|
102
|
+
|
|
103
|
+
def _detect_single_cli(self, cli_name: str, cli_info: Dict[str, Any]) -> Dict[str, Any]:
|
|
104
|
+
"""
|
|
105
|
+
检测单个CLI工具
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
cli_name: CLI工具名称
|
|
109
|
+
cli_info: CLI工具信息
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Dict[str, Any]: 检测结果
|
|
113
|
+
"""
|
|
114
|
+
result = {
|
|
115
|
+
'name': cli_name,
|
|
116
|
+
'available': False,
|
|
117
|
+
'command': None,
|
|
118
|
+
'version': None,
|
|
119
|
+
'extension_mechanism': cli_info['extension_mechanism'],
|
|
120
|
+
'config_dir': self._expand_path(cli_info['config_dir']),
|
|
121
|
+
'config_exists': False,
|
|
122
|
+
'extension_ready': False,
|
|
123
|
+
'error': None
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
# 1. 检测CLI命令是否可用
|
|
128
|
+
command = self._find_cli_command(cli_info['commands'])
|
|
129
|
+
if command:
|
|
130
|
+
result['command'] = command
|
|
131
|
+
result['available'] = True
|
|
132
|
+
|
|
133
|
+
# 2. 获取版本信息
|
|
134
|
+
version = self._get_cli_version(command, cli_info['version_flag'])
|
|
135
|
+
result['version'] = version
|
|
136
|
+
|
|
137
|
+
# 3. 检查配置目录
|
|
138
|
+
config_dir = result['config_dir']
|
|
139
|
+
if os.path.exists(config_dir):
|
|
140
|
+
result['config_exists'] = True
|
|
141
|
+
|
|
142
|
+
# 4. 检查扩展机制是否准备就绪
|
|
143
|
+
result['extension_ready'] = self._check_extension_readiness(cli_name, cli_info, config_dir)
|
|
144
|
+
|
|
145
|
+
else:
|
|
146
|
+
result['error'] = f"CLI命令未找到: {cli_info['commands']}"
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
result['error'] = str(e)
|
|
150
|
+
logger.error(f"检测 {cli_name} CLI失败: {e}")
|
|
151
|
+
|
|
152
|
+
return result
|
|
153
|
+
|
|
154
|
+
def _find_cli_command(self, commands: List[str]) -> Optional[str]:
|
|
155
|
+
"""
|
|
156
|
+
查找可用的CLI命令
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
commands: 候选命令列表
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Optional[str]: 找到的命令路径,如果没找到返回None
|
|
163
|
+
"""
|
|
164
|
+
for command in commands:
|
|
165
|
+
# 首先检查是否在PATH中
|
|
166
|
+
command_path = shutil.which(command)
|
|
167
|
+
if command_path:
|
|
168
|
+
return command_path
|
|
169
|
+
|
|
170
|
+
# 检查是否为Python模块
|
|
171
|
+
if self._is_python_module(command):
|
|
172
|
+
return f"python -m {command}"
|
|
173
|
+
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
def _is_python_module(self, module_name: str) -> bool:
|
|
177
|
+
"""
|
|
178
|
+
检查是否为Python模块
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
module_name: 模块名称
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
bool: 是否为Python模块
|
|
185
|
+
"""
|
|
186
|
+
try:
|
|
187
|
+
# 尝试导入模块
|
|
188
|
+
import importlib.util
|
|
189
|
+
spec = importlib.util.find_spec(module_name)
|
|
190
|
+
return spec is not None
|
|
191
|
+
except (ImportError, ValueError):
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
def _get_cli_version(self, command: str, version_flag: str) -> Optional[str]:
|
|
195
|
+
"""
|
|
196
|
+
获取CLI工具版本
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
command: CLI命令
|
|
200
|
+
version_flag: 版本标志
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Optional[str]: 版本信息
|
|
204
|
+
"""
|
|
205
|
+
try:
|
|
206
|
+
result = subprocess.run(
|
|
207
|
+
f"{command} {version_flag}",
|
|
208
|
+
shell=True,
|
|
209
|
+
capture_output=True,
|
|
210
|
+
text=True,
|
|
211
|
+
timeout=10
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if result.returncode == 0:
|
|
215
|
+
return result.stdout.strip()
|
|
216
|
+
else:
|
|
217
|
+
logger.warning(f"获取版本失败: {command} {version_flag}")
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
except subprocess.TimeoutExpired:
|
|
221
|
+
logger.error(f"获取版本超时: {command}")
|
|
222
|
+
return None
|
|
223
|
+
except Exception as e:
|
|
224
|
+
logger.error(f"获取版本异常: {command}, {e}")
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
def _check_extension_readiness(self, cli_name: str, cli_info: Dict[str, Any], config_dir: str) -> bool:
|
|
228
|
+
"""
|
|
229
|
+
检查扩展机制是否准备就绪
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
cli_name: CLI工具名称
|
|
233
|
+
cli_info: CLI工具信息
|
|
234
|
+
config_dir: 配置目录
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
bool: 扩展机制是否准备就绪
|
|
238
|
+
"""
|
|
239
|
+
try:
|
|
240
|
+
mechanism = cli_info['extension_mechanism']
|
|
241
|
+
|
|
242
|
+
if mechanism == 'hook_system':
|
|
243
|
+
# 检查Hook系统
|
|
244
|
+
hooks_file = self._expand_path(cli_info.get('hooks_file', ''))
|
|
245
|
+
return self._check_hook_system(hooks_file)
|
|
246
|
+
|
|
247
|
+
elif mechanism == 'extension_system':
|
|
248
|
+
# 检查扩展系统
|
|
249
|
+
extensions_file = self._expand_path(cli_info.get('extensions_file', ''))
|
|
250
|
+
return self._check_extension_system(extensions_file)
|
|
251
|
+
|
|
252
|
+
elif mechanism == 'class_inheritance':
|
|
253
|
+
# 检查类继承机制
|
|
254
|
+
config_file = self._expand_path(cli_info.get('config_file', ''))
|
|
255
|
+
return self._check_class_inheritance(config_file)
|
|
256
|
+
|
|
257
|
+
elif mechanism == 'workflow_nodes':
|
|
258
|
+
# 检查工作流节点
|
|
259
|
+
workflows_dir = self._expand_path(cli_info.get('workflows_dir', ''))
|
|
260
|
+
return self._check_workflow_nodes(workflows_dir)
|
|
261
|
+
|
|
262
|
+
elif mechanism == 'plugin_system':
|
|
263
|
+
# 检查插件系统
|
|
264
|
+
plugins_file = self._expand_path(cli_info.get('plugins_file', ''))
|
|
265
|
+
return self._check_plugin_system(plugins_file)
|
|
266
|
+
|
|
267
|
+
elif mechanism == 'buddy_system':
|
|
268
|
+
# 检查伙伴系统
|
|
269
|
+
buddies_file = self._expand_path(cli_info.get('buddies_file', ''))
|
|
270
|
+
return self._check_buddy_system(buddies_file)
|
|
271
|
+
|
|
272
|
+
else:
|
|
273
|
+
logger.warning(f"未知的扩展机制: {mechanism}")
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
except Exception as e:
|
|
277
|
+
logger.error(f"检查扩展机制失败: {cli_name}, {e}")
|
|
278
|
+
return False
|
|
279
|
+
|
|
280
|
+
def _check_hook_system(self, hooks_file: str) -> bool:
|
|
281
|
+
"""检查Hook系统"""
|
|
282
|
+
if os.path.exists(hooks_file):
|
|
283
|
+
try:
|
|
284
|
+
with open(hooks_file, 'r') as f:
|
|
285
|
+
hooks_config = json.load(f)
|
|
286
|
+
return isinstance(hooks_config, dict) and 'hooks' in hooks_config
|
|
287
|
+
except:
|
|
288
|
+
return False
|
|
289
|
+
return True # 文件不存在说明尚未配置,但机制可用
|
|
290
|
+
|
|
291
|
+
def _check_extension_system(self, extensions_file: str) -> bool:
|
|
292
|
+
"""检查扩展系统"""
|
|
293
|
+
if os.path.exists(extensions_file):
|
|
294
|
+
try:
|
|
295
|
+
with open(extensions_file, 'r') as f:
|
|
296
|
+
extensions_config = json.load(f)
|
|
297
|
+
return isinstance(extensions_config, dict) and 'extensions' in extensions_config
|
|
298
|
+
except:
|
|
299
|
+
return False
|
|
300
|
+
return True
|
|
301
|
+
|
|
302
|
+
def _check_class_inheritance(self, config_file: str) -> bool:
|
|
303
|
+
"""检查类继承机制"""
|
|
304
|
+
# 对于Python模块,主要检查模块是否可导入
|
|
305
|
+
return self._is_python_module('qwencode_cli')
|
|
306
|
+
|
|
307
|
+
def _check_workflow_nodes(self, workflows_dir: str) -> bool:
|
|
308
|
+
"""检查工作流节点"""
|
|
309
|
+
return os.path.exists(workflows_dir) or not os.path.exists(workflows_dir)
|
|
310
|
+
|
|
311
|
+
def _check_plugin_system(self, plugins_file: str) -> bool:
|
|
312
|
+
"""检查插件系统"""
|
|
313
|
+
if os.path.exists(plugins_file):
|
|
314
|
+
try:
|
|
315
|
+
with open(plugins_file, 'r') as f:
|
|
316
|
+
plugins_config = json.load(f)
|
|
317
|
+
return isinstance(plugins_config, dict) and 'plugins' in plugins_config
|
|
318
|
+
except:
|
|
319
|
+
return False
|
|
320
|
+
return True
|
|
321
|
+
|
|
322
|
+
def _check_buddy_system(self, buddies_file: str) -> bool:
|
|
323
|
+
"""检查伙伴系统"""
|
|
324
|
+
if os.path.exists(buddies_file):
|
|
325
|
+
return True # YAML文件存在,可以使用
|
|
326
|
+
return True # 文件不存在说明尚未配置,但机制可用
|
|
327
|
+
|
|
328
|
+
def _expand_path(self, path: str) -> str:
|
|
329
|
+
"""展开路径中的~符号"""
|
|
330
|
+
return os.path.expanduser(path)
|
|
331
|
+
|
|
332
|
+
def get_cli_capabilities(self, cli_name: str) -> Dict[str, Any]:
|
|
333
|
+
"""
|
|
334
|
+
获取CLI工具的能力信息
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
cli_name: CLI工具名称
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
Dict[str, Any]: 能力信息
|
|
341
|
+
"""
|
|
342
|
+
capabilities = {
|
|
343
|
+
'supported_protocols': [],
|
|
344
|
+
'hooks_available': [],
|
|
345
|
+
'extension_points': [],
|
|
346
|
+
'native_integration': False
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if cli_name == 'claude':
|
|
350
|
+
capabilities.update({
|
|
351
|
+
'supported_protocols': [
|
|
352
|
+
'请用claude帮我{task}',
|
|
353
|
+
'调用claude来{task}',
|
|
354
|
+
'use claude to {task}',
|
|
355
|
+
'ask claude for {task}'
|
|
356
|
+
],
|
|
357
|
+
'hooks_available': [
|
|
358
|
+
'user_prompt_submit',
|
|
359
|
+
'tool_use_pre',
|
|
360
|
+
'tool_use_post',
|
|
361
|
+
'response_generated'
|
|
362
|
+
],
|
|
363
|
+
'extension_points': ['hooks.json'],
|
|
364
|
+
'native_integration': True
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
elif cli_name == 'gemini':
|
|
368
|
+
capabilities.update({
|
|
369
|
+
'supported_protocols': [
|
|
370
|
+
'请用gemini{task}',
|
|
371
|
+
'调用gemini{task}',
|
|
372
|
+
'use gemini to {task}',
|
|
373
|
+
'call gemini for {task}'
|
|
374
|
+
],
|
|
375
|
+
'hooks_available': [
|
|
376
|
+
'preprocessor',
|
|
377
|
+
'postprocessor',
|
|
378
|
+
'command_handler'
|
|
379
|
+
],
|
|
380
|
+
'extension_points': ['extensions.json'],
|
|
381
|
+
'native_integration': True
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
# 其他CLI工具的能力可以在这里添加...
|
|
385
|
+
|
|
386
|
+
return capabilities
|
|
387
|
+
|
|
388
|
+
def generate_report(self) -> Dict[str, Any]:
|
|
389
|
+
"""
|
|
390
|
+
生成检测报告
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
Dict[str, Any]: 完整的检测报告
|
|
394
|
+
"""
|
|
395
|
+
detection_results = self.detect_all_cli_tools()
|
|
396
|
+
|
|
397
|
+
# 使用跨平台的时间戳生成
|
|
398
|
+
from datetime import datetime
|
|
399
|
+
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
400
|
+
|
|
401
|
+
report = {
|
|
402
|
+
'timestamp': timestamp,
|
|
403
|
+
'system_info': {
|
|
404
|
+
'platform': sys.platform,
|
|
405
|
+
'python_version': sys.version,
|
|
406
|
+
'path': os.environ.get('PATH', ''),
|
|
407
|
+
},
|
|
408
|
+
'cli_tools': detection_results,
|
|
409
|
+
'summary': {
|
|
410
|
+
'total_tools': len(self.cli_tools),
|
|
411
|
+
'available_tools': sum(1 for result in detection_results.values() if result['available']),
|
|
412
|
+
'ready_for_integration': sum(1 for result in detection_results.values()
|
|
413
|
+
if result['available'] and result['extension_ready']),
|
|
414
|
+
'highest_priority_tools': [
|
|
415
|
+
name for name, result in detection_results.items()
|
|
416
|
+
if result['available'] and name in ['claude', 'gemini', 'qwencode']
|
|
417
|
+
]
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return report
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
# 全局检测器实例
|
|
425
|
+
_global_detector: Optional[CLIDetector] = None
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def get_cli_detector() -> CLIDetector:
|
|
429
|
+
"""获取全局CLI检测器实例"""
|
|
430
|
+
global _global_detector
|
|
431
|
+
if _global_detector is None:
|
|
432
|
+
_global_detector = CLIDetector()
|
|
433
|
+
return _global_detector
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def detect_claude_cli_status() -> Dict[str, Any]:
|
|
437
|
+
"""检测Claude CLI状态的便捷函数"""
|
|
438
|
+
detector = get_cli_detector()
|
|
439
|
+
return detector.detect_claude_cli()
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def generate_environment_report() -> Dict[str, Any]:
|
|
443
|
+
"""生成环境报告的便捷函数"""
|
|
444
|
+
detector = get_cli_detector()
|
|
445
|
+
return detector.generate_report()
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File utility functions for safe file operations
|
|
3
|
+
Cross-platform safe file reading and writing with encoding support
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import logging
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, Any, Optional, Union
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def ensure_directory_exists(directory_path: Union[str, Path]) -> bool:
|
|
16
|
+
"""
|
|
17
|
+
Ensure a directory exists, creating it if necessary
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
directory_path: Path to the directory
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
True if directory exists or was created successfully, False otherwise
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
path = Path(directory_path)
|
|
27
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
return True
|
|
29
|
+
except Exception as e:
|
|
30
|
+
logger.error(f"Failed to create directory {directory_path}: {e}")
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def safe_write_json_file(file_path: Union[str, Path], data: Dict[str, Any],
|
|
35
|
+
encoding: str = 'utf-8', indent: int = 2) -> bool:
|
|
36
|
+
"""
|
|
37
|
+
Safely write JSON data to a file with proper encoding handling
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
file_path: Path to the JSON file
|
|
41
|
+
data: Dictionary to write as JSON
|
|
42
|
+
encoding: File encoding (default: utf-8)
|
|
43
|
+
indent: JSON indentation (default: 2)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
True if write was successful, False otherwise
|
|
47
|
+
"""
|
|
48
|
+
try:
|
|
49
|
+
path = Path(file_path)
|
|
50
|
+
|
|
51
|
+
# Ensure parent directory exists
|
|
52
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
|
|
54
|
+
# Write JSON data
|
|
55
|
+
with open(path, 'w', encoding=encoding) as f:
|
|
56
|
+
json.dump(data, f, indent=indent, ensure_ascii=False, sort_keys=True)
|
|
57
|
+
|
|
58
|
+
logger.info(f"Successfully wrote JSON file: {file_path}")
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
logger.error(f"Failed to write JSON file {file_path}: {e}")
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def safe_read_json_file(file_path: Union[str, Path],
|
|
67
|
+
encoding: str = 'utf-8') -> Optional[Dict[str, Any]]:
|
|
68
|
+
"""
|
|
69
|
+
Safely read JSON data from a file with proper encoding handling
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
file_path: Path to the JSON file
|
|
73
|
+
encoding: File encoding (default: utf-8)
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Dictionary with JSON data, or None if read failed
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
path = Path(file_path)
|
|
80
|
+
|
|
81
|
+
if not path.exists():
|
|
82
|
+
logger.warning(f"JSON file does not exist: {file_path}")
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
# Read JSON data
|
|
86
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
87
|
+
data = json.load(f)
|
|
88
|
+
|
|
89
|
+
logger.info(f"Successfully read JSON file: {file_path}")
|
|
90
|
+
return data
|
|
91
|
+
|
|
92
|
+
except json.JSONDecodeError as e:
|
|
93
|
+
logger.error(f"Invalid JSON in file {file_path}: {e}")
|
|
94
|
+
return None
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.error(f"Failed to read JSON file {file_path}: {e}")
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def safe_write_text_file(file_path: Union[str, Path], content: str,
|
|
101
|
+
encoding: str = 'utf-8') -> bool:
|
|
102
|
+
"""
|
|
103
|
+
Safely write text content to a file with proper encoding handling
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
file_path: Path to the text file
|
|
107
|
+
content: Text content to write
|
|
108
|
+
encoding: File encoding (default: utf-8)
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
True if write was successful, False otherwise
|
|
112
|
+
"""
|
|
113
|
+
try:
|
|
114
|
+
path = Path(file_path)
|
|
115
|
+
|
|
116
|
+
# Ensure parent directory exists
|
|
117
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
118
|
+
|
|
119
|
+
# Write text content
|
|
120
|
+
with open(path, 'w', encoding=encoding) as f:
|
|
121
|
+
f.write(content)
|
|
122
|
+
|
|
123
|
+
logger.info(f"Successfully wrote text file: {file_path}")
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
except Exception as e:
|
|
127
|
+
logger.error(f"Failed to write text file {file_path}: {e}")
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def safe_read_text_file(file_path: Union[str, Path],
|
|
132
|
+
encoding: str = 'utf-8') -> Optional[str]:
|
|
133
|
+
"""
|
|
134
|
+
Safely read text content from a file with proper encoding handling
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
file_path: Path to the text file
|
|
138
|
+
encoding: File encoding (default: utf-8)
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Text content as string, or None if read failed
|
|
142
|
+
"""
|
|
143
|
+
try:
|
|
144
|
+
path = Path(file_path)
|
|
145
|
+
|
|
146
|
+
if not path.exists():
|
|
147
|
+
logger.warning(f"Text file does not exist: {file_path}")
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
# Read text content
|
|
151
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
152
|
+
content = f.read()
|
|
153
|
+
|
|
154
|
+
logger.info(f"Successfully read text file: {file_path}")
|
|
155
|
+
return content
|
|
156
|
+
|
|
157
|
+
except Exception as e:
|
|
158
|
+
logger.error(f"Failed to read text file {file_path}: {e}")
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def get_file_size(file_path: Union[str, Path]) -> int:
|
|
163
|
+
"""
|
|
164
|
+
Get the size of a file in bytes
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
file_path: Path to the file
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
File size in bytes, or 0 if file doesn't exist or error occurred
|
|
171
|
+
"""
|
|
172
|
+
try:
|
|
173
|
+
path = Path(file_path)
|
|
174
|
+
if path.exists():
|
|
175
|
+
return path.stat().st_size
|
|
176
|
+
return 0
|
|
177
|
+
except Exception as e:
|
|
178
|
+
logger.error(f"Failed to get file size for {file_path}: {e}")
|
|
179
|
+
return 0
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def file_exists(file_path: Union[str, Path]) -> bool:
|
|
183
|
+
"""
|
|
184
|
+
Check if a file exists
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
file_path: Path to the file
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
True if file exists, False otherwise
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
return Path(file_path).exists()
|
|
194
|
+
except Exception:
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def delete_file_safely(file_path: Union[str, Path]) -> bool:
|
|
199
|
+
"""
|
|
200
|
+
Safely delete a file
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
file_path: Path to the file to delete
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
True if deletion was successful or file didn't exist, False otherwise
|
|
207
|
+
"""
|
|
208
|
+
try:
|
|
209
|
+
path = Path(file_path)
|
|
210
|
+
if path.exists():
|
|
211
|
+
path.unlink()
|
|
212
|
+
logger.info(f"Successfully deleted file: {file_path}")
|
|
213
|
+
return True
|
|
214
|
+
except Exception as e:
|
|
215
|
+
logger.error(f"Failed to delete file {file_path}: {e}")
|
|
216
|
+
return False
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def copy_file_safely(source_path: Union[str, Path], dest_path: Union[str, Path]) -> bool:
|
|
220
|
+
"""
|
|
221
|
+
Safely copy a file from source to destination
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
source_path: Source file path
|
|
225
|
+
dest_path: Destination file path
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
True if copy was successful, False otherwise
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
src = Path(source_path)
|
|
232
|
+
dst = Path(dest_path)
|
|
233
|
+
|
|
234
|
+
# Ensure destination directory exists
|
|
235
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
236
|
+
|
|
237
|
+
# Copy file
|
|
238
|
+
import shutil
|
|
239
|
+
shutil.copy2(src, dst)
|
|
240
|
+
|
|
241
|
+
logger.info(f"Successfully copied file from {source_path} to {dest_path}")
|
|
242
|
+
return True
|
|
243
|
+
|
|
244
|
+
except Exception as e:
|
|
245
|
+
logger.error(f"Failed to copy file from {source_path} to {dest_path}: {e}")
|
|
246
|
+
return False
|