Skip to content

Repository files navigation

gitmatch

Enter one GitHub handle → a dossier on your dev persona, read from public commits. Enter two → are you mergeable? Dating-app voice, strictly about code.

Live: https://gitmatch.nvps742.workers.dev

Overview

A Vite frontend + one Cloudflare Worker + KV, served from a single origin. One handle becomes a "case file" persona card; two handles become a compatibility face-off. Every word on a card is generated from the numbers by rule — no LLM, no API keys beyond a free GitHub token, genuinely $0 to run.

Each card is also a shareable image: /api/card.svg?handle=x and /api/compare.svg?a=x&b=y render self-contained SVGs (embeddable in a README or a tweet), and a shared link unfurls into that image via server-injected OG tags.

What it reads

Public activity only, plus one thing more:

  • Public commits, repos, profile via the REST API — the last 100 commits, languages, stars, fork ratio, abandoned repos.
  • Commit hours in the author's local time. GitHub returns each commit date with its original UTC offset (…T02:51:59.000+05:30); we read the hour on the author's own clock, not UTC. "Night owl" means night where they are.
  • An anonymized count of private work via the GraphQL contributionsCollection (restrictedContributionsCount). We can see how much someone keeps private — never what it is. For many developers this is the majority of their year, so a quiet public profile is never read as "inactive."

A zero-scope GitHub PAT is enough for all of it: it only ever returns public data plus these anonymized counts. No private repo, commit, or diff is reachable.

How it works

Pure core, thin I/O shell. The functions that decide what a card says are pure and deterministic; everything that touches the network or KV is isolated at the edges, so the interesting logic is trivially testable and can never invent data.

  • src/types.ts — locked data contracts (Facts, Card, MatchCard, PrivateSignal, …).
  • src/facts.tspure. extractFacts() turns raw GitHub data into Facts (local-time commit hours, garbage-date filtering, private share); compatibility() scores two Facts 0–100 from public behavior only (language 0.4 + hours 0.3 + habits 0.3).
  • src/persona.tspure. Turns Facts into a developer's dating profile: a curated archetype ("The Tinkerer") from a combination of public behaviors, a witty bio, trait chips, and the merge face-off (a dating-match blurb + the click/clash signals). Deterministic; every number it prints is interpolated from Facts, so nothing can be invented.
  • src/github.ts — I/O. 4 calls per uncached handle: user, repos, commit search (REST) + contributions (GraphQL, best-effort — a failure never blocks a card).
  • src/cache.ts — KV get/put, versioned keys (bump VERSION on any shape change).
  • src/ratelimit.ts — per-IP, per-hour counter in KV.
  • src/card.ts — orchestration: getSoloCard() / getMatchCard().
  • src/index.ts — Worker router: /api/card, /api/compare, the .svg card endpoints, per-handle OG-tag injection on /, CORS, static assets.
  • src/client/ — Vite frontend ("The File On You"): case-file solo card, the merge face-off, local-time commit clock, and the private-work redaction bar. Builds to dist/, which the Worker serves as static assets.

The Worker serves dist/ and handles /api/* in the same script ([assets] in wrangler.toml), so the frontend's relative fetch("/api/card…") calls are same-origin by construction — no routing rule to configure, and no secret ever reaches the client.

Setup

npm install

# create the KV namespace, then paste its id into wrangler.toml
npx wrangler kv namespace create GITMATCH_KV

# the only secret (never committed)
npx wrangler secret put GITHUB_TOKEN   # PAT with NO scopes — public data only

Develop & test

npm test            # vitest + Miniflare, one file per module
npm run typecheck   # tsc --noEmit, strict mode
npm run build       # build the client into dist/

# Full stack, one origin. Two terminals (deliberately not chained — the
# shell operators for backgrounding differ across platforms):
npm run dev         # terminal 1: wrangler dev, serves dist/ + /api/*
npm run watch       # terminal 2 (optional): rebuild dist/ on client edits

Deploy

npm run deploy      # vite build → dist/, then wrangler deploy

First deploy only: register a free *.workers.dev subdomain in the Cloudflare dashboard, and set the GITHUB_TOKEN secret (above). Secrets apply on the next request, no redeploy.

Not in scope (by design)

Accounts/auth/swiping, a user database, deeper history via GraphQL beyond the contribution count, anything romantic about a real person.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages