crackerjack 0.31.10__py3-none-any.whl → 0.31.13__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 (155) hide show
  1. crackerjack/CLAUDE.md +288 -705
  2. crackerjack/__main__.py +22 -8
  3. crackerjack/agents/__init__.py +0 -3
  4. crackerjack/agents/architect_agent.py +0 -43
  5. crackerjack/agents/base.py +1 -9
  6. crackerjack/agents/coordinator.py +2 -148
  7. crackerjack/agents/documentation_agent.py +109 -81
  8. crackerjack/agents/dry_agent.py +122 -97
  9. crackerjack/agents/formatting_agent.py +3 -16
  10. crackerjack/agents/import_optimization_agent.py +1174 -130
  11. crackerjack/agents/performance_agent.py +956 -188
  12. crackerjack/agents/performance_helpers.py +229 -0
  13. crackerjack/agents/proactive_agent.py +1 -48
  14. crackerjack/agents/refactoring_agent.py +516 -246
  15. crackerjack/agents/refactoring_helpers.py +282 -0
  16. crackerjack/agents/security_agent.py +393 -90
  17. crackerjack/agents/test_creation_agent.py +1776 -120
  18. crackerjack/agents/test_specialist_agent.py +59 -15
  19. crackerjack/agents/tracker.py +0 -102
  20. crackerjack/api.py +145 -37
  21. crackerjack/cli/handlers.py +48 -30
  22. crackerjack/cli/interactive.py +11 -11
  23. crackerjack/cli/options.py +66 -4
  24. crackerjack/code_cleaner.py +808 -148
  25. crackerjack/config/global_lock_config.py +110 -0
  26. crackerjack/config/hooks.py +43 -64
  27. crackerjack/core/async_workflow_orchestrator.py +247 -97
  28. crackerjack/core/autofix_coordinator.py +192 -109
  29. crackerjack/core/enhanced_container.py +46 -63
  30. crackerjack/core/file_lifecycle.py +549 -0
  31. crackerjack/core/performance.py +9 -8
  32. crackerjack/core/performance_monitor.py +395 -0
  33. crackerjack/core/phase_coordinator.py +281 -94
  34. crackerjack/core/proactive_workflow.py +9 -58
  35. crackerjack/core/resource_manager.py +501 -0
  36. crackerjack/core/service_watchdog.py +490 -0
  37. crackerjack/core/session_coordinator.py +4 -8
  38. crackerjack/core/timeout_manager.py +504 -0
  39. crackerjack/core/websocket_lifecycle.py +475 -0
  40. crackerjack/core/workflow_orchestrator.py +343 -209
  41. crackerjack/dynamic_config.py +50 -9
  42. crackerjack/errors.py +3 -4
  43. crackerjack/executors/async_hook_executor.py +63 -13
  44. crackerjack/executors/cached_hook_executor.py +14 -14
  45. crackerjack/executors/hook_executor.py +100 -37
  46. crackerjack/executors/hook_lock_manager.py +856 -0
  47. crackerjack/executors/individual_hook_executor.py +120 -86
  48. crackerjack/intelligence/__init__.py +0 -7
  49. crackerjack/intelligence/adaptive_learning.py +13 -86
  50. crackerjack/intelligence/agent_orchestrator.py +15 -78
  51. crackerjack/intelligence/agent_registry.py +12 -59
  52. crackerjack/intelligence/agent_selector.py +31 -92
  53. crackerjack/intelligence/integration.py +1 -41
  54. crackerjack/interactive.py +9 -9
  55. crackerjack/managers/async_hook_manager.py +25 -8
  56. crackerjack/managers/hook_manager.py +9 -9
  57. crackerjack/managers/publish_manager.py +57 -59
  58. crackerjack/managers/test_command_builder.py +6 -36
  59. crackerjack/managers/test_executor.py +9 -61
  60. crackerjack/managers/test_manager.py +17 -63
  61. crackerjack/managers/test_manager_backup.py +77 -127
  62. crackerjack/managers/test_progress.py +4 -23
  63. crackerjack/mcp/cache.py +5 -12
  64. crackerjack/mcp/client_runner.py +10 -10
  65. crackerjack/mcp/context.py +64 -6
  66. crackerjack/mcp/dashboard.py +14 -11
  67. crackerjack/mcp/enhanced_progress_monitor.py +55 -55
  68. crackerjack/mcp/file_monitor.py +72 -42
  69. crackerjack/mcp/progress_components.py +103 -84
  70. crackerjack/mcp/progress_monitor.py +122 -49
  71. crackerjack/mcp/rate_limiter.py +12 -12
  72. crackerjack/mcp/server_core.py +16 -22
  73. crackerjack/mcp/service_watchdog.py +26 -26
  74. crackerjack/mcp/state.py +15 -0
  75. crackerjack/mcp/tools/core_tools.py +95 -39
  76. crackerjack/mcp/tools/error_analyzer.py +6 -32
  77. crackerjack/mcp/tools/execution_tools.py +1 -56
  78. crackerjack/mcp/tools/execution_tools_backup.py +35 -131
  79. crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
  80. crackerjack/mcp/tools/intelligence_tools.py +2 -55
  81. crackerjack/mcp/tools/monitoring_tools.py +308 -145
  82. crackerjack/mcp/tools/proactive_tools.py +12 -42
  83. crackerjack/mcp/tools/progress_tools.py +23 -15
  84. crackerjack/mcp/tools/utility_tools.py +3 -40
  85. crackerjack/mcp/tools/workflow_executor.py +40 -60
  86. crackerjack/mcp/websocket/app.py +0 -3
  87. crackerjack/mcp/websocket/endpoints.py +206 -268
  88. crackerjack/mcp/websocket/jobs.py +213 -66
  89. crackerjack/mcp/websocket/server.py +84 -6
  90. crackerjack/mcp/websocket/websocket_handler.py +137 -29
  91. crackerjack/models/config_adapter.py +3 -16
  92. crackerjack/models/protocols.py +162 -3
  93. crackerjack/models/resource_protocols.py +454 -0
  94. crackerjack/models/task.py +3 -3
  95. crackerjack/monitoring/__init__.py +0 -0
  96. crackerjack/monitoring/ai_agent_watchdog.py +25 -71
  97. crackerjack/monitoring/regression_prevention.py +28 -87
  98. crackerjack/orchestration/advanced_orchestrator.py +44 -78
  99. crackerjack/orchestration/coverage_improvement.py +10 -60
  100. crackerjack/orchestration/execution_strategies.py +16 -16
  101. crackerjack/orchestration/test_progress_streamer.py +61 -53
  102. crackerjack/plugins/base.py +1 -1
  103. crackerjack/plugins/managers.py +22 -20
  104. crackerjack/py313.py +65 -21
  105. crackerjack/services/backup_service.py +467 -0
  106. crackerjack/services/bounded_status_operations.py +627 -0
  107. crackerjack/services/cache.py +7 -9
  108. crackerjack/services/config.py +35 -52
  109. crackerjack/services/config_integrity.py +5 -16
  110. crackerjack/services/config_merge.py +542 -0
  111. crackerjack/services/contextual_ai_assistant.py +17 -19
  112. crackerjack/services/coverage_ratchet.py +44 -73
  113. crackerjack/services/debug.py +25 -39
  114. crackerjack/services/dependency_monitor.py +52 -50
  115. crackerjack/services/enhanced_filesystem.py +14 -11
  116. crackerjack/services/file_hasher.py +1 -1
  117. crackerjack/services/filesystem.py +1 -12
  118. crackerjack/services/git.py +71 -47
  119. crackerjack/services/health_metrics.py +31 -27
  120. crackerjack/services/initialization.py +276 -428
  121. crackerjack/services/input_validator.py +760 -0
  122. crackerjack/services/log_manager.py +16 -16
  123. crackerjack/services/logging.py +7 -6
  124. crackerjack/services/metrics.py +43 -43
  125. crackerjack/services/pattern_cache.py +2 -31
  126. crackerjack/services/pattern_detector.py +26 -63
  127. crackerjack/services/performance_benchmarks.py +20 -45
  128. crackerjack/services/regex_patterns.py +2887 -0
  129. crackerjack/services/regex_utils.py +537 -0
  130. crackerjack/services/secure_path_utils.py +683 -0
  131. crackerjack/services/secure_status_formatter.py +534 -0
  132. crackerjack/services/secure_subprocess.py +605 -0
  133. crackerjack/services/security.py +47 -10
  134. crackerjack/services/security_logger.py +492 -0
  135. crackerjack/services/server_manager.py +109 -50
  136. crackerjack/services/smart_scheduling.py +8 -25
  137. crackerjack/services/status_authentication.py +603 -0
  138. crackerjack/services/status_security_manager.py +442 -0
  139. crackerjack/services/thread_safe_status_collector.py +546 -0
  140. crackerjack/services/tool_version_service.py +1 -23
  141. crackerjack/services/unified_config.py +36 -58
  142. crackerjack/services/validation_rate_limiter.py +269 -0
  143. crackerjack/services/version_checker.py +9 -40
  144. crackerjack/services/websocket_resource_limiter.py +572 -0
  145. crackerjack/slash_commands/__init__.py +52 -2
  146. crackerjack/tools/__init__.py +0 -0
  147. crackerjack/tools/validate_input_validator_patterns.py +262 -0
  148. crackerjack/tools/validate_regex_patterns.py +198 -0
  149. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/METADATA +197 -12
  150. crackerjack-0.31.13.dist-info/RECORD +178 -0
  151. crackerjack/cli/facade.py +0 -104
  152. crackerjack-0.31.10.dist-info/RECORD +0 -149
  153. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/WHEEL +0 -0
  154. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/entry_points.txt +0 -0
  155. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/licenses/LICENSE +0 -0
@@ -8,18 +8,9 @@ from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
8
8
 
9
9
 
10
10
  class CoverageRatchetService:
11
- """
12
- Coverage ratchet system that prevents regression and targets 100% coverage.
13
-
14
- Core principles:
15
- - Coverage can only increase, never decrease
16
- - Celebrates milestones and progress toward 100%
17
- - Automatically updates pyproject.toml when coverage improves
18
- - Tracks history and provides visualization
19
- """
20
-
21
- # Milestone thresholds for celebration
22
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
13
+ TOLERANCE_MARGIN = 2.0
23
14
 
24
15
  def __init__(self, pkg_path: Path, console: Console) -> None:
25
16
  self.pkg_path = pkg_path
@@ -28,9 +19,8 @@ class CoverageRatchetService:
28
19
  self.pyproject_file = pkg_path / "pyproject.toml"
29
20
 
30
21
  def initialize_baseline(self, initial_coverage: float) -> None:
31
- """Initialize the coverage ratchet with current baseline."""
32
22
  if self.ratchet_file.exists():
33
- return # Already initialized
23
+ return
34
24
 
35
25
  ratchet_data = {
36
26
  "baseline": initial_coverage,
@@ -51,33 +41,35 @@ class CoverageRatchetService:
51
41
 
52
42
  self.ratchet_file.write_text(json.dumps(ratchet_data, indent=2))
53
43
  self.console.print(
54
- f"[cyan]📊[/cyan] Coverage ratchet initialized at {initial_coverage:.2f}% baseline"
44
+ f"[cyan]📊[/ cyan] Coverage ratchet initialized at {initial_coverage: .2f}% baseline"
55
45
  )
56
46
 
57
47
  def get_ratchet_data(self) -> dict[str, t.Any]:
58
- """Get current ratchet data."""
59
48
  if not self.ratchet_file.exists():
60
49
  return {}
61
50
  return json.loads(self.ratchet_file.read_text())
62
51
 
63
52
  def get_baseline(self) -> float:
64
- """Get current coverage baseline."""
65
53
  return self.get_ratchet_data().get("baseline", 0.0)
66
54
 
67
55
  def update_coverage(self, new_coverage: float) -> dict[str, t.Any]:
68
- """
69
- Update coverage and return achievement info.
56
+ """Update coverage with 2% tolerance margin to prevent test flakiness.
70
57
 
71
- Returns:
72
- dict with status, message, milestones hit, and whether build should pass
58
+ Behavior:
59
+ - Coverage below (baseline - 2%): FAIL (regression)
60
+ - Coverage within ±2% of baseline: PASS (maintained)
61
+ - Coverage above baseline: PASS (improved, updates baseline)
62
+
63
+ This prevents failures due to small coverage fluctuations while
64
+ still catching significant regressions.
73
65
  """
74
66
  if not self.ratchet_file.exists():
75
67
  self.initialize_baseline(new_coverage)
76
68
  return {
77
69
  "status": "initialized",
78
- "message": f"Coverage ratchet initialized at {new_coverage:.2f}%",
70
+ "message": f"Coverage ratchet initialized at {new_coverage: .2f}%",
79
71
  "milestones": [],
80
- "progress_to_100": f"{new_coverage:.1f}% of the way to 100% coverage",
72
+ "progress_to_100": f"{new_coverage: .1f}% of the way to 100 % coverage",
81
73
  "allowed": True,
82
74
  "baseline_updated": True,
83
75
  }
@@ -85,17 +77,18 @@ class CoverageRatchetService:
85
77
  data = self.get_ratchet_data()
86
78
  current_baseline = data["baseline"]
87
79
 
88
- if (
89
- new_coverage < current_baseline - 0.01
90
- ): # Allow tiny float precision differences
80
+ # Check if coverage is below the tolerance margin (baseline - 2%)
81
+ tolerance_threshold = current_baseline - self.TOLERANCE_MARGIN
82
+ if new_coverage < tolerance_threshold:
91
83
  return {
92
84
  "status": "regression",
93
- "message": f"Coverage decreased from {current_baseline:.2f}% to {new_coverage:.2f}%",
85
+ "message": f"Coverage decreased from {current_baseline: .2f}% to {new_coverage: .2f}% (below {self.TOLERANCE_MARGIN}% tolerance margin)",
94
86
  "regression_amount": current_baseline - new_coverage,
87
+ "tolerance_threshold": tolerance_threshold,
95
88
  "allowed": False,
96
89
  "baseline_updated": False,
97
90
  }
98
- elif new_coverage > current_baseline + 0.01: # Significant improvement
91
+ elif new_coverage > current_baseline + 0.01:
99
92
  milestones_hit = self._check_milestones(
100
93
  current_baseline, new_coverage, data
101
94
  )
@@ -104,10 +97,10 @@ class CoverageRatchetService:
104
97
 
105
98
  return {
106
99
  "status": "improved",
107
- "message": f"Coverage improved from {current_baseline:.2f}% to {new_coverage:.2f}%!",
100
+ "message": f"Coverage improved from {current_baseline: .2f}% to {new_coverage: .2f}% !",
108
101
  "improvement": new_coverage - current_baseline,
109
102
  "milestones": milestones_hit,
110
- "progress_to_100": f"{new_coverage:.1f}% of the way to 100% coverage",
103
+ "progress_to_100": f"{new_coverage: .1f}% of the way to 100 % coverage",
111
104
  "next_milestone": self._get_next_milestone(new_coverage),
112
105
  "points_to_next": (next_milestone - new_coverage)
113
106
  if (next_milestone := self._get_next_milestone(new_coverage))
@@ -115,9 +108,10 @@ class CoverageRatchetService:
115
108
  "allowed": True,
116
109
  "baseline_updated": True,
117
110
  }
111
+ # Coverage is within tolerance margin - treat as maintained
118
112
  return {
119
113
  "status": "maintained",
120
- "message": f"Coverage maintained at {new_coverage:.2f}%",
114
+ "message": f"Coverage maintained at {new_coverage: .2f}% (within {self.TOLERANCE_MARGIN}% tolerance margin)",
121
115
  "allowed": True,
122
116
  "baseline_updated": False,
123
117
  }
@@ -125,7 +119,6 @@ class CoverageRatchetService:
125
119
  def _check_milestones(
126
120
  self, old_coverage: float, new_coverage: float, data: dict[str, t.Any]
127
121
  ) -> list[float]:
128
- """Check which milestones were crossed."""
129
122
  achieved_milestones = set(data.get("milestones_achieved", []))
130
123
  return [
131
124
  milestone
@@ -137,7 +130,6 @@ class CoverageRatchetService:
137
130
  ]
138
131
 
139
132
  def _get_next_milestone(self, coverage: float) -> float | None:
140
- """Get the next milestone to target."""
141
133
  for milestone in self.MILESTONES:
142
134
  if milestone > coverage:
143
135
  return milestone
@@ -146,50 +138,40 @@ class CoverageRatchetService:
146
138
  def _update_baseline(
147
139
  self, new_coverage: float, data: dict[str, t.Any], milestones_hit: list[float]
148
140
  ) -> None:
149
- """Update the ratchet baseline and history."""
150
141
  data["baseline"] = new_coverage
151
142
  data["current_minimum"] = new_coverage
152
143
  data["last_updated"] = datetime.now().isoformat()
153
144
 
154
- # Add to history
155
145
  data["history"].append(
156
146
  {
157
147
  "date": datetime.now().isoformat(),
158
148
  "coverage": new_coverage,
159
- "commit": "current", # Could integrate with git later
149
+ "commit": "current",
160
150
  "milestone": len(milestones_hit) > 0,
161
151
  "milestones_hit": milestones_hit,
162
152
  }
163
153
  )
164
154
 
165
- # Update achieved milestones
166
155
  for milestone in milestones_hit:
167
156
  if milestone not in data["milestones_achieved"]:
168
157
  data["milestones_achieved"].append(milestone)
169
158
 
170
159
  data["next_milestone"] = self._get_next_milestone(new_coverage)
171
160
 
172
- # Keep history manageable (last 50 entries)
173
161
  if len(data["history"]) > 50:
174
162
  data["history"] = data["history"][-50:]
175
163
 
176
164
  self.ratchet_file.write_text(json.dumps(data, indent=2))
177
165
 
178
166
  def _update_pyproject_requirement(self, new_coverage: float) -> None:
179
- """Update pyproject.toml with new coverage requirement."""
180
167
  try:
181
168
  content = self.pyproject_file.read_text()
182
169
 
183
- import re
184
-
185
- # Update the --cov-fail-under value
186
- pattern = r"(--cov-fail-under=)\d+\.?\d*"
187
- replacement = f"\\g<1>{new_coverage:.0f}"
170
+ from crackerjack.services.regex_patterns import update_coverage_requirement
188
171
 
189
- updated_content = re.sub(pattern, replacement, content)
172
+ updated_content = update_coverage_requirement(content, new_coverage)
190
173
 
191
174
  if updated_content != content:
192
- # Clean trailing whitespace and ensure single trailing newline
193
175
  from crackerjack.services.filesystem import FileSystemService
194
176
 
195
177
  updated_content = (
@@ -200,16 +182,15 @@ class CoverageRatchetService:
200
182
 
201
183
  self.pyproject_file.write_text(updated_content)
202
184
  self.console.print(
203
- f"[cyan]📝[/cyan] Updated pyproject.toml coverage requirement to {new_coverage:.0f}%"
185
+ f"[cyan]📝[/ cyan] Updated pyproject.toml coverage requirement to {new_coverage: .0f}%"
204
186
  )
205
187
 
206
188
  except Exception as e:
207
189
  self.console.print(
208
- f"[yellow]⚠️[/yellow] Failed to update pyproject.toml: {e}"
190
+ f"[yellow]⚠️[/ yellow] Failed to update pyproject.toml: {e}"
209
191
  )
210
192
 
211
193
  def get_progress_visualization(self) -> str:
212
- """Get a visual progress bar toward 100% coverage."""
213
194
  data = self.get_ratchet_data()
214
195
  if not data:
215
196
  return "Coverage ratchet not initialized"
@@ -218,21 +199,19 @@ class CoverageRatchetService:
218
199
  target = 100.0
219
200
  next_milestone = data.get("next_milestone")
220
201
 
221
- # Create progress bar
222
202
  progress_chars = int(current / target * 20)
223
203
  bar = "█" * progress_chars + "░" * (20 - progress_chars)
224
204
 
225
- result = f"Coverage Progress: {current:.2f}% [{bar}] → 100%\n"
226
- result += f" Current ─┘{'':>18} └─ Goal\n"
205
+ result = f"Coverage Progress: {current: .2f}% [{bar}] → 100 %\n"
206
+ result += f" Current ─┘{'': > 18} └─ Goal\n"
227
207
 
228
208
  if next_milestone:
229
209
  points_needed = next_milestone - current
230
- result += f"Next milestone: {next_milestone:.0f}% (+{points_needed:.2f}% needed)\n"
210
+ result += f"Next milestone: {next_milestone: .0f}% (+{points_needed: .2f}% needed)\n"
231
211
 
232
212
  return result
233
213
 
234
214
  def get_status_report(self) -> dict[str, t.Any]:
235
- """Get comprehensive status report for monitoring."""
236
215
  data = self.get_ratchet_data()
237
216
  if not data:
238
217
  return {"status": "not_initialized"}
@@ -251,12 +230,11 @@ class CoverageRatchetService:
251
230
  }
252
231
 
253
232
  def _calculate_trend(self, data: dict[str, t.Any]) -> str:
254
- """Calculate coverage improvement trend."""
255
233
  history = data.get("history", [])
256
234
  if len(history) < 2:
257
235
  return "insufficient_data"
258
236
 
259
- recent_entries = history[-5:] # Last 5 entries
237
+ recent_entries = history[-5:]
260
238
  if len(recent_entries) < 2:
261
239
  return "insufficient_data"
262
240
 
@@ -270,27 +248,25 @@ class CoverageRatchetService:
270
248
  return "stable"
271
249
 
272
250
  def display_milestone_celebration(self, milestones: list[float]) -> None:
273
- """Display celebration for achieved milestones."""
274
251
  for milestone in milestones:
275
252
  if milestone == 100.0:
276
253
  self.console.print(
277
- "[gold]🎉🏆 PERFECT! 100% COVERAGE ACHIEVED! 🏆🎉[/gold]"
254
+ "[gold]🎉🏆 PERFECT ! 100 % COVERAGE ACHIEVED ! 🏆🎉[/ gold]"
278
255
  )
279
256
  elif milestone >= 90:
280
257
  self.console.print(
281
- f"[gold]🏆 Milestone achieved: {milestone:.0f}% coverage! Approaching perfection![/gold]"
258
+ f"[gold]🏆 Milestone achieved: {milestone: .0f}% coverage ! Approaching perfection ![/ gold]"
282
259
  )
283
260
  elif milestone >= 50:
284
261
  self.console.print(
285
- f"[green]🎯 Milestone achieved: {milestone:.0f}% coverage! Great progress![/green]"
262
+ f"[green]🎯 Milestone achieved: {milestone: .0f}% coverage ! Great progress ![/ green]"
286
263
  )
287
264
  else:
288
265
  self.console.print(
289
- f"[cyan]📈 Milestone achieved: {milestone:.0f}% coverage! Keep it up![/cyan]"
266
+ f"[cyan]📈 Milestone achieved: {milestone: .0f}% coverage ! Keep it up ![/ cyan]"
290
267
  )
291
268
 
292
269
  def show_progress_with_spinner(self) -> None:
293
- """Show animated progress toward 100% coverage."""
294
270
  data = self.get_ratchet_data()
295
271
  if not data:
296
272
  return
@@ -302,15 +278,14 @@ class CoverageRatchetService:
302
278
  SpinnerColumn(),
303
279
  TextColumn("[progress.description]{task.description}"),
304
280
  BarColumn(),
305
- TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
281
+ TextColumn("[progress.percentage]{task.percentage: > 3.0f}%"),
306
282
  ) as progress:
307
283
  task = progress.add_task(
308
284
  "Coverage Progress", total=target, completed=current
309
285
  )
310
- progress.update(task, description=f"Coverage: {current:.1f}%/100%")
286
+ progress.update(task, description=f"Coverage: {current: .1f}% / 100 %")
311
287
 
312
288
  def get_coverage_report(self) -> str | None:
313
- """Get coverage report from the ratchet data."""
314
289
  data = self.get_ratchet_data()
315
290
  if not data:
316
291
  return None
@@ -318,36 +293,32 @@ class CoverageRatchetService:
318
293
  current_coverage = data.get("baseline", 0.0)
319
294
  next_milestone = data.get("next_milestone")
320
295
 
321
- report = f"Coverage: {current_coverage:.2f}%"
296
+ report = f"Coverage: {current_coverage: .2f}%"
322
297
  if next_milestone:
323
298
  progress = (current_coverage / next_milestone) * 100
324
- report += f" (next milestone: {next_milestone:.0f}%, {progress:.1f}% there)"
299
+ report += (
300
+ f" (next milestone: {next_milestone: .0f}%, {progress: .1f}% there)"
301
+ )
325
302
 
326
303
  return report
327
304
 
328
305
  def check_and_update_coverage(self) -> dict[str, t.Any]:
329
- """Check coverage from current test run and update ratchet."""
330
- # Try to read coverage from standard pytest-cov output
331
306
  try:
332
- # Look for .coverage file or coverage.json
333
307
  coverage_file = self.pkg_path / "coverage.json"
334
308
  if not coverage_file.exists():
335
- # No coverage data - this is acceptable, return success
336
309
  return {
337
310
  "success": True,
338
311
  "status": "no_coverage_data",
339
- "message": "No coverage data found - tests passed without coverage",
312
+ "message": "No coverage data found-tests passed without coverage",
340
313
  "allowed": True,
341
314
  "baseline_updated": False,
342
315
  }
343
316
 
344
- # Parse coverage data (simplified for now)
345
317
  coverage_data = json.loads(coverage_file.read_text())
346
318
  current_coverage = coverage_data.get("totals", {}).get(
347
319
  "percent_covered", 0.0
348
320
  )
349
321
 
350
- # Update the ratchet
351
322
  result = self.update_coverage(current_coverage)
352
323
  result["success"] = result.get("allowed", True)
353
324
  return result
@@ -27,7 +27,7 @@ class AIAgentDebugger:
27
27
  if self.enabled:
28
28
  log_manager = get_log_manager()
29
29
  self.debug_log_path = log_manager.create_debug_log_file(
30
- f"ai-agent-{self.session_id}",
30
+ f"ai-agent -{self.session_id}",
31
31
  )
32
32
  else:
33
33
  self.debug_log_path = None
@@ -37,7 +37,6 @@ class AIAgentDebugger:
37
37
  self.workflow_phases: list[dict[str, Any]] = []
38
38
  self.error_events: list[dict[str, Any]] = []
39
39
 
40
- # Enhanced iteration tracking
41
40
  self.iteration_stats: list[dict[str, Any]] = []
42
41
  self.current_iteration = 0
43
42
  self.total_test_failures = 0
@@ -86,7 +85,7 @@ class AIAgentDebugger:
86
85
  else "Debug Log: None (disabled)"
87
86
  )
88
87
  header = Panel(
89
- f"[bold cyan]🐛 AI Agent Debug Mode Active[/bold cyan]\n"
88
+ f"[bold cyan]🐛 AI Agent Debug Mode Active[/ bold cyan]\n"
90
89
  f"Session ID: {self.session_id}\n"
91
90
  f"{debug_log_info}\n"
92
91
  f"Verbose Mode: {'✅' if self.verbose else '❌'}",
@@ -94,7 +93,7 @@ class AIAgentDebugger:
94
93
  border_style="cyan",
95
94
  )
96
95
  self.console.print(header)
97
- self.console.print() # Add blank line after debug panel for better formatting
96
+ self.console.print()
98
97
 
99
98
  @contextmanager
100
99
  def debug_operation(self, operation: str, **kwargs: Any) -> t.Iterator[str]:
@@ -109,7 +108,7 @@ class AIAgentDebugger:
109
108
  self.logger.debug(f"Starting operation: {operation}", extra=kwargs)
110
109
 
111
110
  if self.verbose:
112
- self.console.print(f"[dim]🔍 {operation} starting...[/dim]")
111
+ self.console.print(f"[dim]🔍 {operation} starting...[/ dim]")
113
112
 
114
113
  try:
115
114
  yield op_id
@@ -121,7 +120,7 @@ class AIAgentDebugger:
121
120
 
122
121
  if self.verbose:
123
122
  self.console.print(
124
- f"[dim green]✅ {operation} completed ({duration:.2f}s)[/dim green]",
123
+ f"[dim green]✅ {operation} completed ({duration: .2f}s)[/ dim green]",
125
124
  )
126
125
 
127
126
  except Exception as e:
@@ -133,7 +132,7 @@ class AIAgentDebugger:
133
132
 
134
133
  if self.verbose:
135
134
  self.console.print(
136
- f"[dim red]❌ {operation} failed ({duration:.2f}s): {e}[/dim red]",
135
+ f"[dim red]❌ {operation} failed ({duration: .2f}s): {e}[/ dim red]",
137
136
  )
138
137
  raise
139
138
 
@@ -180,12 +179,12 @@ class AIAgentDebugger:
180
179
 
181
180
  self.console.print(
182
181
  f"[{status_color}]{status_icon} MCP {operation_type}[/{status_color}]: "
183
- f"[bold]{tool_name}[/bold]"
184
- + (f" ({duration:.2f}s)" if duration else ""),
182
+ f"[bold]{tool_name}[/ bold]"
183
+ + (f" ({duration: .2f}s)" if duration else ""),
185
184
  )
186
185
 
187
186
  if error and self.verbose:
188
- self.console.print(f" [red]Error: {error}[/red]")
187
+ self.console.print(f" [red]Error: {error}[/ red]")
189
188
  self.console.print()
190
189
 
191
190
  def log_agent_activity(
@@ -226,11 +225,11 @@ class AIAgentDebugger:
226
225
  )
227
226
 
228
227
  if self.verbose:
229
- confidence_text = f" (confidence: {confidence:.2f})" if confidence else ""
228
+ confidence_text = f" (confidence: {confidence: .2f})" if confidence else ""
230
229
  issue_text = f" [issue: {issue_id}]" if issue_id else ""
231
230
 
232
231
  self.console.print(
233
- f"[blue]🤖 {agent_name}[/blue]: {activity}{confidence_text}{issue_text}",
232
+ f"[blue]🤖 {agent_name}[/ blue]: {activity}{confidence_text}{issue_text}",
234
233
  )
235
234
 
236
235
  def log_workflow_phase(
@@ -270,7 +269,7 @@ class AIAgentDebugger:
270
269
  }
271
270
 
272
271
  color = status_colors.get(status, "white")
273
- duration_text = f" ({duration:.2f}s)" if duration else ""
272
+ duration_text = f" ({duration: .2f}s)" if duration else ""
274
273
 
275
274
  self.console.print(
276
275
  f"[{color}]📋 Workflow {status}: {phase}{duration_text}[/{color}]",
@@ -305,17 +304,15 @@ class AIAgentDebugger:
305
304
  )
306
305
 
307
306
  if self.verbose:
308
- self.console.print(f"[red]💥 {error_type}: {message}[/red]")
307
+ self.console.print(f"[red]💥 {error_type}: {message}[/ red]")
309
308
 
310
309
  def print_debug_summary(self) -> None:
311
310
  if not self.enabled:
312
311
  return
313
312
 
314
- # Determine border style based on workflow success
315
313
  border_style = "green" if self.workflow_success else "red"
316
314
  title_style = "green" if self.workflow_success else "red"
317
315
 
318
- # Main debug summary table
319
316
  table = Table(
320
317
  title=f"[{title_style}]AI Agent Debug Summary[/{title_style}]",
321
318
  border_style=border_style,
@@ -348,7 +345,6 @@ class AIAgentDebugger:
348
345
  f"Types: {len({err['error_type'] for err in self.error_events})}",
349
346
  )
350
347
 
351
- # Add iteration statistics row
352
348
  table.add_row(
353
349
  "Iterations Completed",
354
350
  str(self.current_iteration),
@@ -357,7 +353,6 @@ class AIAgentDebugger:
357
353
 
358
354
  self.console.print(table)
359
355
 
360
- # Print iteration breakdown if we have iterations
361
356
  if self.iteration_stats:
362
357
  self._print_iteration_breakdown(border_style)
363
358
 
@@ -367,22 +362,20 @@ class AIAgentDebugger:
367
362
  if self.verbose and self.mcp_operations:
368
363
  self._print_mcp_operation_breakdown(border_style)
369
364
 
370
- # Print total statistics
371
365
  self._print_total_statistics(border_style)
372
366
 
373
367
  self.console.print(
374
- f"\n[dim]📝 Full debug log available at: {self.debug_log_path}[/dim]"
368
+ f"\n[dim]📝 Full debug log available at: {self.debug_log_path}[/ dim]"
375
369
  if self.debug_log_path
376
370
  else "",
377
371
  )
378
372
 
379
373
  def _print_iteration_breakdown(self, border_style: str = "red") -> None:
380
- """Print detailed breakdown of each iteration."""
381
374
  if not self.iteration_stats:
382
375
  return
383
376
 
384
377
  table = Table(
385
- title="[cyan]Iteration Breakdown[/cyan]",
378
+ title="[cyan]Iteration Breakdown[/ cyan]",
386
379
  border_style=border_style,
387
380
  )
388
381
  table.add_column("Iteration", style="yellow")
@@ -399,7 +392,9 @@ class AIAgentDebugger:
399
392
  str(iteration["test_fixes"]),
400
393
  str(iteration["hook_failures"]),
401
394
  str(iteration["hook_fixes"]),
402
- f"{iteration['duration']:.1f}s" if iteration.get("duration") else "N/A",
395
+ f"{iteration['duration']: .1f}s"
396
+ if iteration.get("duration")
397
+ else "N / A",
403
398
  )
404
399
 
405
400
  self.console.print(table)
@@ -426,7 +421,7 @@ class AIAgentDebugger:
426
421
  )
427
422
 
428
423
  table = Table(
429
- title="[cyan]Agent Activity Breakdown[/cyan]",
424
+ title="[cyan]Agent Activity Breakdown[/ cyan]",
430
425
  border_style=border_style,
431
426
  )
432
427
  table.add_column("Agent", style="blue")
@@ -435,16 +430,15 @@ class AIAgentDebugger:
435
430
 
436
431
  for agent, stats in sorted(agent_stats.items()):
437
432
  confidence_text = (
438
- f"{stats['avg_confidence']:.2f}"
433
+ f"{stats['avg_confidence']: .2f}"
439
434
  if stats["avg_confidence"] > 0
440
- else "N/A"
435
+ else "N / A"
441
436
  )
442
437
  table.add_row(agent, str(stats["activities"]), confidence_text)
443
438
 
444
439
  self.console.print(table)
445
440
 
446
441
  def _print_total_statistics(self, border_style: str = "red") -> None:
447
- """Print total cumulative statistics across all iterations."""
448
442
  success_icon = "✅" if self.workflow_success else "❌"
449
443
  status_text = "SUCCESS" if self.workflow_success else "IN PROGRESS"
450
444
  status_style = "green" if self.workflow_success else "red"
@@ -477,7 +471,7 @@ class AIAgentDebugger:
477
471
 
478
472
  table.add_row(
479
473
  "Overall Fix Rate",
480
- f"{fix_rate:.1f}%",
474
+ f"{fix_rate: .1f}%",
481
475
  f"{total_fixes}/{total_issues} issues resolved",
482
476
  )
483
477
 
@@ -497,7 +491,7 @@ class AIAgentDebugger:
497
491
  tool_stats[tool]["total_duration"] += op["duration"]
498
492
 
499
493
  table = Table(
500
- title="[cyan]MCP Tool Usage[/cyan]",
494
+ title="[cyan]MCP Tool Usage[/ cyan]",
501
495
  border_style=border_style,
502
496
  )
503
497
  table.add_column("Tool", style="cyan")
@@ -513,13 +507,12 @@ class AIAgentDebugger:
513
507
  tool,
514
508
  str(stats["calls"]),
515
509
  str(stats["errors"]),
516
- f"{avg_duration:.2f}s" if avg_duration > 0 else "N/A",
510
+ f"{avg_duration: .2f}s" if avg_duration > 0 else "N / A",
517
511
  )
518
512
 
519
513
  self.console.print(table)
520
514
 
521
515
  def log_iteration_start(self, iteration_number: int) -> None:
522
- """Log the start of a new iteration."""
523
516
  if not self.enabled:
524
517
  return
525
518
 
@@ -537,15 +530,13 @@ class AIAgentDebugger:
537
530
 
538
531
  if self.verbose:
539
532
  self.console.print(
540
- f"[yellow]🔄 Starting Iteration {iteration_number}[/yellow]",
533
+ f"[yellow]🔄 Starting Iteration {iteration_number}[/ yellow]",
541
534
  )
542
535
 
543
536
  def log_iteration_end(self, iteration_number: int, success: bool) -> None:
544
- """Log the end of an iteration with statistics."""
545
537
  if not self.enabled or not self.iteration_stats:
546
538
  return
547
539
 
548
- # Find the iteration data
549
540
  iteration_data = None
550
541
  for data in self.iteration_stats:
551
542
  if data["iteration"] == iteration_number:
@@ -562,7 +553,6 @@ class AIAgentDebugger:
562
553
  )
563
554
 
564
555
  def log_test_failures(self, count: int) -> None:
565
- """Log test failure count for current iteration."""
566
556
  if not self.enabled or not self.iteration_stats:
567
557
  return
568
558
 
@@ -571,7 +561,6 @@ class AIAgentDebugger:
571
561
  self.total_test_failures += count
572
562
 
573
563
  def log_test_fixes(self, count: int) -> None:
574
- """Log test fix count for current iteration."""
575
564
  if not self.enabled or not self.iteration_stats:
576
565
  return
577
566
 
@@ -580,7 +569,6 @@ class AIAgentDebugger:
580
569
  self.total_test_fixes += count
581
570
 
582
571
  def log_hook_failures(self, count: int) -> None:
583
- """Log hook failure count for current iteration."""
584
572
  if not self.enabled or not self.iteration_stats:
585
573
  return
586
574
 
@@ -589,7 +577,6 @@ class AIAgentDebugger:
589
577
  self.total_hook_failures += count
590
578
 
591
579
  def log_hook_fixes(self, count: int) -> None:
592
- """Log hook fix count for current iteration."""
593
580
  if not self.enabled or not self.iteration_stats:
594
581
  return
595
582
 
@@ -598,7 +585,6 @@ class AIAgentDebugger:
598
585
  self.total_hook_fixes += count
599
586
 
600
587
  def set_workflow_success(self, success: bool) -> None:
601
- """Set the overall workflow success status."""
602
588
  if not self.enabled:
603
589
  return
604
590