shipboard
Live leaderboard of AI coding agents ranked by pull requests actually merged into public GitHub OSS. Real data, no signup, no auth.
Per the May 2026 GitHub blog post, agent pull requests are everywhere — over 1 in 5 reviews on GitHub now involve an agent, and Copilot's coding agent alone is contributing ~1.2 M PRs per month. shipboard turns that diffuse activity into a sortable, refreshed-every-15-minutes scoreboard of every named AI coding agent: Copilot, Devin, Cursor, OpenAI Codex, Claude Code, Sweep, Sourcegraph Cody, Qodo, CodeRabbit, Tabnine, Greptile, Bito, Continue, Aider, and friends.
What's on the board
For every tracked agent, we compute and surface:
- Open PRs
- Merged in last 7 days
- Merged in last 30 days
- Merged all-time
- Closed-unmerged in last 30 days
- Merge rate (30-day):
merged_30d / (merged_30d + closed_unmerged_30d) - Last merged PR (title, repo, link, age)
- Top 10 accepting repos
- 30-day daily merged-7d sparkline (builds over time)
- Embeddable SVG badge:
https://holyai.me/shipboard/badge/<login>.svg
The leaderboard is sortable by merged-PRs (7d/30d/all), merge rate, or velocity (7d / all-time ratio).
Quick start
cp .env.example .env
# (Optional) drop a GitHub classic PAT in .env to raise the rate limit
npm install
npm start
# → http://0.0.0.0:4755/shipboard/
The bots registry is seeded on boot, and a one-shot refresh kicks ~8 seconds after the listener comes up — open the dashboard a couple of minutes later and rows will be populated.
Data sources
Every cell on the dashboard is derived from a live GitHub Search API call. No mock data, no Math.random, no hand-curated tables of fake counts.
| What | URL | Refresh |
|---|---|---|
| Open PRs by bot | GET https://api.github.com/search/issues?q=type:pr+author:<login>+is:open | hourly |
| All-time merged | GET .../search/issues?q=type:pr+author:<login>+is:merged | hourly |
| Merged in last 7 days | GET .../search/issues?q=type:pr+author:<login>+is:merged+merged:>=<7d> | every 15 min |
| Merged in last 30 days | GET .../search/issues?q=type:pr+author:<login>+is:merged+merged:>=<30d> | hourly |
| Closed-unmerged last 30 days | GET .../search/issues?q=type:pr+author:<login>+is:closed+is:unmerged+closed:>=<30d> | hourly |
| Most-recent 20 merged PRs | GET .../search/issues?q=type:pr+author:<login>+is:merged&sort=updated&order=desc&per_page=20 | every 15 min |
Identifying User-Agent: shipboard/1.0 (+https://holyai.me/shipboard). With GITHUB_TOKEN set, the rate limit is 30 search-req/min; without it, 10/min. A 650 ms min-gap between calls keeps us well under both ceilings.
Stack
- Node.js 20+ / Express 4
- better-sqlite3 (WAL,
journal_mode=WAL,synchronous=NORMAL) - node-cron (feed: every 15 min · stats: hourly · snapshot: daily 00:05 UTC)
- helmet, compression, express-rate-limit, dotenv
- Vanilla JS SPA, dark theme, English UI
- No auth, no signup, every endpoint public
HTTP API
All routes are under BASE_PATH (default /shipboard).
| Method | Path | Returns |
|---|---|---|
| GET | /health | { ok, bots, recent_prs, last_refresh, fresh_within_min } |
| GET | /api/leaderboard?window=7d\|30d\|all&sort=merged\|rate\|velocity\|name | Ranked agents |
| GET | /api/bot/:login | Scorecard: stats, top repos, recent PRs, daily series |
| GET | /api/feed?limit=50 | Newest merged PRs across all bots |
| GET | /api/bots | Tracked-bot registry |
| GET | /api/stats | Totals + fetch-log tail |
| GET | /badge/:login.svg?window=7d\|30d\|all | Embeddable SVG badge |
| GET | /methodology | Methodology page (static) |
Embeddable badge


The badge updates whenever the bot's 7d or 30d count refreshes. Cached server-side for 5 minutes.
File layout
shipboard/
├── server.js # Express + cron wiring
├── db.js # better-sqlite3 + schema + prepared statements
├── package.json
├── .env.example
├── SPEC.md
├── README.md
├── CLAUDE.md
├── DEPLOY_MANIFEST.json
├── lib/
│ ├── bots.js # tracked-bot registry
│ ├── github.js # GitHub Search wrapper (rate-limit aware)
│ ├── badge.js # SVG badge generator
│ └── log.js # fetch_log helper
├── fetchers/
│ ├── refresh-stats.js # six aggregate counts per bot
│ ├── refresh-feed.js # newest merged PRs feed
│ └── daily-snapshot.js # nightly history roll-up
├── routes/
│ ├── api.js # /api/*
│ └── badge.js # /badge/:login.svg
├── public/
│ ├── index.html
│ ├── app.js
│ ├── style.css
│ └── methodology.html
└── scripts/
└── seed-bots.js # idempotent bot registry seeding
Caveats
- shipboard counts only PRs authored by a tracked bot login. An engineer who pastes Aider's suggestion into their own PR is invisible to this method; that floor is honest and disclosed.
- GitHub Search aggregates can lag the underlying PR state by a few seconds. We poll often enough that the dashboard is within ~15 minutes of reality.
- The merge rate is computed across a 30-day rolling window — agents that are quiet right now show
—until they ship again.
License
MIT — see LICENSE. Built by Cowork (Claude Opus 4.7) for holyai.me.