claude-mpm 5.4.73__py3-none-any.whl → 5.4.74__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.
@@ -656,32 +656,84 @@ class ConfigureCommand(BaseCommand):
656
656
  self.behavior_manager.manage_behaviors()
657
657
 
658
658
  def _manage_skills(self) -> None:
659
- """Skills management interface."""
659
+ """Skills management interface with table display."""
660
660
  from ...cli.interactive.skills_wizard import SkillsWizard
661
+ from ...skills.registry import get_registry
661
662
  from ...skills.skill_manager import get_manager
662
663
 
663
664
  wizard = SkillsWizard()
664
665
  manager = get_manager()
666
+ registry = get_registry()
665
667
 
666
668
  while True:
667
669
  self.console.clear()
668
670
  self._display_header()
669
671
 
670
- self.console.print("\n[bold]Skills Management Options:[/bold]\n")
671
- self.console.print(" [1] View Available Skills")
672
- self.console.print(" [2] Configure Skills for Agents")
673
- self.console.print(" [3] View Current Skill Mappings")
674
- self.console.print(" [4] Auto-Link Skills to Agents")
675
- self.console.print(" [b] Back to Main Menu")
672
+ # Display skills table
673
+ self._display_skills_table(registry)
674
+
675
+ # Show action options
676
+ self.console.print("\n[bold]Actions:[/bold]")
677
+ self.console.print(" [1] Toggle skill installation")
678
+ self.console.print(" [2] Configure skills for agents")
679
+ self.console.print(" [3] View current skill mappings")
680
+ self.console.print(" [4] Auto-link skills to agents")
681
+ self.console.print(" [b] Back to main menu")
676
682
  self.console.print()
677
683
 
678
684
  choice = Prompt.ask("[bold blue]Select an option[/bold blue]", default="b")
679
685
 
680
686
  if choice == "1":
681
- # View available skills
687
+ # Toggle skill installation
682
688
  self.console.clear()
683
689
  self._display_header()
684
- wizard.list_available_skills()
690
+ self._display_skills_table(registry)
691
+
692
+ skill_num = Prompt.ask(
693
+ "\n[bold blue]Enter skill number to toggle (or 'b' to go back)[/bold blue]",
694
+ default="b",
695
+ )
696
+
697
+ if skill_num == "b":
698
+ continue
699
+
700
+ try:
701
+ skill_idx = int(skill_num) - 1
702
+ all_skills = self._get_all_skills_sorted(registry)
703
+
704
+ if 0 <= skill_idx < len(all_skills):
705
+ skill = all_skills[skill_idx]
706
+ deployed_ids = self._get_deployed_skill_ids()
707
+
708
+ if skill.skill_id in deployed_ids:
709
+ # Uninstall
710
+ confirm = Confirm.ask(
711
+ f"\n[yellow]Uninstall skill '{skill.name}'?[/yellow]",
712
+ default=False,
713
+ )
714
+ if confirm:
715
+ self._uninstall_skill(skill)
716
+ self.console.print(
717
+ f"\n[green]✓ Skill '{skill.name}' uninstalled[/green]"
718
+ )
719
+ else:
720
+ # Install
721
+ confirm = Confirm.ask(
722
+ f"\n[cyan]Install skill '{skill.name}'?[/cyan]",
723
+ default=True,
724
+ )
725
+ if confirm:
726
+ self._install_skill(skill)
727
+ self.console.print(
728
+ f"\n[green]✓ Skill '{skill.name}' installed[/green]"
729
+ )
730
+ else:
731
+ self.console.print("[red]Invalid skill number[/red]")
732
+ except ValueError:
733
+ self.console.print(
734
+ "[red]Invalid input. Please enter a number.[/red]"
735
+ )
736
+
685
737
  Prompt.ask("\nPress Enter to continue")
686
738
 
687
739
  elif choice == "2":
@@ -805,6 +857,116 @@ class ConfigureCommand(BaseCommand):
805
857
  self.console.print("[red]Invalid choice. Please try again.[/red]")
806
858
  Prompt.ask("\nPress Enter to continue")
807
859
 
860
+ def _display_skills_table(self, registry) -> None:
861
+ """Display skills in a table format like agents."""
862
+ from rich import box
863
+ from rich.table import Table
864
+
865
+ # Get all skills and deployed skill IDs
866
+ all_skills = self._get_all_skills_sorted(registry)
867
+ deployed_ids = self._get_deployed_skill_ids()
868
+
869
+ # Create table with same styling as agents table
870
+ table = Table(show_header=True, header_style="bold cyan", box=box.ROUNDED)
871
+ table.add_column("#", style="bright_black", width=6)
872
+ table.add_column("Skill ID", style="bright_black", overflow="ellipsis")
873
+ table.add_column("Name", style="bright_cyan", overflow="ellipsis")
874
+ table.add_column("Source", style="bright_yellow")
875
+ table.add_column("Status", style="bright_black")
876
+
877
+ # Populate table
878
+ for i, skill in enumerate(all_skills, 1):
879
+ # Determine status
880
+ if skill.skill_id in deployed_ids:
881
+ status = "[green]Installed[/green]"
882
+ else:
883
+ status = "Available"
884
+
885
+ # Determine source label
886
+ if skill.source == "bundled":
887
+ source = "MPM Skills"
888
+ elif skill.source == "user":
889
+ source = "User Skills"
890
+ elif skill.source == "project":
891
+ source = "Project Skills"
892
+ else:
893
+ source = skill.source.title()
894
+
895
+ # Get display name (fallback to skill_id with formatting)
896
+ name = skill.name or skill.skill_id.replace("-", " ").title()
897
+
898
+ table.add_row(str(i), skill.skill_id, name, source, status)
899
+
900
+ self.console.print(table)
901
+
902
+ # Show summary
903
+ installed_count = len([s for s in all_skills if s.skill_id in deployed_ids])
904
+ self.console.print(
905
+ f"\nShowing {len(all_skills)} skills ({installed_count} installed)"
906
+ )
907
+
908
+ def _get_all_skills_sorted(self, registry):
909
+ """Get all skills from registry, sorted by source and name."""
910
+ # Get skills from all sources
911
+ bundled = registry.list_skills(source="bundled")
912
+ user = registry.list_skills(source="user")
913
+ project = registry.list_skills(source="project")
914
+
915
+ # Combine and sort: bundled first, then user, then project
916
+ # Within each group, sort by name
917
+ all_skills = []
918
+ all_skills.extend(sorted(bundled, key=lambda s: s.name.lower()))
919
+ all_skills.extend(sorted(user, key=lambda s: s.name.lower()))
920
+ all_skills.extend(sorted(project, key=lambda s: s.name.lower()))
921
+
922
+ return all_skills
923
+
924
+ def _get_deployed_skill_ids(self) -> set:
925
+ """Get set of deployed skill IDs from .claude/skills/ directory."""
926
+ from pathlib import Path
927
+
928
+ skills_dir = Path.cwd() / ".claude" / "skills"
929
+ if not skills_dir.exists():
930
+ return set()
931
+
932
+ # Each deployed skill is a directory in .claude/skills/
933
+ deployed_ids = set()
934
+ for skill_dir in skills_dir.iterdir():
935
+ if skill_dir.is_dir() and not skill_dir.name.startswith("."):
936
+ deployed_ids.add(skill_dir.name)
937
+
938
+ return deployed_ids
939
+
940
+ def _install_skill(self, skill) -> None:
941
+ """Install a skill to .claude/skills/ directory."""
942
+ import shutil
943
+ from pathlib import Path
944
+
945
+ # Target directory
946
+ target_dir = Path.cwd() / ".claude" / "skills" / skill.skill_id
947
+ target_dir.mkdir(parents=True, exist_ok=True)
948
+
949
+ # Copy skill file(s)
950
+ if skill.path.is_file():
951
+ # Single file skill - copy to skill.md in target directory
952
+ shutil.copy2(skill.path, target_dir / "skill.md")
953
+ elif skill.path.is_dir():
954
+ # Directory-based skill - copy all contents
955
+ for item in skill.path.iterdir():
956
+ if item.is_file():
957
+ shutil.copy2(item, target_dir / item.name)
958
+ elif item.is_dir():
959
+ shutil.copytree(item, target_dir / item.name, dirs_exist_ok=True)
960
+
961
+ def _uninstall_skill(self, skill) -> None:
962
+ """Uninstall a skill from .claude/skills/ directory."""
963
+ import shutil
964
+ from pathlib import Path
965
+
966
+ target_dir = Path.cwd() / ".claude" / "skills" / skill.skill_id
967
+ if target_dir.exists():
968
+ shutil.rmtree(target_dir)
969
+
808
970
  def _display_behavior_files(self) -> None:
809
971
  """Display current behavior files."""
810
972
  self.behavior_manager.display_behavior_files()
@@ -1437,18 +1599,20 @@ class ConfigureCommand(BaseCommand):
1437
1599
 
1438
1600
  # Add inline control: Select/Deselect all from this collection
1439
1601
  if all_selected:
1602
+ deselect_value = f"__DESELECT_ALL_{collection_id}__"
1440
1603
  choices.append(
1441
1604
  Choice(
1442
- f" [Deselect all from {collection_id}]",
1443
- value=f"__DESELECT_ALL_{collection_id}__",
1605
+ f" [Deselect all from {collection_id}]", # nosec B608
1606
+ value=deselect_value,
1444
1607
  checked=False,
1445
1608
  )
1446
1609
  )
1447
1610
  else:
1611
+ select_value = f"__SELECT_ALL_{collection_id}__"
1448
1612
  choices.append(
1449
1613
  Choice(
1450
- f" [Select all from {collection_id}]",
1451
- value=f"__SELECT_ALL_{collection_id}__",
1614
+ f" [Select all from {collection_id}]", # nosec B608
1615
+ value=select_value,
1452
1616
  checked=False,
1453
1617
  )
1454
1618
  )
@@ -2329,7 +2493,8 @@ class ConfigureCommand(BaseCommand):
2329
2493
  f" Detection Quality: [{'green' if summary.get('detection_quality') == 'high' else 'yellow'}]{summary.get('detection_quality', 'unknown')}[/]"
2330
2494
  )
2331
2495
  self.console.print()
2332
- except Exception:
2496
+ except Exception: # nosec B110 - Suppress broad except for failed safety check
2497
+ # Silent failure on safety check - non-critical feature
2333
2498
  pass
2334
2499
 
2335
2500
  # Build mapping: agent_id -> AgentConfig
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 5.4.73
3
+ Version: 5.4.74
4
4
  Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
5
5
  Author-email: Bob Matsuoka <bob@matsuoka.com>
6
6
  Maintainer: Claude MPM Team
@@ -60,7 +60,7 @@ claude_mpm/cli/commands/auto_configure.py,sha256=0Suzil6O0SBNeHUCwHOkt2q7gfuXRTy
60
60
  claude_mpm/cli/commands/cleanup.py,sha256=RQikOGLuLFWXzjeoHArdr5FA4Pf7tSK9w2NXL4vCrok,19769
61
61
  claude_mpm/cli/commands/cleanup_orphaned_agents.py,sha256=JR8crvgrz7Sa6d-SI-gKywok5S9rwc_DzDVk_h85sVs,4467
62
62
  claude_mpm/cli/commands/config.py,sha256=2M9VUPYcQkBUCIyyB-v1qTL3xYvao9YI2l_JGBUDauA,23374
63
- claude_mpm/cli/commands/configure.py,sha256=IrmAtzR4jnTt0YYWYd4fFEOzMVVxB_NU4Xzg2cbxKtk,113160
63
+ claude_mpm/cli/commands/configure.py,sha256=EB3G9mJ2thGg4oBvMl1KoBFBA3qh2iGaFZxClgSWS0Q,119990
64
64
  claude_mpm/cli/commands/configure_agent_display.py,sha256=oSvUhR861o_Pyqmop4ACAQNjwL02-Rf6TMqFvmQNh24,10575
65
65
  claude_mpm/cli/commands/configure_behavior_manager.py,sha256=_tfpcKW0KgMGO52q6IHFXL3W5xwjC8-q2_KpIvHVuoI,6827
66
66
  claude_mpm/cli/commands/configure_hook_manager.py,sha256=1X5brU6cgKRRF-2lQYA0aiKD7ZjTClqNHUSWuayktEw,9205
@@ -993,10 +993,10 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
993
993
  claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
994
994
  claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
995
995
  claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
996
- claude_mpm-5.4.73.dist-info/licenses/LICENSE,sha256=ca3y_Rk4aPrbF6f62z8Ht5MJM9OAvbGlHvEDcj9vUQ4,3867
997
- claude_mpm-5.4.73.dist-info/licenses/LICENSE-FAQ.md,sha256=TxfEkXVCK98RzDOer09puc7JVCP_q_bN4dHtZKHCMcM,5104
998
- claude_mpm-5.4.73.dist-info/METADATA,sha256=ssUdTTD0LjoFtCfQfcTF4kmOZYeJ6zc5MD1hGZygw3c,38503
999
- claude_mpm-5.4.73.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1000
- claude_mpm-5.4.73.dist-info/entry_points.txt,sha256=n-Uk4vwHPpuvu-g_I7-GHORzTnN_m6iyOsoLveKKD0E,228
1001
- claude_mpm-5.4.73.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
1002
- claude_mpm-5.4.73.dist-info/RECORD,,
996
+ claude_mpm-5.4.74.dist-info/licenses/LICENSE,sha256=ca3y_Rk4aPrbF6f62z8Ht5MJM9OAvbGlHvEDcj9vUQ4,3867
997
+ claude_mpm-5.4.74.dist-info/licenses/LICENSE-FAQ.md,sha256=TxfEkXVCK98RzDOer09puc7JVCP_q_bN4dHtZKHCMcM,5104
998
+ claude_mpm-5.4.74.dist-info/METADATA,sha256=nh2EhPg2C9U9MGIo3T-M0Z5hU6F6oBNUkgqk47yM3sU,38503
999
+ claude_mpm-5.4.74.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1000
+ claude_mpm-5.4.74.dist-info/entry_points.txt,sha256=n-Uk4vwHPpuvu-g_I7-GHORzTnN_m6iyOsoLveKKD0E,228
1001
+ claude_mpm-5.4.74.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
1002
+ claude_mpm-5.4.74.dist-info/RECORD,,