claude-mpm 5.0.9__py3-none-any.whl → 5.4.41__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (263) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT.md +164 -0
  4. claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +468 -468
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +13 -44
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/frontmatter_validator.py +70 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  12. claude_mpm/cli/__init__.py +0 -1
  13. claude_mpm/cli/__main__.py +4 -0
  14. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  15. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  16. claude_mpm/cli/commands/agents.py +175 -37
  17. claude_mpm/cli/commands/auto_configure.py +723 -236
  18. claude_mpm/cli/commands/config.py +88 -2
  19. claude_mpm/cli/commands/configure.py +1262 -157
  20. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  21. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  22. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  23. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  24. claude_mpm/cli/commands/postmortem.py +1 -1
  25. claude_mpm/cli/commands/profile.py +277 -0
  26. claude_mpm/cli/commands/skills.py +214 -189
  27. claude_mpm/cli/commands/summarize.py +413 -0
  28. claude_mpm/cli/executor.py +21 -3
  29. claude_mpm/cli/interactive/agent_wizard.py +85 -10
  30. claude_mpm/cli/parsers/agents_parser.py +54 -9
  31. claude_mpm/cli/parsers/auto_configure_parser.py +13 -138
  32. claude_mpm/cli/parsers/base_parser.py +12 -0
  33. claude_mpm/cli/parsers/config_parser.py +153 -83
  34. claude_mpm/cli/parsers/profile_parser.py +148 -0
  35. claude_mpm/cli/parsers/skills_parser.py +3 -2
  36. claude_mpm/cli/startup.py +879 -149
  37. claude_mpm/commands/mpm-config.md +28 -0
  38. claude_mpm/commands/mpm-doctor.md +9 -22
  39. claude_mpm/commands/mpm-help.md +5 -287
  40. claude_mpm/commands/mpm-init.md +81 -507
  41. claude_mpm/commands/mpm-monitor.md +15 -402
  42. claude_mpm/commands/mpm-organize.md +120 -0
  43. claude_mpm/commands/mpm-postmortem.md +6 -108
  44. claude_mpm/commands/mpm-session-resume.md +12 -363
  45. claude_mpm/commands/mpm-status.md +5 -69
  46. claude_mpm/commands/mpm-ticket-view.md +52 -495
  47. claude_mpm/commands/mpm-version.md +5 -107
  48. claude_mpm/config/agent_sources.py +27 -0
  49. claude_mpm/core/config.py +2 -4
  50. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  51. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  52. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  53. claude_mpm/core/framework_loader.py +4 -2
  54. claude_mpm/core/logger.py +13 -0
  55. claude_mpm/core/optimized_startup.py +59 -0
  56. claude_mpm/core/output_style_manager.py +173 -43
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +3 -3
  59. claude_mpm/core/unified_agent_registry.py +134 -16
  60. claude_mpm/core/unified_config.py +22 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  74. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  75. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  76. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  77. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  78. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  88. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  89. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  90. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  91. claude_mpm/hooks/claude_hooks/memory_integration.py +28 -0
  92. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  97. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  98. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  99. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  100. claude_mpm/hooks/memory_integration_hook.py +46 -1
  101. claude_mpm/init.py +63 -19
  102. claude_mpm/models/agent_definition.py +7 -0
  103. claude_mpm/models/git_repository.py +3 -3
  104. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  105. claude_mpm/scripts/launch_monitor.py +93 -13
  106. claude_mpm/scripts/start_activity_logging.py +0 -0
  107. claude_mpm/services/agents/agent_builder.py +3 -3
  108. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  109. claude_mpm/services/agents/agent_review_service.py +280 -0
  110. claude_mpm/services/agents/cache_git_manager.py +6 -6
  111. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  112. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  113. claude_mpm/services/agents/deployment/agent_template_builder.py +5 -3
  114. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  115. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +320 -29
  116. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +546 -68
  117. claude_mpm/services/agents/git_source_manager.py +36 -2
  118. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  119. claude_mpm/services/agents/recommender.py +5 -3
  120. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  121. claude_mpm/services/agents/sources/git_source_sync_service.py +13 -6
  122. claude_mpm/services/agents/startup_sync.py +22 -2
  123. claude_mpm/services/agents/toolchain_detector.py +10 -6
  124. claude_mpm/services/analysis/__init__.py +11 -1
  125. claude_mpm/services/analysis/clone_detector.py +1030 -0
  126. claude_mpm/services/command_deployment_service.py +81 -10
  127. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  128. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  129. claude_mpm/services/event_bus/config.py +3 -1
  130. claude_mpm/services/git/git_operations_service.py +101 -16
  131. claude_mpm/services/monitor/daemon.py +9 -2
  132. claude_mpm/services/monitor/daemon_manager.py +39 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  134. claude_mpm/services/monitor/server.py +698 -22
  135. claude_mpm/services/pm_skills_deployer.py +676 -0
  136. claude_mpm/services/profile_manager.py +331 -0
  137. claude_mpm/services/project/project_organizer.py +4 -0
  138. claude_mpm/services/self_upgrade_service.py +120 -12
  139. claude_mpm/services/skills/__init__.py +3 -0
  140. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  141. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  142. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  143. claude_mpm/services/skills_deployer.py +126 -9
  144. claude_mpm/services/socketio/dashboard_server.py +1 -0
  145. claude_mpm/services/socketio/event_normalizer.py +51 -6
  146. claude_mpm/services/socketio/server/core.py +386 -108
  147. claude_mpm/services/version_control/git_operations.py +103 -0
  148. claude_mpm/skills/skill_manager.py +92 -3
  149. claude_mpm/utils/agent_dependency_loader.py +14 -2
  150. claude_mpm/utils/agent_filters.py +17 -44
  151. claude_mpm/utils/gitignore.py +3 -0
  152. claude_mpm/utils/migration.py +4 -4
  153. claude_mpm/utils/robust_installer.py +47 -3
  154. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/METADATA +57 -87
  155. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/RECORD +160 -211
  156. claude_mpm-5.4.41.dist-info/entry_points.txt +5 -0
  157. claude_mpm-5.4.41.dist-info/licenses/LICENSE +94 -0
  158. claude_mpm-5.4.41.dist-info/licenses/LICENSE-FAQ.md +153 -0
  159. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  160. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  161. claude_mpm/agents/BASE_OPS.md +0 -219
  162. claude_mpm/agents/BASE_PM.md +0 -480
  163. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  164. claude_mpm/agents/BASE_QA.md +0 -167
  165. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  166. claude_mpm/agents/base_agent_loader.py +0 -601
  167. claude_mpm/cli/commands/agents_detect.py +0 -380
  168. claude_mpm/cli/commands/agents_recommend.py +0 -309
  169. claude_mpm/cli/ticket_cli.py +0 -35
  170. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  171. claude_mpm/commands/mpm-agents-detect.md +0 -177
  172. claude_mpm/commands/mpm-agents-list.md +0 -131
  173. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  174. claude_mpm/commands/mpm-config-view.md +0 -150
  175. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  176. claude_mpm/dashboard/analysis_runner.py +0 -455
  177. claude_mpm/dashboard/index.html +0 -13
  178. claude_mpm/dashboard/open_dashboard.py +0 -66
  179. claude_mpm/dashboard/static/css/activity.css +0 -1958
  180. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  181. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  182. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  183. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  184. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  185. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  186. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  187. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  188. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  189. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  190. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  191. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  192. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  193. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  194. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  195. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  196. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  197. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  198. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  199. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  200. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  201. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  202. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  203. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  204. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  205. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  206. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  207. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  208. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  209. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  210. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  211. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  212. claude_mpm/dashboard/templates/code_simple.html +0 -153
  213. claude_mpm/dashboard/templates/index.html +0 -606
  214. claude_mpm/dashboard/test_dashboard.html +0 -372
  215. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  216. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  217. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  218. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  219. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  226. claude_mpm/scripts/mcp_server.py +0 -75
  227. claude_mpm/scripts/mcp_wrapper.py +0 -39
  228. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  229. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  230. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  231. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  232. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  233. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  234. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  235. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  236. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  237. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  238. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  239. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  240. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  241. claude_mpm/services/mcp_gateway/main.py +0 -589
  242. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  243. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  244. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  245. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  246. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  247. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  248. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  249. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  250. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  251. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  252. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  253. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  254. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  255. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  256. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  257. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  258. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  259. claude_mpm-5.0.9.dist-info/entry_points.txt +0 -10
  260. claude_mpm-5.0.9.dist-info/licenses/LICENSE +0 -21
  261. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  262. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/WHEEL +0 -0
  263. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/top_level.txt +0 -0
@@ -304,6 +304,10 @@ class ClaudeHookHandler:
304
304
  # Perform periodic cleanup if needed
305
305
  if self.state_manager.increment_events_processed():
306
306
  self.state_manager.cleanup_old_entries()
307
+ # Also cleanup old correlation files
308
+ from .correlation_manager import CorrelationManager
309
+
310
+ CorrelationManager.cleanup_old()
307
311
  if DEBUG:
308
312
  print(
309
313
  f"🧹 Performed cleanup after {self.state_manager.events_processed} events",
@@ -390,6 +394,8 @@ class ClaudeHookHandler:
390
394
  Returns:
391
395
  Modified input for PreToolUse events (v2.0.30+), None otherwise
392
396
  """
397
+ import time
398
+
393
399
  # Try multiple field names for compatibility
394
400
  hook_type = (
395
401
  event.get("hook_event_name")
@@ -413,21 +419,48 @@ class ClaudeHookHandler:
413
419
  "Notification": self.event_handlers.handle_notification_fast,
414
420
  "Stop": self.event_handlers.handle_stop_fast,
415
421
  "SubagentStop": self.event_handlers.handle_subagent_stop_fast,
422
+ "SubagentStart": self.event_handlers.handle_session_start_fast,
423
+ "SessionStart": self.event_handlers.handle_session_start_fast,
416
424
  "AssistantResponse": self.event_handlers.handle_assistant_response,
417
425
  }
418
426
 
419
427
  # Call appropriate handler if exists
420
428
  handler = event_handlers.get(hook_type)
421
429
  if handler:
430
+ # Track execution timing for hook emission
431
+ start_time = time.time()
432
+ success = False
433
+ error_message = None
434
+ result = None
435
+
422
436
  try:
423
437
  # Handlers can optionally return modified input
424
438
  result = handler(event)
439
+ success = True
425
440
  # Only PreToolUse handlers should return modified input
426
441
  if hook_type == "PreToolUse" and result is not None:
427
- return result
442
+ return_value = result
443
+ else:
444
+ return_value = None
428
445
  except Exception as e:
446
+ error_message = str(e)
447
+ return_value = None
429
448
  if DEBUG:
430
449
  print(f"Error handling {hook_type}: {e}", file=sys.stderr)
450
+ finally:
451
+ # Calculate duration
452
+ duration_ms = int((time.time() - start_time) * 1000)
453
+
454
+ # Emit hook execution event
455
+ self._emit_hook_execution_event(
456
+ hook_type=hook_type,
457
+ event=event,
458
+ success=success,
459
+ duration_ms=duration_ms,
460
+ error_message=error_message,
461
+ )
462
+
463
+ return return_value
431
464
 
432
465
  return None
433
466
 
@@ -472,6 +505,127 @@ class ClaudeHookHandler:
472
505
  """Generate event key through duplicate detector (backward compatibility)."""
473
506
  return self.duplicate_detector.generate_event_key(event)
474
507
 
508
+ def _emit_hook_execution_event(
509
+ self,
510
+ hook_type: str,
511
+ event: dict,
512
+ success: bool,
513
+ duration_ms: int,
514
+ error_message: Optional[str] = None,
515
+ ):
516
+ """Emit a structured JSON event for hook execution.
517
+
518
+ This emits a normalized event following the claude_event schema to provide
519
+ visibility into hook processing, timing, and success/failure status.
520
+
521
+ Args:
522
+ hook_type: The type of hook that executed (e.g., "UserPromptSubmit", "PreToolUse")
523
+ event: The original hook event data
524
+ success: Whether the hook executed successfully
525
+ duration_ms: How long the hook took to execute in milliseconds
526
+ error_message: Optional error message if the hook failed
527
+ """
528
+ # Generate a human-readable summary based on hook type
529
+ summary = self._generate_hook_summary(hook_type, event, success)
530
+
531
+ # Extract common fields
532
+ session_id = event.get("session_id", "")
533
+ working_dir = event.get("cwd", "")
534
+
535
+ # Build hook execution data
536
+ hook_data = {
537
+ "hook_name": hook_type,
538
+ "hook_type": hook_type,
539
+ "session_id": session_id,
540
+ "working_directory": working_dir,
541
+ "success": success,
542
+ "duration_ms": duration_ms,
543
+ "result_summary": summary,
544
+ "timestamp": datetime.now(timezone.utc).isoformat(),
545
+ }
546
+
547
+ # Add error information if present
548
+ if error_message:
549
+ hook_data["error_message"] = error_message
550
+
551
+ # Add hook-specific context
552
+ if hook_type == "PreToolUse":
553
+ hook_data["tool_name"] = event.get("tool_name", "")
554
+ elif hook_type == "PostToolUse":
555
+ hook_data["tool_name"] = event.get("tool_name", "")
556
+ hook_data["exit_code"] = event.get("exit_code", 0)
557
+ elif hook_type == "UserPromptSubmit":
558
+ prompt = event.get("prompt", "")
559
+ hook_data["prompt_preview"] = prompt[:100] if len(prompt) > 100 else prompt
560
+ hook_data["prompt_length"] = len(prompt)
561
+ elif hook_type == "SubagentStop":
562
+ hook_data["agent_type"] = event.get("agent_type", "unknown")
563
+ hook_data["reason"] = event.get("reason", "unknown")
564
+
565
+ # Emit through connection manager with proper structure
566
+ # This uses the existing event infrastructure
567
+ self._emit_socketio_event("", "hook_execution", hook_data)
568
+
569
+ if DEBUG:
570
+ print(
571
+ f"📊 Hook execution event: {hook_type} - {duration_ms}ms - {'✅' if success else '❌'}",
572
+ file=sys.stderr,
573
+ )
574
+
575
+ def _generate_hook_summary(self, hook_type: str, event: dict, success: bool) -> str:
576
+ """Generate a human-readable summary of what the hook did.
577
+
578
+ Args:
579
+ hook_type: The type of hook
580
+ event: The hook event data
581
+ success: Whether the hook executed successfully
582
+
583
+ Returns:
584
+ A brief description of what happened
585
+ """
586
+ if not success:
587
+ return f"Hook {hook_type} failed during processing"
588
+
589
+ # Generate hook-specific summaries
590
+ if hook_type == "UserPromptSubmit":
591
+ prompt = event.get("prompt", "")
592
+ if prompt.startswith("/"):
593
+ return f"Processed command: {prompt.split()[0]}"
594
+ return f"Processed user prompt ({len(prompt)} chars)"
595
+
596
+ if hook_type == "PreToolUse":
597
+ tool_name = event.get("tool_name", "unknown")
598
+ return f"Pre-processing tool call: {tool_name}"
599
+
600
+ if hook_type == "PostToolUse":
601
+ tool_name = event.get("tool_name", "unknown")
602
+ exit_code = event.get("exit_code", 0)
603
+ status = "success" if exit_code == 0 else "failed"
604
+ return f"Completed tool call: {tool_name} ({status})"
605
+
606
+ if hook_type == "SubagentStop":
607
+ agent_type = event.get("agent_type", "unknown")
608
+ reason = event.get("reason", "unknown")
609
+ return f"Subagent {agent_type} stopped: {reason}"
610
+
611
+ if hook_type == "SessionStart":
612
+ return "New session started"
613
+
614
+ if hook_type == "Stop":
615
+ reason = event.get("reason", "unknown")
616
+ return f"Session stopped: {reason}"
617
+
618
+ if hook_type == "Notification":
619
+ notification_type = event.get("notification_type", "unknown")
620
+ return f"Notification received: {notification_type}"
621
+
622
+ if hook_type == "AssistantResponse":
623
+ response_len = len(event.get("response", ""))
624
+ return f"Assistant response generated ({response_len} chars)"
625
+
626
+ # Default summary
627
+ return f"Hook {hook_type} processed successfully"
628
+
475
629
  def __del__(self):
476
630
  """Cleanup on handler destruction."""
477
631
  # Clean up connection manager if it exists
@@ -202,8 +202,9 @@ main "$@"
202
202
  self.hooks_dir = self.claude_dir / "hooks" # Kept for backward compatibility
203
203
  # Use settings.json for hooks (Claude Code reads from this file)
204
204
  self.settings_file = self.claude_dir / "settings.json"
205
- # Keep reference to old file for migration
206
- self.old_settings_file = self.claude_dir / "settings.json"
205
+ # There is no legacy settings file - this was a bug where both pointed to same file
206
+ # Setting to None to disable cleanup that was deleting freshly installed hooks
207
+ self.old_settings_file = None
207
208
  self._claude_version: Optional[str] = None
208
209
  self._hook_script_path: Optional[Path] = None
209
210
 
@@ -462,7 +463,9 @@ main "$@"
462
463
 
463
464
  def _cleanup_old_settings(self) -> None:
464
465
  """Remove hooks from old settings.json file if present."""
465
- if not self.old_settings_file.exists():
466
+ # No-op: old_settings_file was pointing to same file as settings_file (bug)
467
+ # This was causing freshly installed hooks to be immediately deleted
468
+ if self.old_settings_file is None or not self.old_settings_file.exists():
466
469
  return
467
470
 
468
471
  try:
@@ -518,15 +521,31 @@ main "$@"
518
521
  }
519
522
  ]
520
523
 
521
- # Non-tool events don't need a matcher
522
- non_tool_events = ["UserPromptSubmit", "Stop", "SubagentStop", "SubagentStart"]
523
- for event_type in non_tool_events:
524
+ # Simple events (no subtypes, no matcher needed)
525
+ simple_events = ["Stop", "SubagentStop", "SubagentStart"]
526
+ for event_type in simple_events:
524
527
  settings["hooks"][event_type] = [
525
528
  {
526
529
  "hooks": [hook_command],
527
530
  }
528
531
  ]
529
532
 
533
+ # SessionStart needs matcher for subtypes (startup, resume)
534
+ settings["hooks"]["SessionStart"] = [
535
+ {
536
+ "matcher": "*", # Match all SessionStart subtypes
537
+ "hooks": [hook_command],
538
+ }
539
+ ]
540
+
541
+ # UserPromptSubmit needs matcher for potential subtypes
542
+ settings["hooks"]["UserPromptSubmit"] = [
543
+ {
544
+ "matcher": "*",
545
+ "hooks": [hook_command],
546
+ }
547
+ ]
548
+
530
549
  # Write settings to settings.json
531
550
  with self.settings_file.open("w") as f:
532
551
  json.dump(settings, f, indent=2)
@@ -650,9 +669,13 @@ main "$@"
650
669
  old_script.unlink()
651
670
  self.logger.info(f"Removed old deployed script: {old_script}")
652
671
 
653
- # Remove from Claude settings (both old and new locations)
654
- for settings_path in [self.settings_file, self.old_settings_file]:
655
- if settings_path.exists():
672
+ # Remove from Claude settings
673
+ settings_paths = [self.settings_file]
674
+ if self.old_settings_file is not None:
675
+ settings_paths.append(self.old_settings_file)
676
+
677
+ for settings_path in settings_paths:
678
+ if settings_path and settings_path.exists():
656
679
  with settings_path.open() as f:
657
680
  settings = json.load(f)
658
681
 
@@ -763,7 +786,7 @@ main "$@"
763
786
  pass
764
787
 
765
788
  # Also check old settings file
766
- if self.old_settings_file.exists():
789
+ if self.old_settings_file is not None and self.old_settings_file.exists():
767
790
  try:
768
791
  with self.old_settings_file.open() as f:
769
792
  old_settings = json.load(f)
@@ -5,8 +5,36 @@ This module provides utilities for integrating with the memory system,
5
5
  including pre and post delegation hooks.
6
6
  """
7
7
 
8
+ import logging
8
9
  import os
9
10
  import sys
11
+
12
+ # Install-type-aware logging configuration BEFORE kuzu-memory imports
13
+ # This overrides kuzu-memory's WARNING-level basicConfig (fixes 1M-445)
14
+ # but respects production install silence
15
+ try:
16
+ from claude_mpm.core.unified_paths import DeploymentContext, PathContext
17
+
18
+ context = PathContext.detect_deployment_context()
19
+
20
+ # Only configure verbose logging for development/editable installs
21
+ # Production installs remain silent by default
22
+ if context in (DeploymentContext.DEVELOPMENT, DeploymentContext.EDITABLE_INSTALL):
23
+ logging.basicConfig(
24
+ level=logging.INFO,
25
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
26
+ force=True, # Python 3.8+ - reconfigures root logger
27
+ stream=sys.stderr,
28
+ )
29
+ except ImportError:
30
+ # Fallback: if unified_paths not available, configure logging
31
+ # This maintains backward compatibility
32
+ logging.basicConfig(
33
+ level=logging.INFO,
34
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
35
+ force=True,
36
+ stream=sys.stderr,
37
+ )
10
38
  from datetime import datetime, timezone
11
39
  from typing import Optional
12
40
 
@@ -18,8 +18,7 @@ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
18
18
 
19
19
  # Response tracking integration
20
20
  # NOTE: ResponseTracker import moved to _initialize_response_tracking() for lazy loading
21
- # This prevents unnecessary import of base_agent_loader and other heavy dependencies
22
- # when hooks don't need response tracking
21
+ # This prevents unnecessary import of heavy dependencies when hooks don't need response tracking
23
22
  RESPONSE_TRACKING_AVAILABLE = (
24
23
  True # Assume available, will check on actual initialization
25
24
  )
@@ -51,7 +50,7 @@ class ResponseTrackingManager:
51
50
  response tracking without code changes.
52
51
 
53
52
  NOTE: ResponseTracker is imported lazily here to avoid loading
54
- base_agent_loader and other heavy dependencies unless actually needed.
53
+ heavy dependencies unless actually needed.
55
54
  """
56
55
  try:
57
56
  # Lazy import of ResponseTracker to avoid unnecessary dependency loading
@@ -58,7 +58,7 @@ except ImportError:
58
58
  (),
59
59
  {
60
60
  "to_dict": lambda: {
61
- "event": "claude_event",
61
+ "event": "mpm_event",
62
62
  "type": event_data.get("type", "unknown"),
63
63
  "subtype": event_data.get("subtype", "generic"),
64
64
  "timestamp": event_data.get(
@@ -115,14 +115,38 @@ class ConnectionManagerService:
115
115
  - Fallback: HTTP POST for reliability when direct connection fails
116
116
  - Eliminates duplicate events from multiple emission paths
117
117
  """
118
+ # Extract tool_call_id from data if present for correlation
119
+ tool_call_id = data.get("tool_call_id")
120
+
118
121
  # Create event data for normalization
122
+ # Extract session_id (try both camelCase and snake_case)
123
+ session_id = data.get("session_id") or data.get("sessionId")
124
+
125
+ # Extract working directory for project identification
126
+ # Try multiple field names for maximum compatibility
127
+ cwd = (
128
+ data.get("cwd")
129
+ or data.get("working_directory")
130
+ or data.get("workingDirectory")
131
+ )
132
+
133
+ # For hook_execution events, extract the actual hook type from data
134
+ # Otherwise use "hook" as the type
135
+ if event == "hook_execution":
136
+ hook_type = data.get("hook_type", "unknown")
137
+ event_type = hook_type
138
+ else:
139
+ event_type = "hook"
140
+
119
141
  raw_event = {
120
- "type": "hook",
121
- "subtype": event, # e.g., "user_prompt", "pre_tool", "subagent_stop"
142
+ "type": event_type, # Use actual hook type for hook_execution, "hook" otherwise
143
+ "subtype": event, # e.g., "user_prompt", "pre_tool", "subagent_stop", "execution"
122
144
  "timestamp": datetime.now(timezone.utc).isoformat(),
123
145
  "data": data,
124
- "source": "claude_hooks", # Identify the source
125
- "session_id": data.get("sessionId"), # Include session if available
146
+ "source": "mpm_hook", # Identify the source as mpm_hook
147
+ "session_id": session_id, # Include session if available (supports both naming conventions)
148
+ "cwd": cwd, # Add working directory at top level for easy frontend access
149
+ "correlation_id": tool_call_id, # Set from tool_call_id for event correlation
126
150
  }
127
151
 
128
152
  # Normalize the event using EventNormalizer for consistent schema
@@ -150,7 +174,7 @@ class ConnectionManagerService:
150
174
  if self.connection_pool:
151
175
  try:
152
176
  # Emit to Socket.IO server directly
153
- self.connection_pool.emit("claude_event", claude_event_data)
177
+ self.connection_pool.emit("mpm_event", claude_event_data)
154
178
  if DEBUG:
155
179
  print(f"✅ Emitted via connection pool: {event}", file=sys.stderr)
156
180
  return # Success - no need for fallback
@@ -13,6 +13,7 @@ agent outputs because:
13
13
  """
14
14
 
15
15
  import re
16
+ from datetime import datetime
16
17
  from typing import Dict, List
17
18
 
18
19
  from claude_mpm.core.config import Config
@@ -47,6 +48,16 @@ except ImportError as e:
47
48
  SOCKETIO_AVAILABLE = False
48
49
  get_socketio_server = None
49
50
 
51
+ # Try to import event bus with fallback handling
52
+ try:
53
+ from claude_mpm.services.event_bus.event_bus import EventBus
54
+
55
+ EVENT_BUS_AVAILABLE = True
56
+ except ImportError as e:
57
+ logger.debug(f"EventBus not available: {e}")
58
+ EVENT_BUS_AVAILABLE = False
59
+ EventBus = None
60
+
50
61
 
51
62
  class MemoryPreDelegationHook(PreDelegationHook):
52
63
  """Inject agent memory into delegation context.
@@ -83,6 +94,16 @@ class MemoryPreDelegationHook(PreDelegationHook):
83
94
  logger.info("Memory manager not available - hook will be inactive")
84
95
  self.memory_manager = None
85
96
 
97
+ # Initialize event bus for observability
98
+ if EVENT_BUS_AVAILABLE and EventBus:
99
+ try:
100
+ self.event_bus = EventBus.get_instance()
101
+ except Exception as e:
102
+ logger.debug(f"Failed to get EventBus instance: {e}")
103
+ self.event_bus = None
104
+ else:
105
+ self.event_bus = None
106
+
86
107
  def execute(self, context: HookContext) -> HookResult:
87
108
  """Add agent memory to delegation context.
88
109
 
@@ -137,7 +158,31 @@ INSTRUCTIONS: Review your memory above before proceeding. Apply learned patterns
137
158
 
138
159
  logger.info(f"Injected memory for agent '{agent_id}'")
139
160
 
140
- # Emit Socket.IO event for memory injected
161
+ # Calculate memory size for observability
162
+ memory_size = len(memory_content)
163
+
164
+ # Emit event bus event for observability
165
+ if self.event_bus:
166
+ try:
167
+ # Determine memory source (project or user level)
168
+ # This is inferred from the memory manager's behavior
169
+ memory_source = (
170
+ "runtime" # Runtime loading from memory manager
171
+ )
172
+
173
+ self.event_bus.publish(
174
+ "agent.memory.loaded",
175
+ {
176
+ "agent_id": agent_id,
177
+ "memory_source": memory_source,
178
+ "memory_size": memory_size,
179
+ "timestamp": datetime.now(datetime.UTC).isoformat(),
180
+ },
181
+ )
182
+ except Exception as event_error:
183
+ logger.debug(f"EventBus publish failed: {event_error}")
184
+
185
+ # Emit Socket.IO event for memory injected (legacy compatibility)
141
186
  try:
142
187
  socketio_server = get_socketio_server()
143
188
  # Calculate size of injected content
claude_mpm/init.py CHANGED
@@ -141,25 +141,6 @@ class ProjectInitializer:
141
141
  if not gitignore.exists():
142
142
  gitignore.write_text("logs/\n*.log\n*.pyc\n__pycache__/\n")
143
143
 
144
- # Also ensure MCP directories are in main project .gitignore
145
- try:
146
- from claude_mpm.services.project.project_organizer import (
147
- ProjectOrganizer,
148
- )
149
-
150
- # Check if we're in a git repository
151
- if (project_root / ".git").exists():
152
- organizer = ProjectOrganizer(project_root)
153
- # This will add MCP directories and other standard patterns
154
- organizer.update_gitignore()
155
- self.logger.debug(
156
- "Updated project .gitignore with MCP and standard patterns"
157
- )
158
- except Exception as e:
159
- self.logger.debug(
160
- f"Could not update project gitignore with MCP patterns: {e}"
161
- )
162
-
163
144
  # Log successful creation with details
164
145
  self.logger.info(f"Initialized project directory at {self.project_dir}")
165
146
  self.logger.debug("Created directories: agents, config, responses, logs")
@@ -182,6 +163,9 @@ class ProjectInitializer:
182
163
  f"✓ Found {agent_count} project agent(s) in .claude-mpm/agents/"
183
164
  )
184
165
 
166
+ # Verify and deploy PM skills (non-blocking)
167
+ self._verify_and_deploy_pm_skills(project_root, is_mcp_mode)
168
+
185
169
  return True
186
170
 
187
171
  except Exception as e:
@@ -189,6 +173,66 @@ class ProjectInitializer:
189
173
  print(f"✗ Failed to create .claude-mpm/ directory: {e}")
190
174
  return False
191
175
 
176
+ def _verify_and_deploy_pm_skills(
177
+ self, project_root: Path, is_mcp_mode: bool = False
178
+ ) -> None:
179
+ """Verify PM skills are deployed and auto-deploy if missing.
180
+
181
+ Non-blocking operation that gracefully handles errors.
182
+
183
+ Args:
184
+ project_root: Project root directory
185
+ is_mcp_mode: Whether running in MCP mode (suppress console output)
186
+ """
187
+ try:
188
+ from claude_mpm.services.pm_skills_deployer import PMSkillsDeployerService
189
+
190
+ deployer = PMSkillsDeployerService()
191
+ result = deployer.verify_pm_skills(project_root)
192
+
193
+ if not result.verified:
194
+ # Log warnings
195
+ for warning in result.warnings:
196
+ self.logger.warning(warning)
197
+
198
+ # Auto-deploy PM skills
199
+ self.logger.info("Auto-deploying PM skills...")
200
+ deploy_result = deployer.deploy_pm_skills(project_root)
201
+
202
+ if deploy_result.success:
203
+ self.logger.info(
204
+ f"PM skills deployed: {len(deploy_result.deployed)} deployed, "
205
+ f"{len(deploy_result.skipped)} skipped"
206
+ )
207
+
208
+ # Print to console if not in MCP mode
209
+ if not is_mcp_mode:
210
+ if deploy_result.deployed:
211
+ print(
212
+ f"✓ Deployed {len(deploy_result.deployed)} PM skill(s) "
213
+ f"to .claude-mpm/skills/pm/"
214
+ )
215
+ else:
216
+ self.logger.warning(
217
+ f"PM skills deployment had errors: {len(deploy_result.errors)}"
218
+ )
219
+ if not is_mcp_mode and deploy_result.errors:
220
+ print(f"⚠ PM skills deployment had {len(deploy_result.errors)} error(s)")
221
+ else:
222
+ # Skills verified successfully
223
+ registry = deployer._load_registry(project_root)
224
+ skill_count = len(registry.get("skills", []))
225
+ self.logger.debug(f"PM skills verified: {skill_count} skills")
226
+
227
+ if not is_mcp_mode and skill_count > 0:
228
+ print(f"✓ Verified {skill_count} PM skill(s)")
229
+
230
+ except ImportError:
231
+ self.logger.debug("PM skills deployer not available")
232
+ except Exception as e:
233
+ self.logger.warning(f"PM skills verification failed: {e}")
234
+ # Don't print to console - this is a non-critical failure
235
+
192
236
  def _migrate_project_agents(self):
193
237
  """Migrate agents from old subdirectory structure to direct agents directory.
194
238
 
@@ -103,6 +103,10 @@ class AgentMetadata:
103
103
  author: Optional[str] = None
104
104
  tags: List[str] = field(default_factory=list)
105
105
  specializations: List[str] = field(default_factory=list)
106
+ # NEW: Collection metadata for enhanced agent matching
107
+ collection_id: Optional[str] = None # Format: owner/repo-name
108
+ source_path: Optional[str] = None # Relative path in repository
109
+ canonical_id: Optional[str] = None # Format: collection_id:agent_id
106
110
 
107
111
  def increment_serial_version(self) -> None:
108
112
  """Increment the patch version number.
@@ -181,6 +185,9 @@ class AgentDefinition:
181
185
  "author": self.metadata.author,
182
186
  "tags": self.metadata.tags,
183
187
  "specializations": self.metadata.specializations,
188
+ "collection_id": self.metadata.collection_id,
189
+ "source_path": self.metadata.source_path,
190
+ "canonical_id": self.metadata.canonical_id,
184
191
  },
185
192
  "primary_role": self.primary_role,
186
193
  "when_to_use": self.when_to_use,
@@ -34,7 +34,7 @@ class GitRepository:
34
34
  def cache_path(self) -> Path:
35
35
  """Return cache directory path for this repository.
36
36
 
37
- Cache structure: ~/.claude-mpm/cache/remote-agents/{owner}/{repo}/{subdirectory}/
37
+ Cache structure: ~/.claude-mpm/cache/agents/{owner}/{repo}/{subdirectory}/
38
38
 
39
39
  Returns:
40
40
  Absolute path to cache directory for this repository
@@ -45,10 +45,10 @@ class GitRepository:
45
45
  ... subdirectory="agents"
46
46
  ... )
47
47
  >>> repo.cache_path
48
- Path('/Users/user/.claude-mpm/cache/remote-agents/bobmatnyc/claude-mpm-agents/agents')
48
+ Path('/Users/user/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/agents')
49
49
  """
50
50
  home = Path.home()
51
- base_cache = home / ".claude-mpm" / "cache" / "remote-agents"
51
+ base_cache = home / ".claude-mpm" / "cache" / "agents"
52
52
 
53
53
  # Extract owner and repo from URL
54
54
  owner, repo = self._parse_github_url(self.url)