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,697 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
多智能体协作代理
|
|
5
|
-
通过项目背景实现CLI工具间的间接协同(Stigmergy)
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import os
|
|
9
|
-
import sys
|
|
10
|
-
import json
|
|
11
|
-
import subprocess
|
|
12
|
-
import time
|
|
13
|
-
import re
|
|
14
|
-
from datetime import datetime
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ProjectContext:
|
|
19
|
-
"""项目背景管理器 - 实现Stigmergy机制"""
|
|
20
|
-
|
|
21
|
-
def __init__(self, project_path="."):
|
|
22
|
-
self.project_path = Path(project_path)
|
|
23
|
-
self.spec_file = self.project_path / "PROJECT_SPEC.json"
|
|
24
|
-
self.tasks_file = self.project_path / "TASKS.md"
|
|
25
|
-
self.log_file = self.project_path / "COLLABORATION_LOG.md"
|
|
26
|
-
self.background_files = [self.spec_file, self.tasks_file, self.log_file]
|
|
27
|
-
self.data = self._load_context()
|
|
28
|
-
|
|
29
|
-
def _load_context(self):
|
|
30
|
-
"""加载项目背景"""
|
|
31
|
-
if self.spec_file.exists():
|
|
32
|
-
with open(self.spec_file, 'r', encoding='utf-8') as f:
|
|
33
|
-
return json.load(f)
|
|
34
|
-
else:
|
|
35
|
-
# 创建默认背景
|
|
36
|
-
default_context = {
|
|
37
|
-
"project_name": "Collaboration Project",
|
|
38
|
-
"created_at": datetime.now().isoformat(),
|
|
39
|
-
"status": "active",
|
|
40
|
-
"current_agents": {},
|
|
41
|
-
"tasks": {},
|
|
42
|
-
"collaboration_history": [],
|
|
43
|
-
"decisions": [],
|
|
44
|
-
"communication_log": [],
|
|
45
|
-
"current_state": {
|
|
46
|
-
"active_task": None,
|
|
47
|
-
"completed_tasks": [],
|
|
48
|
-
"pending_tasks": [],
|
|
49
|
-
"next_scheduled_task": None
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
self._save_context(default_context)
|
|
53
|
-
return default_context
|
|
54
|
-
|
|
55
|
-
def _save_context(self, data=None):
|
|
56
|
-
"""保存项目背景"""
|
|
57
|
-
if data:
|
|
58
|
-
self.data = data
|
|
59
|
-
with open(self.spec_file, 'w', encoding='utf-8') as f:
|
|
60
|
-
json.dump(self.data, f, ensure_ascii=False, indent=2)
|
|
61
|
-
|
|
62
|
-
# 同时更新任务列表文件
|
|
63
|
-
self._update_tasks_file()
|
|
64
|
-
|
|
65
|
-
# 同时更新协作日志
|
|
66
|
-
self._update_log_file()
|
|
67
|
-
|
|
68
|
-
def _update_tasks_file(self):
|
|
69
|
-
"""更新任务列表文件"""
|
|
70
|
-
with open(self.tasks_file, 'w', encoding='utf-8') as f:
|
|
71
|
-
f.write("# 项目任务列表\n\n")
|
|
72
|
-
f.write("## 已完成任务\n")
|
|
73
|
-
for task_id in self.data["current_state"]["completed_tasks"]:
|
|
74
|
-
task = self.data["tasks"].get(task_id, {})
|
|
75
|
-
f.write(f"- [x] {task.get('description', task_id)} (完成于 {task.get('completed_at', '')})\n")
|
|
76
|
-
|
|
77
|
-
f.write("\n## 待完成任务\n")
|
|
78
|
-
for task_id in self.data["current_state"]["pending_tasks"]:
|
|
79
|
-
task = self.data["tasks"].get(task_id, {})
|
|
80
|
-
f.write(f"- [ ] {task.get('description', task_id)}\n")
|
|
81
|
-
|
|
82
|
-
def _update_log_file(self):
|
|
83
|
-
"""更新协作日志文件"""
|
|
84
|
-
with open(self.log_file, 'w', encoding='utf-8') as f:
|
|
85
|
-
f.write("# 协作日志\n\n")
|
|
86
|
-
for log_entry in self.data["collaboration_history"]:
|
|
87
|
-
timestamp = log_entry.get("timestamp", "")
|
|
88
|
-
agent = log_entry.get("agent", "")
|
|
89
|
-
message = log_entry.get("message", "")
|
|
90
|
-
f.write(f"[{timestamp}] [{agent}] {message}\n")
|
|
91
|
-
|
|
92
|
-
def add_collaboration_log(self, message, agent_name=None):
|
|
93
|
-
"""添加协作日志到背景"""
|
|
94
|
-
log_entry = {
|
|
95
|
-
"timestamp": datetime.now().isoformat(),
|
|
96
|
-
"agent": agent_name,
|
|
97
|
-
"message": message
|
|
98
|
-
}
|
|
99
|
-
self.data["collaboration_history"].append(log_entry)
|
|
100
|
-
self._save_context()
|
|
101
|
-
|
|
102
|
-
def update_task_status(self, task_id, status, result=None, completed_by=None):
|
|
103
|
-
"""更新任务状态到背景"""
|
|
104
|
-
if task_id in self.data["tasks"]:
|
|
105
|
-
self.data["tasks"][task_id].update({
|
|
106
|
-
"status": status,
|
|
107
|
-
"completed_at": datetime.now().isoformat() if status == "completed" else None,
|
|
108
|
-
"result": result,
|
|
109
|
-
"completed_by": completed_by
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
if status == "completed":
|
|
113
|
-
# 避免重复添加到已完成列表
|
|
114
|
-
if task_id not in self.data["current_state"]["completed_tasks"]:
|
|
115
|
-
self.data["current_state"]["completed_tasks"].append(task_id)
|
|
116
|
-
if task_id in self.data["current_state"]["pending_tasks"]:
|
|
117
|
-
self.data["current_state"]["pending_tasks"].remove(task_id)
|
|
118
|
-
elif status == "in_progress":
|
|
119
|
-
# 更新活动任务
|
|
120
|
-
self.data["current_state"]["active_task"] = task_id
|
|
121
|
-
|
|
122
|
-
# 强制立即保存以确保状态同步
|
|
123
|
-
self._save_context()
|
|
124
|
-
|
|
125
|
-
# 强制重新加载上下文以确保其他实例能看到最新状态
|
|
126
|
-
self._force_reload_context()
|
|
127
|
-
|
|
128
|
-
def _reload_context(self):
|
|
129
|
-
"""重新加载上下文以确保数据同步"""
|
|
130
|
-
try:
|
|
131
|
-
if self.spec_file.exists():
|
|
132
|
-
with open(self.spec_file, 'r', encoding='utf-8') as f:
|
|
133
|
-
self.data = json.load(f)
|
|
134
|
-
except Exception as e:
|
|
135
|
-
print(f"重载上下文失败: {e}")
|
|
136
|
-
# 如果重载失败,保持当前缓存的数据
|
|
137
|
-
|
|
138
|
-
def create_task(self, task_type, description, assigned_to=None, priority="medium", dependencies=None):
|
|
139
|
-
"""创建新任务到背景"""
|
|
140
|
-
task_id = f"{task_type}_{int(time.time())}_{len(self.data['tasks'])}"
|
|
141
|
-
|
|
142
|
-
task = {
|
|
143
|
-
"id": task_id,
|
|
144
|
-
"type": task_type,
|
|
145
|
-
"description": description,
|
|
146
|
-
"assigned_to": assigned_to,
|
|
147
|
-
"status": "pending",
|
|
148
|
-
"priority": priority,
|
|
149
|
-
"created_at": datetime.now().isoformat(),
|
|
150
|
-
"dependencies": dependencies or [],
|
|
151
|
-
"result": None,
|
|
152
|
-
"completed_by": None,
|
|
153
|
-
"completed_at": None
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
self.data["tasks"][task_id] = task
|
|
157
|
-
self.data["current_state"]["pending_tasks"].append(task_id)
|
|
158
|
-
|
|
159
|
-
# 更新下一个计划任务
|
|
160
|
-
if self.data["current_state"]["next_scheduled_task"] is None:
|
|
161
|
-
self.data["current_state"]["next_scheduled_task"] = task_id
|
|
162
|
-
|
|
163
|
-
self._save_context()
|
|
164
|
-
return task_id
|
|
165
|
-
|
|
166
|
-
def analyze_context_for_next_action(self, agent_name):
|
|
167
|
-
"""分析背景以决定下一步行动 - 实现Stigmergy的核心机制"""
|
|
168
|
-
# 为确保分析基于最新状态,强制重新加载背景
|
|
169
|
-
self._force_reload_context()
|
|
170
|
-
|
|
171
|
-
# 检查是否有明确分配给当前智能体的任务
|
|
172
|
-
for task_id, task in self.data["tasks"].items():
|
|
173
|
-
if (task["status"] == "pending" and
|
|
174
|
-
task.get("assigned_to") == agent_name):
|
|
175
|
-
return task_id, task
|
|
176
|
-
|
|
177
|
-
# 检查是否有未分配的任务,当前智能体可能有能力处理
|
|
178
|
-
for task_id, task in self.data["tasks"].items():
|
|
179
|
-
if (task["status"] == "pending" and
|
|
180
|
-
task.get("assigned_to") is None and
|
|
181
|
-
self._can_handle_task(task, agent_name)):
|
|
182
|
-
|
|
183
|
-
# 尝试认领任务(分配给自己)
|
|
184
|
-
self.data["tasks"][task_id]["assigned_to"] = agent_name
|
|
185
|
-
self._save_context()
|
|
186
|
-
return task_id, task
|
|
187
|
-
|
|
188
|
-
# 检查是否有需要协助的任务(如审查、优化等)
|
|
189
|
-
for task_id, task in self.data["tasks"].items():
|
|
190
|
-
if (task["status"] == "completed" and
|
|
191
|
-
"review_needed" in task.get("result", "").lower()):
|
|
192
|
-
|
|
193
|
-
# 创建审查任务
|
|
194
|
-
review_task_id = self.create_task(
|
|
195
|
-
"review",
|
|
196
|
-
"审查任务 " + task_id + " 的结果: " + task.get('result', '')[:200] + "...",
|
|
197
|
-
assigned_to=agent_name,
|
|
198
|
-
priority="high"
|
|
199
|
-
)
|
|
200
|
-
return review_task_id, self.data["tasks"][review_task_id]
|
|
201
|
-
|
|
202
|
-
return None, None
|
|
203
|
-
|
|
204
|
-
def _force_reload_context(self):
|
|
205
|
-
"""强制重新加载背景以确保获取最新状态"""
|
|
206
|
-
try:
|
|
207
|
-
if self.spec_file.exists():
|
|
208
|
-
with open(self.spec_file, 'r', encoding='utf-8') as f:
|
|
209
|
-
self.data = json.load(f)
|
|
210
|
-
except Exception as e:
|
|
211
|
-
print(f"⚠️ 强制重载背景失败,使用缓存数据: {e}")
|
|
212
|
-
# 如果重载失败,保持当前缓存的数据
|
|
213
|
-
|
|
214
|
-
def _reload_context_if_needed(self):
|
|
215
|
-
"""在分析背景前检查是否需要重新加载以确保最新状态"""
|
|
216
|
-
# 检查文件修改时间戳,如果本地缓存可能已过期则重新加载
|
|
217
|
-
try:
|
|
218
|
-
if self.spec_file.exists():
|
|
219
|
-
import time
|
|
220
|
-
file_modified = self.spec_file.stat().st_mtime
|
|
221
|
-
current_time = time.time()
|
|
222
|
-
|
|
223
|
-
# 如果文件在过去3秒内被修改过,则重新加载(考虑到系统调用的时间差)
|
|
224
|
-
time_diff = abs(current_time - file_modified)
|
|
225
|
-
if time_diff < 3: # 如果文件在最近几秒内被修改
|
|
226
|
-
with open(self.spec_file, 'r', encoding='utf-8') as f:
|
|
227
|
-
self.data = json.load(f)
|
|
228
|
-
except Exception as e:
|
|
229
|
-
# 如果重新加载失败,继续使用缓存的数据
|
|
230
|
-
pass
|
|
231
|
-
|
|
232
|
-
def _can_handle_task(self, task, agent_name):
|
|
233
|
-
"""判断当前智能体是否可以处理任务 - 基于能力匹配"""
|
|
234
|
-
description = task["description"].lower()
|
|
235
|
-
|
|
236
|
-
agent_capabilities = {
|
|
237
|
-
"claude": ["analyze", "logic", "reasoning", "review", "explain", "analysis", "data"],
|
|
238
|
-
"gemini": ["translate", "multilingual", "creative", "document", "writing", "content"],
|
|
239
|
-
"qwen": ["chinese", "中文", "translate", "chat", "question", "answer"],
|
|
240
|
-
"codebuddy": ["code", "function", "program", "bug", "refactor", "debug", "test"],
|
|
241
|
-
"kimi": ["long", "analysis", "research", "content", "report"],
|
|
242
|
-
"qodercli": ["generate", "code", "template", "create", "build"],
|
|
243
|
-
"iflow": ["workflow", "process", "automate", "task", "schedule"],
|
|
244
|
-
"ollama": ["local", "offline", "private", "secure", "model"]
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
capabilities = agent_capabilities.get(agent_name, [])
|
|
248
|
-
return any(cap in description for cap in capabilities)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
class CLICollaborationAgent:
|
|
252
|
-
"""CLI协作智能体 - 基于背景的间接协同"""
|
|
253
|
-
|
|
254
|
-
def __init__(self, agent_name, project_path="."):
|
|
255
|
-
self.agent_name = agent_name
|
|
256
|
-
self.project_context = ProjectContext(project_path)
|
|
257
|
-
self.cli_command = self._determine_cli_command()
|
|
258
|
-
|
|
259
|
-
def _determine_cli_command(self):
|
|
260
|
-
"""确定CLI命令"""
|
|
261
|
-
# 根据智能体名称确定CLI命令
|
|
262
|
-
tool_commands = {
|
|
263
|
-
"claude": "claude",
|
|
264
|
-
"gemini": "gemini",
|
|
265
|
-
"kimi": "kimi",
|
|
266
|
-
"qwen": "qwen",
|
|
267
|
-
"ollama": "ollama",
|
|
268
|
-
"codebuddy": "codebuddy",
|
|
269
|
-
"qodercli": "qodercli",
|
|
270
|
-
"iflow": "iflow"
|
|
271
|
-
}
|
|
272
|
-
return tool_commands.get(self.agent_name, self.agent_name)
|
|
273
|
-
|
|
274
|
-
def execute_command(self, command_args):
|
|
275
|
-
"""执行CLI命令 - 支持内部协作功能"""
|
|
276
|
-
try:
|
|
277
|
-
# 分析输入中是否包含协作指令
|
|
278
|
-
user_input = " ".join(command_args) if command_args else ""
|
|
279
|
-
|
|
280
|
-
# 检查协作意图
|
|
281
|
-
collaboration_match = self._analyze_collaboration_intent(user_input)
|
|
282
|
-
if collaboration_match:
|
|
283
|
-
target_agent, task_desc = collaboration_match
|
|
284
|
-
|
|
285
|
-
# 在新窗口中执行目标智能体,当前项目路径
|
|
286
|
-
success = self._execute_collaboration_in_new_window(target_agent, task_desc)
|
|
287
|
-
if success:
|
|
288
|
-
return True, f"协作任务完成: 已让{target_agent}处理 {task_desc}", ""
|
|
289
|
-
else:
|
|
290
|
-
return False, "", f"协作任务执行失败: 无法启动{target_agent},使用原始功能"
|
|
291
|
-
else:
|
|
292
|
-
# 执行原始命令
|
|
293
|
-
result = subprocess.run(
|
|
294
|
-
[self.cli_command] + command_args,
|
|
295
|
-
capture_output=True,
|
|
296
|
-
text=True,
|
|
297
|
-
timeout=300
|
|
298
|
-
)
|
|
299
|
-
return result.returncode == 0, result.stdout, result.stderr
|
|
300
|
-
except Exception as e:
|
|
301
|
-
return False, "", str(e)
|
|
302
|
-
|
|
303
|
-
def _analyze_collaboration_intent(self, user_input):
|
|
304
|
-
"""分析协作意图 - 检查是否需要路由到其他智能体"""
|
|
305
|
-
patterns = [
|
|
306
|
-
# 让<工具名>帮我<任务>
|
|
307
|
-
r'让\s*([a-zA-Z]+)\s*帮我\s*(.+)',
|
|
308
|
-
# 用<工具名><任务>
|
|
309
|
-
r'用\s*([a-zA-Z]+)\s*(.+)',
|
|
310
|
-
# 请<工具名><任务>
|
|
311
|
-
r'请\s*([a-zA-Z]+)\s*(.+)'
|
|
312
|
-
]
|
|
313
|
-
|
|
314
|
-
for pattern in patterns:
|
|
315
|
-
match = re.search(pattern, user_input, re.IGNORECASE)
|
|
316
|
-
if match:
|
|
317
|
-
target_agent = match.group(1).lower().strip()
|
|
318
|
-
task_desc = match.group(2).strip()
|
|
319
|
-
|
|
320
|
-
# 检查目标工具是否在系统中可用
|
|
321
|
-
if self._is_target_agent_available(target_agent):
|
|
322
|
-
return target_agent, task_desc
|
|
323
|
-
|
|
324
|
-
return None
|
|
325
|
-
|
|
326
|
-
def _is_target_agent_available(self, target_agent):
|
|
327
|
-
"""检查目标智能体是否可用"""
|
|
328
|
-
try:
|
|
329
|
-
import platform
|
|
330
|
-
if platform.system().lower() == "windows":
|
|
331
|
-
result = subprocess.run(["where", target_agent],
|
|
332
|
-
capture_output=True, text=True, timeout=5)
|
|
333
|
-
else:
|
|
334
|
-
result = subprocess.run(["which", target_agent],
|
|
335
|
-
capture_output=True, text=True, timeout=5)
|
|
336
|
-
return result.returncode == 0
|
|
337
|
-
except:
|
|
338
|
-
return False
|
|
339
|
-
|
|
340
|
-
def _execute_collaboration_in_new_window(self, target_agent, task_desc):
|
|
341
|
-
"""在新窗口中执行协作任务,保持当前项目路径"""
|
|
342
|
-
import platform
|
|
343
|
-
import shutil
|
|
344
|
-
current_dir = os.getcwd()
|
|
345
|
-
|
|
346
|
-
# 记录协作任务到项目背景
|
|
347
|
-
task_id = self.project_context.create_task(
|
|
348
|
-
"collaboration",
|
|
349
|
-
f"'{task_desc}' (由 {self.agent_name} 委派给 {target_agent})",
|
|
350
|
-
assigned_to=target_agent,
|
|
351
|
-
priority="medium"
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
try:
|
|
355
|
-
system = platform.system().lower()
|
|
356
|
-
if system == "windows":
|
|
357
|
-
# 在新命令提示符窗口中执行目标智能体(保持当前目录)
|
|
358
|
-
import subprocess
|
|
359
|
-
escaped_desc = task_desc.replace('"', "'") # 避免引号冲突
|
|
360
|
-
cmd = f'start cmd /k "cd /d {current_dir} && {target_agent} {escaped_desc}"'
|
|
361
|
-
subprocess.run(cmd, shell=True)
|
|
362
|
-
elif system == "darwin": # macOS
|
|
363
|
-
# 在新终端窗口中执行目标智能体
|
|
364
|
-
import tempfile
|
|
365
|
-
with tempfile.NamedTemporaryFile(mode='w', suffix='.scpt', delete=False) as f:
|
|
366
|
-
script_content = f'''
|
|
367
|
-
tell application "Terminal"
|
|
368
|
-
do script "cd {current_dir} && {target_agent} '{task_desc}'"
|
|
369
|
-
activate
|
|
370
|
-
end tell
|
|
371
|
-
'''
|
|
372
|
-
f.write(script_content)
|
|
373
|
-
script_path = f.name
|
|
374
|
-
|
|
375
|
-
subprocess.run(['osascript', script_path])
|
|
376
|
-
os.unlink(script_path)
|
|
377
|
-
else: # Linux
|
|
378
|
-
# 尝试多种终端
|
|
379
|
-
terminals = ['gnome-terminal', 'konsole', 'xfce4-terminal', 'xterm']
|
|
380
|
-
|
|
381
|
-
launched = False
|
|
382
|
-
for terminal in terminals:
|
|
383
|
-
if shutil.which(terminal):
|
|
384
|
-
try:
|
|
385
|
-
if terminal == 'gnome-terminal':
|
|
386
|
-
subprocess.run([terminal, '--', 'bash', '-c', f'cd "{current_dir}" && {target_agent} "{task_desc}"; exec bash'], detach=True)
|
|
387
|
-
elif terminal == 'konsole':
|
|
388
|
-
subprocess.run([terminal, '-e', 'bash', '-c', f'cd "{current_dir}" && {target_agent} "{task_desc}"; exec bash'], detach=True)
|
|
389
|
-
elif terminal == 'xfce4-terminal':
|
|
390
|
-
subprocess.run([terminal, '-e', f'bash -c "cd \\"{current_dir}\\" && {target_agent} \\"{task_desc}\\"; exec bash"'], detach=True)
|
|
391
|
-
else: # xterm
|
|
392
|
-
subprocess.run([terminal, '-e', f'bash -c "cd \\"{current_dir}\\" && {target_agent} \\"{task_desc}\\"; exec bash"'], detach=True)
|
|
393
|
-
launched = True
|
|
394
|
-
print(f"🔄 已在新窗口启动 {target_agent} 处理: {task_desc}")
|
|
395
|
-
break
|
|
396
|
-
except:
|
|
397
|
-
continue
|
|
398
|
-
|
|
399
|
-
if not launched:
|
|
400
|
-
# 如果没有找到图形终端,直接在后台执行
|
|
401
|
-
result = subprocess.run([target_agent, task_desc],
|
|
402
|
-
capture_output=True,
|
|
403
|
-
text=True,
|
|
404
|
-
encoding='utf-8',
|
|
405
|
-
cwd=current_dir)
|
|
406
|
-
return result.returncode == 0
|
|
407
|
-
|
|
408
|
-
print(f"🔄 已在新窗口启动 {target_agent} 处理: {task_desc}")
|
|
409
|
-
print(f"📁 当前工作目录: {current_dir}")
|
|
410
|
-
print(f"📋 任务已记录到协作背景: {task_id}")
|
|
411
|
-
return True
|
|
412
|
-
except Exception as e:
|
|
413
|
-
print(f"❌ 在新窗口启动 {target_agent} 失败: {e}")
|
|
414
|
-
# 备用执行方式 - 直接执行(但不保持新窗口)
|
|
415
|
-
try:
|
|
416
|
-
result = subprocess.run([target_agent, task_desc],
|
|
417
|
-
capture_output=True,
|
|
418
|
-
text=True,
|
|
419
|
-
encoding='utf-8',
|
|
420
|
-
cwd=current_dir)
|
|
421
|
-
return result.returncode == 0
|
|
422
|
-
except Exception as e2:
|
|
423
|
-
print(f" 备用执行也失败: {e2}")
|
|
424
|
-
return False
|
|
425
|
-
|
|
426
|
-
def work_on_context(self):
|
|
427
|
-
"""基于背景工作 - Stigmergy的核心实现"""
|
|
428
|
-
# 分析当前背景以决定下一步行动
|
|
429
|
-
task_id, task = self.project_context.analyze_context_for_next_action(self.agent_name)
|
|
430
|
-
|
|
431
|
-
if not task_id:
|
|
432
|
-
# 没有直接分配的任务,但可以检查是否有可执行的后续操作
|
|
433
|
-
# 比如检查生成的文件、优化现有代码等
|
|
434
|
-
return self._check_for_opportunistic_work()
|
|
435
|
-
|
|
436
|
-
print(f"智能体 {self.agent_name} 找到任务 {task_id},开始处理...")
|
|
437
|
-
|
|
438
|
-
# 为了确保状态同步,在每次操作后立即保存
|
|
439
|
-
# 更新任务状态为进行中
|
|
440
|
-
self.project_context.update_task_status(task_id, "in_progress", completed_by=self.agent_name)
|
|
441
|
-
self.project_context.add_collaboration_log(
|
|
442
|
-
f"开始处理任务 {task_id}: {task['description']}",
|
|
443
|
-
self.agent_name
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
# 立即保存状态以确保其他组件可以访问最新状态
|
|
447
|
-
self.project_context._save_context()
|
|
448
|
-
|
|
449
|
-
# 执行任务
|
|
450
|
-
success, stdout, stderr = self._execute_task(task)
|
|
451
|
-
|
|
452
|
-
print(f"任务 {task_id} 执行结果: success={success}")
|
|
453
|
-
|
|
454
|
-
if success:
|
|
455
|
-
# 任务成功完成
|
|
456
|
-
self.project_context.update_task_status(task_id, "completed", stdout, self.agent_name)
|
|
457
|
-
self.project_context.add_collaboration_log(
|
|
458
|
-
f"完成任务 {task_id}", self.agent_name
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
# 检查结果,决定是否创建后续任务
|
|
462
|
-
self._analyze_result_and_create_followup_tasks(task_id, task, stdout)
|
|
463
|
-
|
|
464
|
-
# 再次保存以确保完成状态被持久化
|
|
465
|
-
self.project_context._save_context()
|
|
466
|
-
|
|
467
|
-
print(f"任务 {task_id} 成功完成")
|
|
468
|
-
return True
|
|
469
|
-
else:
|
|
470
|
-
# 任务执行失败
|
|
471
|
-
self.project_context.update_task_status(task_id, "failed", stderr, self.agent_name)
|
|
472
|
-
self.project_context.add_collaboration_log(
|
|
473
|
-
f"任务 {task_id} 执行失败: {stderr}", self.agent_name
|
|
474
|
-
)
|
|
475
|
-
|
|
476
|
-
# 立即保存失败状态
|
|
477
|
-
self.project_context._save_context()
|
|
478
|
-
|
|
479
|
-
# 尝试将任务委派给其他智能体
|
|
480
|
-
self._try_delegate_failed_task(task_id, task, stderr)
|
|
481
|
-
print(f"任务 {task_id} 执行失败")
|
|
482
|
-
return False
|
|
483
|
-
|
|
484
|
-
def _execute_task(self, task):
|
|
485
|
-
"""执行具体的任务"""
|
|
486
|
-
print(f"智能体 {self.agent_name} 执行任务: {task['type']} - {task['description'][:50]}...")
|
|
487
|
-
|
|
488
|
-
# 根据任务类型执行相应操作
|
|
489
|
-
try:
|
|
490
|
-
if task["type"] == "command_execution":
|
|
491
|
-
# 直接执行命令
|
|
492
|
-
args = task["description"].split()
|
|
493
|
-
result = self.execute_command(args)
|
|
494
|
-
elif task["type"] == "code_generation":
|
|
495
|
-
# 代码生成任务
|
|
496
|
-
prompt = f"生成代码: {task['description']}"
|
|
497
|
-
result = self.execute_command([prompt])
|
|
498
|
-
elif task["type"] == "review":
|
|
499
|
-
# 审查任务
|
|
500
|
-
prompt = f"审查并优化: {task['description']}"
|
|
501
|
-
result = self.execute_command([prompt])
|
|
502
|
-
elif task["type"] == "documentation":
|
|
503
|
-
# 文档任务
|
|
504
|
-
prompt = f"生成文档: {task['description']}"
|
|
505
|
-
result = self.execute_command([prompt])
|
|
506
|
-
elif task["type"] == "collaboration": # 新增:协作任务类型
|
|
507
|
-
# 这是协作任务,需要解析并调用目标工具
|
|
508
|
-
target_tool = task.get("target_tool", task.get("initiating_tool"))
|
|
509
|
-
|
|
510
|
-
# 检查目标工具是否可用
|
|
511
|
-
if not self._is_tool_available(target_tool):
|
|
512
|
-
return False, "", f"目标工具 {target_tool} 不可用"
|
|
513
|
-
|
|
514
|
-
# 使用目标工具执行协作任务
|
|
515
|
-
prompt = task["description"]
|
|
516
|
-
result = self.execute_command_target(target_tool, [prompt])
|
|
517
|
-
|
|
518
|
-
else:
|
|
519
|
-
# 默认使用描述作为命令
|
|
520
|
-
prompt = task["description"]
|
|
521
|
-
result = self.execute_command([prompt])
|
|
522
|
-
|
|
523
|
-
print(f"任务执行结果: {result[0]}")
|
|
524
|
-
return result
|
|
525
|
-
except Exception as e:
|
|
526
|
-
print(f"任务执行异常: {e}")
|
|
527
|
-
# 返回一个类似执行失败的结果
|
|
528
|
-
return False, "", f"任务执行异常: {str(e)}"
|
|
529
|
-
|
|
530
|
-
def _is_tool_available(self, tool_name):
|
|
531
|
-
"""检查目标工具是否可用"""
|
|
532
|
-
try:
|
|
533
|
-
if self.system == "windows":
|
|
534
|
-
result = subprocess.run(["where", tool_name],
|
|
535
|
-
capture_output=True, text=True, timeout=5)
|
|
536
|
-
else:
|
|
537
|
-
result = subprocess.run(["which", tool_name],
|
|
538
|
-
capture_output=True, text=True, timeout=5)
|
|
539
|
-
return result.returncode == 0
|
|
540
|
-
except:
|
|
541
|
-
return False
|
|
542
|
-
|
|
543
|
-
def execute_command_target(self, target_tool, args):
|
|
544
|
-
"""执行指定目标工具的命令"""
|
|
545
|
-
try:
|
|
546
|
-
if self.system == "windows":
|
|
547
|
-
cmd = [target_tool] + args if args else [target_tool]
|
|
548
|
-
else:
|
|
549
|
-
cmd = [target_tool] + args if args else [target_tool]
|
|
550
|
-
|
|
551
|
-
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8', timeout=60)
|
|
552
|
-
return result.returncode == 0, result.stdout, result.stderr
|
|
553
|
-
except Exception as e:
|
|
554
|
-
return False, "", f"执行目标工具 {target_tool} 失败: {str(e)}"
|
|
555
|
-
|
|
556
|
-
def _analyze_result_and_create_followup_tasks(self, task_id, task, result):
|
|
557
|
-
"""分析结果并创建后续任务"""
|
|
558
|
-
# 检查结果是否需要进一步处理
|
|
559
|
-
if "code" in task.get("description", "").lower() and len(result) > 100:
|
|
560
|
-
# 生成的代码可能需要测试
|
|
561
|
-
test_task_id = self.project_context.create_task(
|
|
562
|
-
"testing",
|
|
563
|
-
f"为代码生成单元测试: {result[:300]}...",
|
|
564
|
-
assigned_to="codebuddy", # 代码智能体
|
|
565
|
-
priority="medium"
|
|
566
|
-
)
|
|
567
|
-
self.project_context.add_collaboration_log(
|
|
568
|
-
f"创建后续测试任务 {test_task_id} 基于任务 {task_id} 的结果",
|
|
569
|
-
self.agent_name
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
-
if "analysis" in task.get("type", "").lower():
|
|
573
|
-
# 分析结果可能需要可视化或其他处理
|
|
574
|
-
vis_task_id = self.project_context.create_task(
|
|
575
|
-
"visualization",
|
|
576
|
-
f"将分析结果可视化: {result[:200]}...",
|
|
577
|
-
assigned_to=None, # 任何合适的智能体
|
|
578
|
-
priority="low"
|
|
579
|
-
)
|
|
580
|
-
self.project_context.add_collaboration_log(
|
|
581
|
-
f"创建可视化任务 {vis_task_id}",
|
|
582
|
-
self.agent_name
|
|
583
|
-
)
|
|
584
|
-
|
|
585
|
-
def _try_delegate_failed_task(self, task_id, task, error_result):
|
|
586
|
-
"""尝试将失败的任务委派给其他智能体"""
|
|
587
|
-
# 分析失败原因,找到更适合的智能体
|
|
588
|
-
suggested_agent = self._analyze_error_and_suggest_agent(error_result)
|
|
589
|
-
|
|
590
|
-
if suggested_agent and suggested_agent != self.agent_name:
|
|
591
|
-
# 更新任务分配
|
|
592
|
-
self.project_context.data["tasks"][task_id]["assigned_to"] = suggested_agent
|
|
593
|
-
self.project_context.data["tasks"][task_id]["status"] = "pending" # 重新变为待处理
|
|
594
|
-
self.project_context._save_context()
|
|
595
|
-
|
|
596
|
-
self.project_context.add_collaboration_log(
|
|
597
|
-
f"将任务 {task_id} 委派给 {suggested_agent}",
|
|
598
|
-
self.agent_name
|
|
599
|
-
)
|
|
600
|
-
|
|
601
|
-
def _analyze_error_and_suggest_agent(self, error_result):
|
|
602
|
-
"""分析错误结果并建议更适合的智能体"""
|
|
603
|
-
error_lower = error_result.lower()
|
|
604
|
-
|
|
605
|
-
# 基于错误内容建议智能体
|
|
606
|
-
if any(keyword in error_lower for keyword in ["translate", "language", "text"]):
|
|
607
|
-
return "gemini" # Gemini在语言处理方面可能更好
|
|
608
|
-
elif any(keyword in error_lower for keyword in ["code", "function", "program"]):
|
|
609
|
-
return "codebuddy" # CodeBuddy在代码方面可能更好
|
|
610
|
-
elif any(keyword in error_lower for keyword in ["analysis", "data", "math"]):
|
|
611
|
-
return "claude" # Claude在逻辑分析方面可能更好
|
|
612
|
-
elif any(keyword in error_lower for keyword in ["chinese", "中文"]):
|
|
613
|
-
return "qwen" # Qwen在中文处理方面可能更好
|
|
614
|
-
|
|
615
|
-
return None
|
|
616
|
-
|
|
617
|
-
def _check_for_opportunistic_work(self):
|
|
618
|
-
"""检查机会性工作 - 智能体根据背景主动寻找可执行的任务"""
|
|
619
|
-
# 检查是否有需要优化的文件
|
|
620
|
-
for file_path in self.project_context.project_path.glob("*.py"):
|
|
621
|
-
if file_path.name != "__pycache__":
|
|
622
|
-
# 检查是否有Python文件需要审查或优化
|
|
623
|
-
with open(file_path, 'r', encoding='utf-8') as f:
|
|
624
|
-
content = f.read()
|
|
625
|
-
|
|
626
|
-
if len(content) > 200: # 文件较大,可能需要审查
|
|
627
|
-
task_id = self.project_context.create_task(
|
|
628
|
-
"code_review",
|
|
629
|
-
f"审查代码文件 {file_path.name}: {content[:500]}...",
|
|
630
|
-
assigned_to=self.agent_name,
|
|
631
|
-
priority="low"
|
|
632
|
-
)
|
|
633
|
-
self.project_context.add_collaboration_log(
|
|
634
|
-
f"发现机会性任务: 审查文件 {file_path.name}",
|
|
635
|
-
self.agent_name
|
|
636
|
-
)
|
|
637
|
-
return self.work_on_context() # 递归调用处理新任务
|
|
638
|
-
|
|
639
|
-
# 没有发现机会性工作
|
|
640
|
-
return False
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
def main():
|
|
644
|
-
"""主函数 - CLI协作智能体的入口"""
|
|
645
|
-
if len(sys.argv) < 2:
|
|
646
|
-
print("用法: python cli_collaboration_agent.py <agent_name> [options]")
|
|
647
|
-
print("例如: python cli_collaboration_agent.py claude --work")
|
|
648
|
-
return
|
|
649
|
-
|
|
650
|
-
agent_name = sys.argv[1]
|
|
651
|
-
agent = CLICollaborationAgent(agent_name)
|
|
652
|
-
|
|
653
|
-
# 处理命令行参数
|
|
654
|
-
if "--work" in sys.argv or "--work-on-context" in sys.argv:
|
|
655
|
-
success = agent.work_on_context()
|
|
656
|
-
sys.exit(0 if success else 1)
|
|
657
|
-
elif len(sys.argv) > 2:
|
|
658
|
-
# 直接执行命令,但同时更新项目背景
|
|
659
|
-
command_args = sys.argv[2:]
|
|
660
|
-
full_command = " ".join(command_args)
|
|
661
|
-
|
|
662
|
-
# 创建一个任务记录
|
|
663
|
-
task_id = agent.project_context.create_task(
|
|
664
|
-
"command_execution",
|
|
665
|
-
full_command,
|
|
666
|
-
assigned_to=agent_name,
|
|
667
|
-
priority="normal"
|
|
668
|
-
)
|
|
669
|
-
|
|
670
|
-
# 更新任务为进行中
|
|
671
|
-
agent.project_context.update_task_status(task_id, "in_progress", completed_by=agent_name)
|
|
672
|
-
|
|
673
|
-
# 执行命令
|
|
674
|
-
success, stdout, stderr = agent.execute_command(command_args)
|
|
675
|
-
|
|
676
|
-
# 更新任务状态
|
|
677
|
-
if success:
|
|
678
|
-
agent.project_context.update_task_status(task_id, "completed", stdout, agent_name)
|
|
679
|
-
print(stdout)
|
|
680
|
-
|
|
681
|
-
# 分析结果并可能创建后续任务
|
|
682
|
-
agent._analyze_result_and_create_followup_tasks(task_id,
|
|
683
|
-
agent.project_context.data["tasks"][task_id], stdout)
|
|
684
|
-
else:
|
|
685
|
-
agent.project_context.update_task_status(task_id, "failed", stderr, agent_name)
|
|
686
|
-
print(stderr, file=sys.stderr)
|
|
687
|
-
|
|
688
|
-
sys.exit(0 if success else 1)
|
|
689
|
-
else:
|
|
690
|
-
# 默认行为:尝试在背景中工作
|
|
691
|
-
print(f"智能体 {agent_name} 开始在项目背景中工作...")
|
|
692
|
-
success = agent.work_on_context()
|
|
693
|
-
if not success:
|
|
694
|
-
print(f"智能体 {agent_name} 在当前背景中没有找到可执行的任务")
|
|
695
|
-
|
|
696
|
-
if __name__ == "__main__":
|
|
697
|
-
main()
|