claude-mpm 5.4.85__py3-none-any.whl → 5.6.1__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
- claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
- claude_mpm/agents/PM_INSTRUCTIONS.md +101 -703
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +46 -0
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/executor.py +119 -16
- claude_mpm/cli/parsers/base_parser.py +71 -1
- claude_mpm/cli/parsers/commander_parser.py +83 -0
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/startup.py +54 -16
- claude_mpm/cli/startup_display.py +72 -5
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +72 -0
- claude_mpm/commander/adapters/__init__.py +31 -0
- claude_mpm/commander/adapters/base.py +191 -0
- claude_mpm/commander/adapters/claude_code.py +361 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +105 -0
- claude_mpm/commander/api/errors.py +112 -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 +215 -0
- claude_mpm/commander/api/routes/work.py +260 -0
- claude_mpm/commander/api/schemas.py +182 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +107 -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/daemon.py +398 -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/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 +404 -0
- claude_mpm/commander/runtime/__init__.py +10 -0
- claude_mpm/commander/runtime/executor.py +191 -0
- claude_mpm/commander/runtime/monitor.py +316 -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 +189 -0
- claude_mpm/commander/work/queue.py +405 -0
- claude_mpm/commander/workflow/__init__.py +27 -0
- claude_mpm/commander/workflow/event_handler.py +219 -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 +9 -1
- 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/core/config.py +5 -0
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/logger.py +10 -7
- claude_mpm/core/logging_utils.py +4 -2
- claude_mpm/core/output_style_manager.py +15 -5
- claude_mpm/core/unified_config.py +10 -6
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +9 -9
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +250 -11
- claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +69 -5
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +20 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +14 -77
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +30 -6
- claude_mpm/hooks/session_resume_hook.py +85 -1
- claude_mpm/init.py +1 -1
- claude_mpm/scripts/claude-hook-handler.sh +36 -10
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +325 -0
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +106 -16
- claude_mpm/services/pm_skills_deployer.py +259 -87
- claude_mpm/services/skills/git_skill_source_manager.py +51 -2
- claude_mpm/services/skills/selective_skill_deployer.py +114 -16
- claude_mpm/services/skills/skill_discovery_service.py +57 -3
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -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-management/SKILL.md +312 -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/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm-5.6.1.dist-info/METADATA +391 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/RECORD +244 -145
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
- claude_mpm-5.4.85.dist-info/METADATA +0 -1023
- /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
- /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/top_level.txt +0 -0
|
@@ -7,7 +7,7 @@ Claude Code hook events.
|
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
9
|
import re
|
|
10
|
-
import subprocess
|
|
10
|
+
import subprocess # nosec B404 - subprocess used for safe claude CLI version checking only
|
|
11
11
|
import sys
|
|
12
12
|
import uuid
|
|
13
13
|
from datetime import datetime, timezone
|
|
@@ -115,7 +115,7 @@ class EventHandlers:
|
|
|
115
115
|
f"Stored prompt for comprehensive tracking: session {session_id[:8]}...",
|
|
116
116
|
file=sys.stderr,
|
|
117
117
|
)
|
|
118
|
-
except Exception:
|
|
118
|
+
except Exception: # nosec B110
|
|
119
119
|
# Response tracking is optional - silently continue if it fails
|
|
120
120
|
pass
|
|
121
121
|
|
|
@@ -189,8 +189,34 @@ class EventHandlers:
|
|
|
189
189
|
if tool_name == "Task" and isinstance(tool_input, dict):
|
|
190
190
|
self._handle_task_delegation(tool_input, pre_tool_data, session_id)
|
|
191
191
|
|
|
192
|
+
# Record tool call for auto-pause if active
|
|
193
|
+
auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
|
|
194
|
+
if auto_pause and auto_pause.is_pause_active():
|
|
195
|
+
try:
|
|
196
|
+
auto_pause.on_tool_call(tool_name, tool_input)
|
|
197
|
+
except Exception as e:
|
|
198
|
+
if DEBUG:
|
|
199
|
+
print(f"Auto-pause tool recording error: {e}", file=sys.stderr)
|
|
200
|
+
|
|
192
201
|
self.hook_handler._emit_socketio_event("", "pre_tool", pre_tool_data)
|
|
193
202
|
|
|
203
|
+
# Handle TodoWrite specially - emit dedicated todo_updated event
|
|
204
|
+
# WHY: Frontend expects todo_updated events for dashboard display
|
|
205
|
+
# The broadcaster.todo_updated() method exists but was never called
|
|
206
|
+
if tool_name == "TodoWrite" and tool_params.get("todos"):
|
|
207
|
+
todo_data = {
|
|
208
|
+
"todos": tool_params["todos"],
|
|
209
|
+
"total_count": len(tool_params["todos"]),
|
|
210
|
+
"session_id": session_id,
|
|
211
|
+
"timestamp": timestamp,
|
|
212
|
+
}
|
|
213
|
+
self.hook_handler._emit_socketio_event("", "todo_updated", todo_data)
|
|
214
|
+
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
|
+
)
|
|
219
|
+
|
|
194
220
|
def _handle_task_delegation(
|
|
195
221
|
self, tool_input: dict, pre_tool_data: dict, session_id: str
|
|
196
222
|
):
|
|
@@ -271,7 +297,7 @@ class EventHandlers:
|
|
|
271
297
|
mhm = getattr(self.hook_handler, "memory_hook_manager", None)
|
|
272
298
|
if mhm and hasattr(mhm, "trigger_pre_delegation_hook"):
|
|
273
299
|
mhm.trigger_pre_delegation_hook(agent_type, tool_input, session_id)
|
|
274
|
-
except Exception:
|
|
300
|
+
except Exception: # nosec B110
|
|
275
301
|
# Memory hooks are optional
|
|
276
302
|
pass
|
|
277
303
|
|
|
@@ -364,7 +390,7 @@ class EventHandlers:
|
|
|
364
390
|
os.chdir(working_dir)
|
|
365
391
|
|
|
366
392
|
# Run git command to get current branch
|
|
367
|
-
result = subprocess.run(
|
|
393
|
+
result = subprocess.run( # nosec B603 B607
|
|
368
394
|
["git", "branch", "--show-current"],
|
|
369
395
|
capture_output=True,
|
|
370
396
|
text=True,
|
|
@@ -474,7 +500,7 @@ class EventHandlers:
|
|
|
474
500
|
mhm = getattr(self.hook_handler, "memory_hook_manager", None)
|
|
475
501
|
if mhm and hasattr(mhm, "trigger_post_delegation_hook"):
|
|
476
502
|
mhm.trigger_post_delegation_hook(agent_type, event, session_id)
|
|
477
|
-
except Exception:
|
|
503
|
+
except Exception: # nosec B110
|
|
478
504
|
# Memory hooks are optional
|
|
479
505
|
pass
|
|
480
506
|
|
|
@@ -488,7 +514,7 @@ class EventHandlers:
|
|
|
488
514
|
rtm.track_agent_response(
|
|
489
515
|
session_id, agent_type, event, delegation_requests
|
|
490
516
|
)
|
|
491
|
-
except Exception:
|
|
517
|
+
except Exception: # nosec B110
|
|
492
518
|
# Response tracking is optional
|
|
493
519
|
pass
|
|
494
520
|
|
|
@@ -550,13 +576,49 @@ class EventHandlers:
|
|
|
550
576
|
if DEBUG:
|
|
551
577
|
self._log_stop_event_debug(event, session_id, metadata)
|
|
552
578
|
|
|
579
|
+
# Auto-pause integration (independent of response tracking)
|
|
580
|
+
# WHY HERE: Auto-pause must work even when response_tracking is disabled
|
|
581
|
+
# Extract usage data directly from event and trigger auto-pause if thresholds crossed
|
|
582
|
+
if "usage" in event:
|
|
583
|
+
auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
|
|
584
|
+
if auto_pause:
|
|
585
|
+
try:
|
|
586
|
+
usage_data = event["usage"]
|
|
587
|
+
metadata["usage"] = {
|
|
588
|
+
"input_tokens": usage_data.get("input_tokens", 0),
|
|
589
|
+
"output_tokens": usage_data.get("output_tokens", 0),
|
|
590
|
+
"cache_creation_input_tokens": usage_data.get(
|
|
591
|
+
"cache_creation_input_tokens", 0
|
|
592
|
+
),
|
|
593
|
+
"cache_read_input_tokens": usage_data.get(
|
|
594
|
+
"cache_read_input_tokens", 0
|
|
595
|
+
),
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
threshold_crossed = auto_pause.on_usage_update(metadata["usage"])
|
|
599
|
+
if threshold_crossed:
|
|
600
|
+
warning = auto_pause.emit_threshold_warning(threshold_crossed)
|
|
601
|
+
print(f"\n⚠️ {warning}", file=sys.stderr)
|
|
602
|
+
|
|
603
|
+
if DEBUG:
|
|
604
|
+
print(
|
|
605
|
+
f" - Auto-pause threshold crossed: {threshold_crossed}",
|
|
606
|
+
file=sys.stderr,
|
|
607
|
+
)
|
|
608
|
+
except Exception as e:
|
|
609
|
+
if DEBUG:
|
|
610
|
+
print(
|
|
611
|
+
f"Auto-pause error in handle_stop_fast: {e}",
|
|
612
|
+
file=sys.stderr,
|
|
613
|
+
)
|
|
614
|
+
|
|
553
615
|
# Track response if enabled
|
|
554
616
|
try:
|
|
555
617
|
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
556
618
|
if rtm and hasattr(rtm, "track_stop_response"):
|
|
557
619
|
pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
|
|
558
620
|
rtm.track_stop_response(event, session_id, metadata, pending_prompts)
|
|
559
|
-
except Exception:
|
|
621
|
+
except Exception: # nosec B110
|
|
560
622
|
# Response tracking is optional
|
|
561
623
|
pass
|
|
562
624
|
|
|
@@ -598,7 +660,7 @@ class EventHandlers:
|
|
|
598
660
|
f" - response_tracker exists: {tracker_exists}",
|
|
599
661
|
file=sys.stderr,
|
|
600
662
|
)
|
|
601
|
-
except Exception:
|
|
663
|
+
except Exception: # nosec B110
|
|
602
664
|
# If debug logging fails, just skip it
|
|
603
665
|
pass
|
|
604
666
|
|
|
@@ -664,7 +726,7 @@ class EventHandlers:
|
|
|
664
726
|
try:
|
|
665
727
|
# Get the original request data (with fuzzy matching fallback)
|
|
666
728
|
delegation_requests = getattr(self.hook_handler, "delegation_requests", {})
|
|
667
|
-
request_info = delegation_requests.get(session_id)
|
|
729
|
+
request_info = delegation_requests.get(session_id) # nosec B113
|
|
668
730
|
|
|
669
731
|
# If exact match fails, try partial matching
|
|
670
732
|
if not request_info and session_id:
|
|
@@ -689,7 +751,7 @@ class EventHandlers:
|
|
|
689
751
|
f" - ✅ Fuzzy match found: {stored_sid[:16]}...",
|
|
690
752
|
file=sys.stderr,
|
|
691
753
|
)
|
|
692
|
-
request_info = delegation_requests.get(stored_sid)
|
|
754
|
+
request_info = delegation_requests.get(stored_sid) # nosec B113
|
|
693
755
|
# Update the key to use the current session_id for consistency
|
|
694
756
|
if request_info:
|
|
695
757
|
delegation_requests[session_id] = request_info
|
|
@@ -789,6 +851,7 @@ class EventHandlers:
|
|
|
789
851
|
- Captures response content and metadata for analysis
|
|
790
852
|
- Enables tracking of conversation flow and response patterns
|
|
791
853
|
- Essential for comprehensive monitoring of Claude interactions
|
|
854
|
+
- Scans for delegation anti-patterns and creates autotodos
|
|
792
855
|
"""
|
|
793
856
|
# Track the response for logging
|
|
794
857
|
try:
|
|
@@ -796,10 +859,17 @@ class EventHandlers:
|
|
|
796
859
|
if rtm and hasattr(rtm, "track_assistant_response"):
|
|
797
860
|
pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
|
|
798
861
|
rtm.track_assistant_response(event, pending_prompts)
|
|
799
|
-
except Exception:
|
|
862
|
+
except Exception: # nosec B110
|
|
800
863
|
# Response tracking is optional
|
|
801
864
|
pass
|
|
802
865
|
|
|
866
|
+
# Scan response for delegation anti-patterns and create autotodos
|
|
867
|
+
try:
|
|
868
|
+
self._scan_for_delegation_patterns(event)
|
|
869
|
+
except Exception as e: # nosec B110
|
|
870
|
+
if DEBUG:
|
|
871
|
+
print(f"Delegation scanning error: {e}", file=sys.stderr)
|
|
872
|
+
|
|
803
873
|
# Get working directory and git branch
|
|
804
874
|
working_dir = event.get("cwd", "")
|
|
805
875
|
git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
|
|
@@ -852,6 +922,21 @@ class EventHandlers:
|
|
|
852
922
|
file=sys.stderr,
|
|
853
923
|
)
|
|
854
924
|
|
|
925
|
+
# Record assistant response for auto-pause if active
|
|
926
|
+
auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
|
|
927
|
+
if auto_pause and auto_pause.is_pause_active():
|
|
928
|
+
try:
|
|
929
|
+
# Summarize response to first 200 chars
|
|
930
|
+
summary = (
|
|
931
|
+
response_text[:200] + "..."
|
|
932
|
+
if len(response_text) > 200
|
|
933
|
+
else response_text
|
|
934
|
+
)
|
|
935
|
+
auto_pause.on_assistant_response(summary)
|
|
936
|
+
except Exception as e:
|
|
937
|
+
if DEBUG:
|
|
938
|
+
print(f"Auto-pause response recording error: {e}", file=sys.stderr)
|
|
939
|
+
|
|
855
940
|
# Emit normalized event
|
|
856
941
|
self.hook_handler._emit_socketio_event(
|
|
857
942
|
"", "assistant_response", assistant_response_data
|
|
@@ -864,6 +949,7 @@ class EventHandlers:
|
|
|
864
949
|
- Provides visibility into new conversation sessions
|
|
865
950
|
- Enables tracking of session lifecycle and duration
|
|
866
951
|
- Useful for monitoring concurrent sessions and resource usage
|
|
952
|
+
- Auto-inject pending autotodos if enabled in config
|
|
867
953
|
"""
|
|
868
954
|
session_id = event.get("session_id", "")
|
|
869
955
|
working_dir = event.get("cwd", "")
|
|
@@ -877,6 +963,39 @@ class EventHandlers:
|
|
|
877
963
|
"hook_event_name": "SessionStart",
|
|
878
964
|
}
|
|
879
965
|
|
|
966
|
+
# Auto-inject pending autotodos if enabled
|
|
967
|
+
try:
|
|
968
|
+
from pathlib import Path
|
|
969
|
+
|
|
970
|
+
from claude_mpm.cli.commands.autotodos import get_pending_todos
|
|
971
|
+
from claude_mpm.core.config import Config
|
|
972
|
+
|
|
973
|
+
config = Config()
|
|
974
|
+
auto_inject_enabled = config.get("autotodos.auto_inject_on_startup", True)
|
|
975
|
+
max_todos = config.get("autotodos.max_todos_per_session", 10)
|
|
976
|
+
|
|
977
|
+
if auto_inject_enabled:
|
|
978
|
+
# Pass working directory from event to avoid Path.cwd() issues
|
|
979
|
+
working_dir_param = None
|
|
980
|
+
if working_dir:
|
|
981
|
+
working_dir_param = Path(working_dir)
|
|
982
|
+
|
|
983
|
+
pending_todos = get_pending_todos(
|
|
984
|
+
max_todos=max_todos, working_dir=working_dir_param
|
|
985
|
+
)
|
|
986
|
+
if pending_todos:
|
|
987
|
+
session_start_data["pending_autotodos"] = pending_todos
|
|
988
|
+
session_start_data["autotodos_count"] = len(pending_todos)
|
|
989
|
+
if DEBUG:
|
|
990
|
+
print(
|
|
991
|
+
f" - Auto-injected {len(pending_todos)} pending autotodos",
|
|
992
|
+
file=sys.stderr,
|
|
993
|
+
)
|
|
994
|
+
except Exception as e: # nosec B110
|
|
995
|
+
# Auto-injection is optional - continue if it fails
|
|
996
|
+
if DEBUG:
|
|
997
|
+
print(f" - Failed to auto-inject autotodos: {e}", file=sys.stderr)
|
|
998
|
+
|
|
880
999
|
# Debug logging
|
|
881
1000
|
if DEBUG:
|
|
882
1001
|
print(
|
|
@@ -886,3 +1005,123 @@ class EventHandlers:
|
|
|
886
1005
|
|
|
887
1006
|
# Emit normalized event
|
|
888
1007
|
self.hook_handler._emit_socketio_event("", "session_start", session_start_data)
|
|
1008
|
+
|
|
1009
|
+
def handle_subagent_start_fast(self, event):
|
|
1010
|
+
"""Handle SubagentStart events with proper agent type extraction.
|
|
1011
|
+
|
|
1012
|
+
WHY separate from SessionStart:
|
|
1013
|
+
- SubagentStart contains agent-specific information
|
|
1014
|
+
- Frontend needs agent_type to create distinct agent nodes
|
|
1015
|
+
- Multiple engineers should show as separate nodes in hierarchy
|
|
1016
|
+
- Research agents must appear in the agent hierarchy
|
|
1017
|
+
|
|
1018
|
+
Unlike SessionStart, SubagentStart events contain agent-specific
|
|
1019
|
+
information that must be preserved and emitted to the dashboard.
|
|
1020
|
+
"""
|
|
1021
|
+
session_id = event.get("session_id", "")
|
|
1022
|
+
|
|
1023
|
+
# Extract agent type from event - Claude provides this in SubagentStart
|
|
1024
|
+
# Try multiple possible field names for compatibility
|
|
1025
|
+
agent_type = event.get("agent_type") or event.get("subagent_type") or "unknown"
|
|
1026
|
+
|
|
1027
|
+
# Generate unique agent ID combining type and session
|
|
1028
|
+
agent_id = event.get("agent_id", f"{agent_type}_{session_id[:8]}")
|
|
1029
|
+
|
|
1030
|
+
# Get working directory and git branch
|
|
1031
|
+
working_dir = event.get("cwd", "")
|
|
1032
|
+
git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
|
|
1033
|
+
|
|
1034
|
+
# Build subagent start data with all required fields
|
|
1035
|
+
subagent_start_data = {
|
|
1036
|
+
"session_id": session_id,
|
|
1037
|
+
"agent_type": agent_type,
|
|
1038
|
+
"agent_id": agent_id,
|
|
1039
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
1040
|
+
"hook_event_name": "SubagentStart", # Preserve correct hook name
|
|
1041
|
+
"working_directory": working_dir,
|
|
1042
|
+
"git_branch": git_branch,
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
# Debug logging
|
|
1046
|
+
if DEBUG:
|
|
1047
|
+
print(
|
|
1048
|
+
f"Hook handler: SubagentStart - agent_type='{agent_type}', "
|
|
1049
|
+
f"agent_id='{agent_id}', session_id='{session_id[:16]}...'",
|
|
1050
|
+
file=sys.stderr,
|
|
1051
|
+
)
|
|
1052
|
+
|
|
1053
|
+
# Emit to /hook namespace as subagent_start (NOT session_start!)
|
|
1054
|
+
self.hook_handler._emit_socketio_event(
|
|
1055
|
+
"", "subagent_start", subagent_start_data
|
|
1056
|
+
)
|
|
1057
|
+
|
|
1058
|
+
def _scan_for_delegation_patterns(self, event):
|
|
1059
|
+
"""Scan assistant response for delegation anti-patterns.
|
|
1060
|
+
|
|
1061
|
+
WHY this is needed:
|
|
1062
|
+
- Detect when PM asks user to do something manually instead of delegating
|
|
1063
|
+
- Flag PM behavior violations for immediate correction
|
|
1064
|
+
- Enforce delegation principle in PM workflow
|
|
1065
|
+
- Help PM recognize delegation opportunities
|
|
1066
|
+
|
|
1067
|
+
This method scans the assistant's response text for patterns like:
|
|
1068
|
+
- "Make sure .env.local is in your .gitignore"
|
|
1069
|
+
- "You'll need to run npm install"
|
|
1070
|
+
- "Please run the tests manually"
|
|
1071
|
+
|
|
1072
|
+
When patterns are detected, PM violations are logged as errors/warnings
|
|
1073
|
+
that should be corrected immediately, NOT as todos to delegate.
|
|
1074
|
+
|
|
1075
|
+
DESIGN DECISION: pm.violation vs autotodo.delegation
|
|
1076
|
+
- Delegation patterns = PM doing something WRONG → pm.violation (error)
|
|
1077
|
+
- Script failures = Something BROKEN → autotodo.error (todo)
|
|
1078
|
+
"""
|
|
1079
|
+
# Only scan if delegation detector is available
|
|
1080
|
+
try:
|
|
1081
|
+
from claude_mpm.services.delegation_detector import get_delegation_detector
|
|
1082
|
+
from claude_mpm.services.event_log import get_event_log
|
|
1083
|
+
except ImportError:
|
|
1084
|
+
if DEBUG:
|
|
1085
|
+
print("Delegation detector or event log not available", file=sys.stderr)
|
|
1086
|
+
return
|
|
1087
|
+
|
|
1088
|
+
response_text = event.get("response", "")
|
|
1089
|
+
if not response_text:
|
|
1090
|
+
return
|
|
1091
|
+
|
|
1092
|
+
# Get the delegation detector
|
|
1093
|
+
detector = get_delegation_detector()
|
|
1094
|
+
|
|
1095
|
+
# Detect delegation patterns
|
|
1096
|
+
detections = detector.detect_user_delegation(response_text)
|
|
1097
|
+
|
|
1098
|
+
if not detections:
|
|
1099
|
+
return # No patterns detected
|
|
1100
|
+
|
|
1101
|
+
# Get event log for violation recording
|
|
1102
|
+
event_log = get_event_log()
|
|
1103
|
+
|
|
1104
|
+
# Create PM violation events (NOT autotodos)
|
|
1105
|
+
for detection in detections:
|
|
1106
|
+
# Create event log entry as pm.violation
|
|
1107
|
+
event_log.append_event(
|
|
1108
|
+
event_type="pm.violation",
|
|
1109
|
+
payload={
|
|
1110
|
+
"violation_type": "delegation_anti_pattern",
|
|
1111
|
+
"pattern_type": detection["pattern_type"],
|
|
1112
|
+
"original_text": detection["original_text"],
|
|
1113
|
+
"suggested_action": detection["suggested_todo"],
|
|
1114
|
+
"action": detection["action"],
|
|
1115
|
+
"session_id": event.get("session_id", ""),
|
|
1116
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
1117
|
+
"severity": "warning", # Not critical, but should be fixed
|
|
1118
|
+
"message": f"PM asked user to do something manually: {detection['original_text'][:80]}...",
|
|
1119
|
+
},
|
|
1120
|
+
status="pending",
|
|
1121
|
+
)
|
|
1122
|
+
|
|
1123
|
+
if DEBUG:
|
|
1124
|
+
print(
|
|
1125
|
+
f"⚠️ PM violation detected: {detection['original_text'][:60]}...",
|
|
1126
|
+
file=sys.stderr,
|
|
1127
|
+
)
|