#!/usr/bin/env python3 # hf-skills — Download and install HF CLI skills for AI assistants # # Usage: # hf tool run skills --claude # Install for Claude (project-level) # hf tool run skills --claude --global # Install for Claude (user-level) # hf tool run skills --claude --codex # Install for multiple agents # hf tool run skills --dest=~/my-skills # Install to a custom directory # hf tool run skills --claude --force # Overwrite existing import argparse import os import shutil import sys from pathlib import Path from urllib.request import Request, urlopen SKILL_ID = "hf-cli" _GITHUB_RAW_BASE = "https://raw.githubusercontent.com/huggingface/huggingface_hub/main/docs/source/en" _SKILL_MD_URL = f"{_GITHUB_RAW_BASE}/guides/cli.md" _REFERENCE_URL = f"{_GITHUB_RAW_BASE}/package_reference/cli.md" _SKILL_YAML_PREFIX = """\ --- name: hf-cli description: > Hugging Face Hub CLI (`hf`) for downloading, uploading, and managing repositories, models, datasets, and Spaces on the Hugging Face Hub. --- The Hugging Face Hub CLI tool `hf` is available. IMPORTANT: The `hf` command replaces the deprecated `huggingface_cli` command. Use `hf --help` to view available functions. Note that auth commands are now all under `hf auth` e.g. `hf auth whoami`. """ CENTRAL_LOCAL = Path(".agents/skills") CENTRAL_GLOBAL = Path("~/.agents/skills") GLOBAL_TARGETS = { "codex": Path("~/.codex/skills"), "claude": Path("~/.claude/skills"), "opencode": Path("~/.config/opencode/skills"), } LOCAL_TARGETS = { "codex": Path(".codex/skills"), "claude": Path(".claude/skills"), "opencode": Path(".opencode/skills"), } def _download(url): req = Request(url, headers={"User-Agent": "hf-skills/1.0"}) with urlopen(req) as resp: return resp.read().decode("utf-8") def _remove_existing(path, force): if not (path.exists() or path.is_symlink()): return if not force: print(f"Skill already exists at {path}.\nRe-run with --force to overwrite.", file=sys.stderr) sys.exit(1) if path.is_dir() and not path.is_symlink(): shutil.rmtree(path) else: path.unlink() def _install_to(skills_dir, force): skills_dir = skills_dir.expanduser().resolve() skills_dir.mkdir(parents=True, exist_ok=True) dest = skills_dir / SKILL_ID _remove_existing(dest, force) dest.mkdir() skill_content = _download(_SKILL_MD_URL) (dest / "SKILL.md").write_text(_SKILL_YAML_PREFIX + skill_content, encoding="utf-8") ref_dir = dest / "references" ref_dir.mkdir() ref_content = _download(_REFERENCE_URL) (ref_dir / "cli.md").write_text(ref_content, encoding="utf-8") return dest def _create_symlink(agent_skills_dir, central_skill_path, force): agent_skills_dir = agent_skills_dir.expanduser().resolve() agent_skills_dir.mkdir(parents=True, exist_ok=True) link_path = agent_skills_dir / SKILL_ID _remove_existing(link_path, force) link_path.symlink_to(os.path.relpath(central_skill_path, agent_skills_dir)) return link_path def main(): parser = argparse.ArgumentParser( description="Download and install HF CLI skills for AI assistants.", epilog="Examples:\n" " hf tool run skills --claude\n" " hf tool run skills --claude --global\n" " hf tool run skills --codex --opencode\n" " hf tool run skills --dest ~/my-skills\n", formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("--claude", action="store_true", help="Install for Claude") parser.add_argument("--codex", action="store_true", help="Install for Codex") parser.add_argument("--opencode", action="store_true", help="Install for OpenCode") parser.add_argument("--global", dest="global_", action="store_true", help="Install globally (user-level)") parser.add_argument("--dest", type=Path, help="Install into a custom destination directory") parser.add_argument("--force", action="store_true", help="Overwrite existing skills") args = parser.parse_args() if not (args.claude or args.codex or args.opencode or args.dest): parser.error("Pick a destination via --claude, --codex, --opencode, or --dest.") if args.dest: if args.claude or args.codex or args.opencode or args.global_: print("--dest cannot be combined with --claude, --codex, --opencode, or --global.", file=sys.stderr) sys.exit(1) skill_dest = _install_to(args.dest, args.force) print(f"Installed '{SKILL_ID}' to {skill_dest}") return targets_dict = GLOBAL_TARGETS if args.global_ else LOCAL_TARGETS agent_targets = [] if args.claude: agent_targets.append(targets_dict["claude"]) if args.codex: agent_targets.append(targets_dict["codex"]) if args.opencode: agent_targets.append(targets_dict["opencode"]) central_path = CENTRAL_GLOBAL if args.global_ else CENTRAL_LOCAL central_skill_path = _install_to(central_path, args.force) print(f"Installed '{SKILL_ID}' to central location: {central_skill_path}") for agent_target in agent_targets: link_path = _create_symlink(agent_target, central_skill_path, args.force) print(f"Created symlink: {link_path}") if __name__ == "__main__": main()