# 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. Use when this capability is needed.

- Skill: `tomevault-io/job-apply` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/job-apply`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/job-apply/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/job-apply

---


# 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):
```bash
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:
```bash
pip3 install python-docx pyyaml --user
```

On macOS (try standard install first, fall back to break-system-packages):
```bash
pip3 install python-docx pyyaml --user 2>&1 || pip3 install python-docx pyyaml --break-system-packages
```

On Windows:
```cmd
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:
```yaml
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

1. **Load candidate info from config.yaml**
   - Name, phone, email, LinkedIn, calendar link

2. **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

3. **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

4. **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

5. **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

6. **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](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):**
1. Work history from `config.yaml` qualifications
2. Portfolio project achievements (from config.yaml)
3. Certifications
4. 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](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](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](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

1. **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

2. **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)

3. **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

4. **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.

5. **Select accent color** based on preset and industry:
   - See color recommendations in [style-presets.md](style-presets.md)

6. **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:

```python
# 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.

#### Cover Letter Requirements

See [cover-letter-template.md](cover-letter-template.md) for structure.
Apply styling from selected preset in [style-presets.md](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](resume-template.md) for structure.
Apply styling from selected preset in [style-presets.md](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):
```python
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.

---
> Source: [prairieaster-ai/claude-code-skills](https://github.com/prairieaster-ai/claude-code-skills) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-06-02 -->

