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,884 @@
1
+ """Generic utility check adapter for simple ACB Quality Assurance checks.
2
+
3
+ This adapter handles simple, configuration-driven checks like whitespace,
4
+ EOF newlines, YAML/TOML validation, file size limits, and dependency locks.
5
+
6
+ ACB Patterns:
7
+ - MODULE_ID and MODULE_STATUS at module level
8
+ - depends.set() registration after class definition
9
+ - Configuration-driven validators and fixers
10
+ - Async execution with semaphore control
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import asyncio
16
+ import logging
17
+ import subprocess
18
+ import typing as t
19
+ from contextlib import suppress
20
+ from enum import Enum
21
+ from pathlib import Path
22
+ from uuid import UUID, uuid4
23
+
24
+ import tomli
25
+ import yaml
26
+ from acb.depends import depends
27
+ from pydantic import Field, field_validator
28
+
29
+ from crackerjack.adapters._qa_adapter_base import QAAdapterBase, QABaseSettings
30
+ from crackerjack.models.qa_results import QACheckType, QAResult, QAResultStatus
31
+ from crackerjack.services.regex_patterns import CompiledPatternCache
32
+
33
+ if t.TYPE_CHECKING:
34
+ from crackerjack.models.qa_config import QACheckConfig
35
+
36
+ # ACB Module Registration (REQUIRED)
37
+ MODULE_ID = uuid4()
38
+ MODULE_STATUS = "stable"
39
+
40
+ # Module-level logger for structured logging
41
+ logger = logging.getLogger(__name__)
42
+
43
+
44
+ class UtilityCheckType(str, Enum):
45
+ """Types of utility checks supported by this adapter."""
46
+
47
+ TEXT_PATTERN = "text_pattern"
48
+ SYNTAX_VALIDATION = "syntax_validation"
49
+ SIZE_CHECK = "size_check"
50
+ EOF_NEWLINE = "eof_newline"
51
+ DEPENDENCY_LOCK = "dependency_lock"
52
+
53
+
54
+ class UtilityCheckSettings(QABaseSettings):
55
+ """Settings for utility check adapter.
56
+
57
+ Configuration-driven checks are defined here or loaded from YAML.
58
+ """
59
+
60
+ check_type: UtilityCheckType = Field(
61
+ ...,
62
+ description="Type of utility check to perform",
63
+ )
64
+ pattern: str | None = Field(
65
+ None,
66
+ description="Regex pattern for TEXT_PATTERN checks",
67
+ )
68
+ parser_type: str | None = Field(
69
+ None,
70
+ description="Parser type for SYNTAX_VALIDATION (yaml, toml, json)",
71
+ )
72
+ max_size_bytes: int | None = Field(
73
+ None,
74
+ ge=0,
75
+ description="Maximum file size in bytes for SIZE_CHECK",
76
+ )
77
+ auto_fix: bool = Field(
78
+ False,
79
+ description="Whether to automatically fix issues when possible",
80
+ )
81
+ lock_command: list[str] | None = Field(
82
+ None,
83
+ description="Command to run for DEPENDENCY_LOCK checks",
84
+ )
85
+
86
+ @field_validator("pattern")
87
+ def validate_pattern(cls, v: str | None, values: dict[str, t.Any]) -> str | None:
88
+ """Validate regex pattern using safe compilation.
89
+
90
+ Uses CompiledPatternCache for security and performance.
91
+ Pattern validation only occurs if pattern is provided.
92
+ Runtime check() will enforce required patterns.
93
+
94
+ Args:
95
+ v: Pattern string to validate
96
+ values: Other field values (unused)
97
+
98
+ Returns:
99
+ Validated pattern string or None
100
+
101
+ Raises:
102
+ ValueError: If pattern compilation fails
103
+ """
104
+ if v is not None:
105
+ try:
106
+ # Use cached, safe pattern compilation
107
+ CompiledPatternCache.get_compiled_pattern(v)
108
+ except ValueError as e:
109
+ raise ValueError(f"Invalid regex pattern: {e}")
110
+ return v
111
+
112
+ @field_validator("parser_type")
113
+ def validate_parser_type(
114
+ cls, v: str | None, values: dict[str, t.Any]
115
+ ) -> str | None:
116
+ """Validate parser type for SYNTAX_VALIDATION checks.
117
+
118
+ Parser type validation only occurs if parser_type is provided.
119
+ Runtime check() will enforce required parser_type.
120
+ """
121
+ if v is not None and v not in ("yaml", "toml", "json"):
122
+ raise ValueError(f"Unsupported parser type: {v}")
123
+ return v
124
+
125
+
126
+ class UtilityCheckAdapter(QAAdapterBase):
127
+ """Generic adapter for configuration-driven utility checks.
128
+
129
+ Handles simple checks like:
130
+ - Trailing whitespace detection/fixing
131
+ - EOF newline enforcement
132
+ - YAML/TOML/JSON syntax validation
133
+ - File size limits
134
+ - Dependency lock verification (uv.lock)
135
+
136
+ Example:
137
+ ```python
138
+ from uuid import uuid7
139
+ from crackerjack.adapters.qa.utility_check import (
140
+ UtilityCheckAdapter,
141
+ UtilityCheckSettings,
142
+ UtilityCheckType,
143
+ )
144
+
145
+ settings = UtilityCheckSettings(
146
+ check_type=UtilityCheckType.TEXT_PATTERN,
147
+ pattern=r"\\s+$",
148
+ auto_fix=True,
149
+ )
150
+ adapter = UtilityCheckAdapter(settings=settings)
151
+ await adapter.init()
152
+ result = await adapter.check(files=[Path("src/file.py")])
153
+ ```
154
+ """
155
+
156
+ settings: UtilityCheckSettings | None = None
157
+
158
+ def __init__(self, settings: UtilityCheckSettings | None = None) -> None:
159
+ """Initialize utility check adapter.
160
+
161
+ Args:
162
+ settings: Optional settings override
163
+ """
164
+ super().__init__()
165
+ if settings:
166
+ self.settings = settings
167
+
168
+ async def init(self) -> None:
169
+ """Initialize adapter with settings validation."""
170
+ if not self.settings:
171
+ # Default to trailing whitespace check if no settings provided
172
+ self.settings = UtilityCheckSettings(
173
+ check_type=UtilityCheckType.TEXT_PATTERN,
174
+ pattern=r"\s+$",
175
+ auto_fix=True,
176
+ )
177
+ await super().init()
178
+
179
+ @property
180
+ def adapter_name(self) -> str:
181
+ """Human-readable adapter name."""
182
+ if self.settings:
183
+ return f"UtilityCheck ({self.settings.check_type.value})"
184
+ return "UtilityCheck"
185
+
186
+ @property
187
+ def module_id(self) -> UUID:
188
+ """Reference to module-level MODULE_ID."""
189
+ return MODULE_ID
190
+
191
+ async def check(
192
+ self,
193
+ files: list[Path] | None = None,
194
+ config: QACheckConfig | None = None,
195
+ ) -> QAResult:
196
+ """Execute utility check on files.
197
+
198
+ Args:
199
+ files: List of files to check (None = check all matching patterns)
200
+ config: Optional configuration override
201
+
202
+ Returns:
203
+ QAResult with check execution results
204
+ """
205
+ if not self._initialized:
206
+ await self.init()
207
+
208
+ if not self.settings:
209
+ raise RuntimeError("Settings not initialized")
210
+
211
+ start_time = asyncio.get_event_loop().time()
212
+
213
+ # Determine files to check
214
+ target_files = await self._get_target_files(files, config)
215
+
216
+ if not target_files:
217
+ return self._create_skipped_result("No files to check", start_time)
218
+
219
+ # Execute appropriate check based on type
220
+ check_type = self.settings.check_type
221
+
222
+ if check_type == UtilityCheckType.TEXT_PATTERN:
223
+ result = await self._check_text_pattern(target_files, start_time)
224
+ elif check_type == UtilityCheckType.EOF_NEWLINE:
225
+ result = await self._check_eof_newline(target_files, start_time)
226
+ elif check_type == UtilityCheckType.SYNTAX_VALIDATION:
227
+ result = await self._check_syntax_validation(target_files, start_time)
228
+ elif check_type == UtilityCheckType.SIZE_CHECK:
229
+ result = await self._check_size_limit(target_files, start_time)
230
+ elif check_type == UtilityCheckType.DEPENDENCY_LOCK:
231
+ result = await self._check_dependency_lock(target_files, start_time)
232
+ else:
233
+ raise ValueError(f"Unsupported check type: {check_type}")
234
+
235
+ return result
236
+
237
+ @staticmethod
238
+ def _is_file_excluded(
239
+ file_path: Path,
240
+ exclude_patterns: list[str],
241
+ ) -> bool:
242
+ """Check if file matches any exclude pattern.
243
+
244
+ Args:
245
+ file_path: File to check
246
+ exclude_patterns: List of glob patterns to exclude
247
+
248
+ Returns:
249
+ True if file should be excluded
250
+ """
251
+ for exclude_pattern in exclude_patterns:
252
+ if file_path.match(exclude_pattern):
253
+ return True
254
+ return False
255
+
256
+ def _apply_exclude_filters(
257
+ self,
258
+ files: list[Path],
259
+ exclude_patterns: list[str],
260
+ ) -> list[Path]:
261
+ """Filter files by exclude patterns.
262
+
263
+ Args:
264
+ files: Files to filter
265
+ exclude_patterns: List of glob patterns to exclude
266
+
267
+ Returns:
268
+ Filtered file list
269
+ """
270
+ return [
271
+ file_path
272
+ for file_path in files
273
+ if not self._is_file_excluded(file_path, exclude_patterns)
274
+ ]
275
+
276
+ async def _get_target_files(
277
+ self,
278
+ files: list[Path] | None,
279
+ config: QACheckConfig | None,
280
+ ) -> list[Path]:
281
+ """Get list of files to check based on patterns.
282
+
283
+ Args:
284
+ files: Explicit file list or None for pattern matching
285
+ config: Configuration with file patterns
286
+
287
+ Returns:
288
+ List of Path objects to check
289
+ """
290
+ if files:
291
+ return files
292
+
293
+ if not self.settings:
294
+ return []
295
+
296
+ # Use config patterns if available, otherwise use settings patterns
297
+ patterns = config.file_patterns if config else self.settings.file_patterns
298
+ exclude_patterns = (
299
+ config.exclude_patterns if config else self.settings.exclude_patterns
300
+ )
301
+
302
+ # Simple glob-based file discovery
303
+ # In production, this would integrate with git/project structure
304
+ target_files: list[Path] = []
305
+ for pattern in patterns:
306
+ target_files.extend(Path.cwd().glob(pattern))
307
+
308
+ # Filter out excluded files
309
+ if exclude_patterns:
310
+ target_files = self._apply_exclude_filters(target_files, exclude_patterns)
311
+
312
+ return [f for f in target_files if f.is_file()]
313
+
314
+ def _process_file_lines(
315
+ self,
316
+ lines: list[str],
317
+ pattern: t.Pattern[str],
318
+ ) -> tuple[list[str], int]:
319
+ """Process file lines for pattern violations.
320
+
321
+ Args:
322
+ lines: File lines to process
323
+ pattern: Compiled regex pattern
324
+
325
+ Returns:
326
+ Tuple of (fixed_lines, issues_count)
327
+ """
328
+ fixed_lines = []
329
+ issues_count = 0
330
+
331
+ for line in lines:
332
+ if pattern.search(line):
333
+ issues_count += 1
334
+ if self.settings and self.settings.auto_fix:
335
+ # Remove pattern matches (e.g., trailing whitespace)
336
+ fixed_lines.append(pattern.sub("", line))
337
+ else:
338
+ fixed_lines.append(line)
339
+ else:
340
+ fixed_lines.append(line)
341
+
342
+ return fixed_lines, issues_count
343
+
344
+ def _create_pattern_result(
345
+ self,
346
+ files: list[Path],
347
+ issues_found: int,
348
+ issues_fixed: int,
349
+ files_modified: list[Path],
350
+ elapsed_ms: float,
351
+ ) -> QAResult:
352
+ """Create QAResult for pattern check.
353
+
354
+ Args:
355
+ files: Files checked
356
+ issues_found: Number of issues found
357
+ issues_fixed: Number of issues fixed
358
+ files_modified: List of modified files
359
+ elapsed_ms: Execution time in milliseconds
360
+
361
+ Returns:
362
+ QAResult with appropriate status
363
+ """
364
+ if issues_found == 0:
365
+ return QAResult(
366
+ check_id=MODULE_ID,
367
+ check_name=self.adapter_name,
368
+ check_type=QACheckType.FORMAT,
369
+ status=QAResultStatus.SUCCESS,
370
+ message="No pattern violations found",
371
+ files_checked=files,
372
+ execution_time_ms=elapsed_ms,
373
+ )
374
+ elif issues_fixed == issues_found:
375
+ return QAResult(
376
+ check_id=MODULE_ID,
377
+ check_name=self.adapter_name,
378
+ check_type=QACheckType.FORMAT,
379
+ status=QAResultStatus.SUCCESS,
380
+ message=f"Fixed {issues_fixed} pattern violations",
381
+ files_checked=files,
382
+ files_modified=files_modified,
383
+ issues_found=issues_found,
384
+ issues_fixed=issues_fixed,
385
+ execution_time_ms=elapsed_ms,
386
+ )
387
+ return QAResult(
388
+ check_id=MODULE_ID,
389
+ check_name=self.adapter_name,
390
+ check_type=QACheckType.FORMAT,
391
+ status=QAResultStatus.FAILURE,
392
+ message=f"Found {issues_found} pattern violations",
393
+ files_checked=files,
394
+ files_modified=files_modified,
395
+ issues_found=issues_found,
396
+ issues_fixed=issues_fixed,
397
+ execution_time_ms=elapsed_ms,
398
+ )
399
+
400
+ async def _check_text_pattern(
401
+ self,
402
+ files: list[Path],
403
+ start_time: float,
404
+ ) -> QAResult:
405
+ """Check files for text pattern violations.
406
+
407
+ Args:
408
+ files: Files to check
409
+ start_time: Check start time
410
+
411
+ Returns:
412
+ QAResult with pattern check results
413
+ """
414
+ if not self.settings or not self.settings.pattern:
415
+ raise ValueError("Pattern not configured")
416
+
417
+ # Use safe cached pattern compilation
418
+ pattern = CompiledPatternCache.get_compiled_pattern(self.settings.pattern)
419
+ issues_found = 0
420
+ issues_fixed = 0
421
+ files_modified: list[Path] = []
422
+
423
+ for file_path in files:
424
+ try:
425
+ content = file_path.read_text()
426
+ lines = content.splitlines(keepends=True)
427
+
428
+ fixed_lines, file_issues = self._process_file_lines(lines, pattern)
429
+ issues_found += file_issues
430
+
431
+ if file_issues > 0 and self.settings.auto_fix:
432
+ file_path.write_text("".join(fixed_lines))
433
+ files_modified.append(file_path)
434
+ issues_fixed += file_issues
435
+
436
+ except Exception as e:
437
+ # Add structured logging for failures
438
+ logger.warning(
439
+ "Failed to check file for pattern violations",
440
+ extra={
441
+ "file_path": str(file_path),
442
+ "check_type": self.settings.check_type.value,
443
+ "pattern": self.settings.pattern,
444
+ "error": str(e),
445
+ "error_type": type(e).__name__,
446
+ },
447
+ )
448
+ continue
449
+
450
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
451
+
452
+ return self._create_pattern_result(
453
+ files, issues_found, issues_fixed, files_modified, elapsed_ms
454
+ )
455
+
456
+ async def _check_eof_newline(
457
+ self,
458
+ files: list[Path],
459
+ start_time: float,
460
+ ) -> QAResult:
461
+ """Check files for missing EOF newlines.
462
+
463
+ Args:
464
+ files: Files to check
465
+ start_time: Check start time
466
+
467
+ Returns:
468
+ QAResult with EOF check results
469
+ """
470
+ issues_found = 0
471
+ issues_fixed = 0
472
+ files_modified: list[Path] = []
473
+
474
+ for file_path in files:
475
+ try:
476
+ content = file_path.read_text()
477
+ if content and not content.endswith("\n"):
478
+ issues_found += 1
479
+ if self.settings and self.settings.auto_fix:
480
+ file_path.write_text(content + "\n")
481
+ issues_fixed += 1
482
+ files_modified.append(file_path)
483
+ except Exception as e:
484
+ # Add structured logging for failures
485
+ logger.warning(
486
+ "Failed to check file for EOF newline",
487
+ extra={
488
+ "file_path": str(file_path),
489
+ "check_type": self.settings.check_type.value
490
+ if self.settings
491
+ else "unknown",
492
+ "auto_fix": self.settings.auto_fix if self.settings else False,
493
+ "error": str(e),
494
+ "error_type": type(e).__name__,
495
+ },
496
+ )
497
+ continue
498
+
499
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
500
+
501
+ if issues_found == 0:
502
+ return QAResult(
503
+ check_id=MODULE_ID,
504
+ check_name=self.adapter_name,
505
+ check_type=QACheckType.FORMAT,
506
+ status=QAResultStatus.SUCCESS,
507
+ message="All files have proper EOF newlines",
508
+ files_checked=files,
509
+ execution_time_ms=elapsed_ms,
510
+ )
511
+ elif issues_fixed == issues_found:
512
+ return QAResult(
513
+ check_id=MODULE_ID,
514
+ check_name=self.adapter_name,
515
+ check_type=QACheckType.FORMAT,
516
+ status=QAResultStatus.SUCCESS,
517
+ message=f"Fixed {issues_fixed} EOF newline issues",
518
+ files_checked=files,
519
+ files_modified=files_modified,
520
+ issues_found=issues_found,
521
+ issues_fixed=issues_fixed,
522
+ execution_time_ms=elapsed_ms,
523
+ )
524
+ return QAResult(
525
+ check_id=MODULE_ID,
526
+ check_name=self.adapter_name,
527
+ check_type=QACheckType.FORMAT,
528
+ status=QAResultStatus.FAILURE,
529
+ message=f"Found {issues_found} files missing EOF newlines",
530
+ files_checked=files,
531
+ issues_found=issues_found,
532
+ execution_time_ms=elapsed_ms,
533
+ )
534
+
535
+ async def _check_syntax_validation(
536
+ self,
537
+ files: list[Path],
538
+ start_time: float,
539
+ ) -> QAResult:
540
+ """Validate file syntax (YAML/TOML/JSON).
541
+
542
+ Args:
543
+ files: Files to check
544
+ start_time: Check start time
545
+
546
+ Returns:
547
+ QAResult with syntax validation results
548
+ """
549
+ if not self.settings or not self.settings.parser_type:
550
+ raise ValueError("Parser type not configured")
551
+
552
+ parser_type = self.settings.parser_type
553
+ issues_found = 0
554
+ error_details: list[str] = []
555
+
556
+ for file_path in files:
557
+ try:
558
+ content = file_path.read_text()
559
+
560
+ if parser_type == "yaml":
561
+ yaml.safe_load(content)
562
+ elif parser_type == "toml":
563
+ tomli.loads(content)
564
+ elif parser_type == "json":
565
+ import json
566
+
567
+ json.loads(content)
568
+
569
+ except Exception as e:
570
+ issues_found += 1
571
+ error_details.append(f"{file_path}: {e}")
572
+ # Add structured logging for failures
573
+ logger.warning(
574
+ "Failed to validate file syntax",
575
+ extra={
576
+ "file_path": str(file_path),
577
+ "check_type": self.settings.check_type.value
578
+ if self.settings
579
+ else "unknown",
580
+ "parser_type": parser_type,
581
+ "error": str(e),
582
+ "error_type": type(e).__name__,
583
+ },
584
+ )
585
+
586
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
587
+
588
+ if issues_found == 0:
589
+ return QAResult(
590
+ check_id=MODULE_ID,
591
+ check_name=self.adapter_name,
592
+ check_type=QACheckType.FORMAT,
593
+ status=QAResultStatus.SUCCESS,
594
+ message=f"All {parser_type.upper()} files are valid",
595
+ files_checked=files,
596
+ execution_time_ms=elapsed_ms,
597
+ )
598
+ return QAResult(
599
+ check_id=MODULE_ID,
600
+ check_name=self.adapter_name,
601
+ check_type=QACheckType.FORMAT,
602
+ status=QAResultStatus.FAILURE,
603
+ message=f"Found {issues_found} {parser_type.upper()} syntax errors",
604
+ details="\n".join(error_details),
605
+ files_checked=files,
606
+ issues_found=issues_found,
607
+ execution_time_ms=elapsed_ms,
608
+ )
609
+
610
+ async def _check_size_limit(
611
+ self,
612
+ files: list[Path],
613
+ start_time: float,
614
+ ) -> QAResult:
615
+ """Check files against size limits.
616
+
617
+ Args:
618
+ files: Files to check
619
+ start_time: Check start time
620
+
621
+ Returns:
622
+ QAResult with size check results
623
+ """
624
+ if not self.settings or self.settings.max_size_bytes is None:
625
+ raise ValueError("Max size not configured")
626
+
627
+ max_size = self.settings.max_size_bytes
628
+ issues_found = 0
629
+ large_files: list[str] = []
630
+
631
+ for file_path in files:
632
+ try:
633
+ file_size = file_path.stat().st_size
634
+ if file_size > max_size:
635
+ issues_found += 1
636
+ size_mb = file_size / (1024 * 1024)
637
+ max_mb = max_size / (1024 * 1024)
638
+ large_files.append(
639
+ f"{file_path} ({size_mb:.2f} MB > {max_mb:.2f} MB)"
640
+ )
641
+ except Exception as e:
642
+ # Add structured logging for failures
643
+ logger.warning(
644
+ "Failed to check file size",
645
+ extra={
646
+ "file_path": str(file_path),
647
+ "check_type": self.settings.check_type.value
648
+ if self.settings
649
+ else "unknown",
650
+ "max_size_bytes": max_size,
651
+ "error": str(e),
652
+ "error_type": type(e).__name__,
653
+ },
654
+ )
655
+ continue
656
+
657
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
658
+
659
+ if issues_found == 0:
660
+ return QAResult(
661
+ check_id=MODULE_ID,
662
+ check_name=self.adapter_name,
663
+ check_type=QACheckType.FORMAT,
664
+ status=QAResultStatus.SUCCESS,
665
+ message="All files within size limits",
666
+ files_checked=files,
667
+ execution_time_ms=elapsed_ms,
668
+ )
669
+ return QAResult(
670
+ check_id=MODULE_ID,
671
+ check_name=self.adapter_name,
672
+ check_type=QACheckType.FORMAT,
673
+ status=QAResultStatus.FAILURE,
674
+ message=f"Found {issues_found} files exceeding size limit",
675
+ details="\n".join(large_files),
676
+ files_checked=files,
677
+ issues_found=issues_found,
678
+ execution_time_ms=elapsed_ms,
679
+ )
680
+
681
+ async def _check_dependency_lock(
682
+ self,
683
+ files: list[Path],
684
+ start_time: float,
685
+ ) -> QAResult:
686
+ """Check dependency lock file integrity.
687
+
688
+ Args:
689
+ files: Files to check (typically uv.lock)
690
+ start_time: Check start time
691
+
692
+ Returns:
693
+ QAResult with dependency lock check results
694
+ """
695
+ if not self.settings or not self.settings.lock_command:
696
+ raise ValueError("Lock command not configured")
697
+
698
+ try:
699
+ result = subprocess.run(
700
+ self.settings.lock_command,
701
+ capture_output=True,
702
+ text=True,
703
+ timeout=self.settings.timeout_seconds,
704
+ check=False,
705
+ )
706
+
707
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
708
+
709
+ if result.returncode == 0:
710
+ return QAResult(
711
+ check_id=MODULE_ID,
712
+ check_name=self.adapter_name,
713
+ check_type=QACheckType.FORMAT,
714
+ status=QAResultStatus.SUCCESS,
715
+ message="Dependency lock file is up to date",
716
+ files_checked=files,
717
+ execution_time_ms=elapsed_ms,
718
+ )
719
+ else:
720
+ return QAResult(
721
+ check_id=MODULE_ID,
722
+ check_name=self.adapter_name,
723
+ check_type=QACheckType.FORMAT,
724
+ status=QAResultStatus.FAILURE,
725
+ message="Dependency lock file is out of sync",
726
+ details=result.stderr or result.stdout,
727
+ files_checked=files,
728
+ issues_found=1,
729
+ execution_time_ms=elapsed_ms,
730
+ )
731
+
732
+ except subprocess.TimeoutExpired:
733
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
734
+ return QAResult(
735
+ check_id=MODULE_ID,
736
+ check_name=self.adapter_name,
737
+ check_type=QACheckType.FORMAT,
738
+ status=QAResultStatus.ERROR,
739
+ message="Dependency lock check timed out",
740
+ files_checked=files,
741
+ execution_time_ms=elapsed_ms,
742
+ )
743
+ except Exception as e:
744
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
745
+ return QAResult(
746
+ check_id=MODULE_ID,
747
+ check_name=self.adapter_name,
748
+ check_type=QACheckType.FORMAT,
749
+ status=QAResultStatus.ERROR,
750
+ message=f"Dependency lock check failed: {e}",
751
+ files_checked=files,
752
+ execution_time_ms=elapsed_ms,
753
+ )
754
+
755
+ def _create_skipped_result(self, reason: str, start_time: float) -> QAResult:
756
+ """Create a skipped result.
757
+
758
+ Args:
759
+ reason: Reason for skipping
760
+ start_time: Check start time
761
+
762
+ Returns:
763
+ QAResult with skipped status
764
+ """
765
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
766
+ return QAResult(
767
+ check_id=MODULE_ID,
768
+ check_name=self.adapter_name,
769
+ check_type=QACheckType.FORMAT,
770
+ status=QAResultStatus.SKIPPED,
771
+ message=reason,
772
+ execution_time_ms=elapsed_ms,
773
+ )
774
+
775
+ @staticmethod
776
+ def _get_check_type() -> QACheckType:
777
+ """Determine QA check type based on utility check type.
778
+
779
+ Utility checks map to FORMAT type since they can format/fix files.
780
+
781
+ Returns:
782
+ QACheckType.FORMAT for all utility checks
783
+ """
784
+ return QACheckType.FORMAT
785
+
786
+ def _get_syntax_validation_patterns(self) -> list[str]:
787
+ """Get file patterns for syntax validation based on parser type."""
788
+ if not self.settings:
789
+ return ["**/*.yaml", "**/*.toml", "**/*.json"]
790
+
791
+ parser_patterns = {
792
+ "yaml": ["**/*.yaml", "**/*.yml"],
793
+ "toml": ["**/*.toml"],
794
+ "json": ["**/*.json"],
795
+ }
796
+ return parser_patterns.get(
797
+ self.settings.parser_type,
798
+ ["**/*.yaml", "**/*.toml", "**/*.json"],
799
+ )
800
+
801
+ def _get_config_for_check_type(
802
+ self, check_type: UtilityCheckType
803
+ ) -> tuple[list[str], str, bool]:
804
+ """Get configuration tuple for a specific check type.
805
+
806
+ Args:
807
+ check_type: The utility check type
808
+
809
+ Returns:
810
+ Tuple of (file_patterns, stage, is_formatter)
811
+ """
812
+ if check_type == UtilityCheckType.TEXT_PATTERN:
813
+ return (
814
+ ["**/*.py", "**/*.yaml", "**/*.toml", "**/*.json", "**/*.md"],
815
+ "fast",
816
+ self.settings.auto_fix if self.settings else False,
817
+ )
818
+ if check_type == UtilityCheckType.EOF_NEWLINE:
819
+ return (["**/*"], "fast", True)
820
+ if check_type == UtilityCheckType.SYNTAX_VALIDATION:
821
+ return (self._get_syntax_validation_patterns(), "comprehensive", False)
822
+ if check_type == UtilityCheckType.SIZE_CHECK:
823
+ return (["**/*"], "fast", False)
824
+ if check_type == UtilityCheckType.DEPENDENCY_LOCK:
825
+ return (["uv.lock", "requirements.lock"], "fast", False)
826
+
827
+ # Fallback defaults
828
+ return (["**/*.py"], "fast", False)
829
+
830
+ def get_default_config(self) -> QACheckConfig:
831
+ """Get default configuration for utility checks.
832
+
833
+ Configuration varies based on the check_type setting:
834
+ - TEXT_PATTERN: fast stage, common text files
835
+ - EOF_NEWLINE: fast stage, all files, is_formatter=True
836
+ - SYNTAX_VALIDATION: comprehensive stage, parser-specific patterns
837
+ - SIZE_CHECK: fast stage, all files
838
+ - DEPENDENCY_LOCK: fast stage, lock files only
839
+
840
+ Returns:
841
+ QACheckConfig with check-type-specific defaults
842
+ """
843
+ from crackerjack.models.qa_config import QACheckConfig
844
+
845
+ # Get configuration based on check type
846
+ if self.settings and self.settings.check_type:
847
+ file_patterns, stage, is_formatter = self._get_config_for_check_type(
848
+ self.settings.check_type
849
+ )
850
+ else:
851
+ # No settings, use generic defaults
852
+ file_patterns = ["**/*.py", "**/*.yaml", "**/*.toml", "**/*.json"]
853
+ stage = "fast"
854
+ is_formatter = False
855
+
856
+ return QACheckConfig(
857
+ check_id=MODULE_ID,
858
+ check_name=self.adapter_name,
859
+ check_type=self._get_check_type(),
860
+ enabled=True,
861
+ file_patterns=file_patterns,
862
+ exclude_patterns=[
863
+ "**/.*",
864
+ "**/.git/**",
865
+ "**/.venv/**",
866
+ "**/node_modules/**",
867
+ "**/__pycache__/**",
868
+ ],
869
+ timeout_seconds=60, # Utility checks should be fast
870
+ parallel_safe=True,
871
+ is_formatter=is_formatter,
872
+ stage=stage,
873
+ settings={
874
+ "check_type": self.settings.check_type.value
875
+ if self.settings
876
+ else "text_pattern",
877
+ "auto_fix": self.settings.auto_fix if self.settings else False,
878
+ },
879
+ )
880
+
881
+
882
+ # ACB Registration (REQUIRED at module level)
883
+ with suppress(Exception):
884
+ depends.set(UtilityCheckAdapter)