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.

Files changed (119) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT.md +164 -0
  3. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  4. claude_mpm/agents/MEMORY.md +1 -1
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +739 -1052
  6. claude_mpm/agents/WORKFLOW.md +5 -254
  7. claude_mpm/agents/agent_loader.py +1 -1
  8. claude_mpm/agents/base_agent.json +31 -0
  9. claude_mpm/agents/frontmatter_validator.py +2 -2
  10. claude_mpm/cli/commands/agent_state_manager.py +10 -10
  11. claude_mpm/cli/commands/agents.py +9 -9
  12. claude_mpm/cli/commands/auto_configure.py +4 -4
  13. claude_mpm/cli/commands/configure.py +1 -1
  14. claude_mpm/cli/commands/configure_agent_display.py +10 -0
  15. claude_mpm/cli/commands/mpm_init/core.py +65 -0
  16. claude_mpm/cli/commands/postmortem.py +1 -1
  17. claude_mpm/cli/commands/profile.py +277 -0
  18. claude_mpm/cli/commands/skills.py +14 -18
  19. claude_mpm/cli/executor.py +10 -0
  20. claude_mpm/cli/interactive/agent_wizard.py +2 -2
  21. claude_mpm/cli/parsers/base_parser.py +7 -0
  22. claude_mpm/cli/parsers/profile_parser.py +148 -0
  23. claude_mpm/cli/parsers/skills_parser.py +0 -6
  24. claude_mpm/cli/startup.py +346 -75
  25. claude_mpm/commands/mpm-config.md +13 -250
  26. claude_mpm/commands/mpm-doctor.md +9 -22
  27. claude_mpm/commands/mpm-help.md +5 -206
  28. claude_mpm/commands/mpm-init.md +81 -507
  29. claude_mpm/commands/mpm-monitor.md +15 -402
  30. claude_mpm/commands/mpm-organize.md +61 -441
  31. claude_mpm/commands/mpm-postmortem.md +6 -108
  32. claude_mpm/commands/mpm-session-resume.md +12 -363
  33. claude_mpm/commands/mpm-status.md +5 -69
  34. claude_mpm/commands/mpm-ticket-view.md +52 -495
  35. claude_mpm/commands/mpm-version.md +5 -107
  36. claude_mpm/core/config.py +2 -4
  37. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  38. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  39. claude_mpm/core/optimized_startup.py +59 -0
  40. claude_mpm/core/shared/config_loader.py +1 -1
  41. claude_mpm/core/unified_agent_registry.py +1 -1
  42. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  43. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  44. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  45. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  46. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  47. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  48. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  49. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  50. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  51. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  52. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  53. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  54. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  55. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  56. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  57. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  58. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  59. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  60. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  63. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  64. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  65. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  66. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  67. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  68. claude_mpm/hooks/claude_hooks/hook_handler.py +149 -1
  69. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  70. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  71. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  72. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  73. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  74. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  75. claude_mpm/hooks/claude_hooks/services/connection_manager.py +26 -6
  76. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  77. claude_mpm/init.py +63 -0
  78. claude_mpm/models/git_repository.py +3 -3
  79. claude_mpm/scripts/start_activity_logging.py +0 -0
  80. claude_mpm/services/agents/agent_builder.py +3 -3
  81. claude_mpm/services/agents/cache_git_manager.py +6 -6
  82. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  83. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -2
  84. claude_mpm/services/agents/deployment/agent_format_converter.py +23 -13
  85. claude_mpm/services/agents/deployment/agent_template_builder.py +29 -19
  86. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  87. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  88. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  89. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +169 -26
  90. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +98 -75
  91. claude_mpm/services/agents/git_source_manager.py +19 -4
  92. claude_mpm/services/agents/recommender.py +5 -3
  93. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  94. claude_mpm/services/agents/sources/git_source_sync_service.py +112 -6
  95. claude_mpm/services/agents/startup_sync.py +22 -2
  96. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  97. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  98. claude_mpm/services/git/git_operations_service.py +8 -8
  99. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  100. claude_mpm/services/monitor/server.py +473 -3
  101. claude_mpm/services/pm_skills_deployer.py +711 -0
  102. claude_mpm/services/profile_manager.py +331 -0
  103. claude_mpm/services/skills/git_skill_source_manager.py +101 -3
  104. claude_mpm/services/skills_deployer.py +4 -3
  105. claude_mpm/services/socketio/dashboard_server.py +1 -0
  106. claude_mpm/services/socketio/event_normalizer.py +37 -6
  107. claude_mpm/services/socketio/server/core.py +262 -123
  108. claude_mpm/skills/skill_manager.py +92 -3
  109. claude_mpm/utils/agent_dependency_loader.py +14 -2
  110. claude_mpm/utils/agent_filters.py +1 -1
  111. claude_mpm/utils/migration.py +4 -4
  112. claude_mpm/utils/robust_installer.py +47 -3
  113. {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/METADATA +7 -4
  114. {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/RECORD +118 -79
  115. {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/WHEEL +0 -0
  116. {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/entry_points.txt +0 -0
  117. {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/licenses/LICENSE +0 -0
  118. {claude_mpm-5.4.22.dist-info → claude_mpm-5.4.48.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  119. {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 check_legacy_cache() -> None:
64
- """Check for legacy cache/agents/ directory and warn user.
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
- WHY: cache/agents/ is deprecated in favor of cache/remote-agents/.
67
- Research confirmed that cache/remote-agents/ is the canonical location
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 DECISIONS:
71
- - Non-blocking warning: Doesn't stop execution, just informs user
72
- - Migration guidance: Provides clear path to migrate
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
- home = Path.home()
76
- legacy_cache = home / ".claude-mpm" / "cache" / "agents"
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
- # Skip if already migrated or no legacy cache
81
- if migration_marker.exists() or not legacy_cache.exists():
82
- return
85
+ from ..core.logger import get_logger
83
86
 
84
- # Check if legacy cache has actual agent files
85
- legacy_files = list(legacy_cache.glob("*.md")) + list(legacy_cache.glob("*.json"))
86
- if not legacy_files:
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
- # Only warn if canonical cache doesn't exist (indicating unmigrated system)
90
- if not canonical_cache.exists():
91
- warnings.warn(
92
- f"\n⚠️ DEPRECATION: Legacy cache directory detected\n"
93
- f" Location: {legacy_cache}\n"
94
- f" Files found: {len(legacy_files)}\n\n"
95
- f"The 'cache/agents/' directory is deprecated. Please migrate to 'cache/remote-agents/'.\n"
96
- f"Run: python scripts/migrate_cache_to_remote_agents.py\n",
97
- DeprecationWarning,
98
- stacklevel=2,
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. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
435
- 2. Deploy agents to ~/.claude/agents/ - Phase 2 progress bar
436
- 3. Cleanup orphaned agents (ours but no longer deployed) - Phase 3
437
- 4. Log deployment results
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
- # Check for legacy cache and warn user if found
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
- deployment_service = AgentDeploymentService()
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" / "remote-agents"
582
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
480
583
  agent_count = 0
481
584
 
482
585
  if cache_dir.exists():
483
- # Count MD files in cache (agent markdown files from Git)
484
- # BUGFIX: Only count files in agent directories, not docs/templates/READMEs
485
- # Valid agent paths must contain "/agents/" or be in root-level category dirs
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 (from git repos)
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/) to prevent double-counting
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 (from git repos like bobmatnyc/claude-mpm-agents/agents/)
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 source + built files)
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 repo count for context and removed count if any
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 repo)"
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 repo)"
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 ready - all unchanged, "
612
- f"{removed} removed ({agent_count} available in repo)"
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 ready - all unchanged "
617
- f"({agent_count} available in repo)"
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. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
683
- 5. Log deployment results with source indication
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 the repo, total_available is what's deployed/needed
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} available in repo)"
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 repo)"
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} available in repo)"
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} available in repo)"
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" / "remote-agents"
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} installed / {available_count} available",
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
- # Quick verification of MCP services installation
1260
- try:
1261
- from ..core.logger import get_logger
1262
- from ..services.mcp_service_verifier import verify_mcp_services_on_startup
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