skill-mirror
Same skill, every library, one comparison view.
A live, public, side-by-side mirror of SKILL.md files across the major AI-agent
skill libraries on GitHub. For every popular skill name (tdd, code-review,debug-loop, security-audit, ...), see exactly how Matt Pocock, Addy Osmani,
Anthropic and other trending skill repositories implement it — content diffed,
freshness tracked, all from real public GitHub data.
- Live:
https://holyai.me/skill-mirror/ - Port:
4840 - Base path:
/skill-mirror - Stack: Node.js 18+ · Express · better-sqlite3 (WAL) · node-cron · helmet · compression
Watched libraries
Every entry below is a real, public GitHub repository. The seed list lives infetchers/skills.js and may be edited without code changes. If a repo or its
configured base_path is missing at ingest time, the library is shown as
"unavailable, last synced never" in the UI — no fake skills are ever inserted.
| Library | Path filter | Why we track it |
|---|---|---|
| anthropics/skills | _(root)_ | Official Anthropic skills |
| addyosmani/agent-skills | _(root)_ | Addy Osmani's curated set (40k+ stars as of May 2026) |
| mattpocock/skills | _(root)_ | Matt Pocock's TypeScript / TDD / guardrail skills |
| ananddtyagi/cc-marketplace | skills/ | Community Claude Code marketplace |
| ComposioHQ/awesome-claude-plugins | skills/ | Composio curated set |
| jeremylongshore/claude-code-plugins-plus-skills | skills/ | Community list |
| quemsah/awesome-claude-plugins | skills/ | Awesome list |
Refresh schedule
- Every 15 minutes — lightweight repo metadata (stars,
pushed_at) - Every 6 hours — full skill ingest: trees + raw
SKILL.md - Daily at 03:30 UTC — vacuum + prune ingest log
- First boot — seed libraries + immediate background ingest
All HTTP responses are conditionally fetched with If-None-Match / If-Modified-Since
so we never re-parse content that hasn't changed.
API
All routes are mounted under BASE_PATH (default /skill-mirror).
| Method | Path | Description |
|---|---|---|
| GET | /health | Liveness — auth-free, returns 200 { status: "ok", ... } |
| GET | / | HTML index |
| GET | /skill/:slug | HTML comparison view (server-rendered for SEO) |
| GET | /library/:owner/:repo | HTML library detail |
| GET | /diff/:libA/:libB/:slug | HTML unified diff (libA/libB encoded as owner~repo) |
| GET | /about | Methodology |
| GET | /card/skill/:slug.svg | 1200×630 dark SVG card |
| GET | /card/library/:owner/:repo.svg | 1200×630 dark SVG card |
| GET | /api/stats | Counters + last full ingest timestamp |
| GET | /api/libraries | All watched libraries + skill counts |
| GET | /api/library/:owner/:repo | One library + its skills |
| GET | /api/skills?slug=&library=&q=&page= | Paginated skills search |
| GET | /api/skill/:slug | Every implementation of one slug |
| GET | /api/skill/:slug/diff?a=&b= | Unified diff between two implementations |
| GET | /api/universal?min=3 | Skills present in ≥ N libraries |
| GET | /api/orphans | Skills present in exactly one library |
| GET | /api/recent?limit=10 | Newest skill rows |
| GET | /api/ingest-log?level=error&limit=50 | Recent ingest log |
There is no POST / PUT / DELETE endpoint anywhere in this service.
The data flow is strictly GitHub → SQLite → HTML/JSON.
Real-data invariants (no mocks)
- Every
skillsrow has acontent_mdthat came fromraw.githubusercontent.comand acontent_hashderived from it. - Every
librariesrow is either successfully synced or visibly marked "never synced" in the UI. - The home-page "Universal skills" panel is empty (with a friendly placeholder) until a real ingest has run.
- No
Math.random(), no hardcoded sample skills, no fallback "example" content anywhere.
Running locally
cp .env.example .env
# optional: paste a GitHub PAT into .env for 5000 req/h instead of 60.
npm install
npm start
# → skill-mirror listening on :4840/skill-mirror
The first ingest fires on boot in the background; the UI renders immediately
with "syncing…" placeholders until rows land in the DB.
Architecture
┌──────────────────────────────────┐
cron 6h → │ fetchers/skills.js (ingest) │ raw.githubusercontent.com
│ ↳ fetchers/github.js (etag, │ ──────────────────────────►
│ rate-limit pacing) │ api.github.com
└──────────────┬───────────────────┘
│ upsert
▼
┌──────────────────────────────────┐
│ db.js · better-sqlite3 WAL │
│ - libraries, skills, │
│ skill_versions, ingest_log │
└──────────────┬───────────────────┘
│ prepared statements
▼
browser ◄── express ──┐ routes/index.js (HTML)
└── routes/api.js (JSON)
+ public/{index,skill,library,diff,about}.html
+ public/app.js (vanilla JS)
+ public/style.css (dark theme)
Deployment
DEPLOY_MANIFEST.json matches the RNDLAB orchestrator schema. The Mac Mini
watcher (in cowork-deploy-bridge/) picks this directory up viaqueue.jsonl, rsyncs it to /var/www/projects/skill-mirror, installs a
systemd unit, points nginx at port 4840, and posts the showcase entry.
The optional GITHUB_TOKEN env var is injected from the RNDLAB key vault via
the __INJECT_FROM_VAULT__ placeholder. The service runs without it (60 req/h),
but with it the every-6-hour full ingest costs ~30 requests against a 5000/h
budget — plenty of headroom.
Differentiation vs. existing products
| Existing | What it does | What skill-mirror adds |
|---|---|---|
| skill-pulse | Flat searchable index across GitHub | Cross-library side-by-side comparison |
| skill-clash | Conflicts within a single library | Inter-library content view |
| skill-portability | A→F harness compat scorecard | Rendered Markdown side-by-side |
| skill-shield | Security scan | Content + diff view |
| plugin-deck | Marketplace aggregator | Granular per-skill view |
| agents-md-radar | Whole-repo AGENTS.md / CLAUDE.md | Per-skill SKILL.md |
License
MIT.