crackerjack 0.33.0__py3-none-any.whl → 0.33.2__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 +618 -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.2.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.2.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.2.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/licenses/LICENSE +0 -0
@@ -19,7 +19,7 @@ def register_utility_tools(mcp_app: t.Any) -> None:
19
19
 
20
20
  def _clean_file_if_old(
21
21
  file_path: Path, cutoff_time: float, dry_run: bool, file_type: str
22
- ) -> dict | None:
22
+ ) -> dict[str, t.Any] | None:
23
23
  with suppress(OSError):
24
24
  if file_path.stat().st_mtime < cutoff_time:
25
25
  file_size = file_path.stat().st_size
@@ -29,7 +29,9 @@ def _clean_file_if_old(
29
29
  return None
30
30
 
31
31
 
32
- def _clean_temp_files(cutoff_time: float, dry_run: bool) -> tuple[list[dict], int]:
32
+ def _clean_temp_files(
33
+ cutoff_time: float, dry_run: bool
34
+ ) -> tuple[list[dict[str, t.Any]], int]:
33
35
  import tempfile
34
36
 
35
37
  cleaned_files = []
@@ -49,7 +51,7 @@ def _clean_temp_files(cutoff_time: float, dry_run: bool) -> tuple[list[dict], in
49
51
 
50
52
  def _clean_progress_files(
51
53
  context: t.Any, cutoff_time: float, dry_run: bool
52
- ) -> tuple[list[dict], int]:
54
+ ) -> tuple[list[dict[str, t.Any]], int]:
53
55
  cleaned_files = []
54
56
  total_size = 0
55
57
 
@@ -65,9 +67,9 @@ def _clean_progress_files(
65
67
  return cleaned_files, total_size
66
68
 
67
69
 
68
- def _parse_cleanup_options(kwargs: str) -> tuple[dict, str | None]:
70
+ def _parse_cleanup_options(kwargs: str) -> tuple[dict[str, t.Any], str | None]:
69
71
  try:
70
- extra_kwargs = json.loads(kwargs) if kwargs.strip() else {}
72
+ extra_kwargs: dict[str, t.Any] = json.loads(kwargs) if kwargs.strip() else {}
71
73
  return extra_kwargs, None
72
74
  except json.JSONDecodeError as e:
73
75
  return {}, f"Invalid JSON in kwargs: {e}"
@@ -91,7 +93,7 @@ def _register_clean_tool(mcp_app: t.Any) -> None:
91
93
  return _create_error_response(f"Cleanup failed: {e}")
92
94
 
93
95
 
94
- def _parse_clean_configuration(args: str, kwargs: str) -> dict:
96
+ def _parse_clean_configuration(args: str, kwargs: str) -> dict[str, t.Any]:
95
97
  extra_kwargs, parse_error = _parse_cleanup_options(kwargs)
96
98
  if parse_error:
97
99
  return {"error": parse_error}
@@ -103,7 +105,9 @@ def _parse_clean_configuration(args: str, kwargs: str) -> dict:
103
105
  }
104
106
 
105
107
 
106
- def _execute_cleanup_operations(context: t.Any, clean_config: dict) -> dict:
108
+ def _execute_cleanup_operations(
109
+ context: t.Any, clean_config: dict[str, t.Any]
110
+ ) -> dict[str, t.Any]:
107
111
  from datetime import datetime, timedelta
108
112
 
109
113
  cutoff_time = (
@@ -130,7 +134,9 @@ def _execute_cleanup_operations(context: t.Any, clean_config: dict) -> dict:
130
134
  return {"all_cleaned_files": all_cleaned_files, "total_size": total_size}
131
135
 
132
136
 
133
- def _create_cleanup_response(clean_config: dict, cleanup_results: dict) -> str:
137
+ def _create_cleanup_response(
138
+ clean_config: dict[str, t.Any], cleanup_results: dict[str, t.Any]
139
+ ) -> str:
134
140
  all_cleaned_files = cleanup_results["all_cleaned_files"]
135
141
 
136
142
  return json.dumps(
@@ -208,16 +214,16 @@ def _register_config_tool(mcp_app: t.Any) -> None:
208
214
  if parse_error:
209
215
  return _create_error_response(parse_error)
210
216
 
211
- args_parts = args.strip().split() if args.strip() else ["list"]
217
+ args_parts = args.strip().split() if args.strip() else ["list[t.Any]"]
212
218
  action = args_parts[0].lower()
213
219
 
214
220
  try:
215
- if action == "list":
221
+ if action == "list[t.Any]":
216
222
  config_info = _handle_config_list(context)
217
223
  result = {
218
224
  "success": True,
219
225
  "command": "config_crackerjack",
220
- "action": "list",
226
+ "action": "list[t.Any]",
221
227
  "configuration": config_info,
222
228
  }
223
229
  elif action == "get" and len(args_parts) > 1:
@@ -226,7 +232,7 @@ def _register_config_tool(mcp_app: t.Any) -> None:
226
232
  result = _handle_config_validate(context)
227
233
  else:
228
234
  return _create_error_response(
229
- f"Invalid action '{action}'. Valid actions: list, get < key >, validate"
235
+ f"Invalid action '{action}'. Valid actions: list[t.Any], get < key >, validate"
230
236
  )
231
237
 
232
238
  return json.dumps(result, indent=2)
@@ -235,7 +241,7 @@ def _register_config_tool(mcp_app: t.Any) -> None:
235
241
  return _create_error_response(f"Config operation failed: {e}")
236
242
 
237
243
 
238
- def _run_hooks_analysis(orchestrator: t.Any, options: t.Any) -> dict:
244
+ def _run_hooks_analysis(orchestrator: t.Any, options: t.Any) -> dict[str, t.Any]:
239
245
  fast_result = orchestrator.run_fast_hooks_only(options)
240
246
  comprehensive_result = orchestrator.run_comprehensive_hooks_only(options)
241
247
 
@@ -245,7 +251,7 @@ def _run_hooks_analysis(orchestrator: t.Any, options: t.Any) -> dict:
245
251
  }
246
252
 
247
253
 
248
- def _run_tests_analysis(orchestrator: t.Any, options: t.Any) -> dict:
254
+ def _run_tests_analysis(orchestrator: t.Any, options: t.Any) -> dict[str, t.Any]:
249
255
  test_result = orchestrator.run_testing_phase(options)
250
256
  return {"status": "passed" if test_result else "failed"}
251
257
 
@@ -13,35 +13,65 @@ async def execute_crackerjack_workflow(
13
13
  ) -> dict[str, t.Any]:
14
14
  job_id = str(uuid.uuid4())[:8]
15
15
 
16
- # Configure extended timeout for long-running test operations
17
- execution_timeout = kwargs.get("execution_timeout", 900) # 15 minutes default
18
- if kwargs.get("test", False) or kwargs.get("testing", False):
19
- execution_timeout = max(execution_timeout, 1200) # 20 minutes for test runs
16
+ # Initialize progress immediately
17
+ await _update_progress(
18
+ job_id,
19
+ {"status": "started", "args": args, "timestamp": time.time()},
20
+ 0,
21
+ message="Crackerjack execution started",
22
+ )
23
+
24
+ # Start execution in background - no timeout!
25
+ context = get_context()
26
+ asyncio.create_task(_execute_crackerjack_background(job_id, args, kwargs, context))
27
+
28
+ # Return job_id immediately for progress monitoring
29
+ return {
30
+ "job_id": job_id,
31
+ "status": "running",
32
+ "message": "Execution started. Use get_job_progress(job_id) to monitor progress.",
33
+ "timestamp": time.time(),
34
+ }
35
+
20
36
 
37
+ async def _execute_crackerjack_background(
38
+ job_id: str,
39
+ args: str,
40
+ kwargs: dict[str, t.Any],
41
+ context: t.Any,
42
+ ) -> None:
43
+ """Execute crackerjack workflow in background with progress updates."""
21
44
  try:
22
- # Add overall execution timeout with keep-alive
23
- return await asyncio.wait_for(
24
- _execute_crackerjack_sync(job_id, args, kwargs, get_context()),
25
- timeout=execution_timeout,
45
+ result = await _execute_crackerjack_sync(job_id, args, kwargs, context)
46
+
47
+ # Update final progress with result
48
+ await _update_progress(
49
+ job_id,
50
+ {
51
+ "status": result.get("status", "completed"),
52
+ "result": result,
53
+ "timestamp": time.time(),
54
+ "final": True,
55
+ },
56
+ 100,
57
+ message=f"Execution {result.get('status', 'completed')}",
26
58
  )
27
- except TimeoutError:
28
- return {
29
- "job_id": job_id,
30
- "status": "timeout",
31
- "error": f"Execution timed out after {execution_timeout} seconds",
32
- "timestamp": time.time(),
33
- }
34
59
  except Exception as e:
35
60
  import traceback
36
61
 
37
- error_details = traceback.format_exc()
38
- return {
39
- "job_id": job_id,
40
- "status": "failed",
41
- "error": f"Execution failed: {e}",
42
- "traceback": error_details,
43
- "timestamp": time.time(),
44
- }
62
+ # Update progress with error
63
+ await _update_progress(
64
+ job_id,
65
+ {
66
+ "status": "failed",
67
+ "error": str(e),
68
+ "traceback": traceback.format_exc(),
69
+ "timestamp": time.time(),
70
+ "final": True,
71
+ },
72
+ -1,
73
+ message=f"Execution failed: {e}",
74
+ )
45
75
 
46
76
 
47
77
  async def _execute_crackerjack_sync(
@@ -81,7 +111,6 @@ async def _initialize_execution(
81
111
  context,
82
112
  )
83
113
 
84
- # Ensure WebSocket server is running for progress tracking
85
114
  await _ensure_websocket_server_running(job_id, context)
86
115
 
87
116
  working_dir = kwargs.get("working_directory", ".")
@@ -228,9 +257,8 @@ async def _run_workflow_iterations(
228
257
  context: t.Any,
229
258
  ) -> dict[str, t.Any]:
230
259
  options = _create_workflow_options(kwargs)
231
- max_iterations = kwargs.get("max_iterations", 10)
260
+ max_iterations = kwargs.get("max_iterations", 5)
232
261
 
233
- # Start keep-alive task to prevent TCP timeouts
234
262
  keep_alive_task = asyncio.create_task(_keep_alive_heartbeat(job_id, context))
235
263
 
236
264
  try:
@@ -250,7 +278,6 @@ async def _execute_iterations_loop(
250
278
  max_iterations: int,
251
279
  context: t.Any,
252
280
  ) -> dict[str, t.Any]:
253
- """Execute the main iterations loop."""
254
281
  for iteration in range(max_iterations):
255
282
  _update_iteration_progress(job_id, iteration, max_iterations, context)
256
283
 
@@ -276,7 +303,6 @@ async def _execute_iterations_loop(
276
303
  def _update_iteration_progress(
277
304
  job_id: str, iteration: int, max_iterations: int, context: t.Any
278
305
  ) -> None:
279
- """Update progress for current iteration."""
280
306
  _update_progress(
281
307
  job_id,
282
308
  {
@@ -296,7 +322,6 @@ async def _handle_iteration_success(
296
322
  kwargs: dict[str, t.Any],
297
323
  context: t.Any,
298
324
  ) -> dict[str, t.Any]:
299
- """Handle successful iteration."""
300
325
  coverage_result = None
301
326
  if kwargs.get("boost_coverage", False):
302
327
  coverage_result = await _attempt_coverage_improvement(
@@ -306,7 +331,6 @@ async def _handle_iteration_success(
306
331
 
307
332
 
308
333
  async def _cleanup_keep_alive_task(keep_alive_task: asyncio.Task[t.Any]) -> None:
309
- """Clean up the keep-alive task."""
310
334
  if not keep_alive_task.cancelled():
311
335
  keep_alive_task.cancel()
312
336
  try:
@@ -316,10 +340,8 @@ async def _cleanup_keep_alive_task(keep_alive_task: asyncio.Task[t.Any]) -> None
316
340
 
317
341
 
318
342
  async def _keep_alive_heartbeat(job_id: str, context: t.Any) -> None:
319
- """Send periodic keep-alive messages to prevent TCP timeouts."""
320
343
  try:
321
344
  while True:
322
- # Send heartbeat every 60 seconds (well under 2-minute TCP timeout)
323
345
  await asyncio.sleep(60)
324
346
  _update_progress(
325
347
  job_id,
@@ -332,7 +354,6 @@ async def _keep_alive_heartbeat(job_id: str, context: t.Any) -> None:
332
354
  context,
333
355
  )
334
356
  except asyncio.CancelledError:
335
- # Task was cancelled, cleanup
336
357
  _update_progress(
337
358
  job_id,
338
359
  {
@@ -404,23 +425,27 @@ async def _execute_single_iteration(
404
425
  raise ValueError(
405
426
  "Method run_complete_workflow_async returned None instead of awaitable"
406
427
  )
407
- return await result
428
+ workflow_result: bool = await result
429
+ return workflow_result
408
430
  elif hasattr(orchestrator, "run_complete_workflow"):
409
431
  result = orchestrator.run_complete_workflow(options)
410
432
  if result is None:
411
433
  raise ValueError(
412
434
  "Method run_complete_workflow returned None instead of awaitable"
413
435
  )
414
- return await result
436
+ workflow_result: bool = await result
437
+ return workflow_result
415
438
  elif hasattr(orchestrator, "execute_workflow"):
416
439
  result = orchestrator.execute_workflow(options)
417
440
  if result is None:
418
441
  raise ValueError(
419
442
  "Method execute_workflow returned None instead of awaitable"
420
443
  )
421
- return await result
444
+ workflow_result: bool = await result
445
+ return workflow_result
422
446
  elif hasattr(orchestrator, "run"):
423
- return orchestrator.run(options)
447
+ run_result: bool = orchestrator.run(options)
448
+ return run_result
424
449
  else:
425
450
  raise ValueError(
426
451
  f"Orchestrator {type(orchestrator)} has no recognized workflow execution method"
@@ -606,11 +631,9 @@ def _create_failure_result(
606
631
 
607
632
 
608
633
  async def _ensure_websocket_server_running(job_id: str, context: t.Any) -> None:
609
- """Ensure WebSocket server is running for progress tracking during crackerjack:run."""
610
634
  try:
611
635
  from crackerjack.mcp.progress_components import ServiceManager
612
636
 
613
- # Initialize and start services if needed
614
637
  service_manager = ServiceManager()
615
638
  await service_manager.ensure_services_running()
616
639
 
@@ -624,7 +647,6 @@ async def _ensure_websocket_server_running(job_id: str, context: t.Any) -> None:
624
647
  context,
625
648
  )
626
649
  except Exception as e:
627
- # Don't fail the whole workflow if WebSocket server fails to start
628
650
  _update_progress(
629
651
  job_id,
630
652
  {
@@ -5,6 +5,10 @@ from fastapi import FastAPI
5
5
 
6
6
  from .endpoints import register_endpoints
7
7
  from .jobs import JobManager
8
+ from .monitoring_endpoints import (
9
+ MonitoringWebSocketManager,
10
+ create_monitoring_endpoints,
11
+ )
8
12
  from .websocket_handler import register_websocket_routes
9
13
 
10
14
 
@@ -33,4 +37,8 @@ def create_websocket_app(job_manager: JobManager, progress_dir: Path) -> FastAPI
33
37
 
34
38
  register_websocket_routes(app, job_manager, progress_dir)
35
39
 
40
+ # Register monitoring endpoints
41
+ monitoring_ws_manager = MonitoringWebSocketManager()
42
+ create_monitoring_endpoints(app, job_manager, progress_dir, monitoring_ws_manager)
43
+
36
44
  return app