empathy-framework 4.6.6__py3-none-any.whl → 4.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 (273) hide show
  1. empathy_framework-4.7.1.dist-info/METADATA +690 -0
  2. empathy_framework-4.7.1.dist-info/RECORD +379 -0
  3. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/top_level.txt +1 -2
  4. empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
  5. empathy_llm_toolkit/agent_factory/__init__.py +6 -6
  6. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +7 -10
  7. empathy_llm_toolkit/agents_md/__init__.py +22 -0
  8. empathy_llm_toolkit/agents_md/loader.py +218 -0
  9. empathy_llm_toolkit/agents_md/parser.py +271 -0
  10. empathy_llm_toolkit/agents_md/registry.py +307 -0
  11. empathy_llm_toolkit/commands/__init__.py +51 -0
  12. empathy_llm_toolkit/commands/context.py +375 -0
  13. empathy_llm_toolkit/commands/loader.py +301 -0
  14. empathy_llm_toolkit/commands/models.py +231 -0
  15. empathy_llm_toolkit/commands/parser.py +371 -0
  16. empathy_llm_toolkit/commands/registry.py +429 -0
  17. empathy_llm_toolkit/config/__init__.py +8 -8
  18. empathy_llm_toolkit/config/unified.py +3 -7
  19. empathy_llm_toolkit/context/__init__.py +22 -0
  20. empathy_llm_toolkit/context/compaction.py +455 -0
  21. empathy_llm_toolkit/context/manager.py +434 -0
  22. empathy_llm_toolkit/hooks/__init__.py +24 -0
  23. empathy_llm_toolkit/hooks/config.py +306 -0
  24. empathy_llm_toolkit/hooks/executor.py +289 -0
  25. empathy_llm_toolkit/hooks/registry.py +302 -0
  26. empathy_llm_toolkit/hooks/scripts/__init__.py +39 -0
  27. empathy_llm_toolkit/hooks/scripts/evaluate_session.py +201 -0
  28. empathy_llm_toolkit/hooks/scripts/first_time_init.py +285 -0
  29. empathy_llm_toolkit/hooks/scripts/pre_compact.py +207 -0
  30. empathy_llm_toolkit/hooks/scripts/session_end.py +183 -0
  31. empathy_llm_toolkit/hooks/scripts/session_start.py +163 -0
  32. empathy_llm_toolkit/hooks/scripts/suggest_compact.py +225 -0
  33. empathy_llm_toolkit/learning/__init__.py +30 -0
  34. empathy_llm_toolkit/learning/evaluator.py +438 -0
  35. empathy_llm_toolkit/learning/extractor.py +514 -0
  36. empathy_llm_toolkit/learning/storage.py +560 -0
  37. empathy_llm_toolkit/providers.py +4 -11
  38. empathy_llm_toolkit/security/__init__.py +17 -17
  39. empathy_llm_toolkit/utils/tokens.py +2 -5
  40. empathy_os/__init__.py +202 -70
  41. empathy_os/cache_monitor.py +5 -3
  42. empathy_os/cli/__init__.py +11 -55
  43. empathy_os/cli/__main__.py +29 -15
  44. empathy_os/cli/commands/inspection.py +21 -12
  45. empathy_os/cli/commands/memory.py +4 -12
  46. empathy_os/cli/commands/profiling.py +198 -0
  47. empathy_os/cli/commands/utilities.py +27 -7
  48. empathy_os/cli.py +28 -57
  49. empathy_os/cli_unified.py +525 -1164
  50. empathy_os/cost_tracker.py +9 -3
  51. empathy_os/dashboard/server.py +200 -2
  52. empathy_os/hot_reload/__init__.py +7 -7
  53. empathy_os/hot_reload/config.py +6 -7
  54. empathy_os/hot_reload/integration.py +35 -35
  55. empathy_os/hot_reload/reloader.py +57 -57
  56. empathy_os/hot_reload/watcher.py +28 -28
  57. empathy_os/hot_reload/websocket.py +2 -2
  58. empathy_os/memory/__init__.py +11 -4
  59. empathy_os/memory/claude_memory.py +1 -1
  60. empathy_os/memory/cross_session.py +8 -12
  61. empathy_os/memory/edges.py +6 -6
  62. empathy_os/memory/file_session.py +770 -0
  63. empathy_os/memory/graph.py +30 -30
  64. empathy_os/memory/nodes.py +6 -6
  65. empathy_os/memory/short_term.py +15 -9
  66. empathy_os/memory/unified.py +606 -140
  67. empathy_os/meta_workflows/agent_creator.py +3 -9
  68. empathy_os/meta_workflows/cli_meta_workflows.py +113 -53
  69. empathy_os/meta_workflows/form_engine.py +6 -18
  70. empathy_os/meta_workflows/intent_detector.py +64 -24
  71. empathy_os/meta_workflows/models.py +3 -1
  72. empathy_os/meta_workflows/pattern_learner.py +13 -31
  73. empathy_os/meta_workflows/plan_generator.py +55 -47
  74. empathy_os/meta_workflows/session_context.py +2 -3
  75. empathy_os/meta_workflows/workflow.py +20 -51
  76. empathy_os/models/cli.py +2 -2
  77. empathy_os/models/tasks.py +1 -2
  78. empathy_os/models/telemetry.py +4 -1
  79. empathy_os/models/token_estimator.py +3 -1
  80. empathy_os/monitoring/alerts.py +938 -9
  81. empathy_os/monitoring/alerts_cli.py +346 -183
  82. empathy_os/orchestration/execution_strategies.py +12 -29
  83. empathy_os/orchestration/pattern_learner.py +20 -26
  84. empathy_os/orchestration/real_tools.py +6 -15
  85. empathy_os/platform_utils.py +2 -1
  86. empathy_os/plugins/__init__.py +2 -2
  87. empathy_os/plugins/base.py +64 -64
  88. empathy_os/plugins/registry.py +32 -32
  89. empathy_os/project_index/index.py +49 -15
  90. empathy_os/project_index/models.py +1 -2
  91. empathy_os/project_index/reports.py +1 -1
  92. empathy_os/project_index/scanner.py +1 -0
  93. empathy_os/redis_memory.py +10 -7
  94. empathy_os/resilience/__init__.py +1 -1
  95. empathy_os/resilience/health.py +10 -10
  96. empathy_os/routing/__init__.py +7 -7
  97. empathy_os/routing/chain_executor.py +37 -37
  98. empathy_os/routing/classifier.py +36 -36
  99. empathy_os/routing/smart_router.py +40 -40
  100. empathy_os/routing/{wizard_registry.py → workflow_registry.py} +47 -47
  101. empathy_os/scaffolding/__init__.py +8 -8
  102. empathy_os/scaffolding/__main__.py +1 -1
  103. empathy_os/scaffolding/cli.py +28 -28
  104. empathy_os/socratic/__init__.py +3 -19
  105. empathy_os/socratic/ab_testing.py +25 -36
  106. empathy_os/socratic/blueprint.py +38 -38
  107. empathy_os/socratic/cli.py +34 -20
  108. empathy_os/socratic/collaboration.py +30 -28
  109. empathy_os/socratic/domain_templates.py +9 -1
  110. empathy_os/socratic/embeddings.py +17 -13
  111. empathy_os/socratic/engine.py +135 -70
  112. empathy_os/socratic/explainer.py +70 -60
  113. empathy_os/socratic/feedback.py +24 -19
  114. empathy_os/socratic/forms.py +15 -10
  115. empathy_os/socratic/generator.py +51 -35
  116. empathy_os/socratic/llm_analyzer.py +25 -23
  117. empathy_os/socratic/mcp_server.py +99 -159
  118. empathy_os/socratic/session.py +19 -13
  119. empathy_os/socratic/storage.py +98 -67
  120. empathy_os/socratic/success.py +38 -27
  121. empathy_os/socratic/visual_editor.py +51 -39
  122. empathy_os/socratic/web_ui.py +99 -66
  123. empathy_os/telemetry/cli.py +3 -1
  124. empathy_os/telemetry/usage_tracker.py +1 -3
  125. empathy_os/test_generator/__init__.py +3 -3
  126. empathy_os/test_generator/cli.py +28 -28
  127. empathy_os/test_generator/generator.py +64 -66
  128. empathy_os/test_generator/risk_analyzer.py +11 -11
  129. empathy_os/vscode_bridge 2.py +173 -0
  130. empathy_os/vscode_bridge.py +173 -0
  131. empathy_os/workflows/__init__.py +212 -120
  132. empathy_os/workflows/batch_processing.py +8 -24
  133. empathy_os/workflows/bug_predict.py +1 -1
  134. empathy_os/workflows/code_review.py +20 -5
  135. empathy_os/workflows/code_review_pipeline.py +13 -8
  136. empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
  137. empathy_os/workflows/manage_documentation.py +1 -0
  138. empathy_os/workflows/orchestrated_health_check.py +6 -11
  139. empathy_os/workflows/orchestrated_release_prep.py +3 -3
  140. empathy_os/workflows/pr_review.py +18 -10
  141. empathy_os/workflows/progressive/README 2.md +454 -0
  142. empathy_os/workflows/progressive/__init__ 2.py +92 -0
  143. empathy_os/workflows/progressive/__init__.py +2 -12
  144. empathy_os/workflows/progressive/cli 2.py +242 -0
  145. empathy_os/workflows/progressive/cli.py +14 -37
  146. empathy_os/workflows/progressive/core 2.py +488 -0
  147. empathy_os/workflows/progressive/core.py +12 -12
  148. empathy_os/workflows/progressive/orchestrator 2.py +701 -0
  149. empathy_os/workflows/progressive/orchestrator.py +166 -144
  150. empathy_os/workflows/progressive/reports 2.py +528 -0
  151. empathy_os/workflows/progressive/reports.py +22 -31
  152. empathy_os/workflows/progressive/telemetry 2.py +280 -0
  153. empathy_os/workflows/progressive/telemetry.py +8 -14
  154. empathy_os/workflows/progressive/test_gen 2.py +514 -0
  155. empathy_os/workflows/progressive/test_gen.py +29 -48
  156. empathy_os/workflows/progressive/workflow 2.py +628 -0
  157. empathy_os/workflows/progressive/workflow.py +31 -70
  158. empathy_os/workflows/release_prep.py +21 -6
  159. empathy_os/workflows/release_prep_crew.py +1 -0
  160. empathy_os/workflows/secure_release.py +13 -6
  161. empathy_os/workflows/security_audit.py +8 -3
  162. empathy_os/workflows/test_coverage_boost_crew.py +3 -2
  163. empathy_os/workflows/test_maintenance_crew.py +1 -0
  164. empathy_os/workflows/test_runner.py +16 -12
  165. empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
  166. empathy_software_plugin/cli.py +0 -122
  167. patterns/README.md +119 -0
  168. patterns/__init__.py +95 -0
  169. patterns/behavior.py +298 -0
  170. patterns/code_review_memory.json +441 -0
  171. patterns/core.py +97 -0
  172. patterns/debugging.json +3763 -0
  173. patterns/empathy.py +268 -0
  174. patterns/health_check_memory.json +505 -0
  175. patterns/input.py +161 -0
  176. patterns/memory_graph.json +8 -0
  177. patterns/refactoring_memory.json +1113 -0
  178. patterns/registry.py +663 -0
  179. patterns/security_memory.json +8 -0
  180. patterns/structural.py +415 -0
  181. patterns/validation.py +194 -0
  182. coach_wizards/__init__.py +0 -45
  183. coach_wizards/accessibility_wizard.py +0 -91
  184. coach_wizards/api_wizard.py +0 -91
  185. coach_wizards/base_wizard.py +0 -209
  186. coach_wizards/cicd_wizard.py +0 -91
  187. coach_wizards/code_reviewer_README.md +0 -60
  188. coach_wizards/code_reviewer_wizard.py +0 -180
  189. coach_wizards/compliance_wizard.py +0 -91
  190. coach_wizards/database_wizard.py +0 -91
  191. coach_wizards/debugging_wizard.py +0 -91
  192. coach_wizards/documentation_wizard.py +0 -91
  193. coach_wizards/generate_wizards.py +0 -347
  194. coach_wizards/localization_wizard.py +0 -173
  195. coach_wizards/migration_wizard.py +0 -91
  196. coach_wizards/monitoring_wizard.py +0 -91
  197. coach_wizards/observability_wizard.py +0 -91
  198. coach_wizards/performance_wizard.py +0 -91
  199. coach_wizards/prompt_engineering_wizard.py +0 -661
  200. coach_wizards/refactoring_wizard.py +0 -91
  201. coach_wizards/scaling_wizard.py +0 -90
  202. coach_wizards/security_wizard.py +0 -92
  203. coach_wizards/testing_wizard.py +0 -91
  204. empathy_framework-4.6.6.dist-info/METADATA +0 -1597
  205. empathy_framework-4.6.6.dist-info/RECORD +0 -410
  206. empathy_llm_toolkit/wizards/__init__.py +0 -43
  207. empathy_llm_toolkit/wizards/base_wizard.py +0 -364
  208. empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
  209. empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
  210. empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
  211. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
  212. empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
  213. empathy_os/wizard_factory_cli.py +0 -170
  214. empathy_software_plugin/wizards/__init__.py +0 -42
  215. empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
  216. empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
  217. empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
  218. empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
  219. empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
  220. empathy_software_plugin/wizards/base_wizard.py +0 -288
  221. empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
  222. empathy_software_plugin/wizards/code_review_wizard.py +0 -604
  223. empathy_software_plugin/wizards/debugging/__init__.py +0 -50
  224. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
  225. empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
  226. empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
  227. empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
  228. empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
  229. empathy_software_plugin/wizards/debugging/verification.py +0 -369
  230. empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
  231. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
  232. empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
  233. empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
  234. empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
  235. empathy_software_plugin/wizards/performance/__init__.py +0 -9
  236. empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
  237. empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
  238. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
  239. empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
  240. empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
  241. empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
  242. empathy_software_plugin/wizards/security/__init__.py +0 -32
  243. empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
  244. empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
  245. empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
  246. empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
  247. empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
  248. empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
  249. empathy_software_plugin/wizards/testing/__init__.py +0 -27
  250. empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
  251. empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
  252. empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
  253. empathy_software_plugin/wizards/testing_wizard.py +0 -274
  254. wizards/__init__.py +0 -82
  255. wizards/admission_assessment_wizard.py +0 -644
  256. wizards/care_plan.py +0 -321
  257. wizards/clinical_assessment.py +0 -769
  258. wizards/discharge_planning.py +0 -77
  259. wizards/discharge_summary_wizard.py +0 -468
  260. wizards/dosage_calculation.py +0 -497
  261. wizards/incident_report_wizard.py +0 -454
  262. wizards/medication_reconciliation.py +0 -85
  263. wizards/nursing_assessment.py +0 -171
  264. wizards/patient_education.py +0 -654
  265. wizards/quality_improvement.py +0 -705
  266. wizards/sbar_report.py +0 -324
  267. wizards/sbar_wizard.py +0 -608
  268. wizards/shift_handoff_wizard.py +0 -535
  269. wizards/soap_note_wizard.py +0 -679
  270. wizards/treatment_plan.py +0 -15
  271. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/WHEEL +0 -0
  272. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/entry_points.txt +0 -0
  273. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,27 +0,0 @@
1
- """Testing Analysis Components
2
-
3
- Supporting modules for Enhanced Testing Wizard.
4
-
5
- Copyright 2025 Smart-AI-Memory
6
- Licensed under Fair Source License 0.9
7
- """
8
-
9
- from .coverage_analyzer import CoverageAnalyzer, CoverageReport, FileCoverage
10
- from .quality_analyzer import TestFunction, TestQualityAnalyzer, TestQualityReport
11
- from .test_suggester import CodeElement, TestPriority, TestSuggester, TestSuggestion
12
-
13
- __all__ = [
14
- "CodeElement",
15
- # Coverage Analysis
16
- "CoverageAnalyzer",
17
- "CoverageReport",
18
- "FileCoverage",
19
- "TestFunction",
20
- "TestPriority",
21
- # Quality Analysis
22
- "TestQualityAnalyzer",
23
- "TestQualityReport",
24
- # Test Suggestions
25
- "TestSuggester",
26
- "TestSuggestion",
27
- ]
@@ -1,459 +0,0 @@
1
- """Coverage Analyzer for Enhanced Testing Wizard
2
-
3
- Parses coverage reports and provides intelligent analysis of test coverage gaps,
4
- including branch coverage, critical path identification, and trend analysis.
5
-
6
- Copyright 2025 Smart-AI-Memory
7
- Licensed under Fair Source License 0.9
8
- """
9
-
10
- import json
11
- import xml.etree.ElementTree as ET
12
- from dataclasses import dataclass
13
- from enum import Enum
14
- from pathlib import Path
15
- from typing import Any
16
-
17
-
18
- class CoverageFormat(Enum):
19
- """Supported coverage report formats"""
20
-
21
- XML = "xml"
22
- JSON = "json"
23
- HTML = "html"
24
- LCOV = "lcov"
25
-
26
-
27
- @dataclass
28
- class FileCoverage:
29
- """Coverage statistics for a single file"""
30
-
31
- file_path: str
32
- lines_total: int
33
- lines_covered: int
34
- lines_missing: list[int]
35
- branches_total: int
36
- branches_covered: int
37
- branches_missing: list[tuple[int, int]] # (line_num, branch_id)
38
- percentage: float
39
-
40
- @property
41
- def lines_uncovered(self) -> int:
42
- """Number of uncovered lines"""
43
- return self.lines_total - self.lines_covered
44
-
45
- @property
46
- def branch_percentage(self) -> float:
47
- """Branch coverage percentage"""
48
- if self.branches_total == 0:
49
- return 100.0
50
- return (self.branches_covered / self.branches_total) * 100
51
-
52
-
53
- @dataclass
54
- class CoverageReport:
55
- """Complete coverage analysis report"""
56
-
57
- overall_percentage: float
58
- lines_total: int
59
- lines_covered: int
60
- branches_total: int
61
- branches_covered: int
62
- files: dict[str, FileCoverage]
63
- critical_gaps: list[str] # Files with <50% coverage
64
- untested_files: list[str] # Files with 0% coverage
65
- timestamp: str | None = None
66
-
67
- @property
68
- def files_total(self) -> int:
69
- """Total number of files analyzed"""
70
- return len(self.files)
71
-
72
- @property
73
- def files_well_covered(self) -> int:
74
- """Number of files with ≥80% coverage"""
75
- return sum(1 for f in self.files.values() if f.percentage >= 80.0)
76
-
77
-
78
- class CoverageAnalyzer:
79
- """Analyzes test coverage reports to identify gaps and provide recommendations.
80
-
81
- Supports multiple formats:
82
- - Coverage.py XML (pytest --cov)
83
- - Coverage.py JSON
84
- - LCOV format
85
- - HTML reports
86
- """
87
-
88
- def __init__(self):
89
- self.critical_threshold = 50.0 # Files below this are critical
90
- self.target_threshold = 80.0 # Target coverage percentage
91
-
92
- def parse_coverage_xml(self, xml_path: Path) -> CoverageReport:
93
- """Parse coverage.xml format (Coverage.py / pytest-cov output)
94
-
95
- Args:
96
- xml_path: Path to coverage.xml file
97
-
98
- Returns:
99
- CoverageReport with full analysis
100
-
101
- Raises:
102
- FileNotFoundError: If XML file doesn't exist
103
- ValueError: If XML is malformed
104
-
105
- """
106
- if not xml_path.exists():
107
- raise FileNotFoundError(f"Coverage XML not found: {xml_path}")
108
-
109
- try:
110
- # Note: Parsing trusted coverage.xml generated by pytest-cov, not untrusted user input
111
- tree = ET.parse(xml_path) # nosec B314
112
- root = tree.getroot()
113
- except ET.ParseError as e:
114
- raise ValueError(f"Malformed coverage XML: {e}") from e
115
-
116
- # Extract overall statistics
117
- overall = root.attrib
118
- lines_total = int(overall.get("lines-valid", 0))
119
- lines_covered = int(overall.get("lines-covered", 0))
120
- branches_total = int(overall.get("branches-valid", 0))
121
- branches_covered = int(overall.get("branches-covered", 0))
122
-
123
- # Calculate overall percentage
124
- if lines_total > 0:
125
- overall_pct = (lines_covered / lines_total) * 100
126
- else:
127
- overall_pct = 0.0
128
-
129
- # Parse individual files
130
- files: dict[str, FileCoverage] = {}
131
- critical_gaps: list[str] = []
132
- untested_files: list[str] = []
133
-
134
- for package in root.findall(".//package"):
135
- for cls in package.findall(".//class"):
136
- filename = cls.attrib.get("filename", "")
137
- if not filename:
138
- continue
139
-
140
- # Get line statistics
141
- lines = cls.findall(".//line")
142
- lines_total_file = len(lines)
143
- lines_covered_file = sum(1 for line in lines if line.attrib.get("hits", "0") != "0")
144
- lines_missing = [
145
- int(line.attrib.get("number", 0))
146
- for line in lines
147
- if line.attrib.get("hits", "0") == "0"
148
- ]
149
-
150
- # Get branch statistics
151
- branches_file = [line for line in lines if line.attrib.get("branch") == "true"]
152
- branches_total_file = len(branches_file) * 2 # Each branch point has 2 paths
153
- branches_covered_file = 0
154
- for line in branches_file:
155
- if "condition-coverage" in line.attrib:
156
- # Format: "50% (1/2)" or "0% (0/2)"
157
- cov_str = line.attrib.get("condition-coverage", "0% (0/2)")
158
- # Extract the fraction part: (1/2)
159
- if "(" in cov_str:
160
- fraction = cov_str.split("(")[1].split(")")[0] # "1/2"
161
- covered = int(fraction.split("/")[0]) # 1
162
- branches_covered_file += covered
163
- branches_missing = [
164
- (int(line.attrib.get("number", 0)), i)
165
- for line in branches_file
166
- for i in range(2) # Each branch has 2 paths
167
- ]
168
-
169
- # Calculate file percentage
170
- if lines_total_file > 0:
171
- file_pct = (lines_covered_file / lines_total_file) * 100
172
- else:
173
- file_pct = 0.0
174
-
175
- file_cov = FileCoverage(
176
- file_path=filename,
177
- lines_total=lines_total_file,
178
- lines_covered=lines_covered_file,
179
- lines_missing=lines_missing,
180
- branches_total=branches_total_file,
181
- branches_covered=branches_covered_file,
182
- branches_missing=branches_missing,
183
- percentage=file_pct,
184
- )
185
-
186
- files[filename] = file_cov
187
-
188
- # Track critical gaps
189
- if file_pct < self.critical_threshold:
190
- critical_gaps.append(filename)
191
- if file_pct == 0.0 and lines_total_file > 0:
192
- untested_files.append(filename)
193
-
194
- return CoverageReport(
195
- overall_percentage=overall_pct,
196
- lines_total=lines_total,
197
- lines_covered=lines_covered,
198
- branches_total=branches_total,
199
- branches_covered=branches_covered,
200
- files=files,
201
- critical_gaps=critical_gaps,
202
- untested_files=untested_files,
203
- timestamp=overall.get("timestamp"),
204
- )
205
-
206
- def parse_coverage_json(self, json_path: Path) -> CoverageReport:
207
- """Parse coverage.json format (Coverage.py JSON output)
208
-
209
- Args:
210
- json_path: Path to coverage.json file
211
-
212
- Returns:
213
- CoverageReport with full analysis
214
-
215
- """
216
- if not json_path.exists():
217
- raise FileNotFoundError(f"Coverage JSON not found: {json_path}")
218
-
219
- with open(json_path) as f:
220
- data = json.load(f)
221
-
222
- # Extract overall totals
223
- totals = data.get("totals", {})
224
- lines_total = totals.get("num_statements", 0)
225
- lines_covered = totals.get("covered_lines", 0)
226
- branches_total = totals.get("num_branches", 0)
227
- branches_covered = totals.get("covered_branches", 0)
228
-
229
- if lines_total > 0:
230
- overall_pct = (lines_covered / lines_total) * 100
231
- else:
232
- overall_pct = 0.0
233
-
234
- # Parse individual files
235
- files: dict[str, FileCoverage] = {}
236
- critical_gaps: list[str] = []
237
- untested_files: list[str] = []
238
-
239
- for filename, file_data in data.get("files", {}).items():
240
- summary = file_data.get("summary", {})
241
-
242
- lines_total_file = summary.get("num_statements", 0)
243
- lines_covered_file = summary.get("covered_lines", 0)
244
- branches_total_file = summary.get("num_branches", 0)
245
- branches_covered_file = summary.get("covered_branches", 0)
246
-
247
- # Get missing lines
248
- missing_lines = file_data.get("missing_lines", [])
249
-
250
- # Get missing branches
251
- missing_branches = [
252
- (line, branch)
253
- for line, branches in file_data.get("missing_branches", {}).items()
254
- for branch in branches
255
- ]
256
-
257
- if lines_total_file > 0:
258
- file_pct = (lines_covered_file / lines_total_file) * 100
259
- else:
260
- file_pct = 0.0
261
-
262
- file_cov = FileCoverage(
263
- file_path=filename,
264
- lines_total=lines_total_file,
265
- lines_covered=lines_covered_file,
266
- lines_missing=missing_lines,
267
- branches_total=branches_total_file,
268
- branches_covered=branches_covered_file,
269
- branches_missing=missing_branches,
270
- percentage=file_pct,
271
- )
272
-
273
- files[filename] = file_cov
274
-
275
- if file_pct < self.critical_threshold:
276
- critical_gaps.append(filename)
277
- if file_pct == 0.0 and lines_total_file > 0:
278
- untested_files.append(filename)
279
-
280
- return CoverageReport(
281
- overall_percentage=overall_pct,
282
- lines_total=lines_total,
283
- lines_covered=lines_covered,
284
- branches_total=branches_total,
285
- branches_covered=branches_covered,
286
- files=files,
287
- critical_gaps=critical_gaps,
288
- untested_files=untested_files,
289
- timestamp=data.get("meta", {}).get("timestamp"),
290
- )
291
-
292
- def identify_critical_gaps(self, report: CoverageReport) -> list[str]:
293
- """Identify files that critically need testing (below threshold)
294
-
295
- Args:
296
- report: Coverage report to analyze
297
-
298
- Returns:
299
- List of file paths sorted by priority (worst coverage first)
300
-
301
- """
302
- critical = [
303
- (file_path, file_cov.percentage)
304
- for file_path, file_cov in report.files.items()
305
- if file_cov.percentage < self.critical_threshold
306
- ]
307
-
308
- # Sort by percentage (lowest first)
309
- critical.sort(key=lambda x: x[1])
310
-
311
- return [file_path for file_path, _ in critical]
312
-
313
- def suggest_priority_files(
314
- self,
315
- report: CoverageReport,
316
- top_n: int = 10,
317
- ) -> list[dict[str, Any]]:
318
- """Suggest which files to test next for maximum impact
319
-
320
- Prioritizes based on:
321
- 1. Current coverage (lower is higher priority)
322
- 2. File size (larger files have more impact)
323
- 3. Missing lines count (more gaps = more opportunity)
324
-
325
- Args:
326
- report: Coverage report to analyze
327
- top_n: Number of suggestions to return
328
-
329
- Returns:
330
- List of suggestions with reasoning
331
-
332
- """
333
- suggestions = []
334
-
335
- for file_path, file_cov in report.files.items():
336
- # Skip well-covered files
337
- if file_cov.percentage >= self.target_threshold:
338
- continue
339
-
340
- # Calculate impact score
341
- coverage_gap = self.target_threshold - file_cov.percentage
342
- size_factor = file_cov.lines_total / 100 # Normalize
343
- missing_factor = file_cov.lines_uncovered / 10 # Normalize
344
-
345
- impact_score = (coverage_gap * 0.5) + (size_factor * 0.3) + (missing_factor * 0.2)
346
-
347
- suggestions.append(
348
- {
349
- "file": file_path,
350
- "current_coverage": file_cov.percentage,
351
- "lines_missing": file_cov.lines_uncovered,
352
- "impact_score": impact_score,
353
- "reason": self._generate_suggestion_reason(file_cov),
354
- },
355
- )
356
-
357
- # Sort by impact score (highest first)
358
- suggestions.sort(key=lambda x: x["impact_score"], reverse=True)
359
-
360
- return suggestions[:top_n]
361
-
362
- def _generate_suggestion_reason(self, file_cov: FileCoverage) -> str:
363
- """Generate human-readable reason for testing suggestion"""
364
- if file_cov.percentage == 0.0:
365
- return "No tests exist - critical gap"
366
- if file_cov.percentage < 30:
367
- return "Very low coverage - high priority"
368
- if file_cov.percentage < 50:
369
- return "Below critical threshold"
370
- if file_cov.percentage < 70:
371
- return "Moderate gap - good opportunity for improvement"
372
- return "Close to target - finish with targeted tests"
373
-
374
- def calculate_coverage_trend(
375
- self,
376
- historical_reports: list[tuple[str, CoverageReport]],
377
- ) -> dict[str, float]:
378
- """Calculate coverage trends over time
379
-
380
- Args:
381
- historical_reports: List of (timestamp, report) tuples in chronological order
382
-
383
- Returns:
384
- Dict mapping file paths to trend percentage (positive = improving)
385
-
386
- """
387
- if len(historical_reports) < 2:
388
- return {}
389
-
390
- trends = {}
391
-
392
- # Get oldest and newest reports
393
- oldest_date, oldest_report = historical_reports[0]
394
- newest_date, newest_report = historical_reports[-1]
395
-
396
- # Calculate trend for each file
397
- all_files = set(oldest_report.files.keys()) | set(newest_report.files.keys())
398
-
399
- for file_path in all_files:
400
- old_cov = oldest_report.files.get(file_path)
401
- new_cov = newest_report.files.get(file_path)
402
-
403
- if old_cov and new_cov:
404
- trend = new_cov.percentage - old_cov.percentage
405
- trends[file_path] = trend
406
- elif new_cov and not old_cov:
407
- # New file added
408
- trends[file_path] = new_cov.percentage
409
- elif old_cov and not new_cov:
410
- # File removed
411
- trends[file_path] = -old_cov.percentage
412
-
413
- return trends
414
-
415
- def generate_summary(self, report: CoverageReport) -> str:
416
- """Generate human-readable coverage summary
417
-
418
- Args:
419
- report: Coverage report to summarize
420
-
421
- Returns:
422
- Formatted summary string
423
-
424
- """
425
- summary = []
426
- summary.append("=" * 60)
427
- summary.append("COVERAGE ANALYSIS SUMMARY")
428
- summary.append("=" * 60)
429
- summary.append(f"Overall Coverage: {report.overall_percentage:.2f}%")
430
- summary.append(f"Lines: {report.lines_covered}/{report.lines_total}")
431
- summary.append(f"Branches: {report.branches_covered}/{report.branches_total}")
432
- summary.append(f"Files: {report.files_total} total")
433
- summary.append(f" - Well covered (≥80%): {report.files_well_covered}")
434
- summary.append(f" - Critical gaps (<50%): {len(report.critical_gaps)}")
435
- summary.append(f" - Untested: {len(report.untested_files)}")
436
- summary.append("")
437
-
438
- if report.untested_files:
439
- summary.append("⚠️ UNTESTED FILES (0% coverage):")
440
- for file_path in report.untested_files[:5]:
441
- summary.append(f" - {file_path}")
442
- if len(report.untested_files) > 5:
443
- summary.append(f" ... and {len(report.untested_files) - 5} more")
444
- summary.append("")
445
-
446
- if report.critical_gaps:
447
- summary.append("🔴 CRITICAL GAPS (<50% coverage):")
448
- critical_sorted = sorted(
449
- [(f, report.files[f].percentage) for f in report.critical_gaps],
450
- key=lambda x: x[1],
451
- )
452
- for file_path, pct in critical_sorted[:5]:
453
- summary.append(f" - {file_path}: {pct:.1f}%")
454
- if len(report.critical_gaps) > 5:
455
- summary.append(f" ... and {len(report.critical_gaps) - 5} more")
456
-
457
- summary.append("=" * 60)
458
-
459
- return "\n".join(summary)