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

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

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (200) 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 +64 -6
  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 +257 -218
  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 +558 -240
  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 +66 -13
  74. crackerjack/managers/test_command_builder.py +5 -17
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +109 -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 +161 -32
  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 +174 -33
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +15 -12
  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 +3 -0
  109. crackerjack/mixins/error_handling.py +145 -0
  110. crackerjack/models/config.py +21 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +176 -107
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/models/task.py +3 -0
  115. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  116. crackerjack/monitoring/metrics_collector.py +426 -0
  117. crackerjack/monitoring/regression_prevention.py +8 -8
  118. crackerjack/monitoring/websocket_server.py +643 -0
  119. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  120. crackerjack/orchestration/coverage_improvement.py +3 -3
  121. crackerjack/orchestration/execution_strategies.py +26 -6
  122. crackerjack/orchestration/test_progress_streamer.py +8 -5
  123. crackerjack/plugins/base.py +2 -2
  124. crackerjack/plugins/hooks.py +7 -0
  125. crackerjack/plugins/managers.py +11 -8
  126. crackerjack/security/__init__.py +0 -1
  127. crackerjack/security/audit.py +90 -105
  128. crackerjack/services/anomaly_detector.py +392 -0
  129. crackerjack/services/api_extractor.py +615 -0
  130. crackerjack/services/backup_service.py +2 -2
  131. crackerjack/services/bounded_status_operations.py +15 -152
  132. crackerjack/services/cache.py +127 -1
  133. crackerjack/services/changelog_automation.py +395 -0
  134. crackerjack/services/config.py +18 -11
  135. crackerjack/services/config_merge.py +30 -85
  136. crackerjack/services/config_template.py +506 -0
  137. crackerjack/services/contextual_ai_assistant.py +48 -22
  138. crackerjack/services/coverage_badge_service.py +171 -0
  139. crackerjack/services/coverage_ratchet.py +41 -17
  140. crackerjack/services/debug.py +3 -3
  141. crackerjack/services/dependency_analyzer.py +460 -0
  142. crackerjack/services/dependency_monitor.py +14 -11
  143. crackerjack/services/documentation_generator.py +491 -0
  144. crackerjack/services/documentation_service.py +675 -0
  145. crackerjack/services/enhanced_filesystem.py +6 -5
  146. crackerjack/services/enterprise_optimizer.py +865 -0
  147. crackerjack/services/error_pattern_analyzer.py +676 -0
  148. crackerjack/services/file_hasher.py +1 -1
  149. crackerjack/services/git.py +41 -45
  150. crackerjack/services/health_metrics.py +10 -8
  151. crackerjack/services/heatmap_generator.py +735 -0
  152. crackerjack/services/initialization.py +30 -33
  153. crackerjack/services/input_validator.py +5 -97
  154. crackerjack/services/intelligent_commit.py +327 -0
  155. crackerjack/services/log_manager.py +15 -12
  156. crackerjack/services/logging.py +4 -3
  157. crackerjack/services/lsp_client.py +628 -0
  158. crackerjack/services/memory_optimizer.py +409 -0
  159. crackerjack/services/metrics.py +42 -33
  160. crackerjack/services/parallel_executor.py +416 -0
  161. crackerjack/services/pattern_cache.py +1 -1
  162. crackerjack/services/pattern_detector.py +6 -6
  163. crackerjack/services/performance_benchmarks.py +250 -576
  164. crackerjack/services/performance_cache.py +382 -0
  165. crackerjack/services/performance_monitor.py +565 -0
  166. crackerjack/services/predictive_analytics.py +510 -0
  167. crackerjack/services/quality_baseline.py +234 -0
  168. crackerjack/services/quality_baseline_enhanced.py +646 -0
  169. crackerjack/services/quality_intelligence.py +785 -0
  170. crackerjack/services/regex_patterns.py +605 -524
  171. crackerjack/services/regex_utils.py +43 -123
  172. crackerjack/services/secure_path_utils.py +5 -164
  173. crackerjack/services/secure_status_formatter.py +30 -141
  174. crackerjack/services/secure_subprocess.py +11 -92
  175. crackerjack/services/security.py +61 -30
  176. crackerjack/services/security_logger.py +18 -22
  177. crackerjack/services/server_manager.py +124 -16
  178. crackerjack/services/status_authentication.py +16 -159
  179. crackerjack/services/status_security_manager.py +4 -131
  180. crackerjack/services/terminal_utils.py +0 -0
  181. crackerjack/services/thread_safe_status_collector.py +19 -125
  182. crackerjack/services/unified_config.py +21 -13
  183. crackerjack/services/validation_rate_limiter.py +5 -54
  184. crackerjack/services/version_analyzer.py +459 -0
  185. crackerjack/services/version_checker.py +1 -1
  186. crackerjack/services/websocket_resource_limiter.py +10 -144
  187. crackerjack/services/zuban_lsp_service.py +390 -0
  188. crackerjack/slash_commands/__init__.py +2 -7
  189. crackerjack/slash_commands/run.md +2 -2
  190. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  191. crackerjack/tools/validate_regex_patterns.py +19 -48
  192. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +197 -26
  193. crackerjack-0.33.1.dist-info/RECORD +229 -0
  194. crackerjack/CLAUDE.md +0 -207
  195. crackerjack/RULES.md +0 -380
  196. crackerjack/py313.py +0 -234
  197. crackerjack-0.32.0.dist-info/RECORD +0 -180
  198. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
  199. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
  200. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,9 @@
1
1
  import typing as t
2
2
 
3
3
 
4
- def _create_architectural_assessment(args: str, parsed_kwargs: dict) -> dict:
4
+ def _create_architectural_assessment(
5
+ args: str, parsed_kwargs: dict[str, t.Any]
6
+ ) -> dict[str, t.Any]:
5
7
  assessment = {
6
8
  "feature": parsed_kwargs.get("feature", "unknown"),
7
9
  "complexity": parsed_kwargs.get("complexity", "medium"),
@@ -56,7 +58,7 @@ def _create_architectural_assessment(args: str, parsed_kwargs: dict) -> dict:
56
58
  return assessment
57
59
 
58
60
 
59
- def _create_validation_results(file_path: str) -> dict:
61
+ def _create_validation_results(file_path: str) -> dict[str, t.Any]:
60
62
  validation = {
61
63
  "file_path": file_path,
62
64
  "validation_results": [],
@@ -95,7 +97,10 @@ def _create_validation_results(file_path: str) -> dict:
95
97
  },
96
98
  ]
97
99
 
98
- validation["validation_results"] = compliance_checks
100
+ validation["validation_results"] = [
101
+ f"{check['check']}: {check['status']} - {check['message']}"
102
+ for check in compliance_checks
103
+ ]
99
104
 
100
105
  validation["recommendations"] = [
101
106
  "Run full crackerjack quality process: python - m crackerjack-t",
@@ -113,7 +118,7 @@ def _create_validation_results(file_path: str) -> dict:
113
118
  return validation
114
119
 
115
120
 
116
- def _create_pattern_suggestions(problem_context: str) -> dict:
121
+ def _create_pattern_suggestions(problem_context: str) -> dict[str, t.Any]:
117
122
  pattern_suggestions = {
118
123
  "context": problem_context,
119
124
  "recommended_patterns": [],
@@ -168,7 +173,9 @@ def _create_pattern_suggestions(problem_context: str) -> dict:
168
173
  return pattern_suggestions
169
174
 
170
175
 
171
- def _add_complexity_patterns(pattern_suggestions: dict, problem_context: str) -> None:
176
+ def _add_complexity_patterns(
177
+ pattern_suggestions: dict[str, t.Any], problem_context: str
178
+ ) -> None:
172
179
  if any(
173
180
  keyword in problem_context.lower()
174
181
  for keyword in ("complex", "refactor", "cleanup")
@@ -197,7 +204,9 @@ def _add_complexity_patterns(pattern_suggestions: dict, problem_context: str) ->
197
204
  )
198
205
 
199
206
 
200
- def _add_dry_patterns(pattern_suggestions: dict, problem_context: str) -> None:
207
+ def _add_dry_patterns(
208
+ pattern_suggestions: dict[str, t.Any], problem_context: str
209
+ ) -> None:
201
210
  if any(
202
211
  keyword in problem_context.lower() for keyword in ("duplicate", "repeat", "dry")
203
212
  ):
@@ -225,7 +234,9 @@ def _add_dry_patterns(pattern_suggestions: dict, problem_context: str) -> None:
225
234
  )
226
235
 
227
236
 
228
- def _add_performance_patterns(pattern_suggestions: dict, problem_context: str) -> None:
237
+ def _add_performance_patterns(
238
+ pattern_suggestions: dict[str, t.Any], problem_context: str
239
+ ) -> None:
229
240
  if any(
230
241
  keyword in problem_context.lower()
231
242
  for keyword in ("slow", "performance", "optimize")
@@ -234,7 +245,7 @@ def _add_performance_patterns(pattern_suggestions: dict, problem_context: str) -
234
245
  [
235
246
  {
236
247
  "pattern": "list_comprehension",
237
- "description": "Use list comprehensions instead of manual loops",
248
+ "description": "Use list[t.Any] comprehensions instead of manual loops",
238
249
  "benefits": [
239
250
  "Better performance",
240
251
  "More readable",
@@ -254,7 +265,9 @@ def _add_performance_patterns(pattern_suggestions: dict, problem_context: str) -
254
265
  )
255
266
 
256
267
 
257
- def _add_security_patterns(pattern_suggestions: dict, problem_context: str) -> None:
268
+ def _add_security_patterns(
269
+ pattern_suggestions: dict[str, t.Any], problem_context: str
270
+ ) -> None:
258
271
  if any(
259
272
  keyword in problem_context.lower() for keyword in ("security", "safe", "secure")
260
273
  ):
@@ -314,7 +327,7 @@ def _register_plan_development_tool(mcp_app: t.Any) -> None:
314
327
  import json
315
328
 
316
329
  try:
317
- parsed_kwargs = json.loads(kwargs) if kwargs else {}
330
+ parsed_kwargs: dict[str, t.Any] = json.loads(kwargs) if kwargs else {}
318
331
  assessment = _create_architectural_assessment(args, parsed_kwargs)
319
332
  return json.dumps(assessment, indent=2)
320
333
  except Exception as e:
@@ -327,7 +340,7 @@ def _register_validate_architecture_tool(mcp_app: t.Any) -> None:
327
340
  import json
328
341
 
329
342
  try:
330
- parsed_kwargs = json.loads(kwargs) if kwargs else {}
343
+ parsed_kwargs: dict[str, t.Any] = json.loads(kwargs) if kwargs else {}
331
344
  file_path = args or parsed_kwargs.get("file_path", "")
332
345
  validation = _create_validation_results(file_path)
333
346
  return json.dumps(validation, indent=2)
@@ -10,23 +10,20 @@ from crackerjack.services.input_validator import get_input_validator
10
10
  def _create_progress_file(job_id: str) -> Path:
11
11
  import tempfile
12
12
 
13
- # Use secure input validation
14
13
  job_id_result = get_input_validator().validate_job_id(job_id)
15
14
 
16
15
  if not job_id_result.valid:
17
16
  msg = f"Invalid job_id: {job_id_result.error_message}"
18
17
  raise ValueError(msg)
19
18
 
20
- # Use sanitized job ID
21
19
  sanitized_job_id = job_id_result.sanitized_value
22
20
 
23
21
  context = get_context()
24
22
  if context:
25
23
  return context.progress_dir / f"job-{sanitized_job_id}.json"
26
24
 
27
- # Create secure temporary directory
28
25
  progress_dir = Path(tempfile.gettempdir()) / "crackerjack-mcp-progress"
29
- progress_dir.mkdir(exist_ok=True, mode=0o750) # Restrictive permissions
26
+ progress_dir.mkdir(exist_ok=True, mode=0o750)
30
27
  return progress_dir / f"job-{sanitized_job_id}.json"
31
28
 
32
29
 
@@ -99,10 +96,10 @@ def _notify_websocket(final_progress_data: dict[str, t.Any]) -> None:
99
96
 
100
97
  def _update_progress(
101
98
  job_id: str,
102
- progress_data: dict[str, t.Any] | str = None,
99
+ progress_data: dict[str, t.Any] | str | None = None,
103
100
  context: t.Any = None,
104
101
  iteration: int = 1,
105
- max_iterations: int = 10,
102
+ max_iterations: int = 5,
106
103
  overall_progress: int = 0,
107
104
  current_stage: str = "initialization",
108
105
  stage_progress: int = 0,
@@ -167,10 +164,10 @@ def _handle_get_job_progress(job_id: str) -> str:
167
164
 
168
165
 
169
166
  async def _execute_session_action(
170
- state_manager,
167
+ state_manager: t.Any,
171
168
  action: str,
172
169
  checkpoint_name: str | None,
173
- context,
170
+ context: t.Any,
174
171
  ) -> str:
175
172
  if action == "start":
176
173
  state_manager.start_session()
@@ -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