crackerjack 0.30.3__py3-none-any.whl → 0.31.7__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 (156) hide show
  1. crackerjack/CLAUDE.md +1005 -0
  2. crackerjack/RULES.md +380 -0
  3. crackerjack/__init__.py +42 -13
  4. crackerjack/__main__.py +227 -299
  5. crackerjack/agents/__init__.py +41 -0
  6. crackerjack/agents/architect_agent.py +281 -0
  7. crackerjack/agents/base.py +170 -0
  8. crackerjack/agents/coordinator.py +512 -0
  9. crackerjack/agents/documentation_agent.py +498 -0
  10. crackerjack/agents/dry_agent.py +388 -0
  11. crackerjack/agents/formatting_agent.py +245 -0
  12. crackerjack/agents/import_optimization_agent.py +281 -0
  13. crackerjack/agents/performance_agent.py +669 -0
  14. crackerjack/agents/proactive_agent.py +104 -0
  15. crackerjack/agents/refactoring_agent.py +788 -0
  16. crackerjack/agents/security_agent.py +529 -0
  17. crackerjack/agents/test_creation_agent.py +657 -0
  18. crackerjack/agents/test_specialist_agent.py +486 -0
  19. crackerjack/agents/tracker.py +212 -0
  20. crackerjack/api.py +560 -0
  21. crackerjack/cli/__init__.py +24 -0
  22. crackerjack/cli/facade.py +104 -0
  23. crackerjack/cli/handlers.py +267 -0
  24. crackerjack/cli/interactive.py +471 -0
  25. crackerjack/cli/options.py +409 -0
  26. crackerjack/cli/utils.py +18 -0
  27. crackerjack/code_cleaner.py +618 -928
  28. crackerjack/config/__init__.py +19 -0
  29. crackerjack/config/hooks.py +218 -0
  30. crackerjack/core/__init__.py +0 -0
  31. crackerjack/core/async_workflow_orchestrator.py +406 -0
  32. crackerjack/core/autofix_coordinator.py +200 -0
  33. crackerjack/core/container.py +104 -0
  34. crackerjack/core/enhanced_container.py +542 -0
  35. crackerjack/core/performance.py +243 -0
  36. crackerjack/core/phase_coordinator.py +585 -0
  37. crackerjack/core/proactive_workflow.py +316 -0
  38. crackerjack/core/session_coordinator.py +289 -0
  39. crackerjack/core/workflow_orchestrator.py +826 -0
  40. crackerjack/dynamic_config.py +94 -103
  41. crackerjack/errors.py +263 -41
  42. crackerjack/executors/__init__.py +11 -0
  43. crackerjack/executors/async_hook_executor.py +431 -0
  44. crackerjack/executors/cached_hook_executor.py +242 -0
  45. crackerjack/executors/hook_executor.py +345 -0
  46. crackerjack/executors/individual_hook_executor.py +669 -0
  47. crackerjack/intelligence/__init__.py +44 -0
  48. crackerjack/intelligence/adaptive_learning.py +751 -0
  49. crackerjack/intelligence/agent_orchestrator.py +551 -0
  50. crackerjack/intelligence/agent_registry.py +414 -0
  51. crackerjack/intelligence/agent_selector.py +502 -0
  52. crackerjack/intelligence/integration.py +290 -0
  53. crackerjack/interactive.py +576 -315
  54. crackerjack/managers/__init__.py +11 -0
  55. crackerjack/managers/async_hook_manager.py +135 -0
  56. crackerjack/managers/hook_manager.py +137 -0
  57. crackerjack/managers/publish_manager.py +433 -0
  58. crackerjack/managers/test_command_builder.py +151 -0
  59. crackerjack/managers/test_executor.py +443 -0
  60. crackerjack/managers/test_manager.py +258 -0
  61. crackerjack/managers/test_manager_backup.py +1124 -0
  62. crackerjack/managers/test_progress.py +114 -0
  63. crackerjack/mcp/__init__.py +0 -0
  64. crackerjack/mcp/cache.py +336 -0
  65. crackerjack/mcp/client_runner.py +104 -0
  66. crackerjack/mcp/context.py +621 -0
  67. crackerjack/mcp/dashboard.py +636 -0
  68. crackerjack/mcp/enhanced_progress_monitor.py +479 -0
  69. crackerjack/mcp/file_monitor.py +336 -0
  70. crackerjack/mcp/progress_components.py +569 -0
  71. crackerjack/mcp/progress_monitor.py +949 -0
  72. crackerjack/mcp/rate_limiter.py +332 -0
  73. crackerjack/mcp/server.py +22 -0
  74. crackerjack/mcp/server_core.py +244 -0
  75. crackerjack/mcp/service_watchdog.py +501 -0
  76. crackerjack/mcp/state.py +395 -0
  77. crackerjack/mcp/task_manager.py +257 -0
  78. crackerjack/mcp/tools/__init__.py +17 -0
  79. crackerjack/mcp/tools/core_tools.py +249 -0
  80. crackerjack/mcp/tools/error_analyzer.py +308 -0
  81. crackerjack/mcp/tools/execution_tools.py +372 -0
  82. crackerjack/mcp/tools/execution_tools_backup.py +1097 -0
  83. crackerjack/mcp/tools/intelligence_tool_registry.py +80 -0
  84. crackerjack/mcp/tools/intelligence_tools.py +314 -0
  85. crackerjack/mcp/tools/monitoring_tools.py +502 -0
  86. crackerjack/mcp/tools/proactive_tools.py +384 -0
  87. crackerjack/mcp/tools/progress_tools.py +217 -0
  88. crackerjack/mcp/tools/utility_tools.py +341 -0
  89. crackerjack/mcp/tools/workflow_executor.py +565 -0
  90. crackerjack/mcp/websocket/__init__.py +14 -0
  91. crackerjack/mcp/websocket/app.py +39 -0
  92. crackerjack/mcp/websocket/endpoints.py +559 -0
  93. crackerjack/mcp/websocket/jobs.py +253 -0
  94. crackerjack/mcp/websocket/server.py +116 -0
  95. crackerjack/mcp/websocket/websocket_handler.py +78 -0
  96. crackerjack/mcp/websocket_server.py +10 -0
  97. crackerjack/models/__init__.py +31 -0
  98. crackerjack/models/config.py +93 -0
  99. crackerjack/models/config_adapter.py +230 -0
  100. crackerjack/models/protocols.py +118 -0
  101. crackerjack/models/task.py +154 -0
  102. crackerjack/monitoring/ai_agent_watchdog.py +450 -0
  103. crackerjack/monitoring/regression_prevention.py +638 -0
  104. crackerjack/orchestration/__init__.py +0 -0
  105. crackerjack/orchestration/advanced_orchestrator.py +970 -0
  106. crackerjack/orchestration/coverage_improvement.py +223 -0
  107. crackerjack/orchestration/execution_strategies.py +341 -0
  108. crackerjack/orchestration/test_progress_streamer.py +636 -0
  109. crackerjack/plugins/__init__.py +15 -0
  110. crackerjack/plugins/base.py +200 -0
  111. crackerjack/plugins/hooks.py +246 -0
  112. crackerjack/plugins/loader.py +335 -0
  113. crackerjack/plugins/managers.py +259 -0
  114. crackerjack/py313.py +8 -3
  115. crackerjack/services/__init__.py +22 -0
  116. crackerjack/services/cache.py +314 -0
  117. crackerjack/services/config.py +358 -0
  118. crackerjack/services/config_integrity.py +99 -0
  119. crackerjack/services/contextual_ai_assistant.py +516 -0
  120. crackerjack/services/coverage_ratchet.py +356 -0
  121. crackerjack/services/debug.py +736 -0
  122. crackerjack/services/dependency_monitor.py +617 -0
  123. crackerjack/services/enhanced_filesystem.py +439 -0
  124. crackerjack/services/file_hasher.py +151 -0
  125. crackerjack/services/filesystem.py +421 -0
  126. crackerjack/services/git.py +176 -0
  127. crackerjack/services/health_metrics.py +611 -0
  128. crackerjack/services/initialization.py +873 -0
  129. crackerjack/services/log_manager.py +286 -0
  130. crackerjack/services/logging.py +174 -0
  131. crackerjack/services/metrics.py +578 -0
  132. crackerjack/services/pattern_cache.py +362 -0
  133. crackerjack/services/pattern_detector.py +515 -0
  134. crackerjack/services/performance_benchmarks.py +653 -0
  135. crackerjack/services/security.py +163 -0
  136. crackerjack/services/server_manager.py +234 -0
  137. crackerjack/services/smart_scheduling.py +144 -0
  138. crackerjack/services/tool_version_service.py +61 -0
  139. crackerjack/services/unified_config.py +437 -0
  140. crackerjack/services/version_checker.py +248 -0
  141. crackerjack/slash_commands/__init__.py +14 -0
  142. crackerjack/slash_commands/init.md +122 -0
  143. crackerjack/slash_commands/run.md +163 -0
  144. crackerjack/slash_commands/status.md +127 -0
  145. crackerjack-0.31.7.dist-info/METADATA +742 -0
  146. crackerjack-0.31.7.dist-info/RECORD +149 -0
  147. crackerjack-0.31.7.dist-info/entry_points.txt +2 -0
  148. crackerjack/.gitignore +0 -34
  149. crackerjack/.libcst.codemod.yaml +0 -18
  150. crackerjack/.pdm.toml +0 -1
  151. crackerjack/crackerjack.py +0 -3805
  152. crackerjack/pyproject.toml +0 -286
  153. crackerjack-0.30.3.dist-info/METADATA +0 -1290
  154. crackerjack-0.30.3.dist-info/RECORD +0 -16
  155. {crackerjack-0.30.3.dist-info → crackerjack-0.31.7.dist-info}/WHEEL +0 -0
  156. {crackerjack-0.30.3.dist-info → crackerjack-0.31.7.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,479 @@
1
+ import asyncio
2
+ import time
3
+ from contextlib import suppress
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+
7
+ from textual.app import App, ComposeResult
8
+ from textual.containers import Container, Horizontal, Vertical
9
+ from textual.reactive import reactive
10
+ from textual.widget import Widget
11
+ from textual.widgets import DataTable, Footer, Label, ProgressBar
12
+
13
+ from .progress_components import (
14
+ JobDataCollector,
15
+ ServiceManager,
16
+ TerminalRestorer,
17
+ )
18
+
19
+
20
+ class MetricCard(Widget):
21
+ value = reactive(" -- ")
22
+ label = reactive("Metric")
23
+ trend = reactive("")
24
+ color = reactive("white")
25
+
26
+ def __init__(
27
+ self,
28
+ label: str,
29
+ value: str = " -- ",
30
+ trend: str = "",
31
+ color: str = "white",
32
+ **kwargs,
33
+ ) -> None:
34
+ super().__init__(**kwargs)
35
+ self.label = label
36
+ self.value = value
37
+ self.trend = trend
38
+ self.color = color
39
+
40
+ def render(self) -> str:
41
+ trend_icon = self.trend or ""
42
+ return f"[{self.color}]{self.label}[/]\n[bold {self.color}]{self.value}[/] {trend_icon}"
43
+
44
+
45
+ class AgentActivityWidget(Widget):
46
+ def __init__(self, **kwargs) -> None:
47
+ super().__init__(**kwargs)
48
+ self.border_title = "🤖 AI Agent Activity"
49
+ self.border_title_align = "left"
50
+
51
+ def compose(self) -> ComposeResult:
52
+ with Vertical():
53
+ with Horizontal(id="agent - metrics"):
54
+ yield MetricCard(
55
+ "Active Agents",
56
+ "0",
57
+ color="cyan",
58
+ id="active - agents - metric",
59
+ )
60
+ yield MetricCard(
61
+ "Issues Fixed",
62
+ "0",
63
+ "↑",
64
+ color="green",
65
+ id="issues - fixed - metric",
66
+ )
67
+ yield MetricCard(
68
+ "Confidence",
69
+ "0 % ",
70
+ color="yellow",
71
+ id="confidence - metric",
72
+ )
73
+ yield MetricCard(
74
+ "Cache Hits",
75
+ "0",
76
+ color="magenta",
77
+ id="cache - hits - metric",
78
+ )
79
+
80
+ yield DataTable(id="agents - detail - table")
81
+
82
+ yield Label("⏸️ Coordinator: Idle", id="coordinator - status - bar")
83
+
84
+ def on_mount(self) -> None:
85
+ table = self.query_one("#agents - detail - table", DataTable)
86
+ table.add_columns(
87
+ ("Agent", 20),
88
+ ("Status", 10),
89
+ ("Type", 15),
90
+ ("Conf.", 8),
91
+ ("Time", 10),
92
+ )
93
+ table.zebra_stripes = True
94
+ table.styles.max_height = 6
95
+
96
+ def update_metrics(self, data: dict) -> None:
97
+ with suppress(Exception):
98
+ activity = data.get("agent_activity", {})
99
+ activity.get("agent_registry", {})
100
+ active_agents = activity.get("active_agents", [])
101
+
102
+ active_count = len(active_agents)
103
+ self.query_one("#active - agents - metric", MetricCard).value = str(
104
+ active_count,
105
+ )
106
+
107
+ total_fixed = sum(agent.get("issues_fixed", 0) for agent in active_agents)
108
+ avg_confidence = sum(
109
+ agent.get("confidence", 0) for agent in active_agents
110
+ ) / max(active_count, 1)
111
+ cache_hits = activity.get("cache_hits", 0)
112
+
113
+ self.query_one("#issues - fixed - metric", MetricCard).value = str(
114
+ total_fixed,
115
+ )
116
+ self.query_one(
117
+ "#confidence - metric",
118
+ MetricCard,
119
+ ).value = f"{avg_confidence: .0 % }"
120
+ self.query_one("#cache - hits - metric", MetricCard).value = str(cache_hits)
121
+
122
+ self._update_coordinator_status(activity)
123
+
124
+ self._update_agent_table(active_agents)
125
+
126
+ def _update_coordinator_status(self, activity: dict) -> None:
127
+ status = activity.get("coordinator_status", "idle")
128
+ total_agents = activity.get("agent_registry", {}).get("total_agents", 0)
129
+
130
+ status_icons = {"active": "🟢", "processing": "🔄", "idle": "⏸️", "error": "🔴"}
131
+
132
+ icon = status_icons.get(status) or "⏸️"
133
+ status_bar = self.query_one("#coordinator - status - bar", Label)
134
+ status_bar.update(
135
+ f"{icon} Coordinator: {status.title()} ({total_agents} agents available)",
136
+ )
137
+
138
+ def _update_agent_table(self, agents: list) -> None:
139
+ table = self.query_one("#agents - detail - table", DataTable)
140
+ table.clear()
141
+
142
+ if not agents:
143
+ table.add_row("No active agents", " - ", " - ", " - ", " - ")
144
+ return
145
+
146
+ for agent in agents:
147
+ name = agent.get("agent_type", "Unknown")
148
+ status = agent.get("status", "idle")
149
+ issue_type = agent.get("issue_type", " - ")
150
+ confidence = f"{agent.get('confidence', 0): .0 % }"
151
+ time_elapsed = f"{agent.get('processing_time', 0): .1f}s"
152
+
153
+ status_emoji = {
154
+ "processing": "🔄",
155
+ "success": "✅",
156
+ "failed": "❌",
157
+ "idle": "⏸️",
158
+ }.get(status, "❓")
159
+
160
+ agent_emoji = {
161
+ "FormattingAgent": "🎨",
162
+ "SecurityAgent": "🛡️",
163
+ "TestCreationAgent": "🧪",
164
+ "TestSpecialistAgent": "🔬",
165
+ "RefactoringAgent": "🔧",
166
+ "ImportOptimizationAgent": "📦",
167
+ }.get(name, "🤖")
168
+
169
+ table.add_row(
170
+ f"{agent_emoji} {name}",
171
+ f"{status_emoji} {status}",
172
+ issue_type,
173
+ confidence,
174
+ time_elapsed,
175
+ )
176
+
177
+
178
+ class JobProgressPanel(Widget):
179
+ def __init__(self, job_data: dict, **kwargs) -> None:
180
+ super().__init__(**kwargs)
181
+ self.job_data = job_data
182
+ self.start_time = time.time()
183
+
184
+ def compose(self) -> ComposeResult:
185
+ project = self.job_data.get("project", "unknown")
186
+ job_id = self.job_data.get("job_id", "unknown")[:8]
187
+
188
+ status = self.job_data.get("status", "").lower()
189
+ status_emoji = {
190
+ "running": "🔄",
191
+ "completed": "✅",
192
+ "failed": "❌",
193
+ "pending": "⏳",
194
+ }.get(status, "❓")
195
+
196
+ self.border_title = f"{status_emoji} {project} [{job_id}]"
197
+ self.border_title_align = "left"
198
+
199
+ with Horizontal():
200
+ with Vertical(id="job - progress - section"):
201
+ yield self._compose_progress_section()
202
+
203
+ with Vertical(id="job - metrics - section"):
204
+ yield self._compose_metrics_section()
205
+
206
+ def _compose_progress_section(self) -> ComposeResult:
207
+ iteration = self.job_data.get("iteration", 1)
208
+ max_iterations = self.job_data.get("max_iterations", 10)
209
+ progress = self.job_data.get("progress", 0)
210
+
211
+ stage = self.job_data.get("stage", "Unknown")
212
+ status = self.job_data.get("status", "Unknown")
213
+
214
+ yield Label(f"Stage: {stage}", classes="stage - label")
215
+ yield Label(f"Status: {status}", classes="status - label")
216
+ yield Label(f"Iteration: {iteration} / {max_iterations}")
217
+
218
+ yield ProgressBar(
219
+ total=100,
220
+ progress=progress,
221
+ id=f"job - progress - {self.job_data.get('job_id', 'unknown')}",
222
+ )
223
+
224
+ elapsed = time.time() - self.start_time
225
+ yield Label(f"⏱️ Elapsed: {self._format_time(elapsed)}")
226
+
227
+ def _compose_metrics_section(self) -> ComposeResult:
228
+ total_issues = self.job_data.get("total_issues", 0)
229
+ fixed = self.job_data.get("errors_fixed", 0)
230
+ remaining = max(0, total_issues - fixed)
231
+
232
+ with Horizontal(classes="metrics - grid"):
233
+ yield MetricCard("Issues Found", str(total_issues), color="yellow")
234
+ yield MetricCard(
235
+ "Fixed",
236
+ str(fixed),
237
+ "↑" if fixed > 0 else "",
238
+ color="green",
239
+ )
240
+ yield MetricCard(
241
+ "Remaining",
242
+ str(remaining),
243
+ "↓" if fixed > 0 else "",
244
+ color="red",
245
+ )
246
+
247
+ if total_issues > 0:
248
+ success_rate = (fixed / total_issues) * 100
249
+ yield Label(
250
+ f"Success Rate: {success_rate: .1f} % ",
251
+ classes="success - rate",
252
+ )
253
+
254
+ def _format_time(self, seconds: float) -> str:
255
+ if seconds < 60:
256
+ return f"{seconds: .0f}s"
257
+ if seconds < 3600:
258
+ return f"{seconds / 60: .0f}m {seconds % 60: .0f}s"
259
+ return f"{seconds / 3600: .0f}h {(seconds % 3600) / 60: .0f}m"
260
+
261
+
262
+ class ServiceHealthPanel(Widget):
263
+ def __init__(self, **kwargs) -> None:
264
+ super().__init__(**kwargs)
265
+ self.border_title = "🏥 Service Health"
266
+ self.border_title_align = "left"
267
+
268
+ def compose(self) -> ComposeResult:
269
+ yield DataTable(id="services - table")
270
+
271
+ def on_mount(self) -> None:
272
+ table = self.query_one("#services - table", DataTable)
273
+ table.add_columns(
274
+ ("Service", 20),
275
+ ("Status", 12),
276
+ ("Health", 10),
277
+ ("Uptime", 15),
278
+ ("Last Check", 20),
279
+ )
280
+ table.zebra_stripes = True
281
+
282
+ def update_services(self, services: list[dict]) -> None:
283
+ table = self.query_one("#services - table", DataTable)
284
+ table.clear()
285
+
286
+ for service in services:
287
+ name = service.get("name", "Unknown")
288
+ status = service.get("status", "unknown")
289
+ health = service.get("health", "unknown")
290
+ uptime = service.get("uptime", 0)
291
+ last_check = service.get("last_check", "Never")
292
+
293
+ status_indicator = {
294
+ "running": "🟢 Running",
295
+ "stopped": "🔴 Stopped",
296
+ "starting": "🟡 Starting",
297
+ "error": "❌ Error",
298
+ }.get(status, "❓ Unknown")
299
+
300
+ health_indicator = {
301
+ "healthy": "✅",
302
+ "unhealthy": "❌",
303
+ "degraded": "⚠️",
304
+ "unknown": "❓",
305
+ }.get(health, "❓")
306
+
307
+ uptime_str = self._format_uptime(uptime)
308
+
309
+ if isinstance(last_check, int | float):
310
+ last_check_str = datetime.fromtimestamp(last_check).strftime(
311
+ " % H: % M: % S",
312
+ )
313
+ else:
314
+ last_check_str = str(last_check)
315
+
316
+ table.add_row(
317
+ f"🔧 {name}",
318
+ status_indicator,
319
+ health_indicator,
320
+ uptime_str,
321
+ last_check_str,
322
+ )
323
+
324
+ def _format_uptime(self, seconds: float) -> str:
325
+ if seconds < 60:
326
+ return f"{seconds: .0f}s"
327
+ if seconds < 3600:
328
+ return f"{seconds / 60: .0f}m"
329
+ if seconds < 86400:
330
+ return f"{seconds / 3600: .1f}h"
331
+ return f"{seconds / 86400: .1f}d"
332
+
333
+
334
+ class EnhancedCrackerjackDashboard(App):
335
+ TITLE = "Crackerjack Progress Monitor"
336
+ CSS_PATH = Path(__file__).parent / "enhanced_progress_monitor.tcss"
337
+
338
+ def __init__(
339
+ self, progress_dir: Path, websocket_url: str = "ws://localhost:8675"
340
+ ) -> None:
341
+ super().__init__()
342
+ self.progress_dir = progress_dir
343
+ self.websocket_url = websocket_url
344
+ self.data_collector = JobDataCollector(progress_dir, websocket_url)
345
+ self.service_manager = ServiceManager()
346
+ self.update_timer = None
347
+ self.jobs_data = {}
348
+
349
+ def compose(self) -> ComposeResult:
350
+ yield Label("🚀 Crackerjack Progress Monitor", id="header")
351
+
352
+ with Container(id="main - content"):
353
+ yield AgentActivityWidget(id="agent - panel")
354
+
355
+ yield ServiceHealthPanel(id="service - panel")
356
+
357
+ with Container(id="jobs - container"):
358
+ yield Label("Loading jobs...", id="jobs - placeholder")
359
+
360
+ yield Footer()
361
+
362
+ def on_mount(self) -> None:
363
+ self.update_timer = self.set_interval(1.0, self.update_dashboard)
364
+
365
+ async def update_dashboard(self) -> None:
366
+ try:
367
+ jobs_result = await self.data_collector.discover_jobs()
368
+ jobs_data = jobs_result.get("data", {})
369
+
370
+ services = self.service_manager.check_all_services()
371
+ self.query_one("#service - panel", ServiceHealthPanel).update_services(
372
+ services,
373
+ )
374
+
375
+ if jobs_data.get("individual_jobs"):
376
+ aggregated_agent_data = self._aggregate_agent_data(
377
+ jobs_data["individual_jobs"],
378
+ )
379
+ self.query_one("#agent - panel", AgentActivityWidget).update_metrics(
380
+ aggregated_agent_data,
381
+ )
382
+
383
+ self._update_job_panels(jobs_data.get("individual_jobs", []))
384
+
385
+ except Exception as e:
386
+ self.console.print(f"[red]Dashboard update error: {e}[/]")
387
+
388
+ def _aggregate_agent_data(self, jobs: list[dict]) -> dict:
389
+ aggregated = {
390
+ "agent_activity": {
391
+ "active_agents": [],
392
+ "coordinator_status": "idle",
393
+ "agent_registry": {"total_agents": 6},
394
+ "cache_hits": 0,
395
+ },
396
+ }
397
+
398
+ for job in jobs:
399
+ if job.get("status", "").lower() == "running":
400
+ agent_summary = job.get("agent_summary", {})
401
+ if agent_summary:
402
+ aggregated["agent_activity"]["coordinator_status"] = "active"
403
+
404
+ aggregated["agent_activity"]["active_agents"].extend(
405
+ [
406
+ {
407
+ "agent_type": agent_type,
408
+ "status": "processing",
409
+ "confidence": 0.85,
410
+ "processing_time": 2.3,
411
+ "issue_type": "complexity"
412
+ if agent_type == "RefactoringAgent"
413
+ else "formatting",
414
+ }
415
+ for agent_type in ("RefactoringAgent", "FormattingAgent")
416
+ ],
417
+ )
418
+
419
+ return aggregated
420
+
421
+ def _update_job_panels(self, jobs: list[dict]) -> None:
422
+ container = self.query_one("#jobs - container", Container)
423
+
424
+ with suppress(Exception):
425
+ container.remove_children("#jobs - placeholder")
426
+
427
+ existing_job_ids = {panel.id for panel in container.query(".job - panel")}
428
+ current_job_ids = {f"job - {job['job_id']}" for job in jobs}
429
+
430
+ for panel_id in existing_job_ids - current_job_ids:
431
+ with suppress(Exception):
432
+ panel = container.query_one(f"#{panel_id}")
433
+ panel.remove()
434
+
435
+ for job in jobs:
436
+ panel_id = f"job - {job['job_id']}"
437
+ if panel_id not in existing_job_ids:
438
+ panel = JobProgressPanel(job, id=panel_id, classes="job - panel")
439
+ container.mount(panel)
440
+ else:
441
+ panel = container.query_one(f"#{panel_id}", JobProgressPanel)
442
+ panel.job_data = job
443
+ panel.refresh()
444
+
445
+
446
+ async def run_enhanced_progress_monitor(
447
+ progress_dir: Path | None = None,
448
+ websocket_url: str = "ws://localhost:8675",
449
+ dev_mode: bool = False,
450
+ ) -> None:
451
+ if progress_dir is None:
452
+ progress_dir = Path(tempfile.gettempdir()) / "crackerjack-mcp-progress"
453
+
454
+ restorer = TerminalRestorer()
455
+ restorer.setup_handlers()
456
+
457
+ try:
458
+ app = EnhancedCrackerjackDashboard(progress_dir, websocket_url)
459
+
460
+ if dev_mode:
461
+ from rich.console import Console
462
+
463
+ console = Console()
464
+ console.print("[bold cyan]🛠️ Development Mode: Enabled[/bold cyan]")
465
+ app.dev = True
466
+
467
+ await app.run_async()
468
+ finally:
469
+ restorer.restore()
470
+
471
+
472
+ if __name__ == "__main__":
473
+ import sys
474
+ import tempfile
475
+
476
+ progress_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else None
477
+ websocket_url = sys.argv[2] if len(sys.argv) > 2 else "ws://localhost:8675"
478
+
479
+ asyncio.run(run_enhanced_progress_monitor(progress_dir, websocket_url))