subagent-deck
Live, public, searchable leaderboard of Claude Code subagents (.claude/agents/*.md) harvested directly from public GitHub. Browse real, production-grade subagent definitions ranked by repo stars — filter by task (security, refactor, test, review, performance), by allowed tools, by model — copy-paste any one into your own .claude/agents/ folder in seconds.
- Live URL (when deployed):
https://holyai.me/subagent-deck/ - Port:
4710 - Base path:
/subagent-deck - Auth: none. Every endpoint is public; writes are IP-rate-limited.
- License: MIT
What this is
By mid-2026 the Claude Code subagent ecosystem has exploded — multiple "awesome-lists" (VoltAgent/awesome-claude-code-subagents, 0xfurai/claude-code-subagents, rahulvrane/awesome-claude-agents, …) curate hundreds of community-contributed .claude/agents/<name>.md files. None of these lists are searchable, filterable, or scored. subagent-deck is.
Every row on the homepage is a real subagent file fetched at runtime from public GitHub. No mocks. No seed arrays of fake content. No Math.random(). The only "seed" allowed in the codebase is data/seed-repos.json, a list of ~25 well-known repo identifiers (owner/repo strings) used purely as a discovery starting point when the GitHub Code Search path is unavailable.
Why no auth?
The product is public-shaped. People come to find subagents, copy them, share links, and embed badges. Nothing here requires a password. The two write endpoints (/api/refresh, /api/submit-repo) are also public — they're rate-limited per-IP via an in-memory token bucket. There is no ADMIN_PASS, no /admin route, no Basic Auth middleware. Anywhere.
Stack
- Node.js 20+
- Express 4
- better-sqlite3 (WAL mode)
- node-cron
- helmet + compression
- gpt-tokenizer (
o200k_base, pure-JS, no network) - Vanilla JS SPA — dark theme, no framework, no build step
Data sources (real, public, no fabrication)
| Source | URL | Auth | Frequency |
|---|---|---|---|
| GitHub Code Search | https://api.github.com/search/code?q=path%3A.claude%2Fagents+extension%3Amd | GITHUB_TOKEN required | every 6 h |
| GitHub Repo metadata | https://api.github.com/repos/:owner/:repo | optional token | every 12 h |
| GitHub Contents | https://api.github.com/repos/:owner/:repo/contents/.claude/agents | optional token | every 12 h |
| GitHub raw content | https://raw.githubusercontent.com/:owner/:repo/:branch/.claude/agents/:name.md | none | every 12 h |
| Curated seed | data/seed-repos.json | n/a | static (~25 repo identifiers) |
GITHUB_TOKEN is strongly recommended (Code Search requires authentication; without it the service falls back to crawling the seed list only). Without a token, the unauthenticated REST API rate limit is 60 req/h per IP, which clamps the discovery footprint substantially.
The orchestrator at deploy time injects the real token from the RNDLAB key vault into GITHUB_TOKEN. The repo's .env.example uses the placeholder __INJECT_FROM_VAULT__; never commit a real key.
Running locally
npm install --omit=optional
cp .env.example .env
# add a GITHUB_TOKEN to .env for the Code Search path (optional but recommended)
node server.js
The server boots on :4710, mounts everything under /subagent-deck/, and (if the DB is empty) runs an initial discovery pass in the background that walks the seed list. After ~30 seconds you should see the first subagents on http://localhost:4710/subagent-deck/.
To prime the DB synchronously:
npm run seed
Endpoints
Pages
GET /subagent-deck/— leaderboard SPAGET /subagent-deck/sub/:owner/:repo/:slug— subagent detail (prompt body + copy button + scoring breakdown + badge embed)GET /subagent-deck/repo/:owner/:repo— per-repo subagent listGET /subagent-deck/about— methodology, sources, FAQ
JSON API
GET /subagent-deck/health—{ok, version, subagents_count, repos_count, last_refresh}(auth-free, used for smoke checks)GET /subagent-deck/api/subagents— paginated list. Query params:q,category,model,min_quality,min_stars,sort(quality|stars|freshness|tokens),order,page,per_pageGET /subagent-deck/api/subagents/:owner/:repo/:slug— single subagent detail (frontmatter, body, scoring, history)GET /subagent-deck/api/repos— paginated repo listGET /subagent-deck/api/repos/:owner/:repo— repo detail + nested subagentsGET /subagent-deck/api/categories— category countsGET /subagent-deck/api/stats— global statsGET /subagent-deck/api/sources— every data source URL + last fetch (transparency)GET /subagent-deck/api/badge?slug=:owner/:repo/:slug— SVG shields-style badgePOST /subagent-deck/api/refresh— kick a manual refresh cycle (1 per IP per 5 min)POST /subagent-deck/api/discover— kick a manual discovery cycle (1 per IP per 5 min)POST /subagent-deck/api/submit-repo—{owner, repo}→ queues the repo for the next discovery cycle (2 per IP per hour)
Honest-failure policy
If a GitHub fetch fails (network, rate-limit, 404), we record the failure in the fetch_runs table and leave the previous snapshot visible. We never substitute synthetic values. If gpt-tokenizer fails to install (native-build flake), token counts fall back to a chars/4 approximation and the server keeps running.
Repo layout
subagent-deck/
├─ SPEC.md
├─ README.md (this)
├─ CLAUDE.md
├─ DEPLOY_MANIFEST.json
├─ package.json
├─ .env.example
├─ .gitignore
├─ server.js # express bootstrap, mount routers, cron, db init
├─ db.js # better-sqlite3 (WAL), migrations, prepared stmts
├─ scoring.js # 0-100 quality score + grade
├─ classifier.js # keyword-based category classifier
├─ frontmatter.js # tiny YAML frontmatter parser (no extra deps)
├─ tokens.js # body_chars + body_tokens via gpt-tokenizer
├─ ratelimit.js # in-memory IP token bucket
├─ github.js # GitHub REST wrapper with rate-limit-aware backoff
├─ data/seed-repos.json
├─ fetchers/
│ ├─ discover.js
│ ├─ refreshRepo.js
│ └─ index.js
├─ routes/
│ ├─ api.js
│ ├─ pages.js
│ └─ badge.js
├─ public/
│ ├─ index.html
│ ├─ app.js
│ ├─ style.css
│ └─ favicon.svg
└─ scripts/
└─ seed.js
License
MIT