| name | job-crawler |
| description | **Job Posting Crawler & Matcher**: Crawls a company's career page to find open positions, then ranks the top 3 roles the user is most qualified for based on their resumes. Use this skill whenever the user shares a company careers URL and wants to find the best-fit roles, or mentions "crawl jobs", "scan careers page", "find roles at [company]", "what positions are open", "best fit roles", or "top jobs for me". Also trigger when the user pastes a careers/jobs URL and asks which roles to apply for, or says something like "check out what [company] has open" or "anything good at [company]?". Trigger even for casual mentions like "palantir careers" or "jobs at stripe".
|
Job Posting Crawler & Matcher
You help the user find the best-fit engineering roles at a specific company by crawling their career page, filtering for relevant positions, and ranking the top 3 based on their resume and experience.
First-Run Setup
Before starting the workflow, check if config.yaml exists in the parent directory (one level up from this skill folder). Read the user, resume, skills, and preferences sections.
If config.yaml does NOT exist or the resume.google_doc_id field is empty:
- Tell the user: "This is your first time using job-crawler. I need to set up your profile โ this only happens once."
- Ask for the required fields using AskUserQuestion:
- Google Doc ID for their resume
- A 2-3 sentence profile summary (title, years, key skills)
- Their transferable skill domains (list 5-10)
- Experience level and years
- Primary specialty (e.g., "backend", "iOS", "fullstack")
- Any hard filters (e.g., "US only", "no defense roles")
- Write the responses to
config.yaml in the parent directory
- Confirm setup is complete, then proceed
If config.yaml exists and has the required fields:
Read it silently and proceed.
Source Documents
Fetch the user's resume from Google Drive at the start of every run. Read the document ID from config.yaml โ resume.google_doc_id.
Also read these fields from config to guide matching:
resume.profile_summary โ quick reference for the user's background
resume.variants โ which resume variants exist
skills.domains โ transferable skill domains for matching
preferences.level โ target experience level
preferences.years_experience โ years of experience
preferences.primary_specialty โ primary platform/domain
preferences.secondary_specialties โ acceptable secondary matches
preferences.us_only โ location filter
preferences.exclude_keywords โ keywords that disqualify roles
Hard Filters
Apply these before any ranking. Read them from config.yaml โ preferences:
-
Location filter. If us_only is true, exclude roles exclusively based outside the US. If a role has both US and non-US locations, include it but only note the US locations. Remote roles are fine as long as they're eligible for the user's location.
-
Keyword exclusions. Check each role against preferences.exclude_keywords. Exclude roles whose title or description contains these terms as hard requirements. If a term appears as "nice to have" or "preferred," include the role but flag it clearly.
Workflow
Step 1: Identify the ATS Platform
The user provides a careers page URL. Detect which Applicant Tracking System (ATS) the company uses, because each has a public JSON API that's far more reliable than scraping HTML.
Known platforms and their API patterns:
| Platform | Career page URL pattern | API endpoint |
|---|
| Lever | jobs.lever.co/{company} or company site embedding Lever | https://api.lever.co/v0/postings/{company}?mode=json |
| Greenhouse | boards.greenhouse.io/{company} or company site embedding Greenhouse | https://api.greenhouse.io/v1/boards/{company}/jobs?content=true |
| Ashby | jobs.ashbyhq.com/{company} | https://api.ashbyhq.com/posting-api/job-board/{company}?includeCompensation=true |
| Recruitee | {company}.recruitee.com | https://{company}.recruitee.com/api/offers |
| Workable | apply.workable.com/{company} | https://apply.workable.com/api/v1/widget/accounts/{company} |
How to detect the platform:
- Look at the URL the user provided. If it matches a known pattern above, use that API directly.
- If it's the company's own domain (e.g.,
acme.com/careers), fetch the page using browser tools or WebFetch and look for clues in the HTML: embedded iframes pointing to lever.co, greenhouse.io, ashbyhq.com, etc., or script tags / API calls to these domains.
- Extract the
{company} slug from the detected platform URL.
If the platform is unknown or detection fails, fall back to using browser tools (read_page, get_page_text) to read the careers page directly and extract job listings from the rendered content.
Step 2: Fetch and Filter Job Listings
Once you've identified the platform, hit the appropriate API endpoint using WebFetch.
Important: Narrow early, but not too narrowly. The goal is to focus on relevant roles without missing good transferable-skill matches.
-
Filter by category/team first. Most APIs return a team, department, or categories field. Look for values related to the user's domain โ Engineering, Software, Technology, Product Development, Platform, Infrastructure, etc.
-
Apply hard filters. Remove roles that fail the location or keyword exclusion filters from config.
-
Cast a wide net on titles, then narrow by reading descriptions. This is the most important step. Don't just match on obvious title keywords โ many of the best-fit roles have titles that don't obviously match. Instead, use the user's skills.domains and preferences.primary_specialty to guide a tiered approach:
Tier 1 โ Direct matches (always include):
Roles whose titles directly match the user's primary specialty. These should almost always rank highest.
Tier 2 โ Transferable-skill matches (include and read descriptions):
Roles in adjacent areas covered by the user's skill domains. Read the actual job description to determine fit โ titles are misleading.
Tier 3 โ Only if nothing better exists:
Roles further from core strengths. Include these only when Tiers 1-2 are sparse.
The key insight: a role title might not match the user's specialty, but the actual work might align perfectly with their skill domains. Read the actual job description for Tier 2 roles before deciding to include or exclude them.
-
Deduplicate similar roles. Many companies post the same role at multiple levels or locations. Treat the same role in different locations as ONE entry. List it once and note all available locations. Same applies to different levels โ pick the level closest to the user's from config. Your top 3 must be 3 distinct roles.
After filtering, you should be working with a manageable set (ideally 8-15 roles).
Step 3: Extract Job Details
For each candidate role from Step 2, extract enough detail to assess fit:
- Lever:
descriptionPlain, lists (contains requirements, responsibilities, etc.)
- Greenhouse:
content field (HTML โ extract text), plus location, metadata
- Ashby:
descriptionHtml or descriptionPlain, compensationTierSummary
If the API gives you full descriptions, use them directly. If it only gives titles and links, fetch the top 5-8 most promising individual posting pages.
From each description, extract:
- Title and level
- Location / remote policy
- Key responsibilities (summarized)
- Must-have requirements
- Nice-to-have requirements
- Tech stack mentioned
- Compensation (if available)
- Any flagged keyword mentions (from
preferences.exclude_keywords)
Step 4: Rank and Select Top 3
Compare each role against the user's resume. For each candidate role, compute a Match Score (0-100) based on these weighted factors:
| Factor | Weight | What to assess |
|---|
| Core skill overlap | 35% | How many of the role's required skills does the user have? Count direct matches and transferable matches from skills.domains. Score: % of requirements met. |
| Experience level fit | 20% | Compare the role's level to the user's from config. Exact match โ 100%. One level above โ 60%. One level below โ 50%. Two+ levels off โ 20%. |
| Domain relevance | 20% | How closely does the role's domain match the user's experience? Same domain โ 100%. Adjacent domain โ 70%. Unrelated โ 30%. |
| Requirement gap severity | 15% | 0 must-have gaps โ 100%. 1 minor gap โ 80%. 1 major gap โ 50%. 2+ major gaps โ 20%. A "major gap" is a core technology the role centers on that the user has no experience with. |
| Growth opportunity | 10% | Does the role expand the user's skill set while leveraging strengths? New domain + familiar skills โ 100%. Same domain + same skills โ 50%. Entirely new everything โ 30%. |
Present the match score for each role so the user can quickly gauge fit. Be honest โ a 45/100 match is useful information. It means "this is the best available but it's a stretch."
Select the top 3 by match score. If there aren't 3 strong matches, include the closest available and be transparent about the gaps.
Step 5: Present Results
Output a ranked list in the chat. For each of the top 3 roles:
[Rank]. [Job Title] โ [Location / Remote] โ Match: [X/100]
- Link: [posting URL]
- Match breakdown: One line per factor showing the sub-score and why.
- Why it fits: 2-3 sentences connecting the role requirements to specific things from the user's resume. Reference concrete achievements where possible.
- Key gaps: Anything the role asks for that the user doesn't have. Be specific and honest.
- Which resume: Recommend which resume variant to use (from
resume.variants) and why.
- Compensation: Include if available from the listing.
- Flagged concerns: Any keyword matches from exclusion filters that appeared as "preferred" rather than required.
After the top 3, include a brief "Also considered" section listing other roles that were close but didn't make the cut, with one-line reasons and their match scores.
If no roles are a good fit, say so clearly. "There's nothing great here right now โ the best I found is X at 35/100" is a valid and useful output.
Important Notes
- Match on transferable skills, not just title keywords. Always read job descriptions before judging fit โ titles are misleading.
- Speed over perfection. The user wants a quick scan, not a dissertation. Keep the output tight and scannable.
- Narrow early, go deep on finalists. Don't fetch and analyze 50 job descriptions. Filter first, then only do full analysis for the top ~5.
- Honesty matters. If the user is not a fit for any roles at this company, say so.
- Deduplicate aggressively. Same role at different locations = one entry.
- Level matters. Use the user's level from config to flag roles that are a stretch up or potentially underleveled.
- Primary specialty first. If
preferences.primary_specialty is set and a company has roles matching both primary and secondary specialties, the primary should always rank higher.