What it does
skills is a CLI that installs "agent skills" — markdown instruction files — into whichever AI coding agents you have installed. Run npx skills add owner/repo and it figures out which agents live on your machine, clones the skill repo, discovers all SKILL.md files, and symlinks or copies them into each agent's expected directory. It supports 40+ agents as of v1.4.9.
Why I starred it
The AI coding agent landscape fragmented fast. Claude Code reads from .claude/skills/, Cursor wants .agents/skills/, Codex has its own path, and that's just three of them. Every agent invented its own convention. This CLI is Vercel's answer to the package manager problem nobody wanted but everybody needed — a single npx command that bridges all of them.
What caught my eye was the ambition of the agent detection system and the fact that it shipped a custom frontmatter parser to replace gray-matter for security reasons.
How it works
The entry point is src/cli.ts, which parses commands and delegates to specialized modules. The architecture breaks into four key pieces.
Agent detection in src/agents.ts defines a registry of 40+ agents, each with a detectInstalled function that checks for config directories on disk:
'claude-code': {
name: 'claude-code',
skillsDir: '.claude/skills',
globalSkillsDir: join(claudeHome, 'skills'),
detectInstalled: async () => existsSync(claudeHome),
},
It respects CLAUDE_CONFIG_DIR, CODEX_HOME, and XDG base directories. The detectInstalledAgents() function runs all checks in parallel with Promise.all and returns whichever agents it finds. Agents that share the .agents/skills/ path (Cursor, Codex, Gemini CLI, GitHub Copilot, and others) are grouped as "universal" agents — they get one canonical copy instead of duplicates.
Source parsing in src/source-parser.ts is where things get interesting. It handles GitHub shorthand (owner/repo), full URLs, GitLab with subgroup support, git@ SSH URLs, branch refs via URL fragments (owner/repo#branch), skill filters (owner/repo@skill-name), and arbitrary well-known HTTP endpoints. The parser chains through regex matches with a clear fallback order: local path, GitHub prefix, GitLab tree URL, GitHub shorthand, well-known URL, generic git. There's also a SOURCE_ALIASES map for renaming repos — coinbase/agentWallet redirects to coinbase/agentic-wallet-skills.
Skill discovery in src/skills.ts searches 30+ directory conventions (skills/, .claude/skills/, .roo/skills/, etc.) before falling back to a recursive findSkillDirs walk capped at 5 levels deep. Each SKILL.md gets parsed through a custom frontmatter parser in src/frontmatter.ts:
export function parseFrontmatter(raw: string): {
data: Record<string, unknown>;
content: string;
} {
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
if (!match) return { data: {}, content: raw };
const data = (parseYaml(match[1]!) as Record<string, unknown>) ?? {};
return { data, content: match[2] ?? '' };
}
They replaced gray-matter with this 6-line function specifically to avoid the eval()-based RCE that gray-matter's ---js engine enables. The comment in the source says it directly. Smart trade.
Installation in src/installer.ts handles the symlink-vs-copy decision. The default strategy creates a canonical copy under .agents/skills/ and symlinks from each agent-specific directory. Path traversal attacks are blocked by sanitizeName() and isPathWithinBase() checks that normalize and validate every path before touching the filesystem.
Using it
# List skills in a repo without installing
npx skills add vercel-labs/agent-skills --list
# Install specific skills to Claude Code only
npx skills add vercel-labs/agent-skills --skill frontend-design -a claude-code -g
# Check what's installed
npx skills list
# One-liner for CI
npx skills add vercel-labs/agent-skills --all -y
The check and update commands compare skill folder hashes via the GitHub API — they fetch the tree SHA for each installed skill's directory and compare it against what's stored in .skill-lock.json. This means updates don't require a full clone.
Rough edges
The only runtime dependency is yaml — everything else is devDependencies. That's lean. But simple-git as a dev dependency means clones shell out to the system's git binary, which can be flaky on machines with unusual git configurations.
The agent registry in src/agents.ts is a hand-maintained record. Every new agent means a PR to add another entry. With 40+ agents already, this is going to be a maintenance burden. A plugin-based approach where agents register themselves would scale better.
The lock file format has already been bumped to v3 (adding skillFolderHash), and old versions get wiped on read — if (parsed.version < CURRENT_LOCK_VERSION) returns an empty lock. Backwards-incompatible version bumps that silently discard your lock state can bite you.
Test coverage is solid — there are 18 test files covering everything from path sanitization to symlink installation to cross-platform paths. Vitest as the runner.
Bottom line
If you work with more than one AI coding agent — and most people exploring this space do — skills is the package manager that prevents you from manually copying markdown files into five different hidden directories. The source is clean, the security posture is thoughtful, and the agent detection system is the most complete catalog of AI coding tool conventions I've seen in one place.
