hf-tools / skills
celinah's picture
celinah HF Staff
Rename hf-skills to skills
c06b563 verified
Raw
History Blame Contribute Delete
5.37 kB
#!/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()