claude-mpm 5.4.85__py3-none-any.whl → 5.6.76__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
- claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
- claude_mpm/agents/PM_INSTRUCTIONS.md +109 -706
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/auth/__init__.py +35 -0
- claude_mpm/auth/callback_server.py +328 -0
- claude_mpm/auth/models.py +104 -0
- claude_mpm/auth/oauth_manager.py +266 -0
- claude_mpm/auth/providers/__init__.py +12 -0
- claude_mpm/auth/providers/base.py +165 -0
- claude_mpm/auth/providers/google.py +261 -0
- claude_mpm/auth/token_storage.py +252 -0
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +216 -0
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/mcp.py +29 -17
- claude_mpm/cli/commands/mcp_command_router.py +39 -0
- claude_mpm/cli/commands/mcp_service_commands.py +304 -0
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/oauth.py +481 -0
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +5 -3
- claude_mpm/cli/executor.py +128 -16
- claude_mpm/cli/helpers.py +1 -1
- claude_mpm/cli/parsers/base_parser.py +84 -1
- claude_mpm/cli/parsers/commander_parser.py +116 -0
- claude_mpm/cli/parsers/mcp_parser.py +79 -0
- claude_mpm/cli/parsers/oauth_parser.py +165 -0
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/parsers/skill_source_parser.py +4 -0
- claude_mpm/cli/parsers/skills_parser.py +5 -0
- claude_mpm/cli/startup.py +345 -40
- claude_mpm/cli/startup_display.py +76 -7
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/startup_migrations.py +236 -0
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +78 -0
- claude_mpm/commander/adapters/__init__.py +60 -0
- claude_mpm/commander/adapters/auggie.py +260 -0
- claude_mpm/commander/adapters/base.py +288 -0
- claude_mpm/commander/adapters/claude_code.py +392 -0
- claude_mpm/commander/adapters/codex.py +237 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/adapters/example_usage.py +310 -0
- claude_mpm/commander/adapters/mpm.py +389 -0
- claude_mpm/commander/adapters/registry.py +204 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +121 -0
- claude_mpm/commander/api/errors.py +133 -0
- claude_mpm/commander/api/routes/__init__.py +8 -0
- claude_mpm/commander/api/routes/events.py +184 -0
- claude_mpm/commander/api/routes/inbox.py +171 -0
- claude_mpm/commander/api/routes/messages.py +148 -0
- claude_mpm/commander/api/routes/projects.py +271 -0
- claude_mpm/commander/api/routes/sessions.py +226 -0
- claude_mpm/commander/api/routes/work.py +296 -0
- claude_mpm/commander/api/schemas.py +186 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +149 -0
- claude_mpm/commander/chat/commands.py +124 -0
- claude_mpm/commander/chat/repl.py +1957 -0
- claude_mpm/commander/config.py +51 -0
- claude_mpm/commander/config_loader.py +115 -0
- claude_mpm/commander/core/__init__.py +10 -0
- claude_mpm/commander/core/block_manager.py +325 -0
- claude_mpm/commander/core/response_manager.py +323 -0
- claude_mpm/commander/daemon.py +603 -0
- claude_mpm/commander/env_loader.py +59 -0
- claude_mpm/commander/events/__init__.py +26 -0
- claude_mpm/commander/events/manager.py +392 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +233 -0
- claude_mpm/commander/frameworks/claude_code.py +58 -0
- claude_mpm/commander/frameworks/mpm.py +57 -0
- claude_mpm/commander/git/__init__.py +5 -0
- claude_mpm/commander/git/worktree_manager.py +212 -0
- claude_mpm/commander/inbox/__init__.py +16 -0
- claude_mpm/commander/inbox/dedup.py +128 -0
- claude_mpm/commander/inbox/inbox.py +224 -0
- claude_mpm/commander/inbox/models.py +70 -0
- claude_mpm/commander/instance_manager.py +868 -0
- claude_mpm/commander/llm/__init__.py +6 -0
- claude_mpm/commander/llm/openrouter_client.py +167 -0
- claude_mpm/commander/llm/summarizer.py +70 -0
- claude_mpm/commander/memory/__init__.py +45 -0
- claude_mpm/commander/memory/compression.py +347 -0
- claude_mpm/commander/memory/embeddings.py +230 -0
- claude_mpm/commander/memory/entities.py +310 -0
- claude_mpm/commander/memory/example_usage.py +290 -0
- claude_mpm/commander/memory/integration.py +325 -0
- claude_mpm/commander/memory/search.py +381 -0
- claude_mpm/commander/memory/store.py +657 -0
- claude_mpm/commander/models/__init__.py +18 -0
- claude_mpm/commander/models/events.py +127 -0
- claude_mpm/commander/models/project.py +162 -0
- claude_mpm/commander/models/work.py +214 -0
- claude_mpm/commander/parsing/__init__.py +20 -0
- claude_mpm/commander/parsing/extractor.py +132 -0
- claude_mpm/commander/parsing/output_parser.py +270 -0
- claude_mpm/commander/parsing/patterns.py +100 -0
- claude_mpm/commander/persistence/__init__.py +11 -0
- claude_mpm/commander/persistence/event_store.py +274 -0
- claude_mpm/commander/persistence/state_store.py +403 -0
- claude_mpm/commander/persistence/work_store.py +164 -0
- claude_mpm/commander/polling/__init__.py +13 -0
- claude_mpm/commander/polling/event_detector.py +104 -0
- claude_mpm/commander/polling/output_buffer.py +49 -0
- claude_mpm/commander/polling/output_poller.py +153 -0
- claude_mpm/commander/project_session.py +268 -0
- claude_mpm/commander/proxy/__init__.py +12 -0
- claude_mpm/commander/proxy/formatter.py +89 -0
- claude_mpm/commander/proxy/output_handler.py +191 -0
- claude_mpm/commander/proxy/relay.py +155 -0
- claude_mpm/commander/registry.py +410 -0
- claude_mpm/commander/runtime/__init__.py +10 -0
- claude_mpm/commander/runtime/executor.py +191 -0
- claude_mpm/commander/runtime/monitor.py +346 -0
- claude_mpm/commander/session/__init__.py +6 -0
- claude_mpm/commander/session/context.py +81 -0
- claude_mpm/commander/session/manager.py +59 -0
- claude_mpm/commander/tmux_orchestrator.py +362 -0
- claude_mpm/commander/web/__init__.py +1 -0
- claude_mpm/commander/work/__init__.py +30 -0
- claude_mpm/commander/work/executor.py +207 -0
- claude_mpm/commander/work/queue.py +405 -0
- claude_mpm/commander/workflow/__init__.py +27 -0
- claude_mpm/commander/workflow/event_handler.py +241 -0
- claude_mpm/commander/workflow/notifier.py +146 -0
- claude_mpm/commands/mpm-config.md +8 -0
- claude_mpm/commands/mpm-doctor.md +8 -0
- claude_mpm/commands/mpm-help.md +8 -0
- claude_mpm/commands/mpm-init.md +8 -0
- claude_mpm/commands/mpm-monitor.md +8 -0
- claude_mpm/commands/mpm-organize.md +8 -0
- claude_mpm/commands/mpm-postmortem.md +8 -0
- claude_mpm/commands/mpm-session-resume.md +9 -1
- claude_mpm/commands/mpm-status.md +8 -0
- claude_mpm/commands/mpm-ticket-view.md +8 -0
- claude_mpm/commands/mpm-version.md +8 -0
- claude_mpm/commands/mpm.md +8 -0
- claude_mpm/config/agent_presets.py +8 -7
- claude_mpm/config/skill_sources.py +16 -0
- claude_mpm/constants.py +5 -0
- claude_mpm/core/claude_runner.py +152 -0
- claude_mpm/core/config.py +35 -22
- claude_mpm/core/config_constants.py +74 -9
- claude_mpm/core/constants.py +56 -12
- claude_mpm/core/hook_manager.py +53 -4
- claude_mpm/core/interactive_session.py +5 -4
- claude_mpm/core/logger.py +26 -9
- claude_mpm/core/logging_utils.py +39 -13
- claude_mpm/core/network_config.py +148 -0
- claude_mpm/core/oneshot_session.py +7 -6
- claude_mpm/core/output_style_manager.py +52 -12
- claude_mpm/core/socketio_pool.py +47 -15
- claude_mpm/core/unified_config.py +10 -6
- claude_mpm/core/unified_paths.py +68 -80
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +9 -9
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
- claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +291 -59
- claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
- claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
- claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
- claude_mpm/hooks/claude_hooks/services/container.py +326 -0
- claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
- claude_mpm/hooks/session_resume_hook.py +89 -1
- claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
- claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
- claude_mpm/init.py +22 -15
- claude_mpm/mcp/__init__.py +9 -0
- claude_mpm/mcp/google_workspace_server.py +610 -0
- claude_mpm/scripts/claude-hook-handler.sh +46 -19
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/command_deployment_service.py +44 -26
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +325 -0
- claude_mpm/services/hook_installer_service.py +77 -8
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/mcp_config_manager.py +99 -19
- claude_mpm/services/mcp_service_registry.py +294 -0
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +111 -16
- claude_mpm/services/pm_skills_deployer.py +261 -87
- claude_mpm/services/skills/git_skill_source_manager.py +130 -10
- claude_mpm/services/skills/selective_skill_deployer.py +142 -16
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +31 -5
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/__init__.py +2 -1
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
- claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
- claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
- claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
- claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
- claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
- claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
- claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
- claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/registry.py +295 -90
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm-5.6.76.dist-info/METADATA +416 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/RECORD +312 -175
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/WHEEL +1 -1
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/entry_points.txt +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
- claude_mpm-5.4.85.dist-info/METADATA +0 -1023
- /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
- /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""Work queue management endpoints for MPM Commander API.
|
|
2
|
+
|
|
3
|
+
This module implements REST endpoints for managing work items
|
|
4
|
+
in project work queues.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
from fastapi import APIRouter, HTTPException, Query, Request
|
|
10
|
+
|
|
11
|
+
from ...models.work import WorkPriority, WorkState
|
|
12
|
+
from ...work import WorkQueue
|
|
13
|
+
from ..schemas import CreateWorkRequest, WorkItemResponse
|
|
14
|
+
|
|
15
|
+
router = APIRouter()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _get_registry(request: Request):
|
|
19
|
+
"""Get registry instance from app.state."""
|
|
20
|
+
if not hasattr(request.app.state, "registry") or request.app.state.registry is None:
|
|
21
|
+
raise RuntimeError("Registry not initialized")
|
|
22
|
+
return request.app.state.registry
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_work_queues(request: Request) -> Dict:
|
|
26
|
+
"""Get work queues dict from app.state (shared with daemon)."""
|
|
27
|
+
if (
|
|
28
|
+
not hasattr(request.app.state, "work_queues")
|
|
29
|
+
or request.app.state.work_queues is None
|
|
30
|
+
):
|
|
31
|
+
raise RuntimeError("Work queues not initialized")
|
|
32
|
+
return request.app.state.work_queues
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get_daemon(request: Request):
|
|
36
|
+
"""Get daemon instance from app.state."""
|
|
37
|
+
if not hasattr(request.app.state, "daemon_instance"):
|
|
38
|
+
return None
|
|
39
|
+
return request.app.state.daemon_instance
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _work_item_to_response(work_item) -> WorkItemResponse:
|
|
43
|
+
"""Convert WorkItem model to WorkItemResponse schema.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
work_item: WorkItem instance
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
WorkItemResponse with all work item data
|
|
50
|
+
"""
|
|
51
|
+
return WorkItemResponse(
|
|
52
|
+
id=work_item.id,
|
|
53
|
+
project_id=work_item.project_id,
|
|
54
|
+
content=work_item.content,
|
|
55
|
+
state=work_item.state.value,
|
|
56
|
+
priority=work_item.priority.value,
|
|
57
|
+
created_at=work_item.created_at,
|
|
58
|
+
started_at=work_item.started_at,
|
|
59
|
+
completed_at=work_item.completed_at,
|
|
60
|
+
result=work_item.result,
|
|
61
|
+
error=work_item.error,
|
|
62
|
+
depends_on=work_item.depends_on,
|
|
63
|
+
metadata=work_item.metadata,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@router.post("/projects/{project_id}/work", response_model=WorkItemResponse)
|
|
68
|
+
async def add_work(
|
|
69
|
+
request: Request, project_id: str, work: CreateWorkRequest
|
|
70
|
+
) -> WorkItemResponse:
|
|
71
|
+
"""Add work item to project queue.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
request: FastAPI request (for accessing app.state)
|
|
75
|
+
project_id: Project identifier
|
|
76
|
+
work: Work item creation request
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Created work item
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
HTTPException: 404 if project not found
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
POST /api/projects/proj-123/work
|
|
86
|
+
Request: {
|
|
87
|
+
"content": "Implement OAuth2 authentication",
|
|
88
|
+
"priority": 3,
|
|
89
|
+
"depends_on": ["work-abc"]
|
|
90
|
+
}
|
|
91
|
+
Response: {
|
|
92
|
+
"id": "work-xyz",
|
|
93
|
+
"project_id": "proj-123",
|
|
94
|
+
"content": "Implement OAuth2 authentication",
|
|
95
|
+
"state": "queued",
|
|
96
|
+
"priority": 3,
|
|
97
|
+
...
|
|
98
|
+
}
|
|
99
|
+
"""
|
|
100
|
+
registry = _get_registry(request)
|
|
101
|
+
work_queues = _get_work_queues(request)
|
|
102
|
+
daemon = _get_daemon(request)
|
|
103
|
+
|
|
104
|
+
# Get project
|
|
105
|
+
project = registry.get(project_id)
|
|
106
|
+
if project is None:
|
|
107
|
+
raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
|
|
108
|
+
|
|
109
|
+
# Get or create work queue (shared with daemon)
|
|
110
|
+
import logging
|
|
111
|
+
|
|
112
|
+
logger = logging.getLogger(__name__)
|
|
113
|
+
logger.info(
|
|
114
|
+
f"work_queues dict id: {id(work_queues)}, keys: {list(work_queues.keys())}"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if project_id not in work_queues:
|
|
118
|
+
logger.info(f"Creating new work queue for {project_id}")
|
|
119
|
+
work_queues[project_id] = WorkQueue(project_id)
|
|
120
|
+
logger.info(f"After creation, work_queues keys: {list(work_queues.keys())}")
|
|
121
|
+
|
|
122
|
+
queue = work_queues[project_id]
|
|
123
|
+
|
|
124
|
+
# Convert priority int to enum
|
|
125
|
+
priority = WorkPriority(work.priority)
|
|
126
|
+
|
|
127
|
+
# Add work item
|
|
128
|
+
work_item = queue.add(
|
|
129
|
+
content=work.content, priority=priority, depends_on=work.depends_on
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Ensure daemon has a session for this project (creates if needed)
|
|
133
|
+
if daemon and not daemon.sessions.get(project_id):
|
|
134
|
+
# Session creation will be handled by daemon's main loop
|
|
135
|
+
# when it detects work in the queue
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
return _work_item_to_response(work_item)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@router.get("/projects/{project_id}/work", response_model=List[WorkItemResponse])
|
|
142
|
+
async def list_work(
|
|
143
|
+
request: Request,
|
|
144
|
+
project_id: str,
|
|
145
|
+
state: Optional[str] = Query(None, description="Filter by state"),
|
|
146
|
+
) -> List[WorkItemResponse]:
|
|
147
|
+
"""List work items for project.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
request: FastAPI request (for accessing app.state)
|
|
151
|
+
project_id: Project identifier
|
|
152
|
+
state: Optional state filter (pending, queued, in_progress, etc.)
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
List of work items (may be empty)
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
HTTPException: 404 if project not found, 400 if invalid state
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
GET /api/projects/proj-123/work
|
|
162
|
+
Response: [
|
|
163
|
+
{"id": "work-1", "state": "queued", ...},
|
|
164
|
+
{"id": "work-2", "state": "in_progress", ...}
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
GET /api/projects/proj-123/work?state=queued
|
|
168
|
+
Response: [
|
|
169
|
+
{"id": "work-1", "state": "queued", ...}
|
|
170
|
+
]
|
|
171
|
+
"""
|
|
172
|
+
registry = _get_registry(request)
|
|
173
|
+
work_queues = _get_work_queues(request)
|
|
174
|
+
|
|
175
|
+
# Get project
|
|
176
|
+
project = registry.get(project_id)
|
|
177
|
+
if project is None:
|
|
178
|
+
raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
|
|
179
|
+
|
|
180
|
+
# Get work queue (shared with daemon)
|
|
181
|
+
if project_id not in work_queues:
|
|
182
|
+
# Return empty list if no work queue exists yet
|
|
183
|
+
return []
|
|
184
|
+
|
|
185
|
+
queue = work_queues[project_id]
|
|
186
|
+
|
|
187
|
+
# Parse state filter
|
|
188
|
+
state_filter = None
|
|
189
|
+
if state:
|
|
190
|
+
try:
|
|
191
|
+
state_filter = WorkState(state)
|
|
192
|
+
except ValueError as e:
|
|
193
|
+
raise HTTPException(
|
|
194
|
+
status_code=400,
|
|
195
|
+
detail=f"Invalid state: {state}. "
|
|
196
|
+
f"Valid states: {[s.value for s in WorkState]}",
|
|
197
|
+
) from e
|
|
198
|
+
|
|
199
|
+
# List work items
|
|
200
|
+
items = queue.list(state=state_filter)
|
|
201
|
+
|
|
202
|
+
return [_work_item_to_response(item) for item in items]
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@router.get("/projects/{project_id}/work/{work_id}", response_model=WorkItemResponse)
|
|
206
|
+
async def get_work(request: Request, project_id: str, work_id: str) -> WorkItemResponse:
|
|
207
|
+
"""Get work item details.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
request: FastAPI request (for accessing app.state)
|
|
211
|
+
project_id: Project identifier
|
|
212
|
+
work_id: Work item identifier
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Work item details
|
|
216
|
+
|
|
217
|
+
Raises:
|
|
218
|
+
HTTPException: 404 if project or work item not found
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
GET /api/projects/proj-123/work/work-xyz
|
|
222
|
+
Response: {
|
|
223
|
+
"id": "work-xyz",
|
|
224
|
+
"project_id": "proj-123",
|
|
225
|
+
"state": "in_progress",
|
|
226
|
+
...
|
|
227
|
+
}
|
|
228
|
+
"""
|
|
229
|
+
registry = _get_registry(request)
|
|
230
|
+
work_queues = _get_work_queues(request)
|
|
231
|
+
|
|
232
|
+
# Get project
|
|
233
|
+
project = registry.get(project_id)
|
|
234
|
+
if project is None:
|
|
235
|
+
raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
|
|
236
|
+
|
|
237
|
+
# Get work queue (shared with daemon)
|
|
238
|
+
if project_id not in work_queues:
|
|
239
|
+
raise HTTPException(status_code=404, detail="Work queue not found")
|
|
240
|
+
|
|
241
|
+
queue = work_queues[project_id]
|
|
242
|
+
|
|
243
|
+
# Get work item
|
|
244
|
+
work_item = queue.get(work_id)
|
|
245
|
+
if not work_item:
|
|
246
|
+
raise HTTPException(status_code=404, detail=f"Work item {work_id} not found")
|
|
247
|
+
|
|
248
|
+
return _work_item_to_response(work_item)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@router.post("/projects/{project_id}/work/{work_id}/cancel")
|
|
252
|
+
async def cancel_work(request: Request, project_id: str, work_id: str) -> dict:
|
|
253
|
+
"""Cancel pending work item.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
request: FastAPI request (for accessing app.state)
|
|
257
|
+
project_id: Project identifier
|
|
258
|
+
work_id: Work item identifier
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Success message
|
|
262
|
+
|
|
263
|
+
Raises:
|
|
264
|
+
HTTPException: 404 if project/work not found, 400 if invalid state
|
|
265
|
+
|
|
266
|
+
Example:
|
|
267
|
+
POST /api/projects/proj-123/work/work-xyz/cancel
|
|
268
|
+
Response: {"status": "cancelled", "id": "work-xyz"}
|
|
269
|
+
"""
|
|
270
|
+
registry = _get_registry(request)
|
|
271
|
+
work_queues = _get_work_queues(request)
|
|
272
|
+
|
|
273
|
+
# Get project
|
|
274
|
+
project = registry.get(project_id)
|
|
275
|
+
if project is None:
|
|
276
|
+
raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
|
|
277
|
+
|
|
278
|
+
# Get work queue (shared with daemon)
|
|
279
|
+
if project_id not in work_queues:
|
|
280
|
+
raise HTTPException(status_code=404, detail="Work queue not found")
|
|
281
|
+
|
|
282
|
+
queue = work_queues[project_id]
|
|
283
|
+
|
|
284
|
+
# Cancel work item
|
|
285
|
+
if not queue.cancel(work_id):
|
|
286
|
+
work_item = queue.get(work_id)
|
|
287
|
+
if not work_item:
|
|
288
|
+
raise HTTPException(
|
|
289
|
+
status_code=404, detail=f"Work item {work_id} not found"
|
|
290
|
+
)
|
|
291
|
+
raise HTTPException(
|
|
292
|
+
status_code=400,
|
|
293
|
+
detail=f"Cannot cancel work item in state {work_item.state.value}",
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
return {"status": "cancelled", "id": work_id}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Pydantic request/response schemas for MPM Commander API.
|
|
2
|
+
|
|
3
|
+
This module defines all request and response models for the REST API,
|
|
4
|
+
providing type safety and automatic validation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
# Request Models
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RegisterProjectRequest(BaseModel):
|
|
16
|
+
"""Request to register a new project.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
path: Filesystem path to project directory
|
|
20
|
+
project_id: Optional project identifier (UUID generated if omitted)
|
|
21
|
+
name: Optional display name (derived from path if omitted)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
path: str = Field(..., description="Filesystem path to project")
|
|
25
|
+
project_id: Optional[str] = Field(
|
|
26
|
+
None, description="Project identifier (UUID generated if omitted)"
|
|
27
|
+
)
|
|
28
|
+
name: Optional[str] = Field(
|
|
29
|
+
None, description="Display name (derived from path if omitted)"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CreateSessionRequest(BaseModel):
|
|
34
|
+
"""Request to create a new tool session.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
runtime: Runtime adapter to use (e.g., "claude-code")
|
|
38
|
+
agent_prompt: Optional custom system prompt for the agent
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
runtime: str = Field("claude-code", description="Runtime adapter to use")
|
|
42
|
+
agent_prompt: Optional[str] = Field(None, description="Custom system prompt")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SendMessageRequest(BaseModel):
|
|
46
|
+
"""Request to send a message to a project.
|
|
47
|
+
|
|
48
|
+
Attributes:
|
|
49
|
+
content: Message content
|
|
50
|
+
session_id: Target session ID (uses active session if omitted)
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
content: str = Field(..., description="Message content")
|
|
54
|
+
session_id: Optional[str] = Field(
|
|
55
|
+
None, description="Target session (uses active if omitted)"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# Response Models
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class SessionResponse(BaseModel):
|
|
63
|
+
"""Session information response.
|
|
64
|
+
|
|
65
|
+
Attributes:
|
|
66
|
+
id: Unique session identifier
|
|
67
|
+
project_id: Parent project ID
|
|
68
|
+
runtime: Runtime adapter name
|
|
69
|
+
tmux_target: Tmux pane target identifier
|
|
70
|
+
status: Current session status
|
|
71
|
+
created_at: Session creation timestamp
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
id: str
|
|
75
|
+
project_id: str
|
|
76
|
+
runtime: str
|
|
77
|
+
tmux_target: str
|
|
78
|
+
status: str
|
|
79
|
+
created_at: datetime
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ProjectResponse(BaseModel):
|
|
83
|
+
"""Project information response.
|
|
84
|
+
|
|
85
|
+
Attributes:
|
|
86
|
+
id: Unique project identifier
|
|
87
|
+
path: Absolute filesystem path
|
|
88
|
+
name: Display name
|
|
89
|
+
state: Current project state
|
|
90
|
+
state_reason: Optional state reason (e.g., error message)
|
|
91
|
+
sessions: List of active sessions
|
|
92
|
+
pending_events_count: Number of pending events
|
|
93
|
+
last_activity: Last activity timestamp
|
|
94
|
+
created_at: Project registration timestamp
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
id: str
|
|
98
|
+
path: str
|
|
99
|
+
name: str
|
|
100
|
+
state: str
|
|
101
|
+
state_reason: Optional[str]
|
|
102
|
+
sessions: List[SessionResponse]
|
|
103
|
+
pending_events_count: int
|
|
104
|
+
last_activity: datetime
|
|
105
|
+
created_at: datetime
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class MessageResponse(BaseModel):
|
|
109
|
+
"""Conversation message response.
|
|
110
|
+
|
|
111
|
+
Attributes:
|
|
112
|
+
id: Unique message identifier
|
|
113
|
+
role: Message sender role (user, assistant, system, tool)
|
|
114
|
+
content: Message content
|
|
115
|
+
session_id: Associated session ID (if from tool)
|
|
116
|
+
timestamp: Message creation timestamp
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
id: str
|
|
120
|
+
role: str
|
|
121
|
+
content: str
|
|
122
|
+
session_id: Optional[str]
|
|
123
|
+
timestamp: datetime
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class CreateWorkRequest(BaseModel):
|
|
127
|
+
"""Request to create a work item.
|
|
128
|
+
|
|
129
|
+
Attributes:
|
|
130
|
+
content: Task/message to execute
|
|
131
|
+
priority: Priority level (1-4, where 4 is CRITICAL)
|
|
132
|
+
depends_on: Optional list of work item IDs that must complete first
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
content: str = Field(..., description="Task/message to execute")
|
|
136
|
+
priority: int = Field(
|
|
137
|
+
2,
|
|
138
|
+
description="Priority level (1=LOW, 2=MEDIUM, 3=HIGH, 4=CRITICAL)",
|
|
139
|
+
ge=1,
|
|
140
|
+
le=4,
|
|
141
|
+
)
|
|
142
|
+
depends_on: Optional[List[str]] = Field(
|
|
143
|
+
None, description="Work item IDs that must complete first"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class WorkItemResponse(BaseModel):
|
|
148
|
+
"""Work item information response.
|
|
149
|
+
|
|
150
|
+
Attributes:
|
|
151
|
+
id: Unique work item identifier
|
|
152
|
+
project_id: Parent project ID
|
|
153
|
+
content: Task/message content
|
|
154
|
+
state: Current state (pending, queued, in_progress, blocked, completed, failed, cancelled)
|
|
155
|
+
priority: Priority level (1-4)
|
|
156
|
+
created_at: Creation timestamp
|
|
157
|
+
started_at: Execution start timestamp (if started)
|
|
158
|
+
completed_at: Completion timestamp (if completed/failed)
|
|
159
|
+
result: Result message (if completed)
|
|
160
|
+
error: Error message (if failed)
|
|
161
|
+
depends_on: List of dependency work item IDs
|
|
162
|
+
metadata: Additional structured data
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
id: str
|
|
166
|
+
project_id: str
|
|
167
|
+
content: str
|
|
168
|
+
state: str
|
|
169
|
+
priority: int
|
|
170
|
+
created_at: datetime
|
|
171
|
+
started_at: Optional[datetime]
|
|
172
|
+
completed_at: Optional[datetime]
|
|
173
|
+
result: Optional[str]
|
|
174
|
+
error: Optional[str]
|
|
175
|
+
depends_on: List[str]
|
|
176
|
+
metadata: dict
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class ErrorResponse(BaseModel):
|
|
180
|
+
"""Error response with structured error information.
|
|
181
|
+
|
|
182
|
+
Attributes:
|
|
183
|
+
error: Error details with code and message
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
error: dict # {code: str, message: str}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Commander CLI entry point."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from claude_mpm.commander.env_loader import load_env
|
|
10
|
+
from claude_mpm.commander.instance_manager import InstanceManager
|
|
11
|
+
from claude_mpm.commander.llm.openrouter_client import (
|
|
12
|
+
OpenRouterClient,
|
|
13
|
+
OpenRouterConfig,
|
|
14
|
+
)
|
|
15
|
+
from claude_mpm.commander.llm.summarizer import OutputSummarizer
|
|
16
|
+
from claude_mpm.commander.proxy.formatter import OutputFormatter
|
|
17
|
+
from claude_mpm.commander.proxy.output_handler import OutputHandler
|
|
18
|
+
from claude_mpm.commander.proxy.relay import OutputRelay
|
|
19
|
+
from claude_mpm.commander.session.manager import SessionManager
|
|
20
|
+
from claude_mpm.commander.tmux_orchestrator import TmuxOrchestrator
|
|
21
|
+
|
|
22
|
+
from .repl import CommanderREPL
|
|
23
|
+
|
|
24
|
+
# Load environment variables at module import
|
|
25
|
+
load_env()
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class CommanderCLIConfig:
|
|
32
|
+
"""Configuration for Commander CLI mode.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
summarize_responses: Whether to use LLM to summarize instance responses
|
|
36
|
+
port: Port for internal services (reserved for future use)
|
|
37
|
+
state_dir: Directory for state persistence (optional)
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
>>> config = CommanderCLIConfig(summarize_responses=False)
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
summarize_responses: bool = True
|
|
44
|
+
port: int = 8765
|
|
45
|
+
state_dir: Optional[Path] = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def run_commander(
|
|
49
|
+
port: int = 8765,
|
|
50
|
+
state_dir: Optional[Path] = None,
|
|
51
|
+
config: Optional[CommanderCLIConfig] = None,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Run Commander in interactive mode.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
port: Port for internal services (unused currently).
|
|
57
|
+
state_dir: Directory for state persistence (optional).
|
|
58
|
+
config: Commander CLI configuration (optional, uses defaults if None).
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
>>> asyncio.run(run_commander())
|
|
62
|
+
# Starts interactive Commander REPL
|
|
63
|
+
>>> config = CommanderCLIConfig(summarize_responses=False)
|
|
64
|
+
>>> asyncio.run(run_commander(config=config))
|
|
65
|
+
# Starts Commander without response summarization
|
|
66
|
+
"""
|
|
67
|
+
# Use default config if not provided
|
|
68
|
+
if config is None:
|
|
69
|
+
config = CommanderCLIConfig(port=port, state_dir=state_dir)
|
|
70
|
+
|
|
71
|
+
# Setup logging - suppress noisy libraries
|
|
72
|
+
logging.basicConfig(
|
|
73
|
+
level=logging.INFO,
|
|
74
|
+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
75
|
+
)
|
|
76
|
+
# Suppress httpx request logging (very verbose)
|
|
77
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
78
|
+
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
|
79
|
+
|
|
80
|
+
# Initialize components
|
|
81
|
+
logger.info("Initializing Commander...")
|
|
82
|
+
|
|
83
|
+
# Create tmux orchestrator
|
|
84
|
+
orchestrator = TmuxOrchestrator()
|
|
85
|
+
|
|
86
|
+
# Create instance manager
|
|
87
|
+
instance_manager = InstanceManager(orchestrator)
|
|
88
|
+
|
|
89
|
+
# Create session manager
|
|
90
|
+
session_manager = SessionManager()
|
|
91
|
+
|
|
92
|
+
# Try to initialize LLM client (optional)
|
|
93
|
+
llm_client: Optional[OpenRouterClient] = None
|
|
94
|
+
try:
|
|
95
|
+
llm_config = OpenRouterConfig()
|
|
96
|
+
llm_client = OpenRouterClient(llm_config)
|
|
97
|
+
logger.info("LLM client initialized")
|
|
98
|
+
except ValueError as e:
|
|
99
|
+
logger.warning(f"LLM client not available: {e}")
|
|
100
|
+
logger.warning("Output summarization will be disabled")
|
|
101
|
+
|
|
102
|
+
# Create output relay (optional)
|
|
103
|
+
output_relay: Optional[OutputRelay] = None
|
|
104
|
+
if llm_client:
|
|
105
|
+
try:
|
|
106
|
+
# Only create summarizer if summarize_responses is enabled
|
|
107
|
+
summarizer = None
|
|
108
|
+
if config.summarize_responses:
|
|
109
|
+
summarizer = OutputSummarizer(llm_client)
|
|
110
|
+
logger.info("Response summarization enabled")
|
|
111
|
+
else:
|
|
112
|
+
logger.info("Response summarization disabled")
|
|
113
|
+
|
|
114
|
+
handler = OutputHandler(orchestrator, summarizer)
|
|
115
|
+
formatter = OutputFormatter()
|
|
116
|
+
output_relay = OutputRelay(handler, formatter)
|
|
117
|
+
logger.info("Output relay initialized")
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.warning(f"Output relay setup failed: {e}")
|
|
120
|
+
|
|
121
|
+
# Create REPL
|
|
122
|
+
repl = CommanderREPL(
|
|
123
|
+
instance_manager=instance_manager,
|
|
124
|
+
session_manager=session_manager,
|
|
125
|
+
output_relay=output_relay,
|
|
126
|
+
llm_client=llm_client,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Run REPL
|
|
130
|
+
try:
|
|
131
|
+
await repl.run()
|
|
132
|
+
except KeyboardInterrupt:
|
|
133
|
+
logger.info("Commander interrupted by user")
|
|
134
|
+
except Exception as e:
|
|
135
|
+
logger.error(f"Commander error: {e}", exc_info=True)
|
|
136
|
+
finally:
|
|
137
|
+
# Cleanup
|
|
138
|
+
logger.info("Shutting down Commander...")
|
|
139
|
+
if output_relay:
|
|
140
|
+
await output_relay.stop_all()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def main() -> None:
|
|
144
|
+
"""Entry point for command-line execution."""
|
|
145
|
+
asyncio.run(run_commander())
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
main()
|