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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (533) hide show
  1. crackerjack/README.md +19 -0
  2. crackerjack/__init__.py +96 -2
  3. crackerjack/__main__.py +637 -138
  4. crackerjack/adapters/README.md +18 -0
  5. crackerjack/adapters/__init__.py +39 -0
  6. crackerjack/adapters/_output_paths.py +167 -0
  7. crackerjack/adapters/_qa_adapter_base.py +309 -0
  8. crackerjack/adapters/_tool_adapter_base.py +706 -0
  9. crackerjack/adapters/ai/README.md +65 -0
  10. crackerjack/adapters/ai/__init__.py +5 -0
  11. crackerjack/adapters/ai/claude.py +853 -0
  12. crackerjack/adapters/complexity/README.md +53 -0
  13. crackerjack/adapters/complexity/__init__.py +10 -0
  14. crackerjack/adapters/complexity/complexipy.py +641 -0
  15. crackerjack/adapters/dependency/__init__.py +22 -0
  16. crackerjack/adapters/dependency/pip_audit.py +418 -0
  17. crackerjack/adapters/format/README.md +72 -0
  18. crackerjack/adapters/format/__init__.py +11 -0
  19. crackerjack/adapters/format/mdformat.py +313 -0
  20. crackerjack/adapters/format/ruff.py +516 -0
  21. crackerjack/adapters/lint/README.md +47 -0
  22. crackerjack/adapters/lint/__init__.py +11 -0
  23. crackerjack/adapters/lint/codespell.py +273 -0
  24. crackerjack/adapters/lsp/README.md +49 -0
  25. crackerjack/adapters/lsp/__init__.py +27 -0
  26. crackerjack/adapters/lsp/_base.py +194 -0
  27. crackerjack/adapters/lsp/_client.py +358 -0
  28. crackerjack/adapters/lsp/_manager.py +193 -0
  29. crackerjack/adapters/lsp/skylos.py +283 -0
  30. crackerjack/adapters/lsp/zuban.py +557 -0
  31. crackerjack/adapters/refactor/README.md +59 -0
  32. crackerjack/adapters/refactor/__init__.py +12 -0
  33. crackerjack/adapters/refactor/creosote.py +318 -0
  34. crackerjack/adapters/refactor/refurb.py +406 -0
  35. crackerjack/adapters/refactor/skylos.py +494 -0
  36. crackerjack/adapters/sast/README.md +132 -0
  37. crackerjack/adapters/sast/__init__.py +32 -0
  38. crackerjack/adapters/sast/_base.py +201 -0
  39. crackerjack/adapters/sast/bandit.py +423 -0
  40. crackerjack/adapters/sast/pyscn.py +405 -0
  41. crackerjack/adapters/sast/semgrep.py +241 -0
  42. crackerjack/adapters/security/README.md +111 -0
  43. crackerjack/adapters/security/__init__.py +17 -0
  44. crackerjack/adapters/security/gitleaks.py +339 -0
  45. crackerjack/adapters/type/README.md +52 -0
  46. crackerjack/adapters/type/__init__.py +12 -0
  47. crackerjack/adapters/type/pyrefly.py +402 -0
  48. crackerjack/adapters/type/ty.py +402 -0
  49. crackerjack/adapters/type/zuban.py +522 -0
  50. crackerjack/adapters/utility/README.md +51 -0
  51. crackerjack/adapters/utility/__init__.py +10 -0
  52. crackerjack/adapters/utility/checks.py +884 -0
  53. crackerjack/agents/README.md +264 -0
  54. crackerjack/agents/__init__.py +66 -0
  55. crackerjack/agents/architect_agent.py +238 -0
  56. crackerjack/agents/base.py +167 -0
  57. crackerjack/agents/claude_code_bridge.py +641 -0
  58. crackerjack/agents/coordinator.py +600 -0
  59. crackerjack/agents/documentation_agent.py +520 -0
  60. crackerjack/agents/dry_agent.py +585 -0
  61. crackerjack/agents/enhanced_coordinator.py +279 -0
  62. crackerjack/agents/enhanced_proactive_agent.py +185 -0
  63. crackerjack/agents/error_middleware.py +53 -0
  64. crackerjack/agents/formatting_agent.py +230 -0
  65. crackerjack/agents/helpers/__init__.py +9 -0
  66. crackerjack/agents/helpers/performance/__init__.py +22 -0
  67. crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
  68. crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
  69. crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
  70. crackerjack/agents/helpers/refactoring/__init__.py +22 -0
  71. crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
  72. crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
  73. crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
  74. crackerjack/agents/helpers/test_creation/__init__.py +19 -0
  75. crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
  76. crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
  77. crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
  78. crackerjack/agents/import_optimization_agent.py +1181 -0
  79. crackerjack/agents/performance_agent.py +325 -0
  80. crackerjack/agents/performance_helpers.py +205 -0
  81. crackerjack/agents/proactive_agent.py +55 -0
  82. crackerjack/agents/refactoring_agent.py +511 -0
  83. crackerjack/agents/refactoring_helpers.py +247 -0
  84. crackerjack/agents/security_agent.py +793 -0
  85. crackerjack/agents/semantic_agent.py +479 -0
  86. crackerjack/agents/semantic_helpers.py +356 -0
  87. crackerjack/agents/test_creation_agent.py +570 -0
  88. crackerjack/agents/test_specialist_agent.py +526 -0
  89. crackerjack/agents/tracker.py +110 -0
  90. crackerjack/api.py +647 -0
  91. crackerjack/cli/README.md +394 -0
  92. crackerjack/cli/__init__.py +24 -0
  93. crackerjack/cli/cache_handlers.py +209 -0
  94. crackerjack/cli/cache_handlers_enhanced.py +680 -0
  95. crackerjack/cli/facade.py +162 -0
  96. crackerjack/cli/formatting.py +13 -0
  97. crackerjack/cli/handlers/__init__.py +85 -0
  98. crackerjack/cli/handlers/advanced.py +103 -0
  99. crackerjack/cli/handlers/ai_features.py +62 -0
  100. crackerjack/cli/handlers/analytics.py +479 -0
  101. crackerjack/cli/handlers/changelog.py +271 -0
  102. crackerjack/cli/handlers/config_handlers.py +16 -0
  103. crackerjack/cli/handlers/coverage.py +84 -0
  104. crackerjack/cli/handlers/documentation.py +280 -0
  105. crackerjack/cli/handlers/main_handlers.py +497 -0
  106. crackerjack/cli/handlers/monitoring.py +371 -0
  107. crackerjack/cli/handlers.py +700 -0
  108. crackerjack/cli/interactive.py +488 -0
  109. crackerjack/cli/options.py +1216 -0
  110. crackerjack/cli/semantic_handlers.py +292 -0
  111. crackerjack/cli/utils.py +19 -0
  112. crackerjack/cli/version.py +19 -0
  113. crackerjack/code_cleaner.py +1307 -0
  114. crackerjack/config/README.md +472 -0
  115. crackerjack/config/__init__.py +275 -0
  116. crackerjack/config/global_lock_config.py +207 -0
  117. crackerjack/config/hooks.py +390 -0
  118. crackerjack/config/loader.py +239 -0
  119. crackerjack/config/settings.py +141 -0
  120. crackerjack/config/tool_commands.py +331 -0
  121. crackerjack/core/README.md +393 -0
  122. crackerjack/core/__init__.py +0 -0
  123. crackerjack/core/async_workflow_orchestrator.py +738 -0
  124. crackerjack/core/autofix_coordinator.py +282 -0
  125. crackerjack/core/container.py +105 -0
  126. crackerjack/core/enhanced_container.py +583 -0
  127. crackerjack/core/file_lifecycle.py +472 -0
  128. crackerjack/core/performance.py +244 -0
  129. crackerjack/core/performance_monitor.py +357 -0
  130. crackerjack/core/phase_coordinator.py +1227 -0
  131. crackerjack/core/proactive_workflow.py +267 -0
  132. crackerjack/core/resource_manager.py +425 -0
  133. crackerjack/core/retry.py +275 -0
  134. crackerjack/core/service_watchdog.py +601 -0
  135. crackerjack/core/session_coordinator.py +239 -0
  136. crackerjack/core/timeout_manager.py +563 -0
  137. crackerjack/core/websocket_lifecycle.py +410 -0
  138. crackerjack/core/workflow/__init__.py +21 -0
  139. crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
  140. crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
  141. crackerjack/core/workflow/workflow_issue_parser.py +714 -0
  142. crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
  143. crackerjack/core/workflow/workflow_security_gates.py +400 -0
  144. crackerjack/core/workflow_orchestrator.py +2243 -0
  145. crackerjack/data/README.md +11 -0
  146. crackerjack/data/__init__.py +8 -0
  147. crackerjack/data/models.py +79 -0
  148. crackerjack/data/repository.py +210 -0
  149. crackerjack/decorators/README.md +180 -0
  150. crackerjack/decorators/__init__.py +35 -0
  151. crackerjack/decorators/error_handling.py +649 -0
  152. crackerjack/decorators/error_handling_decorators.py +334 -0
  153. crackerjack/decorators/helpers.py +58 -0
  154. crackerjack/decorators/patterns.py +281 -0
  155. crackerjack/decorators/utils.py +58 -0
  156. crackerjack/docs/INDEX.md +11 -0
  157. crackerjack/docs/README.md +11 -0
  158. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  159. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  160. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  161. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  162. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  163. crackerjack/documentation/README.md +11 -0
  164. crackerjack/documentation/__init__.py +31 -0
  165. crackerjack/documentation/ai_templates.py +756 -0
  166. crackerjack/documentation/dual_output_generator.py +767 -0
  167. crackerjack/documentation/mkdocs_integration.py +518 -0
  168. crackerjack/documentation/reference_generator.py +1065 -0
  169. crackerjack/dynamic_config.py +678 -0
  170. crackerjack/errors.py +378 -0
  171. crackerjack/events/README.md +11 -0
  172. crackerjack/events/__init__.py +16 -0
  173. crackerjack/events/telemetry.py +175 -0
  174. crackerjack/events/workflow_bus.py +346 -0
  175. crackerjack/exceptions/README.md +301 -0
  176. crackerjack/exceptions/__init__.py +5 -0
  177. crackerjack/exceptions/config.py +4 -0
  178. crackerjack/exceptions/tool_execution_error.py +245 -0
  179. crackerjack/executors/README.md +591 -0
  180. crackerjack/executors/__init__.py +13 -0
  181. crackerjack/executors/async_hook_executor.py +938 -0
  182. crackerjack/executors/cached_hook_executor.py +316 -0
  183. crackerjack/executors/hook_executor.py +1295 -0
  184. crackerjack/executors/hook_lock_manager.py +708 -0
  185. crackerjack/executors/individual_hook_executor.py +739 -0
  186. crackerjack/executors/lsp_aware_hook_executor.py +349 -0
  187. crackerjack/executors/progress_hook_executor.py +282 -0
  188. crackerjack/executors/tool_proxy.py +433 -0
  189. crackerjack/hooks/README.md +485 -0
  190. crackerjack/hooks/lsp_hook.py +93 -0
  191. crackerjack/intelligence/README.md +557 -0
  192. crackerjack/intelligence/__init__.py +37 -0
  193. crackerjack/intelligence/adaptive_learning.py +693 -0
  194. crackerjack/intelligence/agent_orchestrator.py +485 -0
  195. crackerjack/intelligence/agent_registry.py +377 -0
  196. crackerjack/intelligence/agent_selector.py +439 -0
  197. crackerjack/intelligence/integration.py +250 -0
  198. crackerjack/interactive.py +719 -0
  199. crackerjack/managers/README.md +369 -0
  200. crackerjack/managers/__init__.py +11 -0
  201. crackerjack/managers/async_hook_manager.py +135 -0
  202. crackerjack/managers/hook_manager.py +585 -0
  203. crackerjack/managers/publish_manager.py +631 -0
  204. crackerjack/managers/test_command_builder.py +391 -0
  205. crackerjack/managers/test_executor.py +474 -0
  206. crackerjack/managers/test_manager.py +1357 -0
  207. crackerjack/managers/test_progress.py +187 -0
  208. crackerjack/mcp/README.md +374 -0
  209. crackerjack/mcp/__init__.py +0 -0
  210. crackerjack/mcp/cache.py +352 -0
  211. crackerjack/mcp/client_runner.py +121 -0
  212. crackerjack/mcp/context.py +802 -0
  213. crackerjack/mcp/dashboard.py +657 -0
  214. crackerjack/mcp/enhanced_progress_monitor.py +493 -0
  215. crackerjack/mcp/file_monitor.py +394 -0
  216. crackerjack/mcp/progress_components.py +607 -0
  217. crackerjack/mcp/progress_monitor.py +1016 -0
  218. crackerjack/mcp/rate_limiter.py +336 -0
  219. crackerjack/mcp/server.py +24 -0
  220. crackerjack/mcp/server_core.py +526 -0
  221. crackerjack/mcp/service_watchdog.py +505 -0
  222. crackerjack/mcp/state.py +407 -0
  223. crackerjack/mcp/task_manager.py +259 -0
  224. crackerjack/mcp/tools/README.md +27 -0
  225. crackerjack/mcp/tools/__init__.py +19 -0
  226. crackerjack/mcp/tools/core_tools.py +469 -0
  227. crackerjack/mcp/tools/error_analyzer.py +283 -0
  228. crackerjack/mcp/tools/execution_tools.py +384 -0
  229. crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
  230. crackerjack/mcp/tools/intelligence_tools.py +264 -0
  231. crackerjack/mcp/tools/monitoring_tools.py +628 -0
  232. crackerjack/mcp/tools/proactive_tools.py +367 -0
  233. crackerjack/mcp/tools/progress_tools.py +222 -0
  234. crackerjack/mcp/tools/semantic_tools.py +584 -0
  235. crackerjack/mcp/tools/utility_tools.py +358 -0
  236. crackerjack/mcp/tools/workflow_executor.py +699 -0
  237. crackerjack/mcp/websocket/README.md +31 -0
  238. crackerjack/mcp/websocket/__init__.py +14 -0
  239. crackerjack/mcp/websocket/app.py +54 -0
  240. crackerjack/mcp/websocket/endpoints.py +492 -0
  241. crackerjack/mcp/websocket/event_bridge.py +188 -0
  242. crackerjack/mcp/websocket/jobs.py +406 -0
  243. crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
  244. crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
  245. crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
  246. crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
  247. crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
  248. crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
  249. crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
  250. crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
  251. crackerjack/mcp/websocket/monitoring/factory.py +109 -0
  252. crackerjack/mcp/websocket/monitoring/filters.py +10 -0
  253. crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
  254. crackerjack/mcp/websocket/monitoring/models.py +90 -0
  255. crackerjack/mcp/websocket/monitoring/utils.py +171 -0
  256. crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
  257. crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
  258. crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
  259. crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
  260. crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
  261. crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
  262. crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
  263. crackerjack/mcp/websocket/server.py +174 -0
  264. crackerjack/mcp/websocket/websocket_handler.py +276 -0
  265. crackerjack/mcp/websocket_server.py +10 -0
  266. crackerjack/models/README.md +308 -0
  267. crackerjack/models/__init__.py +40 -0
  268. crackerjack/models/config.py +730 -0
  269. crackerjack/models/config_adapter.py +265 -0
  270. crackerjack/models/protocols.py +1535 -0
  271. crackerjack/models/pydantic_models.py +320 -0
  272. crackerjack/models/qa_config.py +145 -0
  273. crackerjack/models/qa_results.py +134 -0
  274. crackerjack/models/resource_protocols.py +299 -0
  275. crackerjack/models/results.py +35 -0
  276. crackerjack/models/semantic_models.py +258 -0
  277. crackerjack/models/task.py +173 -0
  278. crackerjack/models/test_models.py +60 -0
  279. crackerjack/monitoring/README.md +11 -0
  280. crackerjack/monitoring/__init__.py +0 -0
  281. crackerjack/monitoring/ai_agent_watchdog.py +405 -0
  282. crackerjack/monitoring/metrics_collector.py +427 -0
  283. crackerjack/monitoring/regression_prevention.py +580 -0
  284. crackerjack/monitoring/websocket_server.py +406 -0
  285. crackerjack/orchestration/README.md +340 -0
  286. crackerjack/orchestration/__init__.py +43 -0
  287. crackerjack/orchestration/advanced_orchestrator.py +894 -0
  288. crackerjack/orchestration/cache/README.md +312 -0
  289. crackerjack/orchestration/cache/__init__.py +37 -0
  290. crackerjack/orchestration/cache/memory_cache.py +338 -0
  291. crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
  292. crackerjack/orchestration/config.py +297 -0
  293. crackerjack/orchestration/coverage_improvement.py +180 -0
  294. crackerjack/orchestration/execution_strategies.py +361 -0
  295. crackerjack/orchestration/hook_orchestrator.py +1398 -0
  296. crackerjack/orchestration/strategies/README.md +401 -0
  297. crackerjack/orchestration/strategies/__init__.py +39 -0
  298. crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
  299. crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
  300. crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
  301. crackerjack/orchestration/test_progress_streamer.py +647 -0
  302. crackerjack/plugins/README.md +11 -0
  303. crackerjack/plugins/__init__.py +15 -0
  304. crackerjack/plugins/base.py +200 -0
  305. crackerjack/plugins/hooks.py +254 -0
  306. crackerjack/plugins/loader.py +335 -0
  307. crackerjack/plugins/managers.py +264 -0
  308. crackerjack/py313.py +191 -0
  309. crackerjack/security/README.md +11 -0
  310. crackerjack/security/__init__.py +0 -0
  311. crackerjack/security/audit.py +197 -0
  312. crackerjack/services/README.md +374 -0
  313. crackerjack/services/__init__.py +9 -0
  314. crackerjack/services/ai/README.md +295 -0
  315. crackerjack/services/ai/__init__.py +7 -0
  316. crackerjack/services/ai/advanced_optimizer.py +878 -0
  317. crackerjack/services/ai/contextual_ai_assistant.py +542 -0
  318. crackerjack/services/ai/embeddings.py +444 -0
  319. crackerjack/services/ai/intelligent_commit.py +328 -0
  320. crackerjack/services/ai/predictive_analytics.py +510 -0
  321. crackerjack/services/anomaly_detector.py +392 -0
  322. crackerjack/services/api_extractor.py +617 -0
  323. crackerjack/services/backup_service.py +467 -0
  324. crackerjack/services/bounded_status_operations.py +530 -0
  325. crackerjack/services/cache.py +369 -0
  326. crackerjack/services/changelog_automation.py +399 -0
  327. crackerjack/services/command_execution_service.py +305 -0
  328. crackerjack/services/config_integrity.py +132 -0
  329. crackerjack/services/config_merge.py +546 -0
  330. crackerjack/services/config_service.py +198 -0
  331. crackerjack/services/config_template.py +493 -0
  332. crackerjack/services/coverage_badge_service.py +173 -0
  333. crackerjack/services/coverage_ratchet.py +381 -0
  334. crackerjack/services/debug.py +733 -0
  335. crackerjack/services/dependency_analyzer.py +460 -0
  336. crackerjack/services/dependency_monitor.py +622 -0
  337. crackerjack/services/documentation_generator.py +493 -0
  338. crackerjack/services/documentation_service.py +704 -0
  339. crackerjack/services/enhanced_filesystem.py +497 -0
  340. crackerjack/services/enterprise_optimizer.py +865 -0
  341. crackerjack/services/error_pattern_analyzer.py +676 -0
  342. crackerjack/services/file_filter.py +221 -0
  343. crackerjack/services/file_hasher.py +149 -0
  344. crackerjack/services/file_io_service.py +361 -0
  345. crackerjack/services/file_modifier.py +615 -0
  346. crackerjack/services/filesystem.py +381 -0
  347. crackerjack/services/git.py +422 -0
  348. crackerjack/services/health_metrics.py +615 -0
  349. crackerjack/services/heatmap_generator.py +744 -0
  350. crackerjack/services/incremental_executor.py +380 -0
  351. crackerjack/services/initialization.py +823 -0
  352. crackerjack/services/input_validator.py +668 -0
  353. crackerjack/services/intelligent_commit.py +327 -0
  354. crackerjack/services/log_manager.py +289 -0
  355. crackerjack/services/logging.py +228 -0
  356. crackerjack/services/lsp_client.py +628 -0
  357. crackerjack/services/memory_optimizer.py +414 -0
  358. crackerjack/services/metrics.py +587 -0
  359. crackerjack/services/monitoring/README.md +30 -0
  360. crackerjack/services/monitoring/__init__.py +9 -0
  361. crackerjack/services/monitoring/dependency_monitor.py +678 -0
  362. crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
  363. crackerjack/services/monitoring/health_metrics.py +716 -0
  364. crackerjack/services/monitoring/metrics.py +587 -0
  365. crackerjack/services/monitoring/performance_benchmarks.py +410 -0
  366. crackerjack/services/monitoring/performance_cache.py +388 -0
  367. crackerjack/services/monitoring/performance_monitor.py +569 -0
  368. crackerjack/services/parallel_executor.py +527 -0
  369. crackerjack/services/pattern_cache.py +333 -0
  370. crackerjack/services/pattern_detector.py +478 -0
  371. crackerjack/services/patterns/__init__.py +142 -0
  372. crackerjack/services/patterns/agents.py +107 -0
  373. crackerjack/services/patterns/code/__init__.py +15 -0
  374. crackerjack/services/patterns/code/detection.py +118 -0
  375. crackerjack/services/patterns/code/imports.py +107 -0
  376. crackerjack/services/patterns/code/paths.py +159 -0
  377. crackerjack/services/patterns/code/performance.py +119 -0
  378. crackerjack/services/patterns/code/replacement.py +36 -0
  379. crackerjack/services/patterns/core.py +212 -0
  380. crackerjack/services/patterns/documentation/__init__.py +14 -0
  381. crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
  382. crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
  383. crackerjack/services/patterns/documentation/docstrings.py +89 -0
  384. crackerjack/services/patterns/formatting.py +226 -0
  385. crackerjack/services/patterns/operations.py +339 -0
  386. crackerjack/services/patterns/security/__init__.py +23 -0
  387. crackerjack/services/patterns/security/code_injection.py +122 -0
  388. crackerjack/services/patterns/security/credentials.py +190 -0
  389. crackerjack/services/patterns/security/path_traversal.py +221 -0
  390. crackerjack/services/patterns/security/unsafe_operations.py +216 -0
  391. crackerjack/services/patterns/templates.py +62 -0
  392. crackerjack/services/patterns/testing/__init__.py +18 -0
  393. crackerjack/services/patterns/testing/error_patterns.py +107 -0
  394. crackerjack/services/patterns/testing/pytest_output.py +126 -0
  395. crackerjack/services/patterns/tool_output/__init__.py +16 -0
  396. crackerjack/services/patterns/tool_output/bandit.py +72 -0
  397. crackerjack/services/patterns/tool_output/other.py +97 -0
  398. crackerjack/services/patterns/tool_output/pyright.py +67 -0
  399. crackerjack/services/patterns/tool_output/ruff.py +44 -0
  400. crackerjack/services/patterns/url_sanitization.py +114 -0
  401. crackerjack/services/patterns/utilities.py +42 -0
  402. crackerjack/services/patterns/utils.py +339 -0
  403. crackerjack/services/patterns/validation.py +46 -0
  404. crackerjack/services/patterns/versioning.py +62 -0
  405. crackerjack/services/predictive_analytics.py +523 -0
  406. crackerjack/services/profiler.py +280 -0
  407. crackerjack/services/quality/README.md +415 -0
  408. crackerjack/services/quality/__init__.py +11 -0
  409. crackerjack/services/quality/anomaly_detector.py +392 -0
  410. crackerjack/services/quality/pattern_cache.py +333 -0
  411. crackerjack/services/quality/pattern_detector.py +479 -0
  412. crackerjack/services/quality/qa_orchestrator.py +491 -0
  413. crackerjack/services/quality/quality_baseline.py +395 -0
  414. crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
  415. crackerjack/services/quality/quality_intelligence.py +949 -0
  416. crackerjack/services/regex_patterns.py +58 -0
  417. crackerjack/services/regex_utils.py +483 -0
  418. crackerjack/services/secure_path_utils.py +524 -0
  419. crackerjack/services/secure_status_formatter.py +450 -0
  420. crackerjack/services/secure_subprocess.py +635 -0
  421. crackerjack/services/security.py +239 -0
  422. crackerjack/services/security_logger.py +495 -0
  423. crackerjack/services/server_manager.py +411 -0
  424. crackerjack/services/smart_scheduling.py +167 -0
  425. crackerjack/services/status_authentication.py +460 -0
  426. crackerjack/services/status_security_manager.py +315 -0
  427. crackerjack/services/terminal_utils.py +0 -0
  428. crackerjack/services/thread_safe_status_collector.py +441 -0
  429. crackerjack/services/tool_filter.py +368 -0
  430. crackerjack/services/tool_version_service.py +43 -0
  431. crackerjack/services/unified_config.py +115 -0
  432. crackerjack/services/validation_rate_limiter.py +220 -0
  433. crackerjack/services/vector_store.py +689 -0
  434. crackerjack/services/version_analyzer.py +461 -0
  435. crackerjack/services/version_checker.py +223 -0
  436. crackerjack/services/websocket_resource_limiter.py +438 -0
  437. crackerjack/services/zuban_lsp_service.py +391 -0
  438. crackerjack/slash_commands/README.md +11 -0
  439. crackerjack/slash_commands/__init__.py +59 -0
  440. crackerjack/slash_commands/init.md +112 -0
  441. crackerjack/slash_commands/run.md +197 -0
  442. crackerjack/slash_commands/status.md +127 -0
  443. crackerjack/tools/README.md +11 -0
  444. crackerjack/tools/__init__.py +30 -0
  445. crackerjack/tools/_git_utils.py +105 -0
  446. crackerjack/tools/check_added_large_files.py +139 -0
  447. crackerjack/tools/check_ast.py +105 -0
  448. crackerjack/tools/check_json.py +103 -0
  449. crackerjack/tools/check_jsonschema.py +297 -0
  450. crackerjack/tools/check_toml.py +103 -0
  451. crackerjack/tools/check_yaml.py +110 -0
  452. crackerjack/tools/codespell_wrapper.py +72 -0
  453. crackerjack/tools/end_of_file_fixer.py +202 -0
  454. crackerjack/tools/format_json.py +128 -0
  455. crackerjack/tools/mdformat_wrapper.py +114 -0
  456. crackerjack/tools/trailing_whitespace.py +198 -0
  457. crackerjack/tools/validate_input_validator_patterns.py +236 -0
  458. crackerjack/tools/validate_regex_patterns.py +188 -0
  459. crackerjack/ui/README.md +11 -0
  460. crackerjack/ui/__init__.py +1 -0
  461. crackerjack/ui/dashboard_renderer.py +28 -0
  462. crackerjack/ui/templates/README.md +11 -0
  463. crackerjack/utils/console_utils.py +13 -0
  464. crackerjack/utils/dependency_guard.py +230 -0
  465. crackerjack/utils/retry_utils.py +275 -0
  466. crackerjack/workflows/README.md +590 -0
  467. crackerjack/workflows/__init__.py +46 -0
  468. crackerjack/workflows/actions.py +811 -0
  469. crackerjack/workflows/auto_fix.py +444 -0
  470. crackerjack/workflows/container_builder.py +499 -0
  471. crackerjack/workflows/definitions.py +443 -0
  472. crackerjack/workflows/engine.py +177 -0
  473. crackerjack/workflows/event_bridge.py +242 -0
  474. crackerjack-0.45.2.dist-info/METADATA +1678 -0
  475. crackerjack-0.45.2.dist-info/RECORD +478 -0
  476. {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
  477. crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
  478. crackerjack/.gitignore +0 -14
  479. crackerjack/.libcst.codemod.yaml +0 -18
  480. crackerjack/.pdm.toml +0 -1
  481. crackerjack/.pre-commit-config.yaml +0 -91
  482. crackerjack/.pytest_cache/.gitignore +0 -2
  483. crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
  484. crackerjack/.pytest_cache/README.md +0 -8
  485. crackerjack/.pytest_cache/v/cache/nodeids +0 -1
  486. crackerjack/.pytest_cache/v/cache/stepwise +0 -1
  487. crackerjack/.ruff_cache/.gitignore +0 -1
  488. crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
  489. crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
  490. crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
  491. crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
  492. crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
  493. crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
  494. crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
  495. crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
  496. crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
  497. crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
  498. crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
  499. crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
  500. crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
  501. crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
  502. crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
  503. crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
  504. crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
  505. crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
  506. crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
  507. crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
  508. crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
  509. crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
  510. crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
  511. crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
  512. crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
  513. crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
  514. crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
  515. crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
  516. crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
  517. crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
  518. crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
  519. crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
  520. crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
  521. crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
  522. crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
  523. crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
  524. crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
  525. crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
  526. crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
  527. crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
  528. crackerjack/crackerjack.py +0 -855
  529. crackerjack/pyproject.toml +0 -214
  530. crackerjack-0.18.2.dist-info/METADATA +0 -420
  531. crackerjack-0.18.2.dist-info/RECORD +0 -59
  532. crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
  533. {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,461 @@
1
+ """Intelligent version bump analysis based on code changes and commit patterns."""
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from enum import Enum
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from acb.console import Console
10
+ from acb.depends import Inject, depends
11
+
12
+ from .changelog_automation import ChangelogEntry, ChangelogGenerator
13
+ from .git import GitService
14
+
15
+
16
+ class VersionBumpType(Enum):
17
+ """Semantic versioning bump types."""
18
+
19
+ MAJOR = "major" # Breaking changes (x.y.z -> x+1.0.0)
20
+ MINOR = "minor" # New features (x.y.z -> x.y+1.0)
21
+ PATCH = "patch" # Bug fixes (x.y.z -> x.y.z+1)
22
+
23
+
24
+ @dataclass
25
+ class VersionBumpRecommendation:
26
+ """Recommendation for version bump with reasoning."""
27
+
28
+ bump_type: VersionBumpType
29
+ confidence: float # 0.0 to 1.0
30
+ reasoning: list[str]
31
+ current_version: str
32
+ recommended_version: str
33
+ breaking_changes: list[str]
34
+ new_features: list[str]
35
+ bug_fixes: list[str]
36
+ commit_analysis: dict[str, Any]
37
+
38
+
39
+ class BreakingChangeAnalyzer:
40
+ """Analyzes commits for breaking changes that require MAJOR version bump."""
41
+
42
+ def __init__(self) -> None:
43
+ # Patterns that indicate breaking changes
44
+ self.breaking_patterns = [
45
+ re.compile(
46
+ r"BREAKING\s*CHANGE[:\s]", re.IGNORECASE
47
+ ), # REGEX OK: breaking change detection
48
+ re.compile(
49
+ r"^[^:\n]*!:", re.MULTILINE
50
+ ), # REGEX OK: conventional commit breaking marker
51
+ re.compile(
52
+ r"\bremove\s+\w+\s+(api|function|method|class)", re.IGNORECASE
53
+ ), # REGEX OK: API removal detection
54
+ re.compile(
55
+ r"\bdelete\s+\w+\s+(api|endpoint|interface)", re.IGNORECASE
56
+ ), # REGEX OK: API deletion detection
57
+ re.compile(
58
+ r"\bchange\s+\w+\s+(signature|interface|api)", re.IGNORECASE
59
+ ), # REGEX OK: API signature change
60
+ ]
61
+
62
+ def analyze(self, entries: list[ChangelogEntry]) -> tuple[bool, list[str], float]:
63
+ """
64
+ Analyze changelog entries for breaking changes.
65
+
66
+ Returns:
67
+ (has_breaking_changes, breaking_change_descriptions, confidence)
68
+ """
69
+ breaking_changes: list[str] = []
70
+
71
+ for entry in entries:
72
+ # Check if entry is already marked as breaking
73
+ if entry.breaking_change:
74
+ breaking_changes.append(entry.description)
75
+ continue
76
+
77
+ # Check description against breaking change patterns
78
+ for pattern in self.breaking_patterns:
79
+ if pattern.search(entry.description):
80
+ breaking_changes.append(entry.description)
81
+ break
82
+
83
+ has_breaking = len(breaking_changes) > 0
84
+ confidence = 0.9 if has_breaking else 0.0
85
+
86
+ return has_breaking, breaking_changes, confidence
87
+
88
+
89
+ class FeatureAnalyzer:
90
+ """Analyzes commits for new features that require MINOR version bump."""
91
+
92
+ def __init__(self) -> None:
93
+ # Patterns that indicate new features
94
+ self.feature_patterns = [
95
+ re.compile(
96
+ r"^feat[(\[]", re.IGNORECASE
97
+ ), # REGEX OK: conventional commit feat
98
+ re.compile(
99
+ r"\badd\s+(new\s+)?\w+", re.IGNORECASE
100
+ ), # REGEX OK: addition detection
101
+ re.compile(
102
+ r"\bimplement\s+\w+", re.IGNORECASE
103
+ ), # REGEX OK: implementation detection
104
+ re.compile(
105
+ r"\bintroduce\s+\w+", re.IGNORECASE
106
+ ), # REGEX OK: introduction detection
107
+ re.compile(
108
+ r"\bcreate\s+(new\s+)?\w+", re.IGNORECASE
109
+ ), # REGEX OK: creation detection
110
+ ]
111
+
112
+ def analyze(self, entries: list[ChangelogEntry]) -> tuple[bool, list[str], float]:
113
+ """
114
+ Analyze changelog entries for new features.
115
+
116
+ Returns:
117
+ (has_new_features, feature_descriptions, confidence)
118
+ """
119
+ new_features: list[str] = []
120
+
121
+ for entry in entries:
122
+ # Check entry type
123
+ if entry.type in ("Added", "feat"):
124
+ new_features.append(entry.description)
125
+ continue
126
+
127
+ # Check description against feature patterns
128
+ for pattern in self.feature_patterns:
129
+ if pattern.search(entry.description):
130
+ new_features.append(entry.description)
131
+ break
132
+
133
+ has_features = len(new_features) > 0
134
+ confidence = 0.8 if has_features else 0.0
135
+
136
+ return has_features, new_features, confidence
137
+
138
+
139
+ class ConventionalCommitAnalyzer:
140
+ """Analyzes conventional commit messages for semantic versioning hints."""
141
+
142
+ def __init__(self) -> None:
143
+ # Commit type mappings to version bump types
144
+ self.commit_type_mappings = {
145
+ # MAJOR bump triggers
146
+ "breaking": VersionBumpType.MAJOR,
147
+ # MINOR bump triggers
148
+ "feat": VersionBumpType.MINOR,
149
+ "feature": VersionBumpType.MINOR,
150
+ # PATCH bump triggers
151
+ "fix": VersionBumpType.PATCH,
152
+ "bugfix": VersionBumpType.PATCH,
153
+ "patch": VersionBumpType.PATCH,
154
+ "hotfix": VersionBumpType.PATCH,
155
+ # No version bump (could be argued either way)
156
+ "docs": None,
157
+ "style": None,
158
+ "refactor": None,
159
+ "test": None,
160
+ "chore": None,
161
+ "build": None,
162
+ "ci": None,
163
+ }
164
+
165
+ def analyze(self, entries: list[ChangelogEntry]) -> dict[str, Any]:
166
+ """
167
+ Analyze conventional commit patterns in changelog entries.
168
+
169
+ Returns:
170
+ Analysis results including type counts and recommendations
171
+ """
172
+ type_counts: dict[str, int] = {}
173
+ recommended_bumps: list[VersionBumpType] = []
174
+
175
+ for entry in entries:
176
+ # Parse entry type and increment count
177
+ entry_type = entry.type.lower()
178
+ type_counts[entry_type] = type_counts.get(entry_type, 0) + 1
179
+
180
+ # Determine recommended bump type
181
+ if entry.breaking_change:
182
+ recommended_bumps.append(VersionBumpType.MAJOR)
183
+ elif entry_type in self.commit_type_mappings:
184
+ bump_type = self.commit_type_mappings[entry_type]
185
+ if bump_type:
186
+ recommended_bumps.append(bump_type)
187
+
188
+ return {
189
+ "type_counts": type_counts,
190
+ "recommended_bumps": recommended_bumps,
191
+ "total_entries": len(entries),
192
+ }
193
+
194
+
195
+ class VersionAnalyzer:
196
+ """Main service for analyzing changes and recommending version bumps."""
197
+
198
+ @depends.inject
199
+ def __init__(self, console: Inject[Console], git_service: GitService) -> None:
200
+ self.console = console
201
+ self.git = git_service
202
+
203
+ # Initialize specialized analyzers
204
+ self.breaking_analyzer = BreakingChangeAnalyzer()
205
+ self.feature_analyzer = FeatureAnalyzer()
206
+ self.commit_analyzer = ConventionalCommitAnalyzer()
207
+
208
+ # Initialize changelog generator for getting entries (ACB DI injects dependencies)
209
+ self.changelog_generator = ChangelogGenerator()
210
+
211
+ def _get_current_version(self) -> str | None:
212
+ """Get current version from pyproject.toml."""
213
+ pyproject_path = Path("pyproject.toml")
214
+ if not pyproject_path.exists():
215
+ return None
216
+
217
+ try:
218
+ from tomllib import loads
219
+
220
+ content = pyproject_path.read_text(encoding="utf-8")
221
+ data = loads(content)
222
+ version: str | None = data.get("project", {}).get("version")
223
+ return version
224
+ except Exception:
225
+ return None
226
+
227
+ def _calculate_next_version(self, current: str, bump_type: VersionBumpType) -> str:
228
+ """Calculate next version based on current version and bump type."""
229
+ try:
230
+ parts = current.split(".")
231
+ if len(parts) != 3:
232
+ msg = f"Invalid version format: {current}"
233
+ raise ValueError(msg)
234
+
235
+ major, minor, patch = map(int, parts)
236
+
237
+ if bump_type == VersionBumpType.MAJOR:
238
+ return f"{major + 1}.0.0"
239
+ elif bump_type == VersionBumpType.MINOR:
240
+ return f"{major}.{minor + 1}.0"
241
+ elif bump_type == VersionBumpType.PATCH:
242
+ return f"{major}.{minor}.{patch + 1}"
243
+ else:
244
+ # All enum cases are covered above
245
+ from typing import assert_never
246
+
247
+ assert_never(bump_type)
248
+
249
+ except Exception as e:
250
+ self.console.print(f"[red]❌[/red] Error calculating version: {e}")
251
+ raise
252
+
253
+ async def recommend_version_bump(
254
+ self, since_version: str | None = None
255
+ ) -> VersionBumpRecommendation:
256
+ """
257
+ Analyze changes since last version and recommend version bump.
258
+
259
+ Args:
260
+ since_version: Version/tag to analyze changes since (default: latest tag)
261
+
262
+ Returns:
263
+ VersionBumpRecommendation with analysis and recommendation
264
+ """
265
+ current_version = self._get_current_version()
266
+ if not current_version:
267
+ msg = "Could not determine current version from pyproject.toml"
268
+ raise ValueError(msg)
269
+
270
+ all_entries = self._collect_changelog_entries(since_version)
271
+
272
+ if not all_entries:
273
+ return self._create_no_changes_recommendation(current_version)
274
+
275
+ return self._analyze_entries_and_recommend(current_version, all_entries)
276
+
277
+ def _collect_changelog_entries(
278
+ self, since_version: str | None
279
+ ) -> list[ChangelogEntry]:
280
+ """Collect and flatten changelog entries for analysis."""
281
+ entries_by_type = self.changelog_generator.generate_changelog_entries(
282
+ since_version
283
+ )
284
+ all_entries: list[ChangelogEntry] = []
285
+ for entries in entries_by_type.values():
286
+ all_entries.extend(entries)
287
+ return all_entries
288
+
289
+ def _create_no_changes_recommendation(
290
+ self, current_version: str
291
+ ) -> VersionBumpRecommendation:
292
+ """Create recommendation when no changes are detected."""
293
+ return VersionBumpRecommendation(
294
+ bump_type=VersionBumpType.PATCH,
295
+ confidence=1.0,
296
+ reasoning=["No significant changes detected - patch bump recommended"],
297
+ current_version=current_version,
298
+ recommended_version=self._calculate_next_version(
299
+ current_version, VersionBumpType.PATCH
300
+ ),
301
+ breaking_changes=[],
302
+ new_features=[],
303
+ bug_fixes=[],
304
+ commit_analysis={
305
+ "type_counts": {},
306
+ "recommended_bumps": [],
307
+ "total_entries": 0,
308
+ },
309
+ )
310
+
311
+ def _analyze_entries_and_recommend(
312
+ self, current_version: str, all_entries: list[ChangelogEntry]
313
+ ) -> VersionBumpRecommendation:
314
+ """Analyze entries and create version bump recommendation."""
315
+ # Run specialized analyses
316
+ has_breaking, breaking_changes, breaking_confidence = (
317
+ self.breaking_analyzer.analyze(all_entries)
318
+ )
319
+ has_features, new_features, feature_confidence = self.feature_analyzer.analyze(
320
+ all_entries
321
+ )
322
+ commit_analysis = self.commit_analyzer.analyze(all_entries)
323
+
324
+ bug_fixes = [
325
+ entry.description
326
+ for entry in all_entries
327
+ if entry.type.lower() in ("fixed", "fix", "bugfix", "patch")
328
+ ]
329
+
330
+ bump_type, confidence, reasoning = self._determine_bump_type(
331
+ has_breaking,
332
+ breaking_changes,
333
+ breaking_confidence,
334
+ has_features,
335
+ new_features,
336
+ feature_confidence,
337
+ bug_fixes,
338
+ all_entries,
339
+ )
340
+
341
+ recommended_version = self._calculate_next_version(current_version, bump_type)
342
+
343
+ return VersionBumpRecommendation(
344
+ bump_type=bump_type,
345
+ confidence=confidence,
346
+ reasoning=reasoning,
347
+ current_version=current_version,
348
+ recommended_version=recommended_version,
349
+ breaking_changes=breaking_changes,
350
+ new_features=new_features,
351
+ bug_fixes=bug_fixes,
352
+ commit_analysis=commit_analysis,
353
+ )
354
+
355
+ def _determine_bump_type(
356
+ self,
357
+ has_breaking: bool,
358
+ breaking_changes: list[str],
359
+ breaking_confidence: float,
360
+ has_features: bool,
361
+ new_features: list[str],
362
+ feature_confidence: float,
363
+ bug_fixes: list[str],
364
+ all_entries: list[ChangelogEntry],
365
+ ) -> tuple[VersionBumpType, float, list[str]]:
366
+ """Determine the appropriate version bump type and reasoning."""
367
+ if has_breaking:
368
+ return (
369
+ VersionBumpType.MAJOR,
370
+ breaking_confidence,
371
+ [
372
+ f"Breaking changes detected ({len(breaking_changes)} found)",
373
+ "MAJOR version bump required to maintain semantic versioning",
374
+ ],
375
+ )
376
+ elif has_features:
377
+ return (
378
+ VersionBumpType.MINOR,
379
+ feature_confidence,
380
+ [
381
+ f"New features detected ({len(new_features)} found)",
382
+ "MINOR version bump recommended for backward-compatible functionality",
383
+ ],
384
+ )
385
+ elif bug_fixes:
386
+ return (
387
+ VersionBumpType.PATCH,
388
+ 0.9,
389
+ [
390
+ f"Bug fixes detected ({len(bug_fixes)} found)",
391
+ "PATCH version bump recommended for backward-compatible fixes",
392
+ ],
393
+ )
394
+ return (
395
+ VersionBumpType.PATCH,
396
+ 0.5,
397
+ [
398
+ f"Changes detected ({len(all_entries)} commits) with unclear impact",
399
+ "PATCH version bump recommended as conservative choice",
400
+ ],
401
+ )
402
+
403
+ def display_recommendation(self, recommendation: VersionBumpRecommendation) -> None:
404
+ """Display version bump recommendation in a user-friendly format."""
405
+ self._display_summary(recommendation)
406
+ self._display_reasoning(recommendation)
407
+ self._display_changes(recommendation)
408
+ self._display_commit_analysis(recommendation)
409
+
410
+ def _display_summary(self, recommendation: VersionBumpRecommendation) -> None:
411
+ """Display the main version bump summary."""
412
+ self.console.print("\n[cyan]📊 Version Bump Analysis[/cyan]")
413
+ self.console.print(
414
+ f"Current version: [bold]{recommendation.current_version}[/bold]"
415
+ )
416
+ self.console.print(
417
+ f"Recommended version: [bold green]{recommendation.recommended_version}[/bold green]"
418
+ )
419
+ self.console.print(
420
+ f"Bump type: [bold]{recommendation.bump_type.value.upper()}[/bold]"
421
+ )
422
+ self.console.print(f"Confidence: [bold]{recommendation.confidence:.0%}[/bold]")
423
+
424
+ def _display_reasoning(self, recommendation: VersionBumpRecommendation) -> None:
425
+ """Display the reasoning behind the recommendation."""
426
+ self.console.print("\n[yellow]💡 Reasoning:[/yellow]")
427
+ for reason in recommendation.reasoning:
428
+ self.console.print(f" • {reason}")
429
+
430
+ def _display_changes(self, recommendation: VersionBumpRecommendation) -> None:
431
+ """Display breaking changes, new features, and bug fixes."""
432
+ self._display_change_list(
433
+ recommendation.breaking_changes, "[red]⚠️ Breaking Changes", "red"
434
+ )
435
+ self._display_change_list(
436
+ recommendation.new_features, "[green]✨ New Features", "green"
437
+ )
438
+ self._display_change_list(
439
+ recommendation.bug_fixes, "[blue]🔧 Bug Fixes", "blue"
440
+ )
441
+
442
+ def _display_change_list(self, changes: list[str], title: str, color: str) -> None:
443
+ """Display a list[t.Any] of changes with truncation."""
444
+ if changes:
445
+ self.console.print(f"\n{title} ({len(changes)}):[/{color}]")
446
+ for change in changes[:3]:
447
+ self.console.print(f" • {change}")
448
+ if len(changes) > 3:
449
+ self.console.print(f" • ... and {len(changes) - 3} more")
450
+
451
+ def _display_commit_analysis(
452
+ self, recommendation: VersionBumpRecommendation
453
+ ) -> None:
454
+ """Display commit analysis summary."""
455
+ analysis = recommendation.commit_analysis
456
+ if analysis.get("type_counts"):
457
+ self.console.print("\n[dim]📈 Commit Analysis:[/dim]")
458
+ total = analysis["total_entries"]
459
+ for commit_type, count in sorted(analysis["type_counts"].items()):
460
+ percentage = (count / total * 100) if total > 0 else 0
461
+ self.console.print(f" {commit_type}: {count} ({percentage:.0f}%)")
@@ -0,0 +1,223 @@
1
+ import subprocess
2
+ import typing as t
3
+ from dataclasses import dataclass
4
+
5
+ import aiohttp
6
+ from acb.console import Console
7
+ from acb.depends import Inject, depends
8
+
9
+ from crackerjack.core.retry import retry_api_call
10
+
11
+
12
+ @dataclass
13
+ class VersionInfo:
14
+ tool_name: str
15
+ current_version: str
16
+ latest_version: str | None = None
17
+ update_available: bool = False
18
+ error: str | None = None
19
+
20
+
21
+ class VersionChecker:
22
+ @depends.inject
23
+ def __init__(self, console: Inject[Console]) -> None:
24
+ self.console = console
25
+ self.tools_to_check = {
26
+ "ruff": self._get_ruff_version,
27
+ "pyright": self._get_pyright_version,
28
+ "pre-commit": self._get_precommit_version,
29
+ "uv": self._get_uv_version,
30
+ }
31
+
32
+ async def check_tool_updates(self) -> dict[str, VersionInfo]:
33
+ results = {}
34
+ for tool_name, version_getter in self.tools_to_check.items():
35
+ results[tool_name] = await self._check_single_tool(
36
+ tool_name, version_getter
37
+ )
38
+ return results
39
+
40
+ async def _check_single_tool(
41
+ self, tool_name: str, version_getter: t.Callable[[], str | None]
42
+ ) -> VersionInfo:
43
+ try:
44
+ current_version = version_getter()
45
+ if current_version:
46
+ latest_version = await self._fetch_latest_version(tool_name)
47
+ return self._create_installed_version_info(
48
+ tool_name, current_version, latest_version
49
+ )
50
+ else:
51
+ return self._create_missing_tool_info(tool_name)
52
+ except Exception as e:
53
+ return self._create_error_version_info(tool_name, e)
54
+
55
+ def _create_installed_version_info(
56
+ self, tool_name: str, current_version: str, latest_version: str | None
57
+ ) -> VersionInfo:
58
+ update_available = (
59
+ latest_version is not None
60
+ and self._version_compare(current_version, latest_version) < 0
61
+ )
62
+
63
+ if update_available:
64
+ self.console.print(
65
+ f"[yellow]🔄 {tool_name} update available: "
66
+ f"{current_version} → {latest_version}[/ yellow]"
67
+ )
68
+
69
+ return VersionInfo(
70
+ tool_name=tool_name,
71
+ current_version=current_version,
72
+ latest_version=latest_version,
73
+ update_available=update_available,
74
+ )
75
+
76
+ def _create_missing_tool_info(self, tool_name: str) -> VersionInfo:
77
+ self.console.print(f"[red]⚠️ {tool_name} not installed[/ red]")
78
+ return VersionInfo(
79
+ tool_name=tool_name,
80
+ current_version="not installed",
81
+ error=f"{tool_name} not found or not installed",
82
+ )
83
+
84
+ def _create_error_version_info(
85
+ self, tool_name: str, error: Exception
86
+ ) -> VersionInfo:
87
+ self.console.print(f"[red]❌ Error checking {tool_name}: {error}[/ red]")
88
+ return VersionInfo(
89
+ tool_name=tool_name,
90
+ current_version="unknown",
91
+ error=str(error),
92
+ )
93
+
94
+ def _get_ruff_version(self) -> str | None:
95
+ return self._get_tool_version("ruff")
96
+
97
+ def _get_pyright_version(self) -> str | None:
98
+ return self._get_tool_version("pyright")
99
+
100
+ def _get_precommit_version(self) -> str | None:
101
+ return self._get_tool_version("pre-commit")
102
+
103
+ def _get_uv_version(self) -> str | None:
104
+ return self._get_tool_version("uv")
105
+
106
+ def _get_tool_version(self, tool_name: str) -> str | None:
107
+ try:
108
+ result = subprocess.run(
109
+ [tool_name, "--version"],
110
+ capture_output=True,
111
+ text=True,
112
+ timeout=10,
113
+ check=False,
114
+ )
115
+ if result.returncode == 0:
116
+ version_line = result.stdout.strip()
117
+ return version_line.split()[-1] if version_line else None
118
+ except (FileNotFoundError, subprocess.TimeoutExpired):
119
+ pass
120
+ return None
121
+
122
+ @retry_api_call(max_attempts=3, delay=1.0, backoff=2.0, max_delay=30.0)
123
+ async def _fetch_latest_version(self, tool_name: str) -> str | None:
124
+ try:
125
+ # Fix URLs - remove spaces that were added
126
+ pypi_urls = {
127
+ "ruff": "https://pypi.org/pypi/ruff/json",
128
+ "pyright": "https://pypi.org/pypi/pyright/json",
129
+ "pre-commit": "https://pypi.org/pypi/pre-commit/json",
130
+ "uv": "https://pypi.org/pypi/uv/json",
131
+ }
132
+
133
+ url = pypi_urls.get(tool_name)
134
+ if not url:
135
+ return None
136
+
137
+ timeout = aiohttp.ClientTimeout(total=10.0)
138
+ async with aiohttp.ClientSession(timeout=timeout) as session:
139
+ async with session.get(url) as response:
140
+ response.raise_for_status()
141
+ data: dict[str, t.Any] = await response.json()
142
+ return data.get("info", {}).get("version")
143
+
144
+ except Exception:
145
+ return None
146
+
147
+ def _version_compare(self, current: str, latest: str) -> int:
148
+ try:
149
+ current_parts, current_len = self._parse_version_parts(current)
150
+ latest_parts, latest_len = self._parse_version_parts(latest)
151
+
152
+ normalized_current, normalized_latest = self._normalize_version_parts(
153
+ current_parts, latest_parts
154
+ )
155
+
156
+ numeric_result = self._compare_numeric_parts(
157
+ normalized_current, normalized_latest
158
+ )
159
+ if numeric_result != 0:
160
+ return numeric_result
161
+
162
+ return self._handle_length_differences(
163
+ current_len, latest_len, normalized_current, normalized_latest
164
+ )
165
+
166
+ except (ValueError, AttributeError):
167
+ return 0
168
+
169
+ def _parse_version_parts(self, version: str) -> tuple[list[int], int]:
170
+ parts = [int(x) for x in version.split(".")]
171
+ return parts, len(parts)
172
+
173
+ def _normalize_version_parts(
174
+ self, current_parts: list[int], latest_parts: list[int]
175
+ ) -> tuple[list[int], list[int]]:
176
+ max_len = max(len(current_parts), len(latest_parts))
177
+ current_normalized = current_parts + [0] * (max_len - len(current_parts))
178
+ latest_normalized = latest_parts + [0] * (max_len - len(latest_parts))
179
+ return current_normalized, latest_normalized
180
+
181
+ def _compare_numeric_parts(
182
+ self, current_parts: list[int], latest_parts: list[int]
183
+ ) -> int:
184
+ for current_part, latest_part in zip(current_parts, latest_parts):
185
+ if current_part < latest_part:
186
+ return -1
187
+ if current_part > latest_part:
188
+ return 1
189
+ return 0
190
+
191
+ def _handle_length_differences(
192
+ self,
193
+ current_len: int,
194
+ latest_len: int,
195
+ current_parts: list[int],
196
+ latest_parts: list[int],
197
+ ) -> int:
198
+ if current_len == latest_len:
199
+ return 0
200
+
201
+ if current_len < latest_len:
202
+ return self._compare_when_current_shorter(
203
+ current_len, latest_len, latest_parts
204
+ )
205
+ return self._compare_when_latest_shorter(latest_len, current_len, current_parts)
206
+
207
+ def _compare_when_current_shorter(
208
+ self, current_len: int, latest_len: int, latest_parts: list[int]
209
+ ) -> int:
210
+ extra_parts = latest_parts[current_len:]
211
+ if any(part != 0 for part in extra_parts):
212
+ return -1
213
+
214
+ return -1 if current_len > 1 else 0
215
+
216
+ def _compare_when_latest_shorter(
217
+ self, latest_len: int, current_len: int, current_parts: list[int]
218
+ ) -> int:
219
+ extra_parts = current_parts[latest_len:]
220
+ if any(part != 0 for part in extra_parts):
221
+ return 1
222
+
223
+ return 1 if latest_len > 1 else 0