claude-mpm 4.1.1__py3-none-any.whl → 4.1.3__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 (389) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/__main__.py +1 -1
  4. claude_mpm/agents/BASE_PM.md +74 -46
  5. claude_mpm/agents/INSTRUCTIONS.md +11 -153
  6. claude_mpm/agents/WORKFLOW.md +61 -321
  7. claude_mpm/agents/__init__.py +11 -11
  8. claude_mpm/agents/agent_loader.py +23 -20
  9. claude_mpm/agents/agent_loader_integration.py +1 -1
  10. claude_mpm/agents/agents_metadata.py +27 -0
  11. claude_mpm/agents/async_agent_loader.py +5 -8
  12. claude_mpm/agents/base_agent_loader.py +36 -25
  13. claude_mpm/agents/frontmatter_validator.py +6 -6
  14. claude_mpm/agents/schema/agent_schema.json +1 -1
  15. claude_mpm/agents/system_agent_config.py +9 -9
  16. claude_mpm/agents/templates/api_qa.json +47 -2
  17. claude_mpm/agents/templates/engineer.json +33 -11
  18. claude_mpm/agents/templates/imagemagick.json +256 -0
  19. claude_mpm/agents/templates/qa.json +41 -2
  20. claude_mpm/agents/templates/ticketing.json +5 -5
  21. claude_mpm/agents/templates/web_qa.json +50 -2
  22. claude_mpm/cli/__init__.py +51 -46
  23. claude_mpm/cli/__main__.py +1 -1
  24. claude_mpm/cli/commands/__init__.py +10 -12
  25. claude_mpm/cli/commands/agent_manager.py +186 -181
  26. claude_mpm/cli/commands/agents.py +648 -1098
  27. claude_mpm/cli/commands/aggregate.py +30 -29
  28. claude_mpm/cli/commands/cleanup.py +50 -44
  29. claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
  30. claude_mpm/cli/commands/config.py +162 -127
  31. claude_mpm/cli/commands/doctor.py +52 -62
  32. claude_mpm/cli/commands/info.py +37 -25
  33. claude_mpm/cli/commands/mcp.py +3 -7
  34. claude_mpm/cli/commands/mcp_command_router.py +14 -18
  35. claude_mpm/cli/commands/mcp_install_commands.py +28 -23
  36. claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
  37. claude_mpm/cli/commands/mcp_server_commands.py +23 -17
  38. claude_mpm/cli/commands/memory.py +339 -967
  39. claude_mpm/cli/commands/monitor.py +117 -88
  40. claude_mpm/cli/commands/run.py +233 -542
  41. claude_mpm/cli/commands/socketio_monitor.py +17 -19
  42. claude_mpm/cli/commands/tickets.py +92 -92
  43. claude_mpm/cli/parser.py +1 -5
  44. claude_mpm/cli/parsers/__init__.py +1 -1
  45. claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
  46. claude_mpm/cli/parsers/agents_parser.py +2 -3
  47. claude_mpm/cli/parsers/base_parser.py +7 -5
  48. claude_mpm/cli/parsers/mcp_parser.py +4 -2
  49. claude_mpm/cli/parsers/monitor_parser.py +26 -18
  50. claude_mpm/cli/shared/__init__.py +10 -10
  51. claude_mpm/cli/shared/argument_patterns.py +57 -71
  52. claude_mpm/cli/shared/base_command.py +61 -53
  53. claude_mpm/cli/shared/error_handling.py +62 -58
  54. claude_mpm/cli/shared/output_formatters.py +78 -77
  55. claude_mpm/cli/startup_logging.py +280 -172
  56. claude_mpm/cli/utils.py +10 -11
  57. claude_mpm/cli_module/__init__.py +1 -1
  58. claude_mpm/cli_module/args.py +1 -1
  59. claude_mpm/cli_module/migration_example.py +5 -5
  60. claude_mpm/config/__init__.py +9 -9
  61. claude_mpm/config/agent_config.py +15 -14
  62. claude_mpm/config/experimental_features.py +4 -4
  63. claude_mpm/config/paths.py +0 -1
  64. claude_mpm/config/socketio_config.py +5 -6
  65. claude_mpm/constants.py +1 -2
  66. claude_mpm/core/__init__.py +8 -8
  67. claude_mpm/core/agent_name_normalizer.py +1 -1
  68. claude_mpm/core/agent_registry.py +22 -29
  69. claude_mpm/core/agent_session_manager.py +3 -3
  70. claude_mpm/core/base_service.py +7 -15
  71. claude_mpm/core/cache.py +4 -6
  72. claude_mpm/core/claude_runner.py +85 -113
  73. claude_mpm/core/config.py +43 -28
  74. claude_mpm/core/config_aliases.py +0 -9
  75. claude_mpm/core/config_constants.py +52 -30
  76. claude_mpm/core/constants.py +0 -1
  77. claude_mpm/core/container.py +18 -27
  78. claude_mpm/core/exceptions.py +2 -2
  79. claude_mpm/core/factories.py +10 -12
  80. claude_mpm/core/framework_loader.py +500 -680
  81. claude_mpm/core/hook_manager.py +26 -22
  82. claude_mpm/core/hook_performance_config.py +58 -47
  83. claude_mpm/core/injectable_service.py +1 -1
  84. claude_mpm/core/interactive_session.py +61 -152
  85. claude_mpm/core/interfaces.py +1 -100
  86. claude_mpm/core/lazy.py +5 -5
  87. claude_mpm/core/log_manager.py +587 -0
  88. claude_mpm/core/logger.py +125 -8
  89. claude_mpm/core/logging_config.py +15 -17
  90. claude_mpm/core/minimal_framework_loader.py +5 -8
  91. claude_mpm/core/oneshot_session.py +15 -33
  92. claude_mpm/core/optimized_agent_loader.py +4 -6
  93. claude_mpm/core/optimized_startup.py +2 -1
  94. claude_mpm/core/output_style_manager.py +147 -106
  95. claude_mpm/core/pm_hook_interceptor.py +0 -1
  96. claude_mpm/core/service_registry.py +11 -8
  97. claude_mpm/core/session_manager.py +1 -2
  98. claude_mpm/core/shared/__init__.py +1 -1
  99. claude_mpm/core/shared/config_loader.py +101 -97
  100. claude_mpm/core/shared/path_resolver.py +72 -68
  101. claude_mpm/core/shared/singleton_manager.py +56 -50
  102. claude_mpm/core/socketio_pool.py +26 -6
  103. claude_mpm/core/tool_access_control.py +4 -5
  104. claude_mpm/core/typing_utils.py +50 -59
  105. claude_mpm/core/unified_agent_registry.py +14 -19
  106. claude_mpm/core/unified_config.py +4 -6
  107. claude_mpm/core/unified_paths.py +197 -109
  108. claude_mpm/dashboard/open_dashboard.py +2 -4
  109. claude_mpm/experimental/cli_enhancements.py +51 -36
  110. claude_mpm/generators/agent_profile_generator.py +2 -4
  111. claude_mpm/hooks/base_hook.py +1 -2
  112. claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
  113. claude_mpm/hooks/claude_hooks/event_handlers.py +99 -154
  114. claude_mpm/hooks/claude_hooks/hook_handler.py +110 -720
  115. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
  116. claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
  117. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
  118. claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
  119. claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
  120. claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
  121. claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
  122. claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
  123. claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
  124. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
  125. claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
  126. claude_mpm/hooks/memory_integration_hook.py +5 -5
  127. claude_mpm/hooks/tool_call_interceptor.py +1 -1
  128. claude_mpm/hooks/validation_hooks.py +4 -4
  129. claude_mpm/init.py +4 -9
  130. claude_mpm/models/__init__.py +2 -2
  131. claude_mpm/models/agent_session.py +11 -14
  132. claude_mpm/scripts/mcp_server.py +20 -11
  133. claude_mpm/scripts/mcp_wrapper.py +5 -5
  134. claude_mpm/scripts/mpm_doctor.py +321 -0
  135. claude_mpm/scripts/socketio_daemon.py +28 -25
  136. claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
  137. claude_mpm/scripts/socketio_server_manager.py +116 -95
  138. claude_mpm/services/__init__.py +49 -49
  139. claude_mpm/services/agent_capabilities_service.py +12 -18
  140. claude_mpm/services/agents/__init__.py +22 -22
  141. claude_mpm/services/agents/agent_builder.py +140 -119
  142. claude_mpm/services/agents/deployment/__init__.py +3 -3
  143. claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
  144. claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
  145. claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
  146. claude_mpm/services/agents/deployment/agent_deployment.py +129 -511
  147. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
  148. claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
  149. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
  150. claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
  151. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
  152. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
  153. claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
  154. claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
  155. claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
  156. claude_mpm/services/agents/deployment/agent_validator.py +0 -1
  157. claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
  158. claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
  159. claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
  160. claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
  161. claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
  162. claude_mpm/services/agents/deployment/config/__init__.py +1 -1
  163. claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
  164. claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
  165. claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
  166. claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
  167. claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
  168. claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
  169. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
  170. claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
  171. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
  172. claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
  173. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
  174. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
  175. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
  176. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
  177. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
  178. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
  179. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
  180. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
  181. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
  182. claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
  183. claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
  184. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
  185. claude_mpm/services/agents/deployment/results/__init__.py +1 -1
  186. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
  187. claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
  188. claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
  189. claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
  190. claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
  191. claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
  192. claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
  193. claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
  194. claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
  195. claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
  196. claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
  197. claude_mpm/services/agents/loading/__init__.py +1 -1
  198. claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
  199. claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
  200. claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
  201. claude_mpm/services/agents/management/__init__.py +1 -1
  202. claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
  203. claude_mpm/services/agents/management/agent_management_service.py +5 -9
  204. claude_mpm/services/agents/memory/__init__.py +4 -4
  205. claude_mpm/services/agents/memory/agent_memory_manager.py +157 -503
  206. claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
  207. claude_mpm/services/agents/memory/content_manager.py +44 -38
  208. claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
  209. claude_mpm/services/agents/memory/memory_file_service.py +103 -0
  210. claude_mpm/services/agents/memory/memory_format_service.py +201 -0
  211. claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
  212. claude_mpm/services/agents/memory/template_generator.py +4 -6
  213. claude_mpm/services/agents/registry/__init__.py +11 -7
  214. claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
  215. claude_mpm/services/agents/registry/modification_tracker.py +3 -6
  216. claude_mpm/services/async_session_logger.py +1 -2
  217. claude_mpm/services/claude_session_logger.py +1 -2
  218. claude_mpm/services/cli/__init__.py +18 -0
  219. claude_mpm/services/cli/agent_cleanup_service.py +407 -0
  220. claude_mpm/services/cli/agent_dependency_service.py +395 -0
  221. claude_mpm/services/cli/agent_listing_service.py +463 -0
  222. claude_mpm/services/cli/agent_output_formatter.py +605 -0
  223. claude_mpm/services/cli/agent_validation_service.py +589 -0
  224. claude_mpm/services/cli/dashboard_launcher.py +424 -0
  225. claude_mpm/services/cli/memory_crud_service.py +617 -0
  226. claude_mpm/services/cli/memory_output_formatter.py +604 -0
  227. claude_mpm/services/cli/session_manager.py +513 -0
  228. claude_mpm/services/cli/socketio_manager.py +498 -0
  229. claude_mpm/services/cli/startup_checker.py +370 -0
  230. claude_mpm/services/command_deployment_service.py +173 -0
  231. claude_mpm/services/command_handler_service.py +20 -22
  232. claude_mpm/services/core/__init__.py +25 -25
  233. claude_mpm/services/core/base.py +0 -5
  234. claude_mpm/services/core/cache_manager.py +311 -0
  235. claude_mpm/services/core/interfaces/__init__.py +32 -32
  236. claude_mpm/services/core/interfaces/agent.py +0 -21
  237. claude_mpm/services/core/interfaces/communication.py +0 -27
  238. claude_mpm/services/core/interfaces/infrastructure.py +0 -56
  239. claude_mpm/services/core/interfaces/service.py +0 -29
  240. claude_mpm/services/core/memory_manager.py +637 -0
  241. claude_mpm/services/core/path_resolver.py +498 -0
  242. claude_mpm/services/core/service_container.py +520 -0
  243. claude_mpm/services/core/service_interfaces.py +436 -0
  244. claude_mpm/services/diagnostics/__init__.py +1 -1
  245. claude_mpm/services/diagnostics/checks/__init__.py +6 -6
  246. claude_mpm/services/diagnostics/checks/agent_check.py +152 -97
  247. claude_mpm/services/diagnostics/checks/base_check.py +12 -16
  248. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
  249. claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
  250. claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
  251. claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
  252. claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
  253. claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
  254. claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
  255. claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
  256. claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
  257. claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
  258. claude_mpm/services/diagnostics/models.py +21 -19
  259. claude_mpm/services/event_aggregator.py +10 -17
  260. claude_mpm/services/event_bus/__init__.py +1 -1
  261. claude_mpm/services/event_bus/config.py +54 -35
  262. claude_mpm/services/event_bus/event_bus.py +76 -71
  263. claude_mpm/services/event_bus/relay.py +74 -64
  264. claude_mpm/services/events/__init__.py +11 -11
  265. claude_mpm/services/events/consumers/__init__.py +3 -3
  266. claude_mpm/services/events/consumers/dead_letter.py +71 -63
  267. claude_mpm/services/events/consumers/logging.py +39 -37
  268. claude_mpm/services/events/consumers/metrics.py +56 -57
  269. claude_mpm/services/events/consumers/socketio.py +82 -81
  270. claude_mpm/services/events/core.py +110 -99
  271. claude_mpm/services/events/interfaces.py +56 -72
  272. claude_mpm/services/events/producers/__init__.py +1 -1
  273. claude_mpm/services/events/producers/hook.py +38 -38
  274. claude_mpm/services/events/producers/system.py +46 -44
  275. claude_mpm/services/exceptions.py +81 -80
  276. claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
  277. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
  278. claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
  279. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
  280. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
  281. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
  282. claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
  283. claude_mpm/services/hook_service.py +6 -9
  284. claude_mpm/services/infrastructure/__init__.py +1 -1
  285. claude_mpm/services/infrastructure/context_preservation.py +8 -12
  286. claude_mpm/services/infrastructure/monitoring.py +21 -23
  287. claude_mpm/services/mcp_gateway/__init__.py +37 -37
  288. claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
  289. claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
  290. claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
  291. claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
  292. claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
  293. claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
  294. claude_mpm/services/mcp_gateway/core/base.py +0 -3
  295. claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
  296. claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
  297. claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
  298. claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
  299. claude_mpm/services/mcp_gateway/main.py +2 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
  302. claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
  303. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
  304. claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
  305. claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
  306. claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
  307. claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
  308. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
  309. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
  310. claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
  311. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
  312. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
  313. claude_mpm/services/memory/__init__.py +3 -3
  314. claude_mpm/services/memory/builder.py +3 -6
  315. claude_mpm/services/memory/cache/__init__.py +1 -1
  316. claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
  317. claude_mpm/services/memory/cache/simple_cache.py +1 -1
  318. claude_mpm/services/memory/indexed_memory.py +5 -7
  319. claude_mpm/services/memory/optimizer.py +7 -10
  320. claude_mpm/services/memory/router.py +8 -9
  321. claude_mpm/services/memory_hook_service.py +48 -34
  322. claude_mpm/services/monitor_build_service.py +77 -73
  323. claude_mpm/services/port_manager.py +130 -108
  324. claude_mpm/services/project/analyzer.py +12 -10
  325. claude_mpm/services/project/registry.py +11 -11
  326. claude_mpm/services/recovery_manager.py +10 -19
  327. claude_mpm/services/response_tracker.py +0 -1
  328. claude_mpm/services/runner_configuration_service.py +19 -20
  329. claude_mpm/services/session_management_service.py +7 -11
  330. claude_mpm/services/shared/__init__.py +1 -1
  331. claude_mpm/services/shared/async_service_base.py +58 -50
  332. claude_mpm/services/shared/config_service_base.py +73 -67
  333. claude_mpm/services/shared/lifecycle_service_base.py +82 -78
  334. claude_mpm/services/shared/manager_base.py +94 -82
  335. claude_mpm/services/shared/service_factory.py +96 -98
  336. claude_mpm/services/socketio/__init__.py +3 -3
  337. claude_mpm/services/socketio/client_proxy.py +5 -5
  338. claude_mpm/services/socketio/event_normalizer.py +199 -181
  339. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  340. claude_mpm/services/socketio/handlers/base.py +5 -4
  341. claude_mpm/services/socketio/handlers/connection.py +163 -136
  342. claude_mpm/services/socketio/handlers/file.py +13 -14
  343. claude_mpm/services/socketio/handlers/git.py +12 -7
  344. claude_mpm/services/socketio/handlers/hook.py +49 -44
  345. claude_mpm/services/socketio/handlers/memory.py +0 -1
  346. claude_mpm/services/socketio/handlers/project.py +0 -1
  347. claude_mpm/services/socketio/handlers/registry.py +37 -19
  348. claude_mpm/services/socketio/migration_utils.py +98 -84
  349. claude_mpm/services/socketio/server/__init__.py +1 -1
  350. claude_mpm/services/socketio/server/broadcaster.py +81 -87
  351. claude_mpm/services/socketio/server/core.py +65 -54
  352. claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
  353. claude_mpm/services/socketio/server/main.py +64 -38
  354. claude_mpm/services/socketio_client_manager.py +10 -12
  355. claude_mpm/services/subprocess_launcher_service.py +4 -7
  356. claude_mpm/services/system_instructions_service.py +13 -14
  357. claude_mpm/services/ticket_manager.py +2 -2
  358. claude_mpm/services/utility_service.py +5 -13
  359. claude_mpm/services/version_control/__init__.py +16 -16
  360. claude_mpm/services/version_control/branch_strategy.py +5 -8
  361. claude_mpm/services/version_control/conflict_resolution.py +9 -23
  362. claude_mpm/services/version_control/git_operations.py +5 -7
  363. claude_mpm/services/version_control/semantic_versioning.py +16 -17
  364. claude_mpm/services/version_control/version_parser.py +13 -18
  365. claude_mpm/services/version_service.py +10 -11
  366. claude_mpm/storage/__init__.py +1 -1
  367. claude_mpm/storage/state_storage.py +22 -28
  368. claude_mpm/utils/__init__.py +6 -6
  369. claude_mpm/utils/agent_dependency_loader.py +47 -33
  370. claude_mpm/utils/config_manager.py +11 -14
  371. claude_mpm/utils/dependency_cache.py +1 -1
  372. claude_mpm/utils/dependency_manager.py +13 -17
  373. claude_mpm/utils/dependency_strategies.py +8 -10
  374. claude_mpm/utils/environment_context.py +3 -9
  375. claude_mpm/utils/error_handler.py +3 -13
  376. claude_mpm/utils/file_utils.py +1 -1
  377. claude_mpm/utils/path_operations.py +8 -12
  378. claude_mpm/utils/robust_installer.py +110 -33
  379. claude_mpm/utils/subprocess_utils.py +5 -6
  380. claude_mpm/validation/agent_validator.py +3 -6
  381. claude_mpm/validation/frontmatter_validator.py +1 -1
  382. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
  383. claude_mpm-4.1.3.dist-info/RECORD +528 -0
  384. claude_mpm/cli/commands/run_config_checker.py +0 -160
  385. claude_mpm-4.1.1.dist-info/RECORD +0 -494
  386. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
  387. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
  388. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
  389. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env python3
2
+ """Refactored Claude Code hook handler with modular service architecture.
3
+
4
+ This handler uses a service-oriented architecture with:
5
+ - StateManagerService: Manages state and delegation tracking
6
+ - ConnectionManagerService: Handles SocketIO and EventBus connections
7
+ - SubagentResponseProcessor: Processes complex subagent responses
8
+ - DuplicateEventDetector: Detects and filters duplicate events
9
+
10
+ WHY service-oriented approach:
11
+ - Better separation of concerns and modularity
12
+ - Easier testing and maintenance
13
+ - Reduced file size from 1040 to ~400 lines
14
+ - Clear service boundaries and responsibilities
15
+ """
16
+
17
+ import json
18
+ import os
19
+ import select
20
+ import signal
21
+ import sys
22
+ import threading
23
+ from datetime import datetime
24
+
25
+ # Import extracted modules with fallback for direct execution
26
+ try:
27
+ # Try relative imports first (when imported as module)
28
+ from .event_handlers import EventHandlers
29
+ from .memory_integration import MemoryHookManager
30
+ from .response_tracking import ResponseTrackingManager
31
+ from .services import (
32
+ ConnectionManagerService,
33
+ DuplicateEventDetector,
34
+ StateManagerService,
35
+ SubagentResponseProcessor,
36
+ )
37
+ except ImportError:
38
+ # Fall back to absolute imports (when run directly)
39
+ from pathlib import Path
40
+
41
+ # Add parent directory to path
42
+ sys.path.insert(0, str(Path(__file__).parent))
43
+
44
+ from event_handlers import EventHandlers
45
+ from memory_integration import MemoryHookManager
46
+ from response_tracking import ResponseTrackingManager
47
+ from services import (
48
+ ConnectionManagerService,
49
+ DuplicateEventDetector,
50
+ StateManagerService,
51
+ SubagentResponseProcessor,
52
+ )
53
+
54
+ # Debug mode is enabled by default for better visibility into hook processing
55
+ # Set CLAUDE_MPM_HOOK_DEBUG=false to disable debug output
56
+ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
57
+
58
+ # Global singleton handler instance
59
+ _global_handler = None
60
+ _handler_lock = threading.Lock()
61
+
62
+
63
+ class ClaudeHookHandler:
64
+ """Refactored hook handler with service-oriented architecture.
65
+
66
+ WHY service-oriented approach:
67
+ - Modular design with clear service boundaries
68
+ - Each service handles a specific responsibility
69
+ - Easier to test, maintain, and extend
70
+ - Reduced complexity in main handler class
71
+ """
72
+
73
+ def __init__(self):
74
+ # Initialize services
75
+ self.state_manager = StateManagerService()
76
+ self.connection_manager = ConnectionManagerService()
77
+ self.duplicate_detector = DuplicateEventDetector()
78
+
79
+ # Initialize extracted managers
80
+ self.memory_hook_manager = MemoryHookManager()
81
+ self.response_tracking_manager = ResponseTrackingManager()
82
+ self.event_handlers = EventHandlers(self)
83
+
84
+ # Initialize subagent processor with dependencies
85
+ self.subagent_processor = SubagentResponseProcessor(
86
+ self.state_manager, self.response_tracking_manager, self.connection_manager
87
+ )
88
+
89
+ def handle(self):
90
+ """Process hook event with minimal overhead and timeout protection.
91
+
92
+ WHY this approach:
93
+ - Fast path processing for minimal latency (no blocking waits)
94
+ - Non-blocking Socket.IO connection and event emission
95
+ - Timeout protection prevents indefinite hangs
96
+ - Connection timeout prevents indefinite hangs
97
+ - Graceful degradation if Socket.IO unavailable
98
+ - Always continues regardless of event status
99
+ - Process exits after handling to prevent accumulation
100
+ """
101
+ _continue_sent = False # Track if continue has been sent
102
+
103
+ def timeout_handler(signum, frame):
104
+ """Handle timeout by forcing exit."""
105
+ nonlocal _continue_sent
106
+ if DEBUG:
107
+ print(f"Hook handler timeout (pid: {os.getpid()})", file=sys.stderr)
108
+ if not _continue_sent:
109
+ self._continue_execution()
110
+ _continue_sent = True
111
+ sys.exit(0)
112
+
113
+ try:
114
+ # Set a 10-second timeout for the entire operation
115
+ signal.signal(signal.SIGALRM, timeout_handler)
116
+ signal.alarm(10)
117
+
118
+ # Read and parse event
119
+ event = self._read_hook_event()
120
+ if not event:
121
+ if not _continue_sent:
122
+ self._continue_execution()
123
+ _continue_sent = True
124
+ return
125
+
126
+ # Check for duplicate events (same event within 100ms)
127
+ if self.duplicate_detector.is_duplicate(event):
128
+ if DEBUG:
129
+ print(
130
+ f"[{datetime.now().isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
131
+ file=sys.stderr,
132
+ )
133
+ # Still need to output continue for this invocation
134
+ if not _continue_sent:
135
+ self._continue_execution()
136
+ _continue_sent = True
137
+ return
138
+
139
+ # Debug: Log that we're processing an event
140
+ if DEBUG:
141
+ hook_type = event.get("hook_event_name", "unknown")
142
+ print(
143
+ f"\n[{datetime.now().isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
144
+ file=sys.stderr,
145
+ )
146
+
147
+ # Perform periodic cleanup if needed
148
+ if self.state_manager.increment_events_processed():
149
+ self.state_manager.cleanup_old_entries()
150
+ if DEBUG:
151
+ print(
152
+ f"🧹 Performed cleanup after {self.state_manager.events_processed} events",
153
+ file=sys.stderr,
154
+ )
155
+
156
+ # Route event to appropriate handler
157
+ self._route_event(event)
158
+
159
+ # Always continue execution (only if not already sent)
160
+ if not _continue_sent:
161
+ self._continue_execution()
162
+ _continue_sent = True
163
+
164
+ except Exception:
165
+ # Fail fast and silent (only send continue if not already sent)
166
+ if not _continue_sent:
167
+ self._continue_execution()
168
+ _continue_sent = True
169
+ finally:
170
+ # Cancel the alarm
171
+ signal.alarm(0)
172
+
173
+ def _read_hook_event(self) -> dict:
174
+ """
175
+ Read and parse hook event from stdin with timeout.
176
+
177
+ WHY: Centralized event reading with error handling and timeout
178
+ ensures consistent parsing and validation while preventing
179
+ processes from hanging indefinitely on stdin.read().
180
+
181
+ Returns:
182
+ Parsed event dictionary or None if invalid/timeout
183
+ """
184
+ try:
185
+ # Check if data is available on stdin with 1 second timeout
186
+ if sys.stdin.isatty():
187
+ # Interactive terminal - no data expected
188
+ return None
189
+
190
+ ready, _, _ = select.select([sys.stdin], [], [], 1.0)
191
+ if not ready:
192
+ # No data available within timeout
193
+ if DEBUG:
194
+ print("No hook event data received within timeout", file=sys.stderr)
195
+ return None
196
+
197
+ # Data is available, read it
198
+ event_data = sys.stdin.read()
199
+ if not event_data.strip():
200
+ # Empty or whitespace-only data
201
+ return None
202
+
203
+ return json.loads(event_data)
204
+ except (json.JSONDecodeError, ValueError) as e:
205
+ if DEBUG:
206
+ print(f"Failed to parse hook event: {e}", file=sys.stderr)
207
+ return None
208
+ except Exception as e:
209
+ if DEBUG:
210
+ print(f"Error reading hook event: {e}", file=sys.stderr)
211
+ return None
212
+
213
+ def _route_event(self, event: dict) -> None:
214
+ """
215
+ Route event to appropriate handler based on type.
216
+
217
+ WHY: Centralized routing reduces complexity and makes
218
+ it easier to add new event types.
219
+
220
+ Args:
221
+ event: Hook event dictionary
222
+ """
223
+ hook_type = event.get("hook_event_name", "unknown")
224
+
225
+ # Map event types to handlers
226
+ event_handlers = {
227
+ "UserPromptSubmit": self.event_handlers.handle_user_prompt_fast,
228
+ "PreToolUse": self.event_handlers.handle_pre_tool_fast,
229
+ "PostToolUse": self.event_handlers.handle_post_tool_fast,
230
+ "Notification": self.event_handlers.handle_notification_fast,
231
+ "Stop": self.event_handlers.handle_stop_fast,
232
+ "SubagentStop": self.handle_subagent_stop,
233
+ "AssistantResponse": self.event_handlers.handle_assistant_response,
234
+ }
235
+
236
+ # Call appropriate handler if exists
237
+ handler = event_handlers.get(hook_type)
238
+ if handler:
239
+ try:
240
+ handler(event)
241
+ except Exception as e:
242
+ if DEBUG:
243
+ print(f"Error handling {hook_type}: {e}", file=sys.stderr)
244
+
245
+ def handle_subagent_stop(self, event: dict):
246
+ """Delegate subagent stop processing to the specialized processor."""
247
+ self.subagent_processor.process_subagent_stop(event)
248
+
249
+ def _continue_execution(self) -> None:
250
+ """
251
+ Send continue action to Claude.
252
+
253
+ WHY: Centralized response ensures consistent format
254
+ and makes it easier to add response modifications.
255
+ """
256
+ print(json.dumps({"action": "continue"}))
257
+
258
+ # Delegation methods for compatibility with event_handlers
259
+ def _track_delegation(self, session_id: str, agent_type: str, request_data=None):
260
+ """Track delegation through state manager."""
261
+ self.state_manager.track_delegation(session_id, agent_type, request_data)
262
+
263
+ def _get_delegation_agent_type(self, session_id: str) -> str:
264
+ """Get delegation agent type through state manager."""
265
+ return self.state_manager.get_delegation_agent_type(session_id)
266
+
267
+ def _get_git_branch(self, working_dir=None) -> str:
268
+ """Get git branch through state manager."""
269
+ return self.state_manager.get_git_branch(working_dir)
270
+
271
+ def _emit_socketio_event(self, namespace: str, event: str, data: dict):
272
+ """Emit event through connection manager."""
273
+ self.connection_manager.emit_event(namespace, event, data)
274
+
275
+ def __del__(self):
276
+ """Cleanup on handler destruction."""
277
+ # Clean up connection manager if it exists
278
+ if hasattr(self, "connection_manager") and self.connection_manager:
279
+ try:
280
+ self.connection_manager.cleanup()
281
+ except:
282
+ pass # Ignore cleanup errors during destruction
283
+
284
+
285
+ def main():
286
+ """Entry point with singleton pattern and proper cleanup."""
287
+ global _global_handler
288
+ _continue_printed = False # Track if we've already printed continue
289
+
290
+ def cleanup_handler(signum=None, frame=None):
291
+ """Cleanup handler for signals and exit."""
292
+ nonlocal _continue_printed
293
+ if DEBUG:
294
+ print(
295
+ f"Hook handler cleanup (pid: {os.getpid()}, signal: {signum})",
296
+ file=sys.stderr,
297
+ )
298
+ # Only output continue if we haven't already (i.e., if interrupted by signal)
299
+ if signum is not None and not _continue_printed:
300
+ print(json.dumps({"action": "continue"}))
301
+ _continue_printed = True
302
+ sys.exit(0)
303
+
304
+ # Register cleanup handlers
305
+ signal.signal(signal.SIGTERM, cleanup_handler)
306
+ signal.signal(signal.SIGINT, cleanup_handler)
307
+ # Don't register atexit handler since we're handling exit properly in main
308
+
309
+ try:
310
+ # Use singleton pattern to prevent creating multiple instances
311
+ with _handler_lock:
312
+ if _global_handler is None:
313
+ _global_handler = ClaudeHookHandler()
314
+ if DEBUG:
315
+ print(
316
+ f"✅ Created new ClaudeHookHandler singleton (pid: {os.getpid()})",
317
+ file=sys.stderr,
318
+ )
319
+ elif DEBUG:
320
+ print(
321
+ f"♻️ Reusing existing ClaudeHookHandler singleton (pid: {os.getpid()})",
322
+ file=sys.stderr,
323
+ )
324
+
325
+ handler = _global_handler
326
+
327
+ # Mark that handle() will print continue
328
+ handler.handle()
329
+ _continue_printed = True # Mark as printed since handle() always prints it
330
+
331
+ # handler.handle() already calls _continue_execution(), so we don't need to do it again
332
+ # Just exit cleanly
333
+ sys.exit(0)
334
+
335
+ except Exception as e:
336
+ # Only output continue if not already printed
337
+ if not _continue_printed:
338
+ print(json.dumps({"action": "continue"}))
339
+ _continue_printed = True
340
+ # Log error for debugging
341
+ if DEBUG:
342
+ print(f"Hook handler error: {e}", file=sys.stderr)
343
+ sys.exit(0) # Exit cleanly even on error
344
+
345
+
346
+ if __name__ == "__main__":
347
+ main()
@@ -8,7 +8,7 @@ including pre and post delegation hooks.
8
8
  import os
9
9
  import sys
10
10
  from datetime import datetime
11
- from typing import Any, Dict, Optional
11
+ from typing import Optional
12
12
 
13
13
  # Debug mode
14
14
  DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
@@ -21,14 +21,12 @@ try:
21
21
 
22
22
  paths.ensure_in_path()
23
23
 
24
- from claude_mpm.core.config import Config
25
24
  from claude_mpm.core.shared.config_loader import ConfigLoader
26
25
  from claude_mpm.hooks.base_hook import HookContext, HookType
27
26
  from claude_mpm.hooks.memory_integration_hook import (
28
27
  MemoryPostDelegationHook,
29
28
  MemoryPreDelegationHook,
30
29
  )
31
- from claude_mpm.services.hook_service import HookService
32
30
 
33
31
  MEMORY_HOOKS_AVAILABLE = True
34
32
  except Exception as e:
@@ -181,7 +179,7 @@ class MemoryHookManager:
181
179
  if output:
182
180
  result_content = str(output)
183
181
  elif error:
184
- result_content = f"Error: {str(error)}"
182
+ result_content = f"Error: {error!s}"
185
183
  else:
186
184
  result_content = f"Task completed with exit code: {exit_code}"
187
185
 
@@ -10,7 +10,7 @@ import os
10
10
  import re
11
11
  import sys
12
12
  from datetime import datetime
13
- from typing import Any, Dict, Optional
13
+ from typing import Optional
14
14
 
15
15
  # Debug mode
16
16
  DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
@@ -53,7 +53,6 @@ class ResponseTrackingManager:
53
53
  try:
54
54
  # Create configuration with optional config file using ConfigLoader
55
55
  config_file = os.environ.get("CLAUDE_PM_CONFIG_FILE")
56
- from claude_mpm.core.config import Config
57
56
  from claude_mpm.core.shared.config_loader import ConfigLoader, ConfigPattern
58
57
 
59
58
  config_loader = ConfigLoader()
@@ -62,9 +61,11 @@ class ResponseTrackingManager:
62
61
  pattern = ConfigPattern(
63
62
  filenames=[os.path.basename(config_file)],
64
63
  search_paths=[os.path.dirname(config_file)],
65
- env_prefix="CLAUDE_MPM_"
64
+ env_prefix="CLAUDE_MPM_",
65
+ )
66
+ config = config_loader.load_config(
67
+ pattern, cache_key=f"response_tracking_{config_file}"
66
68
  )
67
- config = config_loader.load_config(pattern, cache_key=f"response_tracking_{config_file}")
68
69
  else:
69
70
  config = config_loader.load_main_config()
70
71
 
@@ -101,7 +102,9 @@ class ResponseTrackingManager:
101
102
 
102
103
  except Exception as e:
103
104
  if DEBUG:
104
- print(f"❌ Failed to initialize response tracking: {e}", file=sys.stderr)
105
+ print(
106
+ f"❌ Failed to initialize response tracking: {e}", file=sys.stderr
107
+ )
105
108
  # Don't fail the entire handler - response tracking is optional
106
109
 
107
110
  def track_agent_response(
@@ -203,11 +206,13 @@ class ResponseTrackingManager:
203
206
  "files_modified": structured_response.get("files_modified", []),
204
207
  "tools_used": structured_response.get("tools_used", []),
205
208
  "remember": structured_response.get("remember"),
206
- "MEMORIES": structured_response.get("MEMORIES"), # Complete memory replacement
209
+ "MEMORIES": structured_response.get(
210
+ "MEMORIES"
211
+ ), # Complete memory replacement
207
212
  }
208
-
209
- # Log if MEMORIES field is present
210
- if "MEMORIES" in structured_response and structured_response["MEMORIES"]:
213
+
214
+ # Log if MEMORIES field is present
215
+ if structured_response.get("MEMORIES"):
211
216
  if DEBUG:
212
217
  memories_count = len(structured_response["MEMORIES"])
213
218
  print(
@@ -247,8 +252,7 @@ class ResponseTrackingManager:
247
252
  )
248
253
 
249
254
  # Clean up the request data after successful tracking
250
- if session_id in delegation_requests:
251
- del delegation_requests[session_id]
255
+ delegation_requests.pop(session_id, None)
252
256
 
253
257
  except Exception as e:
254
258
  if DEBUG:
@@ -0,0 +1,13 @@
1
+ """Hook handler services for modular functionality."""
2
+
3
+ from .connection_manager import ConnectionManagerService
4
+ from .duplicate_detector import DuplicateEventDetector
5
+ from .state_manager import StateManagerService
6
+ from .subagent_processor import SubagentResponseProcessor
7
+
8
+ __all__ = [
9
+ "ConnectionManagerService",
10
+ "DuplicateEventDetector",
11
+ "StateManagerService",
12
+ "SubagentResponseProcessor",
13
+ ]
@@ -0,0 +1,190 @@
1
+ """Connection management service for Claude hook handler.
2
+
3
+ This service manages:
4
+ - SocketIO connection pool initialization
5
+ - EventBus initialization
6
+ - Event emission through both channels
7
+ - Connection cleanup
8
+ """
9
+
10
+ import os
11
+ import sys
12
+ from datetime import datetime
13
+
14
+ # Debug mode is enabled by default for better visibility into hook processing
15
+ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
16
+
17
+ # Import extracted modules with fallback for direct execution
18
+ try:
19
+ # Try relative imports first (when imported as module)
20
+ # Use the modern SocketIOConnectionPool instead of the deprecated local one
21
+ from claude_mpm.core.socketio_pool import get_connection_pool
22
+ except ImportError:
23
+ # Fall back to absolute imports (when run directly)
24
+ from pathlib import Path
25
+
26
+ # Add parent directory to path
27
+ sys.path.insert(0, str(Path(__file__).parent))
28
+
29
+ # Try to import get_connection_pool from deprecated location
30
+ try:
31
+ from connection_pool import SocketIOConnectionPool
32
+
33
+ def get_connection_pool():
34
+ return SocketIOConnectionPool()
35
+
36
+ except ImportError:
37
+ get_connection_pool = None
38
+
39
+ # Import EventNormalizer for consistent event formatting
40
+ try:
41
+ from claude_mpm.services.socketio.event_normalizer import EventNormalizer
42
+ except ImportError:
43
+ # Create a simple fallback EventNormalizer if import fails
44
+ class EventNormalizer:
45
+ def normalize(self, event_data, source="hook"):
46
+ """Simple fallback normalizer that returns event as-is."""
47
+ return type(
48
+ "NormalizedEvent",
49
+ (),
50
+ {
51
+ "to_dict": lambda: {
52
+ "event": "claude_event",
53
+ "type": event_data.get("type", "unknown"),
54
+ "subtype": event_data.get("subtype", "generic"),
55
+ "timestamp": event_data.get(
56
+ "timestamp", datetime.now().isoformat()
57
+ ),
58
+ "data": event_data.get("data", event_data),
59
+ }
60
+ },
61
+ )
62
+
63
+
64
+ # Import EventBus for decoupled event distribution
65
+ try:
66
+ from claude_mpm.services.event_bus import EventBus
67
+
68
+ EVENTBUS_AVAILABLE = True
69
+ except ImportError:
70
+ EVENTBUS_AVAILABLE = False
71
+ EventBus = None
72
+
73
+
74
+ class ConnectionManagerService:
75
+ """Manages connections for the Claude hook handler."""
76
+
77
+ def __init__(self):
78
+ """Initialize connection management service."""
79
+ # Event normalizer for consistent event schema
80
+ self.event_normalizer = EventNormalizer()
81
+
82
+ # Initialize SocketIO connection pool for inter-process communication
83
+ # This sends events directly to the Socket.IO server in the daemon process
84
+ self.connection_pool = None
85
+ self._initialize_socketio_pool()
86
+
87
+ # Initialize EventBus for in-process event distribution (optional)
88
+ self.event_bus = None
89
+ self._initialize_eventbus()
90
+
91
+ def _initialize_socketio_pool(self):
92
+ """Initialize the SocketIO connection pool."""
93
+ try:
94
+ self.connection_pool = get_connection_pool()
95
+ if DEBUG:
96
+ print("✅ Modern SocketIO connection pool initialized", file=sys.stderr)
97
+ except Exception as e:
98
+ if DEBUG:
99
+ print(
100
+ f"⚠️ Failed to initialize SocketIO connection pool: {e}",
101
+ file=sys.stderr,
102
+ )
103
+ self.connection_pool = None
104
+
105
+ def _initialize_eventbus(self):
106
+ """Initialize the EventBus for in-process distribution."""
107
+ if EVENTBUS_AVAILABLE:
108
+ try:
109
+ self.event_bus = EventBus.get_instance()
110
+ if DEBUG:
111
+ print("✅ EventBus initialized for hook handler", file=sys.stderr)
112
+ except Exception as e:
113
+ if DEBUG:
114
+ print(f"⚠️ Failed to initialize EventBus: {e}", file=sys.stderr)
115
+ self.event_bus = None
116
+
117
+ def emit_event(self, namespace: str, event: str, data: dict):
118
+ """Emit event through both connection pool and EventBus.
119
+
120
+ WHY dual approach:
121
+ - Connection pool: Direct Socket.IO connection for inter-process communication
122
+ - EventBus: For in-process subscribers (if any)
123
+ - Ensures events reach the dashboard regardless of process boundaries
124
+ """
125
+ # Create event data for normalization
126
+ raw_event = {
127
+ "type": "hook",
128
+ "subtype": event, # e.g., "user_prompt", "pre_tool", "subagent_stop"
129
+ "timestamp": datetime.now().isoformat(),
130
+ "data": data,
131
+ "source": "claude_hooks", # Identify the source
132
+ "session_id": data.get("sessionId"), # Include session if available
133
+ }
134
+
135
+ # Normalize the event using EventNormalizer for consistent schema
136
+ normalized_event = self.event_normalizer.normalize(raw_event, source="hook")
137
+ claude_event_data = normalized_event.to_dict()
138
+
139
+ # Log important events for debugging
140
+ if DEBUG and event in ["subagent_stop", "pre_tool"]:
141
+ if event == "subagent_stop":
142
+ agent_type = data.get("agent_type", "unknown")
143
+ print(
144
+ f"Hook handler: Publishing SubagentStop for agent '{agent_type}'",
145
+ file=sys.stderr,
146
+ )
147
+ elif event == "pre_tool" and data.get("tool_name") == "Task":
148
+ delegation = data.get("delegation_details", {})
149
+ agent_type = delegation.get("agent_type", "unknown")
150
+ print(
151
+ f"Hook handler: Publishing Task delegation to agent '{agent_type}'",
152
+ file=sys.stderr,
153
+ )
154
+
155
+ # First, try to emit through direct Socket.IO connection pool
156
+ # This is the primary path for inter-process communication
157
+ if self.connection_pool:
158
+ try:
159
+ # Emit to Socket.IO server directly
160
+ self.connection_pool.emit("claude_event", claude_event_data)
161
+ if DEBUG:
162
+ print(f"✅ Emitted via connection pool: {event}", file=sys.stderr)
163
+ except Exception as e:
164
+ if DEBUG:
165
+ print(f"⚠️ Failed to emit via connection pool: {e}", file=sys.stderr)
166
+
167
+ # Also publish to EventBus for any in-process subscribers
168
+ if self.event_bus and EVENTBUS_AVAILABLE:
169
+ try:
170
+ # Publish to EventBus with topic format: hook.{event}
171
+ topic = f"hook.{event}"
172
+ self.event_bus.publish(topic, claude_event_data)
173
+ if DEBUG:
174
+ print(f"✅ Published to EventBus: {topic}", file=sys.stderr)
175
+ except Exception as e:
176
+ if DEBUG:
177
+ print(f"⚠️ Failed to publish to EventBus: {e}", file=sys.stderr)
178
+
179
+ # Warn if neither method is available
180
+ if not self.connection_pool and not self.event_bus and DEBUG:
181
+ print(f"⚠️ No event emission method available for: {event}", file=sys.stderr)
182
+
183
+ def cleanup(self):
184
+ """Cleanup connections on service destruction."""
185
+ # Clean up connection pool if it exists
186
+ if self.connection_pool:
187
+ try:
188
+ self.connection_pool.cleanup()
189
+ except:
190
+ pass # Ignore cleanup errors during destruction