crackerjack 0.31.10__py3-none-any.whl → 0.31.12__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 +47 -6
  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.12.dist-info}/METADATA +197 -12
  150. crackerjack-0.31.12.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.12.dist-info}/WHEEL +0 -0
  154. {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/entry_points.txt +0 -0
  155. {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,395 @@
1
+ """
2
+ Performance monitoring and metrics collection for async operations.
3
+
4
+ This module provides comprehensive monitoring of async operations,
5
+ timeout tracking, and performance analysis capabilities.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ import time
11
+ import typing as t
12
+ from collections import defaultdict, deque
13
+ from dataclasses import dataclass, field
14
+ from pathlib import Path
15
+ from threading import Lock
16
+
17
+ from rich.console import Console
18
+ from rich.table import Table
19
+
20
+ logger = logging.getLogger("crackerjack.performance_monitor")
21
+
22
+
23
+ @dataclass
24
+ class OperationMetrics:
25
+ """Metrics for a specific operation."""
26
+
27
+ operation_name: str
28
+ total_calls: int = 0
29
+ successful_calls: int = 0
30
+ failed_calls: int = 0
31
+ timeout_calls: int = 0
32
+ total_time: float = 0.0
33
+ min_time: float = float("inf")
34
+ max_time: float = 0.0
35
+ recent_times: deque[float] = field(default_factory=lambda: deque(maxlen=100))
36
+
37
+ @property
38
+ def success_rate(self) -> float:
39
+ """Calculate success rate as percentage."""
40
+ if self.total_calls == 0:
41
+ return 0.0
42
+ return (self.successful_calls / self.total_calls) * 100
43
+
44
+ @property
45
+ def average_time(self) -> float:
46
+ """Calculate average execution time."""
47
+ if self.successful_calls == 0:
48
+ return 0.0
49
+ return self.total_time / self.successful_calls
50
+
51
+ @property
52
+ def recent_average_time(self) -> float:
53
+ """Calculate recent average execution time."""
54
+ if not self.recent_times:
55
+ return 0.0
56
+ return sum(self.recent_times) / len(self.recent_times)
57
+
58
+
59
+ @dataclass
60
+ class TimeoutEvent:
61
+ """Record of a timeout event."""
62
+
63
+ operation: str
64
+ expected_timeout: float
65
+ actual_duration: float
66
+ timestamp: float
67
+ error_message: str = ""
68
+
69
+
70
+ class AsyncPerformanceMonitor:
71
+ """Monitor and track performance of async operations."""
72
+
73
+ def __init__(self, max_timeout_events: int = 1000) -> None:
74
+ self.metrics: dict[str, OperationMetrics] = {}
75
+ self.timeout_events: deque[TimeoutEvent] = deque(maxlen=max_timeout_events)
76
+ self._lock = Lock()
77
+ self.start_time = time.time()
78
+
79
+ # Circuit breaker tracking
80
+ self.circuit_breaker_events: dict[str, list[float]] = defaultdict(list)
81
+
82
+ # Performance thresholds
83
+ self.performance_thresholds: dict[str, dict[str, float]] = {
84
+ "default": {
85
+ "warning_time": 30.0,
86
+ "critical_time": 60.0,
87
+ "min_success_rate": 80.0,
88
+ },
89
+ "fast_hooks": {
90
+ "warning_time": 30.0,
91
+ "critical_time": 90.0,
92
+ "min_success_rate": 95.0,
93
+ },
94
+ "comprehensive_hooks": {
95
+ "warning_time": 120.0,
96
+ "critical_time": 300.0,
97
+ "min_success_rate": 90.0,
98
+ },
99
+ "test_execution": {
100
+ "warning_time": 300.0,
101
+ "critical_time": 600.0,
102
+ "min_success_rate": 85.0,
103
+ },
104
+ "ai_agent_processing": {
105
+ "warning_time": 60.0,
106
+ "critical_time": 180.0,
107
+ "min_success_rate": 75.0,
108
+ },
109
+ "network_operations": {
110
+ "warning_time": 5.0,
111
+ "critical_time": 15.0,
112
+ "min_success_rate": 95.0,
113
+ },
114
+ "file_operations": {
115
+ "warning_time": 3.0,
116
+ "critical_time": 10.0,
117
+ "min_success_rate": 98.0,
118
+ },
119
+ }
120
+
121
+ def record_operation_start(self, operation: str) -> float:
122
+ """Record the start of an operation."""
123
+ return time.time()
124
+
125
+ def record_operation_success(self, operation: str, start_time: float) -> None:
126
+ """Record successful completion of an operation."""
127
+ duration = time.time() - start_time
128
+
129
+ with self._lock:
130
+ if operation not in self.metrics:
131
+ self.metrics[operation] = OperationMetrics(operation)
132
+
133
+ metrics = self.metrics[operation]
134
+ metrics.total_calls += 1
135
+ metrics.successful_calls += 1
136
+ metrics.total_time += duration
137
+ metrics.min_time = min(metrics.min_time, duration)
138
+ metrics.max_time = max(metrics.max_time, duration)
139
+ metrics.recent_times.append(duration)
140
+
141
+ def record_operation_failure(self, operation: str, start_time: float) -> None:
142
+ """Record failed completion of an operation."""
143
+ duration = time.time() - start_time
144
+
145
+ with self._lock:
146
+ if operation not in self.metrics:
147
+ self.metrics[operation] = OperationMetrics(operation)
148
+
149
+ metrics = self.metrics[operation]
150
+ metrics.total_calls += 1
151
+ metrics.failed_calls += 1
152
+ metrics.total_time += duration
153
+ metrics.min_time = min(metrics.min_time, duration)
154
+ metrics.max_time = max(metrics.max_time, duration)
155
+ metrics.recent_times.append(duration)
156
+
157
+ def record_operation_timeout(
158
+ self,
159
+ operation: str,
160
+ start_time: float,
161
+ expected_timeout: float,
162
+ error_message: str = "",
163
+ ) -> None:
164
+ """Record timeout of an operation."""
165
+ duration = time.time() - start_time
166
+
167
+ with self._lock:
168
+ if operation not in self.metrics:
169
+ self.metrics[operation] = OperationMetrics(operation)
170
+
171
+ metrics = self.metrics[operation]
172
+ metrics.total_calls += 1
173
+ metrics.timeout_calls += 1
174
+
175
+ # Record timeout event
176
+ timeout_event = TimeoutEvent(
177
+ operation=operation,
178
+ expected_timeout=expected_timeout,
179
+ actual_duration=duration,
180
+ timestamp=time.time(),
181
+ error_message=error_message,
182
+ )
183
+ self.timeout_events.append(timeout_event)
184
+
185
+ def record_circuit_breaker_event(self, operation: str, opened: bool) -> None:
186
+ """Record circuit breaker state change."""
187
+ with self._lock:
188
+ if opened:
189
+ self.circuit_breaker_events[operation].append(time.time())
190
+
191
+ def get_operation_metrics(self, operation: str) -> OperationMetrics | None:
192
+ """Get metrics for a specific operation."""
193
+ with self._lock:
194
+ return self.metrics.get(operation)
195
+
196
+ def get_all_metrics(self) -> dict[str, OperationMetrics]:
197
+ """Get all operation metrics."""
198
+ with self._lock:
199
+ return self.metrics.copy()
200
+
201
+ def get_recent_timeout_events(self, limit: int = 10) -> list[TimeoutEvent]:
202
+ """Get recent timeout events."""
203
+ with self._lock:
204
+ return list(self.timeout_events)[-limit:]
205
+
206
+ def get_performance_alerts(self) -> list[dict[str, t.Any]]:
207
+ """Get current performance alerts."""
208
+ alerts = []
209
+
210
+ with self._lock:
211
+ for operation, metrics in self.metrics.items():
212
+ thresholds = self.performance_thresholds.get(
213
+ operation, self.performance_thresholds["default"]
214
+ )
215
+
216
+ # Check success rate
217
+ if metrics.success_rate < thresholds["min_success_rate"]:
218
+ alerts.append(
219
+ {
220
+ "type": "success_rate",
221
+ "operation": operation,
222
+ "current_value": metrics.success_rate,
223
+ "threshold": thresholds["min_success_rate"],
224
+ "severity": "critical"
225
+ if metrics.success_rate < 50
226
+ else "warning",
227
+ }
228
+ )
229
+
230
+ # Check average response time
231
+ avg_time = metrics.recent_average_time
232
+ if avg_time > thresholds["critical_time"]:
233
+ alerts.append(
234
+ {
235
+ "type": "response_time",
236
+ "operation": operation,
237
+ "current_value": avg_time,
238
+ "threshold": thresholds["critical_time"],
239
+ "severity": "critical",
240
+ }
241
+ )
242
+ elif avg_time > thresholds["warning_time"]:
243
+ alerts.append(
244
+ {
245
+ "type": "response_time",
246
+ "operation": operation,
247
+ "current_value": avg_time,
248
+ "threshold": thresholds["warning_time"],
249
+ "severity": "warning",
250
+ }
251
+ )
252
+
253
+ return alerts
254
+
255
+ def get_summary_stats(self) -> dict[str, t.Any]:
256
+ """Get summary statistics."""
257
+ with self._lock:
258
+ total_calls = sum(m.total_calls for m in self.metrics.values())
259
+ total_successes = sum(m.successful_calls for m in self.metrics.values())
260
+ total_timeouts = sum(m.timeout_calls for m in self.metrics.values())
261
+ total_failures = sum(m.failed_calls for m in self.metrics.values())
262
+
263
+ uptime = time.time() - self.start_time
264
+
265
+ return {
266
+ "uptime_seconds": uptime,
267
+ "total_operations": total_calls,
268
+ "total_successes": total_successes,
269
+ "total_timeouts": total_timeouts,
270
+ "total_failures": total_failures,
271
+ "overall_success_rate": (total_successes / total_calls * 100)
272
+ if total_calls > 0
273
+ else 0.0,
274
+ "timeout_rate": (total_timeouts / total_calls * 100)
275
+ if total_calls > 0
276
+ else 0.0,
277
+ "operations_per_minute": (total_calls / (uptime / 60))
278
+ if uptime > 0
279
+ else 0.0,
280
+ "unique_operations": len(self.metrics),
281
+ "circuit_breaker_trips": len(self.circuit_breaker_events),
282
+ }
283
+
284
+ def export_metrics_json(self, filepath: Path) -> None:
285
+ """Export metrics to JSON file."""
286
+ with self._lock:
287
+ data = {
288
+ "summary": self.get_summary_stats(),
289
+ "operations": {
290
+ name: {
291
+ "total_calls": m.total_calls,
292
+ "successful_calls": m.successful_calls,
293
+ "failed_calls": m.failed_calls,
294
+ "timeout_calls": m.timeout_calls,
295
+ "success_rate": m.success_rate,
296
+ "average_time": m.average_time,
297
+ "recent_average_time": m.recent_average_time,
298
+ "min_time": m.min_time if m.min_time != float("inf") else 0,
299
+ "max_time": m.max_time,
300
+ }
301
+ for name, m in self.metrics.items()
302
+ },
303
+ "recent_timeout_events": [
304
+ {
305
+ "operation": event.operation,
306
+ "expected_timeout": event.expected_timeout,
307
+ "actual_duration": event.actual_duration,
308
+ "timestamp": event.timestamp,
309
+ "error_message": event.error_message,
310
+ }
311
+ for event in list(self.timeout_events)[-50:]
312
+ ],
313
+ "performance_alerts": self.get_performance_alerts(),
314
+ }
315
+
316
+ filepath.write_text(json.dumps(data, indent=2))
317
+
318
+ def print_performance_report(self, console: Console | None = None) -> None:
319
+ """Print a formatted performance report."""
320
+ if console is None:
321
+ console = Console()
322
+
323
+ console.print("\n[bold blue]🔍 Async Performance Monitor Report[/bold blue]")
324
+ console.print("=" * 60)
325
+
326
+ # Summary stats
327
+ summary = self.get_summary_stats()
328
+ console.print(f"⏱️ Uptime: {summary['uptime_seconds']:.1f}s")
329
+ console.print(f"📊 Total Operations: {summary['total_operations']}")
330
+ console.print(f"✅ Success Rate: {summary['overall_success_rate']:.1f}%")
331
+ console.print(f"⏰ Timeout Rate: {summary['timeout_rate']:.1f}%")
332
+ console.print(f"🚀 Operations/min: {summary['operations_per_minute']:.1f}")
333
+
334
+ # Operation metrics table
335
+ if self.metrics:
336
+ console.print("\n[bold]Operation Metrics:[/bold]")
337
+ table = Table()
338
+ table.add_column("Operation")
339
+ table.add_column("Calls")
340
+ table.add_column("Success %")
341
+ table.add_column("Avg Time")
342
+ table.add_column("Recent Avg")
343
+ table.add_column("Timeouts")
344
+
345
+ with self._lock:
346
+ for name, metrics in sorted(self.metrics.items()):
347
+ table.add_row(
348
+ name,
349
+ str(metrics.total_calls),
350
+ f"{metrics.success_rate:.1f}%",
351
+ f"{metrics.average_time:.2f}s",
352
+ f"{metrics.recent_average_time:.2f}s",
353
+ str(metrics.timeout_calls),
354
+ )
355
+
356
+ console.print(table)
357
+
358
+ # Performance alerts
359
+ alerts = self.get_performance_alerts()
360
+ if alerts:
361
+ console.print("\n[bold red]⚠️ Performance Alerts:[/bold red]")
362
+ for alert in alerts:
363
+ severity_emoji = "🔴" if alert["severity"] == "critical" else "🟡"
364
+ console.print(
365
+ f"{severity_emoji} {alert['operation']}: {alert['type']} "
366
+ f"{alert['current_value']:.1f} (threshold: {alert['threshold']:.1f})"
367
+ )
368
+
369
+ # Recent timeouts
370
+ recent_timeouts = self.get_recent_timeout_events(5)
371
+ if recent_timeouts:
372
+ console.print("\n[bold yellow]⏰ Recent Timeouts:[/bold yellow]")
373
+ for timeout in recent_timeouts:
374
+ console.print(
375
+ f" • {timeout.operation}: {timeout.actual_duration:.1f}s "
376
+ f"(expected: {timeout.expected_timeout:.1f}s)"
377
+ )
378
+
379
+
380
+ # Global performance monitor instance
381
+ _global_performance_monitor: AsyncPerformanceMonitor | None = None
382
+
383
+
384
+ def get_performance_monitor() -> AsyncPerformanceMonitor:
385
+ """Get the global performance monitor instance."""
386
+ global _global_performance_monitor
387
+ if _global_performance_monitor is None:
388
+ _global_performance_monitor = AsyncPerformanceMonitor()
389
+ return _global_performance_monitor
390
+
391
+
392
+ def reset_performance_monitor() -> None:
393
+ """Reset the global performance monitor."""
394
+ global _global_performance_monitor
395
+ _global_performance_monitor = AsyncPerformanceMonitor()