claude-mpm 5.4.96__py3-none-any.whl → 5.6.17__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
- claude_mpm/agents/PM_INSTRUCTIONS.md +44 -10
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/commands/autotodos.py +45 -5
- claude_mpm/cli/commands/commander.py +216 -0
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +5 -3
- claude_mpm/cli/executor.py +32 -17
- claude_mpm/cli/parsers/base_parser.py +17 -0
- claude_mpm/cli/parsers/commander_parser.py +116 -0
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/parsers/skill_source_parser.py +4 -0
- claude_mpm/cli/parsers/skills_parser.py +5 -0
- claude_mpm/cli/startup.py +124 -3
- claude_mpm/cli/startup_display.py +2 -1
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +78 -0
- claude_mpm/commander/adapters/__init__.py +60 -0
- claude_mpm/commander/adapters/auggie.py +260 -0
- claude_mpm/commander/adapters/base.py +288 -0
- claude_mpm/commander/adapters/claude_code.py +392 -0
- claude_mpm/commander/adapters/codex.py +237 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/adapters/example_usage.py +310 -0
- claude_mpm/commander/adapters/mpm.py +389 -0
- claude_mpm/commander/adapters/registry.py +204 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +121 -0
- claude_mpm/commander/api/errors.py +133 -0
- claude_mpm/commander/api/routes/__init__.py +8 -0
- claude_mpm/commander/api/routes/events.py +184 -0
- claude_mpm/commander/api/routes/inbox.py +171 -0
- claude_mpm/commander/api/routes/messages.py +148 -0
- claude_mpm/commander/api/routes/projects.py +271 -0
- claude_mpm/commander/api/routes/sessions.py +226 -0
- claude_mpm/commander/api/routes/work.py +296 -0
- claude_mpm/commander/api/schemas.py +186 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +111 -0
- claude_mpm/commander/chat/commands.py +96 -0
- claude_mpm/commander/chat/repl.py +310 -0
- claude_mpm/commander/config.py +49 -0
- claude_mpm/commander/config_loader.py +115 -0
- claude_mpm/commander/core/__init__.py +10 -0
- claude_mpm/commander/core/block_manager.py +325 -0
- claude_mpm/commander/core/response_manager.py +323 -0
- claude_mpm/commander/daemon.py +594 -0
- claude_mpm/commander/env_loader.py +59 -0
- claude_mpm/commander/events/__init__.py +26 -0
- claude_mpm/commander/events/manager.py +332 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +143 -0
- claude_mpm/commander/frameworks/claude_code.py +58 -0
- claude_mpm/commander/frameworks/mpm.py +62 -0
- claude_mpm/commander/inbox/__init__.py +16 -0
- claude_mpm/commander/inbox/dedup.py +128 -0
- claude_mpm/commander/inbox/inbox.py +224 -0
- claude_mpm/commander/inbox/models.py +70 -0
- claude_mpm/commander/instance_manager.py +337 -0
- claude_mpm/commander/llm/__init__.py +6 -0
- claude_mpm/commander/llm/openrouter_client.py +167 -0
- claude_mpm/commander/llm/summarizer.py +70 -0
- claude_mpm/commander/memory/__init__.py +45 -0
- claude_mpm/commander/memory/compression.py +347 -0
- claude_mpm/commander/memory/embeddings.py +230 -0
- claude_mpm/commander/memory/entities.py +310 -0
- claude_mpm/commander/memory/example_usage.py +290 -0
- claude_mpm/commander/memory/integration.py +325 -0
- claude_mpm/commander/memory/search.py +381 -0
- claude_mpm/commander/memory/store.py +657 -0
- claude_mpm/commander/models/__init__.py +18 -0
- claude_mpm/commander/models/events.py +121 -0
- claude_mpm/commander/models/project.py +162 -0
- claude_mpm/commander/models/work.py +214 -0
- claude_mpm/commander/parsing/__init__.py +20 -0
- claude_mpm/commander/parsing/extractor.py +132 -0
- claude_mpm/commander/parsing/output_parser.py +270 -0
- claude_mpm/commander/parsing/patterns.py +100 -0
- claude_mpm/commander/persistence/__init__.py +11 -0
- claude_mpm/commander/persistence/event_store.py +274 -0
- claude_mpm/commander/persistence/state_store.py +309 -0
- claude_mpm/commander/persistence/work_store.py +164 -0
- claude_mpm/commander/polling/__init__.py +13 -0
- claude_mpm/commander/polling/event_detector.py +104 -0
- claude_mpm/commander/polling/output_buffer.py +49 -0
- claude_mpm/commander/polling/output_poller.py +153 -0
- claude_mpm/commander/project_session.py +268 -0
- claude_mpm/commander/proxy/__init__.py +12 -0
- claude_mpm/commander/proxy/formatter.py +89 -0
- claude_mpm/commander/proxy/output_handler.py +191 -0
- claude_mpm/commander/proxy/relay.py +155 -0
- claude_mpm/commander/registry.py +410 -0
- claude_mpm/commander/runtime/__init__.py +10 -0
- claude_mpm/commander/runtime/executor.py +191 -0
- claude_mpm/commander/runtime/monitor.py +346 -0
- claude_mpm/commander/session/__init__.py +6 -0
- claude_mpm/commander/session/context.py +81 -0
- claude_mpm/commander/session/manager.py +59 -0
- claude_mpm/commander/tmux_orchestrator.py +361 -0
- claude_mpm/commander/web/__init__.py +1 -0
- claude_mpm/commander/work/__init__.py +30 -0
- claude_mpm/commander/work/executor.py +207 -0
- claude_mpm/commander/work/queue.py +405 -0
- claude_mpm/commander/workflow/__init__.py +27 -0
- claude_mpm/commander/workflow/event_handler.py +241 -0
- claude_mpm/commander/workflow/notifier.py +146 -0
- claude_mpm/commands/mpm-config.md +8 -0
- claude_mpm/commands/mpm-doctor.md +8 -0
- claude_mpm/commands/mpm-help.md +8 -0
- claude_mpm/commands/mpm-init.md +8 -0
- claude_mpm/commands/mpm-monitor.md +8 -0
- claude_mpm/commands/mpm-organize.md +8 -0
- claude_mpm/commands/mpm-postmortem.md +8 -0
- claude_mpm/commands/mpm-session-resume.md +8 -0
- claude_mpm/commands/mpm-status.md +8 -0
- claude_mpm/commands/mpm-ticket-view.md +8 -0
- claude_mpm/commands/mpm-version.md +8 -0
- claude_mpm/commands/mpm.md +8 -0
- claude_mpm/config/agent_presets.py +8 -7
- claude_mpm/config/skill_sources.py +16 -0
- claude_mpm/core/claude_runner.py +143 -0
- claude_mpm/core/config.py +32 -19
- claude_mpm/core/logger.py +26 -9
- claude_mpm/core/logging_utils.py +35 -11
- claude_mpm/core/output_style_manager.py +49 -12
- claude_mpm/core/unified_config.py +10 -6
- claude_mpm/core/unified_paths.py +68 -80
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +29 -30
- claude_mpm/hooks/claude_hooks/event_handlers.py +112 -99
- claude_mpm/hooks/claude_hooks/hook_handler.py +81 -88
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +116 -8
- claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
- claude_mpm/hooks/claude_hooks/response_tracking.py +39 -58
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +23 -28
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
- claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +47 -73
- claude_mpm/hooks/session_resume_hook.py +22 -18
- claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
- claude_mpm/scripts/claude-hook-handler.sh +43 -16
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
- claude_mpm/services/event_log.py +8 -0
- claude_mpm/services/pm_skills_deployer.py +84 -6
- claude_mpm/services/skills/git_skill_source_manager.py +130 -10
- claude_mpm/services/skills/selective_skill_deployer.py +28 -0
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +31 -5
- claude_mpm/skills/__init__.py +2 -1
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
- claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
- claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
- claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
- claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
- claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
- claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
- claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/registry.py +295 -90
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/METADATA +22 -6
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/RECORD +213 -83
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/top_level.txt +0 -0
|
@@ -22,7 +22,7 @@ USAGE:
|
|
|
22
22
|
threshold_crossed = auto_pause.on_usage_update(metadata["usage"])
|
|
23
23
|
if threshold_crossed:
|
|
24
24
|
warning = auto_pause.emit_threshold_warning(threshold_crossed)
|
|
25
|
-
|
|
25
|
+
_log(f"\n⚠️ {warning}")
|
|
26
26
|
|
|
27
27
|
# Record actions during pause mode
|
|
28
28
|
if auto_pause.is_pause_active():
|
|
@@ -34,7 +34,6 @@ USAGE:
|
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
36
|
import os
|
|
37
|
-
import sys
|
|
38
37
|
from datetime import datetime, timezone
|
|
39
38
|
from pathlib import Path
|
|
40
39
|
from typing import Any, Dict, Optional
|
|
@@ -45,6 +44,15 @@ from claude_mpm.services.infrastructure.context_usage_tracker import (
|
|
|
45
44
|
ContextUsageTracker,
|
|
46
45
|
)
|
|
47
46
|
|
|
47
|
+
# Try to import _log from hook_handler, fall back to no-op
|
|
48
|
+
try:
|
|
49
|
+
from claude_mpm.hooks.claude_hooks.hook_handler import _log
|
|
50
|
+
except ImportError:
|
|
51
|
+
|
|
52
|
+
def _log(msg: str) -> None:
|
|
53
|
+
pass # Silent fallback
|
|
54
|
+
|
|
55
|
+
|
|
48
56
|
logger = get_logger(__name__)
|
|
49
57
|
|
|
50
58
|
# Debug mode
|
|
@@ -100,11 +108,10 @@ class AutoPauseHandler:
|
|
|
100
108
|
self._previous_threshold = current_state.threshold_reached
|
|
101
109
|
|
|
102
110
|
if DEBUG:
|
|
103
|
-
|
|
111
|
+
_log(
|
|
104
112
|
f"AutoPauseHandler initialized: "
|
|
105
113
|
f"{current_state.percentage_used:.1f}% context used, "
|
|
106
|
-
f"threshold: {current_state.threshold_reached}"
|
|
107
|
-
file=sys.stderr,
|
|
114
|
+
f"threshold: {current_state.threshold_reached}"
|
|
108
115
|
)
|
|
109
116
|
except Exception as e:
|
|
110
117
|
logger.error(f"Failed to initialize AutoPauseHandler: {e}")
|
|
@@ -169,10 +176,9 @@ class AutoPauseHandler:
|
|
|
169
176
|
self._previous_threshold = current_threshold
|
|
170
177
|
|
|
171
178
|
if DEBUG:
|
|
172
|
-
|
|
179
|
+
_log(
|
|
173
180
|
f"Context threshold crossed: {current_threshold} "
|
|
174
|
-
f"({state.percentage_used:.1f}%)"
|
|
175
|
-
file=sys.stderr,
|
|
181
|
+
f"({state.percentage_used:.1f}%)"
|
|
176
182
|
)
|
|
177
183
|
|
|
178
184
|
# Trigger auto-pause if threshold reached
|
|
@@ -184,7 +190,7 @@ class AutoPauseHandler:
|
|
|
184
190
|
except Exception as e:
|
|
185
191
|
logger.error(f"Failed to update usage: {e}")
|
|
186
192
|
if DEBUG:
|
|
187
|
-
|
|
193
|
+
_log(f"❌ Usage update failed: {e}")
|
|
188
194
|
# Don't propagate error - auto-pause is optional
|
|
189
195
|
return None
|
|
190
196
|
|
|
@@ -220,12 +226,12 @@ class AutoPauseHandler:
|
|
|
220
226
|
)
|
|
221
227
|
|
|
222
228
|
if DEBUG:
|
|
223
|
-
|
|
229
|
+
_log(f"Recorded tool call during pause: {tool_name}")
|
|
224
230
|
|
|
225
231
|
except Exception as e:
|
|
226
232
|
logger.error(f"Failed to record tool call: {e}")
|
|
227
233
|
if DEBUG:
|
|
228
|
-
|
|
234
|
+
_log(f"❌ Failed to record tool call: {e}")
|
|
229
235
|
|
|
230
236
|
def on_assistant_response(self, response_summary: str) -> None:
|
|
231
237
|
"""Record an assistant response if auto-pause is active.
|
|
@@ -257,15 +263,14 @@ class AutoPauseHandler:
|
|
|
257
263
|
)
|
|
258
264
|
|
|
259
265
|
if DEBUG:
|
|
260
|
-
|
|
261
|
-
f"Recorded assistant response during pause (length: {len(summary)})"
|
|
262
|
-
file=sys.stderr,
|
|
266
|
+
_log(
|
|
267
|
+
f"Recorded assistant response during pause (length: {len(summary)})"
|
|
263
268
|
)
|
|
264
269
|
|
|
265
270
|
except Exception as e:
|
|
266
271
|
logger.error(f"Failed to record assistant response: {e}")
|
|
267
272
|
if DEBUG:
|
|
268
|
-
|
|
273
|
+
_log(f"❌ Failed to record assistant response: {e}")
|
|
269
274
|
|
|
270
275
|
def on_user_message(self, message_summary: str) -> None:
|
|
271
276
|
"""Record a user message if auto-pause is active.
|
|
@@ -297,15 +302,12 @@ class AutoPauseHandler:
|
|
|
297
302
|
)
|
|
298
303
|
|
|
299
304
|
if DEBUG:
|
|
300
|
-
|
|
301
|
-
f"Recorded user message during pause (length: {len(summary)})",
|
|
302
|
-
file=sys.stderr,
|
|
303
|
-
)
|
|
305
|
+
_log(f"Recorded user message during pause (length: {len(summary)})")
|
|
304
306
|
|
|
305
307
|
except Exception as e:
|
|
306
308
|
logger.error(f"Failed to record user message: {e}")
|
|
307
309
|
if DEBUG:
|
|
308
|
-
|
|
310
|
+
_log(f"❌ Failed to record user message: {e}")
|
|
309
311
|
|
|
310
312
|
def on_session_end(self) -> Optional[Path]:
|
|
311
313
|
"""Called when session ends. Finalizes any active pause.
|
|
@@ -318,7 +320,7 @@ class AutoPauseHandler:
|
|
|
318
320
|
"""
|
|
319
321
|
if not self.is_pause_active():
|
|
320
322
|
if DEBUG:
|
|
321
|
-
|
|
323
|
+
_log("No active pause to finalize")
|
|
322
324
|
return None
|
|
323
325
|
|
|
324
326
|
try:
|
|
@@ -326,14 +328,14 @@ class AutoPauseHandler:
|
|
|
326
328
|
session_path = self.pause_manager.finalize_pause(create_full_snapshot=True)
|
|
327
329
|
|
|
328
330
|
if session_path and DEBUG:
|
|
329
|
-
|
|
331
|
+
_log(f"✅ Session finalized: {session_path.name}")
|
|
330
332
|
|
|
331
333
|
return session_path
|
|
332
334
|
|
|
333
335
|
except Exception as e:
|
|
334
336
|
logger.error(f"Failed to finalize pause session: {e}")
|
|
335
337
|
if DEBUG:
|
|
336
|
-
|
|
338
|
+
_log(f"❌ Failed to finalize pause: {e}")
|
|
337
339
|
raise
|
|
338
340
|
|
|
339
341
|
def is_pause_active(self) -> bool:
|
|
@@ -417,9 +419,7 @@ class AutoPauseHandler:
|
|
|
417
419
|
# Check if pause is already active
|
|
418
420
|
if self.is_pause_active():
|
|
419
421
|
if DEBUG:
|
|
420
|
-
|
|
421
|
-
"Auto-pause already active, skipping trigger", file=sys.stderr
|
|
422
|
-
)
|
|
422
|
+
_log("Auto-pause already active, skipping trigger")
|
|
423
423
|
return
|
|
424
424
|
|
|
425
425
|
# Start incremental pause
|
|
@@ -429,16 +429,15 @@ class AutoPauseHandler:
|
|
|
429
429
|
)
|
|
430
430
|
|
|
431
431
|
if DEBUG:
|
|
432
|
-
|
|
432
|
+
_log(
|
|
433
433
|
f"✅ Auto-pause triggered: {session_id} "
|
|
434
|
-
f"({state.percentage_used:.1f}% context used)"
|
|
435
|
-
file=sys.stderr,
|
|
434
|
+
f"({state.percentage_used:.1f}% context used)"
|
|
436
435
|
)
|
|
437
436
|
|
|
438
437
|
except Exception as e:
|
|
439
438
|
logger.error(f"Failed to trigger auto-pause: {e}")
|
|
440
439
|
if DEBUG:
|
|
441
|
-
|
|
440
|
+
_log(f"❌ Failed to trigger auto-pause: {e}")
|
|
442
441
|
# Don't propagate - auto-pause is optional
|
|
443
442
|
|
|
444
443
|
def _summarize_dict(
|
|
@@ -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", "
|
|
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,14 +119,22 @@ class EventHandlers:
|
|
|
111
119
|
"working_directory": working_dir,
|
|
112
120
|
}
|
|
113
121
|
if DEBUG:
|
|
114
|
-
|
|
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
|
|
120
127
|
pass
|
|
121
128
|
|
|
129
|
+
# Record user message for auto-pause if active
|
|
130
|
+
auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
|
|
131
|
+
if auto_pause and auto_pause.is_pause_active():
|
|
132
|
+
try:
|
|
133
|
+
auto_pause.on_user_message(prompt)
|
|
134
|
+
except Exception as e:
|
|
135
|
+
if DEBUG:
|
|
136
|
+
_log(f"Auto-pause user message recording error: {e}")
|
|
137
|
+
|
|
122
138
|
# Emit normalized event (namespace no longer needed with normalized events)
|
|
123
139
|
self.hook_handler._emit_socketio_event("", "user_prompt", prompt_data)
|
|
124
140
|
|
|
@@ -133,11 +149,8 @@ class EventHandlers:
|
|
|
133
149
|
# Enhanced debug logging for session correlation
|
|
134
150
|
session_id = event.get("session_id", "")
|
|
135
151
|
if DEBUG:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
file=sys.stderr,
|
|
139
|
-
)
|
|
140
|
-
print(f" - event keys: {list(event.keys())}", file=sys.stderr)
|
|
152
|
+
_log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
|
|
153
|
+
_log(f" - event keys: {list(event.keys())}")
|
|
141
154
|
|
|
142
155
|
tool_name = event.get("tool_name", "")
|
|
143
156
|
tool_input = event.get("tool_input", {})
|
|
@@ -180,9 +193,8 @@ class EventHandlers:
|
|
|
180
193
|
|
|
181
194
|
CorrelationManager.store(session_id, tool_call_id, tool_name)
|
|
182
195
|
if DEBUG:
|
|
183
|
-
|
|
184
|
-
f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
|
|
185
|
-
file=sys.stderr,
|
|
196
|
+
_log(
|
|
197
|
+
f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
|
|
186
198
|
)
|
|
187
199
|
|
|
188
200
|
# Add delegation-specific data if this is a Task tool
|
|
@@ -196,7 +208,7 @@ class EventHandlers:
|
|
|
196
208
|
auto_pause.on_tool_call(tool_name, tool_input)
|
|
197
209
|
except Exception as e:
|
|
198
210
|
if DEBUG:
|
|
199
|
-
|
|
211
|
+
_log(f"Auto-pause tool recording error: {e}")
|
|
200
212
|
|
|
201
213
|
self.hook_handler._emit_socketio_event("", "pre_tool", pre_tool_data)
|
|
202
214
|
|
|
@@ -212,9 +224,8 @@ class EventHandlers:
|
|
|
212
224
|
}
|
|
213
225
|
self.hook_handler._emit_socketio_event("", "todo_updated", todo_data)
|
|
214
226
|
if DEBUG:
|
|
215
|
-
|
|
216
|
-
f" - Emitted todo_updated event with {len(tool_params['todos'])} todos for session {session_id[:8]}..."
|
|
217
|
-
file=sys.stderr,
|
|
227
|
+
_log(
|
|
228
|
+
f" - Emitted todo_updated event with {len(tool_params['todos'])} todos for session {session_id[:8]}..."
|
|
218
229
|
)
|
|
219
230
|
|
|
220
231
|
def _handle_task_delegation(
|
|
@@ -255,12 +266,9 @@ class EventHandlers:
|
|
|
255
266
|
|
|
256
267
|
# Track this delegation for SubagentStop correlation and response tracking
|
|
257
268
|
if DEBUG:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
)
|
|
262
|
-
print(f" - agent_type: {agent_type}", file=sys.stderr)
|
|
263
|
-
print(f" - raw_agent_type: {raw_agent_type}", file=sys.stderr)
|
|
269
|
+
_log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
|
|
270
|
+
_log(f" - agent_type: {agent_type}")
|
|
271
|
+
_log(f" - raw_agent_type: {raw_agent_type}")
|
|
264
272
|
|
|
265
273
|
if session_id and agent_type != "unknown":
|
|
266
274
|
# Prepare request data for response tracking correlation
|
|
@@ -272,24 +280,17 @@ class EventHandlers:
|
|
|
272
280
|
self.hook_handler._track_delegation(session_id, agent_type, request_data)
|
|
273
281
|
|
|
274
282
|
if DEBUG:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
f" - Request data keys: {list(request_data.keys())}",
|
|
278
|
-
file=sys.stderr,
|
|
279
|
-
)
|
|
283
|
+
_log(" - Delegation tracked successfully")
|
|
284
|
+
_log(f" - Request data keys: {list(request_data.keys())}")
|
|
280
285
|
delegation_requests = getattr(
|
|
281
286
|
self.hook_handler, "delegation_requests", {}
|
|
282
287
|
)
|
|
283
|
-
|
|
284
|
-
f" - delegation_requests size: {len(delegation_requests)}",
|
|
285
|
-
file=sys.stderr,
|
|
286
|
-
)
|
|
288
|
+
_log(f" - delegation_requests size: {len(delegation_requests)}")
|
|
287
289
|
|
|
288
290
|
# Log important delegations for debugging
|
|
289
291
|
if DEBUG or agent_type in ["research", "engineer", "qa", "documentation"]:
|
|
290
|
-
|
|
291
|
-
f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'"
|
|
292
|
-
file=sys.stderr,
|
|
292
|
+
_log(
|
|
293
|
+
f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'"
|
|
293
294
|
)
|
|
294
295
|
|
|
295
296
|
# Trigger memory pre-delegation hook
|
|
@@ -359,10 +360,10 @@ class EventHandlers:
|
|
|
359
360
|
)
|
|
360
361
|
|
|
361
362
|
if DEBUG:
|
|
362
|
-
|
|
363
|
+
_log(f" - Agent prompt logged for {agent_type}")
|
|
363
364
|
except Exception as e:
|
|
364
365
|
if DEBUG:
|
|
365
|
-
|
|
366
|
+
_log(f" - Could not log agent prompt: {e}")
|
|
366
367
|
|
|
367
368
|
def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
|
|
368
369
|
"""Get git branch for the given directory with caching."""
|
|
@@ -450,9 +451,8 @@ class EventHandlers:
|
|
|
450
451
|
|
|
451
452
|
tool_call_id = CorrelationManager.retrieve(session_id) if session_id else None
|
|
452
453
|
if DEBUG and tool_call_id:
|
|
453
|
-
|
|
454
|
-
f" - Retrieved tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
|
|
455
|
-
file=sys.stderr,
|
|
454
|
+
_log(
|
|
455
|
+
f" - Retrieved tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
|
|
456
456
|
)
|
|
457
457
|
|
|
458
458
|
post_tool_data = {
|
|
@@ -598,19 +598,32 @@ class EventHandlers:
|
|
|
598
598
|
threshold_crossed = auto_pause.on_usage_update(metadata["usage"])
|
|
599
599
|
if threshold_crossed:
|
|
600
600
|
warning = auto_pause.emit_threshold_warning(threshold_crossed)
|
|
601
|
-
|
|
601
|
+
# CRITICAL: Never write to stderr unconditionally - causes hook errors
|
|
602
|
+
# Use _log() instead which only writes to file if DEBUG=true
|
|
603
|
+
from . import _log
|
|
604
|
+
|
|
605
|
+
_log(f"⚠️ Auto-pause threshold crossed: {warning}")
|
|
602
606
|
|
|
603
607
|
if DEBUG:
|
|
604
|
-
|
|
605
|
-
f" - Auto-pause threshold crossed: {threshold_crossed}"
|
|
606
|
-
file=sys.stderr,
|
|
608
|
+
_log(
|
|
609
|
+
f" - Auto-pause threshold crossed: {threshold_crossed}"
|
|
607
610
|
)
|
|
608
611
|
except Exception as e:
|
|
609
612
|
if DEBUG:
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
613
|
+
_log(f"Auto-pause error in handle_stop_fast: {e}")
|
|
614
|
+
|
|
615
|
+
# Finalize pause session if active
|
|
616
|
+
try:
|
|
617
|
+
if auto_pause.is_pause_active():
|
|
618
|
+
session_file = auto_pause.on_session_end()
|
|
619
|
+
if session_file:
|
|
620
|
+
if DEBUG:
|
|
621
|
+
_log(
|
|
622
|
+
f"✅ Auto-pause session finalized: {session_file.name}"
|
|
623
|
+
)
|
|
624
|
+
except Exception as e:
|
|
625
|
+
if DEBUG:
|
|
626
|
+
_log(f"❌ Failed to finalize auto-pause session: {e}")
|
|
614
627
|
|
|
615
628
|
# Track response if enabled
|
|
616
629
|
try:
|
|
@@ -652,24 +665,15 @@ class EventHandlers:
|
|
|
652
665
|
getattr(rtm, "response_tracker", None) is not None if rtm else False
|
|
653
666
|
)
|
|
654
667
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
file=sys.stderr,
|
|
658
|
-
)
|
|
659
|
-
print(
|
|
660
|
-
f" - response_tracker exists: {tracker_exists}",
|
|
661
|
-
file=sys.stderr,
|
|
662
|
-
)
|
|
668
|
+
_log(f" - response_tracking_enabled: {tracking_enabled}")
|
|
669
|
+
_log(f" - response_tracker exists: {tracker_exists}")
|
|
663
670
|
except Exception: # nosec B110
|
|
664
671
|
# If debug logging fails, just skip it
|
|
665
672
|
pass
|
|
666
673
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
)
|
|
671
|
-
print(f" - reason: {metadata['reason']}", file=sys.stderr)
|
|
672
|
-
print(f" - stop_type: {metadata['stop_type']}", file=sys.stderr)
|
|
674
|
+
_log(f" - session_id: {session_id[:8] if session_id else 'None'}...")
|
|
675
|
+
_log(f" - reason: {metadata['reason']}")
|
|
676
|
+
_log(f" - stop_type: {metadata['stop_type']}")
|
|
673
677
|
|
|
674
678
|
def _emit_stop_event(self, event: dict, session_id: str, metadata: dict) -> None:
|
|
675
679
|
"""Emit stop event data to Socket.IO."""
|
|
@@ -731,10 +735,7 @@ class EventHandlers:
|
|
|
731
735
|
# If exact match fails, try partial matching
|
|
732
736
|
if not request_info and session_id:
|
|
733
737
|
if DEBUG:
|
|
734
|
-
|
|
735
|
-
f" - Trying fuzzy match for session {session_id[:16]}...",
|
|
736
|
-
file=sys.stderr,
|
|
737
|
-
)
|
|
738
|
+
_log(f" - Trying fuzzy match for session {session_id[:16]}...")
|
|
738
739
|
# Try to find a session that matches the first 8-16 characters
|
|
739
740
|
for stored_sid in list(delegation_requests.keys()):
|
|
740
741
|
if (
|
|
@@ -747,10 +748,7 @@ class EventHandlers:
|
|
|
747
748
|
)
|
|
748
749
|
):
|
|
749
750
|
if DEBUG:
|
|
750
|
-
|
|
751
|
-
f" - ✅ Fuzzy match found: {stored_sid[:16]}...",
|
|
752
|
-
file=sys.stderr,
|
|
753
|
-
)
|
|
751
|
+
_log(f" - ✅ Fuzzy match found: {stored_sid[:16]}...")
|
|
754
752
|
request_info = delegation_requests.get(stored_sid) # nosec B113
|
|
755
753
|
# Update the key to use the current session_id for consistency
|
|
756
754
|
if request_info:
|
|
@@ -819,9 +817,8 @@ class EventHandlers:
|
|
|
819
817
|
)
|
|
820
818
|
|
|
821
819
|
if file_path and DEBUG:
|
|
822
|
-
|
|
823
|
-
f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}"
|
|
824
|
-
file=sys.stderr,
|
|
820
|
+
_log(
|
|
821
|
+
f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}"
|
|
825
822
|
)
|
|
826
823
|
|
|
827
824
|
# Clean up the request data
|
|
@@ -832,16 +829,13 @@ class EventHandlers:
|
|
|
832
829
|
del delegation_requests[session_id]
|
|
833
830
|
|
|
834
831
|
elif DEBUG:
|
|
835
|
-
|
|
836
|
-
f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}"
|
|
837
|
-
file=sys.stderr,
|
|
832
|
+
_log(
|
|
833
|
+
f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}"
|
|
838
834
|
)
|
|
839
835
|
|
|
840
836
|
except Exception as e:
|
|
841
837
|
if DEBUG:
|
|
842
|
-
|
|
843
|
-
f"❌ Failed to track response on SubagentStop: {e}", file=sys.stderr
|
|
844
|
-
)
|
|
838
|
+
_log(f"❌ Failed to track response on SubagentStop: {e}")
|
|
845
839
|
|
|
846
840
|
def handle_assistant_response(self, event):
|
|
847
841
|
"""Handle assistant response events for comprehensive response tracking.
|
|
@@ -868,7 +862,7 @@ class EventHandlers:
|
|
|
868
862
|
self._scan_for_delegation_patterns(event)
|
|
869
863
|
except Exception as e: # nosec B110
|
|
870
864
|
if DEBUG:
|
|
871
|
-
|
|
865
|
+
_log(f"Delegation scanning error: {e}")
|
|
872
866
|
|
|
873
867
|
# Get working directory and git branch
|
|
874
868
|
working_dir = event.get("cwd", "")
|
|
@@ -917,9 +911,8 @@ class EventHandlers:
|
|
|
917
911
|
|
|
918
912
|
# Debug logging
|
|
919
913
|
if DEBUG:
|
|
920
|
-
|
|
921
|
-
f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}"
|
|
922
|
-
file=sys.stderr,
|
|
914
|
+
_log(
|
|
915
|
+
f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}"
|
|
923
916
|
)
|
|
924
917
|
|
|
925
918
|
# Record assistant response for auto-pause if active
|
|
@@ -935,7 +928,7 @@ class EventHandlers:
|
|
|
935
928
|
auto_pause.on_assistant_response(summary)
|
|
936
929
|
except Exception as e:
|
|
937
930
|
if DEBUG:
|
|
938
|
-
|
|
931
|
+
_log(f"Auto-pause response recording error: {e}")
|
|
939
932
|
|
|
940
933
|
# Emit normalized event
|
|
941
934
|
self.hook_handler._emit_socketio_event(
|
|
@@ -949,6 +942,7 @@ class EventHandlers:
|
|
|
949
942
|
- Provides visibility into new conversation sessions
|
|
950
943
|
- Enables tracking of session lifecycle and duration
|
|
951
944
|
- Useful for monitoring concurrent sessions and resource usage
|
|
945
|
+
- Auto-inject pending autotodos if enabled in config
|
|
952
946
|
"""
|
|
953
947
|
session_id = event.get("session_id", "")
|
|
954
948
|
working_dir = event.get("cwd", "")
|
|
@@ -962,12 +956,36 @@ class EventHandlers:
|
|
|
962
956
|
"hook_event_name": "SessionStart",
|
|
963
957
|
}
|
|
964
958
|
|
|
959
|
+
# Auto-inject pending autotodos if enabled
|
|
960
|
+
try:
|
|
961
|
+
from pathlib import Path
|
|
962
|
+
|
|
963
|
+
from claude_mpm.cli.commands.autotodos import get_pending_todos
|
|
964
|
+
from claude_mpm.core.config import Config
|
|
965
|
+
|
|
966
|
+
config = Config()
|
|
967
|
+
auto_inject_enabled = config.get("autotodos.auto_inject_on_startup", True)
|
|
968
|
+
max_todos = config.get("autotodos.max_todos_per_session", 10)
|
|
969
|
+
|
|
970
|
+
if auto_inject_enabled:
|
|
971
|
+
# Pass working directory from event to avoid Path.cwd() issues
|
|
972
|
+
working_dir_param = None
|
|
973
|
+
if working_dir:
|
|
974
|
+
working_dir_param = Path(working_dir)
|
|
975
|
+
|
|
976
|
+
pending_todos = get_pending_todos(
|
|
977
|
+
max_todos=max_todos, working_dir=working_dir_param
|
|
978
|
+
)
|
|
979
|
+
if pending_todos:
|
|
980
|
+
session_start_data["pending_autotodos"] = pending_todos
|
|
981
|
+
session_start_data["autotodos_count"] = len(pending_todos)
|
|
982
|
+
_log(f" - Auto-injected {len(pending_todos)} pending autotodos")
|
|
983
|
+
except Exception as e: # nosec B110
|
|
984
|
+
# Auto-injection is optional - continue if it fails
|
|
985
|
+
_log(f" - Failed to auto-inject autotodos: {e}")
|
|
986
|
+
|
|
965
987
|
# Debug logging
|
|
966
|
-
|
|
967
|
-
print(
|
|
968
|
-
f"Hook handler: Processing SessionStart - session: '{session_id}'",
|
|
969
|
-
file=sys.stderr,
|
|
970
|
-
)
|
|
988
|
+
_log(f"Hook handler: Processing SessionStart - session: '{session_id}'")
|
|
971
989
|
|
|
972
990
|
# Emit normalized event
|
|
973
991
|
self.hook_handler._emit_socketio_event("", "session_start", session_start_data)
|
|
@@ -1010,10 +1028,8 @@ class EventHandlers:
|
|
|
1010
1028
|
|
|
1011
1029
|
# Debug logging
|
|
1012
1030
|
if DEBUG:
|
|
1013
|
-
|
|
1014
|
-
f"Hook handler: SubagentStart - agent_type='{agent_type}', "
|
|
1015
|
-
f"agent_id='{agent_id}', session_id='{session_id[:16]}...'",
|
|
1016
|
-
file=sys.stderr,
|
|
1031
|
+
_log(
|
|
1032
|
+
f"Hook handler: SubagentStart - agent_type='{agent_type}', agent_id='{agent_id}', session_id='{session_id[:16]}...'"
|
|
1017
1033
|
)
|
|
1018
1034
|
|
|
1019
1035
|
# Emit to /hook namespace as subagent_start (NOT session_start!)
|
|
@@ -1048,7 +1064,7 @@ class EventHandlers:
|
|
|
1048
1064
|
from claude_mpm.services.event_log import get_event_log
|
|
1049
1065
|
except ImportError:
|
|
1050
1066
|
if DEBUG:
|
|
1051
|
-
|
|
1067
|
+
_log("Delegation detector or event log not available")
|
|
1052
1068
|
return
|
|
1053
1069
|
|
|
1054
1070
|
response_text = event.get("response", "")
|
|
@@ -1087,7 +1103,4 @@ class EventHandlers:
|
|
|
1087
1103
|
)
|
|
1088
1104
|
|
|
1089
1105
|
if DEBUG:
|
|
1090
|
-
|
|
1091
|
-
f"⚠️ PM violation detected: {detection['original_text'][:60]}...",
|
|
1092
|
-
file=sys.stderr,
|
|
1093
|
-
)
|
|
1106
|
+
_log(f"⚠️ PM violation detected: {detection['original_text'][:60]}...")
|