mirror of
https://github.com/prdlk/jobyssey.git
synced 2026-11-02 13:12:49 +00:00
feat(claude): add job‑application‑assistant skill and related commands/files
This commit is contained in:
1 parent
f091cc110a
commit
a46f153f22
27 files changed
+4862
No files matched your search
Executable
+269
@@ -0,0 +1,269 @@
|
||||
---
|
||||
name: scrape
|
||||
description: >
|
||||
Finds new job postings matching your profile via installed portal-search CLIs
|
||||
(LinkedIn, local job boards, and any skills added with /add-portal). Deduplicates
|
||||
across runs. Triggers on: job scrape, find jobs, search jobs, new jobs, job search,
|
||||
scrape jobs, /scrape
|
||||
allowed-tools: Read, Write, Edit, Glob, Grep, Bash(bun --version), Bash(bun run .agents/skills/*/cli/src/cli.ts *), WebFetch, WebSearch, Agent, AskUserQuestion
|
||||
---
|
||||
|
||||
# Job Scraper
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
This skill searches job portals using the **installed portal-search CLIs** in
|
||||
`.agents/skills/` (plus WebSearch as a fallback), using queries from your profile.
|
||||
It deduplicates against previously seen jobs and the application tracker, and
|
||||
presents new matches with a quick fit assessment.
|
||||
|
||||
## Invocation
|
||||
|
||||
The user triggers this skill by saying things like:
|
||||
- "Find new jobs"
|
||||
- "Scrape for jobs"
|
||||
- "Any new positions?"
|
||||
- "/scrape"
|
||||
|
||||
Optional arguments:
|
||||
- A focus area, e.g. "/scrape data science" or "/scrape geophysics"
|
||||
- "broad" to run all search categories, e.g. "/scrape broad"
|
||||
- "health" to run the portal health check only (Step 4.75), without searching, deduplicating, or presenting jobs - e.g. "/scrape health", or "/scrape health jobnet" to probe one portal even if disabled
|
||||
|
||||
---
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 0: Load State
|
||||
|
||||
1. Read `job_scraper/seen_jobs.json` (create if missing - start with `{"seen": {}}`)
|
||||
2. Read `job_search_tracker.csv` to extract already-applied companies+roles
|
||||
3. Read `search-queries.md` (this directory) for the search strategy
|
||||
|
||||
### Step 1: Search
|
||||
|
||||
Read `search-queries.md` (this directory) for the search strategy. By default, run the top 3 priority query categories. If the user said "broad", run all categories. If the user specified a focus area (e.g. "data science"), prioritize queries from that category.
|
||||
|
||||
**Use the installed CLI tools as the primary search mechanism.** Fall back to `WebSearch` only for portals that do not have a CLI skill, or if `bun` is unavailable on the system.
|
||||
|
||||
#### 1a. Check bun availability
|
||||
|
||||
```bash
|
||||
bun --version
|
||||
```
|
||||
|
||||
If this fails (bun not installed), skip to **1c (WebSearch fallback)** for all portals and note the fallback in the Step 5 output.
|
||||
|
||||
#### 1b. Run CLI tools (primary — run these in parallel where possible)
|
||||
|
||||
Discover all installed portal CLI skills by reading every `SKILL.md` found under `.agents/skills/*/SKILL.md`. Each file documents that portal's exact CLI flags and usage examples. **Use each portal's own documented interface — do not guess flags.** This approach automatically includes any new portals added via `/add-portal` without requiring changes to this file.
|
||||
|
||||
**Honor the `enabled` toggle.** A portal is enabled unless its `SKILL.md` frontmatter sets `enabled: false` (a missing key means enabled — the default). Skip each disabled portal and record it for the Step 5 summary. A fork can thus keep a portal installed but sit out a run without deleting its directory.
|
||||
|
||||
For each **enabled** portal skill:
|
||||
|
||||
1. Read its `SKILL.md` to find the correct `bun run …` invocation and supported flags.
|
||||
2. Translate the query terms from `search-queries.md` into that portal's flag format (e.g. `--key`, `--search-string`, `--query`, filter codes — whatever the portal's SKILL.md specifies).
|
||||
3. Scope to the last 14 days using the portal's supported recency **filter** flag (`--jobage`, `--since <YYYY-MM-DD>`, etc. — as documented per portal). A portal with **no recency flag** (jobdanmark offers none) still gets scoped: every portal's search output carries a `date` field, so filter client-side — drop results whose `date` is older than 14 days after the call returns, and never invent a flag the portal's SKILL.md does not document (the CLIs reject unknown flags). `--order PublicationDate` is a sort, and a sort is not a filter — pairing it with a `--limit` is a defensible approximation on a portal that offers nothing better (jobnet), but apply the client-side date filter on top all the same.
|
||||
4. Cap results to ~20 per call using the portal's limit flag.
|
||||
5. Use `--format json` for machine-readable output.
|
||||
|
||||
Run all portal CLI calls in parallel where possible using the Agent tool. Collect all `results` arrays into a single pool for Step 2, keeping each result tagged with its source portal skill (for Step 2 `detail` lookups).
|
||||
|
||||
If a CLI tool exits with a non-zero code, log the error message and continue — do not abort the whole search.
|
||||
|
||||
#### 1c. WebSearch fallback
|
||||
|
||||
Use `WebSearch` for:
|
||||
- Portals listed in `search-queries.md` that do **not** have a corresponding directory under `.agents/skills/`
|
||||
- Any portal whose CLI fails at runtime
|
||||
- When bun is unavailable (Step 1a failed)
|
||||
|
||||
Use the site-specific query strings from `search-queries.md` directly as WebSearch queries for these portals.
|
||||
|
||||
Tag each fallback result as WebSearch-sourced, keeping the portal tag when the fallback stands in for an installed portal whose CLI failed. Step 4 persists this as the entry's `source`, and Step 5 reports which portals ran on the fallback this run.
|
||||
|
||||
### Step 2: Fetch & Parse
|
||||
|
||||
For each promising result from Step 1:
|
||||
|
||||
**From CLI results:** Search output already includes title, company, location, date,
|
||||
and URL. For jobs worth a deeper look, fetch full detail with that portal's `detail`
|
||||
command (see its SKILL.md — do not guess flags) to extract **key requirements**,
|
||||
**application deadline**, and a brief description snippet.
|
||||
|
||||
**From WebSearch results:** Use `WebFetch` on the posting URL and extract the same
|
||||
fields manually. If it returns HTTP 403, retry with browser headers via curl per
|
||||
`.claude/skills/job-application-assistant/09-web-research.md` before giving up — most
|
||||
bank and corporate sites reject WebFetch's user agent while serving browsers normally.
|
||||
|
||||
**Store a URL that actually resolves to the posting.** A listing-page URL with a
|
||||
`#fragment` appended (`.../jobs/ciso/#ikerian`) is not a posting: it fetches fine and
|
||||
returns unrelated job titles, which makes every later `/rank` and `/apply` run fail on
|
||||
that entry. When WebSearch only yields a listing page, search the employer's own careers
|
||||
site for the role and store that URL instead, or drop the candidate rather than saving a
|
||||
fragment link.
|
||||
|
||||
For every candidate:
|
||||
- Skip if the URL or company+title combo already exists in `seen_jobs.json`
|
||||
- Skip if the company+role already appears in `job_search_tracker.csv`
|
||||
|
||||
### Step 2.5: Mass-Posting Detection (within this run)
|
||||
|
||||
A distribution pattern worth flagging to the user as a caution signal, not as an accusation against the employer - it describes how a listing is being distributed, not a verdict on whether the company is legitimate. It alone proves nothing is wrong (companies do legitimately hire the same role across several cities); flag it so the user can factor it in when deciding whether to invest time, don't downgrade fit or silently exclude the result because of it.
|
||||
|
||||
If two or more results in this run's pool (from the same company, or sharing the same req/job ID visible in the URL or title) have substantially the same description and differ only in city/location/title, don't present them as separate rows. Consolidate into a single row and note the spread, e.g. "posted identically across 6 cities (BR, MX, GT)".
|
||||
|
||||
### Step 3: Quick Fit Assessment
|
||||
|
||||
For each new job, do a rapid fit check (NOT the full evaluation from `04-job-evaluation.md` - just a quick signal):
|
||||
|
||||
- **High match**: Role directly involves your core skills
|
||||
- **Medium match**: Role is adjacent to your experience
|
||||
- **Low match**: Role requires significant skills you lack
|
||||
|
||||
**Language override:** before assigning a match level, check the posting against `04-job-evaluation.md`'s Language Gate (a required language you haven't declared at all in your CLAUDE.md Languages table). A required language that's entirely undeclared overrides skill fit: mark it **Low** regardless of how well the skills align, and name it in the highlight bullets so it isn't buried under an otherwise-good-looking match. A **declared** language at a requirement that reads higher than your declared level is *not* an override — score fit normally, but add a red-flag bullet under that job's highlights (Step 5) quoting the posting's requirement next to your declared level, so the gap is visible without being auto-downgraded.
|
||||
|
||||
### Step 4: Deduplicate & Store
|
||||
|
||||
1. Add ALL fetched jobs (new and skipped) to `seen_jobs.json` with structure:
|
||||
```json
|
||||
{
|
||||
"seen": {
|
||||
"<url_or_company_title_key>": {
|
||||
"title": "...",
|
||||
"company": "...",
|
||||
"url": "...",
|
||||
"first_seen": "YYYY-MM-DD",
|
||||
"deadline": "YYYY-MM-DD" | null,
|
||||
"fit": "high/medium/low",
|
||||
"status": "new/skipped/ranked/expired",
|
||||
"portal": "<source portal skill, e.g. jobindex-search>",
|
||||
"source": "cli/websearch"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `portal` field records which CLI skill produced the job (results are already tagged per portal in Step 1b - persist that tag here). Entries written before this field existed lack it; the health check (Step 4.75) attributes those by matching the URL's domain against each portal's base URL, so do not backfill.
|
||||
|
||||
The `source` field records which mechanism produced the entry: `cli` for Step 1b portal-CLI output, `websearch` for the Step 1c fallback. This is what keeps a ghost-job report diagnosable after the run's summary is gone: a stored entry whose URL later resolves to nothing (or to a different job) reads very differently depending on whether it came from live CLI output or from a search index that can be weeks stale - and a presented job with no entry here at all points at fabrication, which Rule 1 forbids. Entries written before this field existed lack it; never backfill it - the mechanism was not recorded.
|
||||
|
||||
`/rank` extends this schema additively: ranked entries also carry `rank_score` (0–100 overall score), `rank_verdict` (fit band, e.g. "strong fit"), `rank_date` (ISO date of ranking), the veto fields `location_verdict` and `language_gate` (both PASS/FAIL/FLAG) with `language_note` (the quoted requirement explaining a non-PASS), and `strengths`/`gaps` (1-3 verbatim bullets each, copied from the scoring agent's findings). The `status` field is set to `"ranked"`. Do not drop any of these fields when re-writing entries. Entries ranked before `strengths`/`gaps` existed simply lack them; readers tolerate their absence and never backfill by guessing. Entries ranked before the verdict rename may carry a legacy PASS/FAIL/FLAG string in `location` - read that as the verdict when `location_verdict` is absent; in fresh entries `location` is always a place, never a verdict.
|
||||
|
||||
`deadline` is a base field rather than a `/rank` extension: Step 2's detail fetch already extracts the application deadline, so it is written when the job is first seen and refreshed by `/rank` Step 4 when a scoring agent returns a different value. `null` means the posting states no deadline; a missing key means the entry predates this field - **never infer a deadline** from either, and never backfill by guessing.
|
||||
|
||||
2. Only present jobs NOT already in the seen list or tracker.
|
||||
|
||||
### Step 4.5: Generate Referral Contact Links (High & Medium Fit Only)
|
||||
|
||||
For every job from this run with `fit` of **high** or **medium** (skip low-fit jobs),
|
||||
build two LinkedIn people-search URLs so the user can find a recruiter or team member to
|
||||
reach out to for a referral or a warm intro. This is deliberately a link-generation step,
|
||||
not an automated lookup: no scraping, no third-party API, zero runtime dependencies or
|
||||
credentials required.
|
||||
|
||||
**A. Recruiters / Talent Acquisition (the referral path)**
|
||||
```
|
||||
https://www.linkedin.com/search/results/people/?keywords=<url-encoded "<Company Name> recruiter">&origin=GLOBAL_SEARCH_HEADER
|
||||
```
|
||||
|
||||
**B. Role/team peers (informational-outreach / warm-intro path)**
|
||||
```
|
||||
https://www.linkedin.com/search/results/people/?keywords=<url-encoded "<Company Name> <role keyword>">&origin=GLOBAL_SEARCH_HEADER
|
||||
```
|
||||
Use a short keyword drawn from the posting's title for `<role keyword>` - e.g. a posting
|
||||
titled "AI Program Manager" becomes `"<Company Name> AI Program Manager"`.
|
||||
|
||||
Both links are for the user to open and browse themselves - never fetch or scrape the
|
||||
LinkedIn people-search result pages programmatically. Never fabricate contacts or claim a
|
||||
specific person was found; these are search links, not results.
|
||||
|
||||
### Step 4.75: Portal Health Check
|
||||
|
||||
Scraper-based portal CLIs rot silently: when a portal changes its markup, the parser usually exits 0 with zero results or with null/garbled fields, and the Step 1c fallback never fires because it only triggers on hard failure. This step catches that from evidence the run already holds.
|
||||
|
||||
**Free pass (no extra requests).** For each enabled portal that ran in Step 1b:
|
||||
|
||||
- **Degraded scan:** inspect the results it returned this run. Flags: `company` null or empty on every result, empty titles, undecoded entities (`&`) or HTML fragments in titles, URLs that do not point at the portal. Any of these means the parser is half-working and `/scrape` is silently collecting junk.
|
||||
- **Yield history:** if the portal returned zero results across all of this run's queries, check whether `seen_jobs.json` holds prior entries from it (via the `portal` field, or by matching URL domains for entries predating the field). A portal that produced jobs on earlier runs and produces nothing now is suspect - the same queries worked before.
|
||||
|
||||
**Escalation (bounded, on suspicion only).** A suspect portal gets **one** sentinel probe: run its documented `search` with the example query from its own SKILL.md (that query provably worked when the skill was registered), the portal's limit flag capped at 3, `--format json`. If that returns nothing, retry **once** with a single common word. Only then is the verdict **broken**. A 429 or block page is **never** evidence of breakage - record the portal as **inconclusive (rate-limited)**, back off, and do not retry.
|
||||
|
||||
**Verdicts.** Healthy portals get silence - no table, no line. Anything else surfaces in the Step 5 summary as a health line.
|
||||
|
||||
**Probe-only mode (`/scrape health`).** Skip Steps 1-4 and this step's free pass (there is no fresh run to scan); instead probe every installed portal directly - enabled ones by default, a disabled one only when named explicitly (e.g. `/scrape health jobnet`). Each portal gets the sentinel probe above, the degraded criteria applied to whatever it returns, and - since the user explicitly asked for diagnosis - one `detail` fetch on the first result of each healthy portal (description must be readable decoded text; a failure downgrades to degraded). Report all statuses in this mode, including healthy. Volume stays bounded: one search, at most one retry, at most one detail per portal.
|
||||
|
||||
### Step 5: Present Results
|
||||
|
||||
Present new jobs in a table sorted by fit (high first). When Step 1b skipped
|
||||
portals (`enabled: false`), report them with the `skipped (disabled):` line below
|
||||
so opting one out stays visible rather than silent; omit the line when nothing
|
||||
was skipped. When any portal's results came from the Step 1c fallback this run
|
||||
(bun unavailable, or its CLI failed at runtime), report it with the
|
||||
`fallback (websearch):` line - fallback results come from a search index that
|
||||
can be stale, so the reader should know which rows carry that caveat; omit the
|
||||
line when every portal ran its CLI. When Step 4.75 found a portal degraded, broken, or inconclusive,
|
||||
add one `health:` line per suspect portal (healthy portals get no line); after
|
||||
the report, offer to set that portal's `enabled: false` so `/scrape` stops
|
||||
running it (and covers it via the Step 1c fallback) until it is fixed - only
|
||||
edit the toggle with the user's confirmation, and never edit anything else in
|
||||
the skill.
|
||||
|
||||
```
|
||||
## New Job Matches - YYYY-MM-DD
|
||||
|
||||
Found X new positions (Y high, Z medium, W low match).
|
||||
|
||||
skipped (disabled): <portal-name>, <portal-name>
|
||||
|
||||
fallback (websearch): <portal-name>, <portal-name>
|
||||
|
||||
health: <portal-name> - degraded (company null on all 12 results); parsing anchors in .agents/skills/<portal-name>/url-reference.md
|
||||
health: <portal-name> - broken (0 results for the SKILL.md test query and a broader retry); parsing anchors in .agents/skills/<portal-name>/url-reference.md
|
||||
|
||||
| # | Fit | Title | Company | Location | Deadline | URL |
|
||||
|---|-----|-------|---------|----------|----------|-----|
|
||||
| 1 | High | ... | ... | ... | ... | [Link](...) |
|
||||
|
||||
If Step 2.5 flagged a mass-posting pattern, note it in the Title cell (e.g. "Frontend Developer (posted in 6 cities)") rather than burying it. Do the same for a declared-language-insufficient-level flag from the Language Gate (e.g. "Backend Engineer ⚠ fluent English required") - both are signals the user should see at a glance, not just in the detail highlights below.
|
||||
|
||||
### High-Match Highlights
|
||||
For each high-match job, add 2-3 bullet points:
|
||||
- Why it matches your profile
|
||||
- Key requirements to check
|
||||
- Any red flags (including mass-posting signals from Step 2.5)
|
||||
|
||||
### Contacts
|
||||
For each high/medium-fit job from Step 4.5, add a short contacts block with the two
|
||||
LinkedIn search links:
|
||||
- Recruiters/TA search link, for the referral path
|
||||
- Role/team-peer search link, for the warm-intro / informational-outreach path
|
||||
```
|
||||
|
||||
After presenting, ask:
|
||||
> "Want me to evaluate any of these in detail? Just give me the number(s)."
|
||||
|
||||
If the user picks a number, invoke the **job-application-assistant** skill workflow (fit evaluation first, then CV + cover letter if approved).
|
||||
|
||||
If the run found many new jobs (roughly 8+), also suggest `/rank` - it batch-scores all new postings against the full fit framework and returns a ranked shortlist, which beats eyeballing a long table. (`/rank` sets the `ranked` and `expired` status values in `seen_jobs.json`; treat both as already-seen for dedup purposes.)
|
||||
|
||||
### Step 6: Update Tracker (Optional)
|
||||
|
||||
If the user decides to apply to any job, the tracker row is written by **job-application-assistant Step 3b**, which Step 5 already routes into - do not add a second row here. Only when the user says they applied to something outside that path, add a row using the header and the match-then-update rule in `/outcome` Step 1.
|
||||
|
||||
---
|
||||
|
||||
## Important Rules
|
||||
|
||||
1. **Never fabricate job postings.** Only present jobs from actual CLI search/detail output or WebSearch/WebFetch results.
|
||||
2. **Respect deduplication.** Always check seen_jobs.json AND job_search_tracker.csv before presenting.
|
||||
3. **Focus on configured geographic area.** Skip jobs that require relocation or are clearly outside commute range.
|
||||
4. **Only open positions.** Skip postings with expired deadlines or those marked as closed.
|
||||
5. **Be efficient with detail fetches.** Don't run `detail` or WebFetch on every search hit — pre-filter by title/snippet, then fetch only promising matches.
|
||||
6. **Parallel searches.** Run portal CLI searches in parallel; use WebSearch only for gaps the CLIs don't cover.
|
||||
7. **No automated people lookups.** Referral contacts (Step 4.5) are LinkedIn search links only - never fetch or scrape LinkedIn people-search result pages programmatically.
|
||||
8. **Health checks are bounded and honest.** Step 4.75 spends at most one probe, one retry, and (in `health` mode) one detail fetch per portal - a diagnosis, not a crawl. A rate-limit is never evidence of breakage. Health verdicts come only from observed CLI output; a portal that could not be tested is reported as inconclusive, never guessed. The `enabled` toggle is the only thing the health check may edit, and only with confirmation.
|
||||
9. **Flag distribution patterns, never accuse.** The mass-posting signal (Step 2.5) describes how a listing is being distributed, not a claim that the employer is a scam. Never name a company as fraudulent or untrustworthy - present the observation and let the user decide.
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
# Search Queries for Job Scraper
|
||||
|
||||
<!-- Generated by /setup on 2026-08-20 for Prad Nukala. Re-run `/setup --section search` to change. -->
|
||||
|
||||
## Installed portal CLIs (primary for `/scrape`)
|
||||
|
||||
`/scrape` discovers every portal skill under `.agents/skills/*/SKILL.md` and runs its CLI first. Enabled here:
|
||||
|
||||
- **`linkedin-search`** (live, public listings)
|
||||
- **`freehire-search`** (live, ATS aggregator, tech-focused; use `--region us` / `--country US`)
|
||||
- **`wellfound-search`** (live, Wellfound startup listings by role + city or remote; page 1 only, so vary role wording: `senior software engineer`, `backend engineer`, `founding engineer`, `blockchain engineer`; results carry compensation ranges, useful for the salary floor)
|
||||
- **`jobsdb-search`** (local snapshot `documents/postings/jobs.db`, ~87k postings, crypto/fintech/AI-heavy, NYC/SF-dense). **Static snapshot**: recency is relative to `meta.snapshot`, not today, and every hit must be URL-verified live before it is shown. See its SKILL.md.
|
||||
|
||||
Danish demo portals (jobindex, jobbank, jobdanmark, jobnet) stay `enabled: false`.
|
||||
|
||||
The `site:` query templates in this file are the **WebSearch fallback** for portals without a CLI, company career pages, or when a CLI fails.
|
||||
|
||||
**Language scope:** all queries in English. The candidate's other declared language (Telugu) has no job-market relevance for these roles; do not generate Telugu queries. A posting that requires a language other than English as a job condition fails the Language Gate in `04-job-evaluation.md`.
|
||||
|
||||
## Search Sites
|
||||
|
||||
Primary:
|
||||
- **linkedin.com/jobs** (filter: United States / New York / San Francisco / Remote); also covered by `linkedin-search` CLI
|
||||
- **freehire.me**, covered by `freehire-search` CLI
|
||||
- **documents/postings/jobs.db**, covered by `jobsdb-search` CLI
|
||||
- **wellfound.com**, covered by `wellfound-search` CLI (`site:wellfound.com` as WebSearch fallback)
|
||||
- **web3.career**, **cryptocurrencyjobs.co** (WebSearch `site:` fallback; scaffold a CLI with `/add-portal` if they prove productive)
|
||||
|
||||
Secondary (company career pages via Google): `site:` searches against the target companies below.
|
||||
|
||||
## Target Companies
|
||||
|
||||
Monitor directly (career pages, plus `jobsdb-search --company`): Circle, Ellipsis Labs, Mural Pay, Gemini, Coinbase, Privy, Turnkey, Fireblocks, Dynamic, Alchemy, Anchorage Digital, Polymarket, Block, Anthropic. The `jobsdb-search --tier 1` list is the broader target set.
|
||||
|
||||
## Query Categories
|
||||
|
||||
Queries are grouped by priority and **organized by function, not title**. By default `/scrape` runs Priorities 1-3. Combine each query with location terms (`New York`, `San Francisco`, `Remote`, `United States`) where the portal supports it.
|
||||
|
||||
Portal flag translation:
|
||||
- `linkedin-search`: `--query "<title>" --location "New York, United States"` (and `"San Francisco Bay Area"`, `--remote remote`), `--jobage 14`
|
||||
- `freehire-search`: `-q "<keyword>" --country US --seniority senior,staff --category backend` (or `--remote remote`), `--jobage 14`
|
||||
- `wellfound-search`: `-q "<role title>" --location new-york` / `"San Francisco"` / `--remote`, `--jobage 14 --no-description`
|
||||
- `jobsdb-search`: `-q "<keyword>" --seniority senior,staff_principal --location "New York"` / `"San Francisco"` / `--remote remote,hybrid`, `--jobage 30`; add `--crypto-native` or `--sector crypto,fintech-nc` for Priority 2
|
||||
|
||||
### Priority 1: Senior Backend & Protocol Engineering (Staff titles are a stretch, keep but deprioritize)
|
||||
|
||||
The strongest and most desired direction: owning backend systems, protocols, and infrastructure in Go/TypeScript at crypto-native, fintech, or AI-infrastructure companies.
|
||||
|
||||
Titles: Staff Software Engineer, Senior Backend Engineer, Senior Software Engineer (Go), Protocol Engineer, Blockchain Engineer, Staff Backend Engineer, Senior Platform Engineer, Senior Infrastructure Engineer
|
||||
|
||||
```
|
||||
site:linkedin.com/jobs "staff software engineer" Go New York
|
||||
site:linkedin.com/jobs "senior backend engineer" Go OR Golang New York OR "San Francisco" OR remote
|
||||
site:linkedin.com/jobs "protocol engineer" United States
|
||||
site:linkedin.com/jobs "blockchain engineer" Cosmos OR IBC OR EVM New York OR remote
|
||||
site:web3.career "backend engineer" Go
|
||||
site:cryptocurrencyjobs.co "protocol engineer"
|
||||
```
|
||||
|
||||
### Priority 2: Founding Engineer at Early-Stage Startups
|
||||
|
||||
Zero-to-one roles where architecture, delivery, and reliability across the stack is the job. Equity-heavy, small team, seed to Series A.
|
||||
|
||||
Titles: Founding Engineer, Founding Backend Engineer, Founding Full-Stack Engineer, First Engineer, Technical Co-Founder, Head of Engineering (seed stage)
|
||||
|
||||
```
|
||||
site:linkedin.com/jobs "founding engineer" New York OR "San Francisco" OR remote
|
||||
site:wellfound.com "founding engineer" backend OR fullstack
|
||||
site:linkedin.com/jobs "founding backend engineer"
|
||||
site:linkedin.com/jobs "first engineer" OR "engineer #1" startup
|
||||
site:ycombinator.com/companies/jobs "founding engineer"
|
||||
```
|
||||
|
||||
### Priority 3: Applied Cryptography, Wallet & Identity Infrastructure
|
||||
|
||||
Domain-expertise roles: MPC/threshold signatures, passkeys/WebAuthn, key management, wallet-as-a-service, decentralized identity.
|
||||
|
||||
Titles: Cryptography Engineer, Applied Cryptographer, Security Engineer (Cryptography), Wallet Infrastructure Engineer, Key Management Engineer, Identity Engineer
|
||||
|
||||
```
|
||||
site:linkedin.com/jobs "cryptography engineer" United States
|
||||
site:linkedin.com/jobs "applied cryptography" MPC OR "threshold signatures"
|
||||
site:linkedin.com/jobs WebAuthn OR passkeys engineer
|
||||
site:linkedin.com/jobs "wallet infrastructure" engineer
|
||||
site:cryptocurrencyjobs.co cryptography
|
||||
```
|
||||
|
||||
### Priority 4: Adjacent Roles (wider net)
|
||||
|
||||
Roles the profile supports but that are not the primary target: mobile, devrel, solutions engineering, small-team engineering management.
|
||||
|
||||
Titles: Senior React Native Engineer, Senior Mobile Engineer, Developer Relations Engineer, Developer Experience Engineer, Solutions Engineer (wallet/custody), Engineering Manager (protocol/platform), Technical Lead
|
||||
|
||||
```
|
||||
site:linkedin.com/jobs "react native" senior engineer New York OR remote
|
||||
site:linkedin.com/jobs "developer relations engineer" blockchain OR infrastructure
|
||||
site:linkedin.com/jobs "solutions engineer" wallet OR custody OR passkeys
|
||||
site:linkedin.com/jobs "engineering manager" protocol OR blockchain New York
|
||||
```
|
||||
|
||||
## Location Filter
|
||||
|
||||
- **Ideal:** New York City (any borough; onsite, hybrid, or remote); San Francisco / Bay Area onsite; fully remote (US)
|
||||
- **Acceptable:** any US city with relocation (candidate is open to relocation); remote-global
|
||||
- **Borderline:** onsite outside NYC/SF with no relocation support; hybrid requiring more than occasional travel to a non-NYC/SF office
|
||||
- **Too far:** onsite outside the US; roles requiring non-US work authorization
|
||||
|
||||
Candidate is a US citizen; no visa constraints. Available from **mid-to-late September 2026**.
|
||||
|
||||
## Hard Filters (beyond location)
|
||||
|
||||
- **AI tooling must be allowed.** Drop or flag any posting that prohibits AI-assisted development (Claude Code, Copilot, etc.); this is a deal-breaker, not a preference.
|
||||
- Target senior / founding-level roles; skip junior, mid, and intern postings unless the title is "Founding Engineer". Staff/Principal postings are kept but flagged as a level stretch (no Staff IC experience).
|
||||
|
||||
## Language Filter
|
||||
|
||||
Working languages are in CLAUDE.md's Languages table (English native, Telugu). Apply `04-job-evaluation.md`'s Language Gate: a posting requiring a language not declared is excluded; a higher-than-declared level in a declared language is flagged, not excluded.
|
||||
|
||||
## Date Filter
|
||||
|
||||
Live portals: only jobs posted within the last 14 days, or with an open deadline. `jobsdb-search`: within 30 days of the row's snapshot date, and URL-verified live. If a posting date cannot be determined, include it but flag as "date unknown".
|
||||
|
||||
## Adapting Queries
|
||||
|
||||
If the user specifies a focus area, select queries from the matching category and also generate 2-3 custom queries for that focus. For example:
|
||||
- `/scrape founding` -> Priority 2 queries + `jobsdb-search -q "founding" --tier 2`
|
||||
- `/scrape crypto` -> Priority 3 + `jobsdb-search --crypto-native --seniority senior,staff_principal`
|
||||
- `/scrape anthropic` -> `jobsdb-search --company Anthropic` + `site:job-boards.greenhouse.io/anthropic`
|
||||
Reference in new issue
Block a user