Your GitHub isn't a 6‑pin grid — it's a 3D city you can walk, and a taxi drives you to any repo you name.
Your GitHub repos aren't a list — they're a city you can walk. Every house is a repo, and its height, brightness, ornaments and garden grow from real traffic (visitors · clones · forks · views), not ⭐. Don't know where to go? Just tell the 🚕 LLM taxi driver — say "take me to the most popular repo" and the cab drives over, picks you up, and carries you straight to that repo's house.
Start here → ▶ Live demo · 💻 Run locally · 🏙️ Fork / build your own city
No sign‑up, no build step · Built with Three.js · one single index.html
- 🚶 Walkable open world — stroll a hand-built low-poly city with WASD / arrows / on-screen joystick. Houses are solid — walk around them, down brown dirt paths and along asphalt ring-roads with painted lane lines. Reach a house (repo) and its card opens (with the GitHub social preview image when set).
- 🏙️ Downtown & Hometown districts — the most popular repos rise as inner‑city towers; the rest become cozy cottages in the outer hometown, along ring roads and radial avenues.
- 📊 Metrics are the architecture — the data builds the city:
Metric Shows up as 👁 unique visitors building height ⑂ forks building width (lot size) ⬇ clones ornamentation (banners · gold trim) 📈 views garden · fence size ★ stars gold-star ornaments on the roof 🌙 activity (recent push · clones · views) window glow at night - 🗓️ Cumulative since move‑in day — visitors & clones are lifetime totals, counted from the day each house was "built" (first seen in the data). The card shows a "since YYYY‑MM‑DD" note, plus live GitHub badges — license, open issues and the latest release tag, shown when present.
- 🚕 LLM taxi driver that actually drives you — ask in natural language; it picks the best‑matching repo, explains it, then the cab comes to your spot, you board, and it carries you to the house. Three modes:
- Local search (default · no key · instant) — synonym‑expanded intent search, also metric‑aware ("most cloned", "most visited", "most forked"); a strong topic match always beats generic metric sorting, and you always get a few alternatives to pick from.
- WebLLM (in‑browser AI · no key · WebGPU)
- 🛰️ AI Foundry Live (optional · grounded) — live questions about your repos route through an Azure AI Search Knowledge Base + GitHub's hosted MCP server; falls back to Local automatically when no backend is set, so a keyless clone still works.
- 📘 Named scholars you can talk to — beyond the taxi, the plaza's NPCs are named night‑sky stars, each grounded by one live MCP knowledge source: POLARIS · the Wayfinder (your taxi driver), VEGA · the Archivist — the MS Docs engineer, and RIGEL · the Cartographer. Walk up to VEGA and ask about Azure, .NET or Copilot — it searches the official Microsoft Learn docs in real time through an Azure AI Search Foundry MCP Knowledge Source, and
gpt-5.4-miniwrites the answer back in your own language with a trace panel linking every doc. Walk up to RIGEL instead and name a public repo (facebook/react,langchain-ai/langchain) — carrying the spirit of Ariadne, it unspools a thread through the code via the keyless DeepWiki MCP and maps how the repo works inside. Each scholar also knows who it is — ask "who are you?", "what is this place?" or "who else is here?" and it answers in‑character (its myth, this city, its owner) instantly, with no knowledge‑source call. The chat keeps multi‑turn context, and every scholar is registered inSCHOLARS.md— add your own with one script. And every scholar — the taxi POLARIS included — can also just chat: ask something off‑topic or small‑talk and it replies in‑persona from general starlit knowledge (Azure AI Foundrygpt-5.4-mini), no repo pushed, with a ✦ how I answered panel. - 🏡 Six house tiers, not just taller boxes — by traffic rank each repo becomes a
cabin → cottage → house → villa → manor → portico mansion, with wings, columns, porticos, dormers, balconies and cupolas. Top repos get grand columned 저택; quiet ones get cosy cabins. - 🌳 A city that feels alive — procedurally textured houses (brick · siding · stone · shingle roofs — zero image assets), gardens, a Colmar‑style flower quarter (Petite‑Venise canals threading the whole map, rose arches, flower carts & market stalls), roaming chow‑chow pets in three coats (ivory · brown · black), street trees, street lamps, rest pavilions you can sit in (and stand back up), and koi ponds & driveway garages for your most active repos. Category logos on the roofs (AI / Data / Software / …) and proper town‑house roads.
- 🌙 Day & night, with living windows — flip the 🌙 / ☀️ switch in the HUD. At night the sky turns navy, lamps and stars switch on, and each repo's windows glow by how active it is (recent pushes · clones · views) — so your busiest repos literally light up the skyline. Overhead, each scholar's myth‑constellation (POLARIS in Ursa Minor, VEGA in Lyra, RIGEL in Orion) twinkles on the dome, above a glowing rune‑circle and drifting star‑dust in the plaza.
- 🟢 Realtime multiplayer — see other visitors walking around as avatars with name tags, plus a live · today · all-time unique-visitor counter (🟢 현재 · 오늘 · 누적) in the HUD. It's already live on the demo above; a fork stays solo until you add one free server (below).
- 🌐 English / 한국어 toggle — switch the whole UI language live from the HUD.
💬 Try asking the taxi: "most popular repo" · "a repo about AI agents" · "most cloned" · "a Korean STT project" · "anything" — it picks the best match, explains why, then drives you there.
github-traffic-monitor (private) Repolis (public)
└ daily cumulative traffic (logs/*.csv) ──┐
├─▶ .github/workflows/refresh.yml (daily)
gh api: your public repos (+ committed forks) ─┘ └ scripts/build_repos.py
└─▶ repos.json ──▶ index.html (Three.js 3D city)
- Your public repos — everything you created, plus any fork you've actually committed to (untouched mirror forks are skipped). A private repo name is never exposed on the public site.
- Traffic totals are the cumulative values gathered by
github-traffic-monitor. (GitHub's own traffic API only keeps a rolling 14‑day window — this is why a daily collector is needed to build lifetime totals.)
Talk to any scholar and your question forks two ways. A repo / docs question runs KB retrieval through that scholar's MCP knowledge source (MS Learn · DeepWiki · Azure AI Search) and comes back grounded, with references. An off‑topic / small‑talk question — or a knowledge‑base miss — is answered in persona by Azure AI Foundry gpt-5.4-mini: starlit general knowledge, no repo pushed. Every reply carries a trace panel (🔎 how I found this / ✦ how I answered) so you can see which path it took.
In a domed rotunda on the edge of town, three debate‑sages argue docs vs. code — 📜 Olddoc quotes the manual, 🌿 Livewire trusts the running source, 🌀 Hearsay echoes the crowd. The Chair, ⏳ KRONOS, weighs every claim by source × recency and lets Time — the newest living source — deliver the verdict. The banter is theater; the verdict is always the deterministic engine (byte‑identical, 52 tests green). Ambient (walk up & watch) is free and runs 0 LLM; Live debate is an optional, guarded paid LLM (LIVE_ENABLED=false by default, silent fallback when no key is set).
Type any topic — even a single noun like reasoning ratio — and three peer sages debate it live over SSE: 🌿 Livewire the advocate, 📜 Olddoc the skeptic, 🌀 Hearsay the analyst, reacting and rebutting each other by name across rounds, paced to human reading speed so you can actually follow it. Then the Chair of Time reads the whole transcript and rules. Under the hood the live debate runs on Azure AI Foundry — the three sages and the adjudicator on gpt-5.4-mini, and the Chair on the stronger gpt-5.4 (+ reasoning) for the final ruling; the models are config‑swappable in council/council.config.json, never hard‑coded. Free‑topic verdicts are AI inference, so they always wear a ⚡ unverified badge — while the six curated cases keep their deterministic math verdict. It's a small, honest take on the multi‑agent debate → judge pattern.
→ Deep‑dive: COUNCIL_PATTERN.md — the diagram, the roles, the golden rule (verified vs. unverified), and the pattern's lineage (Multi‑Agent Debate · Mixture‑of‑Agents · LLM‑as‑a‑Judge · Society of Mind).
- Fork / copy this repo. You also need a daily traffic source — keep a collector like
github-traffic-monitorrunning. - Add a secret —
Settings → Secrets and variables → Actions:GH_PAT: arepo‑scoped Personal Access Token (to check out the private traffic‑monitor + list your repos).
- Enable GitHub Pages —
Settings → Pages → Source: Deploy from a branch → main / (root). - Run the Action —
Actions → Refresh Repolis data → Run workflow(then it auto‑refreshes daily). - Done — your city opens at
https://<you>.github.io/Repolis/.
It's one static file — clone and serve:
git clone https://github.com/hyeonsangjeon/Repolis && cd Repolis
python3 -m http.server 8000 # or: npx serve .
# open http://localhost:8000Rebuild the city from your repos (needs the GitHub CLI, logged in):
gh auth login
GTM_DIR=data python3 scripts/build_repos.py # regenerates repos.jsonThe driver turns a free‑text question into the right house through a small retrieval pipeline, so it stays accurate even on a tiny in‑browser model:
your question
└▶ ① intent agent (deterministic) ─ "library / 도서관", "most popular", "most stars",
│ landmark & metric routing "recent", "most cloned·forked·viewed", "random"
│ → answered directly, no LLM → exact, zero hallucination
└▶ ② search index (inverted index) ─ tokens(name·label·lang·desc·topics) + synonyms,
│ candidate retrieval built once, lazily → top‑K shortlist
└▶ ③ ranking ────────────────────── name‑hit ≫ token‑hit ≫ substring, +topic, +popularity,
│ and your *own* word in a repo name wins
└▶ ④ LLM picks from candidates (RAG)─ WebLLM / proxy choose ONLY among the shortlist → "PICK: <repo>"
└▶ ⑤ multiple suggestions ────────── the remaining candidates become one‑tap chips you can pick from
Why it matters: "take me to the library" is a navigation intent, not a repo search — step ① catches it and drives you to the Contribution Library instead of guessing a random repo (the bug you'd hit when the question went straight to the model). Free‑form questions ("an AI agent repo", "speech‑to‑text") flow through ②–⑤.
Three modes — switch from the chat header:
| Mode | Engine | Key? | Notes |
|---|---|---|---|
| Local | synonym + metric search | none | default · instant · fully offline — what every fresh clone ships with |
| WebLLM | in‑browser LLM (WebGPU) | none | downloads ~1 GB once; picks from the index shortlist |
| 🛰️ AI Foundry Live | Cloudflare Worker → Azure AI Search KB → GitHub MCP | server‑side | optional · live, grounded repo Q&A + in‑persona general chat. Unconfigured → silent Local fallback |
🔌 No backend required. A fresh clone runs entirely in the browser: Local is the default and needs no key, and WebLLM runs on‑device. AI Foundry Live is 100% optional — if you don't deploy it, selecting that mode just falls back to Local search (no errors, no setup). Deploy to GitHub Pages and everything works. (A simpler
api/taxi.jsAzure‑OpenAI proxy is also available — see below.)
The browser always does retrieval first and hands the model only the shortlisted candidates — so a WebLLM/grounded agent just has to choose one:
// api/taxi.js — minimal agent: pick ONE from the candidates, return strict JSON
export default async function handler(req, res) {
const { question, repos } = req.body;
const sys = `You are the Repolis taxi driver. Choose the single best repo for the user
ONLY from this candidate list (never invent one). Reply warmly in one or two sentences.
Return strict JSON {"repo":"<repo-name>","message":"<reply>"}.
Candidates:\n${repos.map(r => `- ${r.repo} | ${r.lang} | ${(r.topics||[]).join(',')} | ${r.desc}`).join('\n')}`;
const r = await fetch(`${process.env.AZURE_OPENAI_ENDPOINT}/openai/deployments/${process.env.AZURE_OPENAI_DEPLOYMENT}/chat/completions?api-version=${process.env.AZURE_OPENAI_API_VERSION || '2024-08-01-preview'}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'api-key': process.env.AZURE_OPENAI_KEY },
body: JSON.stringify({
messages: [{ role: 'system', content: sys }, { role: 'user', content: question }],
temperature: 0.4, max_tokens: 200, response_format: { type: 'json_object' }
})
}).then(x => x.json());
res.setHeader('Access-Control-Allow-Origin', process.env.ALLOW_ORIGIN || '*');
res.json(JSON.parse(r.choices[0].message.content)); // → { repo, message }
}Return { "repo": "<repo-name>", "message": "<reply>" }; the city drives there and offers the remaining candidates as one‑tap alternatives. If the endpoint is unreachable, the driver silently falls back to local search.
Both backends are opt‑in — the city is fully usable without them. All configuration lives in .env.example: copy it to .env for local vercel dev, or paste the values into Vercel → Settings → Environment Variables.
The live site is powered by the Cloudflare Worker in cloudflare-taxi/ (the recommended path — see below); api/taxi-grounded.js is the equivalent Vercel function. Either one routes a free‑text question through an Azure AI Search Knowledge Base whose MCP Knowledge Source calls GitHub's hosted MCP server, then synthesises the answer with a small model — all server‑side. The Vercel function only ever holds a Search key (your Azure OpenAI key and GitHub PAT stay inside the Knowledge Source on Azure, never in the browser). The Worker additionally uses a keyless Entra ID service principal to answer off‑KB questions in the scholar's persona (general chat / small talk) — a superset the Vercel function doesn't have. The chat shows a live trace panel — knowledge source · MCP tools · reference repos — for each grounded answer.
- Azure AI Search — create a service, then a Knowledge Base (e.g.
repolis-github-kb) with a Knowledge Source of kind MCP server pointing at GitHub's hosted MCP (https://api.githubcopilot.com/mcp/, with a read‑only PAT in its header). Give the search service a managed identity with access to an Azure OpenAI deployment for answer synthesis. - Deploy
api/taxi-grounded.jsto Vercel (import the repo → you get/api/taxi-grounded). - Set env vars (see
.env.example):SEARCH_ENDPOINT,SEARCH_API_KEY,SEARCH_KB_NAME,SEARCH_KS_NAME(comma‑separated to attach more MCPs), plus optionalSEARCH_API_VERSION,GROUNDED_TIMEOUT_MS,GROUNDED_MAX_RUNTIME_S,ALLOW_ORIGIN. - In the taxi, pick 🛰️ AI Foundry Live and paste your URL (
https://<project>.vercel.app/api/taxi-grounded). Leave it blank to stay on Local.
⏱️ Vercel Hobby caveat: the KB can take 6–21 s on cold/complex queries, but Hobby caps a function at ~10 s.
GROUNDED_TIMEOUT_MS(9000) aborts just before that and the taxi silently falls back to Local — so it never hangs, but slow queries won't show grounded results. For consistently grounded answers, deploy on Vercel Pro and raisemaxDuration(andlocalStorage.taxiGroundedTimeoutMsin the browser).
☁️ Recommended — and what the live site actually runs — Cloudflare Workers (no ~10 s wall). Workers bill CPU time, not the wall‑clock spent awaiting a subrequest, so the slow KB call finishes instead of being cut off — far fewer Local fallbacks, on the free plan (no card). It's also a superset of the Vercel function: the same grounding plus in‑persona general chat (keyless Entra service principal → Azure OpenAI) and multiple scholar NPCs. The ready‑to‑deploy Worker lives in
cloudflare-taxi/:wrangler secret put SEARCH_ENDPOINT && wrangler secret put SEARCH_API_KEY && wrangler deploy(add the Entra SP secrets for general chat). Paste the resulting Worker URL into the taxi, or intoGROUNDED_DEFAULTinindex.htmlto enable it for every visitor. Full setup + the secret list:cloudflare-taxi/README.md.
Prefer a lightweight LLM picker over the full grounding stack? api/taxi.js takes the shortlist the browser already retrieved and asks Azure OpenAI to choose one repo. Deploy it to Vercel, set AZURE_OPENAI_ENDPOINT / AZURE_OPENAI_DEPLOYMENT / AZURE_OPENAI_KEY (see .env.example), and point the taxi at /api/taxi. If the endpoint is unreachable, the driver falls back to Local search. (This mode isn't in the default dropdown; it stays available for forks that prefer it.)
The static site is solo by default. To let visitors meet each other, run one tiny WebSocket server and point the world at it:
- PartyKit (one command):
npx partykit deploy(usesparty/repolis.js+partykit.json). You'll get a URL likewss://repolis.<you>.partykit.dev/parties/main/world. - Cloudflare Workers (most reliable):
cd cloudflare && npx wrangler login && npx wrangler deploy. Deploys the same server straight to your own Cloudflare account on the free plan (SQLite Durable Objects, no credit card). You'll getwss://repolis-rt.<you>.workers.dev. Seecloudflare/README.md. Handy when PartyKit's hosted login is having a bad day. - Self‑host:
node scripts/dev_realtime.mjs(needsnpm i ws) → listens onws://localhost:1999. - Point the world at it with any one of:
- URL query:
?rt=wss://… localStorage.setItem('repolisRT','wss://…')window.REPOLIS_RT = 'wss://…'
- URL query:
- Count every visitor (not just you): the three options above only affect whoever sets them. To bake it in for all visitors, set
const RT_DEFAULT='wss://…'near the realtime block inindex.htmland push. The HUD then shows 🟢 live · today · all‑time for everyone. On PartyKit the cumulative total is kept in room storage, so it survives restarts (the self‑hostwsserver keeps counts in memory only).
Privacy: the traffic logs that drive the city are committed publicly, and only your public repos — the ones you created, plus forks you've committed to — are ever shown. Private repos never appear.
| Input | Action |
|---|---|
W A S D / arrows / joystick |
Walk |
| 📱 Mobile | Left stick move · right 👁️ stick look & turn · center 🚪 opens the repo |
| Left‑drag | Orbit camera (free look) |
| Right‑drag | Steer character · WoW‑style · wheel = zoom |
Enter / click |
Open the repo you're standing at |
| 🚕 button | Ask the taxi driver |
| ☰ button | Wayfinding menu (search) |
| 🌐 button | Switch English / 한국어 |
| 🌙 / ☀️ button | Toggle day / night (night = activity-lit windows) |
Three.js (r0.160) · toon shading + inverted-hull outlines · Fresnel rim light via onBeforeCompile · procedural canvas textures — walls, roofs, grass & asphalt are all generated in code, no image assets · ACES tone mapping · day/night lighting · circle-collision walking · one dependency‑free index.html (~2,300 lines, zero build step) · GitHub Actions · (optional) Vercel + Azure OpenAI · Azure AI Search + GitHub MCP grounding · WebLLM · (optional) PartyKit / Cloudflare Workers for realtime.
Only six pins on your profile — but all your repos can be a city. If that made you smile, give it a star so more people stumble onto Repolis. New here? See CHANGELOG.md for what shipped.
Data: github-traffic-monitor · social previews: opengraph.githubassets.com.
MIT © Hyeonsang Jeon — see LICENSE.
