claude-mpm 3.4.10__py3-none-any.whl → 5.4.55__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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (950) hide show
  1. claude_mpm/BUILD_NUMBER +1 -0
  2. claude_mpm/VERSION +1 -0
  3. claude_mpm/__init__.py +50 -12
  4. claude_mpm/__main__.py +7 -2
  5. claude_mpm/agents/BASE_AGENT.md +164 -0
  6. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  7. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +290 -0
  8. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  9. claude_mpm/agents/MEMORY.md +72 -0
  10. claude_mpm/agents/PM_INSTRUCTIONS.md +1402 -0
  11. claude_mpm/agents/WORKFLOW.md +111 -0
  12. claude_mpm/agents/__init__.py +92 -80
  13. claude_mpm/agents/agent-template.yaml +83 -0
  14. claude_mpm/agents/agent_loader.py +560 -745
  15. claude_mpm/agents/agent_loader_integration.py +53 -55
  16. claude_mpm/agents/agents_metadata.py +186 -27
  17. claude_mpm/agents/async_agent_loader.py +436 -0
  18. claude_mpm/agents/base_agent.json +8 -4
  19. claude_mpm/agents/frontmatter_validator.py +754 -0
  20. claude_mpm/agents/system_agent_config.py +222 -155
  21. claude_mpm/agents/templates/README.md +465 -0
  22. claude_mpm/agents/templates/__init__.py +17 -13
  23. claude_mpm/agents/templates/circuit-breakers.md +1391 -0
  24. claude_mpm/agents/templates/context-management-examples.md +544 -0
  25. claude_mpm/agents/templates/git-file-tracking.md +584 -0
  26. claude_mpm/agents/templates/pm-examples.md +474 -0
  27. claude_mpm/agents/templates/pm-red-flags.md +310 -0
  28. claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
  29. claude_mpm/agents/templates/research-gate-examples.md +669 -0
  30. claude_mpm/agents/templates/response-format.md +583 -0
  31. claude_mpm/agents/templates/structured-questions-examples.md +615 -0
  32. claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
  33. claude_mpm/agents/templates/ticketing-examples.md +277 -0
  34. claude_mpm/agents/templates/validation-templates.md +312 -0
  35. claude_mpm/cli/__init__.py +90 -128
  36. claude_mpm/cli/__main__.py +33 -0
  37. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  38. claude_mpm/cli/commands/__init__.py +36 -12
  39. claude_mpm/cli/commands/agent_manager.py +1403 -0
  40. claude_mpm/cli/commands/agent_source.py +774 -0
  41. claude_mpm/cli/commands/agent_state_manager.py +335 -0
  42. claude_mpm/cli/commands/agents.py +2503 -168
  43. claude_mpm/cli/commands/agents_cleanup.py +210 -0
  44. claude_mpm/cli/commands/agents_discover.py +338 -0
  45. claude_mpm/cli/commands/aggregate.py +540 -0
  46. claude_mpm/cli/commands/analyze.py +553 -0
  47. claude_mpm/cli/commands/analyze_code.py +528 -0
  48. claude_mpm/cli/commands/auto_configure.py +1053 -0
  49. claude_mpm/cli/commands/cleanup.py +588 -0
  50. claude_mpm/cli/commands/cleanup_orphaned_agents.py +150 -0
  51. claude_mpm/cli/commands/config.py +586 -0
  52. claude_mpm/cli/commands/configure.py +2654 -0
  53. claude_mpm/cli/commands/configure_agent_display.py +282 -0
  54. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  55. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  56. claude_mpm/cli/commands/configure_models.py +18 -0
  57. claude_mpm/cli/commands/configure_navigation.py +184 -0
  58. claude_mpm/cli/commands/configure_paths.py +104 -0
  59. claude_mpm/cli/commands/configure_persistence.py +254 -0
  60. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  61. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  62. claude_mpm/cli/commands/configure_validators.py +73 -0
  63. claude_mpm/cli/commands/dashboard.py +286 -0
  64. claude_mpm/cli/commands/debug.py +1386 -0
  65. claude_mpm/cli/commands/doctor.py +243 -0
  66. claude_mpm/cli/commands/hook_errors.py +277 -0
  67. claude_mpm/cli/commands/info.py +195 -74
  68. claude_mpm/cli/commands/local_deploy.py +534 -0
  69. claude_mpm/cli/commands/mcp.py +205 -0
  70. claude_mpm/cli/commands/mcp_command_router.py +161 -0
  71. claude_mpm/cli/commands/mcp_config.py +154 -0
  72. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  73. claude_mpm/cli/commands/mcp_external_commands.py +249 -0
  74. claude_mpm/cli/commands/mcp_install_commands.py +346 -0
  75. claude_mpm/cli/commands/mcp_pipx_config.py +208 -0
  76. claude_mpm/cli/commands/mcp_server_commands.py +155 -0
  77. claude_mpm/cli/commands/mcp_setup_external.py +868 -0
  78. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  79. claude_mpm/cli/commands/memory.py +585 -846
  80. claude_mpm/cli/commands/monitor.py +228 -310
  81. claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
  82. claude_mpm/cli/commands/mpm_init/core.py +759 -0
  83. claude_mpm/cli/commands/mpm_init/display.py +341 -0
  84. claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
  85. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  86. claude_mpm/cli/commands/mpm_init/modes.py +397 -0
  87. claude_mpm/cli/commands/mpm_init/prompts.py +722 -0
  88. claude_mpm/cli/commands/mpm_init_cli.py +396 -0
  89. claude_mpm/cli/commands/mpm_init_handler.py +195 -0
  90. claude_mpm/cli/commands/postmortem.py +401 -0
  91. claude_mpm/cli/commands/profile.py +276 -0
  92. claude_mpm/cli/commands/run.py +910 -488
  93. claude_mpm/cli/commands/search.py +458 -0
  94. claude_mpm/cli/commands/skill_source.py +694 -0
  95. claude_mpm/cli/commands/skills.py +1246 -0
  96. claude_mpm/cli/commands/summarize.py +413 -0
  97. claude_mpm/cli/commands/tickets.py +536 -53
  98. claude_mpm/cli/commands/uninstall.py +176 -0
  99. claude_mpm/cli/commands/upgrade.py +152 -0
  100. claude_mpm/cli/commands/verify.py +119 -0
  101. claude_mpm/cli/executor.py +297 -0
  102. claude_mpm/cli/helpers.py +105 -0
  103. claude_mpm/cli/interactive/__init__.py +21 -0
  104. claude_mpm/cli/interactive/agent_wizard.py +1947 -0
  105. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  106. claude_mpm/cli/parser.py +87 -563
  107. claude_mpm/cli/parsers/__init__.py +35 -0
  108. claude_mpm/cli/parsers/agent_manager_parser.py +393 -0
  109. claude_mpm/cli/parsers/agent_source_parser.py +171 -0
  110. claude_mpm/cli/parsers/agents_parser.py +575 -0
  111. claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
  112. claude_mpm/cli/parsers/analyze_parser.py +135 -0
  113. claude_mpm/cli/parsers/auto_configure_parser.py +120 -0
  114. claude_mpm/cli/parsers/base_parser.py +644 -0
  115. claude_mpm/cli/parsers/config_parser.py +208 -0
  116. claude_mpm/cli/parsers/configure_parser.py +138 -0
  117. claude_mpm/cli/parsers/dashboard_parser.py +113 -0
  118. claude_mpm/cli/parsers/debug_parser.py +319 -0
  119. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  120. claude_mpm/cli/parsers/mcp_parser.py +195 -0
  121. claude_mpm/cli/parsers/memory_parser.py +138 -0
  122. claude_mpm/cli/parsers/monitor_parser.py +142 -0
  123. claude_mpm/cli/parsers/mpm_init_parser.py +311 -0
  124. claude_mpm/cli/parsers/profile_parser.py +147 -0
  125. claude_mpm/cli/parsers/run_parser.py +157 -0
  126. claude_mpm/cli/parsers/search_parser.py +245 -0
  127. claude_mpm/cli/parsers/skill_source_parser.py +169 -0
  128. claude_mpm/cli/parsers/skills_parser.py +277 -0
  129. claude_mpm/cli/parsers/source_parser.py +138 -0
  130. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  131. claude_mpm/cli/shared/__init__.py +40 -0
  132. claude_mpm/cli/shared/argument_patterns.py +205 -0
  133. claude_mpm/cli/shared/base_command.py +242 -0
  134. claude_mpm/cli/shared/error_handling.py +242 -0
  135. claude_mpm/cli/shared/output_formatters.py +241 -0
  136. claude_mpm/cli/startup.py +1743 -0
  137. claude_mpm/cli/startup_display.py +480 -0
  138. claude_mpm/cli/startup_logging.py +839 -0
  139. claude_mpm/cli/utils.py +136 -47
  140. claude_mpm/cli_module/__init__.py +6 -6
  141. claude_mpm/cli_module/args.py +188 -140
  142. claude_mpm/cli_module/commands.py +79 -70
  143. claude_mpm/cli_module/migration_example.py +42 -64
  144. claude_mpm/commands/__init__.py +14 -0
  145. claude_mpm/commands/mpm-config.md +28 -0
  146. claude_mpm/commands/mpm-doctor.md +20 -0
  147. claude_mpm/commands/mpm-help.md +20 -0
  148. claude_mpm/commands/mpm-init.md +120 -0
  149. claude_mpm/commands/mpm-monitor.md +31 -0
  150. claude_mpm/commands/mpm-organize.md +120 -0
  151. claude_mpm/commands/mpm-postmortem.md +21 -0
  152. claude_mpm/commands/mpm-session-resume.md +30 -0
  153. claude_mpm/commands/mpm-status.md +20 -0
  154. claude_mpm/commands/mpm-ticket-view.md +109 -0
  155. claude_mpm/commands/mpm-version.md +20 -0
  156. claude_mpm/commands/mpm.md +31 -0
  157. claude_mpm/config/__init__.py +42 -2
  158. claude_mpm/config/agent_config.py +402 -0
  159. claude_mpm/config/agent_presets.py +488 -0
  160. claude_mpm/config/agent_sources.py +352 -0
  161. claude_mpm/config/experimental_features.py +217 -0
  162. claude_mpm/config/model_config.py +428 -0
  163. claude_mpm/config/paths.py +258 -0
  164. claude_mpm/config/skill_presets.py +392 -0
  165. claude_mpm/config/skill_sources.py +590 -0
  166. claude_mpm/config/socketio_config.py +125 -83
  167. claude_mpm/constants.py +132 -22
  168. claude_mpm/core/__init__.py +62 -36
  169. claude_mpm/core/agent_name_normalizer.py +71 -73
  170. claude_mpm/core/agent_registry.py +385 -492
  171. claude_mpm/core/agent_session_manager.py +81 -70
  172. claude_mpm/core/api_validator.py +330 -0
  173. claude_mpm/core/base_service.py +159 -122
  174. claude_mpm/core/cache.py +560 -0
  175. claude_mpm/core/claude_runner.py +696 -916
  176. claude_mpm/core/config.py +613 -122
  177. claude_mpm/core/config_aliases.py +74 -73
  178. claude_mpm/core/config_constants.py +314 -0
  179. claude_mpm/core/constants.py +361 -0
  180. claude_mpm/core/container.py +646 -104
  181. claude_mpm/core/enums.py +452 -0
  182. claude_mpm/core/error_handler.py +623 -0
  183. claude_mpm/core/exceptions.py +536 -0
  184. claude_mpm/core/factories.py +105 -109
  185. claude_mpm/core/file_utils.py +764 -0
  186. claude_mpm/core/framework/__init__.py +25 -0
  187. claude_mpm/core/framework/formatters/__init__.py +11 -0
  188. claude_mpm/core/framework/formatters/capability_generator.py +367 -0
  189. claude_mpm/core/framework/formatters/content_formatter.py +278 -0
  190. claude_mpm/core/framework/formatters/context_generator.py +185 -0
  191. claude_mpm/core/framework/loaders/__init__.py +13 -0
  192. claude_mpm/core/framework/loaders/agent_loader.py +213 -0
  193. claude_mpm/core/framework/loaders/file_loader.py +176 -0
  194. claude_mpm/core/framework/loaders/instruction_loader.py +222 -0
  195. claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
  196. claude_mpm/core/framework/processors/__init__.py +11 -0
  197. claude_mpm/core/framework/processors/memory_processor.py +230 -0
  198. claude_mpm/core/framework/processors/metadata_processor.py +146 -0
  199. claude_mpm/core/framework/processors/template_processor.py +244 -0
  200. claude_mpm/core/framework_loader.py +485 -414
  201. claude_mpm/core/hook_error_memory.py +381 -0
  202. claude_mpm/core/hook_manager.py +246 -86
  203. claude_mpm/core/hook_performance_config.py +147 -0
  204. claude_mpm/core/injectable_service.py +72 -63
  205. claude_mpm/core/instruction_reinforcement_hook.py +267 -0
  206. claude_mpm/core/interactive_session.py +670 -0
  207. claude_mpm/core/interfaces.py +570 -164
  208. claude_mpm/core/lazy.py +467 -0
  209. claude_mpm/core/log_manager.py +707 -0
  210. claude_mpm/core/logger.py +295 -134
  211. claude_mpm/core/logging_config.py +474 -0
  212. claude_mpm/core/logging_utils.py +520 -0
  213. claude_mpm/core/minimal_framework_loader.py +24 -22
  214. claude_mpm/core/mixins.py +30 -29
  215. claude_mpm/core/oneshot_session.py +594 -0
  216. claude_mpm/core/optimized_agent_loader.py +479 -0
  217. claude_mpm/core/optimized_startup.py +554 -0
  218. claude_mpm/core/output_style_manager.py +483 -0
  219. claude_mpm/core/pm_hook_interceptor.py +197 -82
  220. claude_mpm/core/protocols/__init__.py +23 -0
  221. claude_mpm/core/protocols/runner_protocol.py +103 -0
  222. claude_mpm/core/protocols/session_protocol.py +131 -0
  223. claude_mpm/core/service_registry.py +153 -116
  224. claude_mpm/core/session_manager.py +179 -64
  225. claude_mpm/core/shared/__init__.py +17 -0
  226. claude_mpm/core/shared/config_loader.py +326 -0
  227. claude_mpm/core/shared/path_resolver.py +281 -0
  228. claude_mpm/core/shared/singleton_manager.py +221 -0
  229. claude_mpm/core/socketio_pool.py +400 -137
  230. claude_mpm/core/system_context.py +38 -0
  231. claude_mpm/core/tool_access_control.py +64 -57
  232. claude_mpm/core/types.py +307 -0
  233. claude_mpm/core/typing_utils.py +553 -0
  234. claude_mpm/core/unified_agent_registry.py +969 -0
  235. claude_mpm/core/unified_config.py +570 -0
  236. claude_mpm/core/unified_paths.py +941 -0
  237. claude_mpm/dashboard/__init__.py +12 -0
  238. claude_mpm/dashboard/api/simple_directory.py +261 -0
  239. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  240. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +1 -0
  241. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +1 -0
  242. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +1 -0
  243. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +24 -0
  244. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B0uc0UOD.js +36 -0
  245. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7RN905-.js +1 -0
  246. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7xVLGWV.js +2 -0
  247. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BIF9m_hv.js +61 -0
  248. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +1 -0
  249. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BPYeabCQ.js +1 -0
  250. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
  251. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BSNlmTZj.js +1 -0
  252. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Be7GpZd6.js +7 -0
  253. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bh0LDWpI.js +145 -0
  254. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BofRWZRR.js +10 -0
  255. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BovzEFCE.js +30 -0
  256. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C30mlcqg.js +165 -0
  257. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4B-KCzX.js +1 -0
  258. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4JcI4KD.js +122 -0
  259. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CBBdVcY8.js +1 -0
  260. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CDuw-vjf.js +1 -0
  261. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C_Usid8X.js +15 -0
  262. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cfqx1Qun.js +10 -0
  263. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CiIAseT4.js +128 -0
  264. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
  265. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CnA0NrzZ.js +1 -0
  266. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cs_tUR18.js +24 -0
  267. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
  268. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CyWMqx4W.js +43 -0
  269. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzZX-COe.js +220 -0
  270. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzeYkLYB.js +65 -0
  271. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D3k0OPJN.js +4 -0
  272. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9lljYKQ.js +1 -0
  273. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DGkLK5U1.js +267 -0
  274. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DI7hHRFL.js +1 -0
  275. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DLVjFsZ3.js +139 -0
  276. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUrLdbGD.js +89 -0
  277. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
  278. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DY1XQ8fi.js +2 -0
  279. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DZX00Y4g.js +1 -0
  280. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +1 -0
  281. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DaimHw_p.js +68 -0
  282. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +323 -0
  283. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dhb8PKl3.js +1 -0
  284. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dle-35c7.js +64 -0
  285. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DmxopI1J.js +1 -0
  286. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DwBR2MJi.js +60 -0
  287. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/GYwsonyD.js +1 -0
  288. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
  289. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/NqQ1dWOy.js +1 -0
  290. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
  291. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Vzk33B_K.js +2 -0
  292. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ZGh7QtNv.js +7 -0
  293. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bT1r9zLR.js +1 -0
  294. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bTOqqlTd.js +1 -0
  295. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/eNVUfhuA.js +1 -0
  296. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/iEWssX7S.js +162 -0
  297. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/sQeU3Y1z.js +1 -0
  298. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uuIeMWc-.js +1 -0
  299. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.D6-I5TpK.js +2 -0
  300. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +1 -0
  301. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.m1gL8KXf.js +1 -0
  302. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.CgNOuw-d.js +1 -0
  303. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +1 -0
  304. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  305. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  306. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  307. claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  308. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  309. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  310. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
  311. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  312. claude_mpm/experimental/__init__.py +10 -0
  313. claude_mpm/experimental/cli_enhancements.py +104 -89
  314. claude_mpm/generators/__init__.py +1 -1
  315. claude_mpm/generators/agent_profile_generator.py +76 -66
  316. claude_mpm/hooks/__init__.py +37 -1
  317. claude_mpm/hooks/base_hook.py +37 -32
  318. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  319. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  320. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  321. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  322. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  323. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  324. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  325. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  326. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  327. claude_mpm/hooks/claude_hooks/connection_pool.py +250 -0
  328. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  329. claude_mpm/hooks/claude_hooks/event_handlers.py +888 -0
  330. claude_mpm/hooks/claude_hooks/hook_handler.py +652 -875
  331. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +10 -7
  332. claude_mpm/hooks/claude_hooks/installer.py +806 -0
  333. claude_mpm/hooks/claude_hooks/memory_integration.py +249 -0
  334. claude_mpm/hooks/claude_hooks/response_tracking.py +412 -0
  335. claude_mpm/hooks/claude_hooks/services/__init__.py +15 -0
  336. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  337. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  338. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  339. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  340. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  341. claude_mpm/hooks/claude_hooks/services/connection_manager.py +229 -0
  342. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +254 -0
  343. claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
  344. claude_mpm/hooks/claude_hooks/services/state_manager.py +284 -0
  345. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
  346. claude_mpm/hooks/claude_hooks/tool_analysis.py +224 -0
  347. claude_mpm/hooks/failure_learning/__init__.py +54 -0
  348. claude_mpm/hooks/failure_learning/failure_detection_hook.py +230 -0
  349. claude_mpm/hooks/failure_learning/fix_detection_hook.py +212 -0
  350. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +281 -0
  351. claude_mpm/hooks/instruction_reinforcement.py +301 -0
  352. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  353. claude_mpm/hooks/kuzu_memory_hook.py +386 -0
  354. claude_mpm/hooks/kuzu_response_hook.py +179 -0
  355. claude_mpm/hooks/memory_integration_hook.py +201 -107
  356. claude_mpm/hooks/session_resume_hook.py +121 -0
  357. claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
  358. claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
  359. claude_mpm/hooks/tool_call_interceptor.py +92 -76
  360. claude_mpm/hooks/validation_hooks.py +62 -54
  361. claude_mpm/init.py +518 -83
  362. claude_mpm/models/__init__.py +9 -9
  363. claude_mpm/models/agent_definition.py +40 -23
  364. claude_mpm/models/agent_session.py +538 -0
  365. claude_mpm/models/git_repository.py +198 -0
  366. claude_mpm/models/resume_log.py +340 -0
  367. claude_mpm/schemas/__init__.py +12 -0
  368. claude_mpm/scripts/__init__.py +15 -0
  369. claude_mpm/scripts/claude-hook-handler.sh +227 -0
  370. claude_mpm/scripts/launch_monitor.py +165 -0
  371. claude_mpm/scripts/mpm_doctor.py +322 -0
  372. claude_mpm/scripts/socketio_daemon.py +189 -200
  373. claude_mpm/scripts/start_activity_logging.py +91 -0
  374. claude_mpm/services/__init__.py +208 -39
  375. claude_mpm/services/agent_capabilities_service.py +266 -0
  376. claude_mpm/services/agents/__init__.py +89 -0
  377. claude_mpm/services/agents/agent_builder.py +514 -0
  378. claude_mpm/services/agents/agent_preset_service.py +238 -0
  379. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  380. claude_mpm/services/agents/agent_review_service.py +280 -0
  381. claude_mpm/services/agents/agent_selection_service.py +484 -0
  382. claude_mpm/services/agents/auto_config_manager.py +796 -0
  383. claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
  384. claude_mpm/services/agents/cache_git_manager.py +621 -0
  385. claude_mpm/services/agents/deployment/__init__.py +21 -0
  386. claude_mpm/services/agents/deployment/agent_config_provider.py +410 -0
  387. claude_mpm/services/agents/deployment/agent_configuration_manager.py +358 -0
  388. claude_mpm/services/agents/deployment/agent_definition_factory.py +80 -0
  389. claude_mpm/services/agents/deployment/agent_deployment.py +1037 -0
  390. claude_mpm/services/agents/deployment/agent_discovery_service.py +546 -0
  391. claude_mpm/services/agents/deployment/agent_environment_manager.py +288 -0
  392. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +383 -0
  393. claude_mpm/services/agents/deployment/agent_format_converter.py +505 -0
  394. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +160 -0
  395. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +957 -0
  396. claude_mpm/services/agents/deployment/agent_metrics_collector.py +273 -0
  397. claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
  398. claude_mpm/services/agents/deployment/agent_record_service.py +418 -0
  399. claude_mpm/services/agents/deployment/agent_restore_handler.py +84 -0
  400. claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
  401. claude_mpm/services/agents/deployment/agent_template_builder.py +1369 -0
  402. claude_mpm/services/agents/deployment/agent_validator.py +376 -0
  403. claude_mpm/services/agents/deployment/agent_version_manager.py +322 -0
  404. claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +10 -13
  405. claude_mpm/services/agents/deployment/agents_directory_resolver.py +149 -0
  406. claude_mpm/services/agents/deployment/async_agent_deployment.py +768 -0
  407. claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
  408. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  409. claude_mpm/services/agents/deployment/config/deployment_config.py +181 -0
  410. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  411. claude_mpm/services/agents/deployment/deployment_config_loader.py +178 -0
  412. claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
  413. claude_mpm/services/agents/deployment/deployment_type_detector.py +120 -0
  414. claude_mpm/services/agents/deployment/deployment_wrapper.py +129 -0
  415. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  416. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  417. claude_mpm/services/agents/deployment/facade/deployment_executor.py +70 -0
  418. claude_mpm/services/agents/deployment/facade/deployment_facade.py +269 -0
  419. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  420. claude_mpm/services/agents/deployment/interface_adapter.py +226 -0
  421. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  422. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  423. claude_mpm/services/agents/deployment/local_template_deployment.py +362 -0
  424. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +1478 -0
  425. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  426. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  427. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +162 -0
  428. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  429. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  430. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +240 -0
  431. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +110 -0
  432. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +80 -0
  433. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +92 -0
  434. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +101 -0
  435. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  436. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +102 -0
  437. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  438. claude_mpm/services/agents/deployment/processors/agent_processor.py +269 -0
  439. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +311 -0
  440. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +862 -0
  441. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  442. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  443. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  444. claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
  445. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  446. claude_mpm/services/agents/deployment/strategies/base_strategy.py +113 -0
  447. claude_mpm/services/agents/deployment/strategies/project_strategy.py +148 -0
  448. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  449. claude_mpm/services/agents/deployment/strategies/system_strategy.py +131 -0
  450. claude_mpm/services/agents/deployment/strategies/user_strategy.py +130 -0
  451. claude_mpm/services/agents/deployment/system_instructions_deployer.py +228 -0
  452. claude_mpm/services/agents/deployment/validation/__init__.py +21 -0
  453. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  454. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  455. claude_mpm/services/agents/deployment/validation/template_validator.py +319 -0
  456. claude_mpm/services/agents/deployment/validation/validation_result.py +214 -0
  457. claude_mpm/services/agents/git_source_manager.py +682 -0
  458. claude_mpm/services/agents/loading/__init__.py +11 -0
  459. claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +306 -228
  460. claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +106 -91
  461. claude_mpm/services/agents/loading/framework_agent_loader.py +433 -0
  462. claude_mpm/services/agents/local_template_manager.py +784 -0
  463. claude_mpm/services/agents/management/__init__.py +9 -0
  464. claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +92 -69
  465. claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +219 -168
  466. claude_mpm/services/agents/memory/__init__.py +22 -0
  467. claude_mpm/services/agents/memory/agent_memory_manager.py +784 -0
  468. claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +20 -18
  469. claude_mpm/services/agents/memory/content_manager.py +470 -0
  470. claude_mpm/services/agents/memory/memory_categorization_service.py +167 -0
  471. claude_mpm/services/agents/memory/memory_file_service.py +129 -0
  472. claude_mpm/services/agents/memory/memory_format_service.py +201 -0
  473. claude_mpm/services/agents/memory/memory_limits_service.py +101 -0
  474. claude_mpm/services/agents/memory/template_generator.py +83 -0
  475. claude_mpm/services/agents/observers.py +547 -0
  476. claude_mpm/services/agents/recommender.py +617 -0
  477. claude_mpm/services/agents/registry/__init__.py +30 -0
  478. claude_mpm/services/agents/registry/deployed_agent_discovery.py +273 -0
  479. claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +370 -295
  480. claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
  481. claude_mpm/services/agents/sources/__init__.py +13 -0
  482. claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
  483. claude_mpm/services/agents/sources/git_source_sync_service.py +1202 -0
  484. claude_mpm/services/agents/startup_sync.py +259 -0
  485. claude_mpm/services/agents/toolchain_detector.py +478 -0
  486. claude_mpm/services/analysis/__init__.py +35 -0
  487. claude_mpm/services/analysis/clone_detector.py +1030 -0
  488. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  489. claude_mpm/services/analysis/postmortem_service.py +765 -0
  490. claude_mpm/services/async_session_logger.py +665 -0
  491. claude_mpm/services/claude_session_logger.py +321 -0
  492. claude_mpm/services/cli/__init__.py +18 -0
  493. claude_mpm/services/cli/agent_cleanup_service.py +408 -0
  494. claude_mpm/services/cli/agent_dependency_service.py +395 -0
  495. claude_mpm/services/cli/agent_listing_service.py +463 -0
  496. claude_mpm/services/cli/agent_output_formatter.py +605 -0
  497. claude_mpm/services/cli/agent_validation_service.py +590 -0
  498. claude_mpm/services/cli/memory_crud_service.py +622 -0
  499. claude_mpm/services/cli/memory_output_formatter.py +604 -0
  500. claude_mpm/services/cli/resume_service.py +617 -0
  501. claude_mpm/services/cli/session_manager.py +604 -0
  502. claude_mpm/services/cli/session_pause_manager.py +504 -0
  503. claude_mpm/services/cli/session_resume_helper.py +372 -0
  504. claude_mpm/services/cli/startup_checker.py +362 -0
  505. claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
  506. claude_mpm/services/command_deployment_service.py +446 -0
  507. claude_mpm/services/command_handler_service.py +221 -0
  508. claude_mpm/services/communication/__init__.py +22 -0
  509. claude_mpm/services/core/__init__.py +108 -0
  510. claude_mpm/services/core/base.py +269 -0
  511. claude_mpm/services/core/cache_manager.py +309 -0
  512. claude_mpm/services/core/interfaces/__init__.py +273 -0
  513. claude_mpm/services/core/interfaces/agent.py +514 -0
  514. claude_mpm/services/core/interfaces/communication.py +316 -0
  515. claude_mpm/services/core/interfaces/health.py +169 -0
  516. claude_mpm/services/core/interfaces/infrastructure.py +357 -0
  517. claude_mpm/services/core/interfaces/model.py +281 -0
  518. claude_mpm/services/core/interfaces/process.py +372 -0
  519. claude_mpm/services/core/interfaces/project.py +121 -0
  520. claude_mpm/services/core/interfaces/restart.py +307 -0
  521. claude_mpm/services/core/interfaces/service.py +405 -0
  522. claude_mpm/services/core/interfaces/stability.py +260 -0
  523. claude_mpm/services/core/interfaces.py +81 -0
  524. claude_mpm/services/core/memory_manager.py +682 -0
  525. claude_mpm/services/core/models/__init__.py +70 -0
  526. claude_mpm/services/core/models/agent_config.py +384 -0
  527. claude_mpm/services/core/models/health.py +162 -0
  528. claude_mpm/services/core/models/process.py +239 -0
  529. claude_mpm/services/core/models/restart.py +302 -0
  530. claude_mpm/services/core/models/stability.py +264 -0
  531. claude_mpm/services/core/models/toolchain.py +306 -0
  532. claude_mpm/services/core/path_resolver.py +517 -0
  533. claude_mpm/services/core/service_container.py +520 -0
  534. claude_mpm/services/core/service_interfaces.py +436 -0
  535. claude_mpm/services/diagnostics/__init__.py +18 -0
  536. claude_mpm/services/diagnostics/checks/__init__.py +38 -0
  537. claude_mpm/services/diagnostics/checks/agent_check.py +370 -0
  538. claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
  539. claude_mpm/services/diagnostics/checks/base_check.py +60 -0
  540. claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
  541. claude_mpm/services/diagnostics/checks/common_issues_check.py +363 -0
  542. claude_mpm/services/diagnostics/checks/configuration_check.py +306 -0
  543. claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
  544. claude_mpm/services/diagnostics/checks/installation_check.py +520 -0
  545. claude_mpm/services/diagnostics/checks/instructions_check.py +415 -0
  546. claude_mpm/services/diagnostics/checks/mcp_check.py +330 -0
  547. claude_mpm/services/diagnostics/checks/mcp_services_check.py +1058 -0
  548. claude_mpm/services/diagnostics/checks/monitor_check.py +281 -0
  549. claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
  550. claude_mpm/services/diagnostics/checks/startup_log_check.py +319 -0
  551. claude_mpm/services/diagnostics/diagnostic_runner.py +286 -0
  552. claude_mpm/services/diagnostics/doctor_reporter.py +578 -0
  553. claude_mpm/services/diagnostics/models.py +138 -0
  554. claude_mpm/services/event_aggregator.py +582 -0
  555. claude_mpm/services/event_bus/__init__.py +18 -0
  556. claude_mpm/services/event_bus/config.py +186 -0
  557. claude_mpm/services/event_bus/direct_relay.py +312 -0
  558. claude_mpm/services/event_bus/event_bus.py +396 -0
  559. claude_mpm/services/event_bus/relay.py +326 -0
  560. claude_mpm/services/events/__init__.py +44 -0
  561. claude_mpm/services/events/consumers/__init__.py +18 -0
  562. claude_mpm/services/events/consumers/dead_letter.py +306 -0
  563. claude_mpm/services/events/consumers/logging.py +184 -0
  564. claude_mpm/services/events/consumers/metrics.py +241 -0
  565. claude_mpm/services/events/consumers/socketio.py +377 -0
  566. claude_mpm/services/events/core.py +480 -0
  567. claude_mpm/services/events/interfaces.py +214 -0
  568. claude_mpm/services/events/producers/__init__.py +14 -0
  569. claude_mpm/services/events/producers/hook.py +269 -0
  570. claude_mpm/services/events/producers/system.py +329 -0
  571. claude_mpm/services/exceptions.py +433 -353
  572. claude_mpm/services/framework_claude_md_generator/__init__.py +81 -80
  573. claude_mpm/services/framework_claude_md_generator/content_assembler.py +74 -67
  574. claude_mpm/services/framework_claude_md_generator/content_validator.py +66 -62
  575. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +82 -60
  576. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +36 -37
  577. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +41 -40
  578. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +15 -15
  579. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +5 -4
  580. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  581. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  582. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  583. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  584. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +5 -4
  585. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  586. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  587. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +26 -30
  588. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +6 -5
  589. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  590. claude_mpm/services/framework_claude_md_generator/version_manager.py +31 -30
  591. claude_mpm/services/git/__init__.py +21 -0
  592. claude_mpm/services/git/git_operations_service.py +579 -0
  593. claude_mpm/services/github/__init__.py +21 -0
  594. claude_mpm/services/github/github_cli_service.py +397 -0
  595. claude_mpm/services/hook_installer_service.py +506 -0
  596. claude_mpm/services/hook_service.py +159 -111
  597. claude_mpm/services/infrastructure/__init__.py +52 -0
  598. claude_mpm/services/infrastructure/context_preservation.py +569 -0
  599. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  600. claude_mpm/services/infrastructure/logging.py +209 -0
  601. claude_mpm/services/infrastructure/monitoring/__init__.py +39 -0
  602. claude_mpm/services/infrastructure/monitoring/aggregator.py +432 -0
  603. claude_mpm/services/infrastructure/monitoring/base.py +122 -0
  604. claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
  605. claude_mpm/services/infrastructure/monitoring/network.py +219 -0
  606. claude_mpm/services/infrastructure/monitoring/process.py +343 -0
  607. claude_mpm/services/infrastructure/monitoring/resources.py +244 -0
  608. claude_mpm/services/infrastructure/monitoring/service.py +368 -0
  609. claude_mpm/services/infrastructure/monitoring.py +71 -0
  610. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  611. claude_mpm/services/instructions/__init__.py +9 -0
  612. claude_mpm/services/instructions/instruction_cache_service.py +374 -0
  613. claude_mpm/services/local_ops/__init__.py +155 -0
  614. claude_mpm/services/local_ops/crash_detector.py +257 -0
  615. claude_mpm/services/local_ops/health_checks/__init__.py +26 -0
  616. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  617. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  618. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  619. claude_mpm/services/local_ops/health_manager.py +427 -0
  620. claude_mpm/services/local_ops/log_monitor.py +396 -0
  621. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  622. claude_mpm/services/local_ops/process_manager.py +595 -0
  623. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  624. claude_mpm/services/local_ops/restart_manager.py +401 -0
  625. claude_mpm/services/local_ops/restart_policy.py +387 -0
  626. claude_mpm/services/local_ops/state_manager.py +372 -0
  627. claude_mpm/services/local_ops/unified_manager.py +600 -0
  628. claude_mpm/services/mcp_config_manager.py +1542 -0
  629. claude_mpm/services/mcp_service_verifier.py +732 -0
  630. claude_mpm/services/memory/__init__.py +19 -0
  631. claude_mpm/services/{memory_builder.py → memory/builder.py} +465 -373
  632. claude_mpm/services/memory/cache/__init__.py +14 -0
  633. claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +237 -200
  634. claude_mpm/services/memory/cache/simple_cache.py +331 -0
  635. claude_mpm/services/memory/failure_tracker.py +578 -0
  636. claude_mpm/services/memory/indexed_memory.py +648 -0
  637. claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +272 -243
  638. claude_mpm/services/memory/router.py +951 -0
  639. claude_mpm/services/memory_hook_service.py +470 -0
  640. claude_mpm/services/model/__init__.py +147 -0
  641. claude_mpm/services/model/base_provider.py +365 -0
  642. claude_mpm/services/model/claude_provider.py +412 -0
  643. claude_mpm/services/model/model_router.py +452 -0
  644. claude_mpm/services/model/ollama_provider.py +415 -0
  645. claude_mpm/services/monitor/__init__.py +20 -0
  646. claude_mpm/services/monitor/daemon.py +698 -0
  647. claude_mpm/services/monitor/daemon_manager.py +1076 -0
  648. claude_mpm/services/monitor/event_emitter.py +350 -0
  649. claude_mpm/services/monitor/handlers/__init__.py +21 -0
  650. claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
  651. claude_mpm/services/monitor/handlers/dashboard.py +299 -0
  652. claude_mpm/services/monitor/handlers/file.py +264 -0
  653. claude_mpm/services/monitor/handlers/hooks.py +512 -0
  654. claude_mpm/services/monitor/management/__init__.py +18 -0
  655. claude_mpm/services/monitor/management/health.py +124 -0
  656. claude_mpm/services/monitor/management/lifecycle.py +730 -0
  657. claude_mpm/services/monitor/server.py +1493 -0
  658. claude_mpm/services/monitor_build_service.py +349 -0
  659. claude_mpm/services/native_agent_converter.py +356 -0
  660. claude_mpm/services/orphan_detection.py +786 -0
  661. claude_mpm/services/pm_skills_deployer.py +707 -0
  662. claude_mpm/services/port_manager.py +597 -0
  663. claude_mpm/services/pr/__init__.py +14 -0
  664. claude_mpm/services/pr/pr_template_service.py +329 -0
  665. claude_mpm/services/profile_manager.py +337 -0
  666. claude_mpm/services/project/__init__.py +44 -0
  667. claude_mpm/services/{project_analyzer.py → project/analyzer.py} +541 -291
  668. claude_mpm/services/project/analyzer_v2.py +566 -0
  669. claude_mpm/services/project/architecture_analyzer.py +461 -0
  670. claude_mpm/services/project/archive_manager.py +1045 -0
  671. claude_mpm/services/project/dependency_analyzer.py +462 -0
  672. claude_mpm/services/project/detection_strategies.py +719 -0
  673. claude_mpm/services/project/documentation_manager.py +554 -0
  674. claude_mpm/services/project/enhanced_analyzer.py +572 -0
  675. claude_mpm/services/project/language_analyzer.py +265 -0
  676. claude_mpm/services/project/metrics_collector.py +407 -0
  677. claude_mpm/services/project/project_organizer.py +1009 -0
  678. claude_mpm/services/project/registry.py +636 -0
  679. claude_mpm/services/project/toolchain_analyzer.py +583 -0
  680. claude_mpm/services/project_port_allocator.py +596 -0
  681. claude_mpm/services/recovery_manager.py +293 -240
  682. claude_mpm/services/response_tracker.py +267 -0
  683. claude_mpm/services/runner_configuration_service.py +605 -0
  684. claude_mpm/services/self_upgrade_service.py +608 -0
  685. claude_mpm/services/session_management_service.py +314 -0
  686. claude_mpm/services/session_manager.py +380 -0
  687. claude_mpm/services/shared/__init__.py +21 -0
  688. claude_mpm/services/shared/async_service_base.py +216 -0
  689. claude_mpm/services/shared/config_service_base.py +301 -0
  690. claude_mpm/services/shared/lifecycle_service_base.py +308 -0
  691. claude_mpm/services/shared/manager_base.py +315 -0
  692. claude_mpm/services/shared/service_factory.py +309 -0
  693. claude_mpm/services/skills/__init__.py +21 -0
  694. claude_mpm/services/skills/git_skill_source_manager.py +1324 -0
  695. claude_mpm/services/skills/selective_skill_deployer.py +744 -0
  696. claude_mpm/services/skills/skill_discovery_service.py +568 -0
  697. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  698. claude_mpm/services/skills_config.py +547 -0
  699. claude_mpm/services/skills_deployer.py +1168 -0
  700. claude_mpm/services/socketio/__init__.py +25 -0
  701. claude_mpm/services/socketio/client_proxy.py +229 -0
  702. claude_mpm/services/socketio/dashboard_server.py +362 -0
  703. claude_mpm/services/socketio/event_normalizer.py +798 -0
  704. claude_mpm/services/socketio/handlers/__init__.py +30 -0
  705. claude_mpm/services/socketio/handlers/base.py +136 -0
  706. claude_mpm/services/socketio/handlers/code_analysis.py +682 -0
  707. claude_mpm/services/socketio/handlers/connection.py +643 -0
  708. claude_mpm/services/socketio/handlers/connection_handler.py +333 -0
  709. claude_mpm/services/socketio/handlers/file.py +263 -0
  710. claude_mpm/services/socketio/handlers/git.py +962 -0
  711. claude_mpm/services/socketio/handlers/hook.py +211 -0
  712. claude_mpm/services/socketio/handlers/memory.py +26 -0
  713. claude_mpm/services/socketio/handlers/project.py +24 -0
  714. claude_mpm/services/socketio/handlers/registry.py +214 -0
  715. claude_mpm/services/socketio/migration_utils.py +343 -0
  716. claude_mpm/services/socketio/monitor_client.py +364 -0
  717. claude_mpm/services/socketio/server/__init__.py +18 -0
  718. claude_mpm/services/socketio/server/broadcaster.py +569 -0
  719. claude_mpm/services/socketio/server/connection_manager.py +579 -0
  720. claude_mpm/services/socketio/server/core.py +1079 -0
  721. claude_mpm/services/socketio/server/eventbus_integration.py +245 -0
  722. claude_mpm/services/socketio/server/main.py +501 -0
  723. claude_mpm/services/socketio_client_manager.py +173 -143
  724. claude_mpm/services/socketio_server.py +38 -1657
  725. claude_mpm/services/subprocess_launcher_service.py +322 -0
  726. claude_mpm/services/system_instructions_service.py +270 -0
  727. claude_mpm/services/ticket_manager.py +25 -209
  728. claude_mpm/services/ticket_services/__init__.py +26 -0
  729. claude_mpm/services/ticket_services/crud_service.py +328 -0
  730. claude_mpm/services/ticket_services/formatter_service.py +290 -0
  731. claude_mpm/services/ticket_services/search_service.py +324 -0
  732. claude_mpm/services/ticket_services/validation_service.py +303 -0
  733. claude_mpm/services/ticket_services/workflow_service.py +244 -0
  734. claude_mpm/services/unified/__init__.py +65 -0
  735. claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
  736. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
  737. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
  738. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +900 -0
  739. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +745 -0
  740. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
  741. claude_mpm/services/unified/config_strategies/__init__.py +175 -0
  742. claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
  743. claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
  744. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
  745. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
  746. claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
  747. claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
  748. claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
  749. claude_mpm/services/unified/deployment_strategies/base.py +553 -0
  750. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
  751. claude_mpm/services/unified/deployment_strategies/local.py +607 -0
  752. claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
  753. claude_mpm/services/unified/deployment_strategies/vercel.py +471 -0
  754. claude_mpm/services/unified/interfaces.py +475 -0
  755. claude_mpm/services/unified/migration.py +509 -0
  756. claude_mpm/services/unified/strategies.py +534 -0
  757. claude_mpm/services/unified/unified_analyzer.py +542 -0
  758. claude_mpm/services/unified/unified_config.py +691 -0
  759. claude_mpm/services/unified/unified_deployment.py +466 -0
  760. claude_mpm/services/utility_service.py +280 -0
  761. claude_mpm/services/version_control/__init__.py +34 -37
  762. claude_mpm/services/version_control/branch_strategy.py +26 -17
  763. claude_mpm/services/version_control/conflict_resolution.py +52 -36
  764. claude_mpm/services/version_control/git_operations.py +183 -49
  765. claude_mpm/services/version_control/semantic_versioning.py +172 -61
  766. claude_mpm/services/version_control/version_parser.py +546 -0
  767. claude_mpm/services/version_service.py +379 -0
  768. claude_mpm/services/visualization/__init__.py +15 -0
  769. claude_mpm/services/visualization/mermaid_generator.py +937 -0
  770. claude_mpm/skills/__init__.py +42 -0
  771. claude_mpm/skills/agent_skills_injector.py +324 -0
  772. claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
  773. claude_mpm/skills/bundled/__init__.py +6 -0
  774. claude_mpm/skills/bundled/api-documentation.md +393 -0
  775. claude_mpm/skills/bundled/async-testing.md +571 -0
  776. claude_mpm/skills/bundled/code-review.md +143 -0
  777. claude_mpm/skills/bundled/database-migration.md +199 -0
  778. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  779. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  780. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  781. claude_mpm/skills/bundled/git-workflow.md +414 -0
  782. claude_mpm/skills/bundled/imagemagick.md +204 -0
  783. claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
  784. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  785. claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
  786. claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
  787. claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
  788. claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
  789. claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
  790. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  791. claude_mpm/skills/bundled/pdf.md +141 -0
  792. claude_mpm/skills/bundled/performance-profiling.md +573 -0
  793. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  794. claude_mpm/skills/bundled/security-scanning.md +439 -0
  795. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  796. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  797. claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
  798. claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
  799. claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
  800. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
  801. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  802. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  803. claude_mpm/skills/bundled/xlsx.md +157 -0
  804. claude_mpm/skills/registry.py +286 -0
  805. claude_mpm/skills/skill_manager.py +405 -0
  806. claude_mpm/skills/skills_registry.py +347 -0
  807. claude_mpm/skills/skills_service.py +739 -0
  808. claude_mpm/storage/__init__.py +9 -0
  809. claude_mpm/storage/state_storage.py +546 -0
  810. claude_mpm/templates/.pre-commit-config.yaml +112 -0
  811. claude_mpm/templates/questions/__init__.py +38 -0
  812. claude_mpm/templates/questions/base.py +193 -0
  813. claude_mpm/templates/questions/pr_strategy.py +311 -0
  814. claude_mpm/templates/questions/project_init.py +385 -0
  815. claude_mpm/templates/questions/ticket_mgmt.py +394 -0
  816. claude_mpm/ticket_wrapper.py +2 -2
  817. claude_mpm/tools/__init__.py +10 -0
  818. claude_mpm/tools/__main__.py +208 -0
  819. claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
  820. claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
  821. claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
  822. claude_mpm/tools/code_tree_analyzer/core.py +380 -0
  823. claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
  824. claude_mpm/tools/code_tree_analyzer/events.py +168 -0
  825. claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
  826. claude_mpm/tools/code_tree_analyzer/models.py +39 -0
  827. claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
  828. claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
  829. claude_mpm/tools/code_tree_builder.py +631 -0
  830. claude_mpm/tools/code_tree_events.py +420 -0
  831. claude_mpm/tools/socketio_debug.py +671 -0
  832. claude_mpm/utils/__init__.py +8 -8
  833. claude_mpm/utils/agent_dependency_loader.py +1090 -0
  834. claude_mpm/utils/agent_filters.py +261 -0
  835. claude_mpm/utils/common.py +544 -0
  836. claude_mpm/utils/config_manager.py +168 -126
  837. claude_mpm/utils/console.py +11 -0
  838. claude_mpm/utils/database_connector.py +298 -0
  839. claude_mpm/utils/dependency_cache.py +373 -0
  840. claude_mpm/utils/dependency_manager.py +60 -59
  841. claude_mpm/utils/dependency_strategies.py +381 -0
  842. claude_mpm/utils/display_helper.py +260 -0
  843. claude_mpm/utils/environment_context.py +313 -0
  844. claude_mpm/utils/error_handler.py +78 -66
  845. claude_mpm/utils/file_utils.py +305 -0
  846. claude_mpm/utils/framework_detection.py +12 -11
  847. claude_mpm/utils/git_analyzer.py +407 -0
  848. claude_mpm/utils/gitignore.py +244 -0
  849. claude_mpm/utils/import_migration_example.py +12 -60
  850. claude_mpm/utils/imports.py +48 -45
  851. claude_mpm/utils/log_cleanup.py +627 -0
  852. claude_mpm/utils/migration.py +372 -0
  853. claude_mpm/utils/path_operations.py +110 -104
  854. claude_mpm/utils/progress.py +387 -0
  855. claude_mpm/utils/robust_installer.py +823 -0
  856. claude_mpm/utils/session_logging.py +121 -0
  857. claude_mpm/utils/structured_questions.py +619 -0
  858. claude_mpm/utils/subprocess_utils.py +343 -0
  859. claude_mpm/validation/__init__.py +1 -1
  860. claude_mpm/validation/agent_validator.py +214 -108
  861. claude_mpm/validation/frontmatter_validator.py +252 -0
  862. claude_mpm-5.4.55.dist-info/METADATA +999 -0
  863. claude_mpm-5.4.55.dist-info/RECORD +868 -0
  864. {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/entry_points.txt +1 -3
  865. claude_mpm-5.4.55.dist-info/licenses/LICENSE +94 -0
  866. claude_mpm-5.4.55.dist-info/licenses/LICENSE-FAQ.md +153 -0
  867. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -88
  868. claude_mpm/agents/INSTRUCTIONS.md +0 -352
  869. claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
  870. claude_mpm/agents/base_agent_loader.py +0 -529
  871. claude_mpm/agents/schema/agent_schema.json +0 -314
  872. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
  873. claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -46
  874. claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -45
  875. claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -49
  876. claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -46
  877. claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -45
  878. claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -49
  879. claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -46
  880. claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -46
  881. claude_mpm/agents/templates/data_engineer.json +0 -110
  882. claude_mpm/agents/templates/documentation.json +0 -109
  883. claude_mpm/agents/templates/engineer.json +0 -113
  884. claude_mpm/agents/templates/ops.json +0 -109
  885. claude_mpm/agents/templates/pm.json +0 -25
  886. claude_mpm/agents/templates/qa.json +0 -111
  887. claude_mpm/agents/templates/research.json +0 -65
  888. claude_mpm/agents/templates/security.json +0 -113
  889. claude_mpm/agents/templates/test_integration.json +0 -112
  890. claude_mpm/agents/templates/version_control.json +0 -107
  891. claude_mpm/cli/commands/ui.py +0 -57
  892. claude_mpm/core/simple_runner.py +0 -1046
  893. claude_mpm/dashboard/open_dashboard.py +0 -34
  894. claude_mpm/deployment_paths.py +0 -261
  895. claude_mpm/hooks/builtin/__init__.py +0 -1
  896. claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
  897. claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
  898. claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
  899. claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
  900. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
  901. claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
  902. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
  903. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
  904. claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
  905. claude_mpm/orchestration/__init__.py +0 -6
  906. claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
  907. claude_mpm/orchestration/archive/factory.py +0 -215
  908. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
  909. claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
  910. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
  911. claude_mpm/orchestration/archive/orchestrator.py +0 -501
  912. claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
  913. claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
  914. claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
  915. claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
  916. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
  917. claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
  918. claude_mpm/schemas/workflow_validator.py +0 -411
  919. claude_mpm/services/agent_deployment.py +0 -1534
  920. claude_mpm/services/agent_lifecycle_manager.py +0 -1169
  921. claude_mpm/services/agent_memory_manager.py +0 -1415
  922. claude_mpm/services/agent_registry.py +0 -676
  923. claude_mpm/services/deployed_agent_discovery.py +0 -226
  924. claude_mpm/services/framework_agent_loader.py +0 -337
  925. claude_mpm/services/framework_claude_md_generator.py +0 -621
  926. claude_mpm/services/health_monitor.py +0 -892
  927. claude_mpm/services/memory_router.py +0 -538
  928. claude_mpm/services/parent_directory_manager/__init__.py +0 -577
  929. claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
  930. claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
  931. claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
  932. claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
  933. claude_mpm/services/parent_directory_manager/operations.py +0 -186
  934. claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
  935. claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
  936. claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
  937. claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
  938. claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
  939. claude_mpm/services/standalone_socketio_server.py +0 -1300
  940. claude_mpm/services/ticket_manager_di.py +0 -318
  941. claude_mpm/services/ticketing_service_original.py +0 -508
  942. claude_mpm/ui/__init__.py +0 -1
  943. claude_mpm/ui/rich_terminal_ui.py +0 -295
  944. claude_mpm/ui/terminal_ui.py +0 -328
  945. claude_mpm/utils/paths.py +0 -289
  946. claude_mpm-3.4.10.dist-info/METADATA +0 -183
  947. claude_mpm-3.4.10.dist-info/RECORD +0 -201
  948. claude_mpm-3.4.10.dist-info/licenses/LICENSE +0 -21
  949. {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/WHEEL +0 -0
  950. {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1493 @@
1
+ """
2
+ Unified Monitor Server for Claude MPM
3
+ ====================================
4
+
5
+ WHY: This server combines HTTP dashboard serving and Socket.IO event handling
6
+ into a single, stable process. It uses real AST analysis instead of mock data
7
+ and provides all monitoring functionality on a single port.
8
+
9
+ DESIGN DECISIONS:
10
+ - Combines aiohttp HTTP server with Socket.IO server
11
+ - Uses real CodeTreeAnalyzer for AST analysis
12
+ - Single port (8765) for all functionality
13
+ - Event-driven architecture with proper handler registration
14
+ - Built for stability and daemon operation
15
+ """
16
+
17
+ import asyncio
18
+ import os
19
+ import threading
20
+ import time
21
+ from datetime import datetime, timezone
22
+ from pathlib import Path
23
+ from typing import Dict, Optional
24
+
25
+ import socketio
26
+ from aiohttp import web
27
+ from watchdog.events import FileSystemEventHandler
28
+ from watchdog.observers import Observer
29
+
30
+ from ...core.enums import ServiceState
31
+ from ...core.logging_config import get_logger
32
+ from .event_emitter import get_event_emitter
33
+ from .handlers.code_analysis import CodeAnalysisHandler
34
+ from .handlers.dashboard import DashboardHandler
35
+ from .handlers.file import FileHandler
36
+ from .handlers.hooks import HookHandler
37
+
38
+ # EventBus integration
39
+ try:
40
+ from ...services.event_bus import EventBus
41
+
42
+ EVENTBUS_AVAILABLE = True
43
+ except ImportError:
44
+ EventBus = None
45
+ EVENTBUS_AVAILABLE = False
46
+
47
+
48
+ class SvelteBuildWatcher(FileSystemEventHandler):
49
+ """File watcher for Svelte build directory changes.
50
+
51
+ Watches for file changes in svelte-build directory and triggers
52
+ hot reload via Socket.IO event emission.
53
+
54
+ STABILITY FIX: Added thread lock and stop() method to prevent timer leaks.
55
+ """
56
+
57
+ def __init__(
58
+ self, sio: socketio.AsyncServer, loop: asyncio.AbstractEventLoop, logger
59
+ ):
60
+ """Initialize the file watcher.
61
+
62
+ Args:
63
+ sio: Socket.IO server instance for emitting events
64
+ loop: Event loop for async operations
65
+ logger: Logger instance
66
+ """
67
+ super().__init__()
68
+ self.sio = sio
69
+ self.loop = loop
70
+ self.logger = logger
71
+ self.debounce_timer = None
72
+ self.debounce_delay = 0.5 # Wait 500ms after last change
73
+ self._timer_lock = threading.Lock() # STABILITY FIX: Prevent race condition
74
+
75
+ def stop(self):
76
+ """Stop the watcher and cancel any pending timers.
77
+
78
+ STABILITY FIX: Ensures timer is cancelled on shutdown.
79
+ """
80
+ with self._timer_lock:
81
+ if self.debounce_timer:
82
+ self.debounce_timer.cancel()
83
+ self.debounce_timer = None
84
+
85
+ def on_any_event(self, event):
86
+ """Handle any file system event.
87
+
88
+ Args:
89
+ event: File system event from watchdog
90
+ """
91
+ # Ignore directory events and temporary files
92
+ if event.is_directory or event.src_path.endswith((".tmp", ".swp", "~")):
93
+ return
94
+
95
+ self.logger.debug(
96
+ f"File change detected: {event.event_type} - {event.src_path}"
97
+ )
98
+
99
+ # STABILITY FIX: Use lock to prevent timer race condition
100
+ with self._timer_lock:
101
+ # Cancel existing timer
102
+ if self.debounce_timer:
103
+ self.debounce_timer.cancel()
104
+
105
+ # Schedule reload after debounce delay
106
+ self.debounce_timer = threading.Timer(
107
+ self.debounce_delay, self._trigger_reload
108
+ )
109
+ self.debounce_timer.start()
110
+
111
+ def _trigger_reload(self):
112
+ """Trigger hot reload by emitting Socket.IO event."""
113
+ try:
114
+ # Schedule the async emit in the event loop
115
+ asyncio.run_coroutine_threadsafe(self._emit_reload_event(), self.loop)
116
+ self.logger.info("Hot reload triggered - Svelte build changed")
117
+ except Exception as e:
118
+ self.logger.error(f"Error triggering reload: {e}")
119
+
120
+ async def _emit_reload_event(self):
121
+ """Emit the reload event to all connected clients."""
122
+ if self.sio:
123
+ await self.sio.emit(
124
+ "reload",
125
+ {
126
+ "type": "reload",
127
+ "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
128
+ "reason": "svelte-build-updated",
129
+ },
130
+ )
131
+
132
+
133
+ class UnifiedMonitorServer:
134
+ """Unified server that combines HTTP dashboard and Socket.IO functionality.
135
+
136
+ WHY: Provides a single server process that handles all monitoring needs.
137
+ Replaces multiple competing server implementations with one stable solution.
138
+ """
139
+
140
+ def __init__(
141
+ self, host: str = "localhost", port: int = 8765, enable_hot_reload: bool = False
142
+ ):
143
+ """Initialize the unified monitor server.
144
+
145
+ Args:
146
+ host: Host to bind to
147
+ port: Port to bind to
148
+ enable_hot_reload: Enable file watching and hot reload for development
149
+ """
150
+ self.host = host
151
+ self.port = port
152
+ self.enable_hot_reload = enable_hot_reload
153
+ self.logger = get_logger(__name__)
154
+
155
+ # Core components
156
+ self.app = None
157
+ self.sio = None
158
+ self.runner = None
159
+ self.site = None
160
+
161
+ # Event handlers
162
+ self.code_analysis_handler = None
163
+ self.dashboard_handler = None
164
+ self.file_handler = None
165
+ self.hook_handler = None
166
+
167
+ # High-performance event emitter
168
+ self.event_emitter = None
169
+
170
+ # File watching (optional for dev mode)
171
+ self.file_observer: Optional[Observer] = None
172
+ self.file_watcher: Optional[SvelteBuildWatcher] = None
173
+
174
+ # State
175
+ self.running = False
176
+ self.loop = None
177
+ self.server_thread = None
178
+ self.startup_error = None # Track startup errors
179
+
180
+ # Heartbeat tracking
181
+ self.heartbeat_task: Optional[asyncio.Task] = None
182
+ self.server_start_time = time.time()
183
+ self.heartbeat_count = 0
184
+
185
+ def start(self) -> bool:
186
+ """Start the unified monitor server.
187
+
188
+ Returns:
189
+ True if started successfully, False otherwise
190
+ """
191
+ try:
192
+ self.logger.info(
193
+ f"Starting unified monitor server on {self.host}:{self.port}"
194
+ )
195
+
196
+ # Start in a separate thread to avoid blocking
197
+ self.server_thread = threading.Thread(target=self._run_server, daemon=True)
198
+ self.server_thread.start()
199
+
200
+ # Wait for server to start
201
+ import time
202
+
203
+ for _ in range(50): # Wait up to 5 seconds
204
+ if self.running:
205
+ break
206
+ if self.startup_error:
207
+ # Server thread reported an error
208
+ self.logger.error(f"Server startup failed: {self.startup_error}")
209
+ return False
210
+ time.sleep(0.1)
211
+
212
+ if not self.running:
213
+ error_msg = (
214
+ self.startup_error or "Server failed to start within timeout"
215
+ )
216
+ self.logger.error(error_msg)
217
+ return False
218
+
219
+ self.logger.info("Unified monitor server started successfully")
220
+ return True
221
+
222
+ except Exception as e:
223
+ self.logger.error(f"Failed to start unified monitor server: {e}")
224
+ return False
225
+
226
+ def _run_server(self):
227
+ """Run the server in its own event loop."""
228
+ loop = None
229
+ try:
230
+ # Create new event loop for this thread
231
+ loop = asyncio.new_event_loop()
232
+ asyncio.set_event_loop(loop)
233
+ self.loop = loop
234
+
235
+ # Run the async server
236
+ loop.run_until_complete(self._start_async_server())
237
+
238
+ except OSError as e:
239
+ # Specific handling for port binding errors
240
+ if "Address already in use" in str(e) or "[Errno 48]" in str(e):
241
+ self.logger.error(f"Port {self.port} is already in use: {e}")
242
+ self.startup_error = f"Port {self.port} is already in use"
243
+ else:
244
+ self.logger.error(f"OS error in server thread: {e}")
245
+ self.startup_error = str(e)
246
+ except Exception as e:
247
+ self.logger.error(f"Error in server thread: {e}")
248
+ self.startup_error = str(e)
249
+ finally:
250
+ # Always ensure loop cleanup happens
251
+ if loop is not None:
252
+ try:
253
+ # Cancel all pending tasks first
254
+ self._cancel_all_tasks(loop)
255
+
256
+ # Give tasks a moment to cancel gracefully
257
+ if not loop.is_closed():
258
+ try:
259
+ loop.run_until_complete(asyncio.sleep(0.1))
260
+ except RuntimeError:
261
+ # Loop might be stopped already, that's ok
262
+ pass
263
+
264
+ except Exception as e:
265
+ self.logger.debug(f"Error during task cancellation: {e}")
266
+ finally:
267
+ try:
268
+ # Clear the loop reference from the instance first
269
+ self.loop = None
270
+
271
+ # Stop the loop if it's still running
272
+ if loop.is_running():
273
+ loop.stop()
274
+
275
+ # CRITICAL: Wait a moment for the loop to stop
276
+ import time
277
+
278
+ time.sleep(0.1)
279
+
280
+ # STABILITY FIX: Give tasks more time to clean up before closing
281
+ time.sleep(0.5)
282
+
283
+ # Clear the event loop from the thread BEFORE closing
284
+ # This prevents other code from accidentally using it
285
+ asyncio.set_event_loop(None)
286
+
287
+ # Now close the loop - this is critical to prevent the kqueue error
288
+ if not loop.is_closed():
289
+ loop.close()
290
+ # Wait for the close to complete
291
+ time.sleep(0.05)
292
+
293
+ except Exception as e:
294
+ self.logger.debug(f"Error during event loop cleanup: {e}")
295
+
296
+ async def _start_async_server(self):
297
+ """Start the async server components."""
298
+ try:
299
+ # Create Socket.IO server with proper ping configuration
300
+ self.sio = socketio.AsyncServer(
301
+ cors_allowed_origins="*",
302
+ logger=True, # Enable to see Socket.IO events and connection lifecycle
303
+ engineio_logger=True, # Enable to see Engine.IO protocol handshake details
304
+ ping_interval=30, # 30 seconds ping interval (matches client expectation)
305
+ ping_timeout=60, # 60 seconds ping timeout (generous for stability)
306
+ )
307
+
308
+ # Create aiohttp application
309
+ self.app = web.Application()
310
+
311
+ # Attach Socket.IO to the app
312
+ self.sio.attach(self.app)
313
+
314
+ # Setup event handlers
315
+ self._setup_event_handlers()
316
+
317
+ # Setup high-performance event emitter
318
+ await self._setup_event_emitter()
319
+
320
+ self.logger.info(
321
+ "Using high-performance async event architecture with direct calls"
322
+ )
323
+
324
+ # Start heartbeat task
325
+ self.heartbeat_task = asyncio.create_task(self._heartbeat_loop())
326
+ self.logger.info("Heartbeat task started (3-minute interval)")
327
+
328
+ # Setup file watching for hot reload (if enabled)
329
+ if self.enable_hot_reload:
330
+ self._setup_file_watcher()
331
+
332
+ # Setup HTTP routes
333
+ self._setup_http_routes()
334
+
335
+ # Create and start the server
336
+ self.runner = web.AppRunner(self.app)
337
+ await self.runner.setup()
338
+
339
+ try:
340
+ self.site = web.TCPSite(self.runner, self.host, self.port)
341
+ await self.site.start()
342
+
343
+ self.running = True
344
+ self.logger.info(f"Server running on http://{self.host}:{self.port}")
345
+ except OSError as e:
346
+ # Port binding error - make sure it's reported clearly
347
+ # Check for common port binding errors
348
+ if (
349
+ "Address already in use" in str(e)
350
+ or "[Errno 48]" in str(e)
351
+ or "[Errno 98]" in str(e)
352
+ ):
353
+ error_msg = f"Port {self.port} is already in use. Another process may be using this port."
354
+ self.logger.error(error_msg)
355
+ self.startup_error = error_msg
356
+ raise OSError(error_msg) from e
357
+ self.logger.error(f"Failed to bind to {self.host}:{self.port}: {e}")
358
+ self.startup_error = str(e)
359
+ raise
360
+
361
+ # Keep the server running
362
+ while self.running:
363
+ await asyncio.sleep(1)
364
+
365
+ except Exception as e:
366
+ self.logger.error(f"Error starting async server: {e}")
367
+ raise
368
+ finally:
369
+ await self._cleanup_async()
370
+
371
+ def _setup_event_handlers(self):
372
+ """Setup Socket.IO event handlers."""
373
+ try:
374
+ # Create event handlers
375
+ self.code_analysis_handler = CodeAnalysisHandler(self.sio)
376
+ self.dashboard_handler = DashboardHandler(self.sio)
377
+ self.file_handler = FileHandler(self.sio)
378
+ self.hook_handler = HookHandler(self.sio)
379
+
380
+ # Register handlers
381
+ self.code_analysis_handler.register()
382
+ self.dashboard_handler.register()
383
+ self.file_handler.register()
384
+ self.hook_handler.register()
385
+
386
+ self.logger.info("Event handlers registered successfully")
387
+
388
+ except Exception as e:
389
+ self.logger.error(f"Error setting up event handlers: {e}")
390
+ raise
391
+
392
+ async def _setup_event_emitter(self):
393
+ """Setup high-performance event emitter."""
394
+ try:
395
+ # Get the global event emitter instance
396
+ self.event_emitter = await get_event_emitter()
397
+
398
+ # Register this Socket.IO server for direct event emission
399
+ self.event_emitter.register_socketio_server(self.sio)
400
+
401
+ self.logger.info("Event emitter setup complete - direct calls enabled")
402
+
403
+ except Exception as e:
404
+ self.logger.error(f"Error setting up event emitter: {e}")
405
+ raise
406
+
407
+ def _setup_file_watcher(self):
408
+ """Setup file watcher for Svelte build directory.
409
+
410
+ Watches for changes in svelte-build and triggers hot reload.
411
+ Only enabled when enable_hot_reload is True.
412
+ """
413
+ try:
414
+ dashboard_dir = Path(__file__).resolve().parent.parent.parent / "dashboard"
415
+ svelte_build_dir = dashboard_dir / "static" / "svelte-build"
416
+
417
+ if not svelte_build_dir.exists():
418
+ self.logger.warning(
419
+ f"Svelte build directory not found: {svelte_build_dir}. "
420
+ "Hot reload disabled."
421
+ )
422
+ return
423
+
424
+ # Create file watcher with Socket.IO reference
425
+ self.file_watcher = SvelteBuildWatcher(
426
+ sio=self.sio, loop=self.loop, logger=self.logger
427
+ )
428
+
429
+ # Create observer and schedule watching
430
+ self.file_observer = Observer()
431
+ self.file_observer.schedule(
432
+ self.file_watcher, str(svelte_build_dir), recursive=True
433
+ )
434
+ self.file_observer.start()
435
+
436
+ self.logger.info(f"🔥 Hot reload enabled - watching {svelte_build_dir}")
437
+
438
+ except Exception as e:
439
+ self.logger.error(f"Error setting up file watcher: {e}")
440
+ # Don't raise - hot reload is optional
441
+
442
+ def _setup_http_routes(self):
443
+ """Setup HTTP routes for the dashboard."""
444
+ try:
445
+ # Dashboard static files - use .resolve() for absolute path
446
+ dashboard_dir = Path(__file__).resolve().parent.parent.parent / "dashboard"
447
+ static_dir = dashboard_dir / "static"
448
+
449
+ # Main dashboard route - serve Svelte dashboard
450
+ async def dashboard_index(request):
451
+ svelte_index = static_dir / "svelte-build" / "index.html"
452
+ if svelte_index.exists():
453
+ with svelte_index.open(encoding="utf-8") as f:
454
+ content = f.read()
455
+ return web.Response(text=content, content_type="text/html")
456
+
457
+ # Log error with path details for debugging
458
+ self.logger.error(
459
+ f"Dashboard index.html not found at: {svelte_index.resolve()}"
460
+ )
461
+ return web.Response(
462
+ text=f"Dashboard not found. Expected location: {svelte_index.resolve()}",
463
+ status=404,
464
+ )
465
+
466
+ # Health check
467
+ async def health_check(request):
468
+ # Get version from VERSION file
469
+ version = "1.0.0"
470
+ try:
471
+ version_file = (
472
+ Path(__file__).resolve().parent.parent.parent.parent.parent
473
+ / "VERSION"
474
+ )
475
+ if version_file.exists():
476
+ version = version_file.read_text().strip()
477
+ except Exception:
478
+ pass
479
+
480
+ return web.json_response(
481
+ {
482
+ "status": ServiceState.RUNNING,
483
+ "service": "claude-mpm-monitor", # Important: must match what is_our_service() checks
484
+ "version": version,
485
+ "port": self.port,
486
+ "pid": os.getpid(),
487
+ "uptime": int(time.time() - self.server_start_time),
488
+ }
489
+ )
490
+
491
+ # Event ingestion endpoint for hook handlers
492
+ async def api_events_handler(request):
493
+ """Handle HTTP POST events from hook handlers."""
494
+ try:
495
+ data = await request.json()
496
+
497
+ # Extract event data
498
+ data.get("namespace", "hook")
499
+ event = data.get("event", "claude_event")
500
+ event_data = data.get("data", {})
501
+
502
+ # Emit to Socket.IO clients via the appropriate event
503
+ if self.sio:
504
+ await self.sio.emit(event, event_data)
505
+ self.logger.debug(f"HTTP event forwarded to Socket.IO: {event}")
506
+
507
+ return web.Response(status=204) # No content response
508
+
509
+ except Exception as e:
510
+ self.logger.error(f"Error handling HTTP event: {e}")
511
+ return web.Response(text=f"Error: {e!s}", status=500)
512
+
513
+ # File content endpoint for file viewer
514
+ async def api_file_handler(request):
515
+ """Handle file content requests."""
516
+ import json
517
+
518
+ try:
519
+ data = await request.json()
520
+ file_path = data.get("path", "")
521
+
522
+ # Security check: ensure path is absolute and exists
523
+ if not file_path or not Path(file_path).is_absolute():
524
+ return web.json_response(
525
+ {"success": False, "error": "Invalid file path"}, status=400
526
+ )
527
+
528
+ # Check if file exists and is readable
529
+ if not Path(file_path).exists():
530
+ return web.json_response(
531
+ {"success": False, "error": "File not found"}, status=404
532
+ )
533
+
534
+ if not Path(file_path).is_file():
535
+ return web.json_response(
536
+ {"success": False, "error": "Path is not a file"},
537
+ status=400,
538
+ )
539
+
540
+ # Read file content (with size limit for safety)
541
+ max_size = 10 * 1024 * 1024 # 10MB limit
542
+ file_size = Path(file_path).stat().st_size
543
+
544
+ if file_size > max_size:
545
+ return web.json_response(
546
+ {
547
+ "success": False,
548
+ "error": f"File too large (>{max_size} bytes)",
549
+ },
550
+ status=413,
551
+ )
552
+
553
+ try:
554
+ with Path(file_path).open(
555
+ encoding="utf-8",
556
+ ) as f:
557
+ content = f.read()
558
+ lines = content.count("\n") + 1
559
+ except UnicodeDecodeError:
560
+ # Try reading as binary if UTF-8 fails
561
+ return web.json_response(
562
+ {"success": False, "error": "File is not a text file"},
563
+ status=415,
564
+ )
565
+
566
+ # Get file extension for type detection
567
+ file_ext = Path(file_path).suffix.lstrip(".")
568
+
569
+ return web.json_response(
570
+ {
571
+ "success": True,
572
+ "content": content,
573
+ "lines": lines,
574
+ "size": file_size,
575
+ "type": file_ext or "text",
576
+ }
577
+ )
578
+
579
+ except json.JSONDecodeError:
580
+ return web.json_response(
581
+ {"success": False, "error": "Invalid JSON in request"},
582
+ status=400,
583
+ )
584
+ except Exception as e:
585
+ self.logger.error(f"Error reading file: {e}")
586
+ return web.json_response(
587
+ {"success": False, "error": str(e)}, status=500
588
+ )
589
+
590
+ # File listing endpoint for file browser
591
+ async def api_files_handler(request):
592
+ """List files in a directory for the file browser."""
593
+ try:
594
+ # Get path from query param, default to working directory
595
+ path = request.query.get("path", str(Path.cwd()))
596
+ dir_path = Path(path)
597
+
598
+ if not dir_path.exists():
599
+ return web.json_response(
600
+ {"success": False, "error": "Directory not found"},
601
+ status=404,
602
+ )
603
+
604
+ if not dir_path.is_dir():
605
+ return web.json_response(
606
+ {"success": False, "error": "Path is not a directory"},
607
+ status=400,
608
+ )
609
+
610
+ # Patterns to exclude
611
+ exclude_patterns = {
612
+ ".git",
613
+ "node_modules",
614
+ "__pycache__",
615
+ ".svelte-kit",
616
+ "venv",
617
+ ".venv",
618
+ "dist",
619
+ "build",
620
+ ".next",
621
+ ".cache",
622
+ ".pytest_cache",
623
+ ".mypy_cache",
624
+ ".ruff_cache",
625
+ "eggs",
626
+ "*.egg-info",
627
+ ".tox",
628
+ ".nox",
629
+ "htmlcov",
630
+ ".coverage",
631
+ }
632
+
633
+ entries = []
634
+ try:
635
+ for entry in sorted(
636
+ dir_path.iterdir(),
637
+ key=lambda x: (not x.is_dir(), x.name.lower()),
638
+ ):
639
+ # Skip hidden files and excluded patterns
640
+ if entry.name.startswith(".") and entry.name not in {
641
+ ".env",
642
+ ".gitignore",
643
+ }:
644
+ if entry.name in {".git", ".svelte-kit", ".cache"}:
645
+ continue
646
+ if entry.name in exclude_patterns:
647
+ continue
648
+ if any(
649
+ entry.name.endswith(p.replace("*", ""))
650
+ for p in exclude_patterns
651
+ if "*" in p
652
+ ):
653
+ continue
654
+
655
+ try:
656
+ stat = entry.stat()
657
+ entries.append(
658
+ {
659
+ "name": entry.name,
660
+ "path": str(entry),
661
+ "type": "directory"
662
+ if entry.is_dir()
663
+ else "file",
664
+ "size": stat.st_size if entry.is_file() else 0,
665
+ "modified": stat.st_mtime,
666
+ "extension": entry.suffix.lstrip(".")
667
+ if entry.is_file()
668
+ else None,
669
+ }
670
+ )
671
+ except (PermissionError, OSError):
672
+ continue
673
+
674
+ except PermissionError:
675
+ return web.json_response(
676
+ {"success": False, "error": "Permission denied"},
677
+ status=403,
678
+ )
679
+
680
+ # Separate directories and files
681
+ directories = [e for e in entries if e["type"] == "directory"]
682
+ files = [e for e in entries if e["type"] == "file"]
683
+
684
+ return web.json_response(
685
+ {
686
+ "success": True,
687
+ "path": str(dir_path),
688
+ "directories": directories,
689
+ "files": files,
690
+ "total_directories": len(directories),
691
+ "total_files": len(files),
692
+ }
693
+ )
694
+
695
+ except Exception as e:
696
+ self.logger.error(f"Error listing directory: {e}")
697
+ return web.json_response(
698
+ {"success": False, "error": str(e)}, status=500
699
+ )
700
+
701
+ # File read endpoint (GET) for file browser
702
+ async def api_file_read_handler(request):
703
+ """Read file content via GET request."""
704
+ import base64
705
+
706
+ try:
707
+ file_path = request.query.get("path", "")
708
+
709
+ if not file_path:
710
+ return web.json_response(
711
+ {"success": False, "error": "Path parameter required"},
712
+ status=400,
713
+ )
714
+
715
+ path = Path(file_path)
716
+
717
+ if not path.exists():
718
+ return web.json_response(
719
+ {"success": False, "error": "File not found"},
720
+ status=404,
721
+ )
722
+
723
+ if not path.is_file():
724
+ return web.json_response(
725
+ {"success": False, "error": "Path is not a file"},
726
+ status=400,
727
+ )
728
+
729
+ # Get file info
730
+ file_size = path.stat().st_size
731
+ file_ext = path.suffix.lstrip(".").lower()
732
+
733
+ # Define image extensions
734
+ image_extensions = {
735
+ "png",
736
+ "jpg",
737
+ "jpeg",
738
+ "gif",
739
+ "svg",
740
+ "webp",
741
+ "ico",
742
+ "bmp",
743
+ }
744
+
745
+ # Check if file is an image
746
+ if file_ext in image_extensions:
747
+ # Read as binary and encode to base64
748
+ try:
749
+ binary_content = path.read_bytes()
750
+ base64_content = base64.b64encode(binary_content).decode(
751
+ "utf-8"
752
+ )
753
+
754
+ # Map extension to MIME type
755
+ mime_types = {
756
+ "png": "image/png",
757
+ "jpg": "image/jpeg",
758
+ "jpeg": "image/jpeg",
759
+ "gif": "image/gif",
760
+ "svg": "image/svg+xml",
761
+ "webp": "image/webp",
762
+ "ico": "image/x-icon",
763
+ "bmp": "image/bmp",
764
+ }
765
+ mime_type = mime_types.get(file_ext, "image/png")
766
+
767
+ return web.json_response(
768
+ {
769
+ "success": True,
770
+ "path": str(path),
771
+ "content": base64_content,
772
+ "size": file_size,
773
+ "type": "image",
774
+ "mime": mime_type,
775
+ "extension": file_ext,
776
+ }
777
+ )
778
+ except Exception as e:
779
+ self.logger.error(f"Error reading image file: {e}")
780
+ return web.json_response(
781
+ {
782
+ "success": False,
783
+ "error": f"Failed to read image: {e!s}",
784
+ },
785
+ status=500,
786
+ )
787
+
788
+ # Read text file content
789
+ try:
790
+ content = path.read_text(encoding="utf-8")
791
+ lines = content.count("\n") + 1
792
+ except UnicodeDecodeError:
793
+ return web.json_response(
794
+ {"success": False, "error": "File is not a text file"},
795
+ status=415,
796
+ )
797
+
798
+ return web.json_response(
799
+ {
800
+ "success": True,
801
+ "path": str(path),
802
+ "content": content,
803
+ "lines": lines,
804
+ "size": file_size,
805
+ "type": file_ext or "text",
806
+ }
807
+ )
808
+
809
+ except Exception as e:
810
+ self.logger.error(f"Error reading file: {e}")
811
+ return web.json_response(
812
+ {"success": False, "error": str(e)}, status=500
813
+ )
814
+
815
+ # Favicon handler
816
+ async def favicon_handler(request):
817
+ """Serve favicon.svg from static directory."""
818
+ from aiohttp.web_fileresponse import FileResponse
819
+
820
+ favicon_path = static_dir / "svelte-build" / "favicon.svg"
821
+ if favicon_path.exists():
822
+ return FileResponse(
823
+ favicon_path, headers={"Content-Type": "image/svg+xml"}
824
+ )
825
+ raise web.HTTPNotFound()
826
+
827
+ # Version endpoint for dashboard build tracker
828
+ async def version_handler(request):
829
+ """Serve version information for dashboard build tracker."""
830
+ try:
831
+ # Try to get version from version service
832
+ from claude_mpm.services.version_service import VersionService
833
+
834
+ version_service = VersionService()
835
+ version_info = version_service.get_version_info()
836
+
837
+ return web.json_response(
838
+ {
839
+ "version": version_info.get("base_version", "1.0.0"),
840
+ "build": version_info.get("build_number", 1),
841
+ "formatted_build": f"{version_info.get('build_number', 1):04d}",
842
+ "full_version": version_info.get("version", "v1.0.0-0001"),
843
+ "service": "unified-monitor",
844
+ }
845
+ )
846
+ except Exception as e:
847
+ self.logger.warning(f"Error getting version info: {e}")
848
+ # Return default version info if service fails
849
+ return web.json_response(
850
+ {
851
+ "version": "1.0.0",
852
+ "build": 1,
853
+ "formatted_build": "0001",
854
+ "full_version": "v1.0.0-0001",
855
+ "service": "unified-monitor",
856
+ }
857
+ )
858
+
859
+ # Configuration endpoint for dashboard initialization
860
+ async def config_handler(request):
861
+ """Return configuration for dashboard initialization."""
862
+ import subprocess
863
+
864
+ config = {
865
+ "workingDirectory": Path.cwd(),
866
+ "gitBranch": "Unknown",
867
+ "serverTime": datetime.now(timezone.utc).isoformat() + "Z",
868
+ "service": "unified-monitor",
869
+ }
870
+
871
+ # Try to get current git branch
872
+ try:
873
+ result = subprocess.run(
874
+ ["git", "branch", "--show-current"],
875
+ capture_output=True,
876
+ text=True,
877
+ timeout=2,
878
+ cwd=Path.cwd(),
879
+ check=False,
880
+ )
881
+ if result.returncode == 0 and result.stdout.strip():
882
+ config["gitBranch"] = result.stdout.strip()
883
+ except Exception:
884
+ pass # Keep default "Unknown" value
885
+
886
+ return web.json_response(config)
887
+
888
+ # Working directory endpoint
889
+ async def working_directory_handler(request):
890
+ """Return the current working directory."""
891
+ return web.json_response(
892
+ {"working_directory": str(Path.cwd()), "success": True}
893
+ )
894
+
895
+ # Monitor page routes
896
+ async def monitor_page_handler(request):
897
+ """Serve monitor HTML pages."""
898
+ page_name = request.match_info.get("page", "agents")
899
+ static_dir = dashboard_dir / "static"
900
+ file_path = static_dir / f"{page_name}.html"
901
+
902
+ if file_path.exists() and file_path.is_file():
903
+ with Path(file_path).open(
904
+ encoding="utf-8",
905
+ ) as f:
906
+ content = f.read()
907
+ return web.Response(text=content, content_type="text/html")
908
+ return web.Response(text="Page not found", status=404)
909
+
910
+ # Git history handler
911
+ async def git_history_handler(request: web.Request) -> web.Response:
912
+ """Get git history for a file."""
913
+ import subprocess
914
+
915
+ try:
916
+ data = await request.json()
917
+ file_path = data.get("path", "")
918
+ limit = data.get("limit", 10)
919
+
920
+ if not file_path:
921
+ return web.json_response(
922
+ {
923
+ "success": False,
924
+ "error": "No path provided",
925
+ "commits": [],
926
+ },
927
+ status=400,
928
+ )
929
+
930
+ path = Path(file_path)
931
+ if not path.exists():
932
+ return web.json_response(
933
+ {
934
+ "success": False,
935
+ "error": "File not found",
936
+ "commits": [],
937
+ },
938
+ status=404,
939
+ )
940
+
941
+ # Get git log for file
942
+ result = subprocess.run(
943
+ [
944
+ "git",
945
+ "log",
946
+ f"-{limit}",
947
+ "--pretty=format:%H|%an|%ar|%s",
948
+ "--",
949
+ str(path),
950
+ ],
951
+ check=False,
952
+ capture_output=True,
953
+ text=True,
954
+ cwd=str(path.parent),
955
+ )
956
+
957
+ commits = []
958
+ if result.returncode == 0 and result.stdout:
959
+ for line in result.stdout.strip().split("\n"):
960
+ if line:
961
+ parts = line.split("|", 3)
962
+ if len(parts) == 4:
963
+ commits.append(
964
+ {
965
+ "hash": parts[0][:7],
966
+ "author": parts[1],
967
+ "date": parts[2],
968
+ "message": parts[3],
969
+ }
970
+ )
971
+
972
+ return web.json_response({"success": True, "commits": commits})
973
+ except Exception as e:
974
+ return web.json_response(
975
+ {"success": False, "error": str(e), "commits": []}, status=500
976
+ )
977
+
978
+ # Git diff handler
979
+ async def git_diff_handler(request: web.Request) -> web.Response:
980
+ """Get git diff for a file with optional commit selection."""
981
+ import subprocess
982
+
983
+ try:
984
+ file_path = request.query.get("path", "")
985
+ commit_hash = request.query.get(
986
+ "commit", ""
987
+ ) # Optional commit hash
988
+
989
+ if not file_path:
990
+ return web.json_response(
991
+ {
992
+ "success": False,
993
+ "error": "No path provided",
994
+ "diff": "",
995
+ "has_changes": False,
996
+ },
997
+ status=400,
998
+ )
999
+
1000
+ path = Path(file_path)
1001
+ if not path.exists():
1002
+ return web.json_response(
1003
+ {
1004
+ "success": False,
1005
+ "error": "File not found",
1006
+ "diff": "",
1007
+ "has_changes": False,
1008
+ },
1009
+ status=404,
1010
+ )
1011
+
1012
+ # Find git repository root
1013
+ git_root_result = subprocess.run(
1014
+ ["git", "rev-parse", "--show-toplevel"],
1015
+ check=False,
1016
+ capture_output=True,
1017
+ text=True,
1018
+ cwd=str(path.parent),
1019
+ )
1020
+
1021
+ if git_root_result.returncode != 0:
1022
+ # Not in a git repository
1023
+ return web.json_response(
1024
+ {
1025
+ "success": True,
1026
+ "diff": "",
1027
+ "has_changes": False,
1028
+ "tracked": False,
1029
+ "history": [],
1030
+ "has_uncommitted": False,
1031
+ }
1032
+ )
1033
+
1034
+ git_root = Path(git_root_result.stdout.strip())
1035
+
1036
+ # Check if file is tracked by git
1037
+ ls_files_result = subprocess.run(
1038
+ ["git", "ls-files", "--error-unmatch", str(path)],
1039
+ check=False,
1040
+ capture_output=True,
1041
+ text=True,
1042
+ cwd=str(git_root),
1043
+ )
1044
+
1045
+ if ls_files_result.returncode != 0:
1046
+ # File is not tracked by git
1047
+ return web.json_response(
1048
+ {
1049
+ "success": True,
1050
+ "diff": "",
1051
+ "has_changes": False,
1052
+ "tracked": False,
1053
+ "history": [],
1054
+ "has_uncommitted": False,
1055
+ }
1056
+ )
1057
+
1058
+ # Get commit history for this file (last 5 commits)
1059
+ history_result = subprocess.run(
1060
+ [
1061
+ "git",
1062
+ "log",
1063
+ "-5",
1064
+ "--pretty=format:%H|%s|%ar",
1065
+ "--",
1066
+ str(path),
1067
+ ],
1068
+ check=False,
1069
+ capture_output=True,
1070
+ text=True,
1071
+ cwd=str(git_root),
1072
+ )
1073
+
1074
+ history = []
1075
+ if history_result.returncode == 0 and history_result.stdout:
1076
+ for line in history_result.stdout.strip().split("\n"):
1077
+ if line:
1078
+ parts = line.split("|", 2)
1079
+ if len(parts) == 3:
1080
+ history.append(
1081
+ {
1082
+ "hash": parts[0][:7], # Short hash
1083
+ "full_hash": parts[0], # Full hash for API
1084
+ "message": parts[1],
1085
+ "time_ago": parts[2],
1086
+ }
1087
+ )
1088
+
1089
+ # Check for uncommitted changes
1090
+ uncommitted_result = subprocess.run(
1091
+ ["git", "diff", "HEAD", str(path)],
1092
+ check=False,
1093
+ capture_output=True,
1094
+ text=True,
1095
+ cwd=str(git_root),
1096
+ )
1097
+
1098
+ has_uncommitted = bool(uncommitted_result.stdout.strip())
1099
+
1100
+ # Get diff based on commit parameter
1101
+ if commit_hash:
1102
+ # Get diff for specific commit
1103
+ result = subprocess.run(
1104
+ ["git", "show", commit_hash, "--", str(path)],
1105
+ check=False,
1106
+ capture_output=True,
1107
+ text=True,
1108
+ cwd=str(git_root),
1109
+ )
1110
+ diff_output = result.stdout if result.returncode == 0 else ""
1111
+ has_changes = bool(diff_output.strip())
1112
+ else:
1113
+ # Get uncommitted diff (default behavior)
1114
+ diff_output = uncommitted_result.stdout
1115
+ has_changes = has_uncommitted
1116
+
1117
+ return web.json_response(
1118
+ {
1119
+ "success": True,
1120
+ "diff": diff_output,
1121
+ "has_changes": has_changes,
1122
+ "tracked": True,
1123
+ "history": history,
1124
+ "has_uncommitted": has_uncommitted,
1125
+ }
1126
+ )
1127
+ except Exception as e:
1128
+ return web.json_response(
1129
+ {
1130
+ "success": False,
1131
+ "error": str(e),
1132
+ "diff": "",
1133
+ "has_changes": False,
1134
+ "history": [],
1135
+ "has_uncommitted": False,
1136
+ },
1137
+ status=500,
1138
+ )
1139
+
1140
+ # Register routes
1141
+ self.app.router.add_get("/", dashboard_index)
1142
+ self.app.router.add_get("/favicon.svg", favicon_handler)
1143
+ self.app.router.add_get("/health", health_check)
1144
+ self.app.router.add_get("/version.json", version_handler)
1145
+ self.app.router.add_get("/api/config", config_handler)
1146
+ self.app.router.add_get("/api/working-directory", working_directory_handler)
1147
+ self.app.router.add_get("/api/files", api_files_handler)
1148
+ self.app.router.add_get("/api/file/read", api_file_read_handler)
1149
+ self.app.router.add_get("/api/file/diff", git_diff_handler)
1150
+ self.app.router.add_post("/api/events", api_events_handler)
1151
+ self.app.router.add_post("/api/file", api_file_handler)
1152
+ self.app.router.add_post("/api/git-history", git_history_handler)
1153
+
1154
+ # Monitor page routes
1155
+ self.app.router.add_get("/monitor", lambda r: monitor_page_handler(r))
1156
+ self.app.router.add_get(
1157
+ "/monitor/agents", lambda r: monitor_page_handler(r)
1158
+ )
1159
+ self.app.router.add_get("/monitor/tools", lambda r: monitor_page_handler(r))
1160
+ self.app.router.add_get("/monitor/files", lambda r: monitor_page_handler(r))
1161
+ self.app.router.add_get(
1162
+ "/monitor/events", lambda r: monitor_page_handler(r)
1163
+ )
1164
+
1165
+ # Serve Svelte _app assets (compiled JS/CSS)
1166
+ svelte_build_dir = static_dir / "svelte-build"
1167
+ if svelte_build_dir.exists():
1168
+ svelte_app_dir = svelte_build_dir / "_app"
1169
+ if svelte_app_dir.exists():
1170
+ # Serve _app assets with proper caching
1171
+ async def app_assets_handler(request):
1172
+ """Serve Svelte _app assets."""
1173
+ from aiohttp.web_fileresponse import FileResponse
1174
+
1175
+ rel_path = request.match_info["filepath"]
1176
+ file_path = svelte_app_dir / rel_path
1177
+
1178
+ if not file_path.exists() or not file_path.is_file():
1179
+ raise web.HTTPNotFound()
1180
+
1181
+ response = FileResponse(file_path)
1182
+
1183
+ # Add cache headers for immutable assets
1184
+ if "/immutable/" in str(rel_path):
1185
+ response.headers["Cache-Control"] = (
1186
+ "public, max-age=31536000, immutable"
1187
+ )
1188
+ else:
1189
+ response.headers["Cache-Control"] = (
1190
+ "no-cache, no-store, must-revalidate"
1191
+ )
1192
+
1193
+ return response
1194
+
1195
+ self.app.router.add_get("/_app/{filepath:.*}", app_assets_handler)
1196
+
1197
+ # Legacy static files (for backward compatibility)
1198
+ if static_dir.exists():
1199
+
1200
+ async def static_handler(request):
1201
+ """Serve legacy static files with cache-control headers for development."""
1202
+
1203
+ from aiohttp.web_fileresponse import FileResponse
1204
+
1205
+ # Get the relative path from the request
1206
+ rel_path = request.match_info["filepath"]
1207
+ file_path = static_dir / rel_path
1208
+
1209
+ if not file_path.exists() or not file_path.is_file():
1210
+ raise web.HTTPNotFound()
1211
+
1212
+ # Create file response
1213
+ response = FileResponse(file_path)
1214
+
1215
+ # Add cache-busting headers for development
1216
+ response.headers["Cache-Control"] = (
1217
+ "no-cache, no-store, must-revalidate"
1218
+ )
1219
+ response.headers["Pragma"] = "no-cache"
1220
+ response.headers["Expires"] = "0"
1221
+
1222
+ return response
1223
+
1224
+ self.app.router.add_get("/static/{filepath:.*}", static_handler)
1225
+
1226
+ # Log dashboard availability
1227
+ if svelte_build_dir.exists():
1228
+ self.logger.info(
1229
+ f"✅ Svelte dashboard available at / (root) (build: {svelte_build_dir})"
1230
+ )
1231
+ else:
1232
+ self.logger.warning(f"Svelte build not found at: {svelte_build_dir}")
1233
+
1234
+ self.logger.info("HTTP routes registered successfully")
1235
+
1236
+ except Exception as e:
1237
+ self.logger.error(f"Error setting up HTTP routes: {e}")
1238
+ raise
1239
+
1240
+ def stop(self):
1241
+ """Stop the unified monitor server."""
1242
+ try:
1243
+ self.logger.info("Stopping unified monitor server")
1244
+
1245
+ # Signal shutdown first
1246
+ self.running = False
1247
+
1248
+ # If we have a loop, schedule the cleanup
1249
+ if self.loop and not self.loop.is_closed():
1250
+ try:
1251
+ # Use call_soon_threadsafe to schedule cleanup from another thread
1252
+ future = asyncio.run_coroutine_threadsafe(
1253
+ self._graceful_shutdown(), self.loop
1254
+ )
1255
+ # Wait for cleanup to complete (with timeout)
1256
+ future.result(timeout=3)
1257
+ except Exception as e:
1258
+ self.logger.debug(f"Error during graceful shutdown: {e}")
1259
+
1260
+ # Wait for server thread to finish with a reasonable timeout
1261
+ if self.server_thread and self.server_thread.is_alive():
1262
+ self.server_thread.join(timeout=5)
1263
+
1264
+ # If thread is still alive after timeout, log a warning
1265
+ if self.server_thread.is_alive():
1266
+ self.logger.warning("Server thread did not stop within timeout")
1267
+
1268
+ # Clear all references to help with cleanup
1269
+ self.server_thread = None
1270
+ self.app = None
1271
+ self.sio = None
1272
+ self.runner = None
1273
+ self.site = None
1274
+ self.event_emitter = None
1275
+
1276
+ # Give the system a moment to cleanup resources
1277
+ import time
1278
+
1279
+ time.sleep(0.2)
1280
+
1281
+ self.logger.info("Unified monitor server stopped")
1282
+
1283
+ except Exception as e:
1284
+ self.logger.error(f"Error stopping unified monitor server: {e}")
1285
+
1286
+ async def _heartbeat_loop(self):
1287
+ """Send heartbeat events every 3 minutes."""
1288
+ try:
1289
+ while self.running:
1290
+ # Wait 3 minutes (180 seconds)
1291
+ await asyncio.sleep(180)
1292
+
1293
+ if not self.running:
1294
+ break
1295
+
1296
+ # Increment heartbeat count
1297
+ self.heartbeat_count += 1
1298
+
1299
+ # Calculate server uptime
1300
+ uptime_seconds = int(time.time() - self.server_start_time)
1301
+ uptime_minutes = uptime_seconds // 60
1302
+ uptime_hours = uptime_minutes // 60
1303
+
1304
+ # Format uptime string
1305
+ if uptime_hours > 0:
1306
+ uptime_str = f"{uptime_hours}h {uptime_minutes % 60}m"
1307
+ else:
1308
+ uptime_str = f"{uptime_minutes}m {uptime_seconds % 60}s"
1309
+
1310
+ # Get connected client count
1311
+ connected_clients = 0
1312
+ if self.dashboard_handler:
1313
+ connected_clients = len(self.dashboard_handler.connected_clients)
1314
+
1315
+ # Create heartbeat data
1316
+ heartbeat_data = {
1317
+ "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
1318
+ "type": "heartbeat",
1319
+ "server_uptime": uptime_seconds,
1320
+ "server_uptime_formatted": uptime_str,
1321
+ "connected_clients": connected_clients,
1322
+ "heartbeat_number": self.heartbeat_count,
1323
+ "message": f"Server heartbeat #{self.heartbeat_count} - Socket.IO connection active",
1324
+ "service": "unified-monitor",
1325
+ "port": self.port,
1326
+ }
1327
+
1328
+ # Emit heartbeat event
1329
+ if self.sio:
1330
+ await self.sio.emit("heartbeat", heartbeat_data)
1331
+ self.logger.debug(
1332
+ f"Heartbeat #{self.heartbeat_count} sent - "
1333
+ f"{connected_clients} clients connected, uptime: {uptime_str}"
1334
+ )
1335
+
1336
+ except asyncio.CancelledError:
1337
+ self.logger.debug("Heartbeat task cancelled")
1338
+ except Exception as e:
1339
+ self.logger.error(f"Error in heartbeat loop: {e}")
1340
+
1341
+ async def _cleanup_async(self):
1342
+ """Cleanup async resources."""
1343
+ try:
1344
+ # Stop file observer if running
1345
+ # STABILITY FIX: Ensure watcher is stopped and verify observer termination
1346
+ if self.file_observer:
1347
+ try:
1348
+ # Stop the watcher first to cancel pending timers
1349
+ if self.file_watcher:
1350
+ self.file_watcher.stop()
1351
+
1352
+ # Stop the observer
1353
+ self.file_observer.stop()
1354
+ self.file_observer.join(timeout=2)
1355
+
1356
+ # Verify observer actually stopped
1357
+ if self.file_observer.is_alive():
1358
+ self.logger.warning("File observer did not stop cleanly")
1359
+
1360
+ self.logger.debug("File observer stopped")
1361
+ except Exception as e:
1362
+ self.logger.debug(f"Error stopping file observer: {e}")
1363
+ finally:
1364
+ self.file_observer = None
1365
+ self.file_watcher = None
1366
+
1367
+ # Cancel heartbeat task if running
1368
+ # STABILITY FIX: Add timeout to prevent infinite wait on cancellation
1369
+ if self.heartbeat_task and not self.heartbeat_task.done():
1370
+ self.heartbeat_task.cancel()
1371
+ try:
1372
+ await asyncio.wait_for(self.heartbeat_task, timeout=2.0)
1373
+ except (asyncio.CancelledError, asyncio.TimeoutError):
1374
+ pass
1375
+ self.logger.debug("Heartbeat task cancelled")
1376
+
1377
+ # Close the Socket.IO server first to stop accepting new connections
1378
+ if self.sio:
1379
+ try:
1380
+ await self.sio.shutdown()
1381
+ self.logger.debug("Socket.IO shutdown complete")
1382
+ except Exception as e:
1383
+ self.logger.debug(f"Error shutting down Socket.IO: {e}")
1384
+ finally:
1385
+ self.sio = None
1386
+
1387
+ # Cleanup event emitter
1388
+ if self.event_emitter:
1389
+ try:
1390
+ if self.sio:
1391
+ self.event_emitter.unregister_socketio_server(self.sio)
1392
+
1393
+ # Use the global cleanup function to ensure proper cleanup
1394
+ from .event_emitter import cleanup_event_emitter
1395
+
1396
+ await cleanup_event_emitter()
1397
+
1398
+ self.logger.info("Event emitter cleaned up")
1399
+ except Exception as e:
1400
+ self.logger.warning(f"Error cleaning up event emitter: {e}")
1401
+ finally:
1402
+ self.event_emitter = None
1403
+
1404
+ # Stop the site (must be done before runner cleanup)
1405
+ if self.site:
1406
+ try:
1407
+ await self.site.stop()
1408
+ self.logger.debug("Site stopped")
1409
+ except Exception as e:
1410
+ self.logger.debug(f"Error stopping site: {e}")
1411
+ finally:
1412
+ self.site = None
1413
+
1414
+ # Cleanup the runner (after site is stopped)
1415
+ if self.runner:
1416
+ try:
1417
+ await self.runner.cleanup()
1418
+ self.logger.debug("Runner cleaned up")
1419
+ except Exception as e:
1420
+ self.logger.debug(f"Error cleaning up runner: {e}")
1421
+ finally:
1422
+ self.runner = None
1423
+
1424
+ # Clear app reference
1425
+ self.app = None
1426
+
1427
+ except Exception as e:
1428
+ self.logger.error(f"Error during async cleanup: {e}")
1429
+
1430
+ def get_status(self) -> Dict:
1431
+ """Get server status information.
1432
+
1433
+ Returns:
1434
+ Dictionary with server status
1435
+ """
1436
+ return {
1437
+ "server_running": self.running,
1438
+ "host": self.host,
1439
+ "port": self.port,
1440
+ "handlers": {
1441
+ "code_analysis": self.code_analysis_handler is not None,
1442
+ "dashboard": self.dashboard_handler is not None,
1443
+ "file": self.file_handler is not None,
1444
+ "hooks": self.hook_handler is not None,
1445
+ },
1446
+ }
1447
+
1448
+ def _cancel_all_tasks(self, loop=None):
1449
+ """Cancel all pending tasks in the event loop."""
1450
+ if loop is None:
1451
+ loop = self.loop
1452
+
1453
+ if not loop or loop.is_closed():
1454
+ return
1455
+
1456
+ try:
1457
+ # Get all tasks in the loop
1458
+ pending = asyncio.all_tasks(loop)
1459
+
1460
+ # Count tasks to cancel
1461
+ tasks_to_cancel = [task for task in pending if not task.done()]
1462
+
1463
+ if tasks_to_cancel:
1464
+ # Cancel each task
1465
+ for task in tasks_to_cancel:
1466
+ task.cancel()
1467
+
1468
+ # Wait for all tasks to complete cancellation
1469
+ gather = asyncio.gather(*tasks_to_cancel, return_exceptions=True)
1470
+ try:
1471
+ loop.run_until_complete(gather)
1472
+ except Exception:
1473
+ # Some tasks might fail to cancel, that's ok
1474
+ pass
1475
+
1476
+ self.logger.debug(f"Cancelled {len(tasks_to_cancel)} pending tasks")
1477
+ except Exception as e:
1478
+ self.logger.debug(f"Error cancelling tasks: {e}")
1479
+
1480
+ async def _graceful_shutdown(self):
1481
+ """Perform graceful shutdown of async resources."""
1482
+ try:
1483
+ # Stop accepting new connections
1484
+ self.running = False
1485
+
1486
+ # Give ongoing operations a moment to complete
1487
+ await asyncio.sleep(0.5)
1488
+
1489
+ # Then cleanup resources
1490
+ await self._cleanup_async()
1491
+
1492
+ except Exception as e:
1493
+ self.logger.debug(f"Error in graceful shutdown: {e}")