Source for sprouse.dev — Andrew Sprouse's interactive resume site.
It's also intended to be a forkable example of a small but production-grade modern stack: Astro 5 + React 19 + Tailwind v4 deployed to Cloudflare Workers, with an in-page chatbot powered by the Vercel AI SDK and a JD-driven tailored-CV flow. MIT-licensed — fork it freely.
| Path | What it is |
|---|---|
resume.json |
Canonical career data — the source of truth for everything rendered on the site |
schemas/ |
JSON Schema (resume.schema.json) that validates resume.json and codegens src/types/resume.ts |
src/pages/ |
Routes. / /principal /cofounder are three positioning variants; /cv is the printable CV |
src/components/ |
Astro components for layout + React islands for Chat.tsx and TailorPanel.tsx |
src/lib/ |
Resume helpers, BM25 retrieval, chat/tailor prompts |
src/pages/api/ |
/api/chat (streaming Claude responses), /api/tailor (JD-driven CV re-rank) |
chatbot/ |
Q&A corpus (qa/*.md), persona (profile.md), and methodology (APPROACH.md) |
public/ |
Static assets — favicon, OG image, portrait illustration |
wrangler.jsonc |
Cloudflare Workers config including the rate-limit bindings |
astro.config.mjs |
Astro + Cloudflare adapter, sitemap, React aliasing for the Edge runtime |
┌─ resume.json ──────────── source of truth (validated against JSON Schema)
│ └─ src/types/resume.ts (auto-generated)
│
├─ chatbot/
│ ├─ profile.md ──────── persona + voice rules
│ ├─ qa/*.md ─────────── Q&A corpus (~150 entries across 6 categories)
│ └─ APPROACH.md ─────── methodology, rendered at /about-the-bot
│
├─ Astro routes
│ ├─ /, /principal, /cofounder ─ three positioning variants of the resume
│ ├─ /cv ─ print-first CV with tailor-on-JD panel
│ └─ /about-the-bot ─ how the chatbot works
│
└─ React islands
├─ Chat.tsx ──────── floating chat with tools (scroll_to_role, expand_role, …)
└─ TailorPanel.tsx ─ JD → /api/tailor → URL-hash-encoded patch → DOM mutation
Three positioning variants (src/lib/variants.ts) let the resume re-frame itself for different audiences. Each variant has its own headline, "open to" line, and SEO meta, but the experience/skills/education content is shared.
The tailor flow is a deliberate experiment: paste a JD, the model returns a structured patch (a positioning variant, a rewritten summary, per-role project ordering, hidden projects, and emphasized skills) which is applied via DOM mutation and encoded into the URL hash so it's shareable and survives reload. The model never rewrites project descriptions — it can only re-rank, hide, and re-emphasize what's already in resume.json. That guarantees you can't accidentally generate experience you don't have.
Requires Node 22+ and an Anthropic API key for the AI endpoints.
npm install
cp .dev.vars.example .dev.vars # then put your real ANTHROPIC_API_KEY in it
npm run devOpen http://localhost:4321.
| Script | What it does |
|---|---|
dev |
Astro dev server |
build |
Astro production build to dist/ |
preview |
Serve the built dist/ locally |
format / format:check |
Prettier write / check |
lint |
ESLint |
typecheck |
astro check (types across .astro + .ts/.tsx) |
validate:resume |
Validate resume.json against schemas/resume.schema.json (Ajv) |
gen:types |
Regenerate src/types/resume.ts from the JSON Schema |
check |
All of the above (validate, format, lint, typecheck) — what CI runs |
Deployed to Cloudflare Workers via @astrojs/cloudflare (output: 'server'). Asset serving is configured through wrangler.jsonc + public/.assetsignore so the Worker bundle and the static asset upload don't fight.
To deploy your own fork:
- Create a Cloudflare Workers project (Pages will also work, but this repo is configured for Workers)
- Set
ANTHROPIC_API_KEYas a Worker secret - Update
wrangler.jsonc:- Change
nameto your own - Change the three
unsafe.bindings.namespace_idvalues — they're per-account globals, so collisions with mine would cross your traffic with mine
- Change
wrangler deploy(or wire it to your CI of choice)
Methodology lives in chatbot/APPROACH.md (also rendered at /about-the-bot on the live site). The short version:
- Q&A corpus over RAG-over-blog. Andrew hand-wrote ~150 Q&A pairs across 6 categories. They're parsed at build time from
chatbot/qa/*.md. - BM25 retrieval, no vector DB. For 150 docs you don't need embeddings;
src/lib/retrieval.tsis a from-scratch BM25 with the usualk1=1.5, b=0.75parameters. - Persona-grounded. Top-5 retrieved Q&As +
chatbot/profile.mdgo into the system prompt. The model is told to answer as Andrew, drawing from these as ground truth. - Tools. The chat can scroll to a specific role, expand a collapsed retrospective entry, navigate between variant landing pages, or open
/about-the-bot.
Steps to make it yours (rough order):
- Replace
resume.json(the schema inschemas/resume.schema.jsonvalidates structure;npm run validate:resumewill tell you what's missing) - Run
npm run gen:typesto regenerate types from your schema if you extend it - Replace
chatbot/profile.mdwith your persona + voice rules - Replace
chatbot/qa/*.mdwith your own Q&A entries — seechatbot/APPROACH.mdfor the format. Empty answers are silently skipped, so you can stub headings as you go. - Replace
public/illustration/portrait.pngandpublic/illustration/original.jpegwith your own images (or removesrc/components/Portrait.astroentirely) - Regenerate the OG image: load
/ogin dev, screenshot at 1200×630, save topublic/og.png - Edit
src/lib/variants.tswith your own positioning headlines - Edit
astro.config.mjssiteto your domain - Update
wrangler.jsoncnameand rate-limitnamespace_idvalues - Run
npm run checkand fix what falls out
- Astro 5 — content-first, SSR via
@astrojs/cloudflare - React 19 islands for
ChatandTailorPanel - Tailwind v4 with CSS-first
@theme+@utility - Vercel AI SDK v6 +
@ai-sdk/anthropic—streamTextfor chat,generateObjectfor the structured tailor patch - Streamdown — purpose-built streaming-LLM-markdown renderer
- Cloudflare Workers + native rate-limit bindings
- JSON Schema (Ajv for validation,
json-schema-to-typescriptfor type codegen) forresume.json - Prettier + ESLint +
astro check— see CONTRIBUTING.md
- CONTRIBUTING.md — local dev, style, PR process, fork-for-yourself recipe
- SECURITY.md — disclosure policy
- CODE_OF_CONDUCT.md
- chatbot/APPROACH.md — chatbot methodology
MIT. The personal content (resume.json, chatbot/, photos) describes me specifically — you obviously shouldn't ship that as-yours. The code is yours to fork.