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
crackerjack/mcp/state.py CHANGED
@@ -92,7 +92,9 @@ class SessionState:
92
92
 
93
93
 
94
94
  class StateManager:
95
- def __init__(self, state_dir: Path | None = None, batched_saver=None) -> None:
95
+ def __init__(
96
+ self, state_dir: Path | None = None, batched_saver: t.Any | None = None
97
+ ) -> None:
96
98
  self._lock = asyncio.Lock()
97
99
  self.state_dir = state_dir or Path.home() / ".cache" / "crackerjack-mcp"
98
100
  self.state_dir.mkdir(exist_ok=True)
@@ -258,7 +260,7 @@ class StateManager:
258
260
  priority_counts = {}
259
261
  for priority in Priority:
260
262
  priority_counts[priority.value] = len(self.get_issues_by_priority(priority))
261
- type_counts = {}
263
+ type_counts: dict[str, int] = {}
262
264
  for issue in issues:
263
265
  type_counts[issue.type] = type_counts.get(issue.type, 0) + 1
264
266
  stage_status = {}
@@ -351,14 +353,9 @@ class StateManager:
351
353
  return checkpoints
352
354
 
353
355
  def start_session(self) -> None:
354
- """Start or initialize a session."""
355
- # Session is already initialized in __init__, this is a no-op
356
- # but provided for API compatibility
357
356
  self._save_state()
358
357
 
359
358
  def complete_session(self) -> None:
360
- """Complete the current session."""
361
- # Mark session as complete in metadata
362
359
  if not self.session_state.metadata:
363
360
  self.session_state.metadata = {}
364
361
  self.session_state.metadata["status"] = "completed"
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
14
14
  @dataclass
15
15
  class TaskInfo:
16
16
  task_id: str
17
- task: asyncio.Task
17
+ task: asyncio.Task[t.Any]
18
18
  created_at: float
19
19
  description: str = ""
20
20
  timeout_seconds: float | None = None
@@ -25,7 +25,7 @@ class AsyncTaskManager:
25
25
  self.max_concurrent_tasks = max_concurrent_tasks
26
26
  self._tasks: dict[str, TaskInfo] = {}
27
27
  self._task_semaphore = asyncio.Semaphore(max_concurrent_tasks)
28
- self._cleanup_task: asyncio.Task | None = None
28
+ self._cleanup_task: asyncio.Task[t.Any] | None = None
29
29
  self._running = False
30
30
  self._lock = asyncio.Lock()
31
31
 
@@ -61,11 +61,11 @@ class AsyncTaskManager:
61
61
 
62
62
  async def create_task(
63
63
  self,
64
- coro: t.Coroutine,
64
+ coro: t.Coroutine[t.Any, t.Any, t.Any],
65
65
  task_id: str,
66
66
  description: str = "",
67
67
  timeout_seconds: float | None = None,
68
- ) -> asyncio.Task:
68
+ ) -> asyncio.Task[t.Any]:
69
69
  async with self._lock:
70
70
  if task_id in self._tasks:
71
71
  msg = f"Task {task_id} already exists"
@@ -100,7 +100,9 @@ class AsyncTaskManager:
100
100
  console.print(f"[blue]🚀 Task {task_id} created: {description}[ / blue]")
101
101
  return task
102
102
 
103
- async def _wrap_task(self, coro: t.Coroutine, task_id: str) -> t.Any:
103
+ async def _wrap_task(
104
+ self, coro: t.Coroutine[t.Any, t.Any, t.Any], task_id: str
105
+ ) -> t.Any:
104
106
  try:
105
107
  async with self._task_semaphore:
106
108
  result = await coro
@@ -185,11 +187,11 @@ class AsyncTaskManager:
185
187
  @asynccontextmanager
186
188
  async def managed_task(
187
189
  self,
188
- coro: t.Coroutine,
190
+ coro: t.Coroutine[t.Any, t.Any, t.Any],
189
191
  task_id: str,
190
192
  description: str = "",
191
193
  timeout_seconds: float | None = None,
192
- ):
194
+ ) -> t.AsyncGenerator[asyncio.Task[t.Any]]:
193
195
  task = await self.create_task(coro, task_id, description, timeout_seconds)
194
196
  try:
195
197
  yield task
@@ -201,7 +203,7 @@ class AsyncTaskManager:
201
203
 
202
204
  async def _cancel_all_tasks(self) -> None:
203
205
  async with self._lock:
204
- tasks_to_cancel = list(self._tasks.values())
206
+ tasks_to_cancel = list[t.Any](self._tasks.values())
205
207
 
206
208
  if not tasks_to_cancel:
207
209
  return
@@ -240,7 +242,7 @@ class AsyncTaskManager:
240
242
  async def _cleanup_completed_tasks(self) -> None:
241
243
  async with self._lock:
242
244
  completed_tasks = []
243
- for task_id, task_info in list(self._tasks.items()):
245
+ for task_id, task_info in list[t.Any](self._tasks.items()):
244
246
  if task_info.task.done():
245
247
  completed_tasks.append(task_id)
246
248
  del self._tasks[task_id]
@@ -1,10 +1,16 @@
1
1
  import typing as t
2
2
 
3
- from crackerjack.mcp.context import get_context
3
+ from crackerjack.mcp.context import MCPServerContext, get_context
4
+ from crackerjack.mcp.rate_limiter import RateLimitMiddleware
4
5
  from crackerjack.services.input_validator import (
6
+ SecureInputValidator,
5
7
  get_input_validator,
6
8
  )
7
9
 
10
+ if t.TYPE_CHECKING:
11
+ from crackerjack.core.workflow_orchestrator import WorkflowOrchestrator
12
+ from crackerjack.models.config import WorkflowOptions
13
+
8
14
 
9
15
  async def create_task_with_subagent(
10
16
  description: str,
@@ -12,10 +18,8 @@ async def create_task_with_subagent(
12
18
  subagent_type: str,
13
19
  ) -> dict[str, t.Any]:
14
20
  try:
15
- # Input validation with security checks
16
21
  validator = get_input_validator()
17
22
 
18
- # Validate description
19
23
  desc_result = validator.validate_command_args(description)
20
24
  if not desc_result.valid:
21
25
  return {
@@ -24,7 +28,6 @@ async def create_task_with_subagent(
24
28
  "validation_type": desc_result.validation_type,
25
29
  }
26
30
 
27
- # Validate prompt
28
31
  prompt_result = validator.validate_command_args(prompt)
29
32
  if not prompt_result.valid:
30
33
  return {
@@ -33,7 +36,6 @@ async def create_task_with_subagent(
33
36
  "validation_type": prompt_result.validation_type,
34
37
  }
35
38
 
36
- # Validate subagent_type (should be safe identifier)
37
39
  subagent_result = validator.sanitizer.sanitize_string(
38
40
  subagent_type, max_length=100, strict_alphanumeric=True
39
41
  )
@@ -44,7 +46,6 @@ async def create_task_with_subagent(
44
46
  "validation_type": subagent_result.validation_type,
45
47
  }
46
48
 
47
- # Use sanitized values
48
49
  sanitized_description = desc_result.sanitized_value or description
49
50
  sanitized_prompt = prompt_result.sanitized_value or prompt
50
51
  sanitized_subagent = subagent_result.sanitized_value
@@ -72,7 +73,9 @@ async def create_task_with_subagent(
72
73
  }
73
74
 
74
75
 
75
- async def _validate_stage_request(context, rate_limiter) -> str | None:
76
+ async def _validate_stage_request(
77
+ context: MCPServerContext | None, rate_limiter: RateLimitMiddleware | None
78
+ ) -> str | None:
76
79
  if not context:
77
80
  return '{"error": "Server context not available", "success": false}'
78
81
 
@@ -83,17 +86,15 @@ async def _validate_stage_request(context, rate_limiter) -> str | None:
83
86
  return None
84
87
 
85
88
 
86
- def _parse_stage_args(args: str, kwargs: str) -> tuple[str, dict] | str:
89
+ def _parse_stage_args(args: str, kwargs: str) -> tuple[str, dict[str, t.Any]] | str:
87
90
  try:
88
91
  validator = get_input_validator()
89
92
 
90
- # Validate stage argument
91
93
  stage_validation = _validate_stage_argument(validator, args)
92
94
  if isinstance(stage_validation, str):
93
95
  return stage_validation
94
96
  stage = stage_validation
95
97
 
96
- # Validate and parse kwargs
97
98
  kwargs_validation = _validate_kwargs_argument(validator, kwargs)
98
99
  if isinstance(kwargs_validation, str):
99
100
  return kwargs_validation
@@ -105,15 +106,14 @@ def _parse_stage_args(args: str, kwargs: str) -> tuple[str, dict] | str:
105
106
  return f'{{"error": "Stage argument parsing failed: {e}", "success": false}}'
106
107
 
107
108
 
108
- def _validate_stage_argument(validator, args: str) -> str:
109
- """Validate and sanitize the stage argument."""
109
+ def _validate_stage_argument(validator: SecureInputValidator, args: str) -> str:
110
110
  stage_result = validator.sanitizer.sanitize_string(
111
111
  args.strip(), max_length=50, strict_alphanumeric=True
112
112
  )
113
113
  if not stage_result.valid:
114
114
  return f'{{"error": "Invalid stage argument: {stage_result.error_message}", "success": false}}'
115
115
 
116
- stage = stage_result.sanitized_value.lower()
116
+ stage: str = str(stage_result.sanitized_value).lower()
117
117
  valid_stages = {"fast", "comprehensive", "tests", "cleaning", "init"}
118
118
 
119
119
  if stage not in valid_stages:
@@ -122,9 +122,10 @@ def _validate_stage_argument(validator, args: str) -> str:
122
122
  return stage
123
123
 
124
124
 
125
- def _validate_kwargs_argument(validator, kwargs: str) -> dict | str:
126
- """Validate and parse the kwargs argument."""
127
- extra_kwargs = {}
125
+ def _validate_kwargs_argument(
126
+ validator: SecureInputValidator, kwargs: str
127
+ ) -> dict[str, t.Any] | str:
128
+ extra_kwargs: dict[str, t.Any] = {}
128
129
  if not kwargs.strip():
129
130
  return extra_kwargs
130
131
 
@@ -134,7 +135,6 @@ def _validate_kwargs_argument(validator, kwargs: str) -> dict | str:
134
135
 
135
136
  extra_kwargs = kwargs_result.sanitized_value
136
137
 
137
- # Additional validation on JSON structure
138
138
  if not isinstance(extra_kwargs, dict):
139
139
  return f'{{"error": "kwargs must be a JSON object, got {type(extra_kwargs).__name__}", "success": false}}'
140
140
 
@@ -146,31 +146,171 @@ def _configure_stage_options(stage: str) -> "WorkflowOptions":
146
146
 
147
147
  options = WorkflowOptions()
148
148
  if stage in {"fast", "comprehensive"}:
149
- options.skip_hooks = False
149
+ options.hooks.skip_hooks = False
150
150
  elif stage == "tests":
151
151
  options.testing.test = True
152
152
  elif stage == "cleaning":
153
153
  options.cleaning.clean = True
154
154
  elif stage == "init":
155
- options.skip_hooks = True
155
+ options.hooks.skip_hooks = True
156
156
  return options
157
157
 
158
158
 
159
- def _execute_stage(orchestrator, stage: str, options) -> bool:
159
+ def _execute_stage(
160
+ orchestrator: "WorkflowOrchestrator", stage: str, options: "WorkflowOptions"
161
+ ) -> bool:
162
+ # Convert WorkflowOptions to OptionsProtocol
163
+ adapted_options = _adapt_workflow_options_to_protocol(options)
164
+
160
165
  if stage == "fast":
161
- return orchestrator.run_fast_hooks_only(options)
166
+ return orchestrator.run_fast_hooks_only(adapted_options)
162
167
  if stage == "comprehensive":
163
- return orchestrator.run_comprehensive_hooks_only(options)
168
+ return orchestrator.run_comprehensive_hooks_only(adapted_options)
164
169
  if stage == "tests":
165
- return orchestrator.run_testing_phase(options)
170
+ return orchestrator.run_testing_phase(adapted_options)
166
171
  if stage == "cleaning":
167
- return orchestrator.run_cleaning_phase(options)
168
- if stage == "init":
169
- return _execute_init_stage(orchestrator)
172
+ return orchestrator.run_cleaning_phase(adapted_options)
170
173
  return False
171
174
 
172
175
 
173
- def _execute_init_stage(orchestrator) -> bool:
176
+ def _adapt_workflow_options_to_protocol(options: "WorkflowOptions") -> t.Any:
177
+ """Adapt WorkflowOptions to match OptionsProtocol."""
178
+ return _AdaptedOptions(options) # type: ignore
179
+
180
+
181
+ class _AdaptedOptions:
182
+ """Adapter class to convert WorkflowOptions to OptionsProtocol."""
183
+
184
+ def __init__(self, opts: "WorkflowOptions"):
185
+ self.opts = opts
186
+
187
+ # Git properties
188
+ @property
189
+ def commit(self) -> bool:
190
+ return getattr(self.opts.git, "commit", False)
191
+
192
+ @property
193
+ def create_pr(self) -> bool:
194
+ return getattr(self.opts.git, "create_pr", False)
195
+
196
+ # Execution properties
197
+ @property
198
+ def interactive(self) -> bool:
199
+ return getattr(self.opts.execution, "interactive", False)
200
+
201
+ @property
202
+ def no_config_updates(self) -> bool:
203
+ return getattr(self.opts.execution, "no_config_updates", False)
204
+
205
+ @property
206
+ def verbose(self) -> bool:
207
+ return getattr(self.opts.execution, "verbose", False)
208
+
209
+ @property
210
+ def async_mode(self) -> bool:
211
+ return getattr(self.opts.execution, "async_mode", False)
212
+
213
+ # Testing properties
214
+ @property
215
+ def test(self) -> bool:
216
+ return getattr(self.opts.testing, "test", False)
217
+
218
+ @property
219
+ def benchmark(self) -> bool:
220
+ return getattr(self.opts.testing, "benchmark", False)
221
+
222
+ @property
223
+ def test_workers(self) -> int:
224
+ return getattr(self.opts.testing, "test_workers", 0)
225
+
226
+ @property
227
+ def test_timeout(self) -> int:
228
+ return getattr(self.opts.testing, "test_timeout", 0)
229
+
230
+ # Publishing properties
231
+ @property
232
+ def publish(self) -> t.Any | None:
233
+ return getattr(self.opts.publishing, "publish", None)
234
+
235
+ @property
236
+ def bump(self) -> t.Any | None:
237
+ return getattr(self.opts.publishing, "bump", None)
238
+
239
+ @property
240
+ def all(self) -> t.Any | None:
241
+ return getattr(self.opts.publishing, "all", None)
242
+
243
+ @property
244
+ def no_git_tags(self) -> bool:
245
+ return getattr(self.opts.publishing, "no_git_tags", False)
246
+
247
+ @property
248
+ def skip_version_check(self) -> bool:
249
+ return getattr(self.opts.publishing, "skip_version_check", False)
250
+
251
+ # AI properties
252
+ @property
253
+ def ai_agent(self) -> bool:
254
+ return getattr(self.opts.ai, "ai_agent", False)
255
+
256
+ @property
257
+ def start_mcp_server(self) -> bool:
258
+ return getattr(self.opts.ai, "start_mcp_server", False)
259
+
260
+ # Hook properties
261
+ @property
262
+ def skip_hooks(self) -> bool:
263
+ return getattr(self.opts.hooks, "skip_hooks", False)
264
+
265
+ @property
266
+ def update_precommit(self) -> bool:
267
+ return getattr(self.opts.hooks, "update_precommit", False)
268
+
269
+ @property
270
+ def experimental_hooks(self) -> bool:
271
+ return getattr(self.opts.hooks, "experimental_hooks", False)
272
+
273
+ @property
274
+ def enable_pyrefly(self) -> bool:
275
+ return getattr(self.opts.hooks, "enable_pyrefly", False)
276
+
277
+ @property
278
+ def enable_ty(self) -> bool:
279
+ return getattr(self.opts.hooks, "enable_ty", False)
280
+
281
+ # Cleaning properties
282
+ @property
283
+ def clean(self) -> bool:
284
+ return getattr(self.opts.cleaning, "clean", False)
285
+
286
+ # Progress properties
287
+ @property
288
+ def track_progress(self) -> bool:
289
+ return getattr(self.opts.progress, "track_progress", False)
290
+
291
+ # Default/static properties
292
+ @property
293
+ def cleanup(self) -> t.Any | None:
294
+ return None
295
+
296
+ @property
297
+ def cleanup_pypi(self) -> bool:
298
+ return False
299
+
300
+ @property
301
+ def coverage(self) -> bool:
302
+ return False
303
+
304
+ @property
305
+ def keep_releases(self) -> int:
306
+ return 10
307
+
308
+ @property
309
+ def fast(self) -> bool:
310
+ return False
311
+
312
+
313
+ def _execute_init_stage(orchestrator: "WorkflowOrchestrator") -> bool:
174
314
  try:
175
315
  from pathlib import Path
176
316
 
@@ -186,9 +326,10 @@ def _execute_init_stage(orchestrator) -> bool:
186
326
 
187
327
  init_service = InitializationService(console, filesystem, git_service, pkg_path)
188
328
 
189
- results = init_service.initialize_project(target_path=Path.cwd())
329
+ results = init_service.initialize_project_full(target_path=Path.cwd())
190
330
 
191
- return results.get("success", False)
331
+ success_result: bool = bool(results.get("success", False))
332
+ return success_result
192
333
 
193
334
  except Exception as e:
194
335
  if hasattr(orchestrator, "console"):
@@ -197,7 +338,7 @@ def _execute_init_stage(orchestrator) -> bool:
197
338
 
198
339
 
199
340
  def register_core_tools(mcp_app: t.Any) -> None:
200
- @mcp_app.tool()
341
+ @mcp_app.tool() # type: ignore[misc]
201
342
  async def run_crackerjack_stage(args: str, kwargs: str) -> str:
202
343
  context = get_context()
203
344
  rate_limiter = context.rate_limiter if context else None
@@ -258,8 +399,8 @@ def _detect_errors_and_suggestions(
258
399
  ) -> tuple[list[str], list[str]]:
259
400
  import re
260
401
 
261
- detected_errors = []
262
- suggestions = []
402
+ detected_errors: list[str] = []
403
+ suggestions: list[str] = []
263
404
 
264
405
  for error_type, pattern in _get_error_patterns():
265
406
  if re.search(pattern, text, re.IGNORECASE):
@@ -271,7 +412,7 @@ def _detect_errors_and_suggestions(
271
412
 
272
413
 
273
414
  def register_analyze_errors_tool(mcp_app: t.Any) -> None:
274
- @mcp_app.tool()
415
+ @mcp_app.tool() # type: ignore[misc]
275
416
  async def analyze_errors(output: str = "", include_suggestions: bool = True) -> str:
276
417
  context = get_context()
277
418
  if not context:
@@ -25,7 +25,8 @@ def _get_cached_patterns(context: t.Any, use_cache: bool) -> list[t.Any]:
25
25
  with suppress(Exception):
26
26
  cache = getattr(context, "cache", None)
27
27
  if cache and hasattr(cache, "get_error_patterns"):
28
- return cache.get_error_patterns()
28
+ patterns: list[t.Any] = cache.get_error_patterns()
29
+ return patterns
29
30
 
30
31
  return []
31
32
 
@@ -70,7 +71,7 @@ def _build_error_analysis(patterns: list[t.Any], context: t.Any) -> dict[str, t.
70
71
 
71
72
 
72
73
  def _categorize_error_patterns(patterns: list[t.Any]) -> dict[str, list[t.Any]]:
73
- categories = {
74
+ categories: dict[str, list[t.Any]] = {
74
75
  "syntax_errors": [],
75
76
  "import_errors": [],
76
77
  "type_errors": [],
@@ -15,7 +15,7 @@ def register_execution_tools(mcp_app: t.Any) -> None:
15
15
 
16
16
 
17
17
  def _register_execute_crackerjack_tool(mcp_app: t.Any) -> None:
18
- @mcp_app.tool()
18
+ @mcp_app.tool() # type: ignore[misc]
19
19
  async def execute_crackerjack(args: str, kwargs: str) -> str:
20
20
  context = get_context()
21
21
 
@@ -29,13 +29,11 @@ def _register_execute_crackerjack_tool(mcp_app: t.Any) -> None:
29
29
 
30
30
  extra_kwargs = kwargs_result["kwargs"]
31
31
 
32
- # Add extended timeout for long-running operations
33
32
  if "execution_timeout" not in extra_kwargs:
34
- # Default to 15 minutes, extend to 20 minutes for test operations
35
33
  if extra_kwargs.get("test", False) or extra_kwargs.get("testing", False):
36
- extra_kwargs["execution_timeout"] = 1200 # 20 minutes for tests
34
+ extra_kwargs["execution_timeout"] = 1200
37
35
  else:
38
- extra_kwargs["execution_timeout"] = 900 # 15 minutes default
36
+ extra_kwargs["execution_timeout"] = 900
39
37
 
40
38
  try:
41
39
  result = await execute_crackerjack_workflow(args, extra_kwargs)
@@ -53,7 +51,7 @@ def _register_execute_crackerjack_tool(mcp_app: t.Any) -> None:
53
51
 
54
52
 
55
53
  def _register_smart_error_analysis_tool(mcp_app: t.Any) -> None:
56
- @mcp_app.tool()
54
+ @mcp_app.tool() # type: ignore[misc]
57
55
  async def smart_error_analysis(use_cache: bool = True) -> str:
58
56
  context = get_context()
59
57
 
@@ -71,7 +69,7 @@ def _register_smart_error_analysis_tool(mcp_app: t.Any) -> None:
71
69
 
72
70
 
73
71
  def _register_init_crackerjack_tool(mcp_app: t.Any) -> None:
74
- @mcp_app.tool()
72
+ @mcp_app.tool() # type: ignore[misc]
75
73
  def init_crackerjack(args: str = "", kwargs: str = "{}") -> str:
76
74
  try:
77
75
  target_path, force, error = _parse_init_arguments(args, kwargs)
@@ -86,7 +84,7 @@ def _register_init_crackerjack_tool(mcp_app: t.Any) -> None:
86
84
 
87
85
 
88
86
  def _register_agent_suggestions_tool(mcp_app: t.Any) -> None:
89
- @mcp_app.tool()
87
+ @mcp_app.tool() # type: ignore[misc]
90
88
  def suggest_agents(
91
89
  task_description: str = "",
92
90
  project_type: str = "python",
@@ -139,7 +137,7 @@ def _parse_kwargs(kwargs: str) -> dict[str, t.Any]:
139
137
  def _parse_init_arguments(args: str, kwargs: str) -> tuple[t.Any, bool, str | None]:
140
138
  try:
141
139
  target_path = args.strip() or "."
142
- kwargs_dict = json.loads(kwargs) if kwargs.strip() else {}
140
+ kwargs_dict: dict[str, t.Any] = json.loads(kwargs) if kwargs.strip() else {}
143
141
  force = kwargs_dict.get("force") or False
144
142
 
145
143
  from pathlib import Path
@@ -161,9 +159,14 @@ def _execute_initialization(target_path: t.Any, force: bool) -> dict[str, t.Any]
161
159
 
162
160
  console = Console()
163
161
 
164
- return InitializationService(console, target_path).initialize_project(
165
- force_update=force
166
- )
162
+ from crackerjack.services.filesystem import FileSystemService
163
+ from crackerjack.services.git import GitService
164
+
165
+ filesystem = FileSystemService()
166
+ git_service = GitService(console)
167
+ return InitializationService(
168
+ console, filesystem, git_service, target_path
169
+ ).initialize_project_full(force=force)
167
170
 
168
171
 
169
172
  def _create_init_error_response(message: str) -> str: