agents-skills
A minimal, stable Rust library for installing and managing AI agent skills, with an optional command-line interface built on top.
agents-skills is library first: import it into your Rust project to install,
list, remove, and update SKILL.md packages for Claude Code,
Codex, Cursor, and 70+ other coding agents. A small CLI (agents-skills) ships alongside,
implemented as a thin rendering layer over the exact same public API.
See also: 中文 README
Why a library?
- Embed skill management into your own tools — a plugin manager, an agent launcher, or a build script can install skills without shelling out to a binary.
- Pure data, no side effects on stdout — every API returns structured results and
surfaces errors via
Result; it never prints and never callsprocess::exit. You decide how to render and when to exit. - Injectable context —
ManagerBuilderlets you point at anyhome/config/cwd, making tests and sandboxes trivial.
Getting started
Add the dependency to your Cargo.toml:
[]
= "1"
Install and list skills with the high-level Manager facade:
use ;
API
High-level: Manager
One-stop operations. Each takes a plain request struct and returns a structured outcome.
| Method | Request | Returns |
|---|---|---|
Manager::add |
AddRequest |
AddOutcome (installed + links + failed) |
Manager::add_source |
impl Into<String> |
AddOutcome (installed + links + failed) |
Manager::link |
LinkRequest |
LinkManagerOutcome (per-agent results) |
Manager::unlink |
UnlinkRequest |
UnlinkManagerOutcome (per-agent results) |
Manager::list |
ListRequest |
Vec<ListedSkill> (serde-serializable) |
Manager::remove |
RemoveRequest |
RemoveOutcome (removed names) |
Manager::update |
UpdateRequest |
UpdateOutcome (updated/failed counts) |
Request structs are Default + Clone with builder-style field overrides; outcomes are
plain data.
Injectable context: ManagerBuilder
use Manager;
let manager = builder
.home
.config
.cwd
.env_var
.build;
Manager::new() is just Manager::builder().build() resolved against the real
environment.
Low-level: core primitives
For finer control, the underlying core functions are re-exported at the crate root:
- Source —
parse_source,owner_repo - Discovery —
discover_skills,filter_skills,parse_skill_md - Install —
install_skill,list_installed_skills,sanitize_name - Links —
link_agent,unlink_agent - Lockfile —
read_local_lock,write_local_lock,compute_folder_hash - Agents —
get_agent,detect_installed_agents,Agent,Env
Examples
Run the bundled examples to see real usage:
Features
- Install from anywhere — local paths, GitHub repos/URLs, GitLab, SSH/git URLs, and arbitrary HTTPS endpoints (well-known discovery or direct download).
- 70+ agents — static directory-mapping table, data-driven and dependency-injectable for testability.
- Directory-level agent links — skills live once in the canonical dir
(
.agents/skills/~/.agents/skills); each agent's own skills dir becomes a relative symlink to it, so every install, update, or remove is instantly visible to all linked agents with no sync step. Agents that natively read.agents/skillsneed no link at all. - Project and global scopes — install to
.agents/skills(project) or~/.agents/skills(global). - Lockfile —
skills-lock.jsonrecords the source and a SHA-256 content hash for every installed skill, enabling reproducibleupdate. - Skill discovery — priority container dirs (
skills/,.curated/,.experimental/,.system/) with shallow-shadowing-deep resolution. - Cross-platform — macOS, Linux, and Windows (directory symlinks on Windows,
git2for transport-agnostic cloning).
Source formats
The source field of AddRequest (and the CLI <source> argument) accepts:
| Format | Example |
|---|---|
| Local path | ./my-skill, /abs/path/skill |
| GitHub shorthand | owner/repo, owner/repo@skill, owner/repo/subpath |
| GitHub URL | https://github.com/owner/repo, .../tree/main/skills |
| GitLab URL | https://gitlab.com/group/repo, .../-/tree/main/skills |
| SSH / git URL | git@github.com:owner/repo.git |
| HTTPS (well-known) | https://example.com/skills (discovery → download fallback) |
| HTTPS (download) | .../skill.zip, .../skill.tar.gz, raw SKILL.md |
Install locations
- Canonical dir (the only place real files live) —
./.agents/skills/<name>at project scope,~/.agents/skills/<name>globally. - Agent integration — agents that don't natively read the canonical dir get a
directory-level symlink:
.claude/skills→../.agents/skills(project) or~/.claude/skills→~/.agents/skills(global). Linked agents share the canonical dir, so installing a skill once makes it visible everywhere.
Command-line interface
A small CLI ships on top of the library:
# Install (from crates.io)
# Install a skill from a GitHub repo
# Install a specific skill, ensuring an agent is linked
# Link an agent's skills dir to the canonical dir
# (adopt existing skills with --migrate)
# List as machine-readable JSON
# Update everything from its lockfile source
| Command | Aliases | Description |
|---|---|---|
add |
a, i, install |
Install skill packages from a source |
remove |
rm, r |
Remove installed skills |
list |
ls |
List installed skills + agent links |
update |
upgrade, check |
Update skills to their latest versions |
link |
ln |
Link agents' skills dirs to canonical |
unlink |
un |
Unlink agents from the canonical dir |
add
agents-skills add <source> [options]
Options:
-g, --global Install globally (user-level) instead of project-level
-a, --agent <a>... Agents to link to the canonical dir ('*' for all)
-s, --skill <s>... Skill names to install ('*' for all)
-l, --list List available skills without installing
--migrate Move existing agent skills dirs into the canonical dir
--all Shorthand for --skill '*' --agent '*' -y
--full-depth Search all subdirectories even with a root SKILL.md
-y, --yes Skip confirmation prompts
remove
agents-skills remove [skills...] [options]
Options:
-g, --global Remove from global scope instead of project scope
-s, --skill <s>... Skills to remove ('*' for all)
--all Shorthand for --skill '*' -y
-y, --yes Skip confirmation prompts
list
agents-skills list [options]
Options:
-g, --global List global skills (default: project)
-a, --agent <a>... Filter by specific agents
--json Output as JSON (machine-readable, no ANSI codes)
update
agents-skills update [skills...] [options]
Options:
-g, --global Update global skills only
-p, --project Update project skills only
-y, --yes Skip the scope prompt (auto-detect)
link / unlink
agents-skills link [agents...] [options]
agents-skills unlink [agents...] [options]
Options (link):
-g, --global Link global skills dirs instead of project ones
--migrate Move existing agent skills dirs into the canonical dir
Options (unlink):
-g, --global Unlink global skills dirs instead of project ones
Agents default to auto-detected installed agents; use '*' for all.
Project structure
src/
├── lib.rs Library root: re-exports Manager + core primitives
├── manager.rs High-level Manager facade (add/list/remove/update)
├── error.rs Unified error type and Result alias
├── core/ Domain logic (pure functions, dependency-injectable)
│ ├── source.rs Source string parsing
│ ├── agents.rs Agent → skills directory mapping table
│ ├── discover.rs SKILL.md discovery + frontmatter parsing
│ ├── fetch.rs git clone / HTTP download / archive extraction
│ ├── install.rs Install skills into the canonical dir + listing
│ ├── link.rs Directory-level agent links (link/unlink/migrate)
│ └── lock.rs skills-lock.json read/write + content hashing
├── main.rs Bin entry point (thin CLI over the library)
├── cli.rs clap command tree (commands, aliases, flags)
└── commands/ CLI rendering layer (arg unpacking + output only)
├── add.rs
├── remove.rs
├── list.rs
├── update.rs
├── link.rs
└── unlink.rs
examples/
├── add_skill.rs Install a skill via the Manager facade (real usage)
└── manage.rs add → list → remove lifecycle on a scratch dir
tests/
├── common/mod.rs Shared integration-test fixtures
├── lib_api.rs Library API integration tests
├── cli_add.rs
├── cli_remove.rs
├── cli_list.rs
├── cli_link.rs
└── cli_version.rs
Development
Tests follow the test pyramid: fast, isolated unit tests live inline in src/ via
#[cfg(test)], while black-box integration tests in tests/ drive the real CLI through
assert_cmd.
Design choices
The crate intentionally stays minimal and stable:
- Library first — the library is the primary interface; the CLI is a thin rendering layer over the same public API.
- Pure data — the library never prints and never calls
process::exit; it returns structured outcomes and surfaces errors viaResult. - No telemetry — nothing leaves your machine.
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.