From 71bb818f40daec4591b7edefd431dbbe820eeb59 Mon Sep 17 00:00:00 2001 From: r266-tech Date: Tue, 7 Apr 2026 16:52:42 +0800 Subject: [PATCH 1/3] fix(skills): include dir_name in _find_all_skills return dict --- tools/skills_tool.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/skills_tool.py b/tools/skills_tool.py index da023a1434fc5..78d69de8d090e 100644 --- a/tools/skills_tool.py +++ b/tools/skills_tool.py @@ -580,6 +580,7 @@ def _find_all_skills(*, skip_disabled: bool = False) -> List[Dict[str, Any]]: seen_names.add(name) skills.append({ "name": name, + "dir_name": skill_dir.name, "description": description, "category": category, }) From b536d4262a9cba35cedf64e5dec9e6241fd4810a Mon Sep 17 00:00:00 2001 From: r266-tech Date: Tue, 7 Apr 2026 16:52:52 +0800 Subject: [PATCH 2/3] fix(skills): check dir_name against manifest for builtin classification --- hermes_cli/skills_hub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hermes_cli/skills_hub.py b/hermes_cli/skills_hub.py index 370b69ab0c7a7..6a7af49afe31b 100644 --- a/hermes_cli/skills_hub.py +++ b/hermes_cli/skills_hub.py @@ -530,7 +530,7 @@ def do_list(source_filter: str = "all", console: Optional[Console] = None) -> No source_display = hub_entry.get("source", "hub") trust = hub_entry.get("trust_level", "community") hub_count += 1 - elif name in builtin_names: + elif name in builtin_names or skill.get("dir_name", name) in builtin_names: source_type = "builtin" source_display = "builtin" trust = "builtin" From 92b4c88e55589452226bb9a8fc6a162df11a6199 Mon Sep 17 00:00:00 2001 From: r266-tech Date: Tue, 7 Apr 2026 16:53:08 +0800 Subject: [PATCH 3/3] test(skills): regression test for builtin classification with dir_name mismatch --- tests/hermes_cli/test_skills_hub.py | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/hermes_cli/test_skills_hub.py b/tests/hermes_cli/test_skills_hub.py index 0ef6c2d69a822..599107e0148ce 100644 --- a/tests/hermes_cli/test_skills_hub.py +++ b/tests/hermes_cli/test_skills_hub.py @@ -148,6 +148,36 @@ def test_do_list_filter_builtin(three_source_env): output = _capture(source_filter="builtin") assert "builtin-skill" in output + + +def test_do_list_builtin_dir_name_mismatch(monkeypatch, hub_env): + """Builtin skill whose directory name differs from frontmatter name is still classified as builtin. + + Regression test for issue #5433: skills_sync stores manifest keys by directory name + while _find_all_skills returns frontmatter name; when they differ the skill was wrongly + shown as local. + """ + import tools.skills_hub as hub + import tools.skills_sync as skills_sync + import tools.skills_tool as skills_tool + + # directory name is "vllm" but SKILL.md frontmatter says "serving-llms-vllm" + _skills = [ + {"name": "serving-llms-vllm", "dir_name": "vllm", "category": "mlops", "description": "vllm"}, + ] + _manifest = {"vllm": "deadbeef"} # manifest key = directory name + + monkeypatch.setattr(hub, "HubLockFile", lambda: _DummyLockFile([])) + monkeypatch.setattr(skills_tool, "_find_all_skills", lambda: list(_skills)) + monkeypatch.setattr(skills_sync, "_read_manifest", lambda: dict(_manifest)) + + sink = StringIO() + console = Console(file=sink, force_terminal=False, color_system=None) + do_list(console=console) + output = sink.getvalue() + + assert "serving-llms-vllm" in output + assert "0 hub-installed, 1 builtin, 0 local" in output assert "hub-skill" not in output assert "local-skill" not in output