A self-hosted admin console for your GitHub starred repositories — auto-enriched by LLM and published to GitHub Pages.
- Fetches all starred repos via the GitHub API
- Enriches each repo with GitHub REST metadata plus an LLM-generated category, summary, and watch note using GitHub Models (free tier)
- Smart re-enrichment — skips LLM work whose source metadata hasn't changed and skips REST metadata refreshes when the repo has not moved since the last successful extended fetch
- Privacy-filtered publishing — keeps the canonical local dataset in
data/stars.jsonand publishes only public repos to the static site - Daily public history snapshots — records
data/history.jsonfor analytics and publishes the public copy alongside the site - Topic discovery engine — generates
data/discoveries.jsonfrom GitHub topic search plus curated synonym groups, and publishes a public-onlysite/public/data/discoveries.json - Publishes an admin console dashboard to GitHub Pages, refreshed daily via GitHub Actions
A dense dashboard with sidebar navigation and workspace tools:
- Sidebar: All / Watch / Discover / Cleanup / Not Enriched + dynamic LLM category counts
- Workspace strip: visible-repo stats, topic cloud, and export actions
- Table: multi-column sorting for repository, category, stars, and activity
- Filters: category, language, status, stars range, and topics
- Search: full-text across name, summary, description
- Row detail modal: repository metadata, LLM fields, and Phase 1 extended GitHub data
- Compare: select 2–4 repos for side-by-side comparison with aggregate metrics
- Analytics: timeline, category and language breakdowns, portfolio health, and trending panels over public daily history snapshots
Status chips:
- Watch — active, non-archived repos with recent pushes
- Stale — enriched repos with no recent activity
- Cleanup — archived or long-inactive repos
- Pending — not yet enriched
GH_STARS_PAT — fetches your starred repos:
- Classic PAT → scopes:
read:user,public_repo - Add
reposcope if you want private starred repos included (see Privacy) - Create at: Settings → Developer settings → Personal access tokens → Tokens (classic)
GH_MODELS_PAT — calls the LLM enrichment API:
- Fine-grained PAT → permission: Models → Read
- Create at: Settings → Developer settings → Personal access tokens → Fine-grained tokens
Settings → Secrets and variables → Actions → New repository secret:
| Name | Value |
|---|---|
GH_STARS_PAT |
your classic PAT |
GH_MODELS_PAT |
your fine-grained PAT |
Settings → Pages → Source: GitHub Actions
Actions → Refresh Starboard → Run workflow
# Install dependencies
pip install -r requirements.txt
# Configure tokens
cp .env.example .env
# edit .env — set GH_STARS_PAT and GH_MODELS_PAT
# Run the pipeline
python scripts/fetch_stars.py # fetch stars + compute heuristics
python scripts/enrich_stars.py # LLM enrichment (requires GH_MODELS_PAT)
python scripts/build_site.py # write privacy-filtered data to site/
# View the dashboard
cd site
npm install
npm run dev
# → open http://localhost:5173/Edit config.json.
"heuristics": {
"recent_star_days": 30,
"recent_activity_days": 90,
"stale_days": 365
}watch_candidate= not archived AND pushed withinrecent_activity_dayscleanup_candidate= archived OR not pushed forstale_days
Models are tried in order; on rate limit or error the next one is used:
"models": {
"chain": [
{"id": "openai/gpt-4o", "family": "openai"},
{"id": "openai/gpt-4.1-mini", "family": "openai"},
...
]
}All models are available on the GitHub Models free tier.
Controls how many source repositories are processed per run:
"discovery": {
"source_repo_limit": 30
}Set lower during development to reduce API calls. Invalid or missing values fall back to 30.
Discovery uses a curated map of topic groups to expand topic-based search coverage without an LLM:
"topic_synonyms": {
"claude-code": ["gemini-cli", "copilot-cli", "codex-cli", "..."],
"mcp": ["mcp-server", "mcp-tools", "..."]
}Keep the list small, explicit and function-oriented so the suggestions stay useful. Topic co-occurrence is also computed automatically from the user's own starred repos and merged with this map at runtime.
Edit prompts/enrich.txt — it is versioned and applied at enrichment time.
fetch_stars.py hashes each repo's relevant metadata (description, topics, language, archived). On subsequent runs, existing LLM data is preserved when:
- The content hash matches (no relevant metadata changed), and
- The enrichment is less than 30 days old
Repos that fail either condition are queued for re-enrichment. The run prints a summary:
Re-enrichment queued: 3 repos (1 content_changed, 2 aged_31d)
fetch_stars.py also caches extended GitHub REST metadata separately from the LLM fields. License, latest release, README excerpt, contributor count, 52-week commit activity and community health data are reused while cached_pushed_at matches the repo's current pushed_at/updated_at. Transient endpoint failures and README decode errors do not advance cached_pushed_at, so a later run can retry instead of freezing incomplete metadata.
build_history.py turns the public subset of data/stars.json into a daily snapshot file keyed by UTC date. The canonical data/history.json and the public site/public/data/history.json are both updated in place, so reruns on the same day stay idempotent instead of duplicating snapshots.
discover_similar.py builds discovery suggestions from the canonical stars dataset using three complementary signals: topic search expanded through curated config.json synonyms plus auto-computed topic co-occurrence from the user's own starred repos; in:description keyword queries derived from existing llm_summary fields (no new model calls); and a fallback path for repos with no GitHub topics that uses keyword queries as primary signal. Each discovery entry records seed_keywords alongside seed_topics for transparency. Writes data/discoveries.json and the public site/public/data/discoveries.json, keeping only public sources and suggestions in the published copy.
If your
GH_STARS_PAThasreposcope, private or internal repositories you have starred can be included in the canonicaldata/stars.json.
scripts/build_site.pyrefuses to publish entries withoutvisibilitymetadata and writes only repositories withvisibility == "public"tosite/public/data/stars.json. Still reviewdata/stars.jsonbefore committing or sharing it, or restrict the token topublic_repoonly.
config.json heuristics + model chain
prompts/enrich.txt LLM prompt template
scripts/
fetch_stars.py fetch stars, REST metadata, heuristics + content hash
enrich_stars.py LLM enrichment via GitHub Models
build_history.py daily public history snapshots for analytics
discover_similar.py topic + keyword discovery with co-occurrence and fallback
build_site.py write privacy-filtered data to site/public/data/
validate_models.py smoke-test model chain availability
data/
stars.json canonical source of truth, may include non-public repos
history.json daily public history snapshots for analytics
discoveries.json topic-based discovery suggestions
site/
src/ React + TypeScript dashboard source
public/data/stars.json privacy-filtered runtime data for the SPA
public/data/history.json public daily history snapshots for analytics
public/data/discoveries.json public discovery suggestions
dist/ production build output
tests/
test_fetch_stars.py unit tests — content hash + re-enrichment logic
test_enrich_stars.py unit tests — LLM payload + retry classification
test_phase1_enrichment.py unit tests — Phase 1 REST metadata + privacy filter
test_build_history.py unit tests — public history snapshot writer
test_discover_similar.py unit tests — topic discovery pipeline + public filtering
.github/workflows/refresh.yml daily refresh + Pages deploy