How JD Skill Gap Analysis Identifies Resume Gaps in Career‑Ops: A Zero‑LLM Deep Dive

Career‑Ops performs JD skill gap analysis using a deterministic, three‑stage regex pipeline that extracts technical tokens from job descriptions, splits your resume into explicit and implicit skill sets, and classifies each requirement as existing, supported‑by‑resume, or a gap—without ever using LLMs or modifying your CV.

JD skill gap analysis helps you identify missing qualifications before submitting applications. The Career‑Ops repository implements this functionality in jd‑skill‑gap.mjs as a read‑only, zero‑LLM checker that parses job descriptions and compares extracted requirements against both the named skills section and prose of your resume. This approach produces three distinct buckets—existing skills, skills supported by resume prose, and actual gaps—enabling honest, evidence‑based tailoring without AI hallucinations.

How the Career‑Ops Skill Gap Analyzer Works

The analysis proceeds through three deterministic stages, each implemented as a pure function with no external API dependencies.

Stage 1: Extracting Technical Skills from Job Descriptions

The process begins with scanJd (lines 56‑73) in jd‑skill‑gap.mjs, which parses the job description line‑by‑line to locate requirement‑style headings. The algorithm uses REQUIREMENT_HEADER_RE to detect synonyms like “Requirements,” “What we’re looking for,” or “Qualifications,” while NON_REQUIREMENT_HEADER_RE closes the block when encountering sections like “Benefits” or “Perks.”

Once inside a requirements block, the scanner processes bullet points with SKILL_TOKEN_RE, a regex that captures uppercase‑leading technical terms (e.g., Kubernetes, C++, Docker) while filtering out generic nouns via the STOPWORDS array (lines 21‑37). The extractJdSkills function (lines 200‑202) returns a deduplicated list of candidate skills and a flag indicating whether a requirements block was ever opened.

Stage 2: Parsing the Resume Skills Section

The analyzer uses splitSkillsSection (lines 81‑113) to bifurcate your cv.md file. It locates the markdown heading “Skills” and treats everything beneath it as named skills (explicitly listed capabilities). All remaining content—work experience, projects, and education—becomes prose (implicit skill mentions). This separation allows the tool to distinguish between skills you claim explicitly versus those buried in narrative text.

Stage 3: Classifying Matches, Partial Matches, and Gaps

The classifySkillGaps function (lines 24‑63) tokenizes both the named skills and prose sections using skill‑extract.mjs (extractSkills + canonicalize), which normalizes aliases like “k8s” to “Kubernetes.” For each JD skill extracted in Stage 1, the algorithm assigns one of three classifications:

  • Existing – The canonical token appears in the named skills section.
  • Supported‑by‑Resume – The token appears only in the prose section (you have experience but didn’t list it explicitly).
  • Gap – The token is absent from the entire CV.

If the canonical extractor fails to recognize a token, the system falls back to skillMentionedInText, a word‑boundary regex that prevents false matches like “Java” inside “JavaScript.”

Key Implementation Details and Edge Case Handling

Diagnosing Extraction Failures

When the JD yields no skills, diagnoseExtraction (lines 31‑55) distinguishes between no requirements section found (the regex never matched a header) and no skill candidates extracted (the section existed but contained no tokens matching SKILL_TOKEN_RE). This distinction allows downstream consumers to decide whether to skip the check or warn the user.

Canonicalization and Aliases

The canonicalize function imported from skill‑extract.mjs handles variant spellings and abbreviations, ensuring that “React.js” and “React” resolve to the same token during comparison. This normalization happens before the three‑bucket classification, preventing surface‑syntax mismatches from appearing as false gaps.

Running JD Skill Gap Analysis from the Command Line

The CLI provides immediate human‑readable reports without modifying source files.


# Analyze a saved JD and get the three‑bucket summary

node jd-skill-gap.mjs jds/acme.md --summary

The --summary flag prints the existing skills, prose‑supported skills, and gaps to stdout. If the JD lacked a recognizable requirements block, the tool emits a warning suggesting manual review. Output from this command feeds into downstream modes like modes/pdf.md, which surface gaps to users before PDF generation begins.

Programmatic Usage in Node.js

Integrate the analyzer into custom workflows by importing the core functions directly.

import { extractJdSkills, classifySkillGaps } from './jd-skill-gap.mjs';
import { readFileSync } from 'fs';

// Load source documents
const jdText = readFileSync('jds/acme.md', 'utf8');
const cvText = readFileSync('cv.md', 'utf8');

// Stage 1: Extract JD requirements
const jdSkills = extractJdSkills(jdText);

// Stage 2 & 3: Compare against resume
const { existing, supportedByResume, gap } = classifySkillGaps(jdSkills, cvText);

console.log('Explicitly listed:', existing);
console.log('Mentioned in experience:', supportedByResume);
console.log('Critical gaps:', gap);

The API returns plain arrays, making it trivial to feed results into JSON APIs, CI pipelines, or custom reporting tools. Because the tool never writes to cv.md, you can run this analysis safely in read‑only environments.

Summary

  • Zero‑LLM Architecture – jd‑skill‑gap.mjs uses only regex patterns (SKILL_TOKEN_RE, REQUIREMENT_HEADER_RE) and deterministic tokenization, ensuring reproducible results without API costs or hallucinations.
  • Three‑Bucket Classification – Every JD skill is categorized as existing (named section), supported‑by‑resume (prose only), or gap (missing entirely).
  • Smart Tokenization – Canonicalization via skill‑extract.mjs handles aliases, while word‑boundary fallbacks prevent substring false positives.
  • Read‑Only Design – The tool strictly reports findings; it never mutates your resume, ensuring manual oversight of any tailoring decisions.
  • CLI and Programmatic APIs – Use node jd-skill-gap.mjs <file> --summary for quick checks, or import extractJdSkills and classifySkillGaps for custom integrations.

Frequently Asked Questions

What makes Career‑Ops JD skill gap analysis “zero‑LLM”?

The entire pipeline relies on deterministic regex matching and static token lists rather than large language model inference. According to the Career‑Ops source code, functions like scanJd and classifySkillGaps use only JavaScript regular expressions (SKILL_TOKEN_RE, word‑boundary checks) and the canonicalize mapper to compare skills. This eliminates API latency, token costs, and the risk of LLM hallucinations inventing skill requirements that do not exist in the job description.

How does the tool prevent false positives when matching skills?

Career‑Ops employs multiple guardrails: the STOPWORDS array filters generic nouns (lines 21‑37), canonicalize normalizes variants like “k8s” to “Kubernetes,” and the skillMentionedInText function uses word‑boundary regex to ensure “Java” does not match inside “JavaScript.” Additionally, the NON_REQUIREMENT_HEADER_RE pattern actively closes parsing blocks when encountering non‑requirement sections like “Benefits,” preventing the extraction of irrelevant tokens.

Can the tool modify my resume to fill the identified gaps?

No. The JD skill gap analysis is strictly read‑only. As implemented in jd‑skill‑gap.mjs, the code never writes to cv.md; it only reports the three classification buckets (existing, supported‑by‑resume, gap) to stdout or consuming modes like modes/pdf.md. This design ensures that only honest, evidence‑based tailoring occurs, requiring you to manually verify and add any missing skills rather than having an AI fabricate experience.

What happens if the job description lacks a clear requirements section?

The diagnoseExtraction function (lines 31‑55) detects whether the JD parser failed to find a requirements header (REQUIREMENT_HEADER_RE never matched) or whether the section existed but contained no skill tokens. In the former case, the tool warns that no requirements block was detected; in the latter, it reports that the section was empty. Downstream consumers can then decide whether to skip the gap check entirely or prompt the user for manual skill extraction.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →