fix: skills hub inspect/resolve — 4 bugs

Cherry-picked from PR #2122 by @AtlasMeridia.

1. do_inspect bytes crash: bundle.files returns bytes for official
   skills, .split() expected str. Added decode guard.
2. GitHub redirects: three httpx.get calls missing follow_redirects=True,
   causing silent 301 failures on renamed orgs.
3. Skill discovery fallback: scan repo root directories when standard
   paths (skills/, .agents/skills/, .claude/skills/) miss.
4. tap list KeyError: t['repo'] crashes for local taps. Use safe .get().
This commit is contained in:
Teknium 2026-03-22 04:03:28 -07:00
parent 306e67f32d
commit 7d0e4510b8
No known key found for this signature in database
2 changed files with 46 additions and 7 deletions

View file

@ -375,7 +375,7 @@ class GitHubSource(SkillSource):
url = f"https://api.github.com/repos/{repo}/contents/{path.rstrip('/')}"
try:
resp = httpx.get(url, headers=self.auth.get_headers(), timeout=15)
resp = httpx.get(url, headers=self.auth.get_headers(), timeout=15, follow_redirects=True)
if resp.status_code != 200:
return []
except httpx.HTTPError:
@ -407,7 +407,7 @@ class GitHubSource(SkillSource):
"""Recursively download all text files from a GitHub directory."""
url = f"https://api.github.com/repos/{repo}/contents/{path.rstrip('/')}"
try:
resp = httpx.get(url, headers=self.auth.get_headers(), timeout=15)
resp = httpx.get(url, headers=self.auth.get_headers(), timeout=15, follow_redirects=True)
if resp.status_code != 200:
return {}
except httpx.HTTPError:
@ -441,7 +441,7 @@ class GitHubSource(SkillSource):
resp = httpx.get(
url,
headers={**self.auth.get_headers(), "Accept": "application/vnd.github.v3.raw"},
timeout=15,
timeout=15, follow_redirects=True,
)
if resp.status_code == 200:
return resp.text
@ -961,8 +961,8 @@ class SkillsShSource(SkillSource):
default_repo = f"{parts[0]}/{parts[1]}"
repo = detail.get("repo", default_repo) if isinstance(detail, dict) else default_repo
skill_token = parts[2]
tokens = [skill_token]
skill_token=parts[2].split("/")[-1]
tokens=[skill_token]
if isinstance(detail, dict):
tokens.extend([
detail.get("install_skill", ""),
@ -970,7 +970,10 @@ class SkillsShSource(SkillSource):
detail.get("body_title", ""),
])
for base_path in ("skills/", ".agents/skills/", ".claude/skills/"):
# Standard skill paths
base_paths = ["skills/", ".agents/skills/", ".claude/skills/"]
for base_path in base_paths:
try:
skills = self.github._list_skills_in_repo(repo, base_path)
except Exception:
@ -978,6 +981,39 @@ class SkillsShSource(SkillSource):
for meta in skills:
if self._matches_skill_tokens(meta, tokens):
return meta.identifier
# Fallback: scan repo root for directories that might contain skills
try:
root_url = f"https://api.github.com/repos/{repo}/contents/"
resp = httpx.get(root_url, headers=self.github.auth.get_headers(),
timeout=15, follow_redirects=True)
if resp.status_code == 200:
entries = resp.json()
if isinstance(entries, list):
for entry in entries:
if entry.get("type") != "dir":
continue
dir_name = entry["name"]
if dir_name.startswith(".") or dir_name.startswith("_"):
continue
if dir_name in ("skills", ".agents", ".claude"):
continue # already tried
# Try direct: repo/dir/skill_token
direct_id = f"{repo}/{dir_name}/{skill_token}"
meta = self.github.inspect(direct_id)
if meta:
return meta.identifier
# Try listing skills in this directory
try:
skills = self.github._list_skills_in_repo(repo, dir_name + "/")
except Exception:
continue
for meta in skills:
if self._matches_skill_tokens(meta, tokens):
return meta.identifier
except Exception:
pass
return None
def _finalize_inspect_meta(self, meta: SkillMeta, canonical: str, detail: Optional[dict]) -> SkillMeta: