claude-mpm 4.1.0__py3-none-any.whl → 4.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) 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/imagemagick.json +256 -0
  18. claude_mpm/agents/templates/qa.json +41 -2
  19. claude_mpm/agents/templates/ticketing.json +5 -5
  20. claude_mpm/agents/templates/web_qa.json +133 -58
  21. claude_mpm/agents/templates/web_ui.json +3 -3
  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 +271 -268
  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 +192 -141
  39. claude_mpm/cli/commands/monitor.py +117 -88
  40. claude_mpm/cli/commands/run.py +120 -84
  41. claude_mpm/cli/commands/run_config_checker.py +4 -5
  42. claude_mpm/cli/commands/socketio_monitor.py +17 -19
  43. claude_mpm/cli/commands/tickets.py +92 -92
  44. claude_mpm/cli/parser.py +1 -5
  45. claude_mpm/cli/parsers/__init__.py +1 -1
  46. claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
  47. claude_mpm/cli/parsers/agents_parser.py +2 -3
  48. claude_mpm/cli/parsers/base_parser.py +7 -5
  49. claude_mpm/cli/parsers/mcp_parser.py +4 -2
  50. claude_mpm/cli/parsers/monitor_parser.py +26 -18
  51. claude_mpm/cli/shared/__init__.py +10 -10
  52. claude_mpm/cli/shared/argument_patterns.py +57 -71
  53. claude_mpm/cli/shared/base_command.py +61 -53
  54. claude_mpm/cli/shared/error_handling.py +62 -58
  55. claude_mpm/cli/shared/output_formatters.py +78 -77
  56. claude_mpm/cli/startup_logging.py +204 -172
  57. claude_mpm/cli/utils.py +10 -11
  58. claude_mpm/cli_module/__init__.py +1 -1
  59. claude_mpm/cli_module/args.py +1 -1
  60. claude_mpm/cli_module/migration_example.py +5 -5
  61. claude_mpm/config/__init__.py +9 -9
  62. claude_mpm/config/agent_config.py +15 -14
  63. claude_mpm/config/experimental_features.py +4 -4
  64. claude_mpm/config/paths.py +0 -1
  65. claude_mpm/config/socketio_config.py +5 -6
  66. claude_mpm/constants.py +1 -2
  67. claude_mpm/core/__init__.py +8 -8
  68. claude_mpm/core/agent_name_normalizer.py +1 -1
  69. claude_mpm/core/agent_registry.py +20 -23
  70. claude_mpm/core/agent_session_manager.py +3 -3
  71. claude_mpm/core/base_service.py +7 -15
  72. claude_mpm/core/cache.py +4 -6
  73. claude_mpm/core/claude_runner.py +85 -113
  74. claude_mpm/core/config.py +43 -28
  75. claude_mpm/core/config_aliases.py +0 -9
  76. claude_mpm/core/config_constants.py +52 -30
  77. claude_mpm/core/constants.py +0 -1
  78. claude_mpm/core/container.py +18 -27
  79. claude_mpm/core/exceptions.py +2 -2
  80. claude_mpm/core/factories.py +10 -12
  81. claude_mpm/core/framework_loader.py +581 -280
  82. claude_mpm/core/hook_manager.py +26 -22
  83. claude_mpm/core/hook_performance_config.py +58 -47
  84. claude_mpm/core/injectable_service.py +1 -1
  85. claude_mpm/core/interactive_session.py +61 -152
  86. claude_mpm/core/interfaces.py +1 -100
  87. claude_mpm/core/lazy.py +5 -5
  88. claude_mpm/core/log_manager.py +587 -0
  89. claude_mpm/core/logger.py +125 -8
  90. claude_mpm/core/logging_config.py +15 -15
  91. claude_mpm/core/minimal_framework_loader.py +5 -8
  92. claude_mpm/core/oneshot_session.py +15 -33
  93. claude_mpm/core/optimized_agent_loader.py +4 -6
  94. claude_mpm/core/optimized_startup.py +2 -1
  95. claude_mpm/core/output_style_manager.py +147 -106
  96. claude_mpm/core/pm_hook_interceptor.py +0 -1
  97. claude_mpm/core/service_registry.py +11 -8
  98. claude_mpm/core/session_manager.py +1 -2
  99. claude_mpm/core/shared/__init__.py +1 -1
  100. claude_mpm/core/shared/config_loader.py +101 -97
  101. claude_mpm/core/shared/path_resolver.py +72 -68
  102. claude_mpm/core/shared/singleton_manager.py +56 -50
  103. claude_mpm/core/socketio_pool.py +26 -6
  104. claude_mpm/core/tool_access_control.py +4 -5
  105. claude_mpm/core/typing_utils.py +50 -59
  106. claude_mpm/core/unified_agent_registry.py +14 -19
  107. claude_mpm/core/unified_config.py +4 -6
  108. claude_mpm/core/unified_paths.py +197 -109
  109. claude_mpm/dashboard/open_dashboard.py +2 -4
  110. claude_mpm/experimental/cli_enhancements.py +51 -36
  111. claude_mpm/generators/agent_profile_generator.py +2 -4
  112. claude_mpm/hooks/base_hook.py +1 -2
  113. claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
  114. claude_mpm/hooks/claude_hooks/event_handlers.py +93 -38
  115. claude_mpm/hooks/claude_hooks/hook_handler.py +130 -76
  116. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
  117. claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
  118. claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
  119. claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
  120. claude_mpm/hooks/memory_integration_hook.py +5 -5
  121. claude_mpm/hooks/tool_call_interceptor.py +1 -1
  122. claude_mpm/hooks/validation_hooks.py +4 -4
  123. claude_mpm/init.py +4 -9
  124. claude_mpm/models/__init__.py +2 -2
  125. claude_mpm/models/agent_session.py +11 -14
  126. claude_mpm/scripts/mcp_server.py +20 -11
  127. claude_mpm/scripts/mcp_wrapper.py +5 -5
  128. claude_mpm/scripts/mpm_doctor.py +321 -0
  129. claude_mpm/scripts/socketio_daemon.py +28 -25
  130. claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
  131. claude_mpm/scripts/socketio_server_manager.py +116 -95
  132. claude_mpm/services/__init__.py +49 -49
  133. claude_mpm/services/agent_capabilities_service.py +12 -18
  134. claude_mpm/services/agents/__init__.py +22 -22
  135. claude_mpm/services/agents/agent_builder.py +140 -119
  136. claude_mpm/services/agents/deployment/__init__.py +3 -3
  137. claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
  138. claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
  139. claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
  140. claude_mpm/services/agents/deployment/agent_deployment.py +136 -106
  141. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
  142. claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
  143. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
  144. claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
  145. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
  146. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
  147. claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
  148. claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
  149. claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
  150. claude_mpm/services/agents/deployment/agent_validator.py +0 -1
  151. claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
  152. claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
  153. claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
  154. claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
  155. claude_mpm/services/agents/deployment/config/__init__.py +1 -1
  156. claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
  157. claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
  158. claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
  159. claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
  160. claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
  161. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
  162. claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
  163. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
  164. claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
  165. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
  166. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
  167. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
  168. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
  169. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
  170. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
  171. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
  172. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
  173. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
  174. claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
  175. claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
  176. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
  177. claude_mpm/services/agents/deployment/results/__init__.py +1 -1
  178. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
  179. claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
  180. claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
  181. claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
  182. claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
  183. claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
  184. claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
  185. claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
  186. claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
  187. claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
  188. claude_mpm/services/agents/loading/__init__.py +1 -1
  189. claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
  190. claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
  191. claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
  192. claude_mpm/services/agents/management/__init__.py +1 -1
  193. claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
  194. claude_mpm/services/agents/management/agent_management_service.py +5 -9
  195. claude_mpm/services/agents/memory/__init__.py +4 -4
  196. claude_mpm/services/agents/memory/agent_memory_manager.py +280 -160
  197. claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
  198. claude_mpm/services/agents/memory/content_manager.py +44 -38
  199. claude_mpm/services/agents/memory/template_generator.py +4 -6
  200. claude_mpm/services/agents/registry/__init__.py +10 -6
  201. claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
  202. claude_mpm/services/agents/registry/modification_tracker.py +3 -6
  203. claude_mpm/services/async_session_logger.py +1 -2
  204. claude_mpm/services/claude_session_logger.py +1 -2
  205. claude_mpm/services/command_deployment_service.py +173 -0
  206. claude_mpm/services/command_handler_service.py +20 -22
  207. claude_mpm/services/core/__init__.py +25 -25
  208. claude_mpm/services/core/base.py +0 -5
  209. claude_mpm/services/core/interfaces/__init__.py +32 -32
  210. claude_mpm/services/core/interfaces/agent.py +0 -21
  211. claude_mpm/services/core/interfaces/communication.py +0 -27
  212. claude_mpm/services/core/interfaces/infrastructure.py +0 -56
  213. claude_mpm/services/core/interfaces/service.py +0 -29
  214. claude_mpm/services/diagnostics/__init__.py +1 -1
  215. claude_mpm/services/diagnostics/checks/__init__.py +6 -6
  216. claude_mpm/services/diagnostics/checks/agent_check.py +89 -80
  217. claude_mpm/services/diagnostics/checks/base_check.py +12 -16
  218. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
  219. claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
  220. claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
  221. claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
  222. claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
  223. claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
  224. claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
  225. claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
  226. claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
  227. claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
  228. claude_mpm/services/diagnostics/models.py +21 -19
  229. claude_mpm/services/event_aggregator.py +10 -17
  230. claude_mpm/services/event_bus/__init__.py +1 -1
  231. claude_mpm/services/event_bus/config.py +54 -35
  232. claude_mpm/services/event_bus/event_bus.py +76 -71
  233. claude_mpm/services/event_bus/relay.py +74 -64
  234. claude_mpm/services/events/__init__.py +11 -11
  235. claude_mpm/services/events/consumers/__init__.py +3 -3
  236. claude_mpm/services/events/consumers/dead_letter.py +71 -63
  237. claude_mpm/services/events/consumers/logging.py +39 -37
  238. claude_mpm/services/events/consumers/metrics.py +56 -57
  239. claude_mpm/services/events/consumers/socketio.py +82 -81
  240. claude_mpm/services/events/core.py +110 -99
  241. claude_mpm/services/events/interfaces.py +56 -72
  242. claude_mpm/services/events/producers/__init__.py +1 -1
  243. claude_mpm/services/events/producers/hook.py +38 -38
  244. claude_mpm/services/events/producers/system.py +46 -44
  245. claude_mpm/services/exceptions.py +81 -80
  246. claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
  247. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
  248. claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
  249. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
  250. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
  251. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
  252. claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
  253. claude_mpm/services/hook_service.py +6 -9
  254. claude_mpm/services/infrastructure/__init__.py +1 -1
  255. claude_mpm/services/infrastructure/context_preservation.py +8 -12
  256. claude_mpm/services/infrastructure/monitoring.py +21 -23
  257. claude_mpm/services/mcp_gateway/__init__.py +37 -37
  258. claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
  259. claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
  260. claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
  261. claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
  262. claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
  263. claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
  264. claude_mpm/services/mcp_gateway/core/base.py +0 -3
  265. claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
  266. claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
  267. claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
  268. claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
  269. claude_mpm/services/mcp_gateway/main.py +2 -1
  270. claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
  271. claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
  272. claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
  273. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
  274. claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
  275. claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
  276. claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
  277. claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
  278. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
  279. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
  280. claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
  281. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
  282. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
  283. claude_mpm/services/memory/__init__.py +3 -3
  284. claude_mpm/services/memory/builder.py +3 -6
  285. claude_mpm/services/memory/cache/__init__.py +1 -1
  286. claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
  287. claude_mpm/services/memory/cache/simple_cache.py +1 -1
  288. claude_mpm/services/memory/indexed_memory.py +5 -7
  289. claude_mpm/services/memory/optimizer.py +7 -10
  290. claude_mpm/services/memory/router.py +8 -9
  291. claude_mpm/services/memory_hook_service.py +48 -34
  292. claude_mpm/services/monitor_build_service.py +77 -73
  293. claude_mpm/services/port_manager.py +130 -108
  294. claude_mpm/services/project/analyzer.py +12 -10
  295. claude_mpm/services/project/registry.py +11 -11
  296. claude_mpm/services/recovery_manager.py +10 -19
  297. claude_mpm/services/response_tracker.py +0 -1
  298. claude_mpm/services/runner_configuration_service.py +19 -20
  299. claude_mpm/services/session_management_service.py +7 -11
  300. claude_mpm/services/shared/__init__.py +1 -1
  301. claude_mpm/services/shared/async_service_base.py +58 -50
  302. claude_mpm/services/shared/config_service_base.py +73 -67
  303. claude_mpm/services/shared/lifecycle_service_base.py +82 -78
  304. claude_mpm/services/shared/manager_base.py +94 -82
  305. claude_mpm/services/shared/service_factory.py +96 -98
  306. claude_mpm/services/socketio/__init__.py +3 -3
  307. claude_mpm/services/socketio/client_proxy.py +5 -5
  308. claude_mpm/services/socketio/event_normalizer.py +199 -181
  309. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  310. claude_mpm/services/socketio/handlers/base.py +5 -4
  311. claude_mpm/services/socketio/handlers/connection.py +163 -136
  312. claude_mpm/services/socketio/handlers/file.py +13 -14
  313. claude_mpm/services/socketio/handlers/git.py +12 -7
  314. claude_mpm/services/socketio/handlers/hook.py +49 -44
  315. claude_mpm/services/socketio/handlers/memory.py +0 -1
  316. claude_mpm/services/socketio/handlers/project.py +0 -1
  317. claude_mpm/services/socketio/handlers/registry.py +37 -19
  318. claude_mpm/services/socketio/migration_utils.py +98 -84
  319. claude_mpm/services/socketio/server/__init__.py +1 -1
  320. claude_mpm/services/socketio/server/broadcaster.py +81 -87
  321. claude_mpm/services/socketio/server/core.py +65 -54
  322. claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
  323. claude_mpm/services/socketio/server/main.py +64 -38
  324. claude_mpm/services/socketio_client_manager.py +10 -12
  325. claude_mpm/services/subprocess_launcher_service.py +4 -7
  326. claude_mpm/services/system_instructions_service.py +13 -14
  327. claude_mpm/services/ticket_manager.py +2 -2
  328. claude_mpm/services/utility_service.py +5 -13
  329. claude_mpm/services/version_control/__init__.py +16 -16
  330. claude_mpm/services/version_control/branch_strategy.py +5 -8
  331. claude_mpm/services/version_control/conflict_resolution.py +9 -23
  332. claude_mpm/services/version_control/git_operations.py +5 -7
  333. claude_mpm/services/version_control/semantic_versioning.py +16 -17
  334. claude_mpm/services/version_control/version_parser.py +13 -18
  335. claude_mpm/services/version_service.py +10 -11
  336. claude_mpm/storage/__init__.py +1 -1
  337. claude_mpm/storage/state_storage.py +22 -28
  338. claude_mpm/utils/__init__.py +6 -6
  339. claude_mpm/utils/agent_dependency_loader.py +47 -33
  340. claude_mpm/utils/config_manager.py +11 -14
  341. claude_mpm/utils/dependency_cache.py +1 -1
  342. claude_mpm/utils/dependency_manager.py +13 -17
  343. claude_mpm/utils/dependency_strategies.py +8 -10
  344. claude_mpm/utils/environment_context.py +3 -9
  345. claude_mpm/utils/error_handler.py +3 -13
  346. claude_mpm/utils/file_utils.py +1 -1
  347. claude_mpm/utils/path_operations.py +8 -12
  348. claude_mpm/utils/robust_installer.py +110 -33
  349. claude_mpm/utils/subprocess_utils.py +5 -6
  350. claude_mpm/validation/agent_validator.py +3 -6
  351. claude_mpm/validation/frontmatter_validator.py +1 -1
  352. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/METADATA +1 -1
  353. claude_mpm-4.1.2.dist-info/RECORD +498 -0
  354. claude_mpm-4.1.0.dist-info/RECORD +0 -494
  355. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/WHEEL +0 -0
  356. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/entry_points.txt +0 -0
  357. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/licenses/LICENSE +0 -0
  358. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,7 @@ import re
11
11
  import subprocess
12
12
  import sys
13
13
  from datetime import datetime
14
- from typing import Any, Dict, Optional
14
+ from typing import Optional
15
15
 
16
16
  # Import tool analysis with fallback for direct execution
17
17
  try:
@@ -221,7 +221,7 @@ class EventHandlers:
221
221
  self.hook_handler._track_delegation(session_id, agent_type, request_data)
222
222
 
223
223
  if DEBUG:
224
- print(f" - Delegation tracked successfully", file=sys.stderr)
224
+ print(" - Delegation tracked successfully", file=sys.stderr)
225
225
  print(
226
226
  f" - Request data keys: {list(request_data.keys())}",
227
227
  file=sys.stderr,
@@ -257,7 +257,56 @@ class EventHandlers:
257
257
  "", "subagent_start", subagent_start_data
258
258
  )
259
259
 
260
- def _get_git_branch(self, working_dir: str = None) -> str:
260
+ # Log agent prompt if LogManager is available
261
+ try:
262
+ from claude_mpm.core.log_manager import get_log_manager
263
+
264
+ log_manager = get_log_manager()
265
+
266
+ # Prepare prompt content
267
+ prompt_content = tool_input.get("prompt", "")
268
+ if not prompt_content:
269
+ prompt_content = tool_input.get("description", "")
270
+
271
+ if prompt_content:
272
+ import asyncio
273
+
274
+ # Prepare metadata
275
+ metadata = {
276
+ "agent_type": agent_type,
277
+ "agent_id": f"{agent_type}_{session_id}",
278
+ "session_id": session_id,
279
+ "delegation_context": {
280
+ "description": tool_input.get("description", ""),
281
+ "timestamp": datetime.now().isoformat(),
282
+ },
283
+ }
284
+
285
+ # Log the agent prompt asynchronously
286
+ try:
287
+ loop = asyncio.get_running_loop()
288
+ asyncio.create_task(
289
+ log_manager.log_prompt(
290
+ f"agent_{agent_type}", prompt_content, metadata
291
+ )
292
+ )
293
+ except RuntimeError:
294
+ # No running loop, create one
295
+ loop = asyncio.new_event_loop()
296
+ asyncio.set_event_loop(loop)
297
+ loop.run_until_complete(
298
+ log_manager.log_prompt(
299
+ f"agent_{agent_type}", prompt_content, metadata
300
+ )
301
+ )
302
+
303
+ if DEBUG:
304
+ print(f" - Agent prompt logged for {agent_type}", file=sys.stderr)
305
+ except Exception as e:
306
+ if DEBUG:
307
+ print(f" - Could not log agent prompt: {e}", file=sys.stderr)
308
+
309
+ def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
261
310
  """Get git branch for the given directory with caching."""
262
311
  # Use current working directory if not specified
263
312
  if not working_dir:
@@ -285,7 +334,8 @@ class EventHandlers:
285
334
  ["git", "branch", "--show-current"],
286
335
  capture_output=True,
287
336
  text=True,
288
- timeout=TimeoutConfig.QUICK_TIMEOUT, # Quick timeout to avoid hanging
337
+ timeout=TimeoutConfig.QUICK_TIMEOUT,
338
+ check=False, # Quick timeout to avoid hanging
289
339
  )
290
340
 
291
341
  # Restore original directory
@@ -297,11 +347,10 @@ class EventHandlers:
297
347
  self.hook_handler._git_branch_cache[cache_key] = branch
298
348
  self.hook_handler._git_branch_cache_time[cache_key] = current_time
299
349
  return branch
300
- else:
301
- # Not a git repository or no branch
302
- self.hook_handler._git_branch_cache[cache_key] = "Unknown"
303
- self.hook_handler._git_branch_cache_time[cache_key] = current_time
304
- return "Unknown"
350
+ # Not a git repository or no branch
351
+ self.hook_handler._git_branch_cache[cache_key] = "Unknown"
352
+ self.hook_handler._git_branch_cache_time[cache_key] = current_time
353
+ return "Unknown"
305
354
 
306
355
  except (
307
356
  subprocess.TimeoutExpired,
@@ -339,11 +388,11 @@ class EventHandlers:
339
388
  "tool_name": tool_name,
340
389
  "exit_code": exit_code,
341
390
  "success": exit_code == 0,
342
- "status": "success"
343
- if exit_code == 0
344
- else "blocked"
345
- if exit_code == 2
346
- else "error",
391
+ "status": (
392
+ "success"
393
+ if exit_code == 0
394
+ else "blocked" if exit_code == 2 else "error"
395
+ ),
347
396
  "duration_ms": duration,
348
397
  "result_summary": result_data,
349
398
  "session_id": event.get("session_id", ""),
@@ -412,9 +461,7 @@ class EventHandlers:
412
461
  }
413
462
 
414
463
  # Emit normalized event
415
- self.hook_handler._emit_socketio_event(
416
- "", "notification", notification_data
417
- )
464
+ self.hook_handler._emit_socketio_event("", "notification", notification_data)
418
465
 
419
466
  def handle_stop_fast(self, event):
420
467
  """Handle stop events when Claude processing stops.
@@ -448,9 +495,9 @@ class EventHandlers:
448
495
  return {
449
496
  "timestamp": datetime.now().isoformat(),
450
497
  "working_directory": working_dir,
451
- "git_branch": self._get_git_branch(working_dir)
452
- if working_dir
453
- else "Unknown",
498
+ "git_branch": (
499
+ self._get_git_branch(working_dir) if working_dir else "Unknown"
500
+ ),
454
501
  "event_type": "stop",
455
502
  "reason": event.get("reason", "unknown"),
456
503
  "stop_type": event.get("stop_type", "normal"),
@@ -612,9 +659,7 @@ class EventHandlers:
612
659
  )
613
660
 
614
661
  # Emit normalized event with high priority
615
- self.hook_handler._emit_socketio_event(
616
- "", "subagent_stop", subagent_stop_data
617
- )
662
+ self.hook_handler._emit_socketio_event("", "subagent_stop", subagent_stop_data)
618
663
 
619
664
  def _handle_subagent_response_tracking(
620
665
  self,
@@ -665,9 +710,9 @@ class EventHandlers:
665
710
  )
666
711
  # Update the key to use the current session_id for consistency
667
712
  if request_info:
668
- self.hook_handler.delegation_requests[
669
- session_id
670
- ] = request_info
713
+ self.hook_handler.delegation_requests[session_id] = (
714
+ request_info
715
+ )
671
716
  # Optionally remove the old key to avoid duplicates
672
717
  if stored_sid != session_id:
673
718
  del self.hook_handler.delegation_requests[stored_sid]
@@ -750,7 +795,7 @@ class EventHandlers:
750
795
 
751
796
  def handle_assistant_response(self, event):
752
797
  """Handle assistant response events for comprehensive response tracking.
753
-
798
+
754
799
  WHY emit assistant response events:
755
800
  - Provides visibility into Claude's responses to user prompts
756
801
  - Captures response content and metadata for analysis
@@ -761,19 +806,21 @@ class EventHandlers:
761
806
  self.hook_handler.response_tracking_manager.track_assistant_response(
762
807
  event, self.hook_handler.pending_prompts
763
808
  )
764
-
809
+
765
810
  # Get working directory and git branch
766
811
  working_dir = event.get("cwd", "")
767
812
  git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
768
-
813
+
769
814
  # Extract response data
770
815
  response_text = event.get("response", "")
771
816
  session_id = event.get("session_id", "")
772
-
817
+
773
818
  # Prepare assistant response data for Socket.IO emission
774
819
  assistant_response_data = {
775
820
  "response_text": response_text,
776
- "response_preview": response_text[:500] if len(response_text) > 500 else response_text,
821
+ "response_preview": (
822
+ response_text[:500] if len(response_text) > 500 else response_text
823
+ ),
777
824
  "response_length": len(response_text),
778
825
  "session_id": session_id,
779
826
  "working_directory": working_dir,
@@ -782,24 +829,32 @@ class EventHandlers:
782
829
  "contains_code": "```" in response_text,
783
830
  "contains_json": "```json" in response_text,
784
831
  "hook_event_name": "AssistantResponse", # Explicitly set for dashboard
785
- "has_structured_response": bool(re.search(r"```json\s*\{.*?\}\s*```", response_text, re.DOTALL)),
832
+ "has_structured_response": bool(
833
+ re.search(r"```json\s*\{.*?\}\s*```", response_text, re.DOTALL)
834
+ ),
786
835
  }
787
-
836
+
788
837
  # Check if this is a response to a tracked prompt
789
838
  if session_id in self.hook_handler.pending_prompts:
790
839
  prompt_data = self.hook_handler.pending_prompts[session_id]
791
- assistant_response_data["original_prompt"] = prompt_data.get("prompt", "")[:200]
792
- assistant_response_data["prompt_timestamp"] = prompt_data.get("timestamp", "")
840
+ assistant_response_data["original_prompt"] = prompt_data.get("prompt", "")[
841
+ :200
842
+ ]
843
+ assistant_response_data["prompt_timestamp"] = prompt_data.get(
844
+ "timestamp", ""
845
+ )
793
846
  assistant_response_data["is_tracked_response"] = True
794
847
  else:
795
848
  assistant_response_data["is_tracked_response"] = False
796
-
849
+
797
850
  # Debug logging
798
851
  if DEBUG:
799
852
  print(
800
853
  f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}",
801
854
  file=sys.stderr,
802
855
  )
803
-
856
+
804
857
  # Emit normalized event
805
- self.hook_handler._emit_socketio_event("", "assistant_response", assistant_response_data)
858
+ self.hook_handler._emit_socketio_event(
859
+ "", "assistant_response", assistant_response_data
860
+ )
@@ -12,7 +12,6 @@ WHY connection pooling approach:
12
12
  - Falls back gracefully when Socket.IO unavailable
13
13
  """
14
14
 
15
- import atexit
16
15
  import json
17
16
  import os
18
17
  import select
@@ -23,21 +22,34 @@ import threading
23
22
  import time
24
23
  from collections import deque
25
24
  from datetime import datetime
25
+ from typing import Optional
26
26
 
27
27
  # Import extracted modules with fallback for direct execution
28
28
  try:
29
29
  # Try relative imports first (when imported as module)
30
- from .connection_pool import SocketIOConnectionPool
30
+ # Use the modern SocketIOConnectionPool instead of the deprecated local one
31
+ from claude_mpm.core.socketio_pool import get_connection_pool
32
+
31
33
  from .event_handlers import EventHandlers
32
34
  from .memory_integration import MemoryHookManager
33
35
  from .response_tracking import ResponseTrackingManager
34
36
  except ImportError:
35
37
  # Fall back to absolute imports (when run directly)
36
- import sys
37
38
  from pathlib import Path
39
+
38
40
  # Add parent directory to path
39
41
  sys.path.insert(0, str(Path(__file__).parent))
40
- from connection_pool import SocketIOConnectionPool
42
+
43
+ # Try to import get_connection_pool from deprecated location
44
+ try:
45
+ from connection_pool import SocketIOConnectionPool
46
+
47
+ def get_connection_pool():
48
+ return SocketIOConnectionPool()
49
+
50
+ except ImportError:
51
+ get_connection_pool = None
52
+
41
53
  from event_handlers import EventHandlers
42
54
  from memory_integration import MemoryHookManager
43
55
  from response_tracking import ResponseTrackingManager
@@ -50,19 +62,27 @@ except ImportError:
50
62
  class EventNormalizer:
51
63
  def normalize(self, event_data):
52
64
  """Simple fallback normalizer that returns event as-is."""
53
- return type('NormalizedEvent', (), {
54
- 'to_dict': lambda: {
55
- 'event': 'claude_event',
56
- 'type': event_data.get('type', 'unknown'),
57
- 'subtype': event_data.get('subtype', 'generic'),
58
- 'timestamp': event_data.get('timestamp', datetime.now().isoformat()),
59
- 'data': event_data.get('data', event_data)
60
- }
61
- })
65
+ return type(
66
+ "NormalizedEvent",
67
+ (),
68
+ {
69
+ "to_dict": lambda: {
70
+ "event": "claude_event",
71
+ "type": event_data.get("type", "unknown"),
72
+ "subtype": event_data.get("subtype", "generic"),
73
+ "timestamp": event_data.get(
74
+ "timestamp", datetime.now().isoformat()
75
+ ),
76
+ "data": event_data.get("data", event_data),
77
+ }
78
+ },
79
+ )
80
+
62
81
 
63
82
  # Import EventBus for decoupled event distribution
64
83
  try:
65
84
  from claude_mpm.services.event_bus import EventBus
85
+
66
86
  EVENTBUS_AVAILABLE = True
67
87
  except ImportError:
68
88
  EVENTBUS_AVAILABLE = False
@@ -124,8 +144,23 @@ class ClaudeHookHandler:
124
144
  self.last_cleanup = time.time()
125
145
  # Event normalizer for consistent event schema
126
146
  self.event_normalizer = EventNormalizer()
127
-
128
- # Initialize EventBus for decoupled event distribution
147
+
148
+ # Initialize SocketIO connection pool for inter-process communication
149
+ # This sends events directly to the Socket.IO server in the daemon process
150
+ self.connection_pool = None
151
+ try:
152
+ self.connection_pool = get_connection_pool()
153
+ if DEBUG:
154
+ print("✅ Modern SocketIO connection pool initialized", file=sys.stderr)
155
+ except Exception as e:
156
+ if DEBUG:
157
+ print(
158
+ f"⚠️ Failed to initialize SocketIO connection pool: {e}",
159
+ file=sys.stderr,
160
+ )
161
+ self.connection_pool = None
162
+
163
+ # Initialize EventBus for in-process event distribution (optional)
129
164
  self.event_bus = None
130
165
  if EVENTBUS_AVAILABLE:
131
166
  try:
@@ -164,7 +199,7 @@ class ClaudeHookHandler:
164
199
  self.pending_prompts = {} # session_id -> prompt data
165
200
 
166
201
  def _track_delegation(
167
- self, session_id: str, agent_type: str, request_data: dict = None
202
+ self, session_id: str, agent_type: str, request_data: Optional[dict] = None
168
203
  ):
169
204
  """Track a new agent delegation with optional request data for response correlation."""
170
205
  if DEBUG:
@@ -224,7 +259,7 @@ class ClaudeHookHandler:
224
259
 
225
260
  def _cleanup_old_entries(self):
226
261
  """Clean up old entries to prevent memory growth."""
227
- cutoff_time = datetime.now().timestamp() - self.MAX_CACHE_AGE_SECONDS
262
+ datetime.now().timestamp() - self.MAX_CACHE_AGE_SECONDS
228
263
 
229
264
  # Clean up delegation tracking dictionaries
230
265
  for storage in [self.active_delegations, self.delegation_requests]:
@@ -266,7 +301,7 @@ class ClaudeHookHandler:
266
301
 
267
302
  return "unknown"
268
303
 
269
- def _get_git_branch(self, working_dir: str = None) -> str:
304
+ def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
270
305
  """Get git branch for the given directory with caching.
271
306
 
272
307
  WHY caching approach:
@@ -301,7 +336,8 @@ class ClaudeHookHandler:
301
336
  ["git", "branch", "--show-current"],
302
337
  capture_output=True,
303
338
  text=True,
304
- timeout=TimeoutConfig.QUICK_TIMEOUT, # Quick timeout to avoid hanging
339
+ timeout=TimeoutConfig.QUICK_TIMEOUT,
340
+ check=False, # Quick timeout to avoid hanging
305
341
  )
306
342
 
307
343
  # Restore original directory
@@ -313,11 +349,10 @@ class ClaudeHookHandler:
313
349
  self._git_branch_cache[cache_key] = branch
314
350
  self._git_branch_cache_time[cache_key] = current_time
315
351
  return branch
316
- else:
317
- # Not a git repository or no branch
318
- self._git_branch_cache[cache_key] = "Unknown"
319
- self._git_branch_cache_time[cache_key] = current_time
320
- return "Unknown"
352
+ # Not a git repository or no branch
353
+ self._git_branch_cache[cache_key] = "Unknown"
354
+ self._git_branch_cache_time[cache_key] = current_time
355
+ return "Unknown"
321
356
 
322
357
  except (
323
358
  subprocess.TimeoutExpired,
@@ -366,31 +401,37 @@ class ClaudeHookHandler:
366
401
  self._continue_execution()
367
402
  _continue_sent = True
368
403
  return
369
-
404
+
370
405
  # Check for duplicate events (same event within 100ms)
371
406
  global _recent_events, _events_lock
372
407
  event_key = self._get_event_key(event)
373
408
  current_time = time.time()
374
-
409
+
375
410
  with _events_lock:
376
411
  # Check if we've seen this event recently
377
412
  for recent_key, recent_time in _recent_events:
378
413
  if recent_key == event_key and (current_time - recent_time) < 0.1:
379
414
  if DEBUG:
380
- print(f"[{datetime.now().isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})", file=sys.stderr)
415
+ print(
416
+ f"[{datetime.now().isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
417
+ file=sys.stderr,
418
+ )
381
419
  # Still need to output continue for this invocation
382
420
  if not _continue_sent:
383
421
  self._continue_execution()
384
422
  _continue_sent = True
385
423
  return
386
-
424
+
387
425
  # Not a duplicate, record it
388
426
  _recent_events.append((event_key, current_time))
389
-
427
+
390
428
  # Debug: Log that we're processing an event
391
429
  if DEBUG:
392
430
  hook_type = event.get("hook_event_name", "unknown")
393
- print(f"\n[{datetime.now().isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})", file=sys.stderr)
431
+ print(
432
+ f"\n[{datetime.now().isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
433
+ file=sys.stderr,
434
+ )
394
435
 
395
436
  # Increment event counter and perform periodic cleanup
396
437
  self.events_processed += 1
@@ -493,7 +534,7 @@ class ClaudeHookHandler:
493
534
 
494
535
  def _get_event_key(self, event: dict) -> str:
495
536
  """Generate a unique key for an event to detect duplicates.
496
-
537
+
497
538
  WHY: Claude Code may call the hook multiple times for the same event
498
539
  because the hook is registered for multiple event types. We need to
499
540
  detect and skip duplicate processing while still returning continue.
@@ -501,7 +542,7 @@ class ClaudeHookHandler:
501
542
  # Create a key from event type, session_id, and key data
502
543
  hook_type = event.get("hook_event_name", "unknown")
503
544
  session_id = event.get("session_id", "")
504
-
545
+
505
546
  # Add type-specific data to make the key unique
506
547
  if hook_type == "PreToolUse":
507
548
  tool_name = event.get("tool_name", "")
@@ -509,17 +550,17 @@ class ClaudeHookHandler:
509
550
  if tool_name == "Task":
510
551
  tool_input = event.get("tool_input", {})
511
552
  agent = tool_input.get("subagent_type", "")
512
- prompt_preview = (tool_input.get("prompt", "") or tool_input.get("description", ""))[:50]
553
+ prompt_preview = (
554
+ tool_input.get("prompt", "") or tool_input.get("description", "")
555
+ )[:50]
513
556
  return f"{hook_type}:{session_id}:{tool_name}:{agent}:{prompt_preview}"
514
- else:
515
- return f"{hook_type}:{session_id}:{tool_name}"
516
- elif hook_type == "UserPromptSubmit":
557
+ return f"{hook_type}:{session_id}:{tool_name}"
558
+ if hook_type == "UserPromptSubmit":
517
559
  prompt_preview = event.get("prompt", "")[:50]
518
560
  return f"{hook_type}:{session_id}:{prompt_preview}"
519
- else:
520
- # For other events, just use type and session
521
- return f"{hook_type}:{session_id}"
522
-
561
+ # For other events, just use type and session
562
+ return f"{hook_type}:{session_id}"
563
+
523
564
  def _continue_execution(self) -> None:
524
565
  """
525
566
  Send continue action to Claude.
@@ -529,16 +570,13 @@ class ClaudeHookHandler:
529
570
  """
530
571
  print(json.dumps({"action": "continue"}))
531
572
 
532
-
533
573
  def _emit_socketio_event(self, namespace: str, event: str, data: dict):
534
- """Emit event through EventBus for Socket.IO relay.
535
-
536
- WHY EventBus-only approach:
537
- - Single event path prevents duplicates
538
- - EventBus relay handles Socket.IO connection management
539
- - Better separation of concerns
540
- - More resilient with centralized failure handling
541
- - Cleaner architecture and easier testing
574
+ """Emit event through both connection pool and EventBus.
575
+
576
+ WHY dual approach:
577
+ - Connection pool: Direct Socket.IO connection for inter-process communication
578
+ - EventBus: For in-process subscribers (if any)
579
+ - Ensures events reach the dashboard regardless of process boundaries
542
580
  """
543
581
  # Create event data for normalization
544
582
  raw_event = {
@@ -549,7 +587,7 @@ class ClaudeHookHandler:
549
587
  "source": "claude_hooks", # Identify the source
550
588
  "session_id": data.get("sessionId"), # Include session if available
551
589
  }
552
-
590
+
553
591
  # Normalize the event using EventNormalizer for consistent schema
554
592
  normalized_event = self.event_normalizer.normalize(raw_event, source="hook")
555
593
  claude_event_data = normalized_event.to_dict()
@@ -569,8 +607,20 @@ class ClaudeHookHandler:
569
607
  f"Hook handler: Publishing Task delegation to agent '{agent_type}'",
570
608
  file=sys.stderr,
571
609
  )
572
-
573
- # Publish to EventBus for distribution through relay
610
+
611
+ # First, try to emit through direct Socket.IO connection pool
612
+ # This is the primary path for inter-process communication
613
+ if self.connection_pool:
614
+ try:
615
+ # Emit to Socket.IO server directly
616
+ self.connection_pool.emit("claude_event", claude_event_data)
617
+ if DEBUG:
618
+ print(f"✅ Emitted via connection pool: {event}", file=sys.stderr)
619
+ except Exception as e:
620
+ if DEBUG:
621
+ print(f"⚠️ Failed to emit via connection pool: {e}", file=sys.stderr)
622
+
623
+ # Also publish to EventBus for any in-process subscribers
574
624
  if self.event_bus and EVENTBUS_AVAILABLE:
575
625
  try:
576
626
  # Publish to EventBus with topic format: hook.{event}
@@ -581,9 +631,10 @@ class ClaudeHookHandler:
581
631
  except Exception as e:
582
632
  if DEBUG:
583
633
  print(f"⚠️ Failed to publish to EventBus: {e}", file=sys.stderr)
584
- else:
585
- if DEBUG:
586
- print(f"⚠️ EventBus not available for event: hook.{event}", file=sys.stderr)
634
+
635
+ # Warn if neither method is available
636
+ if not self.connection_pool and not self.event_bus and DEBUG:
637
+ print(f"⚠️ No event emission method available for: {event}", file=sys.stderr)
587
638
 
588
639
  def handle_subagent_stop(self, event: dict):
589
640
  """Handle subagent stop events with improved agent type detection.
@@ -609,16 +660,14 @@ class ClaudeHookHandler:
609
660
  # Show all stored session IDs for comparison
610
661
  all_sessions = list(self.delegation_requests.keys())
611
662
  if all_sessions:
612
- print(f" - Stored sessions (first 16 chars):", file=sys.stderr)
663
+ print(" - Stored sessions (first 16 chars):", file=sys.stderr)
613
664
  for sid in all_sessions[:10]: # Show up to 10
614
665
  print(
615
666
  f" - {sid[:16]}... (agent: {self.delegation_requests[sid].get('agent_type', 'unknown')})",
616
667
  file=sys.stderr,
617
668
  )
618
669
  else:
619
- print(
620
- f" - No stored sessions in delegation_requests!", file=sys.stderr
621
- )
670
+ print(" - No stored sessions in delegation_requests!", file=sys.stderr)
622
671
 
623
672
  # First try to get agent type from our tracking
624
673
  agent_type = (
@@ -691,16 +740,16 @@ class ClaudeHookHandler:
691
740
  print(f" - reason: {reason}", file=sys.stderr)
692
741
  # Check if session exists in our storage
693
742
  if session_id in self.delegation_requests:
694
- print(f" - ✅ Session found in delegation_requests", file=sys.stderr)
743
+ print(" - ✅ Session found in delegation_requests", file=sys.stderr)
695
744
  print(
696
745
  f" - Stored agent: {self.delegation_requests[session_id].get('agent_type')}",
697
746
  file=sys.stderr,
698
747
  )
699
748
  else:
700
749
  print(
701
- f" - ❌ Session NOT found in delegation_requests!", file=sys.stderr
750
+ " - ❌ Session NOT found in delegation_requests!", file=sys.stderr
702
751
  )
703
- print(f" - Looking for partial match...", file=sys.stderr)
752
+ print(" - Looking for partial match...", file=sys.stderr)
704
753
  # Try to find partial matches
705
754
  for stored_sid in list(self.delegation_requests.keys())[:10]:
706
755
  if stored_sid.startswith(session_id[:8]) or session_id.startswith(
@@ -758,7 +807,7 @@ class ClaudeHookHandler:
758
807
  )
759
808
  if request_info:
760
809
  print(
761
- f" - ✅ Found request data for response tracking",
810
+ " - ✅ Found request data for response tracking",
762
811
  file=sys.stderr,
763
812
  )
764
813
  print(
@@ -820,9 +869,9 @@ class ClaudeHookHandler:
820
869
  metadata["task_completed"] = structured_response.get(
821
870
  "task_completed", False
822
871
  )
823
-
872
+
824
873
  # Check for MEMORIES field and process if present
825
- if "MEMORIES" in structured_response and structured_response["MEMORIES"]:
874
+ if structured_response.get("MEMORIES"):
826
875
  memories = structured_response["MEMORIES"]
827
876
  if DEBUG:
828
877
  print(
@@ -892,11 +941,13 @@ class ClaudeHookHandler:
892
941
  "files_modified": structured_response.get("files_modified", []),
893
942
  "tools_used": structured_response.get("tools_used", []),
894
943
  "remember": structured_response.get("remember"),
895
- "MEMORIES": structured_response.get("MEMORIES"), # Complete memory replacement
944
+ "MEMORIES": structured_response.get(
945
+ "MEMORIES"
946
+ ), # Complete memory replacement
896
947
  }
897
-
948
+
898
949
  # Log if MEMORIES field is present
899
- if "MEMORIES" in structured_response and structured_response["MEMORIES"]:
950
+ if structured_response.get("MEMORIES"):
900
951
  if DEBUG:
901
952
  memories_count = len(structured_response["MEMORIES"])
902
953
  print(
@@ -916,8 +967,12 @@ class ClaudeHookHandler:
916
967
 
917
968
  def __del__(self):
918
969
  """Cleanup on handler destruction."""
919
- # Connection pool no longer used - EventBus handles cleanup
920
- pass
970
+ # Clean up connection pool if it exists
971
+ if hasattr(self, "connection_pool") and self.connection_pool:
972
+ try:
973
+ self.connection_pool.cleanup()
974
+ except:
975
+ pass # Ignore cleanup errors during destruction
921
976
 
922
977
 
923
978
  def main():
@@ -954,12 +1009,11 @@ def main():
954
1009
  f"✅ Created new ClaudeHookHandler singleton (pid: {os.getpid()})",
955
1010
  file=sys.stderr,
956
1011
  )
957
- else:
958
- if DEBUG:
959
- print(
960
- f"♻️ Reusing existing ClaudeHookHandler singleton (pid: {os.getpid()})",
961
- file=sys.stderr,
962
- )
1012
+ elif DEBUG:
1013
+ print(
1014
+ f"♻️ Reusing existing ClaudeHookHandler singleton (pid: {os.getpid()})",
1015
+ file=sys.stderr,
1016
+ )
963
1017
 
964
1018
  handler = _global_handler
965
1019