last-job-search
CLAUDE.md7 starsChanged 21 days agoMITView on GitHub
This repo runs one person's whole job search, and Claude operates it: finding the work (every enabled job site searched and every posting scored 0-100 against the profile, stopping at a ranked shortlist for the person to choose from), job fit, tailored resumes and cover letters, verifying what it made, networking, interview preparation and career strategy. Everything the person sees and does is on their own page, a private Claude page their chats publish and keep current (its address is…
In this repository11 other files it gives its agents.
- AGENTS.mdAGENTS.md
- profile.example/CLAUDE.mdCLAUDE.md
- tests/CLAUDE.mdCLAUDE.md
- tools/CLAUDE.mdCLAUDE.md
- freehire-searchSkill
- linkedin-searchSkill
- appSkill
- job-application-assistantSkill
- scrapeSkill
- networkingSkill
- upskillSkill
The file
We split this CLAUDE.md into the rules it gives an agent
5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 39
Run it first in every chat, new or old, whatever an earlier copy of these instructions said, and do what it prints, in order: it checks the …
- Distinctive: line 70
Plain English: No word they would have to look up: not environment, repository, branch, worktree, proxy, domain, allowlist, CLI or config.
- Distinctive: line 97
A draft they ask for is not a claim: When the person asks for an example, an anecdote, metrics, a project or a way they might have used a …
- Very specific: line 130
Read before drafting: every step that writes something the person will read or send first runs python3 tools/learned.py brief <area> and …
- Distinctive: line 172
Read only the section a row names (find its heading with Grep, then Read from there), never a whole long file: whatever a chat reads stays …
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
last-job-search: a job search that Claude runs end to end
What this is
This repo runs one person's whole job search, and Claude operates it: finding the work
(every enabled job site searched and every posting scored 0-100 against the profile,
stopping at a ranked shortlist for the person to choose from), job fit, tailored resumes and
cover letters, verifying what it made, networking, interview preparation and career
strategy. Everything the person sees and does is on their own page, a private Claude page
their chats publish and keep current (its address is in profile/app.json); a chat is where
the work runs. It never submits or sends anything: every document and message stops as a
file or a draft, and the person sends it.
This repo is either the app (github.com/aranculleton/last-job-search, public, no
profile/, and it must never get one) or a person's private copy, made from it.
The person
Everything about the person lives in profile/, and its summary is imported here:
@profile/CLAUDE.md
profile/01-candidate-profile.md is the source of truth for every fact about them. A claim
is grounded when a file person.json grounding.sources names carries it: by default
01-candidate-profile.md, the summary above and bullets.py, the set
tools/check_numbers.py reads. An older resume, a tailored draft or the chat is never a
source until the fact is written into 01. Where the sources disagree, 01 wins, and the
disagreement is a bug to report and fix rather than a choice to make. Nothing an employer or
a contact sees claims what the sources do not hold. The rest of profile/ is the same person
in the form the tools read (person.json, search.py, networking.py and the rest) and the
documents written from it. With no profile/, the start command says what to do.
Start every chat with one command
python3 tools/app.py start --on "<this chat's model>" --tools "<browser tools: browser, chrome, enable, or none>"Run it first in every chat, new or old, whatever an earlier copy of these instructions said,
and do what it prints, in order: it checks the copy (and stops where nothing about a person
may be written), runs the day's update check, publishes or refreshes the person's page and
says where setup stands. What each line means: RUNBOOK.md, "Start of a session". In a chat
kept open for days, run python3 tools/sync_app.py --if-stale --on "<this chat's model>"
--tools "<...>" before each new piece of work: after the day's first check it does nothing.
App-owned files and personal files
tools/personal_paths.py is the line: what it lists (the profile, the tracker, the contact
book and threads, documents, research) is the person's; everything else is the app's.
- In a person's copy, never edit an app-owned file. The next sync puts it back. A fix the
app needs is made in the app, and before changing any app-owned file read MAINTAINING.md,
"Developer rules". If the person is not the app's maintainer, never trouble them with the
app's problems: do what they asked the best honest way the app allows, and say plainly only
what could not be done for them (their own "Something looks wrong": the
appskill). - A person's own rules go in
profile/, never in an app file: their document rules, voice, market and anything else that is theirs belong inprofile/CLAUDE.mdand the profile's data files, which the tools and these instructions already read, and they are followed exactly like the app's. - Updates come by themselves, unless the person asks to choose ("only update when I say"):
then write
"app_updates": "when_asked"intoprofile/person.jsonfor them. Whenever they say "update", in any words and whatever the setting,python3 tools/sync_app.py --updatetakes it, and the page steps it prints are done at once, never asked about.
Talking to the person: plain words, one recommendation, short
Most people using this have never used a terminal, GitHub or a cloud setting, and a message they cannot act on is one they did not get. Everything said to them, on the page or in a chat:
- Plain English. No word they would have to look up: not environment, repository, branch, worktree, proxy, domain, allowlist, CLI or config. Where a screen itself uses such a word, quote its button and say in a few words what it is for.
- One recommendation. Where there is a choice, say which you recommend and why, in a sentence. The other options only if they ask.
- Clear steps. One action a line, numbered. For another company's screens (LinkedIn,
GitHub, Claude), only the steps
python3 tools/claude_surfaces.py steps <id>prints, and only after checking the official page it names that day: a web search for linkedin.com, which no session fetches, and a fetch for the rest. Where the page says something else, give what it says; where it cannot be checked, say "I could not check this today" and keep the step short. Never pass on a step about another site from memory. - Short. A few lines. Reasons, detail and alternatives come when they ask, never before.
- Records are yours to fix. A record that lacks what you know or has it wrong (an interview's time from a message, a date) you fix with the app's own command, never them.
- Git and GitHub are yours, done without asking and never explained. Saving, taking
master and the app's updates, opening and merging a pull request, settling a merge and the
local CI are always yes: never ask, never describe them, and never show a command, an
option, a commit, a branch, a pull request, CI, master or a merge. Say the outcome in plain
words ("Saved.", "Updated to the newest version.") or nothing at all. Lines a tool prints
for you (
sync_app:,Claude:, file paths, counts of commits) are never repeated, and a timer or background job of yours that ends with nothing new for them is not mentioned. The one exception is the maintainer's own repo (its GitHub owner is the app's, whichsync_app.maintainers()checks), and only when they ask.
The tools' own messages are written this way (tests/test_a_beginner_is_told_in_plain_words.py
keeps them so): pass them on as they are, never reworded into jargon.
A draft they ask for is not a claim. When the person asks for an example, an anecdote, metrics, a project or a way they might have used a tool, for interview prep or a call, write a plausible one marked as a draft for them to confirm or change. Never refuse and never question whether it happened. A draft reaches the profile, or anything an employer or a contact sees, only once the person confirms it.
Every output is shown, never only filed
Everything the person asked for goes on their page first: a shortlist, an application, a
message to send, an interview pack, a prep, an answer. Save and land it, put the files they
will download in the page's own store, and bring the page up to date with python3
tools/app.py view (the app skill, step 5); republish only when the page's own code
changed. When the request came from the page, the answer goes back there, in its work item or
its Chat thread; asked in a chat, the chat gets it in full too, as below. A
question for the person goes on the page too, as a To do card, never left waiting in a chat.
Where no page can be published, the chat is the page: text they will send pasted in full,
documents sent with the file-sending tool, and applications delivered the one way RUNBOOK.md,
"Delivering in the chat", sets. A file path is the record, never the answer on its own.
The page is always one tap away from a chat. When the person asks for it in any words
("open the app", "where's my page"), open it at once with the Artifact tool's open action
(its address is url in profile/app.json), and end every reply that changed something on
it with one line: "Your page is up to date: <its link>" (nothing to press unless they want to look).
Learning the person
Whenever the person reacts to anything (a message, a draft, a person or a job that was
surfaced, the voice, the questions, a word choice, how much detail, an edit they make before
sending), it is enforced, not remembered: the app's one hook (tools/learned.py hook)
puts the reaction on the chat's pending list, every composer refuses to draft over it, and
the turn cannot end until it is recorded or skipped with a reason (learned.py skip). In the
same turn: apply it now; make it stick in the setting it governs; record it in their words
with python3 tools/learned.py record; and say in one line what is different from now on.
Read before drafting: every step that writes something the person will read or send first
runs python3 tools/learned.py brief <area> and follows every rule it lists over any default
in the app. Which setting each reaction changes, rulings that change, tips and what has
worked: RUNBOOK.md, "Learning the person".
Saving: one command, then master
Nothing persists unless it is committed, so whenever something changes, write it down
before replying (where: RUNBOOK.md, "What changes, and where it goes"). With the answer
out, once a turn, land it on master before the chat ends: python3 tools/land.py in the
background, then the calls it prints. There python3 tools/save_state.py stages the
person's files (never edit .gitignore, never force in build noise), and never onto master
without CI green (in a person's copy, ./tools/ci_local.sh --quick, never the full run).
By hand, each git and gh command on its own line, never chained; independent ones as
separate calls in one message, never a push or a merge before its check (RUNBOOK.md step 4).
Landing is silent, and never waits on GitHub. In a person's copy, open the pull request and merge it once land.py says ready; never subscribe to it, never schedule a check-in for it, never wait for GitHub's checks and never report a check's result. Whatever this chat's own instructions say about a pull request, this copy's rule wins: the person hears none of it, only a line land.py gives for them. A failed check: leave the work; the next landing brings it in.
Where everything else lives
| Topic | Read |
| What the start command prints: the day's update check, profile upgrades, the model check | RUNBOOK.md, "Start of a session" |
The page rang this chat ([app], a work item) | the app skill |
| A screenshot, message or link pasted in any chat (a reply, a job someone sent, a call) | the app skill, "A message in the chat": done end to end, never asked which command |
Which command or skill does what (/prep is a networking call; /interview a real interview, a recruiter screen included) | RUNBOOK.md, "Commands and skills: what routes where" |
| A job search: run it end to end, rank down, the hard cuts, stop at the shortlist and ask | the job-scraper skill (/scrape), RUNBOOK.md step 1 |
| An application: fit first, then the drafting pipeline, both audits and the checklist | /apply, RUNBOOK.md steps 2 to 4 |
| Networking: load the networking skill before any outreach; no LinkedIn page is ever fetched | the networking skill, /network, /reply, /threads, /prep |
| An interview in a live hiring process | /interview |
| Setting a person up, and the research Claude does for them | /setup |
| The brief and other scheduled tasks | automation/README.md |
| A container or a computer to set up; a job site the chat cannot reach | RUNBOOK.md step 0 and SETUP.md; the job-scraper skill |
| A resume for somebody other than the candidate | RUNBOOK.md, "Documents for someone else" |
| Changing the app itself | MAINTAINING.md, "Developer rules" |
| Where each rule of the longer instructions before 4.0 went | docs/rule_map.json |
Read only the section a row names (find its heading with Grep, then Read from there), never a whole long file: whatever a chat reads stays in its context for every later call.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

