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
@@ -14,6 +14,8 @@ from textual.containers import Container
14
14
  from textual.widget import Widget
15
15
  from textual.widgets import DataTable, Footer, Label, ProgressBar
16
16
 
17
+ from crackerjack.core.timeout_manager import TimeoutStrategy, get_timeout_manager
18
+
17
19
  from .progress_components import (
18
20
  ErrorCollector,
19
21
  JobDataCollector,
@@ -75,7 +77,7 @@ class AgentStatusPanel(Widget):
75
77
  active_agents = activity.get("active_agents", [])
76
78
 
77
79
  if not active_agents:
78
- agents_table.add_row("No active agents", " - ", " - ", " - ", " - ")
80
+ agents_table.add_row("No active agents", "-", "-", "-", "-")
79
81
  return
80
82
 
81
83
  for agent in active_agents:
@@ -88,9 +90,9 @@ class AgentStatusPanel(Widget):
88
90
 
89
91
  current_issue = agent.get("current_issue", {})
90
92
  issue_type = (
91
- current_issue.get("type", " - ")
93
+ current_issue.get("type", "-")
92
94
  if current_issue
93
- else agent.get("issue_type", " - ")
95
+ else agent.get("issue_type", "-")
94
96
  )
95
97
 
96
98
  status_display = f"{self._get_status_emoji(status)} {status.title()}"
@@ -99,8 +101,8 @@ class AgentStatusPanel(Widget):
99
101
  f"{emoji} {agent_type}",
100
102
  status_display,
101
103
  issue_type,
102
- f"{confidence: .0 % }" if confidence > 0 else " - ",
103
- f"{processing_time: .1f}s" if processing_time > 0 else " - ",
104
+ f"{confidence: .0 % }" if confidence > 0 else "-",
105
+ f"{processing_time: .1f}s" if processing_time > 0 else "-",
104
106
  )
105
107
 
106
108
  def _update_stats(self, data: dict) -> None:
@@ -212,7 +214,7 @@ class JobPanel(Widget):
212
214
  errors_container.border_title = "❌ Errors"
213
215
 
214
216
  errors_table = self.query_one(
215
- f"#job-errors-{self.job_data.get('job_id', 'unknown')}",
217
+ f"#job-errors -{self.job_data.get('job_id', 'unknown')}",
216
218
  DataTable,
217
219
  )
218
220
  errors_table.add_columns("", "", "", "")
@@ -222,7 +224,7 @@ class JobPanel(Widget):
222
224
  def _update_errors_table(self) -> None:
223
225
  with suppress(Exception):
224
226
  errors_table = self.query_one(
225
- f"#job-errors-{self.job_data.get('job_id', 'unknown')}",
227
+ f"#job-errors -{self.job_data.get('job_id', 'unknown')}",
226
228
  DataTable,
227
229
  )
228
230
  errors_table.clear()
@@ -276,7 +278,7 @@ class JobPanel(Widget):
276
278
  def _update_progress_bar(self) -> None:
277
279
  with suppress(Exception):
278
280
  progress_bar = self.query_one(
279
- f"#job-progress-{self.job_data.get('job_id', 'unknown')}",
281
+ f"#job-progress -{self.job_data.get('job_id', 'unknown')}",
280
282
  ProgressBar,
281
283
  )
282
284
  progress_value = self.iteration_count / max(self.max_iterations, 1) * 100
@@ -365,7 +367,7 @@ class JobPanel(Widget):
365
367
  total=100,
366
368
  show_eta=False,
367
369
  show_percentage=False,
368
- id=f"job-progress-{self.job_data.get('job_id', 'unknown')}",
370
+ id=f"job-progress -{self.job_data.get('job_id', 'unknown')}",
369
371
  )
370
372
 
371
373
  def _compose_stage_and_status(self) -> ComposeResult:
@@ -401,7 +403,7 @@ class JobPanel(Widget):
401
403
  def _compose_errors_column(self) -> ComposeResult:
402
404
  with Container(classes="job-errors"):
403
405
  yield DataTable(
404
- id=f"job-errors-{self.job_data.get('job_id', 'unknown')}",
406
+ id=f"job-errors -{self.job_data.get('job_id', 'unknown')}",
405
407
  )
406
408
 
407
409
 
@@ -415,12 +417,13 @@ class CrackerjackDashboard(App):
415
417
 
416
418
  def __init__(self) -> None:
417
419
  super().__init__()
418
- self.progress_dir = Path(tempfile.gettempdir()) / "crackerjack-mcp-progress"
420
+ self.progress_dir = Path(tempfile.gettempdir()) / "crackerjack - mcp-progress"
419
421
  self.websocket_url = "ws://localhost:8675"
420
422
  self.refresh_timer = None
421
423
  self.active_jobs = {}
422
424
  self.completed_jobs_stats = {}
423
425
  self.current_polling_method = "File"
426
+ self.timeout_manager = get_timeout_manager()
424
427
 
425
428
  self.job_collector = JobDataCollector(self.progress_dir, self.websocket_url)
426
429
  self.service_checker = ServiceHealthChecker()
@@ -442,7 +445,7 @@ class CrackerjackDashboard(App):
442
445
  def _compose_left_column(self) -> ComposeResult:
443
446
  with Container(id="left-column"):
444
447
  yield from self._compose_jobs_panel()
445
- yield AgentStatusPanel(id="agent-status-panel")
448
+ yield AgentStatusPanel(id="agent - status-panel")
446
449
 
447
450
  def _compose_right_column(self) -> ComposeResult:
448
451
  with Container(id="right-column"):
@@ -470,7 +473,7 @@ class CrackerjackDashboard(App):
470
473
 
471
474
  def _compose_discovery_section(self) -> ComposeResult:
472
475
  with Container(id="discovery-section"):
473
- yield Container(id="job-discovery-container")
476
+ yield Container(id="job - discovery-container")
474
477
 
475
478
  def on_mount(self) -> None:
476
479
  self._setup_border_titles()
@@ -528,43 +531,113 @@ class CrackerjackDashboard(App):
528
531
  self.refresh_timer = self.set_interval(0.5, self._refresh_data)
529
532
 
530
533
  async def _refresh_data(self) -> None:
531
- with suppress(Exception):
532
- if hasattr(self, "_refresh_counter"):
533
- self._refresh_counter += 1
534
- else:
535
- self._refresh_counter = 0
536
-
537
- if self._refresh_counter % 10 == 0:
538
- await self._ensure_services_running()
539
-
540
- jobs_data = await self._discover_jobs()
534
+ try:
535
+ # Refresh cycle with timeout protection
536
+ async with self.timeout_manager.timeout_context(
537
+ "network_operations",
538
+ timeout=10.0, # Total refresh timeout
539
+ strategy=TimeoutStrategy.GRACEFUL_DEGRADATION,
540
+ ):
541
+ if hasattr(self, "_refresh_counter"):
542
+ self._refresh_counter += 1
543
+ else:
544
+ self._refresh_counter = 0
545
+
546
+ # Ensure services running less frequently to avoid overhead
547
+ if self._refresh_counter % 20 == 0: # Every 10 seconds instead of 5
548
+ with suppress(Exception):
549
+ await self.timeout_manager.with_timeout(
550
+ "network_operations",
551
+ self._ensure_services_running(),
552
+ timeout=5.0,
553
+ strategy=TimeoutStrategy.FAIL_FAST,
554
+ )
555
+
556
+ # Collect data with individual timeouts
557
+ jobs_data = await self.timeout_manager.with_timeout(
558
+ "network_operations",
559
+ self._discover_jobs(),
560
+ timeout=3.0,
561
+ strategy=TimeoutStrategy.FAIL_FAST,
562
+ )
541
563
 
542
- services_data = await self._collect_services_data()
564
+ services_data = await self.timeout_manager.with_timeout(
565
+ "network_operations",
566
+ self._collect_services_data(),
567
+ timeout=2.0,
568
+ strategy=TimeoutStrategy.FAIL_FAST,
569
+ )
543
570
 
544
- errors_data = await self._collect_recent_errors()
571
+ errors_data = await self.timeout_manager.with_timeout(
572
+ "file_operations",
573
+ self._collect_recent_errors(),
574
+ timeout=2.0,
575
+ strategy=TimeoutStrategy.FAIL_FAST,
576
+ )
545
577
 
546
- if jobs_data["individual_jobs"]:
547
- jobs_data["individual_jobs"][0].get("project", "crackerjack")
578
+ # Update UI components
579
+ self.query_one("#services-panel").border_title = "🔧 Services"
548
580
 
549
- self.query_one("#services-panel").border_title = "🔧 Services"
581
+ self._update_jobs_table(jobs_data)
582
+ self._update_services_table(services_data)
583
+ self._update_errors_table(errors_data)
584
+ self._update_job_panels(jobs_data)
585
+ self._update_agent_panel(jobs_data)
586
+ self._update_status_bars(jobs_data)
550
587
 
551
- self._update_jobs_table(jobs_data)
552
- self._update_services_table(services_data)
553
- self._update_errors_table(errors_data)
554
- self._update_job_panels(jobs_data)
555
- self._update_agent_panel(jobs_data)
556
- self._update_status_bars(jobs_data)
588
+ except Exception as e:
589
+ # Log error but don't crash the dashboard
590
+ with suppress(Exception):
591
+ console = Console()
592
+ console.print(f"[red]Dashboard refresh error: {e}[/red]")
557
593
 
558
594
  async def _discover_jobs(self) -> dict:
559
- result = await self.job_collector.discover_jobs()
560
- self.current_polling_method = result["method"]
561
- return result["data"]
595
+ try:
596
+ result = await self.timeout_manager.with_timeout(
597
+ "network_operations",
598
+ self.job_collector.discover_jobs(),
599
+ timeout=5.0,
600
+ strategy=TimeoutStrategy.FAIL_FAST,
601
+ )
602
+ self.current_polling_method = result["method"]
603
+ return result["data"]
604
+ except Exception:
605
+ # Return empty data structure on timeout or error
606
+ return {
607
+ "active": 0,
608
+ "completed": 0,
609
+ "failed": 0,
610
+ "total": 0,
611
+ "individual_jobs": [],
612
+ "total_issues": 0,
613
+ "errors_fixed": 0,
614
+ "errors_failed": 0,
615
+ "current_errors": 0,
616
+ }
562
617
 
563
618
  async def _collect_services_data(self) -> list:
564
- return await self.service_checker.collect_services_data()
619
+ try:
620
+ return await self.timeout_manager.with_timeout(
621
+ "network_operations",
622
+ self.service_checker.collect_services_data(),
623
+ timeout=3.0,
624
+ strategy=TimeoutStrategy.FAIL_FAST,
625
+ )
626
+ except Exception:
627
+ # Return minimal service data on timeout
628
+ return [("Services", "🔴 Timeout", "0")]
565
629
 
566
630
  async def _collect_recent_errors(self) -> list:
567
- return await self.error_collector.collect_recent_errors()
631
+ try:
632
+ return await self.timeout_manager.with_timeout(
633
+ "file_operations",
634
+ self.error_collector.collect_recent_errors(),
635
+ timeout=2.0,
636
+ strategy=TimeoutStrategy.FAIL_FAST,
637
+ )
638
+ except Exception:
639
+ # Return empty error list on timeout
640
+ return []
568
641
 
569
642
  def _update_jobs_table(self, jobs_data: dict) -> None:
570
643
  with suppress(Exception):
@@ -696,7 +769,7 @@ class CrackerjackDashboard(App):
696
769
 
697
770
  def _update_agent_panel(self, jobs_data: dict) -> None:
698
771
  with suppress(Exception):
699
- agent_panel = self.query_one("#agent-status-panel", AgentStatusPanel)
772
+ agent_panel = self.query_one("#agent - status-panel", AgentStatusPanel)
700
773
 
701
774
  agent_data = {}
702
775
  for job in jobs_data.get("individual_jobs", []):
@@ -709,7 +782,7 @@ class CrackerjackDashboard(App):
709
782
 
710
783
  def _update_job_panels(self, jobs_data: dict) -> None:
711
784
  with suppress(Exception):
712
- container = self.query_one("#job-discovery-container")
785
+ container = self.query_one("#job - discovery-container")
713
786
  current_job_ids = self._get_current_job_ids(jobs_data)
714
787
 
715
788
  self._remove_obsolete_panels(current_job_ids)
@@ -792,17 +865,17 @@ class CrackerjackDashboard(App):
792
865
  container.mount(job_panel)
793
866
 
794
867
  def _handle_placeholder_visibility(self, container) -> None:
795
- has_placeholder = bool(container.query("#no-jobs-label"))
868
+ has_placeholder = bool(container.query("#no - jobs-label"))
796
869
 
797
870
  if not self.active_jobs and not has_placeholder:
798
871
  container.mount(
799
872
  Label(
800
873
  "No active jobs detected. Start a Crackerjack job to see progress here.",
801
- id="no-jobs-label",
874
+ id="no - jobs-label",
802
875
  ),
803
876
  )
804
877
  elif self.active_jobs and has_placeholder:
805
- container.query("#no-jobs-label").remove()
878
+ container.query("#no - jobs-label").remove()
806
879
 
807
880
  def _update_status_bars(self, jobs_data: dict) -> None:
808
881
  pass
@@ -816,7 +889,7 @@ class CrackerjackDashboard(App):
816
889
  table = self.query_one(table_id, DataTable)
817
890
  table.clear()
818
891
 
819
- container = self.query_one("#job-discovery-container")
892
+ container = self.query_one("#job - discovery-container")
820
893
  container.query("JobPanel").remove()
821
894
  container.query("Label").remove()
822
895
  self.active_jobs.clear()
@@ -889,14 +962,14 @@ async def run_progress_monitor(
889
962
  with suppress(Exception):
890
963
  console = Console()
891
964
  console.print(
892
- "[bold green]🚀 Starting Crackerjack Progress Monitor[/bold green]",
965
+ "[bold green]🚀 Starting Crackerjack Progress Monitor[/ bold green]",
893
966
  )
894
967
 
895
968
  if enable_watchdog:
896
- console.print("[bold yellow]🐕 Service Watchdog: Enabled[/bold yellow]")
969
+ console.print("[bold yellow]🐕 Service Watchdog: Enabled[/ bold yellow]")
897
970
 
898
971
  if dev_mode:
899
- console.print("[bold cyan]🛠️ Development Mode: Enabled[/bold cyan]")
972
+ console.print("[bold cyan]🛠️ Development Mode: Enabled[/ bold cyan]")
900
973
 
901
974
  app = CrackerjackDashboard()
902
975
 
@@ -912,7 +985,7 @@ async def run_crackerjack_with_progress(
912
985
  with suppress(Exception):
913
986
  console = Console()
914
987
  console.print(
915
- "[bold green]🚀 Starting Crackerjack Progress Monitor[/bold green]",
988
+ "[bold green]🚀 Starting Crackerjack Progress Monitor[/ bold green]",
916
989
  )
917
990
 
918
991
  app = CrackerjackDashboard()
@@ -159,7 +159,7 @@ class ResourceMonitor:
159
159
  and len(self.active_jobs) >= self.config.max_concurrent_jobs
160
160
  ):
161
161
  console.print(
162
- f"[yellow]🚫 Job {job_id} rejected: max concurrent jobs ({self.config.max_concurrent_jobs}) reached[/yellow]",
162
+ f"[yellow]🚫 Job {job_id} rejected: max concurrent jobs ({self.config.max_concurrent_jobs}) reached[/ yellow]",
163
163
  )
164
164
  return False
165
165
 
@@ -167,7 +167,7 @@ class ResourceMonitor:
167
167
  await asyncio.wait_for(self.job_locks.acquire(), timeout=0.1)
168
168
  except TimeoutError:
169
169
  console.print(
170
- f"[yellow]🚫 Job {job_id} rejected: max concurrent jobs ({self.config.max_concurrent_jobs}) reached[/yellow]",
170
+ f"[yellow]🚫 Job {job_id} rejected: max concurrent jobs ({self.config.max_concurrent_jobs}) reached[/ yellow]",
171
171
  )
172
172
  return False
173
173
 
@@ -175,12 +175,12 @@ class ResourceMonitor:
175
175
  self.active_jobs[job_id] = time.time()
176
176
 
177
177
  console.print(
178
- f"[green]🎯 Job {job_id} acquired slot ({len(self.active_jobs)} / {self.config.max_concurrent_jobs})[/green]",
178
+ f"[green]🎯 Job {job_id} acquired slot ({len(self.active_jobs)} / {self.config.max_concurrent_jobs})[/ green]",
179
179
  )
180
180
  return True
181
181
 
182
182
  except Exception as e:
183
- console.print(f"[red]Error acquiring job slot for {job_id}: {e}[/red]")
183
+ console.print(f"[red]Error acquiring job slot for {job_id}: {e}[/ red]")
184
184
  return False
185
185
 
186
186
  async def release_job_slot(self, job_id: str) -> None:
@@ -189,7 +189,7 @@ class ResourceMonitor:
189
189
  start_time = self.active_jobs.pop(job_id)
190
190
  duration = time.time() - start_time
191
191
  console.print(
192
- f"[blue]🏁 Job {job_id} completed in {duration: .1f}s ({len(self.active_jobs)} / {self.config.max_concurrent_jobs} active)[/blue]",
192
+ f"[blue]🏁 Job {job_id} completed in {duration: .1f}s ({len(self.active_jobs)} / {self.config.max_concurrent_jobs} active)[/ blue]",
193
193
  )
194
194
 
195
195
  self.job_locks.release()
@@ -208,7 +208,7 @@ class ResourceMonitor:
208
208
 
209
209
  if stale_jobs:
210
210
  console.print(
211
- f"[yellow]🧹 Cleaned up {len(stale_jobs)} stale jobs (exceeded {self.config.max_job_duration_minutes}m)[/yellow]",
211
+ f"[yellow]🧹 Cleaned up {len(stale_jobs)} stale jobs (exceeded {self.config.max_job_duration_minutes}m)[/ yellow]",
212
212
  )
213
213
 
214
214
  return len(stale_jobs)
@@ -221,7 +221,7 @@ class ResourceMonitor:
221
221
  size_mb = file_path.stat().st_size / (1024 * 1024)
222
222
  if size_mb > self.config.max_file_size_mb:
223
223
  console.print(
224
- f"[red]🚫 File {file_path} ({size_mb: .1f}MB) exceeds limit ({self.config.max_file_size_mb}MB)[/red]",
224
+ f"[red]🚫 File {file_path} ({size_mb: .1f}MB) exceeds limit ({self.config.max_file_size_mb}MB)[/ red]",
225
225
  )
226
226
  return False
227
227
 
@@ -234,10 +234,10 @@ class ResourceMonitor:
234
234
  if not progress_dir.exists():
235
235
  return True
236
236
 
237
- file_count = len(list(progress_dir.glob("job -* .json")))
237
+ file_count = len(list(progress_dir.glob("job-* .json")))
238
238
  if file_count > self.config.max_progress_files:
239
239
  console.print(
240
- f"[red]🚫 Progress files ({file_count}) exceed limit ({self.config.max_progress_files})[/red]",
240
+ f"[red]🚫 Progress files ({file_count}) exceed limit ({self.config.max_progress_files})[/ red]",
241
241
  )
242
242
  return False
243
243
 
@@ -278,7 +278,7 @@ class RateLimitMiddleware:
278
278
  async def start(self) -> None:
279
279
  self._running = True
280
280
  self._cleanup_task = asyncio.create_task(self._cleanup_loop())
281
- console.print("[green]🛡️ Rate limiting middleware started[/green]")
281
+ console.print("[green]🛡️ Rate limiting middleware started[/ green]")
282
282
 
283
283
  async def stop(self) -> None:
284
284
  self._running = False
@@ -286,7 +286,7 @@ class RateLimitMiddleware:
286
286
  self._cleanup_task.cancel()
287
287
  with contextlib.suppress(asyncio.CancelledError):
288
288
  await self._cleanup_task
289
- console.print("[yellow]🛡️ Rate limiting middleware stopped[/yellow]")
289
+ console.print("[yellow]🛡️ Rate limiting middleware stopped[/ yellow]")
290
290
 
291
291
  async def check_request_allowed(
292
292
  self,
@@ -314,7 +314,7 @@ class RateLimitMiddleware:
314
314
  except asyncio.CancelledError:
315
315
  break
316
316
  except Exception as e:
317
- console.print(f"[red]Error in cleanup loop: {e}[/red]")
317
+ console.print(f"[red]Error in cleanup loop: {e}[/ red]")
318
318
  await asyncio.sleep(60)
319
319
 
320
320
  def get_comprehensive_stats(self) -> dict[str, t.Any]:
@@ -65,9 +65,9 @@ def _validate_job_id(job_id: str) -> bool:
65
65
  if len(job_id) > 50:
66
66
  return False
67
67
 
68
- import re
68
+ from crackerjack.services.regex_patterns import is_valid_job_id
69
69
 
70
- return bool(re.match(r"^[a-zA-Z0-9_-]+$", job_id))
70
+ return is_valid_job_id(job_id)
71
71
 
72
72
 
73
73
  async def _start_websocket_server() -> bool:
@@ -81,7 +81,7 @@ def create_mcp_server() -> t.Any | None:
81
81
  if not MCP_AVAILABLE or FastMCP is None:
82
82
  return None
83
83
 
84
- mcp_app = FastMCP("crackerjack-mcp-server")
84
+ mcp_app = FastMCP("crackerjack - mcp-server")
85
85
 
86
86
  from crackerjack.slash_commands import get_slash_command_path
87
87
 
@@ -129,39 +129,37 @@ def handle_mcp_server_command(
129
129
  restart: bool = False,
130
130
  websocket_port: int | None = None,
131
131
  ) -> None:
132
- """Handle MCP server start/stop/restart commands."""
133
132
  if stop or restart:
134
- console.print("[yellow]Stopping MCP servers...[/yellow]")
135
- # Kill any existing MCP server processes
133
+ console.print("[yellow]Stopping MCP servers...[/ yellow]")
134
+
136
135
  try:
137
136
  result = subprocess.run(
138
- ["pkill", "-f", "crackerjack-mcp-server"],
137
+ ["pkill", "- f", "crackerjack - mcp-server"],
139
138
  check=False,
140
139
  capture_output=True,
141
140
  text=True,
142
141
  timeout=10,
143
142
  )
144
143
  if result.returncode == 0:
145
- console.print("[green]✅ MCP servers stopped[/green]")
144
+ console.print("[green]✅ MCP servers stopped[/ green]")
146
145
  else:
147
- console.print("[dim]No MCP servers were running[/dim]")
146
+ console.print("[dim]No MCP servers were running[/ dim]")
148
147
  except subprocess.TimeoutExpired:
149
- console.print("[red]Timeout stopping MCP servers[/red]")
148
+ console.print("[red]Timeout stopping MCP servers[/ red]")
150
149
  except Exception as e:
151
- console.print(f"[red]Error stopping MCP servers: {e}[/red]")
150
+ console.print(f"[red]Error stopping MCP servers: {e}[/ red]")
152
151
 
153
152
  if stop:
154
153
  return
155
154
 
156
- # For restart, wait a moment before starting again
157
155
  time.sleep(2)
158
156
 
159
157
  if start or restart:
160
- console.print("[green]Starting MCP server...[/green]")
158
+ console.print("[green]Starting MCP server...[/ green]")
161
159
  try:
162
160
  main(".", websocket_port)
163
161
  except Exception as e:
164
- console.print(f"[red]Failed to start MCP server: {e}[/red]")
162
+ console.print(f"[red]Failed to start MCP server: {e}[/ red]")
165
163
 
166
164
 
167
165
  def _initialize_context(context: MCPServerContext) -> None:
@@ -174,11 +172,8 @@ def _stop_websocket_server() -> None:
174
172
  from contextlib import suppress
175
173
 
176
174
  with suppress(RuntimeError):
177
- # Context not initialized, nothing to stop
178
175
  context = get_context()
179
176
  if context and hasattr(context, "_stop_websocket_server"):
180
- # The websocket cleanup is handled asynchronously
181
- # and called from the context's cleanup handlers
182
177
  pass
183
178
 
184
179
 
@@ -197,7 +192,6 @@ def main(project_path_arg: str = ".", websocket_port: int | None = None) -> None
197
192
  context = MCPServerContext(config)
198
193
  context.console = console
199
194
 
200
- # Set custom WebSocket port if specified
201
195
  if websocket_port:
202
196
  context.websocket_server_port = websocket_port
203
197
 
@@ -205,19 +199,19 @@ def main(project_path_arg: str = ".", websocket_port: int | None = None) -> None
205
199
 
206
200
  mcp_app = create_mcp_server()
207
201
  if not mcp_app:
208
- console.print("[red]Failed to create MCP server[/red]")
202
+ console.print("[red]Failed to create MCP server[/ red]")
209
203
  return
210
204
 
211
- console.print("[green]Starting Crackerjack MCP Server...[/green]")
205
+ console.print("[green]Starting Crackerjack MCP Server...[/ green]")
212
206
  console.print(f"Project path: {project_path}")
213
207
  if websocket_port:
214
208
  console.print(f"WebSocket port: {websocket_port}")
215
209
 
216
- console.print("[yellow]MCP app created, about to run...[/yellow]")
210
+ console.print("[yellow]MCP app created, about to run...[/ yellow]")
217
211
  try:
218
212
  mcp_app.run()
219
213
  except Exception as e:
220
- console.print(f"[red]MCP run failed: {e}[/red]")
214
+ console.print(f"[red]MCP run failed: {e}[/ red]")
221
215
  import traceback
222
216
 
223
217
  traceback.print_exc()