claude-mpm 5.4.65__py3-none-any.whl → 5.6.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
- claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +107 -1928
- claude_mpm/agents/PM_INSTRUCTIONS.md +119 -689
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/__init__.py +5 -1
- claude_mpm/cli/commands/agents.py +2 -4
- claude_mpm/cli/commands/agents_reconcile.py +197 -0
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +46 -0
- claude_mpm/cli/commands/configure.py +620 -21
- 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/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +171 -17
- claude_mpm/cli/executor.py +120 -16
- claude_mpm/cli/interactive/__init__.py +10 -0
- claude_mpm/cli/interactive/agent_wizard.py +30 -50
- claude_mpm/cli/interactive/questionary_styles.py +65 -0
- claude_mpm/cli/interactive/skill_selector.py +481 -0
- claude_mpm/cli/parsers/base_parser.py +76 -1
- claude_mpm/cli/parsers/commander_parser.py +83 -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 +203 -359
- 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 +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 +228 -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/config/skill_sources.py +16 -0
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +2 -2
- claude_mpm/core/config.py +32 -19
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/interactive_session.py +7 -7
- claude_mpm/core/logger.py +26 -9
- claude_mpm/core/logging_utils.py +35 -11
- claude_mpm/core/output_style_manager.py +31 -13
- claude_mpm/core/unified_config.py +54 -8
- claude_mpm/core/unified_paths.py +95 -90
- 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/__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-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +283 -87
- 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 +116 -8
- claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
- claude_mpm/hooks/claude_hooks/response_tracking.py +42 -59
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.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-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 +39 -24
- 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 +73 -75
- claude_mpm/hooks/session_resume_hook.py +89 -1
- claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
- claude_mpm/init.py +1 -1
- claude_mpm/scripts/claude-hook-handler.sh +43 -16
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- 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/agents/startup_sync.py +5 -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 +135 -11
- claude_mpm/services/skills/selective_skill_deployer.py +142 -26
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +31 -5
- 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-bug-reporting/SKILL.md +248 -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/mpm-teaching-mode/SKILL.md +657 -0
- 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/utils/agent_dependency_loader.py +4 -2
- claude_mpm/utils/robust_installer.py +10 -6
- claude_mpm-5.6.10.dist-info/METADATA +391 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/RECORD +303 -181
- 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.65.dist-info/METADATA +0 -999
- /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.65.dist-info → claude_mpm-5.6.10.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/top_level.txt +0 -0
|
@@ -7,13 +7,21 @@ Claude Code hook events.
|
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
9
|
import re
|
|
10
|
-
import subprocess
|
|
11
|
-
import sys
|
|
10
|
+
import subprocess # nosec B404 - subprocess used for safe claude CLI version checking only
|
|
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,11 +119,10 @@ 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
|
-
except Exception:
|
|
125
|
+
except Exception: # nosec B110
|
|
119
126
|
# Response tracking is optional - silently continue if it fails
|
|
120
127
|
pass
|
|
121
128
|
|
|
@@ -133,11 +140,8 @@ class EventHandlers:
|
|
|
133
140
|
# Enhanced debug logging for session correlation
|
|
134
141
|
session_id = event.get("session_id", "")
|
|
135
142
|
if DEBUG:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
file=sys.stderr,
|
|
139
|
-
)
|
|
140
|
-
print(f" - event keys: {list(event.keys())}", file=sys.stderr)
|
|
143
|
+
_log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
|
|
144
|
+
_log(f" - event keys: {list(event.keys())}")
|
|
141
145
|
|
|
142
146
|
tool_name = event.get("tool_name", "")
|
|
143
147
|
tool_input = event.get("tool_input", {})
|
|
@@ -180,17 +184,41 @@ class EventHandlers:
|
|
|
180
184
|
|
|
181
185
|
CorrelationManager.store(session_id, tool_call_id, tool_name)
|
|
182
186
|
if DEBUG:
|
|
183
|
-
|
|
184
|
-
f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
|
|
185
|
-
file=sys.stderr,
|
|
187
|
+
_log(
|
|
188
|
+
f" - Generated tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
|
|
186
189
|
)
|
|
187
190
|
|
|
188
191
|
# Add delegation-specific data if this is a Task tool
|
|
189
192
|
if tool_name == "Task" and isinstance(tool_input, dict):
|
|
190
193
|
self._handle_task_delegation(tool_input, pre_tool_data, session_id)
|
|
191
194
|
|
|
195
|
+
# Record tool call for auto-pause if active
|
|
196
|
+
auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
|
|
197
|
+
if auto_pause and auto_pause.is_pause_active():
|
|
198
|
+
try:
|
|
199
|
+
auto_pause.on_tool_call(tool_name, tool_input)
|
|
200
|
+
except Exception as e:
|
|
201
|
+
if DEBUG:
|
|
202
|
+
_log(f"Auto-pause tool recording error: {e}")
|
|
203
|
+
|
|
192
204
|
self.hook_handler._emit_socketio_event("", "pre_tool", pre_tool_data)
|
|
193
205
|
|
|
206
|
+
# Handle TodoWrite specially - emit dedicated todo_updated event
|
|
207
|
+
# WHY: Frontend expects todo_updated events for dashboard display
|
|
208
|
+
# The broadcaster.todo_updated() method exists but was never called
|
|
209
|
+
if tool_name == "TodoWrite" and tool_params.get("todos"):
|
|
210
|
+
todo_data = {
|
|
211
|
+
"todos": tool_params["todos"],
|
|
212
|
+
"total_count": len(tool_params["todos"]),
|
|
213
|
+
"session_id": session_id,
|
|
214
|
+
"timestamp": timestamp,
|
|
215
|
+
}
|
|
216
|
+
self.hook_handler._emit_socketio_event("", "todo_updated", todo_data)
|
|
217
|
+
if DEBUG:
|
|
218
|
+
_log(
|
|
219
|
+
f" - Emitted todo_updated event with {len(tool_params['todos'])} todos for session {session_id[:8]}..."
|
|
220
|
+
)
|
|
221
|
+
|
|
194
222
|
def _handle_task_delegation(
|
|
195
223
|
self, tool_input: dict, pre_tool_data: dict, session_id: str
|
|
196
224
|
):
|
|
@@ -229,12 +257,9 @@ class EventHandlers:
|
|
|
229
257
|
|
|
230
258
|
# Track this delegation for SubagentStop correlation and response tracking
|
|
231
259
|
if DEBUG:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
)
|
|
236
|
-
print(f" - agent_type: {agent_type}", file=sys.stderr)
|
|
237
|
-
print(f" - raw_agent_type: {raw_agent_type}", file=sys.stderr)
|
|
260
|
+
_log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
|
|
261
|
+
_log(f" - agent_type: {agent_type}")
|
|
262
|
+
_log(f" - raw_agent_type: {raw_agent_type}")
|
|
238
263
|
|
|
239
264
|
if session_id and agent_type != "unknown":
|
|
240
265
|
# Prepare request data for response tracking correlation
|
|
@@ -246,24 +271,17 @@ class EventHandlers:
|
|
|
246
271
|
self.hook_handler._track_delegation(session_id, agent_type, request_data)
|
|
247
272
|
|
|
248
273
|
if DEBUG:
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
f" - Request data keys: {list(request_data.keys())}",
|
|
252
|
-
file=sys.stderr,
|
|
253
|
-
)
|
|
274
|
+
_log(" - Delegation tracked successfully")
|
|
275
|
+
_log(f" - Request data keys: {list(request_data.keys())}")
|
|
254
276
|
delegation_requests = getattr(
|
|
255
277
|
self.hook_handler, "delegation_requests", {}
|
|
256
278
|
)
|
|
257
|
-
|
|
258
|
-
f" - delegation_requests size: {len(delegation_requests)}",
|
|
259
|
-
file=sys.stderr,
|
|
260
|
-
)
|
|
279
|
+
_log(f" - delegation_requests size: {len(delegation_requests)}")
|
|
261
280
|
|
|
262
281
|
# Log important delegations for debugging
|
|
263
282
|
if DEBUG or agent_type in ["research", "engineer", "qa", "documentation"]:
|
|
264
|
-
|
|
265
|
-
f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'"
|
|
266
|
-
file=sys.stderr,
|
|
283
|
+
_log(
|
|
284
|
+
f"Hook handler: Task delegation started - agent: '{agent_type}', session: '{session_id}'"
|
|
267
285
|
)
|
|
268
286
|
|
|
269
287
|
# Trigger memory pre-delegation hook
|
|
@@ -271,7 +289,7 @@ class EventHandlers:
|
|
|
271
289
|
mhm = getattr(self.hook_handler, "memory_hook_manager", None)
|
|
272
290
|
if mhm and hasattr(mhm, "trigger_pre_delegation_hook"):
|
|
273
291
|
mhm.trigger_pre_delegation_hook(agent_type, tool_input, session_id)
|
|
274
|
-
except Exception:
|
|
292
|
+
except Exception: # nosec B110
|
|
275
293
|
# Memory hooks are optional
|
|
276
294
|
pass
|
|
277
295
|
|
|
@@ -333,10 +351,10 @@ class EventHandlers:
|
|
|
333
351
|
)
|
|
334
352
|
|
|
335
353
|
if DEBUG:
|
|
336
|
-
|
|
354
|
+
_log(f" - Agent prompt logged for {agent_type}")
|
|
337
355
|
except Exception as e:
|
|
338
356
|
if DEBUG:
|
|
339
|
-
|
|
357
|
+
_log(f" - Could not log agent prompt: {e}")
|
|
340
358
|
|
|
341
359
|
def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
|
|
342
360
|
"""Get git branch for the given directory with caching."""
|
|
@@ -364,7 +382,7 @@ class EventHandlers:
|
|
|
364
382
|
os.chdir(working_dir)
|
|
365
383
|
|
|
366
384
|
# Run git command to get current branch
|
|
367
|
-
result = subprocess.run(
|
|
385
|
+
result = subprocess.run( # nosec B603 B607
|
|
368
386
|
["git", "branch", "--show-current"],
|
|
369
387
|
capture_output=True,
|
|
370
388
|
text=True,
|
|
@@ -424,9 +442,8 @@ class EventHandlers:
|
|
|
424
442
|
|
|
425
443
|
tool_call_id = CorrelationManager.retrieve(session_id) if session_id else None
|
|
426
444
|
if DEBUG and tool_call_id:
|
|
427
|
-
|
|
428
|
-
f" - Retrieved tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
|
|
429
|
-
file=sys.stderr,
|
|
445
|
+
_log(
|
|
446
|
+
f" - Retrieved tool_call_id: {tool_call_id[:8]}... for session {session_id[:8]}..."
|
|
430
447
|
)
|
|
431
448
|
|
|
432
449
|
post_tool_data = {
|
|
@@ -474,7 +491,7 @@ class EventHandlers:
|
|
|
474
491
|
mhm = getattr(self.hook_handler, "memory_hook_manager", None)
|
|
475
492
|
if mhm and hasattr(mhm, "trigger_post_delegation_hook"):
|
|
476
493
|
mhm.trigger_post_delegation_hook(agent_type, event, session_id)
|
|
477
|
-
except Exception:
|
|
494
|
+
except Exception: # nosec B110
|
|
478
495
|
# Memory hooks are optional
|
|
479
496
|
pass
|
|
480
497
|
|
|
@@ -488,7 +505,7 @@ class EventHandlers:
|
|
|
488
505
|
rtm.track_agent_response(
|
|
489
506
|
session_id, agent_type, event, delegation_requests
|
|
490
507
|
)
|
|
491
|
-
except Exception:
|
|
508
|
+
except Exception: # nosec B110
|
|
492
509
|
# Response tracking is optional
|
|
493
510
|
pass
|
|
494
511
|
|
|
@@ -550,13 +567,49 @@ class EventHandlers:
|
|
|
550
567
|
if DEBUG:
|
|
551
568
|
self._log_stop_event_debug(event, session_id, metadata)
|
|
552
569
|
|
|
570
|
+
# Auto-pause integration (independent of response tracking)
|
|
571
|
+
# WHY HERE: Auto-pause must work even when response_tracking is disabled
|
|
572
|
+
# Extract usage data directly from event and trigger auto-pause if thresholds crossed
|
|
573
|
+
if "usage" in event:
|
|
574
|
+
auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
|
|
575
|
+
if auto_pause:
|
|
576
|
+
try:
|
|
577
|
+
usage_data = event["usage"]
|
|
578
|
+
metadata["usage"] = {
|
|
579
|
+
"input_tokens": usage_data.get("input_tokens", 0),
|
|
580
|
+
"output_tokens": usage_data.get("output_tokens", 0),
|
|
581
|
+
"cache_creation_input_tokens": usage_data.get(
|
|
582
|
+
"cache_creation_input_tokens", 0
|
|
583
|
+
),
|
|
584
|
+
"cache_read_input_tokens": usage_data.get(
|
|
585
|
+
"cache_read_input_tokens", 0
|
|
586
|
+
),
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
threshold_crossed = auto_pause.on_usage_update(metadata["usage"])
|
|
590
|
+
if threshold_crossed:
|
|
591
|
+
warning = auto_pause.emit_threshold_warning(threshold_crossed)
|
|
592
|
+
# CRITICAL: Never write to stderr unconditionally - causes hook errors
|
|
593
|
+
# Use _log() instead which only writes to file if DEBUG=true
|
|
594
|
+
from . import _log
|
|
595
|
+
|
|
596
|
+
_log(f"⚠️ Auto-pause threshold crossed: {warning}")
|
|
597
|
+
|
|
598
|
+
if DEBUG:
|
|
599
|
+
_log(
|
|
600
|
+
f" - Auto-pause threshold crossed: {threshold_crossed}"
|
|
601
|
+
)
|
|
602
|
+
except Exception as e:
|
|
603
|
+
if DEBUG:
|
|
604
|
+
_log(f"Auto-pause error in handle_stop_fast: {e}")
|
|
605
|
+
|
|
553
606
|
# Track response if enabled
|
|
554
607
|
try:
|
|
555
608
|
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
556
609
|
if rtm and hasattr(rtm, "track_stop_response"):
|
|
557
610
|
pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
|
|
558
611
|
rtm.track_stop_response(event, session_id, metadata, pending_prompts)
|
|
559
|
-
except Exception:
|
|
612
|
+
except Exception: # nosec B110
|
|
560
613
|
# Response tracking is optional
|
|
561
614
|
pass
|
|
562
615
|
|
|
@@ -590,24 +643,15 @@ class EventHandlers:
|
|
|
590
643
|
getattr(rtm, "response_tracker", None) is not None if rtm else False
|
|
591
644
|
)
|
|
592
645
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
)
|
|
597
|
-
print(
|
|
598
|
-
f" - response_tracker exists: {tracker_exists}",
|
|
599
|
-
file=sys.stderr,
|
|
600
|
-
)
|
|
601
|
-
except Exception:
|
|
646
|
+
_log(f" - response_tracking_enabled: {tracking_enabled}")
|
|
647
|
+
_log(f" - response_tracker exists: {tracker_exists}")
|
|
648
|
+
except Exception: # nosec B110
|
|
602
649
|
# If debug logging fails, just skip it
|
|
603
650
|
pass
|
|
604
651
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
)
|
|
609
|
-
print(f" - reason: {metadata['reason']}", file=sys.stderr)
|
|
610
|
-
print(f" - stop_type: {metadata['stop_type']}", file=sys.stderr)
|
|
652
|
+
_log(f" - session_id: {session_id[:8] if session_id else 'None'}...")
|
|
653
|
+
_log(f" - reason: {metadata['reason']}")
|
|
654
|
+
_log(f" - stop_type: {metadata['stop_type']}")
|
|
611
655
|
|
|
612
656
|
def _emit_stop_event(self, event: dict, session_id: str, metadata: dict) -> None:
|
|
613
657
|
"""Emit stop event data to Socket.IO."""
|
|
@@ -664,15 +708,12 @@ class EventHandlers:
|
|
|
664
708
|
try:
|
|
665
709
|
# Get the original request data (with fuzzy matching fallback)
|
|
666
710
|
delegation_requests = getattr(self.hook_handler, "delegation_requests", {})
|
|
667
|
-
request_info = delegation_requests.get(session_id)
|
|
711
|
+
request_info = delegation_requests.get(session_id) # nosec B113
|
|
668
712
|
|
|
669
713
|
# If exact match fails, try partial matching
|
|
670
714
|
if not request_info and session_id:
|
|
671
715
|
if DEBUG:
|
|
672
|
-
|
|
673
|
-
f" - Trying fuzzy match for session {session_id[:16]}...",
|
|
674
|
-
file=sys.stderr,
|
|
675
|
-
)
|
|
716
|
+
_log(f" - Trying fuzzy match for session {session_id[:16]}...")
|
|
676
717
|
# Try to find a session that matches the first 8-16 characters
|
|
677
718
|
for stored_sid in list(delegation_requests.keys()):
|
|
678
719
|
if (
|
|
@@ -685,11 +726,8 @@ class EventHandlers:
|
|
|
685
726
|
)
|
|
686
727
|
):
|
|
687
728
|
if DEBUG:
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
file=sys.stderr,
|
|
691
|
-
)
|
|
692
|
-
request_info = delegation_requests.get(stored_sid)
|
|
729
|
+
_log(f" - ✅ Fuzzy match found: {stored_sid[:16]}...")
|
|
730
|
+
request_info = delegation_requests.get(stored_sid) # nosec B113
|
|
693
731
|
# Update the key to use the current session_id for consistency
|
|
694
732
|
if request_info:
|
|
695
733
|
delegation_requests[session_id] = request_info
|
|
@@ -757,9 +795,8 @@ class EventHandlers:
|
|
|
757
795
|
)
|
|
758
796
|
|
|
759
797
|
if file_path and DEBUG:
|
|
760
|
-
|
|
761
|
-
f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}"
|
|
762
|
-
file=sys.stderr,
|
|
798
|
+
_log(
|
|
799
|
+
f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}"
|
|
763
800
|
)
|
|
764
801
|
|
|
765
802
|
# Clean up the request data
|
|
@@ -770,16 +807,13 @@ class EventHandlers:
|
|
|
770
807
|
del delegation_requests[session_id]
|
|
771
808
|
|
|
772
809
|
elif DEBUG:
|
|
773
|
-
|
|
774
|
-
f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}"
|
|
775
|
-
file=sys.stderr,
|
|
810
|
+
_log(
|
|
811
|
+
f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}"
|
|
776
812
|
)
|
|
777
813
|
|
|
778
814
|
except Exception as e:
|
|
779
815
|
if DEBUG:
|
|
780
|
-
|
|
781
|
-
f"❌ Failed to track response on SubagentStop: {e}", file=sys.stderr
|
|
782
|
-
)
|
|
816
|
+
_log(f"❌ Failed to track response on SubagentStop: {e}")
|
|
783
817
|
|
|
784
818
|
def handle_assistant_response(self, event):
|
|
785
819
|
"""Handle assistant response events for comprehensive response tracking.
|
|
@@ -789,6 +823,7 @@ class EventHandlers:
|
|
|
789
823
|
- Captures response content and metadata for analysis
|
|
790
824
|
- Enables tracking of conversation flow and response patterns
|
|
791
825
|
- Essential for comprehensive monitoring of Claude interactions
|
|
826
|
+
- Scans for delegation anti-patterns and creates autotodos
|
|
792
827
|
"""
|
|
793
828
|
# Track the response for logging
|
|
794
829
|
try:
|
|
@@ -796,10 +831,17 @@ class EventHandlers:
|
|
|
796
831
|
if rtm and hasattr(rtm, "track_assistant_response"):
|
|
797
832
|
pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
|
|
798
833
|
rtm.track_assistant_response(event, pending_prompts)
|
|
799
|
-
except Exception:
|
|
834
|
+
except Exception: # nosec B110
|
|
800
835
|
# Response tracking is optional
|
|
801
836
|
pass
|
|
802
837
|
|
|
838
|
+
# Scan response for delegation anti-patterns and create autotodos
|
|
839
|
+
try:
|
|
840
|
+
self._scan_for_delegation_patterns(event)
|
|
841
|
+
except Exception as e: # nosec B110
|
|
842
|
+
if DEBUG:
|
|
843
|
+
_log(f"Delegation scanning error: {e}")
|
|
844
|
+
|
|
803
845
|
# Get working directory and git branch
|
|
804
846
|
working_dir = event.get("cwd", "")
|
|
805
847
|
git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
|
|
@@ -847,11 +889,25 @@ class EventHandlers:
|
|
|
847
889
|
|
|
848
890
|
# Debug logging
|
|
849
891
|
if DEBUG:
|
|
850
|
-
|
|
851
|
-
f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}"
|
|
852
|
-
file=sys.stderr,
|
|
892
|
+
_log(
|
|
893
|
+
f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}"
|
|
853
894
|
)
|
|
854
895
|
|
|
896
|
+
# Record assistant response for auto-pause if active
|
|
897
|
+
auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
|
|
898
|
+
if auto_pause and auto_pause.is_pause_active():
|
|
899
|
+
try:
|
|
900
|
+
# Summarize response to first 200 chars
|
|
901
|
+
summary = (
|
|
902
|
+
response_text[:200] + "..."
|
|
903
|
+
if len(response_text) > 200
|
|
904
|
+
else response_text
|
|
905
|
+
)
|
|
906
|
+
auto_pause.on_assistant_response(summary)
|
|
907
|
+
except Exception as e:
|
|
908
|
+
if DEBUG:
|
|
909
|
+
_log(f"Auto-pause response recording error: {e}")
|
|
910
|
+
|
|
855
911
|
# Emit normalized event
|
|
856
912
|
self.hook_handler._emit_socketio_event(
|
|
857
913
|
"", "assistant_response", assistant_response_data
|
|
@@ -864,6 +920,7 @@ class EventHandlers:
|
|
|
864
920
|
- Provides visibility into new conversation sessions
|
|
865
921
|
- Enables tracking of session lifecycle and duration
|
|
866
922
|
- Useful for monitoring concurrent sessions and resource usage
|
|
923
|
+
- Auto-inject pending autotodos if enabled in config
|
|
867
924
|
"""
|
|
868
925
|
session_id = event.get("session_id", "")
|
|
869
926
|
working_dir = event.get("cwd", "")
|
|
@@ -877,12 +934,151 @@ class EventHandlers:
|
|
|
877
934
|
"hook_event_name": "SessionStart",
|
|
878
935
|
}
|
|
879
936
|
|
|
937
|
+
# Auto-inject pending autotodos if enabled
|
|
938
|
+
try:
|
|
939
|
+
from pathlib import Path
|
|
940
|
+
|
|
941
|
+
from claude_mpm.cli.commands.autotodos import get_pending_todos
|
|
942
|
+
from claude_mpm.core.config import Config
|
|
943
|
+
|
|
944
|
+
config = Config()
|
|
945
|
+
auto_inject_enabled = config.get("autotodos.auto_inject_on_startup", True)
|
|
946
|
+
max_todos = config.get("autotodos.max_todos_per_session", 10)
|
|
947
|
+
|
|
948
|
+
if auto_inject_enabled:
|
|
949
|
+
# Pass working directory from event to avoid Path.cwd() issues
|
|
950
|
+
working_dir_param = None
|
|
951
|
+
if working_dir:
|
|
952
|
+
working_dir_param = Path(working_dir)
|
|
953
|
+
|
|
954
|
+
pending_todos = get_pending_todos(
|
|
955
|
+
max_todos=max_todos, working_dir=working_dir_param
|
|
956
|
+
)
|
|
957
|
+
if pending_todos:
|
|
958
|
+
session_start_data["pending_autotodos"] = pending_todos
|
|
959
|
+
session_start_data["autotodos_count"] = len(pending_todos)
|
|
960
|
+
_log(f" - Auto-injected {len(pending_todos)} pending autotodos")
|
|
961
|
+
except Exception as e: # nosec B110
|
|
962
|
+
# Auto-injection is optional - continue if it fails
|
|
963
|
+
_log(f" - Failed to auto-inject autotodos: {e}")
|
|
964
|
+
|
|
965
|
+
# Debug logging
|
|
966
|
+
_log(f"Hook handler: Processing SessionStart - session: '{session_id}'")
|
|
967
|
+
|
|
968
|
+
# Emit normalized event
|
|
969
|
+
self.hook_handler._emit_socketio_event("", "session_start", session_start_data)
|
|
970
|
+
|
|
971
|
+
def handle_subagent_start_fast(self, event):
|
|
972
|
+
"""Handle SubagentStart events with proper agent type extraction.
|
|
973
|
+
|
|
974
|
+
WHY separate from SessionStart:
|
|
975
|
+
- SubagentStart contains agent-specific information
|
|
976
|
+
- Frontend needs agent_type to create distinct agent nodes
|
|
977
|
+
- Multiple engineers should show as separate nodes in hierarchy
|
|
978
|
+
- Research agents must appear in the agent hierarchy
|
|
979
|
+
|
|
980
|
+
Unlike SessionStart, SubagentStart events contain agent-specific
|
|
981
|
+
information that must be preserved and emitted to the dashboard.
|
|
982
|
+
"""
|
|
983
|
+
session_id = event.get("session_id", "")
|
|
984
|
+
|
|
985
|
+
# Extract agent type from event - Claude provides this in SubagentStart
|
|
986
|
+
# Try multiple possible field names for compatibility
|
|
987
|
+
agent_type = event.get("agent_type") or event.get("subagent_type") or "unknown"
|
|
988
|
+
|
|
989
|
+
# Generate unique agent ID combining type and session
|
|
990
|
+
agent_id = event.get("agent_id", f"{agent_type}_{session_id[:8]}")
|
|
991
|
+
|
|
992
|
+
# Get working directory and git branch
|
|
993
|
+
working_dir = event.get("cwd", "")
|
|
994
|
+
git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
|
|
995
|
+
|
|
996
|
+
# Build subagent start data with all required fields
|
|
997
|
+
subagent_start_data = {
|
|
998
|
+
"session_id": session_id,
|
|
999
|
+
"agent_type": agent_type,
|
|
1000
|
+
"agent_id": agent_id,
|
|
1001
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
1002
|
+
"hook_event_name": "SubagentStart", # Preserve correct hook name
|
|
1003
|
+
"working_directory": working_dir,
|
|
1004
|
+
"git_branch": git_branch,
|
|
1005
|
+
}
|
|
1006
|
+
|
|
880
1007
|
# Debug logging
|
|
881
1008
|
if DEBUG:
|
|
882
|
-
|
|
883
|
-
f"Hook handler:
|
|
884
|
-
file=sys.stderr,
|
|
1009
|
+
_log(
|
|
1010
|
+
f"Hook handler: SubagentStart - agent_type='{agent_type}', agent_id='{agent_id}', session_id='{session_id[:16]}...'"
|
|
885
1011
|
)
|
|
886
1012
|
|
|
887
|
-
# Emit
|
|
888
|
-
self.hook_handler._emit_socketio_event(
|
|
1013
|
+
# Emit to /hook namespace as subagent_start (NOT session_start!)
|
|
1014
|
+
self.hook_handler._emit_socketio_event(
|
|
1015
|
+
"", "subagent_start", subagent_start_data
|
|
1016
|
+
)
|
|
1017
|
+
|
|
1018
|
+
def _scan_for_delegation_patterns(self, event):
|
|
1019
|
+
"""Scan assistant response for delegation anti-patterns.
|
|
1020
|
+
|
|
1021
|
+
WHY this is needed:
|
|
1022
|
+
- Detect when PM asks user to do something manually instead of delegating
|
|
1023
|
+
- Flag PM behavior violations for immediate correction
|
|
1024
|
+
- Enforce delegation principle in PM workflow
|
|
1025
|
+
- Help PM recognize delegation opportunities
|
|
1026
|
+
|
|
1027
|
+
This method scans the assistant's response text for patterns like:
|
|
1028
|
+
- "Make sure .env.local is in your .gitignore"
|
|
1029
|
+
- "You'll need to run npm install"
|
|
1030
|
+
- "Please run the tests manually"
|
|
1031
|
+
|
|
1032
|
+
When patterns are detected, PM violations are logged as errors/warnings
|
|
1033
|
+
that should be corrected immediately, NOT as todos to delegate.
|
|
1034
|
+
|
|
1035
|
+
DESIGN DECISION: pm.violation vs autotodo.delegation
|
|
1036
|
+
- Delegation patterns = PM doing something WRONG → pm.violation (error)
|
|
1037
|
+
- Script failures = Something BROKEN → autotodo.error (todo)
|
|
1038
|
+
"""
|
|
1039
|
+
# Only scan if delegation detector is available
|
|
1040
|
+
try:
|
|
1041
|
+
from claude_mpm.services.delegation_detector import get_delegation_detector
|
|
1042
|
+
from claude_mpm.services.event_log import get_event_log
|
|
1043
|
+
except ImportError:
|
|
1044
|
+
if DEBUG:
|
|
1045
|
+
_log("Delegation detector or event log not available")
|
|
1046
|
+
return
|
|
1047
|
+
|
|
1048
|
+
response_text = event.get("response", "")
|
|
1049
|
+
if not response_text:
|
|
1050
|
+
return
|
|
1051
|
+
|
|
1052
|
+
# Get the delegation detector
|
|
1053
|
+
detector = get_delegation_detector()
|
|
1054
|
+
|
|
1055
|
+
# Detect delegation patterns
|
|
1056
|
+
detections = detector.detect_user_delegation(response_text)
|
|
1057
|
+
|
|
1058
|
+
if not detections:
|
|
1059
|
+
return # No patterns detected
|
|
1060
|
+
|
|
1061
|
+
# Get event log for violation recording
|
|
1062
|
+
event_log = get_event_log()
|
|
1063
|
+
|
|
1064
|
+
# Create PM violation events (NOT autotodos)
|
|
1065
|
+
for detection in detections:
|
|
1066
|
+
# Create event log entry as pm.violation
|
|
1067
|
+
event_log.append_event(
|
|
1068
|
+
event_type="pm.violation",
|
|
1069
|
+
payload={
|
|
1070
|
+
"violation_type": "delegation_anti_pattern",
|
|
1071
|
+
"pattern_type": detection["pattern_type"],
|
|
1072
|
+
"original_text": detection["original_text"],
|
|
1073
|
+
"suggested_action": detection["suggested_todo"],
|
|
1074
|
+
"action": detection["action"],
|
|
1075
|
+
"session_id": event.get("session_id", ""),
|
|
1076
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
1077
|
+
"severity": "warning", # Not critical, but should be fixed
|
|
1078
|
+
"message": f"PM asked user to do something manually: {detection['original_text'][:80]}...",
|
|
1079
|
+
},
|
|
1080
|
+
status="pending",
|
|
1081
|
+
)
|
|
1082
|
+
|
|
1083
|
+
if DEBUG:
|
|
1084
|
+
_log(f"⚠️ PM violation detected: {detection['original_text'][:60]}...")
|