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,505 @@
1
+ import asyncio
2
+ import socket
3
+ import subprocess
4
+ import sys
5
+ import time
6
+ from contextlib import suppress
7
+ from typing import Any
8
+
9
+ import aiohttp
10
+ from acb import console
11
+ from rich.panel import Panel
12
+ from rich.table import Table
13
+
14
+ watchdog_event_queue: asyncio.Queue[dict[str, Any]] | None = None
15
+
16
+
17
+ class ServiceConfig:
18
+ def __init__(
19
+ self,
20
+ name: str,
21
+ command: list[str],
22
+ health_check_url: str | None = None,
23
+ health_check_interval: float = 30.0,
24
+ restart_delay: float = 5.0,
25
+ max_restarts: int = 10,
26
+ restart_window: float = 300.0,
27
+ ) -> None:
28
+ self.name = name
29
+ self.command = command
30
+ self.health_check_url = health_check_url
31
+ self.health_check_interval = health_check_interval
32
+ self.restart_delay = restart_delay
33
+ self.max_restarts = max_restarts
34
+ self.restart_window = restart_window
35
+
36
+ self.process: subprocess.Popen[str] | None = None
37
+ self.restart_count = 0
38
+ self.restart_timestamps: list[float] = []
39
+ self.last_health_check = 0.0
40
+ self.is_healthy = False
41
+ self._port_acknowledged = False
42
+ self.last_error: str | None = None
43
+
44
+
45
+ class ServiceWatchdog:
46
+ def __init__(
47
+ self,
48
+ services: list[ServiceConfig],
49
+ event_queue: asyncio.Queue[dict[str, Any]] | None = None,
50
+ ) -> None:
51
+ self.services = services
52
+ self.is_running = True
53
+ self.session: aiohttp.ClientSession | None = None
54
+ self.event_queue = event_queue
55
+
56
+ global watchdog_event_queue
57
+ if event_queue:
58
+ watchdog_event_queue = event_queue
59
+
60
+ async def start(self) -> None:
61
+ self.session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10.0))
62
+
63
+ for service in self.services:
64
+ await self._start_service(service)
65
+
66
+ monitor_tasks = [
67
+ asyncio.create_task(self._monitor_service(service))
68
+ for service in self.services
69
+ ]
70
+
71
+ status_task = asyncio.create_task(self._display_status())
72
+
73
+ try:
74
+ await asyncio.gather(*monitor_tasks, status_task)
75
+ finally:
76
+ await self._cleanup()
77
+
78
+ async def stop(self) -> None:
79
+ self.is_running = False
80
+
81
+ for service in self.services:
82
+ if service.process:
83
+ console.print(f"[yellow]🛑 Stopping {service.name}...[/ yellow]")
84
+ service.process.terminate()
85
+ try:
86
+ service.process.wait(timeout=10)
87
+ except subprocess.TimeoutExpired:
88
+ service.process.kill()
89
+
90
+ if self.session:
91
+ await self.session.close()
92
+
93
+ def _is_port_in_use(self, port: int) -> bool:
94
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
95
+ try:
96
+ s.bind(("127.0.0.1", port))
97
+ return False
98
+ except OSError:
99
+ return True
100
+
101
+ async def _start_service(self, service: ServiceConfig) -> bool:
102
+ try:
103
+ if await self._check_websocket_server_running(service):
104
+ return True
105
+
106
+ if not await self._launch_service_process(service):
107
+ return False
108
+
109
+ return await self._finalize_service_startup(service)
110
+
111
+ except Exception as e:
112
+ return await self._handle_service_start_error(service, e)
113
+
114
+ async def _check_websocket_server_running(self, service: ServiceConfig) -> bool:
115
+ if "websocket-server" in " ".join(service.command):
116
+ if self._is_port_in_use(8675):
117
+ await self._emit_event(
118
+ "port_in_use",
119
+ service.name,
120
+ "Port 8675 already in use (server already running)",
121
+ )
122
+ service.is_healthy = True
123
+ return True
124
+ return False
125
+
126
+ async def _launch_service_process(self, service: ServiceConfig) -> bool:
127
+ service.process = subprocess.Popen[str](
128
+ service.command,
129
+ stdout=subprocess.PIPE,
130
+ stderr=subprocess.PIPE,
131
+ text=True,
132
+ )
133
+ service.last_error = None
134
+ await asyncio.sleep(2)
135
+
136
+ return await self._check_process_startup_success(service)
137
+
138
+ async def _check_process_startup_success(self, service: ServiceConfig) -> bool:
139
+ if service.process is None:
140
+ return False
141
+ exit_code = service.process.poll()
142
+ if exit_code is not None:
143
+ return await self._handle_process_died(service, exit_code)
144
+ return True
145
+
146
+ async def _handle_process_died(
147
+ self,
148
+ service: ServiceConfig,
149
+ exit_code: int,
150
+ ) -> bool:
151
+ if service.process is None:
152
+ return False
153
+ stdout, stderr = service.process.communicate()
154
+ error_msg = f"Process died (exit: {exit_code})"
155
+ if stderr and stderr.strip():
156
+ error_msg += f"-{stderr.strip()[:50]}"
157
+ service.last_error = error_msg
158
+ await self._emit_event("process_died", service.name, error_msg)
159
+ return False
160
+
161
+ async def _finalize_service_startup(self, service: ServiceConfig) -> bool:
162
+ if service.health_check_url:
163
+ service.is_healthy = await self._health_check(service)
164
+ else:
165
+ service.is_healthy = True
166
+
167
+ if service.is_healthy:
168
+ await self._emit_event("started", service.name, "Running")
169
+
170
+ return service.is_healthy
171
+
172
+ async def _handle_service_start_error(
173
+ self,
174
+ service: ServiceConfig,
175
+ error: Exception,
176
+ ) -> bool:
177
+ service.last_error = str(error)
178
+ await self._emit_event(
179
+ "start_error",
180
+ service.name,
181
+ f"Failed: {str(error)[:30]}",
182
+ )
183
+ return False
184
+
185
+ async def _handle_websocket_server_monitoring(self, service: ServiceConfig) -> bool:
186
+ if "websocket-server" not in " ".join(service.command):
187
+ return False
188
+
189
+ if self._is_port_in_use(8675):
190
+ if await self._verify_websocket_server_health():
191
+ service.is_healthy = True
192
+ if (
193
+ not hasattr(service, "_port_acknowledged")
194
+ or not service._port_acknowledged
195
+ ):
196
+ service._port_acknowledged = True
197
+ await self._emit_event(
198
+ "port_healthy",
199
+ service.name,
200
+ "WebSocket server verified healthy",
201
+ )
202
+ await asyncio.sleep(30)
203
+ return True
204
+ service.is_healthy = False
205
+ await self._emit_event(
206
+ "port_hijacked",
207
+ service.name,
208
+ "Port 8675 occupied by different service",
209
+ )
210
+ await self._restart_service(service)
211
+ return False
212
+ service._port_acknowledged = False
213
+ if service.process and service.process.poll() is None:
214
+ await self._emit_event(
215
+ "port_unavailable",
216
+ service.name,
217
+ "Process running but port 8675 not available",
218
+ )
219
+ await self._restart_service(service)
220
+ return False
221
+
222
+ async def _check_process_health(self, service: ServiceConfig) -> bool:
223
+ process_running = service.process and service.process.poll() is None
224
+ if not process_running:
225
+ if service.process:
226
+ exit_code = service.process.poll()
227
+ await self._emit_event(
228
+ "died",
229
+ service.name,
230
+ f"Process died (exit: {exit_code})",
231
+ )
232
+ else:
233
+ await self._emit_event("not_started", service.name, "Not started")
234
+ await self._restart_service(service)
235
+ return False
236
+ return True
237
+
238
+ async def _perform_health_check_if_needed(self, service: ServiceConfig) -> bool:
239
+ if not service.health_check_url:
240
+ return True
241
+
242
+ current_time = time.time()
243
+ if current_time - service.last_health_check >= service.health_check_interval:
244
+ service.is_healthy = await self._health_check(service)
245
+ service.last_health_check = current_time
246
+
247
+ if not service.is_healthy:
248
+ await self._emit_event(
249
+ "health_fail",
250
+ service.name,
251
+ "Health check failed",
252
+ )
253
+ await self._restart_service(service)
254
+ return False
255
+ return True
256
+
257
+ async def _monitor_service(self, service: ServiceConfig) -> None:
258
+ while self.is_running:
259
+ try:
260
+ if await self._execute_monitoring_cycle(service):
261
+ await asyncio.sleep(5.0)
262
+ else:
263
+ continue
264
+
265
+ except Exception as e:
266
+ await self._handle_monitoring_error(service, e)
267
+
268
+ async def _execute_monitoring_cycle(self, service: ServiceConfig) -> bool:
269
+ if await self._handle_websocket_server_monitoring(service):
270
+ return True
271
+
272
+ if not await self._check_process_health(service):
273
+ return False
274
+
275
+ return await self._perform_health_check_if_needed(service)
276
+
277
+ async def _handle_monitoring_error(
278
+ self,
279
+ service: ServiceConfig,
280
+ error: Exception,
281
+ ) -> None:
282
+ service.last_error = str(error)
283
+ console.print(f"[red]❌ Error monitoring {service.name}: {error}[/ red]")
284
+ await asyncio.sleep(10.0)
285
+
286
+ async def _health_check(self, service: ServiceConfig) -> bool:
287
+ if not service.health_check_url or not self.session:
288
+ return True
289
+
290
+ try:
291
+ async with self.session.get(service.health_check_url) as response:
292
+ return response.status == 200
293
+ except Exception:
294
+ return False
295
+
296
+ async def _verify_websocket_server_health(self) -> bool:
297
+ if not self.session:
298
+ return False
299
+
300
+ with suppress(Exception):
301
+ async with self.session.get("http: / / localhost: 8675 / ") as response:
302
+ if response.status == 200:
303
+ data = await response.json()
304
+
305
+ return (
306
+ "message" in data
307
+ and "Crackerjack" in data.get("message", "")
308
+ and "progress_dir" in data
309
+ )
310
+ return False
311
+
312
+ async def _restart_service(self, service: ServiceConfig) -> None:
313
+ current_time = time.time()
314
+ reason = self._determine_restart_reason(service)
315
+
316
+ await self._emit_event("restarting", service.name, f"Restarting-{reason}")
317
+
318
+ if not await self._check_restart_rate_limit(service, current_time):
319
+ return
320
+
321
+ await self._terminate_existing_process(service)
322
+ await self._wait_before_restart(service)
323
+ await self._execute_service_restart(service, current_time)
324
+
325
+ def _determine_restart_reason(self, service: ServiceConfig) -> str:
326
+ return (
327
+ "Process died"
328
+ if not service.process or service.process.poll() is not None
329
+ else "Health failed"
330
+ )
331
+
332
+ async def _check_restart_rate_limit(
333
+ self,
334
+ service: ServiceConfig,
335
+ current_time: float,
336
+ ) -> bool:
337
+ service.restart_timestamps = [
338
+ ts
339
+ for ts in service.restart_timestamps
340
+ if current_time - ts < service.restart_window
341
+ ]
342
+
343
+ if len(service.restart_timestamps) >= service.max_restarts:
344
+ console.print(
345
+ f"[red]🚨 {service.name} exceeded restart limit ({service.max_restarts} in {service.restart_window}s)[/ red]",
346
+ )
347
+ service.last_error = "Restart rate limit exceeded"
348
+ await asyncio.sleep(60)
349
+ return False
350
+ return True
351
+
352
+ async def _terminate_existing_process(self, service: ServiceConfig) -> None:
353
+ if not service.process:
354
+ return
355
+
356
+ try:
357
+ console.print(
358
+ f"[yellow]🔪 Terminating existing {service.name} process (PID: {service.process.pid})[/ yellow]",
359
+ )
360
+ service.process.terminate()
361
+ service.process.wait(timeout=10)
362
+ except subprocess.TimeoutExpired:
363
+ console.print(f"[red]💀 Force killing {service.name} process[/ red]")
364
+ service.process.kill()
365
+ except Exception as e:
366
+ console.print(f"[yellow]⚠️ Error terminating {service.name}: {e}[/ yellow]")
367
+
368
+ async def _wait_before_restart(self, service: ServiceConfig) -> None:
369
+ console.print(
370
+ f"[yellow]⏳ Waiting {service.restart_delay}s before restarting {service.name}...[/ yellow]",
371
+ )
372
+ await asyncio.sleep(service.restart_delay)
373
+
374
+ async def _execute_service_restart(
375
+ self,
376
+ service: ServiceConfig,
377
+ current_time: float,
378
+ ) -> None:
379
+ service.restart_timestamps.append(current_time)
380
+ service.restart_count += 1
381
+
382
+ success = await self._start_service(service)
383
+ if not success:
384
+ await self._emit_event("restart_failed", service.name, "Restart failed")
385
+
386
+ async def _display_status(self) -> None:
387
+ while self.is_running:
388
+ try:
389
+ await self._update_status_display()
390
+ await asyncio.sleep(10.0)
391
+
392
+ except Exception as e:
393
+ console.print(f"[red]Error updating display: {e}[/ red]")
394
+ await asyncio.sleep(5.0)
395
+
396
+ async def _update_status_display(self) -> None:
397
+ console.clear()
398
+ table = self._create_status_table()
399
+
400
+ for service in self.services:
401
+ status = self._get_service_status(service)
402
+ health = self._get_service_health(service)
403
+ restarts = str(service.restart_count)
404
+ error = self._format_error_message(service.last_error)
405
+
406
+ table.add_row(service.name, status, health, restarts, error)
407
+
408
+ console.print(
409
+ Panel(table, title="Crackerjack Service Watchdog", border_style="cyan")
410
+ )
411
+ console.print("\n[dim]Press Ctrl + C to stop monitoring[/ dim]")
412
+
413
+ def _create_status_table(self) -> Table:
414
+ table = Table(title="🔍 Crackerjack Service Watchdog")
415
+ table.add_column("Service", style="cyan", no_wrap=True)
416
+ table.add_column("Status", style="white")
417
+ table.add_column("Health", style="white")
418
+ table.add_column("Restarts", style="white")
419
+ table.add_column("Last Error", style="red")
420
+ return table
421
+
422
+ def _get_service_status(self, service: ServiceConfig) -> str:
423
+ if service.process and service.process.poll() is None:
424
+ return "[green]✅ Running[/ green]"
425
+ return "[red]❌ Stopped[/ red]"
426
+
427
+ def _get_service_health(self, service: ServiceConfig) -> str:
428
+ if service.health_check_url:
429
+ return (
430
+ "[green]🟢 Healthy[/ green]"
431
+ if service.is_healthy
432
+ else "[red]🔴 Unhealthy[/ red]"
433
+ )
434
+ return "[dim]N / A[/ dim]"
435
+
436
+ def _format_error_message(self, error_message: str | None) -> str:
437
+ error = error_message or "[dim]None[/ dim]"
438
+ if len(error) > 30:
439
+ error = error[:27] + "..."
440
+ return error
441
+
442
+ async def _emit_event(
443
+ self,
444
+ event_type: str,
445
+ service_name: str,
446
+ message: str,
447
+ ) -> None:
448
+ if self.event_queue:
449
+ with suppress(Exception):
450
+ event = {
451
+ "type": event_type,
452
+ "service": service_name,
453
+ "message": message,
454
+ "timestamp": time.time(),
455
+ }
456
+ await self.event_queue.put(event)
457
+
458
+ async def _cleanup(self) -> None:
459
+ if self.session and not self.session.closed:
460
+ await self.session.close()
461
+
462
+
463
+ async def create_default_watchdog(
464
+ event_queue: asyncio.Queue[dict[str, Any]] | None = None,
465
+ ) -> ServiceWatchdog:
466
+ python_path = sys.executable
467
+
468
+ services = [
469
+ ServiceConfig(
470
+ name="MCP Server",
471
+ command=[
472
+ python_path,
473
+ "-m",
474
+ "crackerjack",
475
+ "--start-mcp-server",
476
+ ],
477
+ ),
478
+ ServiceConfig(
479
+ name="WebSocket Server",
480
+ command=[
481
+ python_path,
482
+ "-m",
483
+ "crackerjack",
484
+ " - - websocket-server",
485
+ ],
486
+ health_check_url="http: / / localhost: 8675 / ",
487
+ ),
488
+ ]
489
+
490
+ return ServiceWatchdog(services, event_queue)
491
+
492
+
493
+ async def main() -> None:
494
+ watchdog = await create_default_watchdog()
495
+
496
+ try:
497
+ await watchdog.start()
498
+ except KeyboardInterrupt:
499
+ console.print("\n[yellow]🛑 Shutting down watchdog...[/ yellow]")
500
+ finally:
501
+ await watchdog.stop()
502
+
503
+
504
+ if __name__ == "__main__":
505
+ asyncio.run(main())