crackerjack 0.37.9__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 (425) hide show
  1. crackerjack/README.md +19 -0
  2. crackerjack/__init__.py +30 -1
  3. crackerjack/__main__.py +342 -1263
  4. crackerjack/adapters/README.md +18 -0
  5. crackerjack/adapters/__init__.py +27 -5
  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/{rust_tool_manager.py → lsp/_manager.py} +3 -3
  27. crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
  28. crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
  29. crackerjack/adapters/refactor/README.md +59 -0
  30. crackerjack/adapters/refactor/__init__.py +12 -0
  31. crackerjack/adapters/refactor/creosote.py +318 -0
  32. crackerjack/adapters/refactor/refurb.py +406 -0
  33. crackerjack/adapters/refactor/skylos.py +494 -0
  34. crackerjack/adapters/sast/README.md +132 -0
  35. crackerjack/adapters/sast/__init__.py +32 -0
  36. crackerjack/adapters/sast/_base.py +201 -0
  37. crackerjack/adapters/sast/bandit.py +423 -0
  38. crackerjack/adapters/sast/pyscn.py +405 -0
  39. crackerjack/adapters/sast/semgrep.py +241 -0
  40. crackerjack/adapters/security/README.md +111 -0
  41. crackerjack/adapters/security/__init__.py +17 -0
  42. crackerjack/adapters/security/gitleaks.py +339 -0
  43. crackerjack/adapters/type/README.md +52 -0
  44. crackerjack/adapters/type/__init__.py +12 -0
  45. crackerjack/adapters/type/pyrefly.py +402 -0
  46. crackerjack/adapters/type/ty.py +402 -0
  47. crackerjack/adapters/type/zuban.py +522 -0
  48. crackerjack/adapters/utility/README.md +51 -0
  49. crackerjack/adapters/utility/__init__.py +10 -0
  50. crackerjack/adapters/utility/checks.py +884 -0
  51. crackerjack/agents/README.md +264 -0
  52. crackerjack/agents/__init__.py +40 -12
  53. crackerjack/agents/base.py +1 -0
  54. crackerjack/agents/claude_code_bridge.py +641 -0
  55. crackerjack/agents/coordinator.py +49 -53
  56. crackerjack/agents/dry_agent.py +187 -3
  57. crackerjack/agents/enhanced_coordinator.py +279 -0
  58. crackerjack/agents/enhanced_proactive_agent.py +185 -0
  59. crackerjack/agents/error_middleware.py +53 -0
  60. crackerjack/agents/formatting_agent.py +6 -8
  61. crackerjack/agents/helpers/__init__.py +9 -0
  62. crackerjack/agents/helpers/performance/__init__.py +22 -0
  63. crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
  64. crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
  65. crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
  66. crackerjack/agents/helpers/refactoring/__init__.py +22 -0
  67. crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
  68. crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
  69. crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
  70. crackerjack/agents/helpers/test_creation/__init__.py +19 -0
  71. crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
  72. crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
  73. crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
  74. crackerjack/agents/performance_agent.py +121 -1152
  75. crackerjack/agents/refactoring_agent.py +156 -655
  76. crackerjack/agents/semantic_agent.py +479 -0
  77. crackerjack/agents/semantic_helpers.py +356 -0
  78. crackerjack/agents/test_creation_agent.py +19 -1605
  79. crackerjack/api.py +5 -7
  80. crackerjack/cli/README.md +394 -0
  81. crackerjack/cli/__init__.py +1 -1
  82. crackerjack/cli/cache_handlers.py +23 -18
  83. crackerjack/cli/cache_handlers_enhanced.py +1 -4
  84. crackerjack/cli/facade.py +70 -8
  85. crackerjack/cli/formatting.py +13 -0
  86. crackerjack/cli/handlers/__init__.py +85 -0
  87. crackerjack/cli/handlers/advanced.py +103 -0
  88. crackerjack/cli/handlers/ai_features.py +62 -0
  89. crackerjack/cli/handlers/analytics.py +479 -0
  90. crackerjack/cli/handlers/changelog.py +271 -0
  91. crackerjack/cli/handlers/config_handlers.py +16 -0
  92. crackerjack/cli/handlers/coverage.py +84 -0
  93. crackerjack/cli/handlers/documentation.py +280 -0
  94. crackerjack/cli/handlers/main_handlers.py +497 -0
  95. crackerjack/cli/handlers/monitoring.py +371 -0
  96. crackerjack/cli/handlers.py +249 -49
  97. crackerjack/cli/interactive.py +8 -5
  98. crackerjack/cli/options.py +203 -110
  99. crackerjack/cli/semantic_handlers.py +292 -0
  100. crackerjack/cli/version.py +19 -0
  101. crackerjack/code_cleaner.py +60 -24
  102. crackerjack/config/README.md +472 -0
  103. crackerjack/config/__init__.py +256 -0
  104. crackerjack/config/global_lock_config.py +191 -54
  105. crackerjack/config/hooks.py +188 -16
  106. crackerjack/config/loader.py +239 -0
  107. crackerjack/config/settings.py +141 -0
  108. crackerjack/config/tool_commands.py +331 -0
  109. crackerjack/core/README.md +393 -0
  110. crackerjack/core/async_workflow_orchestrator.py +79 -53
  111. crackerjack/core/autofix_coordinator.py +22 -9
  112. crackerjack/core/container.py +10 -9
  113. crackerjack/core/enhanced_container.py +9 -9
  114. crackerjack/core/performance.py +1 -1
  115. crackerjack/core/performance_monitor.py +5 -3
  116. crackerjack/core/phase_coordinator.py +1018 -634
  117. crackerjack/core/proactive_workflow.py +3 -3
  118. crackerjack/core/retry.py +275 -0
  119. crackerjack/core/service_watchdog.py +167 -23
  120. crackerjack/core/session_coordinator.py +187 -382
  121. crackerjack/core/timeout_manager.py +161 -44
  122. crackerjack/core/workflow/__init__.py +21 -0
  123. crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
  124. crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
  125. crackerjack/core/workflow/workflow_issue_parser.py +714 -0
  126. crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
  127. crackerjack/core/workflow/workflow_security_gates.py +400 -0
  128. crackerjack/core/workflow_orchestrator.py +1247 -953
  129. crackerjack/data/README.md +11 -0
  130. crackerjack/data/__init__.py +8 -0
  131. crackerjack/data/models.py +79 -0
  132. crackerjack/data/repository.py +210 -0
  133. crackerjack/decorators/README.md +180 -0
  134. crackerjack/decorators/__init__.py +35 -0
  135. crackerjack/decorators/error_handling.py +649 -0
  136. crackerjack/decorators/error_handling_decorators.py +334 -0
  137. crackerjack/decorators/helpers.py +58 -0
  138. crackerjack/decorators/patterns.py +281 -0
  139. crackerjack/decorators/utils.py +58 -0
  140. crackerjack/docs/README.md +11 -0
  141. crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
  142. crackerjack/documentation/README.md +11 -0
  143. crackerjack/documentation/ai_templates.py +1 -1
  144. crackerjack/documentation/dual_output_generator.py +11 -9
  145. crackerjack/documentation/reference_generator.py +104 -59
  146. crackerjack/dynamic_config.py +52 -61
  147. crackerjack/errors.py +1 -1
  148. crackerjack/events/README.md +11 -0
  149. crackerjack/events/__init__.py +16 -0
  150. crackerjack/events/telemetry.py +175 -0
  151. crackerjack/events/workflow_bus.py +346 -0
  152. crackerjack/exceptions/README.md +301 -0
  153. crackerjack/exceptions/__init__.py +5 -0
  154. crackerjack/exceptions/config.py +4 -0
  155. crackerjack/exceptions/tool_execution_error.py +245 -0
  156. crackerjack/executors/README.md +591 -0
  157. crackerjack/executors/__init__.py +2 -0
  158. crackerjack/executors/async_hook_executor.py +539 -77
  159. crackerjack/executors/cached_hook_executor.py +3 -3
  160. crackerjack/executors/hook_executor.py +967 -102
  161. crackerjack/executors/hook_lock_manager.py +31 -22
  162. crackerjack/executors/individual_hook_executor.py +66 -32
  163. crackerjack/executors/lsp_aware_hook_executor.py +136 -57
  164. crackerjack/executors/progress_hook_executor.py +282 -0
  165. crackerjack/executors/tool_proxy.py +23 -7
  166. crackerjack/hooks/README.md +485 -0
  167. crackerjack/hooks/lsp_hook.py +8 -9
  168. crackerjack/intelligence/README.md +557 -0
  169. crackerjack/interactive.py +37 -10
  170. crackerjack/managers/README.md +369 -0
  171. crackerjack/managers/async_hook_manager.py +41 -57
  172. crackerjack/managers/hook_manager.py +449 -79
  173. crackerjack/managers/publish_manager.py +81 -36
  174. crackerjack/managers/test_command_builder.py +290 -12
  175. crackerjack/managers/test_executor.py +93 -8
  176. crackerjack/managers/test_manager.py +1082 -75
  177. crackerjack/managers/test_progress.py +118 -26
  178. crackerjack/mcp/README.md +374 -0
  179. crackerjack/mcp/cache.py +25 -2
  180. crackerjack/mcp/client_runner.py +35 -18
  181. crackerjack/mcp/context.py +9 -9
  182. crackerjack/mcp/dashboard.py +24 -8
  183. crackerjack/mcp/enhanced_progress_monitor.py +34 -23
  184. crackerjack/mcp/file_monitor.py +27 -6
  185. crackerjack/mcp/progress_components.py +45 -34
  186. crackerjack/mcp/progress_monitor.py +6 -9
  187. crackerjack/mcp/rate_limiter.py +11 -7
  188. crackerjack/mcp/server.py +2 -0
  189. crackerjack/mcp/server_core.py +187 -55
  190. crackerjack/mcp/service_watchdog.py +12 -9
  191. crackerjack/mcp/task_manager.py +2 -2
  192. crackerjack/mcp/tools/README.md +27 -0
  193. crackerjack/mcp/tools/__init__.py +2 -0
  194. crackerjack/mcp/tools/core_tools.py +75 -52
  195. crackerjack/mcp/tools/execution_tools.py +87 -31
  196. crackerjack/mcp/tools/intelligence_tools.py +2 -2
  197. crackerjack/mcp/tools/proactive_tools.py +1 -1
  198. crackerjack/mcp/tools/semantic_tools.py +584 -0
  199. crackerjack/mcp/tools/utility_tools.py +180 -132
  200. crackerjack/mcp/tools/workflow_executor.py +87 -46
  201. crackerjack/mcp/websocket/README.md +31 -0
  202. crackerjack/mcp/websocket/app.py +11 -1
  203. crackerjack/mcp/websocket/event_bridge.py +188 -0
  204. crackerjack/mcp/websocket/jobs.py +27 -4
  205. crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
  206. crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
  207. crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
  208. crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
  209. crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
  210. crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
  211. crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
  212. crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
  213. crackerjack/mcp/websocket/monitoring/factory.py +109 -0
  214. crackerjack/mcp/websocket/monitoring/filters.py +10 -0
  215. crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
  216. crackerjack/mcp/websocket/monitoring/models.py +90 -0
  217. crackerjack/mcp/websocket/monitoring/utils.py +171 -0
  218. crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
  219. crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
  220. crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
  221. crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
  222. crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
  223. crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
  224. crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
  225. crackerjack/mcp/websocket/server.py +1 -3
  226. crackerjack/mcp/websocket/websocket_handler.py +107 -6
  227. crackerjack/models/README.md +308 -0
  228. crackerjack/models/__init__.py +10 -1
  229. crackerjack/models/config.py +639 -22
  230. crackerjack/models/config_adapter.py +6 -6
  231. crackerjack/models/protocols.py +1167 -23
  232. crackerjack/models/pydantic_models.py +320 -0
  233. crackerjack/models/qa_config.py +145 -0
  234. crackerjack/models/qa_results.py +134 -0
  235. crackerjack/models/results.py +35 -0
  236. crackerjack/models/semantic_models.py +258 -0
  237. crackerjack/models/task.py +19 -3
  238. crackerjack/models/test_models.py +60 -0
  239. crackerjack/monitoring/README.md +11 -0
  240. crackerjack/monitoring/ai_agent_watchdog.py +5 -4
  241. crackerjack/monitoring/metrics_collector.py +4 -3
  242. crackerjack/monitoring/regression_prevention.py +4 -3
  243. crackerjack/monitoring/websocket_server.py +4 -241
  244. crackerjack/orchestration/README.md +340 -0
  245. crackerjack/orchestration/__init__.py +43 -0
  246. crackerjack/orchestration/advanced_orchestrator.py +20 -67
  247. crackerjack/orchestration/cache/README.md +312 -0
  248. crackerjack/orchestration/cache/__init__.py +37 -0
  249. crackerjack/orchestration/cache/memory_cache.py +338 -0
  250. crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
  251. crackerjack/orchestration/config.py +297 -0
  252. crackerjack/orchestration/coverage_improvement.py +13 -6
  253. crackerjack/orchestration/execution_strategies.py +6 -6
  254. crackerjack/orchestration/hook_orchestrator.py +1398 -0
  255. crackerjack/orchestration/strategies/README.md +401 -0
  256. crackerjack/orchestration/strategies/__init__.py +39 -0
  257. crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
  258. crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
  259. crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
  260. crackerjack/orchestration/test_progress_streamer.py +1 -1
  261. crackerjack/plugins/README.md +11 -0
  262. crackerjack/plugins/hooks.py +3 -2
  263. crackerjack/plugins/loader.py +3 -3
  264. crackerjack/plugins/managers.py +1 -1
  265. crackerjack/py313.py +191 -0
  266. crackerjack/security/README.md +11 -0
  267. crackerjack/services/README.md +374 -0
  268. crackerjack/services/__init__.py +8 -21
  269. crackerjack/services/ai/README.md +295 -0
  270. crackerjack/services/ai/__init__.py +7 -0
  271. crackerjack/services/ai/advanced_optimizer.py +878 -0
  272. crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
  273. crackerjack/services/ai/embeddings.py +444 -0
  274. crackerjack/services/ai/intelligent_commit.py +328 -0
  275. crackerjack/services/ai/predictive_analytics.py +510 -0
  276. crackerjack/services/api_extractor.py +5 -3
  277. crackerjack/services/bounded_status_operations.py +45 -5
  278. crackerjack/services/cache.py +249 -318
  279. crackerjack/services/changelog_automation.py +7 -3
  280. crackerjack/services/command_execution_service.py +305 -0
  281. crackerjack/services/config_integrity.py +83 -39
  282. crackerjack/services/config_merge.py +9 -6
  283. crackerjack/services/config_service.py +198 -0
  284. crackerjack/services/config_template.py +13 -26
  285. crackerjack/services/coverage_badge_service.py +6 -4
  286. crackerjack/services/coverage_ratchet.py +53 -27
  287. crackerjack/services/debug.py +18 -7
  288. crackerjack/services/dependency_analyzer.py +4 -4
  289. crackerjack/services/dependency_monitor.py +13 -13
  290. crackerjack/services/documentation_generator.py +4 -2
  291. crackerjack/services/documentation_service.py +62 -33
  292. crackerjack/services/enhanced_filesystem.py +81 -27
  293. crackerjack/services/enterprise_optimizer.py +1 -1
  294. crackerjack/services/error_pattern_analyzer.py +10 -10
  295. crackerjack/services/file_filter.py +221 -0
  296. crackerjack/services/file_hasher.py +5 -7
  297. crackerjack/services/file_io_service.py +361 -0
  298. crackerjack/services/file_modifier.py +615 -0
  299. crackerjack/services/filesystem.py +80 -109
  300. crackerjack/services/git.py +99 -5
  301. crackerjack/services/health_metrics.py +4 -6
  302. crackerjack/services/heatmap_generator.py +12 -3
  303. crackerjack/services/incremental_executor.py +380 -0
  304. crackerjack/services/initialization.py +101 -49
  305. crackerjack/services/log_manager.py +2 -2
  306. crackerjack/services/logging.py +120 -68
  307. crackerjack/services/lsp_client.py +12 -12
  308. crackerjack/services/memory_optimizer.py +27 -22
  309. crackerjack/services/monitoring/README.md +30 -0
  310. crackerjack/services/monitoring/__init__.py +9 -0
  311. crackerjack/services/monitoring/dependency_monitor.py +678 -0
  312. crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
  313. crackerjack/services/monitoring/health_metrics.py +716 -0
  314. crackerjack/services/monitoring/metrics.py +587 -0
  315. crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
  316. crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
  317. crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
  318. crackerjack/services/parallel_executor.py +166 -55
  319. crackerjack/services/patterns/__init__.py +142 -0
  320. crackerjack/services/patterns/agents.py +107 -0
  321. crackerjack/services/patterns/code/__init__.py +15 -0
  322. crackerjack/services/patterns/code/detection.py +118 -0
  323. crackerjack/services/patterns/code/imports.py +107 -0
  324. crackerjack/services/patterns/code/paths.py +159 -0
  325. crackerjack/services/patterns/code/performance.py +119 -0
  326. crackerjack/services/patterns/code/replacement.py +36 -0
  327. crackerjack/services/patterns/core.py +212 -0
  328. crackerjack/services/patterns/documentation/__init__.py +14 -0
  329. crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
  330. crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
  331. crackerjack/services/patterns/documentation/docstrings.py +89 -0
  332. crackerjack/services/patterns/formatting.py +226 -0
  333. crackerjack/services/patterns/operations.py +339 -0
  334. crackerjack/services/patterns/security/__init__.py +23 -0
  335. crackerjack/services/patterns/security/code_injection.py +122 -0
  336. crackerjack/services/patterns/security/credentials.py +190 -0
  337. crackerjack/services/patterns/security/path_traversal.py +221 -0
  338. crackerjack/services/patterns/security/unsafe_operations.py +216 -0
  339. crackerjack/services/patterns/templates.py +62 -0
  340. crackerjack/services/patterns/testing/__init__.py +18 -0
  341. crackerjack/services/patterns/testing/error_patterns.py +107 -0
  342. crackerjack/services/patterns/testing/pytest_output.py +126 -0
  343. crackerjack/services/patterns/tool_output/__init__.py +16 -0
  344. crackerjack/services/patterns/tool_output/bandit.py +72 -0
  345. crackerjack/services/patterns/tool_output/other.py +97 -0
  346. crackerjack/services/patterns/tool_output/pyright.py +67 -0
  347. crackerjack/services/patterns/tool_output/ruff.py +44 -0
  348. crackerjack/services/patterns/url_sanitization.py +114 -0
  349. crackerjack/services/patterns/utilities.py +42 -0
  350. crackerjack/services/patterns/utils.py +339 -0
  351. crackerjack/services/patterns/validation.py +46 -0
  352. crackerjack/services/patterns/versioning.py +62 -0
  353. crackerjack/services/predictive_analytics.py +21 -8
  354. crackerjack/services/profiler.py +280 -0
  355. crackerjack/services/quality/README.md +415 -0
  356. crackerjack/services/quality/__init__.py +11 -0
  357. crackerjack/services/quality/anomaly_detector.py +392 -0
  358. crackerjack/services/quality/pattern_cache.py +333 -0
  359. crackerjack/services/quality/pattern_detector.py +479 -0
  360. crackerjack/services/quality/qa_orchestrator.py +491 -0
  361. crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
  362. crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
  363. crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
  364. crackerjack/services/regex_patterns.py +58 -2987
  365. crackerjack/services/regex_utils.py +55 -29
  366. crackerjack/services/secure_status_formatter.py +42 -15
  367. crackerjack/services/secure_subprocess.py +35 -2
  368. crackerjack/services/security.py +16 -8
  369. crackerjack/services/server_manager.py +40 -51
  370. crackerjack/services/smart_scheduling.py +46 -6
  371. crackerjack/services/status_authentication.py +3 -3
  372. crackerjack/services/thread_safe_status_collector.py +1 -0
  373. crackerjack/services/tool_filter.py +368 -0
  374. crackerjack/services/tool_version_service.py +9 -5
  375. crackerjack/services/unified_config.py +43 -351
  376. crackerjack/services/vector_store.py +689 -0
  377. crackerjack/services/version_analyzer.py +6 -4
  378. crackerjack/services/version_checker.py +14 -8
  379. crackerjack/services/zuban_lsp_service.py +5 -4
  380. crackerjack/slash_commands/README.md +11 -0
  381. crackerjack/slash_commands/init.md +2 -12
  382. crackerjack/slash_commands/run.md +84 -50
  383. crackerjack/tools/README.md +11 -0
  384. crackerjack/tools/__init__.py +30 -0
  385. crackerjack/tools/_git_utils.py +105 -0
  386. crackerjack/tools/check_added_large_files.py +139 -0
  387. crackerjack/tools/check_ast.py +105 -0
  388. crackerjack/tools/check_json.py +103 -0
  389. crackerjack/tools/check_jsonschema.py +297 -0
  390. crackerjack/tools/check_toml.py +103 -0
  391. crackerjack/tools/check_yaml.py +110 -0
  392. crackerjack/tools/codespell_wrapper.py +72 -0
  393. crackerjack/tools/end_of_file_fixer.py +202 -0
  394. crackerjack/tools/format_json.py +128 -0
  395. crackerjack/tools/mdformat_wrapper.py +114 -0
  396. crackerjack/tools/trailing_whitespace.py +198 -0
  397. crackerjack/tools/validate_regex_patterns.py +7 -3
  398. crackerjack/ui/README.md +11 -0
  399. crackerjack/ui/dashboard_renderer.py +28 -0
  400. crackerjack/ui/templates/README.md +11 -0
  401. crackerjack/utils/console_utils.py +13 -0
  402. crackerjack/utils/dependency_guard.py +230 -0
  403. crackerjack/utils/retry_utils.py +275 -0
  404. crackerjack/workflows/README.md +590 -0
  405. crackerjack/workflows/__init__.py +46 -0
  406. crackerjack/workflows/actions.py +811 -0
  407. crackerjack/workflows/auto_fix.py +444 -0
  408. crackerjack/workflows/container_builder.py +499 -0
  409. crackerjack/workflows/definitions.py +443 -0
  410. crackerjack/workflows/engine.py +177 -0
  411. crackerjack/workflows/event_bridge.py +242 -0
  412. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
  413. crackerjack-0.45.2.dist-info/RECORD +478 -0
  414. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
  415. crackerjack/managers/test_manager_backup.py +0 -1075
  416. crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
  417. crackerjack/mixins/__init__.py +0 -3
  418. crackerjack/mixins/error_handling.py +0 -145
  419. crackerjack/services/config.py +0 -358
  420. crackerjack/ui/server_panels.py +0 -125
  421. crackerjack-0.37.9.dist-info/RECORD +0 -231
  422. /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
  423. /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
  424. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
  425. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +0,0 @@
1
- from .error_handling import ErrorHandlingMixin
2
-
3
- __all__ = ["ErrorHandlingMixin"]
@@ -1,145 +0,0 @@
1
- import subprocess
2
- import typing as t
3
- from pathlib import Path
4
-
5
- from rich.console import Console
6
-
7
-
8
- class ErrorHandlingMixin:
9
- def __init__(self) -> None:
10
- self.console: Console
11
- self.logger: t.Any
12
-
13
- def handle_subprocess_error(
14
- self,
15
- error: Exception,
16
- command: list[str],
17
- operation_name: str,
18
- critical: bool = False,
19
- ) -> bool:
20
- error_msg = f"{operation_name} failed: {error}"
21
-
22
- if hasattr(self, "logger") and self.logger:
23
- self.logger.error(
24
- error_msg,
25
- command=" ".join(command),
26
- error_type=type(error).__name__,
27
- critical=critical,
28
- )
29
-
30
- if critical:
31
- self.console.print(f"[red]🚨 CRITICAL: {error_msg}[/red]")
32
- else:
33
- self.console.print(f"[red]❌ {error_msg}[/red]")
34
-
35
- return False
36
-
37
- def handle_file_operation_error(
38
- self,
39
- error: Exception,
40
- file_path: Path,
41
- operation: str,
42
- critical: bool = False,
43
- ) -> bool:
44
- error_msg = f"Failed to {operation} {file_path}: {error}"
45
-
46
- if hasattr(self, "logger") and self.logger:
47
- self.logger.error(
48
- error_msg,
49
- file_path=str(file_path),
50
- operation=operation,
51
- error_type=type(error).__name__,
52
- critical=critical,
53
- )
54
-
55
- if critical:
56
- self.console.print(f"[red]🚨 CRITICAL: {error_msg}[/red]")
57
- else:
58
- self.console.print(f"[red]❌ {error_msg}[/red]")
59
-
60
- return False
61
-
62
- def handle_timeout_error(
63
- self,
64
- operation_name: str,
65
- timeout_seconds: float,
66
- command: list[str] | None = None,
67
- ) -> bool:
68
- error_msg = f"{operation_name} timed out after {timeout_seconds}s"
69
-
70
- if hasattr(self, "logger") and self.logger:
71
- self.logger.warning(
72
- error_msg,
73
- timeout=timeout_seconds,
74
- command=" ".join(command) if command else None,
75
- )
76
-
77
- self.console.print(f"[yellow]⏰ {error_msg}[/yellow]")
78
-
79
- return False
80
-
81
- def log_operation_success(
82
- self,
83
- operation_name: str,
84
- details: dict[str, t.Any] | None = None,
85
- ) -> None:
86
- if hasattr(self, "logger") and self.logger:
87
- self.logger.info(
88
- f"{operation_name} completed successfully", **(details or {})
89
- )
90
-
91
- def validate_required_tools(
92
- self,
93
- tools: dict[str, str],
94
- operation_name: str,
95
- ) -> bool:
96
- missing_tools = []
97
-
98
- for tool_name, command in tools.items():
99
- try:
100
- subprocess.run(
101
- [command, "--version"],
102
- capture_output=True,
103
- check=True,
104
- timeout=5,
105
- )
106
- except (
107
- subprocess.CalledProcessError,
108
- subprocess.TimeoutExpired,
109
- FileNotFoundError,
110
- ):
111
- missing_tools.append(tool_name)
112
-
113
- if missing_tools:
114
- error_msg = f"Missing required tools for {operation_name}: {', '.join(missing_tools)}"
115
-
116
- if hasattr(self, "logger") and self.logger:
117
- self.logger.error(
118
- error_msg,
119
- missing_tools=missing_tools,
120
- operation=operation_name,
121
- )
122
-
123
- self.console.print(f"[red]❌ {error_msg}[/red]")
124
- return False
125
-
126
- return True
127
-
128
- def safe_get_attribute(
129
- self,
130
- obj: t.Any,
131
- attribute: str,
132
- default: t.Any = None,
133
- operation_name: str = "attribute access",
134
- ) -> t.Any:
135
- try:
136
- return getattr(obj, attribute, default)
137
- except Exception as e:
138
- if hasattr(self, "logger") and self.logger:
139
- self.logger.warning(
140
- f"Error accessing {attribute} during {operation_name}: {e}",
141
- attribute=attribute,
142
- operation=operation_name,
143
- error_type=type(e).__name__,
144
- )
145
- return default
@@ -1,358 +0,0 @@
1
- import subprocess
2
- import typing as t
3
- from pathlib import Path
4
-
5
- from rich.console import Console
6
-
7
- from crackerjack.dynamic_config import DynamicConfigGenerator, generate_config_for_mode
8
- from crackerjack.models.protocols import OptionsProtocol
9
-
10
-
11
- class ConfigurationService:
12
- def __init__(self, console: Console, pkg_path: Path) -> None:
13
- self.console = console
14
- self.pkg_path = pkg_path
15
- # Extract package directory name from the pkg_path and sanitize it
16
- package_directory = pkg_path.name if pkg_path != Path.cwd() else None
17
- if package_directory:
18
- package_directory = package_directory.replace("-", "_")
19
- self.config_generator = DynamicConfigGenerator(package_directory)
20
-
21
- def update_precommit_config(self, options: OptionsProtocol) -> bool:
22
- try:
23
- mode = self._determine_config_mode(options)
24
- # Extract package directory name from the pkg_path
25
- package_directory = (
26
- self.pkg_path.name if self.pkg_path != Path.cwd() else None
27
- )
28
- config_temp_path = generate_config_for_mode(
29
- mode, package_directory=package_directory
30
- )
31
- if not config_temp_path:
32
- self.console.print("[yellow]⚠️ No configuration generated[/ yellow]")
33
- return False
34
-
35
- config_file = self.pkg_path / ".pre-commit-config.yaml"
36
- config_content = config_temp_path.read_text()
37
-
38
- from crackerjack.services.filesystem import FileSystemService
39
-
40
- config_content = FileSystemService.clean_trailing_whitespace_and_newlines(
41
- config_content
42
- )
43
- config_file.write_text(config_content)
44
-
45
- self._temp_config_path = config_temp_path
46
- self.console.print("[green]✅[/ green] Pre-commit configuration generated")
47
-
48
- if getattr(options, "update_precommit", False):
49
- success = self._run_precommit_autoupdate()
50
- if success:
51
- self.console.print("[green]✅[/ green] Pre-commit hooks updated")
52
- else:
53
- self.console.print(
54
- "[yellow]⚠️[/ yellow] Pre-commit autoupdate had issues"
55
- )
56
-
57
- self._update_dynamic_config_versions()
58
-
59
- return True
60
- except Exception as e:
61
- self.console.print(
62
- f"[red]❌[/ red] Failed to generate pre-commit config: {e}",
63
- )
64
- return False
65
-
66
- def get_temp_config_path(self) -> str | None:
67
- path = getattr(self, "_temp_config_path", None)
68
- return str(path) if path else None
69
-
70
- def _determine_config_mode(self, options: OptionsProtocol) -> str:
71
- if options.experimental_hooks:
72
- return "experimental"
73
- if hasattr(options, "test") and options.test:
74
- return "comprehensive"
75
- return "comprehensive"
76
-
77
- def validate_config(self) -> bool:
78
- try:
79
- config_file = self.pkg_path / ".pre-commit-config.yaml"
80
- if not config_file.exists():
81
- self.console.print(
82
- "[yellow]⚠️ No pre-commit configuration found[/ yellow]",
83
- )
84
- return False
85
- import yaml
86
-
87
- with config_file.open() as f:
88
- yaml_result = yaml.safe_load(f)
89
- _ = (
90
- t.cast("dict[str, t.Any]", yaml_result)
91
- if yaml_result is not None
92
- else {}
93
- )
94
- self.console.print("[green]✅[/ green] Pre-commit configuration is valid")
95
- return True
96
- except Exception as e:
97
- self.console.print(f"[red]❌[/ red] Configuration validation failed: {e}")
98
- return False
99
-
100
- def backup_config(self) -> Path | None:
101
- try:
102
- config_file = self.pkg_path / ".pre-commit-config.yaml"
103
- if not config_file.exists():
104
- return None
105
- import time
106
-
107
- timestamp = int(time.time())
108
- backup_file = self.pkg_path / f".pre-commit-config.yaml.backup.{timestamp}"
109
- backup_file.write_text(config_file.read_text())
110
- self.console.print(
111
- f"[cyan]💾[/ cyan] Configuration backed up to {backup_file.name}",
112
- )
113
- return backup_file
114
- except Exception as e:
115
- self.console.print(
116
- f"[yellow]⚠️[/ yellow] Failed to backup configuration: {e}",
117
- )
118
- return None
119
-
120
- def restore_config(self, backup_file: Path) -> bool:
121
- try:
122
- if not backup_file.exists():
123
- self.console.print(
124
- f"[red]❌[/ red] Backup file not found: {backup_file}",
125
- )
126
- return False
127
- config_file = self.pkg_path / ".pre-commit-config.yaml"
128
- config_file.write_text(backup_file.read_text())
129
- self.console.print(
130
- f"[green]✅[/ green] Configuration restored from {backup_file.name}",
131
- )
132
- return True
133
- except Exception as e:
134
- self.console.print(f"[red]❌[/ red] Failed to restore configuration: {e}")
135
- return False
136
-
137
- def get_config_info(self) -> dict[str, t.Any]:
138
- try:
139
- config_file = self.pkg_path / ".pre-commit-config.yaml"
140
- if not config_file.exists():
141
- return {"exists": False}
142
- import yaml
143
-
144
- with config_file.open() as f:
145
- yaml_result = yaml.safe_load(f)
146
- config_data = yaml_result if isinstance(yaml_result, dict) else {}
147
- repos = config_data.get("repos", [])
148
- if not isinstance(repos, list):
149
- repos = []
150
- hook_count = sum(
151
- len(repo.get("hooks", []))
152
- for repo in t.cast(list[dict[str, t.Any]], repos)
153
- if isinstance(repo, dict)
154
- )
155
- stat = config_file.stat()
156
-
157
- return {
158
- "exists": True,
159
- "file_size": stat.st_size,
160
- "modified_time": stat.st_mtime,
161
- "repo_count": len(
162
- [
163
- r
164
- for r in t.cast(list[dict[str, t.Any]], repos)
165
- if isinstance(r, dict)
166
- ]
167
- ),
168
- "hook_count": hook_count,
169
- "repos": [
170
- {
171
- "repo": repo.get("repo", "unknown"),
172
- "rev": repo.get("rev", "unknown"),
173
- "hooks": len(repo.get("hooks", [])),
174
- }
175
- for repo in t.cast(list[dict[str, t.Any]], repos)
176
- if isinstance(repo, dict)
177
- ],
178
- }
179
- except Exception as e:
180
- return {"exists": True, "error": str(e)}
181
-
182
- def update_pyproject_config(self, options: OptionsProtocol) -> bool:
183
- try:
184
- pyproject_file = self.pkg_path / "pyproject.toml"
185
- if not pyproject_file.exists():
186
- self.console.print("[yellow]⚠️ No pyproject.toml found[/ yellow]")
187
- return False
188
- from tomllib import loads
189
-
190
- from tomli_w import dumps
191
-
192
- with pyproject_file.open() as f:
193
- content = f.read()
194
- config = loads(content)
195
- if "tool" not in config:
196
- config["tool"] = {}
197
- if "ruff" not in config["tool"]:
198
- config["tool"]["ruff"] = {
199
- "target-version": "py313",
200
- "line-length": 88,
201
- "fix": True,
202
- "unsafe-fixes": True,
203
- "show-fixes": True,
204
- "output-format": "full",
205
- }
206
- config["tool"]["ruff"]["format"] = {"docstring - code-format": True}
207
- config["tool"]["ruff"]["lint"] = {
208
- "extend-select": ["C901", "F", "I", "UP"],
209
- "ignore": ["E402", "F821"],
210
- "fixable": ["ALL"],
211
- }
212
- if "pytest" not in config["tool"]:
213
- config["tool"]["pytest"] = {
214
- "ini_options": {
215
- "asyncio_mode": "auto",
216
- "timeout": 300,
217
- "addopts": "- - cov=crackerjack - - cov-report=term",
218
- "markers": [
219
- "unit: marks test as a unit test",
220
- "benchmark: mark test as a benchmark",
221
- "integration: marks test as an integration test",
222
- "no_leaks: detect asyncio task leaks",
223
- ],
224
- },
225
- }
226
-
227
- from crackerjack.services.filesystem import FileSystemService
228
-
229
- content = dumps(config)
230
- content = FileSystemService.clean_trailing_whitespace_and_newlines(content)
231
- with pyproject_file.open("w") as f:
232
- f.write(content)
233
- self.console.print(
234
- "[green]✅[/ green] pyproject.toml configuration updated"
235
- )
236
- return True
237
- except Exception as e:
238
- self.console.print(f"[red]❌[/ red] Failed to update pyproject.toml: {e}")
239
- return False
240
-
241
- def _run_precommit_autoupdate(self) -> bool:
242
- import subprocess
243
-
244
- try:
245
- self.console.print("[cyan]🔄[/ cyan] Running pre-commit autoupdate...")
246
- result = self._execute_precommit_autoupdate()
247
-
248
- if result.returncode == 0:
249
- self._display_autoupdate_results(result.stdout)
250
- return True
251
- else:
252
- self._handle_autoupdate_error(result.stderr)
253
- return False
254
-
255
- except subprocess.TimeoutExpired:
256
- self.console.print("[red]❌[/ red] Pre-commit autoupdate timed out")
257
- return False
258
- except Exception as e:
259
- self.console.print(
260
- f"[red]❌[/ red] Failed to run pre-commit autoupdate: {e}"
261
- )
262
- return False
263
-
264
- def _execute_precommit_autoupdate(self) -> subprocess.CompletedProcess[str]:
265
- return subprocess.run(
266
- ["uv", "run", "pre-commit", "autoupdate"],
267
- cwd=self.pkg_path,
268
- capture_output=True,
269
- text=True,
270
- timeout=60,
271
- )
272
-
273
- def _display_autoupdate_results(self, stdout: str) -> None:
274
- if self._has_updates(stdout):
275
- for line in stdout.split("\n"):
276
- if self._is_update_line(line):
277
- self.console.print(f"[dim] {line.strip()}[/ dim]")
278
-
279
- def _has_updates(self, stdout: str) -> bool:
280
- stdout_lower = stdout.lower()
281
- return "updating" in stdout_lower or "updated" in stdout_lower
282
-
283
- def _is_update_line(self, line: str) -> bool:
284
- return "updating" in line.lower() or "- >" in line
285
-
286
- def _handle_autoupdate_error(self, stderr: str) -> None:
287
- if stderr:
288
- self.console.print(
289
- f"[yellow]Pre-commit autoupdate stderr: [/ yellow] {stderr}"
290
- )
291
-
292
- def _update_dynamic_config_versions(self) -> None:
293
- try:
294
- self.console.print("[cyan]🔄[/ cyan] Updating dynamic config versions...")
295
-
296
- version_updates = self._extract_version_updates()
297
- if version_updates:
298
- self._update_dynamic_config_file(version_updates)
299
-
300
- except Exception as e:
301
- self.console.print(
302
- f"[yellow]⚠️[/ yellow] Failed to update dynamic config versions: {e}"
303
- )
304
-
305
- def _extract_version_updates(self) -> dict[str, str]:
306
- config_file = self.pkg_path / ".pre-commit-config.yaml"
307
- if not config_file.exists():
308
- return {}
309
-
310
- import yaml
311
-
312
- with config_file.open() as f:
313
- config = yaml.safe_load(f)
314
-
315
- if not config or "repos" not in config:
316
- return {}
317
-
318
- version_updates = {}
319
- repos: list[dict[str, t.Any]] = (
320
- config.get("repos", []) if isinstance(config, dict) else []
321
- )
322
- for repo in repos:
323
- repo_url = repo.get("repo", "")
324
- rev = repo.get("rev", "")
325
- if repo_url and rev:
326
- version_updates[repo_url] = rev
327
-
328
- return version_updates
329
-
330
- def _update_dynamic_config_file(self, version_updates: dict[str, str]) -> None:
331
- dynamic_config_path = self.pkg_path / "crackerjack" / "dynamic_config.py"
332
- if dynamic_config_path.exists():
333
- self._apply_version_updates(dynamic_config_path, version_updates)
334
-
335
- def _apply_version_updates(
336
- self, config_path: Path, version_updates: dict[str, str]
337
- ) -> None:
338
- try:
339
- content = config_path.read_text()
340
- updated = False
341
-
342
- for repo_url, new_rev in version_updates.items():
343
- from .regex_patterns import update_repo_revision
344
-
345
- new_content = update_repo_revision(content, repo_url, new_rev)
346
- if new_content != content:
347
- content = new_content
348
- updated = True
349
- self.console.print(f"[dim] Updated {repo_url} to {new_rev}[/ dim]")
350
-
351
- if updated:
352
- config_path.write_text(content)
353
- self.console.print("[green]✅[/ green] Dynamic config versions updated")
354
-
355
- except Exception as e:
356
- self.console.print(
357
- f"[yellow]⚠️[/ yellow] Failed to apply version updates: {e}"
358
- )
@@ -1,125 +0,0 @@
1
- """Rich panel utilities for MCP server operations with consistent styling."""
2
-
3
- from pathlib import Path
4
-
5
- from rich.console import Console
6
- from rich.panel import Panel
7
- from rich.text import Text
8
-
9
-
10
- class ServerPanels:
11
- """Rich panel utilities for server operations with 74-char width limit."""
12
-
13
- WIDTH = 74 # Match session-mgmt-mcp width constraint
14
-
15
- def __init__(self, console: Console | None = None) -> None:
16
- self.console = console or Console()
17
-
18
- def restart_header(self) -> None:
19
- """Display server restart header panel."""
20
- panel = Panel(
21
- "🔄 Restarting Crackerjack MCP Server...",
22
- width=self.WIDTH,
23
- title="Server Restart",
24
- style="cyan",
25
- )
26
- self.console.print(panel)
27
-
28
- def stop_servers(self, count: int) -> None:
29
- """Display stopping servers status."""
30
- self.console.print("📴 Stopping existing servers...")
31
- self.console.print(f"🛑 Stopping {count} server process(es)...")
32
-
33
- def process_stopped(self, pid: int) -> None:
34
- """Display process stopped message."""
35
- self.console.print(f"Stopping process {pid}...")
36
- self.console.print(f"✅ Process {pid} terminated gracefully")
37
-
38
- def stop_complete(self, count: int) -> None:
39
- """Display stop completion panel."""
40
- panel = Panel(
41
- f"✅ Successfully stopped {count} process(es)",
42
- width=self.WIDTH,
43
- title="Server Stopped",
44
- style="green",
45
- )
46
- self.console.print(panel)
47
-
48
- def cleanup_wait(self) -> None:
49
- """Display cleanup waiting message."""
50
- self.console.print("⏳ Waiting for cleanup...")
51
-
52
- def starting_server(self) -> None:
53
- """Display starting server message."""
54
- self.console.print("🚀 Starting fresh server instance...")
55
- self.console.print("🚀 Starting Crackerjack MCP Server...")
56
- self.console.print("⏳ Waiting for server to start...")
57
-
58
- def success_panel(
59
- self,
60
- http_endpoint: str | None = None,
61
- websocket_monitor: str | None = None,
62
- process_id: int | None = None,
63
- ) -> None:
64
- """Display success panel with server details."""
65
- content = Text()
66
- content.append("✅ Server started successfully!\n", style="green bold")
67
-
68
- if http_endpoint:
69
- content.append(f"🌐 HTTP Endpoint: {http_endpoint}\n", style="cyan")
70
-
71
- if websocket_monitor:
72
- content.append(f"🔌 WebSocket Monitor: {websocket_monitor}\n", style="cyan")
73
-
74
- if process_id:
75
- content.append(f"📊 Process ID: {process_id}", style="cyan")
76
-
77
- panel = Panel(
78
- content,
79
- width=self.WIDTH,
80
- title="Crackerjack MCP Server",
81
- style="green",
82
- )
83
- self.console.print(panel)
84
-
85
- def failure_panel(self, error: str) -> None:
86
- """Display failure panel with error details."""
87
- panel = Panel(
88
- f"❌ Server failed to start: {error}",
89
- width=self.WIDTH,
90
- title="Server Error",
91
- style="red",
92
- )
93
- self.console.print(panel)
94
-
95
- def start_panel(
96
- self,
97
- project_path: Path,
98
- mode: str = "STDIO",
99
- http_endpoint: str | None = None,
100
- websocket_port: int | None = None,
101
- ) -> None:
102
- """Display server start panel with configuration details."""
103
- content = Text()
104
- content.append("🚀 Starting Crackerjack MCP Server...\n", style="green bold")
105
- content.append(f"📁 Project: {project_path.name}\n", style="cyan")
106
- content.append(f"🔗 Mode: {mode}\n", style="cyan")
107
-
108
- if http_endpoint:
109
- content.append(f"🌐 HTTP: {http_endpoint}\n", style="cyan")
110
-
111
- if websocket_port:
112
- content.append(f"🔌 WebSocket: {websocket_port}", style="cyan")
113
-
114
- panel = Panel(
115
- content,
116
- width=self.WIDTH,
117
- title="Server Configuration",
118
- style="green",
119
- )
120
- self.console.print(panel)
121
-
122
-
123
- def create_server_panels(console: Console | None = None) -> ServerPanels:
124
- """Factory function to create ServerPanels instance."""
125
- return ServerPanels(console)