CV Builder
Opinionated CV generation workflow: source materials → structured profile → job-tailored Markdown → PDF.
Dependencies
- Pandoc — pandoc.org/installing
- WeasyPrint —
pip install weasyprint(all platforms; see weasyprint docs for system dependencies)
IBM Plex Sans font — download from Google Fonts, place .ttf files in styles/fonts/:
IBMPlexSans-Regular.ttf, IBMPlexSans-Medium.ttf, IBMPlexSans-SemiBold.ttf, IBMPlexSans-Bold.ttf, IBMPlexSans-Italic.ttf
Project Structure
Scaffold once in the working directory:
sources/
cv_or_resume.* # existing CV (PDF, DOCX, or text)
linkedin_profile.pdf # LinkedIn export
notes.md # freeform notes — wins on any conflict
job_description.md # target JD, swapped per application
data/
profile.json # canonical profile (generated)
jd_analysis.json # per-application JD analysis (generated)
styles/
cv-onepage.css
cv-twopage.css
fonts/
scripts/
build_onepager.sh # see scripts/ in this skill
build_twopager.sh
Workflow
First: ask the user — 1-pager, 2-pager, or both?
Step 1 — Ingest & Profile
Read all files in sources/. notes.md overrides everything else on conflicts.
Extract data/profile.json:
{
"personal": { "name": "", "title": "", "email": "", "linkedin": "", "location": "" },
"experience": [{
"company": "", "company_context": "", "title": "",
"period": { "start": "MMM YYYY", "end": "MMM YYYY or Present" },
"location": "", "bullets": []
}],
"education": [{ "degree": "", "institution": "", "year": "" }],
"certifications": [{ "name": "", "issuer": "", "year": "" }],
"languages": []
}
Never invent data. Mark unknowns as "MISSING". Ask one clarifying question at a time.
Step 2 — JD Analysis
Read sources/job_description.md. Extract data/jd_analysis.json:
{
"company": "", "role_title": "",
"hard_skill_keywords": [],
"matching_candidate_strengths": [],
"missing_or_weak_matches": [],
"why_this_role_draft": ""
}
why_this_role_draft: ≤400 chars, no pronouns, ties 2–3 strengths to JD keywords.
Step 3 — Generate cv-{COMPANY}.md
Only Why This Role changes per application. Everything else is constant.
Sections in order:
- Name + title + contact
- Value Proposition (5–6 bullets, no pronouns, include any notable awards/certifications)
- Why This Role (≤400 chars, inside
.why-this-rolediv) - Experience (reverse chronological)
- Competency Matrix (EXPERT / PROFICIENT / FAMILIAR)
- Education
- Certifications
- Languages
Experience block format:
### [Title] | [Company] | [Period] | [Location]
*[One-line context: sector, team size, scale — no client-per-bullet attribution]*
- Action verb + what + metric/result
*[Sub-section italic label if grouping needed]:*
- ...
Competency Matrix format:
::: {.competency-matrix}
::: {.matrix-row}
::: {.matrix-label}
EXPERT
:::
::: {.matrix-items}
GCP (...), Kubernetes, Terraform, ...
:::
:::
... repeat for PROFICIENT, FAMILIAR ...
:::
Why This Role box:
::: {.why-this-role}
**WHY THIS ROLE**
[text, ≤400 chars]
:::
Step 4 — Approval Gate ⛔
Stop. Show the Markdown. Wait for explicit approval before building any PDF.
Iterate on content until approved. Do not run the build scripts without approval.
Step 5 — Build PDF
Generate CSS files (see references/css.md) and build scripts (see scripts/), then run:
bash scripts/build_onepager.sh {COMPANY} # for 1-pager
bash scripts/build_twopager.sh {COMPANY} # for 2-pager
Check page count:
# macOS
mdls -name kMDItemNumberOfPages cv-onepager-{COMPANY}-latest.pdf
# Linux / Windows (requires poppler-utils)
pdfinfo cv-onepager-{COMPANY}-latest.pdf | grep Pages
If page count is wrong, adjust CSS spacing — reduce margin-top on h2/h3 and margin-bottom on li first, before touching font size. Hard floor: 8.5pt for 1-pager, 9pt for 2-pager.
Rules
- No personal pronouns (I, my, me, we, our)
- No banned phrases: passionate, leveraged, spearheaded, dynamic, synergy, results-driven, detail-oriented, thought leader, cutting-edge, streamlined, robust, innovative, transformative, proactive
- Why This Role ≤400 characters (count exactly)
- All bullets start with action verbs
- Company context line on every detailed role
- No photo, no date of birth, no nationality
notes.mdalways wins on conflicts
ATS Check (before PDF)
- ≥70% of top-10 JD hard skills in CV
- Why This Role ≤400 chars
- No banned phrases, no personal pronouns
- All bullets start with action verbs
Per-application update
Only regenerate the Why This Role block. Re-run ATS check. Rebuild PDF.
Reference Files
references/css.md— Full CSS for both layouts with tuned values