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,527 @@
1
+ import asyncio
2
+ import subprocess
3
+ import time
4
+ import typing as t
5
+ from contextlib import suppress
6
+ from dataclasses import dataclass, field
7
+ from enum import Enum
8
+ from pathlib import Path
9
+
10
+ from acb.depends import Inject, depends
11
+ from acb.logger import Logger
12
+
13
+ from crackerjack.config.hooks import HookDefinition, SecurityLevel
14
+ from crackerjack.models.protocols import (
15
+ AsyncCommandExecutorProtocol,
16
+ ParallelHookExecutorProtocol,
17
+ PerformanceCacheProtocol,
18
+ ServiceProtocol,
19
+ )
20
+ from crackerjack.models.results import ExecutionResult, ParallelExecutionResult
21
+
22
+
23
+ class ExecutionStrategy(Enum):
24
+ SEQUENTIAL = "sequential"
25
+ PARALLEL_SAFE = "parallel_safe"
26
+ PARALLEL_AGGRESSIVE = "parallel_aggressive"
27
+
28
+
29
+ @dataclass
30
+ class ExecutionGroup:
31
+ name: str
32
+ operations: list[t.Any]
33
+ max_workers: int = 3
34
+ timeout_seconds: int = 300
35
+ dependencies: set[str] = field(default_factory=set)
36
+ security_level: SecurityLevel = SecurityLevel.MEDIUM
37
+
38
+
39
+ class ParallelHookExecutor(ParallelHookExecutorProtocol, ServiceProtocol):
40
+ """Executes hooks in parallel or sequentially based on defined strategies.
41
+
42
+ This service manages the concurrent execution of various hooks (e.g., linting, formatting)
43
+ to optimize performance and provide faster feedback loops. It supports different execution
44
+ strategies and handles dependencies between hooks.
45
+ """
46
+
47
+ @depends.inject
48
+ def __init__(
49
+ self,
50
+ logger: Inject[Logger],
51
+ cache: Inject[PerformanceCacheProtocol],
52
+ max_workers: int = 3,
53
+ timeout_seconds: int = 300,
54
+ strategy: ExecutionStrategy = ExecutionStrategy.PARALLEL_SAFE,
55
+ ):
56
+ self.max_workers = max_workers
57
+ self.timeout_seconds = timeout_seconds
58
+ self.strategy = strategy
59
+ self._logger = logger
60
+ self._cache = cache
61
+
62
+ def initialize(self) -> None:
63
+ pass
64
+
65
+ def cleanup(self) -> None:
66
+ pass
67
+
68
+ async def async_cleanup(self) -> None:
69
+ """Async cleanup for any remaining tasks."""
70
+ with suppress(RuntimeError):
71
+ loop = asyncio.get_running_loop()
72
+ pending_tasks = (
73
+ task
74
+ for task in asyncio.all_tasks(loop)
75
+ if not task.done()
76
+ and ("hook" in str(task).lower() or "parallel" in str(task).lower())
77
+ )
78
+
79
+ for task in pending_tasks:
80
+ if not task.done():
81
+ try:
82
+ task.cancel()
83
+ await asyncio.wait_for(task, timeout=0.1)
84
+ except (TimeoutError, asyncio.CancelledError):
85
+ pass
86
+ except RuntimeError as e:
87
+ if "Event loop is closed" in str(e):
88
+ return
89
+ else:
90
+ raise
91
+
92
+ def health_check(self) -> bool:
93
+ return True
94
+
95
+ def shutdown(self) -> None:
96
+ pass
97
+
98
+ def metrics(self) -> dict[str, t.Any]:
99
+ return {}
100
+
101
+ def is_healthy(self) -> bool:
102
+ return True
103
+
104
+ def register_resource(self, resource: t.Any) -> None:
105
+ pass
106
+
107
+ def cleanup_resource(self, resource: t.Any) -> None:
108
+ pass
109
+
110
+ def record_error(self, error: Exception) -> None:
111
+ pass
112
+
113
+ def increment_requests(self) -> None:
114
+ pass
115
+
116
+ def get_custom_metric(self, name: str) -> t.Any:
117
+ return None
118
+
119
+ def set_custom_metric(self, name: str, value: t.Any) -> None:
120
+ pass
121
+
122
+ def analyze_hook_dependencies(
123
+ self,
124
+ hooks: list[HookDefinition],
125
+ ) -> dict[str, list[HookDefinition]]:
126
+ groups: dict[str, list[HookDefinition]] = {
127
+ "formatting": [],
128
+ "validation": [],
129
+ "security": [],
130
+ "comprehensive": [],
131
+ }
132
+
133
+ for hook in hooks:
134
+ if hook.is_formatting or hook.security_level == SecurityLevel.LOW:
135
+ groups["formatting"].append(hook)
136
+ elif hook.security_level == SecurityLevel.CRITICAL:
137
+ groups["security"].append(hook)
138
+ elif hook.name in {"check-yaml", "check-json", "check-toml"}:
139
+ groups["validation"].append(hook)
140
+ else:
141
+ groups["comprehensive"].append(hook)
142
+
143
+ return {k: v for k, v in groups.items() if v}
144
+
145
+ def can_execute_in_parallel(
146
+ self,
147
+ hook1: HookDefinition,
148
+ hook2: HookDefinition,
149
+ ) -> bool:
150
+ if hook1.security_level != hook2.security_level:
151
+ return False
152
+
153
+ if hook1.is_formatting and not hook2.is_formatting:
154
+ return False
155
+
156
+ safe_parallel_combinations = [
157
+ (lambda h: h.is_formatting, lambda h: h.is_formatting),
158
+ (
159
+ lambda h: h.name in {"check-yaml", "check-json", "check-toml"},
160
+ lambda h: h.name in {"check-yaml", "check-json", "check-toml"},
161
+ ),
162
+ (
163
+ lambda h: not h.is_formatting
164
+ and h.security_level == SecurityLevel.MEDIUM,
165
+ lambda h: not h.is_formatting
166
+ and h.security_level == SecurityLevel.MEDIUM,
167
+ ),
168
+ ]
169
+
170
+ for check1, check2 in safe_parallel_combinations:
171
+ if check1(hook1) and check2(hook2):
172
+ return True
173
+
174
+ return False
175
+
176
+ async def execute_hooks_parallel(
177
+ self,
178
+ hooks: list[HookDefinition],
179
+ hook_runner: t.Callable[[HookDefinition], t.Awaitable[ExecutionResult]],
180
+ ) -> ParallelExecutionResult:
181
+ start_time = time.time()
182
+
183
+ if self.strategy == ExecutionStrategy.SEQUENTIAL:
184
+ return await self._execute_sequential(hooks, hook_runner, start_time)
185
+
186
+ groups = self.analyze_hook_dependencies(hooks)
187
+ all_results: list[ExecutionResult] = []
188
+
189
+ self._logger.info(
190
+ f"Executing {len(hooks)} hooks in {len(groups)} parallel groups"
191
+ )
192
+
193
+ for group_name, group_hooks in groups.items():
194
+ if len(group_hooks) == 1 or not self._can_parallelize_group(group_hooks):
195
+ for hook in group_hooks:
196
+ result = await hook_runner(hook)
197
+ all_results.append(result)
198
+ else:
199
+ group_results = await self._execute_group_parallel(
200
+ group_hooks,
201
+ hook_runner,
202
+ group_name,
203
+ )
204
+ all_results.extend(group_results)
205
+
206
+ total_duration = time.time() - start_time
207
+ successful = sum(1 for r in all_results if r.success)
208
+ failed = len(all_results) - successful
209
+
210
+ return ParallelExecutionResult(
211
+ group_name="all_hooks",
212
+ total_operations=len(hooks),
213
+ successful_operations=successful,
214
+ failed_operations=failed,
215
+ total_duration_seconds=total_duration,
216
+ results=all_results,
217
+ )
218
+
219
+ async def _execute_sequential(
220
+ self,
221
+ hooks: list[HookDefinition],
222
+ hook_runner: t.Callable[[HookDefinition], t.Awaitable[ExecutionResult]],
223
+ start_time: float,
224
+ ) -> ParallelExecutionResult:
225
+ results: list[ExecutionResult] = []
226
+
227
+ for hook in hooks:
228
+ result = await hook_runner(hook)
229
+ results.append(result)
230
+
231
+ total_duration = time.time() - start_time
232
+ successful = sum(1 for r in results if r.success)
233
+ failed = len(results) - successful
234
+
235
+ return ParallelExecutionResult(
236
+ group_name="sequential",
237
+ total_operations=len(hooks),
238
+ successful_operations=successful,
239
+ failed_operations=failed,
240
+ total_duration_seconds=total_duration,
241
+ results=results,
242
+ )
243
+
244
+ def _can_parallelize_group(self, hooks: list[HookDefinition]) -> bool:
245
+ if len(hooks) < 2:
246
+ return False
247
+
248
+ for i, hook1 in enumerate(hooks):
249
+ for hook2 in hooks[i + 1 :]:
250
+ if not self.can_execute_in_parallel(hook1, hook2):
251
+ return False
252
+
253
+ return True
254
+
255
+ async def _execute_group_parallel(
256
+ self,
257
+ hooks: list[HookDefinition],
258
+ hook_runner: t.Callable[[HookDefinition], t.Awaitable[ExecutionResult]],
259
+ group_name: str,
260
+ ) -> list[ExecutionResult]:
261
+ self._logger.debug(f"Executing {len(hooks)} {group_name} hooks in parallel")
262
+
263
+ max_workers = min(self.max_workers, len(hooks))
264
+
265
+ semaphore = asyncio.Semaphore(max_workers)
266
+
267
+ async def run_with_semaphore(hook: HookDefinition) -> ExecutionResult:
268
+ async with semaphore:
269
+ return await hook_runner(hook)
270
+
271
+ tasks = [run_with_semaphore(hook) for hook in hooks]
272
+ results = await asyncio.gather(*tasks, return_exceptions=True)
273
+
274
+ processed_results: list[ExecutionResult] = []
275
+ for i, result in enumerate(results):
276
+ if isinstance(result, Exception):
277
+ error_result = ExecutionResult(
278
+ operation_id=hooks[i].name,
279
+ success=False,
280
+ duration_seconds=0.0,
281
+ error=str(result),
282
+ )
283
+ processed_results.append(error_result)
284
+ self._logger.error(
285
+ f"Hook {hooks[i].name} failed with exception: {result}"
286
+ )
287
+ else:
288
+ processed_results.append(t.cast(ExecutionResult, result))
289
+
290
+ successful = sum(1 for r in processed_results if r.success)
291
+ self._logger.info(
292
+ f"Parallel {group_name} execution: {successful}/{len(hooks)} succeeded"
293
+ )
294
+
295
+ return processed_results
296
+
297
+
298
+ class AsyncCommandExecutor(AsyncCommandExecutorProtocol, ServiceProtocol):
299
+ """Executes shell commands asynchronously, with caching capabilities.
300
+
301
+ This service provides a robust way to run external commands without blocking
302
+ the main event loop, supporting parallel execution and caching of results
303
+ to improve performance and responsiveness.
304
+ """
305
+
306
+ @depends.inject
307
+ def __init__(
308
+ self,
309
+ logger: Inject[Logger],
310
+ cache: Inject[PerformanceCacheProtocol],
311
+ max_workers: int = 4,
312
+ cache_results: bool = True,
313
+ ):
314
+ self.max_workers = max_workers
315
+ self.cache_results = cache_results
316
+ self._logger = logger
317
+ self._cache = cache
318
+ from concurrent.futures import ThreadPoolExecutor
319
+
320
+ self._thread_pool = ThreadPoolExecutor(max_workers=max_workers)
321
+
322
+ def shutdown(self) -> None:
323
+ if hasattr(self, "_thread_pool"):
324
+ self._thread_pool.shutdown(wait=True)
325
+
326
+ def metrics(self) -> dict[str, t.Any]:
327
+ return {}
328
+
329
+ def is_healthy(self) -> bool:
330
+ return True
331
+
332
+ def register_resource(self, resource: t.Any) -> None:
333
+ pass
334
+
335
+ def cleanup_resource(self, resource: t.Any) -> None:
336
+ pass
337
+
338
+ def record_error(self, error: Exception) -> None:
339
+ pass
340
+
341
+ def increment_requests(self) -> None:
342
+ pass
343
+
344
+ def get_custom_metric(self, name: str) -> t.Any:
345
+ return None
346
+
347
+ def set_custom_metric(self, name: str, value: t.Any) -> None:
348
+ pass
349
+
350
+ async def async_cleanup(self) -> None:
351
+ """Async cleanup for any remaining command executor tasks."""
352
+ with suppress(RuntimeError):
353
+ loop = asyncio.get_running_loop()
354
+ pending_tasks = (
355
+ task
356
+ for task in asyncio.all_tasks(loop)
357
+ if not task.done()
358
+ and ("command" in str(task).lower() or "async" in str(task).lower())
359
+ )
360
+
361
+ for task in pending_tasks:
362
+ if not task.done():
363
+ try:
364
+ task.cancel()
365
+ await asyncio.wait_for(task, timeout=0.1)
366
+ except (TimeoutError, asyncio.CancelledError):
367
+ pass
368
+ except RuntimeError as e:
369
+ if "Event loop is closed" in str(e):
370
+ return
371
+ else:
372
+ raise
373
+
374
+ async def execute_command(
375
+ self,
376
+ command: list[str],
377
+ cwd: Path | None = None,
378
+ timeout: int = 60,
379
+ cache_ttl: int = 120,
380
+ ) -> ExecutionResult:
381
+ if self.cache_results:
382
+ cached_result = await self._get_cached_result(command, cwd)
383
+ if cached_result:
384
+ self._logger.debug(
385
+ f"Using cached result for command: {' '.join(command)}"
386
+ )
387
+ return cached_result
388
+
389
+ start_time = time.time()
390
+ result = await self._run_command_async(command, cwd, timeout)
391
+ result.duration_seconds = time.time() - start_time
392
+
393
+ if self.cache_results and result.success:
394
+ await self._cache_result(command, result, cache_ttl, cwd)
395
+
396
+ return result
397
+
398
+ async def execute_commands_batch(
399
+ self,
400
+ commands: list[tuple[list[str], Path | None]],
401
+ timeout: int = 60,
402
+ ) -> list[ExecutionResult]:
403
+ self._logger.info(f"Executing {len(commands)} commands in batch")
404
+
405
+ tasks = [self.execute_command(cmd, cwd, timeout) for cmd, cwd in commands]
406
+
407
+ results = await asyncio.gather(*tasks, return_exceptions=True)
408
+
409
+ processed_results: list[ExecutionResult] = []
410
+ for i, result in enumerate(results):
411
+ if isinstance(result, Exception):
412
+ error_result = ExecutionResult(
413
+ operation_id=f"command_{i}",
414
+ success=False,
415
+ duration_seconds=0.0,
416
+ error=str(result),
417
+ )
418
+ processed_results.append(error_result)
419
+ else:
420
+ processed_results.append(t.cast(ExecutionResult, result))
421
+
422
+ successful = sum(1 for r in processed_results if r.success)
423
+ self._logger.info(
424
+ f"Batch execution: {successful}/{len(commands)} commands succeeded"
425
+ )
426
+
427
+ return processed_results
428
+
429
+ async def _run_command_async(
430
+ self,
431
+ command: list[str],
432
+ cwd: Path | None = None,
433
+ timeout: int = 60,
434
+ ) -> ExecutionResult:
435
+ loop = asyncio.get_event_loop()
436
+
437
+ def run_sync_command() -> ExecutionResult:
438
+ try:
439
+ result = subprocess.run(
440
+ command,
441
+ cwd=cwd,
442
+ capture_output=True,
443
+ text=True,
444
+ timeout=timeout,
445
+ check=False,
446
+ )
447
+
448
+ return ExecutionResult(
449
+ operation_id=" ".join(command),
450
+ success=result.returncode == 0,
451
+ duration_seconds=0.0,
452
+ output=result.stdout,
453
+ error=result.stderr,
454
+ exit_code=result.returncode,
455
+ )
456
+
457
+ except subprocess.TimeoutExpired:
458
+ return ExecutionResult(
459
+ operation_id=" ".join(command),
460
+ success=False,
461
+ duration_seconds=timeout,
462
+ error=f"Command timeout after {timeout}s",
463
+ exit_code=-1,
464
+ )
465
+ except Exception as e:
466
+ return ExecutionResult(
467
+ operation_id=" ".join(command),
468
+ success=False,
469
+ duration_seconds=0.0,
470
+ error=str(e),
471
+ exit_code=-1,
472
+ )
473
+
474
+ return await loop.run_in_executor(self._thread_pool, run_sync_command)
475
+
476
+ async def _get_cached_result(
477
+ self,
478
+ command: list[str],
479
+ cwd: Path | None = None,
480
+ ) -> ExecutionResult | None:
481
+ cache_result = self._cache.get(self._get_cache_key(command, cwd))
482
+ return t.cast(ExecutionResult | None, cache_result)
483
+
484
+ async def _cache_result(
485
+ self,
486
+ command: list[str],
487
+ result: ExecutionResult,
488
+ ttl_seconds: int,
489
+ cwd: Path | None = None,
490
+ ) -> None:
491
+ self._cache.set(self._get_cache_key(command, cwd), result, ttl_seconds)
492
+
493
+ def _get_cache_key(self, command: list[str], cwd: Path | None) -> str:
494
+ key_parts = [" ".join(command)]
495
+ if cwd:
496
+ key_parts.append(str(cwd))
497
+ return ":".join(key_parts)
498
+
499
+
500
+ _parallel_executor: ParallelHookExecutor | None = None
501
+ _async_executor: AsyncCommandExecutor | None = None
502
+
503
+
504
+ def get_parallel_executor(
505
+ max_workers: int = 3,
506
+ strategy: ExecutionStrategy = ExecutionStrategy.PARALLEL_SAFE,
507
+ ) -> ParallelHookExecutor:
508
+ global _parallel_executor
509
+ if _parallel_executor is None:
510
+ _parallel_executor = ParallelHookExecutor(
511
+ logger=depends.get_sync(Logger),
512
+ cache=depends.get_sync(PerformanceCacheProtocol),
513
+ max_workers=max_workers,
514
+ strategy=strategy,
515
+ )
516
+ return _parallel_executor
517
+
518
+
519
+ def get_async_executor(max_workers: int = 4) -> AsyncCommandExecutor:
520
+ global _async_executor
521
+ if _async_executor is None:
522
+ _async_executor = AsyncCommandExecutor(
523
+ logger=depends.get_sync(Logger),
524
+ cache=depends.get_sync(PerformanceCacheProtocol),
525
+ max_workers=max_workers,
526
+ )
527
+ return _async_executor