pranavworks100/fennec

Daily tech digest for engineers

Python

1

85 commits

updated Oct 6, 2026

See the code

See what people are saying

README

🦊 Fennec

Zero-cost, ultra-premium daily tech news digest for software engineers. Runs fully serverless on GitHub. No hosting fees. No databases. No nonsense.

Live site: https://pranavworks100.github.io/fennec/


How It Works

A Python script (scrape.py) runs once a day via GitHub Actions. It uses the Google Gemini API and LangGraph to:

  1. Pull the past 24 hours of tech signals from Hacker News (top stories) and a curated set of RSS feeds
  2. Filter and de-duplicate results, prioritise high-score HN stories, and cap the candidate pool
  3. Ask the model to rank the candidate headlines and keep the top 10
  4. Roast and classify each of the top items as COOKING 🔥 or COOKED 💀 and write them to public/news.json

A Vite + React + TypeScript frontend hosted on GitHub Pages reads news.json as a static file — no backend, no API, zero cost.


Stack

LayerTechnology
FrontendVite + React + TypeScript
HostingGitHub Pages (free)
ScraperPython + Google Gemini 3.1 Flash Lite (via google-genai)
AI OrchestrationLangGraph
Data validationPydantic v2
AutomationGitHub Actions
Cost$0/month

Local Setup

Prerequisites

  • Node.js 20+ (CI uses Node 20)
  • Python 3.11+
  • A Gemini API key (free tier works)

1. Clone and install

git clone https://github.com/pranavworks100/fennec.git
cd fennec
./dev.sh --setup

2. Configure environment

cp .env.example .env
# Edit .env and add your GEMINI_API_KEY

3. Run locally

# Scrape fresh news AND start dev server (recommended)
./dev.sh

# Skip scraping, use existing news.json
./dev.sh --skip

# Or via npm
npm run start    # scrape + dev server
npm run dev      # just dev server
npm run scrape   # just scraper

The app will be available at http://localhost:5173


GitHub Setup

1. Enable GitHub Pages

Go to your repo → Settings → Pages → Source: GitHub Actions

2. Set the repo name in Vite config

Edit vite.config.ts and set base to your repo name:

base: "/fennec/",

3. Add your Gemini API key as a secret

Go to Settings → Secrets and variables → Actions → New repository secret

Name:  GEMINI_API_KEY
Value: your_key_here

Or via GitHub CLI:

gh secret set GEMINI_API_KEY --body "your_gemini_api_key_here"

Notes:

  • The workflows read GEMINI_API_KEY from repository secrets; do not store your key in the repo.
  • You can trigger the scraper manually from Actions → Daily News Scraper → Run workflow to test the secret.

4. Push to main

Both workflows trigger automatically:

  • deploy.yml → builds and deploys the frontend
  • scrape.yml → runs daily at 13:00 UTC to refresh news.json (scheduled cron)

Workflows

WorkflowTriggerWhat it does
scrape.ymlDaily cron (13:00 UTC) + manualRuns scrape.py, commits new news.json to main
deploy.ymlPush to mainBuilds Vite app, deploys to GitHub Pages

When the scraper commits news.json, it triggers deploy.yml automatically — so the live site always reflects today's news within minutes.

To trigger a manual scrape: Actions → Daily News Scraper → Run workflow


Customizing the Scraper

The scraper is intentionally opinionated. Edit scrape.py to change:

  • MODEL — swap to gemini-3.1-flash-lite, gemini-2.0-flash, or gemini-1.5-pro
  • fetch_signals prompt — change which topics/companies are included
  • curate_and_format prompt — adjust the tone, wit level, or format
  • Cron schedule in scrape.yml — currently set to 0 13 * * * (13:00 UTC); change the cron expression to a different UTC time if desired

Project Structure

fennec/
├── .github/
│   └── workflows/
│       ├── scrape.yml      # Daily cron: runs scrape.py
│       └── deploy.yml      # On push: builds + deploys to Pages
├── public/
│   ├── favicon.svg
│   ├── logo.png
│   ├── logo-dark.png
│   └── news.json           # Generated daily by scrape.py
├── src/
│   ├── components/
│   │   ├── Header.tsx
│   │   ├── SceneOverview.tsx
│   │   ├── Feed.tsx
│   │   └── NewsCard.tsx
│   ├── types/
│   │   └── news.ts
│   ├── App.tsx
│   ├── main.tsx
│   └── index.css
├── scrape.py               # Python scraper (HN + RSS + LangGraph + Gemini)
├── requirements.txt        # Python dependencies (google-genai, langgraph, feedparser...)
├── dev.sh                  # Local dev helper (setup, scrape, start dev server)
├── .env.example            # Example env vars (do NOT commit real secrets)
├── index.html
├── package.json
├── package-lock.json
├── vite.config.ts
├── tsconfig.json
└── tsconfig.node.json

License

MIT — fork it, ship it, make it yours.

pranavworks100/fennec

Daily tech digest for engineers

Python

1

85 commits

updated Oct 6, 2026

See the code

See what people are saying

README

🦊 Fennec

Zero-cost, ultra-premium daily tech news digest for software engineers. Runs fully serverless on GitHub. No hosting fees. No databases. No nonsense.

Live site: https://pranavworks100.github.io/fennec/


How It Works

A Python script (scrape.py) runs once a day via GitHub Actions. It uses the Google Gemini API and LangGraph to:

  1. Pull the past 24 hours of tech signals from Hacker News (top stories) and a curated set of RSS feeds
  2. Filter and de-duplicate results, prioritise high-score HN stories, and cap the candidate pool
  3. Ask the model to rank the candidate headlines and keep the top 10
  4. Roast and classify each of the top items as COOKING 🔥 or COOKED 💀 and write them to public/news.json

A Vite + React + TypeScript frontend hosted on GitHub Pages reads news.json as a static file — no backend, no API, zero cost.


Stack

LayerTechnology
FrontendVite + React + TypeScript
HostingGitHub Pages (free)
ScraperPython + Google Gemini 3.1 Flash Lite (via google-genai)
AI OrchestrationLangGraph
Data validationPydantic v2
AutomationGitHub Actions
Cost$0/month

Local Setup

Prerequisites

  • Node.js 20+ (CI uses Node 20)
  • Python 3.11+
  • A Gemini API key (free tier works)

1. Clone and install

git clone https://github.com/pranavworks100/fennec.git
cd fennec
./dev.sh --setup

2. Configure environment

cp .env.example .env
# Edit .env and add your GEMINI_API_KEY

3. Run locally

# Scrape fresh news AND start dev server (recommended)
./dev.sh

# Skip scraping, use existing news.json
./dev.sh --skip

# Or via npm
npm run start    # scrape + dev server
npm run dev      # just dev server
npm run scrape   # just scraper

The app will be available at http://localhost:5173


GitHub Setup

1. Enable GitHub Pages

Go to your repo → Settings → Pages → Source: GitHub Actions

2. Set the repo name in Vite config

Edit vite.config.ts and set base to your repo name:

base: "/fennec/",

3. Add your Gemini API key as a secret

Go to Settings → Secrets and variables → Actions → New repository secret

Name:  GEMINI_API_KEY
Value: your_key_here

Or via GitHub CLI:

gh secret set GEMINI_API_KEY --body "your_gemini_api_key_here"

Notes:

  • The workflows read GEMINI_API_KEY from repository secrets; do not store your key in the repo.
  • You can trigger the scraper manually from Actions → Daily News Scraper → Run workflow to test the secret.

4. Push to main

Both workflows trigger automatically:

  • deploy.yml → builds and deploys the frontend
  • scrape.yml → runs daily at 13:00 UTC to refresh news.json (scheduled cron)

Workflows

WorkflowTriggerWhat it does
scrape.ymlDaily cron (13:00 UTC) + manualRuns scrape.py, commits new news.json to main
deploy.ymlPush to mainBuilds Vite app, deploys to GitHub Pages

When the scraper commits news.json, it triggers deploy.yml automatically — so the live site always reflects today's news within minutes.

To trigger a manual scrape: Actions → Daily News Scraper → Run workflow


Customizing the Scraper

The scraper is intentionally opinionated. Edit scrape.py to change:

  • MODEL — swap to gemini-3.1-flash-lite, gemini-2.0-flash, or gemini-1.5-pro
  • fetch_signals prompt — change which topics/companies are included
  • curate_and_format prompt — adjust the tone, wit level, or format
  • Cron schedule in scrape.yml — currently set to 0 13 * * * (13:00 UTC); change the cron expression to a different UTC time if desired

Project Structure

fennec/
├── .github/
│   └── workflows/
│       ├── scrape.yml      # Daily cron: runs scrape.py
│       └── deploy.yml      # On push: builds + deploys to Pages
├── public/
│   ├── favicon.svg
│   ├── logo.png
│   ├── logo-dark.png
│   └── news.json           # Generated daily by scrape.py
├── src/
│   ├── components/
│   │   ├── Header.tsx
│   │   ├── SceneOverview.tsx
│   │   ├── Feed.tsx
│   │   └── NewsCard.tsx
│   ├── types/
│   │   └── news.ts
│   ├── App.tsx
│   ├── main.tsx
│   └── index.css
├── scrape.py               # Python scraper (HN + RSS + LangGraph + Gemini)
├── requirements.txt        # Python dependencies (google-genai, langgraph, feedparser...)
├── dev.sh                  # Local dev helper (setup, scrape, start dev server)
├── .env.example            # Example env vars (do NOT commit real secrets)
├── index.html
├── package.json
├── package-lock.json
├── vite.config.ts
├── tsconfig.json
└── tsconfig.node.json

License

MIT — fork it, ship it, make it yours.