TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기AI resume tailoring: Next.js + FastAPI + Gemini. Paste a JD, upload a resume, get an ATS-friendly tailored version.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Paste a job description, upload your resume (PDF or DOCX), and get back a version tailored to that job. The summary is rewritten, the most relevant bullets come first, and the JD's terminology is used, without inventing experience. You also get a keyword-match and ATS score, a diff of every change, gap suggestions, a streamed cover letter, and PDF / DOCX / TXT downloads.
• Live app: https://resume-customizer-gamma-topaz.vercel.app
• API: https://resume-customizer-api-8zoe.onrender.com (/api/health, interactive docs at /docs)README 전문 보기 ↗Paste a job description, upload your resume (PDF or DOCX), and get back a version tailored to that job. The summary is rewritten, the most relevant bullets come first, and the JD's terminology is used, without inventing experience. You also get a keyword-match and ATS score, a diff of every change, gap suggestions, a streamed cover letter, and PDF / DOCX / TXT downloads.
/api/health, interactive docs at /docs)The API runs on Render's free plan and sleeps when idle, so the first request after a while can take ~50 s to wake it.
No resume handy? Click "Try it with a sample JD + resume" at the top of the form.
Reviewing with an AI agent? The backend is also a remote MCP server. See Use it from AI agents.
| Requirement | Implementation |
|---|---|
| JD input + validation | Textarea with live character count. Must be non-empty, ≥ 100 and ≤ 20,000 characters. Enforced on both client and server. |
| Resume upload | Drag & drop or browse. PDF and DOCX. Shows the file name and size. Type, size (5 MB) and empty-file checks run on both client and server, plus magic-byte validation on the server. |
| Parsing | pdfplumber (PDF) and python-docx (DOCX, including tables and headers). The text is then structured into sections by the LLM. Hyperlinks are extracted too (PDF link annotations, DOCX hyperlink relationships), so a "GitHub" or "LinkedIn" label keeps its real URL. |
| LLM customization | Rewrites the summary, reorders and rephrases bullets, highlights matching skills and suggests missing keywords. Uses Gemini structured JSON output. |
| Output + download | Rendered resume with JD keywords highlighted. Download as PDF or DOCX (generated server-side, ATS-friendly single column, embedded Lato font, clickable contact and project links) or TXT. The on-screen preview uses the same typography as the PDF. |
| Resume length | Choose 1 page, 2 pages or Auto. The prompt budgets content for the target. The PDF renderer then fits the layout (slightly tighter type and spacing, never below ~8.5 pt). If it still overflows, one extra LLM pass condenses by JD relevance. The UI shows the resulting page count. |
| Error handling | Friendly messages for bad file type/size, empty input, corrupted or scanned PDFs, LLM rate limits/overload/timeouts, and the backend being unreachable. |
Bonus features implemented
The same backend serves a remote Model Context Protocol server at
https://resume-customizer-api-8zoe.onrender.com/mcp (Streamable HTTP, stateless, no auth). Any MCP client can use the app directly: no install, no API key.
# Claude Code
claude mcp add --transport http resume-customizer https://resume-customizer-api-8zoe.onrender.com/mcp
# Codex CLI
codex mcp add resume-customizer --url https://resume-customizer-api-8zoe.onrender.com/mcp
{"mcpServers": {"resume-customizer": {"url": "https://resume-customizer-api-8zoe.onrender.com/mcp"}}}.Then ask, for example: "Tailor my resume (attached) to this job description, one page, and give me the PDF."
| Tool | What it does |
|---|---|
tailor_resume | Takes the JD plus the resume, as text or as the original PDF/DOCX in base64 (which keeps hyperlinks). Returns the tailored resume as markdown, before/after scores, the change log, honest gap suggestions, and PDF/DOCX download links. |
write_cover_letter | Writes a cover letter grounded in the resume's facts. |
export_resume | Renders an edited structured resume to PDF/DOCX and returns a download link. No LLM call. |
run_sample_demo | Runs an end-to-end check with bundled sample data, for reviewers who don't want to share a resume. |
The tools call the same service layer as the REST API (service.py). Truthfulness guards, page fitting and rate limits are identical to the web app. Tool failures come back as MCP tool errors with readable messages (bad input, quota, overload), not crashes. Download links come from a small in-memory store and expire after an hour.
WebMCP (in-browser agents). The frontend also registers tailor_resume and get_tailored_resume through the experimental WebMCP API (document.modelContext.registerTool). A browser agent can then tailor a resume on the page itself, and the user watches the result appear. This is feature-detected and does nothing in browsers without WebMCP.
| Layer | Tech |
|---|---|
| Frontend | Next.js 15 (App Router), React 19, TypeScript, Tailwind CSS v4, lucide-react, diff |
| Backend | Python 3.12, FastAPI, Pydantic v2, pdfplumber, python-docx, fpdf2 |
| LLM | Google Gemini via the official google-genai SDK |
| Hosting | Vercel (frontend), Render (backend, Docker) |
| Agents | Remote MCP server via the official mcp Python SDK, and WebMCP in the frontend |
| Tests | pytest (backend: parsing, link extraction, export/fit-to-page, scoring, API, MCP protocol, LLM fallback; 58 tests) |
Google Gemini (Google AI Studio API). The primary model is gemini-3.5-flash. It falls back automatically to gemini-3.8-flash, then gemini-2.5-flash, gemini-3.5-flash-lite and gemini-3.1-flash-lite. The whole chain is configurable with environment variables. Free-tier quotas are per model (only 20 requests/day for the 3.x Flash models), so the longer chain also multiplies the app's daily capacity.
Why Flash models with low thinking: tailoring is a structured rewrite task, not deep reasoning. With the default thinking setting, tailoring took 25–55 s. With thinking set to low (3.x) or a small token budget (2.x), it takes about 8–15 s with the same output quality, and parsing takes 3–7 s. The fallback chain exists because free-tier models regularly return 503 overloaded. When that happens, the request moves to the next model instead of failing. Each attempt also has its own timeout, so a hung model costs seconds, not minutes. gemini-2.5-flash is last in the chain because, in testing, it followed the no-invented-outcomes rule less reliably than the 3.x models.
flowchart LR
subgraph FE["Next.js frontend (Vercel)"]
A[JD textarea] --> C
B[Resume upload] -->|multipart| P
C[Tailor options<br/>sections + intensity]
V[Resume view · Diff · Scores<br/>Cover letter · Compare]
end
subgraph BE["FastAPI backend (Render)"]
P["/api/resume/parse<br/>pdfplumber / python-docx"] --> S1[LLM: structure resume → JSON]
T["/api/resume/tailor"] --> S2[LLM: tailor + JD analysis]
S2 --> G[Invariant guard<br/>restore facts the LLM must not touch]
G --> SC[Deterministic scoring<br/>keyword match + ATS checklist]
CL["/api/cover-letter"] -->|stream| S3[LLM: cover letter]
X["/api/export/{pdf|docx}"]
end
LLM[(Gemini<br/>3.5-flash → 3.8-flash → 2.5-flash)]
S1 & S2 & S3 --> LLM
C -->|JSON| T
SC --> V
V -->|stream| CL
V --> X
Request flow
/api/resume/parse. The file is validated (extension, size, magic bytes) and its text extracted. The LLM converts the raw text into a typed Resume (name, contact, summary, skill groups, experience, projects, education, extra sections) at temperature 0, copying content verbatim. The UI shows "Parsed N roles, M skills" as confirmation./api/resume/tailor. The structured resume and JD go to the tailoring prompt, which returns a TailorOutput (tailored resume, JD keywords, matched skills, missing keywords with suggestions, change log). Then:
/api/cover-letter. Streamed as text/plain chunks. The backend reads the first chunk before sending the 200, so auth/quota errors still arrive as proper HTTP errors./api/export/{pdf|docx}. Renders the structured resume into a clean single-column document (no tables or columns, so ATS parsers read it correctly).The backend is stateless. Nothing is written to disk or stored, and resumes exist only for the duration of a request.
backend/
app/
main.py # FastAPI routes, error handlers, rate limit, invariant guard
llm.py # Gemini client, structured output, retries + model fallback, streaming
prompts.py # all prompt templates (parse / tailor / cover letter)
schemas.py # Pydantic models: LLM response schemas + API contracts
parsing.py # PDF / DOCX text extraction and normalisation
scoring.py # keyword match + ATS checklist + resume → text
export.py # DOCX (python-docx) and PDF (fpdf2) renderers
config.py # env-based settings
tests/ # pytest suite (LLM stubbed → runs offline)
Dockerfile
frontend/
src/app/page.tsx # page state & layout
src/components/ # JD input, upload, options, resume view, diff, insights, cover letter, compare
src/lib/api.ts # typed API client + streaming reader
samples/ # sample JD + resume used by "Try it with a sample"
render.yaml # Render blueprint for the backend
All prompts live in backend/app/prompts.py. The key decisions:
missing_keywords with honest suggestions instead of into the resume. The backend's invariant guard enforces the hard facts in code as well.response_schema (generated from Pydantic models) removes JSON-format instructions from the prompt and parsing failures from the code. Fields have no defaults, so the model must emit a complete shape.<job_description> and <resume_json> tags, and the model is told to treat their content as data, never instructions.Prerequisites: Python 3.11+, Node 20+, and a free Gemini API key from Google AI Studio.
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dev.txt
cp .env.example .env # then put your GEMINI_API_KEY in .env
uvicorn app.main:app --reload --port 8000
API docs: http://localhost:8000/docs. Run the tests with pytest. They stub the LLM, so they need no key or network.
cd frontend
npm install
cp .env.example .env.local # NEXT_PUBLIC_API_URL=http://localhost:8000
npm run dev
Open http://localhost:3000.
| Variable | Where | Default | Purpose |
|---|---|---|---|
GEMINI_API_KEY | backend | – | Required. Google AI Studio key |
GEMINI_MODEL | backend | gemini-3.5-flash | Primary model |
GEMINI_FALLBACK_MODELS | backend | gemini-3.8-flash,gemini-2.5-flash,gemini-3.5-flash-lite,gemini-3.1-flash-lite | Tried in order on 429/503/504/timeouts |
ALLOWED_ORIGINS | backend | http://localhost:3000 | CORS allow-list (comma-separated) |
ALLOWED_ORIGIN_REGEX | backend | – | Optional, e.g. for Vercel preview URLs |
MAX_UPLOAD_MB | backend | 5 | Upload size limit |
RATE_LIMIT_PER_HOUR | backend | 60 | Per-IP limit on LLM endpoints |
LLM_TIMEOUT_SECONDS | backend | 120 | Per-request LLM timeout |
PUBLIC_API_URL | backend | from request | Base URL for download links returned by MCP tools |
MCP_ALLOWED_HOSTS | backend | – | Optional Host allow-list for /mcp (turns on DNS-rebinding protection) |
NEXT_PUBLIC_API_URL | frontend | http://localhost:8000 | Backend base URL |
render.yaml, Docker, root backend/). Set GEMINI_API_KEY, and set ALLOWED_ORIGINS to the Vercel URL. Any Docker host works the same way, e.g. Cloud Run or Railway, since the container listens on $PORT.frontend, and add NEXT_PUBLIC_API_URL = the Render URL..doc is rejected with a "save as .docx" hint..js, CI/CD, acronyms in parentheses, etc.). It won't credit synonyms. That is intentional, because it mirrors how many ATS systems match, and the number stays reproducible rather than LLM-estimated.backend/app/fonts), which covers Latin scripts. Resumes in Devanagari or CJK would need an extra font such as Noto. DOCX and TXT exports use system fonts and keep full Unicode.gemini-2.5-flash and the Flash-Lite models, which follow the "no unstated claims" rules a little less strictly. A paid key removes this.