Tech topics curator/presenter/tracker for the CEC live stream daily show.
A local Claude Code session researches current PC-building and gaming news and loads candidate topics into a local SQLite database. The hosts review them in a simple localhost site and mark each topic ★ highlighted (cover it on stream) or ✕ declined (skip it). The bot reads a window of those recent decisions before building its next list, so it learns what the show does and doesn't want.
No dependencies — Python 3.9+ stdlib only.
Needs just (single binary) and Python 3.9+.
# 1. one-time, after cloning: checks python, creates the database
just setup
# 2. start the site (leave it running)
just dev # → http://127.0.0.1:8765 (also reachable on the LAN)
# 3. in the browser: hit "⟳ Fetch new topics", then highlight / declineThe ⟳ Fetch new topics button runs your local claude CLI headless
(claude -p) with the workflow in CLAUDE.md — so the machine
running the server needs Claude Code installed and signed in. The status bar
under the header shows progress, lets you cancel, and keeps the bot's rundown
under "bot output". Running claude "find new topics" in a terminal does the
exact same thing.
Fetching is additive: a new batch stacks on top of whatever is still pending. Topics only leave the queue when you decline (or highlight) them.
Each card has an optional note box — type a quick why before (or after) hitting highlight/decline ("too rumor-y", "more like this"). Those reasons go into the feedback the bot reads on its next fetch, so they directly steer future batches. On decided topics, edit the note and hit Save note (or Enter).
The 😂 Jokes sidebar shows 3 jokes at a time. The set holds still — a joke only changes when you react to it: Laugh keeps it (it may resurface a few days later) and Pass bins it for good, and a fresh one slides into the freed slot. Once a joke has been shown it sits out of rotation for a few days before it can come back, so the carousel doesn't repeat itself. Its ⟳ button runs a jokes-only bot fetch to add fresh jokes (the carousel's equivalent of the topics Fetch — no topic run needed), and every topics fetch tops the pool up too. The DB keeps a rolling month and prunes older ones, and the 😂 spine collapses the sidebar when you need the width.
For hands-off display on stream, the ⤓ Auto-scroll toggle in the tab row ping-pongs the feed down and back up at one steady speed; the slider next to it sets that speed. Both the on/off state and the speed are remembered between sessions.
The page is quiet by default — it talks to the server only when you click something (↻ Refresh re-pulls the list) or when you return to the tab. The exception: while a fetch job is running, it watches the job status every few seconds so the bar updates and the batch appears when it lands, then goes quiet again. The jokes never auto-advance — they change only when you react. The research itself runs entirely server-side, so you can kick it off, close the browser, and find the results waiting later.
No just? It's all plain stdlib underneath: python3 server.py.
To customize how the bot is launched (flags, model, a test stub), set
TOPICS_FETCH_CMD to the full command before starting the server.
just dev binds to all interfaces by default, so a co-host can open
http://<this-machine-ip>:8765 (LAN IP or tailscale IP) from another machine
on the same network — no extra flags. The site has no auth, so keep it to
networks you trust (your LAN or tailnet).
Want localhost-only instead? Pass the bind address:
just dev 127.0.0.1 # or: python3 server.py --host 127.0.0.1The bot drives everything through topics.py, but it works by hand too:
python3 topics.py feedback --window 50 # recent highlight/decline decisions (JSON)
python3 topics.py recent --days 14 # recently added topics, for dedupe (JSON)
python3 topics.py add-batch batch.json # insert a JSON array of topics (dedupes)
python3 topics.py add --title "..." --url "..." --category GPUs
python3 topics.py list --status pending
python3 topics.py statsJokes have a parallel CLI:
python3 jokes.py feedback # recently laughed / passed jokes (JSON)
python3 jokes.py add-batch jokes.json # insert a JSON array of jokes (dedupes)
python3 jokes.py list --status laughed
python3 jokes.py prune # drop jokes past the 30-day window
python3 jokes.py statsThe bot's full workflow and the batch JSON shape live in CLAUDE.md.
| Path | What it is |
|---|---|
justfile |
just setup / just dev |
server.py |
localhost web server + JSON API |
topics.py |
CLI used by the bot (and humans) |
jokes.py |
CLI for the jokes carousel |
fetch.py |
runs the bot headless for the UI's fetch button |
db.py |
SQLite schema/helpers |
static/ |
the review UI |
data/topics.db |
the database (created on first run, gitignored) |