empathy-framework 3.7.0__py3-none-any.whl → 3.7.1__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 (267) 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.7.1.dist-info}/METADATA +20 -2
  4. empathy_framework-3.7.1.dist-info/RECORD +327 -0
  5. {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.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/cli.py +118 -8
  64. empathy_os/cli_unified.py +121 -1
  65. empathy_os/config/__init__.py +63 -0
  66. empathy_os/config/xml_config.py +239 -0
  67. empathy_os/dashboard/__init__.py +15 -0
  68. empathy_os/dashboard/server.py +743 -0
  69. empathy_os/memory/__init__.py +195 -0
  70. empathy_os/memory/claude_memory.py +466 -0
  71. empathy_os/memory/config.py +224 -0
  72. empathy_os/memory/control_panel.py +1298 -0
  73. empathy_os/memory/edges.py +179 -0
  74. empathy_os/memory/graph.py +567 -0
  75. empathy_os/memory/long_term.py +1193 -0
  76. empathy_os/memory/nodes.py +179 -0
  77. empathy_os/memory/redis_bootstrap.py +540 -0
  78. empathy_os/memory/security/__init__.py +31 -0
  79. empathy_os/memory/security/audit_logger.py +930 -0
  80. empathy_os/memory/security/pii_scrubber.py +640 -0
  81. empathy_os/memory/security/secrets_detector.py +678 -0
  82. empathy_os/memory/short_term.py +2119 -0
  83. empathy_os/memory/storage/__init__.py +15 -0
  84. empathy_os/memory/summary_index.py +583 -0
  85. empathy_os/memory/unified.py +619 -0
  86. empathy_os/metrics/__init__.py +12 -0
  87. empathy_os/metrics/prompt_metrics.py +190 -0
  88. empathy_os/models/__init__.py +136 -0
  89. empathy_os/models/__main__.py +13 -0
  90. empathy_os/models/cli.py +655 -0
  91. empathy_os/models/empathy_executor.py +354 -0
  92. empathy_os/models/executor.py +252 -0
  93. empathy_os/models/fallback.py +671 -0
  94. empathy_os/models/provider_config.py +563 -0
  95. empathy_os/models/registry.py +382 -0
  96. empathy_os/models/tasks.py +302 -0
  97. empathy_os/models/telemetry.py +548 -0
  98. empathy_os/models/token_estimator.py +378 -0
  99. empathy_os/models/validation.py +274 -0
  100. empathy_os/monitoring/__init__.py +52 -0
  101. empathy_os/monitoring/alerts.py +23 -0
  102. empathy_os/monitoring/alerts_cli.py +268 -0
  103. empathy_os/monitoring/multi_backend.py +271 -0
  104. empathy_os/monitoring/otel_backend.py +363 -0
  105. empathy_os/optimization/__init__.py +19 -0
  106. empathy_os/optimization/context_optimizer.py +272 -0
  107. empathy_os/plugins/__init__.py +28 -0
  108. empathy_os/plugins/base.py +361 -0
  109. empathy_os/plugins/registry.py +268 -0
  110. empathy_os/project_index/__init__.py +30 -0
  111. empathy_os/project_index/cli.py +335 -0
  112. empathy_os/project_index/crew_integration.py +430 -0
  113. empathy_os/project_index/index.py +425 -0
  114. empathy_os/project_index/models.py +501 -0
  115. empathy_os/project_index/reports.py +473 -0
  116. empathy_os/project_index/scanner.py +538 -0
  117. empathy_os/prompts/__init__.py +61 -0
  118. empathy_os/prompts/config.py +77 -0
  119. empathy_os/prompts/context.py +177 -0
  120. empathy_os/prompts/parser.py +285 -0
  121. empathy_os/prompts/registry.py +313 -0
  122. empathy_os/prompts/templates.py +208 -0
  123. empathy_os/resilience/__init__.py +56 -0
  124. empathy_os/resilience/circuit_breaker.py +256 -0
  125. empathy_os/resilience/fallback.py +179 -0
  126. empathy_os/resilience/health.py +300 -0
  127. empathy_os/resilience/retry.py +209 -0
  128. empathy_os/resilience/timeout.py +135 -0
  129. empathy_os/routing/__init__.py +43 -0
  130. empathy_os/routing/chain_executor.py +433 -0
  131. empathy_os/routing/classifier.py +217 -0
  132. empathy_os/routing/smart_router.py +234 -0
  133. empathy_os/routing/wizard_registry.py +307 -0
  134. empathy_os/trust/__init__.py +28 -0
  135. empathy_os/trust/circuit_breaker.py +579 -0
  136. empathy_os/validation/__init__.py +19 -0
  137. empathy_os/validation/xml_validator.py +281 -0
  138. empathy_os/wizard_factory_cli.py +170 -0
  139. empathy_os/workflows/__init__.py +360 -0
  140. empathy_os/workflows/base.py +1530 -0
  141. empathy_os/workflows/bug_predict.py +962 -0
  142. empathy_os/workflows/code_review.py +960 -0
  143. empathy_os/workflows/code_review_adapters.py +310 -0
  144. empathy_os/workflows/code_review_pipeline.py +720 -0
  145. empathy_os/workflows/config.py +600 -0
  146. empathy_os/workflows/dependency_check.py +648 -0
  147. empathy_os/workflows/document_gen.py +1069 -0
  148. empathy_os/workflows/documentation_orchestrator.py +1205 -0
  149. empathy_os/workflows/health_check.py +679 -0
  150. empathy_os/workflows/keyboard_shortcuts/__init__.py +39 -0
  151. empathy_os/workflows/keyboard_shortcuts/generators.py +386 -0
  152. empathy_os/workflows/keyboard_shortcuts/parsers.py +414 -0
  153. empathy_os/workflows/keyboard_shortcuts/prompts.py +295 -0
  154. empathy_os/workflows/keyboard_shortcuts/schema.py +193 -0
  155. empathy_os/workflows/keyboard_shortcuts/workflow.py +505 -0
  156. empathy_os/workflows/manage_documentation.py +804 -0
  157. empathy_os/workflows/new_sample_workflow1.py +146 -0
  158. empathy_os/workflows/new_sample_workflow1_README.md +150 -0
  159. empathy_os/workflows/perf_audit.py +687 -0
  160. empathy_os/workflows/pr_review.py +748 -0
  161. empathy_os/workflows/progress.py +445 -0
  162. empathy_os/workflows/progress_server.py +322 -0
  163. empathy_os/workflows/refactor_plan.py +691 -0
  164. empathy_os/workflows/release_prep.py +808 -0
  165. empathy_os/workflows/research_synthesis.py +404 -0
  166. empathy_os/workflows/secure_release.py +585 -0
  167. empathy_os/workflows/security_adapters.py +297 -0
  168. empathy_os/workflows/security_audit.py +1050 -0
  169. empathy_os/workflows/step_config.py +234 -0
  170. empathy_os/workflows/test5.py +125 -0
  171. empathy_os/workflows/test5_README.md +158 -0
  172. empathy_os/workflows/test_gen.py +1855 -0
  173. empathy_os/workflows/test_lifecycle.py +526 -0
  174. empathy_os/workflows/test_maintenance.py +626 -0
  175. empathy_os/workflows/test_maintenance_cli.py +590 -0
  176. empathy_os/workflows/test_maintenance_crew.py +821 -0
  177. empathy_os/workflows/xml_enhanced_crew.py +285 -0
  178. empathy_software_plugin/cli/__init__.py +120 -0
  179. empathy_software_plugin/cli/inspect.py +362 -0
  180. empathy_software_plugin/cli.py +3 -1
  181. empathy_software_plugin/wizards/__init__.py +42 -0
  182. empathy_software_plugin/wizards/advanced_debugging_wizard.py +392 -0
  183. empathy_software_plugin/wizards/agent_orchestration_wizard.py +511 -0
  184. empathy_software_plugin/wizards/ai_collaboration_wizard.py +503 -0
  185. empathy_software_plugin/wizards/ai_context_wizard.py +441 -0
  186. empathy_software_plugin/wizards/ai_documentation_wizard.py +503 -0
  187. empathy_software_plugin/wizards/base_wizard.py +288 -0
  188. empathy_software_plugin/wizards/book_chapter_wizard.py +519 -0
  189. empathy_software_plugin/wizards/code_review_wizard.py +606 -0
  190. empathy_software_plugin/wizards/debugging/__init__.py +50 -0
  191. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +414 -0
  192. empathy_software_plugin/wizards/debugging/config_loaders.py +442 -0
  193. empathy_software_plugin/wizards/debugging/fix_applier.py +469 -0
  194. empathy_software_plugin/wizards/debugging/language_patterns.py +383 -0
  195. empathy_software_plugin/wizards/debugging/linter_parsers.py +470 -0
  196. empathy_software_plugin/wizards/debugging/verification.py +369 -0
  197. empathy_software_plugin/wizards/enhanced_testing_wizard.py +537 -0
  198. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +816 -0
  199. empathy_software_plugin/wizards/multi_model_wizard.py +501 -0
  200. empathy_software_plugin/wizards/pattern_extraction_wizard.py +422 -0
  201. empathy_software_plugin/wizards/pattern_retriever_wizard.py +400 -0
  202. empathy_software_plugin/wizards/performance/__init__.py +9 -0
  203. empathy_software_plugin/wizards/performance/bottleneck_detector.py +221 -0
  204. empathy_software_plugin/wizards/performance/profiler_parsers.py +278 -0
  205. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +429 -0
  206. empathy_software_plugin/wizards/performance_profiling_wizard.py +305 -0
  207. empathy_software_plugin/wizards/prompt_engineering_wizard.py +425 -0
  208. empathy_software_plugin/wizards/rag_pattern_wizard.py +461 -0
  209. empathy_software_plugin/wizards/security/__init__.py +32 -0
  210. empathy_software_plugin/wizards/security/exploit_analyzer.py +290 -0
  211. empathy_software_plugin/wizards/security/owasp_patterns.py +241 -0
  212. empathy_software_plugin/wizards/security/vulnerability_scanner.py +604 -0
  213. empathy_software_plugin/wizards/security_analysis_wizard.py +322 -0
  214. empathy_software_plugin/wizards/security_learning_wizard.py +740 -0
  215. empathy_software_plugin/wizards/tech_debt_wizard.py +726 -0
  216. empathy_software_plugin/wizards/testing/__init__.py +27 -0
  217. empathy_software_plugin/wizards/testing/coverage_analyzer.py +459 -0
  218. empathy_software_plugin/wizards/testing/quality_analyzer.py +531 -0
  219. empathy_software_plugin/wizards/testing/test_suggester.py +533 -0
  220. empathy_software_plugin/wizards/testing_wizard.py +274 -0
  221. hot_reload/README.md +473 -0
  222. hot_reload/__init__.py +62 -0
  223. hot_reload/config.py +84 -0
  224. hot_reload/integration.py +228 -0
  225. hot_reload/reloader.py +298 -0
  226. hot_reload/watcher.py +179 -0
  227. hot_reload/websocket.py +176 -0
  228. scaffolding/README.md +589 -0
  229. scaffolding/__init__.py +35 -0
  230. scaffolding/__main__.py +14 -0
  231. scaffolding/cli.py +240 -0
  232. test_generator/__init__.py +38 -0
  233. test_generator/__main__.py +14 -0
  234. test_generator/cli.py +226 -0
  235. test_generator/generator.py +325 -0
  236. test_generator/risk_analyzer.py +216 -0
  237. workflow_patterns/__init__.py +33 -0
  238. workflow_patterns/behavior.py +249 -0
  239. workflow_patterns/core.py +76 -0
  240. workflow_patterns/output.py +99 -0
  241. workflow_patterns/registry.py +255 -0
  242. workflow_patterns/structural.py +288 -0
  243. workflow_scaffolding/__init__.py +11 -0
  244. workflow_scaffolding/__main__.py +12 -0
  245. workflow_scaffolding/cli.py +206 -0
  246. workflow_scaffolding/generator.py +265 -0
  247. agents/code_inspection/patterns/inspection/recurring_B112.json +0 -18
  248. agents/code_inspection/patterns/inspection/recurring_F541.json +0 -16
  249. agents/code_inspection/patterns/inspection/recurring_FORMAT.json +0 -25
  250. agents/code_inspection/patterns/inspection/recurring_bug_20250822_def456.json +0 -16
  251. agents/code_inspection/patterns/inspection/recurring_bug_20250915_abc123.json +0 -16
  252. agents/code_inspection/patterns/inspection/recurring_bug_20251212_3c5b9951.json +0 -16
  253. agents/code_inspection/patterns/inspection/recurring_bug_20251212_97c0f72f.json +0 -16
  254. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a0871d53.json +0 -16
  255. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a9b6ec41.json +0 -16
  256. agents/code_inspection/patterns/inspection/recurring_bug_null_001.json +0 -16
  257. agents/code_inspection/patterns/inspection/recurring_builtin.json +0 -16
  258. agents/compliance_anticipation_agent.py +0 -1422
  259. agents/compliance_db.py +0 -339
  260. agents/epic_integration_wizard.py +0 -530
  261. agents/notifications.py +0 -291
  262. agents/trust_building_behaviors.py +0 -872
  263. empathy_framework-3.7.0.dist-info/RECORD +0 -105
  264. {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/WHEEL +0 -0
  265. {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/entry_points.txt +0 -0
  266. {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/licenses/LICENSE +0 -0
  267. /empathy_os/{monitoring.py → agent_monitoring.py} +0 -0
@@ -0,0 +1,679 @@
1
+ """Health Check Workflow
2
+
3
+ A workflow wrapper for HealthCheckCrew that provides project health
4
+ diagnosis and fixing capabilities.
5
+
6
+ Uses XML-enhanced prompts and 5 specialized agents:
7
+ 1. Health Lead - Coordinator
8
+ 2. Lint Fixer - Ruff analysis and fixes
9
+ 3. Type Resolver - Mypy analysis
10
+ 4. Test Doctor - Pytest analysis
11
+ 5. Dep Auditor - Dependency security
12
+
13
+ Copyright 2025 Smart-AI-Memory
14
+ Licensed under Fair Source License 0.9
15
+ """
16
+
17
+ import json
18
+ import logging
19
+ import os
20
+ import time
21
+ from dataclasses import dataclass, field
22
+ from datetime import datetime
23
+ from typing import Any
24
+
25
+ from .base import BaseWorkflow, ModelTier
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ @dataclass
31
+ class HealthCheckResult:
32
+ """Result from HealthCheckWorkflow execution."""
33
+
34
+ success: bool
35
+ health_score: float
36
+ is_healthy: bool
37
+ issues: list[dict]
38
+ fixes: list[dict]
39
+ checks_run: dict[str, Any]
40
+ agents_used: list[str]
41
+ critical_count: int
42
+ high_count: int
43
+ applied_fixes_count: int
44
+ duration_seconds: float
45
+ cost: float
46
+ metadata: dict = field(default_factory=dict)
47
+
48
+
49
+ class HealthCheckWorkflow(BaseWorkflow):
50
+ """Workflow wrapper for HealthCheckCrew.
51
+
52
+ Provides comprehensive project health diagnosis and fixing using
53
+ 5 specialized agents with XML-enhanced prompts.
54
+
55
+ Checks:
56
+ - Lint (ruff) - Code style and quality
57
+ - Types (mypy) - Type safety
58
+ - Tests (pytest) - Test suite health
59
+ - Dependencies - Security vulnerabilities and outdated packages
60
+
61
+ Usage:
62
+ workflow = HealthCheckWorkflow()
63
+ result = await workflow.execute(path=".", auto_fix=True)
64
+
65
+ if result.is_healthy:
66
+ print("Project is healthy!")
67
+ else:
68
+ print(f"Health Score: {result.health_score}/100")
69
+ for issue in result.issues:
70
+ print(f" - {issue['title']}")
71
+ """
72
+
73
+ name = "health-check"
74
+ description = "Project health diagnosis and fixing with 5-agent crew"
75
+
76
+ stages = ["diagnose", "fix"]
77
+ tier_map = {
78
+ "diagnose": ModelTier.CAPABLE,
79
+ "fix": ModelTier.CAPABLE,
80
+ }
81
+
82
+ def __init__(
83
+ self,
84
+ auto_fix: bool = False,
85
+ check_lint: bool = True,
86
+ check_types: bool = True,
87
+ check_tests: bool = True,
88
+ check_deps: bool = True,
89
+ xml_prompts: bool = True,
90
+ **kwargs: Any,
91
+ ):
92
+ """Initialize health check workflow.
93
+
94
+ Args:
95
+ auto_fix: Automatically apply safe fixes
96
+ check_lint: Run lint checks
97
+ check_types: Run type checks
98
+ check_tests: Run test checks
99
+ check_deps: Run dependency checks
100
+ xml_prompts: Use XML-enhanced prompts
101
+ **kwargs: Additional arguments passed to BaseWorkflow
102
+
103
+ """
104
+ super().__init__(**kwargs)
105
+ self.auto_fix = auto_fix
106
+ self.check_lint = check_lint
107
+ self.check_types = check_types
108
+ self.check_tests = check_tests
109
+ self.check_deps = check_deps
110
+ self.xml_prompts = xml_prompts
111
+ self._crew: Any = None
112
+ self._crew_available = False
113
+
114
+ def _check_crew_available(self) -> bool:
115
+ """Check if HealthCheckCrew is available."""
116
+ import importlib.util
117
+
118
+ return importlib.util.find_spec("empathy_llm_toolkit.agent_factory.crews") is not None
119
+
120
+ async def _initialize_crew(self) -> None:
121
+ """Initialize the HealthCheckCrew."""
122
+ if self._crew is not None:
123
+ return
124
+
125
+ try:
126
+ from empathy_llm_toolkit.agent_factory.crews import HealthCheckConfig, HealthCheckCrew
127
+
128
+ config = HealthCheckConfig(
129
+ check_lint=self.check_lint,
130
+ check_types=self.check_types,
131
+ check_tests=self.check_tests,
132
+ check_deps=self.check_deps,
133
+ auto_fix=self.auto_fix,
134
+ xml_prompts_enabled=self.xml_prompts,
135
+ )
136
+ self._crew = HealthCheckCrew(config=config)
137
+ self._crew_available = True
138
+ logger.info("HealthCheckCrew initialized successfully")
139
+ except ImportError as e:
140
+ logger.warning(f"HealthCheckCrew not available: {e}")
141
+ self._crew_available = False
142
+
143
+ async def run_stage(
144
+ self,
145
+ stage_name: str,
146
+ tier: ModelTier,
147
+ input_data: Any,
148
+ ) -> tuple[Any, int, int]:
149
+ """Route to specific stage implementation."""
150
+ if stage_name == "diagnose":
151
+ return await self._diagnose(input_data, tier)
152
+ if stage_name == "fix":
153
+ return await self._fix(input_data, tier)
154
+ raise ValueError(f"Unknown stage: {stage_name}")
155
+
156
+ async def _diagnose(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
157
+ """Run health diagnosis using HealthCheckCrew.
158
+
159
+ Falls back to basic checks if crew not available.
160
+ """
161
+ path = input_data.get("path", ".")
162
+
163
+ # Initialize crew
164
+ await self._initialize_crew()
165
+
166
+ if self._crew_available and self._crew:
167
+ # Run crew-based health check
168
+ report = await self._crew.check(
169
+ path=path,
170
+ auto_fix=False, # Don't auto-fix in diagnose stage
171
+ )
172
+
173
+ result = {
174
+ "health_score": report.health_score,
175
+ "is_healthy": report.is_healthy,
176
+ "issues": [i.to_dict() for i in report.issues],
177
+ "checks_run": report.checks_run,
178
+ "agents_used": report.agents_used,
179
+ "crew_available": True,
180
+ }
181
+
182
+ input_tokens = 500 # Estimate
183
+ output_tokens = len(str(result)) // 4
184
+
185
+ return (
186
+ {
187
+ "diagnosis": result,
188
+ **input_data,
189
+ },
190
+ input_tokens,
191
+ output_tokens,
192
+ )
193
+
194
+ # Fallback to basic checks without crew
195
+ result = await self._basic_health_check(path)
196
+ result["crew_available"] = False
197
+
198
+ return (
199
+ {
200
+ "diagnosis": result,
201
+ **input_data,
202
+ },
203
+ 100,
204
+ len(str(result)) // 4,
205
+ )
206
+
207
+ async def _basic_health_check(self, path: str) -> dict:
208
+ """Basic health check without crew (fallback)."""
209
+ import subprocess
210
+
211
+ issues = []
212
+ checks_run: dict[str, dict[str, Any]] = {}
213
+ health_score = 100.0
214
+
215
+ # Lint check
216
+ if self.check_lint:
217
+ try:
218
+ result = subprocess.run(
219
+ ["python", "-m", "ruff", "check", path],
220
+ check=False,
221
+ capture_output=True,
222
+ text=True,
223
+ timeout=60,
224
+ )
225
+ lint_errors = result.stdout.count("\n")
226
+ checks_run["lint"] = {"passed": result.returncode == 0}
227
+ if result.returncode != 0:
228
+ health_score -= min(20, lint_errors)
229
+ issues.append(
230
+ {
231
+ "title": f"Lint: {lint_errors} issues found",
232
+ "category": "lint",
233
+ "severity": "medium",
234
+ },
235
+ )
236
+ except subprocess.TimeoutExpired:
237
+ logger.warning("Lint check timed out after 60s")
238
+ checks_run["lint"] = {"passed": True, "skipped": True, "reason": "timeout"}
239
+ except FileNotFoundError:
240
+ logger.info("Ruff not installed, skipping lint check")
241
+ checks_run["lint"] = {"passed": True, "skipped": True, "reason": "tool_missing"}
242
+ except subprocess.SubprocessError as e:
243
+ logger.error(f"Lint check subprocess error: {e}")
244
+ checks_run["lint"] = {"passed": True, "skipped": True, "reason": "subprocess_error"}
245
+ except Exception:
246
+ # INTENTIONAL: Graceful degradation - health checks are best-effort
247
+ logger.exception("Unexpected error in lint check")
248
+ checks_run["lint"] = {"passed": True, "skipped": True, "reason": "unexpected_error"}
249
+
250
+ # Type check
251
+ if self.check_types:
252
+ try:
253
+ result = subprocess.run(
254
+ ["python", "-m", "mypy", path, "--ignore-missing-imports"],
255
+ check=False,
256
+ capture_output=True,
257
+ text=True,
258
+ timeout=120,
259
+ )
260
+ type_errors = result.stdout.count("error:")
261
+ checks_run["types"] = {"passed": result.returncode == 0}
262
+ if result.returncode != 0:
263
+ health_score -= min(20, type_errors * 2)
264
+ issues.append(
265
+ {
266
+ "title": f"Types: {type_errors} errors found",
267
+ "category": "types",
268
+ "severity": "medium",
269
+ },
270
+ )
271
+ except subprocess.TimeoutExpired:
272
+ logger.warning("Type check timed out after 120s")
273
+ checks_run["types"] = {"passed": True, "skipped": True, "reason": "timeout"}
274
+ except FileNotFoundError:
275
+ logger.info("Mypy not installed, skipping type check")
276
+ checks_run["types"] = {"passed": True, "skipped": True, "reason": "tool_missing"}
277
+ except subprocess.SubprocessError as e:
278
+ logger.error(f"Type check subprocess error: {e}")
279
+ checks_run["types"] = {
280
+ "passed": True,
281
+ "skipped": True,
282
+ "reason": "subprocess_error",
283
+ }
284
+ except Exception:
285
+ # INTENTIONAL: Graceful degradation - health checks are best-effort
286
+ logger.exception("Unexpected error in type check")
287
+ checks_run["types"] = {
288
+ "passed": True,
289
+ "skipped": True,
290
+ "reason": "unexpected_error",
291
+ }
292
+
293
+ # Test check
294
+ if self.check_tests:
295
+ try:
296
+ # Use "tests/" directory or rely on pytest.ini testpaths
297
+ # Don't pass path="." which overrides testpaths and causes collection errors
298
+ result = subprocess.run(
299
+ ["python", "-m", "pytest", "tests/", "-q", "--tb=no", "--no-cov"],
300
+ check=False,
301
+ capture_output=True,
302
+ text=True,
303
+ timeout=180,
304
+ )
305
+ checks_run["tests"] = {"passed": result.returncode == 0}
306
+ if result.returncode != 0:
307
+ health_score -= 25
308
+ issues.append(
309
+ {
310
+ "title": "Tests: Some tests failing",
311
+ "category": "tests",
312
+ "severity": "high",
313
+ },
314
+ )
315
+ except subprocess.TimeoutExpired:
316
+ logger.warning("Test check timed out after 180s")
317
+ checks_run["tests"] = {"passed": True, "skipped": True, "reason": "timeout"}
318
+ except FileNotFoundError:
319
+ logger.info("Pytest not installed, skipping test check")
320
+ checks_run["tests"] = {"passed": True, "skipped": True, "reason": "tool_missing"}
321
+ except subprocess.SubprocessError as e:
322
+ logger.error(f"Test check subprocess error: {e}")
323
+ checks_run["tests"] = {
324
+ "passed": True,
325
+ "skipped": True,
326
+ "reason": "subprocess_error",
327
+ }
328
+ except Exception:
329
+ # INTENTIONAL: Graceful degradation - health checks are best-effort
330
+ logger.exception("Unexpected error in test check")
331
+ checks_run["tests"] = {
332
+ "passed": True,
333
+ "skipped": True,
334
+ "reason": "unexpected_error",
335
+ }
336
+
337
+ return {
338
+ "health_score": max(0, health_score),
339
+ "is_healthy": health_score >= 80,
340
+ "issues": issues,
341
+ "checks_run": checks_run,
342
+ "agents_used": [],
343
+ }
344
+
345
+ async def _fix(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
346
+ """Apply fixes for identified issues.
347
+
348
+ Only runs if auto_fix is enabled and issues were found.
349
+ """
350
+ path = input_data.get("path", ".")
351
+ fixes = []
352
+
353
+ if not self.auto_fix:
354
+ return (
355
+ {
356
+ "fixes": [],
357
+ "auto_fix_enabled": False,
358
+ **input_data,
359
+ },
360
+ 0,
361
+ 0,
362
+ )
363
+
364
+ # Use crew if available
365
+ if self._crew_available and self._crew:
366
+ report = await self._crew.check(
367
+ path=path,
368
+ auto_fix=True,
369
+ )
370
+ fixes = [f.to_dict() for f in report.fixes]
371
+ # Basic auto-fix with ruff
372
+ elif self.check_lint:
373
+ import subprocess
374
+
375
+ try:
376
+ result = subprocess.run(
377
+ ["python", "-m", "ruff", "check", path, "--fix"],
378
+ check=False,
379
+ capture_output=True,
380
+ text=True,
381
+ timeout=60,
382
+ )
383
+ if "fixed" in result.stdout.lower():
384
+ fixes.append(
385
+ {
386
+ "title": "Lint auto-fixes applied",
387
+ "category": "lint",
388
+ "status": "applied",
389
+ },
390
+ )
391
+ except subprocess.TimeoutExpired:
392
+ logger.warning("Ruff auto-fix timed out after 60s")
393
+ except FileNotFoundError:
394
+ logger.info("Ruff not installed, skipping auto-fix")
395
+ except subprocess.SubprocessError as e:
396
+ logger.error(f"Ruff auto-fix subprocess error: {e}")
397
+ except Exception:
398
+ # INTENTIONAL: Graceful degradation - auto-fix is best-effort
399
+ logger.exception("Unexpected error in ruff auto-fix")
400
+
401
+ return (
402
+ {
403
+ "fixes": fixes,
404
+ "auto_fix_enabled": True,
405
+ **input_data,
406
+ },
407
+ 100,
408
+ len(str(fixes)) // 4,
409
+ )
410
+
411
+ async def execute(self, **kwargs: Any) -> HealthCheckResult: # type: ignore[override]
412
+ """Execute the health check workflow.
413
+
414
+ Args:
415
+ path: Path to check (default: ".")
416
+ auto_fix: Override auto_fix setting
417
+ **kwargs: Additional arguments
418
+
419
+ Returns:
420
+ HealthCheckResult with health score and findings
421
+
422
+ """
423
+ start_time = time.time()
424
+
425
+ # Override auto_fix if provided
426
+ if "auto_fix" in kwargs:
427
+ self.auto_fix = kwargs.pop("auto_fix")
428
+
429
+ # Run base workflow
430
+ result = await super().execute(**kwargs)
431
+
432
+ duration = time.time() - start_time
433
+ final_output = result.final_output or {}
434
+ diagnosis = final_output.get("diagnosis", {})
435
+ fixes = final_output.get("fixes", [])
436
+
437
+ # Count severities
438
+ issues = diagnosis.get("issues", [])
439
+ critical_count = sum(1 for i in issues if i.get("severity") == "critical")
440
+ high_count = sum(1 for i in issues if i.get("severity") == "high")
441
+ applied_count = sum(1 for f in fixes if f.get("status") == "applied")
442
+
443
+ health_result = HealthCheckResult(
444
+ success=result.success,
445
+ health_score=diagnosis.get("health_score", 0),
446
+ is_healthy=diagnosis.get("is_healthy", False),
447
+ issues=issues,
448
+ fixes=fixes,
449
+ checks_run=diagnosis.get("checks_run", {}),
450
+ agents_used=diagnosis.get("agents_used", []),
451
+ critical_count=critical_count,
452
+ high_count=high_count,
453
+ applied_fixes_count=applied_count,
454
+ duration_seconds=duration,
455
+ cost=result.cost_report.total_cost,
456
+ metadata={
457
+ "crew_available": diagnosis.get("crew_available", False),
458
+ "auto_fix": self.auto_fix,
459
+ "xml_prompts": self.xml_prompts,
460
+ },
461
+ )
462
+
463
+ # Auto-save to .empathy/health.json for dashboard
464
+ self._save_health_data(health_result, kwargs.get("path", "."))
465
+
466
+ # Add formatted report to metadata for human readability
467
+ health_result.metadata["formatted_report"] = format_health_check_report(health_result)
468
+
469
+ return health_result
470
+
471
+ def _save_health_data(self, result: HealthCheckResult, project_path: str) -> None:
472
+ """Save health check results to .empathy/health.json for dashboard."""
473
+ try:
474
+ # Determine empathy dir relative to project path
475
+ if os.path.isabs(project_path):
476
+ empathy_dir = os.path.join(project_path, ".empathy")
477
+ else:
478
+ empathy_dir = os.path.join(os.getcwd(), ".empathy")
479
+
480
+ os.makedirs(empathy_dir, exist_ok=True)
481
+
482
+ # Count issues by category
483
+ lint_errors = sum(1 for i in result.issues if i.get("category") == "lint")
484
+ type_errors = sum(1 for i in result.issues if i.get("category") == "types")
485
+ test_failures = sum(1 for i in result.issues if i.get("category") == "tests")
486
+ security_high = sum(
487
+ 1
488
+ for i in result.issues
489
+ if i.get("category") == "security" and i.get("severity") == "high"
490
+ )
491
+ security_medium = sum(
492
+ 1
493
+ for i in result.issues
494
+ if i.get("category") == "security" and i.get("severity") == "medium"
495
+ )
496
+
497
+ # Extract test stats from checks_run
498
+ tests_info = result.checks_run.get("tests", {})
499
+
500
+ health_data = {
501
+ "score": result.health_score,
502
+ "lint": {"errors": lint_errors, "warnings": 0},
503
+ "types": {"errors": type_errors},
504
+ "security": {"high": security_high, "medium": security_medium, "low": 0},
505
+ "tests": {
506
+ "passed": tests_info.get("passed", 0) if tests_info.get("passed") else 0,
507
+ "failed": test_failures,
508
+ "total": tests_info.get("total", 0) if tests_info.get("total") else 0,
509
+ "coverage": tests_info.get("coverage", 0) if tests_info.get("coverage") else 0,
510
+ },
511
+ "tech_debt": {"total": 0, "todos": 0, "fixmes": 0, "hacks": 0},
512
+ "timestamp": datetime.now().isoformat(),
513
+ }
514
+
515
+ health_file = os.path.join(empathy_dir, "health.json")
516
+ with open(health_file, "w") as f:
517
+ json.dump(health_data, f, indent=2)
518
+
519
+ logger.info(f"Saved health data to {health_file}")
520
+ except OSError as e:
521
+ # File system errors (disk full, permission denied, etc.)
522
+ logger.warning(f"Failed to save health data (file system error): {e}")
523
+ except (TypeError, ValueError) as e:
524
+ # Cannot serialize health data - json.dump raises TypeError/ValueError
525
+ logger.error(f"Failed to save health data (serialization error): {e}")
526
+ except Exception as e: # noqa: BLE001
527
+ # INTENTIONAL: Saving health data should never crash a health check
528
+ # This is best-effort diagnostics output
529
+ logger.warning(f"Failed to save health data (unexpected error): {e}")
530
+
531
+
532
+ def format_health_check_report(result: HealthCheckResult) -> str:
533
+ """Format health check output as a human-readable report.
534
+
535
+ Args:
536
+ result: The HealthCheckResult dataclass
537
+
538
+ Returns:
539
+ Formatted report string
540
+
541
+ """
542
+ lines = []
543
+
544
+ # Header with health status
545
+ score = result.health_score
546
+ if score >= 90:
547
+ status_icon = "🟢"
548
+ status_text = "EXCELLENT"
549
+ elif score >= 80:
550
+ status_icon = "🟡"
551
+ status_text = "HEALTHY"
552
+ elif score >= 60:
553
+ status_icon = "🟠"
554
+ status_text = "NEEDS ATTENTION"
555
+ else:
556
+ status_icon = "🔴"
557
+ status_text = "UNHEALTHY"
558
+
559
+ lines.append("=" * 60)
560
+ lines.append("PROJECT HEALTH CHECK REPORT")
561
+ lines.append("=" * 60)
562
+ lines.append("")
563
+ lines.append(f"Health Score: {status_icon} {score:.0f}/100 ({status_text})")
564
+ lines.append(f"Status: {'✅ Healthy' if result.is_healthy else '⚠️ Issues Found'}")
565
+ lines.append("")
566
+
567
+ # Checks run summary
568
+ lines.append("-" * 60)
569
+ lines.append("CHECKS PERFORMED")
570
+ lines.append("-" * 60)
571
+ for check_name, check_result in result.checks_run.items():
572
+ passed = check_result.get("passed", False)
573
+ skipped = check_result.get("skipped", False)
574
+ if skipped:
575
+ icon = "⏭️"
576
+ status = "Skipped"
577
+ elif passed:
578
+ icon = "✅"
579
+ status = "Passed"
580
+ else:
581
+ icon = "❌"
582
+ status = "Failed"
583
+ lines.append(f" {icon} {check_name.capitalize()}: {status}")
584
+ lines.append("")
585
+
586
+ # Issue summary
587
+ if result.issues:
588
+ lines.append("-" * 60)
589
+ lines.append("ISSUES FOUND")
590
+ lines.append("-" * 60)
591
+ lines.append(f"Total: {len(result.issues)}")
592
+ lines.append(f" 🔴 Critical: {result.critical_count}")
593
+ lines.append(f" 🟠 High: {result.high_count}")
594
+ lines.append("")
595
+
596
+ # Group issues by category
597
+ by_category: dict[str, list] = {}
598
+ for issue in result.issues:
599
+ cat = issue.get("category", "other")
600
+ if cat not in by_category:
601
+ by_category[cat] = []
602
+ by_category[cat].append(issue)
603
+
604
+ for category, issues in by_category.items():
605
+ lines.append(f" {category.upper()} ({len(issues)} issues):")
606
+ for issue in issues[:5]: # Show top 5 per category
607
+ severity = issue.get("severity", "unknown").upper()
608
+ title = issue.get("title", "Unknown issue")
609
+ sev_icon = {"CRITICAL": "🔴", "HIGH": "🟠", "MEDIUM": "🟡", "LOW": "🟢"}.get(
610
+ severity,
611
+ "⚪",
612
+ )
613
+ lines.append(f" {sev_icon} [{severity}] {title}")
614
+ if len(issues) > 5:
615
+ lines.append(f" ... and {len(issues) - 5} more")
616
+ lines.append("")
617
+
618
+ # Fixes applied
619
+ if result.fixes:
620
+ lines.append("-" * 60)
621
+ lines.append("FIXES APPLIED")
622
+ lines.append("-" * 60)
623
+ lines.append(f"Total Fixes: {result.applied_fixes_count}")
624
+ for fix in result.fixes[:10]:
625
+ status = fix.get("status", "unknown")
626
+ title = fix.get("title", "Unknown fix")
627
+ status_icon = "✅" if status == "applied" else "⚠️"
628
+ lines.append(f" {status_icon} {title}")
629
+ lines.append("")
630
+
631
+ # Agents used
632
+ if result.agents_used:
633
+ lines.append("-" * 60)
634
+ lines.append("AGENTS USED")
635
+ lines.append("-" * 60)
636
+ for agent in result.agents_used:
637
+ lines.append(f" 🤖 {agent}")
638
+ lines.append("")
639
+
640
+ # Footer
641
+ lines.append("=" * 60)
642
+ duration_ms = result.duration_seconds * 1000
643
+ lines.append(f"Health check completed in {duration_ms:.0f}ms | Cost: ${result.cost:.4f}")
644
+ lines.append("=" * 60)
645
+
646
+ return "\n".join(lines)
647
+
648
+
649
+ def main():
650
+ """CLI entry point for health check workflow."""
651
+ import asyncio
652
+
653
+ async def run():
654
+ workflow = HealthCheckWorkflow(auto_fix=False)
655
+ result = await workflow.execute(path=".")
656
+
657
+ print("\nHealth Check Results")
658
+ print("=" * 50)
659
+ print(f"Health Score: {result.health_score}/100")
660
+ print(f"Is Healthy: {result.is_healthy}")
661
+ print(f"Issues Found: {len(result.issues)}")
662
+ print(f" Critical: {result.critical_count}")
663
+ print(f" High: {result.high_count}")
664
+
665
+ if result.issues:
666
+ print("\nTop Issues:")
667
+ for issue in result.issues[:5]:
668
+ print(
669
+ f" - [{issue.get('severity', 'N/A').upper()}] {issue.get('title', 'Unknown')}",
670
+ )
671
+
672
+ print(f"\nChecks Run: {list(result.checks_run.keys())}")
673
+ print(f"Duration: {result.duration_seconds * 1000:.0f}ms")
674
+
675
+ asyncio.run(run())
676
+
677
+
678
+ if __name__ == "__main__":
679
+ main()