English · 简体中文
A Chrome side panel that reads the job posting in your current tab, compares it against your PDF CV, and answers one question: is this worth an evening?
Interface and analysis both in English and 中文.
It is not an interview prediction, a legal opinion, an immigration assessment, or an automated application tool.
Add it from the Chrome Web Store — one click, and Chrome keeps it updated.
Or load it unpacked, to run a specific version or your own build
Download the latest marketfit-job-lens-*.zip from
Releases, unzip it, then:
chrome://extensions → turn on Developer mode → Load unpacked → select the
unzipped folder.
The panel opens from the extension icon. Its header shows the loaded version — if that does not match the version you expect, Chrome is still running the old code and needs a reload.
- Upload a PDF CV. It must be a text PDF, not a scan — the text is extracted in your browser, and an image has none to extract.
- Say whether you can work where the job is. One dropdown. Left on Unknown it changes nothing; set truthfully, the employer's stated conditions get checked against it.
- Choose a provider and paste an API key. See the table below. The key lives in the open panel only.
Open a job posting and click Analyze current job with AI.
The first time on any given site, the panel will ask you to allow reading it. That
is Chrome, not a fault: activeTab is granted per invocation, so a site you have never
analysed before has not been allowed yet. Click Allow reading this site once and it
is settled for that site. Nothing has failed and nothing needs retrying.
Then read down:
| What you see | What it is telling you |
|---|---|
| The verdict word | Apply / Worth applying / Fix gaps first / Probably skip |
| The bar under it | Your required-area coverage, drawn: met, partial, missing |
| The cost tag | What applying takes — wording only, an evening, more, or not closable |
| What would change this | The single edit that moves the application furthest |
| Conditions the employer states | Their own sentence, plus the question that settles it — send that sentence as written |
| Requirements | Each tagged required/preferred and met/partial/not met, hard filters first |
| Whether your CV gets read | The same posting read the other way — not can you do it, but does your CV contain the words a recruiter searches. A term missing beside a requirement you meet is the line worth having |
| What this market also weighs | Hiring conventions of the employer's market that the posting never states, and where your CV stands against each. Written by hand and dated, shown only for the markets the table covers — the model places your CV against them, it does not write them |
| Gaps to close | With how to close each, and which cannot be closed before applying |
| What a screener may hesitate over | What the CV raises about your history whatever the job — an unexplained break, several short tenures, a claim with no work behind it. Most cannot be fixed, so each comes with the honest thing to say |
| Do this next | The whole plan, grouped by when and numbered. Everything asked of you appears here once |
| What to ask them / What your CV leaves unanswered | Two different jobs — the second is one only you can do |
Open full report gives the same analysis as a full-width page you can print or keep. Reports last for the browser session.
If a page cannot be read — a login wall, a heavy single-page app — use Edit job text and paste the posting. Pasted text is never overwritten by a re-capture.
Worth understanding, because it is what makes the output checkable rather than merely confident.
1 · The page is read once, and trimmed. One click, activeTab, the tab you are
looking at. Navigation, cookie banners, similar-job rails and application forms are
removed before anything is sent; the panel reports how many lines went and lists
them on hover. A screening condition — sponsorship, a licence, a clearance — is
never removed by any filter rule, because dropping one silently would flip the
verdict.
2 · Both documents are cut into addressable blocks. Your CV becomes CV-001,
CV-002… and the posting JD-001, JD-002… roughly a paragraph each. The model
may only cite blocks that exist; every reference is resolved back to the real text
on arrival, and any that does not resolve is dropped. That is what stops invented
evidence. It is a plausibility bound, not a proof — it guarantees a claim points at
something real, not that the claim follows from it.
3 · The answer has a fixed shape. A JSON schema travels with every request, including to models that cannot enforce it and on the fallback path where enforcement is stripped. A reply that overruns a limit is trimmed rather than refused, and a missing section is treated as empty, because discarding an analysis you already paid for is the worse failure.
4 · Some things are decided in code, not by the model.
| Decided by the model | Decided in code |
|---|---|
| What the role is, and how your CV compares | Whether a stated condition conflicts with what you declared |
| Which requirements screen you out | The coverage count under the verdict |
| What to change, and what to ask | Stripping block IDs out of prose |
| Which list an item belongs in | What counts as page furniture |
| — | Whether the verdict and the effort estimate contradict each other |
| — | Whether so many requirements are called hard filters that the ranking stops meaning anything |
| — | Whether the plan is shorter than the gaps it says it closes |
Anything whose failure would be both invisible and decisive sits on the right. The
model is asked not to write JD-001 into a sentence and mostly complies — mostly is
not enough, so the parser removes them. The last three are the same argument: each is
stated in the prompt, each is followed most of the time, and each fails in a way that
looks like an ordinary result. Where two fields disagree the panel says so rather than
picking one, because code can see the disagreement and not which side is right.
The analysis also says what it could not see. A requirement the page filter removed and one the posting never carried are the same silence to a model, and it will report the second as a finding. So when a lot of the page was filtered, when your CV hit its length limit, or when the text is what you pasted, that limitation is stated against the verdict — not left in a line above it you have already scrolled past.
5 · What it will not do. No visa routes, quotas or processing times: there is no policy data here and no way to verify what a model recalls, and a confident wrong answer about your eligibility is worse than none. No interview odds. No match percentage — collapsing met, partial and missing into one score needs a weight for a partial match, and any weight would be invented. No salary or market figures, because nothing here measures them.
Tailoring stays honest. It reorders and sharpens evidence your CV already contains. It will not add a skill, upgrade "contributed to" into "led", restate a team result as yours, or supply a number you did not.
You bring the key; usage is billed by that provider to your own account.
| Provider | Models | Get a key |
|---|---|---|
| OpenAI | GPT-5 mini, GPT-5 | platform.openai.com → API keys |
| Anthropic | Claude Opus 4.6, Claude Sonnet 4.6 | platform.claude.com/settings/keys |
| DeepSeek | DeepSeek V4 Flash, DeepSeek V4 Pro | platform.deepseek.com → API keys |
All three are verified working end to end.
The step that stops people is not creating the key — it is billing. A new key on an account with no credit fails on the first call, with an error that reads like the extension is broken. Top up first. Keys are shown once; if you lose one, make another.
No backend, no account, no analytics, no telemetry. Nothing is transmitted until you click Analyze.
- The PDF is parsed in your browser; the file is never uploaded.
- Running an analysis sends the CV text and the filtered posting only to the provider you chose — there is no relay in between.
- Chrome asks before reaching a provider's domain, and before reading a site.
- Never stored: the PDF, its text, your API key.
- Stored: interface language, and how long past runs took. Reports live for the browser session and contain no CV text — quoted passages are stripped first.
- Clear local session wipes the in-memory state and every stored report.
Preview optional AI payload in the panel shows exactly what a run would send, with obvious PII redacted. Full detail in PRIVACY.md.
No build step, no dev dependencies, plain ES modules.
npm install && npm run vendor:pdfjs # pdfjs-dist is the only runtime dependency
npm run check # lint + tests + audit — the gateThe audit covers what spans files and no unit test can see: a manifest pointing at a
deleted icon, an id renamed in HTML but not JS, a string added in one language only,
an unescaped innerHTML, a fetched or linked network host that is not one of the
three provider APIs — a convention's citation url is the one exception, permitted
only in src/market/conventions.js and never fetched — a rendered CSS class with no
rule in either stylesheet, an error code with no
translation, visible markup with no data-i18n, a README model table that has
drifted from the registry, a card the panel renders that neither README describes,
and a const in the injected page extractor that would sit in a temporal dead zone.
Most exist because the corresponding bug shipped once. Several failure modes here
are silent by construction — t() renders a missing key as the key name; a filter
that drops a requirement looks identical to a posting that never had one — so the
guardrails are mechanical, and each was verified by reintroducing the bug and
watching the check fail.
docs/analysis-model.md · docs/privacy-data-flow.md · docs/chrome-store-submission.md
MIT — see LICENSE.