Jobautopilot Tailor
Tailors your resume and cover letter to a specific job description. Fetches the JD, rewrites bullet points to match keywords, and exports polished .docx file...
Like a lobster shell, security has layers — review code before you run it.
License
Runtime requirements
SKILL.md
Job Hunt — Resume Tailor
Produces a tailored resume and cover letter for each shortlist job in the tracker. Delivers .docx files ready to attach and send.
Core principles
- 100% truthful — never invent experience, inflate metrics, or fabricate credentials.
- Resume content comes from the user's original files first — always read
$RESUME_DIRbefore writing anything. - md before docx — complete markdown drafts for ALL shortlisted jobs first, then convert to docx in batch. Do not interleave md writing and docx conversion.
- One job at a time for reporting — finish and report each job's result before moving to the next.
- No silent spinning — if a job cannot be reliably completed within 30 minutes, mark it
errorwith a clear reason and move on.
Setup
Add to ~/.openclaw/workspace/job_search/config.sh:
export RESUME_DIR="$HOME/Documents/jobs/" # your original resume files live here
export RESUME_OUTPUT_DIR="$HOME/Documents/jobs/tailored/" # where tailored files are saved
export RESUME_TEMPLATE="$HOME/.openclaw/workspace/job_search/sample_placeholders.docx"
# Download template: https://github.com/jerronl/jobautopilot/raw/main/jobautopilot-tailor/scripts/sample_placeholders.docx
export MD_TO_DOCX_SCRIPT="$HOME/.openclaw/workspace/job_search/md_to_docx.py"
export JOB_SEARCH_TRACKER="$HOME/.openclaw/workspace/job_search/job_application_tracker.md"
export USER_FULL_NAME="Your Name"
export USER_EMAIL="your@email.com"
export USER_PHONE="+1-555-000-0000"
export USER_LINKEDIN="https://linkedin.com/in/yourprofile"
mkdir -p "$RESUME_OUTPUT_DIR"
Session start
Read in order:
$RESUME_DIR— understand the user's full experience and skills$JOB_SEARCH_TRACKER— find allshortlistentries to process
JD fetch order
For each shortlist job:
- Use the exact URL from the tracker
- Try
web_searchfirst to extract job responsibilities, skills, keywords, asset classes - If
web_searchreturns no useful JD, use browser to open the URL directly - If the URL is broken, a generic careers page, or wrong role → mark tracker
errorand explain why
Content production order
For each job, strictly in this sequence:
Step 1 — Read source material
Read all files in $RESUME_DIR. The pool may contain:
| File type | What to extract |
|---|---|
Master resume (.docx / .pdf) | Full work history, bullet points, metrics, dates |
| Older tailored versions | Phrasing that worked well for similar roles |
| Cover letter drafts | Preferred voice, opening formulas, recurring themes |
Skills list / bio (.md / .txt) | Certifications, tools, side projects, publications |
Extract everything factual — every bullet, every metric, every tool name. This is your raw material. Do not invent anything not present in these files.
Resume markdown format specification
The markdown file must follow this exact format. md_to_docx.py parses it structurally — any deviation will produce wrong or missing output.
Full Name
Email | Phone | LinkedIn | Location
SUMMARY
Two to three sentences summarizing the candidate.
CORE SKILLS
List of skills, tools, and technologies relevant to this role.
EXPERIENCE
Job Title — Company Name | City, ST | Jan 2022 – Present
• Accomplished X by doing Y, resulting in Z
• Another bullet point with a metric
Job Title — Company Name | City, ST | Jun 2019 – Dec 2021
• Bullet point
• Bullet point
EARLIER EXPERIENCE
Earlier Role — Company, Year–Year
Another Earlier Role — Company, Year–Year
EDUCATION
University Name — Degree, Major (Year)
Parsing rules the script enforces — follow these exactly:
| Element | Rule |
|---|---|
| Line 1 | Full name, plain text, no # heading marker |
| Line 2 | Contact info, pipe-separated |
| Section headers | ALL CAPS, no ## — exactly SUMMARY, CORE SKILLS, EXPERIENCE, EARLIER EXPERIENCE, EDUCATION |
| Job header | Title — Company | Location | Date range — separator is — (em dash with spaces), fields separated by | |
| Bullets | Start with • or -, one per line |
| Earlier experience | One line per entry: Role — Company, Years |
| Education | One line per entry: University — Degree |
What the script handles automatically:
- More jobs than template slots → clones the last job's formatting
- Fewer jobs than template slots → removes unused placeholders
- Same logic for bullets, earlier experience, and education entries
Step 2 — Write resume markdown
Tailor bullet points to match the JD keywords. Prioritize:
- Skills explicitly mentioned in JD
- Quantified achievements relevant to the role
- Asset classes, systems, or methodologies named in JD
Save to: $RESUME_OUTPUT_DIR/${USER_FIRST_NAME}_<Company>_<Title>_Resume_2026.md
Self-check before converting to docx
Before running md_to_docx.py, verify the markdown against these rules:
# Line 1 must be plain name (no # prefix)
head -1 resume.md
# Line 2 must contain pipes (contact info)
sed -n '2p' resume.md | grep '|'
# Section headers must be ALL CAPS with no ## prefix
grep -E '^[A-Z ]+$' resume.md
# Job headers must match: Title — Company | Location | Date
grep -E '^.+ — .+ \| .+ \| .+$' resume.md
# Bullets must start with • or -
grep -E '^[•\-]' resume.md
If any check fails, fix the markdown before proceeding — a malformed file will silently produce an incomplete docx.
Step 3 — Write cover letter markdown
Three paragraphs max:
- Why this role + company
- Most relevant experience match (2–3 specific points)
- Brief close
Save to: $RESUME_OUTPUT_DIR/${USER_FIRST_NAME}_<Company>_<Title>_Cover_Letter_2026.md
Step 4 — Update tracker
Change status to md_ready. Record md file paths.
Step 5 — Generate docx files
Resume — use md_to_docx.py with the template:
python3 "$MD_TO_DOCX_SCRIPT" \
--input "$RESUME_OUTPUT_DIR/${USER_FIRST_NAME}_<Company>_<Title>_Resume_2026.md" \
--template "$RESUME_TEMPLATE" \
--output "$RESUME_OUTPUT_DIR/${USER_FIRST_NAME}_<Company>_<Title>_Resume_2026.docx"
Cover letter — use python-docx directly (plain text, no template):
from docx import Document
from docx.shared import Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
doc = Document()
style = doc.styles['Normal']
style.font.name = 'Calibri'
style.font.size = Pt(11)
# Add paragraphs from cover letter md content
for para in cover_letter_paragraphs:
p = doc.add_paragraph(para)
doc.save(f"{output_dir}/{os.environ['USER_FIRST_NAME']}_<Company>_<Title>_Cover_Letter_2026.docx")
Step 6 — Verify docx
"Text looks right" is not the same as "file is deliverable." Both conditions must pass:
- Content check — open the docx and compare section by section against the md:
- No missing sections
- No leftover
{{PLACEHOLDER}}strings anywhere in the document - Company name and job title are correct throughout
- File check — the docx must open without errors, have non-zero file size, and be saved to
$RESUME_OUTPUT_DIR - URL check — validate the job URL from the tracker is still reachable
Do not call partial verification "good enough." If any check fails, fix and re-verify before updating the tracker.
Step 7 — Update tracker
- Success →
resume_ready, record docx paths - Cannot reliably complete →
error, write reason
Step 8 — Report
After each job, report: company, title, files produced, any issues.
File naming convention
${USER_FIRST_NAME}_<CompanyName>_<JobTitle>_Resume_2026.docx
${USER_FIRST_NAME}_<CompanyName>_<JobTitle>_Cover_Letter_2026.docx
Spaces → underscores. Keep company and title short (≤ 20 chars each if possible).
Tracker status flow
shortlist → md_ready → resume_ready
↘ error
Known failure modes to avoid
- Do not call partial verification "good enough"
- Do not treat "text looks right" as equivalent to "docx is deliverable"
- Do not spend more than 30 minutes on a single job without reporting status
- Do not write a generic script to handle all cases; get the md layer working first
Scope
Resume tailoring only. Do not submit applications. Hand off resume_ready entries to the jobautopilot-submitter skill.
Files
2 totalComments
Loading comments…
