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