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,630 @@
1
+ """Adaptive execution strategy with dependency-aware batching.
2
+
3
+ Implements intelligent parallel execution that:
4
+ - Analyzes dependency graph to identify independent groups
5
+ - Executes hooks in waves (parallel within wave, sequential between waves)
6
+ - Maximizes parallelism while respecting dependencies
7
+ - Handles critical failures with early exit
8
+
9
+ This is the optimal strategy for mixed workloads with some dependencies.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import asyncio
15
+ import logging
16
+ import typing as t
17
+ from contextlib import suppress
18
+
19
+ from crackerjack.config.hooks import HookDefinition, SecurityLevel
20
+ from crackerjack.models.task import HookResult
21
+
22
+ # Module-level logger
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class AdaptiveExecutionStrategy:
27
+ """Adaptive execution with dependency-aware batching.
28
+
29
+ Features:
30
+ - Topological sort for dependency-aware wave computation
31
+ - Parallel execution within each wave (independent hooks)
32
+ - Sequential execution between waves (respects dependencies)
33
+ - Early exit on critical security failures
34
+ - Resource limiting via asyncio.Semaphore
35
+
36
+ Algorithm:
37
+ 1. Compute execution waves using topological sort
38
+ 2. Wave 1: All hooks with zero dependencies (parallel)
39
+ 3. Wave 2: Hooks whose dependencies completed (parallel within wave)
40
+ 4. Wave N: Repeat until all hooks executed
41
+ 5. Stop early if critical hook fails
42
+
43
+ Example:
44
+ ```python
45
+ # Dependency graph:
46
+ # gitleaks → bandit
47
+ # zuban → refurb
48
+ # ruff-format (independent)
49
+
50
+ strategy = AdaptiveExecutionStrategy(
51
+ dependency_graph={"bandit": ["gitleaks"], "refurb": ["zuban"]}
52
+ )
53
+
54
+ # Execution waves:
55
+ # Wave 1 (parallel): gitleaks, zuban, ruff-format
56
+ # Wave 2 (parallel): bandit, refurb
57
+ ```
58
+ """
59
+
60
+ def __init__(
61
+ self,
62
+ dependency_graph: dict[str, list[str]],
63
+ max_parallel: int = 4,
64
+ default_timeout: int = 300,
65
+ stop_on_critical_failure: bool = True,
66
+ ) -> None:
67
+ """Initialize adaptive execution strategy.
68
+
69
+ Args:
70
+ dependency_graph: Hook dependencies (dependent → list of prerequisites)
71
+ max_parallel: Maximum concurrent executions per wave
72
+ default_timeout: Default timeout per hook in seconds
73
+ stop_on_critical_failure: Stop execution if critical hook fails
74
+ """
75
+ self.dependency_graph = dependency_graph
76
+ self.max_parallel = max_parallel
77
+ self.default_timeout = default_timeout
78
+ self.stop_on_critical_failure = stop_on_critical_failure
79
+
80
+ logger.debug(
81
+ "AdaptiveExecutionStrategy initialized",
82
+ extra={
83
+ "dependency_count": len(dependency_graph),
84
+ "max_parallel": max_parallel,
85
+ "default_timeout": default_timeout,
86
+ "stop_on_critical_failure": stop_on_critical_failure,
87
+ },
88
+ )
89
+
90
+ async def execute(
91
+ self,
92
+ hooks: list[HookDefinition],
93
+ max_parallel: int | None = None,
94
+ timeout: int | None = None,
95
+ executor_callable: t.Callable[[HookDefinition], t.Awaitable[HookResult]]
96
+ | None = None,
97
+ progress_callback: t.Callable[[int, int], None] | None = None,
98
+ progress_start_callback: t.Callable[[int, int], None] | None = None,
99
+ ) -> list[HookResult]:
100
+ """Execute hooks in dependency-aware waves.
101
+
102
+ Args:
103
+ hooks: List of hook definitions to execute
104
+ max_parallel: Optional override for max concurrent executions per wave
105
+ timeout: Optional override for default timeout
106
+ executor_callable: Async function that executes a single hook
107
+ progress_callback: Optional callback(completed, total) for progress updates
108
+
109
+ Returns:
110
+ List of HookResult objects (combined from all waves)
111
+ """
112
+ if not hooks:
113
+ logger.debug("No hooks to execute")
114
+ return []
115
+
116
+ max_par = max_parallel or self.max_parallel
117
+ timeout_sec = timeout or self.default_timeout
118
+
119
+ logger.info(
120
+ "Starting adaptive execution",
121
+ extra={
122
+ "hook_count": len(hooks),
123
+ "max_parallel": max_par,
124
+ "timeout": timeout_sec,
125
+ },
126
+ )
127
+
128
+ # Compute execution waves using topological sort
129
+ waves = self._compute_execution_waves(hooks)
130
+
131
+ logger.info(
132
+ f"Computed {len(waves)} execution waves",
133
+ extra={
134
+ "wave_count": len(waves),
135
+ "waves": [
136
+ {
137
+ "wave_idx": idx,
138
+ "hook_count": len(wave),
139
+ "hooks": [h.name for h in wave],
140
+ }
141
+ for idx, wave in enumerate(waves, 1)
142
+ ],
143
+ },
144
+ )
145
+
146
+ # Execute each wave in parallel, waves sequentially
147
+ all_results = []
148
+ total_hooks = len(hooks)
149
+ completed_hooks = 0
150
+
151
+ # Initial progress report
152
+ if progress_callback:
153
+ progress_callback(0, total_hooks)
154
+
155
+ for wave_idx, wave_hooks in enumerate(waves, 1):
156
+ logger.info(
157
+ f"Executing wave {wave_idx}/{len(waves)}",
158
+ extra={
159
+ "wave_idx": wave_idx,
160
+ "total_waves": len(waves),
161
+ "hooks_in_wave": len(wave_hooks),
162
+ "hook_names": [h.name for h in wave_hooks],
163
+ },
164
+ )
165
+
166
+ # Execute this wave in parallel
167
+ # Define per-hook completion notifier to update progress incrementally
168
+ def on_hook_start() -> None:
169
+ nonlocal completed_hooks
170
+ # Use started count as the metric to tick the progress bar
171
+ started = completed_hooks + 1
172
+ if progress_start_callback:
173
+ progress_start_callback(started, total_hooks)
174
+
175
+ def on_hook_complete() -> None:
176
+ nonlocal completed_hooks
177
+ completed_hooks += 1
178
+ if progress_callback:
179
+ progress_callback(completed_hooks, total_hooks)
180
+
181
+ wave_results = await self._execute_wave(
182
+ wave_hooks,
183
+ max_parallel=max_par,
184
+ timeout=timeout_sec,
185
+ executor_callable=executor_callable,
186
+ on_hook_complete=on_hook_complete,
187
+ on_hook_start=on_hook_start,
188
+ )
189
+
190
+ all_results.extend(wave_results)
191
+
192
+ logger.info(
193
+ f"Wave {wave_idx} complete",
194
+ extra={
195
+ "wave_idx": wave_idx,
196
+ "passed": sum(1 for r in wave_results if r.status == "passed"),
197
+ "failed": sum(1 for r in wave_results if r.status == "failed"),
198
+ "errors": sum(
199
+ 1 for r in wave_results if r.status in ("timeout", "error")
200
+ ),
201
+ },
202
+ )
203
+
204
+ # Check for critical failures that should stop execution
205
+ if self.stop_on_critical_failure and self._has_critical_failure(
206
+ wave_hooks, wave_results
207
+ ):
208
+ remaining_hooks = sum(len(w) for w in waves[wave_idx:])
209
+ logger.warning(
210
+ "Critical failure detected, stopping execution",
211
+ extra={
212
+ "completed_waves": wave_idx,
213
+ "remaining_waves": len(waves) - wave_idx,
214
+ "remaining_hooks": remaining_hooks,
215
+ },
216
+ )
217
+ break
218
+
219
+ logger.info(
220
+ "Adaptive execution complete",
221
+ extra={
222
+ "total_hooks": len(all_results),
223
+ "executed_waves": min(wave_idx, len(waves)),
224
+ "total_waves": len(waves),
225
+ "passed": sum(1 for r in all_results if r.status == "passed"),
226
+ "failed": sum(1 for r in all_results if r.status == "failed"),
227
+ "errors": sum(
228
+ 1 for r in all_results if r.status in ("timeout", "error")
229
+ ),
230
+ },
231
+ )
232
+
233
+ return all_results
234
+
235
+ def _compute_execution_waves(
236
+ self,
237
+ hooks: list[HookDefinition],
238
+ ) -> list[list[HookDefinition]]:
239
+ """Compute execution waves using topological sort.
240
+
241
+ Algorithm:
242
+ 1. Build in-degree map (count of unsatisfied dependencies per hook)
243
+ 2. Wave 1: All hooks with zero dependencies
244
+ 3. After wave completes, decrement in-degree for dependent hooks
245
+ 4. Next wave: Hooks that now have zero dependencies
246
+ 5. Repeat until all hooks assigned to waves
247
+
248
+ Returns:
249
+ List of waves, each wave contains hooks that can execute in parallel
250
+
251
+ Raises:
252
+ ValueError: If circular dependency detected
253
+ """
254
+ hook_map = {hook.name: hook for hook in hooks}
255
+ in_degree = self._build_in_degree_map(hooks, hook_map)
256
+
257
+ logger.debug(
258
+ "Computed in-degree map",
259
+ extra={
260
+ "in_degree": {name: degree for name, degree in in_degree.items()},
261
+ },
262
+ )
263
+
264
+ waves = self._compute_waves_from_dependencies(hooks, hook_map, in_degree)
265
+
266
+ logger.debug(
267
+ f"Computed {len(waves)} execution waves",
268
+ extra={
269
+ "wave_count": len(waves),
270
+ "total_hooks": sum(len(wave) for wave in waves),
271
+ },
272
+ )
273
+
274
+ return waves
275
+
276
+ def _build_in_degree_map(
277
+ self,
278
+ hooks: list[HookDefinition],
279
+ hook_map: dict[str, HookDefinition],
280
+ ) -> dict[str, int]:
281
+ """Build in-degree map counting dependencies per hook."""
282
+ in_degree = {hook.name: 0 for hook in hooks}
283
+ for hook_name in hook_map:
284
+ if hook_name in self.dependency_graph:
285
+ deps = self.dependency_graph[hook_name]
286
+ in_degree[hook_name] = sum(1 for dep in deps if dep in hook_map)
287
+ return in_degree
288
+
289
+ def _compute_waves_from_dependencies(
290
+ self,
291
+ hooks: list[HookDefinition],
292
+ hook_map: dict[str, HookDefinition],
293
+ in_degree: dict[str, int],
294
+ ) -> list[list[HookDefinition]]:
295
+ """Compute waves by iteratively processing hooks with satisfied dependencies."""
296
+ waves: list[list[HookDefinition]] = []
297
+ remaining_hooks = set(hook_map.keys())
298
+ iteration = 0
299
+ max_iterations = len(hooks) + 1
300
+
301
+ while remaining_hooks:
302
+ iteration += 1
303
+ if self._check_max_iterations_exceeded(
304
+ iteration, max_iterations, remaining_hooks, hook_map, waves
305
+ ):
306
+ break
307
+
308
+ ready_hooks = self._find_ready_hooks(remaining_hooks, in_degree, hook_map)
309
+ self._validate_wave_progress(ready_hooks, remaining_hooks, in_degree)
310
+
311
+ waves.append(ready_hooks)
312
+ self._log_wave_ready(waves, ready_hooks, remaining_hooks)
313
+
314
+ self._update_dependencies_after_wave(
315
+ ready_hooks, remaining_hooks, in_degree
316
+ )
317
+
318
+ return waves
319
+
320
+ def _check_max_iterations_exceeded(
321
+ self,
322
+ iteration: int,
323
+ max_iterations: int,
324
+ remaining_hooks: set[str],
325
+ hook_map: dict[str, HookDefinition],
326
+ waves: list[list[HookDefinition]],
327
+ ) -> bool:
328
+ """Check if max iterations exceeded and handle fallback."""
329
+ if iteration > max_iterations:
330
+ logger.error(
331
+ "Max iterations exceeded, circular dependency suspected",
332
+ extra={
333
+ "remaining_hooks": list(remaining_hooks),
334
+ "iteration": iteration,
335
+ },
336
+ )
337
+ waves.append([hook_map[name] for name in remaining_hooks])
338
+ return True
339
+ return False
340
+
341
+ def _find_ready_hooks(
342
+ self,
343
+ remaining_hooks: set[str],
344
+ in_degree: dict[str, int],
345
+ hook_map: dict[str, HookDefinition],
346
+ ) -> list[HookDefinition]:
347
+ """Find all hooks with zero dependencies in current wave."""
348
+ return [hook_map[name] for name in remaining_hooks if in_degree[name] == 0]
349
+
350
+ def _validate_wave_progress(
351
+ self,
352
+ ready_hooks: list[HookDefinition],
353
+ remaining_hooks: set[str],
354
+ in_degree: dict[str, int],
355
+ ) -> None:
356
+ """Validate that wave has ready hooks or raise circular dependency error."""
357
+ if not ready_hooks:
358
+ logger.error(
359
+ "Circular dependency detected",
360
+ extra={
361
+ "remaining_hooks": list(remaining_hooks),
362
+ "in_degrees": {name: in_degree[name] for name in remaining_hooks},
363
+ },
364
+ )
365
+ raise ValueError(
366
+ f"Circular dependency detected in hooks: {list(remaining_hooks)}"
367
+ )
368
+
369
+ def _log_wave_ready(
370
+ self,
371
+ waves: list[list[HookDefinition]],
372
+ ready_hooks: list[HookDefinition],
373
+ remaining_hooks: set[str],
374
+ ) -> None:
375
+ """Log wave readiness information."""
376
+ logger.debug(
377
+ f"Wave {len(waves)} ready",
378
+ extra={
379
+ "wave_idx": len(waves),
380
+ "hooks": [h.name for h in ready_hooks],
381
+ "remaining_count": len(remaining_hooks) - len(ready_hooks),
382
+ },
383
+ )
384
+
385
+ def _update_dependencies_after_wave(
386
+ self,
387
+ ready_hooks: list[HookDefinition],
388
+ remaining_hooks: set[str],
389
+ in_degree: dict[str, int],
390
+ ) -> None:
391
+ """Update in-degrees after wave completion."""
392
+ for hook in ready_hooks:
393
+ remaining_hooks.remove(hook.name)
394
+
395
+ for hook in ready_hooks:
396
+ for dependent_name, deps in self.dependency_graph.items():
397
+ if hook.name in deps and dependent_name in remaining_hooks:
398
+ in_degree[dependent_name] -= 1
399
+ logger.debug(
400
+ f"Decremented in-degree for {dependent_name}",
401
+ extra={
402
+ "dependent": dependent_name,
403
+ "completed_dependency": hook.name,
404
+ "new_in_degree": in_degree[dependent_name],
405
+ },
406
+ )
407
+
408
+ async def _execute_wave(
409
+ self,
410
+ hooks: list[HookDefinition],
411
+ max_parallel: int,
412
+ timeout: int,
413
+ executor_callable: t.Callable[[HookDefinition], t.Awaitable[HookResult]] | None,
414
+ on_hook_complete: t.Callable[[], None] | None = None,
415
+ on_hook_start: t.Callable[[], None] | None = None,
416
+ ) -> list[HookResult]:
417
+ """Execute a single wave of hooks in parallel.
418
+
419
+ Args:
420
+ hooks: Hooks to execute in this wave
421
+ max_parallel: Maximum concurrent executions
422
+ timeout: Timeout per hook in seconds
423
+ executor_callable: Async function that executes a single hook
424
+
425
+ Returns:
426
+ List of HookResult objects for this wave
427
+ """
428
+ if not hooks:
429
+ return []
430
+
431
+ semaphore = asyncio.Semaphore(max_parallel)
432
+
433
+ async def execute_with_limit(hook: HookDefinition) -> HookResult:
434
+ """Execute hook with semaphore and timeout."""
435
+ async with semaphore:
436
+ self._notify_hook_start(on_hook_start)
437
+ result = await self._execute_single_hook_in_wave(
438
+ hook, timeout, executor_callable
439
+ )
440
+ self._notify_hook_complete(on_hook_complete)
441
+ return result
442
+
443
+ tasks = [execute_with_limit(hook) for hook in hooks]
444
+ results = await asyncio.gather(*tasks, return_exceptions=True)
445
+ return self._process_wave_results(hooks, results)
446
+
447
+ def _notify_hook_start(self, on_hook_start: t.Callable[[], None] | None) -> None:
448
+ """Notify that hook execution has started."""
449
+ if on_hook_start:
450
+ with suppress(Exception):
451
+ on_hook_start()
452
+
453
+ def _notify_hook_complete(
454
+ self, on_hook_complete: t.Callable[[], None] | None
455
+ ) -> None:
456
+ """Notify that hook execution has completed."""
457
+ if on_hook_complete:
458
+ with suppress(Exception):
459
+ on_hook_complete()
460
+
461
+ async def _execute_single_hook_in_wave(
462
+ self,
463
+ hook: HookDefinition,
464
+ timeout: int,
465
+ executor_callable: t.Callable[[HookDefinition], t.Awaitable[HookResult]] | None,
466
+ ) -> HookResult:
467
+ """Execute a single hook with timeout and error handling."""
468
+ try:
469
+ hook_timeout = hook.timeout or timeout
470
+ logger.debug(
471
+ f"Executing hook {hook.name}",
472
+ extra={
473
+ "hook": hook.name,
474
+ "timeout": hook_timeout,
475
+ },
476
+ )
477
+
478
+ result = await self._run_hook_with_executor(
479
+ hook, hook_timeout, executor_callable
480
+ )
481
+
482
+ logger.debug(
483
+ f"Hook {hook.name} completed",
484
+ extra={
485
+ "hook": hook.name,
486
+ "status": result.status,
487
+ "duration": result.duration,
488
+ },
489
+ )
490
+ return result
491
+
492
+ except TimeoutError:
493
+ return self._create_timeout_result(hook, hook.timeout or timeout)
494
+ except Exception as e:
495
+ return self._create_error_result(hook, e)
496
+
497
+ async def _run_hook_with_executor(
498
+ self,
499
+ hook: HookDefinition,
500
+ hook_timeout: int,
501
+ executor_callable: t.Callable[[HookDefinition], t.Awaitable[HookResult]] | None,
502
+ ) -> HookResult:
503
+ """Run hook with executor or return placeholder."""
504
+ if executor_callable:
505
+ return await asyncio.wait_for(executor_callable(hook), timeout=hook_timeout)
506
+ return HookResult(
507
+ id=hook.name,
508
+ name=hook.name,
509
+ status="passed",
510
+ duration=0.0,
511
+ issues_found=[], # Initialize with empty list to match expected format
512
+ files_processed=0,
513
+ )
514
+
515
+ def _create_timeout_result(
516
+ self, hook: HookDefinition, hook_timeout: int
517
+ ) -> HookResult:
518
+ """Create HookResult for timeout condition."""
519
+ logger.warning(
520
+ f"Hook {hook.name} timed out",
521
+ extra={
522
+ "hook": hook.name,
523
+ "timeout": hook_timeout,
524
+ },
525
+ )
526
+ return HookResult(
527
+ id=hook.name,
528
+ name=hook.name,
529
+ status="timeout",
530
+ duration=hook_timeout,
531
+ )
532
+
533
+ def _create_error_result(
534
+ self, hook: HookDefinition, exception: Exception
535
+ ) -> HookResult:
536
+ """Create HookResult for exception condition."""
537
+ logger.error(
538
+ f"Hook {hook.name} raised exception",
539
+ extra={
540
+ "hook": hook.name,
541
+ "exception": str(exception),
542
+ "exception_type": type(exception).__name__,
543
+ },
544
+ )
545
+ return HookResult(
546
+ id=hook.name,
547
+ name=hook.name,
548
+ status="error",
549
+ duration=0.0,
550
+ )
551
+
552
+ def _process_wave_results(
553
+ self,
554
+ hooks: list[HookDefinition],
555
+ results: list[HookResult | BaseException],
556
+ ) -> list[HookResult]:
557
+ """Process results from asyncio.gather, converting exceptions to HookResults."""
558
+ final_results = []
559
+ for hook, result in zip(hooks, results):
560
+ if isinstance(result, HookResult):
561
+ final_results.append(result)
562
+ else:
563
+ logger.error(
564
+ "Unexpected exception from gather",
565
+ extra={
566
+ "hook": hook.name,
567
+ "exception": str(result),
568
+ "exception_type": type(result).__name__,
569
+ },
570
+ )
571
+ final_results.append(
572
+ HookResult(
573
+ id=hook.name,
574
+ name=hook.name,
575
+ status="error",
576
+ duration=0.0,
577
+ )
578
+ )
579
+ return final_results
580
+
581
+ def _has_critical_failure(
582
+ self,
583
+ hooks: list[HookDefinition],
584
+ results: list[HookResult],
585
+ ) -> bool:
586
+ """Check if any critical failure occurred in wave results.
587
+
588
+ Args:
589
+ hooks: Hooks that were executed
590
+ results: Results from execution
591
+
592
+ Returns:
593
+ True if a critical-level hook failed, False otherwise
594
+ """
595
+ for hook, result in zip(hooks, results):
596
+ if hook.security_level == SecurityLevel.CRITICAL:
597
+ if result.status in ("failed", "timeout", "error"):
598
+ logger.warning(
599
+ f"Critical hook {hook.name} failed",
600
+ extra={
601
+ "hook": hook.name,
602
+ "status": result.status,
603
+ "security_level": "critical",
604
+ },
605
+ )
606
+ return True
607
+
608
+ return False
609
+
610
+ def get_execution_order(
611
+ self,
612
+ hooks: list[HookDefinition],
613
+ ) -> list[list[HookDefinition]]:
614
+ """Return batches of hooks for execution (waves).
615
+
616
+ Args:
617
+ hooks: List of hook definitions
618
+
619
+ Returns:
620
+ List of batches (waves), each containing hooks that can execute in parallel
621
+ """
622
+ try:
623
+ return self._compute_execution_waves(hooks)
624
+ except ValueError as e:
625
+ # Circular dependency detected, fall back to sequential
626
+ logger.error(
627
+ f"Failed to compute waves: {e}, falling back to sequential execution",
628
+ extra={"error": str(e)},
629
+ )
630
+ return [[hook] for hook in hooks] # Sequential fallback