claude-mpm 5.4.48__py3-none-any.whl → 5.6.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
- claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +109 -1925
- claude_mpm/agents/PM_INSTRUCTIONS.md +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 +216 -0
- claude_mpm/cli/commands/configure.py +620 -21
- claude_mpm/cli/commands/configure_agent_display.py +3 -1
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +15 -8
- claude_mpm/cli/commands/profile.py +9 -10
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +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 +116 -0
- claude_mpm/cli/parsers/profile_parser.py +0 -1
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/parsers/skill_source_parser.py +4 -0
- claude_mpm/cli/parsers/skills_parser.py +5 -0
- claude_mpm/cli/startup.py +544 -511
- claude_mpm/cli/startup_display.py +74 -6
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +78 -0
- claude_mpm/commander/adapters/__init__.py +60 -0
- claude_mpm/commander/adapters/auggie.py +260 -0
- claude_mpm/commander/adapters/base.py +288 -0
- claude_mpm/commander/adapters/claude_code.py +392 -0
- claude_mpm/commander/adapters/codex.py +237 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/adapters/example_usage.py +310 -0
- claude_mpm/commander/adapters/mpm.py +389 -0
- claude_mpm/commander/adapters/registry.py +204 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +121 -0
- claude_mpm/commander/api/errors.py +133 -0
- claude_mpm/commander/api/routes/__init__.py +8 -0
- claude_mpm/commander/api/routes/events.py +184 -0
- claude_mpm/commander/api/routes/inbox.py +171 -0
- claude_mpm/commander/api/routes/messages.py +148 -0
- claude_mpm/commander/api/routes/projects.py +271 -0
- claude_mpm/commander/api/routes/sessions.py +226 -0
- claude_mpm/commander/api/routes/work.py +296 -0
- claude_mpm/commander/api/schemas.py +186 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +146 -0
- claude_mpm/commander/chat/commands.py +96 -0
- claude_mpm/commander/chat/repl.py +310 -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 +332 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +146 -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 +450 -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 +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 +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 +361 -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 +1 -0
- claude_mpm/core/claude_runner.py +154 -2
- claude_mpm/core/config.py +35 -22
- claude_mpm/core/config_constants.py +74 -9
- claude_mpm/core/constants.py +56 -12
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/interactive_session.py +12 -11
- claude_mpm/core/logger.py +26 -9
- claude_mpm/core/logging_utils.py +39 -13
- claude_mpm/core/network_config.py +148 -0
- claude_mpm/core/oneshot_session.py +7 -6
- claude_mpm/core/optimized_startup.py +3 -1
- claude_mpm/core/output_style_manager.py +66 -18
- claude_mpm/core/shared/config_loader.py +3 -1
- claude_mpm/core/socketio_pool.py +47 -15
- claude_mpm/core/unified_config.py +54 -8
- claude_mpm/core/unified_paths.py +95 -90
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/1WZnGYqX.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/67pF3qNn.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/6RxdMKe4.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/8cZrfX0h.js +60 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/9a6T2nm-.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B443AUzu.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B8AwtY2H.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BF15LAsF.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BRcwIQNr.js +4 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uj46x2Wr.js → BSNlmTZj.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BV6nKitt.js +43 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BViJ8lZt.js +128 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BcQ-Q0FE.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bpyvgze_.js +30 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C3rbW_a-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C8WYN38h.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C9I8FlXH.js +61 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIQcWgO2.js +36 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIctN7YN.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CKrS_JZW.js +145 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CR6P9C4A.js +89 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRRR9MD_.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CSXtMOf0.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CT-sbxSk.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWm6DJsp.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CpqQ1Kzn.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D2nGpDRe.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9iCMida.js +267 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9ykgMoY.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DL2Ldur1.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DPfltzjH.js +165 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{N4qtv3Hx.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUliQN2b.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DXlhR01x.js +122 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D_lyTybS.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DngoTTgh.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DqkmHtDC.js +220 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DsDh8EYs.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DypDmXgd.js +139 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/IPYC-LnN.js +162 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JpevfAFt.js +68 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DjhvlsAc.js → NqQ1dWOy.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/R8CEIRAd.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Zxy7qc-l.js +64 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/qtd3IeO4.js +15 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ulBFON_C.js +65 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/wQVh1CoA.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.Dr7t0z2J.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.CAGBuiOw.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DG-KkbDf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +11 -11
- claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +527 -136
- claude_mpm/hooks/claude_hooks/hook_handler.py +170 -104
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +206 -36
- claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
- claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
- claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
- claude_mpm/hooks/claude_hooks/services/container.py +310 -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 +215 -2
- claude_mpm/scripts/claude-hook-handler.sh +46 -19
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -1
- claude_mpm/services/agents/deployment/agent_format_converter.py +8 -6
- claude_mpm/services/agents/deployment/agent_template_builder.py +14 -4
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +4 -4
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +4 -1
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/git_source_manager.py +6 -2
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
- claude_mpm/services/agents/sources/git_source_sync_service.py +10 -5
- claude_mpm/services/agents/startup_sync.py +5 -2
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/command_deployment_service.py +44 -26
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +325 -0
- claude_mpm/services/hook_installer_service.py +77 -8
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -3
- claude_mpm/services/monitor/server.py +106 -16
- claude_mpm/services/pm_skills_deployer.py +267 -94
- claude_mpm/services/profile_manager.py +10 -4
- claude_mpm/services/skills/git_skill_source_manager.py +192 -29
- claude_mpm/services/skills/selective_skill_deployer.py +211 -46
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +188 -67
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/__init__.py +2 -1
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
- claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
- claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
- claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
- claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
- claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
- claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
- claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
- claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
- claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
- claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
- claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/security-scanning.md +112 -0
- claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
- claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
- claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
- claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
- claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
- claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
- claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
- claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
- claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
- claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
- claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/registry.py +295 -90
- claude_mpm/skills/skill_manager.py +29 -23
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- claude_mpm/utils/agent_dependency_loader.py +103 -4
- claude_mpm/utils/robust_installer.py +45 -24
- claude_mpm-5.6.34.dist-info/METADATA +393 -0
- {claude_mpm-5.4.48.dist-info → claude_mpm-5.6.34.dist-info}/RECORD +453 -151
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- claude_mpm-5.4.48.dist-info/METADATA +0 -999
- {claude_mpm-5.4.48.dist-info → claude_mpm-5.6.34.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.48.dist-info → claude_mpm-5.6.34.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.48.dist-info → claude_mpm-5.6.34.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.48.dist-info → claude_mpm-5.6.34.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.48.dist-info → claude_mpm-5.6.34.dist-info}/top_level.txt +0 -0
claude_mpm/cli/startup.py
CHANGED
|
@@ -34,13 +34,13 @@ def sync_hooks_on_startup(quiet: bool = False) -> bool:
|
|
|
34
34
|
installer = HookInstaller()
|
|
35
35
|
|
|
36
36
|
# Show brief status (hooks sync is fast)
|
|
37
|
-
if not quiet:
|
|
37
|
+
if not quiet and sys.stdout.isatty():
|
|
38
38
|
print("Syncing Claude Code hooks...", end=" ", flush=True)
|
|
39
39
|
|
|
40
40
|
# Reinstall hooks (force=True ensures update)
|
|
41
41
|
success = installer.install_hooks(force=True)
|
|
42
42
|
|
|
43
|
-
if not quiet:
|
|
43
|
+
if not quiet and sys.stdout.isatty():
|
|
44
44
|
if success:
|
|
45
45
|
print("✓")
|
|
46
46
|
else:
|
|
@@ -49,7 +49,7 @@ def sync_hooks_on_startup(quiet: bool = False) -> bool:
|
|
|
49
49
|
return success
|
|
50
50
|
|
|
51
51
|
except Exception as e:
|
|
52
|
-
if not quiet:
|
|
52
|
+
if not quiet and sys.stdout.isatty():
|
|
53
53
|
print("(error)")
|
|
54
54
|
# Log but don't fail startup
|
|
55
55
|
from ..core.logger import get_logger
|
|
@@ -161,7 +161,25 @@ def setup_early_environment(argv):
|
|
|
161
161
|
# CRITICAL: Suppress ALL logging by default
|
|
162
162
|
# This catches all loggers (claude_mpm.*, service.*, framework_loader, etc.)
|
|
163
163
|
# This will be overridden by setup_mcp_server_logging() based on user preference
|
|
164
|
-
logging.getLogger()
|
|
164
|
+
root_logger = logging.getLogger()
|
|
165
|
+
root_logger.setLevel(logging.CRITICAL + 1) # Root logger catches everything
|
|
166
|
+
root_logger.handlers = [] # Remove any handlers
|
|
167
|
+
|
|
168
|
+
# Also suppress common module loggers explicitly to prevent handler leakage
|
|
169
|
+
for logger_name in [
|
|
170
|
+
"claude_mpm",
|
|
171
|
+
"path_resolver",
|
|
172
|
+
"file_loader",
|
|
173
|
+
"framework_loader",
|
|
174
|
+
"service",
|
|
175
|
+
"instruction_loader",
|
|
176
|
+
"agent_loader",
|
|
177
|
+
"startup",
|
|
178
|
+
]:
|
|
179
|
+
module_logger = logging.getLogger(logger_name)
|
|
180
|
+
module_logger.setLevel(logging.CRITICAL + 1)
|
|
181
|
+
module_logger.handlers = []
|
|
182
|
+
module_logger.propagate = False
|
|
165
183
|
|
|
166
184
|
# Process argv
|
|
167
185
|
if argv is None:
|
|
@@ -191,7 +209,17 @@ def should_skip_background_services(args, processed_argv):
|
|
|
191
209
|
skip_commands = ["--version", "-v", "--help", "-h"]
|
|
192
210
|
return any(cmd in (processed_argv or sys.argv[1:]) for cmd in skip_commands) or (
|
|
193
211
|
hasattr(args, "command")
|
|
194
|
-
and args.command
|
|
212
|
+
and args.command
|
|
213
|
+
in [
|
|
214
|
+
"info",
|
|
215
|
+
"doctor",
|
|
216
|
+
"config",
|
|
217
|
+
"mcp",
|
|
218
|
+
"configure",
|
|
219
|
+
"hook-errors",
|
|
220
|
+
"autotodos",
|
|
221
|
+
"commander",
|
|
222
|
+
]
|
|
195
223
|
)
|
|
196
224
|
|
|
197
225
|
|
|
@@ -234,7 +262,7 @@ def deploy_bundled_skills():
|
|
|
234
262
|
if not skills_config.get("auto_deploy", True):
|
|
235
263
|
# Auto-deploy disabled, skip silently
|
|
236
264
|
return
|
|
237
|
-
except Exception:
|
|
265
|
+
except Exception: # nosec B110
|
|
238
266
|
# If config loading fails, assume auto-deploy is enabled (default)
|
|
239
267
|
pass
|
|
240
268
|
|
|
@@ -252,11 +280,13 @@ def deploy_bundled_skills():
|
|
|
252
280
|
if deployment_result.get("deployed"):
|
|
253
281
|
# Show simple feedback for deployed skills
|
|
254
282
|
deployed_count = len(deployment_result["deployed"])
|
|
255
|
-
|
|
283
|
+
if sys.stdout.isatty():
|
|
284
|
+
print(f"✓ Bundled skills ready ({deployed_count} deployed)", flush=True)
|
|
256
285
|
logger.info(f"Skills: Deployed {deployed_count} skill(s)")
|
|
257
286
|
elif not deployment_result.get("errors"):
|
|
258
287
|
# No deployment needed, skills already present
|
|
259
|
-
|
|
288
|
+
if sys.stdout.isatty():
|
|
289
|
+
print("✓ Bundled skills ready", flush=True)
|
|
260
290
|
|
|
261
291
|
if deployment_result.get("errors"):
|
|
262
292
|
logger.warning(
|
|
@@ -290,7 +320,8 @@ def discover_and_link_runtime_skills():
|
|
|
290
320
|
|
|
291
321
|
discover_skills()
|
|
292
322
|
# Show simple success feedback
|
|
293
|
-
|
|
323
|
+
if sys.stdout.isatty():
|
|
324
|
+
print("✓ Runtime skills linked", flush=True)
|
|
294
325
|
except Exception as e:
|
|
295
326
|
# Import logger here to avoid circular imports
|
|
296
327
|
from ..core.logger import get_logger
|
|
@@ -308,63 +339,62 @@ def deploy_output_style_on_startup():
|
|
|
308
339
|
communication without emojis and exclamation points. Styles are project-specific
|
|
309
340
|
to allow different projects to have different communication styles.
|
|
310
341
|
|
|
311
|
-
DESIGN DECISION: This is non-blocking and idempotent. Deploys to
|
|
312
|
-
directory (
|
|
313
|
-
|
|
342
|
+
DESIGN DECISION: This is non-blocking and idempotent. Deploys to user-level
|
|
343
|
+
directory (~/.claude/output-styles/) which is the official Claude Code location
|
|
344
|
+
for custom output styles.
|
|
314
345
|
|
|
315
|
-
Deploys
|
|
316
|
-
- claude-mpm
|
|
346
|
+
Deploys all styles:
|
|
347
|
+
- claude-mpm.md (professional mode)
|
|
317
348
|
- claude-mpm-teacher.md (teaching mode)
|
|
349
|
+
- claude-mpm-research.md (research mode - for codebase analysis)
|
|
318
350
|
"""
|
|
319
351
|
try:
|
|
320
|
-
import
|
|
321
|
-
from pathlib import Path
|
|
352
|
+
from ..core.output_style_manager import OutputStyleManager
|
|
322
353
|
|
|
323
|
-
#
|
|
324
|
-
|
|
325
|
-
professional_source = package_dir / "CLAUDE_MPM_OUTPUT_STYLE.md"
|
|
326
|
-
teacher_source = package_dir / "CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md"
|
|
354
|
+
# Initialize the output style manager
|
|
355
|
+
manager = OutputStyleManager()
|
|
327
356
|
|
|
328
|
-
#
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
# Create directory if it doesn't exist
|
|
335
|
-
output_styles_dir.mkdir(parents=True, exist_ok=True)
|
|
336
|
-
|
|
337
|
-
# Check if already deployed (both files exist and have content)
|
|
338
|
-
already_deployed = (
|
|
339
|
-
professional_target.exists()
|
|
340
|
-
and teacher_target.exists()
|
|
341
|
-
and professional_target.stat().st_size > 0
|
|
342
|
-
and teacher_target.stat().st_size > 0
|
|
343
|
-
)
|
|
357
|
+
# Check if Claude Code version supports output styles (>= 1.0.83)
|
|
358
|
+
if not manager.supports_output_styles():
|
|
359
|
+
# Skip deployment for older versions
|
|
360
|
+
# The manager will fall back to injecting content directly
|
|
361
|
+
return
|
|
344
362
|
|
|
345
|
-
if
|
|
363
|
+
# Check if all styles are already deployed and up-to-date
|
|
364
|
+
all_up_to_date = True
|
|
365
|
+
for style_config in manager.styles.values():
|
|
366
|
+
source_path = style_config["source"]
|
|
367
|
+
target_path = style_config["target"]
|
|
368
|
+
|
|
369
|
+
if not (
|
|
370
|
+
target_path.exists()
|
|
371
|
+
and source_path.exists()
|
|
372
|
+
and target_path.stat().st_size == source_path.stat().st_size
|
|
373
|
+
):
|
|
374
|
+
all_up_to_date = False
|
|
375
|
+
break
|
|
376
|
+
|
|
377
|
+
if all_up_to_date:
|
|
346
378
|
# Show feedback that output styles are ready
|
|
347
|
-
|
|
379
|
+
if sys.stdout.isatty():
|
|
380
|
+
print("✓ Output styles ready", flush=True)
|
|
348
381
|
return
|
|
349
382
|
|
|
350
|
-
# Deploy
|
|
351
|
-
|
|
352
|
-
if professional_source.exists():
|
|
353
|
-
shutil.copy2(professional_source, professional_target)
|
|
354
|
-
deployed_count += 1
|
|
383
|
+
# Deploy all styles using the manager
|
|
384
|
+
results = manager.deploy_all_styles(activate_default=True)
|
|
355
385
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
deployed_count += 1
|
|
386
|
+
# Count successful deployments
|
|
387
|
+
deployed_count = sum(1 for success in results.values() if success)
|
|
359
388
|
|
|
360
389
|
if deployed_count > 0:
|
|
361
|
-
|
|
390
|
+
if sys.stdout.isatty():
|
|
391
|
+
print(f"✓ Output styles deployed ({deployed_count} styles)", flush=True)
|
|
362
392
|
else:
|
|
363
|
-
#
|
|
393
|
+
# Deployment failed - log but don't fail startup
|
|
364
394
|
from ..core.logger import get_logger
|
|
365
395
|
|
|
366
396
|
logger = get_logger("cli")
|
|
367
|
-
logger.debug("
|
|
397
|
+
logger.debug("Failed to deploy any output styles")
|
|
368
398
|
|
|
369
399
|
except Exception as e:
|
|
370
400
|
# Non-critical - log but don't fail startup
|
|
@@ -453,7 +483,95 @@ def _cleanup_orphaned_agents(deploy_target: Path, deployed_agents: list[str]) ->
|
|
|
453
483
|
return removed_count
|
|
454
484
|
|
|
455
485
|
|
|
456
|
-
def
|
|
486
|
+
def _save_deployment_state_after_reconciliation(
|
|
487
|
+
agent_result, project_path: Path
|
|
488
|
+
) -> None:
|
|
489
|
+
"""Save deployment state after reconciliation to prevent duplicate deployment.
|
|
490
|
+
|
|
491
|
+
WHY: After perform_startup_reconciliation() deploys agents to .claude/agents/,
|
|
492
|
+
we need to save a deployment state file so that ClaudeRunner.setup_agents()
|
|
493
|
+
can detect agents are already deployed and skip redundant deployment.
|
|
494
|
+
|
|
495
|
+
This prevents the "✓ Deployed 31 native agents" duplicate deployment that
|
|
496
|
+
occurs when setup_agents() doesn't know reconciliation already ran.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
agent_result: DeploymentResult from perform_startup_reconciliation()
|
|
500
|
+
project_path: Project root directory
|
|
501
|
+
|
|
502
|
+
DESIGN DECISION: Use same state file format as ClaudeRunner._save_deployment_state()
|
|
503
|
+
Located at: .claude-mpm/cache/deployment_state.json
|
|
504
|
+
|
|
505
|
+
State file format:
|
|
506
|
+
{
|
|
507
|
+
"version": "5.6.13",
|
|
508
|
+
"agent_count": 15,
|
|
509
|
+
"deployment_hash": "sha256:...",
|
|
510
|
+
"deployed_at": 1234567890.123
|
|
511
|
+
}
|
|
512
|
+
"""
|
|
513
|
+
import hashlib
|
|
514
|
+
import json
|
|
515
|
+
import time
|
|
516
|
+
|
|
517
|
+
from ..core.logger import get_logger
|
|
518
|
+
|
|
519
|
+
logger = get_logger("cli")
|
|
520
|
+
|
|
521
|
+
try:
|
|
522
|
+
# Get version from package
|
|
523
|
+
from claude_mpm import __version__
|
|
524
|
+
|
|
525
|
+
# Path to state file (matches ClaudeRunner._get_deployment_state_path())
|
|
526
|
+
state_file = project_path / ".claude-mpm" / "cache" / "deployment_state.json"
|
|
527
|
+
agents_dir = project_path / ".claude" / "agents"
|
|
528
|
+
|
|
529
|
+
# Count deployed agents
|
|
530
|
+
if agents_dir.exists():
|
|
531
|
+
agent_count = len(list(agents_dir.glob("*.md")))
|
|
532
|
+
else:
|
|
533
|
+
agent_count = 0
|
|
534
|
+
|
|
535
|
+
# Calculate deployment hash (matches ClaudeRunner._calculate_deployment_hash())
|
|
536
|
+
# CRITICAL: Must match exact hash algorithm used in ClaudeRunner
|
|
537
|
+
# Hashes filename + file content (not mtime) for consistency
|
|
538
|
+
deployment_hash = ""
|
|
539
|
+
if agents_dir.exists():
|
|
540
|
+
agent_files = sorted(agents_dir.glob("*.md"))
|
|
541
|
+
hash_obj = hashlib.sha256()
|
|
542
|
+
for agent_file in agent_files:
|
|
543
|
+
# Include filename and content in hash (matches ClaudeRunner)
|
|
544
|
+
hash_obj.update(agent_file.name.encode())
|
|
545
|
+
try:
|
|
546
|
+
hash_obj.update(agent_file.read_bytes())
|
|
547
|
+
except Exception as e:
|
|
548
|
+
logger.debug(f"Error reading {agent_file} for hash: {e}")
|
|
549
|
+
|
|
550
|
+
deployment_hash = hash_obj.hexdigest()
|
|
551
|
+
|
|
552
|
+
# Create state data
|
|
553
|
+
state_data = {
|
|
554
|
+
"version": __version__,
|
|
555
|
+
"agent_count": agent_count,
|
|
556
|
+
"deployment_hash": deployment_hash,
|
|
557
|
+
"deployed_at": time.time(),
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
# Ensure directory exists
|
|
561
|
+
state_file.parent.mkdir(parents=True, exist_ok=True)
|
|
562
|
+
|
|
563
|
+
# Write state file
|
|
564
|
+
state_file.write_text(json.dumps(state_data, indent=2))
|
|
565
|
+
logger.debug(
|
|
566
|
+
f"Saved deployment state after reconciliation: {agent_count} agents"
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
except Exception as e:
|
|
570
|
+
# Non-critical error - log but don't fail startup
|
|
571
|
+
logger.debug(f"Failed to save deployment state: {e}")
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def sync_remote_agents_on_startup(force_sync: bool = False):
|
|
457
575
|
"""
|
|
458
576
|
Synchronize agent templates from remote sources on startup.
|
|
459
577
|
|
|
@@ -466,28 +584,28 @@ def sync_remote_agents_on_startup():
|
|
|
466
584
|
block startup to ensure claude-mpm remains functional.
|
|
467
585
|
|
|
468
586
|
Workflow:
|
|
469
|
-
1.
|
|
470
|
-
2.
|
|
471
|
-
3.
|
|
472
|
-
4. Cleanup
|
|
587
|
+
1. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
|
|
588
|
+
2. Deploy agents to ~/.claude/agents/ - Phase 2 progress bar
|
|
589
|
+
3. Cleanup orphaned agents (ours but no longer deployed) - Phase 3
|
|
590
|
+
4. Cleanup legacy agent cache directories (after sync/deployment) - Phase 4
|
|
473
591
|
5. Log deployment results
|
|
474
|
-
"""
|
|
475
|
-
# Cleanup legacy cache directories first (before syncing)
|
|
476
|
-
cleanup_legacy_agent_cache()
|
|
477
592
|
|
|
478
|
-
|
|
593
|
+
Args:
|
|
594
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
595
|
+
"""
|
|
596
|
+
# DEPRECATED: Legacy warning - no-op function, kept for compatibility
|
|
479
597
|
check_legacy_cache()
|
|
480
598
|
|
|
481
599
|
try:
|
|
600
|
+
# Load active profile if configured
|
|
601
|
+
# Get project root (where .claude-mpm exists)
|
|
602
|
+
from pathlib import Path
|
|
603
|
+
|
|
482
604
|
from ..core.shared.config_loader import ConfigLoader
|
|
483
|
-
from ..services.agents.deployment.agent_deployment import AgentDeploymentService
|
|
484
605
|
from ..services.agents.startup_sync import sync_agents_on_startup
|
|
485
606
|
from ..services.profile_manager import ProfileManager
|
|
486
607
|
from ..utils.progress import ProgressBar
|
|
487
608
|
|
|
488
|
-
# Load active profile if configured
|
|
489
|
-
# Get project root (where .claude-mpm exists)
|
|
490
|
-
from pathlib import Path
|
|
491
609
|
project_root = Path.cwd()
|
|
492
610
|
|
|
493
611
|
profile_manager = ProfileManager(project_dir=project_root)
|
|
@@ -508,7 +626,7 @@ def sync_remote_agents_on_startup():
|
|
|
508
626
|
)
|
|
509
627
|
|
|
510
628
|
# Phase 1: Sync files from Git sources
|
|
511
|
-
result = sync_agents_on_startup()
|
|
629
|
+
result = sync_agents_on_startup(force_refresh=force_sync)
|
|
512
630
|
|
|
513
631
|
# Only proceed with deployment if sync was enabled and ran
|
|
514
632
|
if result.get("enabled") and result.get("sources_synced", 0) > 0:
|
|
@@ -531,294 +649,95 @@ def sync_remote_agents_on_startup():
|
|
|
531
649
|
logger.warning(f"Agent sync completed with {len(errors)} errors")
|
|
532
650
|
|
|
533
651
|
# Phase 2: Deploy agents from cache to ~/.claude/agents/
|
|
534
|
-
#
|
|
652
|
+
# Use reconciliation service to respect configuration.yaml settings
|
|
535
653
|
try:
|
|
536
|
-
# Initialize deployment service with profile-filtered configuration
|
|
537
|
-
from ..core.config import Config
|
|
538
|
-
|
|
539
|
-
deploy_config = None
|
|
540
|
-
if active_profile and profile_manager.active_profile:
|
|
541
|
-
# Create config with excluded agents based on profile
|
|
542
|
-
# Get all agents that should be excluded (not in enabled list)
|
|
543
|
-
from pathlib import Path
|
|
544
|
-
|
|
545
|
-
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
546
|
-
if cache_dir.exists():
|
|
547
|
-
# Find all agent files
|
|
548
|
-
# Supports both flat cache and {owner}/{repo}/agents/ structure
|
|
549
|
-
all_agent_files = [
|
|
550
|
-
f
|
|
551
|
-
for f in cache_dir.rglob("*.md")
|
|
552
|
-
if "/agents/" in str(f)
|
|
553
|
-
and f.stem.lower() != "base-agent"
|
|
554
|
-
and f.name.lower() not in {"readme.md", "changelog.md", "contributing.md"}
|
|
555
|
-
]
|
|
556
|
-
|
|
557
|
-
# Build exclusion list for agents not in profile
|
|
558
|
-
excluded_agents = []
|
|
559
|
-
for agent_file in all_agent_files:
|
|
560
|
-
agent_name = agent_file.stem
|
|
561
|
-
if not profile_manager.is_agent_enabled(agent_name):
|
|
562
|
-
excluded_agents.append(agent_name)
|
|
563
|
-
|
|
564
|
-
if excluded_agents:
|
|
565
|
-
# Get singleton config and update with profile settings
|
|
566
|
-
# BUGFIX: Config is a singleton that ignores dict parameter if already initialized.
|
|
567
|
-
# Creating Config({...}) doesn't store excluded_agents - use set() instead.
|
|
568
|
-
deploy_config = Config()
|
|
569
|
-
deploy_config.set("agent_deployment.excluded_agents", excluded_agents)
|
|
570
|
-
deploy_config.set("agent_deployment.filter_non_mpm_agents", False)
|
|
571
|
-
deploy_config.set("agent_deployment.case_sensitive", False)
|
|
572
|
-
deploy_config.set("agent_deployment.exclude_dependencies", False)
|
|
573
|
-
logger.info(
|
|
574
|
-
f"Profile '{active_profile}': Excluding {len(excluded_agents)} agents from deployment"
|
|
575
|
-
)
|
|
576
|
-
|
|
577
|
-
deployment_service = AgentDeploymentService(config=deploy_config)
|
|
578
|
-
|
|
579
|
-
# Count agents in cache to show accurate progress
|
|
580
654
|
from pathlib import Path
|
|
581
655
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
# BUGFIX (cache-count-inflation): Clean up stale cache files
|
|
587
|
-
# from old repositories before counting to prevent inflated counts.
|
|
588
|
-
# Issue: Old caches like bobmatnyc/claude-mpm-agents/agents/
|
|
589
|
-
# were counted alongside current agents, inflating count
|
|
590
|
-
# from 44 to 85.
|
|
591
|
-
#
|
|
592
|
-
# Solution: Remove files with nested /agents/ paths
|
|
593
|
-
# (e.g., cache/agents/user/repo/agents/...)
|
|
594
|
-
# Keep only current agents (e.g., cache/agents/engineer/...)
|
|
595
|
-
removed_count = 0
|
|
596
|
-
stale_dirs = set()
|
|
597
|
-
|
|
598
|
-
for md_file in cache_dir.rglob("*.md"):
|
|
599
|
-
# Stale cache files have multiple /agents/ in their path
|
|
600
|
-
# Current: ~/.claude-mpm/cache/agents/engineer/...
|
|
601
|
-
# (1 occurrence)
|
|
602
|
-
# Old: ~/.claude-mpm/cache/agents/bobmatnyc/.../agents/...
|
|
603
|
-
# (2+ occurrences)
|
|
604
|
-
if str(md_file).count("/agents/") > 1:
|
|
605
|
-
# Track parent directory for cleanup
|
|
606
|
-
# Extract subdirectory under cache/agents/
|
|
607
|
-
# (e.g., "bobmatnyc")
|
|
608
|
-
parts = md_file.parts
|
|
609
|
-
cache_agents_idx = parts.index("agents")
|
|
610
|
-
if cache_agents_idx + 1 < len(parts):
|
|
611
|
-
stale_subdir = parts[cache_agents_idx + 1]
|
|
612
|
-
# Only remove if it's not a known category directory
|
|
613
|
-
if stale_subdir not in [
|
|
614
|
-
"engineer",
|
|
615
|
-
"ops",
|
|
616
|
-
"qa",
|
|
617
|
-
"universal",
|
|
618
|
-
"documentation",
|
|
619
|
-
"claude-mpm",
|
|
620
|
-
"security",
|
|
621
|
-
]:
|
|
622
|
-
stale_dirs.add(cache_dir / stale_subdir)
|
|
623
|
-
|
|
624
|
-
md_file.unlink()
|
|
625
|
-
removed_count += 1
|
|
626
|
-
|
|
627
|
-
# Remove empty stale directories
|
|
628
|
-
for stale_dir in stale_dirs:
|
|
629
|
-
if stale_dir.exists() and stale_dir.is_dir():
|
|
630
|
-
try:
|
|
631
|
-
# Remove directory and all contents
|
|
632
|
-
import shutil
|
|
633
|
-
|
|
634
|
-
shutil.rmtree(stale_dir)
|
|
635
|
-
except Exception:
|
|
636
|
-
pass # Ignore cleanup errors
|
|
637
|
-
|
|
638
|
-
if removed_count > 0:
|
|
639
|
-
from loguru import logger
|
|
640
|
-
|
|
641
|
-
logger.info(
|
|
642
|
-
f"Cleaned up {removed_count} stale cache files "
|
|
643
|
-
f"from old repositories"
|
|
644
|
-
)
|
|
656
|
+
from ..core.unified_config import UnifiedConfig
|
|
657
|
+
from ..services.agents.deployment.startup_reconciliation import (
|
|
658
|
+
perform_startup_reconciliation,
|
|
659
|
+
)
|
|
645
660
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
# (
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
"
|
|
659
|
-
"response_format.md",
|
|
660
|
-
"ticket_completeness_examples.md",
|
|
661
|
-
"validation_templates.md",
|
|
662
|
-
"git_file_tracking.md",
|
|
663
|
-
}
|
|
664
|
-
# Documentation files to exclude (by filename)
|
|
665
|
-
doc_files = {
|
|
666
|
-
"readme.md",
|
|
667
|
-
"changelog.md",
|
|
668
|
-
"contributing.md",
|
|
669
|
-
"implementation-summary.md",
|
|
670
|
-
"reorganization-plan.md",
|
|
671
|
-
"auto-deploy-index.md",
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
# Find all markdown files (after cleanup)
|
|
675
|
-
all_md_files = list(cache_dir.rglob("*.md"))
|
|
676
|
-
|
|
677
|
-
# Filter to only agent files:
|
|
678
|
-
# 1. Must have "/agents/" in path (current structure supports
|
|
679
|
-
# both flat and {owner}/{repo}/agents/ patterns)
|
|
680
|
-
# 2. Must not be in PM templates or doc files
|
|
681
|
-
# 3. Exclude BASE-AGENT.md which is not a deployable agent
|
|
682
|
-
# 4. Exclude build artifacts (dist/, build/, .cache/)
|
|
683
|
-
# to prevent double-counting
|
|
684
|
-
agent_files = [
|
|
685
|
-
f
|
|
686
|
-
for f in all_md_files
|
|
687
|
-
if (
|
|
688
|
-
# Must be in an agent directory
|
|
689
|
-
# Supports: cache/agents/{category}/... (flat)
|
|
690
|
-
# Supports: cache/agents/{owner}/{repo}/agents/{category}/... (GitHub sync)
|
|
691
|
-
"/agents/" in str(f)
|
|
692
|
-
# Exclude PM templates, doc files, and BASE-AGENT
|
|
693
|
-
and f.name.lower() not in pm_templates
|
|
694
|
-
and f.name.lower() not in doc_files
|
|
695
|
-
and f.name.lower() != "base-agent.md"
|
|
696
|
-
# Exclude build artifacts (prevents double-counting
|
|
697
|
-
# source + built files)
|
|
698
|
-
and not any(
|
|
699
|
-
part in str(f).split("/")
|
|
700
|
-
for part in ["dist", "build", ".cache"]
|
|
701
|
-
)
|
|
702
|
-
)
|
|
703
|
-
]
|
|
704
|
-
agent_count = len(agent_files)
|
|
705
|
-
|
|
706
|
-
if agent_count > 0:
|
|
707
|
-
# Deploy agents to project-level directory where Claude Code expects them
|
|
708
|
-
deploy_target = Path.cwd() / ".claude" / "agents"
|
|
709
|
-
deployment_result = deployment_service.deploy_agents(
|
|
710
|
-
target_dir=deploy_target,
|
|
711
|
-
force_rebuild=False, # Only deploy if versions differ
|
|
712
|
-
deployment_mode="update", # Version-aware updates
|
|
713
|
-
config=deploy_config, # Pass config to respect profile filtering
|
|
661
|
+
# Load configuration
|
|
662
|
+
unified_config = UnifiedConfig()
|
|
663
|
+
|
|
664
|
+
# Override with profile settings if active
|
|
665
|
+
if active_profile and profile_manager.active_profile:
|
|
666
|
+
# Get enabled agents from profile (returns Set[str])
|
|
667
|
+
profile_enabled_agents = (
|
|
668
|
+
profile_manager.active_profile.get_enabled_agents()
|
|
669
|
+
)
|
|
670
|
+
# Update config with profile's enabled list (convert Set to List)
|
|
671
|
+
unified_config.agents.enabled = list(profile_enabled_agents)
|
|
672
|
+
logger.info(
|
|
673
|
+
f"Profile '{active_profile}': Using {len(profile_enabled_agents)} enabled agents"
|
|
714
674
|
)
|
|
715
675
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
agent_count_in_target = len(
|
|
729
|
-
[
|
|
730
|
-
f
|
|
731
|
-
for f in existing_agents
|
|
732
|
-
if not f.name.startswith(("README", "INSTRUCTIONS"))
|
|
733
|
-
]
|
|
734
|
-
)
|
|
735
|
-
if agent_count_in_target > 0:
|
|
736
|
-
# All agents already deployed - count them as skipped
|
|
737
|
-
skipped = agent_count_in_target
|
|
738
|
-
total_configured = agent_count_in_target
|
|
676
|
+
# Perform reconciliation to deploy configured agents
|
|
677
|
+
project_path = Path.cwd()
|
|
678
|
+
agent_result, _skill_result = perform_startup_reconciliation(
|
|
679
|
+
project_path=project_path, config=unified_config, silent=False
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
# Display results with progress bar
|
|
683
|
+
total_operations = (
|
|
684
|
+
len(agent_result.deployed)
|
|
685
|
+
+ len(agent_result.removed)
|
|
686
|
+
+ len(agent_result.unchanged)
|
|
687
|
+
)
|
|
739
688
|
|
|
740
|
-
|
|
689
|
+
if total_operations > 0:
|
|
741
690
|
deploy_progress = ProgressBar(
|
|
742
|
-
total=
|
|
691
|
+
total=total_operations,
|
|
743
692
|
prefix="Deploying agents",
|
|
744
693
|
show_percentage=True,
|
|
745
694
|
show_counter=True,
|
|
746
695
|
)
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
696
|
+
deploy_progress.update(total_operations)
|
|
697
|
+
|
|
698
|
+
# Build summary message
|
|
699
|
+
deployed = len(agent_result.deployed)
|
|
700
|
+
removed = len(agent_result.removed)
|
|
701
|
+
unchanged = len(agent_result.unchanged)
|
|
702
|
+
|
|
703
|
+
summary_parts = []
|
|
704
|
+
if deployed > 0:
|
|
705
|
+
summary_parts.append(f"{deployed} new")
|
|
706
|
+
if removed > 0:
|
|
707
|
+
summary_parts.append(f"{removed} removed")
|
|
708
|
+
if unchanged > 0:
|
|
709
|
+
summary_parts.append(f"{unchanged} unchanged")
|
|
710
|
+
|
|
711
|
+
summary = f"Complete: {', '.join(summary_parts)}"
|
|
712
|
+
deploy_progress.finish(summary)
|
|
713
|
+
|
|
714
|
+
# Display errors if any
|
|
715
|
+
if agent_result.errors:
|
|
716
|
+
logger.warning(
|
|
717
|
+
f"Agent deployment completed with {len(agent_result.errors)} errors"
|
|
751
718
|
)
|
|
719
|
+
print("\n⚠️ Agent Deployment Errors:")
|
|
720
|
+
max_errors_to_show = 10
|
|
721
|
+
errors_to_display = agent_result.errors[:max_errors_to_show]
|
|
752
722
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
deployed_filenames = []
|
|
756
|
-
for agent_name in deployment_result.get("deployed", []):
|
|
757
|
-
deployed_filenames.append(f"{agent_name}.md")
|
|
758
|
-
for agent_name in deployment_result.get("updated", []):
|
|
759
|
-
deployed_filenames.append(f"{agent_name}.md")
|
|
760
|
-
for agent_name in deployment_result.get("skipped", []):
|
|
761
|
-
deployed_filenames.append(f"{agent_name}.md")
|
|
762
|
-
|
|
763
|
-
# Run cleanup and get count of removed agents
|
|
764
|
-
removed = _cleanup_orphaned_agents(
|
|
765
|
-
deploy_target, deployed_filenames
|
|
766
|
-
)
|
|
723
|
+
for error in errors_to_display:
|
|
724
|
+
print(f" - {error}")
|
|
767
725
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
if removed > 0:
|
|
772
|
-
deploy_progress.finish(
|
|
773
|
-
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged, "
|
|
774
|
-
f"{removed} removed ({total_configured} configured from {agent_count} files in cache)"
|
|
775
|
-
)
|
|
776
|
-
else:
|
|
777
|
-
deploy_progress.finish(
|
|
778
|
-
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged "
|
|
779
|
-
f"({total_configured} configured from {agent_count} files in cache)"
|
|
780
|
-
)
|
|
781
|
-
elif removed > 0:
|
|
782
|
-
deploy_progress.finish(
|
|
783
|
-
f"Complete: {total_configured} agents deployed, "
|
|
784
|
-
f"{removed} removed ({agent_count} files in cache)"
|
|
785
|
-
)
|
|
786
|
-
else:
|
|
787
|
-
deploy_progress.finish(
|
|
788
|
-
f"Complete: {total_configured} agents deployed "
|
|
789
|
-
f"({agent_count} files in cache)"
|
|
790
|
-
)
|
|
726
|
+
if len(agent_result.errors) > max_errors_to_show:
|
|
727
|
+
remaining = len(agent_result.errors) - max_errors_to_show
|
|
728
|
+
print(f" ... and {remaining} more error(s)")
|
|
791
729
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
f"Agent deployment completed with {len(deploy_errors)} errors: {deploy_errors}"
|
|
798
|
-
)
|
|
799
|
-
|
|
800
|
-
# Display errors to user with clear formatting
|
|
801
|
-
print("\n⚠️ Agent Deployment Errors:")
|
|
802
|
-
|
|
803
|
-
# Show first 10 errors to avoid overwhelming output
|
|
804
|
-
max_errors_to_show = 10
|
|
805
|
-
errors_to_display = deploy_errors[:max_errors_to_show]
|
|
806
|
-
|
|
807
|
-
for error in errors_to_display:
|
|
808
|
-
# Format error message for readability
|
|
809
|
-
# Errors typically come as strings like "agent.md: Error message"
|
|
810
|
-
print(f" - {error}")
|
|
811
|
-
|
|
812
|
-
# If more errors exist, show count
|
|
813
|
-
if len(deploy_errors) > max_errors_to_show:
|
|
814
|
-
remaining = len(deploy_errors) - max_errors_to_show
|
|
815
|
-
print(f" ... and {remaining} more error(s)")
|
|
730
|
+
print(
|
|
731
|
+
f"\n❌ Failed to deploy {len(agent_result.errors)} agent(s). "
|
|
732
|
+
"Please check the error messages above."
|
|
733
|
+
)
|
|
734
|
+
print(" Run with --verbose for detailed error information.\n")
|
|
816
735
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
736
|
+
# Save deployment state to prevent duplicate deployment in ClaudeRunner
|
|
737
|
+
# This ensures setup_agents() skips deployment since we already reconciled
|
|
738
|
+
_save_deployment_state_after_reconciliation(
|
|
739
|
+
agent_result=agent_result, project_path=project_path
|
|
740
|
+
)
|
|
822
741
|
|
|
823
742
|
except Exception as e:
|
|
824
743
|
# Deployment failure shouldn't block startup
|
|
@@ -827,6 +746,11 @@ def sync_remote_agents_on_startup():
|
|
|
827
746
|
logger = get_logger("cli")
|
|
828
747
|
logger.warning(f"Failed to deploy agents from cache: {e}")
|
|
829
748
|
|
|
749
|
+
# Phase 4: Cleanup legacy agent cache directories (after sync/deployment)
|
|
750
|
+
# CRITICAL: This must run AFTER sync completes because sync may recreate
|
|
751
|
+
# legacy directories. Running cleanup here ensures they're removed.
|
|
752
|
+
cleanup_legacy_agent_cache()
|
|
753
|
+
|
|
830
754
|
except Exception as e:
|
|
831
755
|
# Non-critical - log but don't fail startup
|
|
832
756
|
from ..core.logger import get_logger
|
|
@@ -835,8 +759,14 @@ def sync_remote_agents_on_startup():
|
|
|
835
759
|
logger.debug(f"Failed to sync remote agents: {e}")
|
|
836
760
|
# Continue execution - agent sync failure shouldn't block startup
|
|
837
761
|
|
|
762
|
+
# Cleanup legacy cache even if sync failed
|
|
763
|
+
try:
|
|
764
|
+
cleanup_legacy_agent_cache()
|
|
765
|
+
except Exception: # nosec B110
|
|
766
|
+
pass # Ignore cleanup errors
|
|
767
|
+
|
|
838
768
|
|
|
839
|
-
def sync_remote_skills_on_startup():
|
|
769
|
+
def sync_remote_skills_on_startup(force_sync: bool = False):
|
|
840
770
|
"""
|
|
841
771
|
Synchronize skill templates from remote sources on startup.
|
|
842
772
|
|
|
@@ -854,6 +784,9 @@ def sync_remote_skills_on_startup():
|
|
|
854
784
|
4. Apply profile filtering if active
|
|
855
785
|
5. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
|
|
856
786
|
6. Log deployment results with source indication
|
|
787
|
+
|
|
788
|
+
Args:
|
|
789
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
857
790
|
"""
|
|
858
791
|
try:
|
|
859
792
|
from pathlib import Path
|
|
@@ -959,7 +892,7 @@ def sync_remote_skills_on_startup():
|
|
|
959
892
|
|
|
960
893
|
# Sync all sources with progress callback
|
|
961
894
|
results = manager.sync_all_sources(
|
|
962
|
-
force=
|
|
895
|
+
force=force_sync, progress_callback=sync_progress.update
|
|
963
896
|
)
|
|
964
897
|
|
|
965
898
|
# Finish sync progress bar with clear breakdown
|
|
@@ -979,150 +912,191 @@ def sync_remote_skills_on_startup():
|
|
|
979
912
|
|
|
980
913
|
# Phase 2: Scan agents and save to configuration.yaml
|
|
981
914
|
# This step populates configuration.yaml with agent-referenced skills
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
# Phase 3: Resolve which skills to deploy (user_defined or agent_referenced)
|
|
993
|
-
skills_to_deploy, skill_source = get_skills_to_deploy(project_config_path)
|
|
994
|
-
|
|
995
|
-
# Phase 4: Apply profile filtering if active
|
|
996
|
-
if active_profile and profile_manager.active_profile:
|
|
997
|
-
# Filter skills based on profile
|
|
998
|
-
if skills_to_deploy:
|
|
999
|
-
# Filter the resolved skill list
|
|
1000
|
-
original_count = len(skills_to_deploy)
|
|
1001
|
-
filtered_skills = [
|
|
1002
|
-
skill
|
|
1003
|
-
for skill in skills_to_deploy
|
|
1004
|
-
if profile_manager.is_skill_enabled(skill)
|
|
1005
|
-
]
|
|
1006
|
-
filtered_count = original_count - len(filtered_skills)
|
|
915
|
+
# CRITICAL: Always scan agents to populate agent_referenced, even when using cached skills.
|
|
916
|
+
# Without this, skill_filter=None causes ALL skills to deploy and NO cleanup to run.
|
|
917
|
+
agents_dir = Path.cwd() / ".claude" / "agents"
|
|
918
|
+
|
|
919
|
+
# Scan agents for skill requirements (ALWAYS run to ensure cleanup works)
|
|
920
|
+
agent_skills = get_required_skills_from_agents(agents_dir)
|
|
921
|
+
logger.info(
|
|
922
|
+
f"Agent scan found {len(agent_skills)} unique skills across deployed agents"
|
|
923
|
+
)
|
|
1007
924
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
elif filtered_count > 0:
|
|
1015
|
-
logger.info(
|
|
1016
|
-
f"Profile '{active_profile}' filtered {filtered_count} skills "
|
|
1017
|
-
f"({len(filtered_skills)} remaining)"
|
|
1018
|
-
)
|
|
925
|
+
# Save to project-level configuration.yaml
|
|
926
|
+
project_config_path = Path.cwd() / ".claude-mpm" / "configuration.yaml"
|
|
927
|
+
save_agent_skills_to_config(list(agent_skills), project_config_path)
|
|
928
|
+
logger.debug(
|
|
929
|
+
f"Saved {len(agent_skills)} agent-referenced skills to {project_config_path}"
|
|
930
|
+
)
|
|
1019
931
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
932
|
+
# Phase 3: Resolve which skills to deploy (user_defined or agent_referenced)
|
|
933
|
+
skills_to_deploy, skill_source = get_skills_to_deploy(project_config_path)
|
|
934
|
+
|
|
935
|
+
# CRITICAL DEBUG: Log deployment resolution to diagnose cleanup issues
|
|
936
|
+
if skills_to_deploy:
|
|
937
|
+
logger.info(
|
|
938
|
+
f"Resolved {len(skills_to_deploy)} skills from {skill_source} (cleanup will run)"
|
|
939
|
+
)
|
|
940
|
+
else:
|
|
941
|
+
logger.warning(
|
|
942
|
+
f"No skills resolved from {skill_source} - will deploy ALL skills WITHOUT cleanup! "
|
|
943
|
+
f"This may indicate agent_referenced is empty in configuration.yaml."
|
|
944
|
+
)
|
|
945
|
+
|
|
946
|
+
# Phase 4: Apply profile filtering if active
|
|
947
|
+
if active_profile and profile_manager.active_profile:
|
|
948
|
+
# Filter skills based on profile
|
|
949
|
+
if skills_to_deploy:
|
|
950
|
+
# Filter the resolved skill list
|
|
951
|
+
original_count = len(skills_to_deploy)
|
|
952
|
+
filtered_skills = [
|
|
953
|
+
skill
|
|
954
|
+
for skill in skills_to_deploy
|
|
955
|
+
if profile_manager.is_skill_enabled(skill)
|
|
956
|
+
]
|
|
957
|
+
filtered_count = original_count - len(filtered_skills)
|
|
958
|
+
|
|
959
|
+
# SAFEGUARD: Warn if all skills were filtered out (misconfiguration)
|
|
960
|
+
if not filtered_skills and original_count > 0:
|
|
961
|
+
logger.warning(
|
|
962
|
+
f"Profile '{active_profile}' filtered ALL {original_count} skills. "
|
|
963
|
+
f"This may indicate a naming mismatch in the profile."
|
|
964
|
+
)
|
|
965
|
+
elif filtered_count > 0:
|
|
1032
966
|
logger.info(
|
|
1033
|
-
f"Profile '{active_profile}'
|
|
1034
|
-
f"{len(filtered_skills)}
|
|
967
|
+
f"Profile '{active_profile}' filtered {filtered_count} skills "
|
|
968
|
+
f"({len(filtered_skills)} remaining)"
|
|
1035
969
|
)
|
|
1036
970
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
971
|
+
skills_to_deploy = filtered_skills
|
|
972
|
+
skill_source = f"{skill_source} + profile filtered"
|
|
973
|
+
else:
|
|
974
|
+
# No explicit skill list - filter from all available
|
|
975
|
+
all_skills = manager.get_all_skills()
|
|
976
|
+
filtered_skills = [
|
|
977
|
+
skill["name"]
|
|
978
|
+
for skill in all_skills
|
|
979
|
+
if profile_manager.is_skill_enabled(skill["name"])
|
|
980
|
+
]
|
|
981
|
+
skills_to_deploy = filtered_skills
|
|
982
|
+
skill_source = "profile filtered"
|
|
983
|
+
logger.info(
|
|
984
|
+
f"Profile '{active_profile}': "
|
|
985
|
+
f"{len(filtered_skills)} skills enabled from {len(all_skills)} available"
|
|
986
|
+
)
|
|
1040
987
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
988
|
+
# Get all skills to determine counts
|
|
989
|
+
all_skills = manager.get_all_skills()
|
|
990
|
+
total_skill_count = len(all_skills)
|
|
991
|
+
|
|
992
|
+
# Determine skill count based on resolution
|
|
993
|
+
skill_count = len(skills_to_deploy) if skills_to_deploy else total_skill_count
|
|
994
|
+
|
|
995
|
+
if skill_count > 0:
|
|
996
|
+
# Deploy skills with resolved filter
|
|
997
|
+
# Deploy ONLY to project directory (not user-level)
|
|
998
|
+
# DESIGN DECISION: Project-level deployment keeps skills isolated per project,
|
|
999
|
+
# avoiding pollution of user's global ~/.claude/skills/ directory.
|
|
1000
|
+
|
|
1001
|
+
# Deploy to project-local directory with cleanup
|
|
1002
|
+
deployment_result = manager.deploy_skills(
|
|
1003
|
+
target_dir=Path.cwd() / ".claude" / "skills",
|
|
1004
|
+
force=force_sync,
|
|
1005
|
+
# CRITICAL FIX: Empty list should mean "deploy no skills", not "deploy all"
|
|
1006
|
+
# When skills_to_deploy is [], we want skill_filter=set() NOT skill_filter=None
|
|
1007
|
+
# None means "no filtering" (deploy all), empty set means "filter to nothing"
|
|
1008
|
+
skill_filter=set(skills_to_deploy)
|
|
1009
|
+
if skills_to_deploy is not None
|
|
1010
|
+
else None,
|
|
1044
1011
|
)
|
|
1045
1012
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1013
|
+
# REMOVED: User-level deployment (lines 1068-1074)
|
|
1014
|
+
# Reason: Skills should be project-specific, not user-global.
|
|
1015
|
+
# Claude Code can read from project-level .claude/skills/ directory.
|
|
1016
|
+
|
|
1017
|
+
# Get actual counts from deployment result (use project-local for display)
|
|
1018
|
+
deployed = deployment_result.get("deployed_count", 0)
|
|
1019
|
+
skipped = deployment_result.get("skipped_count", 0)
|
|
1020
|
+
filtered = deployment_result.get("filtered_count", 0)
|
|
1021
|
+
removed = deployment_result.get("removed_count", 0)
|
|
1022
|
+
total_available = deployed + skipped
|
|
1023
|
+
|
|
1024
|
+
# Only show progress bar if there are skills to deploy
|
|
1025
|
+
if total_available > 0:
|
|
1026
|
+
deploy_progress = ProgressBar(
|
|
1027
|
+
total=total_available,
|
|
1028
|
+
prefix="Deploying skill directories",
|
|
1029
|
+
show_percentage=True,
|
|
1030
|
+
show_counter=True,
|
|
1053
1031
|
)
|
|
1032
|
+
# Update progress bar to completion
|
|
1033
|
+
deploy_progress.update(total_available)
|
|
1034
|
+
else:
|
|
1035
|
+
# No skills to deploy - create dummy progress for message only
|
|
1036
|
+
deploy_progress = ProgressBar(
|
|
1037
|
+
total=1,
|
|
1038
|
+
prefix="Deploying skill directories",
|
|
1039
|
+
show_percentage=False,
|
|
1040
|
+
show_counter=False,
|
|
1041
|
+
)
|
|
1042
|
+
deploy_progress.update(1)
|
|
1054
1043
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1044
|
+
# Show total available skills (deployed + already existing)
|
|
1045
|
+
# Include source indication (user_defined vs agent_referenced)
|
|
1046
|
+
# Note: total_skill_count is from cache, total_available is what's deployed/needed
|
|
1047
|
+
source_label = (
|
|
1048
|
+
"user override" if skill_source == "user_defined" else "from agents"
|
|
1049
|
+
)
|
|
1060
1050
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
deploy_progress.update(total_available)
|
|
1071
|
-
else:
|
|
1072
|
-
# No skills to deploy - create dummy progress for message only
|
|
1073
|
-
deploy_progress = ProgressBar(
|
|
1074
|
-
total=1,
|
|
1075
|
-
prefix="Deploying skill directories",
|
|
1076
|
-
show_percentage=False,
|
|
1077
|
-
show_counter=False,
|
|
1078
|
-
)
|
|
1079
|
-
deploy_progress.update(1)
|
|
1051
|
+
# Build finish message with cleanup info
|
|
1052
|
+
if deployed > 0 or removed > 0:
|
|
1053
|
+
parts = []
|
|
1054
|
+
if deployed > 0:
|
|
1055
|
+
parts.append(f"{deployed} new")
|
|
1056
|
+
if skipped > 0:
|
|
1057
|
+
parts.append(f"{skipped} unchanged")
|
|
1058
|
+
if removed > 0:
|
|
1059
|
+
parts.append(f"{removed} removed")
|
|
1080
1060
|
|
|
1081
|
-
|
|
1082
|
-
# Include source indication (user_defined vs agent_referenced)
|
|
1083
|
-
# Note: total_skill_count is from cache, total_available is what's deployed/needed
|
|
1084
|
-
source_label = (
|
|
1085
|
-
"user override" if skill_source == "user_defined" else "from agents"
|
|
1086
|
-
)
|
|
1061
|
+
status = ", ".join(parts)
|
|
1087
1062
|
|
|
1088
|
-
if
|
|
1089
|
-
if filtered > 0:
|
|
1090
|
-
deploy_progress.finish(
|
|
1091
|
-
f"Complete: {deployed} new, {skipped} unchanged "
|
|
1092
|
-
f"({total_available} {source_label}, {filtered} files in cache)"
|
|
1093
|
-
)
|
|
1094
|
-
else:
|
|
1095
|
-
deploy_progress.finish(
|
|
1096
|
-
f"Complete: {deployed} new, {skipped} unchanged "
|
|
1097
|
-
f"({total_available} skills {source_label} from {total_skill_count} files in cache)"
|
|
1098
|
-
)
|
|
1099
|
-
elif filtered > 0:
|
|
1100
|
-
# Skills filtered means agents require fewer skills than available
|
|
1063
|
+
if filtered > 0:
|
|
1101
1064
|
deploy_progress.finish(
|
|
1102
|
-
f"
|
|
1065
|
+
f"Complete: {status} ({total_available} {source_label}, {filtered} files in cache)"
|
|
1103
1066
|
)
|
|
1104
1067
|
else:
|
|
1105
1068
|
deploy_progress.finish(
|
|
1106
|
-
f"Complete: {total_available} skills {source_label} "
|
|
1107
|
-
f"({total_skill_count} files in cache)"
|
|
1069
|
+
f"Complete: {status} ({total_available} skills {source_label} from {total_skill_count} files in cache)"
|
|
1108
1070
|
)
|
|
1071
|
+
elif filtered > 0:
|
|
1072
|
+
# Skills filtered means agents require fewer skills than available
|
|
1073
|
+
deploy_progress.finish(
|
|
1074
|
+
f"No skills needed ({source_label}, {total_skill_count} files in cache)"
|
|
1075
|
+
)
|
|
1076
|
+
else:
|
|
1077
|
+
# No changes - all skills already deployed
|
|
1078
|
+
msg = f"Complete: {total_available} skills {source_label}"
|
|
1079
|
+
if removed > 0:
|
|
1080
|
+
msg += f", {removed} removed"
|
|
1081
|
+
msg += f" ({total_skill_count} files in cache)"
|
|
1082
|
+
deploy_progress.finish(msg)
|
|
1083
|
+
|
|
1084
|
+
# Log deployment errors if any
|
|
1085
|
+
from ..core.logger import get_logger
|
|
1109
1086
|
|
|
1110
|
-
|
|
1111
|
-
from ..core.logger import get_logger
|
|
1112
|
-
|
|
1113
|
-
logger = get_logger("cli")
|
|
1087
|
+
logger = get_logger("cli")
|
|
1114
1088
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1089
|
+
errors = deployment_result.get("errors", [])
|
|
1090
|
+
if errors:
|
|
1091
|
+
logger.warning(
|
|
1092
|
+
f"Skill deployment completed with {len(errors)} errors: {errors}"
|
|
1093
|
+
)
|
|
1120
1094
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1095
|
+
# Log sync errors if any
|
|
1096
|
+
if results["failed_count"] > 0:
|
|
1097
|
+
logger.warning(
|
|
1098
|
+
f"Skill sync completed with {results['failed_count']} failures"
|
|
1099
|
+
)
|
|
1126
1100
|
|
|
1127
1101
|
except Exception as e:
|
|
1128
1102
|
# Non-critical - log but don't fail startup
|
|
@@ -1222,61 +1196,64 @@ def show_skill_summary():
|
|
|
1222
1196
|
Display skill availability summary on startup.
|
|
1223
1197
|
|
|
1224
1198
|
WHY: Users should see at a glance how many skills are deployed and available
|
|
1225
|
-
from
|
|
1199
|
+
from cache, similar to the agent summary showing "X deployed / Y cached".
|
|
1226
1200
|
|
|
1227
|
-
DESIGN DECISION: Fast, non-blocking check that counts skills from
|
|
1228
|
-
|
|
1201
|
+
DESIGN DECISION: Fast, non-blocking check that counts skills from:
|
|
1202
|
+
- Deployed skills: PROJECT-level .claude/skills/ directory
|
|
1203
|
+
- Cached skills: ~/.claude-mpm/cache/skills/ directory (from remote sources)
|
|
1204
|
+
|
|
1205
|
+
Shows format: "✓ Skills: X deployed / Y cached"
|
|
1229
1206
|
Failures are silent to avoid blocking startup.
|
|
1230
1207
|
"""
|
|
1231
1208
|
try:
|
|
1232
1209
|
from pathlib import Path
|
|
1233
1210
|
|
|
1234
|
-
# Count deployed skills (
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
if
|
|
1211
|
+
# Count deployed skills (PROJECT-level, not user-level)
|
|
1212
|
+
project_skills_dir = Path.cwd() / ".claude" / "skills"
|
|
1213
|
+
deployed_count = 0
|
|
1214
|
+
if project_skills_dir.exists():
|
|
1238
1215
|
# Count directories with SKILL.md (excludes collection repos)
|
|
1239
1216
|
# Exclude collection directories (obra-superpowers, etc.)
|
|
1240
1217
|
skill_dirs = [
|
|
1241
1218
|
d
|
|
1242
|
-
for d in
|
|
1219
|
+
for d in project_skills_dir.iterdir()
|
|
1243
1220
|
if d.is_dir()
|
|
1244
1221
|
and (d / "SKILL.md").exists()
|
|
1245
1222
|
and not (d / ".git").exists() # Exclude collection repos
|
|
1246
1223
|
]
|
|
1247
|
-
|
|
1224
|
+
deployed_count = len(skill_dirs)
|
|
1248
1225
|
|
|
1249
|
-
# Count
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
):
|
|
1226
|
+
# Count cached skills (from remote sources, not deployed yet)
|
|
1227
|
+
# This matches the agent summary pattern: deployed vs cached
|
|
1228
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "skills"
|
|
1229
|
+
cached_count = 0
|
|
1230
|
+
if cache_dir.exists():
|
|
1231
|
+
# Scan all repository directories in cache
|
|
1232
|
+
# Cache structure: ~/.claude-mpm/cache/skills/{owner}/{repo}/...
|
|
1233
|
+
for repo_dir in cache_dir.rglob("*"):
|
|
1234
|
+
if not repo_dir.is_dir():
|
|
1258
1235
|
continue
|
|
1259
1236
|
|
|
1260
|
-
# Count skill directories
|
|
1237
|
+
# Count skill directories (those with SKILL.md)
|
|
1261
1238
|
# Skills can be nested in: skills/category/skill-name/SKILL.md
|
|
1262
1239
|
# or in flat structure: skill-name/SKILL.md
|
|
1263
|
-
for root, dirs, files in os.walk(
|
|
1240
|
+
for root, dirs, files in os.walk(repo_dir):
|
|
1264
1241
|
if "SKILL.md" in files:
|
|
1265
|
-
# Exclude build artifacts and hidden directories
|
|
1266
|
-
# Get relative path from collection_dir to avoid excluding based on .claude parent
|
|
1242
|
+
# Exclude build artifacts and hidden directories
|
|
1267
1243
|
root_path = Path(root)
|
|
1268
|
-
relative_parts = root_path.relative_to(collection_dir).parts
|
|
1269
1244
|
if not any(
|
|
1270
1245
|
part.startswith(".")
|
|
1271
1246
|
or part in ["dist", "build", "__pycache__"]
|
|
1272
|
-
for part in
|
|
1247
|
+
for part in root_path.parts
|
|
1273
1248
|
):
|
|
1274
|
-
|
|
1249
|
+
cached_count += 1
|
|
1275
1250
|
|
|
1276
|
-
# Display summary
|
|
1277
|
-
|
|
1251
|
+
# Display summary using agent summary format: "X deployed / Y cached"
|
|
1252
|
+
# Only show non-deployed cached skills (subtract deployed from cached)
|
|
1253
|
+
non_deployed_cached = max(0, cached_count - deployed_count)
|
|
1254
|
+
if deployed_count > 0 or non_deployed_cached > 0:
|
|
1278
1255
|
print(
|
|
1279
|
-
f"✓ Skills: {
|
|
1256
|
+
f"✓ Skills: {deployed_count} deployed / {non_deployed_cached} cached",
|
|
1280
1257
|
flush=True,
|
|
1281
1258
|
)
|
|
1282
1259
|
|
|
@@ -1289,33 +1266,74 @@ def show_skill_summary():
|
|
|
1289
1266
|
|
|
1290
1267
|
|
|
1291
1268
|
def verify_and_show_pm_skills():
|
|
1292
|
-
"""Verify PM skills and display status.
|
|
1269
|
+
"""Verify PM skills and display status with enhanced validation.
|
|
1293
1270
|
|
|
1294
|
-
WHY: PM skills are
|
|
1295
|
-
|
|
1271
|
+
WHY: PM skills are CRITICAL for PM agent operation. PM must KNOW if
|
|
1272
|
+
framework knowledge is unavailable at startup. Enhanced validation
|
|
1273
|
+
checks all required skills exist, are not corrupted, and auto-repairs
|
|
1274
|
+
if needed.
|
|
1275
|
+
|
|
1276
|
+
Shows deployment status:
|
|
1277
|
+
- "✓ PM skills: 8/8 verified" if all required skills are valid
|
|
1278
|
+
- "⚠ PM skills: 2 missing, auto-repairing..." if issues detected
|
|
1279
|
+
- Non-blocking but visible warning if auto-repair fails
|
|
1296
1280
|
"""
|
|
1297
1281
|
try:
|
|
1298
1282
|
from pathlib import Path
|
|
1299
1283
|
|
|
1300
|
-
from ..services.pm_skills_deployer import
|
|
1284
|
+
from ..services.pm_skills_deployer import (
|
|
1285
|
+
REQUIRED_PM_SKILLS,
|
|
1286
|
+
PMSkillsDeployerService,
|
|
1287
|
+
)
|
|
1301
1288
|
|
|
1302
1289
|
deployer = PMSkillsDeployerService()
|
|
1303
1290
|
project_dir = Path.cwd()
|
|
1304
1291
|
|
|
1305
|
-
|
|
1292
|
+
# Verify with auto-repair enabled
|
|
1293
|
+
result = deployer.verify_pm_skills(project_dir, auto_repair=True)
|
|
1306
1294
|
|
|
1307
1295
|
if result.verified:
|
|
1308
|
-
# Show verified status
|
|
1309
|
-
|
|
1296
|
+
# Show verified status with count
|
|
1297
|
+
total_required = len(REQUIRED_PM_SKILLS)
|
|
1298
|
+
if sys.stdout.isatty():
|
|
1299
|
+
print(
|
|
1300
|
+
f"✓ PM skills: {total_required}/{total_required} verified",
|
|
1301
|
+
flush=True,
|
|
1302
|
+
)
|
|
1310
1303
|
else:
|
|
1311
|
-
#
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1304
|
+
# Show warning with details
|
|
1305
|
+
missing_count = len(result.missing_skills)
|
|
1306
|
+
corrupted_count = len(result.corrupted_skills)
|
|
1307
|
+
|
|
1308
|
+
# Build status message
|
|
1309
|
+
issues = []
|
|
1310
|
+
if missing_count > 0:
|
|
1311
|
+
issues.append(f"{missing_count} missing")
|
|
1312
|
+
if corrupted_count > 0:
|
|
1313
|
+
issues.append(f"{corrupted_count} corrupted")
|
|
1314
|
+
|
|
1315
|
+
status = ", ".join(issues)
|
|
1316
|
+
|
|
1317
|
+
# Check if auto-repair was attempted
|
|
1318
|
+
if "Auto-repaired" in result.message:
|
|
1319
|
+
# Auto-repair succeeded
|
|
1320
|
+
total_required = len(REQUIRED_PM_SKILLS)
|
|
1321
|
+
if sys.stdout.isatty():
|
|
1322
|
+
print(
|
|
1323
|
+
f"✓ PM skills: {total_required}/{total_required} verified (auto-repaired)",
|
|
1324
|
+
flush=True,
|
|
1325
|
+
)
|
|
1317
1326
|
else:
|
|
1318
|
-
|
|
1327
|
+
# Auto-repair failed or not attempted
|
|
1328
|
+
if sys.stdout.isatty():
|
|
1329
|
+
print(f"⚠ PM skills: {status}", flush=True)
|
|
1330
|
+
|
|
1331
|
+
# Log warnings for debugging
|
|
1332
|
+
from ..core.logger import get_logger
|
|
1333
|
+
|
|
1334
|
+
logger = get_logger("cli")
|
|
1335
|
+
for warning in result.warnings:
|
|
1336
|
+
logger.warning(f"PM skills: {warning}")
|
|
1319
1337
|
|
|
1320
1338
|
except ImportError:
|
|
1321
1339
|
# PM skills deployer not available - skip silently
|
|
@@ -1350,7 +1368,7 @@ def auto_install_chrome_devtools_on_startup():
|
|
|
1350
1368
|
if not chrome_devtools_config.get("auto_install", True):
|
|
1351
1369
|
# Auto-install disabled, skip silently
|
|
1352
1370
|
return
|
|
1353
|
-
except Exception:
|
|
1371
|
+
except Exception: # nosec B110
|
|
1354
1372
|
# If config loading fails, assume auto-install is enabled (default)
|
|
1355
1373
|
pass
|
|
1356
1374
|
|
|
@@ -1368,7 +1386,7 @@ def auto_install_chrome_devtools_on_startup():
|
|
|
1368
1386
|
# Continue execution - chrome-devtools installation failure shouldn't block startup
|
|
1369
1387
|
|
|
1370
1388
|
|
|
1371
|
-
def run_background_services():
|
|
1389
|
+
def run_background_services(force_sync: bool = False):
|
|
1372
1390
|
"""
|
|
1373
1391
|
Initialize all background services on startup.
|
|
1374
1392
|
|
|
@@ -1379,6 +1397,9 @@ def run_background_services():
|
|
|
1379
1397
|
explicitly requests them via agent-manager commands. This prevents unwanted
|
|
1380
1398
|
file creation in project .claude/ directories.
|
|
1381
1399
|
See: SystemInstructionsDeployer and agent_deployment.py line 504-509
|
|
1400
|
+
|
|
1401
|
+
Args:
|
|
1402
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
1382
1403
|
"""
|
|
1383
1404
|
# Sync hooks early to ensure up-to-date configuration
|
|
1384
1405
|
# RATIONALE: Hooks should be synced before other services to fix stale configs
|
|
@@ -1389,7 +1410,9 @@ def run_background_services():
|
|
|
1389
1410
|
check_mcp_auto_configuration()
|
|
1390
1411
|
verify_mcp_gateway_startup()
|
|
1391
1412
|
check_for_updates_async()
|
|
1392
|
-
sync_remote_agents_on_startup(
|
|
1413
|
+
sync_remote_agents_on_startup(
|
|
1414
|
+
force_sync=force_sync
|
|
1415
|
+
) # Sync agents from remote sources
|
|
1393
1416
|
show_agent_summary() # Display agent counts after deployment
|
|
1394
1417
|
|
|
1395
1418
|
# Skills deployment order (precedence: remote > bundled)
|
|
@@ -1398,7 +1421,9 @@ def run_background_services():
|
|
|
1398
1421
|
# 3. Discover and link runtime skills (user-added skills)
|
|
1399
1422
|
# This ensures remote skills take precedence over bundled skills when names conflict
|
|
1400
1423
|
deploy_bundled_skills() # Base layer: package-bundled skills
|
|
1401
|
-
sync_remote_skills_on_startup(
|
|
1424
|
+
sync_remote_skills_on_startup(
|
|
1425
|
+
force_sync=force_sync
|
|
1426
|
+
) # Override layer: Git-based skills (takes precedence)
|
|
1402
1427
|
discover_and_link_runtime_skills() # Discovery: user-added skills
|
|
1403
1428
|
show_skill_summary() # Display skill counts after deployment
|
|
1404
1429
|
verify_and_show_pm_skills() # PM skills verification and status
|
|
@@ -1500,7 +1525,9 @@ def check_mcp_auto_configuration():
|
|
|
1500
1525
|
from ..services.mcp_gateway.auto_configure import check_and_configure_mcp
|
|
1501
1526
|
|
|
1502
1527
|
# Show progress feedback - this operation can take 10+ seconds
|
|
1503
|
-
|
|
1528
|
+
# Only show progress message in TTY mode to avoid interfering with Claude Code's status display
|
|
1529
|
+
if sys.stdout.isatty():
|
|
1530
|
+
print("Checking MCP configuration...", end="", flush=True)
|
|
1504
1531
|
|
|
1505
1532
|
# This function handles all the logic:
|
|
1506
1533
|
# - Checks if already configured
|
|
@@ -1511,11 +1538,17 @@ def check_mcp_auto_configuration():
|
|
|
1511
1538
|
check_and_configure_mcp()
|
|
1512
1539
|
|
|
1513
1540
|
# Clear the "Checking..." message by overwriting with spaces
|
|
1514
|
-
|
|
1541
|
+
# Only use carriage return clearing if stdout is a real TTY
|
|
1542
|
+
if sys.stdout.isatty():
|
|
1543
|
+
print("\r" + " " * 30 + "\r", end="", flush=True)
|
|
1544
|
+
# In non-TTY mode, don't print anything - the "Checking..." message will just remain on its line
|
|
1515
1545
|
|
|
1516
1546
|
except Exception as e:
|
|
1517
1547
|
# Clear progress message on error
|
|
1518
|
-
|
|
1548
|
+
# Only use carriage return clearing if stdout is a real TTY
|
|
1549
|
+
if sys.stdout.isatty():
|
|
1550
|
+
print("\r" + " " * 30 + "\r", end="", flush=True)
|
|
1551
|
+
# In non-TTY mode, don't print anything - the "Checking..." message will just remain on its line
|
|
1519
1552
|
|
|
1520
1553
|
# Non-critical - log but don't fail
|
|
1521
1554
|
from ..core.logger import get_logger
|
|
@@ -1598,7 +1631,7 @@ def verify_mcp_gateway_startup():
|
|
|
1598
1631
|
loop.run_until_complete(
|
|
1599
1632
|
asyncio.gather(*pending, return_exceptions=True)
|
|
1600
1633
|
)
|
|
1601
|
-
except Exception:
|
|
1634
|
+
except Exception: # nosec B110
|
|
1602
1635
|
pass # Ignore cleanup errors
|
|
1603
1636
|
finally:
|
|
1604
1637
|
loop.close()
|
|
@@ -1692,7 +1725,7 @@ def check_for_updates_async():
|
|
|
1692
1725
|
|
|
1693
1726
|
logger = get_logger("upgrade_check")
|
|
1694
1727
|
logger.debug(f"Update check failed (non-critical): {e}")
|
|
1695
|
-
except Exception:
|
|
1728
|
+
except Exception: # nosec B110
|
|
1696
1729
|
pass # Avoid any errors in error handling
|
|
1697
1730
|
finally:
|
|
1698
1731
|
# Properly clean up event loop
|
|
@@ -1707,7 +1740,7 @@ def check_for_updates_async():
|
|
|
1707
1740
|
loop.run_until_complete(
|
|
1708
1741
|
asyncio.gather(*pending, return_exceptions=True)
|
|
1709
1742
|
)
|
|
1710
|
-
except Exception:
|
|
1743
|
+
except Exception: # nosec B110
|
|
1711
1744
|
pass # Ignore cleanup errors
|
|
1712
1745
|
finally:
|
|
1713
1746
|
loop.close()
|