Skip to main content
Glama
andreak3779

career-fit-assistant

by andreak3779

career-fit-assistant

A deterministic, fit-gated document generation engine exposed to AI assistants over MCP — refuses to draft a resume or cover letter when a job description's skill match falls below a Good rating, rather than producing a generic one. Built as three coordinated projects powering a real job-search workflow: Project 2 is the single source of truth for career facts, Projects 1 and 3 consume generated bundles from it.

This is a sanitized public snapshot. It's exported from a private working repo with the same architecture, replacing all personal data with a fictional example persona (project-2-profile-learning-hub/Resume_Snapshot.md — "Sarah Ashford," a made-up person) and starting a fresh git history with no link back to that private repo. Clone it, install, and run python3 -m cli.career_fit_api build — the whole pipeline — fit rating, resume/cover-letter generation, learning-plan PDFs — works end to end against the example data:

git clone <repo-url> career-fit-assistant && cd career-fit-assistant
python3 -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[docx,yaml,schema,pdf]"
python3 -m cli.career_fit_api build

For the MCP server (Claude Desktop integration), see the step-by-step setup in mcp_server/README.md.

Architecture

flowchart TB
    subgraph Clients
        Term["Terminal / Claude Code"]
        Desktop["Claude Desktop / other MCP client"]
    end

    MCP["mcp_server/server.py\n(MCP tools)"]
    CLI["cli/career_fit_api.py"]

    P2["Project 2 — Profile & Learning Hub"]
    P1["Project 1 — Application Engine"]
    P3["Project 3 — Presence & Identity"]

    Term --> CLI
    Desktop --> MCP
    MCP -->|calls cli.career_fit_api.main| CLI

    CLI --> P1
    CLI --> P2
    CLI --> P3

    P2 -->|profile-bundle.json| P1
    P2 -->|presence-bundle.json| P3

Two ways in: a terminal invokes cli/career_fit_api.py directly; an MCP client like Claude Desktop calls mcp_server/server.py, which is a thin adapter that builds the same argv the CLI would and calls cli.career_fit_api.main() — see mcp_server/README.md. Either way, the CLI is the single dispatcher into the three projects below.

Bundle model (v3 sequential): Project 2's profile-hub-bundle-generator skill compiles the four canonical source files into two bundles. Each downstream project reads its bundle directly — no network fetch, no cross-project file imports. Bundle regeneration is gated by 6 consistency checks (cert sync, AI-200 wording sync, gap closure, differentiator coverage, course count regression, badge URL format). The authoritative bundles are now produced as JSON: outputs/profile-bundle.json (PII-allowed) and outputs/presence-bundle.json (GitHub-safe).

Distribution: this public repo is a sanitized snapshot exported from a private working repo (andreak3779/claude-projects). app-engine-bundle.md contains PII and is gitignored, same as in the private repo. presence-bundle.md is committed and serves as the public-safe source of truth. Resume_Snapshot.md here is the fictional example persona, not real career data.

Related MCP server: ResumePilot MCP

Projects

  • project-1-application-engine/ — generates tailored job-application artifacts: gap analyses, resumes, cover letters, interview prep, salary research. Reads outputs/profile-bundle.json. 13 skills, 4 DOCX templates, 5 reference docs.

  • project-2-profile-learning-hub/ — source of truth for career facts, Pluralsight/MS-Learn learning history, and portfolio projects. 8 skills, 2 Python utility scripts (Pluralsight HTML parser, learning-plan PDF template), 8 markdown data files.

  • project-3-presence-identity/ — LinkedIn, GitHub, Jobgether, Indeed/ZipRecruiter, and Pluralsight profile updates. Reads presence-bundle.md directly (GitHub's generator is the exception, reading outputs/presence-bundle.json). 5 skills.

Each project has its own README.md and CLAUDE.md; the CLAUDE.md files document project-local conventions, the sync protocol, and PII rules.

Bundle generation

The single skill that owns all bundle regeneration lives in Project 2: skills/profile-hub-bundle-generator_SKILL.md.

It runs 6 sequential consistency checks before writing anything, and produces two output files in their canonical project locations:

Output

Path

PII

Git

app-engine-bundle.md

project-1-application-engine/

yes (email, phone)

ignored

presence-bundle.md

project-3-presence-identity/

no

committed

outputs/profile-bundle.json

repo root

yes

ignored

outputs/presence-bundle.json

repo root

no

ignored

Run it whenever any of these events happen:

  • Certification status changes (AZ-900, AI-200, …)

  • Gap status changes in profile-facts.md

  • Resume section edited

  • Portfolio project added or updated in github-repos.md

  • A Pluralsight course closes a known gap

  • (Course count update only, no gap change → no regen needed)

See the skill file for the full sync protocol and consistency-check rules.

Fresh OS install

Start here after a clean OS install/reimage, before touching any individual project. There are two independent toolchains — Python (repo root + Project 2) and Node.js (Project 1) — but Node.js is only needed for one specific workflow, not for the CLI or MCP server. See the diagram below, then follow the numbered steps for your use case.

flowchart TB
    subgraph Always["Always required"]
        PY["Python 3.11+"]
        VENV["repo-root .venv\npip install -e '.[docx,parser,yaml,schema,pdf]'"]
        PY --> VENV
    end

    VENV --> CLI["CLI usable\ncli/career_fit_api.py\n(build, fit-check, generate-resume, ...)"]
    VENV -->|"+ mcp extra"| MCP["MCP server usable\nmcp_server/server.py\n(Claude Desktop / container)"]

    subgraph SkillOnly["Only for the live-chat SKILL.md workflow"]
        NODE["Node.js >=10"]
        NPM["cd project-1-application-engine\nnpm install"]
        NODE --> NPM
    end
    NPM --> SKILL["Claude runs a SKILL.md directly in chat\ntemplates/*.js DOCX generation"]

    CLI -. "generates DOCX in pure Python\n(shared/docx_layout.py) — no Node.js involved" .-> SKILL
  1. Python 3.11+ — check with python3 --version. On Debian/Ubuntu the venv module ships separately: sudo apt install python3-venv if step 2 fails.

  2. Repo-root Python setup — see the quick-start block at the top of this file. Installs the monorepo CLI and its dependencies (docx, yaml, schema, pdf extras) into .venv/.

  3. MCP server (optional, only if you use Claude Desktop) — see mcp_server/README.md. Adds the mcp extra on top of step 2's venv.

  4. Project 2 Python deps — see project-2-profile-learning-hub/README.md. Only needed if you'll run parse_pluralsight_html.py or pdf-template.py directly; both packages' dependencies already overlap with the root parser/pdf/docx extras.

  5. Node.js (Project 1 only, and only for the live-chat SKILL.md workflow) — skip this step entirely if you'll only use the CLI or MCP server: generate-resume/generate-cover-letter/etc. generate DOCX files in pure Python (shared/docx_layout.py + python-docx, already installed by step 2's docx extra) and never shell out to Node.js. Node.js is only used when Claude runs a skill's SKILL.md instructions directly in a chat session (not through the CLI/MCP), which shells out to templates/*.js for DOCX generation instead. If you need that workflow: check with node --version (any Node ≥10), install via your distro's package manager or nodesource if missing, then cd project-1-application-engine && npm install — see project-1-application-engine/README.md.

  6. Pre-commit hook (optional but recommended) — bash scripts/install-hooks.sh from the repo root. See Local pre-commit hook below.

Project 3 needs no local install — it only reads a committed markdown bundle.

CLI

A monorepo CLI lives in cli/career_fit_api.py. From the repo root:

python3 -m cli.career_fit_api build          # regenerate JSON bundles
python3 -m cli.career_fit_api validate       # run 6 canonical consistency checks
python3 -m cli.career_fit_api fit-check <jd.md>        # inline fit table for a JD
python3 -m cli.career_fit_api gap-analysis <jd.md>     # markdown gap-analysis report
python3 -m cli.career_fit_api generate-linkedin        # LinkedIn profile copy
python3 -m cli.career_fit_api generate-github          # GitHub profile README copy
python3 -m cli.career_fit_api generate-jobgether       # Jobgether profile copy
python3 -m cli.career_fit_api generate-job-board       # Indeed & ZipRecruiter profile copy
python3 -m cli.career_fit_api generate-pluralsight     # Pluralsight profile copy

Use python3 -m cli.career_fit_api <command> --help for each command's options.

MCP server

mcp_server/ exposes the same CLI commands as MCP tools for Claude Desktop or any other MCP-capable client — see mcp_server/README.md for install and setup.

Fit engine

shared/fit_engine.py is the deterministic rating engine used by fit-check and gap-analysis. It classifies each JD skill as production (match), portfolio (portfolio), coursework (course), or no evidence (gap) and derives an overall Strong / Good / Stretch / Pass rating. It handles multi-token phrases, years-of-experience prefixes, and a small alias map for common co-occurring terms (e.g., C# ↔ .NET, REST API ↔ ASP.NET Core Web API).

Ratings are driven by a weighted score where production evidence is worth more than portfolio or coursework: match=1.0, portfolio=0.7, course=0.4, gap=0.0. A required-skill score of 85%+ with 50%+ nice-to-have coverage yields Strong; 60%+ without gaps yields Good; any gaps but 30%+ weighted score yields Stretch; otherwise Pass.

The engine handles:

  • Alias expansion — common co-occurring terms (e.g., C# ↔ .NET, REST API ↔ ASP.NET Core Web API) via shared/aliases.json.

  • Compound splitting — slashes, semicolons, "and"/"or" in JD phrases are split so partial matches register.

  • Years-prefix normalization — "5+ years of C#" becomes "c#" before matching.

  • Genuine-gap fallback — skills explicitly listed as known genuine gaps can still earn portfolio or course status if the gap record says so.

Alias data lives in shared/aliases.json. The registry is loaded at import time, and validate confirms it is well-formed. To add a new synonym pair, edit that JSON file and re-run validate; no Python code changes are required.

Tests

Run the full suite with:

python3 -m pytest tests/ -q

The suite covers bundle loading, the fit engine, JD parsing, and CLI smoke tests for fit-check, gap-analysis, and all five profile-update generators (generate-linkedin, generate-github, generate-jobgether, generate-job-board, generate-pluralsight).

CI

.github/workflows/ci.yml runs:

  1. DOCX template syntax check (project-1)

  2. PII guard on app-engine-bundle.md

  3. Pluralsight JSON validation

  4. JSON bundle build (Project 2)

  5. 6 canonical bundle-validation checks plus alias-registry shape check

  6. pytest suite

  7. CLI end-to-end smoke tests (build, validate, fit-check, gap-analysis, generate-linkedin, generate-github, generate-jobgether, generate-job-board, generate-pluralsight)

This workflow is workflow_dispatch-only (no push/pull_request trigger) — deliberately, to avoid billed GitHub Actions runner time on every commit. That means none of the above runs automatically. The local git pre-commit hook below is the actual enforcement for the two checks that matter most day to day (bundle consistency, skill hardcoding) — run the full CI workflow by hand before anything you especially want double-checked.

Local pre-commit hook (zero-cost substitute for CI-on-push)

One-time setup:

bash scripts/install-hooks.sh

This installs scripts/hooks/pre-commit into .git/hooks/pre-commit (git hooks aren't tracked by git themselves, so this copy step is required once, and again any time scripts/hooks/pre-commit changes). It then runs automatically on every git commit and blocks the commit if ruff check, check_skill_hardcoding.py, check_bundle_freshness.py, or validate.py (when a bundle exists) fail. It intentionally skips the full pytest suite and mypy — those are quick enough to run manually before pushing but slower than what belongs on every commit. Bypass with git commit --no-verify if you're deliberately committing through a known failure.

Data flow scripts

Project 2 has two personal-utility Python scripts:

  • parse_pluralsight_html.py — parses a Pluralsight HTML export, merges into data/pluralsight_learning_history.json (authoritative), and writes a regenerated output JSON. Run after every Pluralsight sync. No automated tests — manually spot-checked against pluralsight-courses.md after changes. This snapshot doesn't include data/pluralsight_learning_history.json itself (personal learning-activity data, not needed by the build pipeline or test suite) — the script and its fixture-based tests are still here for reference.

  • pdf-template.py — Data-driven ReportLab engine for role-specific learning-plan PDFs. Reads a role config from roles/<slug>.json (see roles/_example.json for the schema) and writes the PDF to the path declared in output. Run with python pdf-template.py roles/<slug>.json.

Notes

  • This is a public snapshot exported from a private working repo — real personal data was replaced before publishing: Resume_Snapshot.md here is a fictional example persona (see the note at the top of this file), and app-engine-bundle.md (real PII when generated) stays gitignored exactly as in the private repo.

  • outputs/ directories in each project are working dirs (regenerated artifacts) and are gitignored at the repo root.

  • venv: .venv/ is gitignored at both the repo root and project-2 level (belt-and-suspenders).

Changelog

v0.2.0

  • Strict alias validation in shared/fit_engine.py — _load_aliases() now raises ValueError on malformed alias entries (non-list, non-string items, missing keys) instead of iterating single-character strings silently and silently scoring everything as a match. Malformed aliases.json will now fail loudly at fit-check time.

  • Expanded JD parser section-end regex (shared/jd_parser.py) — adds "What we offer", "Benefits", "Perks", "Compensation", "Why join/work", "About us/the company", "Our culture", and "Company overview" as section-reset headings so the requirements parser stops picking up boilerplate as required skills.

  • pre-commit already includes check_bundle_freshness.py + outputs/profile-bundle.json gate (this was a no-op migration — public was already ahead of private on this front).

  • New tests/test_cli_common.py (5 tests) + _cli_common.py extended with load_bundle_or_none(), print_wrote(), run_and_report() helpers from the private repo.

  • New shared/copy_fragments.py and shared/cert_status.py — extraction of cert-status and copy-fragment computation from render_md_bundles.py into shared helpers, so future generators (LinkedIn / Jobgether / Job Board / Pluralsight profile drafts) can call them directly from a loaded bundle without re-parsing the rendered markdown. render_md_bundles.py is wired to the new modules; tests/test_render_md_bundles.py updated to test the public-API surface.

v0.1.0

Initial public release.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables Claude Desktop to manage a job search end-to-end: find and score job listings, tailor resumes, generate application messages, and track application history, while leaving final external actions to the user.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables parsing a master resume, analyzing job descriptions for ATS/match scores, generating truthful tailored resumes, tracking applications, and producing PDF reports through Claude Desktop.
    17
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables LLM clients to analyse job postings, tailor resumes from an evidence-labelled profile, validate every claim against that profile, and track applications, all through deterministic MCP tools.
    7
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to tailor a resume to any job description by extracting keywords, analyzing gaps and ATS compliance, managing versions, and compiling PDF/LaTeX outputs entirely locally without external API keys.
    MIT