claude-mpm 5.4.41__py3-none-any.whl → 5.6.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
- claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +109 -1925
- claude_mpm/agents/PM_INSTRUCTIONS.md +161 -298
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/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 +182 -32
- 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 +2 -3
- claude_mpm/cli/startup.py +527 -506
- 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 +35 -11
- 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 +63 -18
- claude_mpm/core/shared/config_loader.py +3 -1
- claude_mpm/core/socketio_pool.py +13 -5
- 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 +305 -87
- claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +116 -8
- claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
- claude_mpm/hooks/claude_hooks/response_tracking.py +42 -59
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.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 +39 -24
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
- claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +73 -75
- claude_mpm/hooks/kuzu_memory_hook.py +5 -5
- claude_mpm/hooks/session_resume_hook.py +89 -1
- claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
- claude_mpm/init.py +215 -2
- claude_mpm/scripts/claude-hook-handler.sh +43 -16
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -1
- claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +37 -17
- claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +36 -8
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +50 -26
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/git_source_manager.py +21 -2
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
- claude_mpm/services/agents/sources/git_source_sync_service.py +116 -5
- claude_mpm/services/agents/startup_sync.py +5 -2
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +325 -0
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -3
- claude_mpm/services/monitor/server.py +106 -16
- claude_mpm/services/pm_skills_deployer.py +302 -94
- claude_mpm/services/profile_manager.py +10 -4
- claude_mpm/services/skills/git_skill_source_manager.py +192 -29
- claude_mpm/services/skills/selective_skill_deployer.py +211 -46
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +192 -70
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/__init__.py +2 -1
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
- claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
- claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
- claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
- claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
- claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
- claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
- claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
- claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
- claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
- claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
- claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/security-scanning.md +112 -0
- claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
- claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
- claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
- claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
- claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
- claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
- claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
- claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
- claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
- claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
- claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/registry.py +295 -90
- claude_mpm/skills/skill_manager.py +29 -23
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- claude_mpm/utils/agent_dependency_loader.py +103 -4
- claude_mpm/utils/robust_installer.py +45 -24
- claude_mpm-5.6.23.dist-info/METADATA +393 -0
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/RECORD +447 -149
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- claude_mpm-5.4.41.dist-info/METADATA +0 -998
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/top_level.txt +0 -0
claude_mpm/cli/startup.py
CHANGED
|
@@ -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
|
|
|
@@ -308,63 +336,60 @@ def deploy_output_style_on_startup():
|
|
|
308
336
|
communication without emojis and exclamation points. Styles are project-specific
|
|
309
337
|
to allow different projects to have different communication styles.
|
|
310
338
|
|
|
311
|
-
DESIGN DECISION: This is non-blocking and idempotent. Deploys to
|
|
312
|
-
directory (
|
|
313
|
-
|
|
339
|
+
DESIGN DECISION: This is non-blocking and idempotent. Deploys to user-level
|
|
340
|
+
directory (~/.claude/output-styles/) which is the official Claude Code location
|
|
341
|
+
for custom output styles.
|
|
314
342
|
|
|
315
|
-
Deploys
|
|
316
|
-
- claude-mpm
|
|
343
|
+
Deploys all styles:
|
|
344
|
+
- claude-mpm.md (professional mode)
|
|
317
345
|
- claude-mpm-teacher.md (teaching mode)
|
|
346
|
+
- claude-mpm-research.md (research mode - for codebase analysis)
|
|
318
347
|
"""
|
|
319
348
|
try:
|
|
320
|
-
import
|
|
321
|
-
from pathlib import Path
|
|
349
|
+
from ..core.output_style_manager import OutputStyleManager
|
|
322
350
|
|
|
323
|
-
#
|
|
324
|
-
|
|
325
|
-
professional_source = package_dir / "CLAUDE_MPM_OUTPUT_STYLE.md"
|
|
326
|
-
teacher_source = package_dir / "CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md"
|
|
351
|
+
# Initialize the output style manager
|
|
352
|
+
manager = OutputStyleManager()
|
|
327
353
|
|
|
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
|
-
)
|
|
354
|
+
# Check if Claude Code version supports output styles (>= 1.0.83)
|
|
355
|
+
if not manager.supports_output_styles():
|
|
356
|
+
# Skip deployment for older versions
|
|
357
|
+
# The manager will fall back to injecting content directly
|
|
358
|
+
return
|
|
344
359
|
|
|
345
|
-
if
|
|
360
|
+
# Check if all styles are already deployed and up-to-date
|
|
361
|
+
all_up_to_date = True
|
|
362
|
+
for style_config in manager.styles.values():
|
|
363
|
+
source_path = style_config["source"]
|
|
364
|
+
target_path = style_config["target"]
|
|
365
|
+
|
|
366
|
+
if not (
|
|
367
|
+
target_path.exists()
|
|
368
|
+
and source_path.exists()
|
|
369
|
+
and target_path.stat().st_size == source_path.stat().st_size
|
|
370
|
+
):
|
|
371
|
+
all_up_to_date = False
|
|
372
|
+
break
|
|
373
|
+
|
|
374
|
+
if all_up_to_date:
|
|
346
375
|
# Show feedback that output styles are ready
|
|
347
376
|
print("✓ Output styles ready", flush=True)
|
|
348
377
|
return
|
|
349
378
|
|
|
350
|
-
# Deploy
|
|
351
|
-
|
|
352
|
-
if professional_source.exists():
|
|
353
|
-
shutil.copy2(professional_source, professional_target)
|
|
354
|
-
deployed_count += 1
|
|
379
|
+
# Deploy all styles using the manager
|
|
380
|
+
results = manager.deploy_all_styles(activate_default=True)
|
|
355
381
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
deployed_count += 1
|
|
382
|
+
# Count successful deployments
|
|
383
|
+
deployed_count = sum(1 for success in results.values() if success)
|
|
359
384
|
|
|
360
385
|
if deployed_count > 0:
|
|
361
386
|
print(f"✓ Output styles deployed ({deployed_count} styles)", flush=True)
|
|
362
387
|
else:
|
|
363
|
-
#
|
|
388
|
+
# Deployment failed - log but don't fail startup
|
|
364
389
|
from ..core.logger import get_logger
|
|
365
390
|
|
|
366
391
|
logger = get_logger("cli")
|
|
367
|
-
logger.debug("
|
|
392
|
+
logger.debug("Failed to deploy any output styles")
|
|
368
393
|
|
|
369
394
|
except Exception as e:
|
|
370
395
|
# Non-critical - log but don't fail startup
|
|
@@ -453,7 +478,95 @@ def _cleanup_orphaned_agents(deploy_target: Path, deployed_agents: list[str]) ->
|
|
|
453
478
|
return removed_count
|
|
454
479
|
|
|
455
480
|
|
|
456
|
-
def
|
|
481
|
+
def _save_deployment_state_after_reconciliation(
|
|
482
|
+
agent_result, project_path: Path
|
|
483
|
+
) -> None:
|
|
484
|
+
"""Save deployment state after reconciliation to prevent duplicate deployment.
|
|
485
|
+
|
|
486
|
+
WHY: After perform_startup_reconciliation() deploys agents to .claude/agents/,
|
|
487
|
+
we need to save a deployment state file so that ClaudeRunner.setup_agents()
|
|
488
|
+
can detect agents are already deployed and skip redundant deployment.
|
|
489
|
+
|
|
490
|
+
This prevents the "✓ Deployed 31 native agents" duplicate deployment that
|
|
491
|
+
occurs when setup_agents() doesn't know reconciliation already ran.
|
|
492
|
+
|
|
493
|
+
Args:
|
|
494
|
+
agent_result: DeploymentResult from perform_startup_reconciliation()
|
|
495
|
+
project_path: Project root directory
|
|
496
|
+
|
|
497
|
+
DESIGN DECISION: Use same state file format as ClaudeRunner._save_deployment_state()
|
|
498
|
+
Located at: .claude-mpm/cache/deployment_state.json
|
|
499
|
+
|
|
500
|
+
State file format:
|
|
501
|
+
{
|
|
502
|
+
"version": "5.6.13",
|
|
503
|
+
"agent_count": 15,
|
|
504
|
+
"deployment_hash": "sha256:...",
|
|
505
|
+
"deployed_at": 1234567890.123
|
|
506
|
+
}
|
|
507
|
+
"""
|
|
508
|
+
import hashlib
|
|
509
|
+
import json
|
|
510
|
+
import time
|
|
511
|
+
|
|
512
|
+
from ..core.logger import get_logger
|
|
513
|
+
|
|
514
|
+
logger = get_logger("cli")
|
|
515
|
+
|
|
516
|
+
try:
|
|
517
|
+
# Get version from package
|
|
518
|
+
from claude_mpm import __version__
|
|
519
|
+
|
|
520
|
+
# Path to state file (matches ClaudeRunner._get_deployment_state_path())
|
|
521
|
+
state_file = project_path / ".claude-mpm" / "cache" / "deployment_state.json"
|
|
522
|
+
agents_dir = project_path / ".claude" / "agents"
|
|
523
|
+
|
|
524
|
+
# Count deployed agents
|
|
525
|
+
if agents_dir.exists():
|
|
526
|
+
agent_count = len(list(agents_dir.glob("*.md")))
|
|
527
|
+
else:
|
|
528
|
+
agent_count = 0
|
|
529
|
+
|
|
530
|
+
# Calculate deployment hash (matches ClaudeRunner._calculate_deployment_hash())
|
|
531
|
+
# CRITICAL: Must match exact hash algorithm used in ClaudeRunner
|
|
532
|
+
# Hashes filename + file content (not mtime) for consistency
|
|
533
|
+
deployment_hash = ""
|
|
534
|
+
if agents_dir.exists():
|
|
535
|
+
agent_files = sorted(agents_dir.glob("*.md"))
|
|
536
|
+
hash_obj = hashlib.sha256()
|
|
537
|
+
for agent_file in agent_files:
|
|
538
|
+
# Include filename and content in hash (matches ClaudeRunner)
|
|
539
|
+
hash_obj.update(agent_file.name.encode())
|
|
540
|
+
try:
|
|
541
|
+
hash_obj.update(agent_file.read_bytes())
|
|
542
|
+
except Exception as e:
|
|
543
|
+
logger.debug(f"Error reading {agent_file} for hash: {e}")
|
|
544
|
+
|
|
545
|
+
deployment_hash = hash_obj.hexdigest()
|
|
546
|
+
|
|
547
|
+
# Create state data
|
|
548
|
+
state_data = {
|
|
549
|
+
"version": __version__,
|
|
550
|
+
"agent_count": agent_count,
|
|
551
|
+
"deployment_hash": deployment_hash,
|
|
552
|
+
"deployed_at": time.time(),
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
# Ensure directory exists
|
|
556
|
+
state_file.parent.mkdir(parents=True, exist_ok=True)
|
|
557
|
+
|
|
558
|
+
# Write state file
|
|
559
|
+
state_file.write_text(json.dumps(state_data, indent=2))
|
|
560
|
+
logger.debug(
|
|
561
|
+
f"Saved deployment state after reconciliation: {agent_count} agents"
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
except Exception as e:
|
|
565
|
+
# Non-critical error - log but don't fail startup
|
|
566
|
+
logger.debug(f"Failed to save deployment state: {e}")
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def sync_remote_agents_on_startup(force_sync: bool = False):
|
|
457
570
|
"""
|
|
458
571
|
Synchronize agent templates from remote sources on startup.
|
|
459
572
|
|
|
@@ -466,28 +579,28 @@ def sync_remote_agents_on_startup():
|
|
|
466
579
|
block startup to ensure claude-mpm remains functional.
|
|
467
580
|
|
|
468
581
|
Workflow:
|
|
469
|
-
1.
|
|
470
|
-
2.
|
|
471
|
-
3.
|
|
472
|
-
4. Cleanup
|
|
582
|
+
1. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
|
|
583
|
+
2. Deploy agents to ~/.claude/agents/ - Phase 2 progress bar
|
|
584
|
+
3. Cleanup orphaned agents (ours but no longer deployed) - Phase 3
|
|
585
|
+
4. Cleanup legacy agent cache directories (after sync/deployment) - Phase 4
|
|
473
586
|
5. Log deployment results
|
|
474
|
-
"""
|
|
475
|
-
# Cleanup legacy cache directories first (before syncing)
|
|
476
|
-
cleanup_legacy_agent_cache()
|
|
477
587
|
|
|
478
|
-
|
|
588
|
+
Args:
|
|
589
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
590
|
+
"""
|
|
591
|
+
# DEPRECATED: Legacy warning - no-op function, kept for compatibility
|
|
479
592
|
check_legacy_cache()
|
|
480
593
|
|
|
481
594
|
try:
|
|
595
|
+
# Load active profile if configured
|
|
596
|
+
# Get project root (where .claude-mpm exists)
|
|
597
|
+
from pathlib import Path
|
|
598
|
+
|
|
482
599
|
from ..core.shared.config_loader import ConfigLoader
|
|
483
|
-
from ..services.agents.deployment.agent_deployment import AgentDeploymentService
|
|
484
600
|
from ..services.agents.startup_sync import sync_agents_on_startup
|
|
485
601
|
from ..services.profile_manager import ProfileManager
|
|
486
602
|
from ..utils.progress import ProgressBar
|
|
487
603
|
|
|
488
|
-
# Load active profile if configured
|
|
489
|
-
# Get project root (where .claude-mpm exists)
|
|
490
|
-
from pathlib import Path
|
|
491
604
|
project_root = Path.cwd()
|
|
492
605
|
|
|
493
606
|
profile_manager = ProfileManager(project_dir=project_root)
|
|
@@ -508,7 +621,7 @@ def sync_remote_agents_on_startup():
|
|
|
508
621
|
)
|
|
509
622
|
|
|
510
623
|
# Phase 1: Sync files from Git sources
|
|
511
|
-
result = sync_agents_on_startup()
|
|
624
|
+
result = sync_agents_on_startup(force_refresh=force_sync)
|
|
512
625
|
|
|
513
626
|
# Only proceed with deployment if sync was enabled and ran
|
|
514
627
|
if result.get("enabled") and result.get("sources_synced", 0) > 0:
|
|
@@ -531,297 +644,95 @@ def sync_remote_agents_on_startup():
|
|
|
531
644
|
logger.warning(f"Agent sync completed with {len(errors)} errors")
|
|
532
645
|
|
|
533
646
|
# Phase 2: Deploy agents from cache to ~/.claude/agents/
|
|
534
|
-
#
|
|
647
|
+
# Use reconciliation service to respect configuration.yaml settings
|
|
535
648
|
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
|
-
all_agent_files = [
|
|
549
|
-
f
|
|
550
|
-
for f in cache_dir.rglob("*.md")
|
|
551
|
-
if "/agents/" in str(f)
|
|
552
|
-
and str(f).count("/agents/") == 1
|
|
553
|
-
and f.stem.lower() != "base-agent"
|
|
554
|
-
]
|
|
555
|
-
|
|
556
|
-
# Build exclusion list for agents not in profile
|
|
557
|
-
excluded_agents = []
|
|
558
|
-
for agent_file in all_agent_files:
|
|
559
|
-
agent_name = agent_file.stem
|
|
560
|
-
if not profile_manager.is_agent_enabled(agent_name):
|
|
561
|
-
excluded_agents.append(agent_name)
|
|
562
|
-
|
|
563
|
-
if excluded_agents:
|
|
564
|
-
# Get singleton config and update with profile settings
|
|
565
|
-
# BUGFIX: Config is a singleton that ignores dict parameter if already initialized.
|
|
566
|
-
# Creating Config({...}) doesn't store excluded_agents - use set() instead.
|
|
567
|
-
deploy_config = Config()
|
|
568
|
-
deploy_config.set("agent_deployment.excluded_agents", excluded_agents)
|
|
569
|
-
deploy_config.set("agent_deployment.filter_non_mpm_agents", False)
|
|
570
|
-
deploy_config.set("agent_deployment.case_sensitive", False)
|
|
571
|
-
deploy_config.set("agent_deployment.exclude_dependencies", False)
|
|
572
|
-
logger.info(
|
|
573
|
-
f"Profile '{active_profile}': Excluding {len(excluded_agents)} agents from deployment"
|
|
574
|
-
)
|
|
575
|
-
|
|
576
|
-
deployment_service = AgentDeploymentService(config=deploy_config)
|
|
577
|
-
|
|
578
|
-
# Count agents in cache to show accurate progress
|
|
579
649
|
from pathlib import Path
|
|
580
650
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
# BUGFIX (cache-count-inflation): Clean up stale cache files
|
|
586
|
-
# from old repositories before counting to prevent inflated counts.
|
|
587
|
-
# Issue: Old caches like bobmatnyc/claude-mpm-agents/agents/
|
|
588
|
-
# were counted alongside current agents, inflating count
|
|
589
|
-
# from 44 to 85.
|
|
590
|
-
#
|
|
591
|
-
# Solution: Remove files with nested /agents/ paths
|
|
592
|
-
# (e.g., cache/agents/user/repo/agents/...)
|
|
593
|
-
# Keep only current agents (e.g., cache/agents/engineer/...)
|
|
594
|
-
removed_count = 0
|
|
595
|
-
stale_dirs = set()
|
|
596
|
-
|
|
597
|
-
for md_file in cache_dir.rglob("*.md"):
|
|
598
|
-
# Stale cache files have multiple /agents/ in their path
|
|
599
|
-
# Current: ~/.claude-mpm/cache/agents/engineer/...
|
|
600
|
-
# (1 occurrence)
|
|
601
|
-
# Old: ~/.claude-mpm/cache/agents/bobmatnyc/.../agents/...
|
|
602
|
-
# (2+ occurrences)
|
|
603
|
-
if str(md_file).count("/agents/") > 1:
|
|
604
|
-
# Track parent directory for cleanup
|
|
605
|
-
# Extract subdirectory under cache/agents/
|
|
606
|
-
# (e.g., "bobmatnyc")
|
|
607
|
-
parts = md_file.parts
|
|
608
|
-
cache_agents_idx = parts.index("agents")
|
|
609
|
-
if cache_agents_idx + 1 < len(parts):
|
|
610
|
-
stale_subdir = parts[cache_agents_idx + 1]
|
|
611
|
-
# Only remove if it's not a known category directory
|
|
612
|
-
if stale_subdir not in [
|
|
613
|
-
"engineer",
|
|
614
|
-
"ops",
|
|
615
|
-
"qa",
|
|
616
|
-
"universal",
|
|
617
|
-
"documentation",
|
|
618
|
-
"claude-mpm",
|
|
619
|
-
"security",
|
|
620
|
-
]:
|
|
621
|
-
stale_dirs.add(cache_dir / stale_subdir)
|
|
622
|
-
|
|
623
|
-
md_file.unlink()
|
|
624
|
-
removed_count += 1
|
|
625
|
-
|
|
626
|
-
# Remove empty stale directories
|
|
627
|
-
for stale_dir in stale_dirs:
|
|
628
|
-
if stale_dir.exists() and stale_dir.is_dir():
|
|
629
|
-
try:
|
|
630
|
-
# Remove directory and all contents
|
|
631
|
-
import shutil
|
|
632
|
-
|
|
633
|
-
shutil.rmtree(stale_dir)
|
|
634
|
-
except Exception:
|
|
635
|
-
pass # Ignore cleanup errors
|
|
636
|
-
|
|
637
|
-
if removed_count > 0:
|
|
638
|
-
from loguru import logger
|
|
639
|
-
|
|
640
|
-
logger.info(
|
|
641
|
-
f"Cleaned up {removed_count} stale cache files "
|
|
642
|
-
f"from old repositories"
|
|
643
|
-
)
|
|
651
|
+
from ..core.unified_config import UnifiedConfig
|
|
652
|
+
from ..services.agents.deployment.startup_reconciliation import (
|
|
653
|
+
perform_startup_reconciliation,
|
|
654
|
+
)
|
|
644
655
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
# (
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
"
|
|
658
|
-
"response_format.md",
|
|
659
|
-
"ticket_completeness_examples.md",
|
|
660
|
-
"validation_templates.md",
|
|
661
|
-
"git_file_tracking.md",
|
|
662
|
-
}
|
|
663
|
-
# Documentation files to exclude (by filename)
|
|
664
|
-
doc_files = {
|
|
665
|
-
"readme.md",
|
|
666
|
-
"changelog.md",
|
|
667
|
-
"contributing.md",
|
|
668
|
-
"implementation-summary.md",
|
|
669
|
-
"reorganization-plan.md",
|
|
670
|
-
"auto-deploy-index.md",
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
# Find all markdown files (after cleanup)
|
|
674
|
-
all_md_files = list(cache_dir.rglob("*.md"))
|
|
675
|
-
|
|
676
|
-
# Filter to only agent files:
|
|
677
|
-
# 1. Must have "/agents/" in path exactly ONCE
|
|
678
|
-
# (current structure)
|
|
679
|
-
# 2. Must not be in PM templates or doc files
|
|
680
|
-
# 3. Exclude BASE-AGENT.md which is not a deployable agent
|
|
681
|
-
# 4. Exclude build artifacts (dist/, build/, .cache/)
|
|
682
|
-
# to prevent double-counting
|
|
683
|
-
agent_files = [
|
|
684
|
-
f
|
|
685
|
-
for f in all_md_files
|
|
686
|
-
if (
|
|
687
|
-
# Must be in an agent directory (from current
|
|
688
|
-
# cache structure)
|
|
689
|
-
"/agents/" in str(f)
|
|
690
|
-
# NEW: Only ONE /agents/ in path (excludes old
|
|
691
|
-
# nested repos)
|
|
692
|
-
# This prevents counting old caches like
|
|
693
|
-
# bobmatnyc/claude-mpm-agents/agents/...
|
|
694
|
-
and str(f).count("/agents/") == 1
|
|
695
|
-
# Exclude PM templates, doc files, and BASE-AGENT
|
|
696
|
-
and f.name.lower() not in pm_templates
|
|
697
|
-
and f.name.lower() not in doc_files
|
|
698
|
-
and f.name.lower() != "base-agent.md"
|
|
699
|
-
# Exclude build artifacts (prevents double-counting
|
|
700
|
-
# source + built files)
|
|
701
|
-
and not any(
|
|
702
|
-
part in str(f).split("/")
|
|
703
|
-
for part in ["dist", "build", ".cache"]
|
|
704
|
-
)
|
|
705
|
-
)
|
|
706
|
-
]
|
|
707
|
-
agent_count = len(agent_files)
|
|
708
|
-
|
|
709
|
-
if agent_count > 0:
|
|
710
|
-
# Deploy agents to project-level directory where Claude Code expects them
|
|
711
|
-
deploy_target = Path.cwd() / ".claude" / "agents"
|
|
712
|
-
deployment_result = deployment_service.deploy_agents(
|
|
713
|
-
target_dir=deploy_target,
|
|
714
|
-
force_rebuild=False, # Only deploy if versions differ
|
|
715
|
-
deployment_mode="update", # Version-aware updates
|
|
716
|
-
config=deploy_config, # Pass config to respect profile filtering
|
|
656
|
+
# Load configuration
|
|
657
|
+
unified_config = UnifiedConfig()
|
|
658
|
+
|
|
659
|
+
# Override with profile settings if active
|
|
660
|
+
if active_profile and profile_manager.active_profile:
|
|
661
|
+
# Get enabled agents from profile (returns Set[str])
|
|
662
|
+
profile_enabled_agents = (
|
|
663
|
+
profile_manager.active_profile.get_enabled_agents()
|
|
664
|
+
)
|
|
665
|
+
# Update config with profile's enabled list (convert Set to List)
|
|
666
|
+
unified_config.agents.enabled = list(profile_enabled_agents)
|
|
667
|
+
logger.info(
|
|
668
|
+
f"Profile '{active_profile}': Using {len(profile_enabled_agents)} enabled agents"
|
|
717
669
|
)
|
|
718
670
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
agent_count_in_target = len(
|
|
732
|
-
[
|
|
733
|
-
f
|
|
734
|
-
for f in existing_agents
|
|
735
|
-
if not f.name.startswith(("README", "INSTRUCTIONS"))
|
|
736
|
-
]
|
|
737
|
-
)
|
|
738
|
-
if agent_count_in_target > 0:
|
|
739
|
-
# All agents already deployed - count them as skipped
|
|
740
|
-
skipped = agent_count_in_target
|
|
741
|
-
total_configured = agent_count_in_target
|
|
671
|
+
# Perform reconciliation to deploy configured agents
|
|
672
|
+
project_path = Path.cwd()
|
|
673
|
+
agent_result, _skill_result = perform_startup_reconciliation(
|
|
674
|
+
project_path=project_path, config=unified_config, silent=False
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
# Display results with progress bar
|
|
678
|
+
total_operations = (
|
|
679
|
+
len(agent_result.deployed)
|
|
680
|
+
+ len(agent_result.removed)
|
|
681
|
+
+ len(agent_result.unchanged)
|
|
682
|
+
)
|
|
742
683
|
|
|
743
|
-
|
|
684
|
+
if total_operations > 0:
|
|
744
685
|
deploy_progress = ProgressBar(
|
|
745
|
-
total=
|
|
686
|
+
total=total_operations,
|
|
746
687
|
prefix="Deploying agents",
|
|
747
688
|
show_percentage=True,
|
|
748
689
|
show_counter=True,
|
|
749
690
|
)
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
)
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
691
|
+
deploy_progress.update(total_operations)
|
|
692
|
+
|
|
693
|
+
# Build summary message
|
|
694
|
+
deployed = len(agent_result.deployed)
|
|
695
|
+
removed = len(agent_result.removed)
|
|
696
|
+
unchanged = len(agent_result.unchanged)
|
|
697
|
+
|
|
698
|
+
summary_parts = []
|
|
699
|
+
if deployed > 0:
|
|
700
|
+
summary_parts.append(f"{deployed} new")
|
|
701
|
+
if removed > 0:
|
|
702
|
+
summary_parts.append(f"{removed} removed")
|
|
703
|
+
if unchanged > 0:
|
|
704
|
+
summary_parts.append(f"{unchanged} unchanged")
|
|
705
|
+
|
|
706
|
+
summary = f"Complete: {', '.join(summary_parts)}"
|
|
707
|
+
deploy_progress.finish(summary)
|
|
708
|
+
|
|
709
|
+
# Display errors if any
|
|
710
|
+
if agent_result.errors:
|
|
711
|
+
logger.warning(
|
|
712
|
+
f"Agent deployment completed with {len(agent_result.errors)} errors"
|
|
769
713
|
)
|
|
714
|
+
print("\n⚠️ Agent Deployment Errors:")
|
|
715
|
+
max_errors_to_show = 10
|
|
716
|
+
errors_to_display = agent_result.errors[:max_errors_to_show]
|
|
770
717
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
if deployed > 0 or updated > 0:
|
|
774
|
-
if removed > 0:
|
|
775
|
-
deploy_progress.finish(
|
|
776
|
-
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged, "
|
|
777
|
-
f"{removed} removed ({total_configured} configured from {agent_count} files in cache)"
|
|
778
|
-
)
|
|
779
|
-
else:
|
|
780
|
-
deploy_progress.finish(
|
|
781
|
-
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged "
|
|
782
|
-
f"({total_configured} configured from {agent_count} files in cache)"
|
|
783
|
-
)
|
|
784
|
-
elif removed > 0:
|
|
785
|
-
deploy_progress.finish(
|
|
786
|
-
f"Complete: {total_configured} agents deployed, "
|
|
787
|
-
f"{removed} removed ({agent_count} files in cache)"
|
|
788
|
-
)
|
|
789
|
-
else:
|
|
790
|
-
deploy_progress.finish(
|
|
791
|
-
f"Complete: {total_configured} agents deployed "
|
|
792
|
-
f"({agent_count} files in cache)"
|
|
793
|
-
)
|
|
794
|
-
|
|
795
|
-
# Display deployment errors to user (not just logs)
|
|
796
|
-
deploy_errors = deployment_result.get("errors", [])
|
|
797
|
-
if deploy_errors:
|
|
798
|
-
# Log for debugging
|
|
799
|
-
logger.warning(
|
|
800
|
-
f"Agent deployment completed with {len(deploy_errors)} errors: {deploy_errors}"
|
|
801
|
-
)
|
|
802
|
-
|
|
803
|
-
# Display errors to user with clear formatting
|
|
804
|
-
print("\n⚠️ Agent Deployment Errors:")
|
|
805
|
-
|
|
806
|
-
# Show first 10 errors to avoid overwhelming output
|
|
807
|
-
max_errors_to_show = 10
|
|
808
|
-
errors_to_display = deploy_errors[:max_errors_to_show]
|
|
718
|
+
for error in errors_to_display:
|
|
719
|
+
print(f" - {error}")
|
|
809
720
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
print(f" - {error}")
|
|
721
|
+
if len(agent_result.errors) > max_errors_to_show:
|
|
722
|
+
remaining = len(agent_result.errors) - max_errors_to_show
|
|
723
|
+
print(f" ... and {remaining} more error(s)")
|
|
814
724
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
725
|
+
print(
|
|
726
|
+
f"\n❌ Failed to deploy {len(agent_result.errors)} agent(s). "
|
|
727
|
+
"Please check the error messages above."
|
|
728
|
+
)
|
|
729
|
+
print(" Run with --verbose for detailed error information.\n")
|
|
819
730
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
731
|
+
# Save deployment state to prevent duplicate deployment in ClaudeRunner
|
|
732
|
+
# This ensures setup_agents() skips deployment since we already reconciled
|
|
733
|
+
_save_deployment_state_after_reconciliation(
|
|
734
|
+
agent_result=agent_result, project_path=project_path
|
|
735
|
+
)
|
|
825
736
|
|
|
826
737
|
except Exception as e:
|
|
827
738
|
# Deployment failure shouldn't block startup
|
|
@@ -830,6 +741,11 @@ def sync_remote_agents_on_startup():
|
|
|
830
741
|
logger = get_logger("cli")
|
|
831
742
|
logger.warning(f"Failed to deploy agents from cache: {e}")
|
|
832
743
|
|
|
744
|
+
# Phase 4: Cleanup legacy agent cache directories (after sync/deployment)
|
|
745
|
+
# CRITICAL: This must run AFTER sync completes because sync may recreate
|
|
746
|
+
# legacy directories. Running cleanup here ensures they're removed.
|
|
747
|
+
cleanup_legacy_agent_cache()
|
|
748
|
+
|
|
833
749
|
except Exception as e:
|
|
834
750
|
# Non-critical - log but don't fail startup
|
|
835
751
|
from ..core.logger import get_logger
|
|
@@ -838,8 +754,14 @@ def sync_remote_agents_on_startup():
|
|
|
838
754
|
logger.debug(f"Failed to sync remote agents: {e}")
|
|
839
755
|
# Continue execution - agent sync failure shouldn't block startup
|
|
840
756
|
|
|
757
|
+
# Cleanup legacy cache even if sync failed
|
|
758
|
+
try:
|
|
759
|
+
cleanup_legacy_agent_cache()
|
|
760
|
+
except Exception: # nosec B110
|
|
761
|
+
pass # Ignore cleanup errors
|
|
762
|
+
|
|
841
763
|
|
|
842
|
-
def sync_remote_skills_on_startup():
|
|
764
|
+
def sync_remote_skills_on_startup(force_sync: bool = False):
|
|
843
765
|
"""
|
|
844
766
|
Synchronize skill templates from remote sources on startup.
|
|
845
767
|
|
|
@@ -857,6 +779,9 @@ def sync_remote_skills_on_startup():
|
|
|
857
779
|
4. Apply profile filtering if active
|
|
858
780
|
5. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
|
|
859
781
|
6. Log deployment results with source indication
|
|
782
|
+
|
|
783
|
+
Args:
|
|
784
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
860
785
|
"""
|
|
861
786
|
try:
|
|
862
787
|
from pathlib import Path
|
|
@@ -962,7 +887,7 @@ def sync_remote_skills_on_startup():
|
|
|
962
887
|
|
|
963
888
|
# Sync all sources with progress callback
|
|
964
889
|
results = manager.sync_all_sources(
|
|
965
|
-
force=
|
|
890
|
+
force=force_sync, progress_callback=sync_progress.update
|
|
966
891
|
)
|
|
967
892
|
|
|
968
893
|
# Finish sync progress bar with clear breakdown
|
|
@@ -982,150 +907,191 @@ def sync_remote_skills_on_startup():
|
|
|
982
907
|
|
|
983
908
|
# Phase 2: Scan agents and save to configuration.yaml
|
|
984
909
|
# This step populates configuration.yaml with agent-referenced skills
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
# Phase 3: Resolve which skills to deploy (user_defined or agent_referenced)
|
|
996
|
-
skills_to_deploy, skill_source = get_skills_to_deploy(project_config_path)
|
|
997
|
-
|
|
998
|
-
# Phase 4: Apply profile filtering if active
|
|
999
|
-
if active_profile and profile_manager.active_profile:
|
|
1000
|
-
# Filter skills based on profile
|
|
1001
|
-
if skills_to_deploy:
|
|
1002
|
-
# Filter the resolved skill list
|
|
1003
|
-
original_count = len(skills_to_deploy)
|
|
1004
|
-
filtered_skills = [
|
|
1005
|
-
skill
|
|
1006
|
-
for skill in skills_to_deploy
|
|
1007
|
-
if profile_manager.is_skill_enabled(skill)
|
|
1008
|
-
]
|
|
1009
|
-
filtered_count = original_count - len(filtered_skills)
|
|
910
|
+
# CRITICAL: Always scan agents to populate agent_referenced, even when using cached skills.
|
|
911
|
+
# Without this, skill_filter=None causes ALL skills to deploy and NO cleanup to run.
|
|
912
|
+
agents_dir = Path.cwd() / ".claude" / "agents"
|
|
913
|
+
|
|
914
|
+
# Scan agents for skill requirements (ALWAYS run to ensure cleanup works)
|
|
915
|
+
agent_skills = get_required_skills_from_agents(agents_dir)
|
|
916
|
+
logger.info(
|
|
917
|
+
f"Agent scan found {len(agent_skills)} unique skills across deployed agents"
|
|
918
|
+
)
|
|
1010
919
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
elif filtered_count > 0:
|
|
1018
|
-
logger.info(
|
|
1019
|
-
f"Profile '{active_profile}' filtered {filtered_count} skills "
|
|
1020
|
-
f"({len(filtered_skills)} remaining)"
|
|
1021
|
-
)
|
|
920
|
+
# Save to project-level configuration.yaml
|
|
921
|
+
project_config_path = Path.cwd() / ".claude-mpm" / "configuration.yaml"
|
|
922
|
+
save_agent_skills_to_config(list(agent_skills), project_config_path)
|
|
923
|
+
logger.debug(
|
|
924
|
+
f"Saved {len(agent_skills)} agent-referenced skills to {project_config_path}"
|
|
925
|
+
)
|
|
1022
926
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
927
|
+
# Phase 3: Resolve which skills to deploy (user_defined or agent_referenced)
|
|
928
|
+
skills_to_deploy, skill_source = get_skills_to_deploy(project_config_path)
|
|
929
|
+
|
|
930
|
+
# CRITICAL DEBUG: Log deployment resolution to diagnose cleanup issues
|
|
931
|
+
if skills_to_deploy:
|
|
932
|
+
logger.info(
|
|
933
|
+
f"Resolved {len(skills_to_deploy)} skills from {skill_source} (cleanup will run)"
|
|
934
|
+
)
|
|
935
|
+
else:
|
|
936
|
+
logger.warning(
|
|
937
|
+
f"No skills resolved from {skill_source} - will deploy ALL skills WITHOUT cleanup! "
|
|
938
|
+
f"This may indicate agent_referenced is empty in configuration.yaml."
|
|
939
|
+
)
|
|
940
|
+
|
|
941
|
+
# Phase 4: Apply profile filtering if active
|
|
942
|
+
if active_profile and profile_manager.active_profile:
|
|
943
|
+
# Filter skills based on profile
|
|
944
|
+
if skills_to_deploy:
|
|
945
|
+
# Filter the resolved skill list
|
|
946
|
+
original_count = len(skills_to_deploy)
|
|
947
|
+
filtered_skills = [
|
|
948
|
+
skill
|
|
949
|
+
for skill in skills_to_deploy
|
|
950
|
+
if profile_manager.is_skill_enabled(skill)
|
|
951
|
+
]
|
|
952
|
+
filtered_count = original_count - len(filtered_skills)
|
|
953
|
+
|
|
954
|
+
# SAFEGUARD: Warn if all skills were filtered out (misconfiguration)
|
|
955
|
+
if not filtered_skills and original_count > 0:
|
|
956
|
+
logger.warning(
|
|
957
|
+
f"Profile '{active_profile}' filtered ALL {original_count} skills. "
|
|
958
|
+
f"This may indicate a naming mismatch in the profile."
|
|
959
|
+
)
|
|
960
|
+
elif filtered_count > 0:
|
|
1035
961
|
logger.info(
|
|
1036
|
-
f"Profile '{active_profile}'
|
|
1037
|
-
f"{len(filtered_skills)}
|
|
962
|
+
f"Profile '{active_profile}' filtered {filtered_count} skills "
|
|
963
|
+
f"({len(filtered_skills)} remaining)"
|
|
1038
964
|
)
|
|
1039
965
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
966
|
+
skills_to_deploy = filtered_skills
|
|
967
|
+
skill_source = f"{skill_source} + profile filtered"
|
|
968
|
+
else:
|
|
969
|
+
# No explicit skill list - filter from all available
|
|
970
|
+
all_skills = manager.get_all_skills()
|
|
971
|
+
filtered_skills = [
|
|
972
|
+
skill["name"]
|
|
973
|
+
for skill in all_skills
|
|
974
|
+
if profile_manager.is_skill_enabled(skill["name"])
|
|
975
|
+
]
|
|
976
|
+
skills_to_deploy = filtered_skills
|
|
977
|
+
skill_source = "profile filtered"
|
|
978
|
+
logger.info(
|
|
979
|
+
f"Profile '{active_profile}': "
|
|
980
|
+
f"{len(filtered_skills)} skills enabled from {len(all_skills)} available"
|
|
981
|
+
)
|
|
1043
982
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
983
|
+
# Get all skills to determine counts
|
|
984
|
+
all_skills = manager.get_all_skills()
|
|
985
|
+
total_skill_count = len(all_skills)
|
|
986
|
+
|
|
987
|
+
# Determine skill count based on resolution
|
|
988
|
+
skill_count = len(skills_to_deploy) if skills_to_deploy else total_skill_count
|
|
989
|
+
|
|
990
|
+
if skill_count > 0:
|
|
991
|
+
# Deploy skills with resolved filter
|
|
992
|
+
# Deploy ONLY to project directory (not user-level)
|
|
993
|
+
# DESIGN DECISION: Project-level deployment keeps skills isolated per project,
|
|
994
|
+
# avoiding pollution of user's global ~/.claude/skills/ directory.
|
|
995
|
+
|
|
996
|
+
# Deploy to project-local directory with cleanup
|
|
997
|
+
deployment_result = manager.deploy_skills(
|
|
998
|
+
target_dir=Path.cwd() / ".claude" / "skills",
|
|
999
|
+
force=force_sync,
|
|
1000
|
+
# CRITICAL FIX: Empty list should mean "deploy no skills", not "deploy all"
|
|
1001
|
+
# When skills_to_deploy is [], we want skill_filter=set() NOT skill_filter=None
|
|
1002
|
+
# None means "no filtering" (deploy all), empty set means "filter to nothing"
|
|
1003
|
+
skill_filter=set(skills_to_deploy)
|
|
1004
|
+
if skills_to_deploy is not None
|
|
1005
|
+
else None,
|
|
1047
1006
|
)
|
|
1048
1007
|
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1008
|
+
# REMOVED: User-level deployment (lines 1068-1074)
|
|
1009
|
+
# Reason: Skills should be project-specific, not user-global.
|
|
1010
|
+
# Claude Code can read from project-level .claude/skills/ directory.
|
|
1011
|
+
|
|
1012
|
+
# Get actual counts from deployment result (use project-local for display)
|
|
1013
|
+
deployed = deployment_result.get("deployed_count", 0)
|
|
1014
|
+
skipped = deployment_result.get("skipped_count", 0)
|
|
1015
|
+
filtered = deployment_result.get("filtered_count", 0)
|
|
1016
|
+
removed = deployment_result.get("removed_count", 0)
|
|
1017
|
+
total_available = deployed + skipped
|
|
1018
|
+
|
|
1019
|
+
# Only show progress bar if there are skills to deploy
|
|
1020
|
+
if total_available > 0:
|
|
1021
|
+
deploy_progress = ProgressBar(
|
|
1022
|
+
total=total_available,
|
|
1023
|
+
prefix="Deploying skill directories",
|
|
1024
|
+
show_percentage=True,
|
|
1025
|
+
show_counter=True,
|
|
1056
1026
|
)
|
|
1027
|
+
# Update progress bar to completion
|
|
1028
|
+
deploy_progress.update(total_available)
|
|
1029
|
+
else:
|
|
1030
|
+
# No skills to deploy - create dummy progress for message only
|
|
1031
|
+
deploy_progress = ProgressBar(
|
|
1032
|
+
total=1,
|
|
1033
|
+
prefix="Deploying skill directories",
|
|
1034
|
+
show_percentage=False,
|
|
1035
|
+
show_counter=False,
|
|
1036
|
+
)
|
|
1037
|
+
deploy_progress.update(1)
|
|
1057
1038
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1039
|
+
# Show total available skills (deployed + already existing)
|
|
1040
|
+
# Include source indication (user_defined vs agent_referenced)
|
|
1041
|
+
# Note: total_skill_count is from cache, total_available is what's deployed/needed
|
|
1042
|
+
source_label = (
|
|
1043
|
+
"user override" if skill_source == "user_defined" else "from agents"
|
|
1044
|
+
)
|
|
1063
1045
|
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
deploy_progress.update(total_available)
|
|
1074
|
-
else:
|
|
1075
|
-
# No skills to deploy - create dummy progress for message only
|
|
1076
|
-
deploy_progress = ProgressBar(
|
|
1077
|
-
total=1,
|
|
1078
|
-
prefix="Deploying skill directories",
|
|
1079
|
-
show_percentage=False,
|
|
1080
|
-
show_counter=False,
|
|
1081
|
-
)
|
|
1082
|
-
deploy_progress.update(1)
|
|
1046
|
+
# Build finish message with cleanup info
|
|
1047
|
+
if deployed > 0 or removed > 0:
|
|
1048
|
+
parts = []
|
|
1049
|
+
if deployed > 0:
|
|
1050
|
+
parts.append(f"{deployed} new")
|
|
1051
|
+
if skipped > 0:
|
|
1052
|
+
parts.append(f"{skipped} unchanged")
|
|
1053
|
+
if removed > 0:
|
|
1054
|
+
parts.append(f"{removed} removed")
|
|
1083
1055
|
|
|
1084
|
-
|
|
1085
|
-
# Include source indication (user_defined vs agent_referenced)
|
|
1086
|
-
# Note: total_skill_count is from cache, total_available is what's deployed/needed
|
|
1087
|
-
source_label = (
|
|
1088
|
-
"user override" if skill_source == "user_defined" else "from agents"
|
|
1089
|
-
)
|
|
1056
|
+
status = ", ".join(parts)
|
|
1090
1057
|
|
|
1091
|
-
if
|
|
1092
|
-
if filtered > 0:
|
|
1093
|
-
deploy_progress.finish(
|
|
1094
|
-
f"Complete: {deployed} new, {skipped} unchanged "
|
|
1095
|
-
f"({total_available} {source_label}, {filtered} files in cache)"
|
|
1096
|
-
)
|
|
1097
|
-
else:
|
|
1098
|
-
deploy_progress.finish(
|
|
1099
|
-
f"Complete: {deployed} new, {skipped} unchanged "
|
|
1100
|
-
f"({total_available} skills {source_label} from {total_skill_count} files in cache)"
|
|
1101
|
-
)
|
|
1102
|
-
elif filtered > 0:
|
|
1103
|
-
# Skills filtered means agents require fewer skills than available
|
|
1058
|
+
if filtered > 0:
|
|
1104
1059
|
deploy_progress.finish(
|
|
1105
|
-
f"
|
|
1060
|
+
f"Complete: {status} ({total_available} {source_label}, {filtered} files in cache)"
|
|
1106
1061
|
)
|
|
1107
1062
|
else:
|
|
1108
1063
|
deploy_progress.finish(
|
|
1109
|
-
f"Complete: {total_available} skills {source_label} "
|
|
1110
|
-
f"({total_skill_count} files in cache)"
|
|
1064
|
+
f"Complete: {status} ({total_available} skills {source_label} from {total_skill_count} files in cache)"
|
|
1111
1065
|
)
|
|
1066
|
+
elif filtered > 0:
|
|
1067
|
+
# Skills filtered means agents require fewer skills than available
|
|
1068
|
+
deploy_progress.finish(
|
|
1069
|
+
f"No skills needed ({source_label}, {total_skill_count} files in cache)"
|
|
1070
|
+
)
|
|
1071
|
+
else:
|
|
1072
|
+
# No changes - all skills already deployed
|
|
1073
|
+
msg = f"Complete: {total_available} skills {source_label}"
|
|
1074
|
+
if removed > 0:
|
|
1075
|
+
msg += f", {removed} removed"
|
|
1076
|
+
msg += f" ({total_skill_count} files in cache)"
|
|
1077
|
+
deploy_progress.finish(msg)
|
|
1078
|
+
|
|
1079
|
+
# Log deployment errors if any
|
|
1080
|
+
from ..core.logger import get_logger
|
|
1112
1081
|
|
|
1113
|
-
|
|
1114
|
-
from ..core.logger import get_logger
|
|
1115
|
-
|
|
1116
|
-
logger = get_logger("cli")
|
|
1082
|
+
logger = get_logger("cli")
|
|
1117
1083
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1084
|
+
errors = deployment_result.get("errors", [])
|
|
1085
|
+
if errors:
|
|
1086
|
+
logger.warning(
|
|
1087
|
+
f"Skill deployment completed with {len(errors)} errors: {errors}"
|
|
1088
|
+
)
|
|
1123
1089
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1090
|
+
# Log sync errors if any
|
|
1091
|
+
if results["failed_count"] > 0:
|
|
1092
|
+
logger.warning(
|
|
1093
|
+
f"Skill sync completed with {results['failed_count']} failures"
|
|
1094
|
+
)
|
|
1129
1095
|
|
|
1130
1096
|
except Exception as e:
|
|
1131
1097
|
# Non-critical - log but don't fail startup
|
|
@@ -1208,7 +1174,7 @@ def show_agent_summary():
|
|
|
1208
1174
|
# Display summary if we have agents
|
|
1209
1175
|
if installed_count > 0 or available_count > 0:
|
|
1210
1176
|
print(
|
|
1211
|
-
f"✓ Agents: {installed_count} deployed / {available_count} cached",
|
|
1177
|
+
f"✓ Agents: {installed_count} deployed / {max(0, available_count - installed_count)} cached",
|
|
1212
1178
|
flush=True,
|
|
1213
1179
|
)
|
|
1214
1180
|
|
|
@@ -1225,61 +1191,64 @@ def show_skill_summary():
|
|
|
1225
1191
|
Display skill availability summary on startup.
|
|
1226
1192
|
|
|
1227
1193
|
WHY: Users should see at a glance how many skills are deployed and available
|
|
1228
|
-
from
|
|
1194
|
+
from cache, similar to the agent summary showing "X deployed / Y cached".
|
|
1229
1195
|
|
|
1230
|
-
DESIGN DECISION: Fast, non-blocking check that counts skills from
|
|
1231
|
-
|
|
1196
|
+
DESIGN DECISION: Fast, non-blocking check that counts skills from:
|
|
1197
|
+
- Deployed skills: PROJECT-level .claude/skills/ directory
|
|
1198
|
+
- Cached skills: ~/.claude-mpm/cache/skills/ directory (from remote sources)
|
|
1199
|
+
|
|
1200
|
+
Shows format: "✓ Skills: X deployed / Y cached"
|
|
1232
1201
|
Failures are silent to avoid blocking startup.
|
|
1233
1202
|
"""
|
|
1234
1203
|
try:
|
|
1235
1204
|
from pathlib import Path
|
|
1236
1205
|
|
|
1237
|
-
# Count deployed skills (
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
if
|
|
1206
|
+
# Count deployed skills (PROJECT-level, not user-level)
|
|
1207
|
+
project_skills_dir = Path.cwd() / ".claude" / "skills"
|
|
1208
|
+
deployed_count = 0
|
|
1209
|
+
if project_skills_dir.exists():
|
|
1241
1210
|
# Count directories with SKILL.md (excludes collection repos)
|
|
1242
1211
|
# Exclude collection directories (obra-superpowers, etc.)
|
|
1243
1212
|
skill_dirs = [
|
|
1244
1213
|
d
|
|
1245
|
-
for d in
|
|
1214
|
+
for d in project_skills_dir.iterdir()
|
|
1246
1215
|
if d.is_dir()
|
|
1247
1216
|
and (d / "SKILL.md").exists()
|
|
1248
1217
|
and not (d / ".git").exists() # Exclude collection repos
|
|
1249
1218
|
]
|
|
1250
|
-
|
|
1219
|
+
deployed_count = len(skill_dirs)
|
|
1251
1220
|
|
|
1252
|
-
# Count
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
):
|
|
1221
|
+
# Count cached skills (from remote sources, not deployed yet)
|
|
1222
|
+
# This matches the agent summary pattern: deployed vs cached
|
|
1223
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "skills"
|
|
1224
|
+
cached_count = 0
|
|
1225
|
+
if cache_dir.exists():
|
|
1226
|
+
# Scan all repository directories in cache
|
|
1227
|
+
# Cache structure: ~/.claude-mpm/cache/skills/{owner}/{repo}/...
|
|
1228
|
+
for repo_dir in cache_dir.rglob("*"):
|
|
1229
|
+
if not repo_dir.is_dir():
|
|
1261
1230
|
continue
|
|
1262
1231
|
|
|
1263
|
-
# Count skill directories
|
|
1232
|
+
# Count skill directories (those with SKILL.md)
|
|
1264
1233
|
# Skills can be nested in: skills/category/skill-name/SKILL.md
|
|
1265
1234
|
# or in flat structure: skill-name/SKILL.md
|
|
1266
|
-
for root, dirs, files in os.walk(
|
|
1235
|
+
for root, dirs, files in os.walk(repo_dir):
|
|
1267
1236
|
if "SKILL.md" in files:
|
|
1268
|
-
# Exclude build artifacts and hidden directories
|
|
1269
|
-
# Get relative path from collection_dir to avoid excluding based on .claude parent
|
|
1237
|
+
# Exclude build artifacts and hidden directories
|
|
1270
1238
|
root_path = Path(root)
|
|
1271
|
-
relative_parts = root_path.relative_to(collection_dir).parts
|
|
1272
1239
|
if not any(
|
|
1273
1240
|
part.startswith(".")
|
|
1274
1241
|
or part in ["dist", "build", "__pycache__"]
|
|
1275
|
-
for part in
|
|
1242
|
+
for part in root_path.parts
|
|
1276
1243
|
):
|
|
1277
|
-
|
|
1244
|
+
cached_count += 1
|
|
1278
1245
|
|
|
1279
|
-
# Display summary
|
|
1280
|
-
|
|
1246
|
+
# Display summary using agent summary format: "X deployed / Y cached"
|
|
1247
|
+
# Only show non-deployed cached skills (subtract deployed from cached)
|
|
1248
|
+
non_deployed_cached = max(0, cached_count - deployed_count)
|
|
1249
|
+
if deployed_count > 0 or non_deployed_cached > 0:
|
|
1281
1250
|
print(
|
|
1282
|
-
f"✓ Skills: {
|
|
1251
|
+
f"✓ Skills: {deployed_count} deployed / {non_deployed_cached} cached",
|
|
1283
1252
|
flush=True,
|
|
1284
1253
|
)
|
|
1285
1254
|
|
|
@@ -1292,33 +1261,70 @@ def show_skill_summary():
|
|
|
1292
1261
|
|
|
1293
1262
|
|
|
1294
1263
|
def verify_and_show_pm_skills():
|
|
1295
|
-
"""Verify PM skills and display status.
|
|
1264
|
+
"""Verify PM skills and display status with enhanced validation.
|
|
1296
1265
|
|
|
1297
|
-
WHY: PM skills are
|
|
1298
|
-
|
|
1266
|
+
WHY: PM skills are CRITICAL for PM agent operation. PM must KNOW if
|
|
1267
|
+
framework knowledge is unavailable at startup. Enhanced validation
|
|
1268
|
+
checks all required skills exist, are not corrupted, and auto-repairs
|
|
1269
|
+
if needed.
|
|
1270
|
+
|
|
1271
|
+
Shows deployment status:
|
|
1272
|
+
- "✓ PM skills: 8/8 verified" if all required skills are valid
|
|
1273
|
+
- "⚠ PM skills: 2 missing, auto-repairing..." if issues detected
|
|
1274
|
+
- Non-blocking but visible warning if auto-repair fails
|
|
1299
1275
|
"""
|
|
1300
1276
|
try:
|
|
1301
1277
|
from pathlib import Path
|
|
1302
1278
|
|
|
1303
|
-
from ..services.pm_skills_deployer import
|
|
1279
|
+
from ..services.pm_skills_deployer import (
|
|
1280
|
+
REQUIRED_PM_SKILLS,
|
|
1281
|
+
PMSkillsDeployerService,
|
|
1282
|
+
)
|
|
1304
1283
|
|
|
1305
1284
|
deployer = PMSkillsDeployerService()
|
|
1306
1285
|
project_dir = Path.cwd()
|
|
1307
1286
|
|
|
1308
|
-
|
|
1287
|
+
# Verify with auto-repair enabled
|
|
1288
|
+
result = deployer.verify_pm_skills(project_dir, auto_repair=True)
|
|
1309
1289
|
|
|
1310
1290
|
if result.verified:
|
|
1311
|
-
# Show verified status
|
|
1312
|
-
|
|
1291
|
+
# Show verified status with count
|
|
1292
|
+
total_required = len(REQUIRED_PM_SKILLS)
|
|
1293
|
+
print(
|
|
1294
|
+
f"✓ PM skills: {total_required}/{total_required} verified", flush=True
|
|
1295
|
+
)
|
|
1313
1296
|
else:
|
|
1314
|
-
#
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1297
|
+
# Show warning with details
|
|
1298
|
+
missing_count = len(result.missing_skills)
|
|
1299
|
+
corrupted_count = len(result.corrupted_skills)
|
|
1300
|
+
|
|
1301
|
+
# Build status message
|
|
1302
|
+
issues = []
|
|
1303
|
+
if missing_count > 0:
|
|
1304
|
+
issues.append(f"{missing_count} missing")
|
|
1305
|
+
if corrupted_count > 0:
|
|
1306
|
+
issues.append(f"{corrupted_count} corrupted")
|
|
1307
|
+
|
|
1308
|
+
status = ", ".join(issues)
|
|
1309
|
+
|
|
1310
|
+
# Check if auto-repair was attempted
|
|
1311
|
+
if "Auto-repaired" in result.message:
|
|
1312
|
+
# Auto-repair succeeded
|
|
1313
|
+
total_required = len(REQUIRED_PM_SKILLS)
|
|
1314
|
+
print(
|
|
1315
|
+
f"✓ PM skills: {total_required}/{total_required} verified (auto-repaired)",
|
|
1316
|
+
flush=True,
|
|
1317
|
+
)
|
|
1320
1318
|
else:
|
|
1321
|
-
|
|
1319
|
+
# Auto-repair failed or not attempted
|
|
1320
|
+
print(f"⚠ PM skills: {status}", flush=True)
|
|
1321
|
+
|
|
1322
|
+
# Log warnings for debugging
|
|
1323
|
+
from ..core.logger import get_logger
|
|
1324
|
+
|
|
1325
|
+
logger = get_logger("cli")
|
|
1326
|
+
for warning in result.warnings:
|
|
1327
|
+
logger.warning(f"PM skills: {warning}")
|
|
1322
1328
|
|
|
1323
1329
|
except ImportError:
|
|
1324
1330
|
# PM skills deployer not available - skip silently
|
|
@@ -1353,7 +1359,7 @@ def auto_install_chrome_devtools_on_startup():
|
|
|
1353
1359
|
if not chrome_devtools_config.get("auto_install", True):
|
|
1354
1360
|
# Auto-install disabled, skip silently
|
|
1355
1361
|
return
|
|
1356
|
-
except Exception:
|
|
1362
|
+
except Exception: # nosec B110
|
|
1357
1363
|
# If config loading fails, assume auto-install is enabled (default)
|
|
1358
1364
|
pass
|
|
1359
1365
|
|
|
@@ -1371,7 +1377,7 @@ def auto_install_chrome_devtools_on_startup():
|
|
|
1371
1377
|
# Continue execution - chrome-devtools installation failure shouldn't block startup
|
|
1372
1378
|
|
|
1373
1379
|
|
|
1374
|
-
def run_background_services():
|
|
1380
|
+
def run_background_services(force_sync: bool = False):
|
|
1375
1381
|
"""
|
|
1376
1382
|
Initialize all background services on startup.
|
|
1377
1383
|
|
|
@@ -1382,6 +1388,9 @@ def run_background_services():
|
|
|
1382
1388
|
explicitly requests them via agent-manager commands. This prevents unwanted
|
|
1383
1389
|
file creation in project .claude/ directories.
|
|
1384
1390
|
See: SystemInstructionsDeployer and agent_deployment.py line 504-509
|
|
1391
|
+
|
|
1392
|
+
Args:
|
|
1393
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
1385
1394
|
"""
|
|
1386
1395
|
# Sync hooks early to ensure up-to-date configuration
|
|
1387
1396
|
# RATIONALE: Hooks should be synced before other services to fix stale configs
|
|
@@ -1392,7 +1401,9 @@ def run_background_services():
|
|
|
1392
1401
|
check_mcp_auto_configuration()
|
|
1393
1402
|
verify_mcp_gateway_startup()
|
|
1394
1403
|
check_for_updates_async()
|
|
1395
|
-
sync_remote_agents_on_startup(
|
|
1404
|
+
sync_remote_agents_on_startup(
|
|
1405
|
+
force_sync=force_sync
|
|
1406
|
+
) # Sync agents from remote sources
|
|
1396
1407
|
show_agent_summary() # Display agent counts after deployment
|
|
1397
1408
|
|
|
1398
1409
|
# Skills deployment order (precedence: remote > bundled)
|
|
@@ -1401,7 +1412,9 @@ def run_background_services():
|
|
|
1401
1412
|
# 3. Discover and link runtime skills (user-added skills)
|
|
1402
1413
|
# This ensures remote skills take precedence over bundled skills when names conflict
|
|
1403
1414
|
deploy_bundled_skills() # Base layer: package-bundled skills
|
|
1404
|
-
sync_remote_skills_on_startup(
|
|
1415
|
+
sync_remote_skills_on_startup(
|
|
1416
|
+
force_sync=force_sync
|
|
1417
|
+
) # Override layer: Git-based skills (takes precedence)
|
|
1405
1418
|
discover_and_link_runtime_skills() # Discovery: user-added skills
|
|
1406
1419
|
show_skill_summary() # Display skill counts after deployment
|
|
1407
1420
|
verify_and_show_pm_skills() # PM skills verification and status
|
|
@@ -1514,11 +1527,19 @@ def check_mcp_auto_configuration():
|
|
|
1514
1527
|
check_and_configure_mcp()
|
|
1515
1528
|
|
|
1516
1529
|
# Clear the "Checking..." message by overwriting with spaces
|
|
1517
|
-
|
|
1530
|
+
# Only use carriage return clearing if stdout is a real TTY
|
|
1531
|
+
if sys.stdout.isatty():
|
|
1532
|
+
print("\r" + " " * 30 + "\r", end="", flush=True)
|
|
1533
|
+
else:
|
|
1534
|
+
print() # Simple newline for non-TTY (like Claude Code REPL)
|
|
1518
1535
|
|
|
1519
1536
|
except Exception as e:
|
|
1520
1537
|
# Clear progress message on error
|
|
1521
|
-
|
|
1538
|
+
# Only use carriage return clearing if stdout is a real TTY
|
|
1539
|
+
if sys.stdout.isatty():
|
|
1540
|
+
print("\r" + " " * 30 + "\r", end="", flush=True)
|
|
1541
|
+
else:
|
|
1542
|
+
print() # Simple newline for non-TTY (like Claude Code REPL)
|
|
1522
1543
|
|
|
1523
1544
|
# Non-critical - log but don't fail
|
|
1524
1545
|
from ..core.logger import get_logger
|
|
@@ -1601,7 +1622,7 @@ def verify_mcp_gateway_startup():
|
|
|
1601
1622
|
loop.run_until_complete(
|
|
1602
1623
|
asyncio.gather(*pending, return_exceptions=True)
|
|
1603
1624
|
)
|
|
1604
|
-
except Exception:
|
|
1625
|
+
except Exception: # nosec B110
|
|
1605
1626
|
pass # Ignore cleanup errors
|
|
1606
1627
|
finally:
|
|
1607
1628
|
loop.close()
|
|
@@ -1695,7 +1716,7 @@ def check_for_updates_async():
|
|
|
1695
1716
|
|
|
1696
1717
|
logger = get_logger("upgrade_check")
|
|
1697
1718
|
logger.debug(f"Update check failed (non-critical): {e}")
|
|
1698
|
-
except Exception:
|
|
1719
|
+
except Exception: # nosec B110
|
|
1699
1720
|
pass # Avoid any errors in error handling
|
|
1700
1721
|
finally:
|
|
1701
1722
|
# Properly clean up event loop
|
|
@@ -1710,7 +1731,7 @@ def check_for_updates_async():
|
|
|
1710
1731
|
loop.run_until_complete(
|
|
1711
1732
|
asyncio.gather(*pending, return_exceptions=True)
|
|
1712
1733
|
)
|
|
1713
|
-
except Exception:
|
|
1734
|
+
except Exception: # nosec B110
|
|
1714
1735
|
pass # Ignore cleanup errors
|
|
1715
1736
|
finally:
|
|
1716
1737
|
loop.close()
|