empathy-framework 4.6.6__py3-none-any.whl → 4.7.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 (247) hide show
  1. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/METADATA +7 -6
  2. empathy_framework-4.7.0.dist-info/RECORD +354 -0
  3. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/top_level.txt +0 -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.py +173 -0
  130. empathy_os/workflows/__init__.py +212 -120
  131. empathy_os/workflows/batch_processing.py +8 -24
  132. empathy_os/workflows/bug_predict.py +1 -1
  133. empathy_os/workflows/code_review.py +20 -5
  134. empathy_os/workflows/code_review_pipeline.py +13 -8
  135. empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
  136. empathy_os/workflows/manage_documentation.py +1 -0
  137. empathy_os/workflows/orchestrated_health_check.py +6 -11
  138. empathy_os/workflows/orchestrated_release_prep.py +3 -3
  139. empathy_os/workflows/pr_review.py +18 -10
  140. empathy_os/workflows/progressive/__init__.py +2 -12
  141. empathy_os/workflows/progressive/cli.py +14 -37
  142. empathy_os/workflows/progressive/core.py +12 -12
  143. empathy_os/workflows/progressive/orchestrator.py +166 -144
  144. empathy_os/workflows/progressive/reports.py +22 -31
  145. empathy_os/workflows/progressive/telemetry.py +8 -14
  146. empathy_os/workflows/progressive/test_gen.py +29 -48
  147. empathy_os/workflows/progressive/workflow.py +31 -70
  148. empathy_os/workflows/release_prep.py +21 -6
  149. empathy_os/workflows/release_prep_crew.py +1 -0
  150. empathy_os/workflows/secure_release.py +13 -6
  151. empathy_os/workflows/security_audit.py +8 -3
  152. empathy_os/workflows/test_coverage_boost_crew.py +3 -2
  153. empathy_os/workflows/test_maintenance_crew.py +1 -0
  154. empathy_os/workflows/test_runner.py +16 -12
  155. empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
  156. empathy_software_plugin/cli.py +0 -122
  157. coach_wizards/__init__.py +0 -45
  158. coach_wizards/accessibility_wizard.py +0 -91
  159. coach_wizards/api_wizard.py +0 -91
  160. coach_wizards/base_wizard.py +0 -209
  161. coach_wizards/cicd_wizard.py +0 -91
  162. coach_wizards/code_reviewer_README.md +0 -60
  163. coach_wizards/code_reviewer_wizard.py +0 -180
  164. coach_wizards/compliance_wizard.py +0 -91
  165. coach_wizards/database_wizard.py +0 -91
  166. coach_wizards/debugging_wizard.py +0 -91
  167. coach_wizards/documentation_wizard.py +0 -91
  168. coach_wizards/generate_wizards.py +0 -347
  169. coach_wizards/localization_wizard.py +0 -173
  170. coach_wizards/migration_wizard.py +0 -91
  171. coach_wizards/monitoring_wizard.py +0 -91
  172. coach_wizards/observability_wizard.py +0 -91
  173. coach_wizards/performance_wizard.py +0 -91
  174. coach_wizards/prompt_engineering_wizard.py +0 -661
  175. coach_wizards/refactoring_wizard.py +0 -91
  176. coach_wizards/scaling_wizard.py +0 -90
  177. coach_wizards/security_wizard.py +0 -92
  178. coach_wizards/testing_wizard.py +0 -91
  179. empathy_framework-4.6.6.dist-info/RECORD +0 -410
  180. empathy_llm_toolkit/wizards/__init__.py +0 -43
  181. empathy_llm_toolkit/wizards/base_wizard.py +0 -364
  182. empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
  183. empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
  184. empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
  185. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
  186. empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
  187. empathy_os/wizard_factory_cli.py +0 -170
  188. empathy_software_plugin/wizards/__init__.py +0 -42
  189. empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
  190. empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
  191. empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
  192. empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
  193. empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
  194. empathy_software_plugin/wizards/base_wizard.py +0 -288
  195. empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
  196. empathy_software_plugin/wizards/code_review_wizard.py +0 -604
  197. empathy_software_plugin/wizards/debugging/__init__.py +0 -50
  198. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
  199. empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
  200. empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
  201. empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
  202. empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
  203. empathy_software_plugin/wizards/debugging/verification.py +0 -369
  204. empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
  205. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
  206. empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
  207. empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
  208. empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
  209. empathy_software_plugin/wizards/performance/__init__.py +0 -9
  210. empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
  211. empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
  212. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
  213. empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
  214. empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
  215. empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
  216. empathy_software_plugin/wizards/security/__init__.py +0 -32
  217. empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
  218. empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
  219. empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
  220. empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
  221. empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
  222. empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
  223. empathy_software_plugin/wizards/testing/__init__.py +0 -27
  224. empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
  225. empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
  226. empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
  227. empathy_software_plugin/wizards/testing_wizard.py +0 -274
  228. wizards/__init__.py +0 -82
  229. wizards/admission_assessment_wizard.py +0 -644
  230. wizards/care_plan.py +0 -321
  231. wizards/clinical_assessment.py +0 -769
  232. wizards/discharge_planning.py +0 -77
  233. wizards/discharge_summary_wizard.py +0 -468
  234. wizards/dosage_calculation.py +0 -497
  235. wizards/incident_report_wizard.py +0 -454
  236. wizards/medication_reconciliation.py +0 -85
  237. wizards/nursing_assessment.py +0 -171
  238. wizards/patient_education.py +0 -654
  239. wizards/quality_improvement.py +0 -705
  240. wizards/sbar_report.py +0 -324
  241. wizards/sbar_wizard.py +0 -608
  242. wizards/shift_handoff_wizard.py +0 -535
  243. wizards/soap_note_wizard.py +0 -679
  244. wizards/treatment_plan.py +0 -15
  245. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/WHEEL +0 -0
  246. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/entry_points.txt +0 -0
  247. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.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)