Skip to Content
ReferenceCommand line

Command line

37 verbs, one entry point. Three of them are the whole daily loop: karani run fetches and filters for free, karani qualify spends a model call on what survived, and karani console lays out what is ready to send.

Every verb returns 0 when all is well and non-zero when it could not do what you asked, so each one composes into a shell chain. Long operations print a machine-readable summary line (fetched=N inserted=N passed=N) because something is usually reading the log.

Billed verbs say so in their description. Every one of them obeys the monthly budget in [budget]: past the cap, a billed provider is swapped for the local model and the pass says once that it happened, rather than failing or overspending (ADR 0019).

Setup

Run once, then when the hunt changes.

karani init

The interactive setup. Writes karani.toml: what to hunt, which model runs each task, your positioning, your declared legal facts. Secrets are never written here — they live in .env.

karani init [--yes]

Options

OptionDescription
--yesAccept all defaults.

karani config

karani config [{check}]

Show the resolved configuration.

Arguments

ArgumentDescription
actionOne of check. Default: check.

karani infra

karani infra [--profile PROFILE] {up,down}

Manage karani’s docker infra.

Arguments

ArgumentDescription
actionOne of up, down.

Options

OptionDescription
--profile PROFILECompose profile, e.g. local-llm.

Examples

# Postgres with pgvector on localhost:5433 karani infra up # and Ollama on localhost:11434 karani infra up --profile local-llm

karani hunt

Schedules the continuous hunt. Installs the launchd agents: the hourly pass, the twice-daily summaries, and an out-of-process watchdog that is deliberately nothing but sh and curl so it cannot die the same way the pipeline does (ADR 0018).

karani hunt [-h]

karani unschedule

karani unschedule [-h]

Remove the scheduled hunt.

The hunt

Fetching postings and deciding which deserve a model call. run and qualify are the two tiers (ADR 0001); hourly is both plus delivery, which is what the scheduler actually runs.

karani run

The deterministic tier, and the only one that is free. It fetches every configured board and feed, classifies each posting’s role and seniority, runs the pre-filter, de-duplicates across sources, stores what survives and closes what has gone stale. No model is called. About 95% of a pass ends here.

karani run [-h]

Examples

# one deterministic pass over every source karani run

karani qualify

The LLM tier. Top-scored pending rows go to a model with your resume, your positioning and your past verdicts as few-shot examples, and come back with a fit score, a three-way verdict, evidence-backed strengths and gaps with mitigations. Billed unless [llm.qualify] points at a local model, which the reference config does. Idempotent per resume hash: change your resume and the next run re-judges, leave it alone and a second run costs nothing.

karani qualify [--limit LIMIT] [--concurrency CONCURRENCY] [--provider {None,openrouter,anthropic,local}] [--model MODEL] [--resume RESUME] [--agent]

Options

OptionDescription
--limit LIMITDefault: 50.
--concurrency CONCURRENCYDefault: 3.
--provider PROVIDEROne of None, openrouter, anthropic, local.
--model MODEL—
--resume RESUMEDefault: data/resume.md.
--agentTool-using agent loop (OpenRouter only).

Examples

# judge the top 50 pending rows karani qualify --limit 50 # on a local model, at zero token cost karani qualify --provider local --model qwen3:4b # let the qualifier gather evidence first; 5-10x the cost per row karani qualify --agent --limit 5

karani refilter

Re-runs the pre-filter over every active row. This is what makes changing the rules a config edit rather than a re-ingest: edit karani.toml, run this, and newly-passing rows queue for the next qualify pass.

karani refilter [-h]

karani discover

karani discover [--limit LIMIT]

Probe unpromoted companies for ATS presence.

Options

OptionDescription
--limit LIMITDefault: 10.

karani sweep

karani sweep [--days DAYS]

Mark stale jobs inactive.

Options

OptionDescription
--days DAYSDefault: 10.

karani hourly

One full pass: ingest, qualify the arrivals, draft packs for new top-fit roles, deliver review cards. This is what the scheduler runs. It runs under a hard deadline, because a pass that cannot finish silences every pass after it (ADR 0025).

karani hourly [--loop [LOOP]]

Options

OptionDescription
--loop LOOP—

karani autopilot

karani autopilot [--min-fit MIN_FIT] [--max-drafts MAX_DRAFTS]

Draft packs for top-fit roles and deliver to Slack for review.

Options

OptionDescription
--min-fit MIN_FIT—
--max-drafts MAX_DRAFTS—

Reviewing and applying

The daily loop: see what came in, build the pack, send it yourself, record that you did.

karani actions

karani actions [--review-limit REVIEW_LIMIT] [--follow-up-days FOLLOW_UP_DAYS]

Prioritized worklist: review, draft, submit, follow up.

Options

OptionDescription
--review-limit REVIEW_LIMITDefault: 10.
--follow-up-days FOLLOW_UP_DAYSDefault: 7.

karani digest

karani digest [--limit LIMIT] [--format {text,md,html}] [--output OUTPUT]

Render top qualified roles.

Options

OptionDescription
--limit LIMITDefault: 20.
--format FORMATOne of text, md, html. Default: text.
--output OUTPUTWrite to file instead of stdout.

Examples

# render the shortlist to a file karani digest --format html --output data/digest.html # the top five, for a terminal karani digest --format text --limit 5

karani draft

The pack factory: a tailored resume, a cover letter in your voice, and answers to the application’s questions. Every claim about you is a selection over your own profile records with the citation verified before it ships — an unverifiable claim is dropped, not repaired (CLAUDE.md 4.13). Billed.

karani draft [--provider {None,openrouter,anthropic,local}] [--model MODEL] [--resume RESUME] [--output OUTPUT] job_id

Arguments

ArgumentDescription
job_id—

Options

OptionDescription
--provider PROVIDEROne of None, openrouter, anthropic, local.
--model MODEL—
--resume RESUMEDefault: data/resume.md.
--output OUTPUTOutput path; default drafts/<id>_<company>__<title>.md.

Examples

# build the pack for one job karani draft 1482 # use a stronger model for drafting than for bulk qualification karani draft 1482 --provider openrouter

karani console

Builds the submission console: one self-contained HTML file with every pack waiting to be sent. Per job, the apply link, presigned links to the rendered documents, and a field-by-field answer sheet with a copy button on each field — answering that job’s real questions, read from the ATS’s own public API. Free: no model is called. It works on a phone, which is where the queue actually gets cleared.

karani console [--limit LIMIT] [--output OUTPUT] [--refresh]

Options

OptionDescription
--limit LIMITDefault: 50.
--output OUTPUTDefault data/submission_console.html.
--refreshRe-read each job’s questions from the ATS.

Examples

# build the console from every pack in drafting or ready karani console # re-read each job's application questions from its ATS karani console --refresh

karani submitted

Records what you actually sent: status, timestamp, and a snapshot of the artifact version that went out. That write-back is what arms the follow-up logic and gives the conversion funnel its labels, so skipping it costs you the measurement, not just the bookkeeping. The console prints the exact line to run.

karani submitted [--warm | --cold] [--via VIA] job_ids [job_ids ...]

Arguments

ArgumentDescription
job_ids—

Options

OptionDescription
--warmWent through a warm contact or referral.
--coldCold portal application.
--via VIAConsole | portal | email | referral. Default: console.

Examples

# record two applications sent through the portal karani submitted 1482 1511 --cold # record one sent through a referral; feeds the warm-vs-cold split karani submitted 1523 --warm

karani verdict

Your reaction to a suggestion. This is the taste-calibration signal: it becomes a few-shot example in the next qualify prompt and a distilled fact in the memory ledger, so the pipeline’s judgement converges on yours without any retraining.

karani verdict job_id {apply,shortlist,later,skip,applied}

Arguments

ArgumentDescription
job_id—
verdictOne of apply, shortlist, later, skip, applied.

Examples

# agree with the suggestion karani verdict 1482 apply # disagree; the next qualify pass sees this karani verdict 1523 skip

Tracking an application

The state machine, and what fires off it. Moving an application to screen or interview builds an interview pack by itself (ADR 0024).

karani status

karani status [--warm | --cold] job_id {applied,declined,drafting,ghosted,interview,new,offer,ready,rejected,screen}

Set application state machine status.

Arguments

ArgumentDescription
job_id—
statusOne of applied, declined, drafting, ghosted, interview, new, offer, ready, rejected, screen.

Options

OptionDescription
--warmApplication went through a warm contact.
--coldCold portal application.

Examples

# move it to applied, and say how karani status 1482 applied --cold # a reply came in; this also fires the interview prep pack karani status 1511 screen

karani stage

karani stage [--notes NOTES] job_id stage

Log an interview or application stage.

Arguments

ArgumentDescription
job_id—
stageE.g. recruiter_screen, hiring_manager, technical, onsite.

Options

OptionDescription
--notes NOTESDefault: “.

karani asked

karani asked [--stage STAGE] job_id question

Log an interview question into the question bank.

Arguments

ArgumentDescription
job_id—
question—

Options

OptionDescription
--stage STAGEDefault: “.

karani outcome

karani outcome job_id {declined,ghosted,offer,rejection,withdrew}

Record final outcome.

Arguments

ArgumentDescription
job_id—
outcomeOne of declined, ghosted, offer, rejection, withdrew.

karani prep

An interview prep pack: a refreshed company dossier, likely questions derived from the qualification’s gap analysis, STAR stories mined from your own records with every citation verified, comp research derived from the posted band, and questions to ask. Usually you never type this — moving an application to screen or interview fires it. Billed.

karani prep [--stage {screen,interview}] [--provider {None,openrouter,anthropic,local}] [--model MODEL] [--resume RESUME] job_id

Arguments

ArgumentDescription
job_id—

Options

OptionDescription
--stage STAGEOne of screen, interview. Default: interview.
--provider PROVIDEROne of None, openrouter, anthropic, local.
--model MODEL—
--resume RESUMEDefault: data/resume.md.

karani followup

karani followup [--provider {None,openrouter,anthropic,local}] [--model MODEL] job_id

Draft a follow-up note for a silent application.

Arguments

ArgumentDescription
job_id—

Options

OptionDescription
--provider PROVIDEROne of None, openrouter, anthropic, local.
--model MODEL—

karani watch

Reads recent replies over a read-only Gmail scope, classifies each one, matches it to an application conservatively, advances the state machine only on a confident match, and drafts a reply for review. It cannot send, delete or mark anything read — the scope makes all three impossible, not the code.

karani watch [--days DAYS] [--limit LIMIT] [--no-slack] [--authorize]

Options

OptionDescription
--days DAYSLookback window.
--limit LIMITMax messages to read.
--no-slackPrint the pass instead of pushing it to Slack.
--authorizeRun the one-time read-only OAuth consent flow.

Examples

# once: consent to the read-only scope, print the refresh token karani watch --authorize # print the pass instead of pushing it karani watch --no-slack # a wider window karani watch --days 30

Measuring

Conversion, segmented and interval-reported (ADR 0022). A bare percentage over a handful of applications is a lie told with arithmetic, so these never print one.

karani stats

karani stats [-h]

Print DB counts + pipeline funnel.

karani funnel

Conversion by positioning angle, variant, company, comp band and posting age, each rate with its interval. Fit bands are pooled only when one scorer produced every score, because a fit score means nothing across models: measured over 598 rows, one model’s mean was 38 and another’s 47 (ADR 0022).

karani funnel [-h]

Examples

# conversion rates, each with its interval karani funnel # the derived weekly review over the same ledger karani coach --window-days 7

karani coach

karani coach [--window-days WINDOW_DAYS] [--slack] [--save SAVE] [--docs]

Weekly review over the conversion ledger.

Options

OptionDescription
--window-days WINDOW_DAYSDefault: 7.
--slackAlso post the review as a Slack card.
--save SAVEWrite the review markdown to this path.
--docsWith —save, also render .docx/.pdf.

karani variant

karani variant [--dimension {positioning,draft_prompt,resume_order,outreach}] [--description DESCRIPTION] [--guidance GUIDANCE] [--job JOB] {list,add,retire,assign} [name]

Strategy variants: list, add, retire, assign.

Arguments

ArgumentDescription
actionOne of list, add, retire, assign.
name—

Options

OptionDescription
--dimension DIMENSIONOne of positioning, draft_prompt, resume_order, outreach. Default: positioning.
--description DESCRIPTIONDefault: “.
--guidance GUIDANCEWhat a drafter should actually do differently. Default: “.
--job JOBJob id, for variant assign.

Memory and intel

What karani retains between passes.

karani remember

karani remember [--kind {company,outcome,preference,question,strategy}] [--company COMPANY] content

Store a fact in the memory layer.

Arguments

ArgumentDescription
content—

Options

OptionDescription
--kind KINDOne of company, outcome, preference, question, strategy. Default: preference.
--company COMPANY—

Examples

# bank what they actually asked karani remember "Their screen asked about incident ownership" --kind question --company 'Northwind Robotics' # a global preference; every later qualification recalls it karani remember "Not considering crypto companies" --kind preference

karani recall

karani recall [--kind {None,company,outcome,preference,question,strategy}] [--company COMPANY] [--limit LIMIT] query

Query the memory layer.

Arguments

ArgumentDescription
query—

Options

OptionDescription
--kind KINDOne of None, company, outcome, preference, question, strategy.
--company COMPANY—
--limit LIMITDefault: 5.

karani reindex

karani reindex [-h]

Rebuild the mem0 index from the memory ledger.

karani intel

karani intel [--refresh] company

Company dossier from public probes (cached).

Arguments

ArgumentDescription
company—

Options

OptionDescription
--refresh—

Other surfaces

The same pipeline, different front doors. All three are thin adapters over the same storage and runners.

karani mcp

karani mcp [-h]

Serve the MCP server over stdio.

karani slack

karani slack [-h]

Run the two-way Slack listener.

karani notify

karani notify [--kind {digest,actions}] [--limit LIMIT]

Push digest or actions to Slack.

Options

OptionDescription
--kind KINDOne of digest, actions. Default: digest.
--limit LIMITDefault: 10.

karani notion

karani notion {init,sync} [parent_page_id]

Mirror tracked applications to a Notion board.

Arguments

ArgumentDescription
actionOne of init, sync.
parent_page_idFor init: page id to create the database under.
Last updated on