Skip to content

About

Browser-side résumé ↔ job-description matcher and ghost-job checker powering SearchSteward's free tools. Your text never leaves the page.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

@searchsteward/match-tools

tests license: MIT

The exact TypeScript that powers SearchSteward's free, no-signup tools:

Zero dependencies, fully deterministic, runs entirely in the browser — your résumé and the job posting never leave the page. This repo is the same code, published so you can verify that claim (and reuse it).

scoreMatch(resumeText, jdText)

Keyword-coverage scoring, the way ATS filters and skimming recruiters actually read a résumé:

  1. Drop the employer's self-description. A posting opens by describing the company ("Acme provides settlement rails to marketplaces") and closes by describing the job; the opening is unmatchable by construction, and scoring it spends the term budget on terms no candidate can earn.
  2. Tokenize both texts; drop stopwords, generic JD filler ("experience", "strong", "responsibilities"…), posting scaffolding ("seeking", "candidate", "comfortable"), and bare numbers.
  3. Aggressively stem so meaning-preserving variants unify (managed ≈ management ≈ manager, led ≈ lead, drove ≈ drive).
  4. Preserve acronyms (SQL, ETL, CPA) and punctuation-bearing tech names (C++, C#, .NET, Node.js, CI/CD) as first-class tokens — stripping the punctuation would make C++ and C# the same word.
  5. Take the top 40 JD terms by frequency — bigram phrases get a 1.2× boost — and compute weighted coverage against the résumé. A phrase counts only where the résumé actually contains it: both halves adjacent, in one clause.
import { scoreMatch } from "./src/resumeMatch";

const r = scoreMatch(resumeText, jdText);
// { score: 82, band: "strong", matched: [...], missing: [...], ok: true, thinJd: false }

Bands: strong ≥ 75, partial ≥ 45, else weak. The calibration suite in tests/ pins real-world fixtures so the score can't silently inflate.

matched and missing are weight-ordered and capped at 12 — the missing list is advice, and advice past a dozen items is a wall rather than a list.

thinJd: true means the job description carried fewer than 35 meaningful tokens — a pasted teaser, not a full posting. The score is still computed, but treat it as low-confidence and ask for the full text. (Coverage of a stub reads as false confidence; flagging it is the honest move.)

checkGhostJob(postingText)

A weighted checklist of documented scam and ghost-job signals: upfront-payment asks, requests for sensitive personal/financial info, chat-app interviews, personal-email contacts, MLM language, missing compensation, vague stubs. Every flag returned carries a human-readable explanation of why it matters.

import { checkGhostJob } from "./src/ghostJob";

const r = checkGhostJob(postingText);
// { score: 40, band: "caution", flags: [{ label, detail, weight }...], positives: [...], ok: true }

It's a heuristic starting point, not a verdict — a careful scam can avoid these patterns and a real posting can trip a flag or two.

Run the tests

npm install
npm test

License

MIT.

About

Browser-side résumé ↔ job-description matcher and ghost-job checker powering SearchSteward's free tools. Your text never leaves the page.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages