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,173 @@
1
+ from contextlib import suppress
2
+ from pathlib import Path
3
+
4
+ from acb.console import Console
5
+ from acb.depends import Inject, depends
6
+
7
+ from .regex_patterns import SAFE_PATTERNS
8
+
9
+
10
+ class CoverageBadgeService:
11
+ """Service for managing coverage badges in README.md files."""
12
+
13
+ @depends.inject
14
+ def __init__(self, console: Inject[Console], project_root: Path) -> None:
15
+ self.console = console
16
+ self.project_root = project_root
17
+ self.readme_path = project_root / "README.md"
18
+
19
+ def update_readme_coverage_badge(self, coverage_percent: float) -> bool:
20
+ """Update or insert coverage badge in README.md with current coverage percentage."""
21
+ if not self.readme_path.exists():
22
+ self.console.print(
23
+ "[yellow]⚠️[/yellow] README.md not found, skipping badge update"
24
+ )
25
+ return False
26
+
27
+ try:
28
+ readme_content = self.readme_path.read_text(encoding="utf-8")
29
+ badge_url = self._generate_badge_url(coverage_percent)
30
+
31
+ if self._has_coverage_badge(readme_content):
32
+ updated_content = self._update_existing_badge(readme_content, badge_url)
33
+ action = "updated"
34
+ else:
35
+ updated_content = self._insert_new_badge(readme_content, badge_url)
36
+ action = "added"
37
+
38
+ if updated_content != readme_content:
39
+ self.readme_path.write_text(updated_content, encoding="utf-8")
40
+ self.console.print(
41
+ f"[green]📊[/green] Coverage badge {action}: {coverage_percent:.1f}%"
42
+ )
43
+ return True
44
+ else:
45
+ return False
46
+
47
+ except Exception as e:
48
+ self.console.print(f"[red]❌[/red] Failed to update coverage badge: {e}")
49
+ return False
50
+
51
+ def _generate_badge_url(self, coverage_percent: float) -> str:
52
+ """Generate shields.io badge URL with appropriate color coding."""
53
+ color = self._get_badge_color(coverage_percent)
54
+ # URL encode the % symbol as %25
55
+ encoded_percent = f"{coverage_percent:.1f}%25"
56
+ return f"https://img.shields.io/badge/coverage-{encoded_percent}-{color}"
57
+
58
+ def _get_badge_color(self, coverage_percent: float) -> str:
59
+ """Determine badge color based on coverage percentage."""
60
+ if coverage_percent < 50:
61
+ return "red"
62
+ elif coverage_percent < 80:
63
+ return "yellow"
64
+ return "brightgreen"
65
+
66
+ def _has_coverage_badge(self, content: str) -> bool:
67
+ """Check if README already contains a coverage badge."""
68
+ # Use safe pattern for badge detection
69
+ return SAFE_PATTERNS["detect_coverage_badge"].search(content) is not None
70
+
71
+ def _update_existing_badge(self, content: str, new_badge_url: str) -> str:
72
+ """Replace existing coverage badge with new one."""
73
+ # Try different safe patterns for badge replacement
74
+ patterns_to_try = [
75
+ "update_coverage_badge_url",
76
+ "update_coverage_badge_any",
77
+ "update_shields_coverage_url",
78
+ ]
79
+
80
+ for pattern_name in patterns_to_try:
81
+ pattern_obj = SAFE_PATTERNS[pattern_name]
82
+ # Use the pattern and manually replace NEW_BADGE_URL with actual URL
83
+ temp_content = pattern_obj.apply(content)
84
+ if temp_content != content:
85
+ # Replace placeholder with actual URL
86
+ new_content = temp_content.replace("NEW_BADGE_URL", new_badge_url)
87
+ return new_content
88
+
89
+ return content
90
+
91
+ def _insert_new_badge(self, content: str, badge_url: str) -> str:
92
+ """Insert new coverage badge in the appropriate location."""
93
+ lines = content.split("\n")
94
+
95
+ # Find the badge section (after title, before first heading)
96
+ insert_index = self._find_badge_insertion_point(lines)
97
+
98
+ if insert_index is not None:
99
+ coverage_badge = f"![Coverage]({badge_url})"
100
+ lines.insert(insert_index, coverage_badge)
101
+ return "\n".join(lines)
102
+ # Fallback: add after title
103
+ return self._insert_after_title(content, badge_url)
104
+
105
+ def _find_badge_insertion_point(self, lines: list[str]) -> int | None:
106
+ """Find the best location to insert the coverage badge."""
107
+ # Look for existing badge lines
108
+ badge_lines = [
109
+ i for i, line in enumerate(lines) if line.strip().startswith(("[![", "!["))
110
+ ]
111
+
112
+ if badge_lines:
113
+ # Insert after the last existing badge
114
+ return badge_lines[-1] + 1
115
+
116
+ # Look for first non-empty line after title
117
+ title_found = False
118
+ for i, line in enumerate(lines):
119
+ if line.startswith("#") and not title_found:
120
+ title_found = True
121
+ continue
122
+ elif title_found and line.strip() == "":
123
+ continue
124
+ elif title_found and line.strip():
125
+ return i
126
+
127
+ return None
128
+
129
+ def _insert_after_title(self, content: str, badge_url: str) -> str:
130
+ """Fallback method to insert badge after the title."""
131
+ lines = content.split("\n")
132
+
133
+ # Find title line
134
+ for i, line in enumerate(lines):
135
+ if line.startswith("#"):
136
+ # Insert after title with blank line
137
+ coverage_badge = f"![Coverage]({badge_url})"
138
+ if i + 1 < len(lines) and lines[i + 1].strip() == "":
139
+ lines.insert(i + 2, coverage_badge)
140
+ else:
141
+ lines.insert(i + 1, "")
142
+ lines.insert(i + 2, coverage_badge)
143
+ break
144
+
145
+ return "\n".join(lines)
146
+
147
+ def should_update_badge(self, coverage_percent: float) -> bool:
148
+ """Check if badge should be updated based on coverage change."""
149
+ if not self.readme_path.exists():
150
+ return False
151
+
152
+ try:
153
+ content = self.readme_path.read_text(encoding="utf-8")
154
+ current_coverage = self._extract_current_coverage(content)
155
+
156
+ if current_coverage is None:
157
+ return True # No badge exists, should add one
158
+
159
+ # Only update if coverage changed by at least 0.01% (more accurate reporting)
160
+ return abs(coverage_percent - current_coverage) >= 0.01
161
+
162
+ except Exception:
163
+ return True # On error, attempt update
164
+
165
+ def _extract_current_coverage(self, content: str) -> float | None:
166
+ """Extract current coverage percentage from existing badge."""
167
+ match = SAFE_PATTERNS["extract_coverage_percentage"].search(content)
168
+
169
+ if match:
170
+ with suppress(ValueError):
171
+ return float(match.group(1))
172
+
173
+ return None
@@ -0,0 +1,381 @@
1
+ import json
2
+ import typing as t
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+
6
+ from acb.console import Console
7
+ from acb.depends import Inject, depends
8
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
9
+
10
+ from crackerjack.models.protocols import CoverageRatchetProtocol
11
+ from crackerjack.services.filesystem import FileSystemService
12
+ from crackerjack.services.regex_patterns import update_coverage_requirement
13
+
14
+
15
+ class CoverageRatchetService(CoverageRatchetProtocol):
16
+ MILESTONES = [15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 95, 100]
17
+
18
+ TOLERANCE_MARGIN = 2.0
19
+
20
+ @depends.inject
21
+ def __init__(self, pkg_path: Path, console: Inject[Console]) -> None:
22
+ # Normalize to pathlib.Path to avoid async path behaviors
23
+ try:
24
+ self.pkg_path = Path(str(pkg_path))
25
+ except Exception:
26
+ self.pkg_path = Path(pkg_path)
27
+ self.console = console
28
+ self.ratchet_file = self.pkg_path / ".coverage-ratchet.json"
29
+ self.pyproject_file = self.pkg_path / "pyproject.toml"
30
+
31
+ def initialize(self) -> None:
32
+ pass
33
+
34
+ def cleanup(self) -> None:
35
+ pass
36
+
37
+ def health_check(self) -> bool:
38
+ return True
39
+
40
+ def shutdown(self) -> None:
41
+ pass
42
+
43
+ def metrics(self) -> dict[str, t.Any]:
44
+ return {}
45
+
46
+ def is_healthy(self) -> bool:
47
+ return True
48
+
49
+ def register_resource(self, resource: t.Any) -> None:
50
+ pass
51
+
52
+ def cleanup_resource(self, resource: t.Any) -> None:
53
+ pass
54
+
55
+ def record_error(self, error: Exception) -> None:
56
+ pass
57
+
58
+ def increment_requests(self) -> None:
59
+ pass
60
+
61
+ def get_custom_metric(self, name: str) -> t.Any:
62
+ return None
63
+
64
+ def set_custom_metric(self, name: str, value: t.Any) -> None:
65
+ pass
66
+
67
+ def initialize_baseline(self, initial_coverage: float) -> None:
68
+ if self.ratchet_file.exists():
69
+ return
70
+
71
+ ratchet_data: dict[str, t.Any] = {
72
+ "baseline": initial_coverage,
73
+ "current_minimum": initial_coverage,
74
+ "target": 100.0,
75
+ "last_updated": datetime.now().isoformat(),
76
+ "history": [
77
+ {
78
+ "date": datetime.now().isoformat(),
79
+ "coverage": initial_coverage,
80
+ "commit": "baseline",
81
+ "milestone": False,
82
+ }
83
+ ],
84
+ "milestones_achieved": [],
85
+ "next_milestone": self._get_next_milestone(initial_coverage),
86
+ }
87
+
88
+ self.ratchet_file.write_text(json.dumps(ratchet_data, indent=2))
89
+ self.console.print(
90
+ f"[cyan]📊[/ cyan] Coverage ratchet initialized at {initial_coverage: .2f}% baseline"
91
+ )
92
+
93
+ def get_ratchet_data(self) -> dict[str, t.Any]:
94
+ if not self.ratchet_file.exists():
95
+ return {}
96
+ return t.cast(dict[str, t.Any], json.loads(self.ratchet_file.read_text()))
97
+
98
+ def get_status_report(self) -> dict[str, t.Any]:
99
+ """Get status report for coverage ratchet service."""
100
+ return self.get_ratchet_data()
101
+
102
+ def get_baseline(self) -> float:
103
+ data = self.get_ratchet_data()
104
+ baseline = data.get("baseline")
105
+ return float(baseline) if baseline is not None else 0.0
106
+
107
+ def get_baseline_coverage(self) -> float:
108
+ return self.get_baseline()
109
+
110
+ def update_baseline_coverage(self, new_coverage: float) -> bool:
111
+ result: bool = self.update_coverage(new_coverage).get("success", False)
112
+ return result
113
+
114
+ def is_coverage_regression(self, current_coverage: float) -> bool:
115
+ baseline = self.get_baseline()
116
+ return current_coverage < (baseline - self.TOLERANCE_MARGIN)
117
+
118
+ def calculate_coverage_gap(self) -> float:
119
+ data = self.get_ratchet_data()
120
+ baseline = data.get("baseline")
121
+ baseline = float(baseline) if baseline is not None else 0.0
122
+ next_milestone = data.get("next_milestone")
123
+ next_milestone = float(next_milestone) if next_milestone is not None else None
124
+ if next_milestone:
125
+ return next_milestone - baseline
126
+ return 100.0 - baseline
127
+
128
+ def update_coverage(self, new_coverage: float) -> dict[str, t.Any]:
129
+ if not self.ratchet_file.exists():
130
+ self.initialize_baseline(new_coverage)
131
+ return {
132
+ "status": "initialized",
133
+ "message": f"Coverage ratchet initialized at {new_coverage: .2f}%",
134
+ "milestones": [],
135
+ "progress_to_100": f"{new_coverage: .1f}% of the way to 100 % coverage",
136
+ "allowed": True,
137
+ "baseline_updated": True,
138
+ }
139
+
140
+ data = self.get_ratchet_data()
141
+ current_baseline = data["baseline"]
142
+
143
+ tolerance_threshold = current_baseline - self.TOLERANCE_MARGIN
144
+ if new_coverage < tolerance_threshold:
145
+ return {
146
+ "status": "regression",
147
+ "message": f"Coverage decreased from {current_baseline: .2f}% to {new_coverage: .2f}% (below {self.TOLERANCE_MARGIN}% tolerance margin)",
148
+ "regression_amount": current_baseline - new_coverage,
149
+ "tolerance_threshold": tolerance_threshold,
150
+ "allowed": False,
151
+ "baseline_updated": False,
152
+ }
153
+ elif new_coverage > current_baseline + 0.01:
154
+ milestones_hit = self._check_milestones(
155
+ current_baseline, new_coverage, data
156
+ )
157
+ self._update_baseline(new_coverage, data, milestones_hit)
158
+ self._update_pyproject_requirement(new_coverage)
159
+
160
+ return {
161
+ "status": "improved",
162
+ "message": f"Coverage improved from {current_baseline: .2f}% to {new_coverage: .2f}% !",
163
+ "improvement": new_coverage - current_baseline,
164
+ "milestones": milestones_hit,
165
+ "progress_to_100": f"{new_coverage: .1f}% of the way to 100 % coverage",
166
+ "next_milestone": self._get_next_milestone(new_coverage),
167
+ "points_to_next": (next_milestone - new_coverage)
168
+ if (next_milestone := self._get_next_milestone(new_coverage))
169
+ is not None
170
+ else 0,
171
+ "allowed": True,
172
+ "baseline_updated": True,
173
+ }
174
+
175
+ return {
176
+ "status": "maintained",
177
+ "message": f"Coverage maintained at {new_coverage: .2f}% (within {self.TOLERANCE_MARGIN}% tolerance margin)",
178
+ "allowed": True,
179
+ "baseline_updated": False,
180
+ }
181
+
182
+ def _check_milestones(
183
+ self, old_coverage: float, new_coverage: float, data: dict[str, t.Any]
184
+ ) -> list[float]:
185
+ achieved_milestones = set(data.get("milestones_achieved", []))
186
+ return [
187
+ milestone
188
+ for milestone in self.MILESTONES
189
+ if (
190
+ old_coverage < milestone <= new_coverage
191
+ and milestone not in achieved_milestones
192
+ )
193
+ ]
194
+
195
+ def _get_next_milestone(self, coverage: float) -> float | None:
196
+ for milestone in self.MILESTONES:
197
+ if milestone > coverage:
198
+ return milestone
199
+ return None
200
+
201
+ def _update_baseline(
202
+ self, new_coverage: float, data: dict[str, t.Any], milestones_hit: list[float]
203
+ ) -> None:
204
+ data["baseline"] = new_coverage
205
+ data["current_minimum"] = new_coverage
206
+ data["last_updated"] = datetime.now().isoformat()
207
+
208
+ data["history"].append(
209
+ {
210
+ "date": datetime.now().isoformat(),
211
+ "coverage": new_coverage,
212
+ "commit": "current",
213
+ "milestone": len(milestones_hit) > 0,
214
+ "milestones_hit": milestones_hit,
215
+ }
216
+ )
217
+
218
+ for milestone in milestones_hit:
219
+ if milestone not in data["milestones_achieved"]:
220
+ data["milestones_achieved"].append(milestone)
221
+
222
+ data["next_milestone"] = self._get_next_milestone(new_coverage)
223
+
224
+ if len(data["history"]) > 50:
225
+ data["history"] = data["history"][-50:]
226
+
227
+ self.ratchet_file.write_text(json.dumps(data, indent=2))
228
+
229
+ def _update_pyproject_requirement(self, new_coverage: float) -> None:
230
+ try:
231
+ content = self.pyproject_file.read_text()
232
+
233
+ updated_content = update_coverage_requirement(content, new_coverage)
234
+
235
+ if updated_content != content:
236
+ updated_content = (
237
+ FileSystemService.clean_trailing_whitespace_and_newlines(
238
+ updated_content
239
+ )
240
+ )
241
+
242
+ self.pyproject_file.write_text(updated_content)
243
+ self.console.print(
244
+ f"[cyan]📝[/ cyan] Updated pyproject.toml coverage requirement to {new_coverage: .0f}%"
245
+ )
246
+
247
+ except Exception as e:
248
+ self.console.print(
249
+ f"[yellow]⚠️[/ yellow] Failed to update pyproject.toml: {e}"
250
+ )
251
+
252
+ def get_progress_visualization(self) -> str:
253
+ data = self.get_ratchet_data()
254
+ if not data:
255
+ return "Coverage ratchet not initialized"
256
+
257
+ current = data["baseline"]
258
+ target = 100.0
259
+ next_milestone = data.get("next_milestone")
260
+
261
+ progress_chars = int(current / target * 20)
262
+ bar = "█" * progress_chars + "░" * (20 - progress_chars)
263
+
264
+ result = f"Coverage Progress: {current: .2f}% [{bar}] → 100 %\n"
265
+ result += f" Current ─┘{'': > 18} └─ Goal\n"
266
+
267
+ if next_milestone:
268
+ points_needed = next_milestone - current
269
+ result += f"Next milestone: {next_milestone: .0f}% (+{points_needed: .2f}% needed)\n"
270
+
271
+ return result
272
+
273
+ def get_coverage_improvement_needed(self) -> float:
274
+ """Get percentage improvement needed to reach next milestone."""
275
+ current = self.get_baseline_coverage()
276
+ for milestone in self.MILESTONES:
277
+ if current < milestone:
278
+ needed = milestone - current
279
+ return max(0.0, needed)
280
+ return 0.0
281
+
282
+ def _calculate_trend(self, data: dict[str, t.Any]) -> str:
283
+ history = data.get("history", [])
284
+ if len(history) < 2:
285
+ return "insufficient_data"
286
+
287
+ recent_entries = history[-5:]
288
+ if len(recent_entries) < 2:
289
+ return "insufficient_data"
290
+
291
+ start_coverage = recent_entries[0]["coverage"]
292
+ end_coverage = recent_entries[-1]["coverage"]
293
+
294
+ if end_coverage > start_coverage + 0.5:
295
+ return "improving"
296
+ elif end_coverage < start_coverage - 0.5:
297
+ return "declining"
298
+ return "stable"
299
+
300
+ def display_milestone_celebration(self, milestones: list[float]) -> None:
301
+ for milestone in milestones:
302
+ if milestone == 100.0:
303
+ self.console.print(
304
+ "[gold]🎉🏆 PERFECT ! 100 % COVERAGE ACHIEVED ! 🏆🎉[/ gold]"
305
+ )
306
+ elif milestone >= 90:
307
+ self.console.print(
308
+ f"[gold]🏆 Milestone achieved: {milestone: .0f}% coverage ! Approaching perfection ![/ gold]"
309
+ )
310
+ elif milestone >= 50:
311
+ self.console.print(
312
+ f"[green]🎯 Milestone achieved: {milestone: .0f}% coverage ! Great progress ![/ green]"
313
+ )
314
+ else:
315
+ self.console.print(
316
+ f"[cyan]📈 Milestone achieved: {milestone: .0f}% coverage ! Keep it up ![/ cyan]"
317
+ )
318
+
319
+ def show_progress_with_spinner(self) -> None:
320
+ data = self.get_ratchet_data()
321
+ if not data:
322
+ return
323
+
324
+ current = data["baseline"]
325
+ target = 100.0
326
+
327
+ with Progress(
328
+ SpinnerColumn(),
329
+ TextColumn("[progress.description]{task.description}"),
330
+ BarColumn(),
331
+ TextColumn("[progress.percentage]{task.percentage: > 3.0f}%"),
332
+ ) as progress:
333
+ task = progress.add_task(
334
+ "Coverage Progress", total=target, completed=current
335
+ )
336
+ progress.update(task, description=f"Coverage: {current: .1f}% / 100 %")
337
+
338
+ def get_coverage_report(self) -> str | None:
339
+ data = self.get_ratchet_data()
340
+ if not data:
341
+ return None
342
+
343
+ current_coverage = data.get("baseline", 0.0)
344
+ next_milestone = data.get("next_milestone")
345
+
346
+ report = f"Coverage: {current_coverage: .2f}%"
347
+ if next_milestone:
348
+ progress = (current_coverage / next_milestone) * 100
349
+ report += (
350
+ f" (next milestone: {next_milestone: .0f}%, {progress: .1f}% there)"
351
+ )
352
+
353
+ return report
354
+
355
+ def check_and_update_coverage(self) -> dict[str, t.Any]:
356
+ try:
357
+ coverage_file = self.pkg_path / "coverage.json"
358
+ if not coverage_file.exists():
359
+ return {
360
+ "success": True,
361
+ "status": "no_coverage_data",
362
+ "message": "No coverage data found-tests passed without coverage",
363
+ "allowed": True,
364
+ "baseline_updated": False,
365
+ }
366
+
367
+ coverage_data = json.loads(coverage_file.read_text())
368
+ current_coverage = coverage_data.get("totals", {}).get(
369
+ "percent_covered", 0.0
370
+ )
371
+
372
+ result = self.update_coverage(current_coverage)
373
+ result["success"] = result.get("allowed", True)
374
+ return result
375
+
376
+ except Exception as e:
377
+ return {
378
+ "success": False,
379
+ "error": str(e),
380
+ "message": "Failed to read coverage data",
381
+ }