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,198 @@
1
+ """Generic configuration loading and validation service."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ import yaml
8
+ from loguru import logger
9
+ from pydantic import BaseModel, ValidationError
10
+
11
+
12
+ class ConfigService:
13
+ """Generic configuration loading and validation service."""
14
+
15
+ @staticmethod
16
+ def load_config(path: str | Path) -> dict[str, Any]:
17
+ """
18
+ Load configuration file based on extension.
19
+
20
+ Args:
21
+ path: Path to the configuration file
22
+
23
+ Returns:
24
+ Dictionary with configuration data
25
+
26
+ Raises:
27
+ ValueError: If file extension is not supported
28
+ FileNotFoundError: If the file doesn't exist
29
+ Exception: For other file loading errors
30
+ """
31
+ path = Path(path)
32
+
33
+ if not path.exists():
34
+ raise FileNotFoundError(f"Configuration file does not exist: {path}")
35
+
36
+ if path.suffix.lower() == ".json":
37
+ return ConfigService._load_json(path)
38
+ elif path.suffix.lower() in (".yml", ".yaml"):
39
+ return ConfigService._load_yaml(path)
40
+ elif path.suffix.lower() == ".toml":
41
+ return ConfigService._load_toml(path)
42
+ else:
43
+ raise ValueError(f"Unsupported config format: {path.suffix}")
44
+
45
+ @staticmethod
46
+ async def load_config_async(path: str | Path) -> dict[str, Any]:
47
+ """
48
+ Asynchronously load configuration file based on extension.
49
+
50
+ Args:
51
+ path: Path to the configuration file
52
+
53
+ Returns:
54
+ Dictionary with configuration data
55
+
56
+ Raises:
57
+ ValueError: If file extension is not supported
58
+ FileNotFoundError: If the file doesn't exist
59
+ Exception: For other file loading errors
60
+ """
61
+ from crackerjack.services.file_io_service import FileIOService
62
+
63
+ path = Path(path)
64
+
65
+ if not path.exists():
66
+ raise FileNotFoundError(f"Configuration file does not exist: {path}")
67
+
68
+ if path.suffix.lower() == ".json":
69
+ content = await FileIOService.read_text_file(path)
70
+ return json.loads(content)
71
+ elif path.suffix.lower() in (".yml", ".yaml"):
72
+ content = await FileIOService.read_text_file(path)
73
+ return yaml.safe_load(content)
74
+ elif path.suffix.lower() == ".toml":
75
+ content = await FileIOService.read_text_file(path)
76
+ import toml
77
+
78
+ return toml.loads(content)
79
+ else:
80
+ raise ValueError(f"Unsupported config format: {path.suffix}")
81
+
82
+ @staticmethod
83
+ def _load_json(path: Path) -> dict[str, Any]:
84
+ """Load JSON configuration."""
85
+ with path.open(encoding="utf-8") as f:
86
+ return json.load(f)
87
+
88
+ @staticmethod
89
+ def _load_yaml(path: Path) -> dict[str, Any]:
90
+ """Load YAML configuration."""
91
+ with path.open(encoding="utf-8") as f:
92
+ return yaml.safe_load(f)
93
+
94
+ @staticmethod
95
+ def _load_toml(path: Path) -> dict[str, Any]:
96
+ """Load TOML configuration."""
97
+ import toml
98
+
99
+ with path.open("r", encoding="utf-8") as f:
100
+ return toml.load(f)
101
+
102
+ @staticmethod
103
+ def validate_config(
104
+ config: dict[str, Any], model_class: type[BaseModel]
105
+ ) -> BaseModel:
106
+ """
107
+ Validate configuration against a Pydantic model.
108
+
109
+ Args:
110
+ config: Configuration dictionary to validate
111
+ model_class: Pydantic model class to validate against
112
+
113
+ Returns:
114
+ Validated Pydantic model instance
115
+
116
+ Raises:
117
+ ValidationError: If the configuration doesn't match the model
118
+ """
119
+ try:
120
+ return model_class.model_validate(config)
121
+ except ValidationError as e:
122
+ logger.error(f"Config validation failed: {e}")
123
+ raise
124
+
125
+ @staticmethod
126
+ def save_config(
127
+ config: dict[str, Any], path: str | Path, format: str | None = None
128
+ ) -> None:
129
+ """
130
+ Save configuration to file.
131
+
132
+ Args:
133
+ config: Configuration dictionary to save
134
+ path: Path to save the configuration to
135
+ format: Format to save as ('json', 'yaml', 'toml'). If None, inferred from path extension.
136
+ """
137
+ path = Path(path)
138
+ format = format or path.suffix.lower().lstrip(".")
139
+
140
+ # Create parent directories if they don't exist
141
+ path.parent.mkdir(parents=True, exist_ok=True)
142
+
143
+ if format == "json":
144
+ ConfigService._save_json(config, path)
145
+ elif format in ("yml", "yaml"):
146
+ ConfigService._save_yaml(config, path)
147
+ elif format == "toml":
148
+ ConfigService._save_toml(config, path)
149
+ else:
150
+ raise ValueError(f"Unsupported config format: {format}")
151
+
152
+ @staticmethod
153
+ def _save_json(config: dict[str, Any], path: Path) -> None:
154
+ """Save configuration as JSON."""
155
+ with path.open("w", encoding="utf-8") as f:
156
+ json.dump(config, f, indent=2, ensure_ascii=False)
157
+
158
+ @staticmethod
159
+ def _save_yaml(config: dict[str, Any], path: Path) -> None:
160
+ """Save configuration as YAML."""
161
+ with path.open("w", encoding="utf-8") as f:
162
+ yaml.dump(config, f, default_flow_style=False, allow_unicode=True)
163
+
164
+ @staticmethod
165
+ def _save_toml(config: dict[str, Any], path: Path) -> None:
166
+ """Save configuration as TOML."""
167
+ import toml
168
+
169
+ with path.open("w", encoding="utf-8") as f:
170
+ toml.dump(config, f)
171
+
172
+ @staticmethod
173
+ def merge_configs(
174
+ base_config: dict[str, Any], override_config: dict[str, Any]
175
+ ) -> dict[str, Any]:
176
+ """
177
+ Recursively merge two configuration dictionaries.
178
+
179
+ Args:
180
+ base_config: Base configuration
181
+ override_config: Configuration to merge on top of base
182
+
183
+ Returns:
184
+ Merged configuration dictionary
185
+ """
186
+ result = base_config.copy()
187
+
188
+ for key, value in override_config.items():
189
+ if (
190
+ key in result
191
+ and isinstance(result[key], dict)
192
+ and isinstance(value, dict)
193
+ ):
194
+ result[key] = ConfigService.merge_configs(result[key], value)
195
+ else:
196
+ result[key] = value
197
+
198
+ return result
@@ -0,0 +1,493 @@
1
+ import hashlib
2
+ import typing as t
3
+ from contextlib import suppress
4
+ from dataclasses import dataclass, field
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+
8
+ import tomli
9
+ import yaml
10
+ from acb.console import Console
11
+
12
+
13
+ @dataclass
14
+ class ConfigUpdateInfo:
15
+ config_type: str
16
+ current_version: str
17
+ latest_version: str
18
+ needs_update: bool
19
+ diff_preview: str = ""
20
+ last_updated: datetime | None = None
21
+
22
+
23
+ @dataclass
24
+ class ConfigVersion:
25
+ version: str
26
+ config_data: dict[str, t.Any]
27
+ dependencies: list[str] = field(default_factory=list)
28
+ description: str = ""
29
+
30
+
31
+ class ConfigTemplateService:
32
+ """Version-based configuration template management service."""
33
+
34
+ def __init__(self, console: Console, pkg_path: Path) -> None:
35
+ self.console = console
36
+ self.pkg_path = pkg_path
37
+ self.templates = self._load_config_templates()
38
+
39
+ def _load_config_templates(self) -> dict[str, ConfigVersion]:
40
+ """Load configuration templates as structured data."""
41
+ return {
42
+ "pre-commit": self._create_precommit_template(),
43
+ "pyproject": self._create_pyproject_template(),
44
+ }
45
+
46
+ def _create_precommit_template(self) -> ConfigVersion:
47
+ """Create pre-commit configuration template."""
48
+ return ConfigVersion(
49
+ version="3.0.0",
50
+ description="Pre-commit hooks configuration with modern tools",
51
+ config_data={"repos": self._build_precommit_repos()},
52
+ )
53
+
54
+ def _create_pyproject_template(self) -> ConfigVersion:
55
+ """Create pyproject.toml configuration template."""
56
+ return ConfigVersion(
57
+ version="1.2.0",
58
+ description="Modern Python project configuration with Ruff and pytest",
59
+ config_data={"tool": self._build_pyproject_tools()},
60
+ )
61
+
62
+ def _build_precommit_repos(self) -> list[dict[str, t.Any]]:
63
+ """Build pre-commit repository configurations."""
64
+ return [
65
+ {
66
+ "repo": "local",
67
+ "hooks": self._build_local_precommit_hooks(),
68
+ },
69
+ {
70
+ "repo": "https://github.com/pre-commit/pre-commit-hooks",
71
+ "rev": "v6.0.0",
72
+ "hooks": self._build_standard_precommit_hooks(),
73
+ },
74
+ ]
75
+
76
+ def _build_local_precommit_hooks(self) -> list[dict[str, t.Any]]:
77
+ """Build local pre-commit hook configurations."""
78
+ return [
79
+ {
80
+ "id": "validate-regex-patterns",
81
+ "name": "validate-regex-patterns",
82
+ "entry": "uv run python -m crackerjack.tools.validate_regex_patterns",
83
+ "language": "system",
84
+ "files": r"\.py$",
85
+ "exclude": r"^\.venv/",
86
+ },
87
+ {
88
+ "id": "skylos",
89
+ "name": "skylos-dead-code-detection",
90
+ "entry": "skylos",
91
+ "language": "system",
92
+ "args": ["crackerjack"],
93
+ "pass_filenames": False,
94
+ "stages": ["pre-push", "manual"],
95
+ },
96
+ {
97
+ "id": "zuban",
98
+ "name": "zuban-type-checking",
99
+ "entry": "uv run zuban check",
100
+ "language": "system",
101
+ "args": ["--config-file", "mypy.ini", "./crackerjack"],
102
+ "pass_filenames": False,
103
+ "exclude": r"^tests/|^src/",
104
+ "stages": ["pre-push", "manual"],
105
+ },
106
+ ]
107
+
108
+ def _build_standard_precommit_hooks(self) -> list[dict[str, t.Any]]:
109
+ """Build standard pre-commit hook configurations."""
110
+ exclude_pattern = r"^\.venv/"
111
+ return [
112
+ {
113
+ "id": "trailing-whitespace",
114
+ "name": "trailing-whitespace",
115
+ "exclude": exclude_pattern,
116
+ },
117
+ {
118
+ "id": "end-of-file-fixer",
119
+ "name": "end-of-file-fixer",
120
+ "exclude": exclude_pattern,
121
+ },
122
+ {"id": "check-yaml", "name": "check-yaml", "exclude": exclude_pattern},
123
+ {"id": "check-toml", "name": "check-toml", "exclude": exclude_pattern},
124
+ {
125
+ "id": "check-added-large-files",
126
+ "name": "check-added-large-files",
127
+ "exclude": exclude_pattern,
128
+ },
129
+ ]
130
+
131
+ def _build_pyproject_tools(self) -> dict[str, t.Any]:
132
+ """Build pyproject.toml tool configurations."""
133
+ return {
134
+ "ruff": self._build_ruff_config(),
135
+ "pytest": self._build_pytest_config(),
136
+ }
137
+
138
+ def _build_ruff_config(self) -> dict[str, t.Any]:
139
+ """Build Ruff configuration."""
140
+ return {
141
+ "target-version": "py313",
142
+ "line-length": 88,
143
+ "fix": True,
144
+ "unsafe-fixes": True,
145
+ "show-fixes": True,
146
+ "output-format": "full",
147
+ "format": {"docstring-code-format": True},
148
+ "lint": {
149
+ "extend-select": ["C901", "F", "I", "UP"],
150
+ "ignore": ["E402", "F821"],
151
+ "fixable": ["ALL"],
152
+ },
153
+ }
154
+
155
+ def _build_pytest_config(self) -> dict[str, t.Any]:
156
+ """Build pytest configuration."""
157
+ return {
158
+ "ini_options": {
159
+ "asyncio_mode": "auto",
160
+ "timeout": 300,
161
+ "addopts": "--cov=crackerjack --cov-report=term-missing:skip-covered",
162
+ "testpaths": ["tests"],
163
+ "markers": [
164
+ "unit: marks test as a unit test",
165
+ "integration: marks test as an integration test",
166
+ "no_leaks: detect asyncio task leaks",
167
+ ],
168
+ },
169
+ }
170
+
171
+ def get_template(
172
+ self, config_type: str, version: str | None = None
173
+ ) -> ConfigVersion | None:
174
+ """Get configuration template by type and optional version."""
175
+ if config_type not in self.templates:
176
+ return None
177
+
178
+ template = self.templates[config_type]
179
+ if version and template.version != version:
180
+ return None
181
+
182
+ return template
183
+
184
+ def check_updates(self, project_path: Path) -> dict[str, ConfigUpdateInfo]:
185
+ """Check if newer configuration versions are available."""
186
+ updates = {}
187
+
188
+ version_file = project_path / ".crackerjack-config.yaml"
189
+ current_versions = self._load_current_versions(version_file)
190
+
191
+ for config_type, template in self.templates.items():
192
+ current_version = current_versions.get(config_type, "0.0.0")
193
+ needs_update = self._version_compare(current_version, template.version) < 0
194
+
195
+ update_info = ConfigUpdateInfo(
196
+ config_type=config_type,
197
+ current_version=current_version,
198
+ latest_version=template.version,
199
+ needs_update=needs_update,
200
+ )
201
+
202
+ if needs_update:
203
+ update_info.diff_preview = self._generate_diff_preview(
204
+ config_type, project_path
205
+ )
206
+
207
+ updates[config_type] = update_info
208
+
209
+ return updates
210
+
211
+ def _load_current_versions(self, version_file: Path) -> dict[str, str]:
212
+ """Load current configuration versions from tracking file."""
213
+ if not version_file.exists():
214
+ return {}
215
+
216
+ try:
217
+ with version_file.open() as f:
218
+ data = yaml.safe_load(f)
219
+ if not isinstance(data, dict):
220
+ return {}
221
+ configs = data.get("configs", {})
222
+ if not isinstance(configs, dict):
223
+ return {}
224
+ return {
225
+ name: config.get("version", "0.0.0")
226
+ for name, config in configs.items()
227
+ if isinstance(config, dict)
228
+ }
229
+ except Exception:
230
+ return {}
231
+
232
+ def _version_compare(self, version1: str, version2: str) -> int:
233
+ """Compare two semantic versions. Returns -1, 0, or 1."""
234
+
235
+ def version_tuple(v: str) -> tuple[int, ...]:
236
+ return tuple(int(x) for x in v.split("."))
237
+
238
+ v1_tuple = version_tuple(version1)
239
+ v2_tuple = version_tuple(version2)
240
+
241
+ if v1_tuple < v2_tuple:
242
+ return -1
243
+ elif v1_tuple > v2_tuple:
244
+ return 1
245
+ return 0
246
+
247
+ def _generate_diff_preview(self, config_type: str, project_path: Path) -> str:
248
+ """Generate a preview of changes that would be made."""
249
+ if config_type == "pre-commit":
250
+ config_file = project_path / ".pre-commit-config.yaml"
251
+ elif config_type == "pyproject":
252
+ config_file = project_path / "pyproject.toml"
253
+ else:
254
+ return "Diff preview not available for this config type"
255
+
256
+ if not config_file.exists():
257
+ return f"Would create new {config_file.name} file"
258
+
259
+ try:
260
+ with config_file.open() as f:
261
+ if config_type == "pre-commit":
262
+ current_config = yaml.safe_load(f)
263
+ else:
264
+ content = f.read()
265
+ current_config = tomli.loads(content)
266
+
267
+ template = self.get_template(config_type)
268
+ if not template:
269
+ return "Template not found"
270
+
271
+ return self._create_config_diff(current_config, template.config_data)
272
+ except Exception as e:
273
+ return f"Error generating diff preview: {e}"
274
+
275
+ def _create_config_diff(
276
+ self, current: dict[str, t.Any], new: dict[str, t.Any]
277
+ ) -> str:
278
+ """Create a simple diff between two configurations."""
279
+ changes: list[str] = []
280
+ self._collect_config_changes(current, new, changes)
281
+
282
+ if not changes:
283
+ return "No changes detected"
284
+
285
+ return "\n".join(changes[:10]) # Limit to first 10 changes
286
+
287
+ def _collect_config_changes(
288
+ self,
289
+ current: dict[str, t.Any],
290
+ new: dict[str, t.Any],
291
+ changes: list[str],
292
+ path: str = "",
293
+ ) -> None:
294
+ """Collect configuration changes recursively."""
295
+ self._collect_additions_and_modifications(current, new, changes, path)
296
+ self._collect_removals(current, new, changes, path)
297
+
298
+ def _collect_additions_and_modifications(
299
+ self,
300
+ current: dict[str, t.Any],
301
+ new: dict[str, t.Any],
302
+ changes: list[str],
303
+ path: str,
304
+ ) -> None:
305
+ """Collect additions and modifications in configuration."""
306
+ for key, value in new.items():
307
+ key_path = f"{path}.{key}" if path else key
308
+
309
+ if key not in current:
310
+ changes.append(f"+ Add {key_path}: {value}")
311
+ elif self._is_nested_dict(value, current[key]):
312
+ self._collect_config_changes(current[key], value, changes, key_path)
313
+ elif current[key] != value:
314
+ changes.append(f"~ Change {key_path}: {current[key]} → {value}")
315
+
316
+ def _collect_removals(
317
+ self,
318
+ current: dict[str, t.Any],
319
+ new: dict[str, t.Any],
320
+ changes: list[str],
321
+ path: str,
322
+ ) -> None:
323
+ """Collect removals in configuration."""
324
+ for key in current:
325
+ if key not in new:
326
+ key_path = f"{path}.{key}" if path else key
327
+ changes.append(f"- Remove {key_path}")
328
+
329
+ def _is_nested_dict(self, new_value: t.Any, current_value: t.Any) -> bool:
330
+ """Check if both values are dictionaries for nested comparison."""
331
+ return isinstance(new_value, dict) and isinstance(current_value, dict)
332
+
333
+ def apply_update(
334
+ self,
335
+ config_type: str,
336
+ project_path: Path,
337
+ interactive: bool = False,
338
+ ) -> bool:
339
+ """Apply configuration update to project."""
340
+ template = self.get_template(config_type)
341
+ if not template:
342
+ self.console.print(f"[red]❌[/red] Template not found: {config_type}")
343
+ return False
344
+
345
+ try:
346
+ if config_type == "pre-commit":
347
+ return self._apply_precommit_update(template, project_path, interactive)
348
+ elif config_type == "pyproject":
349
+ return self._apply_pyproject_update(template, project_path, interactive)
350
+ else:
351
+ self.console.print(
352
+ f"[yellow]⚠️[/yellow] Unsupported config type: {config_type}"
353
+ )
354
+ return False
355
+ except Exception as e:
356
+ self.console.print(f"[red]❌[/red] Failed to apply update: {e}")
357
+ return False
358
+
359
+ def _apply_precommit_update(
360
+ self, template: ConfigVersion, project_path: Path, interactive: bool
361
+ ) -> bool:
362
+ """Apply pre-commit configuration update."""
363
+ config_file = project_path / ".pre-commit-config.yaml"
364
+
365
+ if interactive and config_file.exists():
366
+ self.console.print(f"\n[bold cyan]Updating {config_file.name}[/bold cyan]")
367
+ diff = self._generate_diff_preview("pre-commit", project_path)
368
+ self.console.print(f"Changes:\n{diff}")
369
+
370
+ if not self._confirm_update():
371
+ return False
372
+
373
+ try:
374
+ with config_file.open("w") as f:
375
+ yaml.dump(
376
+ template.config_data, f, default_flow_style=False, sort_keys=False
377
+ )
378
+
379
+ self._update_version_tracking(project_path, "pre-commit", template.version)
380
+ self._invalidate_cache(project_path)
381
+
382
+ self.console.print(
383
+ f"[green]✅[/green] Updated {config_file.name} to version {template.version}"
384
+ )
385
+ return True
386
+ except Exception as e:
387
+ self.console.print(f"[red]❌[/red] Failed to write config: {e}")
388
+ return False
389
+
390
+ def _apply_pyproject_update(
391
+ self, template: ConfigVersion, project_path: Path, interactive: bool
392
+ ) -> bool:
393
+ """Apply pyproject.toml configuration update."""
394
+ config_file = project_path / "pyproject.toml"
395
+
396
+ if not config_file.exists():
397
+ self.console.print(
398
+ f"[yellow]⚠️[/yellow] pyproject.toml not found at {project_path}"
399
+ )
400
+ return False
401
+
402
+ if interactive:
403
+ self.console.print(f"\n[bold cyan]Updating {config_file.name}[/bold cyan]")
404
+ diff = self._generate_diff_preview("pyproject", project_path)
405
+ self.console.print(f"Changes:\n{diff}")
406
+
407
+ if not self._confirm_update():
408
+ return False
409
+
410
+ try:
411
+ # Read existing config
412
+ with config_file.open() as f:
413
+ content = f.read()
414
+ existing_config = tomli.loads(content)
415
+
416
+ # Merge tool sections from template
417
+ if "tool" not in existing_config:
418
+ existing_config["tool"] = {}
419
+
420
+ for tool_name, tool_config in template.config_data.get("tool", {}).items():
421
+ existing_config["tool"][tool_name] = tool_config
422
+
423
+ # Write back using tomli_w
424
+ from tomli_w import dumps
425
+
426
+ updated_content = dumps(existing_config)
427
+
428
+ with config_file.open("w") as f:
429
+ f.write(updated_content)
430
+
431
+ self._update_version_tracking(project_path, "pyproject", template.version)
432
+
433
+ self.console.print(
434
+ f"[green]✅[/green] Updated {config_file.name} to version {template.version}"
435
+ )
436
+ return True
437
+ except Exception as e:
438
+ self.console.print(f"[red]❌[/red] Failed to update pyproject.toml: {e}")
439
+ return False
440
+
441
+ def _confirm_update(self) -> bool:
442
+ """Ask user to confirm update."""
443
+ try:
444
+ response = input("\nApply this update? [y/N]: ").strip().lower()
445
+ return response in ("y", "yes")
446
+ except (EOFError, KeyboardInterrupt):
447
+ return False
448
+
449
+ def _update_version_tracking(
450
+ self, project_path: Path, config_type: str, version: str
451
+ ) -> None:
452
+ """Update version tracking file."""
453
+ version_file = project_path / ".crackerjack-config.yaml"
454
+
455
+ data: dict[str, t.Any] = {"version": "1.0.0", "configs": {}}
456
+ if version_file.exists():
457
+ with suppress(Exception):
458
+ with version_file.open() as f:
459
+ existing_data = yaml.safe_load(f)
460
+ if isinstance(existing_data, dict):
461
+ data = existing_data
462
+
463
+ if "configs" not in data:
464
+ data["configs"] = {}
465
+
466
+ data["configs"][config_type] = {
467
+ "version": version,
468
+ "last_updated": datetime.now().isoformat(),
469
+ }
470
+
471
+ with suppress(Exception):
472
+ with version_file.open("w") as f:
473
+ yaml.dump(data, f, default_flow_style=False, sort_keys=False)
474
+
475
+ def _invalidate_cache(self, project_path: Path) -> None:
476
+ """Invalidate cache to ensure fresh environment."""
477
+ # No-op in the new system - cache invalidation handled differently
478
+ pass
479
+
480
+ def get_config_hash(self, config_path: Path) -> str:
481
+ """Generate hash of configuration file for cache invalidation."""
482
+ if not config_path.exists():
483
+ return ""
484
+
485
+ try:
486
+ content = config_path.read_text()
487
+ return hashlib.sha256(content.encode()).hexdigest()[:16]
488
+ except Exception:
489
+ return ""
490
+
491
+ def list_available_templates(self) -> dict[str, str]:
492
+ """List all available configuration templates."""
493
+ return {name: template.description for name, template in self.templates.items()}