@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,698 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Agent Router - Dynamic Agent Selection
4
+
5
+ Automatically selects the best agent(s) for a task based on:
6
+ - Task complexity analysis
7
+ - Code context and file types
8
+ - Historical performance data
9
+ - Task type detection (bug, feature, refactor, etc.)
10
+
11
+ Features:
12
+ - Smart task classification
13
+ - Multi-agent routing for complex tasks
14
+ - Confidence scoring
15
+ - Override support for manual selection
16
+
17
+ Usage:
18
+ from lib.agent_router import AgentRouter, TaskAnalysis
19
+
20
+ router = AgentRouter()
21
+ result = router.route("Fix authentication bug in login.py")
22
+ print(f"Recommended agents: {result.agents}")
23
+ print(f"Confidence: {result.confidence}")
24
+ """
25
+
26
+ import re
27
+ from dataclasses import dataclass, field
28
+ from enum import Enum
29
+ from pathlib import Path
30
+ from typing import Optional
31
+
32
+
33
+ class TaskType(Enum):
34
+ """Types of development tasks."""
35
+
36
+ FEATURE = "feature" # New functionality
37
+ BUGFIX = "bugfix" # Bug fix
38
+ REFACTOR = "refactor" # Code restructuring
39
+ SECURITY = "security" # Security-related
40
+ PERFORMANCE = "performance" # Performance optimization
41
+ DOCUMENTATION = "documentation" # Docs update
42
+ TESTING = "testing" # Test addition/fix
43
+ MIGRATION = "migration" # Data/code migration
44
+ TECH_DEBT = "tech_debt" # Technical debt
45
+ INVESTIGATION = "investigation" # Code exploration
46
+ ARCHITECTURE = "architecture" # Design decisions
47
+ QUICK_FIX = "quick_fix" # Small, targeted fix
48
+
49
+
50
+ class Complexity(Enum):
51
+ """Task complexity levels."""
52
+
53
+ TRIVIAL = 1 # Single file, simple change
54
+ LOW = 2 # Few files, straightforward
55
+ MEDIUM = 3 # Multiple files, some complexity
56
+ HIGH = 4 # Many files, complex logic
57
+ CRITICAL = 5 # System-wide, critical path
58
+
59
+
60
+ @dataclass
61
+ class Agent:
62
+ """Agent configuration."""
63
+
64
+ name: str
65
+ model: str
66
+ specialties: list[str]
67
+ cost_tier: str # low, medium, high
68
+ max_complexity: int # Max complexity this agent handles well
69
+
70
+ def __hash__(self):
71
+ return hash(self.name)
72
+
73
+
74
+ # Agent definitions with specialties
75
+ AGENTS = {
76
+ "SM": Agent(
77
+ name="SM",
78
+ model="sonnet",
79
+ specialties=["planning", "context", "coordination", "review"],
80
+ cost_tier="low",
81
+ max_complexity=5,
82
+ ),
83
+ "DEV": Agent(
84
+ name="DEV",
85
+ model="opus",
86
+ specialties=["implementation", "coding", "feature", "bugfix"],
87
+ cost_tier="high",
88
+ max_complexity=5,
89
+ ),
90
+ "BA": Agent(
91
+ name="BA",
92
+ model="sonnet",
93
+ specialties=["requirements", "analysis", "user-stories", "acceptance-criteria"],
94
+ cost_tier="low",
95
+ max_complexity=3,
96
+ ),
97
+ "ARCHITECT": Agent(
98
+ name="ARCHITECT",
99
+ model="sonnet",
100
+ specialties=["design", "architecture", "patterns", "system-design", "scalability"],
101
+ cost_tier="low",
102
+ max_complexity=5,
103
+ ),
104
+ "PM": Agent(
105
+ name="PM",
106
+ model="sonnet",
107
+ specialties=["planning", "prioritization", "stakeholders", "roadmap"],
108
+ cost_tier="low",
109
+ max_complexity=3,
110
+ ),
111
+ "WRITER": Agent(
112
+ name="WRITER",
113
+ model="sonnet",
114
+ specialties=["documentation", "readme", "api-docs", "tutorials"],
115
+ cost_tier="low",
116
+ max_complexity=3,
117
+ ),
118
+ "MAINTAINER": Agent(
119
+ name="MAINTAINER",
120
+ model="sonnet", # Uses Opus for complex tasks
121
+ specialties=["bugfix", "refactor", "tech-debt", "legacy", "maintenance"],
122
+ cost_tier="medium",
123
+ max_complexity=4,
124
+ ),
125
+ "REVIEWER": Agent(
126
+ name="REVIEWER",
127
+ model="opus",
128
+ specialties=["review", "quality", "security", "best-practices", "critique"],
129
+ cost_tier="high",
130
+ max_complexity=5,
131
+ ),
132
+ "SECURITY": Agent(
133
+ name="SECURITY",
134
+ model="opus",
135
+ specialties=["security", "vulnerabilities", "auth", "encryption", "penetration"],
136
+ cost_tier="high",
137
+ max_complexity=5,
138
+ ),
139
+ }
140
+
141
+
142
+ # Keyword patterns for task type detection
143
+ TASK_PATTERNS = {
144
+ TaskType.BUGFIX: [
145
+ r"\bbug\b",
146
+ r"\bfix\b",
147
+ r"\berror\b",
148
+ r"\bcrash\b",
149
+ r"\bbroken\b",
150
+ r"\bfailing\b",
151
+ r"\bissue\b",
152
+ r"\bdefect\b",
153
+ r"\bregression\b",
154
+ ],
155
+ TaskType.SECURITY: [
156
+ r"\bsecurity\b",
157
+ r"\bvulnerability\b",
158
+ r"\bauth\b",
159
+ r"\bpassword\b",
160
+ r"\btoken\b",
161
+ r"\bencrypt\b",
162
+ r"\bxss\b",
163
+ r"\bsql.?injection\b",
164
+ r"\bcve\b",
165
+ r"\boauth\b",
166
+ r"\bjwt\b",
167
+ r"\bcors\b",
168
+ ],
169
+ TaskType.PERFORMANCE: [
170
+ r"\bperformance\b",
171
+ r"\bslow\b",
172
+ r"\boptimize\b",
173
+ r"\blatency\b",
174
+ r"\bcache\b",
175
+ r"\bmemory\b",
176
+ r"\bcpu\b",
177
+ r"\bprofile\b",
178
+ r"\bbottleneck\b",
179
+ ],
180
+ TaskType.REFACTOR: [
181
+ r"\brefactor\b",
182
+ r"\brestructure\b",
183
+ r"\bcleanup\b",
184
+ r"\bclean.?up\b",
185
+ r"\breorganize\b",
186
+ r"\bsimplify\b",
187
+ r"\bmodularize\b",
188
+ ],
189
+ TaskType.DOCUMENTATION: [
190
+ r"\bdoc\b",
191
+ r"\bdocument\b",
192
+ r"\breadme\b",
193
+ r"\bcomment\b",
194
+ r"\bexplain\b",
195
+ r"\bapi.?doc\b",
196
+ r"\btutorial\b",
197
+ ],
198
+ TaskType.TESTING: [
199
+ r"\btest\b",
200
+ r"\bspec\b",
201
+ r"\bcoverage\b",
202
+ r"\bassert\b",
203
+ r"\bmock\b",
204
+ r"\bunit.?test\b",
205
+ r"\bintegration.?test\b",
206
+ ],
207
+ TaskType.MIGRATION: [
208
+ r"\bmigrat\b",
209
+ r"\bupgrad\b",
210
+ r"\bconvert\b",
211
+ r"\bport\b",
212
+ r"\bversion\b",
213
+ r"\bdeprecate\b",
214
+ ],
215
+ TaskType.TECH_DEBT: [
216
+ r"\btech.?debt\b",
217
+ r"\btodo\b",
218
+ r"\bfixme\b",
219
+ r"\bhack\b",
220
+ r"\bworkaround\b",
221
+ r"\btemporary\b",
222
+ r"\blegacy\b",
223
+ ],
224
+ TaskType.ARCHITECTURE: [
225
+ r"\barchitect\b",
226
+ r"\bdesign\b",
227
+ r"\bpattern\b",
228
+ r"\bstructure\b",
229
+ r"\bscalable\b",
230
+ r"\bmicroservice\b",
231
+ r"\bmonolith\b",
232
+ r"\bapi.?design\b",
233
+ ],
234
+ TaskType.INVESTIGATION: [
235
+ r"\binvestigate\b",
236
+ r"\bexplore\b",
237
+ r"\banalyze\b",
238
+ r"\bunderstand\b",
239
+ r"\bresearch\b",
240
+ r"\bspike\b",
241
+ r"\bpoc\b",
242
+ ],
243
+ }
244
+
245
+ # File extension to specialty mapping
246
+ FILE_SPECIALTIES = {
247
+ # Security-sensitive files
248
+ ".pem": ["security"],
249
+ ".key": ["security"],
250
+ ".env": ["security"],
251
+ # Documentation
252
+ ".md": ["documentation"],
253
+ ".rst": ["documentation"],
254
+ ".txt": ["documentation"],
255
+ # Test files
256
+ "test_": ["testing"],
257
+ "_test.": ["testing"],
258
+ ".spec.": ["testing"],
259
+ # Config files - often architectural
260
+ ".yaml": ["architecture", "devops"],
261
+ ".yml": ["architecture", "devops"],
262
+ ".toml": ["architecture"],
263
+ ".json": ["configuration"],
264
+ # Database
265
+ ".sql": ["database", "migration"],
266
+ "migration": ["migration", "database"],
267
+ }
268
+
269
+
270
+ @dataclass
271
+ class TaskAnalysis:
272
+ """Result of analyzing a task."""
273
+
274
+ task_type: TaskType
275
+ complexity: Complexity
276
+ detected_patterns: list[str]
277
+ file_contexts: list[str]
278
+ confidence: float # 0.0 to 1.0
279
+
280
+ def to_dict(self) -> dict:
281
+ return {
282
+ "task_type": self.task_type.value,
283
+ "complexity": self.complexity.value,
284
+ "detected_patterns": self.detected_patterns,
285
+ "file_contexts": self.file_contexts,
286
+ "confidence": self.confidence,
287
+ }
288
+
289
+
290
+ @dataclass
291
+ class RoutingResult:
292
+ """Result of agent routing."""
293
+
294
+ agents: list[str] # Ordered list of recommended agents
295
+ workflow: str # sequential, parallel, swarm
296
+ task_analysis: TaskAnalysis
297
+ reasoning: str
298
+ alternative_agents: list[str] = field(default_factory=list)
299
+ model_overrides: dict[str, str] = field(default_factory=dict) # agent -> model
300
+
301
+ def to_dict(self) -> dict:
302
+ return {
303
+ "agents": self.agents,
304
+ "workflow": self.workflow,
305
+ "task_analysis": self.task_analysis.to_dict(),
306
+ "reasoning": self.reasoning,
307
+ "alternative_agents": self.alternative_agents,
308
+ "model_overrides": self.model_overrides,
309
+ }
310
+
311
+
312
+ class AgentRouter:
313
+ """Dynamic agent selection based on task analysis."""
314
+
315
+ def __init__(self, project_root: Optional[Path] = None):
316
+ self.project_root = project_root or Path.cwd()
317
+ self.agents = AGENTS.copy()
318
+
319
+ def analyze_task(self, description: str, files: Optional[list[str]] = None) -> TaskAnalysis:
320
+ """
321
+ Analyze a task to determine type and complexity.
322
+
323
+ Args:
324
+ description: Natural language description of the task to analyze.
325
+ Keywords in the description are matched against patterns
326
+ to determine task type (bugfix, security, feature, etc.)
327
+ files: Optional list of file paths related to the task. Used to:
328
+ - Detect security-sensitive files (.pem, .key, .env)
329
+ - Identify documentation tasks (.md, .rst files)
330
+ - Detect test-related work (test_ prefixed files)
331
+ - Infer architectural scope from config files
332
+ If None, analysis is based solely on the description.
333
+
334
+ Returns:
335
+ TaskAnalysis object containing detected task type, complexity level,
336
+ matched patterns, file contexts, and confidence score (0.0-1.0).
337
+ """
338
+ description_lower = description.lower()
339
+
340
+ # Detect task type from patterns
341
+ type_scores: dict[TaskType, int] = {}
342
+ detected_patterns: list[str] = []
343
+
344
+ for task_type, patterns in TASK_PATTERNS.items():
345
+ score = 0
346
+ for pattern in patterns:
347
+ matches = re.findall(pattern, description_lower)
348
+ if matches:
349
+ score += len(matches)
350
+ detected_patterns.extend(matches)
351
+ if score > 0:
352
+ type_scores[task_type] = score
353
+
354
+ # Determine primary task type
355
+ if type_scores:
356
+ task_type = max(type_scores.keys(), key=lambda t: type_scores[t])
357
+ else:
358
+ task_type = TaskType.FEATURE # Default
359
+
360
+ # Analyze file contexts
361
+ file_contexts: list[str] = []
362
+ if files:
363
+ for file_path in files:
364
+ for pattern, specialties in FILE_SPECIALTIES.items():
365
+ if pattern in file_path.lower():
366
+ file_contexts.extend(specialties)
367
+
368
+ # Estimate complexity
369
+ complexity = self._estimate_complexity(description, files)
370
+
371
+ # Calculate confidence
372
+ confidence = min(1.0, 0.3 + (0.1 * len(detected_patterns)) + (0.1 * len(file_contexts)))
373
+
374
+ return TaskAnalysis(
375
+ task_type=task_type,
376
+ complexity=complexity,
377
+ detected_patterns=list(set(detected_patterns)),
378
+ file_contexts=list(set(file_contexts)),
379
+ confidence=confidence,
380
+ )
381
+
382
+ def _estimate_complexity(
383
+ self, description: str, files: Optional[list[str]] = None
384
+ ) -> Complexity:
385
+ """Estimate task complexity."""
386
+ score = 1
387
+
388
+ # Description-based heuristics
389
+ complexity_indicators = {
390
+ "simple": -1,
391
+ "trivial": -1,
392
+ "minor": -1,
393
+ "quick": -1,
394
+ "complex": 2,
395
+ "difficult": 2,
396
+ "critical": 3,
397
+ "major": 2,
398
+ "redesign": 3,
399
+ "rewrite": 3,
400
+ "overhaul": 3,
401
+ "multiple": 1,
402
+ "several": 1,
403
+ "many": 2,
404
+ "across": 1,
405
+ "throughout": 2,
406
+ "system-wide": 3,
407
+ }
408
+
409
+ desc_lower = description.lower()
410
+ for indicator, delta in complexity_indicators.items():
411
+ if indicator in desc_lower:
412
+ score += delta
413
+
414
+ # File count based
415
+ if files:
416
+ file_count = len(files)
417
+ if file_count > 10:
418
+ score += 2
419
+ elif file_count > 5:
420
+ score += 1
421
+
422
+ # Clamp to valid range
423
+ score = max(1, min(5, score))
424
+
425
+ return Complexity(score)
426
+
427
+ def route(
428
+ self,
429
+ description: str,
430
+ files: Optional[list[str]] = None,
431
+ prefer_cost: bool = False,
432
+ force_agents: Optional[list[str]] = None,
433
+ ) -> RoutingResult:
434
+ """Route a task to the appropriate agent(s)."""
435
+
436
+ # Allow manual override
437
+ if force_agents:
438
+ analysis = self.analyze_task(description, files)
439
+ return RoutingResult(
440
+ agents=force_agents,
441
+ workflow="sequential",
442
+ task_analysis=analysis,
443
+ reasoning="Manual agent selection override",
444
+ )
445
+
446
+ # Analyze the task
447
+ analysis = self.analyze_task(description, files)
448
+
449
+ # Select agents based on task type and complexity
450
+ agents, workflow, reasoning = self._select_agents(analysis, prefer_cost)
451
+
452
+ # Determine model overrides for complex tasks
453
+ model_overrides = {}
454
+ if analysis.complexity.value >= 4:
455
+ # Use Opus for complex tasks even for typically Sonnet agents
456
+ for agent in agents:
457
+ if self.agents[agent].cost_tier == "low":
458
+ model_overrides[agent] = "opus"
459
+
460
+ # Find alternative agents
461
+ alternatives = self._find_alternatives(agents, analysis)
462
+
463
+ return RoutingResult(
464
+ agents=agents,
465
+ workflow=workflow,
466
+ task_analysis=analysis,
467
+ reasoning=reasoning,
468
+ alternative_agents=alternatives,
469
+ model_overrides=model_overrides,
470
+ )
471
+
472
+ def _select_agents(
473
+ self, analysis: TaskAnalysis, prefer_cost: bool
474
+ ) -> tuple[list[str], str, str]:
475
+ """Select agents based on task analysis."""
476
+
477
+ task_type = analysis.task_type
478
+ complexity = analysis.complexity
479
+
480
+ # Define routing rules
481
+ routing_rules = {
482
+ TaskType.FEATURE: {
483
+ "simple": (["DEV"], "sequential", "Direct implementation"),
484
+ "complex": (
485
+ ["SM", "ARCHITECT", "DEV", "REVIEWER"],
486
+ "sequential",
487
+ "Full pipeline for complex feature",
488
+ ),
489
+ },
490
+ TaskType.BUGFIX: {
491
+ "simple": (["MAINTAINER"], "sequential", "Simple bug fix"),
492
+ "complex": (
493
+ ["DEV", "REVIEWER"],
494
+ "sequential",
495
+ "Complex bug requires senior review",
496
+ ),
497
+ },
498
+ TaskType.SECURITY: {
499
+ "simple": (["SECURITY", "DEV"], "sequential", "Security fix with review"),
500
+ "complex": (
501
+ ["SECURITY", "DEV", "REVIEWER"],
502
+ "swarm",
503
+ "Critical security requires multi-agent review",
504
+ ),
505
+ },
506
+ TaskType.PERFORMANCE: {
507
+ "simple": (["DEV"], "sequential", "Performance optimization"),
508
+ "complex": (
509
+ ["ARCHITECT", "DEV", "REVIEWER"],
510
+ "sequential",
511
+ "Architectural review for performance",
512
+ ),
513
+ },
514
+ TaskType.REFACTOR: {
515
+ "simple": (["MAINTAINER"], "sequential", "Simple refactor"),
516
+ "complex": (
517
+ ["ARCHITECT", "DEV", "REVIEWER"],
518
+ "sequential",
519
+ "Architectural oversight for major refactor",
520
+ ),
521
+ },
522
+ TaskType.DOCUMENTATION: {
523
+ "simple": (["WRITER"], "sequential", "Documentation update"),
524
+ "complex": (["BA", "WRITER"], "sequential", "Requirements review before docs"),
525
+ },
526
+ TaskType.TESTING: {
527
+ "simple": (["DEV"], "sequential", "Test implementation"),
528
+ "complex": (["DEV", "REVIEWER"], "sequential", "Test review for coverage"),
529
+ },
530
+ TaskType.MIGRATION: {
531
+ "simple": (["MAINTAINER"], "sequential", "Simple migration"),
532
+ "complex": (
533
+ ["ARCHITECT", "DEV", "REVIEWER"],
534
+ "sequential",
535
+ "Full review for migration",
536
+ ),
537
+ },
538
+ TaskType.TECH_DEBT: {
539
+ "simple": (["MAINTAINER"], "sequential", "Tech debt cleanup"),
540
+ "complex": (
541
+ ["ARCHITECT", "MAINTAINER", "REVIEWER"],
542
+ "sequential",
543
+ "Architectural review for major cleanup",
544
+ ),
545
+ },
546
+ TaskType.ARCHITECTURE: {
547
+ "simple": (["ARCHITECT"], "sequential", "Design decision"),
548
+ "complex": (["ARCHITECT", "DEV", "REVIEWER"], "swarm", "Multi-agent design review"),
549
+ },
550
+ TaskType.INVESTIGATION: {
551
+ "simple": (["SM"], "sequential", "Code exploration"),
552
+ "complex": (
553
+ ["SM", "ARCHITECT"],
554
+ "sequential",
555
+ "Deep analysis with architecture context",
556
+ ),
557
+ },
558
+ TaskType.QUICK_FIX: {
559
+ "simple": (["MAINTAINER"], "sequential", "Quick targeted fix"),
560
+ "complex": (["MAINTAINER"], "sequential", "Quick fix"),
561
+ },
562
+ }
563
+
564
+ # Determine complexity level
565
+ complexity_level = "simple" if complexity.value <= 2 else "complex"
566
+
567
+ # Get routing
568
+ rule = routing_rules.get(task_type, {}).get(complexity_level)
569
+ if not rule:
570
+ rule = (["DEV"], "sequential", "Default routing")
571
+
572
+ agents, workflow, reasoning = rule
573
+
574
+ # Cost optimization
575
+ if prefer_cost and complexity.value <= 3:
576
+ # Replace Opus agents with Sonnet equivalents where possible
577
+ cost_effective_agents = []
578
+ for agent in agents:
579
+ if agent == "REVIEWER" and complexity.value <= 2:
580
+ cost_effective_agents.append("SM") # SM can do simple reviews
581
+ else:
582
+ cost_effective_agents.append(agent)
583
+ agents = cost_effective_agents
584
+ reasoning += " (cost-optimized)"
585
+
586
+ return agents, workflow, reasoning
587
+
588
+ def _find_alternatives(self, primary: list[str], analysis: TaskAnalysis) -> list[str]:
589
+ """Find alternative agents that could handle the task."""
590
+ alternatives = []
591
+
592
+ for name, agent in self.agents.items():
593
+ if name in primary:
594
+ continue
595
+
596
+ # Check if agent specialties match any file contexts or detected patterns
597
+ matches = set(agent.specialties) & (
598
+ set(analysis.file_contexts) | {p.lower() for p in analysis.detected_patterns}
599
+ )
600
+
601
+ if matches and agent.max_complexity >= analysis.complexity.value:
602
+ alternatives.append(name)
603
+
604
+ return alternatives[:3] # Top 3 alternatives
605
+
606
+ def get_workflow_for_agents(self, agents: list[str]) -> str:
607
+ """Determine the best workflow for a set of agents."""
608
+ if len(agents) == 1:
609
+ return "single"
610
+
611
+ # If includes REVIEWER and DEV, could use pair mode
612
+ if "DEV" in agents and "REVIEWER" in agents and len(agents) == 2:
613
+ return "pair"
614
+
615
+ # If includes ARCHITECT, likely needs sequential
616
+ if "ARCHITECT" in agents:
617
+ return "sequential"
618
+
619
+ # If 3+ agents with REVIEWER, consider swarm
620
+ if len(agents) >= 3 and "REVIEWER" in agents:
621
+ return "swarm"
622
+
623
+ return "sequential"
624
+
625
+ def explain_routing(self, result: RoutingResult) -> str:
626
+ """Generate a human-readable explanation of the routing decision."""
627
+ lines = [
628
+ "🎯 **Task Analysis**",
629
+ f" - Type: {result.task_analysis.task_type.value}",
630
+ f" - Complexity: {result.task_analysis.complexity.name} ({result.task_analysis.complexity.value}/5)",
631
+ f" - Confidence: {result.task_analysis.confidence:.0%}",
632
+ "",
633
+ "🤖 **Recommended Agents**",
634
+ ]
635
+
636
+ for i, agent in enumerate(result.agents, 1):
637
+ agent_info = self.agents.get(agent)
638
+ model = result.model_overrides.get(agent, agent_info.model if agent_info else "sonnet")
639
+ lines.append(f" {i}. **{agent}** (model: {model})")
640
+
641
+ lines.extend(
642
+ [
643
+ "",
644
+ f"📋 **Workflow**: {result.workflow}",
645
+ f"💡 **Reasoning**: {result.reasoning}",
646
+ ]
647
+ )
648
+
649
+ if result.alternative_agents:
650
+ lines.extend(["", f"🔄 **Alternatives**: {', '.join(result.alternative_agents)}"])
651
+
652
+ if result.task_analysis.detected_patterns:
653
+ lines.extend(
654
+ [
655
+ "",
656
+ f"🔍 **Detected patterns**: {', '.join(result.task_analysis.detected_patterns[:5])}",
657
+ ]
658
+ )
659
+
660
+ return "\n".join(lines)
661
+
662
+
663
+ # Convenience functions
664
+ def route_task(description: str, files: Optional[list[str]] = None) -> RoutingResult:
665
+ """Quick routing of a task."""
666
+ router = AgentRouter()
667
+ return router.route(description, files)
668
+
669
+
670
+ def explain_route(description: str, files: Optional[list[str]] = None) -> str:
671
+ """Get an explanation of how a task would be routed."""
672
+ router = AgentRouter()
673
+ result = router.route(description, files)
674
+ return router.explain_routing(result)
675
+
676
+
677
+ if __name__ == "__main__":
678
+ # Demo usage
679
+ print("=== Agent Router Demo ===\n")
680
+
681
+ router = AgentRouter()
682
+
683
+ test_cases = [
684
+ "Fix login authentication bug in auth.py",
685
+ "Add user profile feature with photo upload",
686
+ "Security vulnerability in password hashing",
687
+ "Refactor payment service for better maintainability",
688
+ "Update README with new API documentation",
689
+ "Performance optimization for database queries",
690
+ "Investigate memory leak in production",
691
+ "Migrate from React 17 to React 18",
692
+ ]
693
+
694
+ for task in test_cases:
695
+ print(f'Task: "{task}"')
696
+ result = router.route(task)
697
+ print(router.explain_routing(result))
698
+ print("\n" + "=" * 60 + "\n")