Meta-layer for Claude Code skills: catalog, health check, cross-machine sync, usage stats.
When your ~/.claude/skills/ has 5 skills, you remember what each does.
When it has 50, you don't.
When it has 100 across two machines, you have no idea what's installed, what's actually being used, or whether your laptop and desktop are in sync.
skillctl builds the missing meta layer:
- Catalog — scan all skill source roots, emit JSONL + Markdown + searchable HTML dashboard
- Health — flag dead
/slashreferences, detect cross-machine drift - Usage — aggregate skill invocation events; surface top-used and never-used
- Budget — warn when
descriptiontext exceeds Claude Code's silent truncation limit
Zero runtime dependencies. Single binary. Python 3.11+.
# Not on PyPI yet:
git clone https://github.com/Fengsh0923/skillctl.git
cd skillctl
pip install -e .skillctl init # writes ~/.skillctl/config.toml with sensible defaults
skillctl rebuild # scan + emit catalog outputs
skillctl check # health check (drift + dead refs)
skillctl usage --top 10 # leaderboardOpen ~/.skillctl/catalog/catalog.html in a browser.
Walks every [[sources]] root in your config and produces:
| Output | Purpose |
|---|---|
catalog.jsonl |
Full record per skill (status, origin, triggers, path, usage). |
catalog_active.jsonl |
Only status="active" — what's actually loadable. |
catalog_inactive.jsonl |
Archival / reference / deprecated. |
catalog.md |
Human-readable inventory. |
catalog.html |
Searchable dashboard with charts. |
reference_issues.md |
Skills that reference /xxx slashes which don't exist. |
records/<machine>.jsonl |
This machine's snapshot, for cross-machine merge. |
- Cross-machine drift: skill
foois active onlaptopbut missing ondesktop. Listed by machine pair. - Dead references: skill
bar's SKILL.md mentions/bazbut/bazisn't installed anywhere.
Exits non-zero if issues found — wire into CI or pre-commit.
Reads usage events from ~/.skillctl/catalog/usage/<machine>.jsonl. Each event is one line:
{"ts": "2026-05-13T10:00:00Z", "skill": "skillname"}You append events however you like. Easiest: have your Claude Code PostToolUse hook call skillctl record <skill-name>.
After events accumulate, skillctl usage shows top-N skills by 30-day calls — telling you which skills earn their keep and which 6-month-old .skills/foo/ is dead weight.
~/.skillctl/config.toml. Run skillctl init to scaffold one. See docs/CONFIG.md for every option.
Minimal example:
[[sources]]
status = "active"
root = "~/.claude/skills"
glob = "*/SKILL.md"
[[sources]]
status = "active"
root = "~/.claude/commands"
glob = "*.md"I was running Claude Code on two Macs with shared skills via cloud sync. After 9 months I had 67 active skills and 113 inactive ones, with no way to tell:
- which skills I actually invoke vs. which sit there from 6 months ago
- whether laptop and desktop were in sync (spoiler: they weren't)
- which skills' descriptions were so long they got silently truncated by Claude Code
- which skills referenced other skills that I'd since renamed or deleted
So I built this for myself. Open-sourced because the gap is generic, not personal.
v0.1 — works for me daily. APIs and config schema may change before v1.
MIT