@pjmendonca/devflow 1.9.0

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 (124) hide show
  1. package/CHANGELOG.md +526 -0
  2. package/LICENSE +21 -0
  3. package/README.md +620 -0
  4. package/bin/devflow-checkpoint.js +10 -0
  5. package/bin/devflow-collab.js +10 -0
  6. package/bin/devflow-cost.js +10 -0
  7. package/bin/devflow-create-persona.js +10 -0
  8. package/bin/devflow-init.js +10 -0
  9. package/bin/devflow-memory.js +10 -0
  10. package/bin/devflow-new-doc.js +10 -0
  11. package/bin/devflow-personalize.js +10 -0
  12. package/bin/devflow-setup-checkpoint.js +10 -0
  13. package/bin/devflow-story.js +10 -0
  14. package/bin/devflow-tech-debt.js +10 -0
  15. package/bin/devflow-validate-overrides.js +10 -0
  16. package/bin/devflow-validate.js +10 -0
  17. package/bin/devflow-version.js +10 -0
  18. package/lib/constants.js +30 -0
  19. package/lib/exec-python.js +78 -0
  20. package/lib/python-check.js +178 -0
  21. package/package.json +64 -0
  22. package/tooling/.automation/agents/architect.md +135 -0
  23. package/tooling/.automation/agents/ba.md +70 -0
  24. package/tooling/.automation/agents/dev.md +79 -0
  25. package/tooling/.automation/agents/maintainer.md +97 -0
  26. package/tooling/.automation/agents/pm.md +116 -0
  27. package/tooling/.automation/agents/reviewer.md +141 -0
  28. package/tooling/.automation/agents/sm.md +61 -0
  29. package/tooling/.automation/agents/writer.md +193 -0
  30. package/tooling/.automation/config.ps1.template +61 -0
  31. package/tooling/.automation/config.sh.template +48 -0
  32. package/tooling/.automation/memory/.gitkeep +6 -0
  33. package/tooling/.automation/memory/knowledge/kg_integration-test.json +94 -0
  34. package/tooling/.automation/memory/knowledge/kg_test-story.json +300 -0
  35. package/tooling/.automation/memory/shared/shared_integration-test.json +30 -0
  36. package/tooling/.automation/memory/shared/shared_test-story.json +78 -0
  37. package/tooling/.automation/overrides/templates/README.md +113 -0
  38. package/tooling/.automation/overrides/templates/architect/README.md +27 -0
  39. package/tooling/.automation/overrides/templates/architect/cloud-native.yaml +92 -0
  40. package/tooling/.automation/overrides/templates/architect/enterprise-architect.yaml +85 -0
  41. package/tooling/.automation/overrides/templates/architect/pragmatic-minimalist.yaml +88 -0
  42. package/tooling/.automation/overrides/templates/ba/README.md +27 -0
  43. package/tooling/.automation/overrides/templates/ba/agile-storyteller.yaml +86 -0
  44. package/tooling/.automation/overrides/templates/ba/domain-expert.yaml +91 -0
  45. package/tooling/.automation/overrides/templates/ba/requirements-engineer.yaml +89 -0
  46. package/tooling/.automation/overrides/templates/dev/README.md +32 -0
  47. package/tooling/.automation/overrides/templates/dev/junior-mentored.yaml +39 -0
  48. package/tooling/.automation/overrides/templates/dev/performance-engineer.yaml +43 -0
  49. package/tooling/.automation/overrides/templates/dev/rapid-prototyper.yaml +52 -0
  50. package/tooling/.automation/overrides/templates/dev/security-focused.yaml +43 -0
  51. package/tooling/.automation/overrides/templates/dev/senior-fullstack.yaml +39 -0
  52. package/tooling/.automation/overrides/templates/maintainer/README.md +27 -0
  53. package/tooling/.automation/overrides/templates/maintainer/devops-maintainer.yaml +113 -0
  54. package/tooling/.automation/overrides/templates/maintainer/legacy-steward.yaml +94 -0
  55. package/tooling/.automation/overrides/templates/maintainer/oss-maintainer.yaml +94 -0
  56. package/tooling/.automation/overrides/templates/pm/README.md +27 -0
  57. package/tooling/.automation/overrides/templates/pm/agile-pm.yaml +91 -0
  58. package/tooling/.automation/overrides/templates/pm/hybrid-delivery.yaml +87 -0
  59. package/tooling/.automation/overrides/templates/pm/traditional-pm.yaml +91 -0
  60. package/tooling/.automation/overrides/templates/reviewer/README.md +11 -0
  61. package/tooling/.automation/overrides/templates/reviewer/mentoring-reviewer.yaml +45 -0
  62. package/tooling/.automation/overrides/templates/reviewer/quick-sanity.yaml +50 -0
  63. package/tooling/.automation/overrides/templates/reviewer/thorough-critic.yaml +48 -0
  64. package/tooling/.automation/overrides/templates/sm/README.md +11 -0
  65. package/tooling/.automation/overrides/templates/sm/agile-coach.yaml +52 -0
  66. package/tooling/.automation/overrides/templates/sm/startup-pm.yaml +50 -0
  67. package/tooling/.automation/overrides/templates/sm/technical-lead.yaml +47 -0
  68. package/tooling/.automation/overrides/templates/user-profile.template.yaml +62 -0
  69. package/tooling/.automation/overrides/templates/writer/README.md +27 -0
  70. package/tooling/.automation/overrides/templates/writer/api-documentarian.yaml +99 -0
  71. package/tooling/.automation/overrides/templates/writer/docs-as-code.yaml +108 -0
  72. package/tooling/.automation/overrides/templates/writer/user-guide-author.yaml +100 -0
  73. package/tooling/completions/DevflowCompletion.ps1 +213 -0
  74. package/tooling/completions/_run-story +116 -0
  75. package/tooling/completions/run-story-completion.bash +136 -0
  76. package/tooling/docs/DOC-STANDARD.md +717 -0
  77. package/tooling/docs/sprint-status.yaml.template +24 -0
  78. package/tooling/docs/templates/bug-report.md +234 -0
  79. package/tooling/docs/templates/migration-spec.md +274 -0
  80. package/tooling/docs/templates/refactor-spec.md +86 -0
  81. package/tooling/docs/templates/tech-debt.md +86 -0
  82. package/tooling/scripts/context_checkpoint.py +556 -0
  83. package/tooling/scripts/cost_dashboard.py +617 -0
  84. package/tooling/scripts/create-persona.py +690 -0
  85. package/tooling/scripts/create-persona.sh +435 -0
  86. package/tooling/scripts/init-project-workflow.ps1 +651 -0
  87. package/tooling/scripts/init-project-workflow.py +70 -0
  88. package/tooling/scripts/init-project-workflow.sh +746 -0
  89. package/tooling/scripts/lib/__init__.py +35 -0
  90. package/tooling/scripts/lib/agent_handoff.py +526 -0
  91. package/tooling/scripts/lib/agent_router.py +698 -0
  92. package/tooling/scripts/lib/checkpoint-integration.ps1 +245 -0
  93. package/tooling/scripts/lib/checkpoint-integration.sh +191 -0
  94. package/tooling/scripts/lib/claude-cli.ps1 +952 -0
  95. package/tooling/scripts/lib/claude-cli.sh +1293 -0
  96. package/tooling/scripts/lib/cost_config.py +222 -0
  97. package/tooling/scripts/lib/cost_display.py +443 -0
  98. package/tooling/scripts/lib/cost_tracker.py +710 -0
  99. package/tooling/scripts/lib/currency_converter.py +328 -0
  100. package/tooling/scripts/lib/errors.py +438 -0
  101. package/tooling/scripts/lib/override-loader.sh +286 -0
  102. package/tooling/scripts/lib/pair_programming.py +589 -0
  103. package/tooling/scripts/lib/shared_memory.py +637 -0
  104. package/tooling/scripts/lib/swarm_orchestrator.py +689 -0
  105. package/tooling/scripts/memory_summarize.py +324 -0
  106. package/tooling/scripts/new-doc.ps1 +405 -0
  107. package/tooling/scripts/new-doc.py +93 -0
  108. package/tooling/scripts/new-doc.sh +534 -0
  109. package/tooling/scripts/personalize_agent.py +385 -0
  110. package/tooling/scripts/rollback-migration.sh +540 -0
  111. package/tooling/scripts/run-collab.ps1 +251 -0
  112. package/tooling/scripts/run-collab.py +605 -0
  113. package/tooling/scripts/run-collab.sh +110 -0
  114. package/tooling/scripts/run-story.ps1 +490 -0
  115. package/tooling/scripts/run-story.py +387 -0
  116. package/tooling/scripts/run-story.sh +467 -0
  117. package/tooling/scripts/setup-checkpoint-service.ps1 +219 -0
  118. package/tooling/scripts/setup-checkpoint-service.py +87 -0
  119. package/tooling/scripts/setup-checkpoint-service.sh +236 -0
  120. package/tooling/scripts/tech-debt-tracker.py +608 -0
  121. package/tooling/scripts/update_version.py +244 -0
  122. package/tooling/scripts/validate-overrides.py +511 -0
  123. package/tooling/scripts/validate-overrides.sh +432 -0
  124. package/tooling/scripts/validate_setup.py +539 -0
@@ -0,0 +1,35 @@
1
+ """
2
+ Devflow Library - Core modules for the Devflow automation system.
3
+
4
+ This package provides:
5
+ - cost_tracker: Token usage and cost tracking
6
+ - cost_display: Terminal-based cost monitoring display
7
+ - cost_config: Configuration management for costs
8
+ - currency_converter: Multi-currency support
9
+ - errors: Enhanced error handling
10
+ - agent_router: Dynamic agent selection
11
+ - agent_handoff: Structured agent transitions
12
+ - shared_memory: Cross-agent knowledge sharing
13
+ - swarm_orchestrator: Multi-agent collaboration
14
+ - pair_programming: DEV + REVIEWER collaboration
15
+
16
+ Usage:
17
+ from lib.cost_tracker import CostTracker
18
+ from lib.agent_router import AgentRouter
19
+ """
20
+
21
+ __version__ = "1.9.0"
22
+
23
+ # Lazy imports to avoid circular dependencies
24
+ __all__ = [
25
+ "cost_tracker",
26
+ "cost_display",
27
+ "cost_config",
28
+ "currency_converter",
29
+ "errors",
30
+ "agent_router",
31
+ "agent_handoff",
32
+ "shared_memory",
33
+ "swarm_orchestrator",
34
+ "pair_programming",
35
+ ]
@@ -0,0 +1,526 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Agent Handoff System - Structured Handoffs Between Agents
4
+
5
+ Creates explicit handoff summaries when transitioning work between agents.
6
+ Ensures context is preserved and the next agent has all necessary information.
7
+
8
+ Features:
9
+ - Automatic context extraction
10
+ - Git diff analysis for file changes
11
+ - Decision tracking
12
+ - Warning/blocker identification
13
+ - Structured handoff documents
14
+
15
+ Usage:
16
+ from lib.agent_handoff import HandoffGenerator, create_handoff
17
+
18
+ generator = HandoffGenerator(story_key="3-5")
19
+ handoff = generator.generate(
20
+ from_agent="SM",
21
+ to_agent="DEV",
22
+ work_summary="Created story context with all acceptance criteria"
23
+ )
24
+ print(handoff.to_markdown())
25
+ """
26
+
27
+ import re
28
+ import subprocess
29
+ from dataclasses import dataclass, field
30
+ from datetime import datetime
31
+ from pathlib import Path
32
+ from typing import Any, Optional
33
+
34
+ # Import shared memory for integration
35
+ try:
36
+ from shared_memory import (
37
+ HandoffSummary,
38
+ get_knowledge_graph,
39
+ get_shared_memory,
40
+ )
41
+ except ImportError:
42
+ from lib.shared_memory import (
43
+ HandoffSummary,
44
+ get_knowledge_graph,
45
+ get_shared_memory,
46
+ )
47
+
48
+
49
+ PROJECT_ROOT = Path(__file__).parent.parent.parent.parent
50
+
51
+
52
+ @dataclass
53
+ class FileChange:
54
+ """Represents a changed file."""
55
+
56
+ path: str
57
+ status: str # added, modified, deleted
58
+ additions: int = 0
59
+ deletions: int = 0
60
+
61
+ def to_dict(self) -> dict:
62
+ return {
63
+ "path": self.path,
64
+ "status": self.status,
65
+ "additions": self.additions,
66
+ "deletions": self.deletions,
67
+ }
68
+
69
+
70
+ @dataclass
71
+ class AgentWorkSummary:
72
+ """Summary of work done by an agent."""
73
+
74
+ agent: str
75
+ story_key: str
76
+ start_time: str
77
+ end_time: str
78
+ description: str
79
+ files_changed: list[FileChange] = field(default_factory=list)
80
+ decisions_made: list[str] = field(default_factory=list)
81
+ blockers_encountered: list[str] = field(default_factory=list)
82
+ blockers_resolved: list[str] = field(default_factory=list)
83
+ warnings: list[str] = field(default_factory=list)
84
+ questions_for_next: list[str] = field(default_factory=list)
85
+ artifacts_created: list[str] = field(default_factory=list)
86
+
87
+ def to_dict(self) -> dict:
88
+ return {
89
+ "agent": self.agent,
90
+ "story_key": self.story_key,
91
+ "start_time": self.start_time,
92
+ "end_time": self.end_time,
93
+ "description": self.description,
94
+ "files_changed": [f.to_dict() for f in self.files_changed],
95
+ "decisions_made": self.decisions_made,
96
+ "blockers_encountered": self.blockers_encountered,
97
+ "blockers_resolved": self.blockers_resolved,
98
+ "warnings": self.warnings,
99
+ "questions_for_next": self.questions_for_next,
100
+ "artifacts_created": self.artifacts_created,
101
+ }
102
+
103
+
104
+ # Standard handoff templates for each agent transition
105
+ HANDOFF_TEMPLATES = {
106
+ ("SM", "DEV"): {
107
+ "focus_areas": [
108
+ "Story acceptance criteria",
109
+ "Technical context and constraints",
110
+ "Related files and patterns to follow",
111
+ "Known blockers or dependencies",
112
+ ],
113
+ "expected_questions": [
114
+ "What patterns should I follow?",
115
+ "Are there existing similar implementations?",
116
+ "What tests are expected?",
117
+ ],
118
+ },
119
+ ("SM", "ARCHITECT"): {
120
+ "focus_areas": [
121
+ "High-level requirements",
122
+ "System constraints",
123
+ "Integration points",
124
+ "Scale requirements",
125
+ ],
126
+ "expected_questions": [
127
+ "What are the non-functional requirements?",
128
+ "What existing systems need integration?",
129
+ "What are the performance targets?",
130
+ ],
131
+ },
132
+ ("ARCHITECT", "DEV"): {
133
+ "focus_areas": [
134
+ "Architecture decisions",
135
+ "Design patterns to use",
136
+ "Component structure",
137
+ "Interface definitions",
138
+ ],
139
+ "expected_questions": [
140
+ "What patterns should I implement?",
141
+ "How should components communicate?",
142
+ "What are the boundaries?",
143
+ ],
144
+ },
145
+ ("DEV", "REVIEWER"): {
146
+ "focus_areas": [
147
+ "Implementation approach",
148
+ "Key decisions made",
149
+ "Areas of uncertainty",
150
+ "Test coverage",
151
+ ],
152
+ "expected_questions": [
153
+ "Why was this approach chosen?",
154
+ "What alternatives were considered?",
155
+ "What edge cases were handled?",
156
+ ],
157
+ },
158
+ ("REVIEWER", "DEV"): {
159
+ "focus_areas": [
160
+ "Issues found",
161
+ "Required changes",
162
+ "Suggestions (optional)",
163
+ "Approval status",
164
+ ],
165
+ "expected_questions": [
166
+ "Which issues are blocking?",
167
+ "Are there acceptable alternatives?",
168
+ "What's the priority order?",
169
+ ],
170
+ },
171
+ ("BA", "DEV"): {
172
+ "focus_areas": [
173
+ "Refined requirements",
174
+ "Acceptance criteria",
175
+ "User stories",
176
+ "Edge cases",
177
+ ],
178
+ "expected_questions": [
179
+ "What are the exact acceptance criteria?",
180
+ "What user flows need support?",
181
+ "What error scenarios exist?",
182
+ ],
183
+ },
184
+ }
185
+
186
+
187
+ class HandoffGenerator:
188
+ """Generates structured handoffs between agents."""
189
+
190
+ def __init__(self, story_key: str, project_root: Optional[Path] = None):
191
+ self.story_key = story_key
192
+ self.project_root = project_root or PROJECT_ROOT
193
+ self.knowledge_graph = get_knowledge_graph(story_key)
194
+ self.shared_memory = get_shared_memory(story_key)
195
+
196
+ def get_git_changes(self, since_commit: Optional[str] = None) -> list[FileChange]:
197
+ """Get list of changed files from git."""
198
+ try:
199
+ if since_commit:
200
+ cmd = ["git", "diff", "--numstat", since_commit]
201
+ else:
202
+ cmd = ["git", "diff", "--numstat", "HEAD~1"]
203
+
204
+ result = subprocess.run(cmd, capture_output=True, text=True, cwd=str(self.project_root))
205
+
206
+ changes = []
207
+ for line in result.stdout.strip().split("\n"):
208
+ if not line:
209
+ continue
210
+ parts = line.split("\t")
211
+ if len(parts) >= 3:
212
+ additions = int(parts[0]) if parts[0] != "-" else 0
213
+ deletions = int(parts[1]) if parts[1] != "-" else 0
214
+ path = parts[2]
215
+
216
+ status = "modified"
217
+ if additions > 0 and deletions == 0:
218
+ status = "added"
219
+ elif additions == 0 and deletions > 0:
220
+ status = "deleted"
221
+
222
+ changes.append(
223
+ FileChange(
224
+ path=path, status=status, additions=additions, deletions=deletions
225
+ )
226
+ )
227
+
228
+ return changes
229
+ except Exception:
230
+ return []
231
+
232
+ def get_staged_changes(self) -> list[FileChange]:
233
+ """Get staged changes from git."""
234
+ try:
235
+ result = subprocess.run(
236
+ ["git", "diff", "--cached", "--numstat"],
237
+ capture_output=True,
238
+ text=True,
239
+ cwd=str(self.project_root),
240
+ )
241
+
242
+ changes = []
243
+ for line in result.stdout.strip().split("\n"):
244
+ if not line:
245
+ continue
246
+ parts = line.split("\t")
247
+ if len(parts) >= 3:
248
+ changes.append(
249
+ FileChange(
250
+ path=parts[2],
251
+ status="staged",
252
+ additions=int(parts[0]) if parts[0] != "-" else 0,
253
+ deletions=int(parts[1]) if parts[1] != "-" else 0,
254
+ )
255
+ )
256
+
257
+ return changes
258
+ except Exception:
259
+ return []
260
+
261
+ def extract_decisions_from_memory(self, agent: str) -> list[str]:
262
+ """Extract decisions made by an agent from shared memory."""
263
+ decisions = self.knowledge_graph.get_decisions_by_agent(agent)
264
+ return [f"{d.topic}: {d.decision}" for d in decisions]
265
+
266
+ def extract_warnings_from_log(self, log_content: str) -> list[str]:
267
+ """Extract warnings from agent log output."""
268
+ warnings = []
269
+ warning_patterns = [
270
+ r"⚠️\s*(.+)",
271
+ r"WARNING:\s*(.+)",
272
+ r"WARN:\s*(.+)",
273
+ r"Watch out.*?:\s*(.+)",
274
+ r"Note:\s*(.+)",
275
+ ]
276
+
277
+ for pattern in warning_patterns:
278
+ matches = re.findall(pattern, log_content, re.IGNORECASE)
279
+ warnings.extend(matches)
280
+
281
+ return list(set(warnings))[:5] # Dedupe and limit
282
+
283
+ def generate(
284
+ self,
285
+ from_agent: str,
286
+ to_agent: str,
287
+ work_summary: str,
288
+ decisions_made: Optional[list[str]] = None,
289
+ blockers_resolved: Optional[list[str]] = None,
290
+ warnings: Optional[list[str]] = None,
291
+ files_changed: Optional[list[FileChange]] = None,
292
+ next_steps: Optional[list[str]] = None,
293
+ log_content: Optional[str] = None,
294
+ ) -> HandoffSummary:
295
+ """Generate a handoff from one agent to another."""
296
+
297
+ # Auto-detect file changes if not provided
298
+ if files_changed is None:
299
+ files_changed = self.get_git_changes()
300
+
301
+ # Auto-extract decisions from memory
302
+ if decisions_made is None:
303
+ decisions_made = self.extract_decisions_from_memory(from_agent)
304
+
305
+ # Auto-extract warnings from log
306
+ if warnings is None and log_content:
307
+ warnings = self.extract_warnings_from_log(log_content)
308
+
309
+ # Get template for this transition
310
+ template = HANDOFF_TEMPLATES.get((from_agent, to_agent), {})
311
+
312
+ # Generate next steps if not provided
313
+ if next_steps is None:
314
+ next_steps = self._generate_next_steps(from_agent, to_agent, files_changed, template)
315
+
316
+ # Create handoff
317
+ handoff = self.knowledge_graph.add_handoff(
318
+ from_agent=from_agent,
319
+ to_agent=to_agent,
320
+ story_key=self.story_key,
321
+ summary=work_summary,
322
+ key_decisions=decisions_made or [],
323
+ blockers_resolved=blockers_resolved or [],
324
+ watch_out_for=warnings or [],
325
+ files_touched=[f.path for f in files_changed] if files_changed else [],
326
+ next_steps=next_steps or [],
327
+ )
328
+
329
+ # Also record in shared memory
330
+ self.shared_memory.add(
331
+ agent=from_agent,
332
+ content=f"Handed off to {to_agent}: {work_summary}",
333
+ tags=["handoff", to_agent.lower()],
334
+ )
335
+
336
+ return handoff
337
+
338
+ def _generate_next_steps(
339
+ self, from_agent: str, to_agent: str, files: list[FileChange], template: dict
340
+ ) -> list[str]:
341
+ """Generate suggested next steps for the receiving agent."""
342
+ steps = []
343
+
344
+ # Generic steps based on agent transition
345
+ if to_agent == "DEV":
346
+ steps.append("Review the acceptance criteria in the story context")
347
+ if files:
348
+ steps.append(f"Examine the {len(files)} files that have context")
349
+ steps.append("Implement the required functionality")
350
+ steps.append("Write tests for the implementation")
351
+
352
+ elif to_agent == "REVIEWER":
353
+ if files:
354
+ steps.append(f"Review the {len(files)} changed files")
355
+ steps.append("Check for code quality and best practices")
356
+ steps.append("Verify test coverage")
357
+ steps.append("Provide actionable feedback")
358
+
359
+ elif to_agent == "ARCHITECT":
360
+ steps.append("Analyze the requirements for design implications")
361
+ steps.append("Document key architectural decisions")
362
+ steps.append("Define component interfaces")
363
+
364
+ elif to_agent == "SM":
365
+ steps.append("Review the completed work")
366
+ steps.append("Update story status")
367
+ steps.append("Prepare for next phase or story")
368
+
369
+ return steps
370
+
371
+ def get_latest_handoff_for(self, agent: str) -> Optional[HandoffSummary]:
372
+ """Get the most recent handoff for an agent."""
373
+ return self.knowledge_graph.get_latest_handoff(agent)
374
+
375
+ def generate_context_for_agent(self, agent: str) -> str:
376
+ """Generate full context string for an agent including handoffs."""
377
+ lines = [f"## Context for {agent}", ""]
378
+
379
+ # Get latest handoff
380
+ handoff = self.get_latest_handoff_for(agent)
381
+ if handoff:
382
+ lines.append("### Handoff from Previous Agent")
383
+ lines.append(handoff.to_markdown())
384
+ lines.append("")
385
+
386
+ # Get relevant shared memory
387
+ recent_memory = self.shared_memory.get_recent(5)
388
+ if recent_memory:
389
+ lines.append("### Recent Team Activity")
390
+ for entry in recent_memory:
391
+ lines.append(f"- **{entry.agent}**: {entry.content}")
392
+ lines.append("")
393
+
394
+ # Get relevant decisions
395
+ decisions = list(self.knowledge_graph.decisions.values())
396
+ active_decisions = [d for d in decisions if d.status == "active"]
397
+ if active_decisions:
398
+ lines.append("### Active Decisions")
399
+ for dec in active_decisions[-5:]:
400
+ lines.append(f"- **{dec.topic}** ({dec.agent}): {dec.decision}")
401
+ lines.append("")
402
+
403
+ return "\n".join(lines)
404
+
405
+
406
+ class WorkTracker:
407
+ """Tracks work in progress for handoff generation."""
408
+
409
+ def __init__(self, story_key: str, agent: str):
410
+ self.story_key = story_key
411
+ self.agent = agent
412
+ self.start_time = datetime.now().isoformat()
413
+ self.decisions: list[str] = []
414
+ self.blockers: list[str] = []
415
+ self.warnings: list[str] = []
416
+ self.notes: list[str] = []
417
+ self.files_touched: list[str] = []
418
+ self.shared_memory = get_shared_memory(story_key)
419
+ self.knowledge_graph = get_knowledge_graph(story_key)
420
+
421
+ def record_decision(self, topic: str, decision: str, context: Optional[dict[str, Any]] = None):
422
+ """Record a decision made during work."""
423
+ self.decisions.append(f"{topic}: {decision}")
424
+ self.knowledge_graph.add_decision(
425
+ agent=self.agent, topic=topic, decision=decision, context=context or {}
426
+ )
427
+
428
+ def record_blocker(self, blocker: str, resolved: bool = False):
429
+ """Record a blocker encountered."""
430
+ if resolved:
431
+ self.blockers.append(f"✅ {blocker} (resolved)")
432
+ else:
433
+ self.blockers.append(f"❌ {blocker}")
434
+
435
+ def record_warning(self, warning: str):
436
+ """Record a warning for the next agent."""
437
+ self.warnings.append(warning)
438
+
439
+ def record_note(self, note: str):
440
+ """Record a general note."""
441
+ self.notes.append(note)
442
+ self.shared_memory.add(agent=self.agent, content=note, tags=["note"])
443
+
444
+ def record_file(self, file_path: str):
445
+ """Record a file that was touched."""
446
+ if file_path not in self.files_touched:
447
+ self.files_touched.append(file_path)
448
+
449
+ def generate_handoff(self, to_agent: str, summary: str) -> HandoffSummary:
450
+ """Generate a handoff based on tracked work."""
451
+ generator = HandoffGenerator(self.story_key)
452
+
453
+ # Separate resolved and unresolved blockers
454
+ resolved = [
455
+ b.replace("✅ ", "").replace(" (resolved)", "")
456
+ for b in self.blockers
457
+ if b.startswith("✅")
458
+ ]
459
+
460
+ return generator.generate(
461
+ from_agent=self.agent,
462
+ to_agent=to_agent,
463
+ work_summary=summary,
464
+ decisions_made=self.decisions,
465
+ blockers_resolved=resolved,
466
+ warnings=self.warnings,
467
+ next_steps=self.notes if self.notes else None,
468
+ )
469
+
470
+
471
+ # Convenience functions
472
+ def create_handoff(
473
+ from_agent: str, to_agent: str, story_key: str, summary: str, **kwargs
474
+ ) -> HandoffSummary:
475
+ """Quick function to create a handoff."""
476
+ generator = HandoffGenerator(story_key)
477
+ return generator.generate(from_agent, to_agent, summary, **kwargs)
478
+
479
+
480
+ def get_agent_context(agent: str, story_key: str) -> str:
481
+ """Get full context for an agent including handoffs."""
482
+ generator = HandoffGenerator(story_key)
483
+ return generator.generate_context_for_agent(agent)
484
+
485
+
486
+ def start_work_tracking(story_key: str, agent: str) -> WorkTracker:
487
+ """Start tracking work for handoff generation."""
488
+ return WorkTracker(story_key, agent)
489
+
490
+
491
+ if __name__ == "__main__":
492
+ # Demo usage
493
+ print("=== Handoff System Demo ===\n")
494
+
495
+ # Simulate a workflow
496
+ story_key = "demo-handoff"
497
+
498
+ # SM starts work
499
+ tracker = start_work_tracking(story_key, "SM")
500
+ tracker.record_decision(
501
+ "implementation-approach",
502
+ "Use existing UserService class",
503
+ {"reason": "Follows established patterns"},
504
+ )
505
+ tracker.record_decision(
506
+ "testing-strategy",
507
+ "Unit tests for service, integration for API",
508
+ {"coverage_target": "80%"},
509
+ )
510
+ tracker.record_warning("Rate limiting on profile image uploads")
511
+ tracker.record_note("Existing user.py has the patterns to follow")
512
+
513
+ # Generate handoff to DEV
514
+ handoff = tracker.generate_handoff(
515
+ to_agent="DEV",
516
+ summary="Created story context for user profile feature. Requirements are clear, patterns are established.",
517
+ )
518
+
519
+ print(handoff.to_markdown())
520
+
521
+ print("\n" + "=" * 60 + "\n")
522
+
523
+ # DEV receives context
524
+ generator = HandoffGenerator(story_key)
525
+ context = generator.generate_context_for_agent("DEV")
526
+ print(context)