claude-mpm 5.4.41__py3-none-any.whl → 5.6.72__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 +109 -1925
- claude_mpm/agents/PM_INSTRUCTIONS.md +161 -298
- 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/__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 +216 -0
- claude_mpm/cli/commands/configure.py +620 -21
- claude_mpm/cli/commands/configure_agent_display.py +3 -1
- 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 +15 -8
- claude_mpm/cli/commands/oauth.py +481 -0
- claude_mpm/cli/commands/profile.py +9 -10
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +182 -32
- claude_mpm/cli/executor.py +129 -16
- claude_mpm/cli/helpers.py +1 -1
- 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 +89 -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/profile_parser.py +0 -1
- 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 +2 -3
- claude_mpm/cli/startup.py +662 -524
- claude_mpm/cli/startup_display.py +76 -7
- claude_mpm/cli/startup_logging.py +2 -2
- 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 +122 -0
- claude_mpm/commander/chat/repl.py +1821 -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 +865 -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 +6 -0
- claude_mpm/core/claude_runner.py +154 -2
- 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 +12 -11
- 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/optimized_startup.py +3 -1
- claude_mpm/core/output_style_manager.py +66 -18
- claude_mpm/core/shared/config_loader.py +3 -1
- claude_mpm/core/socketio_pool.py +47 -15
- 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/1WZnGYqX.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/67pF3qNn.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/6RxdMKe4.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/8cZrfX0h.js +60 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/9a6T2nm-.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B443AUzu.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B8AwtY2H.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BF15LAsF.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BRcwIQNr.js +4 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uj46x2Wr.js → BSNlmTZj.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BV6nKitt.js +43 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BViJ8lZt.js +128 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BcQ-Q0FE.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bpyvgze_.js +30 -0
- 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/C3rbW_a-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C8WYN38h.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C9I8FlXH.js +61 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIQcWgO2.js +36 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIctN7YN.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CKrS_JZW.js +145 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CR6P9C4A.js +89 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRRR9MD_.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CSXtMOf0.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CT-sbxSk.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWm6DJsp.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CpqQ1Kzn.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D2nGpDRe.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9iCMida.js +267 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9ykgMoY.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DL2Ldur1.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DPfltzjH.js +165 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{N4qtv3Hx.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUliQN2b.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DXlhR01x.js +122 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D_lyTybS.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DngoTTgh.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DqkmHtDC.js +220 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DsDh8EYs.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DypDmXgd.js +139 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/IPYC-LnN.js +162 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JpevfAFt.js +68 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DjhvlsAc.js → NqQ1dWOy.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/R8CEIRAd.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Zxy7qc-l.js +64 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/qtd3IeO4.js +15 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ulBFON_C.js +65 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/wQVh1CoA.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.Dr7t0z2J.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.CAGBuiOw.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DG-KkbDf.js +1 -0
- 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 +11 -11
- claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
- 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__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -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/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -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/kuzu_memory_hook.py +5 -5
- 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 +224 -4
- 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/agent_discovery_service.py +3 -1
- claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +37 -17
- claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +36 -8
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +50 -26
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/git_source_manager.py +21 -2
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
- claude_mpm/services/agents/sources/git_source_sync_service.py +116 -5
- 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/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 -3
- claude_mpm/services/monitor/server.py +111 -16
- claude_mpm/services/pm_skills_deployer.py +302 -94
- claude_mpm/services/profile_manager.py +10 -4
- claude_mpm/services/skills/git_skill_source_manager.py +192 -29
- claude_mpm/services/skills/selective_skill_deployer.py +211 -46
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +192 -70
- 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/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- 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-delegation-patterns/SKILL.md +167 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
- claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -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-pr-workflow/SKILL.md +124 -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/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-ticketing-integration/SKILL.md +154 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/security-scanning.md +112 -0
- claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
- claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
- claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
- claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
- claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
- claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
- claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
- claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
- claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
- claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
- claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/registry.py +295 -90
- claude_mpm/skills/skill_manager.py +29 -23
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- claude_mpm/utils/agent_dependency_loader.py +103 -4
- claude_mpm/utils/robust_installer.py +45 -24
- claude_mpm-5.6.72.dist-info/METADATA +416 -0
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/RECORD +477 -159
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/WHEEL +1 -1
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/entry_points.txt +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- claude_mpm-5.4.41.dist-info/METADATA +0 -998
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
"""Work queue management for MPM Commander.
|
|
2
|
+
|
|
3
|
+
This module provides WorkQueue for managing work items within a project,
|
|
4
|
+
including priority-based execution and dependency resolution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import uuid
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
|
|
11
|
+
from ..models.work import WorkItem, WorkPriority, WorkState
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class WorkQueue:
|
|
17
|
+
"""Manages work items for a project.
|
|
18
|
+
|
|
19
|
+
Provides operations for adding, retrieving, and updating work items
|
|
20
|
+
with support for priority-based execution and linear dependencies.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
project_id: ID of the project this queue belongs to
|
|
24
|
+
_items: Internal storage of work items by ID
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
>>> queue = WorkQueue("proj-123")
|
|
28
|
+
>>> work = queue.add("Implement feature X", priority=WorkPriority.HIGH)
|
|
29
|
+
>>> next_work = queue.get_next()
|
|
30
|
+
>>> queue.start(next_work.id)
|
|
31
|
+
>>> queue.complete(next_work.id, "Feature implemented")
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, project_id: str):
|
|
35
|
+
"""Initialize work queue for a project.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
project_id: Unique project identifier
|
|
39
|
+
"""
|
|
40
|
+
self.project_id = project_id
|
|
41
|
+
self._items: dict[str, WorkItem] = {}
|
|
42
|
+
logger.debug(f"Initialized WorkQueue for project {project_id}")
|
|
43
|
+
|
|
44
|
+
def add(
|
|
45
|
+
self,
|
|
46
|
+
content: str,
|
|
47
|
+
priority: WorkPriority = WorkPriority.MEDIUM,
|
|
48
|
+
depends_on: Optional[List[str]] = None,
|
|
49
|
+
) -> WorkItem:
|
|
50
|
+
"""Add work item to queue.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
content: The task/message to execute
|
|
54
|
+
priority: Execution priority (default: MEDIUM)
|
|
55
|
+
depends_on: List of work item IDs that must complete first
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
The created WorkItem
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
>>> work = queue.add("Fix bug #123", WorkPriority.HIGH)
|
|
62
|
+
>>> work.state
|
|
63
|
+
<WorkState.QUEUED: 'queued'>
|
|
64
|
+
"""
|
|
65
|
+
work_id = f"work-{uuid.uuid4().hex[:8]}"
|
|
66
|
+
|
|
67
|
+
work = WorkItem(
|
|
68
|
+
id=work_id,
|
|
69
|
+
project_id=self.project_id,
|
|
70
|
+
content=content,
|
|
71
|
+
state=WorkState.QUEUED,
|
|
72
|
+
priority=priority,
|
|
73
|
+
depends_on=depends_on or [],
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
self._items[work_id] = work
|
|
77
|
+
logger.info(
|
|
78
|
+
f"Added work item {work_id} to project {self.project_id} "
|
|
79
|
+
f"with priority {priority.name}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return work
|
|
83
|
+
|
|
84
|
+
def get_next(self) -> Optional[WorkItem]:
|
|
85
|
+
"""Get next ready work item (dependencies satisfied, highest priority).
|
|
86
|
+
|
|
87
|
+
Returns work items in this order:
|
|
88
|
+
1. QUEUED items with satisfied dependencies
|
|
89
|
+
2. Ordered by priority (CRITICAL > HIGH > MEDIUM > LOW)
|
|
90
|
+
3. Within same priority, oldest first (FIFO)
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Next executable work item, or None if queue empty or all blocked
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
>>> work = queue.get_next()
|
|
97
|
+
>>> if work:
|
|
98
|
+
... print(f"Next: {work.content}")
|
|
99
|
+
"""
|
|
100
|
+
# Get all completed work IDs for dependency checking
|
|
101
|
+
completed = self.completed_ids
|
|
102
|
+
|
|
103
|
+
# Filter for QUEUED items with satisfied dependencies
|
|
104
|
+
ready_items = [
|
|
105
|
+
item
|
|
106
|
+
for item in self._items.values()
|
|
107
|
+
if item.state == WorkState.QUEUED and item.can_start(completed)
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
if not ready_items:
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
# Sort by priority (descending), then by created_at (ascending)
|
|
114
|
+
ready_items.sort(key=lambda x: (-x.priority.value, x.created_at))
|
|
115
|
+
|
|
116
|
+
next_item = ready_items[0]
|
|
117
|
+
logger.debug(
|
|
118
|
+
f"Next work item for {self.project_id}: {next_item.id} "
|
|
119
|
+
f"(priority={next_item.priority.name})"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return next_item
|
|
123
|
+
|
|
124
|
+
def start(self, work_id: str) -> bool:
|
|
125
|
+
"""Mark work as in progress.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
work_id: Work item ID to start
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
True if state changed, False if not found or invalid state
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
>>> queue.start("work-123")
|
|
135
|
+
True
|
|
136
|
+
"""
|
|
137
|
+
item = self._items.get(work_id)
|
|
138
|
+
if not item:
|
|
139
|
+
logger.warning(f"Work item {work_id} not found")
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
if item.state != WorkState.QUEUED:
|
|
143
|
+
logger.warning(
|
|
144
|
+
f"Work item {work_id} not QUEUED (current: {item.state.value})"
|
|
145
|
+
)
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
from datetime import datetime, timezone
|
|
149
|
+
|
|
150
|
+
item.state = WorkState.IN_PROGRESS
|
|
151
|
+
item.started_at = datetime.now(timezone.utc)
|
|
152
|
+
|
|
153
|
+
logger.info(f"Started work item {work_id}")
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
def complete(self, work_id: str, result: Optional[str] = None) -> bool:
|
|
157
|
+
"""Mark work as completed.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
work_id: Work item ID to complete
|
|
161
|
+
result: Optional result message
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
True if state changed, False if not found or invalid state
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
>>> queue.complete("work-123", "Successfully implemented feature")
|
|
168
|
+
True
|
|
169
|
+
"""
|
|
170
|
+
item = self._items.get(work_id)
|
|
171
|
+
if not item:
|
|
172
|
+
logger.warning(f"Work item {work_id} not found")
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
if item.state not in (WorkState.IN_PROGRESS, WorkState.BLOCKED):
|
|
176
|
+
logger.warning(
|
|
177
|
+
f"Work item {work_id} not IN_PROGRESS or BLOCKED "
|
|
178
|
+
f"(current: {item.state.value})"
|
|
179
|
+
)
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
from datetime import datetime, timezone
|
|
183
|
+
|
|
184
|
+
item.state = WorkState.COMPLETED
|
|
185
|
+
item.completed_at = datetime.now(timezone.utc)
|
|
186
|
+
item.result = result
|
|
187
|
+
|
|
188
|
+
logger.info(f"Completed work item {work_id}")
|
|
189
|
+
return True
|
|
190
|
+
|
|
191
|
+
def fail(self, work_id: str, error: str) -> bool:
|
|
192
|
+
"""Mark work as failed.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
work_id: Work item ID to fail
|
|
196
|
+
error: Error message
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
True if state changed, False if not found or invalid state
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
>>> queue.fail("work-123", "Execution timeout")
|
|
203
|
+
True
|
|
204
|
+
"""
|
|
205
|
+
item = self._items.get(work_id)
|
|
206
|
+
if not item:
|
|
207
|
+
logger.warning(f"Work item {work_id} not found")
|
|
208
|
+
return False
|
|
209
|
+
|
|
210
|
+
if item.state not in (WorkState.IN_PROGRESS, WorkState.BLOCKED):
|
|
211
|
+
logger.warning(
|
|
212
|
+
f"Work item {work_id} not IN_PROGRESS or BLOCKED "
|
|
213
|
+
f"(current: {item.state.value})"
|
|
214
|
+
)
|
|
215
|
+
return False
|
|
216
|
+
|
|
217
|
+
from datetime import datetime, timezone
|
|
218
|
+
|
|
219
|
+
item.state = WorkState.FAILED
|
|
220
|
+
item.completed_at = datetime.now(timezone.utc)
|
|
221
|
+
item.error = error
|
|
222
|
+
|
|
223
|
+
logger.error(f"Failed work item {work_id}: {error}")
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
def block(self, work_id: str, reason: str) -> bool:
|
|
227
|
+
"""Mark work as blocked (e.g., waiting for event resolution).
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
work_id: Work item ID to block
|
|
231
|
+
reason: Reason for blocking
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
True if state changed, False if not found or invalid state
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
>>> queue.block("work-123", "Waiting for user approval")
|
|
238
|
+
True
|
|
239
|
+
"""
|
|
240
|
+
item = self._items.get(work_id)
|
|
241
|
+
if not item:
|
|
242
|
+
logger.warning(f"Work item {work_id} not found")
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
if item.state != WorkState.IN_PROGRESS:
|
|
246
|
+
logger.warning(
|
|
247
|
+
f"Work item {work_id} not IN_PROGRESS (current: {item.state.value})"
|
|
248
|
+
)
|
|
249
|
+
return False
|
|
250
|
+
|
|
251
|
+
item.state = WorkState.BLOCKED
|
|
252
|
+
item.metadata["block_reason"] = reason
|
|
253
|
+
|
|
254
|
+
logger.info(f"Blocked work item {work_id}: {reason}")
|
|
255
|
+
return True
|
|
256
|
+
|
|
257
|
+
def unblock(self, work_id: str) -> bool:
|
|
258
|
+
"""Unblock work item (resume execution).
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
work_id: Work item ID to unblock
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
True if state changed, False if not found or invalid state
|
|
265
|
+
|
|
266
|
+
Example:
|
|
267
|
+
>>> queue.unblock("work-123")
|
|
268
|
+
True
|
|
269
|
+
"""
|
|
270
|
+
item = self._items.get(work_id)
|
|
271
|
+
if not item:
|
|
272
|
+
logger.warning(f"Work item {work_id} not found")
|
|
273
|
+
return False
|
|
274
|
+
|
|
275
|
+
if item.state != WorkState.BLOCKED:
|
|
276
|
+
logger.warning(
|
|
277
|
+
f"Work item {work_id} not BLOCKED (current: {item.state.value})"
|
|
278
|
+
)
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
item.state = WorkState.IN_PROGRESS
|
|
282
|
+
if "block_reason" in item.metadata:
|
|
283
|
+
del item.metadata["block_reason"]
|
|
284
|
+
|
|
285
|
+
logger.info(f"Unblocked work item {work_id}")
|
|
286
|
+
return True
|
|
287
|
+
|
|
288
|
+
def cancel(self, work_id: str) -> bool:
|
|
289
|
+
"""Cancel pending work.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
work_id: Work item ID to cancel
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
True if state changed, False if not found or invalid state
|
|
296
|
+
|
|
297
|
+
Example:
|
|
298
|
+
>>> queue.cancel("work-123")
|
|
299
|
+
True
|
|
300
|
+
"""
|
|
301
|
+
item = self._items.get(work_id)
|
|
302
|
+
if not item:
|
|
303
|
+
logger.warning(f"Work item {work_id} not found")
|
|
304
|
+
return False
|
|
305
|
+
|
|
306
|
+
if item.state not in (WorkState.PENDING, WorkState.QUEUED, WorkState.BLOCKED):
|
|
307
|
+
logger.warning(
|
|
308
|
+
f"Cannot cancel work item {work_id} in state {item.state.value}"
|
|
309
|
+
)
|
|
310
|
+
return False
|
|
311
|
+
|
|
312
|
+
from datetime import datetime, timezone
|
|
313
|
+
|
|
314
|
+
item.state = WorkState.CANCELLED
|
|
315
|
+
item.completed_at = datetime.now(timezone.utc)
|
|
316
|
+
|
|
317
|
+
logger.info(f"Cancelled work item {work_id}")
|
|
318
|
+
return True
|
|
319
|
+
|
|
320
|
+
def get(self, work_id: str) -> Optional[WorkItem]:
|
|
321
|
+
"""Get work item by ID.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
work_id: Work item ID to retrieve
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
WorkItem if found, None otherwise
|
|
328
|
+
|
|
329
|
+
Example:
|
|
330
|
+
>>> work = queue.get("work-123")
|
|
331
|
+
>>> if work:
|
|
332
|
+
... print(work.content)
|
|
333
|
+
"""
|
|
334
|
+
return self._items.get(work_id)
|
|
335
|
+
|
|
336
|
+
def list(self, state: Optional[WorkState] = None) -> List[WorkItem]:
|
|
337
|
+
"""List work items, optionally filtered by state.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
state: Optional state filter
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
List of work items matching criteria (may be empty)
|
|
344
|
+
|
|
345
|
+
Example:
|
|
346
|
+
>>> queued = queue.list(WorkState.QUEUED)
|
|
347
|
+
>>> all_items = queue.list()
|
|
348
|
+
"""
|
|
349
|
+
if state is None:
|
|
350
|
+
return list(self._items.values())
|
|
351
|
+
|
|
352
|
+
return [item for item in self._items.values() if item.state == state]
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
def pending_count(self) -> int:
|
|
356
|
+
"""Get count of pending/queued work items.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Number of items in PENDING or QUEUED state
|
|
360
|
+
|
|
361
|
+
Example:
|
|
362
|
+
>>> count = queue.pending_count
|
|
363
|
+
"""
|
|
364
|
+
return sum(
|
|
365
|
+
1
|
|
366
|
+
for item in self._items.values()
|
|
367
|
+
if item.state in (WorkState.PENDING, WorkState.QUEUED)
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def completed_ids(self) -> set[str]:
|
|
372
|
+
"""Get IDs of completed work items (for dependency checking).
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
Set of work item IDs in COMPLETED state
|
|
376
|
+
|
|
377
|
+
Example:
|
|
378
|
+
>>> completed = queue.completed_ids
|
|
379
|
+
"""
|
|
380
|
+
return {
|
|
381
|
+
item.id
|
|
382
|
+
for item in self._items.values()
|
|
383
|
+
if item.state == WorkState.COMPLETED
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
def load_items(self, items: List[WorkItem]) -> None:
|
|
387
|
+
"""Load work items from persistence.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
items: List of WorkItem instances to load
|
|
391
|
+
|
|
392
|
+
Example:
|
|
393
|
+
>>> queue.load_items(persisted_items)
|
|
394
|
+
"""
|
|
395
|
+
for item in items:
|
|
396
|
+
if item.project_id != self.project_id:
|
|
397
|
+
logger.warning(
|
|
398
|
+
f"Skipping work item {item.id} - wrong project "
|
|
399
|
+
f"(expected {self.project_id}, got {item.project_id})"
|
|
400
|
+
)
|
|
401
|
+
continue
|
|
402
|
+
|
|
403
|
+
self._items[item.id] = item
|
|
404
|
+
|
|
405
|
+
logger.info(f"Loaded {len(items)} work items for project {self.project_id}")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Event resolution workflow for MPM Commander.
|
|
2
|
+
|
|
3
|
+
This package provides event handling and notification capabilities for
|
|
4
|
+
managing blocking events that require user input.
|
|
5
|
+
|
|
6
|
+
Modules:
|
|
7
|
+
event_handler: Handles blocking events and session pause/resume
|
|
8
|
+
notifier: Sends notifications for events and resolutions
|
|
9
|
+
|
|
10
|
+
Classes:
|
|
11
|
+
EventHandler: Main event resolution coordinator
|
|
12
|
+
Notifier: Notification delivery system
|
|
13
|
+
NotifierConfig: Configuration for notification channels
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> from claude_mpm.commander.workflow import EventHandler, Notifier
|
|
17
|
+
>>> from claude_mpm.commander.workflow import NotifierConfig
|
|
18
|
+
>>>
|
|
19
|
+
>>> notifier = Notifier(NotifierConfig(log_level="INFO"))
|
|
20
|
+
>>> handler = EventHandler(inbox, sessions)
|
|
21
|
+
>>> await handler.process_event(event)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from .event_handler import EventHandler
|
|
25
|
+
from .notifier import Notifier, NotifierConfig
|
|
26
|
+
|
|
27
|
+
__all__ = ["EventHandler", "Notifier", "NotifierConfig"]
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Event handler for pause/resume workflow on blocking events.
|
|
2
|
+
|
|
3
|
+
This module provides EventHandler which manages blocking events that require
|
|
4
|
+
user input and coordinates session pause/resume.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
from ..inbox import Inbox
|
|
11
|
+
from ..models.events import BLOCKING_EVENTS, Event, EventStatus
|
|
12
|
+
from ..project_session import ProjectSession
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from ..core.block_manager import BlockManager
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EventHandler:
|
|
21
|
+
"""Handles events requiring user resolution and session pause/resume.
|
|
22
|
+
|
|
23
|
+
This class detects blocking events (permission requests, errors, confirmations),
|
|
24
|
+
pauses the associated project session, and resumes execution after the event
|
|
25
|
+
is resolved with a user response.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
inbox: Inbox for event management
|
|
29
|
+
session_manager: Dict mapping project_id to ProjectSession
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
>>> handler = EventHandler(inbox, sessions)
|
|
33
|
+
>>> await handler.process_event(event) # Pauses if blocking
|
|
34
|
+
>>> success = await handler.resolve_event(event_id, "User response")
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
inbox: Inbox,
|
|
40
|
+
session_manager: Dict[str, ProjectSession],
|
|
41
|
+
block_manager: Optional["BlockManager"] = None,
|
|
42
|
+
) -> None:
|
|
43
|
+
"""Initialize event handler.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
inbox: Inbox instance for event access
|
|
47
|
+
session_manager: Dict mapping project_id -> ProjectSession
|
|
48
|
+
block_manager: Optional BlockManager for automatic work unblocking
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
ValueError: If inbox or session_manager is None
|
|
52
|
+
"""
|
|
53
|
+
if inbox is None:
|
|
54
|
+
raise ValueError("Inbox cannot be None")
|
|
55
|
+
if session_manager is None:
|
|
56
|
+
raise ValueError("Session manager cannot be None")
|
|
57
|
+
|
|
58
|
+
self.inbox = inbox
|
|
59
|
+
self.session_manager = session_manager
|
|
60
|
+
self._event_manager = inbox.events
|
|
61
|
+
self.block_manager = block_manager
|
|
62
|
+
|
|
63
|
+
logger.debug(
|
|
64
|
+
"EventHandler initialized (block_manager: %s)",
|
|
65
|
+
"enabled" if block_manager else "disabled",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
async def process_event(self, event: Event) -> None:
|
|
69
|
+
"""Process an event - pause session if blocking.
|
|
70
|
+
|
|
71
|
+
If the event is blocking (requires user input), pauses the associated
|
|
72
|
+
project session until the event is resolved.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
event: Event to process
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
>>> await handler.process_event(error_event)
|
|
79
|
+
# Session paused for project
|
|
80
|
+
"""
|
|
81
|
+
if not self.is_blocking(event):
|
|
82
|
+
logger.debug(
|
|
83
|
+
"Event %s is non-blocking (%s), no pause needed",
|
|
84
|
+
event.id,
|
|
85
|
+
event.type.value,
|
|
86
|
+
)
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
logger.info(
|
|
90
|
+
"Processing blocking event %s for project %s: %s",
|
|
91
|
+
event.id,
|
|
92
|
+
event.project_id,
|
|
93
|
+
event.title,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Get the project session
|
|
97
|
+
session = self.session_manager.get(event.project_id)
|
|
98
|
+
if not session:
|
|
99
|
+
logger.warning(
|
|
100
|
+
"No session found for project %s, cannot pause", event.project_id
|
|
101
|
+
)
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
# Pause the session
|
|
105
|
+
try:
|
|
106
|
+
await session.pause(f"Event {event.id}: {event.title}")
|
|
107
|
+
logger.info(
|
|
108
|
+
"Paused session for project %s due to event %s",
|
|
109
|
+
event.project_id,
|
|
110
|
+
event.id,
|
|
111
|
+
)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logger.error(
|
|
114
|
+
"Failed to pause session for project %s: %s", event.project_id, e
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
async def resolve_event(self, event_id: str, response: str) -> bool:
|
|
118
|
+
"""Resolve an event with user response and resume session if applicable.
|
|
119
|
+
|
|
120
|
+
Marks the event as resolved, sends the response to the runtime, and
|
|
121
|
+
resumes the project session if it was paused for this event.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
event_id: ID of event to resolve
|
|
125
|
+
response: User's response to the event
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
True if resolution successful and session resumed, False otherwise
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
KeyError: If event_id not found
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
>>> success = await handler.resolve_event("evt_123", "Use authlib")
|
|
135
|
+
>>> if success:
|
|
136
|
+
... print("Event resolved and session resumed")
|
|
137
|
+
"""
|
|
138
|
+
# Get the event
|
|
139
|
+
event = self._event_manager.get(event_id)
|
|
140
|
+
if not event:
|
|
141
|
+
raise KeyError(f"Event not found: {event_id}")
|
|
142
|
+
|
|
143
|
+
logger.info("Resolving event %s: %s", event_id, response[:50])
|
|
144
|
+
|
|
145
|
+
# Check if event WAS blocking BEFORE resolving
|
|
146
|
+
was_blocking = self.is_blocking(event)
|
|
147
|
+
|
|
148
|
+
# Mark event as resolved
|
|
149
|
+
self._event_manager.respond(event_id, response)
|
|
150
|
+
|
|
151
|
+
# Automatically unblock work items if BlockManager is available
|
|
152
|
+
if self.block_manager and was_blocking:
|
|
153
|
+
unblocked_work = await self.block_manager.check_and_unblock(event_id)
|
|
154
|
+
if unblocked_work:
|
|
155
|
+
logger.info(
|
|
156
|
+
"Event %s resolution unblocked %d work items: %s",
|
|
157
|
+
event_id,
|
|
158
|
+
len(unblocked_work),
|
|
159
|
+
unblocked_work,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# If event was NOT blocking, no need to resume
|
|
163
|
+
if not was_blocking:
|
|
164
|
+
logger.debug("Event %s was non-blocking, no resume needed", event_id)
|
|
165
|
+
return True
|
|
166
|
+
|
|
167
|
+
# Get the project session
|
|
168
|
+
session = self.session_manager.get(event.project_id)
|
|
169
|
+
if not session:
|
|
170
|
+
logger.warning(
|
|
171
|
+
"No session found for project %s, cannot resume", event.project_id
|
|
172
|
+
)
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
# Check if session was paused for this event
|
|
176
|
+
if (
|
|
177
|
+
session.pause_reason
|
|
178
|
+
and event_id in session.pause_reason
|
|
179
|
+
and session.state.value == "paused"
|
|
180
|
+
):
|
|
181
|
+
try:
|
|
182
|
+
# Send response to the runtime
|
|
183
|
+
if session.active_pane and session.executor:
|
|
184
|
+
await session.executor.send_message(session.active_pane, response)
|
|
185
|
+
logger.debug("Sent response to pane %s", session.active_pane)
|
|
186
|
+
|
|
187
|
+
# Resume the session
|
|
188
|
+
await session.resume()
|
|
189
|
+
logger.info(
|
|
190
|
+
"Resumed session for project %s after resolving event %s",
|
|
191
|
+
event.project_id,
|
|
192
|
+
event_id,
|
|
193
|
+
)
|
|
194
|
+
return True
|
|
195
|
+
|
|
196
|
+
except Exception as e:
|
|
197
|
+
logger.error(
|
|
198
|
+
"Failed to resume session for project %s: %s", event.project_id, e
|
|
199
|
+
)
|
|
200
|
+
return False
|
|
201
|
+
else:
|
|
202
|
+
logger.debug("Session not paused for event %s, no resume needed", event_id)
|
|
203
|
+
return True
|
|
204
|
+
|
|
205
|
+
async def get_pending_events(self, project_id: Optional[str] = None) -> List[Event]:
|
|
206
|
+
"""Get unresolved events, optionally filtered by project.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
project_id: If provided, only return events for this project
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
List of pending events sorted by priority and time
|
|
213
|
+
|
|
214
|
+
Example:
|
|
215
|
+
>>> all_pending = await handler.get_pending_events()
|
|
216
|
+
>>> project_pending = await handler.get_pending_events("proj_123")
|
|
217
|
+
"""
|
|
218
|
+
return self._event_manager.get_pending(project_id)
|
|
219
|
+
|
|
220
|
+
def is_blocking(self, event: Event) -> bool:
|
|
221
|
+
"""Check if event type requires pausing execution.
|
|
222
|
+
|
|
223
|
+
Blocking event types:
|
|
224
|
+
- permission_request: Requires user approval
|
|
225
|
+
- error: Critical error blocking progress
|
|
226
|
+
- confirmation_request: User confirmation needed
|
|
227
|
+
- decision_needed: User must choose option
|
|
228
|
+
- approval: Destructive action needs approval
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
event: Event to check
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
True if event blocks progress, False otherwise
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
>>> if handler.is_blocking(event):
|
|
238
|
+
... await handler.process_event(event)
|
|
239
|
+
"""
|
|
240
|
+
# Check if event type is blocking and status is pending
|
|
241
|
+
return event.type in BLOCKING_EVENTS and event.status == EventStatus.PENDING
|