claude-mpm 5.6.4__py3-none-any.whl → 5.6.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/PM_INSTRUCTIONS.md +8 -3
  3. claude_mpm/cli/commands/commander.py +173 -3
  4. claude_mpm/cli/commands/skill_source.py +51 -2
  5. claude_mpm/cli/commands/skills.py +5 -3
  6. claude_mpm/cli/parsers/commander_parser.py +41 -8
  7. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  8. claude_mpm/cli/parsers/skills_parser.py +5 -0
  9. claude_mpm/cli/startup.py +10 -1
  10. claude_mpm/cli/startup_display.py +2 -1
  11. claude_mpm/commander/__init__.py +6 -0
  12. claude_mpm/commander/adapters/__init__.py +32 -3
  13. claude_mpm/commander/adapters/auggie.py +260 -0
  14. claude_mpm/commander/adapters/base.py +98 -1
  15. claude_mpm/commander/adapters/claude_code.py +32 -1
  16. claude_mpm/commander/adapters/codex.py +237 -0
  17. claude_mpm/commander/adapters/example_usage.py +310 -0
  18. claude_mpm/commander/adapters/mpm.py +389 -0
  19. claude_mpm/commander/adapters/registry.py +204 -0
  20. claude_mpm/commander/api/app.py +32 -16
  21. claude_mpm/commander/api/errors.py +21 -0
  22. claude_mpm/commander/api/routes/messages.py +11 -11
  23. claude_mpm/commander/api/routes/projects.py +20 -20
  24. claude_mpm/commander/api/routes/sessions.py +37 -26
  25. claude_mpm/commander/api/routes/work.py +86 -50
  26. claude_mpm/commander/api/schemas.py +4 -0
  27. claude_mpm/commander/chat/cli.py +4 -0
  28. claude_mpm/commander/core/__init__.py +10 -0
  29. claude_mpm/commander/core/block_manager.py +325 -0
  30. claude_mpm/commander/core/response_manager.py +323 -0
  31. claude_mpm/commander/daemon.py +206 -10
  32. claude_mpm/commander/env_loader.py +59 -0
  33. claude_mpm/commander/memory/__init__.py +45 -0
  34. claude_mpm/commander/memory/compression.py +347 -0
  35. claude_mpm/commander/memory/embeddings.py +230 -0
  36. claude_mpm/commander/memory/entities.py +310 -0
  37. claude_mpm/commander/memory/example_usage.py +290 -0
  38. claude_mpm/commander/memory/integration.py +325 -0
  39. claude_mpm/commander/memory/search.py +381 -0
  40. claude_mpm/commander/memory/store.py +657 -0
  41. claude_mpm/commander/registry.py +10 -4
  42. claude_mpm/commander/runtime/monitor.py +32 -2
  43. claude_mpm/commander/work/executor.py +38 -20
  44. claude_mpm/commander/workflow/event_handler.py +25 -3
  45. claude_mpm/config/skill_sources.py +16 -0
  46. claude_mpm/core/claude_runner.py +143 -0
  47. claude_mpm/core/config.py +27 -19
  48. claude_mpm/core/output_style_manager.py +34 -7
  49. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  50. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
  51. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  52. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  53. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
  54. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
  55. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  56. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  57. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
  58. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
  59. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  60. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  63. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
  64. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  65. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
  66. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
  67. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  68. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
  69. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
  70. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  71. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
  72. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
  73. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +29 -30
  74. claude_mpm/hooks/claude_hooks/event_handlers.py +22 -0
  75. claude_mpm/hooks/claude_hooks/hook_handler.py +0 -0
  76. claude_mpm/hooks/claude_hooks/installer.py +41 -0
  77. claude_mpm/hooks/claude_hooks/memory_integration.py +30 -21
  78. claude_mpm/hooks/claude_hooks/response_tracking.py +39 -58
  79. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
  88. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
  89. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  90. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
  91. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
  92. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/connection_manager.py +23 -28
  96. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +22 -26
  97. claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
  98. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +47 -73
  99. claude_mpm/hooks/session_resume_hook.py +22 -18
  100. claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
  101. claude_mpm/scripts/claude-hook-handler.sh +5 -5
  102. claude_mpm/scripts/start_activity_logging.py +0 -0
  103. claude_mpm/services/agents/agent_selection_service.py +2 -2
  104. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  105. claude_mpm/services/skills/git_skill_source_manager.py +79 -8
  106. claude_mpm/services/skills/selective_skill_deployer.py +28 -0
  107. claude_mpm/services/skills/skill_discovery_service.py +17 -1
  108. claude_mpm/services/skills_deployer.py +31 -5
  109. claude_mpm/skills/__init__.py +2 -1
  110. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  111. claude_mpm/skills/registry.py +295 -90
  112. {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.16.dist-info}/METADATA +5 -3
  113. {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.16.dist-info}/RECORD +116 -58
  114. {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.16.dist-info}/WHEEL +0 -0
  115. {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.16.dist-info}/entry_points.txt +0 -0
  116. {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.16.dist-info}/licenses/LICENSE +0 -0
  117. {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.16.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  118. {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.16.dist-info}/top_level.txt +0 -0
@@ -8,11 +8,19 @@ with their original requests.
8
8
  import json
9
9
  import os
10
10
  import re
11
- import sys
12
11
  from datetime import datetime, timezone
13
12
  from pathlib import Path
14
13
  from typing import Any, Optional
15
14
 
15
+ # Try to import _log from hook_handler, fall back to no-op
16
+ try:
17
+ from claude_mpm.hooks.claude_hooks.hook_handler import _log
18
+ except ImportError:
19
+
20
+ def _log(msg: str) -> None:
21
+ pass # Silent fallback
22
+
23
+
16
24
  # Debug mode
17
25
  DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
18
26
 
@@ -80,10 +88,7 @@ class ResponseTrackingManager:
80
88
 
81
89
  if not (response_tracking_enabled or response_logging_enabled):
82
90
  if DEBUG:
83
- print(
84
- "Response tracking disabled - skipping initialization",
85
- file=sys.stderr,
86
- )
91
+ _log("Response tracking disabled - skipping initialization")
87
92
  return
88
93
 
89
94
  # Initialize response tracker with config
@@ -101,15 +106,11 @@ class ResponseTrackingManager:
101
106
  if self.track_all_interactions
102
107
  else "Task delegations only"
103
108
  )
104
- print(
105
- f"✅ Response tracking initialized (mode: {mode})", file=sys.stderr
106
- )
109
+ _log(f"✅ Response tracking initialized (mode: {mode})")
107
110
 
108
111
  except Exception as e:
109
112
  if DEBUG:
110
- print(
111
- f"❌ Failed to initialize response tracking: {e}", file=sys.stderr
112
- )
113
+ _log(f"❌ Failed to initialize response tracking: {e}")
113
114
  # Don't fail the entire handler - response tracking is optional
114
115
 
115
116
  def track_agent_response(
@@ -133,9 +134,8 @@ class ResponseTrackingManager:
133
134
  request_info = delegation_requests.get(session_id) # nosec B113 - False positive: dict.get(), not requests library
134
135
  if not request_info:
135
136
  if DEBUG:
136
- print(
137
- f"No request data found for session {session_id}, skipping response tracking",
138
- file=sys.stderr,
137
+ _log(
138
+ f"No request data found for session {session_id}, skipping response tracking"
139
139
  )
140
140
  return
141
141
 
@@ -163,15 +163,11 @@ class ResponseTrackingManager:
163
163
  if json_match:
164
164
  structured_response = json.loads(json_match.group(1))
165
165
  if DEBUG:
166
- print(
167
- f"Extracted structured response from {agent_type} agent",
168
- file=sys.stderr,
169
- )
166
+ _log(f"Extracted structured response from {agent_type} agent")
170
167
  except (json.JSONDecodeError, AttributeError) as e:
171
168
  if DEBUG:
172
- print(
173
- f"No structured JSON response found in {agent_type} agent output: {e}",
174
- file=sys.stderr,
169
+ _log(
170
+ f"No structured JSON response found in {agent_type} agent output: {e}"
175
171
  )
176
172
 
177
173
  # Get the original request (prompt + description)
@@ -220,9 +216,8 @@ class ResponseTrackingManager:
220
216
  if structured_response.get("MEMORIES"):
221
217
  if DEBUG:
222
218
  memories_count = len(structured_response["MEMORIES"])
223
- print(
224
- f"Agent {agent_type} returned MEMORIES field with {memories_count} items",
225
- file=sys.stderr,
219
+ _log(
220
+ f"Agent {agent_type} returned MEMORIES field with {memories_count} items"
226
221
  )
227
222
 
228
223
  # Check if task was completed for logging purposes
@@ -232,9 +227,7 @@ class ResponseTrackingManager:
232
227
  # Log files modified for debugging
233
228
  if DEBUG and structured_response.get("files_modified"):
234
229
  files = [f["file"] for f in structured_response["files_modified"]]
235
- print(
236
- f"Agent {agent_type} modified files: {files}", file=sys.stderr
237
- )
230
+ _log(f"Agent {agent_type} modified files: {files}")
238
231
 
239
232
  # Track the response
240
233
  file_path = self.response_tracker.track_response(
@@ -246,14 +239,12 @@ class ResponseTrackingManager:
246
239
  )
247
240
 
248
241
  if file_path and DEBUG:
249
- print(
250
- f"✅ Tracked response for {agent_type} agent in session {session_id}: {file_path.name}",
251
- file=sys.stderr,
242
+ _log(
243
+ f"✅ Tracked response for {agent_type} agent in session {session_id}: {file_path.name}"
252
244
  )
253
245
  elif DEBUG and not file_path:
254
- print(
255
- f"Response tracking returned None for {agent_type} agent (might be excluded or disabled)",
256
- file=sys.stderr,
246
+ _log(
247
+ f"Response tracking returned None for {agent_type} agent (might be excluded or disabled)"
257
248
  )
258
249
 
259
250
  # Clean up the request data after successful tracking
@@ -261,7 +252,7 @@ class ResponseTrackingManager:
261
252
 
262
253
  except Exception as e:
263
254
  if DEBUG:
264
- print(f"❌ Failed to track agent response: {e}", file=sys.stderr)
255
+ _log(f"❌ Failed to track agent response: {e}")
265
256
  # Don't fail the hook processing - response tracking is optional
266
257
 
267
258
  def track_stop_response(
@@ -286,11 +277,10 @@ class ResponseTrackingManager:
286
277
  prompt_data = pending_prompts.get(session_id)
287
278
 
288
279
  if DEBUG:
289
- print(
290
- f" - output present: {bool(output)} (length: {len(str(output)) if output else 0})",
291
- file=sys.stderr,
280
+ _log(
281
+ f" - output present: {bool(output)} (length: {len(str(output)) if output else 0})"
292
282
  )
293
- print(f" - prompt_data present: {bool(prompt_data)}", file=sys.stderr)
283
+ _log(f" - prompt_data present: {bool(prompt_data)}")
294
284
 
295
285
  if output and prompt_data:
296
286
  # Add prompt timestamp to metadata
@@ -300,10 +290,7 @@ class ResponseTrackingManager:
300
290
  if "stop_reason" in event:
301
291
  metadata["stop_reason"] = event["stop_reason"]
302
292
  if DEBUG:
303
- print(
304
- f" - Captured stop_reason: {event['stop_reason']}",
305
- file=sys.stderr,
306
- )
293
+ _log(f" - Captured stop_reason: {event['stop_reason']}")
307
294
 
308
295
  # Capture Claude API usage data if available
309
296
  # NOTE: Usage data is already captured in metadata by handle_stop_fast()
@@ -324,10 +311,7 @@ class ResponseTrackingManager:
324
311
  total_tokens = usage_data.get(
325
312
  "input_tokens", 0
326
313
  ) + usage_data.get("output_tokens", 0)
327
- print(
328
- f" - Captured usage: {total_tokens} total tokens",
329
- file=sys.stderr,
330
- )
314
+ _log(f" - Captured usage: {total_tokens} total tokens")
331
315
 
332
316
  # Track the main Claude response
333
317
  file_path = self.response_tracker.track_response(
@@ -339,14 +323,14 @@ class ResponseTrackingManager:
339
323
  )
340
324
 
341
325
  if file_path and DEBUG:
342
- print(f" - Response tracked to: {file_path}", file=sys.stderr)
326
+ _log(f" - Response tracked to: {file_path}")
343
327
 
344
328
  # Clean up pending prompt
345
329
  del pending_prompts[session_id]
346
330
 
347
331
  except Exception as e:
348
332
  if DEBUG:
349
- print(f"Error tracking stop response: {e}", file=sys.stderr)
333
+ _log(f"Error tracking stop response: {e}")
350
334
 
351
335
  def track_assistant_response(self, event: dict, pending_prompts: dict):
352
336
  """Handle assistant response events for comprehensive response tracking."""
@@ -361,9 +345,8 @@ class ResponseTrackingManager:
361
345
  prompt_data = pending_prompts.get(session_id)
362
346
  if not prompt_data:
363
347
  if DEBUG:
364
- print(
365
- f"No stored prompt for session {session_id[:8]}..., skipping response tracking",
366
- file=sys.stderr,
348
+ _log(
349
+ f"No stored prompt for session {session_id[:8]}..., skipping response tracking"
367
350
  )
368
351
  return
369
352
 
@@ -377,9 +360,8 @@ class ResponseTrackingManager:
377
360
 
378
361
  if not response_content:
379
362
  if DEBUG:
380
- print(
381
- f"No response content in event for session {session_id[:8]}...",
382
- file=sys.stderr,
363
+ _log(
364
+ f"No response content in event for session {session_id[:8]}..."
383
365
  )
384
366
  return
385
367
 
@@ -401,9 +383,8 @@ class ResponseTrackingManager:
401
383
  )
402
384
 
403
385
  if file_path and DEBUG:
404
- print(
405
- f"✅ Tracked Claude response for session {session_id[:8]}...: {file_path.name}",
406
- file=sys.stderr,
386
+ _log(
387
+ f"✅ Tracked Claude response for session {session_id[:8]}...: {file_path.name}"
407
388
  )
408
389
 
409
390
  # Clean up the stored prompt
@@ -411,4 +392,4 @@ class ResponseTrackingManager:
411
392
 
412
393
  except Exception as e:
413
394
  if DEBUG:
414
- print(f"❌ Failed to track assistant response: {e}", file=sys.stderr)
395
+ _log(f"❌ Failed to track assistant response: {e}")
@@ -20,6 +20,15 @@ import os
20
20
  import sys
21
21
  from datetime import datetime, timezone
22
22
 
23
+ # Try to import _log from hook_handler, fall back to no-op
24
+ try:
25
+ from claude_mpm.hooks.claude_hooks.hook_handler import _log
26
+ except ImportError:
27
+
28
+ def _log(msg: str) -> None:
29
+ pass # Silent fallback
30
+
31
+
23
32
  # Debug mode is enabled by default for better visibility into hook processing
24
33
  DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
25
34
 
@@ -94,13 +103,10 @@ class ConnectionManagerService:
94
103
  try:
95
104
  self.connection_pool = get_connection_pool()
96
105
  if DEBUG:
97
- print("✅ Modern SocketIO connection pool initialized", file=sys.stderr)
106
+ _log("✅ Modern SocketIO connection pool initialized")
98
107
  except Exception as e:
99
108
  if DEBUG:
100
- print(
101
- f"⚠️ Failed to initialize SocketIO connection pool: {e}",
102
- file=sys.stderr,
103
- )
109
+ _log(f"⚠️ Failed to initialize SocketIO connection pool: {e}")
104
110
  self.connection_pool = None
105
111
 
106
112
  def emit_event(self, namespace: str, event: str, data: dict):
@@ -149,10 +155,7 @@ class ConnectionManagerService:
149
155
 
150
156
  # Debug log when we detect invalid hook_type for troubleshooting
151
157
  if DEBUG:
152
- print(
153
- f"⚠️ Invalid hook_type detected, using fallback: {hook_type}",
154
- file=sys.stderr,
155
- )
158
+ _log(f"⚠️ Invalid hook_type detected, using fallback: {hook_type}")
156
159
 
157
160
  event_type = hook_type
158
161
  else:
@@ -177,16 +180,12 @@ class ConnectionManagerService:
177
180
  if DEBUG and event in ["subagent_stop", "pre_tool"]:
178
181
  if event == "subagent_stop":
179
182
  agent_type = data.get("agent_type", "unknown")
180
- print(
181
- f"Hook handler: Publishing SubagentStop for agent '{agent_type}'",
182
- file=sys.stderr,
183
- )
183
+ _log(f"Hook handler: Publishing SubagentStop for agent '{agent_type}'")
184
184
  elif event == "pre_tool" and data.get("tool_name") == "Task":
185
185
  delegation = data.get("delegation_details", {})
186
186
  agent_type = delegation.get("agent_type", "unknown")
187
- print(
188
- f"Hook handler: Publishing Task delegation to agent '{agent_type}'",
189
- file=sys.stderr,
187
+ _log(
188
+ f"Hook handler: Publishing Task delegation to agent '{agent_type}'"
190
189
  )
191
190
 
192
191
  # Emit through direct Socket.IO connection pool (primary path)
@@ -196,11 +195,11 @@ class ConnectionManagerService:
196
195
  # Emit to Socket.IO server directly
197
196
  self.connection_pool.emit("mpm_event", claude_event_data)
198
197
  if DEBUG:
199
- print(f"✅ Emitted via connection pool: {event}", file=sys.stderr)
198
+ _log(f"✅ Emitted via connection pool: {event}")
200
199
  return # Success - no need for fallback
201
200
  except Exception as e:
202
201
  if DEBUG:
203
- print(f"⚠️ Failed to emit via connection pool: {e}", file=sys.stderr)
202
+ _log(f"⚠️ Failed to emit via connection pool: {e}")
204
203
 
205
204
  # HTTP fallback for cross-process communication (when direct calls fail)
206
205
  # This replaces EventBus for reliability without the complexity
@@ -221,22 +220,18 @@ class ConnectionManagerService:
221
220
 
222
221
  if response.status_code in [200, 204]:
223
222
  if DEBUG:
224
- print("✅ HTTP fallback successful", file=sys.stderr)
223
+ _log("✅ HTTP fallback successful")
225
224
  elif DEBUG:
226
- print(
227
- f"⚠️ HTTP fallback failed: {response.status_code}",
228
- file=sys.stderr,
229
- )
225
+ _log(f"⚠️ HTTP fallback failed: {response.status_code}")
230
226
 
231
227
  except Exception as e:
232
228
  if DEBUG:
233
- print(f"⚠️ HTTP fallback error: {e}", file=sys.stderr)
229
+ _log(f"⚠️ HTTP fallback error: {e}")
234
230
 
235
231
  # Warn if no emission method is available
236
232
  if not self.connection_pool and DEBUG:
237
- print(
238
- f"⚠️ No event emission method available for: {claude_event_data.get('event', 'unknown')}",
239
- file=sys.stderr,
233
+ _log(
234
+ f"⚠️ No event emission method available for: {claude_event_data.get('event', 'unknown')}"
240
235
  )
241
236
 
242
237
  def cleanup(self):
@@ -245,5 +240,5 @@ class ConnectionManagerService:
245
240
  if self.connection_pool:
246
241
  try:
247
242
  self.connection_pool.cleanup()
248
- except Exception:
243
+ except Exception: # nosec B110
249
244
  pass # Ignore cleanup errors during destruction
@@ -16,10 +16,18 @@ is simpler and more reliable for ephemeral processes.
16
16
  """
17
17
 
18
18
  import os
19
- import sys
20
19
  from concurrent.futures import ThreadPoolExecutor
21
20
  from datetime import datetime, timezone
22
21
 
22
+ # Try to import _log from hook_handler, fall back to no-op
23
+ try:
24
+ from claude_mpm.hooks.claude_hooks.hook_handler import _log
25
+ except ImportError:
26
+
27
+ def _log(msg: str) -> None:
28
+ pass # Silent fallback
29
+
30
+
23
31
  # Debug mode is enabled by default for better visibility into hook processing
24
32
  DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
25
33
 
@@ -78,9 +86,8 @@ class ConnectionManagerService:
78
86
  )
79
87
 
80
88
  if DEBUG:
81
- print(
82
- f"✅ HTTP connection manager initialized - endpoint: {self.http_endpoint}",
83
- file=sys.stderr,
89
+ _log(
90
+ f"✅ HTTP connection manager initialized - endpoint: {self.http_endpoint}"
84
91
  )
85
92
 
86
93
  def emit_event(self, namespace: str, event: str, data: dict):
@@ -110,16 +117,12 @@ class ConnectionManagerService:
110
117
  if DEBUG and event in ["subagent_stop", "pre_tool"]:
111
118
  if event == "subagent_stop":
112
119
  agent_type = data.get("agent_type", "unknown")
113
- print(
114
- f"Hook handler: Publishing SubagentStop for agent '{agent_type}'",
115
- file=sys.stderr,
116
- )
120
+ _log(f"Hook handler: Publishing SubagentStop for agent '{agent_type}'")
117
121
  elif event == "pre_tool" and data.get("tool_name") == "Task":
118
122
  delegation = data.get("delegation_details", {})
119
123
  agent_type = delegation.get("agent_type", "unknown")
120
- print(
121
- f"Hook handler: Publishing Task delegation to agent '{agent_type}'",
122
- file=sys.stderr,
124
+ _log(
125
+ f"Hook handler: Publishing Task delegation to agent '{agent_type}'"
123
126
  )
124
127
 
125
128
  # Emit via HTTP POST (non-blocking, runs in thread pool)
@@ -133,10 +136,7 @@ class ConnectionManagerService:
133
136
  """
134
137
  if not REQUESTS_AVAILABLE:
135
138
  if DEBUG:
136
- print(
137
- "⚠️ requests module not available - cannot emit via HTTP",
138
- file=sys.stderr,
139
- )
139
+ _log("⚠️ requests module not available - cannot emit via HTTP")
140
140
  return
141
141
 
142
142
  # Submit to thread pool - don't wait for result (fire-and-forget)
@@ -162,25 +162,21 @@ class ConnectionManagerService:
162
162
 
163
163
  if response.status_code in [200, 204]:
164
164
  if DEBUG:
165
- print(f"✅ HTTP POST successful: {event}", file=sys.stderr)
165
+ _log(f"✅ HTTP POST successful: {event}")
166
166
  elif DEBUG:
167
- print(
168
- f"⚠️ HTTP POST failed with status {response.status_code}: {event}",
169
- file=sys.stderr,
170
- )
167
+ _log(f"⚠️ HTTP POST failed with status {response.status_code}: {event}")
171
168
 
172
169
  except requests.exceptions.Timeout:
173
170
  if DEBUG:
174
- print(f"⚠️ HTTP POST timeout for: {event}", file=sys.stderr)
171
+ _log(f"⚠️ HTTP POST timeout for: {event}")
175
172
  except requests.exceptions.ConnectionError:
176
173
  if DEBUG:
177
- print(
178
- f"⚠️ HTTP POST connection failed for: {event} (server not running?)",
179
- file=sys.stderr,
174
+ _log(
175
+ f"⚠️ HTTP POST connection failed for: {event} (server not running?)"
180
176
  )
181
177
  except Exception as e:
182
178
  if DEBUG:
183
- print(f"⚠️ HTTP POST error for {event}: {e}", file=sys.stderr)
179
+ _log(f"⚠️ HTTP POST error for {event}: {e}")
184
180
 
185
181
  def cleanup(self):
186
182
  """Cleanup connections on service destruction."""
@@ -188,4 +184,4 @@ class ConnectionManagerService:
188
184
  if hasattr(self, "_http_executor"):
189
185
  self._http_executor.shutdown(wait=False)
190
186
  if DEBUG:
191
- print("✅ HTTP executor shutdown", file=sys.stderr)
187
+ _log("✅ HTTP executor shutdown")
@@ -8,13 +8,22 @@ This service manages:
8
8
  """
9
9
 
10
10
  import os
11
- import subprocess
11
+ import subprocess # nosec B404
12
12
  import time
13
13
  from collections import deque
14
14
  from datetime import datetime, timezone
15
15
  from pathlib import Path
16
16
  from typing import Optional
17
17
 
18
+ # Try to import _log from hook_handler, fall back to no-op
19
+ try:
20
+ from claude_mpm.hooks.claude_hooks.hook_handler import _log
21
+ except ImportError:
22
+
23
+ def _log(msg: str) -> None:
24
+ pass # Silent fallback
25
+
26
+
18
27
  # Import constants for configuration
19
28
  try:
20
29
  from claude_mpm.core.constants import TimeoutConfig
@@ -63,17 +72,11 @@ class StateManagerService:
63
72
  ):
64
73
  """Track a new agent delegation with optional request data for response correlation."""
65
74
  if DEBUG:
66
- import sys
67
-
68
- print(
69
- f" - session_id: {session_id[:16] if session_id else 'None'}...",
70
- file=sys.stderr,
71
- )
72
- print(f" - agent_type: {agent_type}", file=sys.stderr)
73
- print(f" - request_data provided: {bool(request_data)}", file=sys.stderr)
74
- print(
75
- f" - delegation_requests size before: {len(self.delegation_requests)}",
76
- file=sys.stderr,
75
+ _log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
76
+ _log(f" - agent_type: {agent_type}")
77
+ _log(f" - request_data provided: {bool(request_data)}")
78
+ _log(
79
+ f" - delegation_requests size before: {len(self.delegation_requests)}"
77
80
  )
78
81
 
79
82
  if session_id and agent_type and agent_type != "unknown":
@@ -89,15 +92,9 @@ class StateManagerService:
89
92
  "timestamp": datetime.now(timezone.utc).isoformat(),
90
93
  }
91
94
  if DEBUG:
92
- import sys
93
-
94
- print(
95
- f" - ✅ Stored in delegation_requests[{session_id[:16]}...]",
96
- file=sys.stderr,
97
- )
98
- print(
99
- f" - delegation_requests size after: {len(self.delegation_requests)}",
100
- file=sys.stderr,
95
+ _log(f" - ✅ Stored in delegation_requests[{session_id[:16]}...]")
96
+ _log(
97
+ f" - delegation_requests size after: {len(self.delegation_requests)}"
101
98
  )
102
99
 
103
100
  # Clean up old delegations (older than 5 minutes)
@@ -197,7 +194,7 @@ class StateManagerService:
197
194
  os.chdir(working_dir)
198
195
 
199
196
  # Run git command to get current branch
200
- result = subprocess.run(
197
+ result = subprocess.run( # nosec B603 B607
201
198
  ["git", "branch", "--show-current"],
202
199
  capture_output=True,
203
200
  text=True,
@@ -233,17 +230,12 @@ class StateManagerService:
233
230
  def find_matching_request(self, session_id: str) -> Optional[dict]:
234
231
  """Find matching request data for a session, with fuzzy matching fallback."""
235
232
  # First try exact match
236
- request_info = self.delegation_requests.get(session_id)
233
+ request_info = self.delegation_requests.get(session_id) # nosec B113
237
234
 
238
235
  # If exact match fails, try partial matching
239
236
  if not request_info and session_id:
240
237
  if DEBUG:
241
- import sys
242
-
243
- print(
244
- f" - Trying fuzzy match for session {session_id[:16]}...",
245
- file=sys.stderr,
246
- )
238
+ _log(f" - Trying fuzzy match for session {session_id[:16]}...")
247
239
  # Try to find a session that matches the first 8-16 characters
248
240
  for stored_sid in list(self.delegation_requests.keys()):
249
241
  if (
@@ -256,13 +248,8 @@ class StateManagerService:
256
248
  )
257
249
  ):
258
250
  if DEBUG:
259
- import sys
260
-
261
- print(
262
- f" - ✅ Fuzzy match found: {stored_sid[:16]}...",
263
- file=sys.stderr,
264
- )
265
- request_info = self.delegation_requests.get(stored_sid)
251
+ _log(f" - ✅ Fuzzy match found: {stored_sid[:16]}...")
252
+ request_info = self.delegation_requests.get(stored_sid) # nosec B113
266
253
  # Update the key to use the current session_id for consistency
267
254
  if request_info:
268
255
  self.delegation_requests[session_id] = request_info