diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 861cc038b..9b308f763 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -2201,7 +2201,7 @@ def cmd_pairing(args): skills_inspect.add_argument("identifier", help="Skill identifier") skills_list = skills_subparsers.add_parser("list", help="List installed skills") - skills_list.add_argument("--source", default="all", choices=["all", "hub", "builtin"]) + skills_list.add_argument("--source", default="all", choices=["all", "hub", "builtin", "local"]) skills_audit = skills_subparsers.add_parser("audit", help="Re-scan installed hub skills") skills_audit.add_argument("name", nargs="?", help="Specific skill to audit (default: all)") diff --git a/hermes_cli/skills_hub.py b/hermes_cli/skills_hub.py index 8b72fe4f4..1975a60f2 100644 --- a/hermes_cli/skills_hub.py +++ b/hermes_cli/skills_hub.py @@ -409,6 +409,7 @@ def do_inspect(identifier: str, console: Optional[Console] = None) -> None: def do_list(source_filter: str = "all", console: Optional[Console] = None) -> None: """List installed skills, distinguishing builtins from hub-installed.""" from tools.skills_hub import HubLockFile, ensure_hub_dirs + from tools.skills_sync import _read_manifest from tools.skills_tool import _find_all_skills c = console or _console @@ -416,6 +417,9 @@ def do_list(source_filter: str = "all", console: Optional[Console] = None) -> No lock = HubLockFile() hub_installed = {e["name"]: e for e in lock.list_installed()} + # Read bundled manifest to distinguish true builtins from local skills + bundled_skills = _read_manifest() + all_skills = _find_all_skills() table = Table(title="Installed Skills") @@ -432,22 +436,29 @@ def do_list(source_filter: str = "all", console: Optional[Console] = None) -> No if hub_entry: source_display = hub_entry.get("source", "hub") trust = hub_entry.get("trust_level", "community") - else: + elif name in bundled_skills: source_display = "builtin" trust = "builtin" + else: + # User-provided local skills (e.g., from ~/.hermes/skills/) + source_display = "local" + trust = "local" if source_filter == "hub" and not hub_entry: continue - if source_filter == "builtin" and hub_entry: + if source_filter == "builtin" and source_display != "builtin": + continue + if source_filter == "local" and source_display != "local": continue - trust_style = {"builtin": "bright_cyan", "trusted": "green", "community": "yellow"}.get(trust, "dim") + trust_style = {"builtin": "bright_cyan", "trusted": "green", "community": "yellow", "local": "dim"}.get(trust, "dim") trust_label = "official" if source_display == "official" else trust table.add_row(name, category, source_display, f"[{trust_style}]{trust_label}[/]") c.print(table) c.print(f"[dim]{len(hub_installed)} hub-installed, " - f"{len(all_skills) - len(hub_installed)} builtin[/]\n") + f"{len([s for s in all_skills if s['name'] in bundled_skills])} builtin, " + f"{len([s for s in all_skills if s['name'] not in hub_installed and s['name'] not in bundled_skills])} local[/]\n") def do_audit(name: Optional[str] = None, console: Optional[Console] = None) -> None: @@ -1014,7 +1025,7 @@ def _print_skills_help(console: Console) -> None: " [cyan]search[/] Search registries for skills\n" " [cyan]install[/] Install a skill (with security scan)\n" " [cyan]inspect[/] Preview a skill without installing\n" - " [cyan]list[/] [--source hub|builtin] List installed skills\n" + " [cyan]list[/] [--source hub|builtin|local] List installed skills\n" " [cyan]audit[/] [name] Re-scan hub skills for security\n" " [cyan]uninstall[/] Remove a hub-installed skill\n" " [cyan]publish[/] --repo Publish a skill to GitHub via PR\n"