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,57 @@
|
|
|
1
|
+
"""Claude MPM framework implementation."""
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .base import BaseFramework
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MPMFramework(BaseFramework):
|
|
11
|
+
"""Claude MPM framework.
|
|
12
|
+
|
|
13
|
+
This framework launches Claude MPM with full agent orchestration.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> framework = MPMFramework()
|
|
17
|
+
>>> framework.name
|
|
18
|
+
'mpm'
|
|
19
|
+
>>> framework.is_available()
|
|
20
|
+
True
|
|
21
|
+
>>> framework.get_startup_command(Path("/Users/user/myapp"))
|
|
22
|
+
"cd '/Users/user/myapp' && claude-mpm"
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
name = "mpm"
|
|
26
|
+
display_name = "Claude MPM"
|
|
27
|
+
command = "claude-mpm"
|
|
28
|
+
|
|
29
|
+
def get_startup_command(self, project_path: Path) -> str:
|
|
30
|
+
"""Get the command to start Claude MPM in a project.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
project_path: Path to the project directory
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Shell command string to start Claude MPM
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
>>> framework = MPMFramework()
|
|
40
|
+
>>> framework.get_startup_command(Path("/Users/user/myapp"))
|
|
41
|
+
"cd '/Users/user/myapp' && claude-mpm"
|
|
42
|
+
"""
|
|
43
|
+
quoted_path = shlex.quote(str(project_path))
|
|
44
|
+
return f"cd {quoted_path} && claude-mpm"
|
|
45
|
+
|
|
46
|
+
def is_available(self) -> bool:
|
|
47
|
+
"""Check if 'claude-mpm' command is available.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
True if 'claude-mpm' command exists in PATH
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
>>> framework = MPMFramework()
|
|
54
|
+
>>> framework.is_available()
|
|
55
|
+
True
|
|
56
|
+
"""
|
|
57
|
+
return shutil.which("claude-mpm") is not None
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""Git worktree manager for session isolation."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import subprocess # nosec B404 - subprocess needed for git commands
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class WorktreeInfo:
|
|
14
|
+
"""Info about a git worktree."""
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
path: Path
|
|
18
|
+
branch: str
|
|
19
|
+
base_path: Path # Original repo path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WorktreeManager:
|
|
23
|
+
"""Manages git worktrees for session isolation."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, base_path: Path):
|
|
26
|
+
"""Initialize with base project path."""
|
|
27
|
+
self.base_path = Path(base_path)
|
|
28
|
+
self.worktrees_dir = self.base_path.parent / f".worktrees-{self.base_path.name}"
|
|
29
|
+
|
|
30
|
+
def create(self, name: str, branch: Optional[str] = None) -> WorktreeInfo:
|
|
31
|
+
"""Create a worktree for a session.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
name: Session/worktree name (e.g., "izzie")
|
|
35
|
+
branch: Branch name, defaults to session-{name}
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
WorktreeInfo with path and branch details
|
|
39
|
+
"""
|
|
40
|
+
branch = branch or f"session-{name}"
|
|
41
|
+
worktree_path = self.worktrees_dir / name
|
|
42
|
+
|
|
43
|
+
# Ensure worktrees directory exists
|
|
44
|
+
self.worktrees_dir.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
|
|
46
|
+
# Check if worktree already exists
|
|
47
|
+
if worktree_path.exists():
|
|
48
|
+
logger.info(f"Worktree '{name}' already exists at {worktree_path}")
|
|
49
|
+
return self._get_worktree_info(name, worktree_path)
|
|
50
|
+
|
|
51
|
+
# Create new branch if it doesn't exist
|
|
52
|
+
try:
|
|
53
|
+
subprocess.run( # nosec B603 B607 - git command with safe args
|
|
54
|
+
["git", "branch", branch],
|
|
55
|
+
cwd=self.base_path,
|
|
56
|
+
capture_output=True,
|
|
57
|
+
check=False, # Branch may already exist
|
|
58
|
+
)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logger.warning(f"Could not create branch {branch}: {e}")
|
|
61
|
+
|
|
62
|
+
# Create worktree
|
|
63
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
64
|
+
["git", "worktree", "add", str(worktree_path), branch],
|
|
65
|
+
check=False,
|
|
66
|
+
cwd=self.base_path,
|
|
67
|
+
capture_output=True,
|
|
68
|
+
text=True,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if result.returncode != 0:
|
|
72
|
+
raise RuntimeError(f"Failed to create worktree: {result.stderr}")
|
|
73
|
+
|
|
74
|
+
logger.info(f"Created worktree '{name}' at {worktree_path} on branch {branch}")
|
|
75
|
+
return WorktreeInfo(
|
|
76
|
+
name=name, path=worktree_path, branch=branch, base_path=self.base_path
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def _get_worktree_info(self, name: str, path: Path) -> WorktreeInfo:
|
|
80
|
+
"""Get info for existing worktree."""
|
|
81
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
82
|
+
["git", "branch", "--show-current"],
|
|
83
|
+
check=False,
|
|
84
|
+
cwd=path,
|
|
85
|
+
capture_output=True,
|
|
86
|
+
text=True,
|
|
87
|
+
)
|
|
88
|
+
branch = result.stdout.strip() or f"session-{name}"
|
|
89
|
+
return WorktreeInfo(
|
|
90
|
+
name=name, path=path, branch=branch, base_path=self.base_path
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def list(self) -> list[WorktreeInfo]:
|
|
94
|
+
"""List all worktrees for this project."""
|
|
95
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
96
|
+
["git", "worktree", "list", "--porcelain"],
|
|
97
|
+
check=False,
|
|
98
|
+
cwd=self.base_path,
|
|
99
|
+
capture_output=True,
|
|
100
|
+
text=True,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
worktrees = []
|
|
104
|
+
current_path = None
|
|
105
|
+
current_branch = None
|
|
106
|
+
|
|
107
|
+
for line in result.stdout.strip().split("\n"):
|
|
108
|
+
if line.startswith("worktree "):
|
|
109
|
+
current_path = Path(line.split(" ", 1)[1])
|
|
110
|
+
elif line.startswith("branch "):
|
|
111
|
+
current_branch = line.split("/")[-1] # refs/heads/branch -> branch
|
|
112
|
+
elif line == "":
|
|
113
|
+
if current_path and str(self.worktrees_dir) in str(current_path):
|
|
114
|
+
name = current_path.name
|
|
115
|
+
worktrees.append(
|
|
116
|
+
WorktreeInfo(
|
|
117
|
+
name=name,
|
|
118
|
+
path=current_path,
|
|
119
|
+
branch=current_branch or f"session-{name}",
|
|
120
|
+
base_path=self.base_path,
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
current_path = None
|
|
124
|
+
current_branch = None
|
|
125
|
+
|
|
126
|
+
return worktrees
|
|
127
|
+
|
|
128
|
+
def remove(self, name: str, force: bool = False) -> bool:
|
|
129
|
+
"""Remove a worktree."""
|
|
130
|
+
worktree_path = self.worktrees_dir / name
|
|
131
|
+
|
|
132
|
+
if not worktree_path.exists():
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
args = ["git", "worktree", "remove", str(worktree_path)]
|
|
136
|
+
if force:
|
|
137
|
+
args.insert(3, "--force")
|
|
138
|
+
|
|
139
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
140
|
+
args, check=False, cwd=self.base_path, capture_output=True, text=True
|
|
141
|
+
)
|
|
142
|
+
return result.returncode == 0
|
|
143
|
+
|
|
144
|
+
def get(self, name: str) -> Optional[WorktreeInfo]:
|
|
145
|
+
"""Get worktree by name."""
|
|
146
|
+
worktree_path = self.worktrees_dir / name
|
|
147
|
+
if worktree_path.exists():
|
|
148
|
+
return self._get_worktree_info(name, worktree_path)
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
def merge_to_main(self, name: str, delete_after: bool = True) -> tuple[bool, str]:
|
|
152
|
+
"""Merge worktree branch back to main and optionally delete worktree.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
name: Worktree name to merge
|
|
156
|
+
delete_after: Remove worktree after merge (default True)
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Tuple of (success, message)
|
|
160
|
+
"""
|
|
161
|
+
worktree = self.get(name)
|
|
162
|
+
if not worktree:
|
|
163
|
+
return False, f"Worktree '{name}' not found"
|
|
164
|
+
|
|
165
|
+
# Get main branch name
|
|
166
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
167
|
+
["git", "symbolic-ref", "refs/remotes/origin/HEAD"],
|
|
168
|
+
check=False,
|
|
169
|
+
cwd=self.base_path,
|
|
170
|
+
capture_output=True,
|
|
171
|
+
text=True,
|
|
172
|
+
)
|
|
173
|
+
main_branch = (
|
|
174
|
+
result.stdout.strip().split("/")[-1] if result.returncode == 0 else "main"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Checkout main in base repo
|
|
178
|
+
checkout_result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
179
|
+
["git", "checkout", main_branch],
|
|
180
|
+
check=False,
|
|
181
|
+
cwd=self.base_path,
|
|
182
|
+
capture_output=True,
|
|
183
|
+
text=True,
|
|
184
|
+
)
|
|
185
|
+
if checkout_result.returncode != 0:
|
|
186
|
+
return False, f"Failed to checkout {main_branch}: {checkout_result.stderr}"
|
|
187
|
+
|
|
188
|
+
# Merge worktree branch
|
|
189
|
+
merge_result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
190
|
+
["git", "merge", worktree.branch, "--no-edit"],
|
|
191
|
+
check=False,
|
|
192
|
+
cwd=self.base_path,
|
|
193
|
+
capture_output=True,
|
|
194
|
+
text=True,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
if merge_result.returncode != 0:
|
|
198
|
+
return False, f"Merge failed: {merge_result.stderr}"
|
|
199
|
+
|
|
200
|
+
# Delete worktree if requested
|
|
201
|
+
if delete_after:
|
|
202
|
+
self.remove(name, force=True)
|
|
203
|
+
# Delete branch
|
|
204
|
+
subprocess.run( # nosec B603 B607 - git command with safe args
|
|
205
|
+
["git", "branch", "-d", worktree.branch],
|
|
206
|
+
check=False,
|
|
207
|
+
cwd=self.base_path,
|
|
208
|
+
capture_output=True,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
logger.info(f"Merged '{worktree.branch}' to {main_branch}")
|
|
212
|
+
return True, f"Merged '{worktree.branch}' to {main_branch}"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Inbox system for MPM Commander.
|
|
2
|
+
|
|
3
|
+
Provides centralized event aggregation and display.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .dedup import DedupEntry, EventDeduplicator
|
|
7
|
+
from .inbox import Inbox, InboxCounts
|
|
8
|
+
from .models import InboxItem
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"DedupEntry",
|
|
12
|
+
"EventDeduplicator",
|
|
13
|
+
"Inbox",
|
|
14
|
+
"InboxCounts",
|
|
15
|
+
"InboxItem",
|
|
16
|
+
]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Event deduplication logic for inbox system.
|
|
2
|
+
|
|
3
|
+
Prevents duplicate events within a configurable time window using
|
|
4
|
+
content hashing and time-based expiration.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import hashlib
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from datetime import datetime, timedelta, timezone
|
|
10
|
+
from typing import Dict
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class DedupEntry:
|
|
15
|
+
"""Deduplication cache entry.
|
|
16
|
+
|
|
17
|
+
Tracks when an event was first seen and how many times it's appeared.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
key: Unique deduplication key
|
|
21
|
+
first_seen: When this key was first encountered
|
|
22
|
+
count: Number of times this key has been seen
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
key: str
|
|
26
|
+
first_seen: datetime
|
|
27
|
+
count: int = 1
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class EventDeduplicator:
|
|
31
|
+
"""Prevent duplicate events within a time window.
|
|
32
|
+
|
|
33
|
+
Uses content hashing to detect duplicates and time-based expiration
|
|
34
|
+
to automatically clean up old entries.
|
|
35
|
+
|
|
36
|
+
The deduplication key is constructed from:
|
|
37
|
+
- Project ID
|
|
38
|
+
- Event type
|
|
39
|
+
- Title hash (first 8 chars of MD5)
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> dedup = EventDeduplicator(window_seconds=60)
|
|
43
|
+
>>> dedup.is_duplicate("proj_123", "error", "Connection failed")
|
|
44
|
+
False # First occurrence
|
|
45
|
+
>>> dedup.is_duplicate("proj_123", "error", "Connection failed")
|
|
46
|
+
True # Duplicate within 60 seconds
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, window_seconds: int = 60):
|
|
50
|
+
"""Initialize deduplicator with time window.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
window_seconds: Duration in seconds to consider events as duplicates.
|
|
54
|
+
Default is 60 seconds.
|
|
55
|
+
"""
|
|
56
|
+
self.window = timedelta(seconds=window_seconds)
|
|
57
|
+
self._seen: Dict[str, DedupEntry] = {}
|
|
58
|
+
|
|
59
|
+
def make_key(self, project_id: str, event_type: str, title: str) -> str:
|
|
60
|
+
"""Create deduplication key from event attributes.
|
|
61
|
+
|
|
62
|
+
Generates a key in the format: {project_id}:{event_type}:{title_hash}
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
project_id: Unique project identifier
|
|
66
|
+
event_type: Type of event (from EventType enum)
|
|
67
|
+
title: Event title text
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Composite key string for deduplication
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
>>> dedup = EventDeduplicator()
|
|
74
|
+
>>> dedup.make_key("proj_123", "error", "Connection failed")
|
|
75
|
+
'proj_123:error:a1b2c3d4'
|
|
76
|
+
"""
|
|
77
|
+
title_hash = hashlib.md5(title.encode(), usedforsecurity=False).hexdigest()[:8]
|
|
78
|
+
return f"{project_id}:{event_type}:{title_hash}"
|
|
79
|
+
|
|
80
|
+
def is_duplicate(self, project_id: str, event_type: str, title: str) -> bool:
|
|
81
|
+
"""Check if this event is a duplicate within the window.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
project_id: Unique project identifier
|
|
85
|
+
event_type: Type of event
|
|
86
|
+
title: Event title
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
True if this is a duplicate, False if it's new
|
|
90
|
+
|
|
91
|
+
Side Effects:
|
|
92
|
+
- Increments count for duplicates
|
|
93
|
+
- Creates new entry for new events
|
|
94
|
+
- Cleans up expired entries
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
>>> dedup = EventDeduplicator(window_seconds=60)
|
|
98
|
+
>>> dedup.is_duplicate("proj_123", "error", "Timeout")
|
|
99
|
+
False # First occurrence
|
|
100
|
+
>>> dedup.is_duplicate("proj_123", "error", "Timeout")
|
|
101
|
+
True # Duplicate within window
|
|
102
|
+
"""
|
|
103
|
+
self._cleanup_expired()
|
|
104
|
+
key = self.make_key(project_id, event_type, title)
|
|
105
|
+
|
|
106
|
+
if key in self._seen:
|
|
107
|
+
self._seen[key].count += 1
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
self._seen[key] = DedupEntry(
|
|
111
|
+
key=key,
|
|
112
|
+
first_seen=datetime.now(timezone.utc),
|
|
113
|
+
)
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
def _cleanup_expired(self) -> None:
|
|
117
|
+
"""Remove entries older than the deduplication window.
|
|
118
|
+
|
|
119
|
+
Called automatically before each is_duplicate check to prevent
|
|
120
|
+
unbounded memory growth.
|
|
121
|
+
|
|
122
|
+
Side Effects:
|
|
123
|
+
Removes all entries where (now - first_seen) > window
|
|
124
|
+
"""
|
|
125
|
+
now = datetime.now(timezone.utc)
|
|
126
|
+
expired = [k for k, v in self._seen.items() if now - v.first_seen > self.window]
|
|
127
|
+
for k in expired:
|
|
128
|
+
del self._seen[k]
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Centralized inbox for MPM Commander.
|
|
2
|
+
|
|
3
|
+
Aggregates events from all projects with filtering, sorting, and pagination.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
from ..events.manager import EventManager
|
|
11
|
+
from ..models.events import Event, EventPriority, EventType
|
|
12
|
+
from ..registry import ProjectRegistry
|
|
13
|
+
from .dedup import EventDeduplicator
|
|
14
|
+
from .models import InboxItem
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class InboxCounts:
|
|
21
|
+
"""Count of events by priority.
|
|
22
|
+
|
|
23
|
+
Provides quick summary statistics for inbox state.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
critical: Count of CRITICAL priority events
|
|
27
|
+
high: Count of HIGH priority events
|
|
28
|
+
normal: Count of NORMAL priority events
|
|
29
|
+
low: Count of LOW priority events
|
|
30
|
+
info: Count of INFO priority events
|
|
31
|
+
total: Total count of all events
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
critical: int = 0
|
|
35
|
+
high: int = 0
|
|
36
|
+
normal: int = 0
|
|
37
|
+
low: int = 0
|
|
38
|
+
info: int = 0
|
|
39
|
+
total: int = 0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Inbox:
|
|
43
|
+
"""Centralized inbox aggregating events from all projects.
|
|
44
|
+
|
|
45
|
+
Provides a unified view of all pending events with:
|
|
46
|
+
- Multi-level filtering (priority, project, event type)
|
|
47
|
+
- Pagination support
|
|
48
|
+
- Priority-based sorting
|
|
49
|
+
- Project metadata enrichment
|
|
50
|
+
- Deduplication
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
>>> inbox = Inbox(event_manager, project_registry)
|
|
54
|
+
>>> items = inbox.get_items(limit=10, priority=EventPriority.HIGH)
|
|
55
|
+
>>> counts = inbox.get_counts()
|
|
56
|
+
>>> print(f"Total pending: {counts.total}")
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
event_manager: EventManager,
|
|
62
|
+
project_registry: ProjectRegistry,
|
|
63
|
+
dedup_window: int = 60,
|
|
64
|
+
):
|
|
65
|
+
"""Initialize inbox with dependencies.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
event_manager: Event lifecycle manager
|
|
69
|
+
project_registry: Project registration system
|
|
70
|
+
dedup_window: Deduplication window in seconds (default: 60)
|
|
71
|
+
"""
|
|
72
|
+
self.events = event_manager
|
|
73
|
+
self.projects = project_registry
|
|
74
|
+
self.deduplicator = EventDeduplicator(window_seconds=dedup_window)
|
|
75
|
+
|
|
76
|
+
def get_items(
|
|
77
|
+
self,
|
|
78
|
+
limit: int = 50,
|
|
79
|
+
offset: int = 0,
|
|
80
|
+
priority: Optional[EventPriority] = None,
|
|
81
|
+
project_id: Optional[str] = None,
|
|
82
|
+
event_type: Optional[EventType] = None,
|
|
83
|
+
) -> List[InboxItem]:
|
|
84
|
+
"""Get inbox items with optional filtering and pagination.
|
|
85
|
+
|
|
86
|
+
Applies filters in this order:
|
|
87
|
+
1. Get all pending events (optionally for specific project)
|
|
88
|
+
2. Filter by priority if specified
|
|
89
|
+
3. Filter by event type if specified
|
|
90
|
+
4. Sort by priority (high to low) then created_at (old to new)
|
|
91
|
+
5. Paginate with limit and offset
|
|
92
|
+
6. Enrich with project metadata
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
limit: Maximum number of items to return (default: 50)
|
|
96
|
+
offset: Number of items to skip for pagination (default: 0)
|
|
97
|
+
priority: Filter to specific priority level (optional)
|
|
98
|
+
project_id: Filter to specific project (optional)
|
|
99
|
+
event_type: Filter to specific event type (optional)
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
List of enriched InboxItems, sorted and paginated
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
# Get first 20 critical events
|
|
106
|
+
>>> items = inbox.get_items(limit=20, priority=EventPriority.CRITICAL)
|
|
107
|
+
|
|
108
|
+
# Get high-priority errors for specific project
|
|
109
|
+
>>> items = inbox.get_items(
|
|
110
|
+
... priority=EventPriority.HIGH,
|
|
111
|
+
... project_id="proj_123",
|
|
112
|
+
... event_type=EventType.ERROR
|
|
113
|
+
... )
|
|
114
|
+
|
|
115
|
+
# Pagination: get items 50-100
|
|
116
|
+
>>> items = inbox.get_items(limit=50, offset=50)
|
|
117
|
+
"""
|
|
118
|
+
# Get all pending events (optionally filtered by project)
|
|
119
|
+
pending = self.events.get_pending(project_id)
|
|
120
|
+
|
|
121
|
+
# Filter by priority
|
|
122
|
+
if priority:
|
|
123
|
+
pending = [e for e in pending if e.priority == priority]
|
|
124
|
+
|
|
125
|
+
# Filter by event type
|
|
126
|
+
if event_type:
|
|
127
|
+
pending = [e for e in pending if e.type == event_type]
|
|
128
|
+
|
|
129
|
+
# Sort by priority (CRITICAL first) then created_at (oldest first)
|
|
130
|
+
priority_order = [
|
|
131
|
+
EventPriority.CRITICAL,
|
|
132
|
+
EventPriority.HIGH,
|
|
133
|
+
EventPriority.NORMAL,
|
|
134
|
+
EventPriority.LOW,
|
|
135
|
+
EventPriority.INFO,
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
def sort_key(event: Event):
|
|
139
|
+
pri_idx = (
|
|
140
|
+
priority_order.index(event.priority)
|
|
141
|
+
if event.priority in priority_order
|
|
142
|
+
else 99
|
|
143
|
+
)
|
|
144
|
+
return (pri_idx, event.created_at)
|
|
145
|
+
|
|
146
|
+
sorted_events = sorted(pending, key=sort_key)
|
|
147
|
+
|
|
148
|
+
# Paginate
|
|
149
|
+
paginated = sorted_events[offset : offset + limit]
|
|
150
|
+
|
|
151
|
+
# Enrich with project metadata
|
|
152
|
+
items = []
|
|
153
|
+
for event in paginated:
|
|
154
|
+
project = self.projects.get(event.project_id)
|
|
155
|
+
if project:
|
|
156
|
+
session_runtime = None
|
|
157
|
+
if event.session_id and event.session_id in project.sessions:
|
|
158
|
+
session_runtime = project.sessions[event.session_id].runtime
|
|
159
|
+
|
|
160
|
+
items.append(
|
|
161
|
+
InboxItem(
|
|
162
|
+
event=event,
|
|
163
|
+
project_name=project.name,
|
|
164
|
+
project_path=project.path,
|
|
165
|
+
session_runtime=session_runtime,
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return items
|
|
170
|
+
|
|
171
|
+
def get_counts(self, project_id: Optional[str] = None) -> InboxCounts:
|
|
172
|
+
"""Get count of pending events by priority.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
project_id: If provided, only count events for this project
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
InboxCounts with breakdown by priority and total
|
|
179
|
+
|
|
180
|
+
Example:
|
|
181
|
+
>>> counts = inbox.get_counts()
|
|
182
|
+
>>> print(f"Critical: {counts.critical}, Total: {counts.total}")
|
|
183
|
+
|
|
184
|
+
>>> project_counts = inbox.get_counts(project_id="proj_123")
|
|
185
|
+
"""
|
|
186
|
+
pending = self.events.get_pending(project_id)
|
|
187
|
+
|
|
188
|
+
counts = InboxCounts()
|
|
189
|
+
for event in pending:
|
|
190
|
+
counts.total += 1
|
|
191
|
+
if event.priority == EventPriority.CRITICAL:
|
|
192
|
+
counts.critical += 1
|
|
193
|
+
elif event.priority == EventPriority.HIGH:
|
|
194
|
+
counts.high += 1
|
|
195
|
+
elif event.priority == EventPriority.NORMAL:
|
|
196
|
+
counts.normal += 1
|
|
197
|
+
elif event.priority == EventPriority.LOW:
|
|
198
|
+
counts.low += 1
|
|
199
|
+
elif event.priority == EventPriority.INFO:
|
|
200
|
+
counts.info += 1
|
|
201
|
+
|
|
202
|
+
return counts
|
|
203
|
+
|
|
204
|
+
def should_create_event(
|
|
205
|
+
self, project_id: str, event_type: EventType, title: str
|
|
206
|
+
) -> bool:
|
|
207
|
+
"""Check if event should be created (not a duplicate).
|
|
208
|
+
|
|
209
|
+
Uses deduplication to prevent creating duplicate events within
|
|
210
|
+
the configured time window.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
project_id: Project raising the event
|
|
214
|
+
event_type: Type of event
|
|
215
|
+
title: Event title
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
True if event should be created, False if it's a duplicate
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
>>> if inbox.should_create_event("proj_123", EventType.ERROR, "Timeout"):
|
|
222
|
+
... event = event_manager.create(...)
|
|
223
|
+
"""
|
|
224
|
+
return not self.deduplicator.is_duplicate(project_id, event_type.value, title)
|