scripts/list-skills.py
scripts/list-skills.pyBrowse 8 files
705 tokens
2,948 bytes
Token encoding: o200k_base
Snapshot 18d7ace
← Back to SKILL.md
1#!/usr/bin/env python32"""List skills from a GitHub repo path."""3 4from __future__ import annotations5 6import argparse7import json8import os9import sys10import urllib.error11 12from github_utils import github_api_contents_url, github_request13 14DEFAULT_REPO = "openai/skills"15DEFAULT_PATH = "skills/.curated"16DEFAULT_REF = "main"17 18 19class ListError(Exception):20 pass21 22 23class Args(argparse.Namespace):24 repo: str25 path: str26 ref: str27 format: str28 29 30def _request(url: str) -> bytes:31 return github_request(url, "codex-skill-list")32 33 34def _codex_home() -> str:35 return os.environ.get("CODEX_HOME", os.path.expanduser("~/.codex"))36 37 38def _installed_skills() -> set[str]:39 root = os.path.join(_codex_home(), "skills")40 if not os.path.isdir(root):41 return set()42 entries = set()43 for name in os.listdir(root):44 path = os.path.join(root, name)45 if os.path.isdir(path):46 entries.add(name)47 return entries48 49 50def _list_skills(repo: str, path: str, ref: str) -> list[str]:51 api_url = github_api_contents_url(repo, path, ref)52 try:53 payload = _request(api_url)54 except urllib.error.HTTPError as exc:55 if exc.code == 404:56 raise ListError(57 f"Skills path not found: https://github.com/{repo}/tree/{ref}/{path}"58 ) from exc59 raise ListError(f"Failed to fetch skills: HTTP {exc.code}") from exc60 data = json.loads(payload.decode("utf-8"))61 if not isinstance(data, list):62 raise ListError("Unexpected skills listing response.")63 skills = [item["name"] for item in data if item.get("type") == "dir"]64 return sorted(skills)65 66 67def _parse_args(argv: list[str]) -> Args:68 parser = argparse.ArgumentParser(description="List skills.")69 parser.add_argument("--repo", default=DEFAULT_REPO)70 parser.add_argument(71 "--path",72 default=DEFAULT_PATH,73 help="Repo path to list (default: skills/.curated)",74 )75 parser.add_argument("--ref", default=DEFAULT_REF)76 parser.add_argument(77 "--format",78 choices=["text", "json"],79 default="text",80 help="Output format",81 )82 return parser.parse_args(argv, namespace=Args())83 84 85def main(argv: list[str]) -> int:86 args = _parse_args(argv)87 try:88 skills = _list_skills(args.repo, args.path, args.ref)89 installed = _installed_skills()90 if args.format == "json":91 payload = [92 {"name": name, "installed": name in installed} for name in skills93 ]94 print(json.dumps(payload))95 else:96 for idx, name in enumerate(skills, start=1):97 suffix = " (already installed)" if name in installed else ""98 print(f"{idx}. {name}{suffix}")99 return 0100 except ListError as exc:101 print(f"Error: {exc}", file=sys.stderr)102 return 1103 104 105if __name__ == "__main__":106 raise SystemExit(main(sys.argv[1:]))107 