# linkme — Claude skill This project scrapes LinkedIn jobs and filters/ranks them for the user's job search. The user's CV is at `polo.md`. Always use it when ranking opportunities. --- ## Commands | command | what it does | |---|---| | `bun fetch` | scrape Basel area (3 searches), append new jobs to `jobs.csv` | | `bun recheck` | fetch in raw mode, list filtered-out jobs + word-frequency keyword suggestions | --- ## Searching any city temporarily Write a throwaway script (e.g. `bern-temp.ts`) — never touch `config.ts` unless the user explicitly asks to save the change. ```ts import { KEYWORDS } from "./config.ts"; import { fetchJobs } from "./fetch.ts"; import type { SearchConfig } from "./config.ts"; const searches: SearchConfig[] = [ { label: "City Name", keywords: KEYWORDS, location: "City, Switzerland", workType: ["onsite", "hybrid"], timePosted: "month" }, ]; // collect, deduplicate, print ``` Use `location: "City, Switzerland"` (no geoId) when the geoId is unknown — LinkedIn resolves by name. Known Swiss geoIds: - Basel city: `103383283` - Basel Metropolitan Area: `90009885` - Basel-Country: `107117530` --- ## Filtering job descriptions by language Use the `bern-langcheck.ts` pattern: fetch each job page, extract the description, count German/French indicator words, drop anything with ≥ 6 matches. ```ts const DE_CONTENT_RE = /\b(Sie|Ihre?|Ihnen|Wir|wir|und\s|für|mit\s|von\s|bei\s|das\s|ist\s|sind\s|haben\s|werden\s|können|suchen|bieten|Kenntnisse|Erfahrung|Anforderungen|Aufgaben|Stellenbeschreibung|Stellenangebot|Bewerbung|Ausbildung|Berufserfahrung|Teamarbeit|Quereinsteiger|Vollzeit|Teilzeit|unbefristet|Abschluss|gesucht|Deutsch|Englisch|Schweizer)\b/g; const FR_CONTENT_RE = /\b(vous|nous|votre|notre|pour\s|avec\s|profil|poste|emploi|entreprise|compétences|expérience|formation|équipe|rejoindre|recherchons|offrons|missions|candidature|CDI|CDD|temps\s+plein|temps\s+partiel)\b/g; ``` Extract description from `class="show-more-less-html__markup"`, fall back to stripping all HTML. Add 350 ms delay between requests. Title-only filtering runs during scraping via `NON_ENGLISH_RE` in `fetch.ts`. Content-level filtering via `isEnglishContent()` is now always applied in `index.ts` before writing to CSV — every new job has its description page fetched and checked. --- ## Ranking jobs against the CV Read `polo.md` for the full profile. Key signals to match against: - **Seniority**: Senior / Staff / Principal / Lead / Architect — weight titles accordingly - **Core stack**: Python · TypeScript · Ruby on Rails · React · Node.js - **Domain strengths**: platform engineering · data pipelines · AI/LLM integrations · DevOps (Docker, Kubernetes, CI/CD, AWS) - **Background edges**: fintech/CBDC (Moedax), civic tech (Tactical Tech / GFOSS), independent consulting (ExtraPolo, 40+ projects) - **Languages**: English (full) · German B1 TELC certified · Spanish (full) — flag roles that require C1+ German - **Location**: based in Basel, open to Basel / Bern / Zürich; EU citizen, no permit needed --- ## Output format Always present jobs as a markdown table with columns: `#`, `Title`, `Company`, `Why it fits`, `Link`. Links use just the job ID as the anchor text: ``` [4392954199](https://www.linkedin.com/jobs/view/4392954199/) ``` --- ## Workflow for "find jobs in X city" 1. Write a temp script for that city, run it, collect all passing jobs. 2. If the user says "English only" or "filter language": run the content-language check script against the results. 3. Rank survivors against `polo.md`, present top N as a table. 4. Delete the temp scripts after the session unless the user asks to keep them. ## Workflow for "check if new keywords should be added" 1. Run `bun recheck`. 2. Review the filtered-out list manually — identify false negatives (real software roles that got dropped). 3. Check the word-frequency suggestions but ignore generic terms (`engineer`, `development`, `senior`). 4. Propose specific additions to `SOFTWARE_TITLE_RE` in `fetch.ts` or words to `NON_ENGLISH_RE`.