claude-mpm 3.9.11__py3-none-any.whl → 4.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +2 -2
  3. claude_mpm/__main__.py +3 -2
  4. claude_mpm/agents/__init__.py +85 -79
  5. claude_mpm/agents/agent_loader.py +464 -1003
  6. claude_mpm/agents/agent_loader_integration.py +45 -45
  7. claude_mpm/agents/agents_metadata.py +29 -30
  8. claude_mpm/agents/async_agent_loader.py +156 -138
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/base_agent_loader.py +179 -151
  11. claude_mpm/agents/frontmatter_validator.py +229 -130
  12. claude_mpm/agents/schema/agent_schema.json +1 -1
  13. claude_mpm/agents/system_agent_config.py +213 -147
  14. claude_mpm/agents/templates/__init__.py +13 -13
  15. claude_mpm/agents/templates/code_analyzer.json +2 -2
  16. claude_mpm/agents/templates/data_engineer.json +1 -1
  17. claude_mpm/agents/templates/documentation.json +23 -11
  18. claude_mpm/agents/templates/engineer.json +22 -6
  19. claude_mpm/agents/templates/memory_manager.json +1 -1
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/project_organizer.json +1 -1
  22. claude_mpm/agents/templates/qa.json +1 -1
  23. claude_mpm/agents/templates/refactoring_engineer.json +222 -0
  24. claude_mpm/agents/templates/research.json +20 -14
  25. claude_mpm/agents/templates/security.json +1 -1
  26. claude_mpm/agents/templates/ticketing.json +2 -2
  27. claude_mpm/agents/templates/version_control.json +1 -1
  28. claude_mpm/agents/templates/web_qa.json +3 -1
  29. claude_mpm/agents/templates/web_ui.json +2 -2
  30. claude_mpm/cli/__init__.py +79 -51
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +20 -20
  33. claude_mpm/cli/commands/agents.py +279 -247
  34. claude_mpm/cli/commands/aggregate.py +138 -157
  35. claude_mpm/cli/commands/cleanup.py +147 -147
  36. claude_mpm/cli/commands/config.py +93 -76
  37. claude_mpm/cli/commands/info.py +17 -16
  38. claude_mpm/cli/commands/mcp.py +140 -905
  39. claude_mpm/cli/commands/mcp_command_router.py +139 -0
  40. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  41. claude_mpm/cli/commands/mcp_install_commands.py +20 -0
  42. claude_mpm/cli/commands/mcp_server_commands.py +175 -0
  43. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  44. claude_mpm/cli/commands/memory.py +239 -203
  45. claude_mpm/cli/commands/monitor.py +330 -86
  46. claude_mpm/cli/commands/run.py +380 -429
  47. claude_mpm/cli/commands/run_config_checker.py +160 -0
  48. claude_mpm/cli/commands/socketio_monitor.py +235 -0
  49. claude_mpm/cli/commands/tickets.py +363 -220
  50. claude_mpm/cli/parser.py +24 -1156
  51. claude_mpm/cli/parsers/__init__.py +29 -0
  52. claude_mpm/cli/parsers/agents_parser.py +136 -0
  53. claude_mpm/cli/parsers/base_parser.py +331 -0
  54. claude_mpm/cli/parsers/config_parser.py +85 -0
  55. claude_mpm/cli/parsers/mcp_parser.py +152 -0
  56. claude_mpm/cli/parsers/memory_parser.py +138 -0
  57. claude_mpm/cli/parsers/monitor_parser.py +124 -0
  58. claude_mpm/cli/parsers/run_parser.py +147 -0
  59. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  60. claude_mpm/cli/ticket_cli.py +7 -3
  61. claude_mpm/cli/utils.py +55 -37
  62. claude_mpm/cli_module/__init__.py +6 -6
  63. claude_mpm/cli_module/args.py +188 -140
  64. claude_mpm/cli_module/commands.py +79 -70
  65. claude_mpm/cli_module/migration_example.py +38 -60
  66. claude_mpm/config/__init__.py +32 -25
  67. claude_mpm/config/agent_config.py +151 -119
  68. claude_mpm/config/experimental_features.py +71 -73
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +35 -18
  72. claude_mpm/core/__init__.py +9 -6
  73. claude_mpm/core/agent_name_normalizer.py +68 -71
  74. claude_mpm/core/agent_registry.py +372 -521
  75. claude_mpm/core/agent_session_manager.py +74 -63
  76. claude_mpm/core/base_service.py +116 -87
  77. claude_mpm/core/cache.py +119 -153
  78. claude_mpm/core/claude_runner.py +425 -1120
  79. claude_mpm/core/config.py +263 -168
  80. claude_mpm/core/config_aliases.py +69 -61
  81. claude_mpm/core/config_constants.py +292 -0
  82. claude_mpm/core/constants.py +57 -99
  83. claude_mpm/core/container.py +211 -178
  84. claude_mpm/core/exceptions.py +233 -89
  85. claude_mpm/core/factories.py +92 -54
  86. claude_mpm/core/framework_loader.py +378 -220
  87. claude_mpm/core/hook_manager.py +198 -83
  88. claude_mpm/core/hook_performance_config.py +136 -0
  89. claude_mpm/core/injectable_service.py +61 -55
  90. claude_mpm/core/interactive_session.py +165 -155
  91. claude_mpm/core/interfaces.py +221 -195
  92. claude_mpm/core/lazy.py +96 -96
  93. claude_mpm/core/logger.py +133 -107
  94. claude_mpm/core/logging_config.py +185 -157
  95. claude_mpm/core/minimal_framework_loader.py +20 -15
  96. claude_mpm/core/mixins.py +30 -29
  97. claude_mpm/core/oneshot_session.py +215 -181
  98. claude_mpm/core/optimized_agent_loader.py +134 -138
  99. claude_mpm/core/optimized_startup.py +159 -157
  100. claude_mpm/core/pm_hook_interceptor.py +85 -72
  101. claude_mpm/core/service_registry.py +103 -101
  102. claude_mpm/core/session_manager.py +97 -87
  103. claude_mpm/core/socketio_pool.py +212 -158
  104. claude_mpm/core/tool_access_control.py +58 -51
  105. claude_mpm/core/types.py +46 -24
  106. claude_mpm/core/typing_utils.py +166 -82
  107. claude_mpm/core/unified_agent_registry.py +721 -0
  108. claude_mpm/core/unified_config.py +550 -0
  109. claude_mpm/core/unified_paths.py +549 -0
  110. claude_mpm/dashboard/index.html +1 -1
  111. claude_mpm/dashboard/open_dashboard.py +51 -17
  112. claude_mpm/dashboard/static/built/components/agent-inference.js +2 -0
  113. claude_mpm/dashboard/static/built/components/event-processor.js +2 -0
  114. claude_mpm/dashboard/static/built/components/event-viewer.js +2 -0
  115. claude_mpm/dashboard/static/built/components/export-manager.js +2 -0
  116. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +2 -0
  117. claude_mpm/dashboard/static/built/components/hud-library-loader.js +2 -0
  118. claude_mpm/dashboard/static/built/components/hud-manager.js +2 -0
  119. claude_mpm/dashboard/static/built/components/hud-visualizer.js +2 -0
  120. claude_mpm/dashboard/static/built/components/module-viewer.js +2 -0
  121. claude_mpm/dashboard/static/built/components/session-manager.js +2 -0
  122. claude_mpm/dashboard/static/built/components/socket-manager.js +2 -0
  123. claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -0
  124. claude_mpm/dashboard/static/built/components/working-directory.js +2 -0
  125. claude_mpm/dashboard/static/built/dashboard.js +2 -0
  126. claude_mpm/dashboard/static/built/socket-client.js +2 -0
  127. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  128. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  129. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  130. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  131. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  132. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  133. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  134. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  135. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  136. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  137. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  138. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  139. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  140. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  141. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  142. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  143. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  144. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  145. claude_mpm/dashboard/static/js/components/event-viewer.js +93 -72
  146. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  147. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +110 -96
  148. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  149. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  150. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  151. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  152. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  153. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  154. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  155. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  156. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  157. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  158. claude_mpm/dashboard/static/js/socket-client.js +133 -53
  159. claude_mpm/dashboard/templates/index.html +40 -50
  160. claude_mpm/experimental/cli_enhancements.py +60 -58
  161. claude_mpm/generators/__init__.py +1 -1
  162. claude_mpm/generators/agent_profile_generator.py +75 -65
  163. claude_mpm/hooks/__init__.py +1 -1
  164. claude_mpm/hooks/base_hook.py +33 -28
  165. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  166. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  167. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  168. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  169. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  170. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  171. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  172. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  173. claude_mpm/hooks/memory_integration_hook.py +140 -100
  174. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  175. claude_mpm/hooks/validation_hooks.py +57 -49
  176. claude_mpm/init.py +145 -121
  177. claude_mpm/models/__init__.py +9 -9
  178. claude_mpm/models/agent_definition.py +33 -23
  179. claude_mpm/models/agent_session.py +228 -200
  180. claude_mpm/scripts/__init__.py +1 -1
  181. claude_mpm/scripts/socketio_daemon.py +192 -75
  182. claude_mpm/scripts/socketio_server_manager.py +328 -0
  183. claude_mpm/scripts/start_activity_logging.py +25 -22
  184. claude_mpm/services/__init__.py +68 -43
  185. claude_mpm/services/agent_capabilities_service.py +271 -0
  186. claude_mpm/services/agents/__init__.py +23 -32
  187. claude_mpm/services/agents/deployment/__init__.py +3 -3
  188. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  189. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  190. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  191. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  192. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  193. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  194. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  195. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  196. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  197. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  198. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  199. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  200. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  201. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  202. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  203. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  204. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  205. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  206. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  207. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  208. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  209. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  210. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  211. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  212. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  213. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  214. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  215. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  216. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  217. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  218. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  219. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  220. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  221. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  222. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  223. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  224. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  225. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  226. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  227. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  228. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  229. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  230. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  231. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  232. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  233. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  234. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  235. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  236. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  237. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  238. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  239. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  240. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  241. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  242. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  243. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  244. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  245. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  246. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  247. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  248. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  249. claude_mpm/services/agents/loading/__init__.py +2 -2
  250. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  251. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  252. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  253. claude_mpm/services/agents/management/__init__.py +2 -2
  254. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  255. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  256. claude_mpm/services/agents/memory/__init__.py +9 -6
  257. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  258. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  259. claude_mpm/services/agents/memory/analyzer.py +430 -0
  260. claude_mpm/services/agents/memory/content_manager.py +376 -0
  261. claude_mpm/services/agents/memory/template_generator.py +468 -0
  262. claude_mpm/services/agents/registry/__init__.py +7 -10
  263. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  264. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  265. claude_mpm/services/async_session_logger.py +187 -153
  266. claude_mpm/services/claude_session_logger.py +87 -72
  267. claude_mpm/services/command_handler_service.py +217 -0
  268. claude_mpm/services/communication/__init__.py +3 -2
  269. claude_mpm/services/core/__init__.py +50 -97
  270. claude_mpm/services/core/base.py +60 -53
  271. claude_mpm/services/core/interfaces/__init__.py +188 -0
  272. claude_mpm/services/core/interfaces/agent.py +351 -0
  273. claude_mpm/services/core/interfaces/communication.py +343 -0
  274. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  275. claude_mpm/services/core/interfaces/service.py +434 -0
  276. claude_mpm/services/core/interfaces.py +19 -944
  277. claude_mpm/services/event_aggregator.py +208 -170
  278. claude_mpm/services/exceptions.py +387 -308
  279. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  280. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  281. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  282. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  283. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  284. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  285. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  286. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  287. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  288. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  289. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  290. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  291. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  292. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  293. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  294. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  295. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  296. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  297. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  298. claude_mpm/services/hook_service.py +106 -114
  299. claude_mpm/services/infrastructure/__init__.py +7 -5
  300. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  301. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  302. claude_mpm/services/infrastructure/logging.py +83 -76
  303. claude_mpm/services/infrastructure/monitoring.py +547 -404
  304. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  305. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  306. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  307. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  308. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  309. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  310. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  311. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  312. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  313. claude_mpm/services/mcp_gateway/main.py +287 -137
  314. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  315. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  316. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  317. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  318. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  319. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  320. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  321. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  322. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  323. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  324. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  325. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  326. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  327. claude_mpm/services/memory/__init__.py +2 -2
  328. claude_mpm/services/memory/builder.py +451 -362
  329. claude_mpm/services/memory/cache/__init__.py +2 -2
  330. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  331. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  332. claude_mpm/services/memory/indexed_memory.py +195 -193
  333. claude_mpm/services/memory/optimizer.py +267 -234
  334. claude_mpm/services/memory/router.py +571 -263
  335. claude_mpm/services/memory_hook_service.py +237 -0
  336. claude_mpm/services/port_manager.py +575 -0
  337. claude_mpm/services/project/__init__.py +3 -3
  338. claude_mpm/services/project/analyzer.py +451 -305
  339. claude_mpm/services/project/registry.py +262 -240
  340. claude_mpm/services/recovery_manager.py +287 -231
  341. claude_mpm/services/response_tracker.py +87 -67
  342. claude_mpm/services/runner_configuration_service.py +587 -0
  343. claude_mpm/services/session_management_service.py +304 -0
  344. claude_mpm/services/socketio/__init__.py +4 -4
  345. claude_mpm/services/socketio/client_proxy.py +174 -0
  346. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  347. claude_mpm/services/socketio/handlers/base.py +44 -30
  348. claude_mpm/services/socketio/handlers/connection.py +166 -64
  349. claude_mpm/services/socketio/handlers/file.py +123 -108
  350. claude_mpm/services/socketio/handlers/git.py +607 -373
  351. claude_mpm/services/socketio/handlers/hook.py +185 -0
  352. claude_mpm/services/socketio/handlers/memory.py +4 -4
  353. claude_mpm/services/socketio/handlers/project.py +4 -4
  354. claude_mpm/services/socketio/handlers/registry.py +53 -38
  355. claude_mpm/services/socketio/server/__init__.py +18 -0
  356. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  357. claude_mpm/services/socketio/server/core.py +399 -0
  358. claude_mpm/services/socketio/server/main.py +323 -0
  359. claude_mpm/services/socketio_client_manager.py +160 -133
  360. claude_mpm/services/socketio_server.py +36 -1885
  361. claude_mpm/services/subprocess_launcher_service.py +316 -0
  362. claude_mpm/services/system_instructions_service.py +258 -0
  363. claude_mpm/services/ticket_manager.py +19 -533
  364. claude_mpm/services/utility_service.py +285 -0
  365. claude_mpm/services/version_control/__init__.py +18 -21
  366. claude_mpm/services/version_control/branch_strategy.py +20 -10
  367. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  368. claude_mpm/services/version_control/git_operations.py +52 -21
  369. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  370. claude_mpm/services/version_control/version_parser.py +145 -125
  371. claude_mpm/services/version_service.py +270 -0
  372. claude_mpm/storage/__init__.py +2 -2
  373. claude_mpm/storage/state_storage.py +177 -181
  374. claude_mpm/ticket_wrapper.py +2 -2
  375. claude_mpm/utils/__init__.py +2 -2
  376. claude_mpm/utils/agent_dependency_loader.py +453 -243
  377. claude_mpm/utils/config_manager.py +157 -118
  378. claude_mpm/utils/console.py +1 -1
  379. claude_mpm/utils/dependency_cache.py +102 -107
  380. claude_mpm/utils/dependency_manager.py +52 -47
  381. claude_mpm/utils/dependency_strategies.py +131 -96
  382. claude_mpm/utils/environment_context.py +110 -102
  383. claude_mpm/utils/error_handler.py +75 -55
  384. claude_mpm/utils/file_utils.py +80 -67
  385. claude_mpm/utils/framework_detection.py +12 -11
  386. claude_mpm/utils/import_migration_example.py +12 -60
  387. claude_mpm/utils/imports.py +48 -45
  388. claude_mpm/utils/path_operations.py +100 -93
  389. claude_mpm/utils/robust_installer.py +172 -164
  390. claude_mpm/utils/session_logging.py +30 -23
  391. claude_mpm/utils/subprocess_utils.py +99 -61
  392. claude_mpm/validation/__init__.py +1 -1
  393. claude_mpm/validation/agent_validator.py +151 -111
  394. claude_mpm/validation/frontmatter_validator.py +92 -71
  395. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/METADATA +90 -22
  396. claude_mpm-4.0.4.dist-info/RECORD +417 -0
  397. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/entry_points.txt +1 -0
  398. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/licenses/LICENSE +1 -1
  399. claude_mpm/cli/commands/run_guarded.py +0 -511
  400. claude_mpm/config/memory_guardian_config.py +0 -325
  401. claude_mpm/config/memory_guardian_yaml.py +0 -335
  402. claude_mpm/core/config_paths.py +0 -150
  403. claude_mpm/core/memory_aware_runner.py +0 -353
  404. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  405. claude_mpm/deployment_paths.py +0 -261
  406. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  407. claude_mpm/models/state_models.py +0 -433
  408. claude_mpm/services/agent/__init__.py +0 -24
  409. claude_mpm/services/agent/deployment.py +0 -2548
  410. claude_mpm/services/agent/management.py +0 -598
  411. claude_mpm/services/agent/registry.py +0 -813
  412. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  413. claude_mpm/services/communication/socketio.py +0 -1935
  414. claude_mpm/services/communication/websocket.py +0 -479
  415. claude_mpm/services/framework_claude_md_generator.py +0 -624
  416. claude_mpm/services/health_monitor.py +0 -893
  417. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  418. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  419. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  420. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  421. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  422. claude_mpm/services/infrastructure/state_manager.py +0 -774
  423. claude_mpm/services/mcp_gateway/manager.py +0 -334
  424. claude_mpm/services/optimized_hook_service.py +0 -542
  425. claude_mpm/services/project_analyzer.py +0 -864
  426. claude_mpm/services/project_registry.py +0 -608
  427. claude_mpm/services/standalone_socketio_server.py +0 -1300
  428. claude_mpm/services/ticket_manager_di.py +0 -318
  429. claude_mpm/services/ticketing_service_original.py +0 -510
  430. claude_mpm/utils/paths.py +0 -395
  431. claude_mpm/utils/platform_memory.py +0 -524
  432. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  433. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/WHEEL +0 -0
  434. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,252 @@
1
+ """
2
+ SocketIO Event Broadcaster for claude-mpm.
3
+
4
+ WHY: This module contains all the broadcasting methods extracted from the
5
+ monolithic socketio_server.py file. It handles sending events to connected
6
+ clients for various Claude MPM activities.
7
+
8
+ DESIGN DECISION: Separated broadcasting logic from core server management
9
+ to create focused, testable modules with single responsibilities.
10
+ """
11
+
12
+ import asyncio
13
+ from datetime import datetime
14
+ from typing import Any, Dict, List, Optional, Set
15
+
16
+ from ....core.logging_config import get_logger
17
+
18
+
19
+ class SocketIOEventBroadcaster:
20
+ """Handles broadcasting events to connected Socket.IO clients.
21
+
22
+ WHY: This class encapsulates all the event broadcasting logic that was
23
+ scattered throughout the monolithic SocketIOServer class.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ sio,
29
+ connected_clients: Set[str],
30
+ event_buffer,
31
+ buffer_lock,
32
+ stats: Dict[str, Any],
33
+ logger,
34
+ server=None, # Add server reference for event history access
35
+ ):
36
+ self.sio = sio
37
+ self.connected_clients = connected_clients
38
+ self.event_buffer = event_buffer
39
+ self.buffer_lock = buffer_lock
40
+ self.stats = stats
41
+ self.logger = logger
42
+ self.loop = None # Will be set by main server
43
+ self.server = server # Reference to main server for event history
44
+
45
+ def broadcast_event(self, event_type: str, data: Dict[str, Any]):
46
+ """Broadcast an event to all connected clients."""
47
+ if not self.sio:
48
+ return
49
+
50
+ event = {
51
+ "type": event_type,
52
+ "timestamp": datetime.now().isoformat(),
53
+ "data": data,
54
+ }
55
+
56
+ # Buffer the event for reliability AND add to event history for new clients
57
+ with self.buffer_lock:
58
+ self.event_buffer.append(event)
59
+ self.stats["events_buffered"] += 1
60
+
61
+ # Also add to event history if available (for client replay)
62
+ # Access through server reference to maintain single history source
63
+ if hasattr(self, 'server') and hasattr(self.server, 'event_history'):
64
+ self.server.event_history.append(event)
65
+ self.logger.debug(f"Added {event_type} to history (total: {len(self.server.event_history)})")
66
+
67
+ # Broadcast to all connected clients
68
+ try:
69
+ # Use run_coroutine_threadsafe to safely call from any thread
70
+ if hasattr(self, "loop") and self.loop and not self.loop.is_closed():
71
+ future = asyncio.run_coroutine_threadsafe(
72
+ self.sio.emit("claude_event", event), self.loop
73
+ )
74
+ # Don't wait for the result to avoid blocking
75
+ self.stats["events_sent"] += 1
76
+ self.logger.debug(f"Broadcasted event: {event_type}")
77
+ else:
78
+ self.logger.warning(
79
+ f"Cannot broadcast {event_type}: server loop not available"
80
+ )
81
+
82
+ except Exception as e:
83
+ self.logger.error(f"Failed to broadcast event {event_type}: {e}")
84
+
85
+ def session_started(self, session_id: str, launch_method: str, working_dir: str):
86
+ """Notify that a session has started."""
87
+ self.broadcast_event(
88
+ "session_started",
89
+ {
90
+ "session_id": session_id,
91
+ "launch_method": launch_method,
92
+ "working_dir": working_dir,
93
+ "timestamp": datetime.now().isoformat(),
94
+ },
95
+ )
96
+
97
+ def session_ended(self):
98
+ """Notify that a session has ended."""
99
+ self.broadcast_event("session_ended", {"timestamp": datetime.now().isoformat()})
100
+
101
+ def claude_status_changed(
102
+ self, status: str, pid: Optional[int] = None, message: str = ""
103
+ ):
104
+ """Notify Claude status change."""
105
+ self.broadcast_event(
106
+ "claude_status", {"status": status, "pid": pid, "message": message}
107
+ )
108
+
109
+ def claude_output(self, content: str, stream: str = "stdout"):
110
+ """Broadcast Claude output."""
111
+ self.broadcast_event("claude_output", {"content": content, "stream": stream})
112
+
113
+ def agent_delegated(self, agent: str, task: str, status: str = "started"):
114
+ """Notify agent delegation."""
115
+ self.broadcast_event(
116
+ "agent_delegated", {"agent": agent, "task": task, "status": status}
117
+ )
118
+
119
+ def todo_updated(self, todos: List[Dict[str, Any]]):
120
+ """Notify todo list update."""
121
+ # Limit the size of todo data to prevent large payloads
122
+ limited_todos = todos[:50] if len(todos) > 50 else todos
123
+
124
+ self.broadcast_event(
125
+ "todo_updated",
126
+ {
127
+ "todos": limited_todos,
128
+ "total_count": len(todos),
129
+ "truncated": len(todos) > 50,
130
+ },
131
+ )
132
+
133
+ def ticket_created(self, ticket_id: str, title: str, priority: str = "medium"):
134
+ """Notify ticket creation."""
135
+ self.broadcast_event(
136
+ "ticket_created",
137
+ {"ticket_id": ticket_id, "title": title, "priority": priority},
138
+ )
139
+
140
+ def memory_loaded(self, agent_id: str, memory_size: int, sections_count: int):
141
+ """Notify when agent memory is loaded from file."""
142
+ self.broadcast_event(
143
+ "memory_loaded",
144
+ {
145
+ "agent_id": agent_id,
146
+ "memory_size": memory_size,
147
+ "sections_count": sections_count,
148
+ },
149
+ )
150
+
151
+ def memory_created(self, agent_id: str, template_type: str):
152
+ """Notify when new agent memory is created from template."""
153
+ self.broadcast_event(
154
+ "memory_created", {"agent_id": agent_id, "template_type": template_type}
155
+ )
156
+
157
+ def memory_updated(
158
+ self, agent_id: str, learning_type: str, content: str, section: str
159
+ ):
160
+ """Notify when learning is added to agent memory."""
161
+ # Truncate content if too long to prevent large payloads
162
+ truncated_content = content[:500] + "..." if len(content) > 500 else content
163
+
164
+ self.broadcast_event(
165
+ "memory_updated",
166
+ {
167
+ "agent_id": agent_id,
168
+ "learning_type": learning_type,
169
+ "content": truncated_content,
170
+ "section": section,
171
+ "content_length": len(content),
172
+ "truncated": len(content) > 500,
173
+ },
174
+ )
175
+
176
+ def memory_injected(self, agent_id: str, context_size: int):
177
+ """Notify when agent memory is injected into context."""
178
+ self.broadcast_event(
179
+ "memory_injected", {"agent_id": agent_id, "context_size": context_size}
180
+ )
181
+
182
+ def file_changed(
183
+ self, file_path: str, change_type: str, content: Optional[str] = None
184
+ ):
185
+ """Notify file system changes."""
186
+ event_data = {"file_path": file_path, "change_type": change_type}
187
+
188
+ # Include content for small files only
189
+ if content and len(content) < 1000:
190
+ event_data["content"] = content
191
+ elif content:
192
+ event_data["content_preview"] = content[:200] + "..."
193
+ event_data["content_length"] = len(content)
194
+
195
+ self.broadcast_event("file_changed", event_data)
196
+
197
+ def git_operation(self, operation: str, details: Dict[str, Any]):
198
+ """Notify Git operations."""
199
+ self.broadcast_event(
200
+ "git_operation", {"operation": operation, "details": details}
201
+ )
202
+
203
+ def error_occurred(
204
+ self, error_type: str, message: str, details: Optional[Dict[str, Any]] = None
205
+ ):
206
+ """Notify when errors occur."""
207
+ self.broadcast_event(
208
+ "error",
209
+ {"error_type": error_type, "message": message, "details": details or {}},
210
+ )
211
+
212
+ def performance_metric(self, metric_name: str, value: float, unit: str = ""):
213
+ """Broadcast performance metrics."""
214
+ self.broadcast_event(
215
+ "performance", {"metric": metric_name, "value": value, "unit": unit}
216
+ )
217
+
218
+ def system_status(self, status: Dict[str, Any]):
219
+ """Broadcast system status information."""
220
+ self.broadcast_event("system_status", status)
221
+
222
+ def broadcast_system_heartbeat(self, heartbeat_data: Dict[str, Any]):
223
+ """Broadcast system heartbeat event.
224
+
225
+ WHY: System events are separate from hook events to provide
226
+ server health monitoring independent of Claude activity.
227
+ """
228
+ if not self.sio:
229
+ return
230
+
231
+ # Create system event with consistent format
232
+ event = {
233
+ "type": "system",
234
+ "event": "heartbeat",
235
+ "timestamp": datetime.now().isoformat(),
236
+ "data": heartbeat_data,
237
+ }
238
+
239
+ # Broadcast to all connected clients
240
+ try:
241
+ if self.loop and not self.loop.is_closed():
242
+ future = asyncio.run_coroutine_threadsafe(
243
+ self.sio.emit("system_event", event), self.loop
244
+ )
245
+ self.logger.debug(
246
+ f"Broadcasted system heartbeat - clients: {len(self.connected_clients)}, "
247
+ f"uptime: {heartbeat_data.get('uptime_seconds', 0)}s"
248
+ )
249
+ else:
250
+ self.logger.warning("Cannot broadcast heartbeat: server loop not available")
251
+ except Exception as e:
252
+ self.logger.error(f"Failed to broadcast system heartbeat: {e}")
@@ -0,0 +1,399 @@
1
+ """
2
+ SocketIO Server Core for claude-mpm.
3
+
4
+ WHY: This module contains the core server management functionality extracted from
5
+ the monolithic socketio_server.py file. It handles server lifecycle, static file
6
+ serving, and basic server setup.
7
+
8
+ DESIGN DECISION: Separated core server logic from event handling and broadcasting
9
+ to create focused, maintainable modules.
10
+ """
11
+
12
+ import asyncio
13
+ import logging
14
+ import os
15
+ import threading
16
+ import time
17
+ from collections import deque
18
+ from datetime import datetime
19
+ from pathlib import Path
20
+ from typing import Any, Dict, List, Optional, Set
21
+
22
+ try:
23
+ import aiohttp
24
+ import socketio
25
+ from aiohttp import web
26
+
27
+ SOCKETIO_AVAILABLE = True
28
+ except ImportError:
29
+ SOCKETIO_AVAILABLE = False
30
+ socketio = None
31
+ aiohttp = None
32
+ web = None
33
+
34
+ from ....core.constants import (
35
+ NetworkConfig,
36
+ PerformanceConfig,
37
+ SystemLimits,
38
+ TimeoutConfig,
39
+ )
40
+ from ....core.interfaces import SocketIOServiceInterface
41
+ from ....core.logging_config import get_logger, log_operation, log_performance_context
42
+ from ....core.unified_paths import get_project_root, get_scripts_dir
43
+ from ...exceptions import SocketIOServerError as MPMConnectionError
44
+
45
+
46
+ class SocketIOServerCore:
47
+ """Core server management functionality for SocketIO server.
48
+
49
+ WHY: This class handles the basic server lifecycle, static file serving,
50
+ and core server setup. It's separated from event handling to reduce complexity.
51
+ """
52
+
53
+ def __init__(self, host: str = "localhost", port: int = 8765):
54
+ self.host = host
55
+ self.port = port
56
+ self.logger = get_logger(__name__ + ".SocketIOServer")
57
+ self.running = False
58
+ self.server_thread = None
59
+ self.loop = None
60
+ self.app = None
61
+ self.runner = None
62
+ self.site = None
63
+
64
+ # Socket.IO server instance
65
+ self.sio = None
66
+
67
+ # Connection tracking
68
+ self.connected_clients: Set[str] = set()
69
+ self.client_info: Dict[str, Dict[str, Any]] = {}
70
+
71
+ # Event buffering for reliability
72
+ self.event_buffer = deque(
73
+ maxlen=getattr(SystemLimits, "MAX_EVENTS_BUFFER", 1000)
74
+ )
75
+ self.buffer_lock = threading.Lock()
76
+
77
+ # Performance tracking
78
+ self.stats = {
79
+ "events_sent": 0,
80
+ "events_buffered": 0,
81
+ "connections_total": 0,
82
+ "start_time": None,
83
+ }
84
+
85
+ # Static files path
86
+ self.static_path = None
87
+
88
+ # Heartbeat task
89
+ self.heartbeat_task = None
90
+ self.heartbeat_interval = 60 # seconds
91
+ self.main_server = None # Reference to main server for session data
92
+
93
+ def start_sync(self):
94
+ """Start the Socket.IO server in a background thread (synchronous version)."""
95
+ if not SOCKETIO_AVAILABLE:
96
+ self.logger.warning("Socket.IO not available - server not started")
97
+ return
98
+
99
+ if self.running:
100
+ self.logger.warning("Socket.IO server already running")
101
+ return
102
+
103
+ self.logger.info(f"Starting Socket.IO server on {self.host}:{self.port}")
104
+
105
+ # Start server in background thread
106
+ self.server_thread = threading.Thread(target=self._run_server, daemon=True)
107
+ self.server_thread.start()
108
+
109
+ # Wait for server to start
110
+ max_wait = getattr(TimeoutConfig, "SERVER_START_TIMEOUT", 30)
111
+ wait_time = 0
112
+ while not self.running and wait_time < max_wait:
113
+ time.sleep(0.1)
114
+ wait_time += 0.1
115
+
116
+ if not self.running:
117
+ raise MPMConnectionError(
118
+ f"Failed to start Socket.IO server within {max_wait}s"
119
+ )
120
+
121
+ self.logger.info(
122
+ f"Socket.IO server started successfully on {self.host}:{self.port}"
123
+ )
124
+
125
+ def stop_sync(self):
126
+ """Stop the Socket.IO server (synchronous version)."""
127
+ if not self.running:
128
+ return
129
+
130
+ self.logger.info("Stopping Socket.IO server...")
131
+ self.running = False
132
+
133
+ # Stop the server gracefully
134
+ if self.loop and not self.loop.is_closed():
135
+ asyncio.run_coroutine_threadsafe(self._stop_server(), self.loop)
136
+
137
+ def _run_server(self):
138
+ """Run the server event loop."""
139
+ try:
140
+ # Create new event loop for this thread
141
+ self.loop = asyncio.new_event_loop()
142
+ asyncio.set_event_loop(self.loop)
143
+
144
+ # Run the server
145
+ self.loop.run_until_complete(self._start_server())
146
+
147
+ except Exception as e:
148
+ self.logger.error(f"Socket.IO server error: {e}")
149
+ self.running = False
150
+ finally:
151
+ if self.loop and not self.loop.is_closed():
152
+ self.loop.close()
153
+
154
+ async def _start_server(self):
155
+ """Start the Socket.IO server with aiohttp."""
156
+ try:
157
+ # Create Socket.IO server
158
+ self.sio = socketio.AsyncServer(
159
+ cors_allowed_origins="*",
160
+ logger=False, # Disable Socket.IO's own logging
161
+ engineio_logger=False,
162
+ )
163
+
164
+ # Create aiohttp application
165
+ self.app = web.Application()
166
+ self.sio.attach(self.app)
167
+
168
+ # Find and serve static files
169
+ self._setup_static_files()
170
+
171
+ # Create and start the server
172
+ self.runner = web.AppRunner(self.app)
173
+ await self.runner.setup()
174
+
175
+ self.site = web.TCPSite(
176
+ self.runner, self.host, self.port, reuse_address=True, reuse_port=True
177
+ )
178
+ await self.site.start()
179
+
180
+ self.running = True
181
+ self.stats["start_time"] = datetime.now()
182
+
183
+ self.logger.info(
184
+ f"Socket.IO server listening on http://{self.host}:{self.port}"
185
+ )
186
+ if self.static_path:
187
+ self.logger.info(f"Serving static files from: {self.static_path}")
188
+
189
+ # Start heartbeat task
190
+ self.heartbeat_task = asyncio.create_task(self._heartbeat_loop())
191
+ self.logger.info("Started system heartbeat task")
192
+
193
+ # Keep the server running
194
+ while self.running:
195
+ await asyncio.sleep(1)
196
+
197
+ except Exception as e:
198
+ self.logger.error(f"Failed to start Socket.IO server: {e}")
199
+ self.running = False
200
+ raise
201
+
202
+ async def _stop_server(self):
203
+ """Stop the server gracefully."""
204
+ try:
205
+ # Cancel heartbeat task
206
+ if self.heartbeat_task and not self.heartbeat_task.done():
207
+ self.heartbeat_task.cancel()
208
+ try:
209
+ await self.heartbeat_task
210
+ except asyncio.CancelledError:
211
+ pass
212
+ self.logger.info("Stopped system heartbeat task")
213
+
214
+ if self.site:
215
+ await self.site.stop()
216
+ self.site = None
217
+
218
+ if self.runner:
219
+ await self.runner.cleanup()
220
+ self.runner = None
221
+
222
+ self.logger.info("Socket.IO server stopped")
223
+
224
+ except Exception as e:
225
+ self.logger.error(f"Error stopping Socket.IO server: {e}")
226
+
227
+ def _setup_static_files(self):
228
+ """Setup static file serving for the dashboard."""
229
+ try:
230
+ self.dashboard_path = self._find_static_path()
231
+
232
+ if self.dashboard_path and self.dashboard_path.exists():
233
+ # Serve index.html at root
234
+ async def index_handler(request):
235
+ index_file = self.dashboard_path / "index.html"
236
+ if index_file.exists():
237
+ return web.FileResponse(index_file)
238
+ else:
239
+ return web.Response(text="Dashboard not available", status=404)
240
+
241
+ self.app.router.add_get("/", index_handler)
242
+
243
+ # Serve static assets (CSS, JS) from the dashboard static directory
244
+ dashboard_static_path = (
245
+ get_project_root() / "src" / "claude_mpm" / "dashboard" / "static"
246
+ )
247
+ if dashboard_static_path.exists():
248
+ self.app.router.add_static(
249
+ "/static/", dashboard_static_path, name="dashboard_static"
250
+ )
251
+ else:
252
+ self.logger.debug(f"Static assets directory not found at: {dashboard_static_path}")
253
+
254
+ else:
255
+ # Fallback handler
256
+ async def fallback_handler(request):
257
+ return web.Response(
258
+ text="Socket.IO server running - Dashboard not available",
259
+ status=200,
260
+ )
261
+
262
+ self.app.router.add_get("/", fallback_handler)
263
+
264
+ except Exception as e:
265
+ self.logger.warning(f"Error setting up static files: {e}")
266
+ # Ensure we always have a basic handler
267
+ async def error_handler(request):
268
+ return web.Response(
269
+ text="Socket.IO server running - Static files unavailable",
270
+ status=200,
271
+ )
272
+ self.app.router.add_get("/", error_handler)
273
+
274
+ def _find_static_path(self):
275
+ """Find the static files directory using multiple approaches.
276
+
277
+ WHY: The static files location varies depending on how the application
278
+ is installed and run. We try multiple common locations to find them.
279
+ """
280
+ # Try multiple possible locations for static files and dashboard
281
+ possible_paths = [
282
+ # Dashboard template directory (primary location)
283
+ get_project_root() / "src" / "claude_mpm" / "dashboard" / "templates",
284
+ get_project_root() / "dashboard" / "templates",
285
+ # Static file directories
286
+ get_project_root() / "src" / "claude_mpm" / "services" / "static",
287
+ get_project_root()
288
+ / "src"
289
+ / "claude_mpm"
290
+ / "services"
291
+ / "socketio"
292
+ / "static",
293
+ get_project_root() / "static",
294
+ get_project_root() / "src" / "static",
295
+ # Package installation locations
296
+ Path(__file__).parent.parent / "static",
297
+ Path(__file__).parent / "static",
298
+ # Scripts directory (for standalone installations)
299
+ get_scripts_dir() / "static",
300
+ get_scripts_dir() / "socketio" / "static",
301
+ # Current working directory
302
+ Path.cwd() / "static",
303
+ Path.cwd() / "socketio" / "static",
304
+ ]
305
+
306
+ for path in possible_paths:
307
+ if path.exists() and path.is_dir():
308
+ # Check if it contains expected files
309
+ if (path / "index.html").exists():
310
+ self.logger.debug(f"Found static files at: {path}")
311
+ return path
312
+
313
+ self.logger.warning("Static files not found - dashboard will not be available")
314
+ return None
315
+
316
+ def get_connection_count(self) -> int:
317
+ """Get number of connected clients.
318
+
319
+ WHY: Provides interface compliance for monitoring.
320
+
321
+ Returns:
322
+ Number of connected clients
323
+ """
324
+ return len(self.connected_clients)
325
+
326
+ def is_running(self) -> bool:
327
+ """Check if server is running.
328
+
329
+ WHY: Provides interface compliance for status checking.
330
+
331
+ Returns:
332
+ True if server is active
333
+ """
334
+ return self.running
335
+
336
+ async def _heartbeat_loop(self):
337
+ """Send periodic heartbeat events to connected clients.
338
+
339
+ WHY: This provides a way to verify the event flow is working and
340
+ track server health and active sessions without relying on hook events.
341
+ """
342
+ while self.running:
343
+ try:
344
+ # Wait for the interval
345
+ await asyncio.sleep(self.heartbeat_interval)
346
+
347
+ if not self.sio:
348
+ continue
349
+
350
+ # Calculate uptime
351
+ uptime_seconds = 0
352
+ if self.stats.get("start_time"):
353
+ uptime_seconds = int((datetime.now() - self.stats["start_time"]).total_seconds())
354
+
355
+ # Get active sessions from main server if available
356
+ active_sessions = []
357
+ if self.main_server and hasattr(self.main_server, 'get_active_sessions'):
358
+ try:
359
+ active_sessions = self.main_server.get_active_sessions()
360
+ except Exception as e:
361
+ self.logger.debug(f"Could not get active sessions: {e}")
362
+
363
+ # Prepare heartbeat data
364
+ heartbeat_data = {
365
+ "type": "system",
366
+ "event": "heartbeat",
367
+ "timestamp": datetime.now().isoformat(),
368
+ "data": {
369
+ "uptime_seconds": uptime_seconds,
370
+ "connected_clients": len(self.connected_clients),
371
+ "total_events": self.stats.get("events_sent", 0),
372
+ "active_sessions": active_sessions,
373
+ "server_info": {
374
+ "version": "4.0.2",
375
+ "port": self.port,
376
+ },
377
+ },
378
+ }
379
+
380
+ # Add to event history if main server is available
381
+ if self.main_server and hasattr(self.main_server, 'event_history'):
382
+ self.main_server.event_history.append(heartbeat_data)
383
+
384
+ # Emit heartbeat to all connected clients
385
+ await self.sio.emit("system_event", heartbeat_data)
386
+
387
+ self.logger.info(
388
+ f"System heartbeat sent - clients: {len(self.connected_clients)}, "
389
+ f"uptime: {uptime_seconds}s, events: {self.stats.get('events_sent', 0)}, "
390
+ f"sessions: {len(active_sessions)}"
391
+ )
392
+
393
+ except asyncio.CancelledError:
394
+ # Task was cancelled, exit gracefully
395
+ break
396
+ except Exception as e:
397
+ self.logger.error(f"Error in heartbeat loop: {e}")
398
+ # Continue running even if one heartbeat fails
399
+ await asyncio.sleep(5) # Short delay before retry