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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +2 -2
  3. claude_mpm/__main__.py +3 -2
  4. claude_mpm/agents/__init__.py +85 -79
  5. claude_mpm/agents/agent_loader.py +464 -1003
  6. claude_mpm/agents/agent_loader_integration.py +45 -45
  7. claude_mpm/agents/agents_metadata.py +29 -30
  8. claude_mpm/agents/async_agent_loader.py +156 -138
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/base_agent_loader.py +179 -151
  11. claude_mpm/agents/frontmatter_validator.py +229 -130
  12. claude_mpm/agents/schema/agent_schema.json +1 -1
  13. claude_mpm/agents/system_agent_config.py +213 -147
  14. claude_mpm/agents/templates/__init__.py +13 -13
  15. claude_mpm/agents/templates/code_analyzer.json +2 -2
  16. claude_mpm/agents/templates/data_engineer.json +1 -1
  17. claude_mpm/agents/templates/documentation.json +23 -11
  18. claude_mpm/agents/templates/engineer.json +22 -6
  19. claude_mpm/agents/templates/memory_manager.json +1 -1
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/project_organizer.json +1 -1
  22. claude_mpm/agents/templates/qa.json +1 -1
  23. claude_mpm/agents/templates/refactoring_engineer.json +222 -0
  24. claude_mpm/agents/templates/research.json +20 -14
  25. claude_mpm/agents/templates/security.json +1 -1
  26. claude_mpm/agents/templates/ticketing.json +2 -2
  27. claude_mpm/agents/templates/version_control.json +1 -1
  28. claude_mpm/agents/templates/web_qa.json +3 -1
  29. claude_mpm/agents/templates/web_ui.json +2 -2
  30. claude_mpm/cli/__init__.py +79 -51
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +20 -20
  33. claude_mpm/cli/commands/agents.py +279 -247
  34. claude_mpm/cli/commands/aggregate.py +138 -157
  35. claude_mpm/cli/commands/cleanup.py +147 -147
  36. claude_mpm/cli/commands/config.py +93 -76
  37. claude_mpm/cli/commands/info.py +17 -16
  38. claude_mpm/cli/commands/mcp.py +140 -905
  39. claude_mpm/cli/commands/mcp_command_router.py +139 -0
  40. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  41. claude_mpm/cli/commands/mcp_install_commands.py +20 -0
  42. claude_mpm/cli/commands/mcp_server_commands.py +175 -0
  43. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  44. claude_mpm/cli/commands/memory.py +239 -203
  45. claude_mpm/cli/commands/monitor.py +330 -86
  46. claude_mpm/cli/commands/run.py +380 -429
  47. claude_mpm/cli/commands/run_config_checker.py +160 -0
  48. claude_mpm/cli/commands/socketio_monitor.py +235 -0
  49. claude_mpm/cli/commands/tickets.py +363 -220
  50. claude_mpm/cli/parser.py +24 -1156
  51. claude_mpm/cli/parsers/__init__.py +29 -0
  52. claude_mpm/cli/parsers/agents_parser.py +136 -0
  53. claude_mpm/cli/parsers/base_parser.py +331 -0
  54. claude_mpm/cli/parsers/config_parser.py +85 -0
  55. claude_mpm/cli/parsers/mcp_parser.py +152 -0
  56. claude_mpm/cli/parsers/memory_parser.py +138 -0
  57. claude_mpm/cli/parsers/monitor_parser.py +124 -0
  58. claude_mpm/cli/parsers/run_parser.py +147 -0
  59. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  60. claude_mpm/cli/ticket_cli.py +7 -3
  61. claude_mpm/cli/utils.py +55 -37
  62. claude_mpm/cli_module/__init__.py +6 -6
  63. claude_mpm/cli_module/args.py +188 -140
  64. claude_mpm/cli_module/commands.py +79 -70
  65. claude_mpm/cli_module/migration_example.py +38 -60
  66. claude_mpm/config/__init__.py +32 -25
  67. claude_mpm/config/agent_config.py +151 -119
  68. claude_mpm/config/experimental_features.py +71 -73
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +35 -18
  72. claude_mpm/core/__init__.py +9 -6
  73. claude_mpm/core/agent_name_normalizer.py +68 -71
  74. claude_mpm/core/agent_registry.py +372 -521
  75. claude_mpm/core/agent_session_manager.py +74 -63
  76. claude_mpm/core/base_service.py +116 -87
  77. claude_mpm/core/cache.py +119 -153
  78. claude_mpm/core/claude_runner.py +425 -1120
  79. claude_mpm/core/config.py +263 -168
  80. claude_mpm/core/config_aliases.py +69 -61
  81. claude_mpm/core/config_constants.py +292 -0
  82. claude_mpm/core/constants.py +57 -99
  83. claude_mpm/core/container.py +211 -178
  84. claude_mpm/core/exceptions.py +233 -89
  85. claude_mpm/core/factories.py +92 -54
  86. claude_mpm/core/framework_loader.py +378 -220
  87. claude_mpm/core/hook_manager.py +198 -83
  88. claude_mpm/core/hook_performance_config.py +136 -0
  89. claude_mpm/core/injectable_service.py +61 -55
  90. claude_mpm/core/interactive_session.py +165 -155
  91. claude_mpm/core/interfaces.py +221 -195
  92. claude_mpm/core/lazy.py +96 -96
  93. claude_mpm/core/logger.py +133 -107
  94. claude_mpm/core/logging_config.py +185 -157
  95. claude_mpm/core/minimal_framework_loader.py +20 -15
  96. claude_mpm/core/mixins.py +30 -29
  97. claude_mpm/core/oneshot_session.py +215 -181
  98. claude_mpm/core/optimized_agent_loader.py +134 -138
  99. claude_mpm/core/optimized_startup.py +159 -157
  100. claude_mpm/core/pm_hook_interceptor.py +85 -72
  101. claude_mpm/core/service_registry.py +103 -101
  102. claude_mpm/core/session_manager.py +97 -87
  103. claude_mpm/core/socketio_pool.py +212 -158
  104. claude_mpm/core/tool_access_control.py +58 -51
  105. claude_mpm/core/types.py +46 -24
  106. claude_mpm/core/typing_utils.py +166 -82
  107. claude_mpm/core/unified_agent_registry.py +721 -0
  108. claude_mpm/core/unified_config.py +550 -0
  109. claude_mpm/core/unified_paths.py +549 -0
  110. claude_mpm/dashboard/index.html +1 -1
  111. claude_mpm/dashboard/open_dashboard.py +51 -17
  112. claude_mpm/dashboard/static/built/components/agent-inference.js +2 -0
  113. claude_mpm/dashboard/static/built/components/event-processor.js +2 -0
  114. claude_mpm/dashboard/static/built/components/event-viewer.js +2 -0
  115. claude_mpm/dashboard/static/built/components/export-manager.js +2 -0
  116. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +2 -0
  117. claude_mpm/dashboard/static/built/components/hud-library-loader.js +2 -0
  118. claude_mpm/dashboard/static/built/components/hud-manager.js +2 -0
  119. claude_mpm/dashboard/static/built/components/hud-visualizer.js +2 -0
  120. claude_mpm/dashboard/static/built/components/module-viewer.js +2 -0
  121. claude_mpm/dashboard/static/built/components/session-manager.js +2 -0
  122. claude_mpm/dashboard/static/built/components/socket-manager.js +2 -0
  123. claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -0
  124. claude_mpm/dashboard/static/built/components/working-directory.js +2 -0
  125. claude_mpm/dashboard/static/built/dashboard.js +2 -0
  126. claude_mpm/dashboard/static/built/socket-client.js +2 -0
  127. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  128. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  129. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  130. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  131. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  132. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  133. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  134. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  135. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  136. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  137. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  138. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  139. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  140. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  141. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  142. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  143. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  144. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  145. claude_mpm/dashboard/static/js/components/event-viewer.js +93 -72
  146. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  147. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +110 -96
  148. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  149. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  150. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  151. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  152. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  153. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  154. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  155. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  156. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  157. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  158. claude_mpm/dashboard/static/js/socket-client.js +133 -53
  159. claude_mpm/dashboard/templates/index.html +40 -50
  160. claude_mpm/experimental/cli_enhancements.py +60 -58
  161. claude_mpm/generators/__init__.py +1 -1
  162. claude_mpm/generators/agent_profile_generator.py +75 -65
  163. claude_mpm/hooks/__init__.py +1 -1
  164. claude_mpm/hooks/base_hook.py +33 -28
  165. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  166. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  167. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  168. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  169. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  170. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  171. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  172. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  173. claude_mpm/hooks/memory_integration_hook.py +140 -100
  174. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  175. claude_mpm/hooks/validation_hooks.py +57 -49
  176. claude_mpm/init.py +145 -121
  177. claude_mpm/models/__init__.py +9 -9
  178. claude_mpm/models/agent_definition.py +33 -23
  179. claude_mpm/models/agent_session.py +228 -200
  180. claude_mpm/scripts/__init__.py +1 -1
  181. claude_mpm/scripts/socketio_daemon.py +192 -75
  182. claude_mpm/scripts/socketio_server_manager.py +328 -0
  183. claude_mpm/scripts/start_activity_logging.py +25 -22
  184. claude_mpm/services/__init__.py +68 -43
  185. claude_mpm/services/agent_capabilities_service.py +271 -0
  186. claude_mpm/services/agents/__init__.py +23 -32
  187. claude_mpm/services/agents/deployment/__init__.py +3 -3
  188. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  189. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  190. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  191. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  192. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  193. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  194. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  195. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  196. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  197. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  198. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  199. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  200. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  201. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  202. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  203. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  204. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  205. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  206. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  207. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  208. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  209. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  210. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  211. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  212. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  213. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  214. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  215. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  216. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  217. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  218. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  219. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  220. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  221. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  222. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  223. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  224. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  225. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  226. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  227. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  228. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  229. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  230. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  231. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  232. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  233. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  234. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  235. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  236. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  237. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  238. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  239. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  240. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  241. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  242. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  243. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  244. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  245. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  246. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  247. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  248. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  249. claude_mpm/services/agents/loading/__init__.py +2 -2
  250. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  251. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  252. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  253. claude_mpm/services/agents/management/__init__.py +2 -2
  254. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  255. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  256. claude_mpm/services/agents/memory/__init__.py +9 -6
  257. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  258. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  259. claude_mpm/services/agents/memory/analyzer.py +430 -0
  260. claude_mpm/services/agents/memory/content_manager.py +376 -0
  261. claude_mpm/services/agents/memory/template_generator.py +468 -0
  262. claude_mpm/services/agents/registry/__init__.py +7 -10
  263. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  264. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  265. claude_mpm/services/async_session_logger.py +187 -153
  266. claude_mpm/services/claude_session_logger.py +87 -72
  267. claude_mpm/services/command_handler_service.py +217 -0
  268. claude_mpm/services/communication/__init__.py +3 -2
  269. claude_mpm/services/core/__init__.py +50 -97
  270. claude_mpm/services/core/base.py +60 -53
  271. claude_mpm/services/core/interfaces/__init__.py +188 -0
  272. claude_mpm/services/core/interfaces/agent.py +351 -0
  273. claude_mpm/services/core/interfaces/communication.py +343 -0
  274. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  275. claude_mpm/services/core/interfaces/service.py +434 -0
  276. claude_mpm/services/core/interfaces.py +19 -944
  277. claude_mpm/services/event_aggregator.py +208 -170
  278. claude_mpm/services/exceptions.py +387 -308
  279. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  280. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  281. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  282. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  283. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  284. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  285. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  286. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  287. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  288. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  289. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  290. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  291. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  292. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  293. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  294. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  295. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  296. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  297. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  298. claude_mpm/services/hook_service.py +106 -114
  299. claude_mpm/services/infrastructure/__init__.py +7 -5
  300. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  301. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  302. claude_mpm/services/infrastructure/logging.py +83 -76
  303. claude_mpm/services/infrastructure/monitoring.py +547 -404
  304. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  305. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  306. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  307. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  308. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  309. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  310. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  311. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  312. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  313. claude_mpm/services/mcp_gateway/main.py +287 -137
  314. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  315. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  316. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  317. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  318. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  319. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  320. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  321. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  322. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  323. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  324. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  325. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  326. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  327. claude_mpm/services/memory/__init__.py +2 -2
  328. claude_mpm/services/memory/builder.py +451 -362
  329. claude_mpm/services/memory/cache/__init__.py +2 -2
  330. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  331. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  332. claude_mpm/services/memory/indexed_memory.py +195 -193
  333. claude_mpm/services/memory/optimizer.py +267 -234
  334. claude_mpm/services/memory/router.py +571 -263
  335. claude_mpm/services/memory_hook_service.py +237 -0
  336. claude_mpm/services/port_manager.py +575 -0
  337. claude_mpm/services/project/__init__.py +3 -3
  338. claude_mpm/services/project/analyzer.py +451 -305
  339. claude_mpm/services/project/registry.py +262 -240
  340. claude_mpm/services/recovery_manager.py +287 -231
  341. claude_mpm/services/response_tracker.py +87 -67
  342. claude_mpm/services/runner_configuration_service.py +587 -0
  343. claude_mpm/services/session_management_service.py +304 -0
  344. claude_mpm/services/socketio/__init__.py +4 -4
  345. claude_mpm/services/socketio/client_proxy.py +174 -0
  346. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  347. claude_mpm/services/socketio/handlers/base.py +44 -30
  348. claude_mpm/services/socketio/handlers/connection.py +166 -64
  349. claude_mpm/services/socketio/handlers/file.py +123 -108
  350. claude_mpm/services/socketio/handlers/git.py +607 -373
  351. claude_mpm/services/socketio/handlers/hook.py +185 -0
  352. claude_mpm/services/socketio/handlers/memory.py +4 -4
  353. claude_mpm/services/socketio/handlers/project.py +4 -4
  354. claude_mpm/services/socketio/handlers/registry.py +53 -38
  355. claude_mpm/services/socketio/server/__init__.py +18 -0
  356. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  357. claude_mpm/services/socketio/server/core.py +399 -0
  358. claude_mpm/services/socketio/server/main.py +323 -0
  359. claude_mpm/services/socketio_client_manager.py +160 -133
  360. claude_mpm/services/socketio_server.py +36 -1885
  361. claude_mpm/services/subprocess_launcher_service.py +316 -0
  362. claude_mpm/services/system_instructions_service.py +258 -0
  363. claude_mpm/services/ticket_manager.py +19 -533
  364. claude_mpm/services/utility_service.py +285 -0
  365. claude_mpm/services/version_control/__init__.py +18 -21
  366. claude_mpm/services/version_control/branch_strategy.py +20 -10
  367. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  368. claude_mpm/services/version_control/git_operations.py +52 -21
  369. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  370. claude_mpm/services/version_control/version_parser.py +145 -125
  371. claude_mpm/services/version_service.py +270 -0
  372. claude_mpm/storage/__init__.py +2 -2
  373. claude_mpm/storage/state_storage.py +177 -181
  374. claude_mpm/ticket_wrapper.py +2 -2
  375. claude_mpm/utils/__init__.py +2 -2
  376. claude_mpm/utils/agent_dependency_loader.py +453 -243
  377. claude_mpm/utils/config_manager.py +157 -118
  378. claude_mpm/utils/console.py +1 -1
  379. claude_mpm/utils/dependency_cache.py +102 -107
  380. claude_mpm/utils/dependency_manager.py +52 -47
  381. claude_mpm/utils/dependency_strategies.py +131 -96
  382. claude_mpm/utils/environment_context.py +110 -102
  383. claude_mpm/utils/error_handler.py +75 -55
  384. claude_mpm/utils/file_utils.py +80 -67
  385. claude_mpm/utils/framework_detection.py +12 -11
  386. claude_mpm/utils/import_migration_example.py +12 -60
  387. claude_mpm/utils/imports.py +48 -45
  388. claude_mpm/utils/path_operations.py +100 -93
  389. claude_mpm/utils/robust_installer.py +172 -164
  390. claude_mpm/utils/session_logging.py +30 -23
  391. claude_mpm/utils/subprocess_utils.py +99 -61
  392. claude_mpm/validation/__init__.py +1 -1
  393. claude_mpm/validation/agent_validator.py +151 -111
  394. claude_mpm/validation/frontmatter_validator.py +92 -71
  395. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/METADATA +90 -22
  396. claude_mpm-4.0.4.dist-info/RECORD +417 -0
  397. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/entry_points.txt +1 -0
  398. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/licenses/LICENSE +1 -1
  399. claude_mpm/cli/commands/run_guarded.py +0 -511
  400. claude_mpm/config/memory_guardian_config.py +0 -325
  401. claude_mpm/config/memory_guardian_yaml.py +0 -335
  402. claude_mpm/core/config_paths.py +0 -150
  403. claude_mpm/core/memory_aware_runner.py +0 -353
  404. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  405. claude_mpm/deployment_paths.py +0 -261
  406. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  407. claude_mpm/models/state_models.py +0 -433
  408. claude_mpm/services/agent/__init__.py +0 -24
  409. claude_mpm/services/agent/deployment.py +0 -2548
  410. claude_mpm/services/agent/management.py +0 -598
  411. claude_mpm/services/agent/registry.py +0 -813
  412. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  413. claude_mpm/services/communication/socketio.py +0 -1935
  414. claude_mpm/services/communication/websocket.py +0 -479
  415. claude_mpm/services/framework_claude_md_generator.py +0 -624
  416. claude_mpm/services/health_monitor.py +0 -893
  417. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  418. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  419. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  420. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  421. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  422. claude_mpm/services/infrastructure/state_manager.py +0 -774
  423. claude_mpm/services/mcp_gateway/manager.py +0 -334
  424. claude_mpm/services/optimized_hook_service.py +0 -542
  425. claude_mpm/services/project_analyzer.py +0 -864
  426. claude_mpm/services/project_registry.py +0 -608
  427. claude_mpm/services/standalone_socketio_server.py +0 -1300
  428. claude_mpm/services/ticket_manager_di.py +0 -318
  429. claude_mpm/services/ticketing_service_original.py +0 -510
  430. claude_mpm/utils/paths.py +0 -395
  431. claude_mpm/utils/platform_memory.py +0 -524
  432. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  433. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/WHEEL +0 -0
  434. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,743 @@
1
+ #!/usr/bin/env python3
2
+ """Event handlers for Claude Code hook handler.
3
+
4
+ This module provides individual event handlers for different types of
5
+ Claude Code hook events.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import re
11
+ import subprocess
12
+ import sys
13
+ from datetime import datetime
14
+ from typing import Any, Dict, Optional
15
+
16
+ from .tool_analysis import (
17
+ assess_security_risk,
18
+ calculate_duration,
19
+ classify_tool_operation,
20
+ extract_tool_parameters,
21
+ extract_tool_results,
22
+ )
23
+
24
+ # Debug mode
25
+ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
26
+
27
+ # Import constants for configuration
28
+ try:
29
+ from claude_mpm.core.constants import TimeoutConfig
30
+ except ImportError:
31
+ # Fallback values if constants module not available
32
+ class TimeoutConfig:
33
+ QUICK_TIMEOUT = 2.0
34
+
35
+
36
+ class EventHandlers:
37
+ """Collection of event handlers for different Claude Code hook events."""
38
+
39
+ def __init__(self, hook_handler):
40
+ """Initialize with reference to the main hook handler."""
41
+ self.hook_handler = hook_handler
42
+
43
+ def handle_user_prompt_fast(self, event):
44
+ """Handle user prompt with comprehensive data capture.
45
+
46
+ WHY enhanced data capture:
47
+ - Provides full context for debugging and monitoring
48
+ - Captures prompt text, working directory, and session context
49
+ - Enables better filtering and analysis in dashboard
50
+ """
51
+ prompt = event.get("prompt", "")
52
+
53
+ # Skip /mpm commands to reduce noise unless debug is enabled
54
+ if prompt.startswith("/mpm") and not DEBUG:
55
+ return
56
+
57
+ # Get working directory and git branch
58
+ working_dir = event.get("cwd", "")
59
+ git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
60
+
61
+ # Extract comprehensive prompt data
62
+ prompt_data = {
63
+ "prompt_text": prompt,
64
+ "prompt_preview": prompt[:200] if len(prompt) > 200 else prompt,
65
+ "prompt_length": len(prompt),
66
+ "session_id": event.get("session_id", ""),
67
+ "working_directory": working_dir,
68
+ "git_branch": git_branch,
69
+ "timestamp": datetime.now().isoformat(),
70
+ "is_command": prompt.startswith("/"),
71
+ "contains_code": "```" in prompt
72
+ or "python" in prompt.lower()
73
+ or "javascript" in prompt.lower(),
74
+ "urgency": (
75
+ "high"
76
+ if any(
77
+ word in prompt.lower()
78
+ for word in ["urgent", "error", "bug", "fix", "broken"]
79
+ )
80
+ else "normal"
81
+ ),
82
+ }
83
+
84
+ # Store prompt for comprehensive response tracking if enabled
85
+ if (
86
+ self.hook_handler.response_tracking_manager.response_tracking_enabled
87
+ and self.hook_handler.response_tracking_manager.track_all_interactions
88
+ ):
89
+ session_id = event.get("session_id", "")
90
+ if session_id:
91
+ self.hook_handler.pending_prompts[session_id] = {
92
+ "prompt": prompt,
93
+ "timestamp": datetime.now().isoformat(),
94
+ "working_directory": working_dir,
95
+ }
96
+ if DEBUG:
97
+ print(
98
+ f"Stored prompt for comprehensive tracking: session {session_id[:8]}...",
99
+ file=sys.stderr,
100
+ )
101
+
102
+ # Emit to /hook namespace
103
+ self.hook_handler._emit_socketio_event("/hook", "user_prompt", prompt_data)
104
+
105
+ def handle_pre_tool_fast(self, event):
106
+ """Handle pre-tool use with comprehensive data capture.
107
+
108
+ WHY comprehensive capture:
109
+ - Captures tool parameters for debugging and security analysis
110
+ - Provides context about what Claude is about to do
111
+ - Enables pattern analysis and security monitoring
112
+ """
113
+ # Enhanced debug logging for session correlation
114
+ session_id = event.get("session_id", "")
115
+ if DEBUG:
116
+ print(
117
+ f" - session_id: {session_id[:16] if session_id else 'None'}...",
118
+ file=sys.stderr,
119
+ )
120
+ print(f" - event keys: {list(event.keys())}", file=sys.stderr)
121
+
122
+ tool_name = event.get("tool_name", "")
123
+ tool_input = event.get("tool_input", {})
124
+
125
+ # Extract key parameters based on tool type
126
+ tool_params = extract_tool_parameters(tool_name, tool_input)
127
+
128
+ # Classify tool operation
129
+ operation_type = classify_tool_operation(tool_name, tool_input)
130
+
131
+ # Get working directory and git branch
132
+ working_dir = event.get("cwd", "")
133
+ git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
134
+
135
+ pre_tool_data = {
136
+ "tool_name": tool_name,
137
+ "operation_type": operation_type,
138
+ "tool_parameters": tool_params,
139
+ "session_id": event.get("session_id", ""),
140
+ "working_directory": working_dir,
141
+ "git_branch": git_branch,
142
+ "timestamp": datetime.now().isoformat(),
143
+ "parameter_count": len(tool_input) if isinstance(tool_input, dict) else 0,
144
+ "is_file_operation": tool_name
145
+ in ["Write", "Edit", "MultiEdit", "Read", "LS", "Glob"],
146
+ "is_execution": tool_name in ["Bash", "NotebookEdit"],
147
+ "is_delegation": tool_name == "Task",
148
+ "security_risk": assess_security_risk(tool_name, tool_input),
149
+ }
150
+
151
+ # Add delegation-specific data if this is a Task tool
152
+ if tool_name == "Task" and isinstance(tool_input, dict):
153
+ self._handle_task_delegation(tool_input, pre_tool_data, session_id)
154
+
155
+ self.hook_handler._emit_socketio_event("/hook", "pre_tool", pre_tool_data)
156
+
157
+ def _handle_task_delegation(
158
+ self, tool_input: dict, pre_tool_data: dict, session_id: str
159
+ ):
160
+ """Handle Task delegation specific processing."""
161
+ # Normalize agent type to handle capitalized names like "Research", "Engineer", etc.
162
+ raw_agent_type = tool_input.get("subagent_type", "unknown")
163
+
164
+ # Use AgentNameNormalizer if available, otherwise simple lowercase normalization
165
+ try:
166
+ from claude_mpm.core.agent_name_normalizer import AgentNameNormalizer
167
+
168
+ normalizer = AgentNameNormalizer()
169
+ # Convert to Task format (lowercase with hyphens)
170
+ agent_type = (
171
+ normalizer.to_task_format(raw_agent_type)
172
+ if raw_agent_type != "unknown"
173
+ else "unknown"
174
+ )
175
+ except ImportError:
176
+ # Fallback to simple normalization
177
+ agent_type = (
178
+ raw_agent_type.lower().replace("_", "-")
179
+ if raw_agent_type != "unknown"
180
+ else "unknown"
181
+ )
182
+
183
+ pre_tool_data["delegation_details"] = {
184
+ "agent_type": agent_type,
185
+ "original_agent_type": raw_agent_type, # Keep original for debugging
186
+ "prompt": tool_input.get("prompt", ""),
187
+ "description": tool_input.get("description", ""),
188
+ "task_preview": (
189
+ tool_input.get("prompt", "") or tool_input.get("description", "")
190
+ )[:100],
191
+ }
192
+
193
+ # Track this delegation for SubagentStop correlation and response tracking
194
+ if DEBUG:
195
+ print(
196
+ f" - session_id: {session_id[:16] if session_id else 'None'}...",
197
+ file=sys.stderr,
198
+ )
199
+ print(f" - agent_type: {agent_type}", file=sys.stderr)
200
+ print(f" - raw_agent_type: {raw_agent_type}", file=sys.stderr)
201
+
202
+ if session_id and agent_type != "unknown":
203
+ # Prepare request data for response tracking correlation
204
+ request_data = {
205
+ "prompt": tool_input.get("prompt", ""),
206
+ "description": tool_input.get("description", ""),
207
+ "agent_type": agent_type,
208
+ }
209
+ self.hook_handler._track_delegation(session_id, agent_type, request_data)
210
+
211
+ if DEBUG:
212
+ print(f" - Delegation tracked successfully", file=sys.stderr)
213
+ print(
214
+ f" - Request data keys: {list(request_data.keys())}",
215
+ file=sys.stderr,
216
+ )
217
+ print(
218
+ f" - delegation_requests size: {len(self.hook_handler.delegation_requests)}",
219
+ file=sys.stderr,
220
+ )
221
+
222
+ # Log important delegations for debugging
223
+ if DEBUG or agent_type in ["research", "engineer", "qa", "documentation"]:
224
+ print(
225
+ f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'",
226
+ file=sys.stderr,
227
+ )
228
+
229
+ # Trigger memory pre-delegation hook
230
+ self.hook_handler.memory_hook_manager.trigger_pre_delegation_hook(
231
+ agent_type, tool_input, session_id
232
+ )
233
+
234
+ # Emit a subagent_start event for better tracking
235
+ subagent_start_data = {
236
+ "agent_type": agent_type,
237
+ "agent_id": f"{agent_type}_{session_id}",
238
+ "session_id": session_id,
239
+ "prompt": tool_input.get("prompt", ""),
240
+ "description": tool_input.get("description", ""),
241
+ "timestamp": datetime.now().isoformat(),
242
+ "hook_event_name": "SubagentStart", # For dashboard compatibility
243
+ }
244
+ self.hook_handler._emit_socketio_event(
245
+ "/hook", "subagent_start", subagent_start_data
246
+ )
247
+
248
+ def _get_git_branch(self, working_dir: str = None) -> str:
249
+ """Get git branch for the given directory with caching."""
250
+ # Use current working directory if not specified
251
+ if not working_dir:
252
+ working_dir = os.getcwd()
253
+
254
+ # Check cache first (cache for 30 seconds)
255
+ current_time = datetime.now().timestamp()
256
+ cache_key = working_dir
257
+
258
+ if (
259
+ cache_key in self.hook_handler._git_branch_cache
260
+ and cache_key in self.hook_handler._git_branch_cache_time
261
+ and current_time - self.hook_handler._git_branch_cache_time[cache_key] < 30
262
+ ):
263
+ return self.hook_handler._git_branch_cache[cache_key]
264
+
265
+ # Try to get git branch
266
+ try:
267
+ # Change to the working directory temporarily
268
+ original_cwd = os.getcwd()
269
+ os.chdir(working_dir)
270
+
271
+ # Run git command to get current branch
272
+ result = subprocess.run(
273
+ ["git", "branch", "--show-current"],
274
+ capture_output=True,
275
+ text=True,
276
+ timeout=TimeoutConfig.QUICK_TIMEOUT, # Quick timeout to avoid hanging
277
+ )
278
+
279
+ # Restore original directory
280
+ os.chdir(original_cwd)
281
+
282
+ if result.returncode == 0 and result.stdout.strip():
283
+ branch = result.stdout.strip()
284
+ # Cache the result
285
+ self.hook_handler._git_branch_cache[cache_key] = branch
286
+ self.hook_handler._git_branch_cache_time[cache_key] = current_time
287
+ return branch
288
+ else:
289
+ # Not a git repository or no branch
290
+ self.hook_handler._git_branch_cache[cache_key] = "Unknown"
291
+ self.hook_handler._git_branch_cache_time[cache_key] = current_time
292
+ return "Unknown"
293
+
294
+ except (
295
+ subprocess.TimeoutExpired,
296
+ subprocess.CalledProcessError,
297
+ FileNotFoundError,
298
+ OSError,
299
+ ):
300
+ # Git not available or command failed
301
+ self.hook_handler._git_branch_cache[cache_key] = "Unknown"
302
+ self.hook_handler._git_branch_cache_time[cache_key] = current_time
303
+ return "Unknown"
304
+
305
+ def handle_post_tool_fast(self, event):
306
+ """Handle post-tool use with comprehensive data capture.
307
+
308
+ WHY comprehensive capture:
309
+ - Captures execution results and success/failure status
310
+ - Provides duration and performance metrics
311
+ - Enables pattern analysis of tool usage and success rates
312
+ """
313
+ tool_name = event.get("tool_name", "")
314
+ exit_code = event.get("exit_code", 0)
315
+
316
+ # Extract result data
317
+ result_data = extract_tool_results(event)
318
+
319
+ # Calculate duration if timestamps are available
320
+ duration = calculate_duration(event)
321
+
322
+ # Get working directory and git branch
323
+ working_dir = event.get("cwd", "")
324
+ git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
325
+
326
+ post_tool_data = {
327
+ "tool_name": tool_name,
328
+ "exit_code": exit_code,
329
+ "success": exit_code == 0,
330
+ "status": "success"
331
+ if exit_code == 0
332
+ else "blocked"
333
+ if exit_code == 2
334
+ else "error",
335
+ "duration_ms": duration,
336
+ "result_summary": result_data,
337
+ "session_id": event.get("session_id", ""),
338
+ "working_directory": working_dir,
339
+ "git_branch": git_branch,
340
+ "timestamp": datetime.now().isoformat(),
341
+ "has_output": bool(result_data.get("output")),
342
+ "has_error": bool(result_data.get("error")),
343
+ "output_size": (
344
+ len(str(result_data.get("output", "")))
345
+ if result_data.get("output")
346
+ else 0
347
+ ),
348
+ }
349
+
350
+ # Handle Task delegation completion for memory hooks and response tracking
351
+ if tool_name == "Task":
352
+ session_id = event.get("session_id", "")
353
+ agent_type = self.hook_handler._get_delegation_agent_type(session_id)
354
+
355
+ # Trigger memory post-delegation hook
356
+ self.hook_handler.memory_hook_manager.trigger_post_delegation_hook(
357
+ agent_type, event, session_id
358
+ )
359
+
360
+ # Track agent response if response tracking is enabled
361
+ self.hook_handler.response_tracking_manager.track_agent_response(
362
+ session_id, agent_type, event, self.hook_handler.delegation_requests
363
+ )
364
+
365
+ self.hook_handler._emit_socketio_event("/hook", "post_tool", post_tool_data)
366
+
367
+ def handle_notification_fast(self, event):
368
+ """Handle notification events from Claude.
369
+
370
+ WHY enhanced notification capture:
371
+ - Provides visibility into Claude's status and communication flow
372
+ - Captures notification type, content, and context for monitoring
373
+ - Enables pattern analysis of Claude's notification behavior
374
+ - Useful for debugging communication issues and user experience
375
+ """
376
+ notification_type = event.get("notification_type", "unknown")
377
+ message = event.get("message", "")
378
+
379
+ # Get working directory and git branch
380
+ working_dir = event.get("cwd", "")
381
+ git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
382
+
383
+ notification_data = {
384
+ "notification_type": notification_type,
385
+ "message": message,
386
+ "message_preview": message[:200] if len(message) > 200 else message,
387
+ "message_length": len(message),
388
+ "session_id": event.get("session_id", ""),
389
+ "working_directory": working_dir,
390
+ "git_branch": git_branch,
391
+ "timestamp": datetime.now().isoformat(),
392
+ "is_user_input_request": "input" in message.lower()
393
+ or "waiting" in message.lower(),
394
+ "is_error_notification": "error" in message.lower()
395
+ or "failed" in message.lower(),
396
+ "is_status_update": any(
397
+ word in message.lower()
398
+ for word in ["processing", "analyzing", "working", "thinking"]
399
+ ),
400
+ }
401
+
402
+ # Emit to /hook namespace
403
+ self.hook_handler._emit_socketio_event(
404
+ "/hook", "notification", notification_data
405
+ )
406
+
407
+ def handle_stop_fast(self, event):
408
+ """Handle stop events when Claude processing stops.
409
+
410
+ WHY comprehensive stop capture:
411
+ - Provides visibility into Claude's session lifecycle
412
+ - Captures stop reason and context for analysis
413
+ - Enables tracking of session completion patterns
414
+ - Useful for understanding when and why Claude stops responding
415
+ """
416
+ session_id = event.get("session_id", "")
417
+
418
+ # Extract metadata for this stop event
419
+ metadata = self._extract_stop_metadata(event)
420
+
421
+ # Debug logging
422
+ if DEBUG:
423
+ self._log_stop_event_debug(event, session_id, metadata)
424
+
425
+ # Track response if enabled
426
+ self.hook_handler.response_tracking_manager.track_stop_response(
427
+ event, session_id, metadata, self.hook_handler.pending_prompts
428
+ )
429
+
430
+ # Emit stop event to Socket.IO
431
+ self._emit_stop_event(event, session_id, metadata)
432
+
433
+ def _extract_stop_metadata(self, event: dict) -> dict:
434
+ """Extract metadata from stop event."""
435
+ working_dir = event.get("cwd", "")
436
+ return {
437
+ "timestamp": datetime.now().isoformat(),
438
+ "working_directory": working_dir,
439
+ "git_branch": self._get_git_branch(working_dir)
440
+ if working_dir
441
+ else "Unknown",
442
+ "event_type": "stop",
443
+ "reason": event.get("reason", "unknown"),
444
+ "stop_type": event.get("stop_type", "normal"),
445
+ }
446
+
447
+ def _log_stop_event_debug(
448
+ self, event: dict, session_id: str, metadata: dict
449
+ ) -> None:
450
+ """Log debug information for stop events."""
451
+
452
+ print(
453
+ f" - response_tracking_enabled: {self.hook_handler.response_tracking_manager.response_tracking_enabled}",
454
+ file=sys.stderr,
455
+ )
456
+ print(
457
+ f" - response_tracker exists: {self.hook_handler.response_tracking_manager.response_tracker is not None}",
458
+ file=sys.stderr,
459
+ )
460
+ print(
461
+ f" - session_id: {session_id[:8] if session_id else 'None'}...",
462
+ file=sys.stderr,
463
+ )
464
+ print(f" - reason: {metadata['reason']}", file=sys.stderr)
465
+ print(f" - stop_type: {metadata['stop_type']}", file=sys.stderr)
466
+
467
+ def _emit_stop_event(self, event: dict, session_id: str, metadata: dict) -> None:
468
+ """Emit stop event data to Socket.IO."""
469
+ stop_data = {
470
+ "reason": metadata["reason"],
471
+ "stop_type": metadata["stop_type"],
472
+ "session_id": session_id,
473
+ "working_directory": metadata["working_directory"],
474
+ "git_branch": metadata["git_branch"],
475
+ "timestamp": metadata["timestamp"],
476
+ "is_user_initiated": metadata["reason"]
477
+ in ["user_stop", "user_cancel", "interrupt"],
478
+ "is_error_stop": metadata["reason"] in ["error", "timeout", "failed"],
479
+ "is_completion_stop": metadata["reason"]
480
+ in ["completed", "finished", "done"],
481
+ "has_output": bool(event.get("final_output")),
482
+ }
483
+
484
+ # Emit to /hook namespace
485
+ self.hook_handler._emit_socketio_event("/hook", "stop", stop_data)
486
+
487
+ def handle_subagent_stop_fast(self, event):
488
+ """Handle subagent stop events with improved agent type detection."""
489
+ # Enhanced debug logging for session correlation
490
+ session_id = event.get("session_id", "")
491
+ if DEBUG:
492
+ print(
493
+ f" - session_id: {session_id[:16] if session_id else 'None'}...",
494
+ file=sys.stderr,
495
+ )
496
+ print(f" - event keys: {list(event.keys())}", file=sys.stderr)
497
+ print(
498
+ f" - delegation_requests size: {len(self.hook_handler.delegation_requests)}",
499
+ file=sys.stderr,
500
+ )
501
+
502
+ # First try to get agent type from our tracking
503
+ agent_type = (
504
+ self.hook_handler._get_delegation_agent_type(session_id)
505
+ if session_id
506
+ else "unknown"
507
+ )
508
+
509
+ # Fall back to event data if tracking didn't have it
510
+ if agent_type == "unknown":
511
+ agent_type = event.get("agent_type", event.get("subagent_type", "unknown"))
512
+
513
+ agent_id = event.get("agent_id", event.get("subagent_id", ""))
514
+ reason = event.get("reason", event.get("stop_reason", "unknown"))
515
+
516
+ # Try to infer agent type from other fields if still unknown
517
+ if agent_type == "unknown" and "task" in event:
518
+ task_desc = str(event.get("task", "")).lower()
519
+ if "research" in task_desc:
520
+ agent_type = "research"
521
+ elif "engineer" in task_desc or "code" in task_desc:
522
+ agent_type = "engineer"
523
+ elif "pm" in task_desc or "project" in task_desc:
524
+ agent_type = "pm"
525
+
526
+ # Always log SubagentStop events for debugging
527
+ if DEBUG or agent_type != "unknown":
528
+ print(
529
+ f"Hook handler: Processing SubagentStop - agent: '{agent_type}', session: '{session_id}', reason: '{reason}'",
530
+ file=sys.stderr,
531
+ )
532
+
533
+ # Get working directory and git branch
534
+ working_dir = event.get("cwd", "")
535
+ git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
536
+
537
+ # Try to extract structured response from output if available
538
+ output = event.get("output", "")
539
+ structured_response = None
540
+ if output:
541
+ try:
542
+ json_match = re.search(
543
+ r"```json\s*(\{.*?\})\s*```", str(output), re.DOTALL
544
+ )
545
+ if json_match:
546
+ structured_response = json.loads(json_match.group(1))
547
+ if DEBUG:
548
+ print(
549
+ f"Extracted structured response from {agent_type} agent in SubagentStop",
550
+ file=sys.stderr,
551
+ )
552
+ except (json.JSONDecodeError, AttributeError):
553
+ pass # No structured response, that's okay
554
+
555
+ # Handle response tracking with fuzzy matching
556
+ self._handle_subagent_response_tracking(
557
+ session_id,
558
+ agent_type,
559
+ reason,
560
+ output,
561
+ structured_response,
562
+ working_dir,
563
+ git_branch,
564
+ )
565
+
566
+ # Prepare subagent stop data
567
+ subagent_stop_data = {
568
+ "agent_type": agent_type,
569
+ "agent_id": agent_id,
570
+ "reason": reason,
571
+ "session_id": session_id,
572
+ "working_directory": working_dir,
573
+ "git_branch": git_branch,
574
+ "timestamp": datetime.now().isoformat(),
575
+ "is_successful_completion": reason in ["completed", "finished", "done"],
576
+ "is_error_termination": reason in ["error", "timeout", "failed", "blocked"],
577
+ "is_delegation_related": agent_type
578
+ in ["research", "engineer", "pm", "ops", "qa", "documentation", "security"],
579
+ "has_results": bool(event.get("results") or event.get("output")),
580
+ "duration_context": event.get("duration_ms"),
581
+ "hook_event_name": "SubagentStop", # Explicitly set for dashboard
582
+ }
583
+
584
+ # Add structured response data if available
585
+ if structured_response:
586
+ subagent_stop_data["structured_response"] = {
587
+ "task_completed": structured_response.get("task_completed", False),
588
+ "instructions": structured_response.get("instructions", ""),
589
+ "results": structured_response.get("results", ""),
590
+ "files_modified": structured_response.get("files_modified", []),
591
+ "tools_used": structured_response.get("tools_used", []),
592
+ "remember": structured_response.get("remember"),
593
+ }
594
+
595
+ # Debug log the processed data
596
+ if DEBUG:
597
+ print(
598
+ f"SubagentStop processed data: agent_type='{agent_type}', session_id='{session_id}'",
599
+ file=sys.stderr,
600
+ )
601
+
602
+ # Emit to /hook namespace with high priority
603
+ self.hook_handler._emit_socketio_event(
604
+ "/hook", "subagent_stop", subagent_stop_data
605
+ )
606
+
607
+ def _handle_subagent_response_tracking(
608
+ self,
609
+ session_id: str,
610
+ agent_type: str,
611
+ reason: str,
612
+ output: str,
613
+ structured_response: dict,
614
+ working_dir: str,
615
+ git_branch: str,
616
+ ):
617
+ """Handle response tracking for subagent stop events with fuzzy matching."""
618
+ if not (
619
+ self.hook_handler.response_tracking_manager.response_tracking_enabled
620
+ and self.hook_handler.response_tracking_manager.response_tracker
621
+ ):
622
+ return
623
+
624
+ try:
625
+ # Get the original request data (with fuzzy matching fallback)
626
+ request_info = self.hook_handler.delegation_requests.get(session_id)
627
+
628
+ # If exact match fails, try partial matching
629
+ if not request_info and session_id:
630
+ if DEBUG:
631
+ print(
632
+ f" - Trying fuzzy match for session {session_id[:16]}...",
633
+ file=sys.stderr,
634
+ )
635
+ # Try to find a session that matches the first 8-16 characters
636
+ for stored_sid in list(self.hook_handler.delegation_requests.keys()):
637
+ if (
638
+ stored_sid.startswith(session_id[:8])
639
+ or session_id.startswith(stored_sid[:8])
640
+ or (
641
+ len(session_id) >= 16
642
+ and len(stored_sid) >= 16
643
+ and stored_sid[:16] == session_id[:16]
644
+ )
645
+ ):
646
+ if DEBUG:
647
+ print(
648
+ f" - ✅ Fuzzy match found: {stored_sid[:16]}...",
649
+ file=sys.stderr,
650
+ )
651
+ request_info = self.hook_handler.delegation_requests.get(
652
+ stored_sid
653
+ )
654
+ # Update the key to use the current session_id for consistency
655
+ if request_info:
656
+ self.hook_handler.delegation_requests[
657
+ session_id
658
+ ] = request_info
659
+ # Optionally remove the old key to avoid duplicates
660
+ if stored_sid != session_id:
661
+ del self.hook_handler.delegation_requests[stored_sid]
662
+ break
663
+
664
+ if request_info:
665
+ # Use the output as the response
666
+ response_text = (
667
+ str(output)
668
+ if output
669
+ else f"Agent {agent_type} completed with reason: {reason}"
670
+ )
671
+
672
+ # Get the original request
673
+ original_request = request_info.get("request", {})
674
+ prompt = original_request.get("prompt", "")
675
+ description = original_request.get("description", "")
676
+
677
+ # Combine prompt and description
678
+ full_request = prompt
679
+ if description and description != prompt:
680
+ if full_request:
681
+ full_request += f"\n\nDescription: {description}"
682
+ else:
683
+ full_request = description
684
+
685
+ if not full_request:
686
+ full_request = f"Task delegation to {agent_type} agent"
687
+
688
+ # Prepare metadata
689
+ metadata = {
690
+ "exit_code": 0, # SubagentStop doesn't have exit_code
691
+ "success": reason in ["completed", "finished", "done"],
692
+ "has_error": reason in ["error", "timeout", "failed", "blocked"],
693
+ "working_directory": working_dir,
694
+ "git_branch": git_branch,
695
+ "timestamp": datetime.now().isoformat(),
696
+ "event_type": "subagent_stop",
697
+ "reason": reason,
698
+ "original_request_timestamp": request_info.get("timestamp"),
699
+ }
700
+
701
+ # Add structured response if available
702
+ if structured_response:
703
+ metadata["structured_response"] = structured_response
704
+ metadata["task_completed"] = structured_response.get(
705
+ "task_completed", False
706
+ )
707
+
708
+ # Track the response
709
+ file_path = self.hook_handler.response_tracking_manager.response_tracker.track_response(
710
+ agent_name=agent_type,
711
+ request=full_request,
712
+ response=response_text,
713
+ session_id=session_id,
714
+ metadata=metadata,
715
+ )
716
+
717
+ if file_path and DEBUG:
718
+ print(
719
+ f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}",
720
+ file=sys.stderr,
721
+ )
722
+
723
+ # Clean up the request data
724
+ if session_id in self.hook_handler.delegation_requests:
725
+ del self.hook_handler.delegation_requests[session_id]
726
+
727
+ elif DEBUG:
728
+ print(
729
+ f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}",
730
+ file=sys.stderr,
731
+ )
732
+
733
+ except Exception as e:
734
+ if DEBUG:
735
+ print(
736
+ f"❌ Failed to track response on SubagentStop: {e}", file=sys.stderr
737
+ )
738
+
739
+ def handle_assistant_response(self, event):
740
+ """Handle assistant response events for comprehensive response tracking."""
741
+ self.hook_handler.response_tracking_manager.track_assistant_response(
742
+ event, self.hook_handler.pending_prompts
743
+ )