opencode-collaboration 0.2.0__tar.gz → 0.2.1__tar.gz

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 (29) hide show
  1. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/PKG-INFO +1 -1
  2. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/opencode_collaboration.egg-info/PKG-INFO +1 -1
  3. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/opencode_collaboration.egg-info/SOURCES.txt +6 -0
  4. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/pyproject.toml +2 -2
  5. opencode_collaboration-0.2.1/src/cli/__init__.py +0 -0
  6. opencode_collaboration-0.2.1/src/cli/agent.py +285 -0
  7. opencode_collaboration-0.2.1/src/cli/main.py +1106 -0
  8. opencode_collaboration-0.2.1/tests/test_agent_daemon_complete.py +555 -0
  9. opencode_collaboration-0.2.1/tests/test_agent_daemon_long_running.py +252 -0
  10. opencode_collaboration-0.2.1/tests/test_agent_daemon_true_long_running.py +266 -0
  11. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/README.md +0 -0
  12. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/opencode_collaboration.egg-info/dependency_links.txt +0 -0
  13. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/opencode_collaboration.egg-info/entry_points.txt +0 -0
  14. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/opencode_collaboration.egg-info/requires.txt +0 -0
  15. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/opencode_collaboration.egg-info/top_level.txt +0 -0
  16. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/setup.cfg +0 -0
  17. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/src/__init__.py +0 -0
  18. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/src/main.py +0 -0
  19. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_agent_behavior.py +0 -0
  20. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_agent_daemon.py +0 -0
  21. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_detector.py +0 -0
  22. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_doc_generator.py +0 -0
  23. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_e2e.py +0 -0
  24. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_exception_handler.py +0 -0
  25. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_git_monitor.py +0 -0
  26. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_state_machine.py +0 -0
  27. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_state_manager.py +0 -0
  28. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_state_manager_v2.py +0 -0
  29. {opencode_collaboration-0.2.0 → opencode_collaboration-0.2.1}/tests/test_workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencode-collaboration
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: 双Agent协作框架 - 产品经理与开发的分离式协作工具
5
5
  Author-email: OpenCode <dev@opencode.ai>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencode-collaboration
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: 双Agent协作框架 - 产品经理与开发的分离式协作工具
5
5
  Author-email: OpenCode <dev@opencode.ai>
6
6
  License: MIT
@@ -8,8 +8,14 @@ opencode_collaboration.egg-info/requires.txt
8
8
  opencode_collaboration.egg-info/top_level.txt
9
9
  src/__init__.py
10
10
  src/main.py
11
+ src/cli/__init__.py
12
+ src/cli/agent.py
13
+ src/cli/main.py
11
14
  tests/test_agent_behavior.py
12
15
  tests/test_agent_daemon.py
16
+ tests/test_agent_daemon_complete.py
17
+ tests/test_agent_daemon_long_running.py
18
+ tests/test_agent_daemon_true_long_running.py
13
19
  tests/test_detector.py
14
20
  tests/test_doc_generator.py
15
21
  tests/test_e2e.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "opencode-collaboration"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "双Agent协作框架 - 产品经理与开发的分离式协作工具"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -45,7 +45,7 @@ oc-collab = "src.cli.main:main"
45
45
 
46
46
  [tool.setuptools.packages.find]
47
47
  where = ["."]
48
- include = ["src"]
48
+ include = ["src", "src.cli"]
49
49
 
50
50
 
51
51
  [tool.pytest.ini_options]
File without changes
@@ -0,0 +1,285 @@
1
+ """Agent主模块。"""
2
+ import signal
3
+ import sys
4
+ import threading
5
+ import time
6
+ import logging
7
+ from pathlib import Path
8
+ from typing import Any, Dict, List, Optional, Callable
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum
11
+ from datetime import datetime
12
+
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class AgentMode(Enum):
18
+ """Agent运行模式。"""
19
+ MANUAL = "manual"
20
+ AUTO = "auto"
21
+
22
+
23
+ class AgentStatus(Enum):
24
+ """Agent状态枚举。"""
25
+ IDLE = "idle"
26
+ RUNNING = "running"
27
+ PAUSED = "paused"
28
+ STOPPED = "stopped"
29
+ ERROR = "error"
30
+
31
+
32
+ @dataclass
33
+ class AgentConfig:
34
+ """Agent配置。"""
35
+ agent_id: str = "agent1"
36
+ agent_type: str = "产品经理"
37
+ mode: AgentMode = AgentMode.AUTO
38
+ polling_interval: int = 30
39
+ max_retries: int = 3
40
+ auto_retry: bool = True
41
+ enable_webhook: bool = False
42
+
43
+
44
+ class Agent:
45
+ """Agent主类。"""
46
+
47
+ def __init__(self, config: Optional[AgentConfig] = None):
48
+ """初始化Agent。"""
49
+ self.config = config or AgentConfig()
50
+ self.state_machine = None
51
+ self.brain_engine = None
52
+ self.task_executor = None
53
+ self.git_monitor = None
54
+
55
+ self.status = AgentStatus.IDLE
56
+ self.current_phase: str = "project_init"
57
+ self._running = False
58
+ self._paused = False
59
+ self._main_thread: Optional[threading.Thread] = None
60
+ self._lock = threading.Lock()
61
+
62
+ self._event_handlers: Dict[str, List[Callable]] = {}
63
+
64
+ signal.signal(signal.SIGINT, self._handle_shutdown)
65
+ signal.signal(signal.SIGTERM, self._handle_shutdown)
66
+
67
+ def initialize(self, project_path: str, state_manager=None) -> None:
68
+ """初始化Agent组件。"""
69
+ from ..core.state_machine import StateMachine, State
70
+ from ..core.brain_engine import BrainEngine
71
+ from ..core.task_executor import TaskExecutor
72
+ from ..core.git_monitor import GitMonitor, GitConfig
73
+
74
+ self.state_machine = StateMachine(State.PROJECT_INIT)
75
+ self.brain_engine = BrainEngine()
76
+ self.task_executor = TaskExecutor()
77
+
78
+ try:
79
+ git_config = GitConfig(polling_interval=self.config.polling_interval)
80
+ self.git_monitor = GitMonitor(project_path, git_config)
81
+ except Exception as e:
82
+ logger.warning(f"Git监控器初始化失败: {e}")
83
+ self.git_monitor = None
84
+
85
+ self.current_phase = "project_init"
86
+ logger.info(f"Agent {self.config.agent_id} 初始化完成")
87
+
88
+ def start(self) -> None:
89
+ """启动Agent。"""
90
+ if self.status == AgentStatus.RUNNING:
91
+ logger.warning("Agent已在运行中")
92
+ return
93
+
94
+ with self._lock:
95
+ self._running = True
96
+ self.status = AgentStatus.RUNNING
97
+
98
+ self._main_thread = threading.Thread(target=self._run_loop, daemon=True)
99
+ self._main_thread.start()
100
+
101
+ logger.info(f"Agent {self.config.agent_id} 已启动")
102
+
103
+ def stop(self) -> None:
104
+ """停止Agent。"""
105
+ with self._lock:
106
+ if not self._running:
107
+ return
108
+ self._running = False
109
+
110
+ if self.git_monitor:
111
+ self.git_monitor.stop_monitoring()
112
+
113
+ if self._main_thread:
114
+ self._main_thread.join(timeout=5)
115
+
116
+ self.status = AgentStatus.STOPPED
117
+ logger.info(f"Agent {self.config.agent_id} 已停止")
118
+
119
+ def pause(self) -> None:
120
+ """暂停Agent。"""
121
+ with self._lock:
122
+ self._paused = True
123
+ self.status = AgentStatus.PAUSED
124
+
125
+ if self.git_monitor:
126
+ self.git_monitor.stop_monitoring()
127
+
128
+ logger.info(f"Agent {self.config.agent_id} 已暂停")
129
+
130
+ def resume(self) -> None:
131
+ """恢复Agent。"""
132
+ with self._lock:
133
+ self._paused = False
134
+ self.status = AgentStatus.RUNNING
135
+
136
+ if self.git_monitor:
137
+ self.git_monitor.start_monitoring()
138
+
139
+ logger.info(f"Agent {self.config.agent_id} 已恢复")
140
+
141
+ def _run_loop(self) -> None:
142
+ """主循环。"""
143
+ while self._running:
144
+ try:
145
+ if self._paused:
146
+ time.sleep(1)
147
+ continue
148
+
149
+ self._execute_cycle()
150
+
151
+ time.sleep(1)
152
+ except Exception as e:
153
+ logger.error(f"执行循环错误: {e}")
154
+ self.status = AgentStatus.ERROR
155
+ self._notify_event("error", {"error": str(e)})
156
+
157
+ def _execute_cycle(self) -> None:
158
+ """执行一个工作周期。"""
159
+ phase = self.state_machine.current_state.value if self.state_machine else self.current_phase
160
+
161
+ context = {
162
+ "agent_id": self.config.agent_id,
163
+ "agent_type": self.config.agent_type,
164
+ "phase": phase,
165
+ "project_path": getattr(self, '_project_path', '.')
166
+ }
167
+
168
+ action, rule = self.brain_engine.get_action(
169
+ agent_type=self.config.agent_type.lower().replace(" ", "_"),
170
+ phase=phase
171
+ )
172
+
173
+ if action and action.value != "wait":
174
+ self._execute_action(action, context)
175
+
176
+ self._notify_event("cycle_complete", {"phase": phase, "action": action.value if action else None})
177
+
178
+ def _execute_action(self, action, context: Dict[str, Any]) -> None:
179
+ """执行动作。"""
180
+ logger.info(f"Agent {self.config.agent_id} 执行动作: {action.value}")
181
+
182
+ result = self.task_executor.execute_action(action.value, context)
183
+
184
+ if result.success:
185
+ logger.info(f"动作执行成功: {result.message}")
186
+ self._notify_event("action_success", {"action": action.value, "result": result})
187
+ else:
188
+ logger.error(f"动作执行失败: {result.message}")
189
+ self._notify_event("action_failed", {"action": action.value, "result": result})
190
+
191
+ def _handle_shutdown(self, signum, frame) -> None:
192
+ """处理关闭信号。"""
193
+ logger.info(f"收到关闭信号: {signum}")
194
+ self.stop()
195
+ sys.exit(0)
196
+
197
+ def on_event(self, event_type: str, handler: Callable) -> None:
198
+ """注册事件处理器。"""
199
+ if event_type not in self._event_handlers:
200
+ self._event_handlers[event_type] = []
201
+ self._event_handlers[event_type].append(handler)
202
+
203
+ def _notify_event(self, event_type: str, data: Dict[str, Any]) -> None:
204
+ """通知事件。"""
205
+ handlers = self._event_handlers.get(event_type, [])
206
+ for handler in handlers:
207
+ try:
208
+ handler(data)
209
+ except Exception as e:
210
+ logger.error(f"事件处理器执行失败: {e}")
211
+
212
+ def get_status(self) -> Dict[str, Any]:
213
+ """获取Agent状态。"""
214
+ phase = self.state_machine.current_state.value if self.state_machine else self.current_phase
215
+
216
+ return {
217
+ "agent_id": self.config.agent_id,
218
+ "agent_type": self.config.agent_type,
219
+ "status": self.status.value,
220
+ "phase": phase,
221
+ "mode": self.config.mode.value,
222
+ "running": self._running,
223
+ "paused": self._paused
224
+ }
225
+
226
+ def get_summary(self) -> Dict[str, Any]:
227
+ """获取Agent摘要。"""
228
+ return {
229
+ "config": {
230
+ "agent_id": self.config.agent_id,
231
+ "agent_type": self.config.agent_type,
232
+ "mode": self.config.mode.value,
233
+ "polling_interval": self.config.polling_interval
234
+ },
235
+ "status": self.get_status(),
236
+ "brain_engine": self.brain_engine.get_summary() if self.brain_engine else None,
237
+ "task_executor": self.task_executor.get_summary() if self.task_executor else None,
238
+ "git_monitor": self.git_monitor.get_status_summary() if self.git_monitor else None
239
+ }
240
+
241
+ def switch_mode(self, mode: AgentMode) -> None:
242
+ """切换运行模式。"""
243
+ self.config.mode = mode
244
+ logger.info(f"Agent {self.config.agent_id} 模式切换为: {mode.value}")
245
+
246
+ def manual_action(self, action_name: str, params: Optional[Dict] = None) -> Dict[str, Any]:
247
+ """手动执行动作。"""
248
+ if self.config.mode != AgentMode.MANUAL:
249
+ return {
250
+ "success": False,
251
+ "message": "当前不是手动模式,请先切换到手动模式"
252
+ }
253
+
254
+ context = {
255
+ "agent_id": self.config.agent_id,
256
+ "agent_type": self.config.agent_type,
257
+ "phase": self.state_machine.current_state.value if self.state_machine else self.current_phase,
258
+ "params": params or {}
259
+ }
260
+
261
+ result = self.task_executor.execute_action(action_name, context)
262
+
263
+ return {
264
+ "success": result.success,
265
+ "message": result.message,
266
+ "duration": result.duration,
267
+ "files_created": result.files_created
268
+ }
269
+
270
+ def trigger_action(self, action_name: str, params: Optional[Dict] = None) -> Dict[str, Any]:
271
+ """触发动作(自动模式下可用)。"""
272
+ context = {
273
+ "agent_id": self.config.agent_id,
274
+ "agent_type": self.config.agent_type,
275
+ "phase": self.state_machine.current_state.value if self.state_machine else self.current_phase,
276
+ "params": params or {}
277
+ }
278
+
279
+ result = self.task_executor.execute_action(action_name, context)
280
+
281
+ return {
282
+ "success": result.success,
283
+ "message": result.message,
284
+ "duration": result.duration
285
+ }