agentleFS
CodesWhatCodesWhat/careerrat/.agents/skills/setup-searches/SKILL.md

setup-searches

Skill4 starsChanged 10 days agoMITView on GitHub

Build and maintain source config from candidate targeting through `careerrat searches` — generate a baseline from role buckets, curate enabled sources, add or import individual searches (including pasted board URLs with embedded filters preserved), and tune global filters. Upstream of search-jobs; does not scan.

The file

Show its rules and text

We split this skill into the rules it gives an agent

25 rules. About 0 are common, 9 are distinctive, and 16 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈9

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 16

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Run careerrat doctor and inspect the output.

    line 12
  2. Very specific:

    If candidate profile/targeting config is missing or fails schema, halt.

    line 14
  3. Very specific:

    If candidate targeting exists but role_buckets is empty or absent: halt.

    line 15
  4. Very specific:

    If candidate profile + targeting pass and role_buckets is non-empty: proceed to STEP 1.

    line 16
  5. Distinctive:

    Read candidate targeting config through the shared DB-first accessor and report back the detected values that will drive generation:

    line 20
  6. Distinctive:

    Read candidate profile config through the shared DB-first accessor and report:

    line 26
  7. Very specific:

    If the command emits warnings (empty role_buckets, no aggregator found for domain, missing schema field): surface them verbatim to the user.

    line 42
  8. Distinctive:

    Do not assume HiringCafe is the only importable provider.

    line 87
  9. Very specific:

    Use careerrat searches to get current indices before enabling/disabling by index.

    line 102
  10. Very specific:

    After tuning, run careerrat doctor to confirm the schema validates.

    line 128
  11. Very specific:

    Use careerrat searches to review the current state before finalizing.

    line 130
  12. Distinctive:

    Verify the auto-selected aggregator and board set is appropriate for the candidate's domain:

    line 134
  13. Very specific:

    If the candidate is not in the domain that matches the aggregator (e.g.

    line 136
  14. Distinctive:

    If the user mentions a board or aggregator they always want included: add it now, and apply the gate write-back below.

    line 137
  15. Very specific:

    Read the ## Curated Board Registry table in docs/SOURCES.md.

    line 150
  16. Very specific:

    Verify support with careerrat searches --providers --json.

    line 157
  17. Distinctive:

    Wait for explicit confirmation.

    line 174
  18. Distinctive:

    If the candidate's domain has no matching registry boards beyond the general aggregators (which are already auto-generated), say so …

    line 183
  19. Distinctive:

    If the user stated a new preference during this session that should survive future regenerations:

    line 200
  20. Very specific:

    Do not write board preferences to candidate/targeting.yml or candidate/profile.yml — those files govern targeting and identity, not source …

    line 206
  21. Distinctive:

    When source config is ready, continue the post-onboarding discovery pipeline in this exact order:

    line 233
  22. Very specific:

    Never fabricate sources or filters: Only add entries the user explicitly requested or that --from-targeting derives from real targeting …

    line 270
  23. Very specific:

    Preserve embedded filters: when importing a pasted URL.

    line 271
  24. Very specific:

    Salary floor is compensation.minimum_base: Never read or surface current_base — it is private and must not appear in any output or log.

    line 272
  25. Very specific:

    Domain-general: No board name, aggregator, or provider is hardcoded as canonical in this skill's prose.

    line 274

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

setup-searches

Runs under AGENTS.md. These contracts bind without being restated here: Privacy Invariant (current_base never outbound), Honesty Firewall, Placeholder/Bracket Ban, Gate Write-back, Domain-Neutral Rule, Browser Automation Contract, Activity Pulse logging, Tracker verify+snapshot, and Sent-Clears-Draft. Inline reminders at point-of-use are intentional; standalone restatements point back to the relevant AGENTS.md section. Bare candidate/, workspace/, config/, and .internal/ paths below are symbolic; resolve them per AGENTS.md's Path Resolution rule.

STEP 0 — Preflight

Run careerrat doctor and inspect the output.

  • If candidate profile/targeting config is missing or fails schema, halt. Instruct the user to run ingest-profile (careerrat ingest) first, then return here. In DB workspaces, read this through the shared DB-first config accessor; YAML is legacy fallback only.
  • If candidate targeting exists but role_buckets is empty or absent: halt. The baseline generator will produce only a stub with no role-specific searches — a search catalog with no titles is useless. Tell the user to populate role_buckets through onboarding/ingest-profile before continuing.
  • If candidate profile + targeting pass and role_buckets is non-empty: proceed to STEP 1.

STEP 1 — Read targeting and profile

Read candidate targeting config through the shared DB-first accessor and report back the detected values that will drive generation:

  • role_buckets — names, titles per bucket, priority flags
  • keep_signals, cut_signals — these become title_filter.positive / title_filter.negative; note if cut_signals is empty (hardcoded Intern/Junior fallback will apply per Foundation C)
  • excluded_companies — will be noted but does not affect search-source generation

Read candidate profile config through the shared DB-first accessor and report:

  • candidate.domain — drives board/aggregator selection (e.g. "software engineering" → tech aggregator + job board; other domains → domain-appropriate boards, or no aggregator if none configured). Echo back detected domain so the user can correct before generation.
  • compensation.minimum_base — this is the value used for salary_filter.min. Do not read or surface current_base — it is private and must not appear in any output.
  • location.remote, location.home, location.relocation — these feed location_filter

Echo a summary to the user: detected domain, board catalog that will be used, minimum_base as the salary floor, and number of titles across all role buckets. Ask the user to correct anything before proceeding.

STEP 2 — Generate baseline

careerrat searches --from-targeting

Idempotency: a search is added only when no existing entry matches provider + query (case-insensitive). Existing manual entries are preserved. If the user runs this on an already-populated config, confirm that only new titles will be appended and nothing will be overwritten.

If the command emits warnings (empty role_buckets, no aggregator found for domain, missing schema field): surface them verbatim to the user.

Note the aggregator entry behavior: if profile.candidate.domain maps to a known aggregator, one RSS source is generated for it. If no domain-appropriate aggregator is configured, the generator omits the aggregator entry entirely rather than emitting a nonsensical one. If the user is in a non-tech domain and sees an unexpected tech aggregator entry, flag it in STEP 5.

STEP 3 — Curate sources

Perform any curation the user requested (or all of (a)–(d) on initial setup):

(a) Add keyword searches — one per title or per explicit user request:

careerrat searches --add-query "<title or keyword>" [--label "<l>"]

This is the HiringCafe query builder. To add a deterministic Career Ops adapter, inspect careerrat searches --providers, then use one of these instead:

careerrat searches --add-provider "<provider>" --query "<title or keyword>" [--label "<label>"]
careerrat searches --add-provider "<provider>" --url "<branded board URL>" [--label "<label>"]

Public boards that need no key and work for any field: remotive, remoteok, workingnomads, himalayas, jobicy, arbeitnow, and themuse (paged, so add it by hand with a query). hackernews reads the monthly "Who is hiring" thread and suits tech roles. Keyed sources read a key from the environment (USAJobs is free; Adzuna is paid after 25 trial calls):

careerrat searches --add-provider "usajobs" --query "<title or keyword>" [--location "<place>"]
careerrat searches --add-provider "adzuna" --query "<title or keyword>" --country "<us|gb|ca|au|de|fr|...>" [--location "<place>"]

careerrat searches --from-targeting seeds them only when the key is already set. careerrat searches --providers shows which keys are set and where to sign up. A keyed source with no key fails loudly in the scan summary; it never returns an empty list quietly.

(b) Import a pasted board URL — when the user pastes a URL from any job board:

careerrat searches --add-url "<full URL>" [--label "<label>"]

For hiring.cafe URLs: the embedded searchState and query-string filters are parsed and written into the searchState block verbatim. Do not strip, normalize, or drop query params. For wellfound.com URLs: routed to provider Wellfound, source_type: browser (Wellfound is a SPA — agent must use a browser tool for liveness checks and browsing). For jobs.lever.co or api.lever.co URLs: routed to provider Lever, source_type: ats; the company slug is derived from the URL path automatically. For any URL recognized by the pinned Career Ops registry: routed to its deterministic provider with source_type: ats. For an unrecognized branded host whose adapter is known: use --add-provider <id> --url <url> instead of importing it as a browser source. For other hosts: the entry becomes source_type: browser. A company board URL on a supported ATS (Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee, Workday and the rest of the registry) always becomes a direct ats source, even when a caller asked for a browser source. Browser sources and login-backed sites (LinkedIn, Indeed, Glassdoor, Wellfound) can't be searched from the terminal, so when one of them lists an employer, look for that employer's own ATS board and add it with --add-url or --add-provider <id> --url <board URL> instead.

Do not assume HiringCafe is the only importable provider. Any board URL a user pastes can be imported — label it clearly.

(b.1) Authenticated browser sources (LinkedIn / Indeed / Glassdoor)

When the user pastes a search or results URL from LinkedIn, Indeed, or Glassdoor, --add-url creates a browser source bound to that site. Ask one question in the current conversation: “Do you want to log into LinkedIn so I can use it?” with Yes and No choices. Yes enables that source and opens its exact URL in the visible CareerRat browser. No leaves that source disabled and continues with every other source. Do not send the user to Settings or require a capability, platform, consent, or terms-of-service switch matrix.

CareerRat never stores the password. The browser session holds the login. Stop for CAPTCHA, 2FA, or an unexpected account-selection screen so the user can handle it visibly, then resume the same source.

(c) Enable / disable entries:

careerrat searches --enable <index or label>
careerrat searches --disable <index or label>

Use careerrat searches to get current indices before enabling/disabling by index.

(d) Surface disabled entries. List intentionally disabled sources with careerrat searches. When the user wants one, ask the site-specific Yes/No login question in the current conversation.

STEP 4 — Tune global filters

Review the generated filters and adjust as needed:

  • title_filter.positive — must include all titles from role_buckets. If a bucket title is missing, add it.
  • title_filter.negative — must reflect targeting.cut_signals. If cut_signals is populated, these values override the hardcoded ['Intern', 'Junior'] defaults. If cut_signals is empty, the Intern/Junior defaults apply — confirm with the user this is correct for their seniority posture.
  • salary_filter.min — must be set to compensation.minimum_base from profile.yml. Verify this is present and matches.
  • location_filter.allow / location_filter.block — must cover the candidate's location.remote posture and any excluded regions from profile.yml.
  • recency.mode and recency.safetyMinutes — each search entry carries a recency block. The three valid modes are:

    • since-last-run (default) — uses recency.lastRunAt watermark; set recency.safetyMinutes (default 30) to overlap slightly and avoid missing results at the boundary.
    • fixed-hours — always looks back a fixed window (recency.windowHours); use when a source does not support watermark-based deltas.
    • manual — no automatic date filter; the agent applies no recency constraint (use for sources with their own pagination).

    To tune: use the owning source-config command when available. Do not hand-edit exported source YAML in DB workspaces. If no DB-aware command exists for recency yet, stop and report the helper gap rather than editing config/search-sources.yml. In legacy workspaces only, edit the recency block directly in config/search-sources.yml for the relevant entry. Example for a 7-day lookback on a fixed-hours source:

    recency:
      mode: fixed-hours
      windowHours: 168
      safetyMinutes: 60

    After tuning, run careerrat doctor to confirm the schema validates.

Use careerrat searches to review the current state before finalizing.

STEP 5 — Review aggregator and board catalog

Verify the auto-selected aggregator and board set is appropriate for the candidate's domain:

  • If the candidate is not in the domain that matches the aggregator (e.g. a trucking candidate with a tech RSS feed), disable or replace the aggregator entry via careerrat searches --disable <index> and add domain-appropriate boards via --add-url or --add-provider.
  • If the user mentions a board or aggregator they always want included: add it now, and apply the gate write-back below.

Auto-seeded portals (review these):

  • Wellfound (wellfound.com) — tech-domain candidates only; one entry per primary role title, source_type: browser. If the candidate is not in a tech-adjacent domain, disable these entries.
  • Lever (jobs.lever.co) — domain-neutral ATS; one entry per company in targeting.tracked_companies, source_type: ats. Verify each company slug matches the company's actual Lever subdomain — a wrong slug returns an empty result set (HTTP 200, no postings), not a 404.

STEP 5a — Optional: domain-gated starter menu from the board registry (initial setup only)

On initial setup, offer the user a one-time starter menu from the docs/SOURCES.md Curated Board Registry. This step is optional — skip it if the user declines or if source config already has more than the auto-generated baseline entries.

  1. Read the ## Curated Board Registry table in docs/SOURCES.md.
  2. Filter to rows where Domain tag includes general OR matches profile.candidate.domain (e.g. a candidate with domain "software engineering" sees general + tech/software + tech/AI + remote entries; a candidate with domain "finance" sees only general entries and gets a note that no domain-specific vetted boards exist yet for that domain).
  3. Exclude rows already in the dedup set (any board whose root domain already appears in source config).
  4. Verify support with careerrat searches --providers --json. Exclude any row that is not implemented; never infer support from prose alone.
  5. Present the filtered list as a pick-list:

    Board registry matches for domain "<candidate.domain>":
    
    [1] HiringCafe (general / aggregator / high confidence) — already configured, skipped
    [2] Wellfound (tech/software / aggregator / high confidence) — already configured, skipped
    [3] Remote Vibe Coding Jobs (tech/software, remote / aggregator / high confidence) — already configured, skipped
    [4] Example Niche Board (your domain / niche-board / high confidence) — NEW
    [5] Example Specialist Aggregator (your domain / aggregator / high confidence) — NEW
    [6] Example Field Board (your domain / niche-board / borderline) — NEW (borderline: weak company attribution)
    
    Pick all, a subset by number, or none to skip.
  6. Wait for explicit confirmation. Do not add anything automatically. If the user picks boards, add each confirmed one via:

    careerrat searches --add-url "<url>" --label "<label>"

    For niche boards with no canonical search URL yet, use --add-provider "<provider>" --query "<role family keyword>" when the provider is implemented. Otherwise, note that the source needs an adapter and skip it.

  7. If the candidate's domain has no matching registry boards beyond the general aggregators (which are already auto-generated), say so explicitly and do not prompt for picks.

This is a convenience starter menu — not a mandate. The auto-generated baseline from STEP 2 already includes the field-neutral defaults for the candidate's domain. This step adds domain-specific niche boards the candidate might want.

Known limitation — board-preference persistence: there is no canonical source-preference document yet that makes per-candidate board preferences (e.g. "always use this board", "never use LinkedIn") survive a future --from-targeting regeneration. Comment-only state in exported source YAML is not durable, especially in DB workspaces where the file is generated. Until a DB-backed helper ships, explicitly warn the user after writing any persistent board preference, and prefer encoding the intent as targeting keep/cut signals where possible.

STEP 6 — Gate write-back

If the user stated a new preference during this session that should survive future regenerations:

  • "Never show me LinkedIn" or "exclude provider X" — confirm-first (consequential: affects every future scan). Once confirmed: disable the entry via careerrat searches --disable <index>. Do not add comment-only state to exported source YAML in DB workspaces. Persist only supported source state through careerrat searches; until a DB-backed source-preference helper exists, warn that this preference may need to be re-applied after --from-targeting.
  • "Always use [board]" — confirm-first; add the entry via careerrat searches. Do not add comment-only state to exported source YAML in DB workspaces. Persist only supported source state through careerrat searches; until a DB-backed source-preference helper exists, warn that this preference may need to be re-applied after --from-targeting.
  • Unambiguous, low-blast-radius preferences (one extra board the user just pasted) — write-and-report: add the entry via --add-url or --add-query, then echo Added source: <label> (<provider>).

Do not write board preferences to candidate/targeting.yml or candidate/profile.yml — those files govern targeting and identity, not source mechanics.

STEP 7 — Verify

careerrat searches

Confirm for every entry:

  • provider is set
  • label is human-readable
  • target / query / url / rssUrl is present and non-empty
  • enabled is set intentionally (not accidentally true or false)

Then run:

careerrat doctor

Confirm source config passes schema validation. If the CLI refused to write an invalid config, no errors should appear — but verify explicitly. If the doctor flags errors, fix them before handing off.

URL spot-check: For at least one url-query or rss entry, open the target or rssUrl value in a browser (or use WebFetch) and confirm it returns job results rather than a 404, redirect loop, or empty feed. If an entry resolves to an error page, disable it (careerrat searches --disable <index>) and report to the user before handing off.

Final handoff

When source config is ready, continue the post-onboarding discovery pipeline in this exact order:

setup-searches -> research-boards -> discover-companies -> search-jobs

This skill only produces the baseline source config. It does not discover new boards, discover companies, scan, dedupe, gate, or score. Hand off next to research-boards unless the user explicitly says to skip board discovery. After research-boards, run discover-companies before the first search-jobs sweep so employer ATS boards are wired into tracked-company source config through careerrat companies.

End every run by saying the next agent task plainly: research-boards next, or careerrat next --skip research-boards --write if the user explicitly skipped board discovery.


Intent → Command

User intentCommand
Generate baseline from targetingcareerrat searches --from-targeting
See current searchescareerrat searches
See current searches (JSON)careerrat searches --json — emits { exists: bool, searches: [ { index, provider, label, target, enabled, recency? } ] }
List deterministic providerscareerrat searches --providers
Add a HiringCafe keyword searchcareerrat searches --add-query "<query>" [--label "<label>"]
Add a deterministic provider query or branded URL`careerrat searches --add-provider "<id>" [--query "<query>"--url "<url>"] [--label "<label>"]`
Import a pasted board URLcareerrat searches --add-url "<url>" [--label "<label>"]
Enable a searchcareerrat searches --enable <index or label>
Disable a searchcareerrat searches --disable <index or label>
Health checkcareerrat doctor
Bail to onboardingcareerrat ingest

Rules

  • Never fabricate sources or filters. Only add entries the user explicitly requested or that --from-targeting derives from real targeting data.
  • Preserve embedded filters when importing a pasted URL. The searchState block must be written exactly as parsed from the URL — do not normalize, simplify, or drop query params.
  • Salary floor is compensation.minimum_base. Never read or surface current_base — it is private and must not appear in any output or log.
  • Writes source config through CareerRat helpers; reads candidate config through DB-first accessors. In DB workspaces, config/ and candidate/ files are compatibility exports; never hand-edit them. Never commit candidate files.
  • Domain-general. No board name, aggregator, or provider is hardcoded as canonical in this skill's prose. Board selection follows profile.candidate.domain. The skill must execute correctly for a trucking, nursing, or finance candidate.
  • Do not scan or gate here. search-jobs owns discovery and intake. evaluate-job is the fit gate.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.