crackerjack 0.18.2__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 (533) hide show
  1. crackerjack/README.md +19 -0
  2. crackerjack/__init__.py +96 -2
  3. crackerjack/__main__.py +637 -138
  4. crackerjack/adapters/README.md +18 -0
  5. crackerjack/adapters/__init__.py +39 -0
  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/lsp/_base.py +194 -0
  27. crackerjack/adapters/lsp/_client.py +358 -0
  28. crackerjack/adapters/lsp/_manager.py +193 -0
  29. crackerjack/adapters/lsp/skylos.py +283 -0
  30. crackerjack/adapters/lsp/zuban.py +557 -0
  31. crackerjack/adapters/refactor/README.md +59 -0
  32. crackerjack/adapters/refactor/__init__.py +12 -0
  33. crackerjack/adapters/refactor/creosote.py +318 -0
  34. crackerjack/adapters/refactor/refurb.py +406 -0
  35. crackerjack/adapters/refactor/skylos.py +494 -0
  36. crackerjack/adapters/sast/README.md +132 -0
  37. crackerjack/adapters/sast/__init__.py +32 -0
  38. crackerjack/adapters/sast/_base.py +201 -0
  39. crackerjack/adapters/sast/bandit.py +423 -0
  40. crackerjack/adapters/sast/pyscn.py +405 -0
  41. crackerjack/adapters/sast/semgrep.py +241 -0
  42. crackerjack/adapters/security/README.md +111 -0
  43. crackerjack/adapters/security/__init__.py +17 -0
  44. crackerjack/adapters/security/gitleaks.py +339 -0
  45. crackerjack/adapters/type/README.md +52 -0
  46. crackerjack/adapters/type/__init__.py +12 -0
  47. crackerjack/adapters/type/pyrefly.py +402 -0
  48. crackerjack/adapters/type/ty.py +402 -0
  49. crackerjack/adapters/type/zuban.py +522 -0
  50. crackerjack/adapters/utility/README.md +51 -0
  51. crackerjack/adapters/utility/__init__.py +10 -0
  52. crackerjack/adapters/utility/checks.py +884 -0
  53. crackerjack/agents/README.md +264 -0
  54. crackerjack/agents/__init__.py +66 -0
  55. crackerjack/agents/architect_agent.py +238 -0
  56. crackerjack/agents/base.py +167 -0
  57. crackerjack/agents/claude_code_bridge.py +641 -0
  58. crackerjack/agents/coordinator.py +600 -0
  59. crackerjack/agents/documentation_agent.py +520 -0
  60. crackerjack/agents/dry_agent.py +585 -0
  61. crackerjack/agents/enhanced_coordinator.py +279 -0
  62. crackerjack/agents/enhanced_proactive_agent.py +185 -0
  63. crackerjack/agents/error_middleware.py +53 -0
  64. crackerjack/agents/formatting_agent.py +230 -0
  65. crackerjack/agents/helpers/__init__.py +9 -0
  66. crackerjack/agents/helpers/performance/__init__.py +22 -0
  67. crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
  68. crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
  69. crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
  70. crackerjack/agents/helpers/refactoring/__init__.py +22 -0
  71. crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
  72. crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
  73. crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
  74. crackerjack/agents/helpers/test_creation/__init__.py +19 -0
  75. crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
  76. crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
  77. crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
  78. crackerjack/agents/import_optimization_agent.py +1181 -0
  79. crackerjack/agents/performance_agent.py +325 -0
  80. crackerjack/agents/performance_helpers.py +205 -0
  81. crackerjack/agents/proactive_agent.py +55 -0
  82. crackerjack/agents/refactoring_agent.py +511 -0
  83. crackerjack/agents/refactoring_helpers.py +247 -0
  84. crackerjack/agents/security_agent.py +793 -0
  85. crackerjack/agents/semantic_agent.py +479 -0
  86. crackerjack/agents/semantic_helpers.py +356 -0
  87. crackerjack/agents/test_creation_agent.py +570 -0
  88. crackerjack/agents/test_specialist_agent.py +526 -0
  89. crackerjack/agents/tracker.py +110 -0
  90. crackerjack/api.py +647 -0
  91. crackerjack/cli/README.md +394 -0
  92. crackerjack/cli/__init__.py +24 -0
  93. crackerjack/cli/cache_handlers.py +209 -0
  94. crackerjack/cli/cache_handlers_enhanced.py +680 -0
  95. crackerjack/cli/facade.py +162 -0
  96. crackerjack/cli/formatting.py +13 -0
  97. crackerjack/cli/handlers/__init__.py +85 -0
  98. crackerjack/cli/handlers/advanced.py +103 -0
  99. crackerjack/cli/handlers/ai_features.py +62 -0
  100. crackerjack/cli/handlers/analytics.py +479 -0
  101. crackerjack/cli/handlers/changelog.py +271 -0
  102. crackerjack/cli/handlers/config_handlers.py +16 -0
  103. crackerjack/cli/handlers/coverage.py +84 -0
  104. crackerjack/cli/handlers/documentation.py +280 -0
  105. crackerjack/cli/handlers/main_handlers.py +497 -0
  106. crackerjack/cli/handlers/monitoring.py +371 -0
  107. crackerjack/cli/handlers.py +700 -0
  108. crackerjack/cli/interactive.py +488 -0
  109. crackerjack/cli/options.py +1216 -0
  110. crackerjack/cli/semantic_handlers.py +292 -0
  111. crackerjack/cli/utils.py +19 -0
  112. crackerjack/cli/version.py +19 -0
  113. crackerjack/code_cleaner.py +1307 -0
  114. crackerjack/config/README.md +472 -0
  115. crackerjack/config/__init__.py +275 -0
  116. crackerjack/config/global_lock_config.py +207 -0
  117. crackerjack/config/hooks.py +390 -0
  118. crackerjack/config/loader.py +239 -0
  119. crackerjack/config/settings.py +141 -0
  120. crackerjack/config/tool_commands.py +331 -0
  121. crackerjack/core/README.md +393 -0
  122. crackerjack/core/__init__.py +0 -0
  123. crackerjack/core/async_workflow_orchestrator.py +738 -0
  124. crackerjack/core/autofix_coordinator.py +282 -0
  125. crackerjack/core/container.py +105 -0
  126. crackerjack/core/enhanced_container.py +583 -0
  127. crackerjack/core/file_lifecycle.py +472 -0
  128. crackerjack/core/performance.py +244 -0
  129. crackerjack/core/performance_monitor.py +357 -0
  130. crackerjack/core/phase_coordinator.py +1227 -0
  131. crackerjack/core/proactive_workflow.py +267 -0
  132. crackerjack/core/resource_manager.py +425 -0
  133. crackerjack/core/retry.py +275 -0
  134. crackerjack/core/service_watchdog.py +601 -0
  135. crackerjack/core/session_coordinator.py +239 -0
  136. crackerjack/core/timeout_manager.py +563 -0
  137. crackerjack/core/websocket_lifecycle.py +410 -0
  138. crackerjack/core/workflow/__init__.py +21 -0
  139. crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
  140. crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
  141. crackerjack/core/workflow/workflow_issue_parser.py +714 -0
  142. crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
  143. crackerjack/core/workflow/workflow_security_gates.py +400 -0
  144. crackerjack/core/workflow_orchestrator.py +2243 -0
  145. crackerjack/data/README.md +11 -0
  146. crackerjack/data/__init__.py +8 -0
  147. crackerjack/data/models.py +79 -0
  148. crackerjack/data/repository.py +210 -0
  149. crackerjack/decorators/README.md +180 -0
  150. crackerjack/decorators/__init__.py +35 -0
  151. crackerjack/decorators/error_handling.py +649 -0
  152. crackerjack/decorators/error_handling_decorators.py +334 -0
  153. crackerjack/decorators/helpers.py +58 -0
  154. crackerjack/decorators/patterns.py +281 -0
  155. crackerjack/decorators/utils.py +58 -0
  156. crackerjack/docs/INDEX.md +11 -0
  157. crackerjack/docs/README.md +11 -0
  158. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  159. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  160. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  161. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  162. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  163. crackerjack/documentation/README.md +11 -0
  164. crackerjack/documentation/__init__.py +31 -0
  165. crackerjack/documentation/ai_templates.py +756 -0
  166. crackerjack/documentation/dual_output_generator.py +767 -0
  167. crackerjack/documentation/mkdocs_integration.py +518 -0
  168. crackerjack/documentation/reference_generator.py +1065 -0
  169. crackerjack/dynamic_config.py +678 -0
  170. crackerjack/errors.py +378 -0
  171. crackerjack/events/README.md +11 -0
  172. crackerjack/events/__init__.py +16 -0
  173. crackerjack/events/telemetry.py +175 -0
  174. crackerjack/events/workflow_bus.py +346 -0
  175. crackerjack/exceptions/README.md +301 -0
  176. crackerjack/exceptions/__init__.py +5 -0
  177. crackerjack/exceptions/config.py +4 -0
  178. crackerjack/exceptions/tool_execution_error.py +245 -0
  179. crackerjack/executors/README.md +591 -0
  180. crackerjack/executors/__init__.py +13 -0
  181. crackerjack/executors/async_hook_executor.py +938 -0
  182. crackerjack/executors/cached_hook_executor.py +316 -0
  183. crackerjack/executors/hook_executor.py +1295 -0
  184. crackerjack/executors/hook_lock_manager.py +708 -0
  185. crackerjack/executors/individual_hook_executor.py +739 -0
  186. crackerjack/executors/lsp_aware_hook_executor.py +349 -0
  187. crackerjack/executors/progress_hook_executor.py +282 -0
  188. crackerjack/executors/tool_proxy.py +433 -0
  189. crackerjack/hooks/README.md +485 -0
  190. crackerjack/hooks/lsp_hook.py +93 -0
  191. crackerjack/intelligence/README.md +557 -0
  192. crackerjack/intelligence/__init__.py +37 -0
  193. crackerjack/intelligence/adaptive_learning.py +693 -0
  194. crackerjack/intelligence/agent_orchestrator.py +485 -0
  195. crackerjack/intelligence/agent_registry.py +377 -0
  196. crackerjack/intelligence/agent_selector.py +439 -0
  197. crackerjack/intelligence/integration.py +250 -0
  198. crackerjack/interactive.py +719 -0
  199. crackerjack/managers/README.md +369 -0
  200. crackerjack/managers/__init__.py +11 -0
  201. crackerjack/managers/async_hook_manager.py +135 -0
  202. crackerjack/managers/hook_manager.py +585 -0
  203. crackerjack/managers/publish_manager.py +631 -0
  204. crackerjack/managers/test_command_builder.py +391 -0
  205. crackerjack/managers/test_executor.py +474 -0
  206. crackerjack/managers/test_manager.py +1357 -0
  207. crackerjack/managers/test_progress.py +187 -0
  208. crackerjack/mcp/README.md +374 -0
  209. crackerjack/mcp/__init__.py +0 -0
  210. crackerjack/mcp/cache.py +352 -0
  211. crackerjack/mcp/client_runner.py +121 -0
  212. crackerjack/mcp/context.py +802 -0
  213. crackerjack/mcp/dashboard.py +657 -0
  214. crackerjack/mcp/enhanced_progress_monitor.py +493 -0
  215. crackerjack/mcp/file_monitor.py +394 -0
  216. crackerjack/mcp/progress_components.py +607 -0
  217. crackerjack/mcp/progress_monitor.py +1016 -0
  218. crackerjack/mcp/rate_limiter.py +336 -0
  219. crackerjack/mcp/server.py +24 -0
  220. crackerjack/mcp/server_core.py +526 -0
  221. crackerjack/mcp/service_watchdog.py +505 -0
  222. crackerjack/mcp/state.py +407 -0
  223. crackerjack/mcp/task_manager.py +259 -0
  224. crackerjack/mcp/tools/README.md +27 -0
  225. crackerjack/mcp/tools/__init__.py +19 -0
  226. crackerjack/mcp/tools/core_tools.py +469 -0
  227. crackerjack/mcp/tools/error_analyzer.py +283 -0
  228. crackerjack/mcp/tools/execution_tools.py +384 -0
  229. crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
  230. crackerjack/mcp/tools/intelligence_tools.py +264 -0
  231. crackerjack/mcp/tools/monitoring_tools.py +628 -0
  232. crackerjack/mcp/tools/proactive_tools.py +367 -0
  233. crackerjack/mcp/tools/progress_tools.py +222 -0
  234. crackerjack/mcp/tools/semantic_tools.py +584 -0
  235. crackerjack/mcp/tools/utility_tools.py +358 -0
  236. crackerjack/mcp/tools/workflow_executor.py +699 -0
  237. crackerjack/mcp/websocket/README.md +31 -0
  238. crackerjack/mcp/websocket/__init__.py +14 -0
  239. crackerjack/mcp/websocket/app.py +54 -0
  240. crackerjack/mcp/websocket/endpoints.py +492 -0
  241. crackerjack/mcp/websocket/event_bridge.py +188 -0
  242. crackerjack/mcp/websocket/jobs.py +406 -0
  243. crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
  244. crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
  245. crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
  246. crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
  247. crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
  248. crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
  249. crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
  250. crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
  251. crackerjack/mcp/websocket/monitoring/factory.py +109 -0
  252. crackerjack/mcp/websocket/monitoring/filters.py +10 -0
  253. crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
  254. crackerjack/mcp/websocket/monitoring/models.py +90 -0
  255. crackerjack/mcp/websocket/monitoring/utils.py +171 -0
  256. crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
  257. crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
  258. crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
  259. crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
  260. crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
  261. crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
  262. crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
  263. crackerjack/mcp/websocket/server.py +174 -0
  264. crackerjack/mcp/websocket/websocket_handler.py +276 -0
  265. crackerjack/mcp/websocket_server.py +10 -0
  266. crackerjack/models/README.md +308 -0
  267. crackerjack/models/__init__.py +40 -0
  268. crackerjack/models/config.py +730 -0
  269. crackerjack/models/config_adapter.py +265 -0
  270. crackerjack/models/protocols.py +1535 -0
  271. crackerjack/models/pydantic_models.py +320 -0
  272. crackerjack/models/qa_config.py +145 -0
  273. crackerjack/models/qa_results.py +134 -0
  274. crackerjack/models/resource_protocols.py +299 -0
  275. crackerjack/models/results.py +35 -0
  276. crackerjack/models/semantic_models.py +258 -0
  277. crackerjack/models/task.py +173 -0
  278. crackerjack/models/test_models.py +60 -0
  279. crackerjack/monitoring/README.md +11 -0
  280. crackerjack/monitoring/__init__.py +0 -0
  281. crackerjack/monitoring/ai_agent_watchdog.py +405 -0
  282. crackerjack/monitoring/metrics_collector.py +427 -0
  283. crackerjack/monitoring/regression_prevention.py +580 -0
  284. crackerjack/monitoring/websocket_server.py +406 -0
  285. crackerjack/orchestration/README.md +340 -0
  286. crackerjack/orchestration/__init__.py +43 -0
  287. crackerjack/orchestration/advanced_orchestrator.py +894 -0
  288. crackerjack/orchestration/cache/README.md +312 -0
  289. crackerjack/orchestration/cache/__init__.py +37 -0
  290. crackerjack/orchestration/cache/memory_cache.py +338 -0
  291. crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
  292. crackerjack/orchestration/config.py +297 -0
  293. crackerjack/orchestration/coverage_improvement.py +180 -0
  294. crackerjack/orchestration/execution_strategies.py +361 -0
  295. crackerjack/orchestration/hook_orchestrator.py +1398 -0
  296. crackerjack/orchestration/strategies/README.md +401 -0
  297. crackerjack/orchestration/strategies/__init__.py +39 -0
  298. crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
  299. crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
  300. crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
  301. crackerjack/orchestration/test_progress_streamer.py +647 -0
  302. crackerjack/plugins/README.md +11 -0
  303. crackerjack/plugins/__init__.py +15 -0
  304. crackerjack/plugins/base.py +200 -0
  305. crackerjack/plugins/hooks.py +254 -0
  306. crackerjack/plugins/loader.py +335 -0
  307. crackerjack/plugins/managers.py +264 -0
  308. crackerjack/py313.py +191 -0
  309. crackerjack/security/README.md +11 -0
  310. crackerjack/security/__init__.py +0 -0
  311. crackerjack/security/audit.py +197 -0
  312. crackerjack/services/README.md +374 -0
  313. crackerjack/services/__init__.py +9 -0
  314. crackerjack/services/ai/README.md +295 -0
  315. crackerjack/services/ai/__init__.py +7 -0
  316. crackerjack/services/ai/advanced_optimizer.py +878 -0
  317. crackerjack/services/ai/contextual_ai_assistant.py +542 -0
  318. crackerjack/services/ai/embeddings.py +444 -0
  319. crackerjack/services/ai/intelligent_commit.py +328 -0
  320. crackerjack/services/ai/predictive_analytics.py +510 -0
  321. crackerjack/services/anomaly_detector.py +392 -0
  322. crackerjack/services/api_extractor.py +617 -0
  323. crackerjack/services/backup_service.py +467 -0
  324. crackerjack/services/bounded_status_operations.py +530 -0
  325. crackerjack/services/cache.py +369 -0
  326. crackerjack/services/changelog_automation.py +399 -0
  327. crackerjack/services/command_execution_service.py +305 -0
  328. crackerjack/services/config_integrity.py +132 -0
  329. crackerjack/services/config_merge.py +546 -0
  330. crackerjack/services/config_service.py +198 -0
  331. crackerjack/services/config_template.py +493 -0
  332. crackerjack/services/coverage_badge_service.py +173 -0
  333. crackerjack/services/coverage_ratchet.py +381 -0
  334. crackerjack/services/debug.py +733 -0
  335. crackerjack/services/dependency_analyzer.py +460 -0
  336. crackerjack/services/dependency_monitor.py +622 -0
  337. crackerjack/services/documentation_generator.py +493 -0
  338. crackerjack/services/documentation_service.py +704 -0
  339. crackerjack/services/enhanced_filesystem.py +497 -0
  340. crackerjack/services/enterprise_optimizer.py +865 -0
  341. crackerjack/services/error_pattern_analyzer.py +676 -0
  342. crackerjack/services/file_filter.py +221 -0
  343. crackerjack/services/file_hasher.py +149 -0
  344. crackerjack/services/file_io_service.py +361 -0
  345. crackerjack/services/file_modifier.py +615 -0
  346. crackerjack/services/filesystem.py +381 -0
  347. crackerjack/services/git.py +422 -0
  348. crackerjack/services/health_metrics.py +615 -0
  349. crackerjack/services/heatmap_generator.py +744 -0
  350. crackerjack/services/incremental_executor.py +380 -0
  351. crackerjack/services/initialization.py +823 -0
  352. crackerjack/services/input_validator.py +668 -0
  353. crackerjack/services/intelligent_commit.py +327 -0
  354. crackerjack/services/log_manager.py +289 -0
  355. crackerjack/services/logging.py +228 -0
  356. crackerjack/services/lsp_client.py +628 -0
  357. crackerjack/services/memory_optimizer.py +414 -0
  358. crackerjack/services/metrics.py +587 -0
  359. crackerjack/services/monitoring/README.md +30 -0
  360. crackerjack/services/monitoring/__init__.py +9 -0
  361. crackerjack/services/monitoring/dependency_monitor.py +678 -0
  362. crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
  363. crackerjack/services/monitoring/health_metrics.py +716 -0
  364. crackerjack/services/monitoring/metrics.py +587 -0
  365. crackerjack/services/monitoring/performance_benchmarks.py +410 -0
  366. crackerjack/services/monitoring/performance_cache.py +388 -0
  367. crackerjack/services/monitoring/performance_monitor.py +569 -0
  368. crackerjack/services/parallel_executor.py +527 -0
  369. crackerjack/services/pattern_cache.py +333 -0
  370. crackerjack/services/pattern_detector.py +478 -0
  371. crackerjack/services/patterns/__init__.py +142 -0
  372. crackerjack/services/patterns/agents.py +107 -0
  373. crackerjack/services/patterns/code/__init__.py +15 -0
  374. crackerjack/services/patterns/code/detection.py +118 -0
  375. crackerjack/services/patterns/code/imports.py +107 -0
  376. crackerjack/services/patterns/code/paths.py +159 -0
  377. crackerjack/services/patterns/code/performance.py +119 -0
  378. crackerjack/services/patterns/code/replacement.py +36 -0
  379. crackerjack/services/patterns/core.py +212 -0
  380. crackerjack/services/patterns/documentation/__init__.py +14 -0
  381. crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
  382. crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
  383. crackerjack/services/patterns/documentation/docstrings.py +89 -0
  384. crackerjack/services/patterns/formatting.py +226 -0
  385. crackerjack/services/patterns/operations.py +339 -0
  386. crackerjack/services/patterns/security/__init__.py +23 -0
  387. crackerjack/services/patterns/security/code_injection.py +122 -0
  388. crackerjack/services/patterns/security/credentials.py +190 -0
  389. crackerjack/services/patterns/security/path_traversal.py +221 -0
  390. crackerjack/services/patterns/security/unsafe_operations.py +216 -0
  391. crackerjack/services/patterns/templates.py +62 -0
  392. crackerjack/services/patterns/testing/__init__.py +18 -0
  393. crackerjack/services/patterns/testing/error_patterns.py +107 -0
  394. crackerjack/services/patterns/testing/pytest_output.py +126 -0
  395. crackerjack/services/patterns/tool_output/__init__.py +16 -0
  396. crackerjack/services/patterns/tool_output/bandit.py +72 -0
  397. crackerjack/services/patterns/tool_output/other.py +97 -0
  398. crackerjack/services/patterns/tool_output/pyright.py +67 -0
  399. crackerjack/services/patterns/tool_output/ruff.py +44 -0
  400. crackerjack/services/patterns/url_sanitization.py +114 -0
  401. crackerjack/services/patterns/utilities.py +42 -0
  402. crackerjack/services/patterns/utils.py +339 -0
  403. crackerjack/services/patterns/validation.py +46 -0
  404. crackerjack/services/patterns/versioning.py +62 -0
  405. crackerjack/services/predictive_analytics.py +523 -0
  406. crackerjack/services/profiler.py +280 -0
  407. crackerjack/services/quality/README.md +415 -0
  408. crackerjack/services/quality/__init__.py +11 -0
  409. crackerjack/services/quality/anomaly_detector.py +392 -0
  410. crackerjack/services/quality/pattern_cache.py +333 -0
  411. crackerjack/services/quality/pattern_detector.py +479 -0
  412. crackerjack/services/quality/qa_orchestrator.py +491 -0
  413. crackerjack/services/quality/quality_baseline.py +395 -0
  414. crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
  415. crackerjack/services/quality/quality_intelligence.py +949 -0
  416. crackerjack/services/regex_patterns.py +58 -0
  417. crackerjack/services/regex_utils.py +483 -0
  418. crackerjack/services/secure_path_utils.py +524 -0
  419. crackerjack/services/secure_status_formatter.py +450 -0
  420. crackerjack/services/secure_subprocess.py +635 -0
  421. crackerjack/services/security.py +239 -0
  422. crackerjack/services/security_logger.py +495 -0
  423. crackerjack/services/server_manager.py +411 -0
  424. crackerjack/services/smart_scheduling.py +167 -0
  425. crackerjack/services/status_authentication.py +460 -0
  426. crackerjack/services/status_security_manager.py +315 -0
  427. crackerjack/services/terminal_utils.py +0 -0
  428. crackerjack/services/thread_safe_status_collector.py +441 -0
  429. crackerjack/services/tool_filter.py +368 -0
  430. crackerjack/services/tool_version_service.py +43 -0
  431. crackerjack/services/unified_config.py +115 -0
  432. crackerjack/services/validation_rate_limiter.py +220 -0
  433. crackerjack/services/vector_store.py +689 -0
  434. crackerjack/services/version_analyzer.py +461 -0
  435. crackerjack/services/version_checker.py +223 -0
  436. crackerjack/services/websocket_resource_limiter.py +438 -0
  437. crackerjack/services/zuban_lsp_service.py +391 -0
  438. crackerjack/slash_commands/README.md +11 -0
  439. crackerjack/slash_commands/__init__.py +59 -0
  440. crackerjack/slash_commands/init.md +112 -0
  441. crackerjack/slash_commands/run.md +197 -0
  442. crackerjack/slash_commands/status.md +127 -0
  443. crackerjack/tools/README.md +11 -0
  444. crackerjack/tools/__init__.py +30 -0
  445. crackerjack/tools/_git_utils.py +105 -0
  446. crackerjack/tools/check_added_large_files.py +139 -0
  447. crackerjack/tools/check_ast.py +105 -0
  448. crackerjack/tools/check_json.py +103 -0
  449. crackerjack/tools/check_jsonschema.py +297 -0
  450. crackerjack/tools/check_toml.py +103 -0
  451. crackerjack/tools/check_yaml.py +110 -0
  452. crackerjack/tools/codespell_wrapper.py +72 -0
  453. crackerjack/tools/end_of_file_fixer.py +202 -0
  454. crackerjack/tools/format_json.py +128 -0
  455. crackerjack/tools/mdformat_wrapper.py +114 -0
  456. crackerjack/tools/trailing_whitespace.py +198 -0
  457. crackerjack/tools/validate_input_validator_patterns.py +236 -0
  458. crackerjack/tools/validate_regex_patterns.py +188 -0
  459. crackerjack/ui/README.md +11 -0
  460. crackerjack/ui/__init__.py +1 -0
  461. crackerjack/ui/dashboard_renderer.py +28 -0
  462. crackerjack/ui/templates/README.md +11 -0
  463. crackerjack/utils/console_utils.py +13 -0
  464. crackerjack/utils/dependency_guard.py +230 -0
  465. crackerjack/utils/retry_utils.py +275 -0
  466. crackerjack/workflows/README.md +590 -0
  467. crackerjack/workflows/__init__.py +46 -0
  468. crackerjack/workflows/actions.py +811 -0
  469. crackerjack/workflows/auto_fix.py +444 -0
  470. crackerjack/workflows/container_builder.py +499 -0
  471. crackerjack/workflows/definitions.py +443 -0
  472. crackerjack/workflows/engine.py +177 -0
  473. crackerjack/workflows/event_bridge.py +242 -0
  474. crackerjack-0.45.2.dist-info/METADATA +1678 -0
  475. crackerjack-0.45.2.dist-info/RECORD +478 -0
  476. {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
  477. crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
  478. crackerjack/.gitignore +0 -14
  479. crackerjack/.libcst.codemod.yaml +0 -18
  480. crackerjack/.pdm.toml +0 -1
  481. crackerjack/.pre-commit-config.yaml +0 -91
  482. crackerjack/.pytest_cache/.gitignore +0 -2
  483. crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
  484. crackerjack/.pytest_cache/README.md +0 -8
  485. crackerjack/.pytest_cache/v/cache/nodeids +0 -1
  486. crackerjack/.pytest_cache/v/cache/stepwise +0 -1
  487. crackerjack/.ruff_cache/.gitignore +0 -1
  488. crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
  489. crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
  490. crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
  491. crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
  492. crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
  493. crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
  494. crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
  495. crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
  496. crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
  497. crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
  498. crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
  499. crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
  500. crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
  501. crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
  502. crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
  503. crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
  504. crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
  505. crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
  506. crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
  507. crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
  508. crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
  509. crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
  510. crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
  511. crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
  512. crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
  513. crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
  514. crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
  515. crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
  516. crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
  517. crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
  518. crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
  519. crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
  520. crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
  521. crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
  522. crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
  523. crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
  524. crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
  525. crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
  526. crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
  527. crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
  528. crackerjack/crackerjack.py +0 -855
  529. crackerjack/pyproject.toml +0 -214
  530. crackerjack-0.18.2.dist-info/METADATA +0 -420
  531. crackerjack-0.18.2.dist-info/RECORD +0 -59
  532. crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
  533. {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,340 @@
1
+ """Tool proxy cache adapter for hook result caching.
2
+
3
+ Bridges to existing tool_proxy cache infrastructure for consistent caching
4
+ across the crackerjack ecosystem. Implements content-based cache keys using
5
+ hash of hook configuration and file contents.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import json
12
+ import logging
13
+ import typing as t
14
+ from contextlib import suppress
15
+ from pathlib import Path
16
+ from uuid import UUID
17
+
18
+ from pydantic import BaseModel, Field
19
+
20
+ from crackerjack.models.task import HookResult
21
+
22
+ if t.TYPE_CHECKING:
23
+ from crackerjack.config.hooks import HookDefinition
24
+
25
+ # ACB Module Registration (REQUIRED)
26
+ MODULE_ID = UUID("01937d86-ace0-7000-8000-000000000004") # Static UUID7
27
+ MODULE_STATUS = "stable"
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ class ToolProxyCacheSettings(BaseModel):
33
+ """Settings for tool proxy cache adapter."""
34
+
35
+ default_ttl: int = Field(default=3600, ge=60, le=86400) # 1 minute to 24 hours
36
+ max_cache_size_mb: int = Field(default=100, ge=10, le=1000)
37
+ enable_compression: bool = True
38
+
39
+
40
+ class ToolProxyCacheAdapter:
41
+ """Cache adapter bridging to tool_proxy infrastructure.
42
+
43
+ Features:
44
+ - Content-based cache keys (config + file hashes)
45
+ - Configurable TTL per hook result
46
+ - Integration with existing tool_proxy cache
47
+ - Automatic cache invalidation on content changes
48
+
49
+ Cache Key Format:
50
+ {hook_name}:{config_hash}:{content_hash}
51
+
52
+ Example:
53
+ ```python
54
+ cache = ToolProxyCacheAdapter()
55
+ await cache.init()
56
+
57
+ # Check cache
58
+ cached_result = await cache.get(cache_key)
59
+ if cached_result:
60
+ return cached_result
61
+
62
+ # Execute hook and cache result
63
+ result = await execute_hook(hook)
64
+ await cache.set(cache_key, result, ttl=3600)
65
+ ```
66
+ """
67
+
68
+ def __init__(
69
+ self,
70
+ settings: ToolProxyCacheSettings | None = None,
71
+ cache_dir: Path | None = None,
72
+ ) -> None:
73
+ """Initialize tool proxy cache adapter.
74
+
75
+ Args:
76
+ settings: Optional cache settings
77
+ cache_dir: Optional cache directory (defaults to .crackerjack/cache)
78
+ """
79
+ self.settings = settings or ToolProxyCacheSettings()
80
+ self._cache_dir = cache_dir or Path.cwd() / ".crackerjack" / "cache"
81
+ self._cache: dict[
82
+ str, tuple[HookResult, float]
83
+ ] = {} # key -> (result, expiry_timestamp)
84
+ self._initialized = False
85
+
86
+ logger.debug(
87
+ "ToolProxyCacheAdapter initializing",
88
+ extra={
89
+ "cache_dir": str(self._cache_dir),
90
+ "default_ttl": self.settings.default_ttl,
91
+ "enable_compression": self.settings.enable_compression,
92
+ },
93
+ )
94
+
95
+ async def init(self) -> None:
96
+ """Initialize cache adapter and ensure cache directory exists."""
97
+ if self._initialized:
98
+ logger.debug("Cache adapter already initialized")
99
+ return
100
+
101
+ try:
102
+ self._cache_dir.mkdir(parents=True, exist_ok=True)
103
+ self._initialized = True
104
+
105
+ logger.info(
106
+ "ToolProxyCacheAdapter initialized",
107
+ extra={
108
+ "cache_dir": str(self._cache_dir),
109
+ "default_ttl": self.settings.default_ttl,
110
+ },
111
+ )
112
+ except Exception as e:
113
+ logger.error(
114
+ "Failed to initialize cache adapter",
115
+ extra={
116
+ "error": str(e),
117
+ "cache_dir": str(self._cache_dir),
118
+ },
119
+ )
120
+ raise
121
+
122
+ async def get(self, key: str) -> HookResult | None:
123
+ """Retrieve cached hook result.
124
+
125
+ Args:
126
+ key: Cache key (computed via compute_key())
127
+
128
+ Returns:
129
+ Cached HookResult if found and not expired, None otherwise
130
+ """
131
+ if not self._initialized:
132
+ logger.warning("Cache adapter not initialized, returning None")
133
+ return None
134
+
135
+ try:
136
+ import time
137
+
138
+ if key in self._cache:
139
+ result, expiry = self._cache[key]
140
+
141
+ # Check expiration
142
+ if time.time() < expiry:
143
+ logger.debug(
144
+ "Cache hit",
145
+ extra={
146
+ "key": key,
147
+ "hook_name": result.name,
148
+ "status": result.status,
149
+ },
150
+ )
151
+ return result
152
+ else:
153
+ # Expired - remove from cache
154
+ del self._cache[key]
155
+ logger.debug("Cache entry expired", extra={"key": key})
156
+
157
+ logger.debug("Cache miss", extra={"key": key})
158
+ return None
159
+
160
+ except Exception as e:
161
+ logger.error(
162
+ "Failed to retrieve from cache",
163
+ extra={
164
+ "key": key,
165
+ "error": str(e),
166
+ },
167
+ )
168
+ return None
169
+
170
+ async def set(
171
+ self,
172
+ key: str,
173
+ result: HookResult,
174
+ ttl: int | None = None,
175
+ ) -> None:
176
+ """Cache hook result with TTL.
177
+
178
+ Args:
179
+ key: Cache key (computed via compute_key())
180
+ result: HookResult to cache
181
+ ttl: Optional time-to-live in seconds (defaults to settings.default_ttl)
182
+ """
183
+ if not self._initialized:
184
+ logger.warning("Cache adapter not initialized, skipping cache write")
185
+ return
186
+
187
+ try:
188
+ import time
189
+
190
+ ttl_sec = ttl or self.settings.default_ttl
191
+ expiry = time.time() + ttl_sec
192
+
193
+ self._cache[key] = (result, expiry)
194
+
195
+ logger.debug(
196
+ "Cache write",
197
+ extra={
198
+ "key": key,
199
+ "hook_name": result.name,
200
+ "status": result.status,
201
+ "ttl": ttl_sec,
202
+ },
203
+ )
204
+
205
+ except Exception as e:
206
+ logger.error(
207
+ "Failed to write to cache",
208
+ extra={
209
+ "key": key,
210
+ "error": str(e),
211
+ },
212
+ )
213
+
214
+ def compute_key(
215
+ self,
216
+ hook: HookDefinition,
217
+ files: list[Path],
218
+ ) -> str:
219
+ """Compute content-based cache key.
220
+
221
+ Cache key format: {hook_name}:{config_hash}:{content_hash}
222
+
223
+ Args:
224
+ hook: Hook definition
225
+ files: List of files to be checked by hook
226
+
227
+ Returns:
228
+ Cache key string
229
+ """
230
+ try:
231
+ # Hash hook configuration (Phase 8+ direct invocation API)
232
+ config_data = {
233
+ "name": hook.name,
234
+ "command": hook.command, # Direct tool invocation command
235
+ "timeout": hook.timeout,
236
+ "stage": hook.stage.value
237
+ if hasattr(hook.stage, "value")
238
+ else str(hook.stage),
239
+ "security_level": hook.security_level.value
240
+ if hasattr(hook.security_level, "value")
241
+ else str(hook.security_level),
242
+ }
243
+ config_json = json.dumps(config_data, sort_keys=True)
244
+ config_hash = hashlib.sha256(config_json.encode()).hexdigest()[:16]
245
+
246
+ # Hash file contents (for cache invalidation on content changes)
247
+ content_hasher = hashlib.sha256()
248
+ for file_path in sorted(files):
249
+ try:
250
+ if file_path.exists() and file_path.is_file():
251
+ content_hasher.update(file_path.read_bytes())
252
+ except Exception as e:
253
+ logger.warning(
254
+ f"Failed to hash file {file_path}: {e}",
255
+ extra={"file": str(file_path), "error": str(e)},
256
+ )
257
+ continue
258
+
259
+ content_hash = content_hasher.hexdigest()[:16]
260
+
261
+ cache_key = f"{hook.name}:{config_hash}:{content_hash}"
262
+
263
+ logger.debug(
264
+ "Cache key computed",
265
+ extra={
266
+ "hook_name": hook.name,
267
+ "file_count": len(files),
268
+ "config_hash": config_hash,
269
+ "content_hash": content_hash,
270
+ },
271
+ )
272
+
273
+ return cache_key
274
+
275
+ except Exception as e:
276
+ logger.error(
277
+ "Failed to compute cache key",
278
+ extra={
279
+ "hook_name": hook.name,
280
+ "error": str(e),
281
+ },
282
+ )
283
+ # Fallback to simple key
284
+ return f"{hook.name}:error"
285
+
286
+ async def clear(self) -> None:
287
+ """Clear all cached results."""
288
+ try:
289
+ self._cache.clear()
290
+ logger.info("Cache cleared")
291
+ except Exception as e:
292
+ logger.error("Failed to clear cache", extra={"error": str(e)})
293
+
294
+ async def get_stats(self) -> dict[str, t.Any]:
295
+ """Get cache statistics.
296
+
297
+ Returns:
298
+ Dictionary with cache statistics
299
+ """
300
+ import time
301
+
302
+ try:
303
+ total_entries = len(self._cache)
304
+ expired_entries = sum(
305
+ 1 for _, expiry in self._cache.values() if time.time() >= expiry
306
+ )
307
+ active_entries = total_entries - expired_entries
308
+
309
+ stats = {
310
+ "total_entries": total_entries,
311
+ "active_entries": active_entries,
312
+ "expired_entries": expired_entries,
313
+ "cache_dir": str(self._cache_dir),
314
+ "default_ttl": self.settings.default_ttl,
315
+ }
316
+
317
+ logger.debug("Cache statistics", extra=stats)
318
+
319
+ return stats
320
+
321
+ except Exception as e:
322
+ logger.error("Failed to get cache statistics", extra={"error": str(e)})
323
+ return {}
324
+
325
+ @property
326
+ def module_id(self) -> UUID:
327
+ """Reference to module-level MODULE_ID."""
328
+ return MODULE_ID
329
+
330
+ @property
331
+ def adapter_name(self) -> str:
332
+ """Human-readable adapter name."""
333
+ return "ToolProxyCacheAdapter"
334
+
335
+
336
+ # ACB Registration (REQUIRED at module level)
337
+ with suppress(Exception):
338
+ from acb.depends import depends
339
+
340
+ depends.set(ToolProxyCacheAdapter)
@@ -0,0 +1,297 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import os
5
+ from dataclasses import dataclass, field
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ if TYPE_CHECKING:
10
+ from typing import Self
11
+ else:
12
+ Self = "OrchestrationConfig"
13
+
14
+ import yaml
15
+ from acb.depends import depends
16
+
17
+ from crackerjack.config.settings import CrackerjackSettings
18
+
19
+
20
+ def _bool_from_env(value: str) -> bool:
21
+ return value.strip().lower() in {"1", "true", "yes", "on"}
22
+
23
+
24
+ def _int_from_env(value: str, default: int) -> int:
25
+ try:
26
+ return int(value)
27
+ except ValueError:
28
+ return default
29
+
30
+
31
+ @dataclass
32
+ class OrchestrationConfig:
33
+ enable_orchestration: bool = True
34
+ orchestration_mode: str = "acb"
35
+ enable_caching: bool = True
36
+ cache_backend: str = "memory"
37
+ cache_ttl: int = 3600
38
+ cache_max_entries: int = 100
39
+ max_parallel_hooks: int = 4
40
+ default_timeout: int = 600
41
+ stop_on_critical_failure: bool = True
42
+ enable_dependency_resolution: bool = True
43
+ log_cache_stats: bool = False
44
+ log_execution_timing: bool = False
45
+ enable_strategy_parallelism: bool = True
46
+ enable_adaptive_execution: bool = True
47
+ max_concurrent_strategies: int = 2
48
+ enable_tool_proxy: bool = True
49
+ use_precommit_legacy: bool = True
50
+ config_file_path: Path | None = field(default=None, repr=False)
51
+
52
+ # --------------------------------------------------------------------- #
53
+ # Construction helpers
54
+ # --------------------------------------------------------------------- #
55
+
56
+ @classmethod
57
+ def from_settings(
58
+ cls,
59
+ settings: CrackerjackSettings | None = None,
60
+ ) -> OrchestrationConfig:
61
+ """Build configuration from CrackerjackSettings via DI."""
62
+ if settings is None:
63
+ try:
64
+ # Use synchronous DI retrieval to avoid coroutine leakage in tests
65
+ settings = depends.get_sync(CrackerjackSettings) # type: ignore[attr-defined]
66
+ except Exception:
67
+ settings = None
68
+
69
+ if settings is None:
70
+ return cls()
71
+
72
+ return cls(
73
+ enable_orchestration=settings.enable_orchestration,
74
+ orchestration_mode=settings.orchestration_mode,
75
+ enable_caching=settings.enable_caching,
76
+ cache_backend=settings.cache_backend,
77
+ cache_ttl=settings.cache_ttl,
78
+ cache_max_entries=settings.cache_max_entries,
79
+ max_parallel_hooks=settings.max_parallel_hooks,
80
+ default_timeout=settings.default_timeout,
81
+ stop_on_critical_failure=settings.stop_on_critical_failure,
82
+ enable_dependency_resolution=settings.enable_dependency_resolution,
83
+ log_cache_stats=settings.log_cache_stats,
84
+ log_execution_timing=settings.log_execution_timing,
85
+ enable_strategy_parallelism=settings.enable_strategy_parallelism,
86
+ enable_adaptive_execution=settings.enable_adaptive_execution,
87
+ max_concurrent_strategies=settings.max_concurrent_strategies,
88
+ enable_tool_proxy=getattr(settings, "enable_tool_proxy", True),
89
+ use_precommit_legacy=settings.use_precommit_legacy,
90
+ )
91
+
92
+ @classmethod
93
+ def from_file(cls, config_path: Path) -> OrchestrationConfig:
94
+ if not config_path.exists():
95
+ msg = f"Config file not found: {config_path}"
96
+ raise FileNotFoundError(msg)
97
+
98
+ try:
99
+ raw = yaml.safe_load(config_path.read_text(encoding="utf-8"))
100
+ data: dict[str, Any] = raw if isinstance(raw, dict) else {}
101
+ except yaml.YAMLError as exc:
102
+ msg = f"Invalid YAML in {config_path}: {exc}"
103
+ raise ValueError(msg) from exc
104
+
105
+ if not isinstance(data, dict):
106
+ msg = f"Invalid config structure in {config_path}"
107
+ raise ValueError(msg)
108
+
109
+ section_any = data.get("orchestration", {})
110
+ section: dict[str, Any] = section_any if isinstance(section_any, dict) else {}
111
+
112
+ config = cls()
113
+ config.config_file_path = config_path
114
+ config._apply_dict(section)
115
+ return config
116
+
117
+ @classmethod
118
+ def _env_overrides(cls) -> dict[str, Any]:
119
+ """Return environment variable overrides."""
120
+ env = os.environ
121
+ overrides: dict[str, Any] = {}
122
+
123
+ mapping: dict[str, str] = {
124
+ "CRACKERJACK_ORCHESTRATION_MODE": "orchestration_mode",
125
+ "CRACKERJACK_CACHE_BACKEND": "cache_backend",
126
+ }
127
+
128
+ for env_key, attr in mapping.items():
129
+ if env_key in env:
130
+ overrides[attr] = env[env_key]
131
+
132
+ bool_vars = {
133
+ "CRACKERJACK_ENABLE_ORCHESTRATION": "enable_orchestration",
134
+ "CRACKERJACK_ENABLE_CACHING": "enable_caching",
135
+ "CRACKERJACK_STOP_ON_CRITICAL_FAILURE": "stop_on_critical_failure",
136
+ "CRACKERJACK_ENABLE_DEPENDENCY_RESOLUTION": "enable_dependency_resolution",
137
+ "CRACKERJACK_LOG_CACHE_STATS": "log_cache_stats",
138
+ "CRACKERJACK_LOG_EXECUTION_TIMING": "log_execution_timing",
139
+ "CRACKERJACK_ENABLE_STRATEGY_PARALLELISM": "enable_strategy_parallelism",
140
+ "CRACKERJACK_ENABLE_ADAPTIVE_EXECUTION": "enable_adaptive_execution",
141
+ "CRACKERJACK_ENABLE_TOOL_PROXY": "enable_tool_proxy",
142
+ "CRACKERJACK_USE_PRECOMMIT_LEGACY": "use_precommit_legacy",
143
+ }
144
+
145
+ for env_key, attr in bool_vars.items():
146
+ if env_key in env:
147
+ overrides[attr] = _bool_from_env(env[env_key])
148
+
149
+ int_vars = {
150
+ "CRACKERJACK_CACHE_TTL": ("cache_ttl", 3600),
151
+ "CRACKERJACK_CACHE_MAX_ENTRIES": ("cache_max_entries", 100),
152
+ "CRACKERJACK_MAX_PARALLEL_HOOKS": ("max_parallel_hooks", 4),
153
+ "CRACKERJACK_DEFAULT_TIMEOUT": ("default_timeout", 600),
154
+ "CRACKERJACK_MAX_CONCURRENT_STRATEGIES": ("max_concurrent_strategies", 2),
155
+ }
156
+
157
+ for env_key, (attr, default) in int_vars.items():
158
+ if env_key in env:
159
+ overrides[attr] = _int_from_env(env[env_key], default)
160
+
161
+ return overrides
162
+
163
+ @classmethod
164
+ def from_env(cls) -> OrchestrationConfig:
165
+ overrides = cls._env_overrides()
166
+ config = cls()
167
+ config._apply_dict(overrides)
168
+ return config
169
+
170
+ @classmethod
171
+ def load(cls, config_path: Path) -> OrchestrationConfig:
172
+ # Start from hard defaults; only override from file/env.
173
+ config = cls()
174
+
175
+ if config_path.exists():
176
+ file_config = cls.from_file(config_path)
177
+ config = config.merge(file_config) # type: ignore[assignment]
178
+
179
+ env_overrides = cls._env_overrides()
180
+ config = config.with_overrides(**env_overrides) # type: ignore[assignment]
181
+
182
+ return config
183
+
184
+ # ------------------------------------------------------------------ #
185
+ # Mutation helpers
186
+ # ------------------------------------------------------------------ #
187
+
188
+ def _apply_dict(self, values: dict[str, Any]) -> None:
189
+ # Accept aliases from YAML where keys are shorter (e.g., enable, mode)
190
+ aliases = {
191
+ "enable": "enable_orchestration",
192
+ "mode": "orchestration_mode",
193
+ }
194
+ for key, value in values.items():
195
+ attr = aliases.get(key, key)
196
+ if not hasattr(self, attr):
197
+ continue
198
+ if isinstance(value, str):
199
+ value = value.strip()
200
+ setattr(self, attr, value)
201
+
202
+ def merge(self, other: OrchestrationConfig) -> OrchestrationConfig:
203
+ data = dataclasses.asdict(self)
204
+ other_data = dataclasses.asdict(other)
205
+ data.update(other_data)
206
+ merged = OrchestrationConfig(**data)
207
+ merged.config_file_path = other.config_file_path or self.config_file_path
208
+ return merged
209
+
210
+ def with_overrides(self, **overrides: Any) -> OrchestrationConfig:
211
+ # Create a shallow copy without using dataclasses.replace to satisfy type checker
212
+ updated = OrchestrationConfig(**dataclasses.asdict(self))
213
+ for key, value in overrides.items():
214
+ if value is not None and hasattr(updated, key):
215
+ setattr(updated, key, value)
216
+ return updated
217
+
218
+ # ------------------------------------------------------------------ #
219
+ # Serialisation
220
+ # ------------------------------------------------------------------ #
221
+
222
+ def to_dict(self) -> dict[str, Any]:
223
+ return {
224
+ "orchestration": {
225
+ "enable": self.enable_orchestration,
226
+ "mode": self.orchestration_mode,
227
+ "enable_caching": self.enable_caching,
228
+ "cache_backend": self.cache_backend,
229
+ "cache_ttl": self.cache_ttl,
230
+ "cache_max_entries": self.cache_max_entries,
231
+ "max_parallel_hooks": self.max_parallel_hooks,
232
+ "default_timeout": self.default_timeout,
233
+ "stop_on_critical_failure": self.stop_on_critical_failure,
234
+ "enable_dependency_resolution": self.enable_dependency_resolution,
235
+ "log_cache_stats": self.log_cache_stats,
236
+ "log_execution_timing": self.log_execution_timing,
237
+ "enable_strategy_parallelism": self.enable_strategy_parallelism,
238
+ "enable_adaptive_execution": self.enable_adaptive_execution,
239
+ "max_concurrent_strategies": self.max_concurrent_strategies,
240
+ "enable_tool_proxy": self.enable_tool_proxy,
241
+ "use_precommit_legacy": self.use_precommit_legacy,
242
+ }
243
+ }
244
+
245
+ def save(self, config_path: Path) -> None:
246
+ config_path.write_text(
247
+ yaml.safe_dump(self.to_dict(), sort_keys=True),
248
+ encoding="utf-8",
249
+ )
250
+
251
+ # ------------------------------------------------------------------ #
252
+ # Validation / Conversion
253
+ # ------------------------------------------------------------------ #
254
+
255
+ def validate(self) -> list[str]:
256
+ errors: list[str] = []
257
+
258
+ if self.orchestration_mode not in {"acb", "legacy"}:
259
+ errors.append("Invalid orchestration_mode; expected 'acb' or 'legacy'")
260
+
261
+ if self.cache_backend not in {"memory", "tool_proxy", "redis"}:
262
+ errors.append(
263
+ "Invalid cache_backend; expected 'memory', 'tool_proxy', or 'redis'"
264
+ )
265
+
266
+ if self.cache_ttl <= 0:
267
+ errors.append("cache_ttl must be positive")
268
+ if self.cache_max_entries <= 0:
269
+ errors.append("cache_max_entries must be positive")
270
+ if self.max_parallel_hooks <= 0:
271
+ errors.append("max_parallel_hooks must be positive")
272
+ if self.default_timeout <= 0:
273
+ errors.append("default_timeout must be positive")
274
+ if self.max_concurrent_strategies <= 0:
275
+ errors.append("max_concurrent_strategies must be positive")
276
+
277
+ return errors
278
+
279
+ def to_orchestrator_settings(self) -> Any:
280
+ from crackerjack.orchestration.hook_orchestrator import HookOrchestratorSettings
281
+
282
+ execution_mode = self.orchestration_mode
283
+ if not self.enable_orchestration:
284
+ execution_mode = "legacy"
285
+
286
+ return HookOrchestratorSettings(
287
+ execution_mode=execution_mode,
288
+ enable_caching=self.enable_caching,
289
+ cache_backend=self.cache_backend,
290
+ max_parallel_hooks=self.max_parallel_hooks,
291
+ default_timeout=self.default_timeout,
292
+ enable_adaptive_execution=self.enable_adaptive_execution,
293
+ )
294
+
295
+
296
+ # Convenience re-export for legacy imports
297
+ __all__ = ["OrchestrationConfig"]