Résumé as typed JSON. Tweak it with any LLM, edit it by hand. Export a clean PDF, same result every time. Store sections individually so you can reuse and remix them later, reassemble everything back into a polished PDF whenever you need.
betterauth
drizzle-orm
json-render
nextjs
tailwind
tanstack-start
Agentic JSON Resume
Résumé as typed JSON. Tweak it with any LLM, edit it by hand. Export a clean PDF, same result every time. Store sections individually so you can reuse and remix them later, reassemble everything back into a polished PDF whenever you need.
What is this?
The usual résumé workflow sucks. You tailor it for a job, paste it into Google Docs or Word, spend an hour making the formatting look right, and six months later you need a different version and can't remember what changed. Or you ask an LLM to rewrite it, get back a pile of text, and do the whole formatting dance over again.
This keeps your résumé as JSON — structured sections you own entirely. You can feed the whole thing (or just the parts you want) into any LLM alongside a job description and get back revised JSON that fits the same schema. You can edit it in the app, by hand in a text editor, wherever. When you export, it's the same PDF layout every time because the layout is code, not a document.
The real win is that sections stay separate. Your experience section is its own block, your skills are their own block. You can shuffle them around, copy them between versions, or build a totally different resume from the same source data. Export whatever you need whenever you need it.
No vendor lock-in, no waiting for some background service, no proprietary format. Just JSON and React components that turn it into a PDF.
Use vp from the repo root for lint/format/test per CLAUDE.md (e.g. vp check).
AI Assistant
The resume workbench has a built-in AI assistant that can analyze job fit, rewrite summaries, and plan tailored drafts.
No server-side API keys are stored. You bring your own key:
Open any resume and go to the AI tab.
Expand AI Provider Settings, paste your OpenRouter API key, and pick a model.
Choose whether the key is stored in localStorage (persists across tabs) or sessionStorage (cleared on tab close).
For hosted OpenRouter chat, the key is sent to this app's server for the duration of each AI request, forwarded to OpenRouter, and never persisted server-side.
Local testing with LM Studio — no key needed, just set two env vars:
bash
1LMSTUDIO_BASE_URL=http://localhost:1234/v1
2LMSTUDIO_MODEL=gemma-3-12b-it # match the identifier shown in LM Studio
When LMSTUDIO_BASE_URL is set, the server ignores any key sent by the client and routes all requests to LM Studio instead.
See apps/web/src/features/agentic-tools/README.md for the full AI layer reference.