stigmergy 1.0.68 → 1.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.en.md +306 -300
  2. package/README.md +469 -301
  3. package/package.json +97 -81
  4. package/scripts/publish.js +268 -0
  5. package/scripts/simple-publish.js +59 -0
  6. package/src/index.js +12 -0
  7. package/test/enhanced-main-alignment.test.js +298 -0
  8. package/test/hook-system-integration-test.js +307 -0
  9. package/test/natural-language-skills-test.js +320 -0
  10. package/test/nl-integration-test.js +179 -0
  11. package/test/parameter-parsing-test.js +143 -0
  12. package/test/real-test.js +435 -0
  13. package/test/system-compatibility-test.js +447 -0
  14. package/test/tdd-fixes-test.js +211 -0
  15. package/test/third-party-skills-test.js +321 -0
  16. package/test/tool-selection-integration-test.js +157 -0
  17. package/test/unit/cli-scanner.test.js +291 -0
  18. package/test/unit/cross-cli-executor.test.js +399 -0
  19. package/src/adapters/claude/__init__.py +0 -13
  20. package/src/adapters/claude/claude_skills_integration.py +0 -609
  21. package/src/adapters/claude/hook_adapter.py +0 -663
  22. package/src/adapters/claude/install_claude_integration.py +0 -265
  23. package/src/adapters/claude/skills_hook_adapter.py +0 -841
  24. package/src/adapters/claude/standalone_claude_adapter.py +0 -384
  25. package/src/adapters/cline/__init__.py +0 -20
  26. package/src/adapters/cline/config.py +0 -108
  27. package/src/adapters/cline/install_cline_integration.py +0 -617
  28. package/src/adapters/cline/mcp_server.py +0 -713
  29. package/src/adapters/cline/standalone_cline_adapter.py +0 -459
  30. package/src/adapters/codebuddy/__init__.py +0 -13
  31. package/src/adapters/codebuddy/buddy_adapter.py +0 -1125
  32. package/src/adapters/codebuddy/install_codebuddy_integration.py +0 -279
  33. package/src/adapters/codebuddy/skills_hook_adapter.py +0 -672
  34. package/src/adapters/codebuddy/skills_integration.py +0 -395
  35. package/src/adapters/codebuddy/standalone_codebuddy_adapter.py +0 -403
  36. package/src/adapters/codex/__init__.py +0 -11
  37. package/src/adapters/codex/base.py +0 -46
  38. package/src/adapters/codex/install_codex_integration.py +0 -311
  39. package/src/adapters/codex/mcp_server.py +0 -493
  40. package/src/adapters/codex/natural_language_parser.py +0 -82
  41. package/src/adapters/codex/slash_command_adapter.py +0 -326
  42. package/src/adapters/codex/standalone_codex_adapter.py +0 -362
  43. package/src/adapters/copilot/__init__.py +0 -13
  44. package/src/adapters/copilot/install_copilot_integration.py +0 -564
  45. package/src/adapters/copilot/mcp_adapter.py +0 -772
  46. package/src/adapters/copilot/mcp_server.py +0 -168
  47. package/src/adapters/copilot/standalone_copilot_adapter.py +0 -114
  48. package/src/adapters/gemini/__init__.py +0 -13
  49. package/src/adapters/gemini/extension_adapter.py +0 -690
  50. package/src/adapters/gemini/install_gemini_integration.py +0 -257
  51. package/src/adapters/gemini/standalone_gemini_adapter.py +0 -366
  52. package/src/adapters/iflow/__init__.py +0 -7
  53. package/src/adapters/iflow/hook_adapter.py +0 -1038
  54. package/src/adapters/iflow/hook_installer.py +0 -536
  55. package/src/adapters/iflow/install_iflow_integration.py +0 -271
  56. package/src/adapters/iflow/official_hook_adapter.py +0 -1272
  57. package/src/adapters/iflow/standalone_iflow_adapter.py +0 -48
  58. package/src/adapters/iflow/workflow_adapter.py +0 -793
  59. package/src/adapters/qoder/hook_installer.py +0 -732
  60. package/src/adapters/qoder/install_qoder_integration.py +0 -265
  61. package/src/adapters/qoder/notification_hook_adapter.py +0 -863
  62. package/src/adapters/qoder/standalone_qoder_adapter.py +0 -48
  63. package/src/adapters/qwen/__init__.py +0 -17
  64. package/src/adapters/qwencode/__init__.py +0 -13
  65. package/src/adapters/qwencode/inheritance_adapter.py +0 -818
  66. package/src/adapters/qwencode/install_qwencode_integration.py +0 -276
  67. package/src/adapters/qwencode/standalone_qwencode_adapter.py +0 -399
  68. package/src/atomic_collaboration_handler.py +0 -461
  69. package/src/cli_collaboration_agent.py +0 -697
  70. package/src/collaboration/hooks.py +0 -315
  71. package/src/core/__init__.py +0 -21
  72. package/src/core/ai_environment_scanner.py +0 -331
  73. package/src/core/base_adapter.py +0 -220
  74. package/src/core/cli_hook_integration.py +0 -406
  75. package/src/core/cross_cli_executor.py +0 -713
  76. package/src/core/cross_cli_mapping.py +0 -1165
  77. package/src/core/cross_platform_encoding.py +0 -365
  78. package/src/core/cross_platform_safe_cli.py +0 -894
  79. package/src/core/direct_cli_executor.py +0 -805
  80. package/src/core/direct_cli_hook_system.py +0 -958
  81. package/src/core/enhanced_init_processor.py +0 -467
  82. package/src/core/graceful_cli_executor.py +0 -912
  83. package/src/core/md_enhancer.py +0 -342
  84. package/src/core/md_generator.py +0 -619
  85. package/src/core/models.py +0 -218
  86. package/src/core/parser.py +0 -108
  87. package/src/core/real_cli_hook_system.py +0 -852
  88. package/src/core/real_cross_cli_system.py +0 -925
  89. package/src/core/verified_cross_cli_system.py +0 -961
  90. package/src/deploy.js +0 -737
  91. package/src/enhanced-main.js +0 -626
  92. package/src/enhanced_deploy.js +0 -303
  93. package/src/enhanced_universal_cli_setup.py +0 -930
  94. package/src/kimi_wrapper.py +0 -104
  95. package/src/main.js +0 -1309
  96. package/src/shell_integration.py +0 -398
  97. package/src/simple-main.js +0 -315
  98. package/src/smart_router_creator.py +0 -323
  99. package/src/universal_cli_setup.py +0 -1289
  100. package/src/utils/__init__.py +0 -12
  101. package/src/utils/cli_detector.py +0 -445
  102. package/src/utils/file_utils.py +0 -246
@@ -1,315 +0,0 @@
1
- """
2
- AI CLI 协同钩子系统
3
- 为每个CLI工具提供项目协同感知能力
4
- """
5
-
6
- import os
7
- import json
8
- import logging
9
- from pathlib import Path
10
- from typing import Dict, List, Optional, Any
11
- from datetime import datetime, timezone
12
- from dataclasses import asdict
13
- import threading
14
- import time
15
-
16
- logger = logging.getLogger(__name__)
17
-
18
- class CollaborationHook:
19
- """协同钩子基类"""
20
-
21
- def __init__(self, cli_name: str, project_root: Optional[Path] = None):
22
- self.cli_name = cli_name
23
- self.project_root = project_root or Path.cwd()
24
- self.constitution_file = self.project_root / "PROJECT_CONSTITUTION.json"
25
- self.state_file = self.project_root / "PROJECT_STATE.json"
26
- self.tasks_file = self.project_root / "TASKS.json"
27
- self.collaboration_file = self.project_root / "COLLABORATION_LOG.json"
28
-
29
- self.constitution_cache = {}
30
- self.state_cache = {}
31
- self.cache_timestamp = 0
32
- self.cache_ttl = 30 # 缓存30秒
33
-
34
- logger.info(f"协同钩子初始化: {cli_name} @ {self.project_root}")
35
-
36
- def is_project_enabled(self) -> bool:
37
- """检查项目是否启用协同功能"""
38
- if not self.constitution_file.exists():
39
- return False
40
-
41
- try:
42
- with open(self.constitution_file, 'r', encoding='utf-8') as f:
43
- constitution = json.load(f)
44
- return constitution.get("collaboration_config", {}).get("enabled", False)
45
- except Exception:
46
- return False
47
-
48
- def _refresh_cache(self):
49
- """刷新缓存"""
50
- current_time = time.time()
51
- if current_time - self.cache_timestamp > self.cache_ttl:
52
- self.cache_timestamp = current_time
53
-
54
- # 重新加载项目宪法
55
- if self.constitution_file.exists():
56
- try:
57
- with open(self.constitution_file, 'r', encoding='utf-8') as f:
58
- self.constitution_cache = json.load(f)
59
- except Exception as e:
60
- logger.warning(f"加载项目宪法失败: {e}")
61
-
62
- # 重新加载项目状态
63
- if self.state_file.exists():
64
- try:
65
- with open(self.state_file, 'r', encoding='utf-8') as f:
66
- self.state_cache = json.load(f)
67
- except Exception as e:
68
- logger.warning(f"加载项目状态失败: {e}")
69
-
70
- def get_project_constitution(self) -> Dict:
71
- """获取项目宪法"""
72
- self._refresh_cache()
73
- return self.constitution_cache
74
-
75
- def get_project_state(self) -> Dict:
76
- """获取项目状态"""
77
- self._refresh_cache()
78
- return self.state_cache
79
-
80
- def detect_collaboration_intent(self, user_input: str) -> Dict:
81
- """检测协作意图"""
82
- if not self.is_project_enabled():
83
- return {"intent": False}
84
-
85
- constitution = self.get_project_constitution()
86
- collaboration_keywords = constitution.get("cli_preferences", {}).get("collaboration_keywords", [])
87
-
88
- # 检测协作关键词
89
- user_input_lower = user_input.lower()
90
- for keyword in collaboration_keywords:
91
- if keyword in user_input_lower:
92
- return {
93
- "intent": True,
94
- "keyword": keyword,
95
- "detected_at": datetime.now(timezone.utc).isoformat()
96
- }
97
-
98
- return {"intent": False}
99
-
100
- def get_next_task(self) -> Optional[Dict]:
101
- """获取下一个任务"""
102
- if not self.is_project_enabled():
103
- return None
104
-
105
- # 这里应该调用任务管理器获取任务
106
- # 为了简化,直接检查任务文件
107
- if not self.tasks_file.exists():
108
- return None
109
-
110
- try:
111
- with open(self.tasks_file, 'r', encoding='utf-8') as f:
112
- tasks_data = json.load(f)
113
-
114
- # 获取CLI优先级
115
- constitution = self.get_project_constitution()
116
- cli_preferences = constitution.get("cli_preferences", {})
117
- task_priorities = cli_preferences.get("task_priorities", {}).get(self.cli_name, [])
118
-
119
- # 查找待处理任务
120
- pending_tasks = [task for task in tasks_data.get("tasks", [])
121
- if task.get("status") == "pending"]
122
-
123
- if not pending_tasks:
124
- return None
125
-
126
- # 简单优先级排序
127
- priority_scores = {"critical": 4, "high": 3, "normal": 2, "low": 1}
128
-
129
- def task_score(task):
130
- score = priority_scores.get(task.get("priority", "normal"), 2)
131
-
132
- # CLI偏好加分
133
- if task.get("tags"):
134
- for tag in task.get("tags", []):
135
- if tag in task_priorities:
136
- score += 2
137
-
138
- return score
139
-
140
- pending_tasks.sort(key=task_score, reverse=True)
141
- return pending_tasks[0]
142
-
143
- except Exception as e:
144
- logger.error(f"获取下一个任务失败: {e}")
145
- return None
146
-
147
- def create_task(self, title: str, description: str, **kwargs) -> bool:
148
- """创建新任务"""
149
- if not self.is_project_enabled():
150
- logger.warning("项目未启用协同功能")
151
- return False
152
-
153
- try:
154
- # 这里应该调用任务管理器创建任务
155
- # 为了简化,直接修改任务文件
156
- tasks_data = {"tasks": [], "next_id": 1}
157
-
158
- if self.tasks_file.exists():
159
- with open(self.tasks_file, 'r', encoding='utf-8') as f:
160
- tasks_data = json.load(f)
161
-
162
- # 创建新任务
163
- task_id = f"task_{tasks_data['next_id']:04d}"
164
- task = {
165
- "id": task_id,
166
- "title": title,
167
- "description": description,
168
- "status": "pending",
169
- "priority": "normal",
170
- "assigned_to": self.cli_name,
171
- "created_by": self.cli_name,
172
- "created_at": datetime.now(timezone.utc).isoformat(),
173
- "updated_at": datetime.now(timezone.utc).isoformat(),
174
- "tags": [],
175
- "metadata": kwargs
176
- }
177
-
178
- tasks_data["tasks"].append(task)
179
- tasks_data["next_id"] += 1
180
-
181
- with open(self.tasks_file, 'w', encoding='utf-8') as f:
182
- json.dump(tasks_data, f, indent=2, ensure_ascii=False)
183
-
184
- # 记录协作日志
185
- self._log_activity("task_created", {
186
- "task_id": task_id,
187
- "title": title,
188
- "assigned_to": self.cli_name
189
- })
190
-
191
- logger.info(f"✅ 任务创建成功: {task_id}")
192
- return True
193
-
194
- except Exception as e:
195
- logger.error(f"创建任务失败: {e}")
196
- return False
197
-
198
- def update_task_status(self, task_id: str, status: str, **kwargs) -> bool:
199
- """更新任务状态"""
200
- if not self.is_project_enabled():
201
- return False
202
-
203
- try:
204
- if not self.tasks_file.exists():
205
- return False
206
-
207
- with open(self.tasks_file, 'r', encoding='utf-8') as f:
208
- tasks_data = json.load(f)
209
-
210
- # 查找并更新任务
211
- for task in tasks_data.get("tasks", []):
212
- if task.get("id") == task_id:
213
- task["status"] = status
214
- task["updated_at"] = datetime.now(timezone.utc).isoformat()
215
- task.update(kwargs)
216
-
217
- # 如果任务完成,记录完成时间
218
- if status == "completed":
219
- task["completed_at"] = datetime.now(timezone.utc).isoformat()
220
-
221
- # 记录协作日志
222
- self._log_activity("task_completed", {
223
- "task_id": task_id,
224
- "title": task.get("title", ""),
225
- "assigned_to": task.get("assigned_to", "")
226
- })
227
- else:
228
- # 记录协作日志
229
- self._log_activity("task_status_updated", {
230
- "task_id": task_id,
231
- "old_status": task.get("status"),
232
- "new_status": status,
233
- "assigned_to": task.get("assigned_to", "")
234
- })
235
-
236
- break
237
-
238
- with open(self.tasks_file, 'w', encoding='utf-8') as f:
239
- json.dump(tasks_data, f, indent=2, ensure_ascii=False)
240
-
241
- logger.info(f"✅ 任务状态更新: {task_id} -> {status}")
242
- return True
243
-
244
- except Exception as e:
245
- logger.error(f"更新任务状态失败: {e}")
246
- return False
247
-
248
- def _log_activity(self, action: str, details: Dict):
249
- """记录协作活动"""
250
- try:
251
- log_entry = {
252
- "timestamp": datetime.now(timezone.utc).isoformat(),
253
- "cli": self.cli_name,
254
- "action": action,
255
- "details": details
256
- }
257
-
258
- logs = []
259
- if self.collaboration_file.exists():
260
- with open(self.collaboration_file, 'r', encoding='utf-8') as f:
261
- logs = json.load(f)
262
-
263
- logs.append(log_entry)
264
-
265
- # 限制日志大小
266
- if len(logs) > 1000:
267
- logs = logs[-1000:]
268
-
269
- with open(self.collaboration_file, 'w', encoding='utf-8') as f:
270
- json.dump(logs, f, indent=2, ensure_ascii=False)
271
-
272
- except Exception as e:
273
- logger.error(f"记录协作日志失败: {e}")
274
-
275
- class CLICollaborationHooks:
276
- """CLI协作钩子集合"""
277
-
278
- _hooks = {}
279
- _lock = threading.Lock()
280
-
281
- @classmethod
282
- def register_hook(cls, cli_name: str, hook: CollaborationHook):
283
- """注册协作钩子"""
284
- with cls._lock:
285
- cls._hooks[cli_name] = hook
286
- logger.info(f"注册协作钩子: {cli_name}")
287
-
288
- @classmethod
289
- def get_hook(cls, cli_name: str) -> Optional[CollaborationHook]:
290
- """获取协作钩子"""
291
- with cls._lock:
292
- return cls._hooks.get(cli_name)
293
-
294
- @classmethod
295
- def initialize_hook(cls, cli_name: str, project_root: Optional[Path] = None):
296
- """初始化协作钩子"""
297
- if cls.get_hook(cli_name) is None:
298
- hook = CollaborationHook(cli_name, project_root)
299
- cls.register_hook(cli_name, hook)
300
- logger.info(f"初始化协作钩子: {cli_name}")
301
- return hook
302
-
303
- return cls.get_hook(cli_name)
304
-
305
- # 预定义的CLI工具钩子
306
- def initialize_standard_hooks():
307
- """初始化标准CLI工具的协作钩子"""
308
-
309
- standard_clis = ["claude", "gemini", "codex", "qwencode", "codebuddy", "iflow", "qoder", "copilot"]
310
-
311
- for cli_name in standard_clis:
312
- CLICollaborationHooks.initialize_hook(cli_name)
313
-
314
- # 全局初始化
315
- initialize_standard_hooks()
@@ -1,21 +0,0 @@
1
-
2
- """
3
- AI CLI Router - 核心模块
4
- AI CLI协作系统的核心组件
5
- """
6
-
7
- # 导入核心类和函数
8
- from .models import *
9
- from .ai_environment_scanner import AIEnvironmentScanner
10
- from .enhanced_init_processor import EnhancedInitProcessor
11
- from .md_enhancer import MDDocumentEnhancer
12
- from .md_generator import MDDocumentGenerator
13
-
14
- # 版本信息
15
- __version__ = '1.0.0'
16
- __all__ = [
17
- 'AIEnvironmentScanner',
18
- 'EnhancedInitProcessor',
19
- 'MDDocumentEnhancer',
20
- 'MDDocumentGenerator'
21
- ]
@@ -1,331 +0,0 @@
1
- """
2
- AI Environment Scanner - AI环境扫描器
3
- 检测项目可用的AI CLI工具和协作环境
4
- """
5
- import os
6
- import asyncio
7
- import json
8
- import yaml
9
- import subprocess
10
- import logging
11
- from pathlib import Path
12
- from typing import Dict, List, Optional, Any
13
- from datetime import datetime
14
-
15
- from .models import (
16
- CLIInfo, ProjectCLIInfo, AIEnvironmentInfo, CollaborationGuide,
17
- CLIStatus, IntegrationType, CLI_CONFIG_MAPPING, COLLABORATION_PROTOCOLS
18
- )
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
-
23
- class AIEnvironmentScanner:
24
- """AI环境扫描器 - 检测项目可用的AI协作工具"""
25
-
26
- def __init__(self, current_cli: str = None):
27
- self.current_cli = current_cli
28
- self.scan_start_time = None
29
-
30
- async def scan_ai_environment(self, project_path: str) -> AIEnvironmentInfo:
31
- """扫描当前项目的AI环境"""
32
- self.scan_start_time = datetime.now()
33
-
34
- try:
35
- # 1. 检测全局已安装的CLI工具
36
- logger.info("开始扫描全局CLI工具...")
37
- global_clis = await self._scan_global_clis()
38
-
39
- # 2. 检测项目特定的AI配置
40
- logger.info("开始扫描项目特定配置...")
41
- project_clis = await self._scan_project_clis(project_path)
42
-
43
- # 3. 生成协作指南
44
- logger.info("生成协作指南...")
45
- collaboration_guide = await self._generate_collaboration_guide(global_clis, project_clis)
46
-
47
- scan_duration = (datetime.now() - self.scan_start_time).total_seconds()
48
-
49
- ai_environment = AIEnvironmentInfo(
50
- available_clis=global_clis,
51
- project_specific_clis=project_clis,
52
- collaboration_guide=collaboration_guide,
53
- generated_at=self.scan_start_time,
54
- scan_duration=scan_duration
55
- )
56
-
57
- logger.info(f"AI环境扫描完成,耗时: {scan_duration:.2f}秒")
58
- return ai_environment
59
-
60
- except Exception as e:
61
- logger.error(f"AI环境扫描失败: {e}")
62
- raise
63
-
64
- async def _scan_global_clis(self) -> Dict[str, CLIInfo]:
65
- """扫描全局已安装的AI CLI工具"""
66
- available_clis = {}
67
-
68
- scan_tasks = []
69
- for cli_name, config_mapping in CLI_CONFIG_MAPPING.items():
70
- task = self._scan_single_cli(cli_name, config_mapping)
71
- scan_tasks.append(task)
72
-
73
- # 并行扫描所有CLI工具
74
- results = await asyncio.gather(*scan_tasks, return_exceptions=True)
75
-
76
- for i, (cli_name, config_mapping) in enumerate(CLI_CONFIG_MAPPING.items()):
77
- result = results[i]
78
- if isinstance(result, Exception):
79
- logger.warning(f"扫描 {cli_name} 时出错: {result}")
80
- continue
81
-
82
- if result:
83
- available_clis[cli_name] = result
84
-
85
- logger.info(f"发现 {len(available_clis)} 个可用的CLI工具: {list(available_clis.keys())}")
86
- return available_clis
87
-
88
- async def _scan_single_cli(self, cli_name: str, config_mapping) -> Optional[CLIInfo]:
89
- """扫描单个CLI工具"""
90
- try:
91
- # 1. 检查CLI工具是否可用
92
- is_available = await self._is_cli_available(cli_name, config_mapping)
93
- if not is_available:
94
- return None
95
-
96
- # 2. 获取版本信息
97
- version = await self._get_cli_version(cli_name, config_mapping)
98
-
99
- # 3. 获取能力信息
100
- capabilities = await self._get_cli_capabilities(cli_name, config_mapping)
101
-
102
- # 4. 检查配置文件
103
- config_file_exists = await self._check_config_file(config_mapping.config_file)
104
-
105
- status = CLIStatus.AVAILABLE if config_file_exists else CLIStatus.CONFIG_MISSING
106
-
107
- return CLIInfo(
108
- name=cli_name,
109
- display_name=self._get_display_name(cli_name),
110
- version=version,
111
- integration_type=config_mapping.integration_type,
112
- status=status,
113
- capabilities=capabilities,
114
- config_file=config_mapping.config_file,
115
- global_doc=config_mapping.global_doc,
116
- protocols=COLLABORATION_PROTOCOLS["chinese"] + COLLABORATION_PROTOCOLS["english"]
117
- )
118
-
119
- except Exception as e:
120
- logger.warning(f"扫描CLI工具 {cli_name} 失败: {e}")
121
- return None
122
-
123
- async def _is_cli_available(self, cli_name: str, config_mapping) -> bool:
124
- """检查CLI工具是否可用"""
125
- try:
126
- # 检查命令是否存在
127
- command = config_mapping.version_check_command
128
- if not command:
129
- # 如果没有版本检查命令,尝试通用命令
130
- command = f"{cli_name}-cli --version"
131
-
132
- result = await asyncio.create_subprocess_shell(
133
- command,
134
- stdout=asyncio.subprocess.PIPE,
135
- stderr=asyncio.subprocess.PIPE
136
- )
137
- stdout, stderr = await result.communicate()
138
-
139
- return result.returncode == 0
140
-
141
- except Exception:
142
- return False
143
-
144
- async def _get_cli_version(self, cli_name: str, config_mapping) -> str:
145
- """获取CLI工具版本"""
146
- try:
147
- command = config_mapping.version_check_command
148
- if not command:
149
- command = f"{cli_name}-cli --version"
150
-
151
- result = await asyncio.create_subprocess_shell(
152
- command,
153
- stdout=asyncio.subprocess.PIPE,
154
- stderr=asyncio.subprocess.PIPE
155
- )
156
- stdout, stderr = await result.communicate()
157
-
158
- if result.returncode == 0:
159
- version_output = stdout.decode().strip() or stderr.decode().strip()
160
- # 提取版本号
161
- for line in version_output.split('\n'):
162
- if any(keyword in line.lower() for keyword in ['version', 'v']):
163
- return line.strip()
164
- return version_output
165
- else:
166
- return "Unknown"
167
-
168
- except Exception:
169
- return "Unknown"
170
-
171
- async def _get_cli_capabilities(self, cli_name: str, config_mapping) -> List[str]:
172
- """获取CLI工具能力"""
173
- # 基于集成类型返回默认能力
174
- capability_map = {
175
- IntegrationType.HOOK_SYSTEM: ["钩子系统", "事件处理", "跨工具协调"],
176
- IntegrationType.EXTENSION_SYSTEM: ["扩展系统", "智能匹配", "多处理器"],
177
- IntegrationType.CLASS_INHERITANCE: ["插件继承", "代码生成", "方法重写"],
178
- IntegrationType.WORKFLOW_PIPELINE: ["工作流", "流水线处理", "任务队列"],
179
- IntegrationType.NOTIFICATION_HOOK: ["通知系统", "实时监控", "事件通知"],
180
- IntegrationType.MCP_SERVER: ["MCP协议", "服务器集成", "自定义代理"],
181
- IntegrationType.SLASH_COMMAND: ["斜杠命令", "快速执行", "参数化命令"]
182
- }
183
-
184
- return capability_map.get(config_mapping.integration_type, ["通用AI助手"])
185
-
186
- async def _check_config_file(self, config_file_path: str) -> bool:
187
- """检查配置文件是否存在"""
188
- try:
189
- expanded_path = Path(config_file_path.expanduser())
190
- return expanded_path.exists() and expanded_path.is_file()
191
- except Exception:
192
- return False
193
-
194
- def _get_display_name(self, cli_name: str) -> str:
195
- """获取CLI工具显示名称"""
196
- display_names = {
197
- "claude": "Claude CLI",
198
- "gemini": "Gemini CLI",
199
- "qwen": "QwenCode CLI",
200
- "iflow": "iFlow CLI",
201
- "qoder": "Qoder CLI",
202
- "codebuddy": "CodeBuddy CLI",
203
- "copilot": "Copilot CLI",
204
- "codex": "Codex CLI"
205
- }
206
- return display_names.get(cli_name, cli_name.upper())
207
-
208
- async def _scan_project_clis(self, project_path: str) -> Dict[str, ProjectCLIInfo]:
209
- """扫描项目特定的AI配置"""
210
- project_clis = {}
211
-
212
- # 检查项目目录下的AI配置文件
213
- ai_config_files = [
214
- ".ai-config.json",
215
- ".claude-project.json",
216
- ".gemini-project.json",
217
- ".qwen-project.json",
218
- ".iflow-project.json",
219
- "ai-workflow.yml",
220
- ".ai-cli-unified.json"
221
- ]
222
-
223
- for config_file in ai_config_files:
224
- config_path = Path(project_path) / config_file
225
- if config_path.exists():
226
- try:
227
- cli_configs = await self._parse_project_config(config_path)
228
- project_clis.update(cli_configs)
229
- logger.info(f"发现项目配置文件: {config_file}")
230
- except Exception as e:
231
- logger.warning(f"解析项目配置文件 {config_file} 失败: {e}")
232
-
233
- logger.info(f"发现 {len(project_clis)} 个项目特定CLI配置")
234
- return project_clis
235
-
236
- async def _parse_project_config(self, config_path: Path) -> Dict[str, ProjectCLIInfo]:
237
- """解析项目配置文件"""
238
- project_clis = {}
239
-
240
- try:
241
- with open(config_path, 'r', encoding='utf-8') as f:
242
- if config_path.suffix.lower() in ['.yml', '.yaml']:
243
- config_data = yaml.safe_load(f)
244
- else:
245
- config_data = json.load(f)
246
-
247
- # 根据配置文件格式解析CLI配置
248
- if 'ai_tools' in config_data:
249
- for cli_name, cli_config in config_data['ai_tools'].items():
250
- project_clis[cli_name] = ProjectCLIInfo(
251
- cli_name=cli_name,
252
- project_config=cli_config,
253
- custom_settings=cli_config.get('custom_settings', {}),
254
- enabled_features=cli_config.get('enabled_features', [])
255
- )
256
- elif 'tools' in config_data:
257
- # 简化格式
258
- for cli_config in config_data['tools']:
259
- cli_name = cli_config.get('name')
260
- if cli_name:
261
- project_clis[cli_name] = ProjectCLIInfo(
262
- cli_name=cli_name,
263
- project_config=cli_config,
264
- custom_settings=cli_config.get('custom_settings', {}),
265
- enabled_features=cli_config.get('enabled_features', [])
266
- )
267
-
268
- except Exception as e:
269
- logger.error(f"解析配置文件失败 {config_path}: {e}")
270
- raise
271
-
272
- return project_clis
273
-
274
- async def _generate_collaboration_guide(self,
275
- global_clis: Dict[str, CLIInfo],
276
- project_clis: Dict[str, ProjectCLIInfo]) -> CollaborationGuide:
277
- """生成协作指南"""
278
-
279
- # 合并全局和项目特定的CLI信息
280
- all_clis = {**global_clis}
281
- for cli_name, project_cli in project_clis.items():
282
- if cli_name in all_clis:
283
- # 增强现有CLI信息
284
- existing_cli = all_clis[cli_name]
285
- # 可以在这里添加项目特定的增强信息
286
- else:
287
- # 项目特定的CLI可能不在全局列表中
288
- logger.info(f"项目特定CLI {cli_name} 不在全局列表中")
289
-
290
- # 确定当前CLI工具
291
- current_cli = self.current_cli or "claude" # 默认值
292
-
293
- # 生成可协作的伙伴列表(排除当前CLI)
294
- peer_clis = {k: v for k, v in all_clis.items() if k != current_cli}
295
-
296
- # 生成协作示例
297
- examples = self._generate_collaboration_examples(current_cli, peer_clis)
298
-
299
- return CollaborationGuide(
300
- current_cli=current_cli,
301
- available_peers=peer_clis,
302
- protocols=COLLABORATION_PROTOCOLS,
303
- examples=examples
304
- )
305
-
306
- def _generate_collaboration_examples(self, current_cli: str, peer_clis: Dict[str, CLIInfo]) -> List[str]:
307
- """生成协作示例"""
308
- examples = []
309
-
310
- # 基于可用CLI工具生成示例
311
- if "qwen" in peer_clis:
312
- examples.append("请用qwen帮我生成单元测试")
313
- examples.append("调用qwen来重构这个函数")
314
-
315
- if "gemini" in peer_clis:
316
- examples.append("用gemini分析代码性能")
317
- examples.append("让gemini优化这个查询")
318
-
319
- if "iflow" in peer_clis:
320
- examples.append("用iflow创建CI/CD工作流")
321
- examples.append("启动iflow部署流程")
322
-
323
- if "claude" in peer_clis:
324
- examples.append("请用claude审查这段代码")
325
- examples.append("调用claude进行架构分析")
326
-
327
- # 添加通用示例
328
- examples.append("请用{tool}帮我{task}")
329
- examples.append("call {tool} to {task}")
330
-
331
- return examples