Add content-level language filter, recheck command, expanded keywords

- isEnglishContent() fetches each job page and drops descriptions with ≥6 German or French indicator words; called for every new job in index.ts before writing to CSV
- New `bun recheck` command (recheck.ts) runs searches in raw mode, lists filtered-out jobs, and prints word-frequency suggestions for SOFTWARE_TITLE_RE
- Expanded KEYWORDS and SOFTWARE_TITLE_RE to cover ML/AI, system/applications engineer, director, informatics, and more title variants
- Added `raw` flag to fetchJobs() to bypass the software-role filter
- Add CLAUDE.md with project workflow docs; add CV files and city temp scripts
This commit is contained in:
2026-05-12 13:42:27 +02:00
parent ffe2b4467a
commit bcb6ae5ea3
19 changed files with 1201 additions and 14 deletions
+92
View File
@@ -0,0 +1,92 @@
# 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`.