fix(skills): preserve trust for skills-sh identifiers + reduce resolution churn (#3251)
* fix(skills): reduce skills.sh resolution churn and preserve trust for wrapped identifiers - Accept common skills.sh prefix typos (skils-sh/, skils.sh/) - Strip skills-sh/ prefix in _resolve_trust_level() so trusted repos stay trusted when installed through skills.sh - Use resolved identifier (from bundle/meta) for scan_skill source - Prefer tree search before root scan in _discover_identifier() - Add _resolve_github_meta() consolidation for inspect flow Cherry-picked from PR #3001 by kshitijk4poor. * fix: restore candidate loop in SkillsShSource.fetch() for consistency The cherry-picked PR only tried the first candidate identifier in fetch() while inspect() (via _resolve_github_meta) tried all four. This meant skills at repo/skills/path would be found by inspect but missed by fetch, forcing it through the heavier _discover_identifier flow. Restore the candidate loop so both paths behave identically. Updated the test assertion to match. --------- Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,7 @@ from io import StringIO
|
||||
import pytest
|
||||
from rich.console import Console
|
||||
|
||||
from hermes_cli.skills_hub import do_check, do_list, do_update, handle_skills_slash
|
||||
from hermes_cli.skills_hub import do_check, do_install, do_list, do_update, handle_skills_slash
|
||||
|
||||
|
||||
class _DummyLockFile:
|
||||
@@ -177,3 +177,57 @@ def test_do_update_reinstalls_outdated_skills(monkeypatch):
|
||||
|
||||
assert installs == [("skills-sh/example/repo/hub-skill", "category", True)]
|
||||
assert "Updated 1 skill" in output
|
||||
|
||||
|
||||
def test_do_install_scans_with_resolved_identifier(monkeypatch, tmp_path, hub_env):
|
||||
import tools.skills_guard as guard
|
||||
import tools.skills_hub as hub
|
||||
|
||||
canonical_identifier = "skills-sh/anthropics/skills/frontend-design"
|
||||
|
||||
class _ResolvedSource:
|
||||
def inspect(self, identifier):
|
||||
return type("Meta", (), {
|
||||
"extra": {},
|
||||
"identifier": canonical_identifier,
|
||||
})()
|
||||
|
||||
def fetch(self, identifier):
|
||||
return type("Bundle", (), {
|
||||
"name": "frontend-design",
|
||||
"files": {"SKILL.md": "# Frontend Design"},
|
||||
"source": "skills.sh",
|
||||
"identifier": canonical_identifier,
|
||||
"trust_level": "trusted",
|
||||
"metadata": {},
|
||||
})()
|
||||
|
||||
q_path = tmp_path / "skills" / ".hub" / "quarantine" / "frontend-design"
|
||||
q_path.mkdir(parents=True)
|
||||
(q_path / "SKILL.md").write_text("# Frontend Design")
|
||||
|
||||
scanned = {}
|
||||
|
||||
def _scan_skill(skill_path, source="community"):
|
||||
scanned["source"] = source
|
||||
return guard.ScanResult(
|
||||
skill_name="frontend-design",
|
||||
source=source,
|
||||
trust_level="trusted",
|
||||
verdict="safe",
|
||||
)
|
||||
|
||||
monkeypatch.setattr(hub, "ensure_hub_dirs", lambda: None)
|
||||
monkeypatch.setattr(hub, "create_source_router", lambda auth: [_ResolvedSource()])
|
||||
monkeypatch.setattr(hub, "quarantine_bundle", lambda bundle: q_path)
|
||||
monkeypatch.setattr(hub, "HubLockFile", lambda: type("Lock", (), {"get_installed": lambda self, name: None})())
|
||||
monkeypatch.setattr(guard, "scan_skill", _scan_skill)
|
||||
monkeypatch.setattr(guard, "format_scan_report", lambda result: "scan ok")
|
||||
monkeypatch.setattr(guard, "should_allow_install", lambda result, force=False: (False, "stop after scan"))
|
||||
|
||||
sink = StringIO()
|
||||
console = Console(file=sink, force_terminal=False, color_system=None)
|
||||
|
||||
do_install("skils-sh/anthropics/skills/frontend-design", console=console, skip_confirm=True)
|
||||
|
||||
assert scanned["source"] == canonical_identifier
|
||||
|
||||
Reference in New Issue
Block a user