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,472 @@
1
+ import asyncio
2
+ import contextlib
3
+ import fcntl
4
+ import logging
5
+ import os
6
+ import shutil
7
+ import time
8
+ import typing as t
9
+ from pathlib import Path
10
+
11
+ from ..models.resource_protocols import AbstractFileResource
12
+ from .resource_manager import ResourceManager
13
+
14
+
15
+ class AtomicFileWriter(AbstractFileResource):
16
+ def __init__(
17
+ self,
18
+ target_path: Path,
19
+ backup: bool = True,
20
+ manager: ResourceManager | None = None,
21
+ ) -> None:
22
+ super().__init__(target_path)
23
+ self.backup = backup
24
+ self.manager = manager
25
+ self.temp_path: Path | None = None
26
+ self.backup_path: Path | None = None
27
+ self._file_handle: t.IO[str] | None = None
28
+ self.logger = logging.getLogger(__name__)
29
+
30
+ if manager:
31
+ manager.register_resource(self)
32
+
33
+ async def _do_initialize(self) -> None:
34
+ self.temp_path = self.path.parent / f".{self.path.name}.tmp.{os.getpid()}"
35
+
36
+ if self.backup and self.path.exists():
37
+ self.backup_path = self.path.with_suffix(f"{self.path.suffix}.bak")
38
+ shutil.copy2(self.path, self.backup_path)
39
+
40
+ self._file_handle = self.temp_path.open("w", encoding="utf-8")
41
+
42
+ async def _do_cleanup(self) -> None:
43
+ if self._file_handle and not self._file_handle.closed:
44
+ self._file_handle.close()
45
+
46
+ if self.temp_path and self.temp_path.exists():
47
+ try:
48
+ self.temp_path.unlink()
49
+ except OSError as e:
50
+ self.logger.warning(f"Failed to remove temp file {self.temp_path}: {e}")
51
+
52
+ if self.backup_path and self.backup_path.exists():
53
+ try:
54
+ self.backup_path.unlink()
55
+ except OSError as e:
56
+ self.logger.warning(
57
+ f"Failed to remove backup file {self.backup_path}: {e}"
58
+ )
59
+
60
+ def write(self, content: str) -> None:
61
+ if not self._file_handle:
62
+ raise RuntimeError("AtomicFileWriter not initialized")
63
+ self._file_handle.write(content)
64
+
65
+ def writelines(self, lines: t.Iterable[str]) -> None:
66
+ if not self._file_handle:
67
+ raise RuntimeError("AtomicFileWriter not initialized")
68
+ self._file_handle.writelines(lines)
69
+
70
+ def flush(self) -> None:
71
+ if not self._file_handle:
72
+ raise RuntimeError("AtomicFileWriter not initialized")
73
+ self._file_handle.flush()
74
+ os.fsync(self._file_handle.fileno())
75
+
76
+ async def commit(self) -> None:
77
+ if not self.temp_path:
78
+ raise RuntimeError("AtomicFileWriter not initialized")
79
+
80
+ self.flush()
81
+
82
+ if self._file_handle:
83
+ self._file_handle.close()
84
+ self._file_handle = None
85
+
86
+ try:
87
+ self.temp_path.replace(self.path)
88
+ self.logger.debug(f"Successfully committed changes to {self.path}")
89
+ except OSError as e:
90
+ if self.backup_path and self.backup_path.exists():
91
+ try:
92
+ self.backup_path.replace(self.path)
93
+ self.logger.info(
94
+ f"Restored {self.path} from backup after commit failure"
95
+ )
96
+ except OSError:
97
+ self.logger.error(f"Failed to restore {self.path} from backup")
98
+ raise RuntimeError(f"Failed to commit changes to {self.path}") from e
99
+
100
+ async def rollback(self) -> None:
101
+ if self.backup_path and self.backup_path.exists():
102
+ try:
103
+ self.backup_path.replace(self.path)
104
+ self.logger.info(f"Rolled back changes to {self.path}")
105
+ except OSError as e:
106
+ self.logger.error(f"Failed to rollback {self.path}: {e}")
107
+ raise
108
+
109
+
110
+ class LockedFileResource(AbstractFileResource):
111
+ def __init__(
112
+ self,
113
+ path: Path,
114
+ mode: str = "r+",
115
+ timeout: float = 30.0,
116
+ manager: ResourceManager | None = None,
117
+ ) -> None:
118
+ super().__init__(path)
119
+ self.mode = mode
120
+ self.timeout = timeout
121
+ self._file_handle: t.IO[str] | None = None
122
+ self.logger = logging.getLogger(__name__)
123
+
124
+ if manager:
125
+ manager.register_resource(self)
126
+
127
+ async def _do_initialize(self) -> None:
128
+ self.path.parent.mkdir(parents=True, exist_ok=True)
129
+
130
+ self._file_handle = self.path.open(self.mode)
131
+
132
+ start_time = time.time()
133
+ while time.time() - start_time < self.timeout:
134
+ try:
135
+ fcntl.flock(self._file_handle.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
136
+ self.logger.debug(f"Acquired lock on {self.path}")
137
+ return
138
+ except OSError:
139
+ await asyncio.sleep(0.1)
140
+
141
+ raise TimeoutError(
142
+ f"Failed to acquire lock on {self.path} within {self.timeout}s"
143
+ )
144
+
145
+ async def _do_cleanup(self) -> None:
146
+ if self._file_handle and not self._file_handle.closed:
147
+ try:
148
+ fcntl.flock(self._file_handle.fileno(), fcntl.LOCK_UN)
149
+ self.logger.debug(f"Released lock on {self.path}")
150
+ except OSError as e:
151
+ self.logger.warning(f"Failed to release lock on {self.path}: {e}")
152
+ finally:
153
+ self._file_handle.close()
154
+
155
+ @property
156
+ def file_handle(self) -> t.IO[str]:
157
+ if not self._file_handle:
158
+ raise RuntimeError("LockedFileResource not initialized")
159
+ return self._file_handle
160
+
161
+ def read(self) -> str:
162
+ self.file_handle.seek(0)
163
+ return self.file_handle.read()
164
+
165
+ def write(self, content: str) -> None:
166
+ self.file_handle.seek(0)
167
+ self.file_handle.write(content)
168
+ self.file_handle.truncate()
169
+ self.file_handle.flush()
170
+ os.fsync(self.file_handle.fileno())
171
+
172
+
173
+ class SafeDirectoryCreator(AbstractFileResource):
174
+ def __init__(
175
+ self,
176
+ path: Path,
177
+ cleanup_on_error: bool = True,
178
+ manager: ResourceManager | None = None,
179
+ ) -> None:
180
+ super().__init__(path)
181
+ self.cleanup_on_error = cleanup_on_error
182
+ self._created_dirs: list[Path] = []
183
+ self.logger = logging.getLogger(__name__)
184
+
185
+ if manager:
186
+ manager.register_resource(self)
187
+
188
+ async def _do_initialize(self) -> None:
189
+ current = self.path
190
+
191
+ while not current.exists():
192
+ self._created_dirs.append(current)
193
+ current = current.parent
194
+
195
+ self._created_dirs.reverse()
196
+
197
+ for dir_path in self._created_dirs:
198
+ try:
199
+ dir_path.mkdir(exist_ok=True)
200
+ self.logger.debug(f"Created directory: {dir_path}")
201
+ except OSError as e:
202
+ self.logger.error(f"Failed to create directory {dir_path}: {e}")
203
+ if self.cleanup_on_error:
204
+ await self._cleanup_created_dirs()
205
+ raise
206
+
207
+ async def _do_cleanup(self) -> None:
208
+ if self.cleanup_on_error:
209
+ await self._cleanup_created_dirs()
210
+
211
+ async def _cleanup_created_dirs(self) -> None:
212
+ for dir_path in reversed(self._created_dirs):
213
+ try:
214
+ if dir_path.exists() and not any(dir_path.iterdir()):
215
+ dir_path.rmdir()
216
+ self.logger.debug(f"Removed directory: {dir_path}")
217
+ except OSError as e:
218
+ self.logger.warning(f"Failed to remove directory {dir_path}: {e}")
219
+
220
+
221
+ class BatchFileOperations:
222
+ def __init__(self, manager: ResourceManager | None = None) -> None:
223
+ self.manager = manager or ResourceManager()
224
+ self.operations: list[t.Callable[[], None]] = []
225
+ self.rollback_operations: list[t.Callable[[], None]] = []
226
+ self.logger = logging.getLogger(__name__)
227
+
228
+ def add_write_operation(
229
+ self,
230
+ path: Path,
231
+ content: str,
232
+ backup: bool = True,
233
+ ) -> None:
234
+ def write_op() -> None:
235
+ writer = AtomicFileWriter(path, backup, self.manager)
236
+ asyncio.create_task(writer.initialize())
237
+ writer.write(content)
238
+ asyncio.create_task(writer.commit())
239
+
240
+ def rollback_op() -> None:
241
+ writer = AtomicFileWriter(path, backup)
242
+ asyncio.create_task(writer.rollback())
243
+
244
+ self.operations.append(write_op)
245
+ self.rollback_operations.append(rollback_op)
246
+
247
+ def add_copy_operation(
248
+ self,
249
+ source: Path,
250
+ dest: Path,
251
+ backup: bool = True,
252
+ ) -> None:
253
+ def copy_op() -> None:
254
+ if backup and dest.exists():
255
+ backup_path = dest.with_suffix(f"{dest.suffix}.bak")
256
+ shutil.copy2(dest, backup_path)
257
+ shutil.copy2(source, dest)
258
+
259
+ def rollback_op() -> None:
260
+ if backup:
261
+ backup_path = dest.with_suffix(f"{dest.suffix}.bak")
262
+ if backup_path.exists():
263
+ shutil.move(backup_path, dest)
264
+
265
+ self.operations.append(copy_op)
266
+ self.rollback_operations.append(rollback_op)
267
+
268
+ def add_move_operation(
269
+ self,
270
+ source: Path,
271
+ dest: Path,
272
+ ) -> None:
273
+ def move_op() -> None:
274
+ shutil.move(source, dest)
275
+
276
+ def rollback_op() -> None:
277
+ shutil.move(dest, source)
278
+
279
+ self.operations.append(move_op)
280
+ self.rollback_operations.append(rollback_op)
281
+
282
+ def add_delete_operation(
283
+ self,
284
+ path: Path,
285
+ backup: bool = True,
286
+ ) -> None:
287
+ backup_path: Path | None = None
288
+
289
+ def delete_op() -> None:
290
+ nonlocal backup_path
291
+ if backup and path.exists():
292
+ backup_path = path.with_suffix(f"{path.suffix}.bak.{os.getpid()}")
293
+ shutil.move(path, backup_path)
294
+ elif path.exists():
295
+ path.unlink()
296
+
297
+ def rollback_op() -> None:
298
+ if backup_path and backup_path.exists():
299
+ shutil.move(backup_path, path)
300
+
301
+ self.operations.append(delete_op)
302
+ self.rollback_operations.append(rollback_op)
303
+
304
+ async def commit_all(self) -> None:
305
+ executed_ops = 0
306
+
307
+ try:
308
+ for i, operation in enumerate(self.operations):
309
+ operation()
310
+ executed_ops = i + 1
311
+
312
+ self.logger.info(f"Successfully executed {executed_ops} file operations")
313
+
314
+ except Exception as e:
315
+ self.logger.error(f"Batch operation failed at step {executed_ops}: {e}")
316
+
317
+ for i in range(executed_ops - 1, -1, -1):
318
+ try:
319
+ self.rollback_operations[i]()
320
+ except Exception as rollback_error:
321
+ self.logger.error(
322
+ f"Rollback failed for operation {i}: {rollback_error}"
323
+ )
324
+
325
+ raise RuntimeError("Batch file operations failed and rolled back") from e
326
+
327
+
328
+ @contextlib.asynccontextmanager
329
+ async def atomic_file_write(
330
+ path: Path,
331
+ backup: bool = True,
332
+ ) -> t.AsyncGenerator[AtomicFileWriter]:
333
+ writer = AtomicFileWriter(path, backup)
334
+ try:
335
+ await writer.initialize()
336
+ yield writer
337
+ await writer.commit()
338
+ except Exception:
339
+ await writer.rollback()
340
+ raise
341
+ finally:
342
+ await writer.cleanup()
343
+
344
+
345
+ @contextlib.asynccontextmanager
346
+ async def locked_file_access(
347
+ path: Path,
348
+ mode: str = "r+",
349
+ timeout: float = 30.0,
350
+ ) -> t.AsyncGenerator[LockedFileResource]:
351
+ file_resource = LockedFileResource(path, mode, timeout)
352
+ try:
353
+ await file_resource.initialize()
354
+ yield file_resource
355
+ finally:
356
+ await file_resource.cleanup()
357
+
358
+
359
+ @contextlib.asynccontextmanager
360
+ async def safe_directory_creation(
361
+ path: Path,
362
+ cleanup_on_error: bool = True,
363
+ ) -> t.AsyncGenerator[SafeDirectoryCreator]:
364
+ creator = SafeDirectoryCreator(path, cleanup_on_error)
365
+ try:
366
+ await creator.initialize()
367
+ yield creator
368
+ finally:
369
+ await creator.cleanup()
370
+
371
+
372
+ @contextlib.asynccontextmanager
373
+ async def batch_file_operations() -> t.AsyncGenerator[BatchFileOperations]:
374
+ batch = BatchFileOperations()
375
+ try:
376
+ yield batch
377
+ await batch.commit_all()
378
+ except Exception:
379
+ raise
380
+
381
+
382
+ class SafeFileOperations:
383
+ @staticmethod
384
+ async def safe_read_text(
385
+ path: Path,
386
+ encoding: str = "utf-8",
387
+ fallback_encodings: list[str] | None = None,
388
+ ) -> str:
389
+ fallback_encodings = fallback_encodings or ["latin-1", "cp1252"]
390
+
391
+ for enc in [encoding] + fallback_encodings:
392
+ try:
393
+ return path.read_text(encoding=enc)
394
+ except UnicodeDecodeError:
395
+ continue
396
+ except FileNotFoundError:
397
+ raise
398
+ except Exception as e:
399
+ logging.getLogger(__name__).warning(
400
+ f"Failed to read {path} with encoding {enc}: {e}"
401
+ )
402
+ continue
403
+
404
+ raise RuntimeError(f"Failed to read {path} with any supported encoding")
405
+
406
+ @staticmethod
407
+ async def safe_write_text(
408
+ path: Path,
409
+ content: str,
410
+ encoding: str = "utf-8",
411
+ atomic: bool = True,
412
+ backup: bool = True,
413
+ ) -> None:
414
+ if atomic:
415
+ async with atomic_file_write(path, backup) as writer:
416
+ writer.write(content)
417
+ else:
418
+ path.parent.mkdir(parents=True, exist_ok=True)
419
+ path.write_text(content, encoding=encoding)
420
+
421
+ @staticmethod
422
+ async def safe_copy_file(
423
+ source: Path,
424
+ dest: Path,
425
+ preserve_metadata: bool = True,
426
+ backup: bool = True,
427
+ ) -> None:
428
+ if not source.exists():
429
+ raise FileNotFoundError(f"Source file not found: {source}")
430
+
431
+ if backup and dest.exists():
432
+ backup_path = dest.with_suffix(f"{dest.suffix}.bak")
433
+ shutil.copy2(dest, backup_path)
434
+
435
+ try:
436
+ dest.parent.mkdir(parents=True, exist_ok=True)
437
+
438
+ if preserve_metadata:
439
+ shutil.copy2(source, dest)
440
+ else:
441
+ shutil.copy(source, dest)
442
+
443
+ except Exception as e:
444
+ if backup and dest.with_suffix(f"{dest.suffix}.bak").exists():
445
+ shutil.move(dest.with_suffix(f"{dest.suffix}.bak"), dest)
446
+ raise RuntimeError(f"Failed to copy {source} to {dest}") from e
447
+
448
+ @staticmethod
449
+ async def safe_move_file(
450
+ source: Path,
451
+ dest: Path,
452
+ backup: bool = True,
453
+ ) -> None:
454
+ if not source.exists():
455
+ raise FileNotFoundError(f"Source file not found: {source}")
456
+
457
+ backup_path = None
458
+ if backup and dest.exists():
459
+ backup_path = dest.with_suffix(f"{dest.suffix}.bak.{os.getpid()}")
460
+ shutil.move(dest, backup_path)
461
+
462
+ try:
463
+ dest.parent.mkdir(parents=True, exist_ok=True)
464
+ shutil.move(source, dest)
465
+
466
+ if backup_path and backup_path.exists():
467
+ backup_path.unlink()
468
+
469
+ except Exception as e:
470
+ if backup_path and backup_path.exists():
471
+ shutil.move(backup_path, dest)
472
+ raise RuntimeError(f"Failed to move {source} to {dest}") from e
@@ -0,0 +1,244 @@
1
+ import functools
2
+ import time
3
+ import typing as t
4
+ from pathlib import Path
5
+
6
+ from acb.console import Console
7
+
8
+
9
+ class FileCache:
10
+ def __init__(self, ttl: float = 300.0) -> None:
11
+ self.ttl = ttl
12
+ self._cache: dict[str, tuple[float, t.Any]] = {}
13
+
14
+ def get(self, key: str) -> t.Any | None:
15
+ if key not in self._cache:
16
+ return None
17
+ timestamp, value = self._cache[key]
18
+ if time.time() - timestamp > self.ttl:
19
+ del self._cache[key]
20
+ return None
21
+
22
+ return value
23
+
24
+ def set(self, key: str, value: t.Any) -> None:
25
+ self._cache[key] = (time.time(), value)
26
+
27
+ def clear(self) -> None:
28
+ self._cache.clear()
29
+
30
+ def size(self) -> int:
31
+ return len(self._cache)
32
+
33
+
34
+ class PerformanceMonitor:
35
+ def __init__(self, console: Console | None = None) -> None:
36
+ self.console = console
37
+ self.metrics: dict[str, list[float]] = {}
38
+
39
+ def time_operation(
40
+ self,
41
+ operation_name: str,
42
+ ) -> t.Callable[[t.Callable[..., t.Any]], t.Callable[..., t.Any]]:
43
+ def decorator(func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]:
44
+ @functools.wraps(func)
45
+ def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any:
46
+ start_time = time.time()
47
+ try:
48
+ return func(*args, **kwargs)
49
+ finally:
50
+ duration = time.time() - start_time
51
+ self.record_metric(operation_name, duration)
52
+
53
+ return wrapper
54
+
55
+ return decorator
56
+
57
+ def record_metric(self, name: str, value: float) -> None:
58
+ if name not in self.metrics:
59
+ self.metrics[name] = []
60
+ self.metrics[name].append(value)
61
+
62
+ def get_stats(self, name: str) -> dict[str, float]:
63
+ if name not in self.metrics or not self.metrics[name]:
64
+ return {}
65
+ values = self.metrics[name]
66
+ return {
67
+ "count": len(values),
68
+ "total": sum(values),
69
+ "avg": sum(values) / len(values),
70
+ "min": min(values),
71
+ "max": max(values),
72
+ }
73
+
74
+ def print_stats(self, name: str | None = None) -> None:
75
+ if not self.console:
76
+ return
77
+ if name:
78
+ stats = self.get_stats(name)
79
+ if stats:
80
+ self.console.print(
81
+ f"[cyan]📊 {name}: [/ cyan] "
82
+ f"avg={stats['avg']: .3f}s, "
83
+ f"min={stats['min']: .3f}s, "
84
+ f"max={stats['max']: .3f}s, "
85
+ f"count={stats['count']}",
86
+ )
87
+ else:
88
+ for metric_name in self.metrics:
89
+ self.print_stats(metric_name)
90
+
91
+
92
+ def memoize_with_ttl(
93
+ ttl: float = 300.0,
94
+ ) -> t.Callable[[t.Callable[..., t.Any]], t.Callable[..., t.Any]]:
95
+ def decorator(func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]:
96
+ cache: dict[str, tuple[float, t.Any]] = {}
97
+
98
+ @functools.wraps(func)
99
+ def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any:
100
+ key = str(args) + str(sorted(kwargs.items()))
101
+ if key in cache:
102
+ timestamp, value = cache[key]
103
+ if time.time() - timestamp <= ttl:
104
+ return value
105
+ del cache[key]
106
+ result = func(*args, **kwargs)
107
+ cache[key] = (time.time(), result)
108
+ return result
109
+
110
+ setattr(wrapper, "cache_clear", cache.clear)
111
+ setattr(wrapper, "cache_info", lambda: {"size": len(cache), "ttl": ttl})
112
+ return wrapper
113
+
114
+ return decorator
115
+
116
+
117
+ def batch_file_operations(
118
+ operations: list[t.Callable[[], t.Any]],
119
+ batch_size: int = 50,
120
+ ) -> list[t.Any]:
121
+ results: list[t.Any] = []
122
+ for i in range(0, len(operations), batch_size):
123
+ batch = operations[i : i + batch_size]
124
+ batch_results: list[t.Any] = []
125
+ for operation in batch:
126
+ try:
127
+ result = operation()
128
+ batch_results.append(result)
129
+ except Exception as e:
130
+ batch_results.append(e)
131
+ results.extend(batch_results)
132
+ if i + batch_size < len(operations):
133
+ time.sleep(0.01)
134
+
135
+ return results
136
+
137
+
138
+ class OptimizedFileWatcher:
139
+ def __init__(self, root_path: Path) -> None:
140
+ self.root_path = root_path
141
+ self._file_cache = FileCache(ttl=60.0)
142
+
143
+ @memoize_with_ttl(ttl=30.0)
144
+ def get_python_files(self) -> list[Path]:
145
+ return list[t.Any](self.root_path.rglob("*.py"))
146
+
147
+ def get_modified_files(self, since: float) -> list[Path]:
148
+ cache_key = f"modified_since_{since}"
149
+ cached = self._file_cache.get(cache_key)
150
+ if cached is not None:
151
+ return t.cast(list[Path], cached)
152
+ modified_files: list[Path] = []
153
+ for py_file in self.get_python_files():
154
+ try:
155
+ if py_file.stat().st_mtime > since:
156
+ modified_files.append(py_file)
157
+ except (OSError, FileNotFoundError):
158
+ continue
159
+ self._file_cache.set(cache_key, modified_files)
160
+ return modified_files
161
+
162
+ def clear_cache(self) -> None:
163
+ self._file_cache.clear()
164
+
165
+ if hasattr(self.get_python_files, "cache_clear"):
166
+ cache_clear = getattr(self.get_python_files, "cache_clear")
167
+ cache_clear()
168
+
169
+
170
+ class ParallelTaskExecutor:
171
+ def __init__(self, max_workers: int | None = None) -> None:
172
+ import os
173
+
174
+ self.max_workers = max_workers or min(os.cpu_count() or 1, 8)
175
+
176
+ def execute_tasks(
177
+ self,
178
+ tasks: list[t.Callable[[], t.Any]],
179
+ timeout: float = 300.0,
180
+ ) -> list[t.Any]:
181
+ if len(tasks) <= 1:
182
+ return [task() for task in tasks]
183
+ import concurrent.futures
184
+
185
+ results: list[tuple[int, t.Any]] = []
186
+ with concurrent.futures.ThreadPoolExecutor(
187
+ max_workers=self.max_workers,
188
+ ) as executor:
189
+ future_to_task = {executor.submit(task): i for i, task in enumerate(tasks)}
190
+ for future in concurrent.futures.as_completed(
191
+ future_to_task,
192
+ timeout=timeout,
193
+ ):
194
+ task_index = future_to_task[future]
195
+ try:
196
+ result = future.result()
197
+ results.append((task_index, result))
198
+ except Exception as e:
199
+ results.append((task_index, e))
200
+ import operator
201
+
202
+ results.sort(key=operator.itemgetter(0))
203
+ return [result for _, result in results]
204
+
205
+
206
+ def optimize_subprocess_calls(
207
+ commands: list[list[str]],
208
+ cwd: Path | None = None,
209
+ ) -> list[t.Any]:
210
+ if len(commands) <= 1:
211
+ import subprocess
212
+
213
+ return [
214
+ subprocess.run(cmd, check=False, cwd=cwd, capture_output=True, text=True)
215
+ for cmd in commands
216
+ ]
217
+ executor = ParallelTaskExecutor()
218
+
219
+ def run_command(cmd: list[str]) -> t.Any:
220
+ import subprocess
221
+
222
+ return subprocess.run(cmd, check=False, cwd=cwd, capture_output=True, text=True)
223
+
224
+ import functools
225
+
226
+ tasks: list[t.Callable[[], t.Any]] = [
227
+ functools.partial(run_command, cmd) for cmd in commands
228
+ ]
229
+ return executor.execute_tasks(tasks)
230
+
231
+
232
+ _performance_monitor: PerformanceMonitor | None = None
233
+
234
+
235
+ def get_performance_monitor() -> PerformanceMonitor:
236
+ global _performance_monitor
237
+ if _performance_monitor is None:
238
+ _performance_monitor = PerformanceMonitor()
239
+ return _performance_monitor
240
+
241
+
242
+ def reset_performance_monitor() -> None:
243
+ global _performance_monitor
244
+ _performance_monitor = None