A tiny client-side news/recommendation feed. RSS sources are fetched in the browser and rendered as a list of cards (title + full text + an LLM summary). There is no backend — everything runs in the browser.
This is a minimal starting scaffold: it runs out of the box, the feed is a chronological list of every source, ranking/dedup is a stub, and the LLM layer uses a mock summarizer by default.
- Pick the AI coding agent / harness you're fastest in (Claude Code, Cursor, Codex, …) and have it set up and working. During the session you may use any tools available to you — MCP, plugins, sub-agents, whatever you normally use.
- Clone this repo and make sure it runs locally (see Run it below) — so we don't spend interview time on setup.
- That's it. This repo is just the scaffold — the concrete task will be given at the start of the interview. No need to build anything in advance.
npm ci # or: npm install
npm run dev # → http://localhost:5173You should see a working feed within seconds: a local mock feed (served by the dev server, works offline) plus two CORS-friendly network sources. If the network sources are down, the local mock alone keeps the feed alive.
Stack: Vite + React + TypeScript.
npm testA small set of unit tests (Vitest) covering the pure helpers — the mock
summarizer and the personalize stub. They're a smoke check, not full coverage;
extend them as you build. CI runs build + tests on every push.
Some interesting RSS/Atom sources (Hacker News, Lobsters, The Verge) don't
send CORS headers, so a direct browser fetch fails. A minimal proxy backend
fetches them server-side and re-serves them with Access-Control-Allow-Origin: *.
It's protected from SSRF by a hostname allowlist — anonymous otherwise, no
auth, no caching, no rate limiting (see server/proxy-handler.mjs).
- Dev: nothing to do —
npm run devwires a Vite middleware at same-origin/proxy, so it just works (seefeed-proxyplugin invite.config.ts). - Standalone:
npm run proxystarts anode:httpserver onhttp://localhost:8787(override the port withPORT=...). Needed for production, since GitHub Pages only serves static files — point the built frontend at your deployed proxy viaVITE_PROXY_URLat build time.VITE_PROXY_URLreplaces the entire proxy base, path included, so the value must end in/proxy(e.g.https://proxy.example.com/proxy, not justhttps://proxy.example.com). vite preview: also served by the same middleware (configurePreviewServer).
To add a new proxied source: add it to FEEDS in src/lib/feeds.ts with
viaProxy: true, and add its hostname to ALLOWED_HOSTS in
server/proxy-handler.mjs — the two lists are kept in sync by hand.
| File | What it is |
|---|---|
src/lib/feeds.ts |
RSS sources. Heterogeneous on purpose (formats, CORS). |
src/lib/rss.ts |
Fetch + parse RSS/Atom in the browser. Naive — Promise.all, RSS 2.0 + Atom parser. |
src/lib/personalize.ts |
Interest model + ranking. Currently a stub that returns the feed unchanged. |
src/lib/summarizer.ts |
LLM layer behind a narrow summarize() interface. Mock default; WebLLM optional. |
src/lib/types.ts |
Normalized FeedItem (title, full content, …). |
src/App.tsx |
UI: cards (title + full text + summary) + an interests placeholder. |
public/mock-feed.xml |
Local feed with full article text (content:encoded); works offline. |
server/proxy-handler.mjs |
Pure CORS proxy logic (URL allowlist, fetch, response shaping). |
server/proxy.mjs |
Standalone node:http server around the handler above (npm run proxy). |
Change anything — it's a starting point, not a reference.