empathy-framework 3.7.0__py3-none-any.whl → 3.8.0__py3-none-any.whl

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 (274) hide show
  1. coach_wizards/code_reviewer_README.md +60 -0
  2. coach_wizards/code_reviewer_wizard.py +180 -0
  3. {empathy_framework-3.7.0.dist-info → empathy_framework-3.8.0.dist-info}/METADATA +148 -11
  4. empathy_framework-3.8.0.dist-info/RECORD +333 -0
  5. {empathy_framework-3.7.0.dist-info → empathy_framework-3.8.0.dist-info}/top_level.txt +5 -1
  6. empathy_healthcare_plugin/monitors/__init__.py +9 -0
  7. empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +315 -0
  8. empathy_healthcare_plugin/monitors/monitoring/__init__.py +44 -0
  9. empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +300 -0
  10. empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +214 -0
  11. empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +306 -0
  12. empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +389 -0
  13. empathy_llm_toolkit/agent_factory/__init__.py +53 -0
  14. empathy_llm_toolkit/agent_factory/adapters/__init__.py +85 -0
  15. empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +312 -0
  16. empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +454 -0
  17. empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +298 -0
  18. empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +362 -0
  19. empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +333 -0
  20. empathy_llm_toolkit/agent_factory/adapters/native.py +228 -0
  21. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +426 -0
  22. empathy_llm_toolkit/agent_factory/base.py +305 -0
  23. empathy_llm_toolkit/agent_factory/crews/__init__.py +67 -0
  24. empathy_llm_toolkit/agent_factory/crews/code_review.py +1113 -0
  25. empathy_llm_toolkit/agent_factory/crews/health_check.py +1246 -0
  26. empathy_llm_toolkit/agent_factory/crews/refactoring.py +1128 -0
  27. empathy_llm_toolkit/agent_factory/crews/security_audit.py +1018 -0
  28. empathy_llm_toolkit/agent_factory/decorators.py +286 -0
  29. empathy_llm_toolkit/agent_factory/factory.py +558 -0
  30. empathy_llm_toolkit/agent_factory/framework.py +192 -0
  31. empathy_llm_toolkit/agent_factory/memory_integration.py +324 -0
  32. empathy_llm_toolkit/agent_factory/resilient.py +320 -0
  33. empathy_llm_toolkit/cli/__init__.py +8 -0
  34. empathy_llm_toolkit/cli/sync_claude.py +487 -0
  35. empathy_llm_toolkit/code_health.py +150 -3
  36. empathy_llm_toolkit/config/__init__.py +29 -0
  37. empathy_llm_toolkit/config/unified.py +295 -0
  38. empathy_llm_toolkit/routing/__init__.py +32 -0
  39. empathy_llm_toolkit/routing/model_router.py +362 -0
  40. empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +413 -0
  41. empathy_llm_toolkit/security/PHASE2_COMPLETE.md +384 -0
  42. empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
  43. empathy_llm_toolkit/security/QUICK_REFERENCE.md +316 -0
  44. empathy_llm_toolkit/security/README.md +262 -0
  45. empathy_llm_toolkit/security/__init__.py +62 -0
  46. empathy_llm_toolkit/security/audit_logger.py +929 -0
  47. empathy_llm_toolkit/security/audit_logger_example.py +152 -0
  48. empathy_llm_toolkit/security/pii_scrubber.py +640 -0
  49. empathy_llm_toolkit/security/secrets_detector.py +678 -0
  50. empathy_llm_toolkit/security/secrets_detector_example.py +304 -0
  51. empathy_llm_toolkit/security/secure_memdocs.py +1192 -0
  52. empathy_llm_toolkit/security/secure_memdocs_example.py +278 -0
  53. empathy_llm_toolkit/wizards/__init__.py +38 -0
  54. empathy_llm_toolkit/wizards/base_wizard.py +364 -0
  55. empathy_llm_toolkit/wizards/customer_support_wizard.py +190 -0
  56. empathy_llm_toolkit/wizards/healthcare_wizard.py +362 -0
  57. empathy_llm_toolkit/wizards/patient_assessment_README.md +64 -0
  58. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +193 -0
  59. empathy_llm_toolkit/wizards/technology_wizard.py +194 -0
  60. empathy_os/__init__.py +52 -52
  61. empathy_os/adaptive/__init__.py +13 -0
  62. empathy_os/adaptive/task_complexity.py +127 -0
  63. empathy_os/cache/__init__.py +117 -0
  64. empathy_os/cache/base.py +166 -0
  65. empathy_os/cache/dependency_manager.py +253 -0
  66. empathy_os/cache/hash_only.py +248 -0
  67. empathy_os/cache/hybrid.py +390 -0
  68. empathy_os/cache/storage.py +282 -0
  69. empathy_os/cli.py +118 -8
  70. empathy_os/cli_unified.py +121 -1
  71. empathy_os/config/__init__.py +63 -0
  72. empathy_os/config/xml_config.py +239 -0
  73. empathy_os/config.py +2 -1
  74. empathy_os/dashboard/__init__.py +15 -0
  75. empathy_os/dashboard/server.py +743 -0
  76. empathy_os/memory/__init__.py +195 -0
  77. empathy_os/memory/claude_memory.py +466 -0
  78. empathy_os/memory/config.py +224 -0
  79. empathy_os/memory/control_panel.py +1298 -0
  80. empathy_os/memory/edges.py +179 -0
  81. empathy_os/memory/graph.py +567 -0
  82. empathy_os/memory/long_term.py +1194 -0
  83. empathy_os/memory/nodes.py +179 -0
  84. empathy_os/memory/redis_bootstrap.py +540 -0
  85. empathy_os/memory/security/__init__.py +31 -0
  86. empathy_os/memory/security/audit_logger.py +930 -0
  87. empathy_os/memory/security/pii_scrubber.py +640 -0
  88. empathy_os/memory/security/secrets_detector.py +678 -0
  89. empathy_os/memory/short_term.py +2119 -0
  90. empathy_os/memory/storage/__init__.py +15 -0
  91. empathy_os/memory/summary_index.py +583 -0
  92. empathy_os/memory/unified.py +619 -0
  93. empathy_os/metrics/__init__.py +12 -0
  94. empathy_os/metrics/prompt_metrics.py +190 -0
  95. empathy_os/models/__init__.py +136 -0
  96. empathy_os/models/__main__.py +13 -0
  97. empathy_os/models/cli.py +655 -0
  98. empathy_os/models/empathy_executor.py +354 -0
  99. empathy_os/models/executor.py +252 -0
  100. empathy_os/models/fallback.py +671 -0
  101. empathy_os/models/provider_config.py +563 -0
  102. empathy_os/models/registry.py +382 -0
  103. empathy_os/models/tasks.py +302 -0
  104. empathy_os/models/telemetry.py +548 -0
  105. empathy_os/models/token_estimator.py +378 -0
  106. empathy_os/models/validation.py +274 -0
  107. empathy_os/monitoring/__init__.py +52 -0
  108. empathy_os/monitoring/alerts.py +23 -0
  109. empathy_os/monitoring/alerts_cli.py +268 -0
  110. empathy_os/monitoring/multi_backend.py +271 -0
  111. empathy_os/monitoring/otel_backend.py +363 -0
  112. empathy_os/optimization/__init__.py +19 -0
  113. empathy_os/optimization/context_optimizer.py +272 -0
  114. empathy_os/plugins/__init__.py +28 -0
  115. empathy_os/plugins/base.py +361 -0
  116. empathy_os/plugins/registry.py +268 -0
  117. empathy_os/project_index/__init__.py +30 -0
  118. empathy_os/project_index/cli.py +335 -0
  119. empathy_os/project_index/crew_integration.py +430 -0
  120. empathy_os/project_index/index.py +425 -0
  121. empathy_os/project_index/models.py +501 -0
  122. empathy_os/project_index/reports.py +473 -0
  123. empathy_os/project_index/scanner.py +538 -0
  124. empathy_os/prompts/__init__.py +61 -0
  125. empathy_os/prompts/config.py +77 -0
  126. empathy_os/prompts/context.py +177 -0
  127. empathy_os/prompts/parser.py +285 -0
  128. empathy_os/prompts/registry.py +313 -0
  129. empathy_os/prompts/templates.py +208 -0
  130. empathy_os/resilience/__init__.py +56 -0
  131. empathy_os/resilience/circuit_breaker.py +256 -0
  132. empathy_os/resilience/fallback.py +179 -0
  133. empathy_os/resilience/health.py +300 -0
  134. empathy_os/resilience/retry.py +209 -0
  135. empathy_os/resilience/timeout.py +135 -0
  136. empathy_os/routing/__init__.py +43 -0
  137. empathy_os/routing/chain_executor.py +433 -0
  138. empathy_os/routing/classifier.py +217 -0
  139. empathy_os/routing/smart_router.py +234 -0
  140. empathy_os/routing/wizard_registry.py +307 -0
  141. empathy_os/trust/__init__.py +28 -0
  142. empathy_os/trust/circuit_breaker.py +579 -0
  143. empathy_os/validation/__init__.py +19 -0
  144. empathy_os/validation/xml_validator.py +281 -0
  145. empathy_os/wizard_factory_cli.py +170 -0
  146. empathy_os/workflows/__init__.py +360 -0
  147. empathy_os/workflows/base.py +1660 -0
  148. empathy_os/workflows/bug_predict.py +962 -0
  149. empathy_os/workflows/code_review.py +960 -0
  150. empathy_os/workflows/code_review_adapters.py +310 -0
  151. empathy_os/workflows/code_review_pipeline.py +720 -0
  152. empathy_os/workflows/config.py +600 -0
  153. empathy_os/workflows/dependency_check.py +648 -0
  154. empathy_os/workflows/document_gen.py +1069 -0
  155. empathy_os/workflows/documentation_orchestrator.py +1205 -0
  156. empathy_os/workflows/health_check.py +679 -0
  157. empathy_os/workflows/keyboard_shortcuts/__init__.py +39 -0
  158. empathy_os/workflows/keyboard_shortcuts/generators.py +386 -0
  159. empathy_os/workflows/keyboard_shortcuts/parsers.py +414 -0
  160. empathy_os/workflows/keyboard_shortcuts/prompts.py +295 -0
  161. empathy_os/workflows/keyboard_shortcuts/schema.py +193 -0
  162. empathy_os/workflows/keyboard_shortcuts/workflow.py +505 -0
  163. empathy_os/workflows/manage_documentation.py +804 -0
  164. empathy_os/workflows/new_sample_workflow1.py +146 -0
  165. empathy_os/workflows/new_sample_workflow1_README.md +150 -0
  166. empathy_os/workflows/perf_audit.py +687 -0
  167. empathy_os/workflows/pr_review.py +748 -0
  168. empathy_os/workflows/progress.py +445 -0
  169. empathy_os/workflows/progress_server.py +322 -0
  170. empathy_os/workflows/refactor_plan.py +693 -0
  171. empathy_os/workflows/release_prep.py +808 -0
  172. empathy_os/workflows/research_synthesis.py +404 -0
  173. empathy_os/workflows/secure_release.py +585 -0
  174. empathy_os/workflows/security_adapters.py +297 -0
  175. empathy_os/workflows/security_audit.py +1046 -0
  176. empathy_os/workflows/step_config.py +234 -0
  177. empathy_os/workflows/test5.py +125 -0
  178. empathy_os/workflows/test5_README.md +158 -0
  179. empathy_os/workflows/test_gen.py +1855 -0
  180. empathy_os/workflows/test_lifecycle.py +526 -0
  181. empathy_os/workflows/test_maintenance.py +626 -0
  182. empathy_os/workflows/test_maintenance_cli.py +590 -0
  183. empathy_os/workflows/test_maintenance_crew.py +821 -0
  184. empathy_os/workflows/xml_enhanced_crew.py +285 -0
  185. empathy_software_plugin/cli/__init__.py +120 -0
  186. empathy_software_plugin/cli/inspect.py +362 -0
  187. empathy_software_plugin/cli.py +3 -1
  188. empathy_software_plugin/wizards/__init__.py +42 -0
  189. empathy_software_plugin/wizards/advanced_debugging_wizard.py +392 -0
  190. empathy_software_plugin/wizards/agent_orchestration_wizard.py +511 -0
  191. empathy_software_plugin/wizards/ai_collaboration_wizard.py +503 -0
  192. empathy_software_plugin/wizards/ai_context_wizard.py +441 -0
  193. empathy_software_plugin/wizards/ai_documentation_wizard.py +503 -0
  194. empathy_software_plugin/wizards/base_wizard.py +288 -0
  195. empathy_software_plugin/wizards/book_chapter_wizard.py +519 -0
  196. empathy_software_plugin/wizards/code_review_wizard.py +606 -0
  197. empathy_software_plugin/wizards/debugging/__init__.py +50 -0
  198. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +414 -0
  199. empathy_software_plugin/wizards/debugging/config_loaders.py +442 -0
  200. empathy_software_plugin/wizards/debugging/fix_applier.py +469 -0
  201. empathy_software_plugin/wizards/debugging/language_patterns.py +383 -0
  202. empathy_software_plugin/wizards/debugging/linter_parsers.py +470 -0
  203. empathy_software_plugin/wizards/debugging/verification.py +369 -0
  204. empathy_software_plugin/wizards/enhanced_testing_wizard.py +537 -0
  205. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +816 -0
  206. empathy_software_plugin/wizards/multi_model_wizard.py +501 -0
  207. empathy_software_plugin/wizards/pattern_extraction_wizard.py +422 -0
  208. empathy_software_plugin/wizards/pattern_retriever_wizard.py +400 -0
  209. empathy_software_plugin/wizards/performance/__init__.py +9 -0
  210. empathy_software_plugin/wizards/performance/bottleneck_detector.py +221 -0
  211. empathy_software_plugin/wizards/performance/profiler_parsers.py +278 -0
  212. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +429 -0
  213. empathy_software_plugin/wizards/performance_profiling_wizard.py +305 -0
  214. empathy_software_plugin/wizards/prompt_engineering_wizard.py +425 -0
  215. empathy_software_plugin/wizards/rag_pattern_wizard.py +461 -0
  216. empathy_software_plugin/wizards/security/__init__.py +32 -0
  217. empathy_software_plugin/wizards/security/exploit_analyzer.py +290 -0
  218. empathy_software_plugin/wizards/security/owasp_patterns.py +241 -0
  219. empathy_software_plugin/wizards/security/vulnerability_scanner.py +604 -0
  220. empathy_software_plugin/wizards/security_analysis_wizard.py +322 -0
  221. empathy_software_plugin/wizards/security_learning_wizard.py +740 -0
  222. empathy_software_plugin/wizards/tech_debt_wizard.py +726 -0
  223. empathy_software_plugin/wizards/testing/__init__.py +27 -0
  224. empathy_software_plugin/wizards/testing/coverage_analyzer.py +459 -0
  225. empathy_software_plugin/wizards/testing/quality_analyzer.py +531 -0
  226. empathy_software_plugin/wizards/testing/test_suggester.py +533 -0
  227. empathy_software_plugin/wizards/testing_wizard.py +274 -0
  228. hot_reload/README.md +473 -0
  229. hot_reload/__init__.py +62 -0
  230. hot_reload/config.py +84 -0
  231. hot_reload/integration.py +228 -0
  232. hot_reload/reloader.py +298 -0
  233. hot_reload/watcher.py +179 -0
  234. hot_reload/websocket.py +176 -0
  235. scaffolding/README.md +589 -0
  236. scaffolding/__init__.py +35 -0
  237. scaffolding/__main__.py +14 -0
  238. scaffolding/cli.py +240 -0
  239. test_generator/__init__.py +38 -0
  240. test_generator/__main__.py +14 -0
  241. test_generator/cli.py +226 -0
  242. test_generator/generator.py +325 -0
  243. test_generator/risk_analyzer.py +216 -0
  244. workflow_patterns/__init__.py +33 -0
  245. workflow_patterns/behavior.py +249 -0
  246. workflow_patterns/core.py +76 -0
  247. workflow_patterns/output.py +99 -0
  248. workflow_patterns/registry.py +255 -0
  249. workflow_patterns/structural.py +288 -0
  250. workflow_scaffolding/__init__.py +11 -0
  251. workflow_scaffolding/__main__.py +12 -0
  252. workflow_scaffolding/cli.py +206 -0
  253. workflow_scaffolding/generator.py +265 -0
  254. agents/code_inspection/patterns/inspection/recurring_B112.json +0 -18
  255. agents/code_inspection/patterns/inspection/recurring_F541.json +0 -16
  256. agents/code_inspection/patterns/inspection/recurring_FORMAT.json +0 -25
  257. agents/code_inspection/patterns/inspection/recurring_bug_20250822_def456.json +0 -16
  258. agents/code_inspection/patterns/inspection/recurring_bug_20250915_abc123.json +0 -16
  259. agents/code_inspection/patterns/inspection/recurring_bug_20251212_3c5b9951.json +0 -16
  260. agents/code_inspection/patterns/inspection/recurring_bug_20251212_97c0f72f.json +0 -16
  261. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a0871d53.json +0 -16
  262. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a9b6ec41.json +0 -16
  263. agents/code_inspection/patterns/inspection/recurring_bug_null_001.json +0 -16
  264. agents/code_inspection/patterns/inspection/recurring_builtin.json +0 -16
  265. agents/compliance_anticipation_agent.py +0 -1422
  266. agents/compliance_db.py +0 -339
  267. agents/epic_integration_wizard.py +0 -530
  268. agents/notifications.py +0 -291
  269. agents/trust_building_behaviors.py +0 -872
  270. empathy_framework-3.7.0.dist-info/RECORD +0 -105
  271. {empathy_framework-3.7.0.dist-info → empathy_framework-3.8.0.dist-info}/WHEEL +0 -0
  272. {empathy_framework-3.7.0.dist-info → empathy_framework-3.8.0.dist-info}/entry_points.txt +0 -0
  273. {empathy_framework-3.7.0.dist-info → empathy_framework-3.8.0.dist-info}/licenses/LICENSE +0 -0
  274. /empathy_os/{monitoring.py → agent_monitoring.py} +0 -0
@@ -0,0 +1,720 @@
1
+ """Code Review Pipeline
2
+
3
+ A composite workflow that combines CodeReviewCrew with CodeReviewWorkflow
4
+ for comprehensive code analysis.
5
+
6
+ Modes:
7
+ - full: Run CodeReviewCrew (5 agents) + CodeReviewWorkflow
8
+ - standard: Run CodeReviewWorkflow only
9
+ - quick: Run classify + scan stages only (skip architect review)
10
+
11
+ Copyright 2025 Smart-AI-Memory
12
+ Licensed under Fair Source License 0.9
13
+ """
14
+
15
+ import asyncio
16
+ import logging
17
+ import time
18
+ from dataclasses import dataclass, field
19
+ from typing import Any
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ @dataclass
25
+ class CodeReviewPipelineResult:
26
+ """Result from CodeReviewPipeline execution."""
27
+
28
+ success: bool
29
+ verdict: str # "approve", "approve_with_suggestions", "request_changes", "reject"
30
+ quality_score: float
31
+ crew_report: dict | None
32
+ workflow_result: Any # WorkflowResult or None
33
+ combined_findings: list[dict]
34
+ critical_count: int
35
+ high_count: int
36
+ medium_count: int
37
+ agents_used: list[str]
38
+ recommendations: list[str]
39
+ blockers: list[str]
40
+ mode: str
41
+ duration_seconds: float
42
+ cost: float
43
+ metadata: dict = field(default_factory=dict)
44
+
45
+
46
+ class CodeReviewPipeline:
47
+ """Composite workflow combining CodeReviewCrew with CodeReviewWorkflow.
48
+
49
+ Provides multiple modes for different use cases:
50
+ - full: Most comprehensive (crew + workflow)
51
+ - standard: Balanced (workflow only)
52
+ - quick: Fast check (minimal stages)
53
+
54
+ Usage:
55
+ pipeline = CodeReviewPipeline(mode="full")
56
+ result = await pipeline.execute(
57
+ diff="...",
58
+ files_changed=["src/main.py"],
59
+ )
60
+
61
+ # Or use factory methods:
62
+ pipeline = CodeReviewPipeline.for_pr_review(files_changed=12)
63
+ pipeline = CodeReviewPipeline.for_quick_check()
64
+ """
65
+
66
+ def __init__(
67
+ self,
68
+ provider: str = "anthropic",
69
+ mode: str = "full",
70
+ parallel_crew: bool = True,
71
+ crew_config: dict | None = None,
72
+ **kwargs,
73
+ ):
74
+ """Initialize the pipeline.
75
+
76
+ Args:
77
+ provider: LLM provider to use (anthropic, openai, etc.)
78
+ mode: Review mode ("full", "standard", "quick")
79
+ parallel_crew: Run crew in parallel with workflow (full mode only)
80
+ crew_config: Configuration for CodeReviewCrew
81
+ **kwargs: Additional arguments (for CLI compatibility)
82
+
83
+ """
84
+ self.provider = provider
85
+ self.mode = mode
86
+ self.parallel_crew = parallel_crew
87
+ # Inject provider into crew config
88
+ self.crew_config = {"provider": provider, **(crew_config or {})}
89
+ self.crew_enabled = mode == "full"
90
+
91
+ @classmethod
92
+ def for_pr_review(cls, files_changed: int = 0) -> "CodeReviewPipeline":
93
+ """Factory for PR review - uses crew for complex PRs.
94
+
95
+ Args:
96
+ files_changed: Number of files changed in PR
97
+
98
+ Returns:
99
+ Pipeline configured for PR review complexity
100
+
101
+ """
102
+ # Use full mode for complex PRs (5+ files)
103
+ mode = "full" if files_changed > 5 else "standard"
104
+ return cls(mode=mode, parallel_crew=True)
105
+
106
+ @classmethod
107
+ def for_quick_check(cls) -> "CodeReviewPipeline":
108
+ """Quick code check without crew - minimal analysis."""
109
+ return cls(mode="quick", parallel_crew=False)
110
+
111
+ @classmethod
112
+ def for_full_review(cls) -> "CodeReviewPipeline":
113
+ """Full review with all agents and workflow stages."""
114
+ return cls(mode="full", parallel_crew=True)
115
+
116
+ async def execute(
117
+ self,
118
+ diff: str = "",
119
+ files_changed: list[str] | None = None,
120
+ target: str = "",
121
+ context: dict | None = None,
122
+ ) -> CodeReviewPipelineResult:
123
+ """Execute the code review pipeline.
124
+
125
+ Args:
126
+ diff: Code diff to review
127
+ files_changed: List of changed files
128
+ target: Target file/folder path (alternative to diff)
129
+ context: Additional context for review
130
+
131
+ Returns:
132
+ CodeReviewPipelineResult with combined analysis
133
+
134
+ """
135
+ start_time = time.time()
136
+ files_changed = files_changed or []
137
+ context = context or {}
138
+
139
+ # Initialize result collectors
140
+ crew_report: dict | None = None
141
+ workflow_result: Any = None # WorkflowResult or None
142
+ all_findings: list[dict] = []
143
+ recommendations: list[str] = []
144
+ blockers: list[str] = []
145
+ agents_used: list[str] = []
146
+ total_cost = 0.0
147
+
148
+ # Get code to review
149
+ code_to_review = diff or target
150
+
151
+ try:
152
+ if self.mode == "full":
153
+ # Run crew and workflow
154
+ crew_report, workflow_result = await self._run_full_mode(
155
+ code_to_review,
156
+ files_changed,
157
+ context,
158
+ )
159
+ elif self.mode == "standard":
160
+ # Run workflow only
161
+ workflow_result = await self._run_standard_mode(
162
+ code_to_review,
163
+ files_changed,
164
+ context,
165
+ )
166
+ else: # quick
167
+ # Run minimal workflow
168
+ workflow_result = await self._run_quick_mode(code_to_review, files_changed, context)
169
+
170
+ # Aggregate findings
171
+ if crew_report:
172
+ crew_findings = crew_report.get("findings", [])
173
+ all_findings.extend(crew_findings)
174
+ agents_used = crew_report.get("agents_used", [])
175
+
176
+ # Extract crew recommendations
177
+ for finding in crew_findings:
178
+ if finding.get("suggestion"):
179
+ recommendations.append(finding["suggestion"])
180
+
181
+ if workflow_result:
182
+ # Get workflow findings from various stages
183
+ # WorkflowResult is a dataclass, access attributes directly
184
+ wf_output = workflow_result.final_output or {}
185
+ scan_findings = (
186
+ wf_output.get("security_findings", []) if isinstance(wf_output, dict) else []
187
+ )
188
+ all_findings.extend(scan_findings)
189
+
190
+ # Get cost from workflow
191
+ cost_report = workflow_result.cost_report
192
+ if hasattr(cost_report, "total_cost"):
193
+ total_cost = cost_report.total_cost
194
+
195
+ # Deduplicate findings by (file, line, type)
196
+ all_findings = self._deduplicate_findings(all_findings)
197
+
198
+ # Count by severity
199
+ critical_count = len([f for f in all_findings if f.get("severity") == "critical"])
200
+ high_count = len([f for f in all_findings if f.get("severity") == "high"])
201
+ medium_count = len([f for f in all_findings if f.get("severity") == "medium"])
202
+
203
+ # Determine blockers
204
+ if critical_count > 0:
205
+ blockers.append(f"{critical_count} critical issue(s) found")
206
+ if high_count > 3:
207
+ blockers.append(f"{high_count} high severity issues (threshold: 3)")
208
+
209
+ # Calculate combined scores
210
+ quality_score = self._calculate_quality_score(
211
+ crew_report,
212
+ workflow_result,
213
+ all_findings,
214
+ )
215
+
216
+ # Determine verdict
217
+ verdict = self._determine_verdict(crew_report, workflow_result, quality_score, blockers)
218
+
219
+ duration = time.time() - start_time
220
+
221
+ result = CodeReviewPipelineResult(
222
+ success=True,
223
+ verdict=verdict,
224
+ quality_score=quality_score,
225
+ crew_report=crew_report,
226
+ workflow_result=workflow_result,
227
+ combined_findings=all_findings,
228
+ critical_count=critical_count,
229
+ high_count=high_count,
230
+ medium_count=medium_count,
231
+ agents_used=agents_used,
232
+ recommendations=recommendations[:10], # Top 10
233
+ blockers=blockers,
234
+ mode=self.mode,
235
+ duration_seconds=duration,
236
+ cost=total_cost,
237
+ metadata={
238
+ "files_reviewed": len(files_changed),
239
+ "total_findings": len(all_findings),
240
+ "crew_enabled": self.crew_enabled,
241
+ "parallel_crew": self.parallel_crew,
242
+ },
243
+ )
244
+
245
+ # Add formatted report for human readability
246
+ result.metadata["formatted_report"] = format_code_review_pipeline_report(result)
247
+ return result
248
+
249
+ except Exception as e:
250
+ logger.error(f"CodeReviewPipeline failed: {e}")
251
+ duration = time.time() - start_time
252
+ return CodeReviewPipelineResult(
253
+ success=False,
254
+ verdict="reject",
255
+ quality_score=0.0,
256
+ crew_report=crew_report,
257
+ workflow_result=workflow_result,
258
+ combined_findings=all_findings,
259
+ critical_count=0,
260
+ high_count=0,
261
+ medium_count=0,
262
+ agents_used=agents_used,
263
+ recommendations=[],
264
+ blockers=[f"Pipeline error: {e!s}"],
265
+ mode=self.mode,
266
+ duration_seconds=duration,
267
+ cost=total_cost,
268
+ metadata={"error": str(e)},
269
+ )
270
+
271
+ async def _run_full_mode(
272
+ self,
273
+ code_to_review: str,
274
+ files_changed: list[str],
275
+ context: dict,
276
+ ) -> tuple[dict | None, Any]: # Second element is WorkflowResult or None
277
+ """Run full mode with crew and workflow."""
278
+ from .code_review import CodeReviewWorkflow
279
+ from .code_review_adapters import (
280
+ _check_crew_available,
281
+ _get_crew_review,
282
+ crew_report_to_workflow_format,
283
+ )
284
+
285
+ crew_report: dict | None = None
286
+ workflow_result: Any = None # WorkflowResult or None
287
+
288
+ # Check if crew is available
289
+ crew_available = _check_crew_available()
290
+
291
+ if crew_available and self.parallel_crew:
292
+ # Run crew and workflow in parallel
293
+ crew_task = asyncio.create_task(
294
+ _get_crew_review(
295
+ diff=code_to_review,
296
+ files_changed=files_changed,
297
+ config=self.crew_config,
298
+ ),
299
+ )
300
+
301
+ # Run workflow (without crew - we'll merge results)
302
+ workflow = CodeReviewWorkflow(use_crew=False)
303
+ workflow_task = asyncio.create_task(
304
+ workflow.execute(
305
+ diff=code_to_review,
306
+ files_changed=files_changed,
307
+ **context,
308
+ ),
309
+ )
310
+
311
+ # Wait for both
312
+ crew_report_obj, workflow_result = await asyncio.gather(
313
+ crew_task,
314
+ workflow_task,
315
+ return_exceptions=True,
316
+ )
317
+
318
+ # Handle crew result
319
+ if isinstance(crew_report_obj, Exception):
320
+ logger.warning(f"Crew review failed: {crew_report_obj}")
321
+ elif crew_report_obj:
322
+ # crew_report_obj is CodeReviewReport after isinstance check above
323
+ crew_report = crew_report_to_workflow_format(crew_report_obj) # type: ignore[arg-type]
324
+
325
+ # Handle workflow result
326
+ if isinstance(workflow_result, Exception):
327
+ logger.warning(f"Workflow failed: {workflow_result}")
328
+ workflow_result = None
329
+
330
+ elif crew_available:
331
+ # Run sequentially
332
+ crew_report_obj = await _get_crew_review(
333
+ diff=code_to_review,
334
+ files_changed=files_changed,
335
+ config=self.crew_config,
336
+ )
337
+ if crew_report_obj:
338
+ crew_report = crew_report_to_workflow_format(crew_report_obj)
339
+
340
+ workflow = CodeReviewWorkflow(use_crew=False)
341
+ workflow_result = await workflow.execute(
342
+ diff=code_to_review,
343
+ files_changed=files_changed,
344
+ **context,
345
+ )
346
+ else:
347
+ # Crew not available, run workflow only
348
+ logger.info("CodeReviewCrew not available, running workflow only")
349
+ workflow = CodeReviewWorkflow(use_crew=False)
350
+ workflow_result = await workflow.execute(
351
+ diff=code_to_review,
352
+ files_changed=files_changed,
353
+ **context,
354
+ )
355
+
356
+ return crew_report, workflow_result
357
+
358
+ async def _run_standard_mode(
359
+ self,
360
+ code_to_review: str,
361
+ files_changed: list[str],
362
+ context: dict,
363
+ ) -> Any: # Returns WorkflowResult
364
+ """Run standard mode with workflow only."""
365
+ from .code_review import CodeReviewWorkflow
366
+
367
+ workflow = CodeReviewWorkflow(use_crew=False)
368
+ result = await workflow.execute(
369
+ diff=code_to_review,
370
+ files_changed=files_changed,
371
+ **context,
372
+ )
373
+ return result
374
+
375
+ async def _run_quick_mode(
376
+ self,
377
+ code_to_review: str,
378
+ files_changed: list[str],
379
+ context: dict,
380
+ ) -> Any: # Returns WorkflowResult
381
+ """Run quick mode with minimal stages."""
382
+ from .code_review import CodeReviewWorkflow
383
+
384
+ # Use workflow but it will skip architect_review for simple changes
385
+ workflow = CodeReviewWorkflow(
386
+ file_threshold=1000, # High threshold = skip architect review
387
+ use_crew=False,
388
+ )
389
+ result = await workflow.execute(
390
+ diff=code_to_review,
391
+ files_changed=files_changed,
392
+ is_core_module=False,
393
+ **context,
394
+ )
395
+ return result
396
+
397
+ def _deduplicate_findings(self, findings: list[dict]) -> list[dict]:
398
+ """Deduplicate findings by (file, line, type)."""
399
+ seen = set()
400
+ unique = []
401
+ for f in findings:
402
+ key = (f.get("file"), f.get("line"), f.get("type"))
403
+ if key not in seen:
404
+ seen.add(key)
405
+ unique.append(f)
406
+ return unique
407
+
408
+ def _calculate_quality_score(
409
+ self,
410
+ crew_report: dict | None,
411
+ workflow_result: Any, # WorkflowResult or None
412
+ findings: list[dict],
413
+ ) -> float:
414
+ """Calculate combined quality score."""
415
+ scores: list[float] = []
416
+ weights: list[float] = []
417
+
418
+ # Crew quality score (if available)
419
+ if crew_report:
420
+ crew_score: float = float(crew_report.get("quality_score", 100))
421
+ scores.append(crew_score)
422
+ weights.append(1.5) # Crew gets higher weight
423
+
424
+ # Workflow security score (if available)
425
+ if workflow_result:
426
+ wf_output = workflow_result.final_output or {}
427
+ security_score = (
428
+ wf_output.get("security_score", 90) if isinstance(wf_output, dict) else 90
429
+ )
430
+ scores.append(security_score)
431
+ weights.append(1.0)
432
+
433
+ # Calculate weighted average
434
+ if scores:
435
+ weighted_sum = sum(s * w for s, w in zip(scores, weights, strict=False))
436
+ quality_score = weighted_sum / sum(weights)
437
+ else:
438
+ # Fallback: deduct based on findings
439
+ quality_score = 100.0
440
+ for f in findings:
441
+ sev = f.get("severity", "medium")
442
+ if sev == "critical":
443
+ quality_score -= 25
444
+ elif sev == "high":
445
+ quality_score -= 15
446
+ elif sev == "medium":
447
+ quality_score -= 5
448
+ elif sev == "low":
449
+ quality_score -= 2
450
+
451
+ return max(0.0, min(100.0, quality_score))
452
+
453
+ def _determine_verdict(
454
+ self,
455
+ crew_report: dict | None,
456
+ workflow_result: Any, # WorkflowResult or None
457
+ quality_score: float,
458
+ blockers: list[str],
459
+ ) -> str:
460
+ """Determine final verdict based on all inputs."""
461
+ # Start with most severe verdict
462
+ verdict_priority = ["reject", "request_changes", "approve_with_suggestions", "approve"]
463
+
464
+ verdicts = []
465
+
466
+ # Crew verdict
467
+ if crew_report:
468
+ crew_verdict = crew_report.get("verdict", "approve")
469
+ verdicts.append(crew_verdict)
470
+
471
+ # Workflow verdict (from architect review)
472
+ if workflow_result:
473
+ wf_output = workflow_result.final_output or {}
474
+ wf_verdict = (
475
+ wf_output.get("verdict", "approve") if isinstance(wf_output, dict) else "approve"
476
+ )
477
+ verdicts.append(wf_verdict)
478
+
479
+ # Score-based verdict
480
+ if quality_score < 50:
481
+ verdicts.append("reject")
482
+ elif quality_score < 70:
483
+ verdicts.append("request_changes")
484
+ elif quality_score < 90:
485
+ verdicts.append("approve_with_suggestions")
486
+ else:
487
+ verdicts.append("approve")
488
+
489
+ # Blocker-based verdict
490
+ if blockers:
491
+ verdicts.append("request_changes")
492
+
493
+ # Take most severe
494
+ for v in verdict_priority:
495
+ if v in verdicts:
496
+ return v
497
+
498
+ return "approve"
499
+
500
+
501
+ # CLI entry point
502
+ def main():
503
+ """Run CodeReviewPipeline from command line."""
504
+ import argparse
505
+
506
+ parser = argparse.ArgumentParser(description="Code Review Pipeline")
507
+ parser.add_argument("--diff", "-d", help="Code diff to review")
508
+ parser.add_argument("--file", "-f", help="File to review")
509
+ parser.add_argument(
510
+ "--mode",
511
+ "-m",
512
+ default="full",
513
+ choices=["full", "standard", "quick"],
514
+ help="Review mode",
515
+ )
516
+ parser.add_argument(
517
+ "--parallel/--sequential",
518
+ dest="parallel",
519
+ default=True,
520
+ help="Run crew in parallel",
521
+ )
522
+
523
+ args = parser.parse_args()
524
+
525
+ async def run():
526
+ pipeline = CodeReviewPipeline(mode=args.mode, parallel_crew=args.parallel)
527
+
528
+ diff = args.diff or ""
529
+ if args.file:
530
+ try:
531
+ with open(args.file) as f:
532
+ diff = f.read()
533
+ except FileNotFoundError:
534
+ print(f"File not found: {args.file}")
535
+ return
536
+
537
+ result = await pipeline.execute(diff=diff)
538
+
539
+ print("\n" + "=" * 60)
540
+ print("CODE REVIEW PIPELINE RESULTS")
541
+ print("=" * 60)
542
+ print(f"Mode: {result.mode}")
543
+ print(f"Verdict: {result.verdict.upper()}")
544
+ print(f"Quality Score: {result.quality_score:.1f}/100")
545
+ print(f"Duration: {result.duration_seconds * 1000:.0f}ms")
546
+ print(f"Cost: ${result.cost:.4f}")
547
+
548
+ if result.agents_used:
549
+ print(f"\nAgents Used: {', '.join(result.agents_used)}")
550
+
551
+ print(f"\nFindings: {len(result.combined_findings)} total")
552
+ print(f" Critical: {result.critical_count}")
553
+ print(f" High: {result.high_count}")
554
+ print(f" Medium: {result.medium_count}")
555
+
556
+ if result.blockers:
557
+ print("\nBlockers:")
558
+ for b in result.blockers:
559
+ print(f" - {b}")
560
+
561
+ if result.recommendations[:5]:
562
+ print("\nTop Recommendations:")
563
+ for r in result.recommendations[:5]:
564
+ print(f" - {r[:100]}...")
565
+
566
+ asyncio.run(run())
567
+
568
+
569
+ def format_code_review_pipeline_report(result: CodeReviewPipelineResult) -> str:
570
+ """Format code review pipeline result as a human-readable report.
571
+
572
+ Args:
573
+ result: The CodeReviewPipelineResult dataclass
574
+
575
+ Returns:
576
+ Formatted report string
577
+
578
+ """
579
+ lines = []
580
+
581
+ # Header with verdict
582
+ verdict_emoji = {
583
+ "approve": "✅",
584
+ "approve_with_suggestions": "🟡",
585
+ "request_changes": "🟠",
586
+ "reject": "🔴",
587
+ }
588
+ emoji = verdict_emoji.get(result.verdict, "⚪")
589
+
590
+ lines.append("=" * 60)
591
+ lines.append("CODE REVIEW REPORT")
592
+ lines.append("=" * 60)
593
+ lines.append("")
594
+
595
+ # Verdict banner
596
+ lines.append("-" * 60)
597
+ lines.append(f"{emoji} VERDICT: {result.verdict.upper().replace('_', ' ')}")
598
+ lines.append("-" * 60)
599
+ lines.append(f"Mode: {result.mode}")
600
+ lines.append("")
601
+
602
+ # Quality score with visual bar
603
+ score = result.quality_score
604
+ bar = "█" * int(score / 10) + "░" * (10 - int(score / 10))
605
+ quality_label = (
606
+ "EXCELLENT"
607
+ if score >= 90
608
+ else "GOOD"
609
+ if score >= 70
610
+ else "NEEDS WORK"
611
+ if score >= 50
612
+ else "POOR"
613
+ )
614
+ lines.append("-" * 60)
615
+ lines.append("QUALITY SCORE")
616
+ lines.append("-" * 60)
617
+ lines.append(f"[{bar}] {score:.0f}/100 ({quality_label})")
618
+ lines.append("")
619
+
620
+ # Crew summary (if available)
621
+ if result.crew_report and result.crew_report.get("summary"):
622
+ lines.append("-" * 60)
623
+ lines.append("SUMMARY")
624
+ lines.append("-" * 60)
625
+ summary = result.crew_report["summary"]
626
+ # Word wrap the summary
627
+ words = summary.split()
628
+ current_line = ""
629
+ for word in words:
630
+ if len(current_line) + len(word) + 1 <= 58:
631
+ current_line += (" " if current_line else "") + word
632
+ else:
633
+ lines.append(current_line)
634
+ current_line = word
635
+ if current_line:
636
+ lines.append(current_line)
637
+ lines.append("")
638
+
639
+ # Findings summary
640
+ total_findings = len(result.combined_findings)
641
+ lines.append("-" * 60)
642
+ lines.append("FINDINGS")
643
+ lines.append("-" * 60)
644
+
645
+ # Show files reviewed from metadata
646
+ files_reviewed = result.metadata.get("files_reviewed", 0)
647
+ if files_reviewed > 0:
648
+ lines.append(f"Files Reviewed: {files_reviewed}")
649
+
650
+ if total_findings > 0 or result.critical_count > 0 or result.high_count > 0:
651
+ lines.append(f"Issues Found: {total_findings}")
652
+ lines.append(f" 🔴 Critical: {result.critical_count}")
653
+ lines.append(f" 🟠 High: {result.high_count}")
654
+ lines.append(f" 🟡 Medium: {result.medium_count}")
655
+ lines.append("")
656
+
657
+ # Show top critical/high findings
658
+ if result.combined_findings:
659
+ critical_high = [
660
+ f for f in result.combined_findings if f.get("severity") in ("critical", "high")
661
+ ][:5]
662
+ if critical_high:
663
+ lines.append("Top Issues:")
664
+ for i, finding in enumerate(critical_high, 1):
665
+ severity = finding.get("severity", "unknown")
666
+ title = finding.get(
667
+ "title",
668
+ finding.get("message", finding.get("description", "Issue found")),
669
+ )
670
+ emoji_f = "🔴" if severity == "critical" else "🟠"
671
+ if len(str(title)) > 50:
672
+ title = str(title)[:47] + "..."
673
+ lines.append(f" {emoji_f} {i}. {title}")
674
+ lines.append("")
675
+ else:
676
+ lines.append("✅ No issues found!")
677
+ lines.append("")
678
+
679
+ # Blockers
680
+ if result.blockers:
681
+ lines.append("-" * 60)
682
+ lines.append("🚫 BLOCKERS")
683
+ lines.append("-" * 60)
684
+ for blocker in result.blockers:
685
+ lines.append(f" • {blocker}")
686
+ lines.append("")
687
+
688
+ # Recommendations
689
+ if result.recommendations:
690
+ lines.append("-" * 60)
691
+ lines.append("RECOMMENDATIONS")
692
+ lines.append("-" * 60)
693
+ for i, rec in enumerate(result.recommendations[:5], 1):
694
+ rec_str = str(rec)
695
+ if len(rec_str) > 55:
696
+ rec_str = rec_str[:52] + "..."
697
+ lines.append(f" {i}. {rec_str}")
698
+ if len(result.recommendations) > 5:
699
+ lines.append(f" ... and {len(result.recommendations) - 5} more")
700
+ lines.append("")
701
+
702
+ # Agents used
703
+ if result.agents_used:
704
+ lines.append("-" * 60)
705
+ lines.append("AGENTS USED")
706
+ lines.append("-" * 60)
707
+ lines.append(f" {', '.join(result.agents_used)}")
708
+ lines.append("")
709
+
710
+ # Footer
711
+ lines.append("=" * 60)
712
+ duration_ms = result.duration_seconds * 1000
713
+ lines.append(f"Review completed in {duration_ms:.0f}ms | Cost: ${result.cost:.4f}")
714
+ lines.append("=" * 60)
715
+
716
+ return "\n".join(lines)
717
+
718
+
719
+ if __name__ == "__main__":
720
+ main()