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,631 @@
1
+ import subprocess
2
+ import typing as t
3
+ from contextlib import suppress
4
+ from pathlib import Path
5
+
6
+ from acb.console import Console
7
+ from acb.depends import Inject, depends
8
+
9
+ from crackerjack.core.retry import retry_api_call
10
+ from crackerjack.models.protocols import (
11
+ ChangelogGeneratorProtocol,
12
+ FileSystemInterface,
13
+ GitServiceProtocol,
14
+ RegexPatternsProtocol,
15
+ SecurityServiceProtocol,
16
+ VersionAnalyzerProtocol,
17
+ )
18
+
19
+
20
+ class PublishManagerImpl:
21
+ @depends.inject # type: ignore[misc]
22
+ def __init__(
23
+ self,
24
+ git_service: Inject[GitServiceProtocol],
25
+ version_analyzer: Inject[VersionAnalyzerProtocol],
26
+ changelog_generator: Inject[ChangelogGeneratorProtocol],
27
+ filesystem: Inject[FileSystemInterface],
28
+ security: Inject[SecurityServiceProtocol],
29
+ regex_patterns: Inject[RegexPatternsProtocol],
30
+ console: Inject[Console],
31
+ pkg_path: Inject[Path],
32
+ dry_run: bool = False,
33
+ ) -> None:
34
+ # Foundation dependencies
35
+ self.console = console
36
+ self.pkg_path = pkg_path
37
+ self.dry_run = dry_run
38
+
39
+ # Services injected via ACB DI
40
+ self._git_service = git_service
41
+ self._version_analyzer = version_analyzer
42
+ self._changelog_generator = changelog_generator
43
+ self._regex_patterns = regex_patterns
44
+ self.filesystem = filesystem
45
+ self.security = security
46
+
47
+ def _run_command(
48
+ self,
49
+ cmd: list[str],
50
+ timeout: int = 300,
51
+ ) -> subprocess.CompletedProcess[str]:
52
+ secure_env = self.security.create_secure_command_env()
53
+
54
+ result = subprocess.run(
55
+ cmd,
56
+ check=False,
57
+ cwd=self.pkg_path,
58
+ capture_output=True,
59
+ text=True,
60
+ timeout=timeout,
61
+ env=secure_env,
62
+ )
63
+
64
+ if result.stdout:
65
+ result.stdout = self.security.mask_tokens(result.stdout)
66
+ if result.stderr:
67
+ result.stderr = self.security.mask_tokens(result.stderr)
68
+
69
+ return result
70
+
71
+ def _get_current_version(self) -> str | None:
72
+ pyproject_path = self.pkg_path / "pyproject.toml"
73
+ if not pyproject_path.exists():
74
+ return None
75
+ try:
76
+ from tomllib import loads
77
+
78
+ content = self.filesystem.read_file(pyproject_path)
79
+ data = loads(content)
80
+ version = data.get("project", {}).get("version")
81
+ return version if isinstance(version, str) else None
82
+ except Exception as e:
83
+ self.console.print(f"[yellow]โš ๏ธ[/ yellow] Error reading version: {e}")
84
+ return None
85
+
86
+ def _update_version_in_file(self, new_version: str) -> bool:
87
+ pyproject_path = self.pkg_path / "pyproject.toml"
88
+ try:
89
+ content = self.filesystem.read_file(pyproject_path)
90
+
91
+ # Use injected service or get through ACB DI
92
+ if self._regex_patterns is not None:
93
+ update_pyproject_version_func = (
94
+ self._regex_patterns.update_pyproject_version
95
+ )
96
+ else:
97
+ from acb.depends import depends
98
+
99
+ update_pyproject_version_func = depends.get_sync(
100
+ RegexPatternsProtocol
101
+ ).update_pyproject_version
102
+
103
+ new_content = update_pyproject_version_func(content, new_version)
104
+ if content != new_content:
105
+ if not self.dry_run:
106
+ self.filesystem.write_file(pyproject_path, new_content)
107
+ self.console.print(
108
+ f"[green]โœ…[/ green] Updated version to {new_version}",
109
+ )
110
+ return True
111
+ self.console.print(
112
+ "[yellow]โš ๏ธ[/ yellow] Version pattern not found in pyproject.toml",
113
+ )
114
+ return False
115
+ except Exception as e:
116
+ self.console.print(f"[red]โŒ[/ red] Error updating version: {e}")
117
+ return False
118
+
119
+ def _calculate_next_version(self, current: str, bump_type: str) -> str:
120
+ try:
121
+ parts = current.split(".")
122
+ if len(parts) != 3:
123
+ msg = f"Invalid version format: {current}"
124
+ raise ValueError(msg)
125
+ major, minor, patch = map(int, parts)
126
+ if bump_type == "major":
127
+ return f"{major + 1}.0.0"
128
+ if bump_type == "minor":
129
+ return f"{major}.{minor + 1}.0"
130
+ if bump_type == "patch":
131
+ return f"{major}.{minor}.{patch + 1}"
132
+ msg = f"Invalid bump type: {bump_type}"
133
+ raise ValueError(msg)
134
+ except Exception as e:
135
+ self.console.print(f"[red]โŒ[/ red] Error calculating version: {e}")
136
+ raise
137
+
138
+ def bump_version(self, version_type: str) -> str:
139
+ current_version = self._get_current_version()
140
+ if not current_version:
141
+ self.console.print("[red]โŒ[/ red] Could not determine current version")
142
+ msg = "Cannot determine current version"
143
+ raise ValueError(msg)
144
+ self.console.print(f"[cyan]๐Ÿ“ฆ[/ cyan] Current version: {current_version}")
145
+
146
+ # Get intelligent version analysis and recommendation
147
+ recommendation = self._get_version_recommendation()
148
+ if recommendation and version_type != "interactive":
149
+ self._display_version_analysis(recommendation)
150
+ if version_type == "auto":
151
+ version_type = recommendation.bump_type.value
152
+ self.console.print(
153
+ f"[green]๐ŸŽฏ[/green] Using AI-recommended bump type: {version_type}"
154
+ )
155
+
156
+ if version_type == "interactive":
157
+ version_type = self._prompt_for_version_type(recommendation)
158
+
159
+ try:
160
+ new_version = self._calculate_next_version(current_version, version_type)
161
+ if self.dry_run:
162
+ self.console.print(
163
+ f"[yellow]๐Ÿ”[/ yellow] Would bump {version_type} version: {current_version} โ†’ {new_version}",
164
+ )
165
+ elif self._update_version_in_file(new_version):
166
+ self.console.print(
167
+ f"[green]๐Ÿš€[/ green] Bumped {version_type} version: {current_version} โ†’ {new_version}",
168
+ )
169
+ # Update changelog after successful version bump
170
+ self._update_changelog_for_version(current_version, new_version)
171
+ else:
172
+ msg = "Failed to update version in file"
173
+ raise ValueError(msg)
174
+
175
+ return new_version
176
+ except Exception as e:
177
+ self.console.print(f"[red]โŒ[/ red] Version bump failed: {e}")
178
+ raise
179
+
180
+ def _prompt_for_version_type(self, recommendation: t.Any = None) -> str:
181
+ try:
182
+ from rich.prompt import Prompt
183
+
184
+ default_type = "patch"
185
+ if recommendation:
186
+ default_type = recommendation.bump_type.value
187
+ self.console.print(
188
+ f"[dim]AI recommendation: {default_type} (confidence: {recommendation.confidence:.0%})[/dim]"
189
+ )
190
+
191
+ return Prompt.ask(
192
+ "[cyan]๐Ÿ“ฆ[/ cyan] Select version bump type",
193
+ choices=["patch", "minor", "major"],
194
+ default=default_type,
195
+ )
196
+ except ImportError:
197
+ self.console.print(
198
+ "[yellow]โš ๏ธ[/ yellow] Rich prompt not available, defaulting to patch"
199
+ )
200
+ return "patch"
201
+
202
+ def _get_version_recommendation(self) -> t.Any:
203
+ """Get AI-powered version bump recommendation based on git history."""
204
+ try:
205
+ import asyncio
206
+
207
+ # Use injected version analyzer service
208
+ version_analyzer = self._version_analyzer
209
+
210
+ # Get recommendation asynchronously
211
+ try:
212
+ # Try to get existing event loop
213
+ loop = asyncio.get_event_loop()
214
+ if loop.is_running():
215
+ # Create a new event loop in a thread if one is already running
216
+ import concurrent.futures
217
+
218
+ with concurrent.futures.ThreadPoolExecutor() as executor:
219
+ future = executor.submit(
220
+ asyncio.run, version_analyzer.recommend_version_bump()
221
+ )
222
+ recommendation = future.result(timeout=10)
223
+ else:
224
+ recommendation = loop.run_until_complete(
225
+ version_analyzer.recommend_version_bump()
226
+ )
227
+ except RuntimeError:
228
+ # No event loop, create one
229
+ recommendation = asyncio.run(version_analyzer.recommend_version_bump())
230
+
231
+ return recommendation
232
+
233
+ except Exception as e:
234
+ self.console.print(f"[yellow]โš ๏ธ[/yellow] Version analysis failed: {e}")
235
+ return None
236
+
237
+ def _display_version_analysis(self, recommendation: t.Any) -> None:
238
+ """Display version analysis in a compact format."""
239
+ if not recommendation:
240
+ return
241
+
242
+ self.console.print("\n[cyan]๐ŸŽฏ AI Version Analysis[/cyan]")
243
+ self.console.print(
244
+ f"Recommended: [bold green]{recommendation.recommended_version}[/bold green] "
245
+ f"({recommendation.bump_type.value.upper()}) - {recommendation.confidence:.0%} confidence"
246
+ )
247
+
248
+ if recommendation.reasoning:
249
+ self.console.print(f"[dim]โ†’ {recommendation.reasoning[0]}[/dim]")
250
+
251
+ # Show key changes briefly
252
+ if recommendation.breaking_changes:
253
+ self.console.print(
254
+ f"[red]โš ๏ธ[/red] {len(recommendation.breaking_changes)} breaking changes detected"
255
+ )
256
+ elif recommendation.new_features:
257
+ self.console.print(
258
+ f"[green]โœจ[/green] {len(recommendation.new_features)} new features detected"
259
+ )
260
+ elif recommendation.bug_fixes:
261
+ self.console.print(
262
+ f"[blue]๐Ÿ”ง[/blue] {len(recommendation.bug_fixes)} bug fixes detected"
263
+ )
264
+
265
+ def validate_auth(self) -> bool:
266
+ auth_methods = self._collect_auth_methods()
267
+ return self._report_auth_status(auth_methods)
268
+
269
+ def _collect_auth_methods(self) -> list[str]:
270
+ auth_methods: list[str] = []
271
+
272
+ env_auth = self._check_env_token_auth()
273
+ if env_auth:
274
+ auth_methods.append(env_auth)
275
+
276
+ keyring_auth = self._check_keyring_auth()
277
+ if keyring_auth:
278
+ auth_methods.append(keyring_auth)
279
+
280
+ return auth_methods
281
+
282
+ def _check_env_token_auth(self) -> str | None:
283
+ import os
284
+
285
+ token = os.getenv("UV_PUBLISH_TOKEN")
286
+ if not token:
287
+ return None
288
+
289
+ if self.security.validate_token_format(token, "pypi"):
290
+ masked_token = self.security.mask_tokens(token)
291
+ self.console.print(f"[dim]Token format: {masked_token}[/ dim]", style="dim")
292
+ return "Environment variable (UV_PUBLISH_TOKEN)"
293
+ self.console.print(
294
+ "[yellow]โš ๏ธ[/ yellow] UV_PUBLISH_TOKEN format appears invalid",
295
+ )
296
+ return None
297
+
298
+ def _check_keyring_auth(self) -> str | None:
299
+ try:
300
+ result = self._run_command(
301
+ [
302
+ "keyring",
303
+ "get",
304
+ "https://upload.pypi.org/legacy/",
305
+ "__token__",
306
+ ],
307
+ )
308
+ if result.returncode == 0 and result.stdout.strip():
309
+ keyring_token = result.stdout.strip()
310
+ if self.security.validate_token_format(keyring_token, "pypi"):
311
+ return "Keyring storage"
312
+ self.console.print(
313
+ "[yellow]โš ๏ธ[/ yellow] Keyring token format appears invalid",
314
+ )
315
+ except (subprocess.SubprocessError, OSError, FileNotFoundError):
316
+ pass
317
+ return None
318
+
319
+ def _report_auth_status(self, auth_methods: list[str]) -> bool:
320
+ if auth_methods:
321
+ self.console.print("[green]โœ…[/ green] PyPI authentication available: ")
322
+ for method in auth_methods:
323
+ self.console.print(f"-{method}")
324
+ return True
325
+ self._display_auth_setup_instructions()
326
+ return False
327
+
328
+ def _display_auth_setup_instructions(self) -> None:
329
+ self.console.print("[red]โŒ[/ red] No valid PyPI authentication found")
330
+ self.console.print("\n[yellow]๐Ÿ’ก[/ yellow] Setup options: ")
331
+ self.console.print(
332
+ " 1. Set environment variable: export UV_PUBLISH_TOKEN=<your-pypi-token>",
333
+ )
334
+ self.console.print(
335
+ " 2. Use keyring: keyring set[t.Any] https://upload.pypi.org/legacy/ __token__",
336
+ )
337
+ self.console.print(
338
+ " 3. Ensure token starts with 'pypi-' and is properly formatted",
339
+ )
340
+
341
+ def build_package(self) -> bool:
342
+ try:
343
+ self.console.print("[yellow]๐Ÿ”จ[/ yellow] Building package")
344
+
345
+ if self.dry_run:
346
+ return self._handle_dry_run_build()
347
+
348
+ return self._execute_build()
349
+ except Exception as e:
350
+ self.console.print(f"[red]โŒ[/ red] Build error: {e}")
351
+ return False
352
+
353
+ def _handle_dry_run_build(self) -> bool:
354
+ self.console.print("[yellow]๐Ÿ”[/ yellow] Would build package")
355
+ return True
356
+
357
+ def _clean_dist_directory(self) -> None:
358
+ dist_dir = self.pkg_path / "dist"
359
+ if not dist_dir.exists():
360
+ return
361
+
362
+ try:
363
+ import shutil
364
+
365
+ shutil.rmtree(dist_dir)
366
+ dist_dir.mkdir(exist_ok=True)
367
+ self.console.print(
368
+ "[cyan]๐Ÿงน[/ cyan] Cleaned dist directory for fresh build"
369
+ )
370
+ except Exception as e:
371
+ self.console.print(
372
+ f"[yellow]โš ๏ธ[/ yellow] Warning: Could not clean dist directory: {e}"
373
+ )
374
+
375
+ def _execute_build(self) -> bool:
376
+ self._clean_dist_directory()
377
+
378
+ result = self._run_command(["uv", "build"])
379
+
380
+ if result.returncode != 0:
381
+ self.console.print(f"[red]โŒ[/ red] Build failed: {result.stderr}")
382
+ return False
383
+
384
+ self.console.print("[green]โœ…[/ green] Package built successfully")
385
+ self._display_build_artifacts()
386
+ return True
387
+
388
+ def _display_build_artifacts(self) -> None:
389
+ dist_dir = self.pkg_path / "dist"
390
+ if not dist_dir.exists():
391
+ return
392
+
393
+ artifacts = list[t.Any](dist_dir.glob("*"))
394
+ self.console.print(f"[cyan]๐Ÿ“ฆ[/ cyan] Build artifacts ({len(artifacts)}): ")
395
+
396
+ for artifact in artifacts[-5:]:
397
+ size_str = self._format_file_size(artifact.stat().st_size)
398
+ self.console.print(f"-{artifact.name} ({size_str})")
399
+
400
+ def _format_file_size(self, size: int) -> str:
401
+ if size < 1024 * 1024:
402
+ return f"{size / 1024: .1f}KB"
403
+ return f"{size / (1024 * 1024): .1f}MB"
404
+
405
+ def publish_package(self) -> bool:
406
+ if not self._validate_prerequisites():
407
+ return False
408
+
409
+ try:
410
+ self.console.print("[yellow]๐Ÿš€[/ yellow] Publishing to PyPI")
411
+ return self._perform_publish_workflow_with_retry()
412
+ except Exception as e:
413
+ self.console.print(f"[red]โŒ[/ red] Publish error: {e}")
414
+ return False
415
+
416
+ def _validate_prerequisites(self) -> bool:
417
+ return self.validate_auth()
418
+
419
+ def _perform_publish_workflow(self) -> bool:
420
+ if self.dry_run:
421
+ return self._handle_dry_run_publish()
422
+
423
+ if not self.build_package():
424
+ return False
425
+
426
+ return self._execute_publish()
427
+
428
+ @retry_api_call(max_attempts=3, delay=2.0, backoff=2.0, max_delay=60.0)
429
+ def _perform_publish_workflow_with_retry(self) -> bool:
430
+ """Perform the publish workflow with retry logic for API connection errors."""
431
+ if self.dry_run:
432
+ return self._handle_dry_run_publish()
433
+
434
+ if not self.build_package():
435
+ return False
436
+
437
+ return self._execute_publish()
438
+
439
+ def _handle_dry_run_publish(self) -> bool:
440
+ self.console.print("[yellow]๐Ÿ”[/ yellow] Would publish package to PyPI")
441
+ return True
442
+
443
+ def _execute_publish(self) -> bool:
444
+ result = self._run_command(["uv", "publish"])
445
+
446
+ # Check for success indicators in output even if return code is non-zero
447
+ # UV can return non-zero codes for warnings while still succeeding
448
+ success_indicators = [
449
+ "Successfully uploaded",
450
+ "Package uploaded successfully",
451
+ "Upload successful",
452
+ "Successfully published",
453
+ ]
454
+
455
+ has_success_indicator = (
456
+ any(indicator in result.stdout for indicator in success_indicators)
457
+ if result.stdout
458
+ else False
459
+ )
460
+
461
+ # Consider it successful if either return code is 0 OR we find success indicators
462
+ success = result.returncode == 0 or has_success_indicator
463
+
464
+ if success:
465
+ self._handle_publish_success()
466
+ return True
467
+
468
+ self._handle_publish_failure(result.stderr)
469
+ return False
470
+
471
+ def _handle_publish_failure(self, error_msg: str) -> None:
472
+ self.console.print(f"[red]โŒ[/ red] Publish failed: {error_msg}")
473
+
474
+ def _handle_publish_success(self) -> None:
475
+ self.console.print("[green]๐ŸŽ‰[/ green] Package published successfully !")
476
+ self._display_package_url()
477
+
478
+ def _display_package_url(self) -> None:
479
+ current_version = self._get_current_version()
480
+ package_name = self._get_package_name()
481
+
482
+ if package_name and current_version:
483
+ url = f"https://pypi.org/project/{package_name}/{current_version}/"
484
+ self.console.print(f"[cyan]๐Ÿ”—[/ cyan] Package URL: {url}")
485
+
486
+ def _get_package_name(self) -> str | None:
487
+ pyproject_path = self.pkg_path / "pyproject.toml"
488
+
489
+ with suppress(Exception):
490
+ from tomllib import loads
491
+
492
+ content = self.filesystem.read_file(pyproject_path)
493
+ data = loads(content)
494
+ name = data.get("project", {}).get("name", "")
495
+ return name if isinstance(name, str) else None
496
+
497
+ return None
498
+
499
+ def cleanup_old_releases(self, keep_releases: int = 10) -> bool:
500
+ try:
501
+ self.console.print(
502
+ f"[yellow]๐Ÿงน[/ yellow] Cleaning up old releases (keeping {keep_releases})...",
503
+ )
504
+ if self.dry_run:
505
+ self.console.print(
506
+ "[yellow]๐Ÿ”[/ yellow] Would clean up old PyPI releases",
507
+ )
508
+ return True
509
+ pyproject_path = self.pkg_path / "pyproject.toml"
510
+ from tomllib import loads
511
+
512
+ content = self.filesystem.read_file(pyproject_path)
513
+ data = loads(content)
514
+ package_name = data.get("project", {}).get("name", "")
515
+ if not package_name:
516
+ self.console.print(
517
+ "[yellow]โš ๏ธ[/ yellow] Could not determine package name",
518
+ )
519
+ return False
520
+ self.console.print(
521
+ f"[cyan]๐Ÿ“ฆ[/ cyan] Would analyze releases for {package_name}",
522
+ )
523
+ self.console.print(
524
+ f"[cyan]๐Ÿ”ง[/ cyan] Would keep {keep_releases} most recent releases",
525
+ )
526
+
527
+ return True
528
+ except Exception as e:
529
+ self.console.print(f"[red]โŒ[/ red] Cleanup error: {e}")
530
+ return False
531
+
532
+ def create_git_tag_local(self, version: str) -> bool:
533
+ """Create git tag locally without pushing (for use with push_with_tags)."""
534
+ try:
535
+ if self.dry_run:
536
+ self.console.print(
537
+ f"[yellow]๐Ÿ”[/ yellow] Would create git tag: v{version}",
538
+ )
539
+ return True
540
+ result = self._run_command(["git", "tag", f"v{version}"])
541
+ if result.returncode == 0:
542
+ self.console.print(f"[green]๐Ÿท๏ธ[/ green] Created git tag: v{version}")
543
+ return True
544
+ self.console.print(
545
+ f"[red]โŒ[/ red] Failed to create tag: {result.stderr}",
546
+ )
547
+ return False
548
+ except Exception as e:
549
+ self.console.print(f"[red]โŒ[/ red] Tag creation error: {e}")
550
+ return False
551
+
552
+ def create_git_tag(self, version: str) -> bool:
553
+ """Create git tag and push it immediately (legacy method for standalone use)."""
554
+ try:
555
+ if self.dry_run:
556
+ self.console.print(
557
+ f"[yellow]๐Ÿ”[/ yellow] Would create git tag: v{version}",
558
+ )
559
+ return True
560
+ result = self._run_command(["git", "tag", f"v{version}"])
561
+ if result.returncode == 0:
562
+ self.console.print(f"[green]๐Ÿท๏ธ[/ green] Created git tag: v{version}")
563
+ push_result = self._run_command(
564
+ ["git", "push", "origin", f"v{version}"],
565
+ )
566
+ if push_result.returncode == 0:
567
+ self.console.print("[green]๐Ÿ“ค[/ green] Pushed tag to remote")
568
+ else:
569
+ self.console.print(
570
+ f"[yellow]โš ๏ธ[/ yellow] Tag created but push failed: {push_result.stderr}",
571
+ )
572
+
573
+ return True
574
+ self.console.print(
575
+ f"[red]โŒ[/ red] Failed to create tag: {result.stderr}",
576
+ )
577
+ return False
578
+ except Exception as e:
579
+ self.console.print(f"[red]โŒ[/ red] Tag creation error: {e}")
580
+ return False
581
+
582
+ def get_package_info(self) -> dict[str, t.Any]:
583
+ pyproject_path = self.pkg_path / "pyproject.toml"
584
+ if not pyproject_path.exists():
585
+ return {}
586
+ try:
587
+ from tomllib import loads
588
+
589
+ content = self.filesystem.read_file(pyproject_path)
590
+ data = loads(content)
591
+ project = data.get("project", {})
592
+
593
+ return {
594
+ "name": project.get("name", ""),
595
+ "version": project.get("version", ""),
596
+ "description": project.get("description", ""),
597
+ "authors": project.get("authors", []),
598
+ "dependencies": project.get("dependencies", []),
599
+ "python_requires": project.get("requires-python", ""),
600
+ }
601
+ except Exception as e:
602
+ self.console.print(f"[yellow]โš ๏ธ[/ yellow] Error reading package info: {e}")
603
+ return {}
604
+
605
+ def _update_changelog_for_version(self, old_version: str, new_version: str) -> None:
606
+ """Update changelog with entries from git commits since last version."""
607
+ try:
608
+ # Use injected changelog generator service
609
+ changelog_generator = self._changelog_generator
610
+
611
+ # Look for changelog file
612
+ changelog_path = self.pkg_path / "CHANGELOG.md"
613
+
614
+ # Generate changelog entries since last version
615
+ success = changelog_generator.generate_changelog_from_commits(
616
+ changelog_path=changelog_path,
617
+ version=new_version,
618
+ since_version=f"v{old_version}", # Assumes git tags are prefixed with 'v'
619
+ )
620
+
621
+ if success:
622
+ self.console.print(
623
+ f"[green]๐Ÿ“[/green] Updated changelog for version {new_version}"
624
+ )
625
+ else:
626
+ self.console.print(
627
+ "[yellow]โš ๏ธ[/yellow] Changelog update encountered issues"
628
+ )
629
+
630
+ except Exception as e:
631
+ self.console.print(f"[yellow]โš ๏ธ[/yellow] Failed to update changelog: {e}")