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
@@ -1,10 +1,3 @@
1
- """Workflow execution engine for MCP tools.
2
-
3
- This module handles the core workflow execution logic, including orchestrator setup,
4
- iteration management, and result handling. Split from execution_tools.py for better
5
- separation of concerns.
6
- """
7
-
8
1
  import asyncio
9
2
  import time
10
3
  import typing as t
@@ -18,13 +11,11 @@ from .progress_tools import _update_progress
18
11
  async def execute_crackerjack_workflow(
19
12
  args: str, kwargs: dict[str, t.Any]
20
13
  ) -> dict[str, t.Any]:
21
- """Execute the main crackerjack workflow with progress tracking."""
22
14
  job_id = str(uuid.uuid4())[:8]
23
15
 
24
16
  try:
25
17
  return await _execute_crackerjack_sync(job_id, args, kwargs, get_context())
26
18
  except Exception as e:
27
- # Add full traceback for debugging
28
19
  import traceback
29
20
 
30
21
  error_details = traceback.format_exc()
@@ -43,13 +34,10 @@ async def _execute_crackerjack_sync(
43
34
  kwargs: dict[str, t.Any],
44
35
  context: t.Any,
45
36
  ) -> dict[str, t.Any]:
46
- """Execute crackerjack workflow synchronously with progress tracking."""
47
- # Initialize execution environment
48
37
  setup_result = await _initialize_execution(job_id, args, kwargs, context)
49
38
  if setup_result.get("status") == "failed":
50
39
  return setup_result
51
40
 
52
- # Set up orchestrator
53
41
  orchestrator_result = await _setup_orchestrator(
54
42
  job_id, args, kwargs, setup_result["working_dir"], context
55
43
  )
@@ -58,7 +46,6 @@ async def _execute_crackerjack_sync(
58
46
 
59
47
  orchestrator = orchestrator_result["orchestrator"]
60
48
 
61
- # Run workflow iterations
62
49
  return await _run_workflow_iterations(job_id, orchestrator, kwargs, context)
63
50
 
64
51
 
@@ -68,7 +55,6 @@ async def _initialize_execution(
68
55
  kwargs: dict[str, t.Any],
69
56
  context: t.Any,
70
57
  ) -> dict[str, t.Any]:
71
- """Initialize execution environment and validate parameters."""
72
58
  _update_progress(
73
59
  job_id,
74
60
  {
@@ -79,7 +65,9 @@ async def _initialize_execution(
79
65
  context,
80
66
  )
81
67
 
82
- # Validate working directory
68
+ # Ensure WebSocket server is running for progress tracking
69
+ await _ensure_websocket_server_running(job_id, context)
70
+
83
71
  working_dir = kwargs.get("working_directory", ".")
84
72
  from pathlib import Path
85
73
 
@@ -115,7 +103,6 @@ async def _setup_orchestrator(
115
103
  working_dir: t.Any,
116
104
  context: t.Any,
117
105
  ) -> dict[str, t.Any]:
118
- """Set up the appropriate orchestrator based on configuration."""
119
106
  _update_progress(
120
107
  job_id,
121
108
  {
@@ -126,9 +113,7 @@ async def _setup_orchestrator(
126
113
  context,
127
114
  )
128
115
 
129
- use_advanced = kwargs.get(
130
- "advanced_orchestration", False
131
- ) # Temporarily disable advanced orchestration
116
+ use_advanced = kwargs.get("advanced_orchestration", False)
132
117
 
133
118
  try:
134
119
  if use_advanced:
@@ -155,7 +140,6 @@ async def _setup_orchestrator(
155
140
  async def _create_advanced_orchestrator(
156
141
  working_dir: t.Any, kwargs: dict[str, t.Any], context: t.Any
157
142
  ) -> t.Any:
158
- """Create advanced async orchestrator with dependency injection."""
159
143
  from pathlib import Path
160
144
 
161
145
  from crackerjack.core.async_workflow_orchestrator import AsyncWorkflowOrchestrator
@@ -163,11 +147,12 @@ async def _create_advanced_orchestrator(
163
147
 
164
148
  container = EnhancedContainer()
165
149
 
166
- # Register services with the container
167
150
  await _register_core_services(container, Path(working_dir))
168
151
 
169
152
  orchestrator = AsyncWorkflowOrchestrator(
170
153
  pkg_path=Path(working_dir),
154
+ verbose=kwargs.get("verbose", False),
155
+ debug=kwargs.get("debug", False),
171
156
  )
172
157
 
173
158
  return orchestrator
@@ -176,7 +161,6 @@ async def _create_advanced_orchestrator(
176
161
  def _create_standard_orchestrator(
177
162
  working_dir: t.Any, kwargs: dict[str, t.Any]
178
163
  ) -> t.Any:
179
- """Create standard synchronous orchestrator."""
180
164
  from pathlib import Path
181
165
 
182
166
  from crackerjack.core.workflow_orchestrator import WorkflowOrchestrator
@@ -185,7 +169,6 @@ def _create_standard_orchestrator(
185
169
 
186
170
 
187
171
  async def _register_core_services(container: t.Any, working_dir: t.Any) -> None:
188
- """Register core services with the dependency injection container."""
189
172
  from rich.console import Console
190
173
 
191
174
  from crackerjack.core.enhanced_container import ServiceLifetime
@@ -201,7 +184,6 @@ async def _register_core_services(container: t.Any, working_dir: t.Any) -> None:
201
184
 
202
185
  console = Console()
203
186
 
204
- # Register managers
205
187
  container.register_service(
206
188
  HookManagerProtocol,
207
189
  AsyncHookManager(console, working_dir),
@@ -220,7 +202,6 @@ async def _register_core_services(container: t.Any, working_dir: t.Any) -> None:
220
202
  ServiceLifetime.SINGLETON,
221
203
  )
222
204
 
223
- # Register filesystem service
224
205
  container.register_service(
225
206
  EnhancedFileSystemService,
226
207
  EnhancedFileSystemService(),
@@ -234,7 +215,6 @@ async def _run_workflow_iterations(
234
215
  kwargs: dict[str, t.Any],
235
216
  context: t.Any,
236
217
  ) -> dict[str, t.Any]:
237
- """Run workflow iterations until completion or max attempts."""
238
218
  options = _create_workflow_options(kwargs)
239
219
  max_iterations = kwargs.get("max_iterations", 10)
240
220
 
@@ -256,9 +236,8 @@ async def _run_workflow_iterations(
256
236
  )
257
237
 
258
238
  if success:
259
- # Attempt coverage improvement after successful execution (if enabled)
260
239
  coverage_result = None
261
- if kwargs.get("boost_coverage", False): # Temporarily disabled
240
+ if kwargs.get("boost_coverage", False):
262
241
  coverage_result = await _attempt_coverage_improvement(
263
242
  job_id, orchestrator, context
264
243
  )
@@ -266,7 +245,6 @@ async def _run_workflow_iterations(
266
245
  job_id, iteration + 1, context, coverage_result
267
246
  )
268
247
 
269
- # Handle retry logic
270
248
  if iteration < max_iterations - 1:
271
249
  await _handle_iteration_retry(job_id, iteration, context)
272
250
 
@@ -277,13 +255,10 @@ async def _run_workflow_iterations(
277
255
 
278
256
 
279
257
  def _create_workflow_options(kwargs: dict[str, t.Any]) -> t.Any:
280
- """Create workflow options from kwargs."""
281
258
  from types import SimpleNamespace
282
259
 
283
- # Create options object with all required attributes from OptionsProtocol
284
260
  options = SimpleNamespace()
285
261
 
286
- # Core execution options
287
262
  options.commit = kwargs.get("commit", False)
288
263
  options.interactive = kwargs.get("interactive", False)
289
264
  options.no_config_updates = kwargs.get("no_config_updates", False)
@@ -295,11 +270,9 @@ def _create_workflow_options(kwargs: dict[str, t.Any]) -> t.Any:
295
270
  options.ai_agent = kwargs.get("ai_agent", True)
296
271
  options.async_mode = kwargs.get("async_mode", True)
297
272
 
298
- # Test options
299
273
  options.test_workers = kwargs.get("test_workers", 0)
300
274
  options.test_timeout = kwargs.get("test_timeout", 0)
301
275
 
302
- # Publishing options
303
276
  options.publish = kwargs.get("publish")
304
277
  options.bump = kwargs.get("bump")
305
278
  options.all = kwargs.get("all")
@@ -309,23 +282,18 @@ def _create_workflow_options(kwargs: dict[str, t.Any]) -> t.Any:
309
282
  options.cleanup_pypi = kwargs.get("cleanup_pypi", False)
310
283
  options.keep_releases = kwargs.get("keep_releases", 10)
311
284
 
312
- # Server options
313
285
  options.start_mcp_server = kwargs.get("start_mcp_server", False)
314
286
 
315
- # Hook options
316
287
  options.update_precommit = kwargs.get("update_precommit", False)
317
288
  options.experimental_hooks = kwargs.get("experimental_hooks", False)
318
289
  options.enable_pyrefly = kwargs.get("enable_pyrefly", False)
319
290
  options.enable_ty = kwargs.get("enable_ty", False)
320
291
 
321
- # Cleanup options
322
292
  options.cleanup = kwargs.get("cleanup")
323
293
 
324
- # Coverage and progress
325
294
  options.coverage = kwargs.get("coverage", False)
326
295
  options.track_progress = kwargs.get("track_progress", False)
327
296
 
328
- # Speed options
329
297
  options.fast = kwargs.get("fast", False)
330
298
  options.comp = kwargs.get("comp", False)
331
299
 
@@ -339,11 +307,8 @@ async def _execute_single_iteration(
339
307
  iteration: int,
340
308
  context: t.Any,
341
309
  ) -> bool:
342
- """Execute a single workflow iteration."""
343
310
  try:
344
- # Check for orchestrator workflow methods
345
311
  if hasattr(orchestrator, "run_complete_workflow_async"):
346
- # AsyncWorkflowOrchestrator - method returns awaitable
347
312
  result = orchestrator.run_complete_workflow_async(options)
348
313
  if result is None:
349
314
  raise ValueError(
@@ -351,7 +316,6 @@ async def _execute_single_iteration(
351
316
  )
352
317
  return await result
353
318
  elif hasattr(orchestrator, "run_complete_workflow"):
354
- # Standard WorkflowOrchestrator - method is async and returns awaitable boolean
355
319
  result = orchestrator.run_complete_workflow(options)
356
320
  if result is None:
357
321
  raise ValueError(
@@ -366,14 +330,12 @@ async def _execute_single_iteration(
366
330
  )
367
331
  return await result
368
332
  elif hasattr(orchestrator, "run"):
369
- # Fallback for synchronous orchestrators
370
333
  return orchestrator.run(options)
371
334
  else:
372
335
  raise ValueError(
373
336
  f"Orchestrator {type(orchestrator)} has no recognized workflow execution method"
374
337
  )
375
338
  except Exception as e:
376
- # Add detailed error info for debugging
377
339
  raise RuntimeError(
378
340
  f"Error in _execute_single_iteration (iteration {iteration}): {e}"
379
341
  ) from e
@@ -385,7 +347,6 @@ def _create_success_result(
385
347
  context: t.Any,
386
348
  coverage_result: dict[str, t.Any] | None = None,
387
349
  ) -> dict[str, t.Any]:
388
- """Create success result with completion data."""
389
350
  result = {
390
351
  "job_id": job_id,
391
352
  "status": "completed",
@@ -402,7 +363,6 @@ def _create_success_result(
402
363
 
403
364
 
404
365
  async def _handle_iteration_retry(job_id: str, iteration: int, context: t.Any) -> None:
405
- """Handle retry logic between iterations."""
406
366
  _update_progress(
407
367
  job_id,
408
368
  {
@@ -414,14 +374,12 @@ async def _handle_iteration_retry(job_id: str, iteration: int, context: t.Any) -
414
374
  context,
415
375
  )
416
376
 
417
- # Brief pause between iterations
418
377
  await asyncio.sleep(1)
419
378
 
420
379
 
421
380
  async def _handle_iteration_error(
422
381
  job_id: str, iteration: int, error: Exception, context: t.Any
423
382
  ) -> dict[str, t.Any]:
424
- """Handle errors during iteration execution."""
425
383
  _update_progress(
426
384
  job_id,
427
385
  {
@@ -445,7 +403,6 @@ async def _handle_iteration_error(
445
403
  async def _attempt_coverage_improvement(
446
404
  job_id: str, orchestrator: t.Any, context: t.Any
447
405
  ) -> dict[str, t.Any]:
448
- """Attempt proactive coverage improvement after successful workflow execution."""
449
406
  try:
450
407
  _update_progress(
451
408
  job_id,
@@ -457,23 +414,19 @@ async def _attempt_coverage_improvement(
457
414
  context,
458
415
  )
459
416
 
460
- # Get project path from orchestrator
461
417
  project_path = getattr(orchestrator, "pkg_path", None)
462
418
  if not project_path:
463
419
  return {"status": "skipped", "reason": "No project path available"}
464
420
 
465
- # Import coverage improvement orchestrator
466
421
  from crackerjack.orchestration.coverage_improvement import (
467
422
  create_coverage_improvement_orchestrator,
468
423
  )
469
424
 
470
- # Create coverage orchestrator
471
425
  coverage_orchestrator = await create_coverage_improvement_orchestrator(
472
426
  project_path,
473
427
  console=getattr(orchestrator, "console", None),
474
428
  )
475
429
 
476
- # Check if improvement is needed
477
430
  should_improve = await coverage_orchestrator.should_improve_coverage()
478
431
  if not should_improve:
479
432
  _update_progress(
@@ -481,18 +434,16 @@ async def _attempt_coverage_improvement(
481
434
  {
482
435
  "type": "coverage_improvement",
483
436
  "status": "skipped",
484
- "message": "Coverage improvement not needed (already at 100%)",
437
+ "message": "Coverage improvement not needed (already at 100 %)",
485
438
  },
486
439
  context,
487
440
  )
488
- return {"status": "skipped", "reason": "Coverage at 100%"}
441
+ return {"status": "skipped", "reason": "Coverage at 100 %"}
489
442
 
490
- # Create agent context (simplified)
491
443
  from crackerjack.agents.base import AgentContext
492
444
 
493
445
  agent_context = AgentContext(project_path=project_path, console=None)
494
446
 
495
- # Execute coverage improvement
496
447
  _update_progress(
497
448
  job_id,
498
449
  {
@@ -507,7 +458,6 @@ async def _attempt_coverage_improvement(
507
458
  agent_context
508
459
  )
509
460
 
510
- # Update progress with results
511
461
  if improvement_result["status"] == "completed":
512
462
  _update_progress(
513
463
  job_id,
@@ -556,7 +506,6 @@ async def _attempt_coverage_improvement(
556
506
  def _create_failure_result(
557
507
  job_id: str, max_iterations: int, context: t.Any
558
508
  ) -> dict[str, t.Any]:
559
- """Create failure result when max iterations exceeded."""
560
509
  return {
561
510
  "job_id": job_id,
562
511
  "status": "failed",
@@ -564,3 +513,34 @@ def _create_failure_result(
564
513
  "timestamp": time.time(),
565
514
  "success": False,
566
515
  }
516
+
517
+
518
+ async def _ensure_websocket_server_running(job_id: str, context: t.Any) -> None:
519
+ """Ensure WebSocket server is running for progress tracking during crackerjack:run."""
520
+ try:
521
+ from crackerjack.mcp.progress_components import ServiceManager
522
+
523
+ # Initialize and start services if needed
524
+ service_manager = ServiceManager()
525
+ await service_manager.ensure_services_running()
526
+
527
+ _update_progress(
528
+ job_id,
529
+ {
530
+ "type": "service_check",
531
+ "status": "websocket_ready",
532
+ "message": "WebSocket server ensured running for progress tracking",
533
+ },
534
+ context,
535
+ )
536
+ except Exception as e:
537
+ # Don't fail the whole workflow if WebSocket server fails to start
538
+ _update_progress(
539
+ job_id,
540
+ {
541
+ "type": "service_check",
542
+ "status": "websocket_warning",
543
+ "message": f"WebSocket server auto-start failed: {e}. Progress tracking may be limited.",
544
+ },
545
+ context,
546
+ )
@@ -15,12 +15,10 @@ def create_websocket_app(job_manager: JobManager, progress_dir: Path) -> FastAPI
15
15
  version="1.0.0",
16
16
  )
17
17
 
18
- # Store job_manager in app state for startup/shutdown events
19
18
  app.state.job_manager = job_manager
20
19
 
21
20
  @app.on_event("startup")
22
21
  async def startup_event() -> None:
23
- """Start background tasks."""
24
22
  if job_manager:
25
23
  asyncio.create_task(job_manager.monitor_progress_files())
26
24
  asyncio.create_task(job_manager.cleanup_old_jobs())
@@ -28,7 +26,6 @@ def create_websocket_app(job_manager: JobManager, progress_dir: Path) -> FastAPI
28
26
 
29
27
  @app.on_event("shutdown")
30
28
  async def shutdown_event() -> None:
31
- """Cleanup on shutdown."""
32
29
  if job_manager:
33
30
  job_manager.cleanup()
34
31