with one click
job-apply
// Assess job fit and generate tailored cover letter and resume for a job application based on job description, existing resume, and portfolio projects. Includes fit scoring, style presets, and gap analysis.
// Assess job fit and generate tailored cover letter and resume for a job application based on job description, existing resume, and portfolio projects. Includes fit scoring, style presets, and gap analysis.
| name | job-apply |
| description | Assess job fit and generate tailored cover letter and resume for a job application based on job description, existing resume, and portfolio projects. Includes fit scoring, style presets, and gap analysis. |
| argument-hint | [job-description-text or path-to-job-file] |
| allowed-tools | Bash,Read,Write,Edit,Glob,Grep,Task,WebFetch,AskUserQuestion |
Evaluate job fit, then generate a tailored cover letter (1 page max) and resume (1-2 pages, prefer 1) optimized for both ATS parsing and human scanning, with visual styling appropriate for the target industry.
Job Description: $ARGUMENTS
Determine input type:
$ARGUMENTS is empty or blank, use AskUserQuestion to ask the user to provide a job description (paste text or provide a file path).$ARGUMENTS looks like a URL (starts with http:// or https://), use WebFetch to retrieve the job description from the page.$ARGUMENTS looks like a file path (ends in .txt, .md, .docx, or contains /), read the file. For .docx files, use Python's zipfile to extract text (see the technique in import_resume.py's extract_text_from_docx() function).$ARGUMENTS as the job description text directly.Run this check (use python or py -3 instead of python3 on Windows):
python3 -c "import docx; import yaml; print('OK')" 2>&1 || echo "MISSING"
If dependencies are missing, offer to install them automatically:
Use AskUserQuestion:
I need to install python-docx and pyyaml to generate Word documents.
Options:
- "Install now" - I'll run the install command for you
- "Show me the command" - I'll show you what to run manually
- "Skip" - Continue without installing (will fail at document generation)
If user chooses "Install now", run the appropriate command:
On Linux:
pip3 install python-docx pyyaml --user
On macOS (try standard install first, fall back to break-system-packages):
pip3 install python-docx pyyaml --user 2>&1 || pip3 install python-docx pyyaml --break-system-packages
On Windows:
pip install python-docx pyyaml
Verify installation succeeded, then continue.
Look for config file in the same directory as this SKILL.md file (typically ~/.claude/skills/job-apply/config.yaml).
If config exists, load it and check that qualifications.experience is not empty. If qualifications are empty, inform the user and offer to run the resume import flow (from Step 0.3's "Ask about existing resume" section) before continuing to Phase 1.
If config does NOT exist, run the guided first-time setup (Step 0.3).
Welcome the user:
"Welcome to the Job Application skill! Let's set up your profile. This takes about 2 minutes and you only need to do it once."
Gather contact information using AskUserQuestion:
Question 1 - Name:
What is your full name (as it should appear on your resume)?
(Free text input)
Question 2 - Contact Method:
How should employers contact you?
Options:
- "Phone and Email" - Provide both
- "Email only" - Just email address
- "Email + LinkedIn" - Email and LinkedIn profile
Question 3 - Based on previous answer, gather:
Question 4 - Style preference:
What industry are you targeting? (This affects content tone and structure. Visual styling currently uses Modern Professional for all presets.)
Options:
- "Tech/Corporate" - Modern Professional style (recommended for most roles)
- "Finance/Law/Healthcare" - Classic conservative style
- "Design/Marketing/Startups" - Creative style with more personality
Create config.yaml with the gathered information:
Use Write tool to create ~/.claude/skills/job-apply/config.yaml with:
candidate:
name: "{gathered_name}"
phone: "{gathered_phone}"
email: "{gathered_email}"
linkedin: "{gathered_linkedin}"
calendar: ""
paths:
resume_locations:
- "~/Documents/Job Application Docs/resume/"
- "~/Documents/resume/"
- "~/Documents/"
portfolio_dir: "~/Projects/"
output_dir: "~/Documents/Job Application Docs/generated/"
preferences:
default_style: "{selected_style}"
qualifications:
summary: ""
skills: []
experience: []
certifications: []
education: []
portfolio_projects: []
Ask about existing resume:
Use AskUserQuestion:
Do you have an existing resume to import?
Options:
- "Yes, import from file" - I'll help you import your work history
- "No, I'll add details later" - Start with basic profile, add experience manually
- "Yes, let me paste it" - Paste your resume text and I'll parse it
If importing from file:
.txt/.md) or extract_text_from_docx() from import_resume.py (for .docx)qualifications section of config.yaml with the structured dataConfirm setup complete:
"Profile created! Your documents will be saved to ~/Documents/Job Application Docs/generated/. You can edit your full work history anytime by running
python3 ~/.claude/skills/job-apply/import_resume.pyor editingconfig.yamldirectly."
Load candidate info from config.yaml
Load qualifications from config.yaml (preferred):
qualifications.summary: Professional summaryqualifications.skills: Skills by categoryqualifications.experience: Work history with achievementsqualifications.certifications: Professional certificationsqualifications.education: Degrees and schoolsIf qualifications not in config, find and parse existing resume:
~/Documents/Job Application Docs/resume/~/Documents/resume/.docx or .md files with "resume" in the name.docx files, use Python's zipfile to extract text (cross-platform, no external dependencies)Load portfolio projects from config.yaml:
If portfolio projects not in config, scan ~/Projects/ for evidence:
~/Documents/Job Application Docs/Parse job description to extract:
ATS systems and human recruiters do early-stage keyword matching that can miss paraphrased terms. When the JD uses a specific phrase for a concept, the JD's exact wording should win in every output document.
Concrete rules:
config.yaml doesn't contain.config.yaml bullets use a different word for the same concept (e.g., resume says "PRDs" but JD says "product requirement documents" — use "product requirement documents" in the tailored output).See fit-assessment.md for complete evaluation framework.
Must-Have Signals: "Required", "Must possess", "Essential", "Minimum qualifications", first 3-5 listed requirements
Nice-to-Have Signals: "Preferred", "Bonus", "Would be nice", "Ideal candidate", "Plus"
For each requirement, evaluate candidate evidence:
| Requirement | Evidence | Rating |
|---|---|---|
| {Requirement 1} | {Specific evidence from resume/portfolio} | ✅ Strong / ⚠️ Partial / ❌ Gap |
| {Requirement 2} | {Evidence or transferable skill} | Rating |
| ... | ... | ... |
Evidence Sources (in priority order):
config.yaml qualificationsScoring:
Must-Have Score = (Must-have points / Must-have count) × 100
Nice-to-Have Score = (Nice-to-have points / Nice-to-have count) × 100
Overall Score = (Must-Have Score × 0.7) + (Nice-to-Have Score × 0.3)
Non-negotiable gaps (do not proceed):
See fit-assessment.md for the complete disqualifier checklist.
| Overall Score | Must-Have Score | Recommendation |
|---|---|---|
| 80%+ | 80%+ | Strong Fit - Proceed with application |
| 60-79% | 70%+ | Good Fit - Proceed, address gaps in cover letter |
| 50-59% | 60%+ | Stretch Fit - Consider if you have referral or compelling narrative |
| Below 50% | Below 60% | Poor Fit - Recommend focusing on better-matched opportunities |
Display fit assessment summary:
## Job Fit Assessment
**Role:** {Job Title}
**Company:** {Company if known}
### Fit Scores
- Must-Have Requirements: {X}% ({Y}/{Z} requirements)
- Nice-to-Have Requirements: {X}% ({Y}/{Z} requirements)
- Overall Fit Score: {X}%
### Recommendation: {Strong Fit / Good Fit / Stretch Fit / Poor Fit}
### Strengths (Direct Matches)
- {Requirement}: {Your evidence}
- ...
### Portfolio Project Matches
- {Project Name}: Demonstrates {skill} with {metric}
- ...
### Gaps to Address
- {Requirement}: {Gap description} → {Mitigation strategy}
- ...
### Disqualifiers Found: {None / List any}
Ask user to confirm:
Based on this assessment, would you like to:
- Proceed with application (generate documents)
- See detailed gap analysis
- Skip this opportunity
If user chooses "See detailed gap analysis":
If user chooses "Skip this opportunity":
See style-presets.md for complete styling specifications.
Note: Document visual styling currently uses Modern Professional for all presets. Style selection affects content strategy (tone, structure, keyword emphasis) but not the generated document's colors, fonts, or layout.
Check config.yaml for default_style preference first.
Auto-detect industry from job description, or ask user to confirm:
| Industry Signals | Recommended Preset |
|---|---|
| Finance, Banking, Law, Government, Healthcare, Insurance | Classic |
| Tech, Corporate, Consulting, Enterprise Software, B2B | Modern Professional |
| Design, Marketing, Advertising, Startups, Agency, Creative | Creative |
If ambiguous, ask user:
Which style best fits this role? (Affects content tone; visual styling uses Modern Professional for all.)
- Classic (conservative industries: finance, law, healthcare)
- Modern Professional (corporate, tech, consulting)
- Creative (design, marketing, startups)
Map candidate experience to job requirements:
Select strongest evidence for each key requirement:
Match portfolio projects to requirements:
Identify keywords from job description for ATS optimization
Select accent color based on preset and industry:
Craft gap mitigation statements for cover letter:
Create both files in configured output directory (default: ~/Documents/Job Application Docs/generated/).
Primary output format: Microsoft Word (.docx) - preferred by recruiters and ATS systems.
Use the Python script at generate_word_docs.py (in the same directory as this SKILL.md) to create styled Word documents. Dependencies were verified in Step 0.1.
Dependencies (python-docx, pyyaml) should already be installed from Phase 0. If not, re-run the Phase 0 dependency check.
How to execute: Write a temporary Python script and run it via Bash. The script must add the skill directory to sys.path before importing:
# Write this to a temp file, then run with: python3 /tmp/gen_docs.py
import sys, os
sys.path.insert(0, os.path.expanduser("~/.claude/skills/job-apply"))
from generate_word_docs import generate_application_documents
generate_application_documents(
candidate={
"name": "Full Name",
"phone": "Phone",
"email": "Email",
"linkedin": "LinkedIn URL",
"calendar": "Calendar link (optional)",
"title": "Target Role Title"
},
job={
"title": "Job Title",
"company": "Company Name",
"location": "Location"
},
cover_letter={
"opening": "Opening paragraph text...",
"sections": [
{
"title": "Section Title",
"paragraphs": [
{"label": "Bold Label", "text": "Content...", "highlights": ["phrase to bold"]},
["Bullet item 1", "Bullet item 2"]
]
}
],
"closing": "Best regards,",
"signature_title": "Title | Specialty"
},
resume={
"summary": "Summary paragraph...",
"skills": [{"category": "Category", "items": "Skill1, Skill2, Skill3"}],
"experience": [
{
"title": "Job Title",
"company": "Employer Name", # REQUIRED — employer ONLY
"location": "City, ST", # optional
"dates": "Mon YYYY - Mon YYYY", # dates ONLY
"bullets": [
{"text": "Achievement with metric", "highlights": ["metric"]}
]
}
],
#
# ┌──────────────────────────────────────────────────────────┐
# │ ATS CRITICAL: experience field format │
# │ │
# │ ✅ RIGHT — company and dates are SEPARATE fields: │
# │ "company": "Ameriprise Financial", │
# │ "dates": "Jun 2021 - Oct 2022" │
# │ │
# │ ❌ WRONG — company baked into dates with pipe: │
# │ "company": "", │
# │ "dates": "Ameriprise Financial | Jun 2021 - Oct…" │
# │ │
# │ ❌ WRONG — company baked into dates with dash: │
# │ "dates": "Ameriprise Financial — Jun 2021 - Oct…" │
# │ │
# │ WHY: ATS "Fill from Resume" needs employer on its own │
# │ bold line. Combined formats break Workday, Taleo, etc. │
# └──────────────────────────────────────────────────────────┘
#
"certifications": [{"name": "Cert Name", "year": "2023", "issuer": "Org"}],
"education": [{"degree": "Degree, Major", "school": "University"}]
},
output_dir=os.path.expanduser("~/Documents/Job Application Docs/generated/")
)
The script also provides log_application(company, role, fit_score, output_dir) and open_output_folder(output_dir) — use these in Phase 6 instead of manual Bash commands.
See cover-letter-template.md for structure. Apply styling from selected preset in style-presets.md.
Key principles:
See resume-template.md for structure. Apply styling from selected preset in style-presets.md.
ATS Optimization:
ATS-Unsafe Characters (Workday + similar):
Workday and several other ATS systems strip or mangle < and > characters during text extraction — a hierarchy like Capability > Epic > Story > Task can come out as Capability Epic Story Task with the markers gone, or worse, the section silently dropped.
< or > in any user-visible bullet text, summary, skills line, or cover letter content. This applies whether you are writing fresh content for an application or reading from config.yaml (the canonical config has already been cleaned).→ (Unicode rightward arrow, U+2192) instead of >. Example: Capability → Epic → Story → Task.over N instead of >N and under N instead of <N. Example: write "over 80% test coverage" not ">80% test coverage".generate_word_docs.py contains a defense-in-depth sanitizer (_sanitize_data_for_ats) that runs at the top of generate_application_documents() and replaces stray </> characters automatically. Do not rely on it as a substitute for writing clean source content — write it correctly the first time so the resume reads naturally to humans.Human Scanning Optimization:
Portfolio Project Integration:
The generate_application_documents() function handles file naming automatically:
{Full_Name}_Cover_Letter_{Role}_{Company}.docx{Full_Name}_Resume_{Role}_{Company}.docxUse the helper functions from the generation script (call in the same temp Python script):
from generate_word_docs import log_application, open_output_folder
log_application(company, role, fit_score, output_dir)
open_output_folder(output_dir)
Display comprehensive summary:
## Application Complete
### Documents Generated
- Cover Letter: {filename}.docx
- Resume: {filename}.docx
- Location: {output_dir} (folder opened)
### Fit Assessment
- Overall Score: {X}%
- Recommendation: {Strong/Good/Stretch Fit}
### Key Matches Highlighted
- {Top 3-5 strongest qualification matches}
### Gaps Addressed in Cover Letter
- {Gap}: {How it was addressed}
### ATS Keywords Preserved Verbatim
- {Comma-separated list of JD-verbatim required skills, tools, and credentials used in the documents}
### Next Steps
1. Review the documents in the opened folder
2. Upload to the job portal
3. Prepare for interview questions about: {gap areas}
### Interview Prep Tips
- Be ready to discuss: {Gap areas they may probe}
- Emphasize: {Your strongest differentiators}
- Portfolio demos: {Projects to show if asked}
Use AskUserQuestion:
What would you like to do next?
Options:
- "Apply to another job" - Provide the next job description and restart from Phase 1
- "Edit these documents" - Tell me what to change and I'll regenerate
- "Done for now" - Exit
For profile management (switching between configurations), see profiles.py in the skill directory.
[HINT] Download the complete skill directory including SKILL.md and all related files