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.
- claude_mpm/cli/commands/configure.py +179 -14
- {claude_mpm-5.4.73.dist-info → claude_mpm-5.4.74.dist-info}/METADATA +1 -1
- {claude_mpm-5.4.73.dist-info → claude_mpm-5.4.74.dist-info}/RECORD +8 -8
- {claude_mpm-5.4.73.dist-info → claude_mpm-5.4.74.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.73.dist-info → claude_mpm-5.4.74.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.73.dist-info → claude_mpm-5.4.74.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.73.dist-info → claude_mpm-5.4.74.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.73.dist-info → claude_mpm-5.4.74.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
|
|
671
|
-
self.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
self.console.print("
|
|
675
|
-
self.console.print(" [
|
|
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
|
-
#
|
|
687
|
+
# Toggle skill installation
|
|
682
688
|
self.console.clear()
|
|
683
689
|
self._display_header()
|
|
684
|
-
|
|
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=
|
|
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=
|
|
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
|
|
@@ -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=
|
|
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.
|
|
997
|
-
claude_mpm-5.4.
|
|
998
|
-
claude_mpm-5.4.
|
|
999
|
-
claude_mpm-5.4.
|
|
1000
|
-
claude_mpm-5.4.
|
|
1001
|
-
claude_mpm-5.4.
|
|
1002
|
-
claude_mpm-5.4.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|