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
@@ -1,7 +1,9 @@
1
- from dataclasses import dataclass
1
+ from dataclasses import dataclass, field
2
2
  from enum import Enum
3
3
  from pathlib import Path
4
4
 
5
+ from crackerjack.config.tool_commands import get_tool_command
6
+
5
7
 
6
8
  class HookStage(Enum):
7
9
  FAST = "fast"
@@ -27,15 +29,42 @@ class HookDefinition:
27
29
  command: list[str]
28
30
  timeout: int = 60
29
31
  stage: HookStage = HookStage.FAST
32
+ description: str | None = None
30
33
  retry_on_failure: bool = False
31
34
  is_formatting: bool = False
32
35
  manual_stage: bool = False
33
36
  config_path: Path | None = None
34
37
  security_level: SecurityLevel = SecurityLevel.MEDIUM
38
+ use_precommit_legacy: bool = True # Phase 8.2: Backward compatibility flag
39
+ accepts_file_paths: bool = False # Phase 10.4.4: Can tool process individual files?
40
+ _direct_cmd_cache: list[str] | None = field(default=None, init=False, repr=False)
35
41
 
36
42
  def get_command(self) -> list[str]:
43
+ """Get the command to execute this hook.
44
+
45
+ Returns the appropriate command based on use_precommit_legacy flag:
46
+ - If use_precommit_legacy=True: Returns pre-commit wrapper command (legacy mode)
47
+ - If use_precommit_legacy=False: Returns direct tool command (Phase 8+ mode)
48
+
49
+ Returns:
50
+ List of command arguments for subprocess execution
51
+ """
52
+ # Phase 8.2: Direct invocation mode (new behavior)
53
+ if not self.use_precommit_legacy:
54
+ if self._direct_cmd_cache is None:
55
+ try:
56
+ self._direct_cmd_cache = get_tool_command(
57
+ self.name, pkg_path=Path.cwd()
58
+ )
59
+ except KeyError:
60
+ # Fallback to pre-commit if tool not in registry
61
+ # This ensures graceful degradation during migration
62
+ self._direct_cmd_cache = None
63
+ if self._direct_cmd_cache is not None:
64
+ return self._direct_cmd_cache
65
+
66
+ # Legacy mode: Use pre-commit wrapper (Phase 1-7 behavior)
37
67
  import shutil
38
- from pathlib import Path
39
68
 
40
69
  pre_commit_path = None
41
70
  current_dir = Path.cwd()
@@ -53,6 +82,34 @@ class HookDefinition:
53
82
  cmd.extend([self.name, "--all-files"])
54
83
  return cmd
55
84
 
85
+ def build_command(self, files: list[Path] | None = None) -> list[str]:
86
+ """Build command with optional file paths for targeted execution.
87
+
88
+ Phase 10.4.4: Enables incremental execution on specific files when supported.
89
+
90
+ Args:
91
+ files: Optional list of file paths to process. If None, processes all files.
92
+
93
+ Returns:
94
+ Command list with file paths appended if tool accepts them.
95
+
96
+ Example:
97
+ >>> hook = HookDefinition(
98
+ ... name="ruff-check",
99
+ ... command=["ruff", "check"],
100
+ ... accepts_file_paths=True,
101
+ ... )
102
+ >>> hook.build_command([Path("foo.py"), Path("bar.py")])
103
+ ["ruff", "check", "foo.py", "bar.py"]
104
+ """
105
+ base_cmd = self.get_command().copy()
106
+
107
+ # Append file paths if tool accepts them and files are provided
108
+ if files and self.accepts_file_paths:
109
+ base_cmd.extend([str(f) for f in files])
110
+
111
+ return base_cmd
112
+
56
113
 
57
114
  @dataclass
58
115
  class HookStrategy:
@@ -69,74 +126,124 @@ FAST_HOOKS = [
69
126
  name="validate-regex-patterns",
70
127
  command=[],
71
128
  is_formatting=True,
72
- timeout=30,
129
+ timeout=120, # Increased from 60 to handle larger codebases and prevent timeout issues
73
130
  retry_on_failure=True,
74
131
  security_level=SecurityLevel.HIGH,
132
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
75
133
  ),
76
134
  HookDefinition(
77
135
  name="trailing-whitespace",
78
136
  command=[],
79
137
  is_formatting=True,
138
+ timeout=120, # Increased from 60 to handle larger codebases and prevent timeout issues
80
139
  retry_on_failure=True,
81
140
  security_level=SecurityLevel.LOW,
141
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
142
+ accepts_file_paths=True, # Phase 10.4.4: File-level fixer
82
143
  ),
83
144
  HookDefinition(
84
145
  name="end-of-file-fixer",
85
146
  command=[],
86
147
  is_formatting=True,
148
+ timeout=120, # Increased from 60 to handle larger codebases and prevent timeout issues
87
149
  retry_on_failure=True,
88
150
  security_level=SecurityLevel.LOW,
151
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
152
+ accepts_file_paths=True, # Phase 10.4.4: File-level fixer
89
153
  ),
90
154
  HookDefinition(
91
155
  name="check-yaml",
92
156
  command=[],
157
+ timeout=60, # Increased from 20 to reduce timeout issues with larger YAML files
93
158
  security_level=SecurityLevel.MEDIUM,
159
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
160
+ accepts_file_paths=True, # Phase 10.4.4: File-level validator
94
161
  ),
95
162
  HookDefinition(
96
163
  name="check-toml",
97
164
  command=[],
165
+ timeout=150, # Increased from 79 to reduce timeout issues with larger TOML files
98
166
  security_level=SecurityLevel.MEDIUM,
167
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
168
+ accepts_file_paths=True, # Phase 10.4.4: File-level validator
99
169
  ),
100
170
  HookDefinition(
101
- name="check-added-large-files",
171
+ name="check-json",
102
172
  command=[],
173
+ timeout=90, # Increased from 30 to reduce timeout issues with larger JSON files
174
+ security_level=SecurityLevel.MEDIUM,
175
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
176
+ accepts_file_paths=True, # Phase 10.4.4: File-level validator
177
+ ),
178
+ HookDefinition(
179
+ name="check-ast",
180
+ command=[],
181
+ timeout=90, # Increased from 30 to reduce timeout issues with larger Python files
103
182
  security_level=SecurityLevel.HIGH,
183
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
184
+ accepts_file_paths=True, # Phase 10.4.4: File-level validator
104
185
  ),
105
186
  HookDefinition(
106
- name="uv-lock",
187
+ name="format-json",
188
+ command=[],
189
+ is_formatting=True,
190
+ timeout=120, # Increased from 45 to reduce timeout issues with larger JSON files
191
+ retry_on_failure=True,
192
+ security_level=SecurityLevel.LOW,
193
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
194
+ accepts_file_paths=True, # Phase 10.4.4: File-level formatter
195
+ ),
196
+ HookDefinition(
197
+ name="check-added-large-files",
107
198
  command=[],
199
+ timeout=90, # Increased from 30 to reduce timeout issues with repositories with many files
108
200
  security_level=SecurityLevel.HIGH,
201
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
109
202
  ),
110
203
  HookDefinition(
111
- name="gitleaks",
204
+ name="uv-lock",
112
205
  command=[],
113
- security_level=SecurityLevel.CRITICAL,
206
+ timeout=60, # Increased from 20 to reduce timeout issues with complex dependency trees
207
+ security_level=SecurityLevel.HIGH,
208
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
114
209
  ),
115
210
  HookDefinition(
116
211
  name="codespell",
117
212
  command=[],
213
+ timeout=150, # Increased from 45 to reduce timeout issues with large codebases
118
214
  security_level=SecurityLevel.LOW,
215
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
216
+ accepts_file_paths=True, # Phase 10.4.4: File-level spell checker
119
217
  ),
120
218
  HookDefinition(
121
219
  name="ruff-check",
122
220
  command=[],
123
221
  is_formatting=True,
222
+ timeout=240, # Increased from 120 to reduce timeout issues with large codebases
124
223
  retry_on_failure=True,
125
224
  security_level=SecurityLevel.MEDIUM,
225
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
226
+ accepts_file_paths=True, # Phase 10.4.4: File-level Python linter
126
227
  ),
127
228
  HookDefinition(
128
229
  name="ruff-format",
129
230
  command=[],
130
231
  is_formatting=True,
232
+ timeout=240, # Increased from 120 to reduce timeout issues with large codebases
131
233
  retry_on_failure=True,
132
234
  security_level=SecurityLevel.LOW,
235
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
236
+ accepts_file_paths=True, # Phase 10.4.4: File-level Python formatter
133
237
  ),
134
238
  HookDefinition(
135
239
  name="mdformat",
136
240
  command=[],
137
241
  is_formatting=True,
242
+ timeout=300, # Increased from 120 to reduce timeout issues with larger markdown files
138
243
  retry_on_failure=True,
139
244
  security_level=SecurityLevel.LOW,
245
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
246
+ accepts_file_paths=True, # Phase 10.4.4: File-level markdown formatter
140
247
  ),
141
248
  ]
142
249
 
@@ -144,50 +251,111 @@ COMPREHENSIVE_HOOKS = [
144
251
  HookDefinition(
145
252
  name="zuban",
146
253
  command=[],
147
- timeout=30,
254
+ timeout=240, # Increased from 80 to reduce timeout issues with larger codebases during type checking
255
+ stage=HookStage.COMPREHENSIVE,
256
+ manual_stage=True,
257
+ security_level=SecurityLevel.HIGH, # Changed from CRITICAL to HIGH to allow other hooks to run
258
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
259
+ accepts_file_paths=True, # Phase 10.5: Allow incremental execution on changed files to avoid virtual environments
260
+ ),
261
+ HookDefinition(
262
+ name="semgrep",
263
+ command=[],
264
+ timeout=480, # Increased from 240 to reduce timeout issues with comprehensive security scans
148
265
  stage=HookStage.COMPREHENSIVE,
149
266
  manual_stage=True,
150
267
  security_level=SecurityLevel.CRITICAL,
268
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
269
+ accepts_file_paths=True, # Phase 10.4.4: File-level SAST scanner
151
270
  ),
152
271
  HookDefinition(
153
- name="bandit",
272
+ name="pyscn",
154
273
  command=[],
155
- timeout=300,
274
+ timeout=300, # CFG analysis, clone detection, complexity metrics
275
+ stage=HookStage.COMPREHENSIVE,
276
+ manual_stage=True,
277
+ security_level=SecurityLevel.HIGH, # Security + quality analysis
278
+ use_precommit_legacy=False, # Direct invocation
279
+ accepts_file_paths=True, # Can scan specific Python files
280
+ ),
281
+ # NOTE: Bandit replaced with Semgrep (using uvx for Python 3.13 isolation)
282
+ # HookDefinition(
283
+ # name="bandit",
284
+ # command=[],
285
+ # timeout=180, # 3 minutes for SAST scanning
286
+ # stage=HookStage.COMPREHENSIVE,
287
+ # manual_stage=True,
288
+ # security_level=SecurityLevel.CRITICAL,
289
+ # use_precommit_legacy=False, # Phase 8.4: Direct invocation
290
+ # accepts_file_paths=True, # Phase 10.4.4: File-level SAST scanner
291
+ # ),
292
+ HookDefinition(
293
+ name="gitleaks",
294
+ command=[],
295
+ timeout=180, # Increased from 45 to reduce timeout issues with comprehensive security scans
156
296
  stage=HookStage.COMPREHENSIVE,
157
297
  manual_stage=True,
158
298
  security_level=SecurityLevel.CRITICAL,
299
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
300
+ ),
301
+ HookDefinition(
302
+ name="pip-audit",
303
+ command=[],
304
+ timeout=180, # Network calls to vulnerability databases
305
+ stage=HookStage.COMPREHENSIVE,
306
+ manual_stage=True,
307
+ security_level=SecurityLevel.CRITICAL, # CVE vulnerabilities are critical
308
+ use_precommit_legacy=False, # Direct invocation
309
+ accepts_file_paths=False, # Scans entire environment/requirements
159
310
  ),
160
311
  HookDefinition(
161
312
  name="skylos",
162
313
  command=[],
163
- timeout=30,
314
+ timeout=180, # Increased from 60 to reduce timeout issues with comprehensive dead code scanning
164
315
  stage=HookStage.COMPREHENSIVE,
165
316
  manual_stage=True,
166
317
  security_level=SecurityLevel.MEDIUM,
318
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
319
+ accepts_file_paths=True, # Phase 10.5: Incremental execution on changed files
167
320
  ),
168
321
  HookDefinition(
169
322
  name="refurb",
170
323
  command=[],
171
- timeout=300,
324
+ timeout=480, # Increased from 240 to reduce timeout issues with comprehensive refactoring analysis
172
325
  stage=HookStage.COMPREHENSIVE,
173
326
  manual_stage=True,
174
327
  security_level=SecurityLevel.MEDIUM,
328
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
329
+ accepts_file_paths=True, # Phase 10.5: Incremental execution on changed files (240s -> ~10s)
175
330
  ),
176
331
  HookDefinition(
177
332
  name="creosote",
178
333
  command=[],
179
- timeout=300,
334
+ timeout=360, # Increased from 180 to reduce timeout issues with comprehensive dependency analysis
180
335
  stage=HookStage.COMPREHENSIVE,
181
336
  manual_stage=True,
182
337
  security_level=SecurityLevel.HIGH,
338
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
183
339
  ),
184
340
  HookDefinition(
185
341
  name="complexipy",
186
342
  command=[],
187
- timeout=60,
343
+ timeout=300, # Increased from 120 to reduce timeout issues with comprehensive complexity analysis
188
344
  stage=HookStage.COMPREHENSIVE,
189
345
  manual_stage=True,
190
346
  security_level=SecurityLevel.MEDIUM,
347
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
348
+ accepts_file_paths=True, # Phase 10.5: Incremental execution on changed files
349
+ ),
350
+ HookDefinition(
351
+ name="check-jsonschema",
352
+ command=[],
353
+ timeout=180, # Increased from 60 to reduce timeout issues with complex schema validation
354
+ stage=HookStage.COMPREHENSIVE,
355
+ manual_stage=True,
356
+ security_level=SecurityLevel.HIGH,
357
+ use_precommit_legacy=False, # Phase 8.4: Direct invocation
358
+ accepts_file_paths=True, # Phase 10.4.4: File-level schema validator
191
359
  ),
192
360
  ]
193
361
 
@@ -195,15 +363,19 @@ COMPREHENSIVE_HOOKS = [
195
363
  FAST_STRATEGY = HookStrategy(
196
364
  name="fast",
197
365
  hooks=FAST_HOOKS,
198
- timeout=60,
366
+ timeout=300, # Increased from 60 to accommodate all increased hook timeouts
199
367
  retry_policy=RetryPolicy.FORMATTING_ONLY,
368
+ parallel=True, # Phase 6: Enable parallel execution for 2-3x speedup
369
+ max_workers=4, # Optimal concurrency for fast hooks
200
370
  )
201
371
 
202
372
  COMPREHENSIVE_STRATEGY = HookStrategy(
203
373
  name="comprehensive",
204
374
  hooks=COMPREHENSIVE_HOOKS,
205
- timeout=300,
375
+ timeout=1800, # Increased from 300 to accommodate all increased hook timeouts
206
376
  retry_policy=RetryPolicy.NONE,
377
+ parallel=True, # Phase 6: Enable parallel execution for 2x speedup
378
+ max_workers=4, # Optimal concurrency for comprehensive hooks
207
379
  )
208
380
 
209
381
 
@@ -0,0 +1,239 @@
1
+ """Settings loader for ACB configuration with YAML support.
2
+
3
+ This module provides utilities for loading ACB Settings from YAML files
4
+ with support for configuration layering and environment-specific overrides.
5
+
6
+ Configuration Priority (highest to lowest):
7
+ 1. settings/local.yaml (local overrides, gitignored)
8
+ 2. settings/crackerjack.yaml (main configuration)
9
+ 3. Default values from Settings class
10
+
11
+ Example:
12
+ >>> from crackerjack.config import CrackerjackSettings
13
+ >>> settings = CrackerjackSettings.load()
14
+ >>> settings.verbose
15
+ False
16
+
17
+ >>> # Async loading with full ACB initialization
18
+ >>> settings = await CrackerjackSettings.load_async()
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ import typing as t
25
+ from pathlib import Path
26
+ from typing import TypeVar
27
+
28
+ import yaml
29
+ from acb.config import Settings
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+ T = TypeVar("T", bound=Settings)
34
+
35
+
36
+ def _load_single_config_file(config_file: Path) -> dict[str, t.Any]:
37
+ """Load a single config file and return its data."""
38
+ if not config_file.exists():
39
+ logger.debug(f"Configuration file not found: {config_file}")
40
+ return {}
41
+
42
+ try:
43
+ with config_file.open() as f:
44
+ loaded_data: t.Any = yaml.safe_load(f)
45
+ if isinstance(loaded_data, dict):
46
+ logger.debug(f"Loaded configuration from {config_file}")
47
+ return loaded_data
48
+ else:
49
+ logger.warning(
50
+ f"Invalid YAML format in {config_file}: expected dict, got {type(loaded_data).__name__}"
51
+ )
52
+ return {}
53
+ except yaml.YAMLError as e:
54
+ logger.error(f"Failed to parse YAML from {config_file}: {e}")
55
+ return {}
56
+ except OSError as e:
57
+ logger.error(f"Failed to read {config_file}: {e}")
58
+ return {}
59
+
60
+
61
+ def _merge_config_data(config_files: list[Path]) -> dict[str, t.Any]:
62
+ """Merge data from all config files."""
63
+ merged_data = {}
64
+ for config_file in config_files:
65
+ file_data = _load_single_config_file(config_file)
66
+ merged_data.update(file_data)
67
+ return merged_data
68
+
69
+
70
+ def load_settings[T: Settings](
71
+ settings_class: type[T],
72
+ settings_dir: Path | None = None,
73
+ ) -> T:
74
+ """Load settings from YAML files with layered configuration.
75
+
76
+ This function loads configuration from multiple YAML files and merges them
77
+ with priority-based overriding. Unknown YAML fields are silently ignored.
78
+
79
+ Args:
80
+ settings_class: The Settings class to instantiate
81
+ settings_dir: Directory containing YAML files (default: ./settings)
82
+
83
+ Returns:
84
+ Initialized Settings instance with merged configuration
85
+
86
+ Configuration Priority (highest to lowest):
87
+ 1. settings/local.yaml (local overrides, gitignored)
88
+ 2. settings/crackerjack.yaml (main configuration)
89
+ 3. Default values from Settings class
90
+
91
+ Example:
92
+ >>> class MySettings(Settings):
93
+ ... debug: bool = False
94
+ ... max_workers: int = 4
95
+ >>> settings = load_settings(MySettings)
96
+ >>> settings.debug
97
+ False
98
+ """
99
+ if settings_dir is None:
100
+ settings_dir = Path.cwd() / "settings"
101
+
102
+ # Configuration files in priority order (lowest to highest)
103
+ config_files = [
104
+ settings_dir / "crackerjack.yaml",
105
+ settings_dir / "local.yaml",
106
+ ]
107
+
108
+ # Load and merge data from all config files
109
+ merged_data = _merge_config_data(config_files)
110
+
111
+ # Filter to only fields defined in the Settings class
112
+ # This prevents validation errors from unknown YAML keys
113
+ relevant_data = {
114
+ k: v for k, v in merged_data.items() if k in settings_class.model_fields
115
+ }
116
+
117
+ # Log filtered fields if any were excluded
118
+ excluded_fields = set(merged_data.keys()) - set(relevant_data.keys())
119
+ if excluded_fields:
120
+ logger.debug(
121
+ f"Ignored unknown configuration fields: {', '.join(sorted(excluded_fields))}"
122
+ )
123
+
124
+ logger.info(
125
+ f"Loaded {len(relevant_data)} configuration values for {settings_class.__name__}"
126
+ )
127
+
128
+ # Synchronous initialization (safe for module-level code)
129
+ return settings_class(**relevant_data)
130
+
131
+
132
+ async def load_settings_async[T: Settings](
133
+ settings_class: type[T],
134
+ settings_dir: Path | None = None,
135
+ ) -> T:
136
+ """Load settings asynchronously with full ACB initialization.
137
+
138
+ This function provides the same configuration loading as load_settings()
139
+ but uses ACB's async initialization which includes secret loading and
140
+ other async setup operations.
141
+
142
+ Args:
143
+ settings_class: The Settings class to instantiate
144
+ settings_dir: Directory containing YAML files (default: ./settings)
145
+
146
+ Returns:
147
+ Initialized Settings instance with async initialization complete
148
+
149
+ Note:
150
+ Use this for application runtime when async context is available.
151
+ For module-level initialization, use synchronous load_settings().
152
+
153
+ Example:
154
+ >>> settings = await load_settings_async(CrackerjackSettings)
155
+ >>> settings.verbose
156
+ False
157
+ """
158
+ if settings_dir is None:
159
+ settings_dir = Path.cwd() / "settings"
160
+
161
+ # Configuration files in priority order (lowest to highest)
162
+ config_files = [
163
+ settings_dir / "crackerjack.yaml",
164
+ settings_dir / "local.yaml",
165
+ ]
166
+
167
+ # Load and merge YAML data from all files
168
+ merged_data = await _load_yaml_data(config_files)
169
+
170
+ # Process the loaded data
171
+ relevant_data = _filter_relevant_data(merged_data, settings_class)
172
+ _log_filtered_fields(merged_data, relevant_data)
173
+ _log_load_info(settings_class, relevant_data)
174
+
175
+ # Async initialization (loads secrets, performs async setup)
176
+ return await settings_class.create_async(**relevant_data)
177
+
178
+
179
+ async def _load_yaml_data(config_files: list[Path]) -> dict[str, t.Any]:
180
+ """Load and merge YAML data from configuration files."""
181
+ merged_data: dict[str, t.Any] = {}
182
+ for config_file in config_files:
183
+ file_data = await _load_single_yaml_file(config_file)
184
+ if file_data is not None:
185
+ merged_data.update(file_data)
186
+ elif not config_file.exists():
187
+ logger.debug(f"Configuration file not found: {config_file}")
188
+ return merged_data
189
+
190
+
191
+ async def _load_single_yaml_file(config_file: Path) -> dict[str, t.Any] | None:
192
+ """Load a single YAML file and return its content."""
193
+ if not config_file.exists():
194
+ return None
195
+
196
+ try:
197
+ with config_file.open() as f:
198
+ loaded_data: t.Any = yaml.safe_load(f)
199
+ if isinstance(loaded_data, dict):
200
+ logger.debug(f"Loaded configuration from {config_file}")
201
+ return loaded_data
202
+ else:
203
+ logger.warning(
204
+ f"Invalid YAML format in {config_file}: expected dict, got {type(loaded_data).__name__}"
205
+ )
206
+ return {}
207
+ except yaml.YAMLError as e:
208
+ logger.error(f"Failed to parse YAML from {config_file}: {e}")
209
+ return None
210
+ except OSError as e:
211
+ logger.error(f"Failed to read {config_file}: {e}")
212
+ return None
213
+
214
+
215
+ def _filter_relevant_data[T: Settings](
216
+ merged_data: dict[str, t.Any], settings_class: type[T]
217
+ ) -> dict[str, t.Any]:
218
+ """Filter the loaded data to only fields defined in the Settings class."""
219
+ return {k: v for k, v in merged_data.items() if k in settings_class.model_fields}
220
+
221
+
222
+ def _log_filtered_fields(
223
+ merged_data: dict[str, t.Any], relevant_data: dict[str, t.Any]
224
+ ) -> None:
225
+ """Log any fields that were excluded due to not being in the settings class."""
226
+ excluded_fields = set(merged_data.keys()) - set(relevant_data.keys())
227
+ if excluded_fields:
228
+ logger.debug(
229
+ f"Ignored unknown configuration fields: {', '.join(sorted(excluded_fields))}"
230
+ )
231
+
232
+
233
+ def _log_load_info[T: Settings](
234
+ settings_class: type[T], relevant_data: dict[str, t.Any]
235
+ ) -> None:
236
+ """Log information about the loaded configuration."""
237
+ logger.info(
238
+ f"Loaded {len(relevant_data)} configuration values for {settings_class.__name__} (async)"
239
+ )