claude-mpm 5.4.96__py3-none-any.whl → 5.6.10__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (191) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
  3. claude_mpm/agents/PM_INSTRUCTIONS.md +44 -10
  4. claude_mpm/agents/WORKFLOW.md +2 -0
  5. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  6. claude_mpm/cli/commands/autotodos.py +45 -5
  7. claude_mpm/cli/commands/commander.py +46 -0
  8. claude_mpm/cli/commands/hook_errors.py +60 -60
  9. claude_mpm/cli/commands/run.py +35 -3
  10. claude_mpm/cli/commands/skill_source.py +51 -2
  11. claude_mpm/cli/commands/skills.py +5 -3
  12. claude_mpm/cli/executor.py +32 -17
  13. claude_mpm/cli/parsers/base_parser.py +17 -0
  14. claude_mpm/cli/parsers/commander_parser.py +83 -0
  15. claude_mpm/cli/parsers/run_parser.py +10 -0
  16. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  17. claude_mpm/cli/parsers/skills_parser.py +5 -0
  18. claude_mpm/cli/startup.py +20 -2
  19. claude_mpm/cli/utils.py +7 -3
  20. claude_mpm/commander/__init__.py +72 -0
  21. claude_mpm/commander/adapters/__init__.py +31 -0
  22. claude_mpm/commander/adapters/base.py +191 -0
  23. claude_mpm/commander/adapters/claude_code.py +361 -0
  24. claude_mpm/commander/adapters/communication.py +366 -0
  25. claude_mpm/commander/api/__init__.py +16 -0
  26. claude_mpm/commander/api/app.py +105 -0
  27. claude_mpm/commander/api/errors.py +133 -0
  28. claude_mpm/commander/api/routes/__init__.py +8 -0
  29. claude_mpm/commander/api/routes/events.py +184 -0
  30. claude_mpm/commander/api/routes/inbox.py +171 -0
  31. claude_mpm/commander/api/routes/messages.py +148 -0
  32. claude_mpm/commander/api/routes/projects.py +271 -0
  33. claude_mpm/commander/api/routes/sessions.py +228 -0
  34. claude_mpm/commander/api/routes/work.py +260 -0
  35. claude_mpm/commander/api/schemas.py +182 -0
  36. claude_mpm/commander/chat/__init__.py +7 -0
  37. claude_mpm/commander/chat/cli.py +107 -0
  38. claude_mpm/commander/chat/commands.py +96 -0
  39. claude_mpm/commander/chat/repl.py +310 -0
  40. claude_mpm/commander/config.py +49 -0
  41. claude_mpm/commander/config_loader.py +115 -0
  42. claude_mpm/commander/daemon.py +398 -0
  43. claude_mpm/commander/events/__init__.py +26 -0
  44. claude_mpm/commander/events/manager.py +332 -0
  45. claude_mpm/commander/frameworks/__init__.py +12 -0
  46. claude_mpm/commander/frameworks/base.py +143 -0
  47. claude_mpm/commander/frameworks/claude_code.py +58 -0
  48. claude_mpm/commander/frameworks/mpm.py +62 -0
  49. claude_mpm/commander/inbox/__init__.py +16 -0
  50. claude_mpm/commander/inbox/dedup.py +128 -0
  51. claude_mpm/commander/inbox/inbox.py +224 -0
  52. claude_mpm/commander/inbox/models.py +70 -0
  53. claude_mpm/commander/instance_manager.py +337 -0
  54. claude_mpm/commander/llm/__init__.py +6 -0
  55. claude_mpm/commander/llm/openrouter_client.py +167 -0
  56. claude_mpm/commander/llm/summarizer.py +70 -0
  57. claude_mpm/commander/models/__init__.py +18 -0
  58. claude_mpm/commander/models/events.py +121 -0
  59. claude_mpm/commander/models/project.py +162 -0
  60. claude_mpm/commander/models/work.py +214 -0
  61. claude_mpm/commander/parsing/__init__.py +20 -0
  62. claude_mpm/commander/parsing/extractor.py +132 -0
  63. claude_mpm/commander/parsing/output_parser.py +270 -0
  64. claude_mpm/commander/parsing/patterns.py +100 -0
  65. claude_mpm/commander/persistence/__init__.py +11 -0
  66. claude_mpm/commander/persistence/event_store.py +274 -0
  67. claude_mpm/commander/persistence/state_store.py +309 -0
  68. claude_mpm/commander/persistence/work_store.py +164 -0
  69. claude_mpm/commander/polling/__init__.py +13 -0
  70. claude_mpm/commander/polling/event_detector.py +104 -0
  71. claude_mpm/commander/polling/output_buffer.py +49 -0
  72. claude_mpm/commander/polling/output_poller.py +153 -0
  73. claude_mpm/commander/project_session.py +268 -0
  74. claude_mpm/commander/proxy/__init__.py +12 -0
  75. claude_mpm/commander/proxy/formatter.py +89 -0
  76. claude_mpm/commander/proxy/output_handler.py +191 -0
  77. claude_mpm/commander/proxy/relay.py +155 -0
  78. claude_mpm/commander/registry.py +404 -0
  79. claude_mpm/commander/runtime/__init__.py +10 -0
  80. claude_mpm/commander/runtime/executor.py +191 -0
  81. claude_mpm/commander/runtime/monitor.py +316 -0
  82. claude_mpm/commander/session/__init__.py +6 -0
  83. claude_mpm/commander/session/context.py +81 -0
  84. claude_mpm/commander/session/manager.py +59 -0
  85. claude_mpm/commander/tmux_orchestrator.py +361 -0
  86. claude_mpm/commander/web/__init__.py +1 -0
  87. claude_mpm/commander/work/__init__.py +30 -0
  88. claude_mpm/commander/work/executor.py +189 -0
  89. claude_mpm/commander/work/queue.py +405 -0
  90. claude_mpm/commander/workflow/__init__.py +27 -0
  91. claude_mpm/commander/workflow/event_handler.py +219 -0
  92. claude_mpm/commander/workflow/notifier.py +146 -0
  93. claude_mpm/commands/mpm-config.md +8 -0
  94. claude_mpm/commands/mpm-doctor.md +8 -0
  95. claude_mpm/commands/mpm-help.md +8 -0
  96. claude_mpm/commands/mpm-init.md +8 -0
  97. claude_mpm/commands/mpm-monitor.md +8 -0
  98. claude_mpm/commands/mpm-organize.md +8 -0
  99. claude_mpm/commands/mpm-postmortem.md +8 -0
  100. claude_mpm/commands/mpm-session-resume.md +8 -0
  101. claude_mpm/commands/mpm-status.md +8 -0
  102. claude_mpm/commands/mpm-ticket-view.md +8 -0
  103. claude_mpm/commands/mpm-version.md +8 -0
  104. claude_mpm/commands/mpm.md +8 -0
  105. claude_mpm/config/agent_presets.py +8 -7
  106. claude_mpm/config/skill_sources.py +16 -0
  107. claude_mpm/core/config.py +32 -19
  108. claude_mpm/core/logger.py +26 -9
  109. claude_mpm/core/logging_utils.py +35 -11
  110. claude_mpm/core/output_style_manager.py +15 -5
  111. claude_mpm/core/unified_config.py +10 -6
  112. claude_mpm/core/unified_paths.py +68 -80
  113. claude_mpm/experimental/cli_enhancements.py +2 -1
  114. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
  115. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  116. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  117. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
  118. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
  119. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  120. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
  121. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
  122. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  123. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
  124. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
  125. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  126. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
  127. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  128. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
  129. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
  130. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  131. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
  132. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
  133. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
  134. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
  135. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +29 -30
  136. claude_mpm/hooks/claude_hooks/event_handlers.py +90 -99
  137. claude_mpm/hooks/claude_hooks/hook_handler.py +81 -88
  138. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  139. claude_mpm/hooks/claude_hooks/installer.py +116 -8
  140. claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
  141. claude_mpm/hooks/claude_hooks/response_tracking.py +39 -58
  142. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
  143. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
  144. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  145. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
  146. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
  147. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
  148. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
  149. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  150. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
  151. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
  152. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  153. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
  154. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
  155. claude_mpm/hooks/claude_hooks/services/connection_manager.py +23 -28
  156. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
  157. claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
  158. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +47 -73
  159. claude_mpm/hooks/session_resume_hook.py +22 -18
  160. claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
  161. claude_mpm/scripts/claude-hook-handler.sh +43 -16
  162. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  163. claude_mpm/services/agents/agent_selection_service.py +2 -2
  164. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  165. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  166. claude_mpm/services/event_log.py +8 -0
  167. claude_mpm/services/pm_skills_deployer.py +84 -6
  168. claude_mpm/services/skills/git_skill_source_manager.py +130 -10
  169. claude_mpm/services/skills/selective_skill_deployer.py +28 -0
  170. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  171. claude_mpm/services/skills_deployer.py +31 -5
  172. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  173. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  174. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  175. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  176. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  177. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  178. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  179. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  180. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  181. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  182. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  183. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  184. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/METADATA +18 -4
  185. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/RECORD +190 -79
  186. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  187. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/WHEEL +0 -0
  188. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/entry_points.txt +0 -0
  189. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE +0 -0
  190. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  191. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/top_level.txt +0 -0
@@ -8,12 +8,20 @@ Claude Code hook events.
8
8
  import os
9
9
  import re
10
10
  import subprocess # nosec B404 - subprocess used for safe claude CLI version checking only
11
- import sys
12
11
  import uuid
13
12
  from datetime import datetime, timezone
14
13
  from pathlib import Path
15
14
  from typing import Optional
16
15
 
16
+ # Import _log helper to avoid stderr writes (which cause hook errors)
17
+ try:
18
+ from .hook_handler import _log
19
+ except ImportError:
20
+ # Fallback for direct execution
21
+ def _log(message: str) -> None:
22
+ """Fallback logger when hook_handler not available."""
23
+
24
+
17
25
  # Import tool analysis with fallback for direct execution
18
26
  try:
19
27
  # Try relative import first (when imported as module)
@@ -34,8 +42,8 @@ except ImportError:
34
42
  extract_tool_results,
35
43
  )
36
44
 
37
- # Debug mode
38
- DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
45
+ # Debug mode - MUST match hook_handler.py default (false) to prevent stderr writes
46
+ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "false").lower() == "true"
39
47
 
40
48
  # Import constants for configuration
41
49
  try:
@@ -111,9 +119,8 @@ class EventHandlers:
111
119
  "working_directory": working_dir,
112
120
  }
113
121
  if DEBUG:
114
- print(
115
- f"Stored prompt for comprehensive tracking: session {session_id[:8]}...",
116
- file=sys.stderr,
122
+ _log(
123
+ f"Stored prompt for comprehensive tracking: session {session_id[:8]}..."
117
124
  )
118
125
  except Exception: # nosec B110
119
126
  # Response tracking is optional - silently continue if it fails
@@ -133,11 +140,8 @@ class EventHandlers:
133
140
  # Enhanced debug logging for session correlation
134
141
  session_id = event.get("session_id", "")
135
142
  if DEBUG:
136
- print(
137
- f" - session_id: {session_id[:16] if session_id else 'None'}...",
138
- file=sys.stderr,
139
- )
140
- print(f" - event keys: {list(event.keys())}", file=sys.stderr)
143
+ _log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
144
+ _log(f" - event keys: {list(event.keys())}")
141
145
 
142
146
  tool_name = event.get("tool_name", "")
143
147
  tool_input = event.get("tool_input", {})
@@ -180,9 +184,8 @@ class EventHandlers:
180
184
 
181
185
  CorrelationManager.store(session_id, tool_call_id, tool_name)
182
186
  if DEBUG:
183
- print(
184
- f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}...",
185
- file=sys.stderr,
187
+ _log(
188
+ f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
186
189
  )
187
190
 
188
191
  # Add delegation-specific data if this is a Task tool
@@ -196,7 +199,7 @@ class EventHandlers:
196
199
  auto_pause.on_tool_call(tool_name, tool_input)
197
200
  except Exception as e:
198
201
  if DEBUG:
199
- print(f"Auto-pause tool recording error: {e}", file=sys.stderr)
202
+ _log(f"Auto-pause tool recording error: {e}")
200
203
 
201
204
  self.hook_handler._emit_socketio_event("", "pre_tool", pre_tool_data)
202
205
 
@@ -212,9 +215,8 @@ class EventHandlers:
212
215
  }
213
216
  self.hook_handler._emit_socketio_event("", "todo_updated", todo_data)
214
217
  if DEBUG:
215
- print(
216
- f" - Emitted todo_updated event with {len(tool_params['todos'])} todos for session {session_id[:8]}...",
217
- file=sys.stderr,
218
+ _log(
219
+ f" - Emitted todo_updated event with {len(tool_params['todos'])} todos for session {session_id[:8]}..."
218
220
  )
219
221
 
220
222
  def _handle_task_delegation(
@@ -255,12 +257,9 @@ class EventHandlers:
255
257
 
256
258
  # Track this delegation for SubagentStop correlation and response tracking
257
259
  if DEBUG:
258
- print(
259
- f" - session_id: {session_id[:16] if session_id else 'None'}...",
260
- file=sys.stderr,
261
- )
262
- print(f" - agent_type: {agent_type}", file=sys.stderr)
263
- print(f" - raw_agent_type: {raw_agent_type}", file=sys.stderr)
260
+ _log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
261
+ _log(f" - agent_type: {agent_type}")
262
+ _log(f" - raw_agent_type: {raw_agent_type}")
264
263
 
265
264
  if session_id and agent_type != "unknown":
266
265
  # Prepare request data for response tracking correlation
@@ -272,24 +271,17 @@ class EventHandlers:
272
271
  self.hook_handler._track_delegation(session_id, agent_type, request_data)
273
272
 
274
273
  if DEBUG:
275
- print(" - Delegation tracked successfully", file=sys.stderr)
276
- print(
277
- f" - Request data keys: {list(request_data.keys())}",
278
- file=sys.stderr,
279
- )
274
+ _log(" - Delegation tracked successfully")
275
+ _log(f" - Request data keys: {list(request_data.keys())}")
280
276
  delegation_requests = getattr(
281
277
  self.hook_handler, "delegation_requests", {}
282
278
  )
283
- print(
284
- f" - delegation_requests size: {len(delegation_requests)}",
285
- file=sys.stderr,
286
- )
279
+ _log(f" - delegation_requests size: {len(delegation_requests)}")
287
280
 
288
281
  # Log important delegations for debugging
289
282
  if DEBUG or agent_type in ["research", "engineer", "qa", "documentation"]:
290
- print(
291
- f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'",
292
- file=sys.stderr,
283
+ _log(
284
+ f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'"
293
285
  )
294
286
 
295
287
  # Trigger memory pre-delegation hook
@@ -359,10 +351,10 @@ class EventHandlers:
359
351
  )
360
352
 
361
353
  if DEBUG:
362
- print(f" - Agent prompt logged for {agent_type}", file=sys.stderr)
354
+ _log(f" - Agent prompt logged for {agent_type}")
363
355
  except Exception as e:
364
356
  if DEBUG:
365
- print(f" - Could not log agent prompt: {e}", file=sys.stderr)
357
+ _log(f" - Could not log agent prompt: {e}")
366
358
 
367
359
  def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
368
360
  """Get git branch for the given directory with caching."""
@@ -450,9 +442,8 @@ class EventHandlers:
450
442
 
451
443
  tool_call_id = CorrelationManager.retrieve(session_id) if session_id else None
452
444
  if DEBUG and tool_call_id:
453
- print(
454
- f" - Retrieved tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}...",
455
- file=sys.stderr,
445
+ _log(
446
+ f" - Retrieved tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
456
447
  )
457
448
 
458
449
  post_tool_data = {
@@ -598,19 +589,19 @@ class EventHandlers:
598
589
  threshold_crossed = auto_pause.on_usage_update(metadata["usage"])
599
590
  if threshold_crossed:
600
591
  warning = auto_pause.emit_threshold_warning(threshold_crossed)
601
- print(f"\n⚠️ {warning}", file=sys.stderr)
592
+ # CRITICAL: Never write to stderr unconditionally - causes hook errors
593
+ # Use _log() instead which only writes to file if DEBUG=true
594
+ from . import _log
595
+
596
+ _log(f"⚠️ Auto-pause threshold crossed: {warning}")
602
597
 
603
598
  if DEBUG:
604
- print(
605
- f" - Auto-pause threshold crossed: {threshold_crossed}",
606
- file=sys.stderr,
599
+ _log(
600
+ f" - Auto-pause threshold crossed: {threshold_crossed}"
607
601
  )
608
602
  except Exception as e:
609
603
  if DEBUG:
610
- print(
611
- f"Auto-pause error in handle_stop_fast: {e}",
612
- file=sys.stderr,
613
- )
604
+ _log(f"Auto-pause error in handle_stop_fast: {e}")
614
605
 
615
606
  # Track response if enabled
616
607
  try:
@@ -652,24 +643,15 @@ class EventHandlers:
652
643
  getattr(rtm, "response_tracker", None) is not None if rtm else False
653
644
  )
654
645
 
655
- print(
656
- f" - response_tracking_enabled: {tracking_enabled}",
657
- file=sys.stderr,
658
- )
659
- print(
660
- f" - response_tracker exists: {tracker_exists}",
661
- file=sys.stderr,
662
- )
646
+ _log(f" - response_tracking_enabled: {tracking_enabled}")
647
+ _log(f" - response_tracker exists: {tracker_exists}")
663
648
  except Exception: # nosec B110
664
649
  # If debug logging fails, just skip it
665
650
  pass
666
651
 
667
- print(
668
- f" - session_id: {session_id[:8] if session_id else 'None'}...",
669
- file=sys.stderr,
670
- )
671
- print(f" - reason: {metadata['reason']}", file=sys.stderr)
672
- print(f" - stop_type: {metadata['stop_type']}", file=sys.stderr)
652
+ _log(f" - session_id: {session_id[:8] if session_id else 'None'}...")
653
+ _log(f" - reason: {metadata['reason']}")
654
+ _log(f" - stop_type: {metadata['stop_type']}")
673
655
 
674
656
  def _emit_stop_event(self, event: dict, session_id: str, metadata: dict) -> None:
675
657
  """Emit stop event data to Socket.IO."""
@@ -731,10 +713,7 @@ class EventHandlers:
731
713
  # If exact match fails, try partial matching
732
714
  if not request_info and session_id:
733
715
  if DEBUG:
734
- print(
735
- f" - Trying fuzzy match for session {session_id[:16]}...",
736
- file=sys.stderr,
737
- )
716
+ _log(f" - Trying fuzzy match for session {session_id[:16]}...")
738
717
  # Try to find a session that matches the first 8-16 characters
739
718
  for stored_sid in list(delegation_requests.keys()):
740
719
  if (
@@ -747,10 +726,7 @@ class EventHandlers:
747
726
  )
748
727
  ):
749
728
  if DEBUG:
750
- print(
751
- f" - ✅ Fuzzy match found: {stored_sid[:16]}...",
752
- file=sys.stderr,
753
- )
729
+ _log(f" - ✅ Fuzzy match found: {stored_sid[:16]}...")
754
730
  request_info = delegation_requests.get(stored_sid) # nosec B113
755
731
  # Update the key to use the current session_id for consistency
756
732
  if request_info:
@@ -819,9 +795,8 @@ class EventHandlers:
819
795
  )
820
796
 
821
797
  if file_path and DEBUG:
822
- print(
823
- f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}",
824
- file=sys.stderr,
798
+ _log(
799
+ f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}"
825
800
  )
826
801
 
827
802
  # Clean up the request data
@@ -832,16 +807,13 @@ class EventHandlers:
832
807
  del delegation_requests[session_id]
833
808
 
834
809
  elif DEBUG:
835
- print(
836
- f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}",
837
- file=sys.stderr,
810
+ _log(
811
+ f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}"
838
812
  )
839
813
 
840
814
  except Exception as e:
841
815
  if DEBUG:
842
- print(
843
- f"❌ Failed to track response on SubagentStop: {e}", file=sys.stderr
844
- )
816
+ _log(f"❌ Failed to track response on SubagentStop: {e}")
845
817
 
846
818
  def handle_assistant_response(self, event):
847
819
  """Handle assistant response events for comprehensive response tracking.
@@ -868,7 +840,7 @@ class EventHandlers:
868
840
  self._scan_for_delegation_patterns(event)
869
841
  except Exception as e: # nosec B110
870
842
  if DEBUG:
871
- print(f"Delegation scanning error: {e}", file=sys.stderr)
843
+ _log(f"Delegation scanning error: {e}")
872
844
 
873
845
  # Get working directory and git branch
874
846
  working_dir = event.get("cwd", "")
@@ -917,9 +889,8 @@ class EventHandlers:
917
889
 
918
890
  # Debug logging
919
891
  if DEBUG:
920
- print(
921
- f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}",
922
- file=sys.stderr,
892
+ _log(
893
+ f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}"
923
894
  )
924
895
 
925
896
  # Record assistant response for auto-pause if active
@@ -935,7 +906,7 @@ class EventHandlers:
935
906
  auto_pause.on_assistant_response(summary)
936
907
  except Exception as e:
937
908
  if DEBUG:
938
- print(f"Auto-pause response recording error: {e}", file=sys.stderr)
909
+ _log(f"Auto-pause response recording error: {e}")
939
910
 
940
911
  # Emit normalized event
941
912
  self.hook_handler._emit_socketio_event(
@@ -949,6 +920,7 @@ class EventHandlers:
949
920
  - Provides visibility into new conversation sessions
950
921
  - Enables tracking of session lifecycle and duration
951
922
  - Useful for monitoring concurrent sessions and resource usage
923
+ - Auto-inject pending autotodos if enabled in config
952
924
  """
953
925
  session_id = event.get("session_id", "")
954
926
  working_dir = event.get("cwd", "")
@@ -962,12 +934,36 @@ class EventHandlers:
962
934
  "hook_event_name": "SessionStart",
963
935
  }
964
936
 
937
+ # Auto-inject pending autotodos if enabled
938
+ try:
939
+ from pathlib import Path
940
+
941
+ from claude_mpm.cli.commands.autotodos import get_pending_todos
942
+ from claude_mpm.core.config import Config
943
+
944
+ config = Config()
945
+ auto_inject_enabled = config.get("autotodos.auto_inject_on_startup", True)
946
+ max_todos = config.get("autotodos.max_todos_per_session", 10)
947
+
948
+ if auto_inject_enabled:
949
+ # Pass working directory from event to avoid Path.cwd() issues
950
+ working_dir_param = None
951
+ if working_dir:
952
+ working_dir_param = Path(working_dir)
953
+
954
+ pending_todos = get_pending_todos(
955
+ max_todos=max_todos, working_dir=working_dir_param
956
+ )
957
+ if pending_todos:
958
+ session_start_data["pending_autotodos"] = pending_todos
959
+ session_start_data["autotodos_count"] = len(pending_todos)
960
+ _log(f" - Auto-injected {len(pending_todos)} pending autotodos")
961
+ except Exception as e: # nosec B110
962
+ # Auto-injection is optional - continue if it fails
963
+ _log(f" - Failed to auto-inject autotodos: {e}")
964
+
965
965
  # Debug logging
966
- if DEBUG:
967
- print(
968
- f"Hook handler: Processing SessionStart - session: '{session_id}'",
969
- file=sys.stderr,
970
- )
966
+ _log(f"Hook handler: Processing SessionStart - session: '{session_id}'")
971
967
 
972
968
  # Emit normalized event
973
969
  self.hook_handler._emit_socketio_event("", "session_start", session_start_data)
@@ -1010,10 +1006,8 @@ class EventHandlers:
1010
1006
 
1011
1007
  # Debug logging
1012
1008
  if DEBUG:
1013
- print(
1014
- f"Hook handler: SubagentStart - agent_type='{agent_type}', "
1015
- f"agent_id='{agent_id}', session_id='{session_id[:16]}...'",
1016
- file=sys.stderr,
1009
+ _log(
1010
+ f"Hook handler: SubagentStart - agent_type='{agent_type}', agent_id='{agent_id}', session_id='{session_id[:16]}...'"
1017
1011
  )
1018
1012
 
1019
1013
  # Emit to /hook namespace as subagent_start (NOT session_start!)
@@ -1048,7 +1042,7 @@ class EventHandlers:
1048
1042
  from claude_mpm.services.event_log import get_event_log
1049
1043
  except ImportError:
1050
1044
  if DEBUG:
1051
- print("Delegation detector or event log not available", file=sys.stderr)
1045
+ _log("Delegation detector or event log not available")
1052
1046
  return
1053
1047
 
1054
1048
  response_text = event.get("response", "")
@@ -1087,7 +1081,4 @@ class EventHandlers:
1087
1081
  )
1088
1082
 
1089
1083
  if DEBUG:
1090
- print(
1091
- f"⚠️ PM violation detected: {detection['original_text'][:60]}...",
1092
- file=sys.stderr,
1093
- )
1084
+ _log(f"⚠️ PM violation detected: {detection['original_text'][:60]}...")