crackerjack 0.33.0__py3-none-any.whl → 0.33.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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 +605 -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.1.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.1.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.1.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,643 @@
1
+ """Unified WebSocket server for real-time monitoring dashboard."""
2
+
3
+ import asyncio
4
+ import json
5
+ import logging
6
+ import typing as t
7
+ from contextlib import suppress
8
+ from datetime import datetime
9
+ from typing import Any
10
+
11
+ import uvicorn
12
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
13
+ from fastapi.responses import HTMLResponse, JSONResponse
14
+
15
+ from crackerjack.mcp.websocket.monitoring_endpoints import MonitoringWebSocketManager
16
+ from crackerjack.monitoring.ai_agent_watchdog import AIAgentWatchdog
17
+ from crackerjack.monitoring.metrics_collector import (
18
+ MetricsCollector,
19
+ UnifiedDashboardMetrics,
20
+ )
21
+ from crackerjack.services.cache import CrackerjackCache
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class CrackerjackMonitoringServer:
27
+ """
28
+ Unified monitoring server for Crackerjack with real-time WebSocket streaming.
29
+
30
+ Provides comprehensive monitoring capabilities:
31
+ - Real-time metrics via WebSocket
32
+ - Historical data storage and retrieval
33
+ - AI agent performance tracking
34
+ - System health monitoring
35
+ - Interactive dashboard interface
36
+ """
37
+
38
+ def __init__(self, port: int = 8675, host: str = "localhost"):
39
+ self.port = port
40
+ self.host = host
41
+
42
+ # Core services
43
+ self.app = FastAPI(title="Crackerjack Monitoring", version="1.0.0")
44
+ self.cache = CrackerjackCache()
45
+ self.metrics_collector = MetricsCollector(self.cache)
46
+ self.websocket_manager = MonitoringWebSocketManager()
47
+ self.ai_watchdog = AIAgentWatchdog()
48
+
49
+ # Server state
50
+ self.is_running = False
51
+ self.server_task: asyncio.Task[None] | None = None
52
+
53
+ # WebSocket connections
54
+ self.active_connections: dict[str, WebSocket] = {}
55
+ self.metrics_subscribers: set[WebSocket] = set()
56
+ self.alerts_subscribers: set[WebSocket] = set()
57
+
58
+ self._setup_routes()
59
+ self._setup_websocket_endpoints()
60
+
61
+ def _setup_routes(self) -> None:
62
+ """Setup HTTP API routes."""
63
+
64
+ @self.app.get("/")
65
+ async def dashboard() -> HTMLResponse:
66
+ """Main dashboard interface."""
67
+ return HTMLResponse(content=self._get_dashboard_html())
68
+
69
+ @self.app.get("/api/status")
70
+ async def status() -> JSONResponse:
71
+ """Get server status."""
72
+ return JSONResponse(
73
+ {
74
+ "status": "running" if self.is_running else "stopped",
75
+ "timestamp": datetime.now().isoformat(),
76
+ "host": self.host,
77
+ "port": self.port,
78
+ "metrics_collecting": self.metrics_collector.is_collecting,
79
+ "active_connections": len(self.active_connections),
80
+ }
81
+ )
82
+
83
+ @self.app.get("/api/metrics")
84
+ async def current_metrics() -> JSONResponse:
85
+ """Get current metrics snapshot."""
86
+ return JSONResponse(self.metrics_collector.get_current_metrics().to_dict())
87
+
88
+ @self.app.get("/api/metrics/summary")
89
+ async def metrics_summary() -> JSONResponse:
90
+ """Get metrics summary for quick display."""
91
+ return JSONResponse(self.metrics_collector.get_metrics_summary())
92
+
93
+ @self.app.get("/api/metrics/history")
94
+ async def metrics_history(hours: int = 1) -> JSONResponse:
95
+ """Get metrics history."""
96
+ history = self.metrics_collector.get_metrics_history(hours)
97
+ return JSONResponse([m.to_dict() for m in history])
98
+
99
+ @self.app.get("/api/agents/status")
100
+ async def agents_status() -> JSONResponse:
101
+ """Get AI agent status and performance."""
102
+ return JSONResponse(
103
+ {
104
+ "agents": {
105
+ name: {
106
+ "total_handled": metrics.total_issues_handled,
107
+ "success_rate": metrics.successful_fixes
108
+ / max(1, metrics.total_issues_handled),
109
+ "avg_confidence": metrics.average_confidence,
110
+ "avg_execution_time": metrics.average_execution_time,
111
+ "recent_failures": len(metrics.recent_failures),
112
+ }
113
+ for name, metrics in self.ai_watchdog.performance_metrics.items()
114
+ },
115
+ "alerts": [
116
+ {
117
+ "level": alert.level,
118
+ "message": alert.message,
119
+ "agent": alert.agent_name,
120
+ "timestamp": alert.timestamp.isoformat(),
121
+ }
122
+ for alert in self.ai_watchdog.get_recent_alerts(hours=1)
123
+ ],
124
+ }
125
+ )
126
+
127
+ @self.app.post("/api/metrics/record")
128
+ async def record_metrics(data: dict[str, Any]) -> JSONResponse:
129
+ """Record metrics from external sources."""
130
+ try:
131
+ if "job_start" in data:
132
+ self.metrics_collector.record_job_start(data["job_start"]["job_id"])
133
+
134
+ if "job_completion" in data:
135
+ completion = data["job_completion"]
136
+ self.metrics_collector.record_job_completion(
137
+ completion["job_id"], completion.get("success", True)
138
+ )
139
+
140
+ if "quality_data" in data:
141
+ quality = data["quality_data"]
142
+ self.metrics_collector.record_quality_data(
143
+ quality.get("issues_found", 0),
144
+ quality.get("issues_fixed", 0),
145
+ quality.get("coverage", 0.0),
146
+ quality.get("success_rate", 0.0),
147
+ )
148
+
149
+ return JSONResponse({"status": "recorded"})
150
+ except Exception as e:
151
+ logger.error(f"Error recording metrics: {e}")
152
+ return JSONResponse({"error": str(e)}, status_code=400)
153
+
154
+ def _setup_websocket_endpoints(self) -> None:
155
+ """Setup WebSocket endpoints for real-time communication."""
156
+ self.app.websocket("/ws/metrics")(self._handle_metrics_websocket)
157
+ self.app.websocket("/ws/alerts")(self._handle_alerts_websocket)
158
+
159
+ async def _handle_metrics_websocket(self, websocket: WebSocket) -> None:
160
+ """Handle metrics WebSocket connection."""
161
+ client_id = f"metrics_{datetime.now().timestamp()}"
162
+ await websocket.accept()
163
+
164
+ try:
165
+ await self._setup_metrics_connection(websocket, client_id)
166
+ await self._send_initial_metrics(websocket)
167
+ await self._handle_metrics_messages(websocket)
168
+ finally:
169
+ self._cleanup_connection(websocket, client_id)
170
+
171
+ async def _handle_alerts_websocket(self, websocket: WebSocket) -> None:
172
+ """Handle alerts WebSocket connection."""
173
+ client_id = f"alerts_{datetime.now().timestamp()}"
174
+ await websocket.accept()
175
+
176
+ try:
177
+ await self._setup_alerts_connection(websocket, client_id)
178
+ await self._send_initial_alerts(websocket)
179
+ await self._handle_alerts_messages(websocket)
180
+ finally:
181
+ self._cleanup_connection(websocket, client_id)
182
+
183
+ async def _setup_metrics_connection(
184
+ self, websocket: WebSocket, client_id: str
185
+ ) -> None:
186
+ """Setup metrics websocket connection."""
187
+ self.active_connections[client_id] = websocket
188
+ self.metrics_subscribers.add(websocket)
189
+
190
+ async def _setup_alerts_connection(
191
+ self, websocket: WebSocket, client_id: str
192
+ ) -> None:
193
+ """Setup alerts websocket connection."""
194
+ self.active_connections[client_id] = websocket
195
+ self.alerts_subscribers.add(websocket)
196
+
197
+ async def _send_initial_metrics(self, websocket: WebSocket) -> None:
198
+ """Send initial metrics to websocket client."""
199
+ current_metrics = self.metrics_collector.get_current_metrics()
200
+ await websocket.send_text(
201
+ json.dumps(
202
+ {
203
+ "type": "initial_metrics",
204
+ "data": current_metrics.to_dict(),
205
+ }
206
+ )
207
+ )
208
+
209
+ async def _send_initial_alerts(self, websocket: WebSocket) -> None:
210
+ """Send initial alerts to websocket client."""
211
+ recent_alerts = self.ai_watchdog.get_recent_alerts(hours=24)
212
+ await websocket.send_text(
213
+ json.dumps(
214
+ {
215
+ "type": "initial_alerts",
216
+ "data": [
217
+ {
218
+ "level": alert.level,
219
+ "message": alert.message,
220
+ "agent": alert.agent_name,
221
+ "timestamp": alert.timestamp.isoformat(),
222
+ }
223
+ for alert in recent_alerts
224
+ ],
225
+ }
226
+ )
227
+ )
228
+
229
+ async def _handle_metrics_messages(self, websocket: WebSocket) -> None:
230
+ """Handle incoming metrics websocket messages."""
231
+ while True:
232
+ try:
233
+ message = await websocket.receive_text()
234
+ data = json.loads(message)
235
+ await self._process_websocket_message(websocket, data)
236
+ except WebSocketDisconnect:
237
+ break
238
+ except Exception as e:
239
+ logger.error(f"WebSocket error: {e}")
240
+ break
241
+
242
+ async def _handle_alerts_messages(self, websocket: WebSocket) -> None:
243
+ """Handle incoming alerts websocket messages."""
244
+ while True:
245
+ try:
246
+ message = await websocket.receive_text()
247
+ data = json.loads(message)
248
+ await self._process_websocket_message(websocket, data)
249
+ except WebSocketDisconnect:
250
+ break
251
+ except Exception as e:
252
+ logger.error(f"WebSocket error: {e}")
253
+ break
254
+
255
+ async def _process_websocket_message(
256
+ self, websocket: WebSocket, data: dict[str, Any]
257
+ ) -> None:
258
+ """Process incoming websocket message."""
259
+ if data.get("type") == "ping":
260
+ await websocket.send_text(
261
+ json.dumps(
262
+ {
263
+ "type": "pong",
264
+ "timestamp": datetime.now().isoformat(),
265
+ }
266
+ )
267
+ )
268
+
269
+ def _cleanup_connection(self, websocket: WebSocket, client_id: str) -> None:
270
+ """Clean up a WebSocket connection."""
271
+ if client_id in self.active_connections:
272
+ del self.active_connections[client_id]
273
+ self.metrics_subscribers.discard(websocket)
274
+ self.alerts_subscribers.discard(websocket)
275
+
276
+ async def start_monitoring(self, port: int | None = None) -> None:
277
+ """Start the monitoring server with real-time updates."""
278
+ if self.is_running:
279
+ logger.warning("Monitoring server already running")
280
+ return
281
+
282
+ server_port = port or self.port
283
+
284
+ try:
285
+ # Start metrics collection
286
+ await self.metrics_collector.start_collection()
287
+
288
+ # Setup metrics listener for WebSocket broadcasting
289
+ self.metrics_collector.add_metrics_listener(self._broadcast_metrics)
290
+
291
+ # Start the web server
292
+ config = uvicorn.Config(
293
+ self.app,
294
+ host=self.host,
295
+ port=server_port,
296
+ log_level="info",
297
+ )
298
+ server = uvicorn.Server(config)
299
+
300
+ self.server_task = asyncio.create_task(server.serve())
301
+ self.is_running = True
302
+
303
+ logger.info(
304
+ f"🚀 Crackerjack Monitoring Server started on http://{self.host}:{server_port}"
305
+ )
306
+ logger.info(f"📊 Dashboard available at http://{self.host}:{server_port}/")
307
+ logger.info("🔗 WebSocket endpoints:")
308
+ logger.info(f" - Metrics: ws://{self.host}:{server_port}/ws/metrics")
309
+ logger.info(f" - Alerts: ws://{self.host}:{server_port}/ws/alerts")
310
+
311
+ # Wait for server to complete
312
+ await self.server_task
313
+
314
+ except Exception as e:
315
+ logger.error(f"Error starting monitoring server: {e}")
316
+ await self.stop_monitoring()
317
+ raise
318
+
319
+ async def stop_monitoring(self) -> None:
320
+ """Stop the monitoring server."""
321
+ if not self.is_running:
322
+ return
323
+
324
+ self.is_running = False
325
+
326
+ try:
327
+ # Stop metrics collection
328
+ await self.metrics_collector.stop_collection()
329
+
330
+ # Close all WebSocket connections
331
+ for websocket in list[t.Any](self.active_connections.values()):
332
+ with suppress(Exception):
333
+ await websocket.close()
334
+ self.active_connections.clear()
335
+ self.metrics_subscribers.clear()
336
+ self.alerts_subscribers.clear()
337
+
338
+ # Stop server task
339
+ if self.server_task and not self.server_task.done():
340
+ self.server_task.cancel()
341
+ try:
342
+ await self.server_task
343
+ except asyncio.CancelledError:
344
+ pass
345
+
346
+ logger.info("🔻 Crackerjack Monitoring Server stopped")
347
+
348
+ except Exception as e:
349
+ logger.error(f"Error stopping monitoring server: {e}")
350
+
351
+ def _broadcast_metrics(self, metrics: UnifiedDashboardMetrics) -> None:
352
+ """Broadcast metrics to all connected WebSocket clients."""
353
+ if not self.metrics_subscribers:
354
+ return
355
+
356
+ message = {
357
+ "type": "metrics_update",
358
+ "data": metrics.to_dict(),
359
+ "timestamp": datetime.now().isoformat(),
360
+ }
361
+
362
+ # Use asyncio to broadcast to all subscribers
363
+ asyncio.create_task(self._async_broadcast_metrics(message))
364
+
365
+ async def _async_broadcast_metrics(self, message: dict[str, Any]) -> None:
366
+ """Async broadcast helper."""
367
+ disconnected = []
368
+ for websocket in self.metrics_subscribers:
369
+ try:
370
+ await websocket.send_text(json.dumps(message))
371
+ except Exception:
372
+ disconnected.append(websocket)
373
+
374
+ # Clean up disconnected clients
375
+ self.metrics_subscribers.difference_update(disconnected)
376
+
377
+ def _get_dashboard_html(self) -> str:
378
+ """Generate the dashboard HTML interface."""
379
+ html_template = self._get_html_template()
380
+ css_styles = self._get_dashboard_css()
381
+ javascript_code = self._get_dashboard_javascript()
382
+
383
+ return html_template.format(
384
+ css_styles=css_styles, javascript_code=javascript_code
385
+ )
386
+
387
+ def _get_html_template(self) -> str:
388
+ """Get the HTML template structure."""
389
+ return """<!DOCTYPE html>
390
+ <html lang="en">
391
+ <head>
392
+ <meta charset="UTF-8">
393
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
394
+ <title>Crackerjack Monitoring Dashboard</title>
395
+ <style>{css_styles}</style>
396
+ </head>
397
+ <body>
398
+ <div class="connection-status disconnected" id="connection-status">Connecting...</div>
399
+
400
+ <div class="header">
401
+ <h1>🔧 Crackerjack Monitoring Dashboard</h1>
402
+ <p>Real-time project monitoring and analytics</p>
403
+ </div>
404
+
405
+ <div class="dashboard">
406
+ <div class="card">
407
+ <h3>System Health</h3>
408
+ <div class="metric">
409
+ <span class="metric-label">CPU Usage</span>
410
+ <span class="metric-value" id="cpu">--</span>
411
+ </div>
412
+ <div class="metric">
413
+ <span class="metric-label">Memory</span>
414
+ <span class="metric-value" id="memory">--</span>
415
+ </div>
416
+ <div class="metric">
417
+ <span class="metric-label">Uptime</span>
418
+ <span class="metric-value" id="uptime">--</span>
419
+ </div>
420
+ </div>
421
+
422
+ <div class="card">
423
+ <h3>Quality Metrics</h3>
424
+ <div class="metric">
425
+ <span class="metric-label">Success Rate</span>
426
+ <span class="metric-value" id="success-rate">--</span>
427
+ </div>
428
+ <div class="metric">
429
+ <span class="metric-label">Issues Fixed</span>
430
+ <span class="metric-value" id="issues-fixed">--</span>
431
+ </div>
432
+ <div class="metric">
433
+ <span class="metric-label">Test Coverage</span>
434
+ <span class="metric-value" id="coverage">--</span>
435
+ </div>
436
+ </div>
437
+
438
+ <div class="card">
439
+ <h3>Workflow Status</h3>
440
+ <div class="metric">
441
+ <span class="metric-label">Jobs Completed</span>
442
+ <span class="metric-value" id="jobs-completed">--</span>
443
+ </div>
444
+ <div class="metric">
445
+ <span class="metric-label">Avg Duration</span>
446
+ <span class="metric-value" id="avg-duration">--</span>
447
+ </div>
448
+ <div class="metric">
449
+ <span class="metric-label">Throughput</span>
450
+ <span class="metric-value" id="throughput">--</span>
451
+ </div>
452
+ </div>
453
+
454
+ <div class="card">
455
+ <h3>AI Agents</h3>
456
+ <div class="metric">
457
+ <span class="metric-label">Active Agents</span>
458
+ <span class="metric-value" id="active-agents">--</span>
459
+ </div>
460
+ <div class="metric">
461
+ <span class="metric-label">Total Fixes</span>
462
+ <span class="metric-value" id="total-fixes">--</span>
463
+ </div>
464
+ <div class="metric">
465
+ <span class="metric-label">Cache Hit Rate</span>
466
+ <span class="metric-value" id="cache-hit-rate">--</span>
467
+ </div>
468
+ </div>
469
+
470
+ <div class="card" style="grid-column: 1/-1;">
471
+ <h3>Activity Log</h3>
472
+ <div id="logs"></div>
473
+ </div>
474
+ </div>
475
+
476
+ <script>{javascript_code}</script>
477
+ </body>
478
+ </html>"""
479
+
480
+ def _get_dashboard_css(self) -> str:
481
+ """Get the CSS styles for the dashboard."""
482
+ return """
483
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #1a1a1a; color: #fff; }
484
+ .dashboard { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
485
+ .card { background: #2d2d2d; border-radius: 8px; padding: 20px; border: 1px solid #404040; }
486
+ .metric { display: flex; justify-content: space-between; margin: 10px 0; }
487
+ .metric-label { color: #aaa; }
488
+ .metric-value { color: #fff; font-weight: bold; }
489
+ .status-good { color: #4ade80; }
490
+ .status-warning { color: #fbbf24; }
491
+ .status-error { color: #ef4444; }
492
+ .header { text-align: center; margin-bottom: 30px; }
493
+ .connection-status { position: fixed; top: 20px; right: 20px; padding: 10px; border-radius: 4px; }
494
+ .connected { background: #16a34a; }
495
+ .disconnected { background: #dc2626; }
496
+ #logs { height: 200px; overflow-y: auto; background: #000; padding: 10px; border-radius: 4px; font-family: monospace; font-size: 12px; }
497
+ """
498
+
499
+ def _get_dashboard_javascript(self) -> str:
500
+ """Get the JavaScript code for the dashboard."""
501
+ js_variables = self._get_js_variables()
502
+ js_websocket_handlers = self._get_js_websocket_handlers()
503
+ js_dashboard_functions = self._get_js_dashboard_functions()
504
+ js_initialization = self._get_js_initialization()
505
+
506
+ return f"""
507
+ {js_variables}
508
+ {js_websocket_handlers}
509
+ {js_dashboard_functions}
510
+ {js_initialization}
511
+ """
512
+
513
+ def _get_js_variables(self) -> str:
514
+ """Get JavaScript variable declarations."""
515
+ return """
516
+ const wsUrl = `ws://${window.location.host}/ws/metrics`;
517
+ let ws = null;
518
+ let reconnectInterval = 5000;
519
+ """
520
+
521
+ def _get_js_websocket_handlers(self) -> str:
522
+ """Get JavaScript WebSocket connection handlers."""
523
+ return """
524
+ function connect() {
525
+ ws = new WebSocket(wsUrl);
526
+ ws.onopen = handleWebSocketOpen;
527
+ ws.onmessage = handleWebSocketMessage;
528
+ ws.onclose = handleWebSocketClose;
529
+ ws.onerror = handleWebSocketError;
530
+ }
531
+
532
+ function handleWebSocketOpen() {
533
+ document.getElementById('connection-status').textContent = 'Connected';
534
+ document.getElementById('connection-status').className = 'connection-status connected';
535
+ log('Connected to monitoring server');
536
+ }
537
+
538
+ function handleWebSocketMessage(event) {
539
+ const message = JSON.parse(event.data);
540
+ if (message.type === 'metrics_update' || message.type === 'initial_metrics') {
541
+ updateDashboard(message.data);
542
+ }
543
+ }
544
+
545
+ function handleWebSocketClose() {
546
+ document.getElementById('connection-status').textContent = 'Disconnected';
547
+ document.getElementById('connection-status').className = 'connection-status disconnected';
548
+ log('Disconnected from monitoring server');
549
+ setTimeout(connect, reconnectInterval);
550
+ }
551
+
552
+ function handleWebSocketError(error) {
553
+ log(`WebSocket error: ${error}`);
554
+ }
555
+ """
556
+
557
+ def _get_js_dashboard_functions(self) -> str:
558
+ """Get JavaScript dashboard utility functions."""
559
+ return """
560
+ function updateDashboard(data) {
561
+ updateSystemMetrics(data.system);
562
+ updateQualityMetrics(data.quality);
563
+ updateWorkflowMetrics(data.workflow);
564
+ updateAgentMetrics(data.agents);
565
+ }
566
+
567
+ function updateSystemMetrics(system) {
568
+ document.getElementById('cpu').textContent = system.cpu_usage.toFixed(1) + '%';
569
+ document.getElementById('memory').textContent = (system.memory_usage_mb / 1024).toFixed(1) + 'GB';
570
+ document.getElementById('uptime').textContent = formatUptime(system.uptime_seconds);
571
+ }
572
+
573
+ function updateQualityMetrics(quality) {
574
+ document.getElementById('success-rate').textContent = (quality.success_rate * 100).toFixed(1) + '%';
575
+ document.getElementById('issues-fixed').textContent = quality.issues_fixed;
576
+ document.getElementById('coverage').textContent = (quality.test_coverage * 100).toFixed(1) + '%';
577
+ }
578
+
579
+ function updateWorkflowMetrics(workflow) {
580
+ document.getElementById('jobs-completed').textContent = workflow.jobs_completed;
581
+ document.getElementById('avg-duration').textContent = workflow.average_job_duration.toFixed(1) + 's';
582
+ document.getElementById('throughput').textContent = workflow.throughput_per_hour.toFixed(1) + '/h';
583
+ }
584
+
585
+ function updateAgentMetrics(agents) {
586
+ document.getElementById('active-agents').textContent = agents.active_agents;
587
+ document.getElementById('total-fixes').textContent = agents.total_fixes_applied;
588
+ document.getElementById('cache-hit-rate').textContent = (agents.cache_hit_rate * 100).toFixed(1) + '%';
589
+ }
590
+
591
+ function formatUptime(seconds) {
592
+ if (seconds < 3600) return Math.floor(seconds/60) + 'm';
593
+ if (seconds < 86400) return Math.floor(seconds/3600) + 'h';
594
+ return Math.floor(seconds/86400) + 'd';
595
+ }
596
+
597
+ function log(message) {
598
+ const logs = document.getElementById('logs');
599
+ const timestamp = new Date().toLocaleTimeString();
600
+ logs.innerHTML += `<div>[${timestamp}] ${message}</div>`;
601
+ logs.scrollTop = logs.scrollHeight;
602
+ }
603
+ """
604
+
605
+ def _get_js_initialization(self) -> str:
606
+ """Get JavaScript initialization code."""
607
+ return """
608
+ // Start connection
609
+ connect();
610
+
611
+ // Periodic ping to keep connection alive
612
+ setInterval(() => {
613
+ if (ws && ws.readyState === WebSocket.OPEN) {
614
+ ws.send(JSON.stringify({type: 'ping'}));
615
+ }
616
+ }, 30000);
617
+ """
618
+
619
+
620
+ # Convenience function for quick server startup
621
+ async def start_crackerjack_monitoring_server(
622
+ port: int = 8675, host: str = "localhost"
623
+ ) -> CrackerjackMonitoringServer:
624
+ """Start a Crackerjack monitoring server."""
625
+ server = CrackerjackMonitoringServer(port=port, host=host)
626
+ await server.start_monitoring()
627
+ return server
628
+
629
+
630
+ if __name__ == "__main__":
631
+ import sys
632
+
633
+ port = int(sys.argv[1]) if len(sys.argv) > 1 else 8675
634
+
635
+ async def main() -> None:
636
+ server = CrackerjackMonitoringServer(port=port)
637
+ try:
638
+ await server.start_monitoring()
639
+ except KeyboardInterrupt:
640
+ logger.info("Shutting down monitoring server...")
641
+ await server.stop_monitoring()
642
+
643
+ asyncio.run(main())
@@ -74,7 +74,7 @@ class CorrelationTracker:
74
74
  current = self.iteration_data[i]
75
75
  previous = self.iteration_data[i - 1]
76
76
 
77
- recurring_failures = set(current["failed_hooks"]) & set(
77
+ recurring_failures = set[t.Any](current["failed_hooks"]) & set[t.Any](
78
78
  previous["failed_hooks"],
79
79
  )
80
80
 
@@ -101,8 +101,13 @@ class CorrelationTracker:
101
101
  }
102
102
 
103
103
 
104
- class MinimalProgressStreamer:
105
- def __init__(self) -> None:
104
+ class MinimalProgressStreamer(ProgressStreamer):
105
+ def __init__(
106
+ self,
107
+ config: OrchestrationConfig | None = None,
108
+ session: SessionCoordinator | None = None,
109
+ ) -> None:
110
+ # Minimal implementation doesn't use config or session
106
111
  pass
107
112
 
108
113
  def update_stage(self, stage: str, substage: str = "") -> None:
@@ -387,9 +392,9 @@ class AdvancedWorkflowOrchestrator:
387
392
 
388
393
  success = False
389
394
  strategy_switches = 0
390
- hooks_time = 0
391
- tests_time = 0
392
- ai_time = 0
395
+ hooks_time: float = 0.0
396
+ tests_time: float = 0.0
397
+ ai_time: float = 0.0
393
398
 
394
399
  for iteration in range(1, max_iterations + 1):
395
400
  self.console.print(