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:
@@ -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`.
|
||||
Reference in New Issue
Block a user