Open source · MIT · No dependencies

An awesome list that
maintains itself.

Point it at a topic. It crawls GitHub and npm for candidates, puts each one to a decision model with a single typed question, and keeps the entries the model is confident about. Then it writes your README and rebuilds your site, every day, without you.

Node 20+ · runs free on GitHub Actions and Pages

topics/your-topic.json
// The one sentence that defines your list.
// Everything else in the repo is plumbing.
{
  "judge": {
    "criterion": "A repository qualifies when its
       own code calls, wraps or extends the
       library. It does not qualify when it
       only mentions it, or is a list of
       other people's projects.",
    "listAt": 0.75,
    "rejectAt": 0.45
  }
}
01 Crawl

Find candidates

GitHub search and npm, sharded past the 1000-result cap, with cheap regex scoring to decide what is worth a judgement.

02 Judge

Ask one question

Each candidate goes to a decision model as state, with your criterion as a single typed question. It returns a probability.

03 Review

Only the unsure

Confident yes is listed, confident no is dropped. The band between lands in a file for you, and your call is permanent.

04 Publish

README and site

The list is written between markers in your README and into a searchable static site. A scheduled Action commits both.

Why not just grep

A keyword crawler finds mentions.
A judge finds members.

Search for a library name and you get every awesome-list clone, every "models we support" table, and every project that happened to use the word. That is why hand-maintained lists go stale: filtering the noise is the work, and nobody wants to do it twice.

Asking a model one typed question per candidate moves that work off your desk and leaves a number behind - so a reader can see on what basis each entry got in, and you can be challenged on it.

GitHub + npmevery query, deduplicated
Regex signalscheap, so it runs on everything
Review queueplausible enough to be worth judging
The judgeone typed question per candidate
≥ 0.75listed
≤ 0.45rejected
in betweena file you review by hand

The judge

Two vendors, one request shape.

These are decision models, not chat models: they take a state and a map of typed questions, and return a calibrated probability for every allowed option. Set credentials for one and the framework resolves the rest.

ModelProviderSizeInput price
@cf/cloudflare/clef Cloudflare Workers AI 27B, multimodal $0.24 / 1M tokens
@cf/cloudflare/clef-flash cheapest Cloudflare Workers AI 9B, multimodal $0.09 / 1M tokens
jev-latest TypeSafe System One, direct or via Vercel AI Gateway - see provider

One adapter

Only tools/lib/judge.js knows which vendor answered. Swapping providers is an environment variable, not a refactor.

A membership check

The number says how confident the model was that an entry meets your criterion. It is not a quality score, and the site says so on every card.

Cheap enough to ignore

Judging a few thousand candidates costs cents. The GitHub API rate limit is what actually sets the pace of a run.

What you get

A repository that runs without you.

A README that updates itself

The list is rendered between two markers, so your hand-written introduction is never touched. Categories, star counts and trending entries are regenerated every run.

A searchable static site

Filter, sort and search the list in the browser. Generated from your config, served by GitHub Pages, no build step and no framework.

A dated coverage check

It fetches the largest rival lists on your topic and records which of them already list each entry. That turns "you will not find this elsewhere" into a claim someone can verify.

State you can read in a diff

Every entry, verdict and dismissal is JSON in the repo. When the crawler does something strange, git diff shows you exactly what and when.

Quickstart

A new directory is a config file.

Copy the example topic, write your criterion, add one provider's credentials as repository secrets, and run it. When a manual run looks right, uncomment the schedule and leave it alone.

terminal
# one provider, in .env.local or the environment
CLOUDFLARE_ACCOUNT_ID=...
CLOUDFLARE_API_TOKEN=...

npm run fetch      # discover and score candidates
npm run judge      # put the queue to the model
npm run refresh    # stars and push dates
npm run coverage   # what rival lists already have
npm run render     # README.md and site/

npm run update     # all of it, in order

Honest status

New code, proven shape.

awesome-x is the extraction of a crawler that has been running daily since September 2026 - same pipeline, same coverage check, with the topic-specific parts lifted into config. The framework itself is days old. The Cloudflare path is written to Cloudflare's published request shape but has not yet been run against a live key, so treat the first run as the real test, and open an issue when it is not.

Built with it

Lists running on awesome-x.