claude-mpm 5.4.85__py3-none-any.whl → 5.6.76__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 (322) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
  3. claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +109 -706
  5. claude_mpm/agents/WORKFLOW.md +2 -0
  6. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  7. claude_mpm/auth/__init__.py +35 -0
  8. claude_mpm/auth/callback_server.py +328 -0
  9. claude_mpm/auth/models.py +104 -0
  10. claude_mpm/auth/oauth_manager.py +266 -0
  11. claude_mpm/auth/providers/__init__.py +12 -0
  12. claude_mpm/auth/providers/base.py +165 -0
  13. claude_mpm/auth/providers/google.py +261 -0
  14. claude_mpm/auth/token_storage.py +252 -0
  15. claude_mpm/cli/commands/autotodos.py +566 -0
  16. claude_mpm/cli/commands/commander.py +216 -0
  17. claude_mpm/cli/commands/hook_errors.py +60 -60
  18. claude_mpm/cli/commands/mcp.py +29 -17
  19. claude_mpm/cli/commands/mcp_command_router.py +39 -0
  20. claude_mpm/cli/commands/mcp_service_commands.py +304 -0
  21. claude_mpm/cli/commands/monitor.py +2 -2
  22. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  23. claude_mpm/cli/commands/oauth.py +481 -0
  24. claude_mpm/cli/commands/run.py +35 -3
  25. claude_mpm/cli/commands/skill_source.py +51 -2
  26. claude_mpm/cli/commands/skills.py +5 -3
  27. claude_mpm/cli/executor.py +128 -16
  28. claude_mpm/cli/helpers.py +1 -1
  29. claude_mpm/cli/parsers/base_parser.py +84 -1
  30. claude_mpm/cli/parsers/commander_parser.py +116 -0
  31. claude_mpm/cli/parsers/mcp_parser.py +79 -0
  32. claude_mpm/cli/parsers/oauth_parser.py +165 -0
  33. claude_mpm/cli/parsers/run_parser.py +10 -0
  34. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  35. claude_mpm/cli/parsers/skills_parser.py +5 -0
  36. claude_mpm/cli/startup.py +345 -40
  37. claude_mpm/cli/startup_display.py +76 -7
  38. claude_mpm/cli/startup_logging.py +2 -2
  39. claude_mpm/cli/startup_migrations.py +236 -0
  40. claude_mpm/cli/utils.py +7 -3
  41. claude_mpm/commander/__init__.py +78 -0
  42. claude_mpm/commander/adapters/__init__.py +60 -0
  43. claude_mpm/commander/adapters/auggie.py +260 -0
  44. claude_mpm/commander/adapters/base.py +288 -0
  45. claude_mpm/commander/adapters/claude_code.py +392 -0
  46. claude_mpm/commander/adapters/codex.py +237 -0
  47. claude_mpm/commander/adapters/communication.py +366 -0
  48. claude_mpm/commander/adapters/example_usage.py +310 -0
  49. claude_mpm/commander/adapters/mpm.py +389 -0
  50. claude_mpm/commander/adapters/registry.py +204 -0
  51. claude_mpm/commander/api/__init__.py +16 -0
  52. claude_mpm/commander/api/app.py +121 -0
  53. claude_mpm/commander/api/errors.py +133 -0
  54. claude_mpm/commander/api/routes/__init__.py +8 -0
  55. claude_mpm/commander/api/routes/events.py +184 -0
  56. claude_mpm/commander/api/routes/inbox.py +171 -0
  57. claude_mpm/commander/api/routes/messages.py +148 -0
  58. claude_mpm/commander/api/routes/projects.py +271 -0
  59. claude_mpm/commander/api/routes/sessions.py +226 -0
  60. claude_mpm/commander/api/routes/work.py +296 -0
  61. claude_mpm/commander/api/schemas.py +186 -0
  62. claude_mpm/commander/chat/__init__.py +7 -0
  63. claude_mpm/commander/chat/cli.py +149 -0
  64. claude_mpm/commander/chat/commands.py +124 -0
  65. claude_mpm/commander/chat/repl.py +1957 -0
  66. claude_mpm/commander/config.py +51 -0
  67. claude_mpm/commander/config_loader.py +115 -0
  68. claude_mpm/commander/core/__init__.py +10 -0
  69. claude_mpm/commander/core/block_manager.py +325 -0
  70. claude_mpm/commander/core/response_manager.py +323 -0
  71. claude_mpm/commander/daemon.py +603 -0
  72. claude_mpm/commander/env_loader.py +59 -0
  73. claude_mpm/commander/events/__init__.py +26 -0
  74. claude_mpm/commander/events/manager.py +392 -0
  75. claude_mpm/commander/frameworks/__init__.py +12 -0
  76. claude_mpm/commander/frameworks/base.py +233 -0
  77. claude_mpm/commander/frameworks/claude_code.py +58 -0
  78. claude_mpm/commander/frameworks/mpm.py +57 -0
  79. claude_mpm/commander/git/__init__.py +5 -0
  80. claude_mpm/commander/git/worktree_manager.py +212 -0
  81. claude_mpm/commander/inbox/__init__.py +16 -0
  82. claude_mpm/commander/inbox/dedup.py +128 -0
  83. claude_mpm/commander/inbox/inbox.py +224 -0
  84. claude_mpm/commander/inbox/models.py +70 -0
  85. claude_mpm/commander/instance_manager.py +868 -0
  86. claude_mpm/commander/llm/__init__.py +6 -0
  87. claude_mpm/commander/llm/openrouter_client.py +167 -0
  88. claude_mpm/commander/llm/summarizer.py +70 -0
  89. claude_mpm/commander/memory/__init__.py +45 -0
  90. claude_mpm/commander/memory/compression.py +347 -0
  91. claude_mpm/commander/memory/embeddings.py +230 -0
  92. claude_mpm/commander/memory/entities.py +310 -0
  93. claude_mpm/commander/memory/example_usage.py +290 -0
  94. claude_mpm/commander/memory/integration.py +325 -0
  95. claude_mpm/commander/memory/search.py +381 -0
  96. claude_mpm/commander/memory/store.py +657 -0
  97. claude_mpm/commander/models/__init__.py +18 -0
  98. claude_mpm/commander/models/events.py +127 -0
  99. claude_mpm/commander/models/project.py +162 -0
  100. claude_mpm/commander/models/work.py +214 -0
  101. claude_mpm/commander/parsing/__init__.py +20 -0
  102. claude_mpm/commander/parsing/extractor.py +132 -0
  103. claude_mpm/commander/parsing/output_parser.py +270 -0
  104. claude_mpm/commander/parsing/patterns.py +100 -0
  105. claude_mpm/commander/persistence/__init__.py +11 -0
  106. claude_mpm/commander/persistence/event_store.py +274 -0
  107. claude_mpm/commander/persistence/state_store.py +403 -0
  108. claude_mpm/commander/persistence/work_store.py +164 -0
  109. claude_mpm/commander/polling/__init__.py +13 -0
  110. claude_mpm/commander/polling/event_detector.py +104 -0
  111. claude_mpm/commander/polling/output_buffer.py +49 -0
  112. claude_mpm/commander/polling/output_poller.py +153 -0
  113. claude_mpm/commander/project_session.py +268 -0
  114. claude_mpm/commander/proxy/__init__.py +12 -0
  115. claude_mpm/commander/proxy/formatter.py +89 -0
  116. claude_mpm/commander/proxy/output_handler.py +191 -0
  117. claude_mpm/commander/proxy/relay.py +155 -0
  118. claude_mpm/commander/registry.py +410 -0
  119. claude_mpm/commander/runtime/__init__.py +10 -0
  120. claude_mpm/commander/runtime/executor.py +191 -0
  121. claude_mpm/commander/runtime/monitor.py +346 -0
  122. claude_mpm/commander/session/__init__.py +6 -0
  123. claude_mpm/commander/session/context.py +81 -0
  124. claude_mpm/commander/session/manager.py +59 -0
  125. claude_mpm/commander/tmux_orchestrator.py +362 -0
  126. claude_mpm/commander/web/__init__.py +1 -0
  127. claude_mpm/commander/work/__init__.py +30 -0
  128. claude_mpm/commander/work/executor.py +207 -0
  129. claude_mpm/commander/work/queue.py +405 -0
  130. claude_mpm/commander/workflow/__init__.py +27 -0
  131. claude_mpm/commander/workflow/event_handler.py +241 -0
  132. claude_mpm/commander/workflow/notifier.py +146 -0
  133. claude_mpm/commands/mpm-config.md +8 -0
  134. claude_mpm/commands/mpm-doctor.md +8 -0
  135. claude_mpm/commands/mpm-help.md +8 -0
  136. claude_mpm/commands/mpm-init.md +8 -0
  137. claude_mpm/commands/mpm-monitor.md +8 -0
  138. claude_mpm/commands/mpm-organize.md +8 -0
  139. claude_mpm/commands/mpm-postmortem.md +8 -0
  140. claude_mpm/commands/mpm-session-resume.md +9 -1
  141. claude_mpm/commands/mpm-status.md +8 -0
  142. claude_mpm/commands/mpm-ticket-view.md +8 -0
  143. claude_mpm/commands/mpm-version.md +8 -0
  144. claude_mpm/commands/mpm.md +8 -0
  145. claude_mpm/config/agent_presets.py +8 -7
  146. claude_mpm/config/skill_sources.py +16 -0
  147. claude_mpm/constants.py +5 -0
  148. claude_mpm/core/claude_runner.py +152 -0
  149. claude_mpm/core/config.py +35 -22
  150. claude_mpm/core/config_constants.py +74 -9
  151. claude_mpm/core/constants.py +56 -12
  152. claude_mpm/core/hook_manager.py +53 -4
  153. claude_mpm/core/interactive_session.py +5 -4
  154. claude_mpm/core/logger.py +26 -9
  155. claude_mpm/core/logging_utils.py +39 -13
  156. claude_mpm/core/network_config.py +148 -0
  157. claude_mpm/core/oneshot_session.py +7 -6
  158. claude_mpm/core/output_style_manager.py +52 -12
  159. claude_mpm/core/socketio_pool.py +47 -15
  160. claude_mpm/core/unified_config.py +10 -6
  161. claude_mpm/core/unified_paths.py +68 -80
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  170. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  220. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  221. claude_mpm/experimental/cli_enhancements.py +2 -1
  222. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  223. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  224. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  225. claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
  226. claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
  227. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  228. claude_mpm/hooks/claude_hooks/installer.py +291 -59
  229. claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
  230. claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
  231. claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
  232. claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
  233. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
  234. claude_mpm/hooks/claude_hooks/services/container.py +326 -0
  235. claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
  236. claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
  237. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
  238. claude_mpm/hooks/session_resume_hook.py +89 -1
  239. claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
  240. claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
  241. claude_mpm/init.py +22 -15
  242. claude_mpm/mcp/__init__.py +9 -0
  243. claude_mpm/mcp/google_workspace_server.py +610 -0
  244. claude_mpm/scripts/claude-hook-handler.sh +46 -19
  245. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  246. claude_mpm/services/agents/agent_selection_service.py +2 -2
  247. claude_mpm/services/agents/cache_git_manager.py +1 -1
  248. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  249. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  250. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  251. claude_mpm/services/cli/__init__.py +3 -0
  252. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  253. claude_mpm/services/cli/session_resume_helper.py +10 -2
  254. claude_mpm/services/command_deployment_service.py +44 -26
  255. claude_mpm/services/delegation_detector.py +175 -0
  256. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  257. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  258. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  259. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  260. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  261. claude_mpm/services/diagnostics/models.py +14 -1
  262. claude_mpm/services/event_log.py +325 -0
  263. claude_mpm/services/hook_installer_service.py +77 -8
  264. claude_mpm/services/infrastructure/__init__.py +4 -0
  265. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  266. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  267. claude_mpm/services/mcp_config_manager.py +99 -19
  268. claude_mpm/services/mcp_service_registry.py +294 -0
  269. claude_mpm/services/monitor/daemon_manager.py +15 -4
  270. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  271. claude_mpm/services/monitor/server.py +111 -16
  272. claude_mpm/services/pm_skills_deployer.py +261 -87
  273. claude_mpm/services/skills/git_skill_source_manager.py +130 -10
  274. claude_mpm/services/skills/selective_skill_deployer.py +142 -16
  275. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  276. claude_mpm/services/skills_deployer.py +31 -5
  277. claude_mpm/services/socketio/handlers/hook.py +14 -7
  278. claude_mpm/services/socketio/server/main.py +12 -4
  279. claude_mpm/skills/__init__.py +2 -1
  280. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  281. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  282. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  283. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  284. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  285. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  286. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  287. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  288. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  289. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  290. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  291. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  292. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  293. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  294. claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
  295. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  296. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  297. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  298. claude_mpm/skills/registry.py +295 -90
  299. claude_mpm/skills/skill_manager.py +4 -4
  300. claude_mpm-5.6.76.dist-info/METADATA +416 -0
  301. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/RECORD +312 -175
  302. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/WHEEL +1 -1
  303. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/entry_points.txt +2 -0
  304. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  305. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  306. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  307. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  308. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  309. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  310. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  311. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  312. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  313. claude_mpm-5.4.85.dist-info/METADATA +0 -1023
  314. /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
  315. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  316. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  317. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  318. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  319. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  320. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE +0 -0
  321. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  322. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,32 @@
1
1
  #!/usr/bin/env python3
2
+ # ==============================================================================
3
+ # CRITICAL: EARLY LOGGING SUPPRESSION - MUST BE FIRST
4
+ # ==============================================================================
5
+ # Suppress ALL logging before any other imports to prevent REPL pollution.
6
+ # The StreamingHandler in logger.py writes carriage returns (\r) and spaces
7
+ # to stderr which pollutes Claude Code's REPL output.
8
+ #
9
+ # This MUST be before any imports that could trigger module-level loggers.
10
+ # ==============================================================================
11
+ import logging as _early_logging
12
+ import sys as _early_sys
13
+
14
+ # Force redirect all logging to NullHandler before any module imports
15
+ # This prevents ANY log output from polluting stdout/stderr during hook execution
16
+ _early_logging.basicConfig(handlers=[_early_logging.NullHandler()], force=True)
17
+ # Also ensure root logger has no handlers that write to stderr
18
+ _early_logging.getLogger().handlers = [_early_logging.NullHandler()]
19
+ # Suppress all loggers by setting a very high level initially
20
+ _early_logging.getLogger().setLevel(_early_logging.CRITICAL + 1)
21
+
22
+ # Clean up namespace to avoid polluting module scope
23
+ del _early_logging
24
+ del _early_sys
25
+
26
+ # ==============================================================================
27
+ # END EARLY LOGGING SUPPRESSION
28
+ # ==============================================================================
29
+
2
30
  """Refactored Claude Code hook handler with modular service architecture.
3
31
 
4
32
  This handler uses a service-oriented architecture with:
@@ -17,12 +45,18 @@ NOTE: Requires Claude Code version 1.0.92 or higher for proper hook support.
17
45
  Earlier versions do not support matcher-based hook configuration.
18
46
  """
19
47
 
48
+ # Suppress RuntimeWarning from frozen runpy (prevents REPL pollution in Claude Code)
49
+ # Must be before other imports to suppress warnings during import
50
+ import warnings
51
+
52
+ warnings.filterwarnings("ignore", category=RuntimeWarning)
53
+
20
54
  import json
21
55
  import os
22
56
  import re
23
57
  import select
24
58
  import signal
25
- import subprocess
59
+ import subprocess # nosec B404
26
60
  import sys
27
61
  import threading
28
62
  from datetime import datetime, timezone
@@ -31,12 +65,14 @@ from typing import Optional, Tuple
31
65
  # Import extracted modules with fallback for direct execution
32
66
  try:
33
67
  # Try relative imports first (when imported as module)
68
+ from .auto_pause_handler import AutoPauseHandler
34
69
  from .event_handlers import EventHandlers
35
70
  from .memory_integration import MemoryHookManager
36
71
  from .response_tracking import ResponseTrackingManager
37
72
  from .services import (
38
73
  ConnectionManagerService,
39
74
  DuplicateEventDetector,
75
+ HookServiceContainer,
40
76
  StateManagerService,
41
77
  SubagentResponseProcessor,
42
78
  )
@@ -47,27 +83,61 @@ except ImportError:
47
83
  # Add parent directory to path
48
84
  sys.path.insert(0, str(Path(__file__).parent))
49
85
 
86
+ from auto_pause_handler import AutoPauseHandler
50
87
  from event_handlers import EventHandlers
51
88
  from memory_integration import MemoryHookManager
52
89
  from response_tracking import ResponseTrackingManager
53
90
  from services import (
54
91
  ConnectionManagerService,
55
92
  DuplicateEventDetector,
93
+ HookServiceContainer,
56
94
  StateManagerService,
57
95
  SubagentResponseProcessor,
58
96
  )
59
97
 
98
+ # Import CorrelationManager with fallback (used in _route_event cleanup)
99
+ # WHY at top level: Runtime relative imports fail with "no known parent package" error
100
+ try:
101
+ from .correlation_manager import CorrelationManager
102
+ except ImportError:
103
+ try:
104
+ from correlation_manager import CorrelationManager
105
+ except ImportError:
106
+ # Fallback: create a no-op class if module unavailable
107
+ class CorrelationManager:
108
+ @staticmethod
109
+ def cleanup_old():
110
+ pass
111
+
112
+
60
113
  """
61
114
  Debug mode configuration for hook processing.
62
115
 
63
- WHY enabled by default: Hook processing can be complex and hard to debug.
64
- Having debug output available by default helps diagnose issues during development.
65
- Production deployments can disable via environment variable.
116
+ WHY disabled by default: Production users should see clean output without debug noise.
117
+ Hook errors appear less confusing when debug output is minimal.
118
+ Development and debugging can enable via CLAUDE_MPM_HOOK_DEBUG=true.
66
119
 
67
120
  Performance Impact: Debug logging adds ~5-10% overhead but provides crucial
68
- visibility into event flow, timing, and error conditions.
121
+ visibility into event flow, timing, and error conditions when enabled.
69
122
  """
70
- DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
123
+ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "false").lower() == "true"
124
+
125
+
126
+ def _log(message: str) -> None:
127
+ """Log message to file if DEBUG enabled. Never write to stderr.
128
+
129
+ WHY: Claude Code interprets ANY stderr output as a hook error.
130
+ Writing to stderr causes confusing "hook error" messages even for debug logs.
131
+
132
+ This helper ensures all debug output goes to a log file instead.
133
+ """
134
+ if DEBUG:
135
+ try:
136
+ with open("/tmp/claude-mpm-hook.log", "a") as f: # nosec B108
137
+ f.write(f"[{datetime.now(timezone.utc).isoformat()}] {message}\n")
138
+ except Exception: # nosec B110 - intentional silent failure
139
+ pass # Never disrupt hook execution
140
+
71
141
 
72
142
  """
73
143
  Conditional imports with graceful fallbacks for testing and modularity.
@@ -109,6 +179,8 @@ WHY version checking:
109
179
  Security: Version checking prevents execution on incompatible environments.
110
180
  """
111
181
  MIN_CLAUDE_VERSION = "1.0.92"
182
+ # Minimum version for user-invocable skills support
183
+ MIN_SKILLS_VERSION = "2.1.3"
112
184
 
113
185
 
114
186
  def check_claude_version() -> Tuple[bool, Optional[str]]:
@@ -153,7 +225,7 @@ def check_claude_version() -> Tuple[bool, Optional[str]]:
153
225
  """
154
226
  try:
155
227
  # Try to detect Claude Code version
156
- result = subprocess.run(
228
+ result = subprocess.run( # nosec B603 B607 - Safe: hardcoded claude CLI with --version flag, no user input
157
229
  ["claude", "--version"],
158
230
  capture_output=True,
159
231
  text=True,
@@ -184,22 +256,17 @@ def check_claude_version() -> Tuple[bool, Optional[str]]:
184
256
  req_part = required[i] if i < len(required) else 0
185
257
 
186
258
  if curr_part < req_part:
187
- if DEBUG:
188
- print(
189
- f"⚠️ Claude Code {version} does not support matcher-based hooks "
190
- f"(requires {MIN_CLAUDE_VERSION}+). Hook monitoring disabled.",
191
- file=sys.stderr,
192
- )
259
+ _log(
260
+ f"⚠️ Claude Code {version} does not support matcher-based hooks "
261
+ f"(requires {MIN_CLAUDE_VERSION}+). Hook monitoring disabled."
262
+ )
193
263
  return False, version
194
264
  if curr_part > req_part:
195
265
  return True, version
196
266
 
197
267
  return True, version
198
268
  except Exception as e:
199
- if DEBUG:
200
- print(
201
- f"Warning: Could not detect Claude Code version: {e}", file=sys.stderr
202
- )
269
+ _log(f"Warning: Could not detect Claude Code version: {e}")
203
270
 
204
271
  return False, None
205
272
 
@@ -212,26 +279,73 @@ class ClaudeHookHandler:
212
279
  - Each service handles a specific responsibility
213
280
  - Easier to test, maintain, and extend
214
281
  - Reduced complexity in main handler class
282
+
283
+ Supports Dependency Injection:
284
+ - Pass a HookServiceContainer to override default services
285
+ - Useful for testing with mock services
286
+ - Maintains backward compatibility when no container is provided
215
287
  """
216
288
 
217
- def __init__(self):
218
- # Initialize services
219
- self.state_manager = StateManagerService()
220
- self.connection_manager = ConnectionManagerService()
221
- self.duplicate_detector = DuplicateEventDetector()
289
+ def __init__(self, container: Optional[HookServiceContainer] = None):
290
+ """Initialize hook handler with optional DI container.
222
291
 
223
- # Initialize extracted managers
224
- self.memory_hook_manager = MemoryHookManager()
225
- self.response_tracking_manager = ResponseTrackingManager()
226
- self.event_handlers = EventHandlers(self)
292
+ Args:
293
+ container: Optional HookServiceContainer for dependency injection.
294
+ If None, services are created directly (backward compatible).
295
+ """
296
+ # Use container if provided, otherwise create services directly
297
+ if container is not None:
298
+ # DI mode: get services from container
299
+ self._container = container
300
+ self.state_manager = container.get_state_manager()
301
+ self.connection_manager = container.get_connection_manager()
302
+ self.duplicate_detector = container.get_duplicate_detector()
303
+ self.memory_hook_manager = container.get_memory_hook_manager()
304
+ self.response_tracking_manager = container.get_response_tracking_manager()
305
+ self.auto_pause_handler = container.get_auto_pause_handler()
306
+
307
+ # Event handlers need reference to this handler (circular, but contained)
308
+ self.event_handlers = EventHandlers(self)
309
+
310
+ # Subagent processor with injected dependencies
311
+ self.subagent_processor = container.get_subagent_processor(
312
+ self.state_manager,
313
+ self.response_tracking_manager,
314
+ self.connection_manager,
315
+ )
316
+ else:
317
+ # Backward compatible mode: create services directly
318
+ self._container = None
319
+ self.state_manager = StateManagerService()
320
+ self.connection_manager = ConnectionManagerService()
321
+ self.duplicate_detector = DuplicateEventDetector()
322
+
323
+ # Initialize extracted managers
324
+ self.memory_hook_manager = MemoryHookManager()
325
+ self.response_tracking_manager = ResponseTrackingManager()
326
+ self.event_handlers = EventHandlers(self)
327
+
328
+ # Initialize subagent processor with dependencies
329
+ self.subagent_processor = SubagentResponseProcessor(
330
+ self.state_manager,
331
+ self.response_tracking_manager,
332
+ self.connection_manager,
333
+ )
227
334
 
228
- # Initialize subagent processor with dependencies
229
- self.subagent_processor = SubagentResponseProcessor(
230
- self.state_manager, self.response_tracking_manager, self.connection_manager
231
- )
335
+ # Initialize auto-pause handler
336
+ try:
337
+ self.auto_pause_handler = AutoPauseHandler()
338
+ except Exception as e:
339
+ self.auto_pause_handler = None
340
+ _log(f"Auto-pause initialization failed: {e}")
341
+
342
+ # Link auto-pause handler to response tracking manager
343
+ if self.auto_pause_handler and hasattr(self, "response_tracking_manager"):
344
+ self.response_tracking_manager.auto_pause_handler = self.auto_pause_handler
232
345
 
233
346
  # Backward compatibility properties for tests
234
- self.connection_pool = self.connection_manager.connection_pool
347
+ # Note: HTTP-based connection manager doesn't use connection_pool
348
+ self.connection_pool = None # Deprecated: No longer needed with HTTP emission
235
349
 
236
350
  # Expose state manager properties for backward compatibility
237
351
  self.active_delegations = self.state_manager.active_delegations
@@ -260,8 +374,7 @@ class ClaudeHookHandler:
260
374
  def timeout_handler(signum, frame):
261
375
  """Handle timeout by forcing exit."""
262
376
  nonlocal _continue_sent
263
- if DEBUG:
264
- print(f"Hook handler timeout (pid: {os.getpid()})", file=sys.stderr)
377
+ _log(f"Hook handler timeout (pid: {os.getpid()})")
265
378
  if not _continue_sent:
266
379
  self._continue_execution()
267
380
  _continue_sent = True
@@ -282,11 +395,9 @@ class ClaudeHookHandler:
282
395
 
283
396
  # Check for duplicate events (same event within 100ms)
284
397
  if self.duplicate_detector.is_duplicate(event):
285
- if DEBUG:
286
- print(
287
- f"[{datetime.now(timezone.utc).isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
288
- file=sys.stderr,
289
- )
398
+ _log(
399
+ f"[{datetime.now(timezone.utc).isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})"
400
+ )
290
401
  # Still need to output continue for this invocation
291
402
  if not _continue_sent:
292
403
  self._continue_execution()
@@ -294,25 +405,19 @@ class ClaudeHookHandler:
294
405
  return
295
406
 
296
407
  # Debug: Log that we're processing an event
297
- if DEBUG:
298
- hook_type = event.get("hook_event_name", "unknown")
299
- print(
300
- f"\n[{datetime.now(timezone.utc).isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
301
- file=sys.stderr,
302
- )
408
+ hook_type = event.get("hook_event_name", "unknown")
409
+ _log(
410
+ f"\n[{datetime.now(timezone.utc).isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})"
411
+ )
303
412
 
304
413
  # Perform periodic cleanup if needed
305
414
  if self.state_manager.increment_events_processed():
306
415
  self.state_manager.cleanup_old_entries()
307
416
  # Also cleanup old correlation files
308
- from .correlation_manager import CorrelationManager
309
-
310
417
  CorrelationManager.cleanup_old()
311
- if DEBUG:
312
- print(
313
- f"🧹 Performed cleanup after {self.state_manager.events_processed} events",
314
- file=sys.stderr,
315
- )
418
+ _log(
419
+ f"🧹 Performed cleanup after {self.state_manager.events_processed} events"
420
+ )
316
421
 
317
422
  # Route event to appropriate handler
318
423
  # Handlers can optionally return modified input for PreToolUse events
@@ -352,8 +457,7 @@ class ClaudeHookHandler:
352
457
  ready, _, _ = select.select([sys.stdin], [], [], 1.0)
353
458
  if not ready:
354
459
  # No data available within timeout
355
- if DEBUG:
356
- print("No hook event data received within timeout", file=sys.stderr)
460
+ _log("No hook event data received within timeout")
357
461
  return None
358
462
 
359
463
  # Data is available, read it
@@ -364,21 +468,16 @@ class ClaudeHookHandler:
364
468
 
365
469
  parsed = json.loads(event_data)
366
470
  # Debug: Log the actual event format we receive
367
- if DEBUG:
368
- print(
369
- f"Received event with keys: {list(parsed.keys())}", file=sys.stderr
370
- )
371
- for key in ["hook_event_name", "event", "type", "event_type"]:
372
- if key in parsed:
373
- print(f" {key} = '{parsed[key]}'", file=sys.stderr)
471
+ _log(f"Received event with keys: {list(parsed.keys())}")
472
+ for key in ["hook_event_name", "event", "type", "event_type"]:
473
+ if key in parsed:
474
+ _log(f" {key} = '{parsed[key]}'")
374
475
  return parsed
375
476
  except (json.JSONDecodeError, ValueError) as e:
376
- if DEBUG:
377
- print(f"Failed to parse hook event: {e}", file=sys.stderr)
477
+ _log(f"Failed to parse hook event: {e}")
378
478
  return None
379
479
  except Exception as e:
380
- if DEBUG:
381
- print(f"Error reading hook event: {e}", file=sys.stderr)
480
+ _log(f"Error reading hook event: {e}")
382
481
  return None
383
482
 
384
483
  def _route_event(self, event: dict) -> Optional[dict]:
@@ -407,9 +506,9 @@ class ClaudeHookHandler:
407
506
  )
408
507
 
409
508
  # Log the actual event structure for debugging
410
- if DEBUG and hook_type == "unknown":
411
- print(f"Unknown event format, keys: {list(event.keys())}", file=sys.stderr)
412
- print(f"Event sample: {str(event)[:200]}", file=sys.stderr)
509
+ if hook_type == "unknown":
510
+ _log(f"Unknown event format, keys: {list(event.keys())}")
511
+ _log(f"Event sample: {str(event)[:200]}")
413
512
 
414
513
  # Map event types to handlers
415
514
  event_handlers = {
@@ -419,7 +518,7 @@ class ClaudeHookHandler:
419
518
  "Notification": self.event_handlers.handle_notification_fast,
420
519
  "Stop": self.event_handlers.handle_stop_fast,
421
520
  "SubagentStop": self.event_handlers.handle_subagent_stop_fast,
422
- "SubagentStart": self.event_handlers.handle_session_start_fast,
521
+ "SubagentStart": self.event_handlers.handle_subagent_start_fast,
423
522
  "SessionStart": self.event_handlers.handle_session_start_fast,
424
523
  "AssistantResponse": self.event_handlers.handle_assistant_response,
425
524
  }
@@ -445,8 +544,7 @@ class ClaudeHookHandler:
445
544
  except Exception as e:
446
545
  error_message = str(e)
447
546
  return_value = None
448
- if DEBUG:
449
- print(f"Error handling {hook_type}: {e}", file=sys.stderr)
547
+ _log(f"Error handling {hook_type}: {e}")
450
548
  finally:
451
549
  # Calculate duration
452
550
  duration_ms = int((time.time() - start_time) * 1000)
@@ -480,9 +578,12 @@ class ClaudeHookHandler:
480
578
  """
481
579
  if modified_input is not None:
482
580
  # Claude Code v2.0.30+ supports modifying PreToolUse tool inputs
483
- print(json.dumps({"action": "continue", "tool_input": modified_input}))
581
+ print(
582
+ json.dumps({"continue": True, "tool_input": modified_input}),
583
+ flush=True,
584
+ )
484
585
  else:
485
- print(json.dumps({"action": "continue"}))
586
+ print(json.dumps({"continue": True}), flush=True)
486
587
 
487
588
  # Delegation methods for compatibility with event_handlers
488
589
  def _track_delegation(self, session_id: str, agent_type: str, request_data=None):
@@ -535,13 +636,15 @@ class ClaudeHookHandler:
535
636
  # Build hook execution data
536
637
  hook_data = {
537
638
  "hook_name": hook_type,
538
- "hook_type": hook_type,
639
+ "hook_type": hook_type, # Actual hook type (PreToolUse, UserPromptSubmit, etc.)
640
+ "hook_event_type": hook_type, # Additional field for clarity
539
641
  "session_id": session_id,
540
642
  "working_directory": working_dir,
541
643
  "success": success,
542
644
  "duration_ms": duration_ms,
543
645
  "result_summary": summary,
544
646
  "timestamp": datetime.now(timezone.utc).isoformat(),
647
+ "source": "claude_hook_handler", # Explicit source identification
545
648
  }
546
649
 
547
650
  # Add error information if present
@@ -566,11 +669,9 @@ class ClaudeHookHandler:
566
669
  # This uses the existing event infrastructure
567
670
  self._emit_socketio_event("", "hook_execution", hook_data)
568
671
 
569
- if DEBUG:
570
- print(
571
- f"📊 Hook execution event: {hook_type} - {duration_ms}ms - {'✅' if success else '❌'}",
572
- file=sys.stderr,
573
- )
672
+ _log(
673
+ f"📊 Hook execution event: {hook_type} - {duration_ms}ms - {'✅' if success else '❌'}"
674
+ )
574
675
 
575
676
  def _generate_hook_summary(self, hook_type: str, event: dict, success: bool) -> str:
576
677
  """Generate a human-readable summary of what the hook did.
@@ -628,12 +729,19 @@ class ClaudeHookHandler:
628
729
 
629
730
  def __del__(self):
630
731
  """Cleanup on handler destruction."""
732
+ # Finalize any active auto-pause session
733
+ if hasattr(self, "auto_pause_handler") and self.auto_pause_handler:
734
+ try:
735
+ self.auto_pause_handler.on_session_end()
736
+ except Exception:
737
+ pass # nosec B110 - Intentionally ignore cleanup errors during handler destruction
738
+
631
739
  # Clean up connection manager if it exists
632
740
  if hasattr(self, "connection_manager") and self.connection_manager:
633
741
  try:
634
742
  self.connection_manager.cleanup()
635
743
  except Exception:
636
- pass # Ignore cleanup errors during destruction
744
+ pass # nosec B110 - Intentionally ignore cleanup errors during handler destruction
637
745
 
638
746
 
639
747
  def main():
@@ -646,25 +754,18 @@ def main():
646
754
  if not is_compatible:
647
755
  # Version incompatible - just continue without processing
648
756
  # This prevents errors on older Claude Code versions
649
- if DEBUG and version:
650
- print(
651
- f"Skipping hook processing due to version incompatibility ({version})",
652
- file=sys.stderr,
653
- )
654
- print(json.dumps({"action": "continue"}))
757
+ if version:
758
+ _log(f"Skipping hook processing due to version incompatibility ({version})")
759
+ print(json.dumps({"continue": True}), flush=True)
655
760
  sys.exit(0)
656
761
 
657
762
  def cleanup_handler(signum=None, frame=None):
658
763
  """Cleanup handler for signals and exit."""
659
764
  nonlocal _continue_printed
660
- if DEBUG:
661
- print(
662
- f"Hook handler cleanup (pid: {os.getpid()}, signal: {signum})",
663
- file=sys.stderr,
664
- )
765
+ _log(f"Hook handler cleanup (pid: {os.getpid()}, signal: {signum})")
665
766
  # Only output continue if we haven't already (i.e., if interrupted by signal)
666
767
  if signum is not None and not _continue_printed:
667
- print(json.dumps({"action": "continue"}))
768
+ print(json.dumps({"continue": True}), flush=True)
668
769
  _continue_printed = True
669
770
  sys.exit(0)
670
771
 
@@ -678,15 +779,10 @@ def main():
678
779
  with _handler_lock:
679
780
  if _global_handler is None:
680
781
  _global_handler = ClaudeHookHandler()
681
- if DEBUG:
682
- print(
683
- f"✅ Created new ClaudeHookHandler singleton (pid: {os.getpid()})",
684
- file=sys.stderr,
685
- )
686
- elif DEBUG:
687
- print(
688
- f"♻️ Reusing existing ClaudeHookHandler singleton (pid: {os.getpid()})",
689
- file=sys.stderr,
782
+ _log(f"✅ Created new ClaudeHookHandler singleton (pid: {os.getpid()})")
783
+ else:
784
+ _log(
785
+ f"♻️ Reusing existing ClaudeHookHandler singleton (pid: {os.getpid()})"
690
786
  )
691
787
 
692
788
  handler = _global_handler
@@ -702,13 +798,17 @@ def main():
702
798
  except Exception as e:
703
799
  # Only output continue if not already printed
704
800
  if not _continue_printed:
705
- print(json.dumps({"action": "continue"}))
801
+ print(json.dumps({"continue": True}), flush=True)
706
802
  _continue_printed = True
707
803
  # Log error for debugging
708
- if DEBUG:
709
- print(f"Hook handler error: {e}", file=sys.stderr)
804
+ _log(f"Hook handler error: {e}")
710
805
  sys.exit(0) # Exit cleanly even on error
711
806
 
712
807
 
713
808
  if __name__ == "__main__":
714
- main()
809
+ try:
810
+ main()
811
+ except Exception:
812
+ # Catastrophic failure (import error, etc.) - always output valid JSON
813
+ print(json.dumps({"continue": True}), flush=True)
814
+ sys.exit(0)
@@ -48,15 +48,10 @@ echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] PYTHONPATH: $PYTHONPATH" >> /tmp/hook
48
48
  echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Running: $PYTHON_CMD -m claude_mpm.hooks.claude_hooks.hook_handler" >> /tmp/hook-wrapper.log
49
49
  echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] SOCKETIO_PORT: $CLAUDE_MPM_SOCKETIO_PORT" >> /tmp/hook-wrapper.log
50
50
 
51
- # Run the Python hook handler as a module with error handling
52
- # Use exec to replace the shell process, but wrap in error handling
53
- if ! "$PYTHON_CMD" -m claude_mpm.hooks.claude_hooks.hook_handler "$@" 2>/tmp/hook-error.log; then
54
- # If the Python handler fails, always return continue to not block Claude
55
- echo '{"action": "continue"}'
56
- # Log the error for debugging
57
- echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Hook handler failed, see /tmp/hook-error.log" >> /tmp/hook-wrapper.log
58
- exit 0
59
- fi
51
+ # Run the Python hook handler as a module
52
+ # Python handler is responsible for ALL stdout output (including error fallback)
53
+ # Redirect stderr to log file for debugging
54
+ "$PYTHON_CMD" -m claude_mpm.hooks.claude_hooks.hook_handler "$@" 2>/tmp/hook-error.log
60
55
 
61
- # Success - Python handler already printed continue, just exit
62
- exit 0
56
+ # Exit with Python's exit code (should always be 0)
57
+ exit $?