claude-mpm 5.4.22__py3-none-any.whl → 5.4.48__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/MEMORY.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +739 -1052
- claude_mpm/agents/WORKFLOW.md +5 -254
- 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/cli/commands/agent_state_manager.py +10 -10
- claude_mpm/cli/commands/agents.py +9 -9
- claude_mpm/cli/commands/auto_configure.py +4 -4
- claude_mpm/cli/commands/configure.py +1 -1
- claude_mpm/cli/commands/configure_agent_display.py +10 -0
- claude_mpm/cli/commands/mpm_init/core.py +65 -0
- claude_mpm/cli/commands/postmortem.py +1 -1
- claude_mpm/cli/commands/profile.py +277 -0
- claude_mpm/cli/commands/skills.py +14 -18
- claude_mpm/cli/executor.py +10 -0
- claude_mpm/cli/interactive/agent_wizard.py +2 -2
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/profile_parser.py +148 -0
- claude_mpm/cli/parsers/skills_parser.py +0 -6
- claude_mpm/cli/startup.py +346 -75
- claude_mpm/commands/mpm-config.md +13 -250
- claude_mpm/commands/mpm-doctor.md +9 -22
- claude_mpm/commands/mpm-help.md +5 -206
- claude_mpm/commands/mpm-init.md +81 -507
- claude_mpm/commands/mpm-monitor.md +15 -402
- claude_mpm/commands/mpm-organize.md +61 -441
- claude_mpm/commands/mpm-postmortem.md +6 -108
- claude_mpm/commands/mpm-session-resume.md +12 -363
- claude_mpm/commands/mpm-status.md +5 -69
- claude_mpm/commands/mpm-ticket-view.md +52 -495
- claude_mpm/commands/mpm-version.md +5 -107
- claude_mpm/core/config.py +2 -4
- claude_mpm/core/framework/loaders/agent_loader.py +1 -1
- claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
- claude_mpm/core/optimized_startup.py +59 -0
- claude_mpm/core/shared/config_loader.py +1 -1
- claude_mpm/core/unified_agent_registry.py +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -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/hooks/claude_hooks/__pycache__/__init__.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__/installer.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/hook_handler.py +149 -1
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.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__/duplicate_detector.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 +26 -6
- claude_mpm/hooks/kuzu_memory_hook.py +5 -5
- claude_mpm/init.py +63 -0
- claude_mpm/models/git_repository.py +3 -3
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_builder.py +3 -3
- claude_mpm/services/agents/cache_git_manager.py +6 -6
- claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
- claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -2
- claude_mpm/services/agents/deployment/agent_format_converter.py +23 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +29 -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/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 +98 -75
- claude_mpm/services/agents/git_source_manager.py +19 -4
- claude_mpm/services/agents/recommender.py +5 -3
- claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +112 -6
- claude_mpm/services/agents/startup_sync.py +22 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
- claude_mpm/services/git/git_operations_service.py +8 -8
- claude_mpm/services/monitor/management/lifecycle.py +8 -1
- claude_mpm/services/monitor/server.py +473 -3
- claude_mpm/services/pm_skills_deployer.py +711 -0
- claude_mpm/services/profile_manager.py +331 -0
- claude_mpm/services/skills/git_skill_source_manager.py +101 -3
- claude_mpm/services/skills_deployer.py +4 -3
- claude_mpm/services/socketio/dashboard_server.py +1 -0
- claude_mpm/services/socketio/event_normalizer.py +37 -6
- claude_mpm/services/socketio/server/core.py +262 -123
- claude_mpm/skills/skill_manager.py +92 -3
- claude_mpm/utils/agent_dependency_loader.py +14 -2
- claude_mpm/utils/agent_filters.py +1 -1
- claude_mpm/utils/migration.py +4 -4
- claude_mpm/utils/robust_installer.py +47 -3
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/METADATA +7 -4
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/RECORD +118 -79
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.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
|
|
|
@@ -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
|
-
if migration_marker.exists() or not legacy_cache.exists():
|
|
82
|
-
return
|
|
85
|
+
from ..core.logger import get_logger
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
logger = get_logger("startup")
|
|
88
|
+
|
|
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):
|
|
@@ -431,19 +466,47 @@ def sync_remote_agents_on_startup():
|
|
|
431
466
|
block startup to ensure claude-mpm remains functional.
|
|
432
467
|
|
|
433
468
|
Workflow:
|
|
434
|
-
1.
|
|
435
|
-
2.
|
|
436
|
-
3.
|
|
437
|
-
4.
|
|
469
|
+
1. Cleanup legacy agent cache directories (if any)
|
|
470
|
+
2. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
|
|
471
|
+
3. Deploy agents to ~/.claude/agents/ - Phase 2 progress bar
|
|
472
|
+
4. Cleanup orphaned agents (ours but no longer deployed) - Phase 3
|
|
473
|
+
5. Log deployment results
|
|
438
474
|
"""
|
|
439
|
-
#
|
|
475
|
+
# Cleanup legacy cache directories first (before syncing)
|
|
476
|
+
cleanup_legacy_agent_cache()
|
|
477
|
+
|
|
478
|
+
# DEPRECATED: Legacy warning - replaced by automatic cleanup above
|
|
440
479
|
check_legacy_cache()
|
|
441
480
|
|
|
442
481
|
try:
|
|
482
|
+
from ..core.shared.config_loader import ConfigLoader
|
|
443
483
|
from ..services.agents.deployment.agent_deployment import AgentDeploymentService
|
|
444
484
|
from ..services.agents.startup_sync import sync_agents_on_startup
|
|
485
|
+
from ..services.profile_manager import ProfileManager
|
|
445
486
|
from ..utils.progress import ProgressBar
|
|
446
487
|
|
|
488
|
+
# Load active profile if configured
|
|
489
|
+
# Get project root (where .claude-mpm exists)
|
|
490
|
+
from pathlib import Path
|
|
491
|
+
project_root = Path.cwd()
|
|
492
|
+
|
|
493
|
+
profile_manager = ProfileManager(project_dir=project_root)
|
|
494
|
+
config_loader = ConfigLoader()
|
|
495
|
+
main_config = config_loader.load_main_config()
|
|
496
|
+
active_profile = main_config.get("active_profile")
|
|
497
|
+
|
|
498
|
+
if active_profile:
|
|
499
|
+
success = profile_manager.load_profile(active_profile)
|
|
500
|
+
if success:
|
|
501
|
+
summary = profile_manager.get_filtering_summary()
|
|
502
|
+
from ..core.logger import get_logger
|
|
503
|
+
|
|
504
|
+
logger = get_logger("cli")
|
|
505
|
+
logger.info(
|
|
506
|
+
f"Profile '{active_profile}' active: "
|
|
507
|
+
f"{summary['enabled_agents_count']} agents enabled"
|
|
508
|
+
)
|
|
509
|
+
|
|
447
510
|
# Phase 1: Sync files from Git sources
|
|
448
511
|
result = sync_agents_on_startup()
|
|
449
512
|
|
|
@@ -470,19 +533,122 @@ def sync_remote_agents_on_startup():
|
|
|
470
533
|
# Phase 2: Deploy agents from cache to ~/.claude/agents/
|
|
471
534
|
# This mirrors the skills deployment pattern (lines 371-407)
|
|
472
535
|
try:
|
|
473
|
-
# Initialize deployment service
|
|
474
|
-
|
|
536
|
+
# Initialize deployment service with profile-filtered configuration
|
|
537
|
+
from ..core.config import Config
|
|
538
|
+
|
|
539
|
+
deploy_config = None
|
|
540
|
+
if active_profile and profile_manager.active_profile:
|
|
541
|
+
# Create config with excluded agents based on profile
|
|
542
|
+
# Get all agents that should be excluded (not in enabled list)
|
|
543
|
+
from pathlib import Path
|
|
544
|
+
|
|
545
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
546
|
+
if cache_dir.exists():
|
|
547
|
+
# Find all agent files
|
|
548
|
+
# Supports both flat cache and {owner}/{repo}/agents/ structure
|
|
549
|
+
all_agent_files = [
|
|
550
|
+
f
|
|
551
|
+
for f in cache_dir.rglob("*.md")
|
|
552
|
+
if "/agents/" in str(f)
|
|
553
|
+
and f.stem.lower() != "base-agent"
|
|
554
|
+
and f.name.lower() not in {"readme.md", "changelog.md", "contributing.md"}
|
|
555
|
+
]
|
|
556
|
+
|
|
557
|
+
# Build exclusion list for agents not in profile
|
|
558
|
+
excluded_agents = []
|
|
559
|
+
for agent_file in all_agent_files:
|
|
560
|
+
agent_name = agent_file.stem
|
|
561
|
+
if not profile_manager.is_agent_enabled(agent_name):
|
|
562
|
+
excluded_agents.append(agent_name)
|
|
563
|
+
|
|
564
|
+
if excluded_agents:
|
|
565
|
+
# Get singleton config and update with profile settings
|
|
566
|
+
# BUGFIX: Config is a singleton that ignores dict parameter if already initialized.
|
|
567
|
+
# Creating Config({...}) doesn't store excluded_agents - use set() instead.
|
|
568
|
+
deploy_config = Config()
|
|
569
|
+
deploy_config.set("agent_deployment.excluded_agents", excluded_agents)
|
|
570
|
+
deploy_config.set("agent_deployment.filter_non_mpm_agents", False)
|
|
571
|
+
deploy_config.set("agent_deployment.case_sensitive", False)
|
|
572
|
+
deploy_config.set("agent_deployment.exclude_dependencies", False)
|
|
573
|
+
logger.info(
|
|
574
|
+
f"Profile '{active_profile}': Excluding {len(excluded_agents)} agents from deployment"
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
deployment_service = AgentDeploymentService(config=deploy_config)
|
|
475
578
|
|
|
476
579
|
# Count agents in cache to show accurate progress
|
|
477
580
|
from pathlib import Path
|
|
478
581
|
|
|
479
|
-
cache_dir = Path.home() / ".claude-mpm" / "cache" / "
|
|
582
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
480
583
|
agent_count = 0
|
|
481
584
|
|
|
482
585
|
if cache_dir.exists():
|
|
483
|
-
#
|
|
484
|
-
#
|
|
485
|
-
#
|
|
586
|
+
# BUGFIX (cache-count-inflation): Clean up stale cache files
|
|
587
|
+
# from old repositories before counting to prevent inflated counts.
|
|
588
|
+
# Issue: Old caches like bobmatnyc/claude-mpm-agents/agents/
|
|
589
|
+
# were counted alongside current agents, inflating count
|
|
590
|
+
# from 44 to 85.
|
|
591
|
+
#
|
|
592
|
+
# Solution: Remove files with nested /agents/ paths
|
|
593
|
+
# (e.g., cache/agents/user/repo/agents/...)
|
|
594
|
+
# Keep only current agents (e.g., cache/agents/engineer/...)
|
|
595
|
+
removed_count = 0
|
|
596
|
+
stale_dirs = set()
|
|
597
|
+
|
|
598
|
+
for md_file in cache_dir.rglob("*.md"):
|
|
599
|
+
# Stale cache files have multiple /agents/ in their path
|
|
600
|
+
# Current: ~/.claude-mpm/cache/agents/engineer/...
|
|
601
|
+
# (1 occurrence)
|
|
602
|
+
# Old: ~/.claude-mpm/cache/agents/bobmatnyc/.../agents/...
|
|
603
|
+
# (2+ occurrences)
|
|
604
|
+
if str(md_file).count("/agents/") > 1:
|
|
605
|
+
# Track parent directory for cleanup
|
|
606
|
+
# Extract subdirectory under cache/agents/
|
|
607
|
+
# (e.g., "bobmatnyc")
|
|
608
|
+
parts = md_file.parts
|
|
609
|
+
cache_agents_idx = parts.index("agents")
|
|
610
|
+
if cache_agents_idx + 1 < len(parts):
|
|
611
|
+
stale_subdir = parts[cache_agents_idx + 1]
|
|
612
|
+
# Only remove if it's not a known category directory
|
|
613
|
+
if stale_subdir not in [
|
|
614
|
+
"engineer",
|
|
615
|
+
"ops",
|
|
616
|
+
"qa",
|
|
617
|
+
"universal",
|
|
618
|
+
"documentation",
|
|
619
|
+
"claude-mpm",
|
|
620
|
+
"security",
|
|
621
|
+
]:
|
|
622
|
+
stale_dirs.add(cache_dir / stale_subdir)
|
|
623
|
+
|
|
624
|
+
md_file.unlink()
|
|
625
|
+
removed_count += 1
|
|
626
|
+
|
|
627
|
+
# Remove empty stale directories
|
|
628
|
+
for stale_dir in stale_dirs:
|
|
629
|
+
if stale_dir.exists() and stale_dir.is_dir():
|
|
630
|
+
try:
|
|
631
|
+
# Remove directory and all contents
|
|
632
|
+
import shutil
|
|
633
|
+
|
|
634
|
+
shutil.rmtree(stale_dir)
|
|
635
|
+
except Exception:
|
|
636
|
+
pass # Ignore cleanup errors
|
|
637
|
+
|
|
638
|
+
if removed_count > 0:
|
|
639
|
+
from loguru import logger
|
|
640
|
+
|
|
641
|
+
logger.info(
|
|
642
|
+
f"Cleaned up {removed_count} stale cache files "
|
|
643
|
+
f"from old repositories"
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
# Count MD files in cache (agent markdown files from
|
|
647
|
+
# current repos)
|
|
648
|
+
# BUGFIX: Only count files in agent directories,
|
|
649
|
+
# not docs/templates/READMEs
|
|
650
|
+
# Valid agent paths must contain "/agents/" exactly ONCE
|
|
651
|
+
# (current structure)
|
|
486
652
|
# Exclude PM templates, BASE-AGENT, and documentation files
|
|
487
653
|
pm_templates = {
|
|
488
654
|
"base-agent.md",
|
|
@@ -505,25 +671,30 @@ def sync_remote_agents_on_startup():
|
|
|
505
671
|
"auto-deploy-index.md",
|
|
506
672
|
}
|
|
507
673
|
|
|
508
|
-
# Find all markdown files
|
|
674
|
+
# Find all markdown files (after cleanup)
|
|
509
675
|
all_md_files = list(cache_dir.rglob("*.md"))
|
|
510
676
|
|
|
511
677
|
# Filter to only agent files:
|
|
512
|
-
# 1. Must have "/agents/" in path (
|
|
678
|
+
# 1. Must have "/agents/" in path (current structure supports
|
|
679
|
+
# both flat and {owner}/{repo}/agents/ patterns)
|
|
513
680
|
# 2. Must not be in PM templates or doc files
|
|
514
681
|
# 3. Exclude BASE-AGENT.md which is not a deployable agent
|
|
515
|
-
# 4. Exclude build artifacts (dist/, build/, .cache/)
|
|
682
|
+
# 4. Exclude build artifacts (dist/, build/, .cache/)
|
|
683
|
+
# to prevent double-counting
|
|
516
684
|
agent_files = [
|
|
517
685
|
f
|
|
518
686
|
for f in all_md_files
|
|
519
687
|
if (
|
|
520
|
-
# Must be in an agent directory
|
|
688
|
+
# Must be in an agent directory
|
|
689
|
+
# Supports: cache/agents/{category}/... (flat)
|
|
690
|
+
# Supports: cache/agents/{owner}/{repo}/agents/{category}/... (GitHub sync)
|
|
521
691
|
"/agents/" in str(f)
|
|
522
692
|
# Exclude PM templates, doc files, and BASE-AGENT
|
|
523
693
|
and f.name.lower() not in pm_templates
|
|
524
694
|
and f.name.lower() not in doc_files
|
|
525
695
|
and f.name.lower() != "base-agent.md"
|
|
526
|
-
# Exclude build artifacts (prevents double-counting
|
|
696
|
+
# Exclude build artifacts (prevents double-counting
|
|
697
|
+
# source + built files)
|
|
527
698
|
and not any(
|
|
528
699
|
part in str(f).split("/")
|
|
529
700
|
for part in ["dist", "build", ".cache"]
|
|
@@ -539,6 +710,7 @@ def sync_remote_agents_on_startup():
|
|
|
539
710
|
target_dir=deploy_target,
|
|
540
711
|
force_rebuild=False, # Only deploy if versions differ
|
|
541
712
|
deployment_mode="update", # Version-aware updates
|
|
713
|
+
config=deploy_config, # Pass config to respect profile filtering
|
|
542
714
|
)
|
|
543
715
|
|
|
544
716
|
# Get actual counts from deployment result (reflects configured agents)
|
|
@@ -594,27 +766,27 @@ def sync_remote_agents_on_startup():
|
|
|
594
766
|
)
|
|
595
767
|
|
|
596
768
|
# Show total configured agents (deployed + updated + already existing)
|
|
597
|
-
# Include
|
|
769
|
+
# Include cache count for context and removed count if any
|
|
598
770
|
if deployed > 0 or updated > 0:
|
|
599
771
|
if removed > 0:
|
|
600
772
|
deploy_progress.finish(
|
|
601
773
|
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged, "
|
|
602
|
-
f"{removed} removed ({total_configured} configured from {agent_count} in
|
|
774
|
+
f"{removed} removed ({total_configured} configured from {agent_count} files in cache)"
|
|
603
775
|
)
|
|
604
776
|
else:
|
|
605
777
|
deploy_progress.finish(
|
|
606
778
|
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged "
|
|
607
|
-
f"({total_configured} configured from {agent_count} in
|
|
779
|
+
f"({total_configured} configured from {agent_count} files in cache)"
|
|
608
780
|
)
|
|
609
781
|
elif removed > 0:
|
|
610
782
|
deploy_progress.finish(
|
|
611
|
-
f"Complete: {total_configured} agents
|
|
612
|
-
f"{removed} removed ({agent_count}
|
|
783
|
+
f"Complete: {total_configured} agents deployed, "
|
|
784
|
+
f"{removed} removed ({agent_count} files in cache)"
|
|
613
785
|
)
|
|
614
786
|
else:
|
|
615
787
|
deploy_progress.finish(
|
|
616
|
-
f"Complete: {total_configured} agents
|
|
617
|
-
f"({agent_count}
|
|
788
|
+
f"Complete: {total_configured} agents deployed "
|
|
789
|
+
f"({agent_count} files in cache)"
|
|
618
790
|
)
|
|
619
791
|
|
|
620
792
|
# Display deployment errors to user (not just logs)
|
|
@@ -679,13 +851,16 @@ def sync_remote_skills_on_startup():
|
|
|
679
851
|
1. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
|
|
680
852
|
2. Scan deployed agents for skill requirements → save to configuration.yaml
|
|
681
853
|
3. Resolve which skills to deploy (user_defined vs agent_referenced)
|
|
682
|
-
4.
|
|
683
|
-
5.
|
|
854
|
+
4. Apply profile filtering if active
|
|
855
|
+
5. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
|
|
856
|
+
6. Log deployment results with source indication
|
|
684
857
|
"""
|
|
685
858
|
try:
|
|
686
859
|
from pathlib import Path
|
|
687
860
|
|
|
688
861
|
from ..config.skill_sources import SkillSourceConfiguration
|
|
862
|
+
from ..core.shared.config_loader import ConfigLoader
|
|
863
|
+
from ..services.profile_manager import ProfileManager
|
|
689
864
|
from ..services.skills.git_skill_source_manager import GitSkillSourceManager
|
|
690
865
|
from ..services.skills.selective_skill_deployer import (
|
|
691
866
|
get_required_skills_from_agents,
|
|
@@ -694,6 +869,28 @@ def sync_remote_skills_on_startup():
|
|
|
694
869
|
)
|
|
695
870
|
from ..utils.progress import ProgressBar
|
|
696
871
|
|
|
872
|
+
# Load active profile if configured
|
|
873
|
+
# Get project root (where .claude-mpm exists)
|
|
874
|
+
project_root = Path.cwd()
|
|
875
|
+
|
|
876
|
+
profile_manager = ProfileManager(project_dir=project_root)
|
|
877
|
+
config_loader = ConfigLoader()
|
|
878
|
+
main_config = config_loader.load_main_config()
|
|
879
|
+
active_profile = main_config.get("active_profile")
|
|
880
|
+
|
|
881
|
+
if active_profile:
|
|
882
|
+
success = profile_manager.load_profile(active_profile)
|
|
883
|
+
if success:
|
|
884
|
+
from ..core.logger import get_logger
|
|
885
|
+
|
|
886
|
+
logger = get_logger("cli")
|
|
887
|
+
summary = profile_manager.get_filtering_summary()
|
|
888
|
+
logger.info(
|
|
889
|
+
f"Profile '{active_profile}' active: "
|
|
890
|
+
f"{summary['enabled_skills_count']} skills enabled, "
|
|
891
|
+
f"{summary['disabled_patterns_count']} patterns disabled"
|
|
892
|
+
)
|
|
893
|
+
|
|
697
894
|
config = SkillSourceConfiguration()
|
|
698
895
|
manager = GitSkillSourceManager(config)
|
|
699
896
|
|
|
@@ -795,6 +992,48 @@ def sync_remote_skills_on_startup():
|
|
|
795
992
|
# Phase 3: Resolve which skills to deploy (user_defined or agent_referenced)
|
|
796
993
|
skills_to_deploy, skill_source = get_skills_to_deploy(project_config_path)
|
|
797
994
|
|
|
995
|
+
# Phase 4: Apply profile filtering if active
|
|
996
|
+
if active_profile and profile_manager.active_profile:
|
|
997
|
+
# Filter skills based on profile
|
|
998
|
+
if skills_to_deploy:
|
|
999
|
+
# Filter the resolved skill list
|
|
1000
|
+
original_count = len(skills_to_deploy)
|
|
1001
|
+
filtered_skills = [
|
|
1002
|
+
skill
|
|
1003
|
+
for skill in skills_to_deploy
|
|
1004
|
+
if profile_manager.is_skill_enabled(skill)
|
|
1005
|
+
]
|
|
1006
|
+
filtered_count = original_count - len(filtered_skills)
|
|
1007
|
+
|
|
1008
|
+
# SAFEGUARD: Warn if all skills were filtered out (misconfiguration)
|
|
1009
|
+
if not filtered_skills and original_count > 0:
|
|
1010
|
+
logger.warning(
|
|
1011
|
+
f"Profile '{active_profile}' filtered ALL {original_count} skills. "
|
|
1012
|
+
f"This may indicate a naming mismatch in the profile."
|
|
1013
|
+
)
|
|
1014
|
+
elif filtered_count > 0:
|
|
1015
|
+
logger.info(
|
|
1016
|
+
f"Profile '{active_profile}' filtered {filtered_count} skills "
|
|
1017
|
+
f"({len(filtered_skills)} remaining)"
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
skills_to_deploy = filtered_skills
|
|
1021
|
+
skill_source = f"{skill_source} + profile filtered"
|
|
1022
|
+
else:
|
|
1023
|
+
# No explicit skill list - filter from all available
|
|
1024
|
+
all_skills = manager.get_all_skills()
|
|
1025
|
+
filtered_skills = [
|
|
1026
|
+
skill["name"]
|
|
1027
|
+
for skill in all_skills
|
|
1028
|
+
if profile_manager.is_skill_enabled(skill["name"])
|
|
1029
|
+
]
|
|
1030
|
+
skills_to_deploy = filtered_skills
|
|
1031
|
+
skill_source = "profile filtered"
|
|
1032
|
+
logger.info(
|
|
1033
|
+
f"Profile '{active_profile}': "
|
|
1034
|
+
f"{len(filtered_skills)} skills enabled from {len(all_skills)} available"
|
|
1035
|
+
)
|
|
1036
|
+
|
|
798
1037
|
# Get all skills to determine counts
|
|
799
1038
|
all_skills = manager.get_all_skills()
|
|
800
1039
|
total_skill_count = len(all_skills)
|
|
@@ -841,7 +1080,7 @@ def sync_remote_skills_on_startup():
|
|
|
841
1080
|
|
|
842
1081
|
# Show total available skills (deployed + already existing)
|
|
843
1082
|
# Include source indication (user_defined vs agent_referenced)
|
|
844
|
-
# Note: total_skill_count is from
|
|
1083
|
+
# Note: total_skill_count is from cache, total_available is what's deployed/needed
|
|
845
1084
|
source_label = (
|
|
846
1085
|
"user override" if skill_source == "user_defined" else "from agents"
|
|
847
1086
|
)
|
|
@@ -850,22 +1089,22 @@ def sync_remote_skills_on_startup():
|
|
|
850
1089
|
if filtered > 0:
|
|
851
1090
|
deploy_progress.finish(
|
|
852
1091
|
f"Complete: {deployed} new, {skipped} unchanged "
|
|
853
|
-
f"({total_available} {source_label}, {filtered}
|
|
1092
|
+
f"({total_available} {source_label}, {filtered} files in cache)"
|
|
854
1093
|
)
|
|
855
1094
|
else:
|
|
856
1095
|
deploy_progress.finish(
|
|
857
1096
|
f"Complete: {deployed} new, {skipped} unchanged "
|
|
858
|
-
f"({total_available} skills {source_label} from {total_skill_count} in
|
|
1097
|
+
f"({total_available} skills {source_label} from {total_skill_count} files in cache)"
|
|
859
1098
|
)
|
|
860
1099
|
elif filtered > 0:
|
|
861
1100
|
# Skills filtered means agents require fewer skills than available
|
|
862
1101
|
deploy_progress.finish(
|
|
863
|
-
f"No skills needed ({source_label}, {total_skill_count}
|
|
1102
|
+
f"No skills needed ({source_label}, {total_skill_count} files in cache)"
|
|
864
1103
|
)
|
|
865
1104
|
else:
|
|
866
1105
|
deploy_progress.finish(
|
|
867
1106
|
f"Complete: {total_available} skills {source_label} "
|
|
868
|
-
f"({total_skill_count}
|
|
1107
|
+
f"({total_skill_count} files in cache)"
|
|
869
1108
|
)
|
|
870
1109
|
|
|
871
1110
|
# Log deployment errors if any
|
|
@@ -921,7 +1160,7 @@ def show_agent_summary():
|
|
|
921
1160
|
installed_count = len(agent_files)
|
|
922
1161
|
|
|
923
1162
|
# Count available agents in cache (from remote sources)
|
|
924
|
-
cache_dir = Path.home() / ".claude-mpm" / "cache" / "
|
|
1163
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
925
1164
|
available_count = 0
|
|
926
1165
|
if cache_dir.exists():
|
|
927
1166
|
# Use same filtering logic as agent deployment (lines 486-533 in startup.py)
|
|
@@ -966,7 +1205,7 @@ def show_agent_summary():
|
|
|
966
1205
|
# Display summary if we have agents
|
|
967
1206
|
if installed_count > 0 or available_count > 0:
|
|
968
1207
|
print(
|
|
969
|
-
f"✓ Agents: {installed_count}
|
|
1208
|
+
f"✓ Agents: {installed_count} deployed / {max(0, available_count - installed_count)} cached",
|
|
970
1209
|
flush=True,
|
|
971
1210
|
)
|
|
972
1211
|
|
|
@@ -1049,6 +1288,45 @@ def show_skill_summary():
|
|
|
1049
1288
|
logger.debug(f"Failed to generate skill summary: {e}")
|
|
1050
1289
|
|
|
1051
1290
|
|
|
1291
|
+
def verify_and_show_pm_skills():
|
|
1292
|
+
"""Verify PM skills and display status.
|
|
1293
|
+
|
|
1294
|
+
WHY: PM skills are essential for PM agent operation.
|
|
1295
|
+
Shows deployment status and auto-deploys if missing.
|
|
1296
|
+
"""
|
|
1297
|
+
try:
|
|
1298
|
+
from pathlib import Path
|
|
1299
|
+
|
|
1300
|
+
from ..services.pm_skills_deployer import PMSkillsDeployerService
|
|
1301
|
+
|
|
1302
|
+
deployer = PMSkillsDeployerService()
|
|
1303
|
+
project_dir = Path.cwd()
|
|
1304
|
+
|
|
1305
|
+
result = deployer.verify_pm_skills(project_dir)
|
|
1306
|
+
|
|
1307
|
+
if result.verified:
|
|
1308
|
+
# Show verified status
|
|
1309
|
+
print(f"✓ PM skills: {result.skill_count} verified", flush=True)
|
|
1310
|
+
else:
|
|
1311
|
+
# Auto-deploy if missing
|
|
1312
|
+
print("Deploying PM skills...", end="", flush=True)
|
|
1313
|
+
deploy_result = deployer.deploy_pm_skills(project_dir)
|
|
1314
|
+
if deploy_result.success:
|
|
1315
|
+
total = len(deploy_result.deployed) + len(deploy_result.skipped)
|
|
1316
|
+
print(f"\r✓ PM skills: {total} deployed" + " " * 20, flush=True)
|
|
1317
|
+
else:
|
|
1318
|
+
print(f"\r⚠ PM skills: deployment failed" + " " * 20, flush=True)
|
|
1319
|
+
|
|
1320
|
+
except ImportError:
|
|
1321
|
+
# PM skills deployer not available - skip silently
|
|
1322
|
+
pass
|
|
1323
|
+
except Exception as e:
|
|
1324
|
+
from ..core.logger import get_logger
|
|
1325
|
+
|
|
1326
|
+
logger = get_logger("cli")
|
|
1327
|
+
logger.debug(f"PM skills verification failed: {e}")
|
|
1328
|
+
|
|
1329
|
+
|
|
1052
1330
|
def auto_install_chrome_devtools_on_startup():
|
|
1053
1331
|
"""
|
|
1054
1332
|
Automatically install chrome-devtools-mcp on startup if enabled.
|
|
@@ -1123,6 +1401,7 @@ def run_background_services():
|
|
|
1123
1401
|
sync_remote_skills_on_startup() # Override layer: Git-based skills (takes precedence)
|
|
1124
1402
|
discover_and_link_runtime_skills() # Discovery: user-added skills
|
|
1125
1403
|
show_skill_summary() # Display skill counts after deployment
|
|
1404
|
+
verify_and_show_pm_skills() # PM skills verification and status
|
|
1126
1405
|
|
|
1127
1406
|
deploy_output_style_on_startup()
|
|
1128
1407
|
|
|
@@ -1256,18 +1535,10 @@ def verify_mcp_gateway_startup():
|
|
|
1256
1535
|
DESIGN DECISION: This is non-blocking - failures are logged but don't prevent
|
|
1257
1536
|
startup to ensure claude-mpm remains functional even if MCP gateway has issues.
|
|
1258
1537
|
"""
|
|
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
|
|
1538
|
+
# DISABLED: MCP service verification removed - Claude Code handles MCP natively
|
|
1539
|
+
# The previous check warned about missing MCP services, but users should configure
|
|
1540
|
+
# MCP servers through Claude Code's native MCP management, not through claude-mpm.
|
|
1541
|
+
# See: https://docs.anthropic.com/en/docs/claude-code/mcp
|
|
1271
1542
|
|
|
1272
1543
|
try:
|
|
1273
1544
|
import asyncio
|