claude-mpm 5.4.85__py3-none-any.whl → 5.6.76__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 +109 -706
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/auth/__init__.py +35 -0
- claude_mpm/auth/callback_server.py +328 -0
- claude_mpm/auth/models.py +104 -0
- claude_mpm/auth/oauth_manager.py +266 -0
- claude_mpm/auth/providers/__init__.py +12 -0
- claude_mpm/auth/providers/base.py +165 -0
- claude_mpm/auth/providers/google.py +261 -0
- claude_mpm/auth/token_storage.py +252 -0
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +216 -0
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/mcp.py +29 -17
- claude_mpm/cli/commands/mcp_command_router.py +39 -0
- claude_mpm/cli/commands/mcp_service_commands.py +304 -0
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/oauth.py +481 -0
- 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 +128 -16
- claude_mpm/cli/helpers.py +1 -1
- claude_mpm/cli/parsers/base_parser.py +84 -1
- claude_mpm/cli/parsers/commander_parser.py +116 -0
- claude_mpm/cli/parsers/mcp_parser.py +79 -0
- claude_mpm/cli/parsers/oauth_parser.py +165 -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 +345 -40
- claude_mpm/cli/startup_display.py +76 -7
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/startup_migrations.py +236 -0
- 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 +149 -0
- claude_mpm/commander/chat/commands.py +124 -0
- claude_mpm/commander/chat/repl.py +1957 -0
- claude_mpm/commander/config.py +51 -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 +603 -0
- claude_mpm/commander/env_loader.py +59 -0
- claude_mpm/commander/events/__init__.py +26 -0
- claude_mpm/commander/events/manager.py +392 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +233 -0
- claude_mpm/commander/frameworks/claude_code.py +58 -0
- claude_mpm/commander/frameworks/mpm.py +57 -0
- claude_mpm/commander/git/__init__.py +5 -0
- claude_mpm/commander/git/worktree_manager.py +212 -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 +868 -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 +127 -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 +403 -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 +362 -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 +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 +5 -0
- claude_mpm/core/claude_runner.py +152 -0
- claude_mpm/core/config.py +35 -22
- claude_mpm/core/config_constants.py +74 -9
- claude_mpm/core/constants.py +56 -12
- claude_mpm/core/hook_manager.py +53 -4
- claude_mpm/core/interactive_session.py +5 -4
- claude_mpm/core/logger.py +26 -9
- claude_mpm/core/logging_utils.py +39 -13
- claude_mpm/core/network_config.py +148 -0
- claude_mpm/core/oneshot_session.py +7 -6
- claude_mpm/core/output_style_manager.py +52 -12
- claude_mpm/core/socketio_pool.py +47 -15
- claude_mpm/core/unified_config.py +10 -6
- claude_mpm/core/unified_paths.py +68 -80
- 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 +485 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
- claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +291 -59
- claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
- claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
- claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
- claude_mpm/hooks/claude_hooks/services/container.py +326 -0
- claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
- claude_mpm/hooks/session_resume_hook.py +89 -1
- claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
- claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
- claude_mpm/init.py +22 -15
- claude_mpm/mcp/__init__.py +9 -0
- claude_mpm/mcp/google_workspace_server.py +610 -0
- claude_mpm/scripts/claude-hook-handler.sh +46 -19
- 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/remote_agent_discovery_service.py +3 -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/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/command_deployment_service.py +44 -26
- 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/hook_installer_service.py +77 -8
- 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/mcp_config_manager.py +99 -19
- claude_mpm/services/mcp_service_registry.py +294 -0
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +111 -16
- claude_mpm/services/pm_skills_deployer.py +261 -87
- claude_mpm/services/skills/git_skill_source_manager.py +130 -10
- claude_mpm/services/skills/selective_skill_deployer.py +142 -16
- 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/__init__.py +2 -1
- 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-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/{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/registry.py +295 -90
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm-5.6.76.dist-info/METADATA +416 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/RECORD +312 -175
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/WHEEL +1 -1
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/entry_points.txt +2 -0
- 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.76.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/top_level.txt +0 -0
|
@@ -8,13 +8,22 @@ This service manages:
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
|
-
import subprocess
|
|
11
|
+
import subprocess # nosec B404
|
|
12
12
|
import time
|
|
13
13
|
from collections import deque
|
|
14
14
|
from datetime import datetime, timezone
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from typing import Optional
|
|
17
17
|
|
|
18
|
+
# Try to import _log from hook_handler, fall back to no-op
|
|
19
|
+
try:
|
|
20
|
+
from claude_mpm.hooks.claude_hooks.hook_handler import _log
|
|
21
|
+
except ImportError:
|
|
22
|
+
|
|
23
|
+
def _log(msg: str) -> None:
|
|
24
|
+
pass # Silent fallback
|
|
25
|
+
|
|
26
|
+
|
|
18
27
|
# Import constants for configuration
|
|
19
28
|
try:
|
|
20
29
|
from claude_mpm.core.constants import TimeoutConfig
|
|
@@ -24,8 +33,8 @@ except ImportError:
|
|
|
24
33
|
QUICK_TIMEOUT = 2.0
|
|
25
34
|
|
|
26
35
|
|
|
27
|
-
# Debug mode
|
|
28
|
-
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "
|
|
36
|
+
# Debug mode - disabled by default to prevent logging overhead in production
|
|
37
|
+
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "false").lower() == "true"
|
|
29
38
|
|
|
30
39
|
|
|
31
40
|
class StateManagerService:
|
|
@@ -63,17 +72,11 @@ class StateManagerService:
|
|
|
63
72
|
):
|
|
64
73
|
"""Track a new agent delegation with optional request data for response correlation."""
|
|
65
74
|
if DEBUG:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
print(f" - agent_type: {agent_type}", file=sys.stderr)
|
|
73
|
-
print(f" - request_data provided: {bool(request_data)}", file=sys.stderr)
|
|
74
|
-
print(
|
|
75
|
-
f" - delegation_requests size before: {len(self.delegation_requests)}",
|
|
76
|
-
file=sys.stderr,
|
|
75
|
+
_log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
|
|
76
|
+
_log(f" - agent_type: {agent_type}")
|
|
77
|
+
_log(f" - request_data provided: {bool(request_data)}")
|
|
78
|
+
_log(
|
|
79
|
+
f" - delegation_requests size before: {len(self.delegation_requests)}"
|
|
77
80
|
)
|
|
78
81
|
|
|
79
82
|
if session_id and agent_type and agent_type != "unknown":
|
|
@@ -89,15 +92,9 @@ class StateManagerService:
|
|
|
89
92
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
90
93
|
}
|
|
91
94
|
if DEBUG:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
f" - ✅ Stored in delegation_requests[{session_id[:16]}...]",
|
|
96
|
-
file=sys.stderr,
|
|
97
|
-
)
|
|
98
|
-
print(
|
|
99
|
-
f" - delegation_requests size after: {len(self.delegation_requests)}",
|
|
100
|
-
file=sys.stderr,
|
|
95
|
+
_log(f" - ✅ Stored in delegation_requests[{session_id[:16]}...]")
|
|
96
|
+
_log(
|
|
97
|
+
f" - delegation_requests size after: {len(self.delegation_requests)}"
|
|
101
98
|
)
|
|
102
99
|
|
|
103
100
|
# Clean up old delegations (older than 5 minutes)
|
|
@@ -197,7 +194,7 @@ class StateManagerService:
|
|
|
197
194
|
os.chdir(working_dir)
|
|
198
195
|
|
|
199
196
|
# Run git command to get current branch
|
|
200
|
-
result = subprocess.run(
|
|
197
|
+
result = subprocess.run( # nosec B603 B607
|
|
201
198
|
["git", "branch", "--show-current"],
|
|
202
199
|
capture_output=True,
|
|
203
200
|
text=True,
|
|
@@ -233,17 +230,12 @@ class StateManagerService:
|
|
|
233
230
|
def find_matching_request(self, session_id: str) -> Optional[dict]:
|
|
234
231
|
"""Find matching request data for a session, with fuzzy matching fallback."""
|
|
235
232
|
# First try exact match
|
|
236
|
-
request_info = self.delegation_requests.get(session_id)
|
|
233
|
+
request_info = self.delegation_requests.get(session_id) # nosec B113
|
|
237
234
|
|
|
238
235
|
# If exact match fails, try partial matching
|
|
239
236
|
if not request_info and session_id:
|
|
240
237
|
if DEBUG:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
print(
|
|
244
|
-
f" - Trying fuzzy match for session {session_id[:16]}...",
|
|
245
|
-
file=sys.stderr,
|
|
246
|
-
)
|
|
238
|
+
_log(f" - Trying fuzzy match for session {session_id[:16]}...")
|
|
247
239
|
# Try to find a session that matches the first 8-16 characters
|
|
248
240
|
for stored_sid in list(self.delegation_requests.keys()):
|
|
249
241
|
if (
|
|
@@ -256,13 +248,8 @@ class StateManagerService:
|
|
|
256
248
|
)
|
|
257
249
|
):
|
|
258
250
|
if DEBUG:
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
print(
|
|
262
|
-
f" - ✅ Fuzzy match found: {stored_sid[:16]}...",
|
|
263
|
-
file=sys.stderr,
|
|
264
|
-
)
|
|
265
|
-
request_info = self.delegation_requests.get(stored_sid)
|
|
251
|
+
_log(f" - ✅ Fuzzy match found: {stored_sid[:16]}...")
|
|
252
|
+
request_info = self.delegation_requests.get(stored_sid) # nosec B113
|
|
266
253
|
# Update the key to use the current session_id for consistency
|
|
267
254
|
if request_info:
|
|
268
255
|
self.delegation_requests[session_id] = request_info
|
|
@@ -10,12 +10,20 @@ This service handles:
|
|
|
10
10
|
import json
|
|
11
11
|
import os
|
|
12
12
|
import re
|
|
13
|
-
import sys
|
|
14
13
|
from datetime import datetime, timezone
|
|
15
14
|
from typing import Optional, Tuple
|
|
16
15
|
|
|
17
|
-
#
|
|
18
|
-
|
|
16
|
+
# Try to import _log from hook_handler, fall back to no-op
|
|
17
|
+
try:
|
|
18
|
+
from claude_mpm.hooks.claude_hooks.hook_handler import _log
|
|
19
|
+
except ImportError:
|
|
20
|
+
|
|
21
|
+
def _log(msg: str) -> None:
|
|
22
|
+
pass # Silent fallback
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Debug mode - disabled by default to prevent logging overhead in production
|
|
26
|
+
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "false").lower() == "true"
|
|
19
27
|
|
|
20
28
|
|
|
21
29
|
class SubagentResponseProcessor:
|
|
@@ -45,35 +53,31 @@ class SubagentResponseProcessor:
|
|
|
45
53
|
# Enhanced debug logging for session correlation
|
|
46
54
|
session_id = event.get("session_id", "")
|
|
47
55
|
if DEBUG:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
print(f" - event keys: {list(event.keys())}", file=sys.stderr)
|
|
53
|
-
print(
|
|
54
|
-
f" - delegation_requests size: {len(self.state_manager.delegation_requests)}",
|
|
55
|
-
file=sys.stderr,
|
|
56
|
+
_log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
|
|
57
|
+
_log(f" - event keys: {list(event.keys())}")
|
|
58
|
+
_log(
|
|
59
|
+
f" - delegation_requests size: {len(self.state_manager.delegation_requests)}"
|
|
56
60
|
)
|
|
57
61
|
# Show all stored session IDs for comparison
|
|
58
62
|
all_sessions = list(self.state_manager.delegation_requests.keys())
|
|
59
63
|
if all_sessions:
|
|
60
|
-
|
|
64
|
+
_log(" - Stored sessions (first 16 chars):")
|
|
61
65
|
for sid in all_sessions[:10]: # Show up to 10
|
|
62
|
-
|
|
63
|
-
f" - {sid[:16]}... (agent: {self.state_manager.delegation_requests[sid].get('agent_type', 'unknown')})"
|
|
64
|
-
file=sys.stderr,
|
|
66
|
+
_log(
|
|
67
|
+
f" - {sid[:16]}... (agent: {self.state_manager.delegation_requests[sid].get('agent_type', 'unknown')})"
|
|
65
68
|
)
|
|
66
69
|
else:
|
|
67
|
-
|
|
70
|
+
_log(" - No stored sessions in delegation_requests!")
|
|
68
71
|
|
|
69
72
|
# Get agent type and other basic info
|
|
70
|
-
agent_type, agent_id, reason = self._extract_basic_info(
|
|
73
|
+
agent_type, agent_id, reason, agent_type_inferred = self._extract_basic_info(
|
|
74
|
+
event, session_id
|
|
75
|
+
)
|
|
71
76
|
|
|
72
77
|
# Always log SubagentStop events for debugging
|
|
73
78
|
if DEBUG or agent_type != "unknown":
|
|
74
|
-
|
|
75
|
-
f"Hook handler: Processing SubagentStop - agent: '{agent_type}', session: '{session_id}', reason: '{reason}'"
|
|
76
|
-
file=sys.stderr,
|
|
79
|
+
_log(
|
|
80
|
+
f"Hook handler: Processing SubagentStop - agent: '{agent_type}', session: '{session_id}', reason: '{reason}'"
|
|
77
81
|
)
|
|
78
82
|
|
|
79
83
|
# Get working directory and git branch
|
|
@@ -108,20 +112,29 @@ class SubagentResponseProcessor:
|
|
|
108
112
|
working_dir,
|
|
109
113
|
git_branch,
|
|
110
114
|
structured_response,
|
|
115
|
+
agent_type_inferred,
|
|
111
116
|
)
|
|
112
117
|
|
|
113
118
|
# Debug log the processed data
|
|
114
119
|
if DEBUG:
|
|
115
|
-
|
|
116
|
-
f"SubagentStop processed data: agent_type='{agent_type}', session_id='{session_id}'"
|
|
117
|
-
file=sys.stderr,
|
|
120
|
+
_log(
|
|
121
|
+
f"SubagentStop processed data: agent_type='{agent_type}', session_id='{session_id}'"
|
|
118
122
|
)
|
|
119
123
|
|
|
120
|
-
# Emit to
|
|
121
|
-
self.connection_manager.emit_event("
|
|
124
|
+
# Emit to default namespace (consistent with subagent_start)
|
|
125
|
+
self.connection_manager.emit_event("", "subagent_stop", subagent_stop_data)
|
|
126
|
+
|
|
127
|
+
def _extract_basic_info(
|
|
128
|
+
self, event: dict, session_id: str
|
|
129
|
+
) -> Tuple[str, str, str, bool]:
|
|
130
|
+
"""Extract basic info from the event.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Tuple of (agent_type, agent_id, reason, agent_type_inferred)
|
|
134
|
+
- agent_type_inferred is True when defaulted to "pm"
|
|
135
|
+
"""
|
|
136
|
+
agent_type_inferred = False
|
|
122
137
|
|
|
123
|
-
def _extract_basic_info(self, event: dict, session_id: str) -> Tuple[str, str, str]:
|
|
124
|
-
"""Extract basic info from the event."""
|
|
125
138
|
# First try to get agent type from our tracking
|
|
126
139
|
agent_type = (
|
|
127
140
|
self.state_manager.get_delegation_agent_type(session_id)
|
|
@@ -146,7 +159,14 @@ class SubagentResponseProcessor:
|
|
|
146
159
|
elif "pm" in task_desc or "project" in task_desc:
|
|
147
160
|
agent_type = "pm"
|
|
148
161
|
|
|
149
|
-
|
|
162
|
+
# Default to "pm" if still unknown (main conversation doesn't use Task tool)
|
|
163
|
+
if agent_type == "unknown":
|
|
164
|
+
agent_type = "pm"
|
|
165
|
+
agent_type_inferred = True
|
|
166
|
+
if DEBUG:
|
|
167
|
+
_log(" - Inferred agent_type='pm' (no explicit type found)")
|
|
168
|
+
|
|
169
|
+
return agent_type, agent_id, reason, agent_type_inferred
|
|
150
170
|
|
|
151
171
|
def _extract_structured_response(
|
|
152
172
|
self, output: str, agent_type: str
|
|
@@ -160,17 +180,15 @@ class SubagentResponseProcessor:
|
|
|
160
180
|
if json_match:
|
|
161
181
|
structured_response = json.loads(json_match.group(1))
|
|
162
182
|
if DEBUG:
|
|
163
|
-
|
|
164
|
-
f"Extracted structured response from {agent_type} agent in SubagentStop"
|
|
165
|
-
file=sys.stderr,
|
|
183
|
+
_log(
|
|
184
|
+
f"Extracted structured response from {agent_type} agent in SubagentStop"
|
|
166
185
|
)
|
|
167
186
|
|
|
168
187
|
# Log if MEMORIES field is present
|
|
169
188
|
if structured_response.get("MEMORIES") and DEBUG:
|
|
170
189
|
memories_count = len(structured_response["MEMORIES"])
|
|
171
|
-
|
|
172
|
-
f"Agent {agent_type} returned MEMORIES field with {memories_count} items"
|
|
173
|
-
file=sys.stderr,
|
|
190
|
+
_log(
|
|
191
|
+
f"Agent {agent_type} returned MEMORIES field with {memories_count} items"
|
|
174
192
|
)
|
|
175
193
|
|
|
176
194
|
return structured_response
|
|
@@ -192,20 +210,15 @@ class SubagentResponseProcessor:
|
|
|
192
210
|
):
|
|
193
211
|
"""Track the agent response if response tracking is enabled."""
|
|
194
212
|
if DEBUG:
|
|
195
|
-
|
|
196
|
-
f" - response_tracking_enabled: {self.response_tracking_manager.response_tracking_enabled}"
|
|
197
|
-
file=sys.stderr,
|
|
213
|
+
_log(
|
|
214
|
+
f" - response_tracking_enabled: {self.response_tracking_manager.response_tracking_enabled}"
|
|
198
215
|
)
|
|
199
|
-
|
|
200
|
-
f" - response_tracker exists: {self.response_tracking_manager.response_tracker is not None}"
|
|
201
|
-
file=sys.stderr,
|
|
216
|
+
_log(
|
|
217
|
+
f" - response_tracker exists: {self.response_tracking_manager.response_tracker is not None}"
|
|
202
218
|
)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
)
|
|
207
|
-
print(f" - agent_type: {agent_type}", file=sys.stderr)
|
|
208
|
-
print(f" - reason: {reason}", file=sys.stderr)
|
|
219
|
+
_log(f" - session_id: {session_id[:16] if session_id else 'None'}...")
|
|
220
|
+
_log(f" - agent_type: {agent_type}")
|
|
221
|
+
_log(f" - reason: {reason}")
|
|
209
222
|
|
|
210
223
|
if (
|
|
211
224
|
self.response_tracking_manager.response_tracking_enabled
|
|
@@ -216,27 +229,16 @@ class SubagentResponseProcessor:
|
|
|
216
229
|
request_info = self.state_manager.find_matching_request(session_id)
|
|
217
230
|
|
|
218
231
|
if DEBUG:
|
|
219
|
-
|
|
220
|
-
f" - request_info present: {bool(request_info)}",
|
|
221
|
-
file=sys.stderr,
|
|
222
|
-
)
|
|
232
|
+
_log(f" - request_info present: {bool(request_info)}")
|
|
223
233
|
if request_info:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
print(
|
|
229
|
-
f" - stored agent_type: {request_info.get('agent_type')}",
|
|
230
|
-
file=sys.stderr,
|
|
231
|
-
)
|
|
232
|
-
print(
|
|
233
|
-
f" - request keys: {list(request_info.get('request', {}).keys())}",
|
|
234
|
-
file=sys.stderr,
|
|
234
|
+
_log(" - ✅ Found request data for response tracking")
|
|
235
|
+
_log(f" - stored agent_type: {request_info.get('agent_type')}")
|
|
236
|
+
_log(
|
|
237
|
+
f" - request keys: {list(request_info.get('request', {}).keys())}"
|
|
235
238
|
)
|
|
236
239
|
else:
|
|
237
|
-
|
|
238
|
-
f" - ❌ No request data found for session {session_id[:16]}..."
|
|
239
|
-
file=sys.stderr,
|
|
240
|
+
_log(
|
|
241
|
+
f" - ❌ No request data found for session {session_id[:16]}..."
|
|
240
242
|
)
|
|
241
243
|
|
|
242
244
|
if request_info:
|
|
@@ -288,9 +290,8 @@ class SubagentResponseProcessor:
|
|
|
288
290
|
# Check for MEMORIES field and process if present
|
|
289
291
|
if structured_response.get("MEMORIES") and DEBUG:
|
|
290
292
|
memories = structured_response["MEMORIES"]
|
|
291
|
-
|
|
292
|
-
f"Found MEMORIES field in {agent_type} response with {len(memories)} items"
|
|
293
|
-
file=sys.stderr,
|
|
293
|
+
_log(
|
|
294
|
+
f"Found MEMORIES field in {agent_type} response with {len(memories)} items"
|
|
294
295
|
)
|
|
295
296
|
# The memory will be processed by extract_and_update_memory
|
|
296
297
|
# which is called by the memory hook service
|
|
@@ -307,26 +308,21 @@ class SubagentResponseProcessor:
|
|
|
307
308
|
)
|
|
308
309
|
|
|
309
310
|
if file_path and DEBUG:
|
|
310
|
-
|
|
311
|
-
f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}"
|
|
312
|
-
file=sys.stderr,
|
|
311
|
+
_log(
|
|
312
|
+
f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}"
|
|
313
313
|
)
|
|
314
314
|
|
|
315
315
|
# Clean up the request data
|
|
316
316
|
self.state_manager.remove_request(session_id)
|
|
317
317
|
|
|
318
318
|
elif DEBUG:
|
|
319
|
-
|
|
320
|
-
f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}"
|
|
321
|
-
file=sys.stderr,
|
|
319
|
+
_log(
|
|
320
|
+
f"No request data for SubagentStop session {session_id[:8]}..., agent: {agent_type}"
|
|
322
321
|
)
|
|
323
322
|
|
|
324
323
|
except Exception as e:
|
|
325
324
|
if DEBUG:
|
|
326
|
-
|
|
327
|
-
f"❌ Failed to track response on SubagentStop: {e}",
|
|
328
|
-
file=sys.stderr,
|
|
329
|
-
)
|
|
325
|
+
_log(f"❌ Failed to track response on SubagentStop: {e}")
|
|
330
326
|
|
|
331
327
|
def _build_subagent_stop_data(
|
|
332
328
|
self,
|
|
@@ -338,10 +334,12 @@ class SubagentResponseProcessor:
|
|
|
338
334
|
working_dir: str,
|
|
339
335
|
git_branch: str,
|
|
340
336
|
structured_response: Optional[dict],
|
|
337
|
+
agent_type_inferred: bool,
|
|
341
338
|
) -> dict:
|
|
342
339
|
"""Build the subagent stop data for event emission."""
|
|
343
340
|
subagent_stop_data = {
|
|
344
341
|
"agent_type": agent_type,
|
|
342
|
+
"agent_type_inferred": agent_type_inferred,
|
|
345
343
|
"agent_id": agent_id,
|
|
346
344
|
"reason": reason,
|
|
347
345
|
"session_id": session_id,
|
|
@@ -8,14 +8,25 @@ DESIGN DECISIONS:
|
|
|
8
8
|
- Non-blocking: doesn't prevent PM from starting if check fails
|
|
9
9
|
- Displays context to stdout for user visibility
|
|
10
10
|
- Integrates with existing session pause/resume infrastructure
|
|
11
|
+
- Checks for ACTIVE-PAUSE.jsonl (incremental auto-pause) before regular paused sessions
|
|
11
12
|
"""
|
|
12
13
|
|
|
14
|
+
import json
|
|
13
15
|
from pathlib import Path
|
|
14
16
|
from typing import Any, Dict, Optional
|
|
15
17
|
|
|
16
18
|
from claude_mpm.core.logger import get_logger
|
|
17
19
|
from claude_mpm.services.cli.session_resume_helper import SessionResumeHelper
|
|
18
20
|
|
|
21
|
+
# Try to import _log from hook_handler, fall back to no-op
|
|
22
|
+
try:
|
|
23
|
+
from claude_mpm.hooks.claude_hooks.hook_handler import _log
|
|
24
|
+
except ImportError:
|
|
25
|
+
|
|
26
|
+
def _log(msg: str) -> None:
|
|
27
|
+
pass # Silent fallback
|
|
28
|
+
|
|
29
|
+
|
|
19
30
|
logger = get_logger(__name__)
|
|
20
31
|
|
|
21
32
|
|
|
@@ -31,10 +42,79 @@ class SessionResumeStartupHook:
|
|
|
31
42
|
self.project_path = project_path or Path.cwd()
|
|
32
43
|
self.resume_helper = SessionResumeHelper(self.project_path)
|
|
33
44
|
self._session_displayed = False
|
|
45
|
+
self.sessions_dir = self.project_path / ".claude-mpm" / "sessions"
|
|
46
|
+
|
|
47
|
+
def check_for_active_pause(self) -> Optional[Dict[str, Any]]:
|
|
48
|
+
"""Check for an active incremental pause session.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Pause session metadata if ACTIVE-PAUSE.jsonl exists, None otherwise
|
|
52
|
+
"""
|
|
53
|
+
active_pause_path = self.sessions_dir / "ACTIVE-PAUSE.jsonl"
|
|
54
|
+
|
|
55
|
+
if not active_pause_path.exists():
|
|
56
|
+
logger.debug("No ACTIVE-PAUSE.jsonl found")
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
# Read JSONL file to get first and last actions
|
|
61
|
+
with active_pause_path.open("r") as f:
|
|
62
|
+
lines = f.readlines()
|
|
63
|
+
|
|
64
|
+
if not lines:
|
|
65
|
+
logger.warning("ACTIVE-PAUSE.jsonl is empty")
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
# Parse first action (session start)
|
|
69
|
+
first_action = json.loads(lines[0])
|
|
70
|
+
|
|
71
|
+
# Parse last action (most recent)
|
|
72
|
+
last_action = json.loads(lines[-1]) if len(lines) > 1 else first_action
|
|
73
|
+
|
|
74
|
+
# Extract metadata
|
|
75
|
+
return {
|
|
76
|
+
"is_incremental": True,
|
|
77
|
+
"session_id": first_action.get("session_id"),
|
|
78
|
+
"started_at": first_action.get("timestamp"),
|
|
79
|
+
"context_at_start": first_action.get("data", {}).get(
|
|
80
|
+
"context_percentage", 0
|
|
81
|
+
),
|
|
82
|
+
"current_context": last_action.get("context_percentage", 0),
|
|
83
|
+
"action_count": len(lines),
|
|
84
|
+
"file_path": str(active_pause_path),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
except (json.JSONDecodeError, OSError, KeyError) as e:
|
|
88
|
+
logger.error(f"Failed to parse ACTIVE-PAUSE.jsonl: {e}", exc_info=True)
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
def display_active_pause_warning(self, pause_info: Dict[str, Any]) -> None:
|
|
92
|
+
"""Display warning about active incremental pause session.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
pause_info: Pause session metadata from check_for_active_pause()
|
|
96
|
+
"""
|
|
97
|
+
_log("=" * 60)
|
|
98
|
+
_log("⚠️ ACTIVE AUTO-PAUSE SESSION DETECTED")
|
|
99
|
+
_log("=" * 60)
|
|
100
|
+
_log(f"Session ID: {pause_info['session_id']}")
|
|
101
|
+
_log(f"Started at: {pause_info['started_at']}")
|
|
102
|
+
_log(f"Context at pause: {pause_info['context_at_start']:.1%}")
|
|
103
|
+
_log(f"Actions recorded: {pause_info['action_count']}")
|
|
104
|
+
_log("\nThis session was auto-paused due to high context usage.")
|
|
105
|
+
_log("Options:")
|
|
106
|
+
_log(" 1. Continue (actions will be appended)")
|
|
107
|
+
_log(" 2. Use /mpm-init pause --finalize to create snapshot")
|
|
108
|
+
_log(" 3. Use /mpm-init pause --discard to abandon")
|
|
109
|
+
_log("=" * 60 + "\n")
|
|
34
110
|
|
|
35
111
|
def on_pm_startup(self) -> Optional[Dict[str, Any]]:
|
|
36
112
|
"""Execute on PM startup to check for paused sessions.
|
|
37
113
|
|
|
114
|
+
Checks in priority order:
|
|
115
|
+
1. ACTIVE-PAUSE.jsonl (incremental auto-pause)
|
|
116
|
+
2. Regular paused sessions (session-*.json)
|
|
117
|
+
|
|
38
118
|
Returns:
|
|
39
119
|
Session data if paused session found, None otherwise
|
|
40
120
|
"""
|
|
@@ -44,7 +124,15 @@ class SessionResumeStartupHook:
|
|
|
44
124
|
logger.debug("Session already displayed, skipping")
|
|
45
125
|
return None
|
|
46
126
|
|
|
47
|
-
# Check for
|
|
127
|
+
# PRIORITY 1: Check for active incremental pause FIRST
|
|
128
|
+
active_pause_info = self.check_for_active_pause()
|
|
129
|
+
if active_pause_info:
|
|
130
|
+
self.display_active_pause_warning(active_pause_info)
|
|
131
|
+
self._session_displayed = True
|
|
132
|
+
logger.info("Active pause session detected and displayed")
|
|
133
|
+
return active_pause_info
|
|
134
|
+
|
|
135
|
+
# PRIORITY 2: Fall back to regular paused sessions
|
|
48
136
|
session_data = self.resume_helper.check_and_display_resume_prompt()
|
|
49
137
|
|
|
50
138
|
if session_data:
|
|
@@ -38,7 +38,7 @@ def main():
|
|
|
38
38
|
# Read event from stdin
|
|
39
39
|
event_data = sys.stdin.read()
|
|
40
40
|
if not event_data.strip():
|
|
41
|
-
print(json.dumps({"
|
|
41
|
+
print(json.dumps({"continue": True}))
|
|
42
42
|
return
|
|
43
43
|
|
|
44
44
|
event = json.loads(event_data)
|
|
@@ -49,7 +49,7 @@ def main():
|
|
|
49
49
|
if tool_name == "Grep" and "-n" not in tool_input:
|
|
50
50
|
modified_input = tool_input.copy()
|
|
51
51
|
modified_input["-n"] = True
|
|
52
|
-
print(json.dumps({"
|
|
52
|
+
print(json.dumps({"continue": True, "tool_input": modified_input}))
|
|
53
53
|
return
|
|
54
54
|
|
|
55
55
|
# Example: Block operations on .env files
|
|
@@ -59,19 +59,19 @@ def main():
|
|
|
59
59
|
print(
|
|
60
60
|
json.dumps(
|
|
61
61
|
{
|
|
62
|
-
"
|
|
63
|
-
"
|
|
62
|
+
"continue": False,
|
|
63
|
+
"stopReason": "Access to .env file blocked for security",
|
|
64
64
|
}
|
|
65
65
|
)
|
|
66
66
|
)
|
|
67
67
|
return
|
|
68
68
|
|
|
69
69
|
# Default: continue without modification
|
|
70
|
-
print(json.dumps({"
|
|
70
|
+
print(json.dumps({"continue": True}))
|
|
71
71
|
|
|
72
72
|
except Exception:
|
|
73
73
|
# Always continue on error to avoid blocking Claude
|
|
74
|
-
print(json.dumps({"
|
|
74
|
+
print(json.dumps({"continue": True}))
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
if __name__ == "__main__":
|
|
@@ -29,7 +29,7 @@ Input Format (stdin):
|
|
|
29
29
|
|
|
30
30
|
Output Format (stdout):
|
|
31
31
|
{
|
|
32
|
-
"
|
|
32
|
+
"continue": true,
|
|
33
33
|
"tool_input": {
|
|
34
34
|
"file_path": "/path/to/file.py",
|
|
35
35
|
"old_string": "foo",
|
|
@@ -39,13 +39,13 @@ Output Format (stdout):
|
|
|
39
39
|
|
|
40
40
|
Or to block execution:
|
|
41
41
|
{
|
|
42
|
-
"
|
|
43
|
-
"
|
|
42
|
+
"continue": false,
|
|
43
|
+
"stopReason": "Reason for blocking"
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
Or to continue without modification:
|
|
47
47
|
{
|
|
48
|
-
"
|
|
48
|
+
"continue": true
|
|
49
49
|
}
|
|
50
50
|
"""
|
|
51
51
|
|
|
@@ -55,6 +55,14 @@ import sys
|
|
|
55
55
|
from pathlib import Path
|
|
56
56
|
from typing import Any, Dict, Optional
|
|
57
57
|
|
|
58
|
+
# Try to import _log from hook_handler, fall back to no-op
|
|
59
|
+
try:
|
|
60
|
+
from claude_mpm.hooks.claude_hooks.hook_handler import _log
|
|
61
|
+
except ImportError:
|
|
62
|
+
|
|
63
|
+
def _log(msg: str) -> None:
|
|
64
|
+
pass # Silent fallback
|
|
65
|
+
|
|
58
66
|
|
|
59
67
|
class PreToolUseHook:
|
|
60
68
|
"""Base class for PreToolUse hooks with input modification support."""
|
|
@@ -64,9 +72,9 @@ class PreToolUseHook:
|
|
|
64
72
|
self.debug = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "false").lower() == "true"
|
|
65
73
|
|
|
66
74
|
def log_debug(self, message: str) -> None:
|
|
67
|
-
"""Log debug message
|
|
75
|
+
"""Log debug message using _log helper."""
|
|
68
76
|
if self.debug:
|
|
69
|
-
|
|
77
|
+
_log(f"[PreToolUse Hook] {message}")
|
|
70
78
|
|
|
71
79
|
def read_event(self) -> Optional[Dict[str, Any]]:
|
|
72
80
|
"""Read and parse the hook event from stdin."""
|
|
@@ -86,14 +94,14 @@ class PreToolUseHook:
|
|
|
86
94
|
self, modified_input: Optional[Dict[str, Any]] = None
|
|
87
95
|
) -> None:
|
|
88
96
|
"""Continue execution with optional modified input."""
|
|
89
|
-
response = {"
|
|
97
|
+
response = {"continue": True}
|
|
90
98
|
if modified_input is not None:
|
|
91
99
|
response["tool_input"] = modified_input
|
|
92
100
|
print(json.dumps(response))
|
|
93
101
|
|
|
94
102
|
def block_execution(self, message: str) -> None:
|
|
95
103
|
"""Block execution with a message."""
|
|
96
|
-
response = {"
|
|
104
|
+
response = {"continue": False, "stopReason": message}
|
|
97
105
|
print(json.dumps(response))
|
|
98
106
|
|
|
99
107
|
def modify_input(
|