crackerjack 0.33.0__py3-none-any.whl → 0.33.2__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.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (198) hide show
  1. crackerjack/__main__.py +1350 -34
  2. crackerjack/adapters/__init__.py +17 -0
  3. crackerjack/adapters/lsp_client.py +358 -0
  4. crackerjack/adapters/rust_tool_adapter.py +194 -0
  5. crackerjack/adapters/rust_tool_manager.py +193 -0
  6. crackerjack/adapters/skylos_adapter.py +231 -0
  7. crackerjack/adapters/zuban_adapter.py +560 -0
  8. crackerjack/agents/base.py +7 -3
  9. crackerjack/agents/coordinator.py +271 -33
  10. crackerjack/agents/documentation_agent.py +9 -15
  11. crackerjack/agents/dry_agent.py +3 -15
  12. crackerjack/agents/formatting_agent.py +1 -1
  13. crackerjack/agents/import_optimization_agent.py +36 -180
  14. crackerjack/agents/performance_agent.py +17 -98
  15. crackerjack/agents/performance_helpers.py +7 -31
  16. crackerjack/agents/proactive_agent.py +1 -3
  17. crackerjack/agents/refactoring_agent.py +16 -85
  18. crackerjack/agents/refactoring_helpers.py +7 -42
  19. crackerjack/agents/security_agent.py +9 -48
  20. crackerjack/agents/test_creation_agent.py +356 -513
  21. crackerjack/agents/test_specialist_agent.py +0 -4
  22. crackerjack/api.py +6 -25
  23. crackerjack/cli/cache_handlers.py +204 -0
  24. crackerjack/cli/cache_handlers_enhanced.py +683 -0
  25. crackerjack/cli/facade.py +100 -0
  26. crackerjack/cli/handlers.py +224 -9
  27. crackerjack/cli/interactive.py +6 -4
  28. crackerjack/cli/options.py +642 -55
  29. crackerjack/cli/utils.py +2 -1
  30. crackerjack/code_cleaner.py +58 -117
  31. crackerjack/config/global_lock_config.py +8 -48
  32. crackerjack/config/hooks.py +53 -62
  33. crackerjack/core/async_workflow_orchestrator.py +24 -34
  34. crackerjack/core/autofix_coordinator.py +3 -17
  35. crackerjack/core/enhanced_container.py +4 -13
  36. crackerjack/core/file_lifecycle.py +12 -89
  37. crackerjack/core/performance.py +2 -2
  38. crackerjack/core/performance_monitor.py +15 -55
  39. crackerjack/core/phase_coordinator.py +104 -204
  40. crackerjack/core/resource_manager.py +14 -90
  41. crackerjack/core/service_watchdog.py +62 -95
  42. crackerjack/core/session_coordinator.py +149 -0
  43. crackerjack/core/timeout_manager.py +14 -72
  44. crackerjack/core/websocket_lifecycle.py +13 -78
  45. crackerjack/core/workflow_orchestrator.py +171 -174
  46. crackerjack/docs/INDEX.md +11 -0
  47. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  48. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  49. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  50. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  51. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  52. crackerjack/documentation/__init__.py +31 -0
  53. crackerjack/documentation/ai_templates.py +756 -0
  54. crackerjack/documentation/dual_output_generator.py +765 -0
  55. crackerjack/documentation/mkdocs_integration.py +518 -0
  56. crackerjack/documentation/reference_generator.py +977 -0
  57. crackerjack/dynamic_config.py +55 -50
  58. crackerjack/executors/async_hook_executor.py +10 -15
  59. crackerjack/executors/cached_hook_executor.py +117 -43
  60. crackerjack/executors/hook_executor.py +8 -34
  61. crackerjack/executors/hook_lock_manager.py +26 -183
  62. crackerjack/executors/individual_hook_executor.py +13 -11
  63. crackerjack/executors/lsp_aware_hook_executor.py +270 -0
  64. crackerjack/executors/tool_proxy.py +417 -0
  65. crackerjack/hooks/lsp_hook.py +79 -0
  66. crackerjack/intelligence/adaptive_learning.py +25 -10
  67. crackerjack/intelligence/agent_orchestrator.py +2 -5
  68. crackerjack/intelligence/agent_registry.py +34 -24
  69. crackerjack/intelligence/agent_selector.py +5 -7
  70. crackerjack/interactive.py +17 -6
  71. crackerjack/managers/async_hook_manager.py +0 -1
  72. crackerjack/managers/hook_manager.py +79 -1
  73. crackerjack/managers/publish_manager.py +44 -8
  74. crackerjack/managers/test_command_builder.py +1 -15
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +98 -7
  77. crackerjack/managers/test_manager_backup.py +10 -9
  78. crackerjack/mcp/cache.py +2 -2
  79. crackerjack/mcp/client_runner.py +1 -1
  80. crackerjack/mcp/context.py +191 -68
  81. crackerjack/mcp/dashboard.py +7 -5
  82. crackerjack/mcp/enhanced_progress_monitor.py +31 -28
  83. crackerjack/mcp/file_monitor.py +30 -23
  84. crackerjack/mcp/progress_components.py +31 -21
  85. crackerjack/mcp/progress_monitor.py +50 -53
  86. crackerjack/mcp/rate_limiter.py +6 -6
  87. crackerjack/mcp/server_core.py +17 -16
  88. crackerjack/mcp/service_watchdog.py +2 -1
  89. crackerjack/mcp/state.py +4 -7
  90. crackerjack/mcp/task_manager.py +11 -9
  91. crackerjack/mcp/tools/core_tools.py +173 -32
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +8 -10
  94. crackerjack/mcp/tools/execution_tools_backup.py +42 -30
  95. crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
  96. crackerjack/mcp/tools/intelligence_tools.py +5 -2
  97. crackerjack/mcp/tools/monitoring_tools.py +33 -70
  98. crackerjack/mcp/tools/proactive_tools.py +24 -11
  99. crackerjack/mcp/tools/progress_tools.py +5 -8
  100. crackerjack/mcp/tools/utility_tools.py +20 -14
  101. crackerjack/mcp/tools/workflow_executor.py +62 -40
  102. crackerjack/mcp/websocket/app.py +8 -0
  103. crackerjack/mcp/websocket/endpoints.py +352 -357
  104. crackerjack/mcp/websocket/jobs.py +40 -57
  105. crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
  106. crackerjack/mcp/websocket/server.py +7 -25
  107. crackerjack/mcp/websocket/websocket_handler.py +6 -17
  108. crackerjack/mixins/__init__.py +0 -2
  109. crackerjack/mixins/error_handling.py +1 -70
  110. crackerjack/models/config.py +12 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +122 -122
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  115. crackerjack/monitoring/metrics_collector.py +426 -0
  116. crackerjack/monitoring/regression_prevention.py +8 -8
  117. crackerjack/monitoring/websocket_server.py +643 -0
  118. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  119. crackerjack/orchestration/coverage_improvement.py +3 -3
  120. crackerjack/orchestration/execution_strategies.py +26 -6
  121. crackerjack/orchestration/test_progress_streamer.py +8 -5
  122. crackerjack/plugins/base.py +2 -2
  123. crackerjack/plugins/hooks.py +7 -0
  124. crackerjack/plugins/managers.py +11 -8
  125. crackerjack/security/__init__.py +0 -1
  126. crackerjack/security/audit.py +6 -35
  127. crackerjack/services/anomaly_detector.py +392 -0
  128. crackerjack/services/api_extractor.py +615 -0
  129. crackerjack/services/backup_service.py +2 -2
  130. crackerjack/services/bounded_status_operations.py +15 -152
  131. crackerjack/services/cache.py +127 -1
  132. crackerjack/services/changelog_automation.py +395 -0
  133. crackerjack/services/config.py +15 -9
  134. crackerjack/services/config_merge.py +19 -80
  135. crackerjack/services/config_template.py +506 -0
  136. crackerjack/services/contextual_ai_assistant.py +48 -22
  137. crackerjack/services/coverage_badge_service.py +171 -0
  138. crackerjack/services/coverage_ratchet.py +27 -25
  139. crackerjack/services/debug.py +3 -3
  140. crackerjack/services/dependency_analyzer.py +460 -0
  141. crackerjack/services/dependency_monitor.py +14 -11
  142. crackerjack/services/documentation_generator.py +491 -0
  143. crackerjack/services/documentation_service.py +675 -0
  144. crackerjack/services/enhanced_filesystem.py +6 -5
  145. crackerjack/services/enterprise_optimizer.py +865 -0
  146. crackerjack/services/error_pattern_analyzer.py +676 -0
  147. crackerjack/services/file_hasher.py +1 -1
  148. crackerjack/services/git.py +8 -25
  149. crackerjack/services/health_metrics.py +10 -8
  150. crackerjack/services/heatmap_generator.py +735 -0
  151. crackerjack/services/initialization.py +11 -30
  152. crackerjack/services/input_validator.py +5 -97
  153. crackerjack/services/intelligent_commit.py +327 -0
  154. crackerjack/services/log_manager.py +15 -12
  155. crackerjack/services/logging.py +4 -3
  156. crackerjack/services/lsp_client.py +628 -0
  157. crackerjack/services/memory_optimizer.py +19 -87
  158. crackerjack/services/metrics.py +42 -33
  159. crackerjack/services/parallel_executor.py +9 -67
  160. crackerjack/services/pattern_cache.py +1 -1
  161. crackerjack/services/pattern_detector.py +6 -6
  162. crackerjack/services/performance_benchmarks.py +18 -59
  163. crackerjack/services/performance_cache.py +20 -81
  164. crackerjack/services/performance_monitor.py +27 -95
  165. crackerjack/services/predictive_analytics.py +510 -0
  166. crackerjack/services/quality_baseline.py +234 -0
  167. crackerjack/services/quality_baseline_enhanced.py +646 -0
  168. crackerjack/services/quality_intelligence.py +785 -0
  169. crackerjack/services/regex_patterns.py +618 -524
  170. crackerjack/services/regex_utils.py +43 -123
  171. crackerjack/services/secure_path_utils.py +5 -164
  172. crackerjack/services/secure_status_formatter.py +30 -141
  173. crackerjack/services/secure_subprocess.py +11 -92
  174. crackerjack/services/security.py +9 -41
  175. crackerjack/services/security_logger.py +12 -24
  176. crackerjack/services/server_manager.py +124 -16
  177. crackerjack/services/status_authentication.py +16 -159
  178. crackerjack/services/status_security_manager.py +4 -131
  179. crackerjack/services/thread_safe_status_collector.py +19 -125
  180. crackerjack/services/unified_config.py +21 -13
  181. crackerjack/services/validation_rate_limiter.py +5 -54
  182. crackerjack/services/version_analyzer.py +459 -0
  183. crackerjack/services/version_checker.py +1 -1
  184. crackerjack/services/websocket_resource_limiter.py +10 -144
  185. crackerjack/services/zuban_lsp_service.py +390 -0
  186. crackerjack/slash_commands/__init__.py +2 -7
  187. crackerjack/slash_commands/run.md +2 -2
  188. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  189. crackerjack/tools/validate_regex_patterns.py +19 -48
  190. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.2.dist-info/RECORD +229 -0
  192. crackerjack/CLAUDE.md +0 -207
  193. crackerjack/RULES.md +0 -380
  194. crackerjack/py313.py +0 -234
  195. crackerjack-0.33.0.dist-info/RECORD +0 -187
  196. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,171 @@
1
+ from contextlib import suppress
2
+ from pathlib import Path
3
+
4
+ from rich.console import Console
5
+
6
+ from .regex_patterns import SAFE_PATTERNS
7
+
8
+
9
+ class CoverageBadgeService:
10
+ """Service for managing coverage badges in README.md files."""
11
+
12
+ def __init__(self, console: Console, project_root: Path) -> None:
13
+ self.console = console
14
+ self.project_root = project_root
15
+ self.readme_path = project_root / "README.md"
16
+
17
+ def update_readme_coverage_badge(self, coverage_percent: float) -> bool:
18
+ """Update or insert coverage badge in README.md with current coverage percentage."""
19
+ if not self.readme_path.exists():
20
+ self.console.print(
21
+ "[yellow]⚠️[/yellow] README.md not found, skipping badge update"
22
+ )
23
+ return False
24
+
25
+ try:
26
+ readme_content = self.readme_path.read_text(encoding="utf-8")
27
+ badge_url = self._generate_badge_url(coverage_percent)
28
+
29
+ if self._has_coverage_badge(readme_content):
30
+ updated_content = self._update_existing_badge(readme_content, badge_url)
31
+ action = "updated"
32
+ else:
33
+ updated_content = self._insert_new_badge(readme_content, badge_url)
34
+ action = "added"
35
+
36
+ if updated_content != readme_content:
37
+ self.readme_path.write_text(updated_content, encoding="utf-8")
38
+ self.console.print(
39
+ f"[green]📊[/green] Coverage badge {action}: {coverage_percent:.1f}%"
40
+ )
41
+ return True
42
+ else:
43
+ return False
44
+
45
+ except Exception as e:
46
+ self.console.print(f"[red]❌[/red] Failed to update coverage badge: {e}")
47
+ return False
48
+
49
+ def _generate_badge_url(self, coverage_percent: float) -> str:
50
+ """Generate shields.io badge URL with appropriate color coding."""
51
+ color = self._get_badge_color(coverage_percent)
52
+ # URL encode the % symbol as %25
53
+ encoded_percent = f"{coverage_percent:.1f}%25"
54
+ return f"https://img.shields.io/badge/coverage-{encoded_percent}-{color}"
55
+
56
+ def _get_badge_color(self, coverage_percent: float) -> str:
57
+ """Determine badge color based on coverage percentage."""
58
+ if coverage_percent < 50:
59
+ return "red"
60
+ elif coverage_percent < 80:
61
+ return "yellow"
62
+ return "brightgreen"
63
+
64
+ def _has_coverage_badge(self, content: str) -> bool:
65
+ """Check if README already contains a coverage badge."""
66
+ # Use safe pattern for badge detection
67
+ return SAFE_PATTERNS["detect_coverage_badge"].search(content) is not None
68
+
69
+ def _update_existing_badge(self, content: str, new_badge_url: str) -> str:
70
+ """Replace existing coverage badge with new one."""
71
+ # Try different safe patterns for badge replacement
72
+ patterns_to_try = [
73
+ "update_coverage_badge_url",
74
+ "update_coverage_badge_any",
75
+ "update_shields_coverage_url",
76
+ ]
77
+
78
+ for pattern_name in patterns_to_try:
79
+ pattern_obj = SAFE_PATTERNS[pattern_name]
80
+ # Use the pattern and manually replace NEW_BADGE_URL with actual URL
81
+ temp_content = pattern_obj.apply(content)
82
+ if temp_content != content:
83
+ # Replace placeholder with actual URL
84
+ new_content = temp_content.replace("NEW_BADGE_URL", new_badge_url)
85
+ return new_content
86
+
87
+ return content
88
+
89
+ def _insert_new_badge(self, content: str, badge_url: str) -> str:
90
+ """Insert new coverage badge in the appropriate location."""
91
+ lines = content.split("\n")
92
+
93
+ # Find the badge section (after title, before first heading)
94
+ insert_index = self._find_badge_insertion_point(lines)
95
+
96
+ if insert_index is not None:
97
+ coverage_badge = f"![Coverage]({badge_url})"
98
+ lines.insert(insert_index, coverage_badge)
99
+ return "\n".join(lines)
100
+ # Fallback: add after title
101
+ return self._insert_after_title(content, badge_url)
102
+
103
+ def _find_badge_insertion_point(self, lines: list[str]) -> int | None:
104
+ """Find the best location to insert the coverage badge."""
105
+ # Look for existing badge lines
106
+ badge_lines = [
107
+ i for i, line in enumerate(lines) if line.strip().startswith(("[![", "!["))
108
+ ]
109
+
110
+ if badge_lines:
111
+ # Insert after the last existing badge
112
+ return badge_lines[-1] + 1
113
+
114
+ # Look for first non-empty line after title
115
+ title_found = False
116
+ for i, line in enumerate(lines):
117
+ if line.startswith("#") and not title_found:
118
+ title_found = True
119
+ continue
120
+ elif title_found and line.strip() == "":
121
+ continue
122
+ elif title_found and line.strip():
123
+ return i
124
+
125
+ return None
126
+
127
+ def _insert_after_title(self, content: str, badge_url: str) -> str:
128
+ """Fallback method to insert badge after the title."""
129
+ lines = content.split("\n")
130
+
131
+ # Find title line
132
+ for i, line in enumerate(lines):
133
+ if line.startswith("#"):
134
+ # Insert after title with blank line
135
+ coverage_badge = f"![Coverage]({badge_url})"
136
+ if i + 1 < len(lines) and lines[i + 1].strip() == "":
137
+ lines.insert(i + 2, coverage_badge)
138
+ else:
139
+ lines.insert(i + 1, "")
140
+ lines.insert(i + 2, coverage_badge)
141
+ break
142
+
143
+ return "\n".join(lines)
144
+
145
+ def should_update_badge(self, coverage_percent: float) -> bool:
146
+ """Check if badge should be updated based on coverage change."""
147
+ if not self.readme_path.exists():
148
+ return False
149
+
150
+ try:
151
+ content = self.readme_path.read_text(encoding="utf-8")
152
+ current_coverage = self._extract_current_coverage(content)
153
+
154
+ if current_coverage is None:
155
+ return True # No badge exists, should add one
156
+
157
+ # Only update if coverage changed by at least 0.1%
158
+ return abs(coverage_percent - current_coverage) >= 0.1
159
+
160
+ except Exception:
161
+ return True # On error, attempt update
162
+
163
+ def _extract_current_coverage(self, content: str) -> float | None:
164
+ """Extract current coverage percentage from existing badge."""
165
+ match = SAFE_PATTERNS["extract_coverage_percentage"].search(content)
166
+
167
+ if match:
168
+ with suppress(ValueError):
169
+ return float(match.group(1))
170
+
171
+ return None
@@ -9,7 +9,7 @@ from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
9
9
 
10
10
  class CoverageRatchetService:
11
11
  MILESTONES = [15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 95, 100]
12
- # 2% tolerance margin for coverage fluctuations to prevent test flakiness
12
+
13
13
  TOLERANCE_MARGIN = 2.0
14
14
 
15
15
  def __init__(self, pkg_path: Path, console: Console) -> None:
@@ -22,7 +22,7 @@ class CoverageRatchetService:
22
22
  if self.ratchet_file.exists():
23
23
  return
24
24
 
25
- ratchet_data = {
25
+ ratchet_data: dict[str, t.Any] = {
26
26
  "baseline": initial_coverage,
27
27
  "current_minimum": initial_coverage,
28
28
  "target": 100.0,
@@ -47,44 +47,39 @@ class CoverageRatchetService:
47
47
  def get_ratchet_data(self) -> dict[str, t.Any]:
48
48
  if not self.ratchet_file.exists():
49
49
  return {}
50
- return json.loads(self.ratchet_file.read_text())
50
+ return t.cast(dict[str, t.Any], json.loads(self.ratchet_file.read_text()))
51
+
52
+ def get_status_report(self) -> dict[str, t.Any]:
53
+ """Get status report for coverage ratchet service."""
54
+ return self.get_ratchet_data()
51
55
 
52
56
  def get_baseline(self) -> float:
53
- return self.get_ratchet_data().get("baseline", 0.0)
57
+ data = self.get_ratchet_data()
58
+ baseline = data.get("baseline")
59
+ return float(baseline) if baseline is not None else 0.0
54
60
 
55
61
  def get_baseline_coverage(self) -> float:
56
- """Protocol method: Get baseline coverage."""
57
62
  return self.get_baseline()
58
63
 
59
64
  def update_baseline_coverage(self, new_coverage: float) -> bool:
60
- """Protocol method: Update baseline coverage."""
61
- return self.update_coverage(new_coverage).get("success", False)
65
+ result: bool = self.update_coverage(new_coverage).get("success", False)
66
+ return result
62
67
 
63
68
  def is_coverage_regression(self, current_coverage: float) -> bool:
64
- """Protocol method: Check if coverage is a regression."""
65
69
  baseline = self.get_baseline()
66
70
  return current_coverage < (baseline - self.TOLERANCE_MARGIN)
67
71
 
68
- def get_coverage_improvement_needed(self) -> float:
69
- """Protocol method: Get coverage improvement needed."""
72
+ def calculate_coverage_gap(self) -> float:
70
73
  data = self.get_ratchet_data()
71
- baseline = data.get("baseline", 0.0)
74
+ baseline = data.get("baseline")
75
+ baseline = float(baseline) if baseline is not None else 0.0
72
76
  next_milestone = data.get("next_milestone")
77
+ next_milestone = float(next_milestone) if next_milestone is not None else None
73
78
  if next_milestone:
74
79
  return next_milestone - baseline
75
80
  return 100.0 - baseline
76
81
 
77
82
  def update_coverage(self, new_coverage: float) -> dict[str, t.Any]:
78
- """Update coverage with 2% tolerance margin to prevent test flakiness.
79
-
80
- Behavior:
81
- - Coverage below (baseline - 2%): FAIL (regression)
82
- - Coverage within ±2% of baseline: PASS (maintained)
83
- - Coverage above baseline: PASS (improved, updates baseline)
84
-
85
- This prevents failures due to small coverage fluctuations while
86
- still catching significant regressions.
87
- """
88
83
  if not self.ratchet_file.exists():
89
84
  self.initialize_baseline(new_coverage)
90
85
  return {
@@ -99,7 +94,6 @@ class CoverageRatchetService:
99
94
  data = self.get_ratchet_data()
100
95
  current_baseline = data["baseline"]
101
96
 
102
- # Check if coverage is below the tolerance margin (baseline - 2%)
103
97
  tolerance_threshold = current_baseline - self.TOLERANCE_MARGIN
104
98
  if new_coverage < tolerance_threshold:
105
99
  return {
@@ -126,11 +120,12 @@ class CoverageRatchetService:
126
120
  "next_milestone": self._get_next_milestone(new_coverage),
127
121
  "points_to_next": (next_milestone - new_coverage)
128
122
  if (next_milestone := self._get_next_milestone(new_coverage))
123
+ is not None
129
124
  else 0,
130
125
  "allowed": True,
131
126
  "baseline_updated": True,
132
127
  }
133
- # Coverage is within tolerance margin - treat as maintained
128
+
134
129
  return {
135
130
  "status": "maintained",
136
131
  "message": f"Coverage maintained at {new_coverage: .2f}% (within {self.TOLERANCE_MARGIN}% tolerance margin)",
@@ -141,7 +136,7 @@ class CoverageRatchetService:
141
136
  def _check_milestones(
142
137
  self, old_coverage: float, new_coverage: float, data: dict[str, t.Any]
143
138
  ) -> list[float]:
144
- achieved_milestones = set(data.get("milestones_achieved", []))
139
+ achieved_milestones = set[t.Any](data.get("milestones_achieved", []))
145
140
  return [
146
141
  milestone
147
142
  for milestone in self.MILESTONES
@@ -233,7 +228,14 @@ class CoverageRatchetService:
233
228
 
234
229
  return result
235
230
 
236
- def get_status_report(self) -> dict[str, t.Any]:
231
+ def get_coverage_improvement_needed(self) -> float:
232
+ """Get percentage improvement needed to reach next milestone."""
233
+ current = self.get_baseline_coverage()
234
+ for milestone in self.MILESTONES:
235
+ if current < milestone:
236
+ needed = milestone - current
237
+ return max(0.0, needed)
238
+ return 0.0
237
239
  data = self.get_ratchet_data()
238
240
  if not data:
239
241
  return {"status": "not_initialized"}
@@ -24,13 +24,13 @@ class AIAgentDebugger:
24
24
  self.logger = get_logger("crackerjack.ai_agent.debug")
25
25
  self.session_id = f"debug_{int(time.time())}"
26
26
 
27
+ self.debug_log_path: Path | None = None
28
+
27
29
  if self.enabled:
28
30
  log_manager = get_log_manager()
29
31
  self.debug_log_path = log_manager.create_debug_log_file(
30
32
  f"ai-agent -{self.session_id}",
31
33
  )
32
- else:
33
- self.debug_log_path = None
34
34
 
35
35
  self.mcp_operations: list[dict[str, Any]] = []
36
36
  self.agent_activities: list[dict[str, Any]] = []
@@ -400,7 +400,7 @@ class AIAgentDebugger:
400
400
  self.console.print(table)
401
401
 
402
402
  def _print_agent_activity_breakdown(self, border_style: str = "red") -> None:
403
- agent_stats = {}
403
+ agent_stats: dict[str, dict[str, t.Any]] = {}
404
404
  for activity in self.agent_activities:
405
405
  agent = activity["agent"]
406
406
  if agent not in agent_stats: