Your first scan¶
From an empty pipeline to a paste-ready application in one Claude conversation. This assumes you finished Install: config.yaml written, profile.yaml and company_list.yaml filled in, and the MCP server registered.
How to scan the companies you care about¶
- List the companies. In
company_list.yaml, put each company under its ATS (greenhouse,lever,ashby,workable,recruitee,smartrecruiters,inhire) and a phase (phase1,phase2,phase3); a plain list with no phases is scanned in every phase. The slug is the company's identifier in its job-board URL: forhttps://boards.greenhouse.io/acmeit isacme. - Try one company first. Ask Claude "what's open at acme on Greenhouse?". That runs
scan_company, which scans one board without touching your list — a wrong slug shows up here, not in the middle of a full scan. - Scan your list. Ask "scan my companies". That runs
scan_and_evaluate, which fetches every posting from the companies underphase1and scores the new ones. Ask for phaseall(orphase2,phase3) to cover more. - Scan without spending LLM calls (optional). From a shell,
moonlighter-scan --no-evaldiscovers and stores postings unscored, asneeds_review; score one later withverify_job. See Command line.
You: scan my companies
moonlighter: 3 sources scanned — 41 postings, 38 already known, 3 new
✓ NEW — Acme Robotics / Senior Backend Engineer
Score: 8.1/10 (threshold: 6.5)
✓ NEW — Nimbus Health / Staff Engineer
Score: 7.4/10
✗ Vandelay Industries / .NET Architect — 3.2/10, archived (hard filter: .NET)
The conversation is illustrative: Claude relays the tool's output in its own words. The tool itself reports how many jobs were processed, how many passed the threshold (with a table of them), and how many were filtered by title, archived for location, or scored below the threshold.
How to read the scores¶
- Each score is 0–10, from the LLM comparing the posting against your
profile.yaml, including the hard and soft filters undercriteria. - Below the threshold, the job is archived automatically. The threshold is
score_thresholdinconfig.yaml, 6.5 by default. A posting that breaks one of your hard filters (".NET" above) scores low and is archived with the reason. - Some jobs are archived before any LLM call. A title matching
title_blocklistinconfig.yamlis saved asarchivedwith a note, unscored. So is an onsite or hybrid posting outside the city you set ascriteria.home_cityinprofile.yaml; without that key, nothing is archived by location. A remote posting, or one whose location doesn't settle the question, goes on to be scored. When a description says both "remote, work from anywhere" and, further down, "from anywhere in Italy", the posting's own location counts by default; setcriteria.location_precedence: genericto trust the company-wide remote claim instead. A posting that requires fluency in a language your profile does not list underlanguagescounts as a triggered hard filter. Two optional profile keys tune what is sent to the model:criteria.eligible_regions(places you can work from, such as["Brazil", "LATAM"]; a posting naming one is classified as eligible, and without the list no posting is eligible by region) andpreferences.engagement(for examplepermanent full-time; postings of another kind are scored lower, and without the key no kind is discounted). moonlighter assumes no profession: it learns yours from the profile. - An empty description can't be scored. Those jobs wait as
needs_review: ask forlist_jobswith statusneeds_review, open the posting, copy the whole page, and pass it toverify_jobto score it. - Browse what passed with
list_jobs(statusnewby default) and open one withget_jobfor its full details and history.
How to prepare your first application¶
- Ask for the sheet. "Prepare the application for the Acme one" runs
prepare_application. Where the ATS publishes the form's questions (Greenhouse, Recruitee, Workable, Lever), moonlighter reads them from there; InHire tells you up front which fields its form requires. - No API? Paste the page. When the questions aren't published, it asks you to open the application page, select all, copy, and hand the text over — that runs
prepare_application_from_paste. - Review the whole sheet. Every question gets an answer drafted from your profile, or a flag saying why it needs you. This excerpt is in the tool's real format; the model is told to answer UNKNOWN when your profile gives it no basis, and that comes back as a gap:
[5/9] How many years have you run Elixir in production? (required)
!! I DON'T KNOW — no basis in your profile to answer
1 of 9 need you
Answer the flagged ones yourself, and read the drafted ones too — the model can still get an answer wrong.
4. Paste and send it yourself. Copy the answers into the employer's form and submit it there. moonlighter never opens the form and never clicks submit.
5. Record that you sent it. Tell Claude "mark job 42 as submitted" — that runs update_status. With Gmail tracking on, the sheet's email field already carries a tracking alias, and the recruiter's reply finds this application by itself.