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,706 @@
1
+ """Base adapter for external tool integration in ACB QA framework.
2
+
3
+ Provides shared functionality for adapters that wrap external CLI tools
4
+ like Ruff, Zuban, Bandit, Gitleaks, etc.
5
+
6
+ ACB Patterns:
7
+ - Extends QAAdapterBase for ACB compliance
8
+ - Async subprocess execution with timeout/cancellation
9
+ - Standardized output parsing and error handling
10
+ - Tool availability validation
11
+ - Version detection and caching
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import asyncio
17
+ import shutil
18
+ import typing as t
19
+ from abc import abstractmethod
20
+ from dataclasses import dataclass, field
21
+ from pathlib import Path
22
+
23
+ from crackerjack.adapters._qa_adapter_base import QAAdapterBase, QABaseSettings
24
+ from crackerjack.models.qa_results import QACheckType, QAResult, QAResultStatus
25
+
26
+ if t.TYPE_CHECKING:
27
+ from crackerjack.models.qa_config import QACheckConfig
28
+
29
+
30
+ @dataclass
31
+ class ToolIssue:
32
+ """Represents a single issue found by a tool.
33
+
34
+ Standardized format for all tool outputs.
35
+ """
36
+
37
+ file_path: Path
38
+ line_number: int | None = None
39
+ column_number: int | None = None
40
+ message: str = ""
41
+ code: str | None = None
42
+ severity: str = "error"
43
+ suggestion: str | None = None
44
+
45
+ def to_dict(self) -> dict[str, t.Any]:
46
+ """Convert to dictionary for serialization."""
47
+ return {
48
+ "file_path": str(self.file_path),
49
+ "line_number": self.line_number,
50
+ "column_number": self.column_number,
51
+ "message": self.message,
52
+ "code": self.code,
53
+ "severity": self.severity,
54
+ "suggestion": self.suggestion,
55
+ }
56
+
57
+
58
+ @dataclass
59
+ class ToolExecutionResult:
60
+ """Result of tool execution with standardized format."""
61
+
62
+ success: bool
63
+ issues: list[ToolIssue] = field(default_factory=list)
64
+ error_message: str | None = None
65
+ raw_output: str = ""
66
+ raw_stderr: str = ""
67
+ execution_time_ms: float = 0.0
68
+ exit_code: int = 0
69
+ tool_version: str | None = None
70
+ files_processed: list[Path] = field(default_factory=list)
71
+ files_modified: list[Path] = field(default_factory=list)
72
+
73
+ @property
74
+ def has_errors(self) -> bool:
75
+ """Check if result contains error-level issues."""
76
+ return any(issue.severity == "error" for issue in self.issues)
77
+
78
+ @property
79
+ def error_count(self) -> int:
80
+ """Count of error-level issues."""
81
+ return len([i for i in self.issues if i.severity == "error"])
82
+
83
+ @property
84
+ def warning_count(self) -> int:
85
+ """Count of warning-level issues."""
86
+ return len([i for i in self.issues if i.severity == "warning"])
87
+
88
+ def to_dict(self) -> dict[str, t.Any]:
89
+ """Convert to dictionary for serialization."""
90
+ return {
91
+ "success": self.success,
92
+ "issues": [issue.to_dict() for issue in self.issues],
93
+ "error_message": self.error_message,
94
+ "raw_output": self.raw_output[:500], # Truncate for logs
95
+ "execution_time_ms": self.execution_time_ms,
96
+ "exit_code": self.exit_code,
97
+ "tool_version": self.tool_version,
98
+ "error_count": self.error_count,
99
+ "warning_count": self.warning_count,
100
+ "files_processed": [str(f) for f in self.files_processed],
101
+ "files_modified": [str(f) for f in self.files_modified],
102
+ }
103
+
104
+
105
+ class ToolAdapterSettings(QABaseSettings):
106
+ """Settings for tool-based adapters.
107
+
108
+ Extends QABaseSettings with tool-specific configuration.
109
+ """
110
+
111
+ tool_name: str = ""
112
+ tool_args: list[str] = field(default_factory=list)
113
+ use_json_output: bool = False
114
+ fix_enabled: bool = False
115
+ include_warnings: bool = True
116
+
117
+
118
+ from acb.adapters import AdapterMetadata
119
+
120
+
121
+ class BaseToolAdapter(QAAdapterBase):
122
+ """Base adapter for external CLI tools in QA framework.
123
+
124
+ Provides shared implementation for tools like Ruff, Zuban, Bandit, etc.
125
+ Subclasses implement tool-specific command building and output parsing.
126
+
127
+ Example:
128
+ ```python
129
+ import uuid
130
+ from contextlib import suppress
131
+ from acb.depends import depends
132
+
133
+ MODULE_ID = uuid.uuid7()
134
+ MODULE_STATUS = "stable"
135
+
136
+
137
+ class RuffAdapter(BaseToolAdapter):
138
+ settings: RuffSettings | None = None
139
+
140
+ @property
141
+ def module_id(self) -> uuid.UUID:
142
+ return MODULE_ID
143
+
144
+ @property
145
+ def tool_name(self) -> str:
146
+ return "ruff"
147
+
148
+ def build_command(self, files: list[Path]) -> list[str]:
149
+ cmd = [self.tool_name, "check"]
150
+ if self.settings and self.settings.fix_enabled:
151
+ cmd.append("--fix")
152
+ cmd.extend([str(f) for f in files])
153
+ return cmd
154
+
155
+ async def parse_output(
156
+ self, result: ToolExecutionResult
157
+ ) -> list[ToolIssue]:
158
+ # Parse ruff JSON output
159
+ if not result.raw_output:
160
+ return []
161
+ data = json.loads(result.raw_output)
162
+ return [self._parse_ruff_issue(issue) for issue in data]
163
+
164
+
165
+ with suppress(Exception):
166
+ depends.set(RuffAdapter)
167
+ ```
168
+ """
169
+
170
+ settings: ToolAdapterSettings | None = None
171
+ metadata: AdapterMetadata | None = None
172
+
173
+ def __init__(self, settings: ToolAdapterSettings | None = None) -> None:
174
+ """Initialize tool adapter.
175
+
176
+ Args:
177
+ settings: Optional settings override
178
+ """
179
+ super().__init__()
180
+ if settings:
181
+ self.settings = settings
182
+ self._tool_version: str | None = None
183
+ self._tool_available: bool | None = None
184
+
185
+ async def init(self) -> None:
186
+ """Initialize adapter with tool validation."""
187
+ if not self.settings:
188
+ self.settings = ToolAdapterSettings(tool_name=self.tool_name)
189
+
190
+ # Validate tool is available
191
+ available = await self.validate_tool_available()
192
+ if not available:
193
+ raise RuntimeError(
194
+ f"Tool '{self.tool_name}' not found in PATH. "
195
+ f"Please install it before using this adapter."
196
+ )
197
+
198
+ # Cache tool version
199
+ self._tool_version = await self.get_tool_version()
200
+
201
+ await super().init()
202
+
203
+ @property
204
+ @abstractmethod
205
+ def tool_name(self) -> str:
206
+ """Get the CLI tool name (e.g., 'ruff', 'bandit', 'zuban').
207
+
208
+ Must be implemented by subclasses.
209
+ """
210
+ ...
211
+
212
+ @abstractmethod
213
+ def build_command(
214
+ self,
215
+ files: list[Path],
216
+ config: QACheckConfig | None = None,
217
+ ) -> list[str]:
218
+ """Build the command to execute for this tool.
219
+
220
+ Args:
221
+ files: Files to check
222
+ config: Optional configuration override
223
+
224
+ Returns:
225
+ Command as list of strings (for subprocess)
226
+
227
+ Must be implemented by subclasses.
228
+ """
229
+ ...
230
+
231
+ @abstractmethod
232
+ async def parse_output(
233
+ self,
234
+ result: ToolExecutionResult,
235
+ ) -> list[ToolIssue]:
236
+ """Parse tool output into standardized issues.
237
+
238
+ Args:
239
+ result: Raw execution result from tool
240
+
241
+ Returns:
242
+ List of parsed issues
243
+
244
+ Must be implemented by subclasses.
245
+ """
246
+ ...
247
+
248
+ async def check(
249
+ self,
250
+ files: list[Path] | None = None,
251
+ config: QACheckConfig | None = None,
252
+ ) -> QAResult:
253
+ """Execute tool check on files.
254
+
255
+ Args:
256
+ files: List of files to check (None = check all matching patterns)
257
+ config: Optional configuration override
258
+
259
+ Returns:
260
+ QAResult with check execution results
261
+ """
262
+ if not self._initialized:
263
+ await self.init()
264
+
265
+ start_time = asyncio.get_event_loop().time()
266
+
267
+ # Determine files to check
268
+ target_files = await self._get_target_files(files, config)
269
+
270
+ if not target_files:
271
+ return self._create_result(
272
+ status=QAResultStatus.SKIPPED,
273
+ message="No files to check",
274
+ start_time=start_time,
275
+ )
276
+
277
+ # Build and execute command
278
+ command = self.build_command(target_files, config)
279
+
280
+ try:
281
+ exec_result = await self._execute_tool(command, target_files, start_time)
282
+ except TimeoutError:
283
+ # At this point, settings should be initialized by the init() method
284
+ assert self.settings is not None, "Settings should be initialized"
285
+ timeout_msg = (
286
+ f"Tool execution timed out after {self.settings.timeout_seconds}s"
287
+ )
288
+ return self._create_result(
289
+ status=QAResultStatus.ERROR,
290
+ message=timeout_msg,
291
+ details=timeout_msg,
292
+ start_time=start_time,
293
+ )
294
+ except Exception as e:
295
+ error_msg = f"Tool execution failed: {e}"
296
+ # Include full traceback in details for better debugging
297
+ import traceback
298
+
299
+ error_details = f"{error_msg}\n\nFull traceback:\n{traceback.format_exc()}"
300
+ return self._create_result(
301
+ status=QAResultStatus.ERROR,
302
+ message=error_msg,
303
+ details=error_details,
304
+ start_time=start_time,
305
+ )
306
+
307
+ # Parse output into issues
308
+ issues = await self.parse_output(exec_result)
309
+
310
+ # Convert to QAResult
311
+ return self._convert_to_qa_result(
312
+ exec_result=exec_result,
313
+ issues=issues,
314
+ target_files=target_files,
315
+ start_time=start_time,
316
+ )
317
+
318
+ async def _get_target_files(
319
+ self, files: list[Path] | None, config: QACheckConfig | None
320
+ ) -> list[Path]:
321
+ """Collect target files based on provided list or config patterns.
322
+
323
+ If explicit files are provided, return them. Otherwise, scan the project
324
+ root for files matching include patterns and not matching exclude patterns.
325
+ """
326
+ if files:
327
+ return files
328
+
329
+ # Fallback to default configuration if none provided
330
+ cfg = config or self.get_default_config()
331
+
332
+ root = Path.cwd() / "crackerjack"
333
+ if not root.exists():
334
+ root = Path.cwd()
335
+
336
+ # Standard directories to always exclude (even if not in config)
337
+ # These are directories that should never be scanned
338
+ standard_excludes = {
339
+ ".venv",
340
+ "venv",
341
+ ".env",
342
+ "env",
343
+ ".tox",
344
+ ".nox",
345
+ "__pycache__",
346
+ ".pytest_cache",
347
+ ".mypy_cache",
348
+ ".ruff_cache",
349
+ ".git",
350
+ ".hg",
351
+ ".svn",
352
+ "node_modules",
353
+ ".uv",
354
+ "dist",
355
+ "build",
356
+ "*.egg-info",
357
+ }
358
+
359
+ # Conditionally exclude tests directory only for comprehensive hooks
360
+ # Fast hooks (formatters, linters) should check test files
361
+ # Comprehensive hooks (type checking, security) can skip tests
362
+ if (
363
+ cfg
364
+ and hasattr(cfg, "is_comprehensive_stage")
365
+ and cfg.is_comprehensive_stage
366
+ ):
367
+ standard_excludes.add("tests")
368
+
369
+ candidates = [p for p in root.rglob("*.py")]
370
+ result: list[Path] = []
371
+ for path in candidates:
372
+ # Skip if path contains any standard exclude directory
373
+ if any(excluded in path.parts for excluded in standard_excludes):
374
+ continue
375
+
376
+ # Include if matches include patterns
377
+ include = any(path.match(pattern) for pattern in cfg.file_patterns)
378
+ if not include:
379
+ continue
380
+ # Exclude if matches any exclude pattern
381
+ if any(path.match(pattern) for pattern in cfg.exclude_patterns):
382
+ continue
383
+ result.append(path)
384
+
385
+ return result
386
+
387
+ async def _execute_tool(
388
+ self,
389
+ command: list[str],
390
+ target_files: list[Path],
391
+ start_time: float,
392
+ ) -> ToolExecutionResult:
393
+ """Execute tool command asynchronously.
394
+
395
+ Args:
396
+ command: Command to execute
397
+ target_files: Files being processed
398
+ start_time: Start time for duration calculation
399
+
400
+ Returns:
401
+ ToolExecutionResult with execution details
402
+
403
+ Raises:
404
+ asyncio.TimeoutError: If execution exceeds timeout
405
+ Exception: For other execution failures
406
+ """
407
+ if not self.settings:
408
+ raise RuntimeError("Settings not initialized")
409
+
410
+ try:
411
+ process = await asyncio.create_subprocess_exec(
412
+ *command,
413
+ stdout=asyncio.subprocess.PIPE,
414
+ stderr=asyncio.subprocess.PIPE,
415
+ cwd=Path.cwd(),
416
+ )
417
+
418
+ # At this point, settings should be initialized by the init() method
419
+ assert self.settings is not None, "Settings should be initialized"
420
+ stdout_bytes, stderr_bytes = await asyncio.wait_for(
421
+ process.communicate(),
422
+ timeout=self.settings.timeout_seconds,
423
+ )
424
+
425
+ stdout = stdout_bytes.decode("utf-8", errors="replace")
426
+ stderr = stderr_bytes.decode("utf-8", errors="replace")
427
+
428
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
429
+
430
+ # Non-zero exit code doesn't always mean failure
431
+ # Some tools return 1 when they find issues
432
+ success = process.returncode == 0 or (
433
+ process.returncode == 1 and bool(stdout)
434
+ )
435
+
436
+ return ToolExecutionResult(
437
+ success=success,
438
+ raw_output=stdout,
439
+ raw_stderr=stderr,
440
+ exit_code=process.returncode or 0,
441
+ execution_time_ms=elapsed_ms,
442
+ tool_version=self._tool_version,
443
+ files_processed=target_files,
444
+ )
445
+
446
+ except TimeoutError:
447
+ # Kill the process if it times out
448
+ if process:
449
+ from contextlib import suppress
450
+
451
+ with suppress(Exception):
452
+ process.kill()
453
+ await process.wait()
454
+ raise
455
+
456
+ async def validate_tool_available(self) -> bool:
457
+ """Check if tool is available in PATH.
458
+
459
+ Returns:
460
+ True if tool is available, False otherwise
461
+ """
462
+ if self._tool_available is not None:
463
+ return self._tool_available
464
+
465
+ tool_path = shutil.which(self.tool_name)
466
+ self._tool_available = tool_path is not None
467
+ return self._tool_available
468
+
469
+ async def get_tool_version(self) -> str | None:
470
+ """Get tool version asynchronously.
471
+
472
+ Returns:
473
+ Version string or None if unavailable
474
+ """
475
+ if self._tool_version is not None:
476
+ return self._tool_version
477
+
478
+ try:
479
+ process = await asyncio.create_subprocess_exec(
480
+ self.tool_name,
481
+ "--version",
482
+ stdout=asyncio.subprocess.PIPE,
483
+ stderr=asyncio.subprocess.PIPE,
484
+ )
485
+
486
+ stdout_bytes, _ = await asyncio.wait_for(
487
+ process.communicate(),
488
+ timeout=10,
489
+ )
490
+
491
+ version_output = stdout_bytes.decode("utf-8", errors="replace")
492
+ # Return first line of version output
493
+ return version_output.strip().split("\n")[0]
494
+
495
+ except (TimeoutError, FileNotFoundError, Exception):
496
+ return None
497
+
498
+ async def health_check(self) -> dict[str, t.Any]:
499
+ """Check adapter and tool health.
500
+
501
+ Returns:
502
+ Dictionary with health status and metadata
503
+ """
504
+ base_health = await super().health_check()
505
+
506
+ tool_available = await self.validate_tool_available()
507
+ tool_version = await self.get_tool_version() if tool_available else None
508
+
509
+ return base_health | {
510
+ "tool_name": self.tool_name,
511
+ "tool_available": tool_available,
512
+ "tool_version": tool_version,
513
+ "metadata": self.metadata.dict() if self.metadata else None,
514
+ }
515
+
516
+ def _count_issues_by_severity(self, issues: list[ToolIssue]) -> tuple[int, int]:
517
+ """Count errors and warnings in issues list.
518
+
519
+ Args:
520
+ issues: List of tool issues
521
+
522
+ Returns:
523
+ Tuple of (error_count, warning_count)
524
+ """
525
+ error_count = sum(1 for i in issues if i.severity == "error")
526
+ warning_count = sum(1 for i in issues if i.severity == "warning")
527
+ return error_count, warning_count
528
+
529
+ def _determine_qa_status_and_message(
530
+ self, exec_result: ToolExecutionResult, issues: list[ToolIssue]
531
+ ) -> tuple[QAResultStatus, str]:
532
+ """Determine QA status and message based on execution result and issues.
533
+
534
+ Args:
535
+ exec_result: Tool execution result
536
+ issues: Parsed issues
537
+
538
+ Returns:
539
+ Tuple of (status, message)
540
+ """
541
+ if exec_result.error_message:
542
+ return QAResultStatus.ERROR, exec_result.error_message
543
+
544
+ if not exec_result.success and exec_result.exit_code != 1:
545
+ return (
546
+ QAResultStatus.ERROR,
547
+ f"Tool exited with code {exec_result.exit_code}",
548
+ )
549
+
550
+ if not issues:
551
+ return QAResultStatus.SUCCESS, "No issues found"
552
+
553
+ error_count, warning_count = self._count_issues_by_severity(issues)
554
+
555
+ if error_count > 0:
556
+ message = f"Found {error_count} errors"
557
+ if warning_count > 0:
558
+ message += f" and {warning_count} warnings"
559
+ return QAResultStatus.FAILURE, message
560
+
561
+ return QAResultStatus.WARNING, f"Found {warning_count} warnings"
562
+
563
+ def _build_details_from_issues(self, issues: list[ToolIssue]) -> str:
564
+ """Build details string from list of issues.
565
+
566
+ Args:
567
+ issues: List of tool issues
568
+
569
+ Returns:
570
+ Formatted details string
571
+ """
572
+ details_lines = []
573
+ for issue in issues[:10]: # Limit to first 10 for readability
574
+ loc = str(issue.file_path)
575
+ if issue.line_number:
576
+ loc += f":{issue.line_number}"
577
+ if issue.column_number:
578
+ loc += f":{issue.column_number}"
579
+ details_lines.append(f"{loc}: {issue.message}")
580
+
581
+ if len(issues) > 10:
582
+ details_lines.append(f"... and {len(issues) - 10} more issues")
583
+
584
+ return "\n".join(details_lines)
585
+
586
+ def _convert_to_qa_result(
587
+ self,
588
+ exec_result: ToolExecutionResult,
589
+ issues: list[ToolIssue],
590
+ target_files: list[Path],
591
+ start_time: float,
592
+ ) -> QAResult:
593
+ """Convert tool execution result to QAResult.
594
+
595
+ Args:
596
+ exec_result: Raw execution result
597
+ issues: Parsed issues
598
+ target_files: Files that were checked
599
+ start_time: Start time for duration
600
+
601
+ Returns:
602
+ QAResult
603
+ """
604
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
605
+ status, message = self._determine_qa_status_and_message(exec_result, issues)
606
+ details = self._build_details_from_issues(issues)
607
+
608
+ return QAResult(
609
+ check_id=self.module_id,
610
+ check_name=self.adapter_name,
611
+ check_type=self._get_check_type(),
612
+ status=status,
613
+ message=message,
614
+ details=details,
615
+ files_checked=target_files,
616
+ files_modified=exec_result.files_modified,
617
+ issues_found=len(issues),
618
+ issues_fixed=len(exec_result.files_modified),
619
+ execution_time_ms=elapsed_ms,
620
+ metadata={
621
+ "tool_version": exec_result.tool_version,
622
+ "exit_code": exec_result.exit_code,
623
+ "error_count": exec_result.error_count,
624
+ "warning_count": exec_result.warning_count,
625
+ },
626
+ )
627
+
628
+ def _create_result(
629
+ self,
630
+ status: QAResultStatus,
631
+ message: str,
632
+ start_time: float,
633
+ files: list[Path] | None = None,
634
+ details: str | None = None,
635
+ ) -> QAResult:
636
+ """Create a QAResult with standard fields.
637
+
638
+ Args:
639
+ status: Result status
640
+ message: Result message
641
+ start_time: Start time for duration
642
+ files: Optional files list
643
+ details: Optional detailed error output
644
+
645
+ Returns:
646
+ QAResult
647
+ """
648
+ elapsed_ms = (asyncio.get_event_loop().time() - start_time) * 1000
649
+
650
+ return QAResult(
651
+ check_id=self.module_id,
652
+ check_name=self.adapter_name,
653
+ check_type=self._get_check_type(),
654
+ status=status,
655
+ message=message,
656
+ details=details or "",
657
+ files_checked=files or [],
658
+ execution_time_ms=elapsed_ms,
659
+ metadata={"tool_version": self._tool_version},
660
+ )
661
+
662
+ def _get_check_type(self) -> QACheckType:
663
+ """Determine QACheckType based on tool name.
664
+
665
+ Subclasses can override for more specific typing.
666
+
667
+ Returns:
668
+ QACheckType
669
+ """
670
+ # Default mapping based on tool name patterns
671
+ tool_lower = self.tool_name.lower()
672
+
673
+ if "format" in tool_lower or "fmt" in tool_lower:
674
+ return QACheckType.FORMAT
675
+ if any(x in tool_lower for x in ("type", "pyright", "mypy", "zuban")):
676
+ return QACheckType.TYPE
677
+ if any(x in tool_lower for x in ("bandit", "safety", "gitleaks", "semgrep")):
678
+ return QACheckType.SECURITY
679
+ if any(x in tool_lower for x in ("test", "pytest", "unittest")):
680
+ return QACheckType.TEST
681
+ if any(x in tool_lower for x in ("refactor", "refurb", "complex")):
682
+ return QACheckType.REFACTOR
683
+ return QACheckType.LINT
684
+
685
+ def get_default_config(self) -> QACheckConfig:
686
+ """Get default configuration for tool adapter.
687
+
688
+ Returns:
689
+ QACheckConfig with sensible defaults
690
+ """
691
+ from crackerjack.models.qa_config import QACheckConfig
692
+
693
+ return QACheckConfig(
694
+ check_id=self.module_id,
695
+ check_name=self.adapter_name,
696
+ check_type=self._get_check_type(),
697
+ enabled=True,
698
+ file_patterns=["**/*.py"],
699
+ timeout_seconds=60,
700
+ parallel_safe=True,
701
+ stage="fast",
702
+ settings={
703
+ "tool_name": self.tool_name,
704
+ "fix_enabled": False,
705
+ },
706
+ )