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,772 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CopilotCLI MCP适配器 - 基于Model Context Protocol (MCP)的原生集成
|
|
3
|
+
|
|
4
|
+
这是TDD驱动的实现,基于test_copilot_adapter.py中的测试用例
|
|
5
|
+
完全符合项目约束条件:
|
|
6
|
+
- 使用Copilot CLI官方MCP服务器机制
|
|
7
|
+
- 不改变CLI启动和使用方式
|
|
8
|
+
- 不依赖包装器
|
|
9
|
+
- 完全无损扩展
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import asyncio
|
|
16
|
+
from typing import Dict, Any, Optional, List
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
from ...core.base_adapter import BaseCrossCLIAdapter, IntentResult
|
|
21
|
+
from ...core.parser import NaturalLanguageParser
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CopilotMCPContext:
|
|
27
|
+
"""Copilot CLI MCP上下文模拟类"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, prompt: str = "", metadata: Optional[Dict] = None):
|
|
30
|
+
self.prompt = prompt
|
|
31
|
+
self.metadata = metadata or {}
|
|
32
|
+
self.session_id = self.metadata.get('session_id', 'unknown')
|
|
33
|
+
self.user_id = self.metadata.get('user_id', 'unknown')
|
|
34
|
+
self.timestamp = datetime.now()
|
|
35
|
+
self.tool_permissions = self.metadata.get('tool_permissions', {})
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CopilotMCPAdapter(BaseCrossCLIAdapter):
|
|
39
|
+
"""
|
|
40
|
+
Copilot CLI MCP适配器
|
|
41
|
+
|
|
42
|
+
通过Copilot CLI官方MCP服务器系统实现跨CLI调用功能。
|
|
43
|
+
这是完全基于原生MCP机制的无损扩展实现。
|
|
44
|
+
|
|
45
|
+
MCP集成机制:
|
|
46
|
+
- 通过自定义MCP服务器注册到Copilot CLI
|
|
47
|
+
- 使用MCP工具调用接口处理跨CLI请求
|
|
48
|
+
- 利用Copilot的权限管理系统确保安全
|
|
49
|
+
- 支持并行工具执行和异步任务委托
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, cli_name: str = "copilot"):
|
|
53
|
+
"""
|
|
54
|
+
初始化Copilot MCP适配器
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
cli_name: CLI工具名称,默认为"copilot"
|
|
58
|
+
"""
|
|
59
|
+
super().__init__(cli_name)
|
|
60
|
+
|
|
61
|
+
# MCP相关配置
|
|
62
|
+
self.copilot_config_dir = os.path.expanduser("~/.copilot")
|
|
63
|
+
self.mcp_config_file = os.path.join(self.copilot_config_dir, "mcp-config.json")
|
|
64
|
+
self.custom_agents_dir = os.path.join(self.copilot_config_dir, "agents")
|
|
65
|
+
self.mcp_server_registered = False
|
|
66
|
+
|
|
67
|
+
# 跨CLI MCP服务器配置
|
|
68
|
+
self.cross_cli_mcp_server = {
|
|
69
|
+
"name": "cross-cli-adapter",
|
|
70
|
+
"description": "Cross-CLI integration adapter for universal AI tool collaboration",
|
|
71
|
+
"version": "1.0.0",
|
|
72
|
+
"command": "python",
|
|
73
|
+
"args": [
|
|
74
|
+
"-m", "src.adapters.copilot.mcp_server"
|
|
75
|
+
],
|
|
76
|
+
"env": {
|
|
77
|
+
"CROSS_CLI_ADAPTER": "enabled",
|
|
78
|
+
"LOG_LEVEL": "INFO"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# 统计信息
|
|
83
|
+
self.mcp_calls_count = 0
|
|
84
|
+
self.cross_cli_calls_count = 0
|
|
85
|
+
self.processed_requests: List[Dict[str, Any]] = []
|
|
86
|
+
self.agent_executions: List[Dict[str, Any]] = []
|
|
87
|
+
|
|
88
|
+
# 解析器
|
|
89
|
+
self.parser = NaturalLanguageParser()
|
|
90
|
+
|
|
91
|
+
# 跨CLI适配器工厂
|
|
92
|
+
from ...core.base_adapter import get_cross_cli_adapter
|
|
93
|
+
self.get_adapter = get_cross_cli_adapter
|
|
94
|
+
|
|
95
|
+
async def initialize(self) -> bool:
|
|
96
|
+
"""
|
|
97
|
+
初始化适配器
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
bool: 初始化是否成功
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
# 1. 检查Copilot CLI环境
|
|
104
|
+
if not self._check_copilot_environment():
|
|
105
|
+
logger.error("Copilot CLI环境检查失败")
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
# 2. 注册MCP服务器
|
|
109
|
+
if not await self._register_mcp_server():
|
|
110
|
+
logger.error("MCP服务器注册失败")
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
# 3. 创建自定义代理
|
|
114
|
+
await self._create_custom_agents()
|
|
115
|
+
|
|
116
|
+
# 4. 确保配置目录存在
|
|
117
|
+
await self._ensure_config_directory()
|
|
118
|
+
|
|
119
|
+
self.mcp_server_registered = True
|
|
120
|
+
logger.info("Copilot MCP适配器初始化成功")
|
|
121
|
+
return True
|
|
122
|
+
|
|
123
|
+
except Exception as e:
|
|
124
|
+
logger.error(f"初始化Copilot MCP适配器失败: {e}")
|
|
125
|
+
self.record_error()
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
async def _register_mcp_server(self) -> bool:
|
|
129
|
+
"""
|
|
130
|
+
注册MCP服务器到Copilot CLI
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
bool: 注册是否成功
|
|
134
|
+
"""
|
|
135
|
+
try:
|
|
136
|
+
# 读取现有MCP配置
|
|
137
|
+
mcp_config = self._load_mcp_config()
|
|
138
|
+
|
|
139
|
+
# 添加我们的跨CLI MCP服务器
|
|
140
|
+
mcp_servers = mcp_config.get('mcpServers', {})
|
|
141
|
+
|
|
142
|
+
# 检查是否已存在
|
|
143
|
+
if self.cross_cli_mcp_server['name'] not in mcp_servers:
|
|
144
|
+
mcp_servers[self.cross_cli_mcp_server['name']] = {
|
|
145
|
+
"command": self.cross_cli_mcp_server['command'],
|
|
146
|
+
"args": self.cross_cli_mcp_server['args'],
|
|
147
|
+
"env": self.cross_cli_mcp_server['env']
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
mcp_config['mcpServers'] = mcp_servers
|
|
151
|
+
|
|
152
|
+
# 保存配置
|
|
153
|
+
await self._save_mcp_config(mcp_config)
|
|
154
|
+
logger.info(f"注册MCP服务器: {self.cross_cli_mcp_server['name']}")
|
|
155
|
+
else:
|
|
156
|
+
logger.info("MCP服务器已存在,跳过注册")
|
|
157
|
+
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
except Exception as e:
|
|
161
|
+
logger.error(f"注册MCP服务器失败: {e}")
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
def _load_mcp_config(self) -> Dict[str, Any]:
|
|
165
|
+
"""
|
|
166
|
+
加载MCP配置
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Dict[str, Any]: MCP配置
|
|
170
|
+
"""
|
|
171
|
+
if os.path.exists(self.mcp_config_file):
|
|
172
|
+
try:
|
|
173
|
+
with open(self.mcp_config_file, 'r', encoding='utf-8') as f:
|
|
174
|
+
return json.load(f)
|
|
175
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
176
|
+
logger.warning(f"加载MCP配置失败,使用默认配置: {e}")
|
|
177
|
+
|
|
178
|
+
# 返回默认配置
|
|
179
|
+
return {
|
|
180
|
+
"mcpServers": {}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async def _save_mcp_config(self, config: Dict[str, Any]) -> bool:
|
|
184
|
+
"""
|
|
185
|
+
保存MCP配置
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
config: MCP配置
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
bool: 保存是否成功
|
|
192
|
+
"""
|
|
193
|
+
try:
|
|
194
|
+
os.makedirs(os.path.dirname(self.mcp_config_file), exist_ok=True)
|
|
195
|
+
|
|
196
|
+
with open(self.mcp_config_file, 'w', encoding='utf-8') as f:
|
|
197
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
198
|
+
|
|
199
|
+
logger.info(f"保存MCP配置到: {self.mcp_config_file}")
|
|
200
|
+
return True
|
|
201
|
+
|
|
202
|
+
except Exception as e:
|
|
203
|
+
logger.error(f"保存MCP配置失败: {e}")
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
async def _create_custom_agents(self) -> bool:
|
|
207
|
+
"""
|
|
208
|
+
创建自定义代理用于跨CLI调用
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
bool: 创建是否成功
|
|
212
|
+
"""
|
|
213
|
+
try:
|
|
214
|
+
os.makedirs(self.custom_agents_dir, exist_ok=True)
|
|
215
|
+
|
|
216
|
+
# 创建跨CLI调用代理
|
|
217
|
+
cross_cli_agent = {
|
|
218
|
+
"name": "cross-cli-caller",
|
|
219
|
+
"description": "Agent for handling cross-CLI tool calls and collaboration",
|
|
220
|
+
"version": "1.0.0",
|
|
221
|
+
"instructions": """You are a cross-CLI integration agent that helps users collaborate between different AI CLI tools.
|
|
222
|
+
|
|
223
|
+
When you detect a request to use another CLI tool (like Claude, Gemini, QwenCode, etc.):
|
|
224
|
+
1. Parse the target CLI and task from the user's request
|
|
225
|
+
2. Execute the task using the appropriate CLI tool
|
|
226
|
+
3. Return the results in a clear, structured format
|
|
227
|
+
|
|
228
|
+
Support both Chinese and English collaboration patterns:
|
|
229
|
+
- "请用{CLI}帮我{task}" -> Use {CLI} to help with {task}
|
|
230
|
+
- "use {CLI} to {task}" -> Execute {task} with {CLI}
|
|
231
|
+
|
|
232
|
+
Always maintain the original intent and context of the user's request.""",
|
|
233
|
+
"tools": [
|
|
234
|
+
"cross_cli_execute",
|
|
235
|
+
"get_available_clis",
|
|
236
|
+
"check_cli_status"
|
|
237
|
+
],
|
|
238
|
+
"permissions": {
|
|
239
|
+
"allowShellExecution": True,
|
|
240
|
+
"allowNetworkAccess": True,
|
|
241
|
+
"allowedPaths": ["*"]
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
agent_file = os.path.join(self.custom_agents_dir, "cross-cli-caller.json")
|
|
246
|
+
with open(agent_file, 'w', encoding='utf-8') as f:
|
|
247
|
+
json.dump(cross_cli_agent, f, indent=2, ensure_ascii=False)
|
|
248
|
+
|
|
249
|
+
logger.info(f"创建自定义代理: {cross_cli_agent['name']}")
|
|
250
|
+
return True
|
|
251
|
+
|
|
252
|
+
except Exception as e:
|
|
253
|
+
logger.error(f"创建自定义代理失败: {e}")
|
|
254
|
+
return False
|
|
255
|
+
|
|
256
|
+
async def _ensure_config_directory(self) -> bool:
|
|
257
|
+
"""
|
|
258
|
+
确保配置目录存在
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
bool: 创建是否成功
|
|
262
|
+
"""
|
|
263
|
+
try:
|
|
264
|
+
directories = [
|
|
265
|
+
self.copilot_config_dir,
|
|
266
|
+
self.custom_agents_dir,
|
|
267
|
+
os.path.join(self.copilot_config_dir, "sessions"),
|
|
268
|
+
os.path.join(self.copilot_config_dir, "logs")
|
|
269
|
+
]
|
|
270
|
+
|
|
271
|
+
for directory in directories:
|
|
272
|
+
os.makedirs(directory, exist_ok=True)
|
|
273
|
+
|
|
274
|
+
logger.info(f"配置目录已准备: {self.copilot_config_dir}")
|
|
275
|
+
return True
|
|
276
|
+
|
|
277
|
+
except Exception as e:
|
|
278
|
+
logger.error(f"创建配置目录失败: {e}")
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
def _check_copilot_environment(self) -> bool:
|
|
282
|
+
"""
|
|
283
|
+
检查Copilot CLI环境
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
bool: 环境是否可用
|
|
287
|
+
"""
|
|
288
|
+
try:
|
|
289
|
+
# 检查配置目录
|
|
290
|
+
if not os.path.exists(self.copilot_config_dir):
|
|
291
|
+
logger.warning(f"Copilot配置目录不存在: {self.copilot_config_dir}")
|
|
292
|
+
return False
|
|
293
|
+
|
|
294
|
+
# 检查是否有copilot命令(简化检查)
|
|
295
|
+
# 在实际环境中,这里应该检查copilot命令是否可用
|
|
296
|
+
return True
|
|
297
|
+
|
|
298
|
+
except Exception as e:
|
|
299
|
+
logger.error(f"检查Copilot环境失败: {e}")
|
|
300
|
+
return False
|
|
301
|
+
|
|
302
|
+
async def on_mcp_tool_call(self, tool_name: str, arguments: Dict[str, Any], context: CopilotMCPContext) -> Optional[str]:
|
|
303
|
+
"""
|
|
304
|
+
处理MCP工具调用
|
|
305
|
+
|
|
306
|
+
这是核心Hook,用于检测和执行跨CLI调用。
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
tool_name: 工具名称
|
|
310
|
+
arguments: 工具参数
|
|
311
|
+
context: MCP上下文
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
Optional[str]: 处理结果
|
|
315
|
+
"""
|
|
316
|
+
try:
|
|
317
|
+
self.mcp_calls_count += 1
|
|
318
|
+
|
|
319
|
+
# 记录请求
|
|
320
|
+
request_record = {
|
|
321
|
+
'tool_name': tool_name,
|
|
322
|
+
'arguments': arguments,
|
|
323
|
+
'context': context.__dict__,
|
|
324
|
+
'timestamp': datetime.now().isoformat()
|
|
325
|
+
}
|
|
326
|
+
self.processed_requests.append(request_record)
|
|
327
|
+
|
|
328
|
+
if tool_name == "cross_cli_execute":
|
|
329
|
+
return await self._handle_cross_cli_execute(arguments, context)
|
|
330
|
+
elif tool_name == "get_available_clis":
|
|
331
|
+
return await self._handle_get_available_clis(context)
|
|
332
|
+
elif tool_name == "check_cli_status":
|
|
333
|
+
return await self._handle_check_cli_status(arguments, context)
|
|
334
|
+
|
|
335
|
+
return None
|
|
336
|
+
|
|
337
|
+
except Exception as e:
|
|
338
|
+
logger.error(f"MCP工具调用处理失败: {tool_name}, {e}")
|
|
339
|
+
self.record_error()
|
|
340
|
+
return None
|
|
341
|
+
|
|
342
|
+
async def _handle_cross_cli_execute(self, arguments: Dict[str, Any], context: CopilotMCPContext) -> str:
|
|
343
|
+
"""
|
|
344
|
+
处理跨CLI执行请求
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
arguments: 执行参数,包含target_cli和task
|
|
348
|
+
context: MCP上下文
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
str: 执行结果
|
|
352
|
+
"""
|
|
353
|
+
target_cli = arguments.get('target_cli')
|
|
354
|
+
task = arguments.get('task')
|
|
355
|
+
|
|
356
|
+
if not target_cli or not task:
|
|
357
|
+
return self._format_error_result(
|
|
358
|
+
target_cli or "unknown",
|
|
359
|
+
task or "unknown",
|
|
360
|
+
"缺少必要参数:target_cli和task"
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
# 避免自我调用
|
|
364
|
+
if target_cli.lower() == self.cli_name:
|
|
365
|
+
return self._format_error_result(
|
|
366
|
+
target_cli,
|
|
367
|
+
task,
|
|
368
|
+
"不能自我调用,请使用其他CLI工具"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
logger.info(f"执行跨CLI调用: {target_cli} -> {task}")
|
|
373
|
+
|
|
374
|
+
# 获取目标CLI适配器
|
|
375
|
+
target_adapter = self.get_adapter(target_cli)
|
|
376
|
+
|
|
377
|
+
if not target_adapter:
|
|
378
|
+
logger.warning(f"目标CLI适配器不可用: {target_cli}")
|
|
379
|
+
return self._format_error_result(
|
|
380
|
+
target_cli,
|
|
381
|
+
task,
|
|
382
|
+
f"目标CLI工具 '{target_cli}' 不可用或未安装"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
if not target_adapter.is_available():
|
|
386
|
+
logger.warning(f"目标CLI工具不可用: {target_cli}")
|
|
387
|
+
return self._format_error_result(
|
|
388
|
+
target_cli,
|
|
389
|
+
task,
|
|
390
|
+
f"目标CLI工具 '{target_cli}' 当前不可用"
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
# 构建执行上下文
|
|
394
|
+
execution_context = {
|
|
395
|
+
'source_cli': self.cli_name,
|
|
396
|
+
'target_cli': target_cli,
|
|
397
|
+
'original_task': task,
|
|
398
|
+
'mcp_context': context.__dict__,
|
|
399
|
+
'timestamp': datetime.now().isoformat()
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
# 执行任务
|
|
403
|
+
result = await target_adapter.execute_task(task, execution_context)
|
|
404
|
+
|
|
405
|
+
# 记录成功的跨CLI调用
|
|
406
|
+
self.cross_cli_calls_count += 1
|
|
407
|
+
self.processed_requests.append({
|
|
408
|
+
'type': 'cross_cli_execution',
|
|
409
|
+
'target_cli': target_cli,
|
|
410
|
+
'task': task,
|
|
411
|
+
'success': True,
|
|
412
|
+
'result_length': len(result),
|
|
413
|
+
'timestamp': datetime.now().isoformat()
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
# 格式化结果
|
|
417
|
+
formatted_result = self._format_success_result(target_cli, task, result)
|
|
418
|
+
|
|
419
|
+
logger.info(f"跨CLI调用成功: {target_cli}")
|
|
420
|
+
return formatted_result
|
|
421
|
+
|
|
422
|
+
except Exception as e:
|
|
423
|
+
logger.error(f"跨CLI调用失败: {target_cli}, {e}")
|
|
424
|
+
self.record_error()
|
|
425
|
+
|
|
426
|
+
self.processed_requests.append({
|
|
427
|
+
'type': 'cross_cli_execution',
|
|
428
|
+
'target_cli': target_cli,
|
|
429
|
+
'task': task,
|
|
430
|
+
'success': False,
|
|
431
|
+
'error': str(e),
|
|
432
|
+
'timestamp': datetime.now().isoformat()
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
return self._format_error_result(target_cli, task, str(e))
|
|
436
|
+
|
|
437
|
+
async def _handle_get_available_clis(self, context: CopilotMCPContext) -> str:
|
|
438
|
+
"""
|
|
439
|
+
处理获取可用CLI列表请求
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
context: MCP上下文
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
str: 可用CLI列表
|
|
446
|
+
"""
|
|
447
|
+
try:
|
|
448
|
+
from ...core.base_adapter import get_all_adapters
|
|
449
|
+
adapters = get_all_adapters()
|
|
450
|
+
|
|
451
|
+
available_clis = []
|
|
452
|
+
for name, adapter in adapters.items():
|
|
453
|
+
if adapter.is_available():
|
|
454
|
+
available_clis.append({
|
|
455
|
+
'name': name,
|
|
456
|
+
'version': getattr(adapter, 'version', 'unknown'),
|
|
457
|
+
'type': adapter.__class__.__name__
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
result = {
|
|
461
|
+
'available_clis': available_clis,
|
|
462
|
+
'total_count': len(available_clis),
|
|
463
|
+
'timestamp': datetime.now().isoformat()
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
467
|
+
|
|
468
|
+
except Exception as e:
|
|
469
|
+
logger.error(f"获取可用CLI列表失败: {e}")
|
|
470
|
+
return json.dumps({'error': str(e)}, ensure_ascii=False)
|
|
471
|
+
|
|
472
|
+
async def _handle_check_cli_status(self, arguments: Dict[str, Any], context: CopilotMCPContext) -> str:
|
|
473
|
+
"""
|
|
474
|
+
处理检查CLI状态请求
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
arguments: 包含cli_name的参数
|
|
478
|
+
context: MCP上下文
|
|
479
|
+
|
|
480
|
+
Returns:
|
|
481
|
+
str: CLI状态信息
|
|
482
|
+
"""
|
|
483
|
+
cli_name = arguments.get('cli_name')
|
|
484
|
+
|
|
485
|
+
if not cli_name:
|
|
486
|
+
return json.dumps({'error': '缺少cli_name参数'}, ensure_ascii=False)
|
|
487
|
+
|
|
488
|
+
try:
|
|
489
|
+
target_adapter = self.get_adapter(cli_name)
|
|
490
|
+
|
|
491
|
+
if not target_adapter:
|
|
492
|
+
return json.dumps({
|
|
493
|
+
'cli_name': cli_name,
|
|
494
|
+
'available': False,
|
|
495
|
+
'reason': '适配器未找到'
|
|
496
|
+
}, ensure_ascii=False)
|
|
497
|
+
|
|
498
|
+
health = await target_adapter.health_check()
|
|
499
|
+
stats = target_adapter.get_statistics()
|
|
500
|
+
|
|
501
|
+
result = {
|
|
502
|
+
'cli_name': cli_name,
|
|
503
|
+
'available': target_adapter.is_available(),
|
|
504
|
+
'health': health,
|
|
505
|
+
'statistics': stats,
|
|
506
|
+
'timestamp': datetime.now().isoformat()
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
510
|
+
|
|
511
|
+
except Exception as e:
|
|
512
|
+
logger.error(f"检查CLI状态失败: {cli_name}, {e}")
|
|
513
|
+
return json.dumps({
|
|
514
|
+
'cli_name': cli_name,
|
|
515
|
+
'available': False,
|
|
516
|
+
'error': str(e)
|
|
517
|
+
}, ensure_ascii=False)
|
|
518
|
+
|
|
519
|
+
def _format_success_result(
|
|
520
|
+
self,
|
|
521
|
+
target_cli: str,
|
|
522
|
+
task: str,
|
|
523
|
+
result: str
|
|
524
|
+
) -> str:
|
|
525
|
+
"""
|
|
526
|
+
格式化成功的跨CLI调用结果
|
|
527
|
+
|
|
528
|
+
Args:
|
|
529
|
+
target_cli: 目标CLI工具
|
|
530
|
+
task: 原始任务
|
|
531
|
+
result: 执行结果
|
|
532
|
+
|
|
533
|
+
Returns:
|
|
534
|
+
str: 格式化的结果
|
|
535
|
+
"""
|
|
536
|
+
return f"""## 🔗 跨CLI调用结果
|
|
537
|
+
|
|
538
|
+
**源工具**: Copilot CLI
|
|
539
|
+
**目标工具**: {target_cli.upper()}
|
|
540
|
+
**原始任务**: {task}
|
|
541
|
+
**执行时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
{result}
|
|
546
|
+
|
|
547
|
+
---
|
|
548
|
+
|
|
549
|
+
*此结果由跨CLI集成系统通过Copilot CLI MCP提供*"""
|
|
550
|
+
|
|
551
|
+
def _format_error_result(
|
|
552
|
+
self,
|
|
553
|
+
target_cli: str,
|
|
554
|
+
task: str,
|
|
555
|
+
error_message: str
|
|
556
|
+
) -> str:
|
|
557
|
+
"""
|
|
558
|
+
格式化错误的跨CLI调用结果
|
|
559
|
+
|
|
560
|
+
Args:
|
|
561
|
+
target_cli: 目标CLI工具
|
|
562
|
+
task: 原始任务
|
|
563
|
+
error_message: 错误信息
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
str: 格式化的错误结果
|
|
567
|
+
"""
|
|
568
|
+
return f"""## ❌ 跨CLI调用失败
|
|
569
|
+
|
|
570
|
+
**源工具**: Copilot CLI
|
|
571
|
+
**目标工具**: {target_cli.upper()}
|
|
572
|
+
**原始任务**: {task}
|
|
573
|
+
**错误信息**: {error_message}
|
|
574
|
+
**失败时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|
575
|
+
|
|
576
|
+
请检查目标CLI工具是否正确安装和配置。
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
*此错误由跨CLI集成系统报告*"""
|
|
581
|
+
|
|
582
|
+
def is_available(self) -> bool:
|
|
583
|
+
"""
|
|
584
|
+
检查适配器是否可用
|
|
585
|
+
|
|
586
|
+
Returns:
|
|
587
|
+
bool: 是否可用
|
|
588
|
+
"""
|
|
589
|
+
return self.mcp_server_registered and self._check_copilot_environment()
|
|
590
|
+
|
|
591
|
+
async def health_check(self) -> Dict[str, Any]:
|
|
592
|
+
"""
|
|
593
|
+
健康检查
|
|
594
|
+
|
|
595
|
+
Returns:
|
|
596
|
+
Dict[str, Any]: 健康状态
|
|
597
|
+
"""
|
|
598
|
+
base_health = await super().health_check()
|
|
599
|
+
|
|
600
|
+
copilot_health = {
|
|
601
|
+
'mcp_server_registered': self.mcp_server_registered,
|
|
602
|
+
'mcp_calls_count': self.mcp_calls_count,
|
|
603
|
+
'cross_cli_calls_count': self.cross_cli_calls_count,
|
|
604
|
+
'processed_requests_count': len(self.processed_requests),
|
|
605
|
+
'agent_executions_count': len(self.agent_executions),
|
|
606
|
+
'mcp_config_file': self.mcp_config_file,
|
|
607
|
+
'mcp_config_exists': os.path.exists(self.mcp_config_file),
|
|
608
|
+
'custom_agents_dir': self.custom_agents_dir,
|
|
609
|
+
'custom_agents_dir_exists': os.path.exists(self.custom_agents_dir)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
# 检查环境
|
|
613
|
+
try:
|
|
614
|
+
copilot_health['copilot_environment'] = self._check_copilot_environment()
|
|
615
|
+
except Exception as e:
|
|
616
|
+
copilot_health['copilot_environment_error'] = str(e)
|
|
617
|
+
|
|
618
|
+
# 合并基础健康信息
|
|
619
|
+
base_health.update(copilot_health)
|
|
620
|
+
return base_health
|
|
621
|
+
|
|
622
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
623
|
+
"""
|
|
624
|
+
获取适配器统计信息
|
|
625
|
+
|
|
626
|
+
Returns:
|
|
627
|
+
Dict[str, Any]: 统计信息
|
|
628
|
+
"""
|
|
629
|
+
base_stats = super().get_statistics()
|
|
630
|
+
|
|
631
|
+
copilot_stats = {
|
|
632
|
+
'mcp_server_registered': self.mcp_server_registered,
|
|
633
|
+
'mcp_calls_count': self.mcp_calls_count,
|
|
634
|
+
'cross_cli_calls_count': self.cross_cli_calls_count,
|
|
635
|
+
'agent_executions_count': len(self.agent_executions),
|
|
636
|
+
'success_rate': self._calculate_success_rate(),
|
|
637
|
+
'last_activity': self._get_last_activity(),
|
|
638
|
+
'mcp_server_name': self.cross_cli_mcp_server['name']
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
base_stats.update(copilot_stats)
|
|
642
|
+
return base_stats
|
|
643
|
+
|
|
644
|
+
def _calculate_success_rate(self) -> float:
|
|
645
|
+
"""
|
|
646
|
+
计算成功率
|
|
647
|
+
|
|
648
|
+
Returns:
|
|
649
|
+
float: 成功率 (0.0 - 1.0)
|
|
650
|
+
"""
|
|
651
|
+
total_cross_cli = self.cross_cli_calls_count
|
|
652
|
+
|
|
653
|
+
if total_cross_cli == 0:
|
|
654
|
+
return 1.0
|
|
655
|
+
|
|
656
|
+
successful_calls = sum(
|
|
657
|
+
1 for req in self.processed_requests
|
|
658
|
+
if req.get('type') == 'cross_cli_execution' and req.get('success')
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
return successful_calls / total_cross_cli
|
|
662
|
+
|
|
663
|
+
def _get_last_activity(self) -> Optional[str]:
|
|
664
|
+
"""
|
|
665
|
+
获取最后活动时间
|
|
666
|
+
|
|
667
|
+
Returns:
|
|
668
|
+
Optional[str]: 最后活动时间戳
|
|
669
|
+
"""
|
|
670
|
+
if not self.processed_requests:
|
|
671
|
+
return None
|
|
672
|
+
|
|
673
|
+
return max(req['timestamp'] for req in self.processed_requests)
|
|
674
|
+
|
|
675
|
+
async def execute_task(self, task: str, context: Dict[str, Any]) -> str:
|
|
676
|
+
"""
|
|
677
|
+
执行跨CLI任务 - Copilot适配器的具体实现
|
|
678
|
+
|
|
679
|
+
Args:
|
|
680
|
+
task: 要执行的任务描述
|
|
681
|
+
context: 执行上下文信息
|
|
682
|
+
|
|
683
|
+
Returns:
|
|
684
|
+
str: 任务执行结果
|
|
685
|
+
"""
|
|
686
|
+
try:
|
|
687
|
+
# 创建MCP上下文
|
|
688
|
+
mcp_context = CopilotMCPContext(
|
|
689
|
+
prompt=task,
|
|
690
|
+
metadata=context.get('metadata', {})
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
# 检查是否为跨CLI调用
|
|
694
|
+
intent = self.parser.parse_intent(task, "copilot")
|
|
695
|
+
if intent.is_cross_cli and intent.target_cli != self.cli_name:
|
|
696
|
+
# 执行跨CLI调用
|
|
697
|
+
result = await self._handle_cross_cli_execute(
|
|
698
|
+
{'target_cli': intent.target_cli, 'task': intent.task},
|
|
699
|
+
mcp_context
|
|
700
|
+
)
|
|
701
|
+
return result
|
|
702
|
+
else:
|
|
703
|
+
# 本地Copilot任务处理
|
|
704
|
+
return f"Copilot MCP适配器本地处理: {task}"
|
|
705
|
+
|
|
706
|
+
except Exception as e:
|
|
707
|
+
logger.error(f"执行任务失败: {task}, 错误: {e}")
|
|
708
|
+
self.record_error()
|
|
709
|
+
return f"任务执行失败: {str(e)}"
|
|
710
|
+
|
|
711
|
+
async def cleanup(self) -> bool:
|
|
712
|
+
"""
|
|
713
|
+
清理适配器资源
|
|
714
|
+
|
|
715
|
+
Returns:
|
|
716
|
+
bool: 清理是否成功
|
|
717
|
+
"""
|
|
718
|
+
try:
|
|
719
|
+
# 清理统计信息
|
|
720
|
+
self.processed_requests.clear()
|
|
721
|
+
self.agent_executions.clear()
|
|
722
|
+
|
|
723
|
+
# 注销MCP服务器(如果需要)
|
|
724
|
+
# 这里可以实现MCP服务器注销逻辑
|
|
725
|
+
|
|
726
|
+
logger.info("Copilot MCP适配器清理完成")
|
|
727
|
+
return True
|
|
728
|
+
|
|
729
|
+
except Exception as e:
|
|
730
|
+
logger.error(f"清理Copilot MCP适配器失败: {e}")
|
|
731
|
+
return False
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
# 创建全局适配器实例
|
|
735
|
+
_global_adapter: Optional[CopilotMCPAdapter] = None
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
def get_copilot_mcp_adapter() -> CopilotMCPAdapter:
|
|
739
|
+
"""
|
|
740
|
+
获取Copilot MCP适配器实例
|
|
741
|
+
|
|
742
|
+
Returns:
|
|
743
|
+
CopilotMCPAdapter: 适配器实例
|
|
744
|
+
"""
|
|
745
|
+
global _global_adapter
|
|
746
|
+
if _global_adapter is None:
|
|
747
|
+
_global_adapter = CopilotMCPAdapter()
|
|
748
|
+
# 异步初始化需要在调用时进行
|
|
749
|
+
return _global_adapter
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
# 便捷函数
|
|
753
|
+
async def initialize_copilot_adapter() -> bool:
|
|
754
|
+
"""
|
|
755
|
+
初始化Copilot MCP适配器
|
|
756
|
+
|
|
757
|
+
Returns:
|
|
758
|
+
bool: 初始化是否成功
|
|
759
|
+
"""
|
|
760
|
+
adapter = get_copilot_mcp_adapter()
|
|
761
|
+
return await adapter.initialize()
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def is_copilot_adapter_available() -> bool:
|
|
765
|
+
"""
|
|
766
|
+
检查Copilot MCP适配器是否可用
|
|
767
|
+
|
|
768
|
+
Returns:
|
|
769
|
+
bool: 是否可用
|
|
770
|
+
"""
|
|
771
|
+
adapter = get_copilot_mcp_adapter()
|
|
772
|
+
return adapter.is_available()
|