Skip to content
Conduit

mcp server

jaja

jaja: a job-search agency for Claude Code. Agents find roles and people, tailor pages from your evidence and never send without your yes.

Kahl-d/jajacommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 29, 2026

Scripts count, gate and check; models judge and draft; you decide and sign. Your profile, contacts, jobs and writing style live in your own data folder and never touch this repository.

Quick start

git clone <repo-url> ~/jaja && cd ~/jaja
./install.sh --demo --yes          # synthetic demo data + a health check; nothing real, nothing sent
claude plugin marketplace add ./ && claude plugin install jaja@jaja --scope user

Then cd ~/jaja-demo && claude and try /jaja:daily-job-plan, /jaja:score-fit or /jaja:research-company. The installer ends with scripts/doctor.py; on a clean machine it reports 0 failures. For your own search run ./install.sh --data ~/jaja-data (a blank skeleton) and follow getting started. Requirements: macOS or Linux, Python 3.11+, Claude Code; uv is recommended for the MCP server. Optional extras (Gmail and Calendar, resume rendering, PDF checks): pip install -r requirements-optional.txt.

How it works

The daily flow. Every box that touches the outside world stops at an approval you sign.

flowchart LR
  A[Find<br/>boards, posts, people] --> B[Judge<br/>gates with evidence]
  B --> C[Write<br/>from your evidence]
  C --> D[Approve<br/>you sign]
  D --> E[Send<br/>approved files only]
  E --> F[Track<br/>one event log]
  F --> G[Learn<br/>outcomes]
  G -.-> A

The architecture. The plugin is this repository; everything personal stays in your data folder.

flowchart TB
  you([you: decide, approve, send]) --> cc[Claude Code session in your data folder]
  subgraph plugin [jaja plugin: this repo]
    agents[agents/<br/>30 roles]
    skills[skills/<br/>52 procedures]
    hooks[hooks/<br/>9 guards]
    mcp[mcp/<br/>records server, never sends]
  end
  cc --> plugin
  plugin --> scripts[scripts/<br/>gates, scoring, rendering,<br/>tracking, approvals]
  scripts <--> data[(your data folder<br/>profile, jobs, prospects,<br/>applications, outbox, hub, config)]
  scripts <--> rt[(runtime folder<br/>approval queue, run receipts)]

The team

30 role agents in seven teams, each defined by a card (charter, tools, autonomy tier, model, budget, KPIs). Open a team:

AgentDoes
job-scoutdiscovers postings from boards, feeds and alerts
platform-scoutreads platform terms and picks the allowed channel
inbox-harvesterturns recruiter and referral mail into contacts
company-researcherbuilds cited company dossiers
AgentDoes
fit-scorergates and scores jobs against your evidence
role-analystdecodes a posting into ranked needs
legal-visatracks work-authorization clocks and risk
prospect-triageranks contacts by warmth and fit
AgentDoes
resume-tailorbuilds a tailored resume from the base
resume-editoreditorial gate for truth, fit and format
base-comparerblind A/B of a variant against the base
hiring-manager-personareads the page as the hiring manager
technical-ai-reviewerchecks technical claims for precision
technical-recruiter-reviewerscreens the page as the company's recruiter
writerdrafts documents and posts, never sends
profile-editorkeeps the master profile aligned and sourced
linkedin-editorkeeps LinkedIn true to the profile
apply-assistbuilds and lints application packs
AgentDoes
prospectorfinds the warmest human path into a role
messengerdrafts every reply and follow-up
negotiatoroffers: bands, comparison, scripts
senderexecutes approved sends, one file at a time
AgentDoes
tracker-opskeeps one true event log per application
mail-triageclassifies mail and syncs the records
corpus-keeperowns your profile records and evidence ids
AgentDoes
prep-coachinterview packs, mocks and debriefs
AgentDoes
ops-watchdogroutine health and silence alerts
reviewerclean-context gate before anything is done
researchercited, read-only research
engineercode work through branches and pull requests

Around them: 52 skills (/jaja:<name>), 8 lanes (apply, focused, hunt, posts, prep, recruiter, research, resume), 16 routines (all disabled), 9 guard hooks and a 24-tool MCP server. Full lists: reference.

Profile, agents and memory

How it works
Your profileprofile/facts.yaml and profile/evidence.yaml hold what you can prove, one evidence id per claim. Every resume line, answer and letter must cite an id; checks refuse a claim with no source and strings you listed as "holds". One frozen, hash-pinned base resume; a tailored variant ships only if it beats the base in a blind comparison.
Agent profilesA role card (agents/<name>.md) states purpose, allowed tools, autonomy tier (T0 to T4), model, effort, budget, KPIs, stop conditions and denied commands. scripts/kernel/sync.py turns the cards into Claude Code agents (and optional Codex adapters) and --check catches drift.
MemoryYour data folder is the single source of truth: records, the hub's rulings, events and registry, append-only logs and run receipts, and the learning loop that turns outcomes into proposals. None of it lives in this repo.
flowchart LR
  work[agents and scripts do the work] --> rec[(records and receipts<br/>append-only)]
  rec --> learn[learning loop<br/>outcomes with sample sizes]
  learn --> prop[proposals for you]
  prop -->|you rule| hub[(hub: rulings, events, registry)]
  hub -->|injected into lanes| work
  prof[(profile: facts and evidence ids)] --> work

Design nuances

What makes it different, one line each. Each is enforced by a script or hook; docs/NUANCES.md names it.

Safety

  • Nothing goes out without your yes: tiers T0 to T4, an approval file per outward action, denied-command hooks.
  • Connectors are optional and read-only first; sending uses a separate token and a separate login.
  • Pacing caps, gaps and a block-page stop protect your accounts; a block stops every lane.
  • Routines ship disabled; each writes a receipt and a watchdog flags silence.

Truth

  • Every claim on a page cites an evidence id from your own records; no source, no claim.
  • Gates fail with the exact line they quoted, so you can overrule them.
  • One frozen base resume; a variant ships only if it beats the base in a blind comparison.
  • Missing is not empty: a check that could not run says so instead of passing.

Structure

  • Scripts count and check, models judge and draft.
  • One writer per file; lanes, an orchestrator and a hub of events and rulings coordinate sessions.
  • Adapters are generated from the cards and a hook blocks editing them; one source of truth per thing.
  • History is append-only: receipts, events and journals are never rewritten.
  • Each role declares its tools, tier, model and budget on its card.
  • A leak guard fails any commit that would carry your records into the repository.

Engineering at a glance

Tests5,192 tests pass on the demo data with no real data and no network; 321 test files plus 70 MCP tests
Scope142 scripts, about 76,000 lines of Python (standard library, PyYAML, requests)
Architecturecode, your data and a runtime folder kept apart: architecture, design decisions
Config seamidentity, style and policy come from config with neutral defaults; tests run as a synthetic persona
Healthpython3 scripts/doctor.py checks install, config, hooks, adapters, MCP server and leak guard

Who it is for

For engineers and other technical job seekers who already use Claude Code and want a structured, auditable pipeline for a long search: many roles, many people, many drafts, one true record. Not for you if you want a bot that applies for you without review (the design refuses that), or if you do not want to keep your own evidence and records in files.

jajaPrompting Claude by handAuto-apply botsA tracker app
Approval before anything goes outevery outward actionyou remember tousually nonen/a
Claims checked against your evidenceyesnonono
Auditable records you ownplain fileschat historyvendor accountvendor account
Setup effortan afternoonnoneminutesminutes

Details and honest trade-offs: comparison, FAQ.

Documentation

Getting started · Your first search · Demo walkthrough · Configuration · Lanes · Customizing agents and skills · Architecture · Concepts · Design nuances · Design decisions · Reference · Charter · Connectors · Roadmap · Changelog

Status

Early, and used by its author for a real search. It works end to end, but expect rough edges: the layout and file formats can still change, only Claude Code is tested (the Codex adapters are generated but not run end to end), and connectors (Gmail, Calendar, X, a logged-in browser) are optional and off until you set them up. Respect each platform's terms; you are responsible for what you approve and send. This project is not affiliated with Anthropic or any job platform.

Everything on one page

The product page ends above. This is the developer depth: every agent, gate, hook, deterministic script, skill, lane, routine, MCP tool, configuration key and nuance in the system, one collapsible section each. The tables are generated from the repository (python3 tools/gen-readme.py), so every name matches a file; the guides in docs/ explain how to use them.

Each card in agents/ states purpose, allowed tools, tier_max (T0 read to T4 forbidden), model, effort, budget, KPIs, stop conditions and denied commands.

AgentWhat it doesMax tierModel
apply-assistBuilds and lints one apply pack per application (every answer copied from a record, with its source), keeps the portal account registry ...T1sonnet
base-comparerThe regression gate, in a clean context: judges every changed bullet of a variant blind against the base line it replaces, for this JD, and ...T0opus
company-researcherBuilds and refreshes the cited company dossier (companies/.md): product, team, stack, news, hiring signals, H-1B/LCA and E-Verify ...T0sonnet
corpus-keeperOwns the profile's records (profile/corpus/**, evidence.yaml, facts.yaml, profile/_inbox/): turns the owner's new work and testimony into ...T1opus
engineerCode work inside builds through branches and PRs, with CI and deploy checksT2opus
fit-scorerSettles review gates, then scores each surviving job with an A to H report and a weighted 1.0 to 5.0 score tied to evidence ids ...T1sonnet
hiring-manager-personaReads a finished page as the hiring manager the Role Package describes, in one pass: would I call this person, which line made me want to ...T0opus
inbox-harvesterHarvests every recruiter, founder, hiring-manager and referral contact from Gmail (backlog since the configured start date) and the ...T1sonnet
job-scoutRuns the discovery pipeline (ATS boards, HN, Adzuna, Himalayas, alert mail), records postings the owner reads, judges what scripts cannot ...T1sonnet
legal-visaTracks STEM OPT and EAD clocks and the unemployment-day ledger, checks each employer for E-Verify, I-983 and sponsorship risk, keeps the ...T1sonnet
linkedin-editorKeeps LinkedIn true to the approved master profile via state/linkedin/LINKEDIN.md: drafts sections with artifact proof, proof posts and ...T3opus
mail-triageKeeps the job search in sync with Gmail on each pass: incremental harvest, L0-L2 classification by script, a model review of L3 and ...T2haiku
messengerDrafts every reply, follow-up, scheduling note, thank-you and approved negotiation message to prospects (email, LinkedIn, X) in the owner's ...T1opus
negotiatorFrom the first offer on: researches comp bands, builds the offer matrix with visa terms (E-Verify, I-983, H-1B, wage level, start-date ...T0opus
ops-watchdogRoutine health, silent-failure alerts, backups, secret rotation and budget-breach alerts (their one writer)T1sonnet
platform-scoutOwns targets/platforms.yaml and research/job-platforms-2026.md: reads each platform's terms, picks the allowed channel (API, alert mail ...T1sonnet
prep-coachPrepares the owner for every interview round from the corpus: prep packs (coding, system design, domain fundamentals, behavioral), STAR ...T1opus
profile-editorOwns the finalized master profile (profile/PROFILE.md): every experience, project, skill and credential in final wording, aligned end to ...T1opus
prospect-triageScores every new or changed prospect for warmth, fit, sponsorship (a weighed signalT1sonnet
prospectorFinds the warmest human path into each target role (alumni, former colleagues, people who engaged, hiring managers) from the LinkedIn ...T1sonnet
researcherResearch with cited reportsT1sonnet
resume-editorThe editorial gate, run in a clean context: checks every resume, cover letter and why-us note for truth against the profile, alignment to ...T0opus
resume-tailorResearches each role and tailors one variant per job from the one general base resume, aligning the page truthfully to the title and the ...T1opus
reviewerClean-context checks against a rubricT1opus
role-analystUnderstands a role before anyone writes: reads the title, JD and company, picks the role lens, decodes the JD into a ranked coverage ...T1sonnet
senderExecutes approved outward actions only, one approval file at a time, fully auditedT3haiku
technical-ai-reviewerA senior AI engineer's read of the page: is every technical claim precise, current and defensible in a two-minute interview follow-upT0opus
technical-recruiter-reviewerThe senior recruiter of THIS company hiring for THIS role, built from a researched brief: screens the page and letters against the ...T0opus
tracker-opsKeeps every application's event log true through scripts/tracker.py, the one status of the search: records applications and stage events ...T1sonnet
writerDrafts emails, posts, documents and resumes as the single writer per artifactT1opus

Nothing reaches you until these have run, in a clean context where it matters.

GateKindWhat it checks
resume-editoragentthe editorial gate: truth against the profile, alignment, style, ATS health; signs a verdict in review.md
base-compareragentblind A/B of every changed line against the base; reverts a line that does not win
hiring-manager-personaagentreads the page as the hiring manager the role package describes
technical-ai-revieweragentchecks every technical claim for precision and defensibility
technical-recruiter-revieweragentscreens the page as the company's recruiter; APPROVE or REVISE
revieweragentclean-context check against a rubric; nothing is done without its verdict
ai-screeneragent (planned)an AI-screen reader; the card is not shipped yet, so skills/page-review records the step as NOT RUN
scripts/fit_gates.pyscripthard gates on a job record and its JD, each failure quoting its evidence line
scripts/rubric_check.pyscriptthe deterministic rubric over a variant
scripts/letters_check.py, answers_check.py, send_preflight.pyscriptstyle, length and pre-send checks from your style.* config
scripts/check_evidence.py, check_holds.py, numbers_ledger.pyscriptevidence ids, held strings and figures on every page
scripts/ats_check.py, page_layout_check.pyscriptwhat an ATS parses; layout of the rendered page
scripts/screener_check.py, review_sign.pyscriptscreener and review files are well formed and signed
EventMatcherHookEnforces
SessionStartanylane_context.pytells a session at start whether it is in a multi-session programme and what its role is
SessionStartanylane_register.pyregisters the session itself in the hub registry (address, branch, role)
SessionStartanylane_rules.pyinjects the rulings digest for this lane type
Stopanylane_handover.pywrites the hand-off section (changed files and sha256) into the lane's own file when a turn changes files in scope
Stopanybrowser_lease.pyeach lane drives its own Chrome window, so keystrokes never land in another lane's tab
PreToolUseWrite|Edit|MultiEditguard_generated_files.pyrefuses edits to files marked GENERATED by
PreToolUseWrite|Edit|MultiEditlane_write_guard.pyone writer per file: refuses a write to another lane's file
PreToolUseBashrole_deny.pyrefuses shell commands the role card denies
PreToolUsemcp__claude-in-chrome__.*browser_lease.pyeach lane drives its own Chrome window, so keystrokes never land in another lane's tab
SubagentStartanyrole_rules.pyinjects the rulings that bind a subagent role

Shared helpers: lane_common.py, rulings_common.py. The list is hooks/hooks.json; sync.py merges it into .claude/settings.json and .codex/hooks.json.

Scripts count, gate, render, track and check; they never call a model. Cards and skills call them.

Find (discovery, capture and platform terms)

ScriptWhat it does
fetch_ats.pyFetch postings from public ATS job boards and normalize them to job records (design §4, §5.1)
adzuna.pyAdzuna job search -> job records (design §4; source: adzuna). Free key, ~250 calls/day
himalayas.pyHimalayas remote-jobs API -> job records (source: himalayas; platform targets/platforms.yaml id himalayas)
hn_hiring.pyHacker News "Ask HN: Who is hiring?" -> candidate job records (source: hn)
aidevjobs.pyAI Dev Jobs (aidevboard.com) public jobs API -> job records (source aidevjobs; platform targets/platforms.yaml id
remote_feeds.pyRemotive and RemoteOK public APIs -> job records (sources remotive, remoteok; platforms.yaml ids remotive ...
yc_boards.pyYC startups -> their public Ashby, Greenhouse or Lever board -> targets/companies.yaml
alert_boards.pyemployers named in job-alert cards -> their public Greenhouse, Ashby or Lever board -> proposals
job_alerts.pyJob-alert emails from the classified Gmail harvest -> job records (research/job-platforms-2026.md)
feed_watch.pythe dream companies' engineering-blog and careers feeds, read by fixed rules
news_watch.pyThe page watch: listing pages that have no board API, and news pages that signal hiring
funding_signals.pySEC EDGAR as a hiring signal: new Form D raises by target companies, and AI/ML keyword hits
pipeline.pyThe daily deterministic discovery harness (routine js-scout)
discover_companies.pynew companies found deterministically
scout_board.pythe day's handed-over roles as one ranked table, generated from the job records, so the day's
scout_coverage.pyhow much of the known job surface the scouts checked on a day, and what they found
scout_review.pyClose job-scout's review by script: re-gate ghost keeps that lack their proof, then count passed_gates
platform_watch.pythe monthly sweep for new job-search MCP servers
platform_yield.pyPer-platform yield and the platform registry check (targets/platforms.yaml; owner platform-scout)
jd_capture.pycapture the JD of every open job at or above the hunt cut, only through channels whose terms allow
web_snapshot.pyFirecrawl (firecrawl.dev) behind our own guards: public pages to dated evidence snapshots
ats_detect.pyrecognise the board vendor of each target the scouts cannot read yet
x_api.pyX (Twitter) for the job search through the official X API. Reads are budgeted; every write waits for the owner
jobs_manual.pyRecord a posting read by hand in the browser (skill browser-job-search) as a job record

Gate and score (hard gates with evidence, fit and ranking)

ScriptWhat it does
fit_gates.pyDeterministic hard gates on a job record + its JD text (design §2 fit-scorer, targets/roles.md §3-4, §8)
hunt_score.pyThe hunt triage score: a deterministic 0 to 100 on every open job, so the best finds surface within hours
hunt_review.pyThe daily fresh-roles review: every new find since the last review, ranked, with its next action
hunt_lessons.pythe loop, read only: what the Scout learned today and whether SETUP answered it
employer_regate.pya dry run of the real-employer gate over the applications we already have
h1b_lookup.pyEmployer sponsorship enrichment from cached USCIS H-1B and DOL LCA data (design §4, research §8)
dedup.pyDedup keys, cross-source duplicates, repost detection, the seen ledger and ghost scores (design §4)
fresh10.pythe day's ten freshest roles worth a page, for the focused lane
fresh10_people.pyone named person per Fresh 10 role, every day
experience_months.pythe one months-of-experience count every fit report cites (charter section 13 [5])
jd_matrix.pythe coverage matrix skeleton: every JD line, typed and ranked, with candidate evidence ids
role_taxonomy.pythe market's role categories, measured from the hunt's own job records
profile_route.pya job title -> its profile base -> the resume PDF a mass application sends
profile_titles.pythe render-safe title of a profile/facts.yaml entry
focus_profiles.pyone profile per role focus, picked by title, each with its best foot forward
segment_companies.pytargets/companies.yaml segment (startup, growth, big-tech, ai-lab, enterprise) from
company_slugs.pykeep every stored company_slug on the one canonical slug (company_index.py)
company_index.pyone company slug per company, so every join finds the dossier, the people and the roles
companies_set.pythe one setter for the watchlist fields scripts discover (targets/companies.yaml)
reader_model.pycheck a Role Package's Reader model (section 6) before anyone writes the page
stale_check.pyis a role's package still about the posting we will apply to? Checked before anyone writes
stale_signatures.pyevery page whose sign-off no longer matches the page, with a re-sign or retire call

Evidence, pages and checks (claims, rendering, lints and gates on every page)

ScriptWhat it does
render_resume.pyRender a resume YAML to an ATS-safe one-page PDF (and a DOCX for Workday), with a strict
ats_check.pyATS check for a resume PDF: what an applicant tracking system will read (design section 4)
page_layout_check.pythe rendered page, measured. Every other gate reads words; this one reads the page
check_evidence.pyvalidate evidence references across profile/
check_holds.pyheld text (profile/PROFILE-HOLDS.md) in any file, with the Markdown blind spots closed
check_dossier.pythe company-researcher's citation audit of a dossier, as a script
check_records.pyevery department record parses; otherwise the file, line and error are named
numbers_ledger.pythe numbers of a governed employer render only from the ledger's measured, released rows
rubric_check.pythe deterministic half of the resume rubric (R3 section B: B3 B4 B5 B7 B9 B12 B13 B14 B18)
letters_check.pythe form of a cover letter, a why-us answer and an anything-else note
screener_check.pyvalidate the AI screener's resumes/variants//screener.md
base_drift.pywas this variant cut from the base that ships today? (trust test 2026-10-01, F71)
blind_pairs.pymake the base comparison blind, then reveal it
review_sign.pysign an editor's review.md without hand-editing its front matter
build_linkedin_md.pyBuild state/linkedin/LINKEDIN.md from the approved texts (state/linkedin/approved/*.txt) and the skills map
skills_map.pyThe approved skills list: name -> (evidence ids, role families), read from the config
answers_check.pythe gate on the form-answer bank, profile/answers/.md
apply_pack.pyevery answer an application form asks, copied from a record, one pack per application
apply_batch.pythe batch path for the mass tier: many applications at once, one sheet
stage_batch.pyStage a LinkedIn approval batch from approved texts, and write the apply list from the same drafts
mass_page.pythe mass-tier page for one role, end to end, with no model call (resume-for --tier mass, step 4m)
mass_prep.pymake scored mass-tier JOBS ready for the mass apply batch: an application folder and a
corpus_audit.pythe corpus-keeper's periodic audit of profile/ (read only)
evidence_slice.pythe part of the owner's record one role needs, so writers and judges stop reading all of it
motif_check.pyrepeated phrasing on a page (editorial-review step 3): n-grams and hyphenated compounds used 3 or
credential_check.pyone detector for credentials in free text
cite_sample.pycitation support sampling for Role Packages (the orchestrator design section 7)

People and outreach (contacts, drafts, approvals' inputs and mail)

ScriptWhat it does
prospects.pyThe job-search CRM: one markdown file per person in prospects/ (schema schemas/prospect.yaml)
outbox.pyOutbound drafts and their approval files. Nothing here sends anything
send_preflight.pySend preflight and read-back: nothing goes out until the text and the transport pass
follow_ups.pythe one daily follow-up list the recruiter lane runs
network_plan.pythe day's network plan
network_map.pythe whole network, classified. Read-only on records
people_traces.pythe public traces a role-analyst reads for a Reader model's "the people" bullet
people_watch.pyThe people watch: the people who post about hiring, watched on X and read on LinkedIn
gmail_pull.pyGmail harvest (read-only) and Gmail draft creation from approved approvals
gmail_send.pyApproval-gated Gmail sends and drafts with local attachments. Nothing sends without the owner's approved file
gmail_auth.pyGoogle OAuth for the job-search scripts: installed-app flow, split tokens, stored outside the repo
mail_classify.pyClassify harvested mail with the L0-L3 ladder (cheapest rule first, a model only when unsure)
mail_events.pyMail-event rows for tracker-ops and mail-triage's Handoffs checklist. Scripts enforce, the model judges
mail_intake.pyMail intake: the hourly collector that carries Gmail into the tracker (pull, classify and match mail into the
calendar_events.pyYour Google Calendar for the job search: read it, invite guests, change and cancel what this system created
calendar_hold.pyTentative Google Calendar holds for interviews. Never invites anyone
linkedin_export.pyParse LinkedIn's official "Download your data" export into normalized people and touches
graph_links.pythe knowledge-graph edges on job-search records, so a notes vault (Obsidian) can draw them as a
build_graph.pythe tracking graph: companies, roles, people and connections, joined from the records
pending_triage.pyA dry-run proposal for the postings parked in jobs-pending/. Writes nothing in jobs/
reclassify_safe.pyre-run the mail classifier over stored harvest files without losing resolve answers
posts.pyHiring posts on LinkedIn and X, one pipeline: the latest posts in; structured records, people and
posts_sweep_check.pyThe posts lane's morning sweep file, checked
everify_note.pyRecord the owner's manual E-Verify Employer Search lookup in legal/checks/.md, in one command
interviews.pythe one number the agency is aimed at
prep_focus.pythe first prep pack for any screen, in one command

Track, report and learn (the one record, the day, the numbers and the learning loop)

ScriptWhat it does
tracker.pyApplication tracker: append-only event log, stage projection, ghost flags, metrics
pursuits.pythe pursuit record: one direction into one company, and the only writer of pursuits/*/pursuit.md
pursuit_hub.pypursuits//HUB.md: one page per pursuit, a deterministic join over every record it names
role_hub.pyThe per-role record hub: one generated applications//HUB.md per application, tracking a role's company,
dashboard.pyRender the job-search dashboard: DASHBOARD.md (for the owner and HOME) + state/dashboard.json (machines)
day_queue.pythe day's ranked work queue the lanes claim from (agency charter §4: lanes are personas; each reads
next_up.pyNEXT-UP, the one ranked list of every role worth the owner's next hour, generated. It replaces a hand-kept table ...
evening_wrap.pythe evening wrap: one file a day,
recruiter_sweep.pythe morning sweep LISTING: everything new since the last sweep, from files
agency_status.pythe job-search agency's health in at most 15 lines, for the daily brief. Read only
channel_report.pythe weekly channel report (agency charter 0.1 item 7, section 13): which way in gets screens
finding_metrics.pyhow complete and how early is our role finding
strategy_review.pythe Lead Recruiter's daily strategy numbers: what worked, and rule flags. Reads the records;
lessons.pywhat the agency learned, read in code: the Recruiter's ledger
learn.pythe shared learning record: every role and lane writes what it learned
cards.pythe morning flashcards (agency charter §3 steps 3-5): the sweep's items ranked, each with why it matters,
bundle.pypursuits//BUNDLE.md: the one page the owner reviews before approving anything on a pursuit
clock.pyThe job-search clock (a fixed number of days from a start date): clock.yaml is the single source of truth; this ...
portal_digest.pythe morning brief's one line per portal. Read only
portal_report.pyone line per portal, and every job seen on more than one portal
portal_registry.pytargets/platforms.yaml is the one source per portal
portal_sitting.pythe prep for one portal sitting with the owner present. Writes
sitting_order.pythe ONBOARDING sittings ranked by expected interviews per hour of the owner's time
check_portals.pythe agency's portal setup is done only when this exits 0
presence_check.pyevery public surface says the current truth, and nothing that arrives there goes unwatched

Pacing, safety and policy (caps, block-page stops, run guards, rulings and model policy)

ScriptWhat it does
pace.pythe hard daily counter for outward acts on LinkedIn and X (human pace)
browser_pace.pythe guard before every browser read of a careers site
browser_research.pythe guardrails a script can hold for reading people in the owner's browser
run_guard.pya run that hangs ends itself, with its stack on disk and a true failed receipt (SETUP-FINAL A1, A2)
rulings.pyrecord the owner's rulings once, verbatim, with an id nobody else holds
routine_drift.pydoes each installed scheduled task still run what its routine spec says (SETUP-FINAL A7)?
model_policy.pyevery agent runs on the model and effort its kind of work needs
card_audit.pya read-only audit of the department's specialists: each role, lane and skill card should be
gate_versions.pythe version (sha256, first 12) of every page gate script, one line, for NOTES ## Gates
effort.pythe one-line effort level (E1 to E5) of a role, for the Recruiter's cards and queue builders (W4)
charter.pyprint only the sections of AGENCY-CHARTER.md a lane needs, plus the goal and the safety lines
connectors_doctor.pyConnector health for the job-search department: one record per connector, never a secret
doctor.pyis this install healthy? One line per check, then a summary. Read-only; no network
agency_paths.pyWhere the agency's code and your data live. Stdlib only; imported first by js_common and the entry scripts
agency_config.pyThe owner's policy, style and identity values. The code ships neutral defaults; your values live in your data ...
js_common.pyShared helpers for the job-search department scripts
js_comms_util.pyShared helpers for the job-search communications/CRM scripts

Other scripts

ScriptWhat it does
rollcall.pythe the orchestrator roll call from files, and GO as an event, instead of check-in messages

Kernel (scripts/kernel/: adapters, lint, approvals, hub tools, receipts)

ScriptWhat it does
approvals.pythe one implementation of the approval rules (schemas/approval.yaml, templates/approval.md)
context_budget.pymeasure the context every session is forced to load before it does any work
evals.pyAgent eval harness for the job agency
frontmatter.pyYAML frontmatter for the agency kernel
hub_events.pythe hub's event feed: one JSON line per ruling, park, go or order change, and SIGNAL.md made from it
lane_state.pythe small live state of a lane, extracted from its append-only hub lane file
lint.pyvalidate the agency's files. Prints findings, returns their count
nightly_sweep.pythe nightly sweep as one deterministic command (an optional routine; none ships enabled)
redact.pyRedact secret-shaped strings in text files in place
roots.pyThe three roots the kernel scripts work against, as one small value
rulings_digest.pythe rulings digest a lane or role sees at start, and the words of any ruling on demand
sweep.pyrebuild the status briefing (HOME.md / HOME.json) and the registries from files
sync.pyregenerate the engine adapters from the canonical files of this repo
token_ledger.pymeasured token use per session, subagent type and model, read from Claude Code transcripts
watchdog.pycatch routines that went silent
SkillUse it when
add-experienceUse in the owner's resume session, whenever the owner describes new work (a job, role, project, award, talk, or a date or title change), or for the ...
apply-assistUse when an application row is ready (the base, or a variant that won): build and lint its apply pack, fill the employer-site form from the pack in ...
apply-sprintUse when the owner approves a big push (for example 20 applications in one sprint) that is too large for one session and must run as a lane programme ...
ats-checkUse after every resume PDF render, before the editorial verdict is signed, when a vendor parse (Workday, Greenhouse, Lever, Ashby, iCIMS, Taleo ...
base-compareUse after a variant renders and before anyone reads it: judges every changed bullet and order blind against the current base for this JD (shuffled ...
browser-job-searchUse only when the owner is present and wants to search job boards in the owner's browser (LinkedIn, Wellfound, HiringCafe, WTTJ, YC, Glassdoor ...
build-resume-baseUse only when the owner orders a change to the one general base resume (resumes/bases/general/: new work, a ruling) or asks for a base designed for a ...
build-resume-pdfUse when a resume YAML (a variant of the general base, or a base change the owner ordered) needs its PDFs (and a DOCX for Workday), when the page ...
calendar-holdUse when mail-triage finds an interview invite or scheduling email with proposed times, or the owner confirms a slot in a thread, to request a ...
connector-searchUse in a session where the owner has added the Indeed, ZipRecruiter or Dice connector in Claude: run the shared alert queries through each ...
corpus-auditUse weekly (before the weekly review), the day the owner rules on profile conflicts, and after an add-experience batch: evidence ids resolve, every ...
daily-job-planUse each morning (js-morning-brief, or when the owner starts the Recruiter) and evening (js-evening-review), or when the owner asks what the job ...
debriefUse right after an interview round or recruiter call, when the owner reports how it went, or when interview feedback or a rejection reason arrives ...
doctorUse when something looks off (stale status, broken link, drift between a card and its generated agent), after any workshop skill, before a commit, or ...
draft-replyUse when a reply batch plan is ready in state/batches/, when a prospect has written back and needs an answer, or when a thank-you or an approved ...
editorial-reviewUse when a resume variant (or a change the owner ordered to the general base), cover letter or why-us note is ready for the editorial gate, after the ...
follow-up-sweepUse each morning (routine js-follow-up-sweep) or when the owner asks who is waiting on a nudge, to draft the human nudges due at 4 and 9 business ...
gmail-syncUse when the owner, the Recruiter's sweep or a daily check starts a mail pass (mail_intake.py), right after reply batches go out, or when the owner ...
harvest-inboundUse for the one-time Gmail backlog harvest (inbound mail since a start date the owner names) or the weekly safety-net pass over the week's harvested ...
hunt-dailyUse each morning after the fresh-roles review (js-hunt-review), when the owner asks what new roles came in, or when the dashboard's Fresh roles ...
job-dashboardUse when DASHBOARD.md or the job-search metrics must be refreshed, when the owner asks where the search stands, after a batch of tracker events, or ...
laneUse when work is split across more than one session: starting/resuming an orchestrator, a lane or a sub-lane, handing work between sessions, or ...
lane-orchestrationUse whenever work on any project is split across more than one session: starting or resuming an orchestrator, a lane or a sub-lane
lettersUse when an application asks for a cover letter, a 'why are you interested in ' answer or an 'anything else you'd like to share' field: the ...
linkedin-assistUse when the owner prefers to send approved LinkedIn items personally (the hand-off mode
linkedin-draftUse when the owner wants a LinkedIn profile section, a proof post or a comment drafted, when the approved master profile changes, or on the first ...
mock-interviewUse only when the owner asks for a mock interview or practice round (coding, LLM and agent system design, ML fundamentals, behavioral), usually from ...
network-dailyUse on a weekday in the Recruiter's day, or when the owner asks who to connect with or what to comment on: builds the day's network plan (connection ...
new-automationUse when something should happen in reaction to an event (session start or end, a tool call, a file change, a new inbox item, a routine finishing, a ...
new-roleUse when the owner wants to hire a new agent role (agency-wide or project-owned) or asks whether something should be a role at all
new-routineUse when something must happen on a schedule (cloud, GitHub Actions or the desktop app) or the owner asks to automate a recurring job
new-skillUse when a procedure has been repeated by hand twice, or the owner asks to add a skill to the agency or a project
orchestrateUse when work is split across more than one session: starting/resuming an orchestrator, a lane or a sub-lane, handing work between sessions, or ...
page-reviewUse when a variant has passed the scripts, the editor and base-compare: runs the layout gate, then four readers in clean contexts (hiring-manager ...
parse-linkedin-exportUse when a new LinkedIn data export has been unzipped into profile/_inbox/linkedin-export/, or the owner asks how to request one, so LinkedIn ...
platform-watchUse monthly (js-platform-watch), when the owner names a job platform or tool, or when the alert review list shows an unmapped sender, to vet ...
prep-packUse when an application reaches screen, oa, interviewing or final, when an interview invite lands, or when the owner asks to prepare for a specific ...
prospect-searchUse within 24 hours of an application reaching applied, when a dream or strong target company has a fresh fit role and no warm path yet, or when the ...
research-companyUse when a queued application's company dossier is missing or older than 30 days, before an interview round, when the owner names a company, a role ...
research-personUse when one person must be found and understood before any outreach: the exact person verified across two independent sources, what they build and ...
resume-forUse when the owner, the Recruiter or a lane wants the page for one role, at one of three depths (charter): dream (full pipeline), focused (package ...
role-packageUse when a role is chosen and needs to be understood before any resume is written: the role-analyst picks the lens, decodes the JD into a ranked ...
score-fitUse when a job in jobs/ has passed the hard gates or was sent to review and needs a fit report, when the owner asks whether a specific role is worth ...
scout-jobsUse when the js-scout routine runs, when the owner asks for fresh AI/ML postings, or when targets/companies.yaml or sources.yaml changed and the job ...
setup-job-alertsUse when the owner sets up or changes job alerts on a platform (the one-time steps per platform in targets/platforms.yaml), or when a platform's ...
sota-watchUse monthly (js-sota-watch) or when the owner asks what changed in agentic engineering tools or AI/ML hiring, to propose approval-gated updates to ...
syncUse after editing any role card, skill, hook, MCP config or AGENTS.md, and whenever the doctor reports drift
tailor-resumeUse when an application is queued (fit at or above the queue threshold in the policy config, or the owner's override) and needs a tailored resume ...
track-applicationUse when anything happens to an application: the owner says they applied, an email or calendar event arrives, a stage changes, a round is scheduled ...
triage-prospectsUse after a harvest adds prospects at status new, when a prospect's record changes with new inbound, or each morning before the reply batch, to score ...
visa-checkUse when a company, job or offer raises a work-authorization question (sponsorship wording, E-Verify, I-983, remote worksite, start date), when the ...
x-channelUse for anything on X (Twitter): read a post and its replies count, look up a person, search or run the hiring watch list, or prepare a follow, DM ...

A lane is a long-lived session with a charter, a roster and a scope; launch it with claude --agent lane-<type> --settings <repo>/.claude/lane-settings/<type>.json.

LaneTitleAgents it uses
applyApply laneapply-assist, tracker-ops, messenger, legal-visa
focusedFocused lanerole-analyst, company-researcher, prospector, messenger, resume-tailor, resume-editor, base-comparer, technical-recruiter-reviewer, technical-ai-reviewer, hiring-manager-persona, legal-visa, tracker-ops, apply-assist
huntHunt lanejob-scout, platform-scout, prospector, company-researcher, fit-scorer, legal-visa, messenger
postsPosts laneprospector, messenger
prepPrep laneprep-coach
recruiterThe Recruiterrole-analyst, fit-scorer, tracker-ops, corpus-keeper, legal-visa, messenger, prospector, job-scout, company-researcher
researchResearch lanerole-analyst, company-researcher, prospector, fit-scorer, legal-visa
resumeResume laneresume-tailor, resume-editor, base-comparer, hiring-manager-persona, technical-ai-reviewer, technical-recruiter-reviewer, ai-screener, corpus-keeper, role-analyst, company-researcher, prospector, fit-scorer, legal-visa
RoutineSchedule (cron)StateWhat it does
js-apply-batch15 8 * * 1-5offRetired (charter section 7: the apply lane does this work after the owner's batch yes)
js-backlog-harvest30 7 * * 1offEvery week, run the safety-net harvest of the week's recruiter, founder and hiring-manager mail (and a new ...
js-evening-review30 18 * * 1-5offOn weekday evenings run python3 scripts/evening_wrap.py --write: the one evening file ...
js-follow-up-sweep0 8 * * 1-5offOn weekday mornings, draft the nudges that are due (the cadence in charter section 6 and config/defaults.d
js-h1b-cache-refresh0 5 1 * *offMonthly, refresh the USCIS H-1B cache, re-enrich job records and re-verify every board token in the watchlist
js-hunt-boards0 7-22/3 * * *offEvery few hours during the day poll every company board (Greenhouse, Lever, Ashby, Workday, Jibe, sitemaps ...
js-hunt-review30 7 * * *offEach morning capture every JD a terms-safe channel reaches (jd_capture.py, no Firecrawl), re-score, then ...
js-hunt-signals0 7 * * *offEach morning read the day's hiring signals: SEC Form D raises by watch-list and AI companies (funding) and ...
js-hunt-x0 8 * * 1-5offOn weekday mornings search X through the official API for new hiring posts (targets/x-watch.yaml: the hiring ...
js-mail-triage0 7-22 * * *offHourly during the day, classify new job-related mail with the L0 to L3 ladder, match pipeline mail to ...
js-morning-brief45 6 * * *offEach morning run the first half of the Recruiter's day (docs/AGENCY-CHARTER.md section 3), records only and ...
js-platform-watch0 7 3 * *offMonthly, check the job-platform registry, write each platform's 30-day yield, re-read terms that are due, vet ...
js-presence-watch0 8 * * 1offWeekly, check that each public surface in state/presence/portals.yaml says the current truth and is watched ...
js-scout0 6 * * *offEach morning refresh the outbound job feed (ATS boards, Adzuna, the HN thread on days 1 to 5), dedup, enrich ...
js-sota-watch0 7 2 * *offMonthly, research what changed last month in agentic engineering (Claude Code, Agent SDK, Codex, MCP, Agent ...
js-weekly-metrics0 16 * * 0offOn Sundays regenerate the dashboard and write the weekly funnel report: interviews booked this week against ...

Run with uv run --directory mcp job-search-mcp; registered in .mcp.json. Text from postings and mail comes back inside <untrusted_*> tags and is data, never instructions.

ToolKindWhat it does
jobs_searchreadSearch normalized job postings in jobs/ (one compact row per job, no JD text)
job_getreadOne job: its full frontmatter (company, title, sponsorship, fit, status, URLs) plus the JD text
portals_listreadList every job portal in the one registry: id, name, control channel
portal_getreadOne portal from the registry: its row (as portals_list), the setup checklist the owner follows (alert_setup),
job_sightingsreadOne job across portals: the canonical record (a duplicate points to it), every portal it was seen on
reader_model_getreadThe Reader model of one role, from its Role Package research/targets/.md section 6: who reads
focus_profile_getreadHow the owner presents for one role focus: pass a profile family (a lens family name
network_forreadWho in the owner's network can help at a company, or everyone of one role kind: pass a company name
prospects_listreadList CRM prospects (recruiters, founders, hiring managers, referrers) filtered by CRM metadata
prospect_getreadOne prospect: full CRM frontmatter, the last timeline_limit Timeline lines and the newest
applications_listreadList applications (applications//application.md), newest update first
application_getreadOne application: frontmatter, the last events_limit events of its append-only log, the fit
application_hubreadThe per-role record for one application: the opening (live or closed), the company dossier, the
pursuits_listreadList pursuits (pursuits//pursuit.md): one direction into a company each (a referral through one
pursuit_getreadOne pursuit: its record (company, referrer, introducer, contacts, picks, master resume, stage, what it
touches_recentreadRecent touches (every message in or out, from touches/YYYY-MM.jsonl), newest first
dashboard_metricsreadHeadline numbers for the department in one call: the application funnel and response rates
profile_factsreadthe owner's canonical facts from profile/facts.yaml (names, dates, titles, degrees, links, conflicts),
evidence_lookupreadResolve evidence ids (profile/evidence.yaml) to their source, locator and note, or search them
resume_variants_listreadList resume bases (resumes/bases//) and tailored variants (resumes/variants//) from
prospect_setwriteUpdate one CRM prospect record (or create it with create=true), validated against the prospect
tracker_eventwriteAppend one event to an application's log through scripts/tracker.py (the single writer), which
outbox_createwriteFile one outbound DRAFT through scripts/outbox.py (outbox/O--.md, status
approval_createwriteFile a PENDING approval request in work/approvals/ for the owner. It does not approve, send, draft
code root (this repo)        data root (your folder)           runtime root
agents/ skills/ hooks/       profile/  jobs/  prospects/       work/approvals/   (the approval queue)
scripts/ mcp/ dashboard/     applications/  outbox/  state/     logs/runs/        (run receipts)
schemas/ templates/ config/  hub/  targets/  config/  views/   vault/            (secret pointers)
  • Plugin. The repository root is the Claude Code plugin (.claude-plugin/plugin.json): skills from skills/, agents from agents/ (each card carries native description and tools fields that sync.py keeps in step), hooks from hooks/hooks.json, the MCP server from .mcp.json.
  • Roots. scripts/agency_paths.py resolves the three roots: JAJA_DATA (or JOB_SEARCH_ROOT, then ~/.config/jaja/config.yaml), JAJA_RUNTIME (default <data>/runtime), under tests or JAJA_DEMO=1 the bundled demo. scripts/js_common.py is the shared record library.
  • Config seam. scripts/agency_config.py reads identity, style and policy from config/defaults.d/*.yaml (neutral defaults), overlaid by your <data>/config/*.yaml; under pytest a synthetic persona (scripts/tests/persona.d) is used instead.
  • Generated adapters. scripts/kernel/sync.py renders agents/ to .claude/agents/ and .codex/agents/, lanes to lane-<type> agents with settings in .claude/lane-settings/, skills to .agents/skills/, and merges hooks and MCP config; --check reports drift.
  • Hub. Lane sessions coordinate through <data>/hub/: programme.json, registry.json, events.jsonl, rulings.yaml, lanes/, briefs/.

Your data folder (tools/init-data.py creates it; it never overwrites a file):

profile/      facts.yaml  evidence.yaml  PROFILE.md  VOICE.md (blank)  skills-taxonomy.yaml  corpus/  answers/
resumes/      one base resume + a folder per tailored variant        jobs/ jobs-pending/   discovered postings with gate results
applications/ per-application fit report, pack and events            prospects/ graph/ touches/   people, graph, every touch
outbox/       drafts waiting for approval                             pursuits/   company-level campaigns
targets/      roles.md  platforms.yaml  fit-gates.yaml  post-queries.yaml  x-deny.yaml  x-watch.yaml  mass.yaml  goals.yaml
state/        reports, learning loop, presence, browser logs          hub/        orchestration files
config/       identity.yaml  style.yaml  policy.yaml                  views/      optional Obsidian Bases tables
sources.yaml  discovery channels (at the data root)                   legal/      your own work-authorization timers (optional)

Every key the code reads has a neutral default in config/defaults.d/*.yaml; override any of them in <data>/config/*.yaml (files merge in name order). Number in brackets: sub-keys.

SectionKeysNames
identity19account_emails, degrees_held, emails, linkedin_url, name, name_variants, owner_aliases, owner_handles, owner_label, owner_speaker, owner_token, resume_file_pattern, resume_pdf_name, schools, signature_names, signature_urls, timezone, timezone_label, vault_prefix
style14ai_tell_density, ai_tell_words, banned_phrases, banned_words, greeting, letters (2), no_em_dash, no_en_dash, owner_label, pitch_phrases, resume_section_order, resume_slop_words, send (1), signoffs
policy43apply (7), browser (4), browser_lease (1), cockpit (2), connectors (1), dossier (1), employer (2), fit_gates (4), focus (1), fresh10 (4), hunt (13), jd_capture (2), lane_aliases, lenses, letters (3), linkedin_batch (4), linkedin_doc (7), location (6), mail (2), nda (1), nda_terms, network (4), numbers_ledger (3), outreach (8), pace (3), people (1), platforms (2), portal_sitting (6), posts (4), prep (2), profile_route (1), prospects (7), ruling_id_pattern, salary, search (7), segments (1), send (2), skills_map (1), sponsorship (1), strategy (8), tiers (2), title_filter (3), years_gate

The one-line claims in the README, each with the code that enforces it. If a line here stops being true, the README is wrong: fix the code or the claim.

Safety

NuanceWhere it is enforced
Nothing goes out without your yes. Tiers T0 (read) to T4 (forbidden); every outward action is an approval file.Each role card states tier_max (agents/*.md, schema schemas/role.yaml). scripts/kernel/approvals.py writes and verifies approval files (they expire after 7 days). scripts/outbox.py can-send refuses anything not approved; the sender card is the only one that executes sends.
Denied commands cannot be run by a role that must not run them.Cards list deny_commands; the PreToolUse hook hooks/role_deny.py blocks them (listed in hooks/hooks.json).
Connectors are optional and read-only first; sending uses a separate token and login.scripts/gmail_auth.py keeps separate read, compose and send tokens (login, login --compose, login --send); tokens live in ~/.config/jaja/, never in the repo. Setup: docs/SETUP-CONNECTORS.md.
Pacing caps, gaps and a stop on a block page.scripts/pace.py (daily caps and gaps per platform, from config policy.pace.*), scripts/browser_pace.py (a 429 or block page stops all lanes), hooks/browser_lease.py (one keyboard on a shared browser).
Routines ship disabled, write receipts, and a watchdog flags silence.Every routines/*.yaml has enabled: false; scripts/kernel/lint.py flags an enabled model routine without enabled_by; scripts/kernel/watchdog.py compares the newest receipt in <runtime>/logs/runs/<name>/ with the schedule.

Truth

NuanceWhere it is enforced
Every claim cites an evidence id from your records.scripts/check_evidence.py validates the evidence registry and citations; scripts/render_resume.py lints missing-evidence and unknown-evidence; scripts/numbers_ledger.py and scripts/check_holds.py check figures and strings you listed as holds.
Gates fail with the exact line they quoted.scripts/fit_gates.py writes gate, gate_reasons and gate_evidence on each job record.
One frozen base resume; a variant ships only if it beats the base in a blind comparison.Base files are hash-pinned (SHA256.txt, read by scripts/apply_batch.py and scripts/profile_route.py); scripts/base_drift.py finds variant lines from an older base; scripts/blind_pairs.py and skills/base-compare run the blind A/B.
Missing is not empty.A check that could not run reports it instead of passing (see docs/CONCEPTS.md, "Missing is not empty"; the dashboard shows "no data yet" where a source is absent).

Structure

NuanceWhere it is enforced
Scripts count and check; models judge and draft.The deterministic work is in scripts/ (gates, scoring, rendering, tracking, approvals); the cards and skills only call it.
One writer per file; lanes, an orchestrator, a hub of events and rulings, and messaging between sessions.hooks/lane_write_guard.py (one writer per file), hooks/lane_context.py, hooks/lane_register.py, hooks/lane_rules.py (rulings digest at session start), hooks/role_rules.py, hooks/lane_handover.py; hub events and state in scripts/kernel/hub_events.py, lane_state.py, rulings_digest.py; the doctrine in skills/lane-orchestration/.
Adapters are generated and a hook blocks editing them.scripts/kernel/sync.py renders .claude/, .codex/ and .agents/; sync.py --check reports drift; hooks/guard_generated_files.py refuses edits to files marked GENERATED by.
History is append-only.Hub events, run receipts and approval records are appended, never rewritten (scripts/kernel/hub_events.py, watchdog.py, approvals.py).
Each role declares its tools, tier, model and budget.Fields on every card in agents/; scripts/model_policy.py checks the model policy; scripts/kernel/token_ledger.py measures token use per session and agent type (a measurement, not a hard stop).
One source of truth per thing.Cards, skills, hooks and config are the sources; adapters are generated; records live only in your data folder.
A leak guard keeps your records out of the repository.tools/leak_check.py (patterns, your private deny-list in ~/.config/jaja/, gitleaks) run by the pre-commit hook installed by install.sh.
CommandWhat it proves
python3 scripts/doctor.py [--data PATH]the whole install: Python, data folder, config, cards, hooks, adapters, MCP server, connectors, routines, leak guard
python3 scripts/kernel/lint.pyschemas, skills, tags, rulings registry
python3 scripts/kernel/sync.py --checkgenerated adapters are current (sync.py regenerates)
python3 scripts/check_records.py --allevery record in your data folder matches its schema
python3 -m pytest scripts/tests -m "not real_records and not workspace"the suite, on the demo data, no network
python3 tools/leak_check.py [--staged]nothing personal in tracked files (patterns, your private deny-list, gitleaks)
python3 tools/gen-reference.py --check and tools/gen-readme.py --checkthe generated tables in docs/REFERENCE.md and this README match the repository
agents/         role cards: the source, and the agents Claude Code loads from the plugin
role-references/  rubrics and gates the cards cite
skills/         procedures, invoked as /jaja:<name>
lanes/          lane definitions: AGENT.md + LANE.yaml per lane
playbooks/      method guides (outreach, send-after-yes, portals/*)
routines/       scheduled work, all enabled: false
hooks/          hooks.json + the guards
mcp/            job-search MCP server (reads your records, writes drafts and approvals, never sends)
scripts/        the working scripts; scripts/kernel = sync, lint, sweep, approvals, hub tools; scripts/tests
dashboard/      local dashboard (stdlib http.server on 127.0.0.1)
schemas/ templates/ config/   record and card schemas; new-card, data-folder and resume templates; neutral defaults
examples/       demo-data: a synthetic candidate
docs/           architecture, concepts, guides, charter, connector setup
evals/          the eval harness (write your own cases; none ship)
tools/          init-data, leak check, migration helpers, generators, pre-commit
.claude-plugin/  .mcp.json   plugin manifest and MCP config        .claude/  .codex/  .agents/   generated adapters

Contributing and support

Issues and pull requests are welcome; start with CONTRIBUTING.md. Report security problems as described in SECURITY.md. By taking part you agree to the code of conduct. If jaja saves you time, a star helps other job seekers find it, and a short issue or discussion about what worked or broke for you is the most useful contribution.

License

MIT. See LICENSE and NOTICE.

About the author

Built by Khalid Mehtab Khan, an engineer who works on AI agent systems and evaluation tooling. jaja began as the system he built to run his own job search; this repository is that system with his records removed. GitHub · LinkedIn

Star history

Star History Chart

↑↓ navigate openesc close