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,132 @@
1
+ import tomllib
2
+ import typing as t
3
+ from pathlib import Path
4
+
5
+ from acb.console import Console
6
+ from acb.depends import Inject, depends
7
+
8
+ from crackerjack.exceptions.config import ConfigIntegrityError
9
+ from crackerjack.models.protocols import ConfigIntegrityServiceProtocol, ServiceProtocol
10
+
11
+
12
+ class ConfigIntegrityService(ConfigIntegrityServiceProtocol, ServiceProtocol):
13
+ @depends.inject
14
+ def __init__(self, console: Inject[Console], project_path: Path) -> None:
15
+ self.console = console
16
+ self.project_path = project_path
17
+ self.cache_dir = Path.home() / ".cache" / "crackerjack"
18
+ self.cache_dir.mkdir(parents=True, exist_ok=True)
19
+
20
+ def initialize(self) -> None:
21
+ pass
22
+
23
+ def cleanup(self) -> None:
24
+ pass
25
+
26
+ def health_check(self) -> bool:
27
+ return True
28
+
29
+ def shutdown(self) -> None:
30
+ pass
31
+
32
+ def metrics(self) -> dict[str, t.Any]:
33
+ return {}
34
+
35
+ def is_healthy(self) -> bool:
36
+ return True
37
+
38
+ def register_resource(self, resource: t.Any) -> None:
39
+ pass
40
+
41
+ def cleanup_resource(self, resource: t.Any) -> None:
42
+ pass
43
+
44
+ def record_error(self, error: Exception) -> None:
45
+ pass
46
+
47
+ def increment_requests(self) -> None:
48
+ pass
49
+
50
+ def get_custom_metric(self, name: str) -> t.Any:
51
+ return None
52
+
53
+ def set_custom_metric(self, name: str, value: t.Any) -> None:
54
+ pass
55
+
56
+ def check_config_integrity(self) -> bool:
57
+ config_files = [
58
+ ".pre-commit-config.yaml",
59
+ "pyproject.toml",
60
+ ]
61
+
62
+ drift_detected = False
63
+
64
+ for file_name in config_files:
65
+ file_path = self.project_path / file_name
66
+ if file_path.exists():
67
+ try:
68
+ if self._check_file_drift(file_path):
69
+ drift_detected = True
70
+ except ConfigIntegrityError as e:
71
+ self.console.print(
72
+ f"[red]❌ Error checking {file_path.name}: {e}[/ red]"
73
+ )
74
+ drift_detected = True
75
+
76
+ try:
77
+ if not self._has_required_config_sections():
78
+ drift_detected = True
79
+ except ConfigIntegrityError as e:
80
+ self.console.print(f"[red]❌ Configuration integrity error: {e}[/ red]")
81
+ drift_detected = True
82
+
83
+ return drift_detected
84
+
85
+ def _check_file_drift(self, file_path: Path) -> bool:
86
+ cache_file = self.cache_dir / f"{file_path.name}.hash"
87
+
88
+ try:
89
+ current_content = file_path.read_text()
90
+ current_hash = hash(current_content)
91
+
92
+ if cache_file.exists():
93
+ cached_hash = int(cache_file.read_text().strip())
94
+ if current_hash != cached_hash:
95
+ self.console.print(
96
+ f"[yellow]⚠️ {file_path.name} has been modified manually[/ yellow]",
97
+ )
98
+ return True
99
+
100
+ cache_file.write_text(str(current_hash))
101
+ return False
102
+
103
+ except OSError as e:
104
+ raise ConfigIntegrityError(
105
+ f"Failed to check file drift for {file_path.name}: {e}"
106
+ ) from e
107
+
108
+ def _has_required_config_sections(self) -> bool:
109
+ pyproject = self.project_path / "pyproject.toml"
110
+ if not pyproject.exists():
111
+ raise ConfigIntegrityError("pyproject.toml not found.")
112
+
113
+ try:
114
+ with pyproject.open("rb") as f:
115
+ config = tomllib.load(f)
116
+ except Exception as e:
117
+ raise ConfigIntegrityError(f"Error parsing pyproject.toml: {e}") from e
118
+
119
+ required = ["tool.ruff", "tool.pyright", "tool.pytest.ini_options"]
120
+
121
+ for section in required:
122
+ keys = section.split(".")
123
+ current = config
124
+
125
+ for key in keys:
126
+ if key not in current:
127
+ raise ConfigIntegrityError(
128
+ f"Missing required config section: {section} in pyproject.toml"
129
+ )
130
+ current = current[key]
131
+
132
+ return True
@@ -0,0 +1,546 @@
1
+ import copy
2
+ import io
3
+ import typing as t
4
+ from pathlib import Path
5
+
6
+ import tomli
7
+ import tomli_w
8
+ import yaml
9
+ from acb.console import Console
10
+ from acb.depends import Inject, depends
11
+ from acb.logger import Logger
12
+
13
+ from crackerjack.models.protocols import (
14
+ ConfigMergeServiceProtocol,
15
+ FileSystemInterface,
16
+ GitInterface,
17
+ )
18
+
19
+
20
+ class ConfigMergeService(ConfigMergeServiceProtocol):
21
+ @depends.inject
22
+ def __init__(
23
+ self,
24
+ console: Inject[Console],
25
+ filesystem: Inject[FileSystemInterface],
26
+ git_service: Inject[GitInterface],
27
+ logger: Inject[Logger],
28
+ ) -> None:
29
+ self.console = console
30
+ self.filesystem = filesystem
31
+ self.git_service = git_service
32
+ self.logger = logger
33
+
34
+ def smart_merge_pyproject(
35
+ self,
36
+ source_content: dict[str, t.Any],
37
+ target_path: str | t.Any,
38
+ project_name: str,
39
+ ) -> dict[str, t.Any]:
40
+ target_path = Path(target_path)
41
+
42
+ if not target_path.exists():
43
+ return t.cast(
44
+ dict[str, t.Any],
45
+ self._replace_project_name_in_config_value(
46
+ source_content, project_name
47
+ ),
48
+ )
49
+
50
+ with target_path.open("rb") as f:
51
+ target_content = tomli.load(f)
52
+
53
+ self._ensure_crackerjack_dev_dependency(target_content, source_content)
54
+
55
+ self._merge_tool_configurations(target_content, source_content, project_name)
56
+
57
+ self._remove_fixed_coverage_requirements(target_content)
58
+
59
+ self.logger.info("Smart merged pyproject.toml", project_name=project_name)
60
+ return target_content
61
+
62
+ def smart_merge_pre_commit_config(
63
+ self,
64
+ source_content: dict[str, t.Any],
65
+ target_path: str | t.Any,
66
+ project_name: str,
67
+ ) -> dict[str, t.Any]:
68
+ target_path = Path(target_path)
69
+
70
+ if not target_path.exists():
71
+ # Process source content for project-specific references
72
+ processed_source = copy.deepcopy(source_content)
73
+ source_repos = processed_source.get("repos", [])
74
+ processed_source["repos"] = self._process_pre_commit_repos_for_project(
75
+ source_repos, project_name
76
+ )
77
+ return processed_source
78
+
79
+ with target_path.open() as f:
80
+ loaded_config = yaml.safe_load(f)
81
+ target_content: dict[str, t.Any] = (
82
+ loaded_config if isinstance(loaded_config, dict) else {}
83
+ )
84
+
85
+ if not isinstance(target_content, dict):
86
+ self.logger.warning(
87
+ f"Target config is not a dictionary, using source: {type(target_content)}"
88
+ )
89
+ return source_content
90
+
91
+ source_repos = source_content.get("repos", [])
92
+ target_repos = target_content.get("repos", [])
93
+
94
+ if not isinstance(target_repos, list):
95
+ target_repos = []
96
+
97
+ existing_repo_urls = {
98
+ repo.get("repo", "") for repo in target_repos if isinstance(repo, dict)
99
+ }
100
+
101
+ new_repos = [
102
+ repo
103
+ for repo in source_repos
104
+ if isinstance(repo, dict) and repo.get("repo", "") not in existing_repo_urls
105
+ ]
106
+
107
+ if new_repos:
108
+ # Replace project-specific references in new repos before adding them
109
+ processed_new_repos = self._process_pre_commit_repos_for_project(
110
+ new_repos, project_name
111
+ )
112
+ target_repos.extend(processed_new_repos)
113
+ target_content["repos"] = target_repos
114
+ self.logger.info(
115
+ "Merged .pre-commit-config.yaml",
116
+ new_repos_count=len(new_repos),
117
+ project_name=project_name,
118
+ )
119
+
120
+ return target_content
121
+
122
+ def smart_append_file(
123
+ self,
124
+ source_content: str,
125
+ target_path: str | t.Any,
126
+ start_marker: str,
127
+ end_marker: str,
128
+ force: bool = False,
129
+ ) -> str:
130
+ target_path = Path(target_path)
131
+
132
+ if not target_path.exists():
133
+ return f"{start_marker}\n{source_content.strip()}\n{end_marker}\n"
134
+
135
+ existing_content = target_path.read_text()
136
+
137
+ if start_marker in existing_content:
138
+ if force:
139
+ start_idx = existing_content.find(start_marker)
140
+ end_idx = existing_content.find(end_marker)
141
+ if end_idx != -1:
142
+ end_idx += len(end_marker)
143
+ existing_content = (
144
+ existing_content[:start_idx] + existing_content[end_idx:]
145
+ ).strip()
146
+ else:
147
+ return existing_content
148
+
149
+ merged_content = existing_content.strip() + "\n\n" + start_marker + "\n"
150
+ merged_content += source_content.strip() + "\n"
151
+ merged_content += end_marker + "\n"
152
+
153
+ self.logger.info("Smart appended file with markers", path=str(target_path))
154
+ return merged_content
155
+
156
+ def smart_merge_gitignore(
157
+ self,
158
+ patterns: list[str],
159
+ target_path: str | t.Any,
160
+ ) -> str:
161
+ target_path = Path(target_path)
162
+
163
+ if not target_path.exists():
164
+ return self._create_new_gitignore(target_path, patterns)
165
+
166
+ lines = target_path.read_text().splitlines()
167
+
168
+ parsed_content = self._parse_existing_gitignore_content(lines)
169
+
170
+ merged_content = self._build_merged_gitignore_content(parsed_content, patterns)
171
+
172
+ target_path.write_text(merged_content)
173
+ new_patterns_count = len(
174
+ [p for p in patterns if p not in parsed_content.existing_patterns]
175
+ )
176
+ all_patterns_count = len(parsed_content.existing_patterns) + new_patterns_count
177
+
178
+ self.logger.info(
179
+ "Smart merged .gitignore (cleaned duplicates)",
180
+ new_patterns_count=new_patterns_count,
181
+ total_crackerjack_patterns=all_patterns_count,
182
+ )
183
+ return merged_content
184
+
185
+ def _create_new_gitignore(self, target_path: Path, patterns: list[str]) -> str:
186
+ merged_content = "# Crackerjack patterns\n"
187
+ for pattern in patterns:
188
+ merged_content += f"{pattern}\n"
189
+ target_path.write_text(merged_content)
190
+ self.logger.info("Created .gitignore", new_patterns_count=len(patterns))
191
+ return merged_content
192
+
193
+ def _parse_existing_gitignore_content(self, lines: list[str]) -> t.Any:
194
+ class ParsedContent:
195
+ def __init__(self) -> None:
196
+ self.cleaned_lines: list[str] = []
197
+ self.existing_patterns: set[str] = set()
198
+
199
+ parsed = ParsedContent()
200
+ parser_state = self._init_parser_state()
201
+
202
+ for line in lines:
203
+ parser_state = self._process_gitignore_line(line, parsed, parser_state)
204
+
205
+ return parsed
206
+
207
+ def _init_parser_state(self) -> dict[str, bool]:
208
+ return {
209
+ "inside_crackerjack_section": False,
210
+ "skip_empty_after_crackerjack": False,
211
+ }
212
+
213
+ def _process_gitignore_line(
214
+ self, line: str, parsed: t.Any, state: dict[str, bool]
215
+ ) -> dict[str, bool]:
216
+ stripped = line.strip()
217
+
218
+ if self._is_crackerjack_header(stripped):
219
+ return self._handle_crackerjack_header(state)
220
+
221
+ if self._should_skip_empty_line(stripped, state):
222
+ state["skip_empty_after_crackerjack"] = False
223
+ return state
224
+
225
+ state["skip_empty_after_crackerjack"] = False
226
+
227
+ self._collect_pattern_if_present(stripped, parsed, state)
228
+ self._add_line_if_non_crackerjack(line, parsed, state)
229
+
230
+ return state
231
+
232
+ def _handle_crackerjack_header(self, state: dict[str, bool]) -> dict[str, bool]:
233
+ if not state["inside_crackerjack_section"]:
234
+ state["inside_crackerjack_section"] = True
235
+ state["skip_empty_after_crackerjack"] = True
236
+ return state
237
+
238
+ def _should_skip_empty_line(self, stripped: str, state: dict[str, bool]) -> bool:
239
+ return state["skip_empty_after_crackerjack"] and not stripped
240
+
241
+ def _collect_pattern_if_present(
242
+ self, stripped: str, parsed: t.Any, state: dict[str, bool]
243
+ ) -> None:
244
+ if stripped and not stripped.startswith("#"):
245
+ parsed.existing_patterns.add(stripped)
246
+
247
+ def _add_line_if_non_crackerjack(
248
+ self, line: str, parsed: t.Any, state: dict[str, bool]
249
+ ) -> None:
250
+ if not state["inside_crackerjack_section"]:
251
+ parsed.cleaned_lines.append(line)
252
+
253
+ def _is_crackerjack_header(self, line: str) -> bool:
254
+ return line in ("# Crackerjack patterns", "# Crackerjack generated files")
255
+
256
+ def _build_merged_gitignore_content(
257
+ self, parsed_content: t.Any, new_patterns: list[str]
258
+ ) -> str:
259
+ if parsed_content.cleaned_lines and not parsed_content.cleaned_lines[-1]:
260
+ parsed_content.cleaned_lines.pop()
261
+
262
+ merged_content = "\n".join(parsed_content.cleaned_lines)
263
+ if merged_content:
264
+ merged_content += "\n"
265
+
266
+ all_crackerjack_patterns = self._get_consolidated_patterns(
267
+ parsed_content.existing_patterns, new_patterns
268
+ )
269
+
270
+ if all_crackerjack_patterns:
271
+ merged_content += "\n# Crackerjack patterns\n"
272
+ for pattern in sorted(all_crackerjack_patterns):
273
+ merged_content += f"{pattern}\n"
274
+
275
+ return merged_content
276
+
277
+ def _get_consolidated_patterns(
278
+ self, existing_patterns: set[str], new_patterns: list[str]
279
+ ) -> list[str]:
280
+ new_patterns_to_add = [p for p in new_patterns if p not in existing_patterns]
281
+ return list[t.Any](existing_patterns) + new_patterns_to_add
282
+
283
+ def write_pyproject_config(
284
+ self,
285
+ config: dict[str, t.Any],
286
+ target_path: str | t.Any,
287
+ ) -> None:
288
+ target_path = Path(target_path)
289
+
290
+ buffer = io.BytesIO()
291
+ tomli_w.dump(config, buffer)
292
+ content = buffer.getvalue().decode("utf-8")
293
+
294
+ from crackerjack.services.filesystem import FileSystemService
295
+
296
+ content = FileSystemService.clean_trailing_whitespace_and_newlines(content)
297
+
298
+ with target_path.open("w", encoding="utf-8") as f:
299
+ f.write(content)
300
+
301
+ self.logger.debug("Wrote pyproject.toml config", path=str(target_path))
302
+
303
+ def write_pre_commit_config(
304
+ self,
305
+ config: dict[str, t.Any],
306
+ target_path: str | t.Any,
307
+ ) -> None:
308
+ target_path = Path(target_path)
309
+
310
+ yaml_content = yaml.dump(
311
+ config,
312
+ default_flow_style=False,
313
+ sort_keys=False,
314
+ width=float("inf"),
315
+ )
316
+ content = yaml_content
317
+
318
+ from crackerjack.services.filesystem import FileSystemService
319
+
320
+ content = FileSystemService.clean_trailing_whitespace_and_newlines(content)
321
+
322
+ with target_path.open("w") as f:
323
+ f.write(content)
324
+
325
+ self.logger.debug("Wrote .pre-commit-config.yaml", path=str(target_path))
326
+
327
+ def _ensure_crackerjack_dev_dependency(
328
+ self,
329
+ target_config: dict[str, t.Any],
330
+ source_config: dict[str, t.Any],
331
+ ) -> None:
332
+ if "dependency-groups" not in target_config:
333
+ target_config["dependency-groups"] = {}
334
+
335
+ if "dev" not in target_config["dependency-groups"]:
336
+ target_config["dependency-groups"]["dev"] = []
337
+
338
+ dev_deps = target_config["dependency-groups"]["dev"]
339
+ if "crackerjack" not in str(dev_deps):
340
+ dev_deps.append("crackerjack")
341
+ self.logger.debug("Added crackerjack to dev dependencies")
342
+
343
+ def _merge_tool_configurations(
344
+ self,
345
+ target_config: dict[str, t.Any],
346
+ source_config: dict[str, t.Any],
347
+ project_name: str,
348
+ ) -> None:
349
+ source_tools = source_config.get("tool", {})
350
+
351
+ if "tool" not in target_config:
352
+ target_config["tool"] = {}
353
+
354
+ target_tools = target_config["tool"]
355
+
356
+ tools_to_merge = [
357
+ "ruff",
358
+ "pyright",
359
+ "bandit",
360
+ "vulture",
361
+ "refurb",
362
+ "complexipy",
363
+ "codespell",
364
+ "creosote",
365
+ ]
366
+
367
+ for tool_name in tools_to_merge:
368
+ if tool_name in source_tools:
369
+ if tool_name not in target_tools:
370
+ target_tools[tool_name] = self._replace_project_name_in_tool_config(
371
+ source_tools[tool_name], project_name
372
+ )
373
+ self.console.print(
374
+ f"[green]➕[/green] Added [tool.{tool_name}] configuration"
375
+ )
376
+ else:
377
+ self._merge_tool_settings(
378
+ target_tools[tool_name],
379
+ source_tools[tool_name],
380
+ tool_name,
381
+ project_name,
382
+ )
383
+
384
+ self._merge_pytest_markers(target_tools, source_tools)
385
+
386
+ def _merge_tool_settings(
387
+ self,
388
+ target_tool: dict[str, t.Any],
389
+ source_tool: dict[str, t.Any],
390
+ tool_name: str,
391
+ project_name: str,
392
+ ) -> None:
393
+ updated_keys = []
394
+
395
+ for key, value in source_tool.items():
396
+ if key not in target_tool:
397
+ target_tool[key] = self._replace_project_name_in_config_value(
398
+ value, project_name
399
+ )
400
+ updated_keys.append(key)
401
+
402
+ if updated_keys:
403
+ self.console.print(
404
+ f"[yellow]🔄[/yellow] Updated [tool.{tool_name}] with: {', '.join(updated_keys)}"
405
+ )
406
+
407
+ def _merge_pytest_markers(
408
+ self,
409
+ target_tools: dict[str, t.Any],
410
+ source_tools: dict[str, t.Any],
411
+ ) -> None:
412
+ if "pytest" not in source_tools or "pytest" not in target_tools:
413
+ return
414
+
415
+ source_pytest = source_tools["pytest"]
416
+ target_pytest = target_tools["pytest"]
417
+
418
+ if "ini_options" not in source_pytest or "ini_options" not in target_pytest:
419
+ return
420
+
421
+ source_markers = source_pytest["ini_options"].get("markers", [])
422
+ target_markers = target_pytest["ini_options"].get("markers", [])
423
+
424
+ existing_marker_names = {marker.split(": ")[0] for marker in target_markers}
425
+ new_markers = [
426
+ marker
427
+ for marker in source_markers
428
+ if marker.split(": ")[0] not in existing_marker_names
429
+ ]
430
+
431
+ if new_markers:
432
+ target_markers.extend(new_markers)
433
+ self.console.print(
434
+ f"[green]➕[/green] Added pytest markers: {len(new_markers)}"
435
+ )
436
+
437
+ def _remove_fixed_coverage_requirements(
438
+ self,
439
+ target_config: dict[str, t.Any],
440
+ ) -> None:
441
+ target_coverage = (
442
+ target_config.get("tool", {}).get("pytest", {}).get("ini_options", {})
443
+ )
444
+
445
+ addopts = target_coverage.get("addopts", "")
446
+ if isinstance(addopts, str):
447
+ original_addopts = addopts
448
+
449
+ from crackerjack.services.regex_patterns import remove_coverage_fail_under
450
+
451
+ addopts = remove_coverage_fail_under(addopts).strip()
452
+ addopts = " ".join(addopts.split())
453
+
454
+ if original_addopts != addopts:
455
+ target_coverage["addopts"] = addopts
456
+ self.console.print(
457
+ "[green]🔄[/green] Removed fixed coverage requirement (using ratchet system)"
458
+ )
459
+
460
+ coverage_report = (
461
+ target_config.get("tool", {}).get("coverage", {}).get("report", {})
462
+ )
463
+ if "fail_under" in coverage_report:
464
+ original_fail_under = coverage_report["fail_under"]
465
+ coverage_report["fail_under"] = 0
466
+ self.console.print(
467
+ f"[green]🔄[/green] Reset coverage.report.fail_under from {original_fail_under} to 0 (ratchet system)"
468
+ )
469
+
470
+ def _replace_project_name_in_tool_config(
471
+ self, tool_config: dict[str, t.Any], project_name: str
472
+ ) -> dict[str, t.Any]:
473
+ if project_name == "crackerjack":
474
+ return tool_config
475
+
476
+ result = copy.deepcopy(tool_config)
477
+ return t.cast(
478
+ dict[str, t.Any],
479
+ self._replace_project_name_in_config_value(result, project_name),
480
+ )
481
+
482
+ def _replace_project_name_in_config_value(
483
+ self, value: t.Any, project_name: str
484
+ ) -> t.Any:
485
+ if project_name == "crackerjack":
486
+ return value
487
+
488
+ if isinstance(value, str):
489
+ return value.replace("crackerjack", project_name)
490
+ elif isinstance(value, list):
491
+ return [
492
+ self._replace_project_name_in_config_value(item, project_name)
493
+ for item in value
494
+ ]
495
+ elif isinstance(value, dict):
496
+ return {
497
+ key: self._replace_project_name_in_config_value(val, project_name)
498
+ for key, val in value.items()
499
+ }
500
+ return value
501
+
502
+ def _process_pre_commit_repos_for_project(
503
+ self, repos: list[dict[str, t.Any]], project_name: str
504
+ ) -> list[dict[str, t.Any]]:
505
+ """Process pre-commit repos to replace project-specific references."""
506
+ if project_name == "crackerjack":
507
+ return repos # No changes needed for crackerjack itself
508
+
509
+ processed_repos = []
510
+ for repo in repos:
511
+ processed_repo = copy.deepcopy(repo)
512
+ self._process_repo_hooks(processed_repo, project_name)
513
+ processed_repos.append(processed_repo)
514
+
515
+ return processed_repos
516
+
517
+ def _process_repo_hooks(self, repo: dict[str, t.Any], project_name: str) -> None:
518
+ """Process hooks within a repo to replace project-specific references."""
519
+ hooks = repo.get("hooks", [])
520
+ for hook in hooks:
521
+ if isinstance(hook, dict):
522
+ self._process_hook_args(hook, project_name)
523
+ self._process_hook_files(hook, project_name)
524
+ # Special handling for validate-regex-patterns hook - keep it pointing to crackerjack package
525
+ # This should reference the installed crackerjack package, not the current project
526
+ # The entry already uses "uv run python -m crackerjack.tools.validate_regex_patterns"
527
+ # which is correct - it runs from the installed crackerjack package
528
+
529
+ def _process_hook_args(self, hook: dict[str, t.Any], project_name: str) -> None:
530
+ """Process hook args to replace project-specific references."""
531
+ if "args" in hook:
532
+ hook["args"] = [
533
+ arg.replace("crackerjack", project_name)
534
+ if isinstance(arg, str)
535
+ else arg
536
+ for arg in hook["args"]
537
+ ]
538
+
539
+ def _process_hook_files(self, hook: dict[str, t.Any], project_name: str) -> None:
540
+ """Process hook files pattern to replace project-specific references."""
541
+ if "files" in hook:
542
+ files_pattern = hook["files"]
543
+ if isinstance(files_pattern, str):
544
+ hook["files"] = files_pattern.replace(
545
+ "^crackerjack/", f"^{project_name}/"
546
+ )