Manage the ai-grind skills library: harvest upstream skills/commands/agents into the catalog, author new skills, and sync the flat mirrors (committed plugin bundle, loadable/, .agents/, project/global .claude dirs) via the skills_sync MCP tool or the scripts directly. Use when adding or editing a skill, when a skill isn't showing up in a session, when asked to "sync the skills", after editing sources.toml, to check what the library contains, or to find skills scattered across the machine that...
Installs into .claude/skills of the current project.
Are you the author of Skills Sync?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/ugbot-skills-sync)
---
name: skills-sync
description: >
Manage the ai-grind skills library: harvest upstream skills/commands/agents
into the catalog, author new skills, and sync the flat mirrors (committed
plugin bundle, loadable/, .agents/, project/global .claude dirs) via the
skills_sync MCP tool or the scripts directly. Use when adding or editing a
skill, when a skill isn't showing up in a session, when asked to "sync the
skills", after editing sources.toml, to check what the library contains, or
to find skills scattered across the machine that the library hasn't adopted
yet (discover/adopt). For CRDT live skills see live-skills instead.
---
# Skills library sync (devtools-mcp `skills_sync`)
## Skill folder anatomy (the ground rules)
- Folder-form skill: `<name>/SKILL.md` with YAML frontmatter declaring
`name:` (**must equal the folder name**) and a trigger-rich `description:`.
Anything else in the folder (references/, scripts, data) is a bundled asset
and travels with the skill.
- Single-file skill: `<name>.md` with the same frontmatter; harvest wraps
it into `<name>/SKILL.md`.
- Command: `.claude/commands/<name>.md`, flat file, name = filename stem.
- Agent: `.claude/agents/<name>.md`, flat file with frontmatter.
- Clients load flat `skills/<name>/SKILL.md` only. They never recurse into
category subtrees, which is why every mirror is flattened and skill names
must be globally unique.
The library has two sources and several derived mirrors:
- `skills/authored/` holds hand-written skills, committed. The source of truth
for original skills. Folder name must equal frontmatter `name:`.
- `skills/catalog/` holds harvested copies of upstream assets listed in
`skills/sources.toml`, regenerated by `harvest.py` (never edit by hand).
- Mirrors (flat, what clients actually load): `plugin/` (committed, the
Claude Code plugin bundle), `skills/loadable/` and `.agents/` (gitignored,
derived), plus per-item overwrite into `<repo>/.claude/` and `~/.claude/`.
## Via MCP (preferred)
```
skills_sync(action="status") # counts + mirror freshness
skills_sync(action="discover") # scan the machine for unharvested assets
skills_sync(action="adopt", src="C:/code/x/.claude/skills/y", category="profiling")
skills_sync(action="harvest") # refresh catalog/ from sources.toml
skills_sync(action="sync", target="all") # local + plugin + agents
skills_sync(action="sync", target="global") # ~/.claude (explicit only)
```
`discover` scans every project `.claude/{skills,commands,agents}` dir. Scan
roots are derived from where sources.toml already harvests (plus `~/.claude`
and `$DEVTOOLS_MCP_SKILL_SCAN_ROOTS`), and reports only assets whose name
isn't in the library, flagging malformed ones (missing SKILL.md, missing
frontmatter, folder/name mismatch). `adopt` validates one candidate, appends
its `[[item]]` to sources.toml (still the single source of truth, and nothing is
harvested that isn't listed there), and re-runs harvest.
`target="all"` covers only the wholly-owned derived mirrors; `project`/`global`
write into shared `.claude` dirs so they must be named explicitly. If the server
is an installed package (not the checkout), set `DEVTOOLS_MCP_SKILLS_ROOT` to
the `skills/` directory.
## Via scripts (same behavior)
```
uv run python skills/harvest.py
uv run python skills/sync.py --target plugin
```
## Adding a new authored skill
1. Write `skills/authored/skills/<category>/<name>/SKILL.md` (frontmatter
`name:` == folder name, plus a trigger-rich `description:`).
2. `skills_sync(action="sync", target="all")`.
3. Commit `skills/authored/...` **and** the regenerated `plugin/` output;
update the counts in `skills/README.md` and `PROJECT_MAP.md`.
4. New skills load in the next session, or after a plugin reload. The
current session's skill list is fixed at startup.
## Adding a harvested (upstream) asset
Add an `[[item]]` to `skills/sources.toml` (src path, type, category), then
`skills_sync(action="harvest")` followed by the sync. Harvest copies; it never
moves or edits upstream files, and re-running is idempotent.
## Gotchas
- **Skill missing from a session** → it was probably authored after session
start; check `skills_sync(action="status")`, then reload the plugin.
- **"duplicate skill name"** assert → an authored skill shadows a harvested
name (or two authored folders collide); names are globally unique.
- **"too many missing sources"** → catalog/ is stale for this checkout; run
harvest. A handful of upstream-only absences is tolerated and listed.
- `plugin/` is committed derived output, so always `git diff plugin/` after a
sync and commit it with the authored change, or clients drift from source.
See [[live-skills]] for the CRDT live-skill system (a separate store, where live
skills materialize into `~/.claude/skills/` directly and are not part of these
mirrors) and [[devtools-mcp-usage]] for the wider server workflow.