crackerjack 0.37.9__py3-none-any.whl → 0.45.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. crackerjack/README.md +19 -0
  2. crackerjack/__init__.py +30 -1
  3. crackerjack/__main__.py +342 -1263
  4. crackerjack/adapters/README.md +18 -0
  5. crackerjack/adapters/__init__.py +27 -5
  6. crackerjack/adapters/_output_paths.py +167 -0
  7. crackerjack/adapters/_qa_adapter_base.py +309 -0
  8. crackerjack/adapters/_tool_adapter_base.py +706 -0
  9. crackerjack/adapters/ai/README.md +65 -0
  10. crackerjack/adapters/ai/__init__.py +5 -0
  11. crackerjack/adapters/ai/claude.py +853 -0
  12. crackerjack/adapters/complexity/README.md +53 -0
  13. crackerjack/adapters/complexity/__init__.py +10 -0
  14. crackerjack/adapters/complexity/complexipy.py +641 -0
  15. crackerjack/adapters/dependency/__init__.py +22 -0
  16. crackerjack/adapters/dependency/pip_audit.py +418 -0
  17. crackerjack/adapters/format/README.md +72 -0
  18. crackerjack/adapters/format/__init__.py +11 -0
  19. crackerjack/adapters/format/mdformat.py +313 -0
  20. crackerjack/adapters/format/ruff.py +516 -0
  21. crackerjack/adapters/lint/README.md +47 -0
  22. crackerjack/adapters/lint/__init__.py +11 -0
  23. crackerjack/adapters/lint/codespell.py +273 -0
  24. crackerjack/adapters/lsp/README.md +49 -0
  25. crackerjack/adapters/lsp/__init__.py +27 -0
  26. crackerjack/adapters/{rust_tool_manager.py → lsp/_manager.py} +3 -3
  27. crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
  28. crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
  29. crackerjack/adapters/refactor/README.md +59 -0
  30. crackerjack/adapters/refactor/__init__.py +12 -0
  31. crackerjack/adapters/refactor/creosote.py +318 -0
  32. crackerjack/adapters/refactor/refurb.py +406 -0
  33. crackerjack/adapters/refactor/skylos.py +494 -0
  34. crackerjack/adapters/sast/README.md +132 -0
  35. crackerjack/adapters/sast/__init__.py +32 -0
  36. crackerjack/adapters/sast/_base.py +201 -0
  37. crackerjack/adapters/sast/bandit.py +423 -0
  38. crackerjack/adapters/sast/pyscn.py +405 -0
  39. crackerjack/adapters/sast/semgrep.py +241 -0
  40. crackerjack/adapters/security/README.md +111 -0
  41. crackerjack/adapters/security/__init__.py +17 -0
  42. crackerjack/adapters/security/gitleaks.py +339 -0
  43. crackerjack/adapters/type/README.md +52 -0
  44. crackerjack/adapters/type/__init__.py +12 -0
  45. crackerjack/adapters/type/pyrefly.py +402 -0
  46. crackerjack/adapters/type/ty.py +402 -0
  47. crackerjack/adapters/type/zuban.py +522 -0
  48. crackerjack/adapters/utility/README.md +51 -0
  49. crackerjack/adapters/utility/__init__.py +10 -0
  50. crackerjack/adapters/utility/checks.py +884 -0
  51. crackerjack/agents/README.md +264 -0
  52. crackerjack/agents/__init__.py +40 -12
  53. crackerjack/agents/base.py +1 -0
  54. crackerjack/agents/claude_code_bridge.py +641 -0
  55. crackerjack/agents/coordinator.py +49 -53
  56. crackerjack/agents/dry_agent.py +187 -3
  57. crackerjack/agents/enhanced_coordinator.py +279 -0
  58. crackerjack/agents/enhanced_proactive_agent.py +185 -0
  59. crackerjack/agents/error_middleware.py +53 -0
  60. crackerjack/agents/formatting_agent.py +6 -8
  61. crackerjack/agents/helpers/__init__.py +9 -0
  62. crackerjack/agents/helpers/performance/__init__.py +22 -0
  63. crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
  64. crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
  65. crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
  66. crackerjack/agents/helpers/refactoring/__init__.py +22 -0
  67. crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
  68. crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
  69. crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
  70. crackerjack/agents/helpers/test_creation/__init__.py +19 -0
  71. crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
  72. crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
  73. crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
  74. crackerjack/agents/performance_agent.py +121 -1152
  75. crackerjack/agents/refactoring_agent.py +156 -655
  76. crackerjack/agents/semantic_agent.py +479 -0
  77. crackerjack/agents/semantic_helpers.py +356 -0
  78. crackerjack/agents/test_creation_agent.py +19 -1605
  79. crackerjack/api.py +5 -7
  80. crackerjack/cli/README.md +394 -0
  81. crackerjack/cli/__init__.py +1 -1
  82. crackerjack/cli/cache_handlers.py +23 -18
  83. crackerjack/cli/cache_handlers_enhanced.py +1 -4
  84. crackerjack/cli/facade.py +70 -8
  85. crackerjack/cli/formatting.py +13 -0
  86. crackerjack/cli/handlers/__init__.py +85 -0
  87. crackerjack/cli/handlers/advanced.py +103 -0
  88. crackerjack/cli/handlers/ai_features.py +62 -0
  89. crackerjack/cli/handlers/analytics.py +479 -0
  90. crackerjack/cli/handlers/changelog.py +271 -0
  91. crackerjack/cli/handlers/config_handlers.py +16 -0
  92. crackerjack/cli/handlers/coverage.py +84 -0
  93. crackerjack/cli/handlers/documentation.py +280 -0
  94. crackerjack/cli/handlers/main_handlers.py +497 -0
  95. crackerjack/cli/handlers/monitoring.py +371 -0
  96. crackerjack/cli/handlers.py +249 -49
  97. crackerjack/cli/interactive.py +8 -5
  98. crackerjack/cli/options.py +203 -110
  99. crackerjack/cli/semantic_handlers.py +292 -0
  100. crackerjack/cli/version.py +19 -0
  101. crackerjack/code_cleaner.py +60 -24
  102. crackerjack/config/README.md +472 -0
  103. crackerjack/config/__init__.py +256 -0
  104. crackerjack/config/global_lock_config.py +191 -54
  105. crackerjack/config/hooks.py +188 -16
  106. crackerjack/config/loader.py +239 -0
  107. crackerjack/config/settings.py +141 -0
  108. crackerjack/config/tool_commands.py +331 -0
  109. crackerjack/core/README.md +393 -0
  110. crackerjack/core/async_workflow_orchestrator.py +79 -53
  111. crackerjack/core/autofix_coordinator.py +22 -9
  112. crackerjack/core/container.py +10 -9
  113. crackerjack/core/enhanced_container.py +9 -9
  114. crackerjack/core/performance.py +1 -1
  115. crackerjack/core/performance_monitor.py +5 -3
  116. crackerjack/core/phase_coordinator.py +1018 -634
  117. crackerjack/core/proactive_workflow.py +3 -3
  118. crackerjack/core/retry.py +275 -0
  119. crackerjack/core/service_watchdog.py +167 -23
  120. crackerjack/core/session_coordinator.py +187 -382
  121. crackerjack/core/timeout_manager.py +161 -44
  122. crackerjack/core/workflow/__init__.py +21 -0
  123. crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
  124. crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
  125. crackerjack/core/workflow/workflow_issue_parser.py +714 -0
  126. crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
  127. crackerjack/core/workflow/workflow_security_gates.py +400 -0
  128. crackerjack/core/workflow_orchestrator.py +1247 -953
  129. crackerjack/data/README.md +11 -0
  130. crackerjack/data/__init__.py +8 -0
  131. crackerjack/data/models.py +79 -0
  132. crackerjack/data/repository.py +210 -0
  133. crackerjack/decorators/README.md +180 -0
  134. crackerjack/decorators/__init__.py +35 -0
  135. crackerjack/decorators/error_handling.py +649 -0
  136. crackerjack/decorators/error_handling_decorators.py +334 -0
  137. crackerjack/decorators/helpers.py +58 -0
  138. crackerjack/decorators/patterns.py +281 -0
  139. crackerjack/decorators/utils.py +58 -0
  140. crackerjack/docs/README.md +11 -0
  141. crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
  142. crackerjack/documentation/README.md +11 -0
  143. crackerjack/documentation/ai_templates.py +1 -1
  144. crackerjack/documentation/dual_output_generator.py +11 -9
  145. crackerjack/documentation/reference_generator.py +104 -59
  146. crackerjack/dynamic_config.py +52 -61
  147. crackerjack/errors.py +1 -1
  148. crackerjack/events/README.md +11 -0
  149. crackerjack/events/__init__.py +16 -0
  150. crackerjack/events/telemetry.py +175 -0
  151. crackerjack/events/workflow_bus.py +346 -0
  152. crackerjack/exceptions/README.md +301 -0
  153. crackerjack/exceptions/__init__.py +5 -0
  154. crackerjack/exceptions/config.py +4 -0
  155. crackerjack/exceptions/tool_execution_error.py +245 -0
  156. crackerjack/executors/README.md +591 -0
  157. crackerjack/executors/__init__.py +2 -0
  158. crackerjack/executors/async_hook_executor.py +539 -77
  159. crackerjack/executors/cached_hook_executor.py +3 -3
  160. crackerjack/executors/hook_executor.py +967 -102
  161. crackerjack/executors/hook_lock_manager.py +31 -22
  162. crackerjack/executors/individual_hook_executor.py +66 -32
  163. crackerjack/executors/lsp_aware_hook_executor.py +136 -57
  164. crackerjack/executors/progress_hook_executor.py +282 -0
  165. crackerjack/executors/tool_proxy.py +23 -7
  166. crackerjack/hooks/README.md +485 -0
  167. crackerjack/hooks/lsp_hook.py +8 -9
  168. crackerjack/intelligence/README.md +557 -0
  169. crackerjack/interactive.py +37 -10
  170. crackerjack/managers/README.md +369 -0
  171. crackerjack/managers/async_hook_manager.py +41 -57
  172. crackerjack/managers/hook_manager.py +449 -79
  173. crackerjack/managers/publish_manager.py +81 -36
  174. crackerjack/managers/test_command_builder.py +290 -12
  175. crackerjack/managers/test_executor.py +93 -8
  176. crackerjack/managers/test_manager.py +1082 -75
  177. crackerjack/managers/test_progress.py +118 -26
  178. crackerjack/mcp/README.md +374 -0
  179. crackerjack/mcp/cache.py +25 -2
  180. crackerjack/mcp/client_runner.py +35 -18
  181. crackerjack/mcp/context.py +9 -9
  182. crackerjack/mcp/dashboard.py +24 -8
  183. crackerjack/mcp/enhanced_progress_monitor.py +34 -23
  184. crackerjack/mcp/file_monitor.py +27 -6
  185. crackerjack/mcp/progress_components.py +45 -34
  186. crackerjack/mcp/progress_monitor.py +6 -9
  187. crackerjack/mcp/rate_limiter.py +11 -7
  188. crackerjack/mcp/server.py +2 -0
  189. crackerjack/mcp/server_core.py +187 -55
  190. crackerjack/mcp/service_watchdog.py +12 -9
  191. crackerjack/mcp/task_manager.py +2 -2
  192. crackerjack/mcp/tools/README.md +27 -0
  193. crackerjack/mcp/tools/__init__.py +2 -0
  194. crackerjack/mcp/tools/core_tools.py +75 -52
  195. crackerjack/mcp/tools/execution_tools.py +87 -31
  196. crackerjack/mcp/tools/intelligence_tools.py +2 -2
  197. crackerjack/mcp/tools/proactive_tools.py +1 -1
  198. crackerjack/mcp/tools/semantic_tools.py +584 -0
  199. crackerjack/mcp/tools/utility_tools.py +180 -132
  200. crackerjack/mcp/tools/workflow_executor.py +87 -46
  201. crackerjack/mcp/websocket/README.md +31 -0
  202. crackerjack/mcp/websocket/app.py +11 -1
  203. crackerjack/mcp/websocket/event_bridge.py +188 -0
  204. crackerjack/mcp/websocket/jobs.py +27 -4
  205. crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
  206. crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
  207. crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
  208. crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
  209. crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
  210. crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
  211. crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
  212. crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
  213. crackerjack/mcp/websocket/monitoring/factory.py +109 -0
  214. crackerjack/mcp/websocket/monitoring/filters.py +10 -0
  215. crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
  216. crackerjack/mcp/websocket/monitoring/models.py +90 -0
  217. crackerjack/mcp/websocket/monitoring/utils.py +171 -0
  218. crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
  219. crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
  220. crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
  221. crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
  222. crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
  223. crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
  224. crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
  225. crackerjack/mcp/websocket/server.py +1 -3
  226. crackerjack/mcp/websocket/websocket_handler.py +107 -6
  227. crackerjack/models/README.md +308 -0
  228. crackerjack/models/__init__.py +10 -1
  229. crackerjack/models/config.py +639 -22
  230. crackerjack/models/config_adapter.py +6 -6
  231. crackerjack/models/protocols.py +1167 -23
  232. crackerjack/models/pydantic_models.py +320 -0
  233. crackerjack/models/qa_config.py +145 -0
  234. crackerjack/models/qa_results.py +134 -0
  235. crackerjack/models/results.py +35 -0
  236. crackerjack/models/semantic_models.py +258 -0
  237. crackerjack/models/task.py +19 -3
  238. crackerjack/models/test_models.py +60 -0
  239. crackerjack/monitoring/README.md +11 -0
  240. crackerjack/monitoring/ai_agent_watchdog.py +5 -4
  241. crackerjack/monitoring/metrics_collector.py +4 -3
  242. crackerjack/monitoring/regression_prevention.py +4 -3
  243. crackerjack/monitoring/websocket_server.py +4 -241
  244. crackerjack/orchestration/README.md +340 -0
  245. crackerjack/orchestration/__init__.py +43 -0
  246. crackerjack/orchestration/advanced_orchestrator.py +20 -67
  247. crackerjack/orchestration/cache/README.md +312 -0
  248. crackerjack/orchestration/cache/__init__.py +37 -0
  249. crackerjack/orchestration/cache/memory_cache.py +338 -0
  250. crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
  251. crackerjack/orchestration/config.py +297 -0
  252. crackerjack/orchestration/coverage_improvement.py +13 -6
  253. crackerjack/orchestration/execution_strategies.py +6 -6
  254. crackerjack/orchestration/hook_orchestrator.py +1398 -0
  255. crackerjack/orchestration/strategies/README.md +401 -0
  256. crackerjack/orchestration/strategies/__init__.py +39 -0
  257. crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
  258. crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
  259. crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
  260. crackerjack/orchestration/test_progress_streamer.py +1 -1
  261. crackerjack/plugins/README.md +11 -0
  262. crackerjack/plugins/hooks.py +3 -2
  263. crackerjack/plugins/loader.py +3 -3
  264. crackerjack/plugins/managers.py +1 -1
  265. crackerjack/py313.py +191 -0
  266. crackerjack/security/README.md +11 -0
  267. crackerjack/services/README.md +374 -0
  268. crackerjack/services/__init__.py +8 -21
  269. crackerjack/services/ai/README.md +295 -0
  270. crackerjack/services/ai/__init__.py +7 -0
  271. crackerjack/services/ai/advanced_optimizer.py +878 -0
  272. crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
  273. crackerjack/services/ai/embeddings.py +444 -0
  274. crackerjack/services/ai/intelligent_commit.py +328 -0
  275. crackerjack/services/ai/predictive_analytics.py +510 -0
  276. crackerjack/services/api_extractor.py +5 -3
  277. crackerjack/services/bounded_status_operations.py +45 -5
  278. crackerjack/services/cache.py +249 -318
  279. crackerjack/services/changelog_automation.py +7 -3
  280. crackerjack/services/command_execution_service.py +305 -0
  281. crackerjack/services/config_integrity.py +83 -39
  282. crackerjack/services/config_merge.py +9 -6
  283. crackerjack/services/config_service.py +198 -0
  284. crackerjack/services/config_template.py +13 -26
  285. crackerjack/services/coverage_badge_service.py +6 -4
  286. crackerjack/services/coverage_ratchet.py +53 -27
  287. crackerjack/services/debug.py +18 -7
  288. crackerjack/services/dependency_analyzer.py +4 -4
  289. crackerjack/services/dependency_monitor.py +13 -13
  290. crackerjack/services/documentation_generator.py +4 -2
  291. crackerjack/services/documentation_service.py +62 -33
  292. crackerjack/services/enhanced_filesystem.py +81 -27
  293. crackerjack/services/enterprise_optimizer.py +1 -1
  294. crackerjack/services/error_pattern_analyzer.py +10 -10
  295. crackerjack/services/file_filter.py +221 -0
  296. crackerjack/services/file_hasher.py +5 -7
  297. crackerjack/services/file_io_service.py +361 -0
  298. crackerjack/services/file_modifier.py +615 -0
  299. crackerjack/services/filesystem.py +80 -109
  300. crackerjack/services/git.py +99 -5
  301. crackerjack/services/health_metrics.py +4 -6
  302. crackerjack/services/heatmap_generator.py +12 -3
  303. crackerjack/services/incremental_executor.py +380 -0
  304. crackerjack/services/initialization.py +101 -49
  305. crackerjack/services/log_manager.py +2 -2
  306. crackerjack/services/logging.py +120 -68
  307. crackerjack/services/lsp_client.py +12 -12
  308. crackerjack/services/memory_optimizer.py +27 -22
  309. crackerjack/services/monitoring/README.md +30 -0
  310. crackerjack/services/monitoring/__init__.py +9 -0
  311. crackerjack/services/monitoring/dependency_monitor.py +678 -0
  312. crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
  313. crackerjack/services/monitoring/health_metrics.py +716 -0
  314. crackerjack/services/monitoring/metrics.py +587 -0
  315. crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
  316. crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
  317. crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
  318. crackerjack/services/parallel_executor.py +166 -55
  319. crackerjack/services/patterns/__init__.py +142 -0
  320. crackerjack/services/patterns/agents.py +107 -0
  321. crackerjack/services/patterns/code/__init__.py +15 -0
  322. crackerjack/services/patterns/code/detection.py +118 -0
  323. crackerjack/services/patterns/code/imports.py +107 -0
  324. crackerjack/services/patterns/code/paths.py +159 -0
  325. crackerjack/services/patterns/code/performance.py +119 -0
  326. crackerjack/services/patterns/code/replacement.py +36 -0
  327. crackerjack/services/patterns/core.py +212 -0
  328. crackerjack/services/patterns/documentation/__init__.py +14 -0
  329. crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
  330. crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
  331. crackerjack/services/patterns/documentation/docstrings.py +89 -0
  332. crackerjack/services/patterns/formatting.py +226 -0
  333. crackerjack/services/patterns/operations.py +339 -0
  334. crackerjack/services/patterns/security/__init__.py +23 -0
  335. crackerjack/services/patterns/security/code_injection.py +122 -0
  336. crackerjack/services/patterns/security/credentials.py +190 -0
  337. crackerjack/services/patterns/security/path_traversal.py +221 -0
  338. crackerjack/services/patterns/security/unsafe_operations.py +216 -0
  339. crackerjack/services/patterns/templates.py +62 -0
  340. crackerjack/services/patterns/testing/__init__.py +18 -0
  341. crackerjack/services/patterns/testing/error_patterns.py +107 -0
  342. crackerjack/services/patterns/testing/pytest_output.py +126 -0
  343. crackerjack/services/patterns/tool_output/__init__.py +16 -0
  344. crackerjack/services/patterns/tool_output/bandit.py +72 -0
  345. crackerjack/services/patterns/tool_output/other.py +97 -0
  346. crackerjack/services/patterns/tool_output/pyright.py +67 -0
  347. crackerjack/services/patterns/tool_output/ruff.py +44 -0
  348. crackerjack/services/patterns/url_sanitization.py +114 -0
  349. crackerjack/services/patterns/utilities.py +42 -0
  350. crackerjack/services/patterns/utils.py +339 -0
  351. crackerjack/services/patterns/validation.py +46 -0
  352. crackerjack/services/patterns/versioning.py +62 -0
  353. crackerjack/services/predictive_analytics.py +21 -8
  354. crackerjack/services/profiler.py +280 -0
  355. crackerjack/services/quality/README.md +415 -0
  356. crackerjack/services/quality/__init__.py +11 -0
  357. crackerjack/services/quality/anomaly_detector.py +392 -0
  358. crackerjack/services/quality/pattern_cache.py +333 -0
  359. crackerjack/services/quality/pattern_detector.py +479 -0
  360. crackerjack/services/quality/qa_orchestrator.py +491 -0
  361. crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
  362. crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
  363. crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
  364. crackerjack/services/regex_patterns.py +58 -2987
  365. crackerjack/services/regex_utils.py +55 -29
  366. crackerjack/services/secure_status_formatter.py +42 -15
  367. crackerjack/services/secure_subprocess.py +35 -2
  368. crackerjack/services/security.py +16 -8
  369. crackerjack/services/server_manager.py +40 -51
  370. crackerjack/services/smart_scheduling.py +46 -6
  371. crackerjack/services/status_authentication.py +3 -3
  372. crackerjack/services/thread_safe_status_collector.py +1 -0
  373. crackerjack/services/tool_filter.py +368 -0
  374. crackerjack/services/tool_version_service.py +9 -5
  375. crackerjack/services/unified_config.py +43 -351
  376. crackerjack/services/vector_store.py +689 -0
  377. crackerjack/services/version_analyzer.py +6 -4
  378. crackerjack/services/version_checker.py +14 -8
  379. crackerjack/services/zuban_lsp_service.py +5 -4
  380. crackerjack/slash_commands/README.md +11 -0
  381. crackerjack/slash_commands/init.md +2 -12
  382. crackerjack/slash_commands/run.md +84 -50
  383. crackerjack/tools/README.md +11 -0
  384. crackerjack/tools/__init__.py +30 -0
  385. crackerjack/tools/_git_utils.py +105 -0
  386. crackerjack/tools/check_added_large_files.py +139 -0
  387. crackerjack/tools/check_ast.py +105 -0
  388. crackerjack/tools/check_json.py +103 -0
  389. crackerjack/tools/check_jsonschema.py +297 -0
  390. crackerjack/tools/check_toml.py +103 -0
  391. crackerjack/tools/check_yaml.py +110 -0
  392. crackerjack/tools/codespell_wrapper.py +72 -0
  393. crackerjack/tools/end_of_file_fixer.py +202 -0
  394. crackerjack/tools/format_json.py +128 -0
  395. crackerjack/tools/mdformat_wrapper.py +114 -0
  396. crackerjack/tools/trailing_whitespace.py +198 -0
  397. crackerjack/tools/validate_regex_patterns.py +7 -3
  398. crackerjack/ui/README.md +11 -0
  399. crackerjack/ui/dashboard_renderer.py +28 -0
  400. crackerjack/ui/templates/README.md +11 -0
  401. crackerjack/utils/console_utils.py +13 -0
  402. crackerjack/utils/dependency_guard.py +230 -0
  403. crackerjack/utils/retry_utils.py +275 -0
  404. crackerjack/workflows/README.md +590 -0
  405. crackerjack/workflows/__init__.py +46 -0
  406. crackerjack/workflows/actions.py +811 -0
  407. crackerjack/workflows/auto_fix.py +444 -0
  408. crackerjack/workflows/container_builder.py +499 -0
  409. crackerjack/workflows/definitions.py +443 -0
  410. crackerjack/workflows/engine.py +177 -0
  411. crackerjack/workflows/event_bridge.py +242 -0
  412. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
  413. crackerjack-0.45.2.dist-info/RECORD +478 -0
  414. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
  415. crackerjack/managers/test_manager_backup.py +0 -1075
  416. crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
  417. crackerjack/mixins/__init__.py +0 -3
  418. crackerjack/mixins/error_handling.py +0 -145
  419. crackerjack/services/config.py +0 -358
  420. crackerjack/ui/server_panels.py +0 -125
  421. crackerjack-0.37.9.dist-info/RECORD +0 -231
  422. /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
  423. /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
  424. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
  425. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,811 @@
1
+ """ACB workflow action handlers for crackerjack.
2
+
3
+ This module provides action handler functions that wrap existing crackerjack
4
+ phase execution logic for use with ACB workflows. These handlers are registered
5
+ with the CrackerjackWorkflowEngine and called during workflow execution.
6
+
7
+ Action handlers follow ACB's async callable signature:
8
+ async def action_name(
9
+ context: dict[str, Any],
10
+ step_id: str,
11
+ **params
12
+ ) -> Any
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import asyncio
18
+ import time
19
+ import typing as t
20
+
21
+ from acb.depends import Inject, depends
22
+
23
+ from crackerjack.core.workflow_orchestrator import WorkflowPipeline
24
+ from crackerjack.events.workflow_bus import WorkflowEvent, WorkflowEventBus
25
+ from crackerjack.models.protocols import OptionsProtocol
26
+
27
+ if t.TYPE_CHECKING:
28
+ pass
29
+
30
+
31
+ async def run_configuration(
32
+ context: dict[str, t.Any],
33
+ step_id: str,
34
+ **params: t.Any,
35
+ ) -> dict[str, t.Any]:
36
+ """Execute configuration phase.
37
+
38
+ This action wraps the configuration phase logic, which typically
39
+ updates dependencies and performs pre-flight checks.
40
+
41
+ Args:
42
+ context: Workflow execution context with "options" key
43
+ step_id: Step identifier (unused, for ACB compatibility)
44
+ **params: Additional step parameters
45
+
46
+ Returns:
47
+ dict with configuration results
48
+ """
49
+ options: OptionsProtocol = context.get("options") # type: ignore[assignment]
50
+ if not options:
51
+ msg = "Missing 'options' in workflow context"
52
+ raise ValueError(msg)
53
+
54
+ # Configuration phase is typically skipped in current implementation
55
+ # but we include it for workflow completeness
56
+ return {
57
+ "phase": "config",
58
+ "success": True,
59
+ "message": "Configuration phase skipped (no automated updates defined)",
60
+ }
61
+
62
+
63
+ @depends.inject # type: ignore[misc]
64
+ async def run_fast_hooks(
65
+ context: dict[str, t.Any],
66
+ step_id: str,
67
+ event_bus: Inject[WorkflowEventBus] = None,
68
+ **params: t.Any,
69
+ ) -> dict[str, t.Any]:
70
+ """Execute fast hooks phase.
71
+
72
+ Fast hooks include:
73
+ - Formatters (ruff, mdformat)
74
+ - Import sorting (ruff)
75
+ - Basic static analysis
76
+
77
+ This action wraps the existing fast hooks execution logic from
78
+ WorkflowPipeline passed explicitly in context.
79
+
80
+ Args:
81
+ context: Workflow execution context with "options" and "pipeline" keys
82
+ step_id: Step identifier (unused, for ACB compatibility)
83
+ event_bus: WorkflowEventBus for event emission (injected)
84
+ **params: Additional step parameters
85
+
86
+ Returns:
87
+ dict with fast hooks execution results
88
+
89
+ Raises:
90
+ RuntimeError: If fast hooks execution fails
91
+ """
92
+ options: OptionsProtocol = context.get("options") # type: ignore[assignment]
93
+ if not options:
94
+ msg = "Missing 'options' in workflow context"
95
+ raise ValueError(msg)
96
+
97
+ # Phase 4.1: Get pipeline from context instead of DI injection
98
+ pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
99
+ if not pipeline:
100
+ msg = "WorkflowPipeline not available in context"
101
+ raise RuntimeError(msg)
102
+
103
+ # Phase 7.2: Emit start event
104
+ start_time = time.time()
105
+ if event_bus:
106
+ await event_bus.publish(
107
+ WorkflowEvent.HOOK_STRATEGY_STARTED,
108
+ {"step_id": step_id, "strategy": "fast", "timestamp": start_time},
109
+ )
110
+
111
+ # Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
112
+ # Use asyncio.to_thread to avoid blocking event loop with synchronous operations
113
+ try:
114
+ success = await asyncio.to_thread(
115
+ pipeline._run_fast_hooks_phase,
116
+ options,
117
+ )
118
+ except Exception as exc:
119
+ # Phase 7.2: Emit failure event
120
+ if event_bus:
121
+ await event_bus.publish(
122
+ WorkflowEvent.HOOK_STRATEGY_FAILED,
123
+ {
124
+ "step_id": step_id,
125
+ "strategy": "fast",
126
+ "error": str(exc),
127
+ "timestamp": time.time(),
128
+ "duration": time.time() - start_time,
129
+ },
130
+ )
131
+ raise
132
+
133
+ if not success:
134
+ # Phase 7.2: Emit failure event
135
+ if event_bus:
136
+ await event_bus.publish(
137
+ WorkflowEvent.HOOK_STRATEGY_FAILED,
138
+ {
139
+ "step_id": step_id,
140
+ "strategy": "fast",
141
+ "timestamp": time.time(),
142
+ "duration": time.time() - start_time,
143
+ },
144
+ )
145
+ msg = "Fast hooks execution failed"
146
+ raise RuntimeError(msg)
147
+
148
+ # Phase 7.2: Emit completion event
149
+ if event_bus:
150
+ await event_bus.publish(
151
+ WorkflowEvent.HOOK_STRATEGY_COMPLETED,
152
+ {
153
+ "step_id": step_id,
154
+ "strategy": "fast",
155
+ "success": True,
156
+ "timestamp": time.time(),
157
+ "duration": time.time() - start_time,
158
+ },
159
+ )
160
+
161
+ return {
162
+ "phase": "fast_hooks",
163
+ "success": True,
164
+ "message": "Fast hooks completed successfully",
165
+ }
166
+
167
+
168
+ @depends.inject # type: ignore[misc]
169
+ async def run_code_cleaning(
170
+ context: dict[str, t.Any],
171
+ step_id: str,
172
+ event_bus: Inject[WorkflowEventBus] = None,
173
+ **params: t.Any,
174
+ ) -> dict[str, t.Any]:
175
+ """Execute code cleaning phase.
176
+
177
+ Code cleaning includes:
178
+ - Unused import removal
179
+ - Dead code elimination
180
+ - Code formatting fixes
181
+
182
+ This action wraps the existing cleaning execution logic from
183
+ WorkflowPipeline passed explicitly in context.
184
+
185
+ Args:
186
+ context: Workflow execution context with "options" and "pipeline" keys
187
+ step_id: Step identifier (unused, for ACB compatibility)
188
+ event_bus: WorkflowEventBus for event emission (injected)
189
+ **params: Additional step parameters
190
+
191
+ Returns:
192
+ dict with cleaning execution results (or skip result if strip_code=False)
193
+
194
+ Raises:
195
+ RuntimeError: If cleaning execution fails
196
+ """
197
+ options: OptionsProtocol = context.get("options") # type: ignore[assignment]
198
+ if not options:
199
+ msg = "Missing 'options' in workflow context"
200
+ raise ValueError(msg)
201
+
202
+ # Skip code cleaning unless -x/--strip-code flag is set
203
+ if not getattr(options, "strip_code", False):
204
+ return {
205
+ "phase": "cleaning",
206
+ "success": True,
207
+ "skipped": True,
208
+ "reason": "Code cleaning only runs with -x/--strip-code flag",
209
+ }
210
+
211
+ # Phase 4.1: Get pipeline from context instead of DI injection
212
+ pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
213
+ if not pipeline:
214
+ msg = "WorkflowPipeline not available in context"
215
+ raise RuntimeError(msg)
216
+
217
+ # Phase 7.2: Emit start event
218
+ start_time = time.time()
219
+ if event_bus:
220
+ await event_bus.publish(
221
+ WorkflowEvent.QUALITY_PHASE_STARTED,
222
+ {"step_id": step_id, "phase": "cleaning", "timestamp": start_time},
223
+ )
224
+
225
+ # Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
226
+ # Use asyncio.to_thread to avoid blocking event loop with synchronous operations
227
+ try:
228
+ success = await asyncio.to_thread(
229
+ pipeline._run_code_cleaning_phase,
230
+ options,
231
+ )
232
+ except Exception as exc:
233
+ # Phase 7.2: Emit failure event
234
+ if event_bus:
235
+ await event_bus.publish(
236
+ WorkflowEvent.WORKFLOW_FAILED,
237
+ {
238
+ "step_id": step_id,
239
+ "phase": "cleaning",
240
+ "error": str(exc),
241
+ "timestamp": time.time(),
242
+ "duration": time.time() - start_time,
243
+ },
244
+ )
245
+ raise
246
+
247
+ if not success:
248
+ # Phase 7.2: Emit failure event
249
+ if event_bus:
250
+ await event_bus.publish(
251
+ WorkflowEvent.WORKFLOW_FAILED,
252
+ {
253
+ "step_id": step_id,
254
+ "phase": "cleaning",
255
+ "timestamp": time.time(),
256
+ "duration": time.time() - start_time,
257
+ },
258
+ )
259
+ msg = "Code cleaning execution failed"
260
+ raise RuntimeError(msg)
261
+
262
+ # Phase 7.2: Emit completion event
263
+ if event_bus:
264
+ await event_bus.publish(
265
+ WorkflowEvent.QUALITY_PHASE_COMPLETED,
266
+ {
267
+ "step_id": step_id,
268
+ "phase": "cleaning",
269
+ "success": True,
270
+ "timestamp": time.time(),
271
+ "duration": time.time() - start_time,
272
+ },
273
+ )
274
+
275
+ return {
276
+ "phase": "cleaning",
277
+ "success": True,
278
+ "message": "Code cleaning completed successfully",
279
+ }
280
+
281
+
282
+ @depends.inject # type: ignore[misc]
283
+ async def run_comprehensive_hooks(
284
+ context: dict[str, t.Any],
285
+ step_id: str,
286
+ event_bus: Inject[WorkflowEventBus] = None,
287
+ **params: t.Any,
288
+ ) -> dict[str, t.Any]:
289
+ """Execute comprehensive hooks phase.
290
+
291
+ Comprehensive hooks include:
292
+ - Type checking (zuban)
293
+ - Security scanning (bandit, gitleaks)
294
+ - Complexity analysis (skylos)
295
+ - Additional static analysis
296
+
297
+ This action wraps the existing comprehensive hooks execution logic from
298
+ WorkflowPipeline passed explicitly in context.
299
+
300
+ Args:
301
+ context: Workflow execution context with "options" and "pipeline" keys
302
+ step_id: Step identifier (unused, for ACB compatibility)
303
+ event_bus: WorkflowEventBus for event emission (injected)
304
+ **params: Additional step parameters
305
+
306
+ Returns:
307
+ dict with comprehensive hooks execution results
308
+
309
+ Raises:
310
+ RuntimeError: If comprehensive hooks execution fails
311
+ """
312
+ options: OptionsProtocol = context.get("options") # type: ignore[assignment]
313
+ if not options:
314
+ msg = "Missing 'options' in workflow context"
315
+ raise ValueError(msg)
316
+
317
+ # Phase 4.1: Get pipeline from context instead of DI injection
318
+ pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
319
+ if not pipeline:
320
+ msg = "WorkflowPipeline not available in context"
321
+ raise RuntimeError(msg)
322
+
323
+ # Phase 7.2: Emit start event
324
+ start_time = time.time()
325
+ if event_bus:
326
+ await event_bus.publish(
327
+ WorkflowEvent.HOOK_STRATEGY_STARTED,
328
+ {"step_id": step_id, "strategy": "comprehensive", "timestamp": start_time},
329
+ )
330
+
331
+ # Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
332
+ # Use asyncio.to_thread to avoid blocking event loop with synchronous operations
333
+ try:
334
+ success = await asyncio.to_thread(
335
+ pipeline._run_comprehensive_hooks_phase,
336
+ options,
337
+ )
338
+ except Exception as exc:
339
+ # Phase 7.2: Emit failure event
340
+ if event_bus:
341
+ await event_bus.publish(
342
+ WorkflowEvent.HOOK_STRATEGY_FAILED,
343
+ {
344
+ "step_id": step_id,
345
+ "strategy": "comprehensive",
346
+ "error": str(exc),
347
+ "timestamp": time.time(),
348
+ "duration": time.time() - start_time,
349
+ },
350
+ )
351
+ raise
352
+
353
+ if not success:
354
+ # Phase 7.2: Emit failure event
355
+ if event_bus:
356
+ await event_bus.publish(
357
+ WorkflowEvent.HOOK_STRATEGY_FAILED,
358
+ {
359
+ "step_id": step_id,
360
+ "strategy": "comprehensive",
361
+ "timestamp": time.time(),
362
+ "duration": time.time() - start_time,
363
+ },
364
+ )
365
+ msg = "Comprehensive hooks execution failed"
366
+ raise RuntimeError(msg)
367
+
368
+ # Phase 7.2: Emit completion event
369
+ if event_bus:
370
+ await event_bus.publish(
371
+ WorkflowEvent.HOOK_STRATEGY_COMPLETED,
372
+ {
373
+ "step_id": step_id,
374
+ "strategy": "comprehensive",
375
+ "success": True,
376
+ "timestamp": time.time(),
377
+ "duration": time.time() - start_time,
378
+ },
379
+ )
380
+
381
+ return {
382
+ "phase": "comprehensive",
383
+ "success": True,
384
+ "message": "Comprehensive hooks completed successfully",
385
+ }
386
+
387
+
388
+ async def _handle_test_exception(
389
+ event_bus: WorkflowEventBus | None,
390
+ step_id: str,
391
+ start_time: float,
392
+ exc: Exception,
393
+ ) -> None:
394
+ """Handle exceptions during test execution."""
395
+ # Phase 7.2: Emit failure event
396
+ if event_bus:
397
+ await event_bus.publish(
398
+ WorkflowEvent.WORKFLOW_FAILED,
399
+ {
400
+ "step_id": step_id,
401
+ "phase": "testing",
402
+ "error": str(exc),
403
+ "timestamp": time.time(),
404
+ "duration": time.time() - start_time,
405
+ },
406
+ )
407
+ # Log the exception details before re-raising
408
+ import traceback
409
+
410
+ print(f"❌ Test execution failed with exception: {exc}")
411
+ print(f"Traceback: {traceback.format_exc()}")
412
+
413
+
414
+ async def _handle_test_failure(
415
+ pipeline: WorkflowPipeline,
416
+ event_bus: WorkflowEventBus | None,
417
+ step_id: str,
418
+ start_time: float,
419
+ ) -> None:
420
+ """Handle test failure cases."""
421
+ # Phase 7.2: Emit failure event
422
+ if event_bus:
423
+ await event_bus.publish(
424
+ WorkflowEvent.WORKFLOW_FAILED,
425
+ {
426
+ "step_id": step_id,
427
+ "phase": "testing",
428
+ "timestamp": time.time(),
429
+ "duration": time.time() - start_time,
430
+ },
431
+ )
432
+
433
+ # Try to get test failure details from test manager
434
+ test_manager = pipeline.phases.test_manager
435
+ if hasattr(test_manager, "get_test_failures"):
436
+ failures = test_manager.get_test_failures()
437
+ if failures:
438
+ print("\n📋 Test failures detected:")
439
+ for i, failure in enumerate(failures[:10], 1): # Show first 10 failures
440
+ print(f" {i}. {failure.strip()}")
441
+
442
+
443
+ async def _handle_test_completion(
444
+ event_bus: WorkflowEventBus | None,
445
+ step_id: str,
446
+ start_time: float,
447
+ ) -> None:
448
+ """Handle test completion event."""
449
+ # Phase 7.2: Emit completion event
450
+ if event_bus:
451
+ await event_bus.publish(
452
+ WorkflowEvent.QUALITY_PHASE_COMPLETED,
453
+ {
454
+ "step_id": step_id,
455
+ "phase": "testing",
456
+ "success": True,
457
+ "timestamp": time.time(),
458
+ "duration": time.time() - start_time,
459
+ },
460
+ )
461
+
462
+
463
+ @depends.inject # type: ignore[misc]
464
+ async def run_test_workflow(
465
+ context: dict[str, t.Any],
466
+ step_id: str,
467
+ event_bus: Inject[WorkflowEventBus] = None,
468
+ **params: t.Any,
469
+ ) -> dict[str, t.Any]:
470
+ """Execute test workflow.
471
+
472
+ This action runs the test suite using pytest with configured options.
473
+
474
+ Args:
475
+ context: Workflow execution context with "options" and "pipeline" keys
476
+ step_id: Step identifier (unused, for ACB compatibility)
477
+ event_bus: WorkflowEventBus for event emission (injected)
478
+ **params: Additional step parameters
479
+
480
+ Returns:
481
+ dict with test execution results
482
+
483
+ Raises:
484
+ RuntimeError: If test execution fails
485
+ """
486
+ options: OptionsProtocol = context.get("options") # type: ignore[assignment]
487
+ if not options:
488
+ msg = "Missing 'options' in workflow context"
489
+ raise ValueError(msg)
490
+
491
+ # Phase 4.1: Get pipeline from context instead of DI injection
492
+ pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
493
+ if not pipeline:
494
+ msg = "WorkflowPipeline not available in context"
495
+ raise RuntimeError(msg)
496
+
497
+ # Phase 7.2: Emit start event
498
+ start_time = time.time()
499
+ if event_bus:
500
+ await event_bus.publish(
501
+ WorkflowEvent.QUALITY_PHASE_STARTED,
502
+ {"step_id": step_id, "phase": "testing", "timestamp": start_time},
503
+ )
504
+
505
+ # Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
506
+ # Use asyncio.to_thread to avoid blocking event loop with synchronous operations
507
+ try:
508
+ success = await asyncio.to_thread(
509
+ pipeline._run_testing_phase,
510
+ options,
511
+ )
512
+ except Exception as exc:
513
+ await _handle_test_exception(event_bus, step_id, start_time, exc)
514
+ raise
515
+
516
+ if not success:
517
+ await _handle_test_failure(pipeline, event_bus, step_id, start_time)
518
+ msg = "Test workflow execution failed"
519
+ raise RuntimeError(msg)
520
+
521
+ await _handle_test_completion(event_bus, step_id, start_time)
522
+
523
+ return {
524
+ "phase": "test_workflow",
525
+ "success": True,
526
+ "message": "Test workflow completed successfully",
527
+ }
528
+
529
+
530
+ async def run_hook(
531
+ context: dict[str, t.Any],
532
+ step_id: str,
533
+ hook_name: str,
534
+ **params: t.Any,
535
+ ) -> dict[str, t.Any]:
536
+ """Execute a single hook by name.
537
+
538
+ This action is used for Phase 3 hook-level parallelization where
539
+ individual hooks (zuban, bandit, gitleaks, etc.) run as separate steps.
540
+
541
+ Args:
542
+ context: Workflow execution context with "options" key
543
+ step_id: Step identifier (unused, for ACB compatibility)
544
+ hook_name: Name of the hook to execute
545
+ **params: Additional step parameters
546
+
547
+ Returns:
548
+ dict with hook execution results
549
+
550
+ Raises:
551
+ NotImplementedError: Phase 3 feature not yet implemented
552
+ """
553
+ msg = "Hook-level parallelization is a Phase 3 feature (not yet implemented)"
554
+ raise NotImplementedError(msg)
555
+
556
+
557
+ @depends.inject # type: ignore[misc]
558
+ async def run_commit_phase(
559
+ context: dict[str, t.Any],
560
+ step_id: str,
561
+ event_bus: Inject[WorkflowEventBus] = None,
562
+ **params: t.Any,
563
+ ) -> dict[str, t.Any]:
564
+ """Execute git commit phase.
565
+
566
+ This action commits all changes to git and optionally pushes to remote.
567
+ Only runs if options.commit is True.
568
+
569
+ Args:
570
+ context: Workflow execution context with "options" and "pipeline" keys
571
+ step_id: Step identifier (unused, for ACB compatibility)
572
+ event_bus: WorkflowEventBus for event emission (injected)
573
+ **params: Additional step parameters
574
+
575
+ Returns:
576
+ dict with commit execution results
577
+
578
+ Raises:
579
+ RuntimeError: If commit execution fails
580
+ """
581
+ options: OptionsProtocol = context.get("options") # type: ignore[assignment]
582
+ if not options:
583
+ msg = "Missing 'options' in workflow context"
584
+ raise ValueError(msg)
585
+
586
+ # Skip commit unless --commit flag is set
587
+ if not getattr(options, "commit", False):
588
+ return {
589
+ "phase": "commit",
590
+ "success": True,
591
+ "skipped": True,
592
+ "reason": "Commit phase only runs with --commit flag",
593
+ }
594
+
595
+ # Phase 4.1: Get pipeline from context instead of DI injection
596
+ pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
597
+ if not pipeline:
598
+ msg = "WorkflowPipeline not available in context"
599
+ raise RuntimeError(msg)
600
+
601
+ # Phase 7.2: Emit start event
602
+ start_time = time.time()
603
+ if event_bus:
604
+ await event_bus.publish(
605
+ WorkflowEvent.QUALITY_PHASE_STARTED,
606
+ {"step_id": step_id, "phase": "commit", "timestamp": start_time},
607
+ )
608
+
609
+ # Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
610
+ # Use asyncio.to_thread to avoid blocking event loop with synchronous operations
611
+ try:
612
+ success = await asyncio.to_thread(
613
+ pipeline.phases.run_commit_phase,
614
+ options,
615
+ )
616
+ except Exception as exc:
617
+ # Phase 7.2: Emit failure event
618
+ if event_bus:
619
+ await event_bus.publish(
620
+ WorkflowEvent.WORKFLOW_FAILED,
621
+ {
622
+ "step_id": step_id,
623
+ "phase": "commit",
624
+ "error": str(exc),
625
+ "timestamp": time.time(),
626
+ "duration": time.time() - start_time,
627
+ },
628
+ )
629
+ raise
630
+
631
+ if not success:
632
+ # Phase 7.2: Emit failure event
633
+ if event_bus:
634
+ await event_bus.publish(
635
+ WorkflowEvent.WORKFLOW_FAILED,
636
+ {
637
+ "step_id": step_id,
638
+ "phase": "commit",
639
+ "timestamp": time.time(),
640
+ "duration": time.time() - start_time,
641
+ },
642
+ )
643
+ msg = "Git commit execution failed"
644
+ raise RuntimeError(msg)
645
+
646
+ # Phase 7.2: Emit completion event
647
+ if event_bus:
648
+ await event_bus.publish(
649
+ WorkflowEvent.QUALITY_PHASE_COMPLETED,
650
+ {
651
+ "step_id": step_id,
652
+ "phase": "commit",
653
+ "success": True,
654
+ "timestamp": time.time(),
655
+ "duration": time.time() - start_time,
656
+ },
657
+ )
658
+
659
+ return {
660
+ "phase": "commit",
661
+ "success": True,
662
+ "message": "Git commit completed successfully",
663
+ }
664
+
665
+
666
+ @depends.inject # type: ignore[misc]
667
+ async def run_publish_phase(
668
+ context: dict[str, t.Any],
669
+ step_id: str,
670
+ event_bus: Inject[WorkflowEventBus] = None,
671
+ **params: t.Any,
672
+ ) -> dict[str, t.Any]:
673
+ """Execute publishing phase (version bump and PyPI publish).
674
+
675
+ This action bumps the version and publishes to PyPI.
676
+ Only runs if options.publish, options.all, or options.bump is set.
677
+
678
+ Args:
679
+ context: Workflow execution context with "options" and "pipeline" keys
680
+ step_id: Step identifier (unused, for ACB compatibility)
681
+ event_bus: WorkflowEventBus for event emission (injected)
682
+ **params: Additional step parameters
683
+
684
+ Returns:
685
+ dict with publish execution results
686
+
687
+ Raises:
688
+ RuntimeError: If publish execution fails
689
+ """
690
+ options: OptionsProtocol = context.get("options") # type: ignore[assignment]
691
+ if not options:
692
+ msg = "Missing 'options' in workflow context"
693
+ raise ValueError(msg)
694
+
695
+ # Skip publish unless --publish, --all, or --bump flags are set
696
+ if not any(
697
+ [
698
+ getattr(options, "publish", False),
699
+ getattr(options, "all", False),
700
+ getattr(options, "bump", False),
701
+ ]
702
+ ):
703
+ return {
704
+ "phase": "publish",
705
+ "success": True,
706
+ "skipped": True,
707
+ "reason": "Publish phase only runs with --publish, --all, or --bump flags",
708
+ }
709
+
710
+ # Phase 4.1: Get pipeline from context instead of DI injection
711
+ pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
712
+ if not pipeline:
713
+ msg = "WorkflowPipeline not available in context"
714
+ raise RuntimeError(msg)
715
+
716
+ # Phase 7.2: Emit start event
717
+ start_time = time.time()
718
+ if event_bus:
719
+ await event_bus.publish(
720
+ WorkflowEvent.QUALITY_PHASE_STARTED,
721
+ {"step_id": step_id, "phase": "publish", "timestamp": start_time},
722
+ )
723
+
724
+ # Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
725
+ # Use asyncio.to_thread to avoid blocking event loop with synchronous operations
726
+ try:
727
+ success = await asyncio.to_thread(
728
+ pipeline.phases.run_publishing_phase,
729
+ options,
730
+ )
731
+ except Exception as exc:
732
+ # Phase 7.2: Emit failure event
733
+ if event_bus:
734
+ await event_bus.publish(
735
+ WorkflowEvent.WORKFLOW_FAILED,
736
+ {
737
+ "step_id": step_id,
738
+ "phase": "publish",
739
+ "error": str(exc),
740
+ "timestamp": time.time(),
741
+ "duration": time.time() - start_time,
742
+ },
743
+ )
744
+ raise
745
+
746
+ if not success:
747
+ # Phase 7.2: Emit failure event
748
+ if event_bus:
749
+ await event_bus.publish(
750
+ WorkflowEvent.WORKFLOW_FAILED,
751
+ {
752
+ "step_id": step_id,
753
+ "phase": "publish",
754
+ "timestamp": time.time(),
755
+ "duration": time.time() - start_time,
756
+ },
757
+ )
758
+ msg = "Publishing execution failed"
759
+ raise RuntimeError(msg)
760
+
761
+ # Phase 7.2: Emit completion event
762
+ if event_bus:
763
+ await event_bus.publish(
764
+ WorkflowEvent.QUALITY_PHASE_COMPLETED,
765
+ {
766
+ "step_id": step_id,
767
+ "phase": "publish",
768
+ "success": True,
769
+ "timestamp": time.time(),
770
+ "duration": time.time() - start_time,
771
+ },
772
+ )
773
+
774
+ return {
775
+ "phase": "publish",
776
+ "success": True,
777
+ "message": "Publishing completed successfully",
778
+ }
779
+
780
+
781
+ # Action registry for easy registration with engine
782
+ ACTION_REGISTRY: dict[str, t.Callable[..., t.Awaitable[t.Any]]] = {
783
+ "run_configuration": run_configuration,
784
+ "run_fast_hooks": run_fast_hooks,
785
+ "run_code_cleaning": run_code_cleaning,
786
+ "run_comprehensive_hooks": run_comprehensive_hooks,
787
+ "run_test_workflow": run_test_workflow,
788
+ "run_commit_phase": run_commit_phase,
789
+ "run_publish_phase": run_publish_phase,
790
+ "run_hook": run_hook,
791
+ }
792
+
793
+
794
+ def register_actions(engine: CrackerjackWorkflowEngine) -> None: # type: ignore[name-defined]
795
+ """Register all action handlers with the workflow engine.
796
+
797
+ This convenience function registers all action handlers from the
798
+ ACTION_REGISTRY with the provided engine.
799
+
800
+ Args:
801
+ engine: CrackerjackWorkflowEngine instance to register actions with
802
+
803
+ Example:
804
+ ```python
805
+ engine = CrackerjackWorkflowEngine()
806
+ register_actions(engine)
807
+ result = await engine.execute(FAST_HOOKS_WORKFLOW)
808
+ ```
809
+ """
810
+ for action_name, action_func in ACTION_REGISTRY.items():
811
+ engine.register_action(action_name, action_func)