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
@@ -7,6 +7,7 @@ from rich.console import Console
7
7
 
8
8
  from crackerjack.code_cleaner import CodeCleaner, PackageCleaningResult
9
9
  from crackerjack.core.autofix_coordinator import AutofixCoordinator
10
+ from crackerjack.mixins import ErrorHandlingMixin
10
11
  from crackerjack.models.protocols import (
11
12
  ConfigMergeServiceProtocol,
12
13
  FileSystemInterface,
@@ -16,12 +17,20 @@ from crackerjack.models.protocols import (
16
17
  PublishManager,
17
18
  TestManagerProtocol,
18
19
  )
19
- from crackerjack.services.config import ConfigurationService
20
+ from crackerjack.services.memory_optimizer import (
21
+ create_lazy_service,
22
+ get_memory_optimizer,
23
+ )
24
+ from crackerjack.services.parallel_executor import (
25
+ get_async_executor,
26
+ get_parallel_executor,
27
+ )
28
+ from crackerjack.services.performance_cache import get_filesystem_cache, get_git_cache
20
29
 
21
30
  from .session_coordinator import SessionCoordinator
22
31
 
23
32
 
24
- class PhaseCoordinator:
33
+ class PhaseCoordinator(ErrorHandlingMixin):
25
34
  def __init__(
26
35
  self,
27
36
  console: Console,
@@ -55,13 +64,26 @@ class PhaseCoordinator:
55
64
  security_logger=None,
56
65
  backup_service=None,
57
66
  )
67
+
68
+ from crackerjack.services.config import ConfigurationService
69
+
58
70
  self.config_service = ConfigurationService(console=console, pkg_path=pkg_path)
59
- self.autofix_coordinator = AutofixCoordinator(
60
- console=console, pkg_path=pkg_path
61
- )
62
71
 
63
72
  self.logger = logging.getLogger("crackerjack.phases")
64
73
 
74
+ self._memory_optimizer = get_memory_optimizer()
75
+ self._parallel_executor = get_parallel_executor()
76
+ self._async_executor = get_async_executor()
77
+ self._git_cache = get_git_cache()
78
+ self._filesystem_cache = get_filesystem_cache()
79
+
80
+ self._lazy_autofix = create_lazy_service(
81
+ lambda: AutofixCoordinator(console=console, pkg_path=pkg_path),
82
+ "autofix_coordinator",
83
+ )
84
+
85
+ super().__init__()
86
+
65
87
  def run_cleaning_phase(self, options: OptionsProtocol) -> bool:
66
88
  if not options.clean:
67
89
  return True
@@ -71,29 +93,54 @@ class PhaseCoordinator:
71
93
  self._display_cleaning_header()
72
94
  return self._execute_cleaning_process()
73
95
  except Exception as e:
74
- self.console.print(f"[red]❌[/ red] Cleaning failed: {e}")
96
+ self.handle_subprocess_error(e, [], "Code cleaning", critical=False)
75
97
  self.session.fail_task("cleaning", str(e))
76
98
  return False
77
99
 
78
100
  def _display_cleaning_header(self) -> None:
79
- self.console.print("\n" + "-" * 80)
101
+ self.console.print("\n" + "-" * 40)
80
102
  self.console.print(
81
103
  "[bold bright_magenta]🛠️ SETUP[/bold bright_magenta] [bold bright_white]Initializing project structure[/bold bright_white]",
82
104
  )
83
- self.console.print("-" * 80 + "\n")
105
+ self.console.print("-" * 40 + "\n")
84
106
  self.console.print("[yellow]🧹[/yellow] Starting code cleaning...")
85
107
 
108
+ def _display_version_bump_header(self, version_type: str) -> None:
109
+ self.console.print("\n" + "-" * 74)
110
+ self.console.print(
111
+ f"[bold bright_magenta]📦 BUMP VERSION[/bold bright_magenta] [bold bright_white]Updating package version ({version_type})[/bold bright_white]",
112
+ )
113
+ self.console.print("-" * 74 + "\n")
114
+
115
+ def _display_publish_header(self) -> None:
116
+ self.console.print("\n" + "-" * 74)
117
+ self.console.print(
118
+ "[bold bright_yellow]🚀 PUBLISH[/bold bright_yellow] [bold bright_white]Publishing to PyPI[/bold bright_white]",
119
+ )
120
+ self.console.print("-" * 74 + "\n")
121
+
122
+ def _display_git_staging_header(self) -> None:
123
+ self.console.print("\n" + "-" * 74)
124
+ self.console.print(
125
+ "[bold bright_cyan]🏷️ GIT OPERATIONS[/bold bright_cyan] [bold bright_white]Staging files and creating tags[/bold bright_white]",
126
+ )
127
+ self.console.print("-" * 74 + "\n")
128
+
129
+ def _display_commit_push_header(self) -> None:
130
+ self.console.print("\n" + "-" * 74)
131
+ self.console.print(
132
+ "[bold bright_green]📤 COMMIT & PUSH[/bold bright_green] [bold bright_white]Committing and pushing changes[/bold bright_white]",
133
+ )
134
+ self.console.print("-" * 74 + "\n")
135
+
86
136
  def _execute_cleaning_process(self) -> bool:
87
- # Use the comprehensive backup cleaning system for safety
88
137
  cleaning_result = self.code_cleaner.clean_files(self.pkg_path, use_backup=True)
89
138
 
90
139
  if isinstance(cleaning_result, list):
91
- # Legacy mode (should not happen with use_backup=True, but handle gracefully)
92
140
  cleaned_files = [str(r.file_path) for r in cleaning_result if r.success]
93
141
  self._report_cleaning_results(cleaned_files)
94
142
  return all(r.success for r in cleaning_result) if cleaning_result else True
95
143
  else:
96
- # PackageCleaningResult from backup mode
97
144
  self._report_package_cleaning_results(cleaning_result)
98
145
  return cleaning_result.overall_success
99
146
 
@@ -114,7 +161,6 @@ class PhaseCoordinator:
114
161
  self.session.complete_task("cleaning", "No cleaning needed")
115
162
 
116
163
  def _report_package_cleaning_results(self, result: PackageCleaningResult) -> None:
117
- """Report package cleaning results with backup information."""
118
164
  if result.overall_success:
119
165
  self.console.print(
120
166
  f"[green]✅[/ green] Package cleaning completed successfully! "
@@ -156,35 +202,18 @@ class PhaseCoordinator:
156
202
  self._complete_configuration_task(success)
157
203
  return success
158
204
  except Exception as e:
205
+ self.handle_subprocess_error(e, [], "Configuration phase", critical=False)
159
206
  self.session.fail_task("configuration", str(e))
160
207
  return False
161
208
 
162
209
  def _execute_configuration_steps(self, options: OptionsProtocol) -> bool:
163
- """Execute all configuration steps and return overall success."""
164
210
  success = True
165
211
 
166
- # FIRST STEP: Smart config merge before all other operations
167
- self._handle_smart_config_merge(options)
168
-
169
- # Handle crackerjack project specific configuration
170
- if self._is_crackerjack_project() and not self._copy_config_files_to_package():
171
- success = False
172
-
173
- # Update configuration files
174
212
  success &= self._update_configuration_files(options)
175
213
 
176
214
  return success
177
215
 
178
- def _handle_smart_config_merge(self, options: OptionsProtocol) -> None:
179
- """Handle smart config merge with warning on failure."""
180
- if not self._perform_smart_config_merge(options):
181
- self.console.print(
182
- "[yellow]⚠️[/yellow] Smart config merge encountered issues (continuing)"
183
- )
184
- # Don't fail the entire configuration phase, just log the warning
185
-
186
216
  def _update_configuration_files(self, options: OptionsProtocol) -> bool:
187
- """Update precommit and pyproject configuration files."""
188
217
  success = True
189
218
  if not self.config_service.update_precommit_config(options):
190
219
  success = False
@@ -193,7 +222,6 @@ class PhaseCoordinator:
193
222
  return success
194
223
 
195
224
  def _complete_configuration_task(self, success: bool) -> None:
196
- """Complete the configuration task with appropriate message."""
197
225
  message = (
198
226
  "Configuration updated successfully"
199
227
  if success
@@ -201,95 +229,6 @@ class PhaseCoordinator:
201
229
  )
202
230
  self.session.complete_task("configuration", message)
203
231
 
204
- def _perform_smart_config_merge(self, options: OptionsProtocol) -> bool:
205
- """Perform smart config merge before git operations."""
206
- try:
207
- self.logger.debug("Starting smart config merge process")
208
-
209
- # Smart merge for critical configuration files
210
- merged_files = []
211
-
212
- # Skip smart merge if explicitly requested or in specific modes
213
- if hasattr(options, "skip_config_merge") and options.skip_config_merge:
214
- self.logger.debug("Config merge skipped by option")
215
- return True
216
-
217
- # Merge .gitignore patterns (always safe to do)
218
- if self._smart_merge_gitignore():
219
- merged_files.append(".gitignore")
220
-
221
- # Merge configuration files (pyproject.toml, .pre-commit-config.yaml)
222
- # Only for crackerjack projects to avoid breaking user projects
223
- if self._is_crackerjack_project():
224
- if self._smart_merge_project_configs():
225
- merged_files.extend(["pyproject.toml", ".pre-commit-config.yaml"])
226
-
227
- if merged_files:
228
- files_str = ", ".join(merged_files)
229
- self.console.print(
230
- f"[green]🔧[/green] Smart-merged configurations: {files_str}"
231
- )
232
- self.logger.info(f"Smart config merge completed: {merged_files}")
233
- else:
234
- self.logger.debug("No configuration files needed smart merging")
235
-
236
- return True
237
-
238
- except Exception as e:
239
- self.console.print(f"[yellow]⚠️[/yellow] Smart config merge failed: {e}")
240
- self.logger.warning(
241
- f"Smart config merge failed: {e} (type: {type(e).__name__})"
242
- )
243
- # Return True to not block the workflow - this is fail-safe
244
- return True
245
-
246
- def _smart_merge_gitignore(self) -> bool:
247
- """Smart merge .gitignore patterns."""
248
- try:
249
- gitignore_path = self.pkg_path / ".gitignore"
250
- if not gitignore_path.exists():
251
- return False
252
-
253
- # Standard crackerjack ignore patterns to merge
254
- standard_patterns = [
255
- "# Crackerjack generated files",
256
- ".crackerjack/",
257
- "*.crackerjack.bak",
258
- ".coverage.*",
259
- "crackerjack-debug-*.log",
260
- "__pycache__/",
261
- "*.py[cod]",
262
- "*$py.class",
263
- ".pytest_cache/",
264
- ".tox/",
265
- ".mypy_cache/",
266
- ".ruff_cache/",
267
- ]
268
-
269
- self.config_merge_service.smart_merge_gitignore(
270
- patterns=standard_patterns, target_path=str(gitignore_path)
271
- )
272
-
273
- return True
274
-
275
- except Exception as e:
276
- self.logger.warning(f"Failed to smart merge .gitignore: {e}")
277
- return False
278
-
279
- def _smart_merge_project_configs(self) -> bool:
280
- """Smart merge pyproject.toml and pre-commit config for crackerjack projects."""
281
- try:
282
- # This would be where we implement project config merging
283
- # For now, just return True as the existing config service handles this
284
- self.logger.debug(
285
- "Project config smart merge placeholder - handled by existing config service"
286
- )
287
- return True
288
-
289
- except Exception as e:
290
- self.logger.warning(f"Failed to smart merge project configs: {e}")
291
- return False
292
-
293
232
  def _is_crackerjack_project(self) -> bool:
294
233
  pyproject_path = self.pkg_path / "pyproject.toml"
295
234
  if not pyproject_path.exists():
@@ -301,58 +240,11 @@ class PhaseCoordinator:
301
240
  with pyproject_path.open("rb") as f:
302
241
  data = tomllib.load(f)
303
242
 
304
- project_name = data.get("project", {}).get("name", "")
243
+ project_name: str = data.get("project", {}).get("name", "")
305
244
  return project_name == "crackerjack"
306
245
  except Exception:
307
246
  return False
308
247
 
309
- def _copy_config_files_to_package(self) -> bool:
310
- try:
311
- files_to_copy = [
312
- "pyproject.toml",
313
- ".pre-commit-config.yaml",
314
- "CLAUDE.md",
315
- "RULES.md",
316
- ".gitignore",
317
- "example.mcp.json",
318
- "uv.lock",
319
- ]
320
-
321
- package_dir = self.pkg_path / "crackerjack"
322
- if not package_dir.exists():
323
- self.console.print(
324
- "[yellow]⚠️[/ yellow] Package directory not found: crackerjack /",
325
- )
326
- return False
327
-
328
- copied_count = 0
329
- for filename in files_to_copy:
330
- src_path = self.pkg_path / filename
331
- if src_path.exists():
332
- dst_path = package_dir / filename
333
- try:
334
- import shutil
335
-
336
- shutil.copy2(src_path, dst_path)
337
- copied_count += 1
338
- self.logger.debug(f"Copied {filename} to package directory")
339
- except Exception as e:
340
- self.console.print(
341
- f"[yellow]⚠️[/ yellow] Failed to copy {filename}: {e}",
342
- )
343
-
344
- if copied_count > 0:
345
- self.console.print(
346
- f"[green]✅[/ green] Copied {copied_count} config files to package directory",
347
- )
348
-
349
- return True
350
- except Exception as e:
351
- self.console.print(
352
- f"[red]❌[/ red] Failed to copy config files to package: {e}",
353
- )
354
- return False
355
-
356
248
  def run_hooks_phase(self, options: OptionsProtocol) -> bool:
357
249
  if options.skip_hooks:
358
250
  return True
@@ -370,32 +262,26 @@ class PhaseCoordinator:
370
262
  if options.skip_hooks:
371
263
  return True
372
264
 
373
- return self._execute_hooks_with_retry(
374
- "fast",
375
- self.hook_manager.run_fast_hooks,
376
- options,
377
- )
265
+ hook_results = self.hook_manager.run_fast_hooks()
266
+ return all(r.status == "passed" for r in hook_results)
378
267
 
379
268
  def run_comprehensive_hooks_only(self, options: OptionsProtocol) -> bool:
380
269
  if options.skip_hooks:
381
270
  return True
382
271
 
383
- return self._execute_hooks_with_retry(
384
- "comprehensive",
385
- self.hook_manager.run_comprehensive_hooks,
386
- options,
387
- )
272
+ hook_results = self.hook_manager.run_comprehensive_hooks()
273
+ return all(r.status == "passed" for r in hook_results)
388
274
 
389
275
  def run_testing_phase(self, options: OptionsProtocol) -> bool:
390
276
  if not options.test:
391
277
  return True
392
278
  self.session.track_task("testing", "Test execution")
393
279
  try:
394
- self.console.print("\n" + "-" * 80)
280
+ self.console.print("\n" + "-" * 74)
395
281
  self.console.print(
396
282
  "[bold bright_blue]🧪 TESTS[/ bold bright_blue] [bold bright_white]Running test suite[/ bold bright_white]",
397
283
  )
398
- self.console.print("-" * 80 + "\n")
284
+ self.console.print("-" * 74 + "\n")
399
285
  if not self.test_manager.validate_test_environment():
400
286
  self.session.fail_task("testing", "Test environment validation failed")
401
287
  return False
@@ -430,9 +316,15 @@ class PhaseCoordinator:
430
316
 
431
317
  def _determine_version_type(self, options: OptionsProtocol) -> str | None:
432
318
  if options.publish:
433
- return options.publish
319
+ publish_value: str | None = (
320
+ options.publish if isinstance(options.publish, str) else None
321
+ )
322
+ return publish_value
434
323
  if options.all:
435
- return options.all
324
+ all_value: str | None = (
325
+ options.all if isinstance(options.all, str) else None
326
+ )
327
+ return all_value
436
328
  if options.bump:
437
329
  self._handle_version_bump_only(options.bump)
438
330
  return None
@@ -443,9 +335,12 @@ class PhaseCoordinator:
443
335
  options: OptionsProtocol,
444
336
  version_type: str,
445
337
  ) -> bool:
338
+ # Display version bump header
339
+ self._display_version_bump_header(version_type)
446
340
  new_version = self.publish_manager.bump_version(version_type)
447
341
 
448
- # Stage all changes after version bumping and code cleaning (if enabled)
342
+ # Display git operations header for staging and tagging
343
+ self._display_git_staging_header()
449
344
  self.console.print("[blue]📂[/ blue] Staging all changes for publishing...")
450
345
  if not self.git_service.add_all_files():
451
346
  self.console.print(
@@ -455,6 +350,8 @@ class PhaseCoordinator:
455
350
  if not options.no_git_tags:
456
351
  self.publish_manager.create_git_tag(new_version)
457
352
 
353
+ # Display publish header
354
+ self._display_publish_header()
458
355
  if self.publish_manager.publish_package():
459
356
  self._handle_successful_publish(options, new_version)
460
357
  return True
@@ -476,6 +373,9 @@ class PhaseCoordinator:
476
373
  def run_commit_phase(self, options: OptionsProtocol) -> bool:
477
374
  if not options.commit:
478
375
  return True
376
+
377
+ # Display commit & push header
378
+ self._display_commit_push_header()
479
379
  self.session.track_task("commit", "Git commit and push")
480
380
  try:
481
381
  changed_files = self.git_service.get_changed_files()
@@ -491,7 +391,6 @@ class PhaseCoordinator:
491
391
  def _handle_no_changes_to_commit(self) -> bool:
492
392
  self.console.print("[yellow]ℹ️[/ yellow] No changes to commit")
493
393
 
494
- # Check if there are unpushed commits
495
394
  from contextlib import suppress
496
395
 
497
396
  with suppress(ValueError, Exception):
@@ -559,6 +458,8 @@ class PhaseCoordinator:
559
458
  def _handle_version_bump_only(self, bump_type: str) -> bool:
560
459
  self.session.track_task("version_bump", f"Version bump ({bump_type})")
561
460
  try:
461
+ # Display version bump header
462
+ self._display_version_bump_header(bump_type)
562
463
  new_version = self.publish_manager.bump_version(bump_type)
563
464
  self.console.print(f"[green]🎯[/ green] Version bumped to {new_version}")
564
465
  self.session.complete_task("version_bump", f"Bumped to {new_version}")
@@ -573,6 +474,49 @@ class PhaseCoordinator:
573
474
  changed_files: list[str],
574
475
  options: OptionsProtocol,
575
476
  ) -> str:
477
+ # Check if smart commit is enabled
478
+ if getattr(options, "smart_commit", False):
479
+ try:
480
+ from crackerjack.services.intelligent_commit import (
481
+ CommitMessageGenerator,
482
+ )
483
+
484
+ self.console.print(
485
+ "[cyan]🤖[/cyan] Generating intelligent commit message..."
486
+ )
487
+ commit_generator = CommitMessageGenerator(
488
+ console=self.console, git_service=self.git_service
489
+ )
490
+
491
+ intelligent_message = commit_generator.generate_commit_message(
492
+ include_body=False,
493
+ conventional_commits=True,
494
+ )
495
+
496
+ if not options.interactive:
497
+ self.console.print(
498
+ f"[green]✨[/green] Generated: {intelligent_message}"
499
+ )
500
+ return intelligent_message
501
+
502
+ # In interactive mode, offer the intelligent message plus fallback suggestions
503
+ suggestions = [intelligent_message]
504
+ fallback_suggestions = self.git_service.get_commit_message_suggestions(
505
+ changed_files
506
+ )
507
+ suggestions.extend(
508
+ fallback_suggestions[:3]
509
+ ) # Add up to 3 fallback options
510
+
511
+ return self._interactive_commit_message_selection(suggestions)
512
+
513
+ except Exception as e:
514
+ self.console.print(
515
+ f"[yellow]⚠️[/yellow] Intelligent commit generation failed: {e}"
516
+ )
517
+ # Fallback to original logic
518
+
519
+ # Original logic for non-smart commits
576
520
  suggestions = self.git_service.get_commit_message_suggestions(changed_files)
577
521
 
578
522
  if not suggestions:
@@ -615,33 +559,58 @@ class PhaseCoordinator:
615
559
 
616
560
  for attempt in range(max_retries):
617
561
  try:
618
- results = hook_runner()
619
- summary = self.hook_manager.get_hook_summary(results)
620
-
621
- if self._has_hook_failures(summary):
622
- if self._should_retry_hooks(
623
- hook_type,
624
- attempt,
625
- max_retries,
626
- results,
627
- ):
628
- continue
629
-
630
- return self._handle_hook_failures(
631
- hook_type,
632
- options,
633
- summary,
634
- results,
635
- attempt,
636
- max_retries,
637
- )
638
- return self._handle_hook_success(hook_type, summary)
562
+ execution_result = self._execute_single_hook_attempt(hook_runner)
563
+ if execution_result is None:
564
+ return False
565
+
566
+ results, summary = execution_result
567
+ should_continue = self._process_hook_results(
568
+ hook_type, options, summary, results, attempt, max_retries
569
+ )
570
+
571
+ if should_continue == "continue":
572
+ continue
573
+ elif should_continue == "success":
574
+ return True
575
+ else:
576
+ return False
639
577
 
640
578
  except Exception as e:
641
579
  return self._handle_hook_exception(hook_type, e)
642
580
 
643
581
  return False
644
582
 
583
+ def _execute_single_hook_attempt(
584
+ self, hook_runner: t.Callable[[], list[t.Any]]
585
+ ) -> tuple[list[t.Any], dict[str, t.Any]] | None:
586
+ try:
587
+ results = hook_runner()
588
+ summary = self.hook_manager.get_hook_summary(results)
589
+ return results, summary
590
+ except Exception:
591
+ return None
592
+
593
+ def _process_hook_results(
594
+ self,
595
+ hook_type: str,
596
+ options: OptionsProtocol,
597
+ summary: dict[str, t.Any],
598
+ results: list[t.Any],
599
+ attempt: int,
600
+ max_retries: int,
601
+ ) -> str:
602
+ if not self._has_hook_failures(summary):
603
+ self._handle_hook_success(hook_type, summary)
604
+ return "success"
605
+
606
+ if self._should_retry_hooks(hook_type, attempt, max_retries, results):
607
+ return "continue"
608
+
609
+ self._handle_hook_failures(
610
+ hook_type, options, summary, results, attempt, max_retries
611
+ )
612
+ return "failure"
613
+
645
614
  def _initialize_hook_execution(self, hook_type: str) -> None:
646
615
  self.logger.info(f"Starting {hook_type} hooks execution")
647
616
  self.session.track_task(
@@ -671,11 +640,12 @@ class PhaseCoordinator:
671
640
  return False
672
641
 
673
642
  def _attempt_autofix_for_fast_hooks(self, results: list[t.Any]) -> bool:
674
- """Attempt to autofix fast hook failures."""
675
643
  try:
676
644
  self.logger.info("Attempting autofix for fast hook failures")
677
- # Apply autofixes for fast hooks
678
- return self.autofix_coordinator.apply_fast_stage_fixes()
645
+
646
+ autofix_coordinator = self._lazy_autofix.get()
647
+ fix_result: bool = autofix_coordinator.apply_fast_stage_fixes()
648
+ return fix_result
679
649
  except Exception as e:
680
650
  self.logger.warning(f"Autofix attempt failed: {e}")
681
651
  return False
@@ -697,15 +667,13 @@ class PhaseCoordinator:
697
667
  f"[red]❌[/ red] {hook_type.title()} hooks failed: {summary['failed']} failed, {summary['errors']} errors",
698
668
  )
699
669
 
700
- # Try autofix for fast hooks before giving up
701
670
  if hook_type == "fast" and attempt < max_retries - 1:
702
671
  if self._attempt_autofix_for_fast_hooks(results):
703
672
  self.console.print(
704
673
  "[yellow]🔧[/ yellow] Applied autofixes for fast hooks, retrying...",
705
674
  )
706
- return True # Return True to continue the retry loop
675
+ return True
707
676
 
708
- # Display detailed hook errors in verbose mode
709
677
  if getattr(options, "verbose", False):
710
678
  self._display_verbose_hook_errors(results, hook_type)
711
679
 
@@ -720,13 +688,11 @@ class PhaseCoordinator:
720
688
  def _display_verbose_hook_errors(
721
689
  self, results: list[t.Any], hook_type: str
722
690
  ) -> None:
723
- """Display detailed hook error output in verbose mode."""
724
691
  self.console.print(
725
- f"\n[bold yellow]📋 Detailed {hook_type} hook errors:[/bold yellow]"
692
+ f"\n[bold yellow]📋 Detailed {hook_type} hook errors: [/bold yellow]"
726
693
  )
727
694
 
728
695
  for result in results:
729
- # Check if this hook failed
730
696
  status = getattr(result, "status", "")
731
697
  if status not in ("failed", "error", "timeout"):
732
698
  continue
@@ -739,12 +705,10 @@ class PhaseCoordinator:
739
705
  if issues:
740
706
  for issue in issues:
741
707
  if isinstance(issue, str) and issue.strip():
742
- # Clean up the issue text and display with proper indentation
743
708
  cleaned_issue = issue.strip()
744
- self.console.print(f" {cleaned_issue}")
709
+ self.console.print(f" {cleaned_issue}")
745
710
  else:
746
- # If no specific issues, show generic failure message
747
- self.console.print(f" Hook failed with exit code (status: {status})")
711
+ self.console.print(f" Hook failed with exit code (status: {status})")
748
712
 
749
713
  def _build_detailed_hook_error_message(
750
714
  self, results: list[t.Any], summary: dict[str, t.Any]
@@ -798,3 +762,78 @@ class PhaseCoordinator:
798
762
  self.console.print(f"[red]❌[/ red] {hook_type.title()} hooks error: {e}")
799
763
  self.session.fail_task(f"{hook_type}_hooks", str(e))
800
764
  return False
765
+
766
+ async def _execute_hooks_with_parallel_support(
767
+ self,
768
+ hook_type: str,
769
+ hook_runner: t.Callable[[], list[t.Any]],
770
+ options: OptionsProtocol,
771
+ ) -> bool:
772
+ self._initialize_hook_execution(hook_type)
773
+
774
+ try:
775
+ return await self._process_parallel_hook_execution(
776
+ hook_type, hook_runner, options
777
+ )
778
+
779
+ except Exception as e:
780
+ return self._handle_hook_exception(hook_type, e)
781
+
782
+ async def _process_parallel_hook_execution(
783
+ self,
784
+ hook_type: str,
785
+ hook_runner: t.Callable[[], list[t.Any]],
786
+ options: OptionsProtocol,
787
+ ) -> bool:
788
+ results = hook_runner()
789
+ summary = self.hook_manager.get_hook_summary(results)
790
+
791
+ if not self._has_hook_failures(summary):
792
+ return self._handle_hook_success(hook_type, summary)
793
+
794
+ return self._handle_parallel_hook_failures(
795
+ hook_type, hook_runner, options, results, summary
796
+ )
797
+
798
+ def _handle_parallel_hook_failures(
799
+ self,
800
+ hook_type: str,
801
+ hook_runner: t.Callable[[], list[t.Any]],
802
+ options: OptionsProtocol,
803
+ results: list[t.Any],
804
+ summary: dict[str, t.Any],
805
+ ) -> bool:
806
+ if hook_type != "fast":
807
+ return self._handle_hook_failures(
808
+ hook_type, options, summary, results, 0, 1
809
+ )
810
+
811
+ if not self._attempt_autofix_for_fast_hooks(results):
812
+ return self._handle_hook_failures(
813
+ hook_type, options, summary, results, 0, 1
814
+ )
815
+
816
+ return self._retry_hooks_after_autofix(hook_type, hook_runner, options)
817
+
818
+ def _retry_hooks_after_autofix(
819
+ self,
820
+ hook_type: str,
821
+ hook_runner: t.Callable[[], list[t.Any]],
822
+ options: OptionsProtocol,
823
+ ) -> bool:
824
+ self.console.print(
825
+ "[yellow]🔧[/ yellow] Applied autofixes for fast hooks, retrying..."
826
+ )
827
+
828
+ results = hook_runner()
829
+ summary = self.hook_manager.get_hook_summary(results)
830
+
831
+ if not self._has_hook_failures(summary):
832
+ return self._handle_hook_success(hook_type, summary)
833
+
834
+ return self._handle_hook_failures(hook_type, options, summary, results, 0, 1)
835
+
836
+ @property
837
+ def autofix_coordinator(self) -> AutofixCoordinator:
838
+ coordinator: AutofixCoordinator = self._lazy_autofix.get()
839
+ return coordinator