| 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 |
Job Application Document Generator
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.
Input
Job Description: $ARGUMENTS
Determine input type:
- If
$ARGUMENTS is empty or blank, use AskUserQuestion to ask the user to provide a job description (paste text or provide a file path).
- If
$ARGUMENTS looks like a URL (starts with http:// or https://), use WebFetch to retrieve the job description from the page.
- If
$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).
- Otherwise, treat
$ARGUMENTS as the job description text directly.
Workflow
Phase 0: Configuration and Dependency Check
Step 0.1: Check and Auto-Install Dependencies
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.
Step 0.2: Check for User Configuration
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).
Step 0.3: Guided First-Time Setup (New Users Only)
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:
- Phone number (if selected)
- Email address
- LinkedIn URL (if selected)
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:
- Ask for the file path
- Read the file using the Read tool (for
.txt/.md) or extract_text_from_docx() from import_resume.py (for .docx)
- Parse the extracted text to identify contact info, summary, skills, experience, certifications, and education
- Populate the
qualifications section of config.yaml with the structured data
- Confirm what was imported
Confirm 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.py or editing config.yaml directly."
Phase 1: Gather Source Materials
-
Load candidate info from config.yaml
- Name, phone, email, LinkedIn, calendar link
-
Load qualifications from config.yaml (preferred):
qualifications.summary: Professional summary
qualifications.skills: Skills by category
qualifications.experience: Work history with achievements
qualifications.certifications: Professional certifications
qualifications.education: Degrees and schools
-
If qualifications not in config, find and parse existing resume:
- Search configured locations (or defaults):
~/Documents/Job Application Docs/resume/
~/Documents/resume/
- Current project directory
- Look for
.docx or .md files with "resume" in the name
- For
.docx files, use Python's zipfile to extract text (cross-platform, no external dependencies)
- Note: PDF files are not currently supported for automatic parsing
-
Load portfolio projects from config.yaml:
- Each project includes: name, url, description, technologies, achievements, keywords
- Match project technologies and keywords against job requirements
- Use achievement metrics as evidence for qualifications
-
If portfolio projects not in config, scan ~/Projects/ for evidence:
- Search for README.md files, package.json, and source code
- Look for qualification assessments in
~/Documents/Job Application Docs/
- Identify technologies, frameworks, and quantifiable achievements
-
Parse job description to extract:
- Required skills and experience (must-haves)
- Preferred qualifications (nice-to-haves)
- Technical stack (cloud platforms, tools, languages)
- Soft skills and culture fit indicators
- Industry/domain context
- Years of experience requirements
ATS Keyword Fidelity Rule (applies to Phases 2, 4, 5)
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:
- When writing cover letter and resume content, prefer the JD's literal phrasing over the candidate's usual wording.
- Extract verbatim phrases from the JD for required skills, tools, methodologies, and credentials - these are the canonical list of terms to preserve.
- Never invent metrics or facts the candidate's
config.yaml doesn't contain.
- This rule applies even when the candidate's
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).
Phase 2: Job Fit Assessment
See fit-assessment.md for complete evaluation framework.
Step 1: Categorize Requirements
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"
Step 2: Build T-Chart Evaluation
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):
- Work history from
config.yaml qualifications
- Portfolio project achievements (from config.yaml)
- Certifications
- Education
Scoring:
- ✅ Strong match = 1.0 point
- ⚠️ Partial/transferable = 0.5 points
- ❌ Gap = 0 points
Step 3: Calculate Fit Scores
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)
Step 4: Check for Disqualifiers
Non-negotiable gaps (do not proceed):
- Missing legally required credentials (licenses, certifications required by law)
- Missing the core technical skill the role is built around
- Experience gap > 10 years (they want 15 years, you have 2)
- Security clearance requirements you cannot meet
- Cannot meet non-negotiable location or relocation requirements
- Compensation range is significantly below candidate expectations
See fit-assessment.md for the complete disqualifier checklist.
Step 5: Generate Recommendation
| 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 |
Step 6: Present Assessment to User
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":
- For each gap, show the full T-chart row with requirement text, candidate evidence (or lack thereof), and rating
- Provide specific mitigation strategies from best-practices.md for each gap
- After showing the analysis, re-ask: "Proceed with application, or skip?"
If user chooses "Skip this opportunity":
- Acknowledge the decision: "Understood - skipping this one."
- Do not generate documents or log the application
- Ask: "Would you like to apply to a different job? Paste the next job description."
Phase 3: Determine Style Preset
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)
Phase 4: Match and Prioritize
-
Map candidate experience to job requirements:
- Lead with strongest matches to must-haves
- Identify transferable skills for partial matches
- Prepare gap mitigation language for cover letter
-
Select strongest evidence for each key requirement:
- Quantified achievements (percentages, dollar amounts, team sizes)
- Recent and relevant project examples
- Certifications that directly apply
- Always reword the chosen evidence to use the JD's exact phrasing for any qualifier the JD names (see ATS Keyword Fidelity Rule)
-
Match portfolio projects to requirements:
- For each relevant portfolio project from config.yaml:
- Match technologies to job's technical requirements
- Match keywords to job description language
- Select most impressive achievement metrics
- Prioritize projects with strongest alignment to must-have requirements
- Include portfolio URLs in resume where appropriate
-
Identify keywords from job description for ATS optimization
- Extract verbatim JD phrases for required skills, tools, methodologies, and credentials. Every such term should appear at least once verbatim in either the cover letter or the resume.
-
Select accent color based on preset and industry:
-
Craft gap mitigation statements for cover letter:
- Frame gaps as growth opportunities
- Highlight transferable skills
- Show learning velocity with examples
- Reference portfolio projects that demonstrate rapid skill acquisition
- Use the JD's exact phrasing for any qualifier the JD names
Phase 5: Generate Documents
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.
Word Document Generation
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:
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",
"location": "City, ST",
"dates": "Mon YYYY - Mon YYYY",
"bullets": [
{"text": "Achievement with metric", "highlights": ["metric"]}
]
}
],
"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.
Cover Letter Requirements
See cover-letter-template.md for structure.
Apply styling from selected preset in style-presets.md.
Key principles:
- Single page maximum
- Opening paragraph: Hook with most relevant qualification
- Body: 2-3 paragraphs with specific evidence mapped to requirements
- Address significant gaps proactively with transferable skills or learning examples, woven naturally into the body (not defensively at the end)
- Reference portfolio projects when they demonstrate relevant skills
- Technical alignment section if role is technical
- Closing: Express genuine interest and availability
- No generic phrases ("I am writing to apply...")
- Every claim backed by specific evidence
- ATS Keyword Fidelity Rule applies - use JD-verbatim wording for required skills, tools, and credentials
Resume Requirements
See resume-template.md for structure.
Apply styling from selected preset in style-presets.md.
ATS Optimization:
- Use standard section headers: Summary, Skills, Experience, Education, Certifications
- Include exact keywords from job description - every required skill, tool, methodology, and credential named in the JD should appear at least once verbatim in the resume (Skills section is the natural place for terms that don't fit organically in bullets)
- Reword existing config.yaml bullets to use the JD's exact phrasing where it names a qualifier (e.g., if config says "PRDs" and JD says "product requirements documents", use the JD's phrasing in the tailored output)
- Use consistent date formatting (Mon YYYY - Mon YYYY)
- Keep creative elements in header only; main content follows standard format
- Use bullet points with action verbs
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.
- Never write
< 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).
- For hierarchies, use
→ (Unicode rightward arrow, U+2192) instead of >. Example: Capability → Epic → Story → Task.
- For numeric comparisons, write
over N instead of >N and under N instead of <N. Example: write "over 80% test coverage" not ">80% test coverage".
AI-Tell Characters (em-dash U+2014 and en-dash U+2013):
The em-dash (Unicode codepoint U+2014, looks like a long horizontal bar between spaces) has become a widely-recognized indicator of AI-generated writing. Recruiters and hiring managers increasingly flag em-dash-heavy prose as likely AI-edited, which carries stigma in 2026 and beyond. The same applies to the en-dash (U+2013) when used in numeric ranges: humans typing on a keyboard produce a plain hyphen instead.
- Never write a U+2014 em-dash or U+2013 en-dash in any user-visible content (cover letter prose, resume summary, bullets, skills lines). This applies to fresh content you draft AND to content quoted or reshaped from
config.yaml.
- For a strong break in a sentence, prefer a colon, semicolon, period, or comma. Example:
- GOOD:
"Portfolio Factory: built and shipped an AI-powered career assessment platform..."
- BAD:
"Portfolio Factory — built and shipped an AI-powered career assessment platform..." (uses U+2014)
- For a parenthetical aside, use commas or parentheses. Example:
- GOOD:
"At Ameriprise, a regulated financial-services firm, I owned..."
- BAD:
"At Ameriprise — a regulated financial-services firm — I owned..." (uses U+2014)
- For numeric ranges, use a plain ASCII hyphen. Example:
- GOOD:
"3-4 month delivery cadence" (ASCII hyphen U+002D)
- BAD:
"3–4 month delivery cadence" (uses en-dash U+2013)
- For project-name-then-description bullets, use a colon. Example:
- GOOD:
"Claude Code Skills (open-source): reusable AI agent skill suite..."
- BAD:
"Claude Code Skills (open-source) — reusable AI agent skill suite..." (uses U+2014)
- The literal U+2014 / U+2013 characters in this rule's examples are written as
— / – escapes on purpose, so a mechanical clean-up pass over SKILL.md cannot delete the BAD examples and silently destroy the rule.
Defense-in-depth sanitizer:
generate_word_docs.py contains a sanitizer (_sanitize_data_for_ats) that runs at the top of generate_application_documents() and replaces stray <, >, U+2014, and U+2013 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, and the sanitizer is only a backstop for source-data drift.
Human Scanning Optimization:
- Apply visual hierarchy through typography (bold, sizing)
- Use accent color strategically (headers, dividers, name)
- Quantified achievements prominently displayed
- Most relevant experience emphasized first within each role
- Single line spacing for compact documents; adequate margins (0.7-1 inch)
Portfolio Project Integration:
- Include relevant portfolio projects in experience or separate "Projects" section
- For each included project:
- Project name with URL (if live)
- Brief description aligned to job requirements
- Key achievement metric
- Technologies used (matching job requirements)
Phase 6: Output
Step 6.1: Save Documents
The generate_application_documents() function handles file naming automatically:
{Full_Name}_Cover_Letter_{Role}_{Company}.docx
{Full_Name}_Resume_{Role}_{Company}.docx
Step 6.2: Log Application and Open Folder
Use 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)
Step 6.3: Provide Summary
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}
Step 6.4: Ask About Next Action
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.