Data and ranking
Rankings shape what developers pick, so the algorithm has to be inspectable. This page covers where the data comes from, how the score is computed, and which plugins get excluded.
Where the data comes from
Three sources, all crawled automatically. Nothing is entered by hand:
- npm registry — every package carrying the
opencode-pluginkeyword, for its name, version, description, licence, deprecation status and repository URL. - GitHub GraphQL API — stars, forks, open issues, last commit date, archived status, topics and README, fetched in batches per repository.
- npm downloads API — downloads over the last 7 and 30 days.
Everything is re-crawled daily and the whole site is rebuilt. The current snapshot was generated on2026-08-20, covering 1553 plugins across14 categories.
How the composite score works
Ranking on stars alone has a structural problem: stars are cumulative, so they permanently favour whoever published first. A plugin released last week gaining 50 stars a day may not crack the top 100. So the composite score blends four signals:
score = 0.35 × credited stars (log-normalised)
+ 0.35 × monthly downloads (log-normalised)
+ 0.20 × activity (decayed by last commit date)
+ 0.10 × growth (30-day star gain, normalised by size)
archived repositories: final score × 0.35
credited stars = min(repo stars, max(monthly downloads × 8, 1000))A few deliberate trade-offs:
- Logarithmic, not linear. The real gap between 100k stars and 10k stars is far smaller than the numbers suggest; linear normalisation would let the head of the list flatten everything else.
- Downloads weigh the same as stars. A star means "this looks good"; a download means "I actually run this". Downloads are harder to game but get inflated by transitive dependencies, so the two hold each other in check.
- Growth is normalised by size. A 100-star project gaining 50 stars deserves more attention than a 5,000-star project gaining the same 50.
- Archived is not deleted. An archived project can still be the best available option — it just has nobody maintaining it. So it is down-weighted rather than removed.
Why stars get discounted
Stars belong to a repository; downloads belong to a package.A plugin published from a large project's monorepo inherits that repository's entire star count.@mem0/opencode-plugin shows 63k stars not because the plugin is popular, but becausemem0ai/mem0 has 63k stars.
Without a correction, the top of the board fills with packages doing a few dozen downloads a month — on our first full crawl, the number one package had 270k stars and 49 monthly downloads.
So stars only count as far as the package's own install base can support. Plugins people actually use clear that ceiling easily; a package that merely sits next to a famous README does not. The floor of 1,000 keeps a newly published plugin with no downloads yet from being zeroed out.
Around 18 packages currently trigger this correction. Their real star count is still shown, but marked with a *; hover it to see the number actually credited.
Other known biases
- Trending needs history. Growth is computed by diffing daily snapshots, so that board is nearly empty when the site is freshly deployed and only becomes meaningful after a few days.
- Only npm-published plugins are indexed. Purely local plugins — scripts living in
.opencode/plugins/— cannot be discovered automatically. - Downloads are polluted by transitive dependencies. If a plugin is depended on by another popular package, its download count includes people who never chose it directly.
How categories are assigned
Rule matching over the package name, description, npm keywords and GitHub topics. The first matching category wins, so the category list is ordered by specificity. Automatic categorisation is wrong sometimes — a manual override layer fixes it, see the submit page.
How health badges are decided
| Badge | Trigger |
|---|---|
| Repository archived | The GitHub repository is marked archived |
| Deprecated on npm | The latest version carries a deprecated field |
| No commits in over a year | Last commit > 365 days ago |
| No commits in six months | Last commit > 180 days ago |
| No public repository | npm metadata has no resolvable repository URL |
| No documentation | README missing or shorter than 200 characters |
| No licence declared | Neither npm nor GitHub reports a licence |
| Actively maintained | None of the above triggered |
What gets excluded
A package needs at least one of these to be listed: a description, a README, any stars, more than 50 monthly downloads, or a manual featured flag. That filters out a substantial number of empty packages and test publishes. There is also a manual blocklist for obvious placeholders and spam.
About this site
OpenDock is not affiliated with opencode. It is an independent community directory. All plugin data comes from public APIs, and the site itself is fully static.
Analytics and privacy
This site uses a self-hosted Umami instance (umami.qunqin.org) to count visits, and records anonymised session replays for 15% of sessions, so we can see which plugins people actually look for and where the site gets in the way. No advertising, no third-party trackers, and nothing is sold or shared.
Those scripts do not load until you press "Allow". A prompt appears in the corner on your first visit; your choice is stored locally in your browser, and clearing site data lets you choose again. If you decline, no analytics script is loaded and no usage data leaves your browser.
One thing declining does not stop: plugin author avatars are served from GitHub's own CDN (avatars.githubusercontent.com), so your browser requests them whenever a listing is on screen. That is inherent to showing the avatars at all, and GitHub sees those requests the same way it would if you visited the repository directly.