Scripts
The scripts behind Seekter's commands: the job application tracker CLI, the job board and company career site sweeps, the live posting check, the posting reader, the answer checker and the resume engine.
Seekter runs these for you. You talk to Claude, and Claude runs the scripts; this page is for when you want to look yourself, check what a run did, or work on the kit. Every script needs Node.js 20 or newer and nothing else, and runs from the Seekter folder. For what you say to Claude, see Commands.
The Tracker
scripts/seekter.mjs is the only thing that writes to applications/.
List records, optionally by status (--status pending) or from a date (--since 2026-10-01):
node scripts/seekter.mjs list
Check whether a posting is already tracked. It exits 0 for new, 1 for already tracked, 2 when the company is on hold; --company adds the company check:
node scripts/seekter.mjs check <url> --company Acme
Add a record:
node scripts/seekter.mjs add --company Acme --role "Designer" --status applied --url <url>
Move a record to another status, with a note:
node scripts/seekter.mjs move <file or url> rejected --note "22 Oct, short reply"
Counts by status, optionally from a date (--since):
node scripts/seekter.mjs stats
Replies and interviews broken down by a field. --min hides small groups, --floor sets how many decided applications a rate needs:
node scripts/seekter.mjs stats --by fit
Rebuild applications/README.md:
node scripts/seekter.mjs index
Tidy values after editing files by hand; --dry-run only shows the changes:
node scripts/seekter.mjs normalize --dry-run
See what's on a default and what's missing in your settings:
node scripts/seekter.mjs settings --check
check-many reads one posting per line on standard input (url | company, or a bare LinkedIn id) and answers each with NEW, DUP, SAMECO or HOLD.
Every command also takes its arguments from a JSON file, such as {"company": "Acme", "role": "Designer", "status": "applied", "url": "…"} in entry.json:
node scripts/seekter.mjs add --json entry.json
Seekter always uses this form for text that came from a posting or an email, because a role named $(…) would otherwise run as a shell command.
Finding Postings
The freehire API sweep, with your queries:
node scripts/freehire_sweep.mjs
The eligibility lines of one candidate from that sweep, here number 3:
node scripts/freehire_sweep.mjs --detail 3
Your employer watchlist:
node scripts/employer_sweep.mjs
This month's Hacker News "Who is hiring?" thread:
node scripts/employer_sweep.mjs --hn
Both sweeps filter by your title rules and drop what's already tracked; what's left still has its location line read before anything is filled.
Job Sites and Companies That Fit You
The lists setup shows, filtered to a country (two-letter code) and a field. Add --relocation if you would move, and --exclude-sectors with the sectors you rule out:
node scripts/catalog.mjs platforms --country PT --discipline design
node scripts/catalog.mjs employers --country PT --discipline design
Add --add to the second one to write the companies to your watchlist. Company Career Sites explains the A, B and C groups.
Is the Posting Still Open?
node scripts/live.mjs <url>
Give it several URLs at once, or one URL per line on standard input.
It asks the posting's own system (Greenhouse, Ashby, Lever, Workable, SmartRecruiters, Recruitee, Gem, Personio, Teamtailor, PeopleForce, Pinpoint, Workday, amazon.jobs) and prints OPEN, CLOSED or UNKNOWN. Only CLOSED skips a posting. LinkedIn is never asked.
Reading a Posting
node scripts/posting.mjs <url>
Reads the posting from its own system, saves the text under runs/<date>/post/, and prints one line for it:
CLOSER, with the sentence that ends it, quoted: the posting is in another language, it asks for a language you don't speak, it says it won't sponsor a visa, it needs a security clearance, or its list of remote countries leaves yours out.CHECK, with the sentences worth reading: where you have to live, which country the role is limited to, time zones.CLEAR, when nothing in it limits who can apply.
Give it several URLs at once, or url | company lines on standard input. LinkedIn is never fetched: save a LinkedIn posting's text to a file and pass --file <file> --url <linkedin url>. The script only reads; whether the role fits you is still decided from the saved text.
Checking What a Form Will Receive
node scripts/claims.mjs answer.txt --posting posting.txt --url <apply url>
Flags a number that's in neither your profile's facts nor the posting, a banned phrase or long dash, and a sentence already sent to another company. Dates, years, links and salary answers aren't checked as claims. --no-repeat skips the last check, for text that isn't sent anywhere, such as interview prep.
Resumes
The resume types and their PDFs:
node scripts/resume.mjs list profile/resume/json/resume.json
Check the master resume against your profile. Add --pages to also print each type, count its pages on A4 and US Letter, and check the file the way a parser reads it:
node scripts/resume.mjs validate profile/resume/json/resume.json
Print every type:
node scripts/resume.mjs pdf profile/resume/json/resume.json --all
Print one type; --drafts prints a review copy that includes text you haven't approved yet:
node scripts/resume.mjs pdf profile/resume/json/resume.json --variant <type>
See Resumes.
Importing a Tracker
See what would be created, then run it again without --dry-run to import:
node scripts/import_csv.mjs export.csv --dry-run
A CSV export from Notion, Airtable, Google Sheets or anything else. See Tracking Replies.
Installing
npx seekter init
Puts the newest release in ./seekter. Add a name (npx seekter init my-search) to choose the folder, or a dot (npx seekter init .) to use the folder you're in. The folder must be new or empty; otherwise nothing is written. See Install and Updating Seekter.
Tests
node --test tests/seekter.test.mjs
Each case builds a throwaway tracker in a temporary folder, so your own is never touched. Run it after changing anything in scripts/.