opencode-collaboration 2.2.0.post2__tar.gz → 2.2.0.post3__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 (97) hide show
  1. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/PKG-INFO +1 -1
  2. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/opencode_collaboration.egg-info/PKG-INFO +1 -1
  3. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/opencode_collaboration.egg-info/SOURCES.txt +2 -0
  4. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/pyproject.toml +1 -1
  5. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/cli/main.py +75 -11
  6. opencode_collaboration-2.2.0.post3/src/core/session_manager.py +213 -0
  7. opencode_collaboration-2.2.0.post3/tests/test_session_manager.py +239 -0
  8. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/README.md +0 -0
  9. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/opencode_collaboration.egg-info/dependency_links.txt +0 -0
  10. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/opencode_collaboration.egg-info/entry_points.txt +0 -0
  11. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/opencode_collaboration.egg-info/requires.txt +0 -0
  12. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/opencode_collaboration.egg-info/top_level.txt +0 -0
  13. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/setup.cfg +0 -0
  14. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/__init__.py +0 -0
  15. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/cli/__init__.py +0 -0
  16. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/cli/agent.py +0 -0
  17. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/__init__.py +0 -0
  18. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/agent_manager.py +0 -0
  19. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/auto_doc_git.py +0 -0
  20. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/auto_docs.py +0 -0
  21. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/auto_engine.py +0 -0
  22. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/auto_git_sync.py +0 -0
  23. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/auto_retry.py +0 -0
  24. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/brain_engine.py +0 -0
  25. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/config_reloader.py +0 -0
  26. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/daemon.py +0 -0
  27. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/design_review_notifier.py +0 -0
  28. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/detector.py +0 -0
  29. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/doc_generator.py +0 -0
  30. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/error_templates.py +0 -0
  31. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/exception_handler.py +0 -0
  32. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/git.py +0 -0
  33. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/git_monitor.py +0 -0
  34. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/git_workflow_enforcer.py +0 -0
  35. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/iteration_status_manager.py +0 -0
  36. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/meeting_manager.py +0 -0
  37. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/monitor.py +0 -0
  38. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/phase_advance.py +0 -0
  39. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/project_manager.py +0 -0
  40. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/resource_lock.py +0 -0
  41. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/signoff.py +0 -0
  42. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/state_machine.py +0 -0
  43. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/state_manager.py +0 -0
  44. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/state_migrator.py +0 -0
  45. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/state_validator.py +0 -0
  46. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/story_manager.py +0 -0
  47. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/supervisor.py +0 -0
  48. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/task_executor.py +0 -0
  49. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/core/workflow.py +0 -0
  50. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/main.py +0 -0
  51. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/utils/__init__.py +0 -0
  52. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/utils/date.py +0 -0
  53. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/utils/file.py +0 -0
  54. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/utils/lock.py +0 -0
  55. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/src/utils/yaml.py +0 -0
  56. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_agent_behavior.py +0 -0
  57. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_agent_daemon.py +0 -0
  58. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_agent_daemon_complete.py +0 -0
  59. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_agent_daemon_long_running.py +0 -0
  60. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_agent_daemon_true_long_running.py +0 -0
  61. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_agent_manager.py +0 -0
  62. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_auto_git_sync.py +0 -0
  63. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_auto_retry.py +0 -0
  64. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_blackbox_v210.py +0 -0
  65. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_brain_engine.py +0 -0
  66. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_config_reloader.py +0 -0
  67. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_daemon.py +0 -0
  68. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_daemon_integration.py +0 -0
  69. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_design_review_notifier.py +0 -0
  70. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_detector.py +0 -0
  71. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_doc_generator.py +0 -0
  72. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_e2e.py +0 -0
  73. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_e2e_v2_2_0.py +0 -0
  74. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_error_templates.py +0 -0
  75. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_exception_handler.py +0 -0
  76. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_git.py +0 -0
  77. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_git_monitor.py +0 -0
  78. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_git_workflow_enforcer.py +0 -0
  79. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_iteration_status_manager.py +0 -0
  80. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_m4_enhancements.py +0 -0
  81. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_meeting_manager.py +0 -0
  82. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_monitor.py +0 -0
  83. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_package_completeness.py +0 -0
  84. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_phase_advance.py +0 -0
  85. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_project_manager.py +0 -0
  86. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_resource_lock.py +0 -0
  87. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_signoff.py +0 -0
  88. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_state_machine.py +0 -0
  89. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_state_manager.py +0 -0
  90. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_state_manager_v2.py +0 -0
  91. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_state_migration.py +0 -0
  92. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_state_structure_compatibility.py +0 -0
  93. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_state_validator.py +0 -0
  94. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_story_manager.py +0 -0
  95. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_supervisor.py +0 -0
  96. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_task_executor.py +0 -0
  97. {opencode_collaboration-2.2.0.post2 → opencode_collaboration-2.2.0.post3}/tests/test_workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencode-collaboration
3
- Version: 2.2.0.post2
3
+ Version: 2.2.0.post3
4
4
  Summary: 双Agent协作框架 - 产品经理与开发的分离式协作工具
5
5
  Author-email: liuzhen <dev@opencode.ai>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencode-collaboration
3
- Version: 2.2.0.post2
3
+ Version: 2.2.0.post3
4
4
  Summary: 双Agent协作框架 - 产品经理与开发的分离式协作工具
5
5
  Author-email: liuzhen <dev@opencode.ai>
6
6
  License: MIT
@@ -35,6 +35,7 @@ src/core/monitor.py
35
35
  src/core/phase_advance.py
36
36
  src/core/project_manager.py
37
37
  src/core/resource_lock.py
38
+ src/core/session_manager.py
38
39
  src/core/signoff.py
39
40
  src/core/state_machine.py
40
41
  src/core/state_manager.py
@@ -80,6 +81,7 @@ tests/test_package_completeness.py
80
81
  tests/test_phase_advance.py
81
82
  tests/test_project_manager.py
82
83
  tests/test_resource_lock.py
84
+ tests/test_session_manager.py
83
85
  tests/test_signoff.py
84
86
  tests/test_state_machine.py
85
87
  tests/test_state_manager.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "opencode-collaboration"
7
- version = "2.2.0.post2"
7
+ version = "2.2.0.post3"
8
8
  description = "双Agent协作框架 - 产品经理与开发的分离式协作工具"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -17,6 +17,7 @@ from ..core.auto_engine import AutoCollaborationEngine, TodoCommandExecutor, Wor
17
17
  from ..core.auto_retry import AutoRetry, AutoRetryConfig
18
18
  from ..core.auto_docs import AutoDocs, AutoDocsConfig
19
19
  from ..core.phase_advance import PhaseAdvanceEngine
20
+ from ..core.session_manager import SessionManager
20
21
  from ..utils.lock import LockExistsError
21
22
 
22
23
 
@@ -123,6 +124,9 @@ def status_command():
123
124
  req_status = state_manager.get_signoff_status("requirements")
124
125
  console.print(f"需求签署 - 产品经理: {'✓' if req_status['pm_signoff'] else '✗'}, 开发: {'✓' if req_status['dev_signoff'] else '✗'}")
125
126
 
127
+ session_manager = SessionManager(project_path)
128
+ session_manager.show_welcome(active_agent)
129
+
126
130
  except StateFileNotFoundError:
127
131
  click.echo("错误: 未找到项目状态文件,请先初始化项目")
128
132
  sys.exit(1)
@@ -136,22 +140,33 @@ def status_command():
136
140
 
137
141
  @main.command("switch")
138
142
  @click.argument("agent_id", type=click.IntRange(1, 2))
139
- def switch_command(agent_id: int):
143
+ @click.option("--welcome/--no-welcome", "-w", default=True, help="显示欢迎信息")
144
+ def switch_command(agent_id: int, welcome: bool):
140
145
  """切换Agent角色。"""
141
146
  try:
142
147
  project_path = get_project_path()
143
148
  state_manager = StateManager(project_path)
144
-
149
+
145
150
  current_agent = state_manager.get_active_agent()
146
151
  if current_agent == f"agent{agent_id}":
147
152
  click.echo(f"已经是 Agent {agent_id}")
148
153
  return
149
-
154
+
150
155
  state_manager.set_active_agent(f"agent{agent_id}")
151
-
152
- agent_info = state_manager.load_state()["agents"][f"agent{agent_id}"]
153
- click.echo(f"已切换到 Agent {agent_id} ({agent_info['role']})")
154
-
156
+
157
+ if welcome:
158
+ session_manager = SessionManager(project_path)
159
+ session_manager.show_welcome(f"agent{agent_id}")
160
+ else:
161
+ try:
162
+ state = state_manager.load_state()
163
+ agents = state.get("agents", {})
164
+ agent_info = agents.get(f"agent{agent_id}", {})
165
+ role = agent_info.get("role", "未知")
166
+ except Exception:
167
+ role = "未知"
168
+ click.echo(f"已切换到 Agent {agent_id} ({role})")
169
+
155
170
  except Exception as e:
156
171
  click.echo(f"错误: {e}")
157
172
  sys.exit(1)
@@ -159,26 +174,75 @@ def switch_command(agent_id: int):
159
174
 
160
175
  @main.command("review")
161
176
  @click.argument("stage", type=click.Choice(["requirements", "design", "test"]))
177
+ @click.option("--file", "-f", help="指定评审文件路径")
178
+ @click.option("--checklist", "-c", is_flag=True, default=False, help="显示动态检查清单")
162
179
  @click.option("--new", is_flag=True, default=False)
163
180
  @click.option("--list", "-l", is_flag=True, default=False)
164
- def review_command(stage: str, new: bool, list: bool):
181
+ def review_command(stage: str, file: str, checklist: bool, new: bool, list: bool):
165
182
  """管理评审流程。"""
166
183
  try:
167
184
  project_path = get_project_path()
168
185
  state_manager = StateManager(project_path)
169
186
  workflow_engine = WorkflowEngine(state_manager)
170
-
187
+
188
+ if checklist:
189
+ from ..core.checklist_generator import ChecklistGenerator, CheckStatus
190
+
191
+ generator = ChecklistGenerator(project_path)
192
+
193
+ if stage == "requirements":
194
+ if file:
195
+ doc_path = file
196
+ else:
197
+ req_dir = Path(project_path) / "docs" / "01-requirements"
198
+ doc_path = str(sorted(req_dir.glob("*.md"))[-1] if req_dir.exists() else "")
199
+
200
+ if doc_path and Path(doc_path).exists():
201
+ checklist_items = generator.generate_requirements_checklist(doc_path)
202
+ rendered = generator.render_checklist(checklist_items, f"{stage.upper()} 评审 Checklist")
203
+ console.print(Panel(rendered, title="动态检查清单", style="green"))
204
+ else:
205
+ click.echo("错误: 未找到需求文档")
206
+
207
+ elif stage == "design":
208
+ if file:
209
+ doc_path = file
210
+ else:
211
+ design_dir = Path(project_path) / "docs" / "02-design"
212
+ doc_path = str(sorted(design_dir.glob("*.md"))[-1] if design_dir.exists() else "")
213
+
214
+ if doc_path and Path(doc_path).exists():
215
+ checklist_items = generator.generate_design_checklist(doc_path)
216
+ rendered = generator.render_checklist(checklist_items, f"{stage.upper()} 评审 Checklist")
217
+ console.print(Panel(rendered, title="动态检查清单", style="green"))
218
+ else:
219
+ click.echo("错误: 未找到设计文档")
220
+
221
+ elif stage == "test":
222
+ if file:
223
+ doc_path = file
224
+ else:
225
+ test_dir = Path(project_path) / "docs" / "03-test"
226
+ doc_path = str(sorted(test_dir.glob("*.md"))[-1] if test_dir.exists() else "")
227
+
228
+ if doc_path and Path(doc_path).exists():
229
+ checklist_items = generator.generate_test_checklist(doc_path)
230
+ rendered = generator.render_checklist(checklist_items, f"{stage.upper()} 评审 Checklist")
231
+ console.print(Panel(rendered, title="动态检查清单", style="green"))
232
+ else:
233
+ click.echo("错误: 未找到测试文档")
234
+
171
235
  if new:
172
236
  workflow_engine.start_review(stage)
173
237
  click.echo(f"已发起 {stage} 评审")
174
-
238
+
175
239
  if list:
176
240
  history = state_manager.get_history()
177
241
  console.print(f"\n[bold]{stage.upper()} 评审历史[/bold]")
178
242
  for item in history[:10]:
179
243
  if "review" in item["action"] or "signoff" in item["action"]:
180
244
  console.print(f"- {item['timestamp']}: Agent {item['agent']} - {item['details']}")
181
-
245
+
182
246
  except Exception as e:
183
247
  click.echo(f"错误: {e}")
184
248
  sys.exit(1)
@@ -0,0 +1,213 @@
1
+ from pathlib import Path
2
+ from typing import Optional, List
3
+ from rich.console import Console
4
+ from rich.panel import Panel
5
+ from rich.text import Text
6
+
7
+ from .state_manager import StateManager
8
+
9
+ console = Console()
10
+
11
+ AGENT_ROLES = {
12
+ "agent1": {
13
+ "role": "产品经理",
14
+ "responsibilities": [
15
+ "编写和评审需求文档",
16
+ "定义验收标准",
17
+ "签署需求确认",
18
+ "评审设计文档",
19
+ "评审测试报告"
20
+ ]
21
+ },
22
+ "agent2": {
23
+ "role": "开发负责人",
24
+ "responsibilities": [
25
+ "评审需求文档",
26
+ "编写详细设计",
27
+ "代码实现",
28
+ "编写单元测试",
29
+ "签署技术确认"
30
+ ]
31
+ }
32
+ }
33
+
34
+ COMMON_COMMANDS = [
35
+ ("oc-collab status", "查看项目状态"),
36
+ ("oc-collab todo", "查看待办事项"),
37
+ ("oc-collab review", "评审文档"),
38
+ ("oc-collab signoff", "签署确认"),
39
+ ("oc-collab history", "查看协作历史"),
40
+ ("oc-collab switch <1|2>", "切换Agent角色")
41
+ ]
42
+
43
+
44
+ class SessionConfig:
45
+ def __init__(self, project_path: str):
46
+ self.project_path = project_path
47
+ self.enabled = True
48
+ self.show_responsibilities = True
49
+ self.show_todo = True
50
+ self.show_pending = True
51
+ self.load_config()
52
+
53
+ def load_config(self):
54
+ state_file = Path(self.project_path) / "state" / "project_state.yaml"
55
+ if state_file.exists():
56
+ try:
57
+ import yaml
58
+ with open(state_file) as f:
59
+ state = yaml.safe_load(f) or {}
60
+ session_config = state.get("session_start", {})
61
+ self.enabled = session_config.get("enabled", True)
62
+ self.show_responsibilities = session_config.get("show_responsibilities", True)
63
+ self.show_todo = session_config.get("show_todo", True)
64
+ self.show_pending = session_config.get("show_pending", True)
65
+ except Exception:
66
+ pass
67
+
68
+
69
+ class SessionManager:
70
+ def __init__(self, project_path: str):
71
+ self.project_path = project_path
72
+ self.state_manager = StateManager(project_path)
73
+ self.config = SessionConfig(project_path)
74
+
75
+ def get_project_info(self) -> dict:
76
+ try:
77
+ state = self.state_manager.load_state()
78
+ metadata = state.get("metadata", {})
79
+ project_info = state.get("project", {})
80
+
81
+ return {
82
+ "name": metadata.get("project_name") or project_info.get("name", "未配置"),
83
+ "phase": project_info.get("phase") or state.get("phase", "未知"),
84
+ "milestone": state.get("current_milestone", "待定义")
85
+ }
86
+ except Exception:
87
+ return {"name": "未配置", "phase": "未知", "milestone": "待定义"}
88
+
89
+ def get_agent_info(self, agent_id: str) -> dict:
90
+ agent_config = AGENT_ROLES.get(agent_id, {
91
+ "role": "未知",
92
+ "responsibilities": []
93
+ })
94
+
95
+ try:
96
+ state = self.state_manager.load_state()
97
+ agents = state.get("agents", {})
98
+ agent_state = agents.get(agent_id, {})
99
+ except KeyError:
100
+ agent_state = {}
101
+ except Exception:
102
+ agent_state = {}
103
+
104
+ return {
105
+ "id": agent_id,
106
+ "role": agent_config["role"],
107
+ "current_task": agent_state.get("current_task", ""),
108
+ "responsibilities": agent_config["responsibilities"]
109
+ }
110
+
111
+ def get_responsibilities_text(self, agent_id: str) -> str:
112
+ agent = self.get_agent_info(agent_id)
113
+ if not self.config.show_responsibilities:
114
+ return ""
115
+
116
+ lines = ["你的职责:"]
117
+ for resp in agent["responsibilities"]:
118
+ lines.append(f" - {resp}")
119
+ return "\n".join(lines)
120
+
121
+ def get_todo_items(self) -> str:
122
+ if not self.config.show_todo:
123
+ return ""
124
+
125
+ try:
126
+ from .auto_engine import TodoCommandExecutor
127
+ executor = TodoCommandExecutor(self.project_path)
128
+ todo_list = executor.get_todo_list()
129
+
130
+ if not todo_list:
131
+ return "待办事项:\n 暂无待办事项"
132
+
133
+ lines = ["待办事项:"]
134
+ for item in todo_list[:5]:
135
+ task = item.get("task", "")
136
+ lines.append(f" [ ] {task}")
137
+ return "\n".join(lines)
138
+ except Exception:
139
+ return "待办事项:\n 暂无待办事项"
140
+
141
+ def get_pending_issues(self) -> str:
142
+ if not self.config.show_pending:
143
+ return ""
144
+
145
+ pending_file = Path(self.project_path) / "state" / "memory" / "pending.yaml"
146
+ if not pending_file.exists():
147
+ return "上次会话遗留:\n 无遗留问题"
148
+
149
+ try:
150
+ import yaml
151
+ with open(pending_file) as f:
152
+ pending = yaml.safe_load(f) or []
153
+
154
+ if not pending:
155
+ return "上次会话遗留:\n 无遗留问题"
156
+
157
+ lines = ["上次会话遗留:"]
158
+ for item in pending[:5]:
159
+ desc = item.get("description", item)
160
+ lines.append(f" - {desc}")
161
+ return "\n".join(lines)
162
+ except Exception:
163
+ return "上次会话遗留:\n 无遗留问题"
164
+
165
+ def get_common_commands(self) -> str:
166
+ lines = ["常用命令:"]
167
+ for cmd, desc in COMMON_COMMANDS:
168
+ lines.append(f" - {cmd}: {desc}")
169
+ return "\n".join(lines)
170
+
171
+ def get_welcome_message(self, agent_id: str) -> str:
172
+ if not self.config.enabled:
173
+ return ""
174
+
175
+ project = self.get_project_info()
176
+ agent = self.get_agent_info(agent_id)
177
+
178
+ parts = [
179
+ f"=== Agent {agent_id.replace('agent', '')} ({agent['role']}) ===",
180
+ "",
181
+ f"当前项目: {project['name']}",
182
+ f"当前阶段: {project['phase']}",
183
+ f"当前里程碑: {project['milestone']}",
184
+ ""
185
+ ]
186
+
187
+ resp_text = self.get_responsibilities_text(agent_id)
188
+ if resp_text:
189
+ parts.append(resp_text)
190
+ parts.append("")
191
+
192
+ todo_text = self.get_todo_items()
193
+ if todo_text:
194
+ parts.append(todo_text)
195
+ parts.append("")
196
+
197
+ pending_text = self.get_pending_issues()
198
+ if pending_text:
199
+ parts.append(pending_text)
200
+ parts.append("")
201
+
202
+ parts.append(self.get_common_commands())
203
+
204
+ return "\n".join(parts)
205
+
206
+ def show_welcome(self, agent_id: str):
207
+ message = self.get_welcome_message(agent_id)
208
+ if message:
209
+ console.print(Panel(
210
+ Text(message, justify="left"),
211
+ title="会话引导",
212
+ style="blue"
213
+ ))
@@ -0,0 +1,239 @@
1
+ import pytest
2
+ import tempfile
3
+ import os
4
+ from pathlib import Path
5
+
6
+
7
+ def test_session_config_defaults():
8
+ from src.core.session_manager import SessionConfig
9
+
10
+ with tempfile.TemporaryDirectory() as tmpdir:
11
+ config = SessionConfig(tmpdir)
12
+ assert config.enabled is True
13
+ assert config.show_responsibilities is True
14
+ assert config.show_todo is True
15
+ assert config.show_pending is True
16
+
17
+
18
+ def test_session_config_with_state_file():
19
+ from src.core.session_manager import SessionConfig
20
+
21
+ with tempfile.TemporaryDirectory() as tmpdir:
22
+ state_file = Path(tmpdir) / "state" / "project_state.yaml"
23
+ state_file.parent.mkdir(parents=True, exist_ok=True)
24
+ state_file.write_text("session_start:\n enabled: false\n show_responsibilities: false\n show_todo: true\n show_pending: true\n")
25
+
26
+ config = SessionConfig(tmpdir)
27
+ assert config.enabled is False
28
+ assert config.show_responsibilities is False
29
+
30
+
31
+ def test_session_config_exception_handling():
32
+ from src.core.session_manager import SessionConfig
33
+
34
+ with tempfile.TemporaryDirectory() as tmpdir:
35
+ state_file = Path(tmpdir) / "state" / "project_state.yaml"
36
+ state_file.parent.mkdir(parents=True, exist_ok=True)
37
+ state_file.write_text("invalid: yaml: [[[")
38
+
39
+ config = SessionConfig(tmpdir)
40
+ assert config.enabled is True
41
+
42
+
43
+ def test_get_agent_info():
44
+ from src.core.session_manager import SessionManager
45
+
46
+ with tempfile.TemporaryDirectory() as tmpdir:
47
+ manager = SessionManager(tmpdir)
48
+ info = manager.get_agent_info("agent1")
49
+ assert "role" in info
50
+ assert "responsibilities" in info
51
+ assert "产品经理" in info["role"]
52
+
53
+
54
+ def test_get_agent_info_with_state():
55
+ from src.core.session_manager import SessionManager
56
+
57
+ with tempfile.TemporaryDirectory() as tmpdir:
58
+ manager = SessionManager(tmpdir)
59
+ info = manager.get_agent_info("agent1")
60
+ assert info["role"] == "产品经理"
61
+
62
+
63
+ def test_get_agent_info_unknown_agent():
64
+ from src.core.session_manager import SessionManager
65
+
66
+ with tempfile.TemporaryDirectory() as tmpdir:
67
+ manager = SessionManager(tmpdir)
68
+ info = manager.get_agent_info("unknown_agent")
69
+ assert info["role"] == "未知"
70
+
71
+
72
+ def test_get_project_info():
73
+ from src.core.session_manager import SessionManager
74
+
75
+ with tempfile.TemporaryDirectory() as tmpdir:
76
+ manager = SessionManager(tmpdir)
77
+ info = manager.get_project_info()
78
+ assert "name" in info
79
+ assert "phase" in info
80
+ assert "milestone" in info
81
+
82
+
83
+ def test_get_project_info_with_metadata():
84
+ from src.core.session_manager import SessionManager
85
+
86
+ with tempfile.TemporaryDirectory() as tmpdir:
87
+ state_file = Path(tmpdir) / "state" / "project_state.yaml"
88
+ state_file.parent.mkdir(parents=True, exist_ok=True)
89
+ state_file.write_text("metadata:\n project_name: TestProject\nproject:\n phase: development\n")
90
+
91
+ manager = SessionManager(tmpdir)
92
+ info = manager.get_project_info()
93
+ assert info["name"] == "TestProject"
94
+ assert info["phase"] == "development"
95
+
96
+
97
+ def test_get_responsibilities_text():
98
+ from src.core.session_manager import SessionManager
99
+
100
+ with tempfile.TemporaryDirectory() as tmpdir:
101
+ manager = SessionManager(tmpdir)
102
+ text = manager.get_responsibilities_text("agent1")
103
+ assert "你的职责:" in text
104
+ assert "编写和评审需求文档" in text
105
+ assert "定义验收标准" in text
106
+
107
+
108
+ def test_get_responsibilities_hidden():
109
+ from src.core.session_manager import SessionManager
110
+
111
+ with tempfile.TemporaryDirectory() as tmpdir:
112
+ state_file = Path(tmpdir) / "state" / "project_state.yaml"
113
+ state_file.parent.mkdir(parents=True, exist_ok=True)
114
+ state_file.write_text("session_start:\n show_responsibilities: false\n")
115
+
116
+ manager = SessionManager(tmpdir)
117
+ text = manager.get_responsibilities_text("agent1")
118
+ assert text == ""
119
+
120
+
121
+ def test_get_todo_items_empty():
122
+ from src.core.session_manager import SessionManager
123
+
124
+ with tempfile.TemporaryDirectory() as tmpdir:
125
+ manager = SessionManager(tmpdir)
126
+ text = manager.get_todo_items()
127
+ assert "待办事项:" in text
128
+ assert "暂无" in text
129
+
130
+
131
+ def test_get_todo_items_with_content():
132
+ from src.core.session_manager import SessionManager
133
+
134
+ with tempfile.TemporaryDirectory() as tmpdir:
135
+ state_file = Path(tmpdir) / "state" / "project_state.yaml"
136
+ state_file.parent.mkdir(parents=True, exist_ok=True)
137
+ state_file.write_text("session_start:\n show_todo: true\n")
138
+
139
+ manager = SessionManager(tmpdir)
140
+ text = manager.get_todo_items()
141
+ assert "待办事项:" in text
142
+
143
+
144
+ def test_get_todo_items_hidden():
145
+ from src.core.session_manager import SessionManager
146
+
147
+ with tempfile.TemporaryDirectory() as tmpdir:
148
+ state_file = Path(tmpdir) / "state" / "project_state.yaml"
149
+ state_file.parent.mkdir(parents=True, exist_ok=True)
150
+ state_file.write_text("session_start:\n show_todo: false\n")
151
+
152
+ manager = SessionManager(tmpdir)
153
+ text = manager.get_todo_items()
154
+ assert text == ""
155
+
156
+
157
+ def test_get_pending_issues_empty():
158
+ from src.core.session_manager import SessionManager
159
+
160
+ with tempfile.TemporaryDirectory() as tmpdir:
161
+ manager = SessionManager(tmpdir)
162
+ text = manager.get_pending_issues()
163
+ assert "上次会话遗留:" in text
164
+ assert "无遗留问题" in text
165
+
166
+
167
+ def test_get_pending_issues_with_content():
168
+ from src.core.session_manager import SessionManager
169
+
170
+ with tempfile.TemporaryDirectory() as tmpdir:
171
+ state_file = Path(tmpdir) / "state" / "project_state.yaml"
172
+ state_file.parent.mkdir(parents=True, exist_ok=True)
173
+ state_file.write_text("session_start:\n show_pending: true\n")
174
+
175
+ pending_file = Path(tmpdir) / "state" / "memory" / "pending.yaml"
176
+ pending_file.parent.mkdir(parents=True, exist_ok=True)
177
+ pending_file.write_text("- description: Bug BUG-001 pending\n")
178
+
179
+ manager = SessionManager(tmpdir)
180
+ text = manager.get_pending_issues()
181
+ assert "上次会话遗留:" in text
182
+ assert "Bug BUG-001 pending" in text
183
+
184
+
185
+ def test_get_pending_issues_hidden():
186
+ from src.core.session_manager import SessionManager
187
+
188
+ with tempfile.TemporaryDirectory() as tmpdir:
189
+ state_file = Path(tmpdir) / "state" / "project_state.yaml"
190
+ state_file.parent.mkdir(parents=True, exist_ok=True)
191
+ state_file.write_text("session_start:\n show_pending: false\n")
192
+
193
+ manager = SessionManager(tmpdir)
194
+ text = manager.get_pending_issues()
195
+ assert text == ""
196
+
197
+
198
+ def test_get_welcome_message():
199
+ from src.core.session_manager import SessionManager
200
+
201
+ with tempfile.TemporaryDirectory() as tmpdir:
202
+ manager = SessionManager(tmpdir)
203
+ message = manager.get_welcome_message("agent1")
204
+ assert "Agent 1" in message
205
+ assert "当前项目" in message
206
+ assert "当前阶段" in message
207
+
208
+
209
+ def test_welcome_disabled():
210
+ from src.core.session_manager import SessionManager
211
+
212
+ with tempfile.TemporaryDirectory() as tmpdir:
213
+ config_path = Path(tmpdir) / "state" / "project_state.yaml"
214
+ config_path.parent.mkdir(parents=True, exist_ok=True)
215
+ config_path.write_text("session_start:\n enabled: false\n")
216
+
217
+ manager = SessionManager(tmpdir)
218
+ message = manager.get_welcome_message("agent1")
219
+ assert message == ""
220
+
221
+
222
+ def test_common_commands():
223
+ from src.core.session_manager import SessionManager
224
+
225
+ with tempfile.TemporaryDirectory() as tmpdir:
226
+ manager = SessionManager(tmpdir)
227
+ message = manager.get_welcome_message("agent2")
228
+ assert "oc-collab status" in message
229
+ assert "oc-collab todo" in message
230
+
231
+
232
+ def test_get_common_commands():
233
+ from src.core.session_manager import SessionManager
234
+
235
+ with tempfile.TemporaryDirectory() as tmpdir:
236
+ manager = SessionManager(tmpdir)
237
+ text = manager.get_common_commands()
238
+ assert "常用命令:" in text
239
+ assert "oc-collab status" in text