
Add Skill
- 5 installs
- 134 repo stars
- Updated August 4, 2026
- openhands/extensions
Helps with ai & agent building tasks.
About
add-skill is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- add-skill
- AI & Agent Building
- AI-coding skill
Add Skill by the numbers
- 5 all-time installs (skills.sh)
- Ranked #13,065 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openhands/extensions --skill add-skillAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5 |
|---|---|
| repo stars | ★ 134 |
| Last updated | August 4, 2026 |
| Repository | openhands/extensions ↗ |
What it does
Helps with ai & agent building tasks.
Files
Add Skill
Import skills from GitHub repositories into the current workspace.
Workflow
When a user requests to add a skill from a GitHub URL:
1. Parse the URL to extract repository owner, name, and skill path 2. Fetch the skill using the bundled script:
python3 <this-skill-path>/scripts/fetch_skill.py "<github-url>" "<workspace-path>"3. Verify that SKILL.md exists in the destination 4. Inform the user the skill is now available
URL Formats Supported
https://github.com/owner/repo/tree/main/path/to/skillhttps://github.com/owner/repo/skill-namegithub.com/owner/repo/skill-nameowner/repo/skill-name(shorthand)
Example
User: /add-skill https://github.com/OpenHands/extensions/tree/main/skills/codereview
# Run the fetch script
python3 scripts/fetch_skill.py "https://github.com/OpenHands/extensions/tree/main/skills/codereview" "/path/to/workspace"
# Verify installation
ls /path/to/workspace/.agents/skills/codereview/SKILL.mdResponse: "✅ Added codereview to your workspace. The skill is now available."
Notes
- Creates
.agents/skills/directory if it doesn't exist - Uses
GITHUB_TOKENfor authentication (required for private repos) - Warns before overwriting existing skills with the same name
.plugin.plugin{
"name": "add-skill",
"version": "1.0.0",
"description": "Add (import) an OpenHands skill from a GitHub repository into the current workspace.",
"author": {
"name": "OpenHands",
"email": "contact@all-hands.dev"
},
"homepage": "https://github.com/OpenHands/extensions",
"repository": "https://github.com/OpenHands/extensions",
"license": "MIT",
"keywords": [
"skill",
"import",
"github",
"sparse-checkout"
]
}
add-skill
Add (import) an OpenHands skill from a GitHub repository into the current workspace.
This skill is useful when a user says things like:
- “Add the
codereviewskill from https://github.com/OpenHands/extensions/” - “/add-skill https://github.com/OpenHands/extensions/tree/main/skills/codereview”
It fetches only the requested skill directory (via git sparse checkout) and installs it into the workspace so OpenHands can use it.
What it does
- Parses a GitHub URL (multiple formats supported)
- Downloads only the requested skill folder (no full repo clone)
- Validates the downloaded folder contains a
SKILL.md - Installs the skill into:
<workspace>/.agents/skills/<skill-name>/Included files
SKILL.md– skill metadata + usage guidance for the agentscripts/fetch_skill.py– implementation used to fetch/install a skill
Usage
From the add-skill skill directory:
python3 scripts/fetch_skill.py "<github-skill-url>" "<workspace-path>" [--force]Examples
# Full URL with explicit branch
python3 scripts/fetch_skill.py \
"https://github.com/OpenHands/extensions/tree/main/skills/docker" \
"/workspace"
# Shorthand form (defaults to main)
python3 scripts/fetch_skill.py \
"OpenHands/extensions/skills/codereview" \
"/workspace"
# Overwrite if the skill already exists
python3 scripts/fetch_skill.py \
"OpenHands/extensions/tree/main/skills/codereview" \
"/workspace" \
--forceSupported URL formats
https://github.com/<owner>/<repo>/tree/<branch>/<path/to/skill>https://github.com/<owner>/<repo>/<path/to/skill>(defaults tomain)github.com/<owner>/<repo>/<path/to/skill><owner>/<repo>/<path/to/skill>
Notes / caveats
- If
GITHUB_TOKENis set, it will be used for authentication (needed for private repos). - If the destination already exists, the script will fail unless
--forceis provided. - The script installs under
.agents/skills/.
#!/usr/bin/env python3
"""
Fetch a skill from a GitHub repository and install it locally.
This script downloads OpenHands skills from GitHub repositories and installs them
into the workspace's .agents/skills/ directory. It uses git sparse checkout to
efficiently download only the skill directory without cloning the entire repository.
Usage:
python fetch_skill.py <github-url> <workspace-path> [--force]
Examples:
# Full GitHub URL with branch
python fetch_skill.py "https://github.com/OpenHands/extensions/tree/main/skills/docker" /workspace
# Simplified URL (assumes 'main' branch)
python fetch_skill.py "https://github.com/OpenHands/extensions/skills/npm" /workspace
# Shorthand format
python fetch_skill.py "OpenHands/extensions/skills/codereview" /workspace
The script will:
1. Parse the GitHub URL to extract owner, repo, branch, and skill path
2. Use git sparse checkout to download only the specified skill directory
3. Validate the skill has a SKILL.md file
4. Copy the skill to <workspace>/.agents/skills/<skill-name>/
"""
import argparse
import os
import re
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
def parse_github_url(url: str) -> tuple[str, str, str, str]:
"""
Parse a GitHub URL into its component parts.
Handles multiple URL formats:
- Full URL: https://github.com/owner/repo/tree/branch/path/to/skill
- Simple URL: https://github.com/owner/repo/path/to/skill
- With github.com: github.com/owner/repo/skill-name
- Shorthand: owner/repo/skill-name
Args:
url: GitHub URL in any of the supported formats
Returns:
Tuple of (owner, repo, branch, skill_path)
- owner: GitHub username or organization
- repo: Repository name
- branch: Git branch (defaults to 'main' if not specified)
- skill_path: Path to the skill directory within the repo
Raises:
ValueError: If the URL cannot be parsed
"""
# Step 1: Normalize the URL by removing protocol prefix (https://, http://)
url = re.sub(r'^https?://', '', url)
url = url.rstrip('/')
# Step 2: Remove github.com prefix if present
url = re.sub(r'^github\.com/', '', url)
# Step 3: Try to match full URL pattern with explicit branch
# Pattern: owner/repo/tree/branch/path/to/skill
# Example: "OpenHands/extensions/tree/main/skills/docker"
tree_match = re.match(r'^([^/]+)/([^/]+)/tree/([^/]+)/(.+)$', url)
if tree_match:
return tree_match.group(1), tree_match.group(2), tree_match.group(3), tree_match.group(4)
# Step 4: Fall back to simple pattern (assumes 'main' branch)
# Pattern: owner/repo/path/to/skill
# Example: "OpenHands/extensions/skills/docker"
parts = url.split('/')
if len(parts) >= 3:
owner = parts[0]
repo = parts[1]
skill_path = '/'.join(parts[2:])
return owner, repo, 'main', skill_path
raise ValueError(f"Unable to parse GitHub URL: {url}")
def fetch_skill(github_url: str, workspace_path: str, force: bool = False) -> str:
"""
Fetch a skill from GitHub and install it to the workspace.
This function performs the following steps:
1. Parses the GitHub URL to extract repository information
2. Creates a temporary directory for the git operation
3. Uses git sparse checkout to download only the skill directory (efficient!)
4. Validates the downloaded content is a valid skill (has SKILL.md)
5. Copies the skill to the workspace's .agents/skills/ directory
Args:
github_url: URL to the skill on GitHub (various formats supported)
workspace_path: Path to the workspace root directory
force: If True, overwrite existing skill with same name
Returns:
Path to the installed skill directory
Raises:
SystemExit: If skill already exists (without --force), path not found,
or skill is invalid (no SKILL.md)
"""
# Parse the URL to get repository details
owner, repo, branch, skill_path = parse_github_url(github_url)
# Extract skill name from the path (last component)
# e.g., "skills/docker" -> "docker"
skill_name = skill_path.rstrip('/').split('/')[-1]
# Determine the destination directory
# Skills are installed to: <workspace>/.agents/skills/<skill-name>/
dest_dir = Path(workspace_path) / '.agents' / 'skills' / skill_name
# Check if skill already exists - prevent accidental overwrites
if dest_dir.exists():
if not force:
print(f"⚠️ Skill '{skill_name}' already exists at {dest_dir}")
print(" Use --force to overwrite")
sys.exit(1)
print(f"Removing existing skill at {dest_dir}")
shutil.rmtree(dest_dir)
# Ensure the parent directory exists (.agents/skills/)
dest_dir.parent.mkdir(parents=True, exist_ok=True)
# Use a temporary directory for the git clone operation
# This keeps the workspace clean and handles cleanup automatically
with tempfile.TemporaryDirectory() as tmpdir:
# Build the repository URL
repo_url = f"https://github.com/{owner}/{repo}.git"
# If GITHUB_TOKEN is available, use it for authentication
# This enables access to private repositories
github_token = os.environ.get('GITHUB_TOKEN')
if github_token:
repo_url = f"https://{github_token}@github.com/{owner}/{repo}.git"
print(f"Fetching skill '{skill_name}' from {owner}/{repo}...")
# ============================================================
# Git Sparse Checkout Process
# ============================================================
# Sparse checkout allows us to download only specific directories
# from a repository, rather than the entire repo. This is much
# faster and uses less bandwidth/disk space.
#
# The process:
# 1. Clone with --filter=blob:none (don't download file contents yet)
# 2. Clone with --no-checkout (don't populate working directory)
# 3. Clone with --depth=1 (only get latest commit, no history)
# 4. Initialize sparse-checkout in cone mode
# 5. Set the specific path we want
# 6. Checkout to actually download just those files
# ============================================================
# Step 1: Clone the repo skeleton (metadata only, no file contents)
subprocess.run(
['git', 'clone', '--filter=blob:none', '--no-checkout', '--depth=1',
'--branch', branch, repo_url, tmpdir],
check=True, capture_output=True, text=True
)
# Step 2: Initialize sparse checkout in "cone" mode
# Cone mode is more efficient and works with directory patterns
subprocess.run(
['git', '-C', tmpdir, 'sparse-checkout', 'init', '--cone'],
check=True, capture_output=True, text=True
)
# Step 3: Specify which directory we want to download
subprocess.run(
['git', '-C', tmpdir, 'sparse-checkout', 'set', skill_path],
check=True, capture_output=True, text=True
)
# Step 4: Checkout - this actually downloads the files we specified
subprocess.run(
['git', '-C', tmpdir, 'checkout'],
check=True, capture_output=True, text=True
)
# ============================================================
# Validation and Installation
# ============================================================
# Verify the skill path exists in the downloaded content
src_skill_dir = Path(tmpdir) / skill_path
if not src_skill_dir.exists():
print(f"❌ Skill path '{skill_path}' not found in repository")
sys.exit(1)
# Verify this is a valid skill by checking for SKILL.md
# Every valid OpenHands skill must have a SKILL.md file
if not (src_skill_dir / 'SKILL.md').exists():
print(f"❌ No SKILL.md found in '{skill_path}' - not a valid skill")
sys.exit(1)
# Copy the skill directory to the final destination
shutil.copytree(src_skill_dir, dest_dir)
print(f"✅ Successfully installed '{skill_name}' to {dest_dir}")
return str(dest_dir)
def main():
"""
Command-line entry point for the fetch_skill script.
Parses command-line arguments and invokes fetch_skill() with appropriate
error handling. Exits with code 1 on any error.
"""
parser = argparse.ArgumentParser(
description='Fetch a skill from a GitHub repository and install it locally.',
epilog='''
Examples:
%(prog)s "https://github.com/OpenHands/extensions/tree/main/skills/docker" /workspace
%(prog)s "OpenHands/extensions/skills/npm" /workspace
%(prog)s "owner/repo/my-skill" /workspace --force
'''
)
parser.add_argument(
'url',
help='GitHub URL to the skill directory (supports full URLs, github.com URLs, or owner/repo/path shorthand)'
)
parser.add_argument(
'workspace',
help='Path to the workspace root where the skill will be installed (to .agents/skills/)'
)
parser.add_argument(
'--force', '-f',
action='store_true',
help='Overwrite existing skill if it already exists'
)
args = parser.parse_args()
try:
fetch_skill(args.url, args.workspace, args.force)
except subprocess.CalledProcessError as e:
# Git command failed - show the error message from git
print(f"❌ Git error: {e.stderr if e.stderr else e}")
sys.exit(1)
except Exception as e:
# Any other unexpected error
print(f"❌ Error: {e}")
sys.exit(1)
if __name__ == '__main__':
main()