claude-mpm 5.4.41__py3-none-any.whl → 5.6.72__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 (490) 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 +109 -1925
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +161 -298
  6. claude_mpm/agents/WORKFLOW.md +2 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  8. claude_mpm/auth/__init__.py +35 -0
  9. claude_mpm/auth/callback_server.py +328 -0
  10. claude_mpm/auth/models.py +104 -0
  11. claude_mpm/auth/oauth_manager.py +266 -0
  12. claude_mpm/auth/providers/__init__.py +12 -0
  13. claude_mpm/auth/providers/base.py +165 -0
  14. claude_mpm/auth/providers/google.py +261 -0
  15. claude_mpm/auth/token_storage.py +252 -0
  16. claude_mpm/cli/__init__.py +5 -1
  17. claude_mpm/cli/commands/agents.py +2 -4
  18. claude_mpm/cli/commands/agents_reconcile.py +197 -0
  19. claude_mpm/cli/commands/autotodos.py +566 -0
  20. claude_mpm/cli/commands/commander.py +216 -0
  21. claude_mpm/cli/commands/configure.py +620 -21
  22. claude_mpm/cli/commands/configure_agent_display.py +3 -1
  23. claude_mpm/cli/commands/hook_errors.py +60 -60
  24. claude_mpm/cli/commands/mcp.py +29 -17
  25. claude_mpm/cli/commands/mcp_command_router.py +39 -0
  26. claude_mpm/cli/commands/mcp_service_commands.py +304 -0
  27. claude_mpm/cli/commands/monitor.py +2 -2
  28. claude_mpm/cli/commands/mpm_init/core.py +15 -8
  29. claude_mpm/cli/commands/oauth.py +481 -0
  30. claude_mpm/cli/commands/profile.py +9 -10
  31. claude_mpm/cli/commands/run.py +35 -3
  32. claude_mpm/cli/commands/skill_source.py +51 -2
  33. claude_mpm/cli/commands/skills.py +182 -32
  34. claude_mpm/cli/executor.py +129 -16
  35. claude_mpm/cli/helpers.py +1 -1
  36. claude_mpm/cli/interactive/__init__.py +10 -0
  37. claude_mpm/cli/interactive/agent_wizard.py +30 -50
  38. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  39. claude_mpm/cli/interactive/skill_selector.py +481 -0
  40. claude_mpm/cli/parsers/base_parser.py +89 -1
  41. claude_mpm/cli/parsers/commander_parser.py +116 -0
  42. claude_mpm/cli/parsers/mcp_parser.py +79 -0
  43. claude_mpm/cli/parsers/oauth_parser.py +165 -0
  44. claude_mpm/cli/parsers/profile_parser.py +0 -1
  45. claude_mpm/cli/parsers/run_parser.py +10 -0
  46. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  47. claude_mpm/cli/parsers/skills_parser.py +2 -3
  48. claude_mpm/cli/startup.py +662 -524
  49. claude_mpm/cli/startup_display.py +76 -7
  50. claude_mpm/cli/startup_logging.py +2 -2
  51. claude_mpm/cli/utils.py +7 -3
  52. claude_mpm/commander/__init__.py +78 -0
  53. claude_mpm/commander/adapters/__init__.py +60 -0
  54. claude_mpm/commander/adapters/auggie.py +260 -0
  55. claude_mpm/commander/adapters/base.py +288 -0
  56. claude_mpm/commander/adapters/claude_code.py +392 -0
  57. claude_mpm/commander/adapters/codex.py +237 -0
  58. claude_mpm/commander/adapters/communication.py +366 -0
  59. claude_mpm/commander/adapters/example_usage.py +310 -0
  60. claude_mpm/commander/adapters/mpm.py +389 -0
  61. claude_mpm/commander/adapters/registry.py +204 -0
  62. claude_mpm/commander/api/__init__.py +16 -0
  63. claude_mpm/commander/api/app.py +121 -0
  64. claude_mpm/commander/api/errors.py +133 -0
  65. claude_mpm/commander/api/routes/__init__.py +8 -0
  66. claude_mpm/commander/api/routes/events.py +184 -0
  67. claude_mpm/commander/api/routes/inbox.py +171 -0
  68. claude_mpm/commander/api/routes/messages.py +148 -0
  69. claude_mpm/commander/api/routes/projects.py +271 -0
  70. claude_mpm/commander/api/routes/sessions.py +226 -0
  71. claude_mpm/commander/api/routes/work.py +296 -0
  72. claude_mpm/commander/api/schemas.py +186 -0
  73. claude_mpm/commander/chat/__init__.py +7 -0
  74. claude_mpm/commander/chat/cli.py +149 -0
  75. claude_mpm/commander/chat/commands.py +122 -0
  76. claude_mpm/commander/chat/repl.py +1821 -0
  77. claude_mpm/commander/config.py +51 -0
  78. claude_mpm/commander/config_loader.py +115 -0
  79. claude_mpm/commander/core/__init__.py +10 -0
  80. claude_mpm/commander/core/block_manager.py +325 -0
  81. claude_mpm/commander/core/response_manager.py +323 -0
  82. claude_mpm/commander/daemon.py +603 -0
  83. claude_mpm/commander/env_loader.py +59 -0
  84. claude_mpm/commander/events/__init__.py +26 -0
  85. claude_mpm/commander/events/manager.py +392 -0
  86. claude_mpm/commander/frameworks/__init__.py +12 -0
  87. claude_mpm/commander/frameworks/base.py +233 -0
  88. claude_mpm/commander/frameworks/claude_code.py +58 -0
  89. claude_mpm/commander/frameworks/mpm.py +57 -0
  90. claude_mpm/commander/git/__init__.py +5 -0
  91. claude_mpm/commander/git/worktree_manager.py +212 -0
  92. claude_mpm/commander/inbox/__init__.py +16 -0
  93. claude_mpm/commander/inbox/dedup.py +128 -0
  94. claude_mpm/commander/inbox/inbox.py +224 -0
  95. claude_mpm/commander/inbox/models.py +70 -0
  96. claude_mpm/commander/instance_manager.py +865 -0
  97. claude_mpm/commander/llm/__init__.py +6 -0
  98. claude_mpm/commander/llm/openrouter_client.py +167 -0
  99. claude_mpm/commander/llm/summarizer.py +70 -0
  100. claude_mpm/commander/memory/__init__.py +45 -0
  101. claude_mpm/commander/memory/compression.py +347 -0
  102. claude_mpm/commander/memory/embeddings.py +230 -0
  103. claude_mpm/commander/memory/entities.py +310 -0
  104. claude_mpm/commander/memory/example_usage.py +290 -0
  105. claude_mpm/commander/memory/integration.py +325 -0
  106. claude_mpm/commander/memory/search.py +381 -0
  107. claude_mpm/commander/memory/store.py +657 -0
  108. claude_mpm/commander/models/__init__.py +18 -0
  109. claude_mpm/commander/models/events.py +127 -0
  110. claude_mpm/commander/models/project.py +162 -0
  111. claude_mpm/commander/models/work.py +214 -0
  112. claude_mpm/commander/parsing/__init__.py +20 -0
  113. claude_mpm/commander/parsing/extractor.py +132 -0
  114. claude_mpm/commander/parsing/output_parser.py +270 -0
  115. claude_mpm/commander/parsing/patterns.py +100 -0
  116. claude_mpm/commander/persistence/__init__.py +11 -0
  117. claude_mpm/commander/persistence/event_store.py +274 -0
  118. claude_mpm/commander/persistence/state_store.py +403 -0
  119. claude_mpm/commander/persistence/work_store.py +164 -0
  120. claude_mpm/commander/polling/__init__.py +13 -0
  121. claude_mpm/commander/polling/event_detector.py +104 -0
  122. claude_mpm/commander/polling/output_buffer.py +49 -0
  123. claude_mpm/commander/polling/output_poller.py +153 -0
  124. claude_mpm/commander/project_session.py +268 -0
  125. claude_mpm/commander/proxy/__init__.py +12 -0
  126. claude_mpm/commander/proxy/formatter.py +89 -0
  127. claude_mpm/commander/proxy/output_handler.py +191 -0
  128. claude_mpm/commander/proxy/relay.py +155 -0
  129. claude_mpm/commander/registry.py +410 -0
  130. claude_mpm/commander/runtime/__init__.py +10 -0
  131. claude_mpm/commander/runtime/executor.py +191 -0
  132. claude_mpm/commander/runtime/monitor.py +346 -0
  133. claude_mpm/commander/session/__init__.py +6 -0
  134. claude_mpm/commander/session/context.py +81 -0
  135. claude_mpm/commander/session/manager.py +59 -0
  136. claude_mpm/commander/tmux_orchestrator.py +362 -0
  137. claude_mpm/commander/web/__init__.py +1 -0
  138. claude_mpm/commander/work/__init__.py +30 -0
  139. claude_mpm/commander/work/executor.py +207 -0
  140. claude_mpm/commander/work/queue.py +405 -0
  141. claude_mpm/commander/workflow/__init__.py +27 -0
  142. claude_mpm/commander/workflow/event_handler.py +241 -0
  143. claude_mpm/commander/workflow/notifier.py +146 -0
  144. claude_mpm/commands/mpm-config.md +8 -0
  145. claude_mpm/commands/mpm-doctor.md +8 -0
  146. claude_mpm/commands/mpm-help.md +8 -0
  147. claude_mpm/commands/mpm-init.md +8 -0
  148. claude_mpm/commands/mpm-monitor.md +8 -0
  149. claude_mpm/commands/mpm-organize.md +8 -0
  150. claude_mpm/commands/mpm-postmortem.md +8 -0
  151. claude_mpm/commands/mpm-session-resume.md +9 -1
  152. claude_mpm/commands/mpm-status.md +8 -0
  153. claude_mpm/commands/mpm-ticket-view.md +8 -0
  154. claude_mpm/commands/mpm-version.md +8 -0
  155. claude_mpm/commands/mpm.md +8 -0
  156. claude_mpm/config/agent_presets.py +8 -7
  157. claude_mpm/config/skill_sources.py +16 -0
  158. claude_mpm/constants.py +6 -0
  159. claude_mpm/core/claude_runner.py +154 -2
  160. claude_mpm/core/config.py +35 -22
  161. claude_mpm/core/config_constants.py +74 -9
  162. claude_mpm/core/constants.py +56 -12
  163. claude_mpm/core/hook_manager.py +53 -4
  164. claude_mpm/core/interactive_session.py +12 -11
  165. claude_mpm/core/logger.py +26 -9
  166. claude_mpm/core/logging_utils.py +39 -13
  167. claude_mpm/core/network_config.py +148 -0
  168. claude_mpm/core/oneshot_session.py +7 -6
  169. claude_mpm/core/optimized_startup.py +3 -1
  170. claude_mpm/core/output_style_manager.py +66 -18
  171. claude_mpm/core/shared/config_loader.py +3 -1
  172. claude_mpm/core/socketio_pool.py +47 -15
  173. claude_mpm/core/unified_config.py +54 -8
  174. claude_mpm/core/unified_paths.py +95 -90
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/1WZnGYqX.js +24 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/67pF3qNn.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/6RxdMKe4.js +1 -0
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/8cZrfX0h.js +60 -0
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/9a6T2nm-.js +7 -0
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B443AUzu.js +1 -0
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B8AwtY2H.js +1 -0
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BF15LAsF.js +1 -0
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BRcwIQNr.js +4 -0
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uj46x2Wr.js → BSNlmTZj.js} +1 -1
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BV6nKitt.js +43 -0
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BViJ8lZt.js +128 -0
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BcQ-Q0FE.js +1 -0
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bpyvgze_.js +30 -0
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C3rbW_a-.js +1 -0
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C8WYN38h.js +1 -0
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C9I8FlXH.js +61 -0
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIQcWgO2.js +36 -0
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIctN7YN.js +7 -0
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CKrS_JZW.js +145 -0
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CR6P9C4A.js +89 -0
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRRR9MD_.js +2 -0
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CSXtMOf0.js +1 -0
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CT-sbxSk.js +1 -0
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWm6DJsp.js +1 -0
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CpqQ1Kzn.js +1 -0
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D2nGpDRe.js +1 -0
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9iCMida.js +267 -0
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9ykgMoY.js +10 -0
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DL2Ldur1.js +1 -0
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DPfltzjH.js +165 -0
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{N4qtv3Hx.js → DR8nis88.js} +2 -2
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUliQN2b.js +1 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DXlhR01x.js +122 -0
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D_lyTybS.js +1 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DngoTTgh.js +1 -0
  220. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DqkmHtDC.js +220 -0
  221. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DsDh8EYs.js +1 -0
  222. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DypDmXgd.js +139 -0
  223. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
  224. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/IPYC-LnN.js +162 -0
  225. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  226. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JpevfAFt.js +68 -0
  227. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DjhvlsAc.js → NqQ1dWOy.js} +1 -1
  228. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/R8CEIRAd.js +2 -0
  229. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Zxy7qc-l.js +64 -0
  230. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  231. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/qtd3IeO4.js +15 -0
  232. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ulBFON_C.js +65 -0
  233. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/wQVh1CoA.js +10 -0
  234. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.Dr7t0z2J.js +2 -0
  235. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  236. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.CAGBuiOw.js → 0.RgBboRvH.js} +1 -1
  237. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DG-KkbDf.js +1 -0
  238. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  239. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  240. claude_mpm/dashboard/static/svelte-build/index.html +11 -11
  241. claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  242. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  243. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  244. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
  245. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  246. claude_mpm/experimental/cli_enhancements.py +2 -1
  247. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  248. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  249. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  250. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  251. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  252. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  253. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  254. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  255. claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
  256. claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
  257. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  258. claude_mpm/hooks/claude_hooks/installer.py +291 -59
  259. claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
  260. claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
  261. claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
  262. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  263. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  264. claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
  265. claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
  266. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  267. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  268. claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
  269. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
  270. claude_mpm/hooks/claude_hooks/services/container.py +326 -0
  271. claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
  272. claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
  273. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
  274. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  275. claude_mpm/hooks/session_resume_hook.py +89 -1
  276. claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
  277. claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
  278. claude_mpm/init.py +224 -4
  279. claude_mpm/mcp/__init__.py +9 -0
  280. claude_mpm/mcp/google_workspace_server.py +610 -0
  281. claude_mpm/scripts/claude-hook-handler.sh +46 -19
  282. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  283. claude_mpm/services/agents/agent_selection_service.py +2 -2
  284. claude_mpm/services/agents/cache_git_manager.py +1 -1
  285. claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -1
  286. claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
  287. claude_mpm/services/agents/deployment/agent_template_builder.py +37 -17
  288. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  289. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  290. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  291. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +36 -8
  292. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +50 -26
  293. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  294. claude_mpm/services/agents/git_source_manager.py +21 -2
  295. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  296. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  297. claude_mpm/services/agents/sources/git_source_sync_service.py +116 -5
  298. claude_mpm/services/agents/startup_sync.py +5 -2
  299. claude_mpm/services/cli/__init__.py +3 -0
  300. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  301. claude_mpm/services/cli/session_resume_helper.py +10 -2
  302. claude_mpm/services/command_deployment_service.py +44 -26
  303. claude_mpm/services/delegation_detector.py +175 -0
  304. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  305. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  306. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  307. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  308. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  309. claude_mpm/services/diagnostics/models.py +14 -1
  310. claude_mpm/services/event_log.py +325 -0
  311. claude_mpm/services/hook_installer_service.py +77 -8
  312. claude_mpm/services/infrastructure/__init__.py +4 -0
  313. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  314. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  315. claude_mpm/services/mcp_config_manager.py +99 -19
  316. claude_mpm/services/mcp_service_registry.py +294 -0
  317. claude_mpm/services/monitor/daemon_manager.py +15 -4
  318. claude_mpm/services/monitor/management/lifecycle.py +8 -3
  319. claude_mpm/services/monitor/server.py +111 -16
  320. claude_mpm/services/pm_skills_deployer.py +302 -94
  321. claude_mpm/services/profile_manager.py +10 -4
  322. claude_mpm/services/skills/git_skill_source_manager.py +192 -29
  323. claude_mpm/services/skills/selective_skill_deployer.py +211 -46
  324. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  325. claude_mpm/services/skills_deployer.py +192 -70
  326. claude_mpm/services/socketio/handlers/hook.py +14 -7
  327. claude_mpm/services/socketio/server/main.py +12 -4
  328. claude_mpm/skills/__init__.py +2 -1
  329. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
  330. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
  331. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
  332. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
  333. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
  334. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
  335. claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
  336. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
  337. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
  338. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
  339. claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
  340. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
  341. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
  342. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
  343. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
  344. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
  345. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
  346. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
  347. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
  348. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
  349. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
  350. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
  351. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
  352. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
  353. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
  354. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
  355. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
  356. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
  357. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
  358. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
  359. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
  360. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
  361. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
  362. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
  363. claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
  364. claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
  365. claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
  366. claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
  367. claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
  368. claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
  369. claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
  370. claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
  371. claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
  372. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
  373. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
  374. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
  375. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
  376. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
  377. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
  378. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
  379. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
  380. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
  381. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
  382. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
  383. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
  384. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
  385. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
  386. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
  387. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
  388. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
  389. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
  390. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
  391. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
  392. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
  393. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
  394. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
  395. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
  396. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
  397. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
  398. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  399. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  400. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  401. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  402. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  403. claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
  404. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  405. claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
  406. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  407. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  408. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  409. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  410. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  411. claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
  412. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  413. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  414. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  415. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  416. claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
  417. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  418. claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
  419. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  420. claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
  421. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  422. claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
  423. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
  424. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
  425. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
  426. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
  427. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
  428. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
  429. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
  430. claude_mpm/skills/bundled/security-scanning.md +112 -0
  431. claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
  432. claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
  433. claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
  434. claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
  435. claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
  436. claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
  437. claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
  438. claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
  439. claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
  440. claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
  441. claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
  442. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
  443. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
  444. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
  445. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
  446. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
  447. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
  448. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
  449. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
  450. claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
  451. claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
  452. claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
  453. claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
  454. claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
  455. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
  456. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
  457. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
  458. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
  459. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
  460. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
  461. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
  462. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
  463. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
  464. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
  465. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
  466. claude_mpm/skills/registry.py +295 -90
  467. claude_mpm/skills/skill_manager.py +29 -23
  468. claude_mpm/templates/.pre-commit-config.yaml +112 -0
  469. claude_mpm/utils/agent_dependency_loader.py +103 -4
  470. claude_mpm/utils/robust_installer.py +45 -24
  471. claude_mpm-5.6.72.dist-info/METADATA +416 -0
  472. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/RECORD +477 -159
  473. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/WHEEL +1 -1
  474. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/entry_points.txt +2 -0
  475. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
  476. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
  477. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
  478. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
  479. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +0 -1
  480. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
  481. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
  482. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
  483. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
  484. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
  485. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  486. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  487. claude_mpm-5.4.41.dist-info/METADATA +0 -998
  488. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE +0 -0
  489. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  490. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/top_level.txt +0 -0
@@ -3,16 +3,30 @@
3
3
 
4
4
  This module provides individual event handlers for different types of
5
5
  Claude Code hook events.
6
+
7
+ Supports Dependency Injection:
8
+ - Optional services can be passed via constructor
9
+ - Lazy loading fallback for services not provided
10
+ - Eliminates runtime imports inside methods
6
11
  """
7
12
 
13
+ import asyncio
8
14
  import os
9
15
  import re
10
- import subprocess
11
- import sys
16
+ import subprocess # nosec B404 - subprocess used for safe claude CLI version checking only
12
17
  import uuid
13
18
  from datetime import datetime, timezone
14
19
  from pathlib import Path
15
- from typing import Optional
20
+ from typing import Any, Optional
21
+
22
+ # Import _log helper to avoid stderr writes (which cause hook errors)
23
+ try:
24
+ from .hook_handler import _log
25
+ except ImportError:
26
+ # Fallback for direct execution
27
+ def _log(message: str) -> None:
28
+ """Fallback logger when hook_handler not available."""
29
+
16
30
 
17
31
  # Import tool analysis with fallback for direct execution
18
32
  try:
@@ -34,8 +48,15 @@ except ImportError:
34
48
  extract_tool_results,
35
49
  )
36
50
 
37
- # Debug mode
38
- DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
51
+ # Import correlation manager with fallback for direct execution
52
+ # WHY at top level: Runtime relative imports fail with "no known parent package" error
53
+ try:
54
+ from .correlation_manager import CorrelationManager
55
+ except ImportError:
56
+ from correlation_manager import CorrelationManager
57
+
58
+ # Debug mode - MUST match hook_handler.py default (false) to prevent stderr writes
59
+ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "false").lower() == "true"
39
60
 
40
61
  # Import constants for configuration
41
62
  try:
@@ -46,13 +67,152 @@ except ImportError:
46
67
  QUICK_TIMEOUT = 2.0
47
68
 
48
69
 
70
+ # ============================================================================
71
+ # Optional Dependencies - loaded once at module level for DI
72
+ # ============================================================================
73
+
74
+ # Log manager (for agent prompt logging)
75
+ _log_manager: Optional[Any] = None
76
+ _log_manager_loaded = False
77
+
78
+
79
+ def _get_log_manager() -> Optional[Any]:
80
+ """Get log manager with lazy loading."""
81
+ global _log_manager, _log_manager_loaded
82
+ if not _log_manager_loaded:
83
+ try:
84
+ from claude_mpm.core.log_manager import get_log_manager
85
+
86
+ _log_manager = get_log_manager()
87
+ except ImportError:
88
+ _log_manager = None
89
+ _log_manager_loaded = True
90
+ return _log_manager
91
+
92
+
93
+ # Config service (for autotodos configuration)
94
+ _config: Optional[Any] = None
95
+ _config_loaded = False
96
+
97
+
98
+ def _get_config() -> Optional[Any]:
99
+ """Get Config with lazy loading."""
100
+ global _config, _config_loaded
101
+ if not _config_loaded:
102
+ try:
103
+ from claude_mpm.core.config import Config
104
+
105
+ _config = Config()
106
+ except ImportError:
107
+ _config = None
108
+ _config_loaded = True
109
+ return _config
110
+
111
+
112
+ # Delegation detector (for anti-pattern detection)
113
+ _delegation_detector: Optional[Any] = None
114
+ _delegation_detector_loaded = False
115
+
116
+
117
+ def _get_delegation_detector_service() -> Optional[Any]:
118
+ """Get delegation detector with lazy loading."""
119
+ global _delegation_detector, _delegation_detector_loaded
120
+ if not _delegation_detector_loaded:
121
+ try:
122
+ from claude_mpm.services.delegation_detector import get_delegation_detector
123
+
124
+ _delegation_detector = get_delegation_detector()
125
+ except ImportError:
126
+ _delegation_detector = None
127
+ _delegation_detector_loaded = True
128
+ return _delegation_detector
129
+
130
+
131
+ # Event log (for PM violation logging)
132
+ _event_log: Optional[Any] = None
133
+ _event_log_loaded = False
134
+
135
+
136
+ def _get_event_log_service() -> Optional[Any]:
137
+ """Get event log with lazy loading."""
138
+ global _event_log, _event_log_loaded
139
+ if not _event_log_loaded:
140
+ try:
141
+ from claude_mpm.services.event_log import get_event_log
142
+
143
+ _event_log = get_event_log()
144
+ except ImportError:
145
+ _event_log = None
146
+ _event_log_loaded = True
147
+ return _event_log
148
+
149
+
49
150
  class EventHandlers:
50
- """Collection of event handlers for different Claude Code hook events."""
151
+ """Collection of event handlers for different Claude Code hook events.
152
+
153
+ Supports dependency injection for optional services:
154
+ - log_manager: For agent prompt logging
155
+ - config: For autotodos configuration
156
+ - delegation_detector: For anti-pattern detection
157
+ - event_log: For PM violation logging
51
158
 
52
- def __init__(self, hook_handler):
53
- """Initialize with reference to the main hook handler."""
159
+ If services are not provided, they are loaded lazily on first use.
160
+ """
161
+
162
+ def __init__(
163
+ self,
164
+ hook_handler,
165
+ *,
166
+ log_manager: Optional[Any] = None,
167
+ config: Optional[Any] = None,
168
+ delegation_detector: Optional[Any] = None,
169
+ event_log: Optional[Any] = None,
170
+ ):
171
+ """Initialize with reference to the main hook handler and optional services.
172
+
173
+ Args:
174
+ hook_handler: The main ClaudeHookHandler instance
175
+ log_manager: Optional LogManager for agent prompt logging
176
+ config: Optional Config for autotodos configuration
177
+ delegation_detector: Optional DelegationDetector for anti-pattern detection
178
+ event_log: Optional EventLog for PM violation logging
179
+ """
54
180
  self.hook_handler = hook_handler
55
181
 
182
+ # Store injected services (None means use lazy loading)
183
+ self._log_manager = log_manager
184
+ self._config = config
185
+ self._delegation_detector = delegation_detector
186
+ self._event_log = event_log
187
+
188
+ @property
189
+ def log_manager(self) -> Optional[Any]:
190
+ """Get log manager (injected or lazy loaded)."""
191
+ if self._log_manager is not None:
192
+ return self._log_manager
193
+ return _get_log_manager()
194
+
195
+ @property
196
+ def config(self) -> Optional[Any]:
197
+ """Get config (injected or lazy loaded)."""
198
+ if self._config is not None:
199
+ return self._config
200
+ return _get_config()
201
+
202
+ @property
203
+ def delegation_detector(self) -> Optional[Any]:
204
+ """Get delegation detector (injected or lazy loaded)."""
205
+ if self._delegation_detector is not None:
206
+ return self._delegation_detector
207
+ return _get_delegation_detector_service()
208
+
209
+ @property
210
+ def event_log(self) -> Optional[Any]:
211
+ """Get event log (injected or lazy loaded)."""
212
+ if self._event_log is not None:
213
+ return self._event_log
214
+ return _get_event_log_service()
215
+
56
216
  def handle_user_prompt_fast(self, event):
57
217
  """Handle user prompt with comprehensive data capture.
58
218
 
@@ -111,14 +271,22 @@ class EventHandlers:
111
271
  "working_directory": working_dir,
112
272
  }
113
273
  if DEBUG:
114
- print(
115
- f"Stored prompt for comprehensive tracking: session {session_id[:8]}...",
116
- file=sys.stderr,
274
+ _log(
275
+ f"Stored prompt for comprehensive tracking: session {session_id[:8]}..."
117
276
  )
118
- except Exception:
277
+ except Exception: # nosec B110
119
278
  # Response tracking is optional - silently continue if it fails
120
279
  pass
121
280
 
281
+ # Record user message for auto-pause if active
282
+ auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
283
+ if auto_pause and auto_pause.is_pause_active():
284
+ try:
285
+ auto_pause.on_user_message(prompt)
286
+ except Exception as e:
287
+ if DEBUG:
288
+ _log(f"Auto-pause user message recording error: {e}")
289
+
122
290
  # Emit normalized event (namespace no longer needed with normalized events)
123
291
  self.hook_handler._emit_socketio_event("", "user_prompt", prompt_data)
124
292
 
@@ -133,11 +301,8 @@ class EventHandlers:
133
301
  # Enhanced debug logging for session correlation
134
302
  session_id = event.get("session_id", "")
135
303
  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)
304
+ _log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
305
+ _log(f" - event keys: {list(event.keys())}")
141
306
 
142
307
  tool_name = event.get("tool_name", "")
143
308
  tool_input = event.get("tool_input", {})
@@ -176,21 +341,43 @@ class EventHandlers:
176
341
 
177
342
  # Store tool_call_id using CorrelationManager for cross-process retrieval
178
343
  if session_id:
179
- from .correlation_manager import CorrelationManager
180
-
181
344
  CorrelationManager.store(session_id, tool_call_id, tool_name)
182
345
  if DEBUG:
183
- print(
184
- f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}...",
185
- file=sys.stderr,
346
+ _log(
347
+ f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
186
348
  )
187
349
 
188
350
  # Add delegation-specific data if this is a Task tool
189
351
  if tool_name == "Task" and isinstance(tool_input, dict):
190
352
  self._handle_task_delegation(tool_input, pre_tool_data, session_id)
191
353
 
354
+ # Record tool call for auto-pause if active
355
+ auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
356
+ if auto_pause and auto_pause.is_pause_active():
357
+ try:
358
+ auto_pause.on_tool_call(tool_name, tool_input)
359
+ except Exception as e:
360
+ if DEBUG:
361
+ _log(f"Auto-pause tool recording error: {e}")
362
+
192
363
  self.hook_handler._emit_socketio_event("", "pre_tool", pre_tool_data)
193
364
 
365
+ # Handle TodoWrite specially - emit dedicated todo_updated event
366
+ # WHY: Frontend expects todo_updated events for dashboard display
367
+ # The broadcaster.todo_updated() method exists but was never called
368
+ if tool_name == "TodoWrite" and tool_params.get("todos"):
369
+ todo_data = {
370
+ "todos": tool_params["todos"],
371
+ "total_count": len(tool_params["todos"]),
372
+ "session_id": session_id,
373
+ "timestamp": timestamp,
374
+ }
375
+ self.hook_handler._emit_socketio_event("", "todo_updated", todo_data)
376
+ if DEBUG:
377
+ _log(
378
+ f" - Emitted todo_updated event with {len(tool_params['todos'])} todos for session {session_id[:8]}..."
379
+ )
380
+
194
381
  def _handle_task_delegation(
195
382
  self, tool_input: dict, pre_tool_data: dict, session_id: str
196
383
  ):
@@ -229,12 +416,9 @@ class EventHandlers:
229
416
 
230
417
  # Track this delegation for SubagentStop correlation and response tracking
231
418
  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)
419
+ _log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
420
+ _log(f" - agent_type: {agent_type}")
421
+ _log(f" - raw_agent_type: {raw_agent_type}")
238
422
 
239
423
  if session_id and agent_type != "unknown":
240
424
  # Prepare request data for response tracking correlation
@@ -246,24 +430,17 @@ class EventHandlers:
246
430
  self.hook_handler._track_delegation(session_id, agent_type, request_data)
247
431
 
248
432
  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
- )
433
+ _log(" - Delegation tracked successfully")
434
+ _log(f" - Request data keys: {list(request_data.keys())}")
254
435
  delegation_requests = getattr(
255
436
  self.hook_handler, "delegation_requests", {}
256
437
  )
257
- print(
258
- f" - delegation_requests size: {len(delegation_requests)}",
259
- file=sys.stderr,
260
- )
438
+ _log(f" - delegation_requests size: {len(delegation_requests)}")
261
439
 
262
440
  # Log important delegations for debugging
263
441
  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,
442
+ _log(
443
+ f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'"
267
444
  )
268
445
 
269
446
  # Trigger memory pre-delegation hook
@@ -271,7 +448,7 @@ class EventHandlers:
271
448
  mhm = getattr(self.hook_handler, "memory_hook_manager", None)
272
449
  if mhm and hasattr(mhm, "trigger_pre_delegation_hook"):
273
450
  mhm.trigger_pre_delegation_hook(agent_type, tool_input, session_id)
274
- except Exception:
451
+ except Exception: # nosec B110
275
452
  # Memory hooks are optional
276
453
  pass
277
454
 
@@ -290,53 +467,50 @@ class EventHandlers:
290
467
  )
291
468
 
292
469
  # Log agent prompt if LogManager is available
293
- try:
294
- from claude_mpm.core.log_manager import get_log_manager
295
-
296
- log_manager = get_log_manager()
297
-
298
- # Prepare prompt content
299
- prompt_content = tool_input.get("prompt", "")
300
- if not prompt_content:
301
- prompt_content = tool_input.get("description", "")
302
-
303
- if prompt_content:
304
- import asyncio
305
-
306
- # Prepare metadata
307
- metadata = {
308
- "agent_type": agent_type,
309
- "agent_id": f"{agent_type}_{session_id}",
310
- "session_id": session_id,
311
- "delegation_context": {
312
- "description": tool_input.get("description", ""),
313
- "timestamp": datetime.now(timezone.utc).isoformat(),
314
- },
315
- }
470
+ # Uses injected log_manager or lazy-loaded module-level instance
471
+ log_manager = self.log_manager
472
+ if log_manager is not None:
473
+ try:
474
+ # Prepare prompt content
475
+ prompt_content = tool_input.get("prompt", "")
476
+ if not prompt_content:
477
+ prompt_content = tool_input.get("description", "")
478
+
479
+ if prompt_content:
480
+ # Prepare metadata
481
+ metadata = {
482
+ "agent_type": agent_type,
483
+ "agent_id": f"{agent_type}_{session_id}",
484
+ "session_id": session_id,
485
+ "delegation_context": {
486
+ "description": tool_input.get("description", ""),
487
+ "timestamp": datetime.now(timezone.utc).isoformat(),
488
+ },
489
+ }
316
490
 
317
- # Log the agent prompt asynchronously
318
- try:
319
- loop = asyncio.get_running_loop()
320
- _task = asyncio.create_task(
321
- log_manager.log_prompt(
322
- f"agent_{agent_type}", prompt_content, metadata
323
- )
324
- ) # Fire-and-forget logging (ephemeral hook process)
325
- except RuntimeError:
326
- # No running loop, create one
327
- loop = asyncio.new_event_loop()
328
- asyncio.set_event_loop(loop)
329
- loop.run_until_complete(
330
- log_manager.log_prompt(
331
- f"agent_{agent_type}", prompt_content, metadata
491
+ # Log the agent prompt asynchronously
492
+ try:
493
+ loop = asyncio.get_running_loop()
494
+ _task = asyncio.create_task(
495
+ log_manager.log_prompt(
496
+ f"agent_{agent_type}", prompt_content, metadata
497
+ )
498
+ ) # Fire-and-forget logging (ephemeral hook process)
499
+ except RuntimeError:
500
+ # No running loop, create one
501
+ loop = asyncio.new_event_loop()
502
+ asyncio.set_event_loop(loop)
503
+ loop.run_until_complete(
504
+ log_manager.log_prompt(
505
+ f"agent_{agent_type}", prompt_content, metadata
506
+ )
332
507
  )
333
- )
334
508
 
509
+ if DEBUG:
510
+ _log(f" - Agent prompt logged for {agent_type}")
511
+ except Exception as e:
335
512
  if DEBUG:
336
- print(f" - Agent prompt logged for {agent_type}", file=sys.stderr)
337
- except Exception as e:
338
- if DEBUG:
339
- print(f" - Could not log agent prompt: {e}", file=sys.stderr)
513
+ _log(f" - Could not log agent prompt: {e}")
340
514
 
341
515
  def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
342
516
  """Get git branch for the given directory with caching."""
@@ -364,7 +538,7 @@ class EventHandlers:
364
538
  os.chdir(working_dir)
365
539
 
366
540
  # Run git command to get current branch
367
- result = subprocess.run(
541
+ result = subprocess.run( # nosec B603 B607
368
542
  ["git", "branch", "--show-current"],
369
543
  capture_output=True,
370
544
  text=True,
@@ -420,13 +594,10 @@ class EventHandlers:
420
594
  git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
421
595
 
422
596
  # Retrieve tool_call_id using CorrelationManager for cross-process correlation
423
- from .correlation_manager import CorrelationManager
424
-
425
597
  tool_call_id = CorrelationManager.retrieve(session_id) if session_id else None
426
598
  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,
599
+ _log(
600
+ f" - Retrieved tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
430
601
  )
431
602
 
432
603
  post_tool_data = {
@@ -474,7 +645,7 @@ class EventHandlers:
474
645
  mhm = getattr(self.hook_handler, "memory_hook_manager", None)
475
646
  if mhm and hasattr(mhm, "trigger_post_delegation_hook"):
476
647
  mhm.trigger_post_delegation_hook(agent_type, event, session_id)
477
- except Exception:
648
+ except Exception: # nosec B110
478
649
  # Memory hooks are optional
479
650
  pass
480
651
 
@@ -488,7 +659,7 @@ class EventHandlers:
488
659
  rtm.track_agent_response(
489
660
  session_id, agent_type, event, delegation_requests
490
661
  )
491
- except Exception:
662
+ except Exception: # nosec B110
492
663
  # Response tracking is optional
493
664
  pass
494
665
 
@@ -550,13 +721,60 @@ class EventHandlers:
550
721
  if DEBUG:
551
722
  self._log_stop_event_debug(event, session_id, metadata)
552
723
 
724
+ # Auto-pause integration (independent of response tracking)
725
+ # WHY HERE: Auto-pause must work even when response_tracking is disabled
726
+ # Extract usage data directly from event and trigger auto-pause if thresholds crossed
727
+ if "usage" in event:
728
+ auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
729
+ if auto_pause:
730
+ try:
731
+ usage_data = event["usage"]
732
+ metadata["usage"] = {
733
+ "input_tokens": usage_data.get("input_tokens", 0),
734
+ "output_tokens": usage_data.get("output_tokens", 0),
735
+ "cache_creation_input_tokens": usage_data.get(
736
+ "cache_creation_input_tokens", 0
737
+ ),
738
+ "cache_read_input_tokens": usage_data.get(
739
+ "cache_read_input_tokens", 0
740
+ ),
741
+ }
742
+
743
+ threshold_crossed = auto_pause.on_usage_update(metadata["usage"])
744
+ if threshold_crossed:
745
+ warning = auto_pause.emit_threshold_warning(threshold_crossed)
746
+ # CRITICAL: Never write to stderr unconditionally - causes hook errors
747
+ # Use _log() instead which only writes to file if DEBUG=true
748
+ _log(f"⚠️ Auto-pause threshold crossed: {warning}")
749
+
750
+ if DEBUG:
751
+ _log(
752
+ f" - Auto-pause threshold crossed: {threshold_crossed}"
753
+ )
754
+ except Exception as e:
755
+ if DEBUG:
756
+ _log(f"Auto-pause error in handle_stop_fast: {e}")
757
+
758
+ # Finalize pause session if active
759
+ try:
760
+ if auto_pause.is_pause_active():
761
+ session_file = auto_pause.on_session_end()
762
+ if session_file:
763
+ if DEBUG:
764
+ _log(
765
+ f"✅ Auto-pause session finalized: {session_file.name}"
766
+ )
767
+ except Exception as e:
768
+ if DEBUG:
769
+ _log(f"❌ Failed to finalize auto-pause session: {e}")
770
+
553
771
  # Track response if enabled
554
772
  try:
555
773
  rtm = getattr(self.hook_handler, "response_tracking_manager", None)
556
774
  if rtm and hasattr(rtm, "track_stop_response"):
557
775
  pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
558
776
  rtm.track_stop_response(event, session_id, metadata, pending_prompts)
559
- except Exception:
777
+ except Exception: # nosec B110
560
778
  # Response tracking is optional
561
779
  pass
562
780
 
@@ -590,24 +808,15 @@ class EventHandlers:
590
808
  getattr(rtm, "response_tracker", None) is not None if rtm else False
591
809
  )
592
810
 
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:
811
+ _log(f" - response_tracking_enabled: {tracking_enabled}")
812
+ _log(f" - response_tracker exists: {tracker_exists}")
813
+ except Exception: # nosec B110
602
814
  # If debug logging fails, just skip it
603
815
  pass
604
816
 
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)
817
+ _log(f" - session_id: {session_id[:8] if session_id else 'None'}...")
818
+ _log(f" - reason: {metadata['reason']}")
819
+ _log(f" - stop_type: {metadata['stop_type']}")
611
820
 
612
821
  def _emit_stop_event(self, event: dict, session_id: str, metadata: dict) -> None:
613
822
  """Emit stop event data to Socket.IO."""
@@ -664,15 +873,12 @@ class EventHandlers:
664
873
  try:
665
874
  # Get the original request data (with fuzzy matching fallback)
666
875
  delegation_requests = getattr(self.hook_handler, "delegation_requests", {})
667
- request_info = delegation_requests.get(session_id)
876
+ request_info = delegation_requests.get(session_id) # nosec B113
668
877
 
669
878
  # If exact match fails, try partial matching
670
879
  if not request_info and session_id:
671
880
  if DEBUG:
672
- print(
673
- f" - Trying fuzzy match for session {session_id[:16]}...",
674
- file=sys.stderr,
675
- )
881
+ _log(f" - Trying fuzzy match for session {session_id[:16]}...")
676
882
  # Try to find a session that matches the first 8-16 characters
677
883
  for stored_sid in list(delegation_requests.keys()):
678
884
  if (
@@ -685,11 +891,8 @@ class EventHandlers:
685
891
  )
686
892
  ):
687
893
  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)
894
+ _log(f" - ✅ Fuzzy match found: {stored_sid[:16]}...")
895
+ request_info = delegation_requests.get(stored_sid) # nosec B113
693
896
  # Update the key to use the current session_id for consistency
694
897
  if request_info:
695
898
  delegation_requests[session_id] = request_info
@@ -757,9 +960,8 @@ class EventHandlers:
757
960
  )
758
961
 
759
962
  if file_path and DEBUG:
760
- print(
761
- f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}",
762
- file=sys.stderr,
963
+ _log(
964
+ f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}"
763
965
  )
764
966
 
765
967
  # Clean up the request data
@@ -770,16 +972,13 @@ class EventHandlers:
770
972
  del delegation_requests[session_id]
771
973
 
772
974
  elif DEBUG:
773
- print(
774
- f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}",
775
- file=sys.stderr,
975
+ _log(
976
+ f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}"
776
977
  )
777
978
 
778
979
  except Exception as e:
779
980
  if DEBUG:
780
- print(
781
- f"❌ Failed to track response on SubagentStop: {e}", file=sys.stderr
782
- )
981
+ _log(f"❌ Failed to track response on SubagentStop: {e}")
783
982
 
784
983
  def handle_assistant_response(self, event):
785
984
  """Handle assistant response events for comprehensive response tracking.
@@ -789,6 +988,7 @@ class EventHandlers:
789
988
  - Captures response content and metadata for analysis
790
989
  - Enables tracking of conversation flow and response patterns
791
990
  - Essential for comprehensive monitoring of Claude interactions
991
+ - Scans for delegation anti-patterns and creates autotodos
792
992
  """
793
993
  # Track the response for logging
794
994
  try:
@@ -796,10 +996,17 @@ class EventHandlers:
796
996
  if rtm and hasattr(rtm, "track_assistant_response"):
797
997
  pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
798
998
  rtm.track_assistant_response(event, pending_prompts)
799
- except Exception:
999
+ except Exception: # nosec B110
800
1000
  # Response tracking is optional
801
1001
  pass
802
1002
 
1003
+ # Scan response for delegation anti-patterns and create autotodos
1004
+ try:
1005
+ self._scan_for_delegation_patterns(event)
1006
+ except Exception as e: # nosec B110
1007
+ if DEBUG:
1008
+ _log(f"Delegation scanning error: {e}")
1009
+
803
1010
  # Get working directory and git branch
804
1011
  working_dir = event.get("cwd", "")
805
1012
  git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
@@ -847,11 +1054,25 @@ class EventHandlers:
847
1054
 
848
1055
  # Debug logging
849
1056
  if DEBUG:
850
- print(
851
- f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}",
852
- file=sys.stderr,
1057
+ _log(
1058
+ f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}"
853
1059
  )
854
1060
 
1061
+ # Record assistant response for auto-pause if active
1062
+ auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
1063
+ if auto_pause and auto_pause.is_pause_active():
1064
+ try:
1065
+ # Summarize response to first 200 chars
1066
+ summary = (
1067
+ response_text[:200] + "..."
1068
+ if len(response_text) > 200
1069
+ else response_text
1070
+ )
1071
+ auto_pause.on_assistant_response(summary)
1072
+ except Exception as e:
1073
+ if DEBUG:
1074
+ _log(f"Auto-pause response recording error: {e}")
1075
+
855
1076
  # Emit normalized event
856
1077
  self.hook_handler._emit_socketio_event(
857
1078
  "", "assistant_response", assistant_response_data
@@ -864,6 +1085,9 @@ class EventHandlers:
864
1085
  - Provides visibility into new conversation sessions
865
1086
  - Enables tracking of session lifecycle and duration
866
1087
  - Useful for monitoring concurrent sessions and resource usage
1088
+
1089
+ NOTE: This handler is intentionally lightweight - only event monitoring.
1090
+ All initialization/deployment logic runs in MPM CLI startup, not here.
867
1091
  """
868
1092
  session_id = event.get("session_id", "")
869
1093
  working_dir = event.get("cwd", "")
@@ -878,11 +1102,117 @@ class EventHandlers:
878
1102
  }
879
1103
 
880
1104
  # Debug logging
881
- if DEBUG:
882
- print(
883
- f"Hook handler: Processing SessionStart - session: '{session_id}'",
884
- file=sys.stderr,
885
- )
1105
+ _log(f"Hook handler: Processing SessionStart - session: '{session_id}'")
886
1106
 
887
1107
  # Emit normalized event
888
1108
  self.hook_handler._emit_socketio_event("", "session_start", session_start_data)
1109
+
1110
+ def handle_subagent_start_fast(self, event):
1111
+ """Handle SubagentStart events with proper agent type extraction.
1112
+
1113
+ WHY separate from SessionStart:
1114
+ - SubagentStart contains agent-specific information
1115
+ - Frontend needs agent_type to create distinct agent nodes
1116
+ - Multiple engineers should show as separate nodes in hierarchy
1117
+ - Research agents must appear in the agent hierarchy
1118
+
1119
+ Unlike SessionStart, SubagentStart events contain agent-specific
1120
+ information that must be preserved and emitted to the dashboard.
1121
+ """
1122
+ session_id = event.get("session_id", "")
1123
+
1124
+ # Extract agent type from event - Claude provides this in SubagentStart
1125
+ # Try multiple possible field names for compatibility
1126
+ agent_type = event.get("agent_type") or event.get("subagent_type") or "unknown"
1127
+
1128
+ # Generate unique agent ID combining type and session
1129
+ agent_id = event.get("agent_id", f"{agent_type}_{session_id[:8]}")
1130
+
1131
+ # Get working directory and git branch
1132
+ working_dir = event.get("cwd", "")
1133
+ git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
1134
+
1135
+ # Build subagent start data with all required fields
1136
+ subagent_start_data = {
1137
+ "session_id": session_id,
1138
+ "agent_type": agent_type,
1139
+ "agent_id": agent_id,
1140
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1141
+ "hook_event_name": "SubagentStart", # Preserve correct hook name
1142
+ "working_directory": working_dir,
1143
+ "git_branch": git_branch,
1144
+ }
1145
+
1146
+ # Debug logging
1147
+ if DEBUG:
1148
+ _log(
1149
+ f"Hook handler: SubagentStart - agent_type='{agent_type}', agent_id='{agent_id}', session_id='{session_id[:16]}...'"
1150
+ )
1151
+
1152
+ # Emit to /hook namespace as subagent_start (NOT session_start!)
1153
+ self.hook_handler._emit_socketio_event(
1154
+ "", "subagent_start", subagent_start_data
1155
+ )
1156
+
1157
+ def _scan_for_delegation_patterns(self, event):
1158
+ """Scan assistant response for delegation anti-patterns.
1159
+
1160
+ WHY this is needed:
1161
+ - Detect when PM asks user to do something manually instead of delegating
1162
+ - Flag PM behavior violations for immediate correction
1163
+ - Enforce delegation principle in PM workflow
1164
+ - Help PM recognize delegation opportunities
1165
+
1166
+ This method scans the assistant's response text for patterns like:
1167
+ - "Make sure .env.local is in your .gitignore"
1168
+ - "You'll need to run npm install"
1169
+ - "Please run the tests manually"
1170
+
1171
+ When patterns are detected, PM violations are logged as errors/warnings
1172
+ that should be corrected immediately, NOT as todos to delegate.
1173
+
1174
+ DESIGN DECISION: pm.violation vs autotodo.delegation
1175
+ - Delegation patterns = PM doing something WRONG → pm.violation (error)
1176
+ - Script failures = Something BROKEN → autotodo.error (todo)
1177
+ """
1178
+ # Only scan if delegation detector and event log are available
1179
+ # Uses injected services or lazy-loaded module-level instances
1180
+ detector = self.delegation_detector
1181
+ event_log_service = self.event_log
1182
+
1183
+ if detector is None or event_log_service is None:
1184
+ if DEBUG:
1185
+ _log("Delegation detector or event log not available")
1186
+ return
1187
+
1188
+ response_text = event.get("response", "")
1189
+ if not response_text:
1190
+ return
1191
+
1192
+ # Detect delegation patterns
1193
+ detections = detector.detect_user_delegation(response_text)
1194
+
1195
+ if not detections:
1196
+ return # No patterns detected
1197
+
1198
+ # Create PM violation events (NOT autotodos)
1199
+ for detection in detections:
1200
+ # Create event log entry as pm.violation
1201
+ event_log_service.append_event(
1202
+ event_type="pm.violation",
1203
+ payload={
1204
+ "violation_type": "delegation_anti_pattern",
1205
+ "pattern_type": detection["pattern_type"],
1206
+ "original_text": detection["original_text"],
1207
+ "suggested_action": detection["suggested_todo"],
1208
+ "action": detection["action"],
1209
+ "session_id": event.get("session_id", ""),
1210
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1211
+ "severity": "warning", # Not critical, but should be fixed
1212
+ "message": f"PM asked user to do something manually: {detection['original_text'][:80]}...",
1213
+ },
1214
+ status="pending",
1215
+ )
1216
+
1217
+ if DEBUG:
1218
+ _log(f"⚠️ PM violation detected: {detection['original_text'][:60]}...")