Inspiration
Internship hunting is hard if you don’t have a career coach or hours to rewrite your resume for every posting. A lot of AI resume tools just give you a score and a few tips, then dump you back at the apply button.
I wanted something that actually finishes the job: read the resume, show which real internships fit, lightly tailor toward a role, and give you a PDF you can download. I built ResumeFit solo for the Opportunity & Access track of 305 SummerCodex (Build with Gemma). The goal was honest AI that helps students apply, not another hype chatbot.
What it does
- Upload a resume PDF, or try the bundled sample (same real pipeline, nothing faked).
- Gemma pulls out a structured profile and runs a resume health check: missing sections, weak bullets, and an honest 0 to 100 score.
- It ranks about 160+ real SWE internship listings from a community GitHub list, with a fit score and matched skills so you can see why something ranked high.
- Pick a listing, get a diff of the tailored edits, then download a one-page PDF. If your resume has a GitHub username, it can optionally pull in public projects.
- Sign in with Google or GitHub (or continue as guest) so you don’t have to re-upload every time you come back.
I’m clear about one limit: the job source only gives title, company, and location, not a full job description. Tailoring stays honest about that.
How I built it
- Frontend is Next.js 16 (App Router), TypeScript, Tailwind, and a brutalist UI. The demo is one client state machine: upload, then profile, then jobs, plus a tailor panel.
- Gemma runs through OpenRouter. All LLM calls go through one structured JSON helper with Zod schemas (parse, rank, tailor), plus a repair retry when the model drifts.
- PDFs: unpdf for reading text out, pdf-lib for generating the one-page resume.
- Jobs come from SpeedyApply-style markdown tables, with a committed snapshot so a network blip doesn’t blank the demo.
- Auth is Supabase (Google/GitHub). Signed-in users get profiles and saved resumes; guests only use localStorage.
- Hosted on Vercel. I had to respect Node runtimes and the 60s function limit, so ranking is batched to stay under Hobby caps.
Challenges I ran into
- Structured output from Gemma on OpenRouter was flaky if I relied on response_format or tool use. I put the JSON schema in the prompt, validated with Zod, and added one bounded repair retry.
- Ranking too many jobs in a single LLM call blew past Vercel’s 60s limit. I pre-filter with a cheap keyword match, then run parallel batches of 8.
- Models love inventing metrics. I match bullets by index, sanitize the output hard, and keep the PDF close to the original resume.
- pdf-lib’s standard fonts only handle WinAnsi, so weird characters can crash drawing. I sanitize carefully and keep the bullet character so skills don’t disappear.
- OAuth redirects were annoying: Site URL and allowlists on Supabase have to include both localhost and production, or you get bounced back to localhost after login.
- I deliberately skipped LinkedIn/Indeed scraping (ToS and fragility). Real community listings and clear product limits beat a fragile “full JD” demo.
What I learned
- Closing the loop (parse, rank, tailor, PDF) helps students more than another chat box.
- Production LLM apps need fallbacks, batching, and schema validation, not just a clever prompt.
- Being honest about data limits (no full JD yet) builds more trust with judges and users.
- Auth and resume caching sound small until you hit Strict Mode races, remote vs local overwrite bugs, and redirect URL config.
Built with
Next.js, TypeScript, Tailwind, Gemma (OpenRouter), Zod, Supabase Auth, pdf-lib, unpdf, Vercel
Live demo: https://starter-hackathon-project.vercel.app
What's next for ResumeFit
The next big upgrade is letting the AI read a full job description and tailor more specifically to that post. Right now I only have title, company, and location, so Gemma has to infer from those. Next I’d let users paste (or eventually pull) the real JD text so I can:
- Rank and explain fit against actual requirements, not just the title
- Surface keywords that already show up on the resume
- Make tighter, still faithful bullet edits for that posting
- Keep the same rules: no invented metrics, one-page PDF, clear diffs
After that, I want saved tailored versions per job, better filters, and maybe cover letter drafts from the same profile plus JD. Still no auto-apply, and still no fragile scraping.
Built With
- css
- gemma
- next.js
- node.js
- openrouter
- pdf-lib
- react
- supabase
- tailwind
- typescript
- unpdf
- vercel
- zod
Log in or sign up for Devpost to join the conversation.