claude-mpm 5.4.65__py3-none-any.whl → 5.6.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
- claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +107 -1928
- claude_mpm/agents/PM_INSTRUCTIONS.md +119 -689
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/__init__.py +5 -1
- claude_mpm/cli/commands/agents.py +2 -4
- claude_mpm/cli/commands/agents_reconcile.py +197 -0
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +46 -0
- claude_mpm/cli/commands/configure.py +620 -21
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +171 -17
- claude_mpm/cli/executor.py +120 -16
- claude_mpm/cli/interactive/__init__.py +10 -0
- claude_mpm/cli/interactive/agent_wizard.py +30 -50
- claude_mpm/cli/interactive/questionary_styles.py +65 -0
- claude_mpm/cli/interactive/skill_selector.py +481 -0
- claude_mpm/cli/parsers/base_parser.py +76 -1
- claude_mpm/cli/parsers/commander_parser.py +83 -0
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/parsers/skill_source_parser.py +4 -0
- claude_mpm/cli/parsers/skills_parser.py +5 -0
- claude_mpm/cli/startup.py +203 -359
- claude_mpm/cli/startup_display.py +72 -5
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +72 -0
- claude_mpm/commander/adapters/__init__.py +31 -0
- claude_mpm/commander/adapters/base.py +191 -0
- claude_mpm/commander/adapters/claude_code.py +361 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +105 -0
- claude_mpm/commander/api/errors.py +133 -0
- claude_mpm/commander/api/routes/__init__.py +8 -0
- claude_mpm/commander/api/routes/events.py +184 -0
- claude_mpm/commander/api/routes/inbox.py +171 -0
- claude_mpm/commander/api/routes/messages.py +148 -0
- claude_mpm/commander/api/routes/projects.py +271 -0
- claude_mpm/commander/api/routes/sessions.py +228 -0
- claude_mpm/commander/api/routes/work.py +260 -0
- claude_mpm/commander/api/schemas.py +182 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +107 -0
- claude_mpm/commander/chat/commands.py +96 -0
- claude_mpm/commander/chat/repl.py +310 -0
- claude_mpm/commander/config.py +49 -0
- claude_mpm/commander/config_loader.py +115 -0
- claude_mpm/commander/daemon.py +398 -0
- claude_mpm/commander/events/__init__.py +26 -0
- claude_mpm/commander/events/manager.py +332 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +143 -0
- claude_mpm/commander/frameworks/claude_code.py +58 -0
- claude_mpm/commander/frameworks/mpm.py +62 -0
- claude_mpm/commander/inbox/__init__.py +16 -0
- claude_mpm/commander/inbox/dedup.py +128 -0
- claude_mpm/commander/inbox/inbox.py +224 -0
- claude_mpm/commander/inbox/models.py +70 -0
- claude_mpm/commander/instance_manager.py +337 -0
- claude_mpm/commander/llm/__init__.py +6 -0
- claude_mpm/commander/llm/openrouter_client.py +167 -0
- claude_mpm/commander/llm/summarizer.py +70 -0
- claude_mpm/commander/models/__init__.py +18 -0
- claude_mpm/commander/models/events.py +121 -0
- claude_mpm/commander/models/project.py +162 -0
- claude_mpm/commander/models/work.py +214 -0
- claude_mpm/commander/parsing/__init__.py +20 -0
- claude_mpm/commander/parsing/extractor.py +132 -0
- claude_mpm/commander/parsing/output_parser.py +270 -0
- claude_mpm/commander/parsing/patterns.py +100 -0
- claude_mpm/commander/persistence/__init__.py +11 -0
- claude_mpm/commander/persistence/event_store.py +274 -0
- claude_mpm/commander/persistence/state_store.py +309 -0
- claude_mpm/commander/persistence/work_store.py +164 -0
- claude_mpm/commander/polling/__init__.py +13 -0
- claude_mpm/commander/polling/event_detector.py +104 -0
- claude_mpm/commander/polling/output_buffer.py +49 -0
- claude_mpm/commander/polling/output_poller.py +153 -0
- claude_mpm/commander/project_session.py +268 -0
- claude_mpm/commander/proxy/__init__.py +12 -0
- claude_mpm/commander/proxy/formatter.py +89 -0
- claude_mpm/commander/proxy/output_handler.py +191 -0
- claude_mpm/commander/proxy/relay.py +155 -0
- claude_mpm/commander/registry.py +404 -0
- claude_mpm/commander/runtime/__init__.py +10 -0
- claude_mpm/commander/runtime/executor.py +191 -0
- claude_mpm/commander/runtime/monitor.py +316 -0
- claude_mpm/commander/session/__init__.py +6 -0
- claude_mpm/commander/session/context.py +81 -0
- claude_mpm/commander/session/manager.py +59 -0
- claude_mpm/commander/tmux_orchestrator.py +361 -0
- claude_mpm/commander/web/__init__.py +1 -0
- claude_mpm/commander/work/__init__.py +30 -0
- claude_mpm/commander/work/executor.py +189 -0
- claude_mpm/commander/work/queue.py +405 -0
- claude_mpm/commander/workflow/__init__.py +27 -0
- claude_mpm/commander/workflow/event_handler.py +219 -0
- claude_mpm/commander/workflow/notifier.py +146 -0
- claude_mpm/commands/mpm-config.md +8 -0
- claude_mpm/commands/mpm-doctor.md +8 -0
- claude_mpm/commands/mpm-help.md +8 -0
- claude_mpm/commands/mpm-init.md +8 -0
- claude_mpm/commands/mpm-monitor.md +8 -0
- claude_mpm/commands/mpm-organize.md +8 -0
- claude_mpm/commands/mpm-postmortem.md +8 -0
- claude_mpm/commands/mpm-session-resume.md +9 -1
- claude_mpm/commands/mpm-status.md +8 -0
- claude_mpm/commands/mpm-ticket-view.md +8 -0
- claude_mpm/commands/mpm-version.md +8 -0
- claude_mpm/commands/mpm.md +8 -0
- claude_mpm/config/agent_presets.py +8 -7
- claude_mpm/config/skill_sources.py +16 -0
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +2 -2
- claude_mpm/core/config.py +32 -19
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/interactive_session.py +7 -7
- claude_mpm/core/logger.py +26 -9
- claude_mpm/core/logging_utils.py +35 -11
- claude_mpm/core/output_style_manager.py +31 -13
- claude_mpm/core/unified_config.py +54 -8
- claude_mpm/core/unified_paths.py +95 -90
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +9 -9
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +283 -87
- claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +116 -8
- claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
- claude_mpm/hooks/claude_hooks/response_tracking.py +42 -59
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +39 -24
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
- claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +73 -75
- claude_mpm/hooks/session_resume_hook.py +89 -1
- claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
- claude_mpm/init.py +1 -1
- claude_mpm/scripts/claude-hook-handler.sh +43 -16
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
- claude_mpm/services/agents/startup_sync.py +5 -2
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +325 -0
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +106 -16
- claude_mpm/services/pm_skills_deployer.py +259 -87
- claude_mpm/services/skills/git_skill_source_manager.py +135 -11
- claude_mpm/services/skills/selective_skill_deployer.py +142 -26
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +31 -5
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
- claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
- claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
- claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
- claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
- claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
- claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
- claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm/utils/agent_dependency_loader.py +4 -2
- claude_mpm/utils/robust_installer.py +10 -6
- claude_mpm-5.6.10.dist-info/METADATA +391 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/RECORD +303 -181
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
- claude_mpm-5.4.65.dist-info/METADATA +0 -999
- /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/top_level.txt +0 -0
|
@@ -57,33 +57,33 @@ def list_errors(format, hook_type):
|
|
|
57
57
|
|
|
58
58
|
if not errors:
|
|
59
59
|
if hook_type:
|
|
60
|
-
click.echo(f"No errors recorded for hook type: {hook_type}")
|
|
60
|
+
click.echo(f"No errors recorded for hook type: {hook_type}", err=True)
|
|
61
61
|
else:
|
|
62
|
-
click.echo("No errors recorded. Hook system is healthy! ✅")
|
|
62
|
+
click.echo("No errors recorded. Hook system is healthy! ✅", err=True)
|
|
63
63
|
return
|
|
64
64
|
|
|
65
65
|
if format == "json":
|
|
66
66
|
# JSON output
|
|
67
|
-
click.echo(json.dumps(errors, indent=2))
|
|
67
|
+
click.echo(json.dumps(errors, indent=2), err=True)
|
|
68
68
|
else:
|
|
69
69
|
# Table output
|
|
70
|
-
click.echo("\n" + "=" * 80)
|
|
71
|
-
click.echo("Hook Error Memory Report")
|
|
72
|
-
click.echo("=" * 80)
|
|
70
|
+
click.echo("\n" + "=" * 80, err=True)
|
|
71
|
+
click.echo("Hook Error Memory Report", err=True)
|
|
72
|
+
click.echo("=" * 80, err=True)
|
|
73
73
|
|
|
74
74
|
for key, data in errors.items():
|
|
75
|
-
click.echo(f"\n🔴 Error: {data['type']}")
|
|
76
|
-
click.echo(f" Hook Type: {data['hook_type']}")
|
|
77
|
-
click.echo(f" Details: {data['details']}")
|
|
78
|
-
click.echo(f" Match: {data['match']}")
|
|
79
|
-
click.echo(f" Count: {data['count']} occurrences")
|
|
80
|
-
click.echo(f" First Seen: {data['first_seen']}")
|
|
81
|
-
click.echo(f" Last Seen: {data['last_seen']}")
|
|
75
|
+
click.echo(f"\n🔴 Error: {data['type']}", err=True)
|
|
76
|
+
click.echo(f" Hook Type: {data['hook_type']}", err=True)
|
|
77
|
+
click.echo(f" Details: {data['details']}", err=True)
|
|
78
|
+
click.echo(f" Match: {data['match']}", err=True)
|
|
79
|
+
click.echo(f" Count: {data['count']} occurrences", err=True)
|
|
80
|
+
click.echo(f" First Seen: {data['first_seen']}", err=True)
|
|
81
|
+
click.echo(f" Last Seen: {data['last_seen']}", err=True)
|
|
82
82
|
|
|
83
|
-
click.echo("\n" + "=" * 80)
|
|
84
|
-
click.echo(f"Total unique errors: {len(errors)}")
|
|
85
|
-
click.echo(f"Memory file: {error_memory.memory_file}")
|
|
86
|
-
click.echo("\nTo clear errors: claude-mpm hook-errors clear")
|
|
83
|
+
click.echo("\n" + "=" * 80, err=True)
|
|
84
|
+
click.echo(f"Total unique errors: {len(errors)}", err=True)
|
|
85
|
+
click.echo(f"Memory file: {error_memory.memory_file}", err=True)
|
|
86
|
+
click.echo("\nTo clear errors: claude-mpm hook-errors clear", err=True)
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
@hook_errors_group.command(name="summary")
|
|
@@ -99,28 +99,28 @@ def show_summary():
|
|
|
99
99
|
summary = error_memory.get_error_summary()
|
|
100
100
|
|
|
101
101
|
if summary["total_errors"] == 0:
|
|
102
|
-
click.echo("No errors recorded. Hook system is healthy! ✅")
|
|
102
|
+
click.echo("No errors recorded. Hook system is healthy! ✅", err=True)
|
|
103
103
|
return
|
|
104
104
|
|
|
105
|
-
click.echo("\n" + "=" * 80)
|
|
106
|
-
click.echo("Hook Error Summary")
|
|
107
|
-
click.echo("=" * 80)
|
|
108
|
-
click.echo("\n📊 Statistics:")
|
|
109
|
-
click.echo(f" Total Errors: {summary['total_errors']}")
|
|
110
|
-
click.echo(f" Unique Errors: {summary['unique_errors']}")
|
|
105
|
+
click.echo("\n" + "=" * 80, err=True)
|
|
106
|
+
click.echo("Hook Error Summary", err=True)
|
|
107
|
+
click.echo("=" * 80, err=True)
|
|
108
|
+
click.echo("\n📊 Statistics:", err=True)
|
|
109
|
+
click.echo(f" Total Errors: {summary['total_errors']}", err=True)
|
|
110
|
+
click.echo(f" Unique Errors: {summary['unique_errors']}", err=True)
|
|
111
111
|
|
|
112
112
|
if summary["errors_by_type"]:
|
|
113
|
-
click.echo("\n🔍 Errors by Type:")
|
|
113
|
+
click.echo("\n🔍 Errors by Type:", err=True)
|
|
114
114
|
for error_type, count in summary["errors_by_type"].items():
|
|
115
|
-
click.echo(f" {error_type}: {count}")
|
|
115
|
+
click.echo(f" {error_type}: {count}", err=True)
|
|
116
116
|
|
|
117
117
|
if summary["errors_by_hook"]:
|
|
118
|
-
click.echo("\n🎣 Errors by Hook Type:")
|
|
118
|
+
click.echo("\n🎣 Errors by Hook Type:", err=True)
|
|
119
119
|
for hook_type, count in summary["errors_by_hook"].items():
|
|
120
|
-
click.echo(f" {hook_type}: {count}")
|
|
120
|
+
click.echo(f" {hook_type}: {count}", err=True)
|
|
121
121
|
|
|
122
|
-
click.echo(f"\n📁 Memory File: {summary['memory_file']}")
|
|
123
|
-
click.echo("\nFor detailed list: claude-mpm hook-errors list")
|
|
122
|
+
click.echo(f"\n📁 Memory File: {summary['memory_file']}", err=True)
|
|
123
|
+
click.echo("\nFor detailed list: claude-mpm hook-errors list", err=True)
|
|
124
124
|
|
|
125
125
|
|
|
126
126
|
@hook_errors_group.command(name="clear")
|
|
@@ -158,21 +158,21 @@ def clear_errors(hook_type, yes):
|
|
|
158
158
|
scope = "all hook types"
|
|
159
159
|
|
|
160
160
|
if count == 0:
|
|
161
|
-
click.echo(f"No errors to clear {scope}.")
|
|
161
|
+
click.echo(f"No errors to clear {scope}.", err=True)
|
|
162
162
|
return
|
|
163
163
|
|
|
164
164
|
# Confirm if not using -y flag
|
|
165
165
|
if not yes:
|
|
166
166
|
message = f"Clear {count} error(s) {scope}?"
|
|
167
167
|
if not click.confirm(message):
|
|
168
|
-
click.echo("Cancelled.")
|
|
168
|
+
click.echo("Cancelled.", err=True)
|
|
169
169
|
return
|
|
170
170
|
|
|
171
171
|
# Clear errors
|
|
172
172
|
error_memory.clear_errors(hook_type)
|
|
173
173
|
|
|
174
|
-
click.echo(f"✅ Cleared {count} error(s) {scope}.")
|
|
175
|
-
click.echo("\nHooks will be retried on next execution.")
|
|
174
|
+
click.echo(f"✅ Cleared {count} error(s) {scope}.", err=True)
|
|
175
|
+
click.echo("\nHooks will be retried on next execution.", err=True)
|
|
176
176
|
|
|
177
177
|
|
|
178
178
|
@hook_errors_group.command(name="diagnose")
|
|
@@ -201,19 +201,19 @@ def diagnose_errors(hook_type):
|
|
|
201
201
|
|
|
202
202
|
if not errors:
|
|
203
203
|
if hook_type:
|
|
204
|
-
click.echo(f"No errors to diagnose for hook type: {hook_type}")
|
|
204
|
+
click.echo(f"No errors to diagnose for hook type: {hook_type}", err=True)
|
|
205
205
|
else:
|
|
206
|
-
click.echo("No errors to diagnose. Hook system is healthy! ✅")
|
|
206
|
+
click.echo("No errors to diagnose. Hook system is healthy! ✅", err=True)
|
|
207
207
|
return
|
|
208
208
|
|
|
209
|
-
click.echo("\n" + "=" * 80)
|
|
210
|
-
click.echo("Hook Error Diagnostics")
|
|
211
|
-
click.echo("=" * 80)
|
|
209
|
+
click.echo("\n" + "=" * 80, err=True)
|
|
210
|
+
click.echo("Hook Error Diagnostics", err=True)
|
|
211
|
+
click.echo("=" * 80, err=True)
|
|
212
212
|
|
|
213
213
|
for key, data in errors.items():
|
|
214
|
-
click.echo(f"\n🔴 Error: {data['type']}")
|
|
215
|
-
click.echo(f" Hook: {data['hook_type']}")
|
|
216
|
-
click.echo(f" Count: {data['count']} failures")
|
|
214
|
+
click.echo(f"\n🔴 Error: {data['type']}", err=True)
|
|
215
|
+
click.echo(f" Hook: {data['hook_type']}", err=True)
|
|
216
|
+
click.echo(f" Count: {data['count']} failures", err=True)
|
|
217
217
|
|
|
218
218
|
# Generate and show fix suggestion
|
|
219
219
|
error_info = {
|
|
@@ -223,13 +223,13 @@ def diagnose_errors(hook_type):
|
|
|
223
223
|
}
|
|
224
224
|
suggestion = error_memory.suggest_fix(error_info)
|
|
225
225
|
|
|
226
|
-
click.echo("\n" + "-" * 80)
|
|
227
|
-
click.echo(suggestion)
|
|
228
|
-
click.echo("-" * 80)
|
|
226
|
+
click.echo("\n" + "-" * 80, err=True)
|
|
227
|
+
click.echo(suggestion, err=True)
|
|
228
|
+
click.echo("-" * 80, err=True)
|
|
229
229
|
|
|
230
|
-
click.echo("\n" + "=" * 80)
|
|
231
|
-
click.echo("After fixing issues, clear errors to retry:")
|
|
232
|
-
click.echo(" claude-mpm hook-errors clear")
|
|
230
|
+
click.echo("\n" + "=" * 80, err=True)
|
|
231
|
+
click.echo("After fixing issues, clear errors to retry:", err=True)
|
|
232
|
+
click.echo(" claude-mpm hook-errors clear", err=True)
|
|
233
233
|
|
|
234
234
|
|
|
235
235
|
@hook_errors_group.command(name="status")
|
|
@@ -244,27 +244,27 @@ def show_status():
|
|
|
244
244
|
error_memory = get_hook_error_memory()
|
|
245
245
|
summary = error_memory.get_error_summary()
|
|
246
246
|
|
|
247
|
-
click.echo("\n📊 Hook Error Memory Status")
|
|
248
|
-
click.echo("=" * 80)
|
|
247
|
+
click.echo("\n📊 Hook Error Memory Status", err=True)
|
|
248
|
+
click.echo("=" * 80, err=True)
|
|
249
249
|
|
|
250
250
|
if summary["total_errors"] == 0:
|
|
251
|
-
click.echo("✅ Status: Healthy (no errors recorded)")
|
|
251
|
+
click.echo("✅ Status: Healthy (no errors recorded)", err=True)
|
|
252
252
|
else:
|
|
253
|
-
click.echo(f"⚠️ Status: {summary['total_errors']} error(s) recorded")
|
|
254
|
-
click.echo(f" Unique errors: {summary['unique_errors']}")
|
|
253
|
+
click.echo(f"⚠️ Status: {summary['total_errors']} error(s) recorded", err=True)
|
|
254
|
+
click.echo(f" Unique errors: {summary['unique_errors']}", err=True)
|
|
255
255
|
|
|
256
256
|
# Show which hooks are affected
|
|
257
257
|
if summary["errors_by_hook"]:
|
|
258
258
|
affected_hooks = list(summary["errors_by_hook"].keys())
|
|
259
|
-
click.echo(f" Affected hooks: {', '.join(affected_hooks)}")
|
|
259
|
+
click.echo(f" Affected hooks: {', '.join(affected_hooks)}", err=True)
|
|
260
260
|
|
|
261
|
-
click.echo(f"\n📁 Memory file: {summary['memory_file']}")
|
|
262
|
-
click.echo(f" Exists: {Path(summary['memory_file']).exists()}")
|
|
261
|
+
click.echo(f"\n📁 Memory file: {summary['memory_file']}", err=True)
|
|
262
|
+
click.echo(f" Exists: {Path(summary['memory_file']).exists()}", err=True)
|
|
263
263
|
|
|
264
|
-
click.echo("\nCommands:")
|
|
265
|
-
click.echo(" claude-mpm hook-errors list # View detailed errors")
|
|
266
|
-
click.echo(" claude-mpm hook-errors diagnose # Get fix suggestions")
|
|
267
|
-
click.echo(" claude-mpm hook-errors clear # Clear and retry")
|
|
264
|
+
click.echo("\nCommands:", err=True)
|
|
265
|
+
click.echo(" claude-mpm hook-errors list # View detailed errors", err=True)
|
|
266
|
+
click.echo(" claude-mpm hook-errors diagnose # Get fix suggestions", err=True)
|
|
267
|
+
click.echo(" claude-mpm hook-errors clear # Clear and retry", err=True)
|
|
268
268
|
|
|
269
269
|
|
|
270
270
|
# Register the command group
|
|
@@ -118,7 +118,7 @@ class MonitorCommand(BaseCommand):
|
|
|
118
118
|
# Check if it's actually running
|
|
119
119
|
if not self.daemon.lifecycle.is_running():
|
|
120
120
|
return CommandResult.error_result(
|
|
121
|
-
"Monitor daemon failed to start. Check ~/.claude-mpm/monitor-daemon
|
|
121
|
+
"Monitor daemon failed to start. Check ~/.claude-mpm/logs/monitor-daemon-*.log for details."
|
|
122
122
|
)
|
|
123
123
|
|
|
124
124
|
# Get the actual PID
|
|
@@ -146,7 +146,7 @@ class MonitorCommand(BaseCommand):
|
|
|
146
146
|
pass
|
|
147
147
|
|
|
148
148
|
return CommandResult.error_result(
|
|
149
|
-
"Failed to start unified monitor daemon. Check ~/.claude-mpm/monitor-daemon
|
|
149
|
+
"Failed to start unified monitor daemon. Check ~/.claude-mpm/logs/monitor-daemon-*.log for details."
|
|
150
150
|
)
|
|
151
151
|
|
|
152
152
|
def _stop_monitor(self, args) -> CommandResult:
|
|
@@ -688,9 +688,9 @@ class MPMInitCommand:
|
|
|
688
688
|
return len(text) // 4
|
|
689
689
|
|
|
690
690
|
def _deploy_pm_skills(self) -> None:
|
|
691
|
-
"""Deploy PM skills templates to project .claude
|
|
691
|
+
"""Deploy PM skills templates to project .claude directory.
|
|
692
692
|
|
|
693
|
-
Copies PM skills from bundled templates to .claude
|
|
693
|
+
Copies PM skills from bundled templates to .claude/skills/
|
|
694
694
|
with version tracking and checksum validation.
|
|
695
695
|
"""
|
|
696
696
|
try:
|
claude_mpm/cli/commands/run.py
CHANGED
|
@@ -13,7 +13,7 @@ DESIGN DECISIONS:
|
|
|
13
13
|
- Support multiple output formats (json, yaml, table, text)
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
import subprocess
|
|
16
|
+
import subprocess # nosec B404 - required for process management
|
|
17
17
|
import sys
|
|
18
18
|
from datetime import datetime, timezone
|
|
19
19
|
from typing import Optional
|
|
@@ -489,6 +489,18 @@ class RunCommand(BaseCommand):
|
|
|
489
489
|
if hasattr(args, "claude_args") and args.claude_args:
|
|
490
490
|
claude_args.extend(args.claude_args)
|
|
491
491
|
|
|
492
|
+
# Add --resume if flag is set
|
|
493
|
+
if getattr(args, "resume", False) and "--resume" not in claude_args:
|
|
494
|
+
claude_args.insert(0, "--resume")
|
|
495
|
+
|
|
496
|
+
# Add --chrome if flag is set
|
|
497
|
+
if getattr(args, "chrome", False) and "--chrome" not in claude_args:
|
|
498
|
+
claude_args.insert(0, "--chrome")
|
|
499
|
+
|
|
500
|
+
# Add --no-chrome if flag is set
|
|
501
|
+
if getattr(args, "no_chrome", False) and "--no-chrome" not in claude_args:
|
|
502
|
+
claude_args.insert(0, "--no-chrome")
|
|
503
|
+
|
|
492
504
|
# Create runner
|
|
493
505
|
runner = ClaudeRunner(
|
|
494
506
|
enable_tickets=enable_tickets,
|
|
@@ -553,7 +565,7 @@ class RunCommand(BaseCommand):
|
|
|
553
565
|
wrapper_path = get_scripts_dir() / "interactive_wrapper.py"
|
|
554
566
|
if wrapper_path.exists():
|
|
555
567
|
print("Starting interactive session with command interception...")
|
|
556
|
-
subprocess.run([sys.executable, str(wrapper_path)], check=False)
|
|
568
|
+
subprocess.run([sys.executable, str(wrapper_path)], check=False) # nosec B603 - trusted internal paths
|
|
557
569
|
else:
|
|
558
570
|
self.logger.warning(
|
|
559
571
|
"Interactive wrapper not found, falling back to normal mode"
|
|
@@ -907,6 +919,26 @@ def run_session_legacy(args):
|
|
|
907
919
|
else:
|
|
908
920
|
logger.info("[INFO]️ --resume already in claude_args")
|
|
909
921
|
|
|
922
|
+
# Add --chrome to claude_args if the flag is set
|
|
923
|
+
chrome_flag_present = getattr(args, "chrome", False)
|
|
924
|
+
if chrome_flag_present:
|
|
925
|
+
logger.info("📌 --chrome flag detected in args")
|
|
926
|
+
if "--chrome" not in raw_claude_args:
|
|
927
|
+
raw_claude_args = ["--chrome", *raw_claude_args]
|
|
928
|
+
logger.info("✅ Added --chrome to claude_args")
|
|
929
|
+
else:
|
|
930
|
+
logger.info("ℹ️ --chrome already in claude_args")
|
|
931
|
+
|
|
932
|
+
# Add --no-chrome to claude_args if the flag is set
|
|
933
|
+
no_chrome_flag_present = getattr(args, "no_chrome", False)
|
|
934
|
+
if no_chrome_flag_present:
|
|
935
|
+
logger.info("📌 --no-chrome flag detected in args")
|
|
936
|
+
if "--no-chrome" not in raw_claude_args:
|
|
937
|
+
raw_claude_args = ["--no-chrome", *raw_claude_args]
|
|
938
|
+
logger.info("✅ Added --no-chrome to claude_args")
|
|
939
|
+
else:
|
|
940
|
+
logger.info("ℹ️ --no-chrome already in claude_args")
|
|
941
|
+
|
|
910
942
|
# Filter out claude-mpm specific flags before passing to Claude CLI
|
|
911
943
|
logger.debug(f"Pre-filter claude_args: {raw_claude_args}")
|
|
912
944
|
claude_args = filter_claude_mpm_args(raw_claude_args)
|
|
@@ -1044,7 +1076,7 @@ def run_session_legacy(args):
|
|
|
1044
1076
|
wrapper_path = get_scripts_dir() / "interactive_wrapper.py"
|
|
1045
1077
|
if wrapper_path.exists():
|
|
1046
1078
|
print("Starting interactive session with command interception...")
|
|
1047
|
-
subprocess.run([sys.executable, str(wrapper_path)], check=False)
|
|
1079
|
+
subprocess.run([sys.executable, str(wrapper_path)], check=False) # nosec B603 - trusted internal paths
|
|
1048
1080
|
else:
|
|
1049
1081
|
logger.warning("Interactive wrapper not found, falling back to normal mode")
|
|
1050
1082
|
runner.run_interactive(context)
|
|
@@ -11,6 +11,7 @@ for better UX. Handles errors gracefully with actionable messages.
|
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
13
|
import logging
|
|
14
|
+
import os
|
|
14
15
|
import re
|
|
15
16
|
|
|
16
17
|
from ...config.skill_sources import SkillSource, SkillSourceConfiguration
|
|
@@ -20,6 +21,33 @@ from ...services.skills.skill_discovery_service import SkillDiscoveryService
|
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
def _get_github_token(source: SkillSource | None = None) -> str | None:
|
|
25
|
+
"""Get GitHub token with source-specific override support.
|
|
26
|
+
|
|
27
|
+
Priority: source.token > GITHUB_TOKEN > GH_TOKEN
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
source: Optional SkillSource to check for per-source token
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
GitHub token if found, None otherwise
|
|
34
|
+
|
|
35
|
+
Security Note:
|
|
36
|
+
Token is never logged or printed to avoid exposure.
|
|
37
|
+
"""
|
|
38
|
+
# Priority 1: Per-source token (env var reference or direct)
|
|
39
|
+
if source and source.token:
|
|
40
|
+
if source.token.startswith("$"):
|
|
41
|
+
# Env var reference: $VAR_NAME -> os.environ.get("VAR_NAME")
|
|
42
|
+
env_var_name = source.token[1:]
|
|
43
|
+
return os.environ.get(env_var_name)
|
|
44
|
+
# Direct token (not recommended but supported)
|
|
45
|
+
return source.token
|
|
46
|
+
|
|
47
|
+
# Priority 2-3: Global environment variables
|
|
48
|
+
return os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
|
|
49
|
+
|
|
50
|
+
|
|
23
51
|
def _test_skill_repository_access(source: SkillSource) -> dict:
|
|
24
52
|
"""Test if skill repository is accessible via GitHub API.
|
|
25
53
|
|
|
@@ -58,7 +86,13 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
|
|
|
58
86
|
# Test GitHub API access
|
|
59
87
|
api_url = f"https://api.github.com/repos/{owner_repo}"
|
|
60
88
|
|
|
61
|
-
|
|
89
|
+
# Build headers with authentication if token available
|
|
90
|
+
headers = {"Accept": "application/vnd.github+json"}
|
|
91
|
+
token = _get_github_token(source)
|
|
92
|
+
if token:
|
|
93
|
+
headers["Authorization"] = f"token {token}"
|
|
94
|
+
|
|
95
|
+
response = requests.get(api_url, headers=headers, timeout=10)
|
|
62
96
|
|
|
63
97
|
if response.status_code == 200:
|
|
64
98
|
return {"accessible": True, "error": None}
|
|
@@ -68,9 +102,14 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
|
|
|
68
102
|
"error": f"Repository not found: {owner_repo}",
|
|
69
103
|
}
|
|
70
104
|
if response.status_code == 403:
|
|
105
|
+
error_msg = "Access denied (private repository or rate limit)"
|
|
106
|
+
if not token:
|
|
107
|
+
error_msg += (
|
|
108
|
+
". Try setting GITHUB_TOKEN environment variable for private repos"
|
|
109
|
+
)
|
|
71
110
|
return {
|
|
72
111
|
"accessible": False,
|
|
73
|
-
"error":
|
|
112
|
+
"error": error_msg,
|
|
74
113
|
}
|
|
75
114
|
return {
|
|
76
115
|
"accessible": False,
|
|
@@ -263,6 +302,15 @@ def handle_add_skill_source(args) -> int:
|
|
|
263
302
|
|
|
264
303
|
# Create new source
|
|
265
304
|
enabled = not args.disabled
|
|
305
|
+
token = getattr(args, "token", None)
|
|
306
|
+
|
|
307
|
+
# Security warning for direct tokens
|
|
308
|
+
if token and not token.startswith("$"):
|
|
309
|
+
print("⚠️ Warning: Direct token values in config are not recommended")
|
|
310
|
+
print(" Consider using environment variable reference instead:")
|
|
311
|
+
print(" --token $MY_PRIVATE_TOKEN")
|
|
312
|
+
print()
|
|
313
|
+
|
|
266
314
|
source = SkillSource(
|
|
267
315
|
id=source_id,
|
|
268
316
|
type="git",
|
|
@@ -270,6 +318,7 @@ def handle_add_skill_source(args) -> int:
|
|
|
270
318
|
branch=args.branch,
|
|
271
319
|
priority=args.priority,
|
|
272
320
|
enabled=enabled,
|
|
321
|
+
token=token,
|
|
273
322
|
)
|
|
274
323
|
|
|
275
324
|
# Determine if we should test
|
|
@@ -18,7 +18,7 @@ ARCHITECTURE:
|
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
import os
|
|
21
|
-
import subprocess
|
|
21
|
+
import subprocess # nosec B404
|
|
22
22
|
from typing import Optional
|
|
23
23
|
|
|
24
24
|
from rich.console import Console
|
|
@@ -84,6 +84,7 @@ class SkillsManagementCommand(BaseCommand):
|
|
|
84
84
|
SkillsCommands.INFO.value: self._show_skill_info,
|
|
85
85
|
SkillsCommands.CONFIG.value: self._manage_config,
|
|
86
86
|
SkillsCommands.CONFIGURE.value: self._configure_skills,
|
|
87
|
+
SkillsCommands.SELECT.value: self._select_skills_interactive,
|
|
87
88
|
# GitHub deployment commands
|
|
88
89
|
SkillsCommands.DEPLOY_FROM_GITHUB.value: self._deploy_from_github,
|
|
89
90
|
SkillsCommands.LIST_AVAILABLE.value: self._list_available_github_skills,
|
|
@@ -504,7 +505,7 @@ class SkillsManagementCommand(BaseCommand):
|
|
|
504
505
|
# Open in editor
|
|
505
506
|
editor = os.environ.get("EDITOR", "nano")
|
|
506
507
|
try:
|
|
507
|
-
subprocess.run([editor, str(config_path)], check=True)
|
|
508
|
+
subprocess.run([editor, str(config_path)], check=True) # nosec B603
|
|
508
509
|
console.print(
|
|
509
510
|
f"\n[green]Configuration saved to {config_path}[/green]\n"
|
|
510
511
|
)
|
|
@@ -537,6 +538,7 @@ class SkillsManagementCommand(BaseCommand):
|
|
|
537
538
|
toolchain = getattr(args, "toolchain", None)
|
|
538
539
|
categories = getattr(args, "categories", None)
|
|
539
540
|
force = getattr(args, "force", False)
|
|
541
|
+
deploy_all = getattr(args, "all", False)
|
|
540
542
|
|
|
541
543
|
if collection:
|
|
542
544
|
console.print(
|
|
@@ -547,14 +549,15 @@ class SkillsManagementCommand(BaseCommand):
|
|
|
547
549
|
"\n[bold cyan]Deploying skills from default collection...[/bold cyan]\n"
|
|
548
550
|
)
|
|
549
551
|
|
|
550
|
-
#
|
|
551
|
-
#
|
|
552
|
+
# Use selective deployment unless --all flag is provided
|
|
553
|
+
# Selective mode deploys only agent-referenced skills
|
|
554
|
+
# --all mode deploys all available skills from the collection
|
|
552
555
|
result = self.skills_deployer.deploy_skills(
|
|
553
556
|
collection=collection,
|
|
554
557
|
toolchain=toolchain,
|
|
555
558
|
categories=categories,
|
|
556
559
|
force=force,
|
|
557
|
-
selective=
|
|
560
|
+
selective=not deploy_all,
|
|
558
561
|
)
|
|
559
562
|
|
|
560
563
|
# Display results
|
|
@@ -673,33 +676,91 @@ class SkillsManagementCommand(BaseCommand):
|
|
|
673
676
|
def _check_deployed_skills(self, args) -> CommandResult:
|
|
674
677
|
"""Check currently deployed skills in ~/.claude/skills/."""
|
|
675
678
|
try:
|
|
676
|
-
|
|
679
|
+
# Get deployed skills
|
|
680
|
+
deployed_result = self.skills_deployer.check_deployed_skills()
|
|
681
|
+
deployed_names = {skill["name"] for skill in deployed_result["skills"]}
|
|
677
682
|
|
|
678
683
|
console.print("\n[bold cyan]Claude Code Skills Status:[/bold cyan]\n")
|
|
679
|
-
console.print(
|
|
684
|
+
console.print(
|
|
685
|
+
f"[dim]Directory: {deployed_result['claude_skills_dir']}[/dim]\n"
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
# Fetch available skills from GitHub to get full list
|
|
689
|
+
try:
|
|
690
|
+
available_result = self.skills_deployer.list_available_skills()
|
|
691
|
+
all_skills = available_result.get("skills", [])
|
|
692
|
+
except Exception as e:
|
|
693
|
+
console.print(
|
|
694
|
+
f"[yellow]Warning: Could not fetch available skills: {e}[/yellow]\n"
|
|
695
|
+
)
|
|
696
|
+
all_skills = []
|
|
680
697
|
|
|
681
|
-
|
|
682
|
-
|
|
698
|
+
# Combine deployed and available skills
|
|
699
|
+
skill_map = {}
|
|
700
|
+
|
|
701
|
+
# Add available skills
|
|
702
|
+
for skill in all_skills:
|
|
703
|
+
skill_name = skill.get("name", "")
|
|
704
|
+
if skill_name:
|
|
705
|
+
skill_map[skill_name] = {
|
|
706
|
+
"name": skill.get("display_name")
|
|
707
|
+
or skill_name.replace("-", " ").title(),
|
|
708
|
+
"skill_id": skill_name,
|
|
709
|
+
"source": "MPM Skills",
|
|
710
|
+
"is_deployed": skill_name in deployed_names,
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
# Add any deployed skills not in available list (local/custom skills)
|
|
714
|
+
for skill in deployed_result["skills"]:
|
|
715
|
+
skill_name = skill["name"]
|
|
716
|
+
if skill_name not in skill_map:
|
|
717
|
+
skill_map[skill_name] = {
|
|
718
|
+
"name": skill_name.replace("-", " ").title(),
|
|
719
|
+
"skill_id": skill_name,
|
|
720
|
+
"source": "Local",
|
|
721
|
+
"is_deployed": True,
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if not skill_map:
|
|
725
|
+
console.print("[yellow]No skills available.[/yellow]")
|
|
683
726
|
console.print(
|
|
684
727
|
"[dim]Use 'claude-mpm skills deploy-github' to deploy skills.[/dim]\n"
|
|
685
728
|
)
|
|
686
729
|
return CommandResult(success=True, exit_code=0)
|
|
687
730
|
|
|
688
|
-
|
|
689
|
-
|
|
731
|
+
# Create table matching agent management format
|
|
732
|
+
table = Table(show_header=True, header_style="bold cyan")
|
|
733
|
+
table.add_column("#", style="bright_black", width=6, no_wrap=True)
|
|
734
|
+
table.add_column(
|
|
735
|
+
"Skill ID", style="bright_black", no_wrap=True, overflow="ellipsis"
|
|
690
736
|
)
|
|
737
|
+
table.add_column(
|
|
738
|
+
"Name", style="bright_cyan", no_wrap=True, overflow="ellipsis"
|
|
739
|
+
)
|
|
740
|
+
table.add_column("Source", style="bright_yellow", no_wrap=True)
|
|
741
|
+
table.add_column("Status", style="bright_black", no_wrap=True)
|
|
691
742
|
|
|
692
|
-
#
|
|
693
|
-
|
|
694
|
-
table.add_column("Skill Name", style="green")
|
|
695
|
-
table.add_column("Path", style="dim")
|
|
743
|
+
# Sort skills by name for consistent display
|
|
744
|
+
sorted_skills = sorted(skill_map.values(), key=lambda s: s["skill_id"])
|
|
696
745
|
|
|
697
|
-
for skill in
|
|
698
|
-
|
|
746
|
+
for idx, skill in enumerate(sorted_skills, 1):
|
|
747
|
+
status = (
|
|
748
|
+
"[green]Installed[/green]" if skill["is_deployed"] else "Available"
|
|
749
|
+
)
|
|
750
|
+
table.add_row(
|
|
751
|
+
str(idx), skill["skill_id"], skill["name"], skill["source"], status
|
|
752
|
+
)
|
|
699
753
|
|
|
700
754
|
console.print(table)
|
|
701
755
|
console.print()
|
|
702
756
|
|
|
757
|
+
# Show summary
|
|
758
|
+
deployed_count = sum(1 for s in skill_map.values() if s["is_deployed"])
|
|
759
|
+
console.print(
|
|
760
|
+
f"[dim]Showing {len(skill_map)} skills ({deployed_count} installed, "
|
|
761
|
+
f"{len(skill_map) - deployed_count} available)[/dim]\n"
|
|
762
|
+
)
|
|
763
|
+
|
|
703
764
|
return CommandResult(success=True, exit_code=0)
|
|
704
765
|
|
|
705
766
|
except Exception as e:
|
|
@@ -1222,6 +1283,99 @@ class SkillsManagementCommand(BaseCommand):
|
|
|
1222
1283
|
console.print(f"[dim]{traceback.format_exc()}[/dim]")
|
|
1223
1284
|
return CommandResult(success=False, message=str(e), exit_code=1)
|
|
1224
1285
|
|
|
1286
|
+
def _select_skills_interactive(self, args) -> CommandResult:
|
|
1287
|
+
"""Interactive skill selection with topic grouping.
|
|
1288
|
+
|
|
1289
|
+
This command provides a two-tier selection interface:
|
|
1290
|
+
1. Select topic groups (toolchains) to explore
|
|
1291
|
+
2. Multi-select skills within each topic group
|
|
1292
|
+
|
|
1293
|
+
Features:
|
|
1294
|
+
- Groups skills by toolchain (universal, python, typescript, etc.)
|
|
1295
|
+
- Shows skills auto-included by agent dependencies
|
|
1296
|
+
- Displays token counts for each skill
|
|
1297
|
+
- Updates config and runs reconciliation
|
|
1298
|
+
|
|
1299
|
+
Returns:
|
|
1300
|
+
CommandResult with success/failure status
|
|
1301
|
+
"""
|
|
1302
|
+
try:
|
|
1303
|
+
from ...cli.interactive.skill_selector import run_skill_selector
|
|
1304
|
+
from ...core.unified_config import UnifiedConfig
|
|
1305
|
+
from ...services.agents.deployment.deployment_reconciler import (
|
|
1306
|
+
DeploymentReconciler,
|
|
1307
|
+
)
|
|
1308
|
+
|
|
1309
|
+
console.print("\n[bold cyan]Interactive Skill Selector[/bold cyan]\n")
|
|
1310
|
+
|
|
1311
|
+
# Run skill selector
|
|
1312
|
+
selected_skills = run_skill_selector()
|
|
1313
|
+
|
|
1314
|
+
if selected_skills is None:
|
|
1315
|
+
console.print("\n[yellow]Skill selection cancelled[/yellow]")
|
|
1316
|
+
return CommandResult(success=True, exit_code=0)
|
|
1317
|
+
|
|
1318
|
+
# Update config with selected skills
|
|
1319
|
+
config = UnifiedConfig()
|
|
1320
|
+
config.skills.enabled = selected_skills
|
|
1321
|
+
|
|
1322
|
+
# Save config
|
|
1323
|
+
try:
|
|
1324
|
+
config.save()
|
|
1325
|
+
console.print(
|
|
1326
|
+
f"\n[green]✓ Saved {len(selected_skills)} skills to configuration[/green]"
|
|
1327
|
+
)
|
|
1328
|
+
except Exception as e:
|
|
1329
|
+
console.print(f"\n[red]Failed to save configuration: {e}[/red]")
|
|
1330
|
+
return CommandResult(success=False, message=str(e), exit_code=1)
|
|
1331
|
+
|
|
1332
|
+
# Run reconciliation to deploy skills
|
|
1333
|
+
console.print("\n[cyan]Running skill reconciliation...[/cyan]")
|
|
1334
|
+
reconciler = DeploymentReconciler(config)
|
|
1335
|
+
|
|
1336
|
+
try:
|
|
1337
|
+
from pathlib import Path
|
|
1338
|
+
|
|
1339
|
+
project_path = Path.cwd()
|
|
1340
|
+
result = reconciler.reconcile_skills(project_path)
|
|
1341
|
+
|
|
1342
|
+
if result.deployed:
|
|
1343
|
+
console.print(
|
|
1344
|
+
f" [green]✓ Deployed: {', '.join(result.deployed)}[/green]"
|
|
1345
|
+
)
|
|
1346
|
+
if result.removed:
|
|
1347
|
+
console.print(
|
|
1348
|
+
f" [yellow]✓ Removed: {', '.join(result.removed)}[/yellow]"
|
|
1349
|
+
)
|
|
1350
|
+
if result.errors:
|
|
1351
|
+
for error in result.errors:
|
|
1352
|
+
console.print(f" [red]✗ {error}[/red]")
|
|
1353
|
+
|
|
1354
|
+
if result.success:
|
|
1355
|
+
console.print(
|
|
1356
|
+
"\n[bold green]✓ Skill deployment complete![/bold green]"
|
|
1357
|
+
)
|
|
1358
|
+
return CommandResult(success=True, exit_code=0)
|
|
1359
|
+
console.print(
|
|
1360
|
+
f"\n[yellow]⚠ Deployment had {len(result.errors)} errors[/yellow]"
|
|
1361
|
+
)
|
|
1362
|
+
return CommandResult(
|
|
1363
|
+
success=False,
|
|
1364
|
+
message=f"{len(result.errors)} deployment errors",
|
|
1365
|
+
exit_code=1,
|
|
1366
|
+
)
|
|
1367
|
+
|
|
1368
|
+
except Exception as e:
|
|
1369
|
+
console.print(f"\n[red]Reconciliation failed: {e}[/red]")
|
|
1370
|
+
return CommandResult(success=False, message=str(e), exit_code=1)
|
|
1371
|
+
|
|
1372
|
+
except Exception as e:
|
|
1373
|
+
console.print(f"[red]Skill selection error: {e}[/red]")
|
|
1374
|
+
import traceback
|
|
1375
|
+
|
|
1376
|
+
console.print(f"[dim]{traceback.format_exc()}[/dim]")
|
|
1377
|
+
return CommandResult(success=False, message=str(e), exit_code=1)
|
|
1378
|
+
|
|
1225
1379
|
|
|
1226
1380
|
def manage_skills(args) -> int:
|
|
1227
1381
|
"""
|