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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. crackerjack/README.md +19 -0
  2. crackerjack/__init__.py +30 -1
  3. crackerjack/__main__.py +342 -1263
  4. crackerjack/adapters/README.md +18 -0
  5. crackerjack/adapters/__init__.py +27 -5
  6. crackerjack/adapters/_output_paths.py +167 -0
  7. crackerjack/adapters/_qa_adapter_base.py +309 -0
  8. crackerjack/adapters/_tool_adapter_base.py +706 -0
  9. crackerjack/adapters/ai/README.md +65 -0
  10. crackerjack/adapters/ai/__init__.py +5 -0
  11. crackerjack/adapters/ai/claude.py +853 -0
  12. crackerjack/adapters/complexity/README.md +53 -0
  13. crackerjack/adapters/complexity/__init__.py +10 -0
  14. crackerjack/adapters/complexity/complexipy.py +641 -0
  15. crackerjack/adapters/dependency/__init__.py +22 -0
  16. crackerjack/adapters/dependency/pip_audit.py +418 -0
  17. crackerjack/adapters/format/README.md +72 -0
  18. crackerjack/adapters/format/__init__.py +11 -0
  19. crackerjack/adapters/format/mdformat.py +313 -0
  20. crackerjack/adapters/format/ruff.py +516 -0
  21. crackerjack/adapters/lint/README.md +47 -0
  22. crackerjack/adapters/lint/__init__.py +11 -0
  23. crackerjack/adapters/lint/codespell.py +273 -0
  24. crackerjack/adapters/lsp/README.md +49 -0
  25. crackerjack/adapters/lsp/__init__.py +27 -0
  26. crackerjack/adapters/{rust_tool_manager.py → lsp/_manager.py} +3 -3
  27. crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
  28. crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
  29. crackerjack/adapters/refactor/README.md +59 -0
  30. crackerjack/adapters/refactor/__init__.py +12 -0
  31. crackerjack/adapters/refactor/creosote.py +318 -0
  32. crackerjack/adapters/refactor/refurb.py +406 -0
  33. crackerjack/adapters/refactor/skylos.py +494 -0
  34. crackerjack/adapters/sast/README.md +132 -0
  35. crackerjack/adapters/sast/__init__.py +32 -0
  36. crackerjack/adapters/sast/_base.py +201 -0
  37. crackerjack/adapters/sast/bandit.py +423 -0
  38. crackerjack/adapters/sast/pyscn.py +405 -0
  39. crackerjack/adapters/sast/semgrep.py +241 -0
  40. crackerjack/adapters/security/README.md +111 -0
  41. crackerjack/adapters/security/__init__.py +17 -0
  42. crackerjack/adapters/security/gitleaks.py +339 -0
  43. crackerjack/adapters/type/README.md +52 -0
  44. crackerjack/adapters/type/__init__.py +12 -0
  45. crackerjack/adapters/type/pyrefly.py +402 -0
  46. crackerjack/adapters/type/ty.py +402 -0
  47. crackerjack/adapters/type/zuban.py +522 -0
  48. crackerjack/adapters/utility/README.md +51 -0
  49. crackerjack/adapters/utility/__init__.py +10 -0
  50. crackerjack/adapters/utility/checks.py +884 -0
  51. crackerjack/agents/README.md +264 -0
  52. crackerjack/agents/__init__.py +40 -12
  53. crackerjack/agents/base.py +1 -0
  54. crackerjack/agents/claude_code_bridge.py +641 -0
  55. crackerjack/agents/coordinator.py +49 -53
  56. crackerjack/agents/dry_agent.py +187 -3
  57. crackerjack/agents/enhanced_coordinator.py +279 -0
  58. crackerjack/agents/enhanced_proactive_agent.py +185 -0
  59. crackerjack/agents/error_middleware.py +53 -0
  60. crackerjack/agents/formatting_agent.py +6 -8
  61. crackerjack/agents/helpers/__init__.py +9 -0
  62. crackerjack/agents/helpers/performance/__init__.py +22 -0
  63. crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
  64. crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
  65. crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
  66. crackerjack/agents/helpers/refactoring/__init__.py +22 -0
  67. crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
  68. crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
  69. crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
  70. crackerjack/agents/helpers/test_creation/__init__.py +19 -0
  71. crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
  72. crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
  73. crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
  74. crackerjack/agents/performance_agent.py +121 -1152
  75. crackerjack/agents/refactoring_agent.py +156 -655
  76. crackerjack/agents/semantic_agent.py +479 -0
  77. crackerjack/agents/semantic_helpers.py +356 -0
  78. crackerjack/agents/test_creation_agent.py +19 -1605
  79. crackerjack/api.py +5 -7
  80. crackerjack/cli/README.md +394 -0
  81. crackerjack/cli/__init__.py +1 -1
  82. crackerjack/cli/cache_handlers.py +23 -18
  83. crackerjack/cli/cache_handlers_enhanced.py +1 -4
  84. crackerjack/cli/facade.py +70 -8
  85. crackerjack/cli/formatting.py +13 -0
  86. crackerjack/cli/handlers/__init__.py +85 -0
  87. crackerjack/cli/handlers/advanced.py +103 -0
  88. crackerjack/cli/handlers/ai_features.py +62 -0
  89. crackerjack/cli/handlers/analytics.py +479 -0
  90. crackerjack/cli/handlers/changelog.py +271 -0
  91. crackerjack/cli/handlers/config_handlers.py +16 -0
  92. crackerjack/cli/handlers/coverage.py +84 -0
  93. crackerjack/cli/handlers/documentation.py +280 -0
  94. crackerjack/cli/handlers/main_handlers.py +497 -0
  95. crackerjack/cli/handlers/monitoring.py +371 -0
  96. crackerjack/cli/handlers.py +249 -49
  97. crackerjack/cli/interactive.py +8 -5
  98. crackerjack/cli/options.py +203 -110
  99. crackerjack/cli/semantic_handlers.py +292 -0
  100. crackerjack/cli/version.py +19 -0
  101. crackerjack/code_cleaner.py +60 -24
  102. crackerjack/config/README.md +472 -0
  103. crackerjack/config/__init__.py +256 -0
  104. crackerjack/config/global_lock_config.py +191 -54
  105. crackerjack/config/hooks.py +188 -16
  106. crackerjack/config/loader.py +239 -0
  107. crackerjack/config/settings.py +141 -0
  108. crackerjack/config/tool_commands.py +331 -0
  109. crackerjack/core/README.md +393 -0
  110. crackerjack/core/async_workflow_orchestrator.py +79 -53
  111. crackerjack/core/autofix_coordinator.py +22 -9
  112. crackerjack/core/container.py +10 -9
  113. crackerjack/core/enhanced_container.py +9 -9
  114. crackerjack/core/performance.py +1 -1
  115. crackerjack/core/performance_monitor.py +5 -3
  116. crackerjack/core/phase_coordinator.py +1018 -634
  117. crackerjack/core/proactive_workflow.py +3 -3
  118. crackerjack/core/retry.py +275 -0
  119. crackerjack/core/service_watchdog.py +167 -23
  120. crackerjack/core/session_coordinator.py +187 -382
  121. crackerjack/core/timeout_manager.py +161 -44
  122. crackerjack/core/workflow/__init__.py +21 -0
  123. crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
  124. crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
  125. crackerjack/core/workflow/workflow_issue_parser.py +714 -0
  126. crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
  127. crackerjack/core/workflow/workflow_security_gates.py +400 -0
  128. crackerjack/core/workflow_orchestrator.py +1247 -953
  129. crackerjack/data/README.md +11 -0
  130. crackerjack/data/__init__.py +8 -0
  131. crackerjack/data/models.py +79 -0
  132. crackerjack/data/repository.py +210 -0
  133. crackerjack/decorators/README.md +180 -0
  134. crackerjack/decorators/__init__.py +35 -0
  135. crackerjack/decorators/error_handling.py +649 -0
  136. crackerjack/decorators/error_handling_decorators.py +334 -0
  137. crackerjack/decorators/helpers.py +58 -0
  138. crackerjack/decorators/patterns.py +281 -0
  139. crackerjack/decorators/utils.py +58 -0
  140. crackerjack/docs/README.md +11 -0
  141. crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
  142. crackerjack/documentation/README.md +11 -0
  143. crackerjack/documentation/ai_templates.py +1 -1
  144. crackerjack/documentation/dual_output_generator.py +11 -9
  145. crackerjack/documentation/reference_generator.py +104 -59
  146. crackerjack/dynamic_config.py +52 -61
  147. crackerjack/errors.py +1 -1
  148. crackerjack/events/README.md +11 -0
  149. crackerjack/events/__init__.py +16 -0
  150. crackerjack/events/telemetry.py +175 -0
  151. crackerjack/events/workflow_bus.py +346 -0
  152. crackerjack/exceptions/README.md +301 -0
  153. crackerjack/exceptions/__init__.py +5 -0
  154. crackerjack/exceptions/config.py +4 -0
  155. crackerjack/exceptions/tool_execution_error.py +245 -0
  156. crackerjack/executors/README.md +591 -0
  157. crackerjack/executors/__init__.py +2 -0
  158. crackerjack/executors/async_hook_executor.py +539 -77
  159. crackerjack/executors/cached_hook_executor.py +3 -3
  160. crackerjack/executors/hook_executor.py +967 -102
  161. crackerjack/executors/hook_lock_manager.py +31 -22
  162. crackerjack/executors/individual_hook_executor.py +66 -32
  163. crackerjack/executors/lsp_aware_hook_executor.py +136 -57
  164. crackerjack/executors/progress_hook_executor.py +282 -0
  165. crackerjack/executors/tool_proxy.py +23 -7
  166. crackerjack/hooks/README.md +485 -0
  167. crackerjack/hooks/lsp_hook.py +8 -9
  168. crackerjack/intelligence/README.md +557 -0
  169. crackerjack/interactive.py +37 -10
  170. crackerjack/managers/README.md +369 -0
  171. crackerjack/managers/async_hook_manager.py +41 -57
  172. crackerjack/managers/hook_manager.py +449 -79
  173. crackerjack/managers/publish_manager.py +81 -36
  174. crackerjack/managers/test_command_builder.py +290 -12
  175. crackerjack/managers/test_executor.py +93 -8
  176. crackerjack/managers/test_manager.py +1082 -75
  177. crackerjack/managers/test_progress.py +118 -26
  178. crackerjack/mcp/README.md +374 -0
  179. crackerjack/mcp/cache.py +25 -2
  180. crackerjack/mcp/client_runner.py +35 -18
  181. crackerjack/mcp/context.py +9 -9
  182. crackerjack/mcp/dashboard.py +24 -8
  183. crackerjack/mcp/enhanced_progress_monitor.py +34 -23
  184. crackerjack/mcp/file_monitor.py +27 -6
  185. crackerjack/mcp/progress_components.py +45 -34
  186. crackerjack/mcp/progress_monitor.py +6 -9
  187. crackerjack/mcp/rate_limiter.py +11 -7
  188. crackerjack/mcp/server.py +2 -0
  189. crackerjack/mcp/server_core.py +187 -55
  190. crackerjack/mcp/service_watchdog.py +12 -9
  191. crackerjack/mcp/task_manager.py +2 -2
  192. crackerjack/mcp/tools/README.md +27 -0
  193. crackerjack/mcp/tools/__init__.py +2 -0
  194. crackerjack/mcp/tools/core_tools.py +75 -52
  195. crackerjack/mcp/tools/execution_tools.py +87 -31
  196. crackerjack/mcp/tools/intelligence_tools.py +2 -2
  197. crackerjack/mcp/tools/proactive_tools.py +1 -1
  198. crackerjack/mcp/tools/semantic_tools.py +584 -0
  199. crackerjack/mcp/tools/utility_tools.py +180 -132
  200. crackerjack/mcp/tools/workflow_executor.py +87 -46
  201. crackerjack/mcp/websocket/README.md +31 -0
  202. crackerjack/mcp/websocket/app.py +11 -1
  203. crackerjack/mcp/websocket/event_bridge.py +188 -0
  204. crackerjack/mcp/websocket/jobs.py +27 -4
  205. crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
  206. crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
  207. crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
  208. crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
  209. crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
  210. crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
  211. crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
  212. crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
  213. crackerjack/mcp/websocket/monitoring/factory.py +109 -0
  214. crackerjack/mcp/websocket/monitoring/filters.py +10 -0
  215. crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
  216. crackerjack/mcp/websocket/monitoring/models.py +90 -0
  217. crackerjack/mcp/websocket/monitoring/utils.py +171 -0
  218. crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
  219. crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
  220. crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
  221. crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
  222. crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
  223. crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
  224. crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
  225. crackerjack/mcp/websocket/server.py +1 -3
  226. crackerjack/mcp/websocket/websocket_handler.py +107 -6
  227. crackerjack/models/README.md +308 -0
  228. crackerjack/models/__init__.py +10 -1
  229. crackerjack/models/config.py +639 -22
  230. crackerjack/models/config_adapter.py +6 -6
  231. crackerjack/models/protocols.py +1167 -23
  232. crackerjack/models/pydantic_models.py +320 -0
  233. crackerjack/models/qa_config.py +145 -0
  234. crackerjack/models/qa_results.py +134 -0
  235. crackerjack/models/results.py +35 -0
  236. crackerjack/models/semantic_models.py +258 -0
  237. crackerjack/models/task.py +19 -3
  238. crackerjack/models/test_models.py +60 -0
  239. crackerjack/monitoring/README.md +11 -0
  240. crackerjack/monitoring/ai_agent_watchdog.py +5 -4
  241. crackerjack/monitoring/metrics_collector.py +4 -3
  242. crackerjack/monitoring/regression_prevention.py +4 -3
  243. crackerjack/monitoring/websocket_server.py +4 -241
  244. crackerjack/orchestration/README.md +340 -0
  245. crackerjack/orchestration/__init__.py +43 -0
  246. crackerjack/orchestration/advanced_orchestrator.py +20 -67
  247. crackerjack/orchestration/cache/README.md +312 -0
  248. crackerjack/orchestration/cache/__init__.py +37 -0
  249. crackerjack/orchestration/cache/memory_cache.py +338 -0
  250. crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
  251. crackerjack/orchestration/config.py +297 -0
  252. crackerjack/orchestration/coverage_improvement.py +13 -6
  253. crackerjack/orchestration/execution_strategies.py +6 -6
  254. crackerjack/orchestration/hook_orchestrator.py +1398 -0
  255. crackerjack/orchestration/strategies/README.md +401 -0
  256. crackerjack/orchestration/strategies/__init__.py +39 -0
  257. crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
  258. crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
  259. crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
  260. crackerjack/orchestration/test_progress_streamer.py +1 -1
  261. crackerjack/plugins/README.md +11 -0
  262. crackerjack/plugins/hooks.py +3 -2
  263. crackerjack/plugins/loader.py +3 -3
  264. crackerjack/plugins/managers.py +1 -1
  265. crackerjack/py313.py +191 -0
  266. crackerjack/security/README.md +11 -0
  267. crackerjack/services/README.md +374 -0
  268. crackerjack/services/__init__.py +8 -21
  269. crackerjack/services/ai/README.md +295 -0
  270. crackerjack/services/ai/__init__.py +7 -0
  271. crackerjack/services/ai/advanced_optimizer.py +878 -0
  272. crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
  273. crackerjack/services/ai/embeddings.py +444 -0
  274. crackerjack/services/ai/intelligent_commit.py +328 -0
  275. crackerjack/services/ai/predictive_analytics.py +510 -0
  276. crackerjack/services/api_extractor.py +5 -3
  277. crackerjack/services/bounded_status_operations.py +45 -5
  278. crackerjack/services/cache.py +249 -318
  279. crackerjack/services/changelog_automation.py +7 -3
  280. crackerjack/services/command_execution_service.py +305 -0
  281. crackerjack/services/config_integrity.py +83 -39
  282. crackerjack/services/config_merge.py +9 -6
  283. crackerjack/services/config_service.py +198 -0
  284. crackerjack/services/config_template.py +13 -26
  285. crackerjack/services/coverage_badge_service.py +6 -4
  286. crackerjack/services/coverage_ratchet.py +53 -27
  287. crackerjack/services/debug.py +18 -7
  288. crackerjack/services/dependency_analyzer.py +4 -4
  289. crackerjack/services/dependency_monitor.py +13 -13
  290. crackerjack/services/documentation_generator.py +4 -2
  291. crackerjack/services/documentation_service.py +62 -33
  292. crackerjack/services/enhanced_filesystem.py +81 -27
  293. crackerjack/services/enterprise_optimizer.py +1 -1
  294. crackerjack/services/error_pattern_analyzer.py +10 -10
  295. crackerjack/services/file_filter.py +221 -0
  296. crackerjack/services/file_hasher.py +5 -7
  297. crackerjack/services/file_io_service.py +361 -0
  298. crackerjack/services/file_modifier.py +615 -0
  299. crackerjack/services/filesystem.py +80 -109
  300. crackerjack/services/git.py +99 -5
  301. crackerjack/services/health_metrics.py +4 -6
  302. crackerjack/services/heatmap_generator.py +12 -3
  303. crackerjack/services/incremental_executor.py +380 -0
  304. crackerjack/services/initialization.py +101 -49
  305. crackerjack/services/log_manager.py +2 -2
  306. crackerjack/services/logging.py +120 -68
  307. crackerjack/services/lsp_client.py +12 -12
  308. crackerjack/services/memory_optimizer.py +27 -22
  309. crackerjack/services/monitoring/README.md +30 -0
  310. crackerjack/services/monitoring/__init__.py +9 -0
  311. crackerjack/services/monitoring/dependency_monitor.py +678 -0
  312. crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
  313. crackerjack/services/monitoring/health_metrics.py +716 -0
  314. crackerjack/services/monitoring/metrics.py +587 -0
  315. crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
  316. crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
  317. crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
  318. crackerjack/services/parallel_executor.py +166 -55
  319. crackerjack/services/patterns/__init__.py +142 -0
  320. crackerjack/services/patterns/agents.py +107 -0
  321. crackerjack/services/patterns/code/__init__.py +15 -0
  322. crackerjack/services/patterns/code/detection.py +118 -0
  323. crackerjack/services/patterns/code/imports.py +107 -0
  324. crackerjack/services/patterns/code/paths.py +159 -0
  325. crackerjack/services/patterns/code/performance.py +119 -0
  326. crackerjack/services/patterns/code/replacement.py +36 -0
  327. crackerjack/services/patterns/core.py +212 -0
  328. crackerjack/services/patterns/documentation/__init__.py +14 -0
  329. crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
  330. crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
  331. crackerjack/services/patterns/documentation/docstrings.py +89 -0
  332. crackerjack/services/patterns/formatting.py +226 -0
  333. crackerjack/services/patterns/operations.py +339 -0
  334. crackerjack/services/patterns/security/__init__.py +23 -0
  335. crackerjack/services/patterns/security/code_injection.py +122 -0
  336. crackerjack/services/patterns/security/credentials.py +190 -0
  337. crackerjack/services/patterns/security/path_traversal.py +221 -0
  338. crackerjack/services/patterns/security/unsafe_operations.py +216 -0
  339. crackerjack/services/patterns/templates.py +62 -0
  340. crackerjack/services/patterns/testing/__init__.py +18 -0
  341. crackerjack/services/patterns/testing/error_patterns.py +107 -0
  342. crackerjack/services/patterns/testing/pytest_output.py +126 -0
  343. crackerjack/services/patterns/tool_output/__init__.py +16 -0
  344. crackerjack/services/patterns/tool_output/bandit.py +72 -0
  345. crackerjack/services/patterns/tool_output/other.py +97 -0
  346. crackerjack/services/patterns/tool_output/pyright.py +67 -0
  347. crackerjack/services/patterns/tool_output/ruff.py +44 -0
  348. crackerjack/services/patterns/url_sanitization.py +114 -0
  349. crackerjack/services/patterns/utilities.py +42 -0
  350. crackerjack/services/patterns/utils.py +339 -0
  351. crackerjack/services/patterns/validation.py +46 -0
  352. crackerjack/services/patterns/versioning.py +62 -0
  353. crackerjack/services/predictive_analytics.py +21 -8
  354. crackerjack/services/profiler.py +280 -0
  355. crackerjack/services/quality/README.md +415 -0
  356. crackerjack/services/quality/__init__.py +11 -0
  357. crackerjack/services/quality/anomaly_detector.py +392 -0
  358. crackerjack/services/quality/pattern_cache.py +333 -0
  359. crackerjack/services/quality/pattern_detector.py +479 -0
  360. crackerjack/services/quality/qa_orchestrator.py +491 -0
  361. crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
  362. crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
  363. crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
  364. crackerjack/services/regex_patterns.py +58 -2987
  365. crackerjack/services/regex_utils.py +55 -29
  366. crackerjack/services/secure_status_formatter.py +42 -15
  367. crackerjack/services/secure_subprocess.py +35 -2
  368. crackerjack/services/security.py +16 -8
  369. crackerjack/services/server_manager.py +40 -51
  370. crackerjack/services/smart_scheduling.py +46 -6
  371. crackerjack/services/status_authentication.py +3 -3
  372. crackerjack/services/thread_safe_status_collector.py +1 -0
  373. crackerjack/services/tool_filter.py +368 -0
  374. crackerjack/services/tool_version_service.py +9 -5
  375. crackerjack/services/unified_config.py +43 -351
  376. crackerjack/services/vector_store.py +689 -0
  377. crackerjack/services/version_analyzer.py +6 -4
  378. crackerjack/services/version_checker.py +14 -8
  379. crackerjack/services/zuban_lsp_service.py +5 -4
  380. crackerjack/slash_commands/README.md +11 -0
  381. crackerjack/slash_commands/init.md +2 -12
  382. crackerjack/slash_commands/run.md +84 -50
  383. crackerjack/tools/README.md +11 -0
  384. crackerjack/tools/__init__.py +30 -0
  385. crackerjack/tools/_git_utils.py +105 -0
  386. crackerjack/tools/check_added_large_files.py +139 -0
  387. crackerjack/tools/check_ast.py +105 -0
  388. crackerjack/tools/check_json.py +103 -0
  389. crackerjack/tools/check_jsonschema.py +297 -0
  390. crackerjack/tools/check_toml.py +103 -0
  391. crackerjack/tools/check_yaml.py +110 -0
  392. crackerjack/tools/codespell_wrapper.py +72 -0
  393. crackerjack/tools/end_of_file_fixer.py +202 -0
  394. crackerjack/tools/format_json.py +128 -0
  395. crackerjack/tools/mdformat_wrapper.py +114 -0
  396. crackerjack/tools/trailing_whitespace.py +198 -0
  397. crackerjack/tools/validate_regex_patterns.py +7 -3
  398. crackerjack/ui/README.md +11 -0
  399. crackerjack/ui/dashboard_renderer.py +28 -0
  400. crackerjack/ui/templates/README.md +11 -0
  401. crackerjack/utils/console_utils.py +13 -0
  402. crackerjack/utils/dependency_guard.py +230 -0
  403. crackerjack/utils/retry_utils.py +275 -0
  404. crackerjack/workflows/README.md +590 -0
  405. crackerjack/workflows/__init__.py +46 -0
  406. crackerjack/workflows/actions.py +811 -0
  407. crackerjack/workflows/auto_fix.py +444 -0
  408. crackerjack/workflows/container_builder.py +499 -0
  409. crackerjack/workflows/definitions.py +443 -0
  410. crackerjack/workflows/engine.py +177 -0
  411. crackerjack/workflows/event_bridge.py +242 -0
  412. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
  413. crackerjack-0.45.2.dist-info/RECORD +478 -0
  414. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
  415. crackerjack/managers/test_manager_backup.py +0 -1075
  416. crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
  417. crackerjack/mixins/__init__.py +0 -3
  418. crackerjack/mixins/error_handling.py +0 -145
  419. crackerjack/services/config.py +0 -358
  420. crackerjack/ui/server_panels.py +0 -125
  421. crackerjack-0.37.9.dist-info/RECORD +0 -231
  422. /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
  423. /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
  424. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
  425. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,53 @@
1
+ > Crackerjack Docs: [Main](<../../../README.md>) | [Adapters](<../README.md>) | [Complexity](<./README.md>)
2
+
3
+ # Complexity Adapter
4
+
5
+ Code complexity analysis with thresholds and structured results. Uses a standardized interface built on the QA adapter base.
6
+
7
+ ## Overview
8
+
9
+ - Reports cyclomatic and cognitive complexity, maintainability index, and LOC
10
+ - Threshold-driven warnings/errors for actionable feedback
11
+ - JSON parsing for precise per-function metrics
12
+
13
+ ## Built-in Implementation
14
+
15
+ | Module | Description | Status |
16
+ | ------ | ----------- | ------ |
17
+ | `complexipy.py` | Complexity analysis with configurable thresholds and metrics | Stable |
18
+
19
+ ## Settings
20
+
21
+ Settings class: `ComplexipySettings`
22
+
23
+ - `max_complexity` (int; default 15)
24
+ - `include_cognitive` (bool; default True)
25
+ - `include_maintainability` (bool; default True)
26
+ - `sort_by` (str; `complexity`/`cognitive`/`name`)
27
+
28
+ ## Basic Usage
29
+
30
+ ```python
31
+ from pathlib import Path
32
+ from crackerjack.adapters.complexity.complexipy import (
33
+ ComplexipyAdapter,
34
+ ComplexipySettings,
35
+ )
36
+
37
+
38
+ async def analyze_complexity() -> None:
39
+ adapter = ComplexipyAdapter(settings=ComplexipySettings(max_complexity=15))
40
+ await adapter.init()
41
+ result = await adapter.check(files=[Path("src/")])
42
+ print(result.status, result.issues_found)
43
+ ```
44
+
45
+ ## Tips
46
+
47
+ - Start with `max_complexity=15` (Crackerjack standard), tighten as code matures
48
+ - Use results to prioritize refactors (high complexity or low maintainability)
49
+
50
+ ## Related
51
+
52
+ - [Refactor](<../refactor/README.md>) — Tools to modernize and reduce complexity
53
+ - [Format](<../format/README.md>) — Formatting often improves readability and maintainability
@@ -0,0 +1,10 @@
1
+ """Complexity adapters for code complexity analysis.
2
+
3
+ Adapters:
4
+ - complexipy: Multi-metric complexity analyzer (cyclomatic, cognitive, maintainability)
5
+ """
6
+
7
+ # ACB will auto-discover these adapters via depends.set() in module files
8
+ # No explicit imports needed here
9
+
10
+ __all__ = []
@@ -0,0 +1,641 @@
1
+ """Complexipy adapter for ACB QA framework - code complexity analysis.
2
+
3
+ Complexipy analyzes Python code complexity using multiple metrics:
4
+ - Cyclomatic complexity (McCabe)
5
+ - Cognitive complexity
6
+ - Maintainability index
7
+ - Lines of code metrics
8
+
9
+ ACB Patterns:
10
+ - MODULE_ID and MODULE_STATUS at module level
11
+ - depends.set() registration after class definition
12
+ - Extends BaseToolAdapter for tool execution
13
+ - Async execution with JSON output parsing
14
+ - Centralized output file management via AdapterOutputPaths
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import json
20
+ import logging
21
+ import shutil
22
+ import typing as t
23
+ from contextlib import suppress
24
+ from pathlib import Path
25
+ from uuid import UUID
26
+
27
+ from acb.depends import depends
28
+
29
+ from crackerjack.adapters._output_paths import AdapterOutputPaths
30
+ from crackerjack.adapters._tool_adapter_base import (
31
+ BaseToolAdapter,
32
+ ToolAdapterSettings,
33
+ ToolExecutionResult,
34
+ ToolIssue,
35
+ )
36
+ from crackerjack.models.qa_results import QACheckType
37
+
38
+ if t.TYPE_CHECKING:
39
+ from crackerjack.models.qa_config import QACheckConfig
40
+
41
+ # ACB Module Registration (REQUIRED)
42
+ MODULE_ID = UUID(
43
+ "01937d86-9e5f-a6b7-c8d9-e0f1a2b3c4d5"
44
+ ) # Static UUID7 for reproducible module identity
45
+ MODULE_STATUS = "stable"
46
+
47
+ # Module-level logger for structured logging
48
+ logger = logging.getLogger(__name__)
49
+
50
+
51
+ class ComplexipySettings(ToolAdapterSettings):
52
+ """Settings for Complexipy adapter."""
53
+
54
+ tool_name: str = "complexipy"
55
+ use_json_output: bool = True
56
+ max_complexity: int = 15 # crackerjack standard
57
+ include_cognitive: bool = True
58
+ include_maintainability: bool = True
59
+ sort_by: str = (
60
+ "desc" # Valid options: asc, desc, name (sorts by complexity descending)
61
+ )
62
+
63
+
64
+ class ComplexipyAdapter(BaseToolAdapter):
65
+ """Adapter for Complexipy - code complexity analyzer.
66
+
67
+ Analyzes code complexity using multiple metrics:
68
+ - Cyclomatic complexity (control flow branches)
69
+ - Cognitive complexity (how hard code is to understand)
70
+ - Maintainability index (overall code quality score)
71
+ - Lines of code (LOC, SLOC)
72
+
73
+ Features:
74
+ - JSON output for structured analysis
75
+ - Configurable complexity thresholds
76
+ - Multiple complexity metrics
77
+ - Sortable results
78
+
79
+ Example:
80
+ ```python
81
+ settings = ComplexipySettings(
82
+ max_complexity=15,
83
+ include_cognitive=True,
84
+ include_maintainability=True,
85
+ )
86
+ adapter = ComplexipyAdapter(settings=settings)
87
+ await adapter.init()
88
+ result = await adapter.check(files=[Path("src/")])
89
+ ```
90
+ """
91
+
92
+ settings: ComplexipySettings | None = None
93
+
94
+ def __init__(self, settings: ComplexipySettings | None = None) -> None:
95
+ """Initialize Complexipy adapter.
96
+
97
+ Args:
98
+ settings: Optional settings override
99
+ """
100
+ super().__init__(settings=settings)
101
+ logger.debug(
102
+ "ComplexipyAdapter initialized",
103
+ extra={"has_settings": settings is not None},
104
+ )
105
+
106
+ async def init(self) -> None:
107
+ """Initialize adapter with default settings."""
108
+ if not self.settings:
109
+ # Load max_complexity from pyproject.toml and use it to initialize settings
110
+ config_data = self._load_config_from_pyproject()
111
+ max_complexity = config_data.get("max_complexity", 15)
112
+ self.settings = ComplexipySettings(max_complexity=max_complexity)
113
+ logger.info(
114
+ "Using default ComplexipySettings",
115
+ extra={"max_complexity": max_complexity},
116
+ )
117
+ await super().init()
118
+ logger.debug(
119
+ "ComplexipyAdapter initialization complete",
120
+ extra={
121
+ "max_complexity": self.settings.max_complexity,
122
+ "include_cognitive": self.settings.include_cognitive,
123
+ "include_maintainability": self.settings.include_maintainability,
124
+ "sort_by": self.settings.sort_by,
125
+ },
126
+ )
127
+
128
+ @property
129
+ def adapter_name(self) -> str:
130
+ """Human-readable adapter name."""
131
+ return "Complexipy (Complexity)"
132
+
133
+ @property
134
+ def module_id(self) -> UUID:
135
+ """Reference to module-level MODULE_ID."""
136
+ return MODULE_ID
137
+
138
+ @property
139
+ def tool_name(self) -> str:
140
+ """CLI tool name."""
141
+ return "complexipy"
142
+
143
+ def build_command(
144
+ self,
145
+ files: list[Path],
146
+ config: QACheckConfig | None = None,
147
+ ) -> list[str]:
148
+ """Build Complexipy command.
149
+
150
+ Args:
151
+ files: Files/directories to analyze
152
+ config: Optional configuration override
153
+
154
+ Returns:
155
+ Command as list of strings
156
+ """
157
+ if not self.settings:
158
+ raise RuntimeError("Settings not initialized")
159
+
160
+ cmd = [self.tool_name]
161
+
162
+ # JSON output (complexipy creates timestamped files automatically)
163
+ # We'll move the file to centralized location after execution
164
+ if self.settings.use_json_output:
165
+ cmd.append("--output-json")
166
+
167
+ # Max complexity threshold (correct flag: --max-complexity-allowed, not --max-complexity)
168
+ # Load max_complexity from pyproject.toml configuration instead of settings
169
+ config_data = self._load_config_from_pyproject()
170
+ max_complexity = config_data.get("max_complexity", self.settings.max_complexity)
171
+ cmd.extend(["--max-complexity-allowed", str(max_complexity)])
172
+
173
+ # NOTE: --cognitive and --maintainability flags don't exist in complexipy
174
+ # Complexity tool only reports cyclomatic complexity, not cognitive/maintainability
175
+ # These settings are kept in ComplexipySettings for backwards compatibility but ignored
176
+
177
+ # Sort results
178
+ cmd.extend(["--sort", self.settings.sort_by])
179
+
180
+ # Add targets - files are already filtered by _get_target_files based on config
181
+ cmd.extend([str(f) for f in files])
182
+
183
+ logger.info(
184
+ "Built Complexipy command",
185
+ extra={
186
+ "file_count": len(files),
187
+ "max_complexity": max_complexity,
188
+ "include_cognitive": self.settings.include_cognitive,
189
+ "include_maintainability": self.settings.include_maintainability,
190
+ },
191
+ )
192
+ return cmd
193
+
194
+ async def parse_output(
195
+ self,
196
+ result: ToolExecutionResult,
197
+ ) -> list[ToolIssue]:
198
+ """Parse Complexipy JSON output into standardized issues.
199
+
200
+ Complexipy with --output-json saves JSON to timestamped files in the current
201
+ directory. We move these files to .crackerjack/outputs/complexipy/ for
202
+ centralized management.
203
+
204
+ Args:
205
+ result: Raw execution result from Complexipy
206
+
207
+ Returns:
208
+ List of parsed issues
209
+ """
210
+ # Move generated complexipy files to centralized location
211
+ json_file = self._move_complexipy_results_to_output_dir()
212
+
213
+ if self.settings.use_json_output and json_file and json_file.exists():
214
+ try:
215
+ with json_file.open() as f:
216
+ data = json.load(f)
217
+ logger.debug(
218
+ "Read Complexipy JSON file",
219
+ extra={
220
+ "file": str(json_file),
221
+ "entries": len(data) if isinstance(data, list) else "N/A",
222
+ },
223
+ )
224
+ except (json.JSONDecodeError, OSError) as e:
225
+ logger.warning(
226
+ "Failed to read JSON file, falling back to stdout parsing",
227
+ extra={"error": str(e), "file": str(json_file)},
228
+ )
229
+ return self._parse_text_output(result.raw_output)
230
+ else:
231
+ # Fall back to parsing stdout (legacy mode or if JSON file not found)
232
+ if not result.raw_output:
233
+ logger.debug("No output to parse")
234
+ return []
235
+
236
+ try:
237
+ data = json.loads(result.raw_output)
238
+ logger.debug(
239
+ "Parsed Complexipy JSON from stdout",
240
+ extra={"entries": len(data) if isinstance(data, list) else "N/A"},
241
+ )
242
+ except json.JSONDecodeError as e:
243
+ logger.debug(
244
+ "JSON parse failed, falling back to text parsing",
245
+ extra={"error": str(e), "output_preview": result.raw_output[:200]},
246
+ )
247
+ return self._parse_text_output(result.raw_output)
248
+
249
+ issues = self._process_complexipy_data(data)
250
+
251
+ logger.info(
252
+ "Parsed Complexipy output",
253
+ extra={
254
+ "total_issues": len(issues),
255
+ "high_complexity": sum(1 for i in issues if i.severity == "error"),
256
+ "files_affected": len({str(i.file_path) for i in issues}),
257
+ },
258
+ )
259
+ return issues
260
+
261
+ def _process_complexipy_data(self, data: list | dict) -> list[ToolIssue]:
262
+ """Process the complexipy JSON data to extract issues.
263
+
264
+ Args:
265
+ data: Parsed JSON data from complexipy (flat list or legacy nested dict)
266
+
267
+ Returns:
268
+ List of ToolIssue objects
269
+ """
270
+ issues = []
271
+
272
+ # Handle flat list structure (current complexipy format)
273
+ if isinstance(data, list):
274
+ for func in data:
275
+ complexity = func.get("complexity", 0)
276
+ if complexity <= self.settings.max_complexity:
277
+ continue
278
+
279
+ file_path = Path(func.get("path", ""))
280
+ function_name = func.get("function_name", "unknown")
281
+ severity = (
282
+ "error"
283
+ if complexity > self.settings.max_complexity * 2
284
+ else "warning"
285
+ )
286
+
287
+ issue = ToolIssue(
288
+ file_path=file_path,
289
+ line_number=None, # complexipy JSON doesn't include line numbers
290
+ message=f"Function '{function_name}' - Complexity: {complexity}",
291
+ code="COMPLEXITY",
292
+ severity=severity,
293
+ suggestion=f"Consider refactoring to reduce complexity below {self.settings.max_complexity}",
294
+ )
295
+ issues.append(issue)
296
+ return issues
297
+
298
+ # Handle legacy nested structure (backwards compatibility)
299
+ for file_data in data.get("files", []):
300
+ file_path = Path(file_data.get("path", ""))
301
+ issues.extend(
302
+ self._process_file_data(file_path, file_data.get("functions", []))
303
+ )
304
+ return issues
305
+
306
+ def _process_file_data(
307
+ self, file_path: Path, functions: list[dict]
308
+ ) -> list[ToolIssue]:
309
+ """Process function data for a specific file.
310
+
311
+ Args:
312
+ file_path: Path of the file being analyzed
313
+ functions: List of function data from complexipy
314
+
315
+ Returns:
316
+ List of ToolIssue objects for this file
317
+ """
318
+ issues = []
319
+ for func in functions:
320
+ issue = self._create_issue_if_needed(file_path, func)
321
+ if issue:
322
+ issues.append(issue)
323
+ return issues
324
+
325
+ def _create_issue_if_needed(self, file_path: Path, func: dict) -> ToolIssue | None:
326
+ """Create an issue if complexity exceeds threshold.
327
+
328
+ Args:
329
+ file_path: Path of the file containing the function
330
+ func: Function data from complexipy
331
+
332
+ Returns:
333
+ ToolIssue if needed, otherwise None
334
+ """
335
+ complexity = func.get("complexity", 0)
336
+
337
+ # Only report if exceeds threshold
338
+ if complexity <= self.settings.max_complexity:
339
+ return None
340
+
341
+ message = self._build_issue_message(func, complexity)
342
+ severity = self._determine_issue_severity(complexity)
343
+
344
+ return ToolIssue(
345
+ file_path=file_path,
346
+ line_number=func.get("line"),
347
+ message=message,
348
+ code="COMPLEXITY",
349
+ severity=severity,
350
+ suggestion=f"Consider refactoring to reduce complexity below {self.settings.max_complexity}",
351
+ )
352
+
353
+ def _build_issue_message(self, func: dict, complexity: int) -> str:
354
+ """Build the message for a complexity issue.
355
+
356
+ Args:
357
+ func: Function data from complexipy
358
+ complexity: Complexity value
359
+
360
+ Returns:
361
+ Formatted message string
362
+ """
363
+ message_parts = [f"Complexity: {complexity}"]
364
+
365
+ if self.settings.include_cognitive:
366
+ cognitive = func.get("cognitive_complexity", 0)
367
+ message_parts.append(f"Cognitive: {cognitive}")
368
+
369
+ if self.settings.include_maintainability:
370
+ maintainability = func.get("maintainability", 100)
371
+ message_parts.append(f"Maintainability: {maintainability:.1f}")
372
+
373
+ return f"Function '{func.get('name', 'unknown')}' - " + ", ".join(message_parts)
374
+
375
+ def _determine_issue_severity(self, complexity: int) -> str:
376
+ """Determine the severity of the issue based on complexity.
377
+
378
+ Args:
379
+ complexity: Complexity value
380
+
381
+ Returns:
382
+ "error" or "warning" based on threshold
383
+ """
384
+ if complexity > self.settings.max_complexity * 2:
385
+ return "error"
386
+ return "warning"
387
+
388
+ def _parse_text_output(self, output: str) -> list[ToolIssue]:
389
+ """Parse Complexipy text output (fallback).
390
+
391
+ Args:
392
+ output: Text output from Complexipy
393
+
394
+ Returns:
395
+ List of ToolIssue objects
396
+ """
397
+ issues = []
398
+ lines = output.strip().split("\n")
399
+ current_file: Path | None = None
400
+
401
+ for line in lines:
402
+ current_file = self._update_current_file(line, current_file)
403
+ if "complexity" in line.lower() and current_file:
404
+ issue = self._parse_complexity_line(line, current_file)
405
+ if issue:
406
+ issues.append(issue)
407
+
408
+ logger.info(
409
+ "Parsed Complexipy text output (fallback)",
410
+ extra={
411
+ "total_issues": len(issues),
412
+ "files_with_issues": len({str(i.file_path) for i in issues}),
413
+ },
414
+ )
415
+ return issues
416
+
417
+ def _update_current_file(self, line: str, current_file: Path | None) -> Path | None:
418
+ """Update current file based on line content.
419
+
420
+ Args:
421
+ line: Current line from output
422
+ current_file: Current file path
423
+
424
+ Returns:
425
+ Updated file path
426
+ """
427
+ if line.strip().startswith("File:"):
428
+ file_str = line.strip().replace("File:", "").strip()
429
+ return Path(file_str)
430
+ return current_file
431
+
432
+ def _parse_complexity_line(self, line: str, current_file: Path) -> ToolIssue | None:
433
+ """Parse a line containing complexity information.
434
+
435
+ Args:
436
+ line: Line from text output
437
+ current_file: Current file path
438
+
439
+ Returns:
440
+ ToolIssue if valid complexity data found, otherwise None
441
+ """
442
+ with suppress(ValueError, IndexError):
443
+ func_data = self._extract_function_data(line)
444
+ if func_data:
445
+ func_name, line_number, complexity = func_data
446
+ if complexity > self.settings.max_complexity:
447
+ severity = (
448
+ "error"
449
+ if complexity > self.settings.max_complexity * 2
450
+ else "warning"
451
+ )
452
+ return ToolIssue(
453
+ file_path=current_file,
454
+ line_number=line_number,
455
+ message=f"Function '{func_name}' has complexity {complexity}",
456
+ code="COMPLEXITY",
457
+ severity=severity,
458
+ )
459
+
460
+ return None
461
+
462
+ def _extract_function_data(self, line: str) -> tuple[str, int, int] | None:
463
+ """Extract function name, line number, and complexity from a line.
464
+
465
+ Args:
466
+ line: Line from text output
467
+
468
+ Returns:
469
+ Tuple of (function name, line number, complexity) or None
470
+ """
471
+ line = line.strip()
472
+ if "(" in line and ")" in line and "complexity" in line.lower():
473
+ func_name = line.split("(")[0].strip()
474
+ line_part = line.split("(")[1].split(")")[0]
475
+ line_number = int(line_part.replace("line", "").strip())
476
+ complexity_part = line.split("complexity")[1].strip()
477
+ complexity = int(complexity_part.split()[0])
478
+ return func_name, line_number, complexity
479
+
480
+ return None
481
+
482
+ def _get_check_type(self) -> QACheckType:
483
+ """Return complexity check type."""
484
+ return QACheckType.COMPLEXITY
485
+
486
+ def get_default_config(self) -> QACheckConfig:
487
+ """Get default configuration for Complexipy adapter.
488
+
489
+ Returns:
490
+ QACheckConfig with sensible defaults
491
+ """
492
+ from crackerjack.models.qa_config import QACheckConfig
493
+
494
+ # Load configuration from pyproject.toml to get actual exclude patterns and max_complexity
495
+ config_data = self._load_config_from_pyproject()
496
+ exclude_patterns = config_data.get(
497
+ "exclude_patterns", ["**/.venv/**", "**/venv/**", "**/tests/**"]
498
+ )
499
+ max_complexity = config_data.get("max_complexity", 15)
500
+
501
+ return QACheckConfig(
502
+ check_id=MODULE_ID,
503
+ check_name=self.adapter_name,
504
+ check_type=QACheckType.COMPLEXITY,
505
+ enabled=True,
506
+ file_patterns=["**/*.py"],
507
+ exclude_patterns=exclude_patterns,
508
+ timeout_seconds=90,
509
+ parallel_safe=True,
510
+ stage="comprehensive", # Complexity analysis in comprehensive stage
511
+ settings={
512
+ "max_complexity": max_complexity,
513
+ "include_cognitive": True,
514
+ "include_maintainability": True,
515
+ "sort_by": "complexity",
516
+ },
517
+ )
518
+
519
+ def _load_config_from_pyproject(self) -> dict:
520
+ """Load complexipy configuration from pyproject.toml.
521
+
522
+ Returns:
523
+ Dictionary with complexipy configuration or defaults
524
+ """
525
+ import tomllib
526
+ from pathlib import Path
527
+
528
+ pyproject_path = Path.cwd() / "pyproject.toml"
529
+ config = {
530
+ "exclude_patterns": ["**/.venv/**", "**/venv/**", "**/tests/**"],
531
+ "max_complexity": 15,
532
+ }
533
+
534
+ if pyproject_path.exists():
535
+ try:
536
+ with pyproject_path.open("rb") as f:
537
+ toml_config = tomllib.load(f)
538
+ complexipy_config = toml_config.get("tool", {}).get("complexipy", {})
539
+
540
+ # Load exclude patterns if specified
541
+ exclude_patterns = complexipy_config.get("exclude_patterns")
542
+ if exclude_patterns:
543
+ config["exclude_patterns"] = exclude_patterns
544
+ logger.info(
545
+ "Loaded exclude patterns from pyproject.toml",
546
+ extra={"exclude_patterns": exclude_patterns},
547
+ )
548
+
549
+ # Load max complexity if specified
550
+ max_complexity = complexipy_config.get("max_complexity")
551
+ if max_complexity is not None:
552
+ config["max_complexity"] = max_complexity
553
+ logger.info(
554
+ "Loaded max_complexity from pyproject.toml",
555
+ extra={"max_complexity": max_complexity},
556
+ )
557
+ except (tomllib.TOMLDecodeError, OSError) as e:
558
+ logger.warning(
559
+ "Failed to load complexipy config from pyproject.toml, using defaults",
560
+ extra={"error": str(e)},
561
+ )
562
+
563
+ return config
564
+
565
+ def _load_exclude_patterns_from_config(self) -> list[str]:
566
+ """Load exclude patterns from pyproject.toml configuration.
567
+
568
+ Returns:
569
+ List of exclude patterns from pyproject.toml or defaults
570
+ """
571
+ config = self._load_config_from_pyproject()
572
+ return config.get(
573
+ "exclude_patterns", ["**/.venv/**", "**/venv/**", "**/tests/**"]
574
+ )
575
+
576
+ def _move_complexipy_results_to_output_dir(self) -> Path | None:
577
+ """Move complexipy-generated result files to centralized output directory.
578
+
579
+ Complexipy creates timestamped files like:
580
+ complexipy_results_2025_12_09__07:08:33.json
581
+
582
+ This method:
583
+ 1. Finds the most recent complexipy_results_*.json file in project root
584
+ 2. Moves it to .crackerjack/outputs/complexipy/
585
+ 3. Returns the new path
586
+
587
+ Returns:
588
+ Path to moved file in centralized location, or None if no file found
589
+ """
590
+ # Find all complexipy result files in project root
591
+ project_root = Path.cwd()
592
+ result_files = sorted(
593
+ project_root.glob("complexipy_results_*.json"),
594
+ key=lambda p: p.stat().st_mtime,
595
+ reverse=True,
596
+ )
597
+
598
+ if not result_files:
599
+ logger.debug("No complexipy result files found in project root")
600
+ return None
601
+
602
+ # Get the most recent file
603
+ source_file = result_files[0]
604
+
605
+ # Determine destination in centralized output directory
606
+ output_dir = AdapterOutputPaths.get_output_dir("complexipy")
607
+ dest_file = output_dir / source_file.name
608
+
609
+ try:
610
+ # Move the file to centralized location
611
+ shutil.move(str(source_file), str(dest_file))
612
+ logger.info(
613
+ "Moved complexipy results to centralized location",
614
+ extra={
615
+ "source": str(source_file),
616
+ "destination": str(dest_file),
617
+ },
618
+ )
619
+
620
+ # Clean up old result files (keep only latest 5)
621
+ AdapterOutputPaths.cleanup_old_outputs(
622
+ "complexipy", "complexipy_results_*.json", keep_latest=5
623
+ )
624
+
625
+ return dest_file
626
+ except (OSError, shutil.Error) as e:
627
+ logger.warning(
628
+ "Failed to move complexipy results file",
629
+ extra={
630
+ "error": str(e),
631
+ "source": str(source_file),
632
+ "destination": str(dest_file),
633
+ },
634
+ )
635
+ # Return source file if move fails (fallback)
636
+ return source_file
637
+
638
+
639
+ # ACB Registration (REQUIRED at module level)
640
+ with suppress(Exception):
641
+ depends.set(ComplexipyAdapter)