Skip to content

@roasted/cli

Complete CLI reference

A non-interactive job-hunt harness for people and agents. Start with one source resume, create a tailored kit for each job, render the PDF, and record the application.

Install and start

Install the global package and follow the golden path below. The executable is roasted. It includes the production connection settings, so no environment setup is required.

terminal
npm i -g @roasted/cli
roasted login
roasted cv push ./resume.yaml --name "Backend Engineer"
roasted apply plan
roasted apply create --cv "Backend Engineer" --jd-file ./jd.txt
roasted apply pdf <applyKitId> --out resume.pdf
roasted app add --from-kit <applyKitId>

Agent orientation

Every command takes --json, every input can come from flags or stdin, and nothing prompts after invocation. Agents should parse stdout and branch on stable error codes rather than message text.

  • Success exits with 0. Any error exits with 1 and returns an error object in JSON mode.
  • apply create --no-wait returns the apply kit ID immediately for fan-out and later polling.
  • apply create --watch streams one NDJSON line per phase before the final result.
  • ROASTED_API_KEY overrides stored profiles for ephemeral headless runs.
  • --profile or ROASTED_PROFILE selects a named account without switching global state.

Authentication and profiles

Browser login uses PKCE. Sessions live in ~/.roasted/profiles.json with owner-only permissions where supported. Selection order is ROASTED_API_KEY, --profile, ROASTED_PROFILE, then the active profile. OAuth sessions refresh automatically. API-key profiles re-exchange their stored key when a session lapses.

terminal
roasted login --profile primary
roasted login --profile second --api-key -
roasted accounts
roasted switch second
roasted cv list --profile primary

API keys

Personal keys start with rmt_pat_ and are shown once. Only their hash is stored by the server. Use --api-key - to read a key from stdin and keep it out of shell history.

terminal
roasted keys create ci-runner --ttl never
roasted login --api-key rmt_pat_...
ROASTED_API_KEY=rmt_pat_... roasted cv list

Command reference

Auth and accounts

CommandPurpose
roasted login [--api-key <key|->]Sign in through browser PKCE or an API key.
roasted accountsList profiles and show the active one.
roasted switch <name>Make a named profile active.
roasted whoamiShow the current identity, role, referral code, and profile.
roasted logout [--all]Remove the selected profile or every profile.
roasted keys create <name> [--ttl <ttl>]Create a personal API key with a 30d, 90d, 1y, or never expiry.
roasted keys listList keys with status and last use.
roasted keys revoke <id|name>Revoke a key by ID or name.

Resumes

CommandPurpose
roasted cv listList account resumes, newest first.
roasted cv search <query>Filter resumes by name.
roasted cv pull <id|name> [--out file]Download resume YAML to stdout or a file.
roasted cv push <file> [--id <id>] [--name <name>]Create or update a resume from validated YAML or JSON.

Apply kits

CommandPurpose
roasted apply planShow the plan, credit balance, and kit costs.
roasted apply create --cv <id|name>Generate a tailored resume and cover letter. This spends credits.
roasted apply listList apply kits, newest first.
roasted apply get <id>Read status and results. Add --pages to render and count pages.
roasted apply cv <kitId> [--out file]Download the tailored resume YAML.
roasted apply push <kitId> <file.yaml>Validate and upload a hand-edited tailored resume.
roasted apply pdf <id>Render the server copy of the tailored resume locally.
roasted apply shrink <id>Fit the tailored resume to one page. This can spend a credit.

Applications tracker

CommandPurpose
roasted app list [--status <status>]List applications, optionally filtered by status.
roasted app get <id>Read a complete application record.
roasted app addCreate an application manually or from a ready apply kit.
roasted app status <id> <status>Move status and optionally set a closed outcome or note.
roasted app note <id> <text>Set notes. An empty value clears them.
roasted app rm <id>Delete an application.

Apply-kit flow

The create command waits by default, then prints the result and dashboard URL. Use get for cheap polling and add --pages only when page count is needed. Resume references resolve by exact ID, then a unique case-insensitive name match.

terminal
roasted cv list
roasted apply plan
roasted apply create --cv "Backend Engineer" --jd-file ./jd.txt \
  --intensity smart --tone professional --length concise
roasted apply get <id> --pages --json
roasted apply cv <id> --out tailored.yaml
roasted apply push <id> tailored.yaml
roasted apply pdf <id> --out resume.pdf
roasted apply shrink <id>

Job description and shaping flags

Supply exactly one of --jd-file, --jd, or --jd-stdin. Text shorter than 50 characters is rejected. Shape the result with intensity safe|smart|bold, tone auto|professional|confident|conversational, length concise|standard|detailed, formality formal|neutral|casual, language and instruction flags, --premium, and the cover-letter toggle. Invalid values fail before credits are spent.

Edit the tailored resume safely

Download with apply cv, edit locally, then upload with apply push before rendering. The server copy is canonical. If you skip the push, apply pdf re-renders the old server version and ignores local changes.

PDF and one-page fitting

apply pdf uses the local typst binary. Without it, the command exits successfully with a dashboard link and installation hint. apply shrink checks the page count when a renderer is available, stops without spending if the resume already fits, and saves the revised YAML back to the kit.

What costs money

  • Free: login, account and key management, resume commands, plan checks, and tracker reads.
  • Hunter Pass plus credits: apply create and apply shrink. A preflight rejects free plans or insufficient balances before generation.
  • Hunter Pass: creating tracker entries, including entries bridged from an apply kit. Status, notes, deletion, and reads are not gated.
  • Generation failures are refunded by the backend. The CLI never modifies balances itself.

End-to-end playbooks

Batch pipeline

Push one source resume, start one kit per saved job description without waiting, poll each ID, render the PDFs, and record applications that were actually submitted.

shell
roasted cv push ./canon-resume.yaml --name "Backend Engineer" --json
CV=$(roasted cv list --json | jq -r '.cvs[] | select(.name=="Backend Engineer") | .id')
for jd in jds/*.txt; do
  roasted apply create --cv "$CV" --jd-file "$jd" --no-wait --json
done > kits.ndjson
jq -r '.applyKitId' kits.ndjson | while read -r id; do
  while :; do
    kit=$(roasted apply get "$id" --json) || { printf 'Retrying kit %s
' "$id" >&2; sleep 2; continue; }
    status=$(printf '%s' "$kit" | jq -r '.status')
    case "$status" in
      ready) break ;;
      generating) sleep 2 ;;
      *) printf 'Kit %s stopped with status: %s
' "$id" "$status" >&2; continue 2 ;;
    esac
  done
  roasted apply pdf "$id" --out "out/$id.pdf" --json
  roasted app add --from-kit "$id" --status applied --json
done

Agent-driven application

For one-at-a-time work, let the agent create and inspect the kit, copy the cover letter, and move the tracker record through the real application status flow.

shell
roasted apply create --cv "QA Engineer" --jd-file ./jd.txt \
  --cv-lang en --cl-lang en --intensity smart --json
ID=<applyKitId>
roasted apply get "$ID" --pages --json
roasted apply shrink "$ID" --json
roasted apply get "$ID" --json | jq -r '.coverLetter' > cover-letter.txt
roasted app add --from-kit "$ID" --status saved --json
roasted app status <appId> applied --json
roasted app status <appId> interviewing --json
roasted app status <appId> closed --outcome offer --json

JSON output schemas

The examples below show the orchestration-critical shapes. Nullable fields remain present. In watch mode, progress objects are emitted one per line before the final result.

whoami
{ "email": string|null, "role": string|null, "refCode": string|null,
  "userId": string, "profile": string|null,
  "auth": "oauth"|"api_key"|"env_api_key" }
accounts
{ "active": string|null, "activeSource": string, "envApiKey": boolean,
  "accounts": [{ "name": string, "type": "oauth"|"api_key",
    "email": string|null, "apiKeyMasked": string|null, "active": boolean }] }
keys create
{ "id": string, "name": string, "key": string, "masked": string,
  "status": "active", "createdAt": string, "expiresAt": string|null,
  "lastUsedAt": null, "revokedAt": null }
keys list
{ "keys": [{ "id": string, "name": string, "masked": string,
  "status": "active"|"revoked"|"expired", "createdAt": string,
  "expiresAt": string|null, "lastUsedAt": string|null,
  "revokedAt": string|null }] }
cv list / cv search
{ "cvs": [{ "id": string, "name": string|null, "updatedAt": string }] }
cv pull
{ "id": string, "name": string|null, "yaml": string }
cv push
{ "id": string, "name": string|null, "created": boolean,
  "warnings": string[], "dashboardUrl": string }
apply plan
{ "plan": string, "credits": number, "cost": number,
  "premiumCost": number, "canAfford": boolean }
apply create --no-wait
{ "applyKitId": string }
apply create --watch
{ "phase": string, "at": string }
apply create / apply get
{ "id": string, "createdAt": string,
  "status": "generating"|"ready"|"failed", "progressStep": string|null,
  "jobTitle": string|null, "company": string|null, "location": string|null,
  "seniority": string|null, "salaryRange": string|null, "techStack": string[],
  "keyRequirements": string[], "niceToHave": string[], "deadline": string|null,
  "intensity": string|null, "tone": string|null, "length": string|null,
  "coverLetter": string|null, "includeCoverLetter": boolean, "premium": boolean,
  "sourceCvFileId": string|null, "tailoredFileId": string|null,
  "failureReason": string|null, "generatedAt": string|null,
  "dashboardUrl": string, "pageCount": number|null }
apply list
{ "kits": [{ "id": string, "createdAt": string, "status": string|null,
  "jobTitle": string|null, "company": string|null, "intensity": string|null,
  "tone": string|null, "length": string|null }] }
apply pdf
{ "applyKitId": string, "pageCount": number|null, "out": string|null,
  "dashboardUrl": string, "hint"?: string }
apply shrink
{ "applyKitId": string, "tailoredFileId"?: string,
  "pageCountBefore": number|null, "pageCountAfter": number|null,
  "changed": boolean, "dashboardUrl": string }
app list
{ "applications": [{ "id": string, "status": string|null,
  "outcome": string|null, "company": string|null, "position": string|null,
  "location": string|null, "source": string|null, "appliedAt": string|null,
  "createdAt": string, "updatedAt": string }] }
app get / add / save / status / note
{ "id": string, "status": string|null, "outcome": string|null,
  "company": string|null, "position": string|null, "location": string|null,
  "salaryRange": string|null, "seniority": string|null, "sourceUrl": string|null,
  "contactPerson": string|null, "contactEmail": string|null, "deadline": string|null,
  "cvFileId": string|null, "applyKitId": string|null, "appliedAt": string|null,
  "source": string|null, "jdText": string|null, "coverLetter": string|null,
  "notes": string|null, "techStack": string[], "keyRequirements": string[],
  "niceToHave": string[], "createdAt": string, "updatedAt": string,
  "dashboardUrl": string }
app rm
{ "id": string, "deleted": true }
error
{ "error": { "code": string, "message": string } }

List commands wrap rows in cvs, kits, applications, or keys arrays. Detail commands return the complete record, including nullable job metadata, timestamps, linked file IDs, source content, and the dashboard URL.

Exit codes and error catalog

All commands exit 0 on success and 1 on any error. In JSON mode, inspect error.code. Backend error codes from generation and shrinking pass through unchanged. PAID_PLAN_REQUIRED on a free account is expected behavior rather than a malfunction: apply kits and new tracker entries need an active Hunter Pass, and subscribing clears it.

  • Auth and profiles: NO_SESSION, SESSION_EXPIRED, AUTH_UNAVAILABLE, BAD_PROFILE, PROFILE_NOT_FOUND, PROFILE_STORE_CORRUPT, API_KEY_INVALID, API_KEY_EXCHANGE_FAILED, BAD_KEY_NAME, BAD_TTL, KEY_NOT_FOUND, KEY_AMBIGUOUS.
  • Resumes and apply kits: CV_NOT_FOUND, CV_AMBIGUOUS, BAD_CV_YAML, FILE_NOT_FOUND, JD_MISSING, JD_CONFLICT, JD_TOO_SHORT, BAD_INTENSITY, BAD_TONE, BAD_LENGTH, BAD_FORMALITY, PAID_PLAN_REQUIRED, INSUFFICIENT_CREDITS, NOT_FOUND, NO_TAILORED_FILE, NO_YAML, NO_CV_CONTENT, TYPST_COMPILE_FAILED, KIT_TIMEOUT.
  • Tracker: BAD_STATUS, BAD_OUTCOME, OUTCOME_REQUIRES_CLOSED, APP_EMPTY, APP_ARG_CONFLICT, PAID_PLAN_REQUIRED, KIT_NOT_READY, NOT_FOUND.