claude-mpm 5.1.9__py3-none-any.whl → 5.4.22__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 (176) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +290 -34
  5. claude_mpm/agents/agent_loader.py +13 -44
  6. claude_mpm/agents/templates/circuit-breakers.md +138 -1
  7. claude_mpm/cli/__main__.py +4 -0
  8. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  9. claude_mpm/cli/commands/agent_state_manager.py +8 -17
  10. claude_mpm/cli/commands/agents.py +0 -31
  11. claude_mpm/cli/commands/auto_configure.py +210 -25
  12. claude_mpm/cli/commands/config.py +88 -2
  13. claude_mpm/cli/commands/configure.py +1097 -158
  14. claude_mpm/cli/commands/configure_agent_display.py +15 -6
  15. claude_mpm/cli/commands/mpm_init/core.py +160 -46
  16. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  17. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  18. claude_mpm/cli/commands/skills.py +214 -189
  19. claude_mpm/cli/commands/summarize.py +413 -0
  20. claude_mpm/cli/executor.py +11 -3
  21. claude_mpm/cli/parsers/agents_parser.py +0 -9
  22. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  23. claude_mpm/cli/parsers/base_parser.py +5 -0
  24. claude_mpm/cli/parsers/config_parser.py +153 -83
  25. claude_mpm/cli/parsers/skills_parser.py +3 -2
  26. claude_mpm/cli/startup.py +550 -94
  27. claude_mpm/commands/mpm-config.md +265 -0
  28. claude_mpm/commands/mpm-help.md +14 -95
  29. claude_mpm/commands/mpm-organize.md +500 -0
  30. claude_mpm/config/agent_sources.py +27 -0
  31. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  32. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  33. claude_mpm/core/framework_loader.py +4 -2
  34. claude_mpm/core/logger.py +13 -0
  35. claude_mpm/core/socketio_pool.py +3 -3
  36. claude_mpm/core/unified_agent_registry.py +5 -15
  37. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  38. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  39. claude_mpm/hooks/claude_hooks/hook_handler.py +6 -0
  40. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  41. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  42. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  43. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  44. claude_mpm/hooks/memory_integration_hook.py +46 -1
  45. claude_mpm/init.py +0 -19
  46. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  47. claude_mpm/scripts/launch_monitor.py +93 -13
  48. claude_mpm/scripts/start_activity_logging.py +0 -0
  49. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  50. claude_mpm/services/agents/agent_review_service.py +280 -0
  51. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
  52. claude_mpm/services/agents/deployment/agent_template_builder.py +4 -2
  53. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +78 -9
  54. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +335 -53
  55. claude_mpm/services/agents/git_source_manager.py +34 -0
  56. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  57. claude_mpm/services/agents/sources/git_source_sync_service.py +8 -1
  58. claude_mpm/services/agents/toolchain_detector.py +10 -6
  59. claude_mpm/services/analysis/__init__.py +11 -1
  60. claude_mpm/services/analysis/clone_detector.py +1030 -0
  61. claude_mpm/services/command_deployment_service.py +81 -10
  62. claude_mpm/services/event_bus/config.py +3 -1
  63. claude_mpm/services/git/git_operations_service.py +93 -8
  64. claude_mpm/services/monitor/daemon.py +9 -2
  65. claude_mpm/services/monitor/daemon_manager.py +39 -3
  66. claude_mpm/services/monitor/server.py +225 -19
  67. claude_mpm/services/self_upgrade_service.py +120 -12
  68. claude_mpm/services/skills/__init__.py +3 -0
  69. claude_mpm/services/skills/git_skill_source_manager.py +32 -2
  70. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  71. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  72. claude_mpm/services/skills_deployer.py +126 -9
  73. claude_mpm/services/socketio/event_normalizer.py +15 -1
  74. claude_mpm/services/socketio/server/core.py +160 -21
  75. claude_mpm/services/version_control/git_operations.py +103 -0
  76. claude_mpm/utils/agent_filters.py +17 -44
  77. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/METADATA +47 -84
  78. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/RECORD +82 -161
  79. claude_mpm-5.4.22.dist-info/entry_points.txt +5 -0
  80. claude_mpm-5.4.22.dist-info/licenses/LICENSE +94 -0
  81. claude_mpm-5.4.22.dist-info/licenses/LICENSE-FAQ.md +153 -0
  82. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  83. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  84. claude_mpm/agents/BASE_ENGINEER.md +0 -658
  85. claude_mpm/agents/BASE_OPS.md +0 -219
  86. claude_mpm/agents/BASE_PM.md +0 -480
  87. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  88. claude_mpm/agents/BASE_QA.md +0 -167
  89. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  90. claude_mpm/agents/base_agent.json +0 -31
  91. claude_mpm/agents/base_agent_loader.py +0 -601
  92. claude_mpm/cli/commands/agents_detect.py +0 -380
  93. claude_mpm/cli/commands/agents_recommend.py +0 -309
  94. claude_mpm/cli/ticket_cli.py +0 -35
  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/commands/mpm-config-view.md +0 -150
  100. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  101. claude_mpm/dashboard/analysis_runner.py +0 -455
  102. claude_mpm/dashboard/index.html +0 -13
  103. claude_mpm/dashboard/open_dashboard.py +0 -66
  104. claude_mpm/dashboard/static/css/activity.css +0 -1958
  105. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  106. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  107. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  108. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  109. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  110. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  111. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  112. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  113. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  114. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  115. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  116. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  117. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  118. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  119. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  120. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  121. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  122. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  123. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  124. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  125. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  126. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  127. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  128. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  129. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  130. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  131. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  132. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  133. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  134. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  135. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  136. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  137. claude_mpm/dashboard/templates/code_simple.html +0 -153
  138. claude_mpm/dashboard/templates/index.html +0 -606
  139. claude_mpm/dashboard/test_dashboard.html +0 -372
  140. claude_mpm/scripts/mcp_server.py +0 -75
  141. claude_mpm/scripts/mcp_wrapper.py +0 -39
  142. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  143. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  144. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  145. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  146. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  147. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  148. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  149. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  150. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  151. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  152. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  153. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  154. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  155. claude_mpm/services/mcp_gateway/main.py +0 -589
  156. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  157. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  158. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  159. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  160. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  161. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  162. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  163. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  164. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  165. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  166. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  167. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  168. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  169. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  170. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  171. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  172. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  173. claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
  174. claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
  175. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/WHEEL +0 -0
  176. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/top_level.txt +0 -0
@@ -537,7 +537,7 @@ class SkillsManagementCommand(BaseCommand):
537
537
  toolchain = getattr(args, "toolchain", None)
538
538
  categories = getattr(args, "categories", None)
539
539
  force = getattr(args, "force", False)
540
- all_skills = getattr(args, "all", False)
540
+ all_skills = getattr(args, "all_skills", False)
541
541
 
542
542
  if collection:
543
543
  console.print(
@@ -554,17 +554,36 @@ class SkillsManagementCommand(BaseCommand):
554
554
  "[yellow]No toolchain specified. Use --toolchain to filter by language,[/yellow]"
555
555
  )
556
556
  console.print(
557
- "[yellow]or --all to deploy all available skills.[/yellow]\n"
557
+ "[yellow]or --all-skills to deploy all available skills (not just agent-referenced).[/yellow]\n"
558
558
  )
559
559
 
560
+ # Selective deployment is enabled by default (deploy only agent-referenced skills)
561
+ # Use --all-skills to disable selective mode
560
562
  result = self.skills_deployer.deploy_skills(
561
563
  collection=collection,
562
564
  toolchain=toolchain,
563
565
  categories=categories,
564
566
  force=force,
567
+ selective=not all_skills, # Disable selective mode if --all-skills is set
565
568
  )
566
569
 
567
570
  # Display results
571
+ # Show selective mode summary
572
+ if result.get("selective_mode"):
573
+ total_available = result.get("total_available", 0)
574
+ deployed_count = result["deployed_count"]
575
+ console.print(
576
+ f"[cyan]📌 Selective deployment: {deployed_count} agent-referenced skills "
577
+ f"(out of {total_available} available)[/cyan]"
578
+ )
579
+ console.print(
580
+ "[dim]Use --all-skills to deploy all available skills[/dim]\n"
581
+ )
582
+ else:
583
+ console.print(
584
+ "[cyan]📦 Deploying all available skills (selective mode disabled)[/cyan]\n"
585
+ )
586
+
568
587
  if result["deployed_count"] > 0:
569
588
  console.print(
570
589
  f"[green]✓ Deployed {result['deployed_count']} skill(s):[/green]"
@@ -953,23 +972,37 @@ class SkillsManagementCommand(BaseCommand):
953
972
  console.print(f"[red]Unexpected error: {e}[/red]")
954
973
 
955
974
  def _configure_skills(self, args) -> CommandResult:
956
- """Interactive skills configuration with checkbox selection.
975
+ """Interactive skills configuration using configuration.yaml.
957
976
 
958
977
  Provides checkbox-based selection interface matching agents configure UX:
959
- - Status column showing Installed/Available
960
- - Pre-selection for installed skills
961
- - Apply/Adjust/Cancel menu
962
- - While loop for adjustment
963
- - Simplified labels (checkbox state only)
964
-
965
- This is Option 3 (Hybrid approach): Separate command for interactive mode
966
- while keeping deploy-github for CLI automation.
978
+ - Shows current mode (user_defined vs agent_referenced)
979
+ - If agent mode: shows agent-scanned skills
980
+ - Allows switching to user_defined mode and selecting skills
981
+ - Can reset to agent mode (clears user_defined)
982
+ - Saves selections to configuration.yaml
983
+
984
+ Configuration structure:
985
+ ```yaml
986
+ skills:
987
+ agent_referenced: # Auto-populated from agent scan (read-only)
988
+ - systematic-debugging
989
+ - typescript-core
990
+ user_defined: # User override - if set, ONLY these are deployed
991
+ [] # Empty = use agent_referenced
992
+ ```
967
993
  """
968
994
  try:
995
+ from pathlib import Path
996
+
969
997
  import questionary
998
+ import yaml
970
999
  from questionary import Choice, Style
971
1000
  from rich.prompt import Prompt
972
1001
 
1002
+ from ...services.skills.selective_skill_deployer import (
1003
+ get_skills_to_deploy,
1004
+ )
1005
+
973
1006
  # Questionary style (matching agents configure)
974
1007
  QUESTIONARY_STYLE = Style(
975
1008
  [
@@ -994,203 +1027,197 @@ class SkillsManagementCommand(BaseCommand):
994
1027
  ]
995
1028
  )
996
1029
 
997
- console.print("\n[bold cyan]Interactive Skills Configuration[/bold cyan]\n")
998
- console.print(
999
- "[dim]Select skills to install/uninstall using checkboxes[/dim]"
1000
- )
1001
- console.print("[dim]● = Installed, ○ = Available[/dim]\n")
1030
+ console.print("\n[bold cyan]Skills Configuration Manager[/bold cyan]\n")
1002
1031
 
1003
- # Get deployed skills for status detection
1004
- deployed_result = self.skills_deployer.check_deployed_skills()
1005
- deployed_skills = {
1006
- skill["name"] for skill in deployed_result.get("skills", [])
1007
- }
1032
+ # Load current configuration
1033
+ project_config_path = Path.cwd() / ".claude-mpm" / "configuration.yaml"
1034
+ skills_to_deploy, current_mode = get_skills_to_deploy(project_config_path)
1008
1035
 
1009
- # Get available skills from GitHub
1010
- console.print("[dim]Fetching available skills from GitHub...[/dim]\n")
1011
- available_result = self.skills_deployer.list_available_skills()
1036
+ # Display current mode and skill count
1037
+ console.print(f"[bold]Current Mode:[/bold] [cyan]{current_mode}[/cyan]")
1038
+ console.print(
1039
+ f"[bold]Active Skills:[/bold] {len(skills_to_deploy)} skills\n"
1040
+ )
1012
1041
 
1013
- if available_result.get("error"):
1014
- console.print(f"[red]Error: {available_result['error']}[/red]")
1015
- return CommandResult(
1016
- success=False, message=available_result["error"], exit_code=1
1042
+ if current_mode == "agent_referenced":
1043
+ console.print(
1044
+ "[dim]Agent mode: Skills are auto-detected from deployed agents[/dim]"
1045
+ )
1046
+ console.print(
1047
+ "[dim]Switch to user mode to manually select skills[/dim]\n"
1017
1048
  )
1049
+ else:
1050
+ console.print(
1051
+ "[dim]User mode: You've manually selected which skills to deploy[/dim]"
1052
+ )
1053
+ console.print("[dim]Reset to agent mode to use auto-detection[/dim]\n")
1054
+
1055
+ # Offer mode switching
1056
+ action_choices = [
1057
+ Choice("View current skills", value="view"),
1058
+ Choice("Switch to user mode (manual selection)", value="switch_user"),
1059
+ Choice("Reset to agent mode (auto-detection)", value="reset_agent"),
1060
+ Choice("Cancel", value="cancel"),
1061
+ ]
1062
+
1063
+ action = questionary.select(
1064
+ "What would you like to do?",
1065
+ choices=action_choices,
1066
+ style=QUESTIONARY_STYLE,
1067
+ ).ask()
1068
+
1069
+ if action == "cancel" or action is None:
1070
+ console.print("[yellow]Configuration cancelled[/yellow]")
1071
+ return CommandResult(success=True, exit_code=0)
1018
1072
 
1019
- # Flatten skills by category
1020
- all_skills = []
1021
- for category, skills in available_result.get("by_category", {}).items():
1022
- for skill in skills:
1023
- skill_info = {
1024
- "name": skill.get("name", "unknown"),
1025
- "category": category,
1026
- "is_deployed": skill.get("name", "unknown") in deployed_skills,
1027
- }
1028
- all_skills.append(skill_info)
1029
-
1030
- # Sort by deployed status (deployed first), then by name
1031
- all_skills.sort(key=lambda s: (not s["is_deployed"], s["name"]))
1032
-
1033
- # Build checkbox choices with pre-selection
1034
- # Loop to allow adjusting selection
1035
- while True:
1036
- skill_choices = []
1037
- skill_map = {} # For lookup after selection
1038
-
1039
- for skill in all_skills:
1040
- skill_name = skill["name"]
1041
- category = skill["category"]
1042
- is_deployed = skill["is_deployed"]
1043
-
1044
- # Simple format: "skill-name (category)"
1045
- # Checkbox state (checked/unchecked) indicates installed status
1046
- choice_text = f"{skill_name} ({category})"
1047
-
1048
- # Pre-select if deployed
1049
- choice = Choice(
1050
- title=choice_text, value=skill_name, checked=is_deployed
1051
- )
1073
+ if action == "view":
1074
+ # Display current skills
1075
+ console.print("\n[bold]Current Skills:[/bold]\n")
1076
+ for skill in sorted(skills_to_deploy):
1077
+ console.print(f" • {skill}")
1078
+ console.print()
1079
+ Prompt.ask("\nPress Enter to continue")
1080
+ return CommandResult(success=True, exit_code=0)
1052
1081
 
1053
- skill_choices.append(choice)
1054
- skill_map[skill_name] = skill
1082
+ if action == "reset_agent":
1083
+ # Reset to agent mode by clearing user_defined
1084
+ with open(project_config_path, encoding="utf-8") as f:
1085
+ config = yaml.safe_load(f) or {}
1055
1086
 
1056
- # Display checkbox selection
1057
- selected_skills = questionary.checkbox(
1058
- "Select skills (Space to toggle, Enter to confirm):",
1059
- choices=skill_choices,
1060
- style=QUESTIONARY_STYLE,
1061
- ).ask()
1087
+ if "skills" not in config:
1088
+ config["skills"] = {}
1062
1089
 
1063
- if selected_skills is None:
1064
- # User cancelled (Ctrl+C)
1065
- console.print("[yellow]Skills configuration cancelled[/yellow]")
1066
- return CommandResult(success=True, exit_code=0)
1090
+ config["skills"]["user_defined"] = []
1067
1091
 
1068
- # Determine changes
1069
- to_install = []
1070
- to_remove = []
1092
+ with open(project_config_path, "w", encoding="utf-8") as f:
1093
+ yaml.dump(config, f, default_flow_style=False, sort_keys=False)
1071
1094
 
1072
- for skill in all_skills:
1073
- skill_name = skill["name"]
1074
- is_deployed = skill["is_deployed"]
1075
- is_selected = skill_name in selected_skills
1095
+ console.print(
1096
+ "\n[green]✓ Reset to agent mode - skills will be auto-detected from agents[/green]\n"
1097
+ )
1098
+ Prompt.ask("\nPress Enter to continue")
1099
+ return CommandResult(success=True, exit_code=0)
1076
1100
 
1077
- if is_selected and not is_deployed:
1078
- to_install.append(skill_name)
1079
- elif not is_selected and is_deployed:
1080
- to_remove.append(skill_name)
1101
+ # Switch to user mode - manual skill selection
1102
+ if action == "switch_user":
1103
+ console.print(
1104
+ "\n[bold cyan]Switching to User Mode - Manual Skill Selection[/bold cyan]\n"
1105
+ )
1106
+ console.print("[dim]Fetching available skills from GitHub...[/dim]\n")
1081
1107
 
1082
- # Show summary of changes
1083
- console.print("\n[bold]Changes to apply:[/bold]")
1084
- if to_install:
1085
- console.print(
1086
- f"\n[green]✓ Install ({len(to_install)} skills):[/green]"
1087
- )
1088
- for skill in to_install:
1089
- console.print(f" • {skill}")
1108
+ # Get available skills
1109
+ available_result = self.skills_deployer.list_available_skills()
1090
1110
 
1091
- if to_remove:
1092
- console.print(
1093
- f"\n[yellow]✗ Remove ({len(to_remove)} skills):[/yellow]"
1111
+ if available_result.get("error"):
1112
+ console.print(f"[red]Error: {available_result['error']}[/red]")
1113
+ return CommandResult(
1114
+ success=False, message=available_result["error"], exit_code=1
1094
1115
  )
1095
- for skill in to_remove:
1096
- console.print(f" • {skill}")
1097
1116
 
1098
- if not to_install and not to_remove:
1099
- console.print(
1100
- "\n[dim]No changes (selection matches current deployment)[/dim]"
1101
- )
1117
+ # Flatten skills by category
1118
+ all_skills = []
1119
+ for category, skills in available_result.get("by_category", {}).items():
1120
+ for skill in skills:
1121
+ skill_name = skill.get("name", "unknown")
1122
+ is_currently_selected = skill_name in skills_to_deploy
1123
+ skill_info = {
1124
+ "name": skill_name,
1125
+ "category": category,
1126
+ "is_selected": is_currently_selected,
1127
+ }
1128
+ all_skills.append(skill_info)
1129
+
1130
+ # Sort by selection status (selected first), then by name
1131
+ all_skills.sort(key=lambda s: (not s["is_selected"], s["name"]))
1132
+
1133
+ # Build checkbox choices
1134
+ while True:
1135
+ skill_choices = []
1136
+
1137
+ for skill in all_skills:
1138
+ skill_name = skill["name"]
1139
+ category = skill["category"]
1140
+ is_selected = skill["is_selected"]
1141
+
1142
+ # Format: "skill-name (category)"
1143
+ choice_text = f"{skill_name} ({category})"
1144
+
1145
+ # Pre-select if currently in skills_to_deploy
1146
+ choice = Choice(
1147
+ title=choice_text, value=skill_name, checked=is_selected
1148
+ )
1102
1149
 
1103
- console.print()
1150
+ skill_choices.append(choice)
1151
+
1152
+ # Display checkbox selection
1153
+ selected_skills = questionary.checkbox(
1154
+ "Select skills (Space to toggle, Enter to confirm):",
1155
+ choices=skill_choices,
1156
+ style=QUESTIONARY_STYLE,
1157
+ ).ask()
1158
+
1159
+ if selected_skills is None:
1160
+ # User cancelled (Ctrl+C)
1161
+ console.print("[yellow]Skills configuration cancelled[/yellow]")
1162
+ return CommandResult(success=True, exit_code=0)
1163
+
1164
+ # Show summary
1165
+ console.print("\n[bold]Selected Skills:[/bold]")
1166
+ console.print(f" {len(selected_skills)} skills selected\n")
1167
+
1168
+ if selected_skills:
1169
+ for skill in sorted(selected_skills):
1170
+ console.print(f" • {skill}")
1171
+ console.print()
1172
+
1173
+ # Ask user to confirm, adjust, or cancel
1174
+ confirm_action = questionary.select(
1175
+ "\nWhat would you like to do?",
1176
+ choices=[
1177
+ Choice("Save to configuration", value="apply"),
1178
+ Choice("Adjust selection", value="adjust"),
1179
+ Choice("Cancel", value="cancel"),
1180
+ ],
1181
+ default="apply",
1182
+ style=QUESTIONARY_STYLE,
1183
+ ).ask()
1184
+
1185
+ if confirm_action == "cancel":
1186
+ console.print("[yellow]Configuration cancelled[/yellow]")
1187
+ Prompt.ask("\nPress Enter to continue")
1188
+ return CommandResult(success=True, exit_code=0)
1189
+
1190
+ if confirm_action == "adjust":
1191
+ # Update selection state and loop back
1192
+ for skill in all_skills:
1193
+ skill["is_selected"] = skill["name"] in selected_skills
1194
+ console.print("\n[dim]Adjusting selection...[/dim]\n")
1195
+ continue
1196
+
1197
+ # Save to configuration.yaml
1198
+ with open(project_config_path, encoding="utf-8") as f:
1199
+ config = yaml.safe_load(f) or {}
1200
+
1201
+ if "skills" not in config:
1202
+ config["skills"] = {}
1203
+
1204
+ config["skills"]["user_defined"] = sorted(selected_skills)
1205
+
1206
+ with open(project_config_path, "w", encoding="utf-8") as f:
1207
+ yaml.dump(config, f, default_flow_style=False, sort_keys=False)
1104
1208
 
1105
- # Ask user to confirm, adjust, or cancel
1106
- action = questionary.select(
1107
- "\nWhat would you like to do?",
1108
- choices=[
1109
- Choice("Apply these changes", value="apply"),
1110
- Choice("Adjust selection", value="adjust"),
1111
- Choice("Cancel", value="cancel"),
1112
- ],
1113
- default="apply",
1114
- style=QUESTIONARY_STYLE,
1115
- ).ask()
1116
-
1117
- if action == "cancel":
1118
- console.print("[yellow]Changes cancelled[/yellow]")
1119
- Prompt.ask("\nPress Enter to continue")
1120
- return CommandResult(success=True, exit_code=0)
1121
- if action == "adjust":
1122
- # Loop back to skill selection
1123
- console.print("\n[dim]Adjusting selection...[/dim]\n")
1124
- continue
1125
-
1126
- # Apply changes
1127
- success = True
1128
- errors = []
1129
-
1130
- # Install skills
1131
- if to_install:
1132
- console.print("\n[bold cyan]Installing skills...[/bold cyan]\n")
1133
- for skill_name in to_install:
1134
- try:
1135
- # Deploy single skill
1136
- result = self.skills_deployer.deploy_skills(
1137
- skill_names=[skill_name], force=False
1138
- )
1139
-
1140
- if result.get("errors"):
1141
- errors.extend(result["errors"])
1142
- success = False
1143
- else:
1144
- console.print(
1145
- f"[green]✓ Installed: {skill_name}[/green]"
1146
- )
1147
- except Exception as e:
1148
- errors.append(f"Failed to install {skill_name}: {e}")
1149
- success = False
1150
-
1151
- # Remove skills
1152
- if to_remove:
1153
- console.print("\n[bold yellow]Removing skills...[/bold yellow]\n")
1154
- for skill_name in to_remove:
1155
- try:
1156
- # Remove single skill
1157
- result = self.skills_deployer.remove_skills(
1158
- skill_names=[skill_name]
1159
- )
1160
-
1161
- if result.get("errors"):
1162
- errors.extend(result["errors"])
1163
- success = False
1164
- else:
1165
- console.print(
1166
- f"[yellow]✗ Removed: {skill_name}[/yellow]"
1167
- )
1168
- except Exception as e:
1169
- errors.append(f"Failed to remove {skill_name}: {e}")
1170
- success = False
1171
-
1172
- # Show errors if any
1173
- if errors:
1174
- console.print(f"\n[red]✗ {len(errors)} error(s):[/red]")
1175
- for error in errors:
1176
- console.print(f" • {error}")
1177
-
1178
- # Show restart instructions
1179
- if success and (to_install or to_remove):
1180
1209
  console.print(
1181
- "\n[bold green]✓ Changes applied successfully![/bold green]"
1210
+ f"\n[green]✓ Saved {len(selected_skills)} skills to user_defined mode[/green]"
1182
1211
  )
1183
- console.print("\n[yellow]⚠️ Important:[/yellow]")
1184
- console.print(" Restart Claude Code for changes to take effect")
1185
-
1186
- console.print()
1187
- Prompt.ask("\nPress Enter to continue")
1212
+ console.print(
1213
+ "[yellow]⚠️ Important:[/yellow] Run [cyan]claude-mpm init[/cyan] to deploy these skills\n"
1214
+ )
1215
+ Prompt.ask("\nPress Enter to continue")
1188
1216
 
1189
- # Exit the loop after successful execution
1190
- break
1217
+ # Exit the loop after successful save
1218
+ break
1191
1219
 
1192
- exit_code = 0 if success else 1
1193
- return CommandResult(success=success, exit_code=exit_code)
1220
+ return CommandResult(success=True, exit_code=0)
1194
1221
 
1195
1222
  except Exception as e:
1196
1223
  console.print(f"[red]Error in skills configuration: {e}[/red]")
@@ -1199,8 +1226,6 @@ class SkillsManagementCommand(BaseCommand):
1199
1226
  console.print(f"[dim]{traceback.format_exc()}[/dim]")
1200
1227
  return CommandResult(success=False, message=str(e), exit_code=1)
1201
1228
 
1202
- return CommandResult(success=False, message=str(e), exit_code=1)
1203
-
1204
1229
 
1205
1230
  def manage_skills(args) -> int:
1206
1231
  """