Receber por email
← Voltar para o catálogo

auto-apply

Search a job board and autonomously apply to matching jobs one at a time, until paused, exhausted, or the max-applications cap is hit.

34estrelas
Atualizado há 3 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add suxrobGM/jobpilot

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/auto-apply/svg)](https://www.skillteca.com.br/skills/auto-apply)

Skills relacionadas

Ver todas de Pesquisa e Web →

Alerta por categoria

Receba novas skills de Pesquisa e Web toda segunda

1 email curto, só com as skills novas de Pesquisa e Web. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Auto-apply - Search + Apply On Demand

Keep the chosen board open in tab 1; for each result that qualifies, delegate the application to the job-worker subagent (it works in its own tab and returns a compact result), then move to the next job. No batch pre-discovery and no per-job approval - launching the campaign is the confirmation. Pause only for 2FA / payment. A CAPTCHA is not a pause - attempt the solve-captcha skill; if unsolved, skip the job (never pause) and the user finishes it later via the apply skill. Live view at $JOBPILOT_WEB/campaigns/<campaign-id>.

Setup

JOBPILOT_API="${JOBPILOT_API:-https://jobpilot.suxrobgm.net}"

Follow ../../shared/setup.md. Read autoApply (defaults applied per field):

| Setting | Default | Notes | | ---------------------------- | ------------------ | ----------------------------------------------------------------------------------------- | | minMatchScore | 60 | Qualification threshold (0-100). Inline --min-score overrides. | | maxApplicationsPerCampaign | null (unlimited) | Stop after this many successful applies. Inline --max-apps overrides; omit → unlimited. | | defaultStartDate | "2 weeks notice" | Default start-date answer. |

Inline argument overrides take precedence. --board <domain> is required unless the argument is resume or retry-failed <campaign-id>.

Campaign Modes

  • "resume" → list incomplete campaigns (GET /api/campaigns?status=in_progress), ask which to resume, replay the apply loop on remaining applying/approved/pending jobs.
  • "retry-failed <campaign-id>" → fetch the campaign; for every retryable failed job, POST its /retry command with the current retryNotes, then replay the apply loop on those approved rows.
  • Otherwise → search query → Phase 0.

To recover wrongly-skipped jobs, use the dedicated rescan-skipped skill (it re-scores and promotes to approved; apply them afterward).

Phase 0: Existing Campaign Check + Create

curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/campaigns?status=in_progress"
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/campaigns?status=paused"

Each response is paginated; inspect its .items array.

If any matches, ask "Found an incomplete campaign from <startedAt> (status: <status>). Resume or start fresh?" Resume → inject the resume skill with that campaignId.

Otherwise the web UI already created the campaign row when the user submitted /campaigns/new - confirm it exists and use that campaignId. Capture its selected base resume: RESUME_ID=$(curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/campaigns/$CAMPAIGN_ID" | jq -r '.config.resumeId // ""') (empty → fall back to the primary downstream). If invoked manually (rare), create one:

# resumeId is REQUIRED for auto-apply - default to the profile's primary.
RESUME_ID=$(curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/user" | jq -r '.user.primaryResumeId // ""')
# maxApplications is OPTIONAL - omit the field entirely for unlimited mode.
CAMPAIGN=$(curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X POST "$JOBPILOT_API/api/campaigns" \
  -H 'content-type: application/json' \
  -d "$(jq -n --arg q "<query>" --arg board "<domain>" --arg rid "$RESUME_ID" \
    --argjson minScore <n> \
    '{query:$q, source:"auto-apply", config:{board:$board, resumeId:$rid, minScore:$minScore}}')")
CAMPAIGN_ID=$(echo "$CAMPAIGN" | jq -r '.campaignId')

Surface live view: $JOBPILOT_WEB/campaigns/<CAMPAIGN_ID>.

Phase 1: Open the Board (tab 1)

1.1 Parse Query

Extract title/role, keywords, location, preferences. If vague, ask before searching.

1.2 Search the Chosen Board

Resolve the board:

curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/job-boards" | jq --arg d "<domain>" '.[] | select(.domain == $d)'

If no row matches, command the campaign to failed with POST /api/campaigns/$CAMPAIGN_ID/status {"status":"failed"} and stop.

  1. browser_navigate to searchUrl (this is tab 1 - keep it open for the whole campaign).
  2. Follow ../../shared/auth.md - logs in, and registers a new account when none exists, without asking.
  3. Fill the search fields and submit.
  4. Take a browser_snapshot narrowed to the results list (per ../../shared/browser-tips.md) to read { title, company, location, url } per row. This is one viewport - scroll/paginate per Pagination & infinite scroll in ../../shared/browser-tips.md as the loop drains rows (see 2.5); never treat the first batch as all jobs.

Phase 2: Apply Loop (on demand)

Walk the tab-1 results top to bottom; at the last loaded row, scroll/page for more (1.2 step 4) before concluding. For each result:

2.1 Pre-filter (no tab)

Dedupe in-board by normalized title+company. Then check previously-applied:

URL_ENCODED=$(jq -rn --arg v "<job-url>" '$v|@uri')
TITLE_ENCODED=$(jq -rn --arg v "<title>" '$v|@uri')
COMPANY_ENCODED=$(jq -rn --arg v "<company>" '$v|@uri')
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" "$JOBPILOT_API/api/applied/check?url=$URL_ENCODED&title=$TITLE_ENCODED&company=$COMPANY_ENCODED"

If .applied, create the job as pending, then POST its /result with outcome:"skipped", skipReason:"Already applied (<kind>)", and move on - don't open a tab.

2.2 Score

If the listing row lacks enough detail, read it from the tab-1 snapshot (don't navigate away). Build the digest (../../shared/digest-schema.md), then score server-side:

FIT=$(curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X POST "$JOBPILOT_API/api/score-fit" \
  -H 'content-type: application/json' \
  -d "$(jq -n --argjson digest "$DIGEST" --arg rid "$RESUME_ID" \
    '{digest:$digest} + (if $rid=="" then {} else {resumeId:$rid} end)')")
SCORE=$(echo "$FIT" | jq -r '.score')
CONF=$(echo "$FIT" | jq -r '.confidence')

If CONF >= 0.7 and SCORE is at least 10 points from minMatchScore on either side, use it directly; otherwise rescore using strongMatches/partialMatches/gaps. A thin/generic row is not a skip. When a full posting read is genuinely needed, delegate the row to job-worker mode:"score" ({campaignId:$CAMPAIGN_ID, jobKey:<key>, url, resumeId:$RESUME_ID, minMatchScore:$MIN_SCORE}) instead of opening the posting in this conversation. The worker creates every row non-terminal, then sends ineligible outcomes to /result; eligible rows remain pending. PATCH an eligible row to applying, then go straight to apply (2.3).

curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X PATCH "$JOBPILOT_API/api/campaigns/$CAMPAIGN_ID/jobs/<key>" \
  -H 'content-type: application/json' -d '{"status":"applying"}'

Otherwise (scoreable from the listing/tab-1 snapshot alone): below minMatchScore after a fair read → create as pending, POST /result with outcome:"skipped", skipReason:"Below minimum match score ($SCORE < $MIN_SCORE)", and move on (no tab). Otherwise add it as applying and apply (2.3):

DIGEST=<stringified digest>
curl -fsS -H "authorization: Bearer $JOBPILOT_API_TOKEN" -X POST "$JOBPILOT_API/api/campaigns/$CAMPAIGN_ID/jobs" \
  -H 'content-type: application/json' \
  -d "$(jq -n --arg key "<stable-id>" --arg title "<title>" --arg company "<company>" \
    --arg location "<location>" --arg url "<url>" --arg board "<board>" \
    --arg matchReason "<one line>" --argjson score <0-100> --arg digest "$DIGEST" --arg desc "<posting text, when read>" \
    '{key:$key, title:$title, company:$company, location:$location, url:$url, board:$board, matchS
Compartilhar X LinkedIn