claude-mpm 5.4.65__py3-none-any.whl → 5.6.10__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 (313) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
  3. claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
  4. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +107 -1928
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +119 -689
  6. claude_mpm/agents/WORKFLOW.md +2 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  8. claude_mpm/cli/__init__.py +5 -1
  9. claude_mpm/cli/commands/agents.py +2 -4
  10. claude_mpm/cli/commands/agents_reconcile.py +197 -0
  11. claude_mpm/cli/commands/autotodos.py +566 -0
  12. claude_mpm/cli/commands/commander.py +46 -0
  13. claude_mpm/cli/commands/configure.py +620 -21
  14. claude_mpm/cli/commands/hook_errors.py +60 -60
  15. claude_mpm/cli/commands/monitor.py +2 -2
  16. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  17. claude_mpm/cli/commands/run.py +35 -3
  18. claude_mpm/cli/commands/skill_source.py +51 -2
  19. claude_mpm/cli/commands/skills.py +171 -17
  20. claude_mpm/cli/executor.py +120 -16
  21. claude_mpm/cli/interactive/__init__.py +10 -0
  22. claude_mpm/cli/interactive/agent_wizard.py +30 -50
  23. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  24. claude_mpm/cli/interactive/skill_selector.py +481 -0
  25. claude_mpm/cli/parsers/base_parser.py +76 -1
  26. claude_mpm/cli/parsers/commander_parser.py +83 -0
  27. claude_mpm/cli/parsers/run_parser.py +10 -0
  28. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  29. claude_mpm/cli/parsers/skills_parser.py +5 -0
  30. claude_mpm/cli/startup.py +203 -359
  31. claude_mpm/cli/startup_display.py +72 -5
  32. claude_mpm/cli/startup_logging.py +2 -2
  33. claude_mpm/cli/utils.py +7 -3
  34. claude_mpm/commander/__init__.py +72 -0
  35. claude_mpm/commander/adapters/__init__.py +31 -0
  36. claude_mpm/commander/adapters/base.py +191 -0
  37. claude_mpm/commander/adapters/claude_code.py +361 -0
  38. claude_mpm/commander/adapters/communication.py +366 -0
  39. claude_mpm/commander/api/__init__.py +16 -0
  40. claude_mpm/commander/api/app.py +105 -0
  41. claude_mpm/commander/api/errors.py +133 -0
  42. claude_mpm/commander/api/routes/__init__.py +8 -0
  43. claude_mpm/commander/api/routes/events.py +184 -0
  44. claude_mpm/commander/api/routes/inbox.py +171 -0
  45. claude_mpm/commander/api/routes/messages.py +148 -0
  46. claude_mpm/commander/api/routes/projects.py +271 -0
  47. claude_mpm/commander/api/routes/sessions.py +228 -0
  48. claude_mpm/commander/api/routes/work.py +260 -0
  49. claude_mpm/commander/api/schemas.py +182 -0
  50. claude_mpm/commander/chat/__init__.py +7 -0
  51. claude_mpm/commander/chat/cli.py +107 -0
  52. claude_mpm/commander/chat/commands.py +96 -0
  53. claude_mpm/commander/chat/repl.py +310 -0
  54. claude_mpm/commander/config.py +49 -0
  55. claude_mpm/commander/config_loader.py +115 -0
  56. claude_mpm/commander/daemon.py +398 -0
  57. claude_mpm/commander/events/__init__.py +26 -0
  58. claude_mpm/commander/events/manager.py +332 -0
  59. claude_mpm/commander/frameworks/__init__.py +12 -0
  60. claude_mpm/commander/frameworks/base.py +143 -0
  61. claude_mpm/commander/frameworks/claude_code.py +58 -0
  62. claude_mpm/commander/frameworks/mpm.py +62 -0
  63. claude_mpm/commander/inbox/__init__.py +16 -0
  64. claude_mpm/commander/inbox/dedup.py +128 -0
  65. claude_mpm/commander/inbox/inbox.py +224 -0
  66. claude_mpm/commander/inbox/models.py +70 -0
  67. claude_mpm/commander/instance_manager.py +337 -0
  68. claude_mpm/commander/llm/__init__.py +6 -0
  69. claude_mpm/commander/llm/openrouter_client.py +167 -0
  70. claude_mpm/commander/llm/summarizer.py +70 -0
  71. claude_mpm/commander/models/__init__.py +18 -0
  72. claude_mpm/commander/models/events.py +121 -0
  73. claude_mpm/commander/models/project.py +162 -0
  74. claude_mpm/commander/models/work.py +214 -0
  75. claude_mpm/commander/parsing/__init__.py +20 -0
  76. claude_mpm/commander/parsing/extractor.py +132 -0
  77. claude_mpm/commander/parsing/output_parser.py +270 -0
  78. claude_mpm/commander/parsing/patterns.py +100 -0
  79. claude_mpm/commander/persistence/__init__.py +11 -0
  80. claude_mpm/commander/persistence/event_store.py +274 -0
  81. claude_mpm/commander/persistence/state_store.py +309 -0
  82. claude_mpm/commander/persistence/work_store.py +164 -0
  83. claude_mpm/commander/polling/__init__.py +13 -0
  84. claude_mpm/commander/polling/event_detector.py +104 -0
  85. claude_mpm/commander/polling/output_buffer.py +49 -0
  86. claude_mpm/commander/polling/output_poller.py +153 -0
  87. claude_mpm/commander/project_session.py +268 -0
  88. claude_mpm/commander/proxy/__init__.py +12 -0
  89. claude_mpm/commander/proxy/formatter.py +89 -0
  90. claude_mpm/commander/proxy/output_handler.py +191 -0
  91. claude_mpm/commander/proxy/relay.py +155 -0
  92. claude_mpm/commander/registry.py +404 -0
  93. claude_mpm/commander/runtime/__init__.py +10 -0
  94. claude_mpm/commander/runtime/executor.py +191 -0
  95. claude_mpm/commander/runtime/monitor.py +316 -0
  96. claude_mpm/commander/session/__init__.py +6 -0
  97. claude_mpm/commander/session/context.py +81 -0
  98. claude_mpm/commander/session/manager.py +59 -0
  99. claude_mpm/commander/tmux_orchestrator.py +361 -0
  100. claude_mpm/commander/web/__init__.py +1 -0
  101. claude_mpm/commander/work/__init__.py +30 -0
  102. claude_mpm/commander/work/executor.py +189 -0
  103. claude_mpm/commander/work/queue.py +405 -0
  104. claude_mpm/commander/workflow/__init__.py +27 -0
  105. claude_mpm/commander/workflow/event_handler.py +219 -0
  106. claude_mpm/commander/workflow/notifier.py +146 -0
  107. claude_mpm/commands/mpm-config.md +8 -0
  108. claude_mpm/commands/mpm-doctor.md +8 -0
  109. claude_mpm/commands/mpm-help.md +8 -0
  110. claude_mpm/commands/mpm-init.md +8 -0
  111. claude_mpm/commands/mpm-monitor.md +8 -0
  112. claude_mpm/commands/mpm-organize.md +8 -0
  113. claude_mpm/commands/mpm-postmortem.md +8 -0
  114. claude_mpm/commands/mpm-session-resume.md +9 -1
  115. claude_mpm/commands/mpm-status.md +8 -0
  116. claude_mpm/commands/mpm-ticket-view.md +8 -0
  117. claude_mpm/commands/mpm-version.md +8 -0
  118. claude_mpm/commands/mpm.md +8 -0
  119. claude_mpm/config/agent_presets.py +8 -7
  120. claude_mpm/config/skill_sources.py +16 -0
  121. claude_mpm/constants.py +1 -0
  122. claude_mpm/core/claude_runner.py +2 -2
  123. claude_mpm/core/config.py +32 -19
  124. claude_mpm/core/hook_manager.py +51 -3
  125. claude_mpm/core/interactive_session.py +7 -7
  126. claude_mpm/core/logger.py +26 -9
  127. claude_mpm/core/logging_utils.py +35 -11
  128. claude_mpm/core/output_style_manager.py +31 -13
  129. claude_mpm/core/unified_config.py +54 -8
  130. claude_mpm/core/unified_paths.py +95 -90
  131. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  132. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  133. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  134. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  135. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  136. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  137. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  138. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  139. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  140. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  141. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  142. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  143. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  144. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  145. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  146. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  147. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  148. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  149. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  150. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  151. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  152. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  153. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  154. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  155. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  156. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  157. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  158. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  159. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  160. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  161. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  170. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  188. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  189. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  190. claude_mpm/experimental/cli_enhancements.py +2 -1
  191. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  192. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  193. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  194. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  195. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
  196. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
  197. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  198. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
  199. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
  200. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  201. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
  202. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
  203. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  204. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
  205. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  206. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
  207. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
  208. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  209. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
  210. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
  211. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
  212. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  213. claude_mpm/hooks/claude_hooks/event_handlers.py +283 -87
  214. claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
  215. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  216. claude_mpm/hooks/claude_hooks/installer.py +116 -8
  217. claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
  218. claude_mpm/hooks/claude_hooks/response_tracking.py +42 -59
  219. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
  226. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
  227. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  228. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
  229. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
  230. claude_mpm/hooks/claude_hooks/services/connection_manager.py +39 -24
  231. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
  232. claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
  233. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +73 -75
  234. claude_mpm/hooks/session_resume_hook.py +89 -1
  235. claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
  236. claude_mpm/init.py +1 -1
  237. claude_mpm/scripts/claude-hook-handler.sh +43 -16
  238. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  239. claude_mpm/services/agents/agent_selection_service.py +2 -2
  240. claude_mpm/services/agents/cache_git_manager.py +1 -1
  241. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  242. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  243. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  244. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  245. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  246. claude_mpm/services/agents/startup_sync.py +5 -2
  247. claude_mpm/services/cli/__init__.py +3 -0
  248. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  249. claude_mpm/services/cli/session_resume_helper.py +10 -2
  250. claude_mpm/services/delegation_detector.py +175 -0
  251. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  252. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  253. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  254. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  255. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  256. claude_mpm/services/diagnostics/models.py +14 -1
  257. claude_mpm/services/event_log.py +325 -0
  258. claude_mpm/services/infrastructure/__init__.py +4 -0
  259. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  260. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  261. claude_mpm/services/monitor/daemon_manager.py +15 -4
  262. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  263. claude_mpm/services/monitor/server.py +106 -16
  264. claude_mpm/services/pm_skills_deployer.py +259 -87
  265. claude_mpm/services/skills/git_skill_source_manager.py +135 -11
  266. claude_mpm/services/skills/selective_skill_deployer.py +142 -26
  267. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  268. claude_mpm/services/skills_deployer.py +31 -5
  269. claude_mpm/services/socketio/handlers/hook.py +14 -7
  270. claude_mpm/services/socketio/server/main.py +12 -4
  271. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  272. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  273. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  274. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  275. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  276. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  277. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  278. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  279. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  280. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  281. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  282. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  283. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  284. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  285. claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
  286. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  287. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  288. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  289. claude_mpm/skills/skill_manager.py +4 -4
  290. claude_mpm/utils/agent_dependency_loader.py +4 -2
  291. claude_mpm/utils/robust_installer.py +10 -6
  292. claude_mpm-5.6.10.dist-info/METADATA +391 -0
  293. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/RECORD +303 -181
  294. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  295. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  296. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  297. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  298. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  299. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  300. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  301. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  302. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  303. claude_mpm-5.4.65.dist-info/METADATA +0 -999
  304. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  305. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  306. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  307. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  308. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  309. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/WHEEL +0 -0
  310. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/entry_points.txt +0 -0
  311. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE +0 -0
  312. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  313. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/top_level.txt +0 -0
@@ -7,13 +7,21 @@ Claude Code hook events.
7
7
 
8
8
  import os
9
9
  import re
10
- import subprocess
11
- import sys
10
+ import subprocess # nosec B404 - subprocess used for safe claude CLI version checking only
12
11
  import uuid
13
12
  from datetime import datetime, timezone
14
13
  from pathlib import Path
15
14
  from typing import Optional
16
15
 
16
+ # Import _log helper to avoid stderr writes (which cause hook errors)
17
+ try:
18
+ from .hook_handler import _log
19
+ except ImportError:
20
+ # Fallback for direct execution
21
+ def _log(message: str) -> None:
22
+ """Fallback logger when hook_handler not available."""
23
+
24
+
17
25
  # Import tool analysis with fallback for direct execution
18
26
  try:
19
27
  # Try relative import first (when imported as module)
@@ -34,8 +42,8 @@ except ImportError:
34
42
  extract_tool_results,
35
43
  )
36
44
 
37
- # Debug mode
38
- DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
45
+ # Debug mode - MUST match hook_handler.py default (false) to prevent stderr writes
46
+ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "false").lower() == "true"
39
47
 
40
48
  # Import constants for configuration
41
49
  try:
@@ -111,11 +119,10 @@ class EventHandlers:
111
119
  "working_directory": working_dir,
112
120
  }
113
121
  if DEBUG:
114
- print(
115
- f"Stored prompt for comprehensive tracking: session {session_id[:8]}...",
116
- file=sys.stderr,
122
+ _log(
123
+ f"Stored prompt for comprehensive tracking: session {session_id[:8]}..."
117
124
  )
118
- except Exception:
125
+ except Exception: # nosec B110
119
126
  # Response tracking is optional - silently continue if it fails
120
127
  pass
121
128
 
@@ -133,11 +140,8 @@ class EventHandlers:
133
140
  # Enhanced debug logging for session correlation
134
141
  session_id = event.get("session_id", "")
135
142
  if DEBUG:
136
- print(
137
- f" - session_id: {session_id[:16] if session_id else 'None'}...",
138
- file=sys.stderr,
139
- )
140
- print(f" - event keys: {list(event.keys())}", file=sys.stderr)
143
+ _log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
144
+ _log(f" - event keys: {list(event.keys())}")
141
145
 
142
146
  tool_name = event.get("tool_name", "")
143
147
  tool_input = event.get("tool_input", {})
@@ -180,17 +184,41 @@ class EventHandlers:
180
184
 
181
185
  CorrelationManager.store(session_id, tool_call_id, tool_name)
182
186
  if DEBUG:
183
- print(
184
- f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}...",
185
- file=sys.stderr,
187
+ _log(
188
+ f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
186
189
  )
187
190
 
188
191
  # Add delegation-specific data if this is a Task tool
189
192
  if tool_name == "Task" and isinstance(tool_input, dict):
190
193
  self._handle_task_delegation(tool_input, pre_tool_data, session_id)
191
194
 
195
+ # Record tool call for auto-pause if active
196
+ auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
197
+ if auto_pause and auto_pause.is_pause_active():
198
+ try:
199
+ auto_pause.on_tool_call(tool_name, tool_input)
200
+ except Exception as e:
201
+ if DEBUG:
202
+ _log(f"Auto-pause tool recording error: {e}")
203
+
192
204
  self.hook_handler._emit_socketio_event("", "pre_tool", pre_tool_data)
193
205
 
206
+ # Handle TodoWrite specially - emit dedicated todo_updated event
207
+ # WHY: Frontend expects todo_updated events for dashboard display
208
+ # The broadcaster.todo_updated() method exists but was never called
209
+ if tool_name == "TodoWrite" and tool_params.get("todos"):
210
+ todo_data = {
211
+ "todos": tool_params["todos"],
212
+ "total_count": len(tool_params["todos"]),
213
+ "session_id": session_id,
214
+ "timestamp": timestamp,
215
+ }
216
+ self.hook_handler._emit_socketio_event("", "todo_updated", todo_data)
217
+ if DEBUG:
218
+ _log(
219
+ f" - Emitted todo_updated event with {len(tool_params['todos'])} todos for session {session_id[:8]}..."
220
+ )
221
+
194
222
  def _handle_task_delegation(
195
223
  self, tool_input: dict, pre_tool_data: dict, session_id: str
196
224
  ):
@@ -229,12 +257,9 @@ class EventHandlers:
229
257
 
230
258
  # Track this delegation for SubagentStop correlation and response tracking
231
259
  if DEBUG:
232
- print(
233
- f" - session_id: {session_id[:16] if session_id else 'None'}...",
234
- file=sys.stderr,
235
- )
236
- print(f" - agent_type: {agent_type}", file=sys.stderr)
237
- print(f" - raw_agent_type: {raw_agent_type}", file=sys.stderr)
260
+ _log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
261
+ _log(f" - agent_type: {agent_type}")
262
+ _log(f" - raw_agent_type: {raw_agent_type}")
238
263
 
239
264
  if session_id and agent_type != "unknown":
240
265
  # Prepare request data for response tracking correlation
@@ -246,24 +271,17 @@ class EventHandlers:
246
271
  self.hook_handler._track_delegation(session_id, agent_type, request_data)
247
272
 
248
273
  if DEBUG:
249
- print(" - Delegation tracked successfully", file=sys.stderr)
250
- print(
251
- f" - Request data keys: {list(request_data.keys())}",
252
- file=sys.stderr,
253
- )
274
+ _log(" - Delegation tracked successfully")
275
+ _log(f" - Request data keys: {list(request_data.keys())}")
254
276
  delegation_requests = getattr(
255
277
  self.hook_handler, "delegation_requests", {}
256
278
  )
257
- print(
258
- f" - delegation_requests size: {len(delegation_requests)}",
259
- file=sys.stderr,
260
- )
279
+ _log(f" - delegation_requests size: {len(delegation_requests)}")
261
280
 
262
281
  # Log important delegations for debugging
263
282
  if DEBUG or agent_type in ["research", "engineer", "qa", "documentation"]:
264
- print(
265
- f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'",
266
- file=sys.stderr,
283
+ _log(
284
+ f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'"
267
285
  )
268
286
 
269
287
  # Trigger memory pre-delegation hook
@@ -271,7 +289,7 @@ class EventHandlers:
271
289
  mhm = getattr(self.hook_handler, "memory_hook_manager", None)
272
290
  if mhm and hasattr(mhm, "trigger_pre_delegation_hook"):
273
291
  mhm.trigger_pre_delegation_hook(agent_type, tool_input, session_id)
274
- except Exception:
292
+ except Exception: # nosec B110
275
293
  # Memory hooks are optional
276
294
  pass
277
295
 
@@ -333,10 +351,10 @@ class EventHandlers:
333
351
  )
334
352
 
335
353
  if DEBUG:
336
- print(f" - Agent prompt logged for {agent_type}", file=sys.stderr)
354
+ _log(f" - Agent prompt logged for {agent_type}")
337
355
  except Exception as e:
338
356
  if DEBUG:
339
- print(f" - Could not log agent prompt: {e}", file=sys.stderr)
357
+ _log(f" - Could not log agent prompt: {e}")
340
358
 
341
359
  def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
342
360
  """Get git branch for the given directory with caching."""
@@ -364,7 +382,7 @@ class EventHandlers:
364
382
  os.chdir(working_dir)
365
383
 
366
384
  # Run git command to get current branch
367
- result = subprocess.run(
385
+ result = subprocess.run( # nosec B603 B607
368
386
  ["git", "branch", "--show-current"],
369
387
  capture_output=True,
370
388
  text=True,
@@ -424,9 +442,8 @@ class EventHandlers:
424
442
 
425
443
  tool_call_id = CorrelationManager.retrieve(session_id) if session_id else None
426
444
  if DEBUG and tool_call_id:
427
- print(
428
- f" - Retrieved tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}...",
429
- file=sys.stderr,
445
+ _log(
446
+ f" - Retrieved tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
430
447
  )
431
448
 
432
449
  post_tool_data = {
@@ -474,7 +491,7 @@ class EventHandlers:
474
491
  mhm = getattr(self.hook_handler, "memory_hook_manager", None)
475
492
  if mhm and hasattr(mhm, "trigger_post_delegation_hook"):
476
493
  mhm.trigger_post_delegation_hook(agent_type, event, session_id)
477
- except Exception:
494
+ except Exception: # nosec B110
478
495
  # Memory hooks are optional
479
496
  pass
480
497
 
@@ -488,7 +505,7 @@ class EventHandlers:
488
505
  rtm.track_agent_response(
489
506
  session_id, agent_type, event, delegation_requests
490
507
  )
491
- except Exception:
508
+ except Exception: # nosec B110
492
509
  # Response tracking is optional
493
510
  pass
494
511
 
@@ -550,13 +567,49 @@ class EventHandlers:
550
567
  if DEBUG:
551
568
  self._log_stop_event_debug(event, session_id, metadata)
552
569
 
570
+ # Auto-pause integration (independent of response tracking)
571
+ # WHY HERE: Auto-pause must work even when response_tracking is disabled
572
+ # Extract usage data directly from event and trigger auto-pause if thresholds crossed
573
+ if "usage" in event:
574
+ auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
575
+ if auto_pause:
576
+ try:
577
+ usage_data = event["usage"]
578
+ metadata["usage"] = {
579
+ "input_tokens": usage_data.get("input_tokens", 0),
580
+ "output_tokens": usage_data.get("output_tokens", 0),
581
+ "cache_creation_input_tokens": usage_data.get(
582
+ "cache_creation_input_tokens", 0
583
+ ),
584
+ "cache_read_input_tokens": usage_data.get(
585
+ "cache_read_input_tokens", 0
586
+ ),
587
+ }
588
+
589
+ threshold_crossed = auto_pause.on_usage_update(metadata["usage"])
590
+ if threshold_crossed:
591
+ warning = auto_pause.emit_threshold_warning(threshold_crossed)
592
+ # CRITICAL: Never write to stderr unconditionally - causes hook errors
593
+ # Use _log() instead which only writes to file if DEBUG=true
594
+ from . import _log
595
+
596
+ _log(f"⚠️ Auto-pause threshold crossed: {warning}")
597
+
598
+ if DEBUG:
599
+ _log(
600
+ f" - Auto-pause threshold crossed: {threshold_crossed}"
601
+ )
602
+ except Exception as e:
603
+ if DEBUG:
604
+ _log(f"Auto-pause error in handle_stop_fast: {e}")
605
+
553
606
  # Track response if enabled
554
607
  try:
555
608
  rtm = getattr(self.hook_handler, "response_tracking_manager", None)
556
609
  if rtm and hasattr(rtm, "track_stop_response"):
557
610
  pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
558
611
  rtm.track_stop_response(event, session_id, metadata, pending_prompts)
559
- except Exception:
612
+ except Exception: # nosec B110
560
613
  # Response tracking is optional
561
614
  pass
562
615
 
@@ -590,24 +643,15 @@ class EventHandlers:
590
643
  getattr(rtm, "response_tracker", None) is not None if rtm else False
591
644
  )
592
645
 
593
- print(
594
- f" - response_tracking_enabled: {tracking_enabled}",
595
- file=sys.stderr,
596
- )
597
- print(
598
- f" - response_tracker exists: {tracker_exists}",
599
- file=sys.stderr,
600
- )
601
- except Exception:
646
+ _log(f" - response_tracking_enabled: {tracking_enabled}")
647
+ _log(f" - response_tracker exists: {tracker_exists}")
648
+ except Exception: # nosec B110
602
649
  # If debug logging fails, just skip it
603
650
  pass
604
651
 
605
- print(
606
- f" - session_id: {session_id[:8] if session_id else 'None'}...",
607
- file=sys.stderr,
608
- )
609
- print(f" - reason: {metadata['reason']}", file=sys.stderr)
610
- print(f" - stop_type: {metadata['stop_type']}", file=sys.stderr)
652
+ _log(f" - session_id: {session_id[:8] if session_id else 'None'}...")
653
+ _log(f" - reason: {metadata['reason']}")
654
+ _log(f" - stop_type: {metadata['stop_type']}")
611
655
 
612
656
  def _emit_stop_event(self, event: dict, session_id: str, metadata: dict) -> None:
613
657
  """Emit stop event data to Socket.IO."""
@@ -664,15 +708,12 @@ class EventHandlers:
664
708
  try:
665
709
  # Get the original request data (with fuzzy matching fallback)
666
710
  delegation_requests = getattr(self.hook_handler, "delegation_requests", {})
667
- request_info = delegation_requests.get(session_id)
711
+ request_info = delegation_requests.get(session_id) # nosec B113
668
712
 
669
713
  # If exact match fails, try partial matching
670
714
  if not request_info and session_id:
671
715
  if DEBUG:
672
- print(
673
- f" - Trying fuzzy match for session {session_id[:16]}...",
674
- file=sys.stderr,
675
- )
716
+ _log(f" - Trying fuzzy match for session {session_id[:16]}...")
676
717
  # Try to find a session that matches the first 8-16 characters
677
718
  for stored_sid in list(delegation_requests.keys()):
678
719
  if (
@@ -685,11 +726,8 @@ class EventHandlers:
685
726
  )
686
727
  ):
687
728
  if DEBUG:
688
- print(
689
- f" - Fuzzy match found: {stored_sid[:16]}...",
690
- file=sys.stderr,
691
- )
692
- request_info = delegation_requests.get(stored_sid)
729
+ _log(f" - ✅ Fuzzy match found: {stored_sid[:16]}...")
730
+ request_info = delegation_requests.get(stored_sid) # nosec B113
693
731
  # Update the key to use the current session_id for consistency
694
732
  if request_info:
695
733
  delegation_requests[session_id] = request_info
@@ -757,9 +795,8 @@ class EventHandlers:
757
795
  )
758
796
 
759
797
  if file_path and DEBUG:
760
- print(
761
- f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}",
762
- file=sys.stderr,
798
+ _log(
799
+ f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}"
763
800
  )
764
801
 
765
802
  # Clean up the request data
@@ -770,16 +807,13 @@ class EventHandlers:
770
807
  del delegation_requests[session_id]
771
808
 
772
809
  elif DEBUG:
773
- print(
774
- f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}",
775
- file=sys.stderr,
810
+ _log(
811
+ f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}"
776
812
  )
777
813
 
778
814
  except Exception as e:
779
815
  if DEBUG:
780
- print(
781
- f"❌ Failed to track response on SubagentStop: {e}", file=sys.stderr
782
- )
816
+ _log(f"❌ Failed to track response on SubagentStop: {e}")
783
817
 
784
818
  def handle_assistant_response(self, event):
785
819
  """Handle assistant response events for comprehensive response tracking.
@@ -789,6 +823,7 @@ class EventHandlers:
789
823
  - Captures response content and metadata for analysis
790
824
  - Enables tracking of conversation flow and response patterns
791
825
  - Essential for comprehensive monitoring of Claude interactions
826
+ - Scans for delegation anti-patterns and creates autotodos
792
827
  """
793
828
  # Track the response for logging
794
829
  try:
@@ -796,10 +831,17 @@ class EventHandlers:
796
831
  if rtm and hasattr(rtm, "track_assistant_response"):
797
832
  pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
798
833
  rtm.track_assistant_response(event, pending_prompts)
799
- except Exception:
834
+ except Exception: # nosec B110
800
835
  # Response tracking is optional
801
836
  pass
802
837
 
838
+ # Scan response for delegation anti-patterns and create autotodos
839
+ try:
840
+ self._scan_for_delegation_patterns(event)
841
+ except Exception as e: # nosec B110
842
+ if DEBUG:
843
+ _log(f"Delegation scanning error: {e}")
844
+
803
845
  # Get working directory and git branch
804
846
  working_dir = event.get("cwd", "")
805
847
  git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
@@ -847,11 +889,25 @@ class EventHandlers:
847
889
 
848
890
  # Debug logging
849
891
  if DEBUG:
850
- print(
851
- f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}",
852
- file=sys.stderr,
892
+ _log(
893
+ f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}"
853
894
  )
854
895
 
896
+ # Record assistant response for auto-pause if active
897
+ auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
898
+ if auto_pause and auto_pause.is_pause_active():
899
+ try:
900
+ # Summarize response to first 200 chars
901
+ summary = (
902
+ response_text[:200] + "..."
903
+ if len(response_text) > 200
904
+ else response_text
905
+ )
906
+ auto_pause.on_assistant_response(summary)
907
+ except Exception as e:
908
+ if DEBUG:
909
+ _log(f"Auto-pause response recording error: {e}")
910
+
855
911
  # Emit normalized event
856
912
  self.hook_handler._emit_socketio_event(
857
913
  "", "assistant_response", assistant_response_data
@@ -864,6 +920,7 @@ class EventHandlers:
864
920
  - Provides visibility into new conversation sessions
865
921
  - Enables tracking of session lifecycle and duration
866
922
  - Useful for monitoring concurrent sessions and resource usage
923
+ - Auto-inject pending autotodos if enabled in config
867
924
  """
868
925
  session_id = event.get("session_id", "")
869
926
  working_dir = event.get("cwd", "")
@@ -877,12 +934,151 @@ class EventHandlers:
877
934
  "hook_event_name": "SessionStart",
878
935
  }
879
936
 
937
+ # Auto-inject pending autotodos if enabled
938
+ try:
939
+ from pathlib import Path
940
+
941
+ from claude_mpm.cli.commands.autotodos import get_pending_todos
942
+ from claude_mpm.core.config import Config
943
+
944
+ config = Config()
945
+ auto_inject_enabled = config.get("autotodos.auto_inject_on_startup", True)
946
+ max_todos = config.get("autotodos.max_todos_per_session", 10)
947
+
948
+ if auto_inject_enabled:
949
+ # Pass working directory from event to avoid Path.cwd() issues
950
+ working_dir_param = None
951
+ if working_dir:
952
+ working_dir_param = Path(working_dir)
953
+
954
+ pending_todos = get_pending_todos(
955
+ max_todos=max_todos, working_dir=working_dir_param
956
+ )
957
+ if pending_todos:
958
+ session_start_data["pending_autotodos"] = pending_todos
959
+ session_start_data["autotodos_count"] = len(pending_todos)
960
+ _log(f" - Auto-injected {len(pending_todos)} pending autotodos")
961
+ except Exception as e: # nosec B110
962
+ # Auto-injection is optional - continue if it fails
963
+ _log(f" - Failed to auto-inject autotodos: {e}")
964
+
965
+ # Debug logging
966
+ _log(f"Hook handler: Processing SessionStart - session: '{session_id}'")
967
+
968
+ # Emit normalized event
969
+ self.hook_handler._emit_socketio_event("", "session_start", session_start_data)
970
+
971
+ def handle_subagent_start_fast(self, event):
972
+ """Handle SubagentStart events with proper agent type extraction.
973
+
974
+ WHY separate from SessionStart:
975
+ - SubagentStart contains agent-specific information
976
+ - Frontend needs agent_type to create distinct agent nodes
977
+ - Multiple engineers should show as separate nodes in hierarchy
978
+ - Research agents must appear in the agent hierarchy
979
+
980
+ Unlike SessionStart, SubagentStart events contain agent-specific
981
+ information that must be preserved and emitted to the dashboard.
982
+ """
983
+ session_id = event.get("session_id", "")
984
+
985
+ # Extract agent type from event - Claude provides this in SubagentStart
986
+ # Try multiple possible field names for compatibility
987
+ agent_type = event.get("agent_type") or event.get("subagent_type") or "unknown"
988
+
989
+ # Generate unique agent ID combining type and session
990
+ agent_id = event.get("agent_id", f"{agent_type}_{session_id[:8]}")
991
+
992
+ # Get working directory and git branch
993
+ working_dir = event.get("cwd", "")
994
+ git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
995
+
996
+ # Build subagent start data with all required fields
997
+ subagent_start_data = {
998
+ "session_id": session_id,
999
+ "agent_type": agent_type,
1000
+ "agent_id": agent_id,
1001
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1002
+ "hook_event_name": "SubagentStart", # Preserve correct hook name
1003
+ "working_directory": working_dir,
1004
+ "git_branch": git_branch,
1005
+ }
1006
+
880
1007
  # Debug logging
881
1008
  if DEBUG:
882
- print(
883
- f"Hook handler: Processing SessionStart - session: '{session_id}'",
884
- file=sys.stderr,
1009
+ _log(
1010
+ f"Hook handler: SubagentStart - agent_type='{agent_type}', agent_id='{agent_id}', session_id='{session_id[:16]}...'"
885
1011
  )
886
1012
 
887
- # Emit normalized event
888
- self.hook_handler._emit_socketio_event("", "session_start", session_start_data)
1013
+ # Emit to /hook namespace as subagent_start (NOT session_start!)
1014
+ self.hook_handler._emit_socketio_event(
1015
+ "", "subagent_start", subagent_start_data
1016
+ )
1017
+
1018
+ def _scan_for_delegation_patterns(self, event):
1019
+ """Scan assistant response for delegation anti-patterns.
1020
+
1021
+ WHY this is needed:
1022
+ - Detect when PM asks user to do something manually instead of delegating
1023
+ - Flag PM behavior violations for immediate correction
1024
+ - Enforce delegation principle in PM workflow
1025
+ - Help PM recognize delegation opportunities
1026
+
1027
+ This method scans the assistant's response text for patterns like:
1028
+ - "Make sure .env.local is in your .gitignore"
1029
+ - "You'll need to run npm install"
1030
+ - "Please run the tests manually"
1031
+
1032
+ When patterns are detected, PM violations are logged as errors/warnings
1033
+ that should be corrected immediately, NOT as todos to delegate.
1034
+
1035
+ DESIGN DECISION: pm.violation vs autotodo.delegation
1036
+ - Delegation patterns = PM doing something WRONG → pm.violation (error)
1037
+ - Script failures = Something BROKEN → autotodo.error (todo)
1038
+ """
1039
+ # Only scan if delegation detector is available
1040
+ try:
1041
+ from claude_mpm.services.delegation_detector import get_delegation_detector
1042
+ from claude_mpm.services.event_log import get_event_log
1043
+ except ImportError:
1044
+ if DEBUG:
1045
+ _log("Delegation detector or event log not available")
1046
+ return
1047
+
1048
+ response_text = event.get("response", "")
1049
+ if not response_text:
1050
+ return
1051
+
1052
+ # Get the delegation detector
1053
+ detector = get_delegation_detector()
1054
+
1055
+ # Detect delegation patterns
1056
+ detections = detector.detect_user_delegation(response_text)
1057
+
1058
+ if not detections:
1059
+ return # No patterns detected
1060
+
1061
+ # Get event log for violation recording
1062
+ event_log = get_event_log()
1063
+
1064
+ # Create PM violation events (NOT autotodos)
1065
+ for detection in detections:
1066
+ # Create event log entry as pm.violation
1067
+ event_log.append_event(
1068
+ event_type="pm.violation",
1069
+ payload={
1070
+ "violation_type": "delegation_anti_pattern",
1071
+ "pattern_type": detection["pattern_type"],
1072
+ "original_text": detection["original_text"],
1073
+ "suggested_action": detection["suggested_todo"],
1074
+ "action": detection["action"],
1075
+ "session_id": event.get("session_id", ""),
1076
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1077
+ "severity": "warning", # Not critical, but should be fixed
1078
+ "message": f"PM asked user to do something manually: {detection['original_text'][:80]}...",
1079
+ },
1080
+ status="pending",
1081
+ )
1082
+
1083
+ if DEBUG:
1084
+ _log(f"⚠️ PM violation detected: {detection['original_text'][:60]}...")