claude-mpm 5.4.14__py3-none-any.whl → 5.4.36__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 (103) 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/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +363 -817
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +1 -1
  9. claude_mpm/agents/base_agent.json +31 -0
  10. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  11. claude_mpm/cli/commands/agent_state_manager.py +10 -10
  12. claude_mpm/cli/commands/agents.py +9 -40
  13. claude_mpm/cli/commands/auto_configure.py +4 -4
  14. claude_mpm/cli/commands/configure.py +1 -1
  15. claude_mpm/cli/commands/postmortem.py +1 -1
  16. claude_mpm/cli/commands/skills.py +193 -187
  17. claude_mpm/cli/interactive/agent_wizard.py +2 -2
  18. claude_mpm/cli/parsers/agents_parser.py +0 -9
  19. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  20. claude_mpm/cli/startup.py +330 -78
  21. claude_mpm/commands/mpm-config.md +1 -2
  22. claude_mpm/commands/mpm-help.md +14 -95
  23. claude_mpm/commands/mpm-organize.md +350 -153
  24. claude_mpm/core/config.py +2 -4
  25. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  26. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  27. claude_mpm/core/unified_agent_registry.py +1 -1
  28. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  29. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  30. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  31. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  32. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  33. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  34. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  35. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  36. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  37. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  38. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  39. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  40. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  41. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  42. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  43. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  44. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  45. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  46. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  47. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  48. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  49. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  50. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  51. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  52. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  53. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  54. claude_mpm/hooks/claude_hooks/event_handlers.py +5 -0
  55. claude_mpm/hooks/claude_hooks/hook_handler.py +149 -1
  56. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  57. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  58. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  59. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  60. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/services/connection_manager.py +26 -6
  63. claude_mpm/models/git_repository.py +3 -3
  64. claude_mpm/scripts/start_activity_logging.py +0 -0
  65. claude_mpm/services/agents/cache_git_manager.py +6 -6
  66. claude_mpm/services/agents/deployment/agent_deployment.py +7 -7
  67. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -2
  68. claude_mpm/services/agents/deployment/agent_template_builder.py +2 -2
  69. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  70. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +20 -22
  71. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +55 -53
  72. claude_mpm/services/agents/git_source_manager.py +2 -2
  73. claude_mpm/services/agents/recommender.py +5 -3
  74. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  75. claude_mpm/services/agents/sources/git_source_sync_service.py +5 -5
  76. claude_mpm/services/agents/startup_sync.py +22 -2
  77. claude_mpm/services/command_deployment_service.py +10 -0
  78. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  79. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  80. claude_mpm/services/git/git_operations_service.py +8 -8
  81. claude_mpm/services/monitor/server.py +473 -3
  82. claude_mpm/services/skills/selective_skill_deployer.py +475 -1
  83. claude_mpm/services/skills_deployer.py +62 -6
  84. claude_mpm/services/socketio/dashboard_server.py +1 -0
  85. claude_mpm/services/socketio/event_normalizer.py +37 -6
  86. claude_mpm/services/socketio/server/core.py +262 -123
  87. claude_mpm/utils/agent_dependency_loader.py +14 -2
  88. claude_mpm/utils/agent_filters.py +1 -1
  89. claude_mpm/utils/migration.py +4 -4
  90. claude_mpm/utils/robust_installer.py +47 -3
  91. {claude_mpm-5.4.14.dist-info → claude_mpm-5.4.36.dist-info}/METADATA +5 -3
  92. {claude_mpm-5.4.14.dist-info → claude_mpm-5.4.36.dist-info}/RECORD +96 -66
  93. claude_mpm/cli/commands/agents_detect.py +0 -380
  94. claude_mpm/cli/commands/agents_recommend.py +0 -309
  95. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  96. claude_mpm/commands/mpm-agents-detect.md +0 -177
  97. claude_mpm/commands/mpm-agents-list.md +0 -131
  98. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  99. {claude_mpm-5.4.14.dist-info → claude_mpm-5.4.36.dist-info}/WHEEL +0 -0
  100. {claude_mpm-5.4.14.dist-info → claude_mpm-5.4.36.dist-info}/entry_points.txt +0 -0
  101. {claude_mpm-5.4.14.dist-info → claude_mpm-5.4.36.dist-info}/licenses/LICENSE +0 -0
  102. {claude_mpm-5.4.14.dist-info → claude_mpm-5.4.36.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  103. {claude_mpm-5.4.14.dist-info → claude_mpm-5.4.36.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
 
@@ -61,42 +60,11 @@ def sync_hooks_on_startup(quiet: bool = False) -> bool:
61
60
 
62
61
 
63
62
  def check_legacy_cache() -> None:
64
- """Check for legacy cache/agents/ directory and warn user.
63
+ """Deprecated: Legacy cache checking is no longer needed.
65
64
 
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.
69
-
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
65
+ This function is kept for backward compatibility but does nothing.
66
+ All agent cache operations now use the standardized cache/agents/ directory.
74
67
  """
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"
79
-
80
- # Skip if already migrated or no legacy cache
81
- if migration_marker.exists() or not legacy_cache.exists():
82
- return
83
-
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
- return
88
-
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
- )
100
68
 
101
69
 
102
70
  def setup_early_environment(argv):
@@ -476,13 +444,76 @@ def sync_remote_agents_on_startup():
476
444
  # Count agents in cache to show accurate progress
477
445
  from pathlib import Path
478
446
 
479
- cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
447
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
480
448
  agent_count = 0
481
449
 
482
450
  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
451
+ # BUGFIX (cache-count-inflation): Clean up stale cache files
452
+ # from old repositories before counting to prevent inflated counts.
453
+ # Issue: Old caches like bobmatnyc/claude-mpm-agents/agents/
454
+ # were counted alongside current agents, inflating count
455
+ # from 44 to 85.
456
+ #
457
+ # Solution: Remove files with nested /agents/ paths
458
+ # (e.g., cache/agents/user/repo/agents/...)
459
+ # Keep only current agents (e.g., cache/agents/engineer/...)
460
+ removed_count = 0
461
+ stale_dirs = set()
462
+
463
+ for md_file in cache_dir.rglob("*.md"):
464
+ # Stale cache files have multiple /agents/ in their path
465
+ # Current: ~/.claude-mpm/cache/agents/engineer/...
466
+ # (1 occurrence)
467
+ # Old: ~/.claude-mpm/cache/agents/bobmatnyc/.../agents/...
468
+ # (2+ occurrences)
469
+ if str(md_file).count("/agents/") > 1:
470
+ # Track parent directory for cleanup
471
+ # Extract subdirectory under cache/agents/
472
+ # (e.g., "bobmatnyc")
473
+ parts = md_file.parts
474
+ cache_agents_idx = parts.index("agents")
475
+ if cache_agents_idx + 1 < len(parts):
476
+ stale_subdir = parts[cache_agents_idx + 1]
477
+ # Only remove if it's not a known category directory
478
+ if stale_subdir not in [
479
+ "engineer",
480
+ "ops",
481
+ "qa",
482
+ "universal",
483
+ "documentation",
484
+ "claude-mpm",
485
+ "security",
486
+ ]:
487
+ stale_dirs.add(cache_dir / stale_subdir)
488
+
489
+ md_file.unlink()
490
+ removed_count += 1
491
+
492
+ # Remove empty stale directories
493
+ for stale_dir in stale_dirs:
494
+ if stale_dir.exists() and stale_dir.is_dir():
495
+ try:
496
+ # Remove directory and all contents
497
+ import shutil
498
+
499
+ shutil.rmtree(stale_dir)
500
+ except Exception:
501
+ pass # Ignore cleanup errors
502
+
503
+ if removed_count > 0:
504
+ from loguru import logger
505
+
506
+ logger.info(
507
+ f"Cleaned up {removed_count} stale cache files "
508
+ f"from old repositories"
509
+ )
510
+
511
+ # Count MD files in cache (agent markdown files from
512
+ # current repos)
513
+ # BUGFIX: Only count files in agent directories,
514
+ # not docs/templates/READMEs
515
+ # Valid agent paths must contain "/agents/" exactly ONCE
516
+ # (current structure)
486
517
  # Exclude PM templates, BASE-AGENT, and documentation files
487
518
  pm_templates = {
488
519
  "base-agent.md",
@@ -505,25 +536,34 @@ def sync_remote_agents_on_startup():
505
536
  "auto-deploy-index.md",
506
537
  }
507
538
 
508
- # Find all markdown files
539
+ # Find all markdown files (after cleanup)
509
540
  all_md_files = list(cache_dir.rglob("*.md"))
510
541
 
511
542
  # Filter to only agent files:
512
- # 1. Must have "/agents/" in path (from git repos)
543
+ # 1. Must have "/agents/" in path exactly ONCE
544
+ # (current structure)
513
545
  # 2. Must not be in PM templates or doc files
514
546
  # 3. Exclude BASE-AGENT.md which is not a deployable agent
515
- # 4. Exclude build artifacts (dist/, build/, .cache/) to prevent double-counting
547
+ # 4. Exclude build artifacts (dist/, build/, .cache/)
548
+ # to prevent double-counting
516
549
  agent_files = [
517
550
  f
518
551
  for f in all_md_files
519
552
  if (
520
- # Must be in an agent directory (from git repos like bobmatnyc/claude-mpm-agents/agents/)
553
+ # Must be in an agent directory (from current
554
+ # cache structure)
521
555
  "/agents/" in str(f)
556
+ # NEW: Only ONE /agents/ in path (excludes old
557
+ # nested repos)
558
+ # This prevents counting old caches like
559
+ # bobmatnyc/claude-mpm-agents/agents/...
560
+ and str(f).count("/agents/") == 1
522
561
  # Exclude PM templates, doc files, and BASE-AGENT
523
562
  and f.name.lower() not in pm_templates
524
563
  and f.name.lower() not in doc_files
525
564
  and f.name.lower() != "base-agent.md"
526
- # Exclude build artifacts (prevents double-counting source + built files)
565
+ # Exclude build artifacts (prevents double-counting
566
+ # source + built files)
527
567
  and not any(
528
568
  part in str(f).split("/")
529
569
  for part in ["dist", "build", ".cache"]
@@ -594,27 +634,27 @@ def sync_remote_agents_on_startup():
594
634
  )
595
635
 
596
636
  # Show total configured agents (deployed + updated + already existing)
597
- # Include repo count for context and removed count if any
637
+ # Include cache count for context and removed count if any
598
638
  if deployed > 0 or updated > 0:
599
639
  if removed > 0:
600
640
  deploy_progress.finish(
601
641
  f"Complete: {deployed} new, {updated} updated, {skipped} unchanged, "
602
- f"{removed} removed ({total_configured} configured from {agent_count} in repo)"
642
+ f"{removed} removed ({total_configured} configured from {agent_count} files in cache)"
603
643
  )
604
644
  else:
605
645
  deploy_progress.finish(
606
646
  f"Complete: {deployed} new, {updated} updated, {skipped} unchanged "
607
- f"({total_configured} configured from {agent_count} in repo)"
647
+ f"({total_configured} configured from {agent_count} files in cache)"
608
648
  )
609
649
  elif removed > 0:
610
650
  deploy_progress.finish(
611
- f"Complete: {total_configured} agents ready - all unchanged, "
612
- f"{removed} removed ({agent_count} available in repo)"
651
+ f"Complete: {total_configured} agents deployed, "
652
+ f"{removed} removed ({agent_count} files in cache)"
613
653
  )
614
654
  else:
615
655
  deploy_progress.finish(
616
- f"Complete: {total_configured} agents ready - all unchanged "
617
- f"({agent_count} available in repo)"
656
+ f"Complete: {total_configured} agents deployed "
657
+ f"({agent_count} files in cache)"
618
658
  )
619
659
 
620
660
  # Display deployment errors to user (not just logs)
@@ -677,14 +717,21 @@ def sync_remote_skills_on_startup():
677
717
 
678
718
  Workflow:
679
719
  1. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
680
- 2. Deploy skills to ~/.claude/skills/ with flat structure - Phase 2 progress bar
681
- 3. Log deployment results
720
+ 2. Scan deployed agents for skill requirements save to configuration.yaml
721
+ 3. Resolve which skills to deploy (user_defined vs agent_referenced)
722
+ 4. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
723
+ 5. Log deployment results with source indication
682
724
  """
683
725
  try:
684
726
  from pathlib import Path
685
727
 
686
728
  from ..config.skill_sources import SkillSourceConfiguration
687
729
  from ..services.skills.git_skill_source_manager import GitSkillSourceManager
730
+ from ..services.skills.selective_skill_deployer import (
731
+ get_required_skills_from_agents,
732
+ get_skills_to_deploy,
733
+ save_agent_skills_to_config,
734
+ )
688
735
  from ..utils.progress import ProgressBar
689
736
 
690
737
  config = SkillSourceConfiguration()
@@ -773,37 +820,37 @@ def sync_remote_skills_on_startup():
773
820
  f"Complete: {downloaded} files downloaded ({total_skill_dirs} skills)"
774
821
  )
775
822
 
776
- # Phase 2: Deploy skills to ~/.claude/skills/
777
- # This flattens nested Git structure (e.g., collaboration/parallel-agents/SKILL.md)
778
- # into flat deployment (e.g., collaboration-dispatching-parallel-agents/SKILL.md)
823
+ # Phase 2: Scan agents and save to configuration.yaml
824
+ # This step populates configuration.yaml with agent-referenced skills
779
825
  if results["synced_count"] > 0:
780
- # Get required skills from deployed agents (selective deployment)
781
- from ..services.skills.selective_skill_deployer import (
782
- get_required_skills_from_agents,
783
- )
784
-
785
826
  agents_dir = Path.cwd() / ".claude" / "agents"
786
- required_skills = get_required_skills_from_agents(agents_dir)
827
+
828
+ # Scan agents for skill requirements
829
+ agent_skills = get_required_skills_from_agents(agents_dir)
830
+
831
+ # Save to project-level configuration.yaml
832
+ project_config_path = Path.cwd() / ".claude-mpm" / "configuration.yaml"
833
+ save_agent_skills_to_config(list(agent_skills), project_config_path)
834
+
835
+ # Phase 3: Resolve which skills to deploy (user_defined or agent_referenced)
836
+ skills_to_deploy, skill_source = get_skills_to_deploy(project_config_path)
787
837
 
788
838
  # Get all skills to determine counts
789
839
  all_skills = manager.get_all_skills()
790
840
  total_skill_count = len(all_skills)
791
841
 
792
- # Determine skill count based on whether we have agent requirements
793
- if required_skills:
794
- # Selective deployment: only skills required by deployed agents
795
- skill_count = len(required_skills)
796
- else:
797
- # No agent requirements found - deploy all skills
798
- skill_count = total_skill_count
842
+ # Determine skill count based on resolution
843
+ skill_count = (
844
+ len(skills_to_deploy) if skills_to_deploy else total_skill_count
845
+ )
799
846
 
800
847
  if skill_count > 0:
801
- # Deploy skills with selective filter (if agent requirements exist)
848
+ # Deploy skills with resolved filter
802
849
  # Deploy to project directory (like agents), not user directory
803
850
  deployment_result = manager.deploy_skills(
804
851
  target_dir=Path.cwd() / ".claude" / "skills",
805
852
  force=False,
806
- skill_filter=required_skills if required_skills else None,
853
+ skill_filter=set(skills_to_deploy) if skills_to_deploy else None,
807
854
  )
808
855
 
809
856
  # Get actual counts from deployment result
@@ -833,28 +880,32 @@ def sync_remote_skills_on_startup():
833
880
  deploy_progress.update(1)
834
881
 
835
882
  # Show total available skills (deployed + already existing)
836
- # Include filtered count if selective deployment was used
837
- # Note: total_skill_count is from the repo, total_available is what's deployed/needed
883
+ # Include source indication (user_defined vs agent_referenced)
884
+ # Note: total_skill_count is from cache, total_available is what's deployed/needed
885
+ source_label = (
886
+ "user override" if skill_source == "user_defined" else "from agents"
887
+ )
888
+
838
889
  if deployed > 0:
839
890
  if filtered > 0:
840
891
  deploy_progress.finish(
841
892
  f"Complete: {deployed} new, {skipped} unchanged "
842
- f"({total_available} required by agents, {filtered} available in repo)"
893
+ f"({total_available} {source_label}, {filtered} files in cache)"
843
894
  )
844
895
  else:
845
896
  deploy_progress.finish(
846
897
  f"Complete: {deployed} new, {skipped} unchanged "
847
- f"({total_available} skills deployed from {total_skill_count} in repo)"
898
+ f"({total_available} skills {source_label} from {total_skill_count} files in cache)"
848
899
  )
849
900
  elif filtered > 0:
850
901
  # Skills filtered means agents require fewer skills than available
851
902
  deploy_progress.finish(
852
- f"Agents require no skills ({total_skill_count} available in repo)"
903
+ f"No skills needed ({source_label}, {total_skill_count} files in cache)"
853
904
  )
854
905
  else:
855
906
  deploy_progress.finish(
856
- f"Complete: {total_available} skills deployed for agents "
857
- f"({total_skill_count} available in repo)"
907
+ f"Complete: {total_available} skills {source_label} "
908
+ f"({total_skill_count} files in cache)"
858
909
  )
859
910
 
860
911
  # Log deployment errors if any
@@ -883,6 +934,202 @@ def sync_remote_skills_on_startup():
883
934
  # Continue execution - skill sync failure shouldn't block startup
884
935
 
885
936
 
937
+ def show_agent_summary():
938
+ """
939
+ Display agent availability summary on startup.
940
+
941
+ WHY: Users should see at a glance how many agents are available and installed
942
+ without having to run /mpm-agents list.
943
+
944
+ DESIGN DECISION: Fast, non-blocking check that counts agents from the deployment
945
+ directory. Shows simple "X installed / Y available" format. Failures are silent
946
+ to avoid blocking startup.
947
+ """
948
+ try:
949
+ from pathlib import Path
950
+
951
+ # Count deployed agents (installed)
952
+ deploy_target = Path.cwd() / ".claude" / "agents"
953
+ installed_count = 0
954
+ if deploy_target.exists():
955
+ # Count .md files, excluding README and other docs
956
+ agent_files = [
957
+ f
958
+ for f in deploy_target.glob("*.md")
959
+ if not f.name.startswith(("README", "INSTRUCTIONS", "."))
960
+ ]
961
+ installed_count = len(agent_files)
962
+
963
+ # Count available agents in cache (from remote sources)
964
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
965
+ available_count = 0
966
+ if cache_dir.exists():
967
+ # Use same filtering logic as agent deployment (lines 486-533 in startup.py)
968
+ pm_templates = {
969
+ "base-agent.md",
970
+ "circuit_breakers.md",
971
+ "pm_examples.md",
972
+ "pm_red_flags.md",
973
+ "research_gate_examples.md",
974
+ "response_format.md",
975
+ "ticket_completeness_examples.md",
976
+ "validation_templates.md",
977
+ "git_file_tracking.md",
978
+ }
979
+ doc_files = {
980
+ "readme.md",
981
+ "changelog.md",
982
+ "contributing.md",
983
+ "implementation-summary.md",
984
+ "reorganization-plan.md",
985
+ "auto-deploy-index.md",
986
+ }
987
+
988
+ # Find all markdown files in agents/ directories
989
+ all_md_files = list(cache_dir.rglob("*.md"))
990
+ agent_files = [
991
+ f
992
+ for f in all_md_files
993
+ if (
994
+ "/agents/" in str(f)
995
+ and f.name.lower() not in pm_templates
996
+ and f.name.lower() not in doc_files
997
+ and f.name.lower() != "base-agent.md"
998
+ and not any(
999
+ part in str(f).split("/")
1000
+ for part in ["dist", "build", ".cache"]
1001
+ )
1002
+ )
1003
+ ]
1004
+ available_count = len(agent_files)
1005
+
1006
+ # Display summary if we have agents
1007
+ if installed_count > 0 or available_count > 0:
1008
+ print(
1009
+ f"✓ Agents: {installed_count} deployed / {available_count} cached",
1010
+ flush=True,
1011
+ )
1012
+
1013
+ except Exception as e:
1014
+ # Silent failure - agent summary is informational only
1015
+ from ..core.logger import get_logger
1016
+
1017
+ logger = get_logger("cli")
1018
+ logger.debug(f"Failed to generate agent summary: {e}")
1019
+
1020
+
1021
+ def show_skill_summary():
1022
+ """
1023
+ Display skill availability summary on startup.
1024
+
1025
+ WHY: Users should see at a glance how many skills are deployed and available
1026
+ from collections, similar to the agent summary.
1027
+
1028
+ DESIGN DECISION: Fast, non-blocking check that counts skills from deployment
1029
+ directory and collection repos. Shows "X installed (Y available)" format.
1030
+ Failures are silent to avoid blocking startup.
1031
+ """
1032
+ try:
1033
+ from pathlib import Path
1034
+
1035
+ # Count deployed skills (installed)
1036
+ skills_dir = Path.home() / ".claude" / "skills"
1037
+ installed_count = 0
1038
+ if skills_dir.exists():
1039
+ # Count directories with SKILL.md (excludes collection repos)
1040
+ # Exclude collection directories (obra-superpowers, etc.)
1041
+ skill_dirs = [
1042
+ d
1043
+ for d in skills_dir.iterdir()
1044
+ if d.is_dir()
1045
+ and (d / "SKILL.md").exists()
1046
+ and not (d / ".git").exists() # Exclude collection repos
1047
+ ]
1048
+ installed_count = len(skill_dirs)
1049
+
1050
+ # Count available skills in collections
1051
+ available_count = 0
1052
+ if skills_dir.exists():
1053
+ # Scan all collection directories (those with .git)
1054
+ for collection_dir in skills_dir.iterdir():
1055
+ if (
1056
+ not collection_dir.is_dir()
1057
+ or not (collection_dir / ".git").exists()
1058
+ ):
1059
+ continue
1060
+
1061
+ # Count skill directories in this collection
1062
+ # Skills can be nested in: skills/category/skill-name/SKILL.md
1063
+ # or in flat structure: skill-name/SKILL.md
1064
+ for root, dirs, files in os.walk(collection_dir):
1065
+ if "SKILL.md" in files:
1066
+ # Exclude build artifacts and hidden directories (within the collection)
1067
+ # Get relative path from collection_dir to avoid excluding based on .claude parent
1068
+ root_path = Path(root)
1069
+ relative_parts = root_path.relative_to(collection_dir).parts
1070
+ if not any(
1071
+ part.startswith(".")
1072
+ or part in ["dist", "build", "__pycache__"]
1073
+ for part in relative_parts
1074
+ ):
1075
+ available_count += 1
1076
+
1077
+ # Display summary if we have skills
1078
+ if installed_count > 0 or available_count > 0:
1079
+ print(
1080
+ f"✓ Skills: {installed_count} installed ({available_count} available)",
1081
+ flush=True,
1082
+ )
1083
+
1084
+ except Exception as e:
1085
+ # Silent failure - skill summary is informational only
1086
+ from ..core.logger import get_logger
1087
+
1088
+ logger = get_logger("cli")
1089
+ logger.debug(f"Failed to generate skill summary: {e}")
1090
+
1091
+
1092
+ def auto_install_chrome_devtools_on_startup():
1093
+ """
1094
+ Automatically install chrome-devtools-mcp on startup if enabled.
1095
+
1096
+ WHY: Browser automation capabilities should be available out-of-the-box without
1097
+ manual MCP server configuration. chrome-devtools-mcp provides powerful browser
1098
+ interaction tools for Claude Code.
1099
+
1100
+ DESIGN DECISION: Non-blocking installation that doesn't prevent startup if it fails.
1101
+ Respects user configuration setting (enabled by default). Only installs if not
1102
+ already configured in Claude.
1103
+ """
1104
+ try:
1105
+ # Check if auto-install is disabled in config
1106
+ from ..config.config_loader import ConfigLoader
1107
+
1108
+ config_loader = ConfigLoader()
1109
+ try:
1110
+ config = config_loader.load_main_config()
1111
+ chrome_devtools_config = config.get("chrome_devtools", {})
1112
+ if not chrome_devtools_config.get("auto_install", True):
1113
+ # Auto-install disabled, skip silently
1114
+ return
1115
+ except Exception:
1116
+ # If config loading fails, assume auto-install is enabled (default)
1117
+ pass
1118
+
1119
+ # Import and run chrome-devtools installation
1120
+ from ..cli.chrome_devtools_installer import auto_install_chrome_devtools
1121
+
1122
+ auto_install_chrome_devtools(quiet=False)
1123
+
1124
+ except Exception as e:
1125
+ # Import logger here to avoid circular imports
1126
+ from ..core.logger import get_logger
1127
+
1128
+ logger = get_logger("cli")
1129
+ logger.debug(f"Failed to auto-install chrome-devtools-mcp: {e}")
1130
+ # Continue execution - chrome-devtools installation failure shouldn't block startup
1131
+
1132
+
886
1133
  def run_background_services():
887
1134
  """
888
1135
  Initialize all background services on startup.
@@ -905,6 +1152,7 @@ def run_background_services():
905
1152
  verify_mcp_gateway_startup()
906
1153
  check_for_updates_async()
907
1154
  sync_remote_agents_on_startup() # Sync agents from remote sources
1155
+ show_agent_summary() # Display agent counts after deployment
908
1156
 
909
1157
  # Skills deployment order (precedence: remote > bundled)
910
1158
  # 1. Deploy bundled skills first (base layer from package)
@@ -914,9 +1162,13 @@ def run_background_services():
914
1162
  deploy_bundled_skills() # Base layer: package-bundled skills
915
1163
  sync_remote_skills_on_startup() # Override layer: Git-based skills (takes precedence)
916
1164
  discover_and_link_runtime_skills() # Discovery: user-added skills
1165
+ show_skill_summary() # Display skill counts after deployment
917
1166
 
918
1167
  deploy_output_style_on_startup()
919
1168
 
1169
+ # Auto-install chrome-devtools-mcp for browser automation
1170
+ auto_install_chrome_devtools_on_startup()
1171
+
920
1172
 
921
1173
  def setup_mcp_server_logging(args):
922
1174
  """
@@ -262,5 +262,4 @@ default_configuration:
262
262
  - `/mpm-doctor`: Diagnose configuration issues
263
263
  - `/mpm-init`: Initialize project configuration
264
264
  - `/mpm-agents`: Manually manage agents
265
- - `/mpm-agents-detect`: View detected toolchain only
266
- - `/mpm-agents-recommend`: Show recommendations only
265
+ - `/mpm-configure`: Unified configuration interface (includes toolchain detection and recommendations)