claude-mpm 5.4.22__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/BASE_AGENT.md +164 -0
- claude_mpm/agents/BASE_ENGINEER.md +658 -0
- 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/MEMORY.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +374 -1257
- claude_mpm/agents/WORKFLOW.md +6 -253
- claude_mpm/agents/agent_loader.py +1 -1
- claude_mpm/agents/base_agent.json +31 -0
- claude_mpm/agents/frontmatter_validator.py +2 -2
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/__init__.py +5 -1
- claude_mpm/cli/commands/agent_state_manager.py +10 -10
- claude_mpm/cli/commands/agents.py +11 -13
- claude_mpm/cli/commands/agents_reconcile.py +197 -0
- claude_mpm/cli/commands/auto_configure.py +4 -4
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +216 -0
- claude_mpm/cli/commands/configure.py +621 -22
- claude_mpm/cli/commands/configure_agent_display.py +12 -0
- 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 +72 -0
- claude_mpm/cli/commands/postmortem.py +1 -1
- claude_mpm/cli/commands/profile.py +276 -0
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +182 -32
- claude_mpm/cli/executor.py +130 -16
- claude_mpm/cli/interactive/__init__.py +10 -0
- claude_mpm/cli/interactive/agent_wizard.py +32 -52
- 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 +83 -1
- claude_mpm/cli/parsers/commander_parser.py +116 -0
- claude_mpm/cli/parsers/profile_parser.py +147 -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 +2 -3
- claude_mpm/cli/startup.py +690 -386
- 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 +20 -249
- claude_mpm/commands/mpm-doctor.md +16 -21
- claude_mpm/commands/mpm-help.md +12 -205
- claude_mpm/commands/mpm-init.md +88 -506
- claude_mpm/commands/mpm-monitor.md +22 -401
- claude_mpm/commands/mpm-organize.md +70 -442
- claude_mpm/commands/mpm-postmortem.md +13 -107
- claude_mpm/commands/mpm-session-resume.md +20 -363
- claude_mpm/commands/mpm-status.md +13 -69
- claude_mpm/commands/mpm-ticket-view.md +60 -495
- claude_mpm/commands/mpm-version.md +13 -107
- 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 +37 -26
- claude_mpm/core/config_constants.py +74 -9
- claude_mpm/core/constants.py +56 -12
- claude_mpm/core/framework/loaders/agent_loader.py +1 -1
- claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
- 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 +61 -0
- 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_agent_registry.py +1 -1
- claude_mpm/core/unified_config.py +54 -8
- claude_mpm/core/unified_paths.py +95 -90
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- 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/BSNlmTZj.js +1 -0
- 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/DR8nis88.js +2 -0
- 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/NqQ1dWOy.js +1 -0
- 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.RgBboRvH.js +1 -0
- 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 -0
- claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
- claude_mpm/dashboard/static/svelte-build/index.html +36 -0
- 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__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.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/__pycache__/tool_analysis.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 +313 -99
- 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__/duplicate_detector.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 +67 -32
- 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/kuzu_memory_hook.py +5 -5
- claude_mpm/hooks/session_resume_hook.py +89 -1
- claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
- claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
- claude_mpm/init.py +276 -0
- claude_mpm/models/git_repository.py +3 -3
- claude_mpm/scripts/claude-hook-handler.sh +46 -19
- claude_mpm/services/agents/agent_builder.py +3 -3
- 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 +7 -7
- claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -2
- claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +39 -19
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
- claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +169 -26
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +101 -75
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/git_source_manager.py +23 -4
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/recommender.py +5 -3
- claude_mpm/services/agents/single_tier_deployment_service.py +6 -6
- claude_mpm/services/agents/sources/git_source_sync_service.py +121 -10
- claude_mpm/services/agents/startup_sync.py +27 -4
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/command_deployment_service.py +44 -26
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +31 -1
- 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/git/git_operations_service.py +8 -8
- 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 +15 -3
- claude_mpm/services/monitor/server.py +571 -11
- claude_mpm/services/pm_skills_deployer.py +884 -0
- claude_mpm/services/profile_manager.py +337 -0
- claude_mpm/services/skills/git_skill_source_manager.py +281 -20
- claude_mpm/services/skills/selective_skill_deployer.py +211 -46
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +192 -70
- claude_mpm/services/socketio/dashboard_server.py +1 -0
- claude_mpm/services/socketio/event_normalizer.py +37 -6
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/core.py +262 -123
- 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 +98 -3
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- claude_mpm/utils/agent_dependency_loader.py +115 -4
- claude_mpm/utils/agent_filters.py +1 -1
- claude_mpm/utils/migration.py +4 -4
- claude_mpm/utils/robust_installer.py +86 -21
- claude_mpm-5.6.34.dist-info/METADATA +393 -0
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/RECORD +486 -145
- claude_mpm-5.4.22.dist-info/METADATA +0 -996
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/top_level.txt +0 -0
claude_mpm/cli/startup.py
CHANGED
|
@@ -10,7 +10,6 @@ Part of cli/__init__.py refactoring to reduce file size and improve modularity.
|
|
|
10
10
|
|
|
11
11
|
import os
|
|
12
12
|
import sys
|
|
13
|
-
import warnings
|
|
14
13
|
from pathlib import Path
|
|
15
14
|
|
|
16
15
|
|
|
@@ -35,13 +34,13 @@ def sync_hooks_on_startup(quiet: bool = False) -> bool:
|
|
|
35
34
|
installer = HookInstaller()
|
|
36
35
|
|
|
37
36
|
# Show brief status (hooks sync is fast)
|
|
38
|
-
if not quiet:
|
|
37
|
+
if not quiet and sys.stdout.isatty():
|
|
39
38
|
print("Syncing Claude Code hooks...", end=" ", flush=True)
|
|
40
39
|
|
|
41
40
|
# Reinstall hooks (force=True ensures update)
|
|
42
41
|
success = installer.install_hooks(force=True)
|
|
43
42
|
|
|
44
|
-
if not quiet:
|
|
43
|
+
if not quiet and sys.stdout.isatty():
|
|
45
44
|
if success:
|
|
46
45
|
print("✓")
|
|
47
46
|
else:
|
|
@@ -50,7 +49,7 @@ def sync_hooks_on_startup(quiet: bool = False) -> bool:
|
|
|
50
49
|
return success
|
|
51
50
|
|
|
52
51
|
except Exception as e:
|
|
53
|
-
if not quiet:
|
|
52
|
+
if not quiet and sys.stdout.isatty():
|
|
54
53
|
print("(error)")
|
|
55
54
|
# Log but don't fail startup
|
|
56
55
|
from ..core.logger import get_logger
|
|
@@ -60,43 +59,79 @@ def sync_hooks_on_startup(quiet: bool = False) -> bool:
|
|
|
60
59
|
return False
|
|
61
60
|
|
|
62
61
|
|
|
63
|
-
def
|
|
64
|
-
"""
|
|
62
|
+
def cleanup_legacy_agent_cache() -> None:
|
|
63
|
+
"""Remove legacy hierarchical agent cache directories.
|
|
64
|
+
|
|
65
|
+
WHY: Old agent cache used category-based directory structure directly in cache.
|
|
66
|
+
New structure uses remote source paths. This cleanup prevents confusion from
|
|
67
|
+
stale cache directories.
|
|
68
|
+
|
|
69
|
+
Old structure (removed):
|
|
70
|
+
~/.claude-mpm/cache/agents/engineer/
|
|
71
|
+
~/.claude-mpm/cache/agents/ops/
|
|
72
|
+
~/.claude-mpm/cache/agents/qa/
|
|
73
|
+
...
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
with 26 active code references, while cache/agents/ has only 7 legacy references.
|
|
75
|
+
New structure (kept):
|
|
76
|
+
~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/agents/...
|
|
69
77
|
|
|
70
|
-
DESIGN
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
- One-time check: Only warns if legacy cache contains files
|
|
78
|
+
DESIGN DECISION: Runs early in startup before agent deployment to ensure
|
|
79
|
+
clean cache state. Removes only known legacy directories to avoid deleting
|
|
80
|
+
user data.
|
|
74
81
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
canonical_cache = home / ".claude-mpm" / "cache" / "remote-agents"
|
|
78
|
-
migration_marker = home / ".claude-mpm" / "cache" / ".migrated_to_remote_agents"
|
|
82
|
+
import shutil
|
|
83
|
+
from pathlib import Path
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
from ..core.logger import get_logger
|
|
86
|
+
|
|
87
|
+
logger = get_logger("startup")
|
|
83
88
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if not legacy_files:
|
|
89
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
90
|
+
if not cache_dir.exists():
|
|
87
91
|
return
|
|
88
92
|
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
# Known legacy category directories (from old hierarchical structure)
|
|
94
|
+
legacy_dirs = [
|
|
95
|
+
"claude-mpm",
|
|
96
|
+
"documentation",
|
|
97
|
+
"engineer",
|
|
98
|
+
"ops",
|
|
99
|
+
"qa",
|
|
100
|
+
"security",
|
|
101
|
+
"universal",
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
removed = []
|
|
105
|
+
|
|
106
|
+
# Remove legacy category directories
|
|
107
|
+
for dir_name in legacy_dirs:
|
|
108
|
+
legacy_path = cache_dir / dir_name
|
|
109
|
+
if legacy_path.exists() and legacy_path.is_dir():
|
|
110
|
+
try:
|
|
111
|
+
shutil.rmtree(legacy_path)
|
|
112
|
+
removed.append(dir_name)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.debug(f"Failed to remove legacy directory {dir_name}: {e}")
|
|
115
|
+
|
|
116
|
+
# Also remove stray BASE-AGENT.md in cache root
|
|
117
|
+
base_agent = cache_dir / "BASE-AGENT.md"
|
|
118
|
+
if base_agent.exists():
|
|
119
|
+
try:
|
|
120
|
+
base_agent.unlink()
|
|
121
|
+
removed.append("BASE-AGENT.md")
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.debug(f"Failed to remove BASE-AGENT.md: {e}")
|
|
124
|
+
|
|
125
|
+
if removed:
|
|
126
|
+
logger.info(f"Cleaned up legacy agent cache: {', '.join(removed)}")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def check_legacy_cache() -> None:
|
|
130
|
+
"""Deprecated: Legacy cache checking is no longer needed.
|
|
131
|
+
|
|
132
|
+
This function is kept for backward compatibility but does nothing.
|
|
133
|
+
All agent cache operations now use the standardized cache/agents/ directory.
|
|
134
|
+
"""
|
|
100
135
|
|
|
101
136
|
|
|
102
137
|
def setup_early_environment(argv):
|
|
@@ -126,7 +161,25 @@ def setup_early_environment(argv):
|
|
|
126
161
|
# CRITICAL: Suppress ALL logging by default
|
|
127
162
|
# This catches all loggers (claude_mpm.*, service.*, framework_loader, etc.)
|
|
128
163
|
# This will be overridden by setup_mcp_server_logging() based on user preference
|
|
129
|
-
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
|
|
130
183
|
|
|
131
184
|
# Process argv
|
|
132
185
|
if argv is None:
|
|
@@ -156,7 +209,17 @@ def should_skip_background_services(args, processed_argv):
|
|
|
156
209
|
skip_commands = ["--version", "-v", "--help", "-h"]
|
|
157
210
|
return any(cmd in (processed_argv or sys.argv[1:]) for cmd in skip_commands) or (
|
|
158
211
|
hasattr(args, "command")
|
|
159
|
-
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
|
+
]
|
|
160
223
|
)
|
|
161
224
|
|
|
162
225
|
|
|
@@ -199,7 +262,7 @@ def deploy_bundled_skills():
|
|
|
199
262
|
if not skills_config.get("auto_deploy", True):
|
|
200
263
|
# Auto-deploy disabled, skip silently
|
|
201
264
|
return
|
|
202
|
-
except Exception:
|
|
265
|
+
except Exception: # nosec B110
|
|
203
266
|
# If config loading fails, assume auto-deploy is enabled (default)
|
|
204
267
|
pass
|
|
205
268
|
|
|
@@ -217,11 +280,13 @@ def deploy_bundled_skills():
|
|
|
217
280
|
if deployment_result.get("deployed"):
|
|
218
281
|
# Show simple feedback for deployed skills
|
|
219
282
|
deployed_count = len(deployment_result["deployed"])
|
|
220
|
-
|
|
283
|
+
if sys.stdout.isatty():
|
|
284
|
+
print(f"✓ Bundled skills ready ({deployed_count} deployed)", flush=True)
|
|
221
285
|
logger.info(f"Skills: Deployed {deployed_count} skill(s)")
|
|
222
286
|
elif not deployment_result.get("errors"):
|
|
223
287
|
# No deployment needed, skills already present
|
|
224
|
-
|
|
288
|
+
if sys.stdout.isatty():
|
|
289
|
+
print("✓ Bundled skills ready", flush=True)
|
|
225
290
|
|
|
226
291
|
if deployment_result.get("errors"):
|
|
227
292
|
logger.warning(
|
|
@@ -255,7 +320,8 @@ def discover_and_link_runtime_skills():
|
|
|
255
320
|
|
|
256
321
|
discover_skills()
|
|
257
322
|
# Show simple success feedback
|
|
258
|
-
|
|
323
|
+
if sys.stdout.isatty():
|
|
324
|
+
print("✓ Runtime skills linked", flush=True)
|
|
259
325
|
except Exception as e:
|
|
260
326
|
# Import logger here to avoid circular imports
|
|
261
327
|
from ..core.logger import get_logger
|
|
@@ -273,63 +339,62 @@ def deploy_output_style_on_startup():
|
|
|
273
339
|
communication without emojis and exclamation points. Styles are project-specific
|
|
274
340
|
to allow different projects to have different communication styles.
|
|
275
341
|
|
|
276
|
-
DESIGN DECISION: This is non-blocking and idempotent. Deploys to
|
|
277
|
-
directory (
|
|
278
|
-
|
|
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.
|
|
279
345
|
|
|
280
|
-
Deploys
|
|
281
|
-
- claude-mpm
|
|
346
|
+
Deploys all styles:
|
|
347
|
+
- claude-mpm.md (professional mode)
|
|
282
348
|
- claude-mpm-teacher.md (teaching mode)
|
|
349
|
+
- claude-mpm-research.md (research mode - for codebase analysis)
|
|
283
350
|
"""
|
|
284
351
|
try:
|
|
285
|
-
import
|
|
286
|
-
from pathlib import Path
|
|
352
|
+
from ..core.output_style_manager import OutputStyleManager
|
|
287
353
|
|
|
288
|
-
#
|
|
289
|
-
|
|
290
|
-
professional_source = package_dir / "CLAUDE_MPM_OUTPUT_STYLE.md"
|
|
291
|
-
teacher_source = package_dir / "CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md"
|
|
354
|
+
# Initialize the output style manager
|
|
355
|
+
manager = OutputStyleManager()
|
|
292
356
|
|
|
293
|
-
#
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
# Create directory if it doesn't exist
|
|
300
|
-
output_styles_dir.mkdir(parents=True, exist_ok=True)
|
|
301
|
-
|
|
302
|
-
# Check if already deployed (both files exist and have content)
|
|
303
|
-
already_deployed = (
|
|
304
|
-
professional_target.exists()
|
|
305
|
-
and teacher_target.exists()
|
|
306
|
-
and professional_target.stat().st_size > 0
|
|
307
|
-
and teacher_target.stat().st_size > 0
|
|
308
|
-
)
|
|
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
|
|
309
362
|
|
|
310
|
-
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:
|
|
311
378
|
# Show feedback that output styles are ready
|
|
312
|
-
|
|
379
|
+
if sys.stdout.isatty():
|
|
380
|
+
print("✓ Output styles ready", flush=True)
|
|
313
381
|
return
|
|
314
382
|
|
|
315
|
-
# Deploy
|
|
316
|
-
|
|
317
|
-
if professional_source.exists():
|
|
318
|
-
shutil.copy2(professional_source, professional_target)
|
|
319
|
-
deployed_count += 1
|
|
383
|
+
# Deploy all styles using the manager
|
|
384
|
+
results = manager.deploy_all_styles(activate_default=True)
|
|
320
385
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
deployed_count += 1
|
|
386
|
+
# Count successful deployments
|
|
387
|
+
deployed_count = sum(1 for success in results.values() if success)
|
|
324
388
|
|
|
325
389
|
if deployed_count > 0:
|
|
326
|
-
|
|
390
|
+
if sys.stdout.isatty():
|
|
391
|
+
print(f"✓ Output styles deployed ({deployed_count} styles)", flush=True)
|
|
327
392
|
else:
|
|
328
|
-
#
|
|
393
|
+
# Deployment failed - log but don't fail startup
|
|
329
394
|
from ..core.logger import get_logger
|
|
330
395
|
|
|
331
396
|
logger = get_logger("cli")
|
|
332
|
-
logger.debug("
|
|
397
|
+
logger.debug("Failed to deploy any output styles")
|
|
333
398
|
|
|
334
399
|
except Exception as e:
|
|
335
400
|
# Non-critical - log but don't fail startup
|
|
@@ -418,7 +483,95 @@ def _cleanup_orphaned_agents(deploy_target: Path, deployed_agents: list[str]) ->
|
|
|
418
483
|
return removed_count
|
|
419
484
|
|
|
420
485
|
|
|
421
|
-
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):
|
|
422
575
|
"""
|
|
423
576
|
Synchronize agent templates from remote sources on startup.
|
|
424
577
|
|
|
@@ -434,18 +587,46 @@ def sync_remote_agents_on_startup():
|
|
|
434
587
|
1. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
|
|
435
588
|
2. Deploy agents to ~/.claude/agents/ - Phase 2 progress bar
|
|
436
589
|
3. Cleanup orphaned agents (ours but no longer deployed) - Phase 3
|
|
437
|
-
4.
|
|
590
|
+
4. Cleanup legacy agent cache directories (after sync/deployment) - Phase 4
|
|
591
|
+
5. Log deployment results
|
|
592
|
+
|
|
593
|
+
Args:
|
|
594
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
438
595
|
"""
|
|
439
|
-
#
|
|
596
|
+
# DEPRECATED: Legacy warning - no-op function, kept for compatibility
|
|
440
597
|
check_legacy_cache()
|
|
441
598
|
|
|
442
599
|
try:
|
|
443
|
-
|
|
600
|
+
# Load active profile if configured
|
|
601
|
+
# Get project root (where .claude-mpm exists)
|
|
602
|
+
from pathlib import Path
|
|
603
|
+
|
|
604
|
+
from ..core.shared.config_loader import ConfigLoader
|
|
444
605
|
from ..services.agents.startup_sync import sync_agents_on_startup
|
|
606
|
+
from ..services.profile_manager import ProfileManager
|
|
445
607
|
from ..utils.progress import ProgressBar
|
|
446
608
|
|
|
609
|
+
project_root = Path.cwd()
|
|
610
|
+
|
|
611
|
+
profile_manager = ProfileManager(project_dir=project_root)
|
|
612
|
+
config_loader = ConfigLoader()
|
|
613
|
+
main_config = config_loader.load_main_config()
|
|
614
|
+
active_profile = main_config.get("active_profile")
|
|
615
|
+
|
|
616
|
+
if active_profile:
|
|
617
|
+
success = profile_manager.load_profile(active_profile)
|
|
618
|
+
if success:
|
|
619
|
+
summary = profile_manager.get_filtering_summary()
|
|
620
|
+
from ..core.logger import get_logger
|
|
621
|
+
|
|
622
|
+
logger = get_logger("cli")
|
|
623
|
+
logger.info(
|
|
624
|
+
f"Profile '{active_profile}' active: "
|
|
625
|
+
f"{summary['enabled_agents_count']} agents enabled"
|
|
626
|
+
)
|
|
627
|
+
|
|
447
628
|
# Phase 1: Sync files from Git sources
|
|
448
|
-
result = sync_agents_on_startup()
|
|
629
|
+
result = sync_agents_on_startup(force_refresh=force_sync)
|
|
449
630
|
|
|
450
631
|
# Only proceed with deployment if sync was enabled and ran
|
|
451
632
|
if result.get("enabled") and result.get("sources_synced", 0) > 0:
|
|
@@ -468,185 +649,95 @@ def sync_remote_agents_on_startup():
|
|
|
468
649
|
logger.warning(f"Agent sync completed with {len(errors)} errors")
|
|
469
650
|
|
|
470
651
|
# Phase 2: Deploy agents from cache to ~/.claude/agents/
|
|
471
|
-
#
|
|
652
|
+
# Use reconciliation service to respect configuration.yaml settings
|
|
472
653
|
try:
|
|
473
|
-
# Initialize deployment service
|
|
474
|
-
deployment_service = AgentDeploymentService()
|
|
475
|
-
|
|
476
|
-
# Count agents in cache to show accurate progress
|
|
477
654
|
from pathlib import Path
|
|
478
655
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
"
|
|
497
|
-
}
|
|
498
|
-
# Documentation files to exclude (by filename)
|
|
499
|
-
doc_files = {
|
|
500
|
-
"readme.md",
|
|
501
|
-
"changelog.md",
|
|
502
|
-
"contributing.md",
|
|
503
|
-
"implementation-summary.md",
|
|
504
|
-
"reorganization-plan.md",
|
|
505
|
-
"auto-deploy-index.md",
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
# Find all markdown files
|
|
509
|
-
all_md_files = list(cache_dir.rglob("*.md"))
|
|
510
|
-
|
|
511
|
-
# Filter to only agent files:
|
|
512
|
-
# 1. Must have "/agents/" in path (from git repos)
|
|
513
|
-
# 2. Must not be in PM templates or doc files
|
|
514
|
-
# 3. Exclude BASE-AGENT.md which is not a deployable agent
|
|
515
|
-
# 4. Exclude build artifacts (dist/, build/, .cache/) to prevent double-counting
|
|
516
|
-
agent_files = [
|
|
517
|
-
f
|
|
518
|
-
for f in all_md_files
|
|
519
|
-
if (
|
|
520
|
-
# Must be in an agent directory (from git repos like bobmatnyc/claude-mpm-agents/agents/)
|
|
521
|
-
"/agents/" in str(f)
|
|
522
|
-
# Exclude PM templates, doc files, and BASE-AGENT
|
|
523
|
-
and f.name.lower() not in pm_templates
|
|
524
|
-
and f.name.lower() not in doc_files
|
|
525
|
-
and f.name.lower() != "base-agent.md"
|
|
526
|
-
# Exclude build artifacts (prevents double-counting source + built files)
|
|
527
|
-
and not any(
|
|
528
|
-
part in str(f).split("/")
|
|
529
|
-
for part in ["dist", "build", ".cache"]
|
|
530
|
-
)
|
|
531
|
-
)
|
|
532
|
-
]
|
|
533
|
-
agent_count = len(agent_files)
|
|
534
|
-
|
|
535
|
-
if agent_count > 0:
|
|
536
|
-
# Deploy agents to project-level directory where Claude Code expects them
|
|
537
|
-
deploy_target = Path.cwd() / ".claude" / "agents"
|
|
538
|
-
deployment_result = deployment_service.deploy_agents(
|
|
539
|
-
target_dir=deploy_target,
|
|
540
|
-
force_rebuild=False, # Only deploy if versions differ
|
|
541
|
-
deployment_mode="update", # Version-aware updates
|
|
656
|
+
from ..core.unified_config import UnifiedConfig
|
|
657
|
+
from ..services.agents.deployment.startup_reconciliation import (
|
|
658
|
+
perform_startup_reconciliation,
|
|
659
|
+
)
|
|
660
|
+
|
|
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"
|
|
542
674
|
)
|
|
543
675
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
# FALLBACK: If deployment result doesn't track skipped agents (async path),
|
|
551
|
-
# count existing agents in target directory as "already deployed"
|
|
552
|
-
# This ensures accurate reporting when agents are already up-to-date
|
|
553
|
-
if total_configured == 0 and deploy_target.exists():
|
|
554
|
-
existing_agents = list(deploy_target.glob("*.md"))
|
|
555
|
-
# Filter out non-agent files (e.g., README.md, INSTRUCTIONS.md)
|
|
556
|
-
agent_count_in_target = len(
|
|
557
|
-
[
|
|
558
|
-
f
|
|
559
|
-
for f in existing_agents
|
|
560
|
-
if not f.name.startswith(("README", "INSTRUCTIONS"))
|
|
561
|
-
]
|
|
562
|
-
)
|
|
563
|
-
if agent_count_in_target > 0:
|
|
564
|
-
# All agents already deployed - count them as skipped
|
|
565
|
-
skipped = agent_count_in_target
|
|
566
|
-
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
|
+
)
|
|
567
681
|
|
|
568
|
-
|
|
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
|
+
)
|
|
688
|
+
|
|
689
|
+
if total_operations > 0:
|
|
569
690
|
deploy_progress = ProgressBar(
|
|
570
|
-
total=
|
|
691
|
+
total=total_operations,
|
|
571
692
|
prefix="Deploying agents",
|
|
572
693
|
show_percentage=True,
|
|
573
694
|
show_counter=True,
|
|
574
695
|
)
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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"
|
|
579
718
|
)
|
|
719
|
+
print("\n⚠️ Agent Deployment Errors:")
|
|
720
|
+
max_errors_to_show = 10
|
|
721
|
+
errors_to_display = agent_result.errors[:max_errors_to_show]
|
|
580
722
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
deployed_filenames = []
|
|
584
|
-
for agent_name in deployment_result.get("deployed", []):
|
|
585
|
-
deployed_filenames.append(f"{agent_name}.md")
|
|
586
|
-
for agent_name in deployment_result.get("updated", []):
|
|
587
|
-
deployed_filenames.append(f"{agent_name}.md")
|
|
588
|
-
for agent_name in deployment_result.get("skipped", []):
|
|
589
|
-
deployed_filenames.append(f"{agent_name}.md")
|
|
590
|
-
|
|
591
|
-
# Run cleanup and get count of removed agents
|
|
592
|
-
removed = _cleanup_orphaned_agents(
|
|
593
|
-
deploy_target, deployed_filenames
|
|
594
|
-
)
|
|
723
|
+
for error in errors_to_display:
|
|
724
|
+
print(f" - {error}")
|
|
595
725
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
if removed > 0:
|
|
600
|
-
deploy_progress.finish(
|
|
601
|
-
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged, "
|
|
602
|
-
f"{removed} removed ({total_configured} configured from {agent_count} in repo)"
|
|
603
|
-
)
|
|
604
|
-
else:
|
|
605
|
-
deploy_progress.finish(
|
|
606
|
-
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged "
|
|
607
|
-
f"({total_configured} configured from {agent_count} in repo)"
|
|
608
|
-
)
|
|
609
|
-
elif removed > 0:
|
|
610
|
-
deploy_progress.finish(
|
|
611
|
-
f"Complete: {total_configured} agents ready - all unchanged, "
|
|
612
|
-
f"{removed} removed ({agent_count} available in repo)"
|
|
613
|
-
)
|
|
614
|
-
else:
|
|
615
|
-
deploy_progress.finish(
|
|
616
|
-
f"Complete: {total_configured} agents ready - all unchanged "
|
|
617
|
-
f"({agent_count} available in repo)"
|
|
618
|
-
)
|
|
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)")
|
|
619
729
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
f"Agent deployment completed with {len(deploy_errors)} errors: {deploy_errors}"
|
|
626
|
-
)
|
|
627
|
-
|
|
628
|
-
# Display errors to user with clear formatting
|
|
629
|
-
print("\n⚠️ Agent Deployment Errors:")
|
|
630
|
-
|
|
631
|
-
# Show first 10 errors to avoid overwhelming output
|
|
632
|
-
max_errors_to_show = 10
|
|
633
|
-
errors_to_display = deploy_errors[:max_errors_to_show]
|
|
634
|
-
|
|
635
|
-
for error in errors_to_display:
|
|
636
|
-
# Format error message for readability
|
|
637
|
-
# Errors typically come as strings like "agent.md: Error message"
|
|
638
|
-
print(f" - {error}")
|
|
639
|
-
|
|
640
|
-
# If more errors exist, show count
|
|
641
|
-
if len(deploy_errors) > max_errors_to_show:
|
|
642
|
-
remaining = len(deploy_errors) - max_errors_to_show
|
|
643
|
-
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")
|
|
644
735
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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
|
+
)
|
|
650
741
|
|
|
651
742
|
except Exception as e:
|
|
652
743
|
# Deployment failure shouldn't block startup
|
|
@@ -655,6 +746,11 @@ def sync_remote_agents_on_startup():
|
|
|
655
746
|
logger = get_logger("cli")
|
|
656
747
|
logger.warning(f"Failed to deploy agents from cache: {e}")
|
|
657
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
|
+
|
|
658
754
|
except Exception as e:
|
|
659
755
|
# Non-critical - log but don't fail startup
|
|
660
756
|
from ..core.logger import get_logger
|
|
@@ -663,8 +759,14 @@ def sync_remote_agents_on_startup():
|
|
|
663
759
|
logger.debug(f"Failed to sync remote agents: {e}")
|
|
664
760
|
# Continue execution - agent sync failure shouldn't block startup
|
|
665
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
|
|
666
767
|
|
|
667
|
-
|
|
768
|
+
|
|
769
|
+
def sync_remote_skills_on_startup(force_sync: bool = False):
|
|
668
770
|
"""
|
|
669
771
|
Synchronize skill templates from remote sources on startup.
|
|
670
772
|
|
|
@@ -679,13 +781,19 @@ def sync_remote_skills_on_startup():
|
|
|
679
781
|
1. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
|
|
680
782
|
2. Scan deployed agents for skill requirements → save to configuration.yaml
|
|
681
783
|
3. Resolve which skills to deploy (user_defined vs agent_referenced)
|
|
682
|
-
4.
|
|
683
|
-
5.
|
|
784
|
+
4. Apply profile filtering if active
|
|
785
|
+
5. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
|
|
786
|
+
6. Log deployment results with source indication
|
|
787
|
+
|
|
788
|
+
Args:
|
|
789
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
684
790
|
"""
|
|
685
791
|
try:
|
|
686
792
|
from pathlib import Path
|
|
687
793
|
|
|
688
794
|
from ..config.skill_sources import SkillSourceConfiguration
|
|
795
|
+
from ..core.shared.config_loader import ConfigLoader
|
|
796
|
+
from ..services.profile_manager import ProfileManager
|
|
689
797
|
from ..services.skills.git_skill_source_manager import GitSkillSourceManager
|
|
690
798
|
from ..services.skills.selective_skill_deployer import (
|
|
691
799
|
get_required_skills_from_agents,
|
|
@@ -694,6 +802,28 @@ def sync_remote_skills_on_startup():
|
|
|
694
802
|
)
|
|
695
803
|
from ..utils.progress import ProgressBar
|
|
696
804
|
|
|
805
|
+
# Load active profile if configured
|
|
806
|
+
# Get project root (where .claude-mpm exists)
|
|
807
|
+
project_root = Path.cwd()
|
|
808
|
+
|
|
809
|
+
profile_manager = ProfileManager(project_dir=project_root)
|
|
810
|
+
config_loader = ConfigLoader()
|
|
811
|
+
main_config = config_loader.load_main_config()
|
|
812
|
+
active_profile = main_config.get("active_profile")
|
|
813
|
+
|
|
814
|
+
if active_profile:
|
|
815
|
+
success = profile_manager.load_profile(active_profile)
|
|
816
|
+
if success:
|
|
817
|
+
from ..core.logger import get_logger
|
|
818
|
+
|
|
819
|
+
logger = get_logger("cli")
|
|
820
|
+
summary = profile_manager.get_filtering_summary()
|
|
821
|
+
logger.info(
|
|
822
|
+
f"Profile '{active_profile}' active: "
|
|
823
|
+
f"{summary['enabled_skills_count']} skills enabled, "
|
|
824
|
+
f"{summary['disabled_patterns_count']} patterns disabled"
|
|
825
|
+
)
|
|
826
|
+
|
|
697
827
|
config = SkillSourceConfiguration()
|
|
698
828
|
manager = GitSkillSourceManager(config)
|
|
699
829
|
|
|
@@ -762,7 +892,7 @@ def sync_remote_skills_on_startup():
|
|
|
762
892
|
|
|
763
893
|
# Sync all sources with progress callback
|
|
764
894
|
results = manager.sync_all_sources(
|
|
765
|
-
force=
|
|
895
|
+
force=force_sync, progress_callback=sync_progress.update
|
|
766
896
|
)
|
|
767
897
|
|
|
768
898
|
# Finish sync progress bar with clear breakdown
|
|
@@ -782,108 +912,191 @@ def sync_remote_skills_on_startup():
|
|
|
782
912
|
|
|
783
913
|
# Phase 2: Scan agents and save to configuration.yaml
|
|
784
914
|
# This step populates configuration.yaml with agent-referenced skills
|
|
785
|
-
|
|
786
|
-
|
|
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
|
+
)
|
|
787
924
|
|
|
788
|
-
|
|
789
|
-
|
|
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
|
+
)
|
|
790
931
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
save_agent_skills_to_config(list(agent_skills), project_config_path)
|
|
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)
|
|
794
934
|
|
|
795
|
-
|
|
796
|
-
|
|
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
|
+
)
|
|
797
945
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
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:
|
|
966
|
+
logger.info(
|
|
967
|
+
f"Profile '{active_profile}' filtered {filtered_count} skills "
|
|
968
|
+
f"({len(filtered_skills)} remaining)"
|
|
969
|
+
)
|
|
801
970
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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
|
+
)
|
|
987
|
+
|
|
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,
|
|
805
1011
|
)
|
|
806
1012
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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,
|
|
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,
|
|
814
1041
|
)
|
|
1042
|
+
deploy_progress.update(1)
|
|
815
1043
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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
|
+
)
|
|
821
1050
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
deploy_progress.update(total_available)
|
|
832
|
-
else:
|
|
833
|
-
# No skills to deploy - create dummy progress for message only
|
|
834
|
-
deploy_progress = ProgressBar(
|
|
835
|
-
total=1,
|
|
836
|
-
prefix="Deploying skill directories",
|
|
837
|
-
show_percentage=False,
|
|
838
|
-
show_counter=False,
|
|
839
|
-
)
|
|
840
|
-
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")
|
|
841
1060
|
|
|
842
|
-
|
|
843
|
-
# Include source indication (user_defined vs agent_referenced)
|
|
844
|
-
# Note: total_skill_count is from the repo, total_available is what's deployed/needed
|
|
845
|
-
source_label = (
|
|
846
|
-
"user override" if skill_source == "user_defined" else "from agents"
|
|
847
|
-
)
|
|
1061
|
+
status = ", ".join(parts)
|
|
848
1062
|
|
|
849
|
-
if
|
|
850
|
-
if filtered > 0:
|
|
851
|
-
deploy_progress.finish(
|
|
852
|
-
f"Complete: {deployed} new, {skipped} unchanged "
|
|
853
|
-
f"({total_available} {source_label}, {filtered} available in repo)"
|
|
854
|
-
)
|
|
855
|
-
else:
|
|
856
|
-
deploy_progress.finish(
|
|
857
|
-
f"Complete: {deployed} new, {skipped} unchanged "
|
|
858
|
-
f"({total_available} skills {source_label} from {total_skill_count} in repo)"
|
|
859
|
-
)
|
|
860
|
-
elif filtered > 0:
|
|
861
|
-
# Skills filtered means agents require fewer skills than available
|
|
1063
|
+
if filtered > 0:
|
|
862
1064
|
deploy_progress.finish(
|
|
863
|
-
f"
|
|
1065
|
+
f"Complete: {status} ({total_available} {source_label}, {filtered} files in cache)"
|
|
864
1066
|
)
|
|
865
1067
|
else:
|
|
866
1068
|
deploy_progress.finish(
|
|
867
|
-
f"Complete: {total_available} skills {source_label} "
|
|
868
|
-
f"({total_skill_count} available in repo)"
|
|
1069
|
+
f"Complete: {status} ({total_available} skills {source_label} from {total_skill_count} files in cache)"
|
|
869
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
|
|
870
1086
|
|
|
871
|
-
|
|
872
|
-
from ..core.logger import get_logger
|
|
873
|
-
|
|
874
|
-
logger = get_logger("cli")
|
|
1087
|
+
logger = get_logger("cli")
|
|
875
1088
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1089
|
+
errors = deployment_result.get("errors", [])
|
|
1090
|
+
if errors:
|
|
1091
|
+
logger.warning(
|
|
1092
|
+
f"Skill deployment completed with {len(errors)} errors: {errors}"
|
|
1093
|
+
)
|
|
881
1094
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
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
|
+
)
|
|
887
1100
|
|
|
888
1101
|
except Exception as e:
|
|
889
1102
|
# Non-critical - log but don't fail startup
|
|
@@ -921,7 +1134,7 @@ def show_agent_summary():
|
|
|
921
1134
|
installed_count = len(agent_files)
|
|
922
1135
|
|
|
923
1136
|
# Count available agents in cache (from remote sources)
|
|
924
|
-
cache_dir = Path.home() / ".claude-mpm" / "cache" / "
|
|
1137
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
925
1138
|
available_count = 0
|
|
926
1139
|
if cache_dir.exists():
|
|
927
1140
|
# Use same filtering logic as agent deployment (lines 486-533 in startup.py)
|
|
@@ -966,7 +1179,7 @@ def show_agent_summary():
|
|
|
966
1179
|
# Display summary if we have agents
|
|
967
1180
|
if installed_count > 0 or available_count > 0:
|
|
968
1181
|
print(
|
|
969
|
-
f"✓ Agents: {installed_count}
|
|
1182
|
+
f"✓ Agents: {installed_count} deployed / {max(0, available_count - installed_count)} cached",
|
|
970
1183
|
flush=True,
|
|
971
1184
|
)
|
|
972
1185
|
|
|
@@ -983,61 +1196,64 @@ def show_skill_summary():
|
|
|
983
1196
|
Display skill availability summary on startup.
|
|
984
1197
|
|
|
985
1198
|
WHY: Users should see at a glance how many skills are deployed and available
|
|
986
|
-
from
|
|
1199
|
+
from cache, similar to the agent summary showing "X deployed / Y cached".
|
|
987
1200
|
|
|
988
|
-
DESIGN DECISION: Fast, non-blocking check that counts skills from
|
|
989
|
-
|
|
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"
|
|
990
1206
|
Failures are silent to avoid blocking startup.
|
|
991
1207
|
"""
|
|
992
1208
|
try:
|
|
993
1209
|
from pathlib import Path
|
|
994
1210
|
|
|
995
|
-
# Count deployed skills (
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
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():
|
|
999
1215
|
# Count directories with SKILL.md (excludes collection repos)
|
|
1000
1216
|
# Exclude collection directories (obra-superpowers, etc.)
|
|
1001
1217
|
skill_dirs = [
|
|
1002
1218
|
d
|
|
1003
|
-
for d in
|
|
1219
|
+
for d in project_skills_dir.iterdir()
|
|
1004
1220
|
if d.is_dir()
|
|
1005
1221
|
and (d / "SKILL.md").exists()
|
|
1006
1222
|
and not (d / ".git").exists() # Exclude collection repos
|
|
1007
1223
|
]
|
|
1008
|
-
|
|
1224
|
+
deployed_count = len(skill_dirs)
|
|
1009
1225
|
|
|
1010
|
-
# Count
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
):
|
|
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():
|
|
1019
1235
|
continue
|
|
1020
1236
|
|
|
1021
|
-
# Count skill directories
|
|
1237
|
+
# Count skill directories (those with SKILL.md)
|
|
1022
1238
|
# Skills can be nested in: skills/category/skill-name/SKILL.md
|
|
1023
1239
|
# or in flat structure: skill-name/SKILL.md
|
|
1024
|
-
for root, dirs, files in os.walk(
|
|
1240
|
+
for root, dirs, files in os.walk(repo_dir):
|
|
1025
1241
|
if "SKILL.md" in files:
|
|
1026
|
-
# Exclude build artifacts and hidden directories
|
|
1027
|
-
# Get relative path from collection_dir to avoid excluding based on .claude parent
|
|
1242
|
+
# Exclude build artifacts and hidden directories
|
|
1028
1243
|
root_path = Path(root)
|
|
1029
|
-
relative_parts = root_path.relative_to(collection_dir).parts
|
|
1030
1244
|
if not any(
|
|
1031
1245
|
part.startswith(".")
|
|
1032
1246
|
or part in ["dist", "build", "__pycache__"]
|
|
1033
|
-
for part in
|
|
1247
|
+
for part in root_path.parts
|
|
1034
1248
|
):
|
|
1035
|
-
|
|
1249
|
+
cached_count += 1
|
|
1036
1250
|
|
|
1037
|
-
# Display summary
|
|
1038
|
-
|
|
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:
|
|
1039
1255
|
print(
|
|
1040
|
-
f"✓ Skills: {
|
|
1256
|
+
f"✓ Skills: {deployed_count} deployed / {non_deployed_cached} cached",
|
|
1041
1257
|
flush=True,
|
|
1042
1258
|
)
|
|
1043
1259
|
|
|
@@ -1049,6 +1265,86 @@ def show_skill_summary():
|
|
|
1049
1265
|
logger.debug(f"Failed to generate skill summary: {e}")
|
|
1050
1266
|
|
|
1051
1267
|
|
|
1268
|
+
def verify_and_show_pm_skills():
|
|
1269
|
+
"""Verify PM skills and display status with enhanced validation.
|
|
1270
|
+
|
|
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
|
|
1280
|
+
"""
|
|
1281
|
+
try:
|
|
1282
|
+
from pathlib import Path
|
|
1283
|
+
|
|
1284
|
+
from ..services.pm_skills_deployer import (
|
|
1285
|
+
REQUIRED_PM_SKILLS,
|
|
1286
|
+
PMSkillsDeployerService,
|
|
1287
|
+
)
|
|
1288
|
+
|
|
1289
|
+
deployer = PMSkillsDeployerService()
|
|
1290
|
+
project_dir = Path.cwd()
|
|
1291
|
+
|
|
1292
|
+
# Verify with auto-repair enabled
|
|
1293
|
+
result = deployer.verify_pm_skills(project_dir, auto_repair=True)
|
|
1294
|
+
|
|
1295
|
+
if result.verified:
|
|
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
|
+
)
|
|
1303
|
+
else:
|
|
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
|
+
)
|
|
1326
|
+
else:
|
|
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}")
|
|
1337
|
+
|
|
1338
|
+
except ImportError:
|
|
1339
|
+
# PM skills deployer not available - skip silently
|
|
1340
|
+
pass
|
|
1341
|
+
except Exception as e:
|
|
1342
|
+
from ..core.logger import get_logger
|
|
1343
|
+
|
|
1344
|
+
logger = get_logger("cli")
|
|
1345
|
+
logger.debug(f"PM skills verification failed: {e}")
|
|
1346
|
+
|
|
1347
|
+
|
|
1052
1348
|
def auto_install_chrome_devtools_on_startup():
|
|
1053
1349
|
"""
|
|
1054
1350
|
Automatically install chrome-devtools-mcp on startup if enabled.
|
|
@@ -1072,7 +1368,7 @@ def auto_install_chrome_devtools_on_startup():
|
|
|
1072
1368
|
if not chrome_devtools_config.get("auto_install", True):
|
|
1073
1369
|
# Auto-install disabled, skip silently
|
|
1074
1370
|
return
|
|
1075
|
-
except Exception:
|
|
1371
|
+
except Exception: # nosec B110
|
|
1076
1372
|
# If config loading fails, assume auto-install is enabled (default)
|
|
1077
1373
|
pass
|
|
1078
1374
|
|
|
@@ -1090,7 +1386,7 @@ def auto_install_chrome_devtools_on_startup():
|
|
|
1090
1386
|
# Continue execution - chrome-devtools installation failure shouldn't block startup
|
|
1091
1387
|
|
|
1092
1388
|
|
|
1093
|
-
def run_background_services():
|
|
1389
|
+
def run_background_services(force_sync: bool = False):
|
|
1094
1390
|
"""
|
|
1095
1391
|
Initialize all background services on startup.
|
|
1096
1392
|
|
|
@@ -1101,6 +1397,9 @@ def run_background_services():
|
|
|
1101
1397
|
explicitly requests them via agent-manager commands. This prevents unwanted
|
|
1102
1398
|
file creation in project .claude/ directories.
|
|
1103
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).
|
|
1104
1403
|
"""
|
|
1105
1404
|
# Sync hooks early to ensure up-to-date configuration
|
|
1106
1405
|
# RATIONALE: Hooks should be synced before other services to fix stale configs
|
|
@@ -1111,7 +1410,9 @@ def run_background_services():
|
|
|
1111
1410
|
check_mcp_auto_configuration()
|
|
1112
1411
|
verify_mcp_gateway_startup()
|
|
1113
1412
|
check_for_updates_async()
|
|
1114
|
-
sync_remote_agents_on_startup(
|
|
1413
|
+
sync_remote_agents_on_startup(
|
|
1414
|
+
force_sync=force_sync
|
|
1415
|
+
) # Sync agents from remote sources
|
|
1115
1416
|
show_agent_summary() # Display agent counts after deployment
|
|
1116
1417
|
|
|
1117
1418
|
# Skills deployment order (precedence: remote > bundled)
|
|
@@ -1120,9 +1421,12 @@ def run_background_services():
|
|
|
1120
1421
|
# 3. Discover and link runtime skills (user-added skills)
|
|
1121
1422
|
# This ensures remote skills take precedence over bundled skills when names conflict
|
|
1122
1423
|
deploy_bundled_skills() # Base layer: package-bundled skills
|
|
1123
|
-
sync_remote_skills_on_startup(
|
|
1424
|
+
sync_remote_skills_on_startup(
|
|
1425
|
+
force_sync=force_sync
|
|
1426
|
+
) # Override layer: Git-based skills (takes precedence)
|
|
1124
1427
|
discover_and_link_runtime_skills() # Discovery: user-added skills
|
|
1125
1428
|
show_skill_summary() # Display skill counts after deployment
|
|
1429
|
+
verify_and_show_pm_skills() # PM skills verification and status
|
|
1126
1430
|
|
|
1127
1431
|
deploy_output_style_on_startup()
|
|
1128
1432
|
|
|
@@ -1221,7 +1525,9 @@ def check_mcp_auto_configuration():
|
|
|
1221
1525
|
from ..services.mcp_gateway.auto_configure import check_and_configure_mcp
|
|
1222
1526
|
|
|
1223
1527
|
# Show progress feedback - this operation can take 10+ seconds
|
|
1224
|
-
|
|
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)
|
|
1225
1531
|
|
|
1226
1532
|
# This function handles all the logic:
|
|
1227
1533
|
# - Checks if already configured
|
|
@@ -1232,11 +1538,17 @@ def check_mcp_auto_configuration():
|
|
|
1232
1538
|
check_and_configure_mcp()
|
|
1233
1539
|
|
|
1234
1540
|
# Clear the "Checking..." message by overwriting with spaces
|
|
1235
|
-
|
|
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
|
|
1236
1545
|
|
|
1237
1546
|
except Exception as e:
|
|
1238
1547
|
# Clear progress message on error
|
|
1239
|
-
|
|
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
|
|
1240
1552
|
|
|
1241
1553
|
# Non-critical - log but don't fail
|
|
1242
1554
|
from ..core.logger import get_logger
|
|
@@ -1256,18 +1568,10 @@ def verify_mcp_gateway_startup():
|
|
|
1256
1568
|
DESIGN DECISION: This is non-blocking - failures are logged but don't prevent
|
|
1257
1569
|
startup to ensure claude-mpm remains functional even if MCP gateway has issues.
|
|
1258
1570
|
"""
|
|
1259
|
-
#
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
logger = get_logger("mcp_verify")
|
|
1265
|
-
all_ok, message = verify_mcp_services_on_startup()
|
|
1266
|
-
if not all_ok:
|
|
1267
|
-
logger.warning(message)
|
|
1268
|
-
except Exception:
|
|
1269
|
-
# Non-critical - continue with startup
|
|
1270
|
-
pass
|
|
1571
|
+
# DISABLED: MCP service verification removed - Claude Code handles MCP natively
|
|
1572
|
+
# The previous check warned about missing MCP services, but users should configure
|
|
1573
|
+
# MCP servers through Claude Code's native MCP management, not through claude-mpm.
|
|
1574
|
+
# See: https://docs.anthropic.com/en/docs/claude-code/mcp
|
|
1271
1575
|
|
|
1272
1576
|
try:
|
|
1273
1577
|
import asyncio
|
|
@@ -1327,7 +1631,7 @@ def verify_mcp_gateway_startup():
|
|
|
1327
1631
|
loop.run_until_complete(
|
|
1328
1632
|
asyncio.gather(*pending, return_exceptions=True)
|
|
1329
1633
|
)
|
|
1330
|
-
except Exception:
|
|
1634
|
+
except Exception: # nosec B110
|
|
1331
1635
|
pass # Ignore cleanup errors
|
|
1332
1636
|
finally:
|
|
1333
1637
|
loop.close()
|
|
@@ -1421,7 +1725,7 @@ def check_for_updates_async():
|
|
|
1421
1725
|
|
|
1422
1726
|
logger = get_logger("upgrade_check")
|
|
1423
1727
|
logger.debug(f"Update check failed (non-critical): {e}")
|
|
1424
|
-
except Exception:
|
|
1728
|
+
except Exception: # nosec B110
|
|
1425
1729
|
pass # Avoid any errors in error handling
|
|
1426
1730
|
finally:
|
|
1427
1731
|
# Properly clean up event loop
|
|
@@ -1436,7 +1740,7 @@ def check_for_updates_async():
|
|
|
1436
1740
|
loop.run_until_complete(
|
|
1437
1741
|
asyncio.gather(*pending, return_exceptions=True)
|
|
1438
1742
|
)
|
|
1439
|
-
except Exception:
|
|
1743
|
+
except Exception: # nosec B110
|
|
1440
1744
|
pass # Ignore cleanup errors
|
|
1441
1745
|
finally:
|
|
1442
1746
|
loop.close()
|