One .zip per skill. Inside: SKILL.md with the intent and the frontmatter, and the code that runs in the page.
Settings → Personalize → Skills. This is Cowork's own skill library, shared across every chat.
Click Add and choose the file you just downloaded. Cowork installs it once; no CLAUDE.md, no per-project setup.
The skill stays available in every session. Cowork runs it in your own Chrome, riding the session you are already signed into.
Search people on LinkedIn, ONE or MANY searches with AUTO-PAGINATION.
{"queries":"array of { keywords, title, company, firstName, lastName, geoUrn, network, page (REQUIRED), pages (default 1, max 100) }"}{"count":"int","results":"array of { query, pagesFetched, count, results:[{ name, degree, slug, profileUrl, headline, location }] }","skipped":"int (only when the 100-fetch cap truncated the run)"}Finds people and returns their slugs, feed each slug straight into linkedin-get-profile to enrich. Resolves in a single bridge call: the extractor fetches the search HTML same-origin and parses it, so no page needs to be opened.
The extractor lives as text in the site's localStorage (key linkedin-search-people) and is rebuilt on each call with synchronous eval. It is stored once and then persists across navigation and browser restarts, so on every run except the first it is already there.
Default action: just call (step 1). The call itself reports NEEDS_STORE when the extractor is missing or out of date, only then do you store it (step 2) and call again.
Versioning. The stored extractor is gated by VER (must match the version in the frontmatter above). Both snippets carry VER = "1", keep them in sync with the frontmatter. When evaluate.js changes, bump version and VER in both snippets: the next call sees the mismatch, returns NEEDS_STORE, and re-stores automatically. Storage stays at two keys (linkedin-search-people and linkedin-search-people:ver), overwritten each store, versions never accumulate.
await (() => {
const KEY = "linkedin-search-people", VER = "1";
const s = localStorage.getItem(KEY);
if (!s || localStorage.getItem(KEY + ":ver") !== VER) return 'NEEDS_STORE';
const fn = eval('(' + s + ')');
return fn(document, { queries: [{ keywords: "growth", title: "SDR", page: 1, pages: 3 }] });
})()
Returns the results JSON. If it returns the string 'NEEDS_STORE', the extractor is missing or stale in this browser → do step 2 once, then run this exact call again.
Run this snippet verbatim. Never add an await before the eval, only the outer await is allowed; a pre-eval await trips LinkedIn's CSP and the call fails.
NEEDS_STORE; persists until localStorage is cleared)While logged in at https://www.linkedin.com/feed/, evaluate the following with the bridge, replacing <FN> with the contents of evaluate.js (the ( async (root, params) => { ... } ) function). Keep VER equal to the call snippet's:
(() => {
const KEY = "linkedin-search-people", VER = "1";
const fn = (<FN>);
localStorage.setItem(KEY, String(fn));
localStorage.setItem(KEY + ":ver", VER);
return 'stored';
})()
Returns "stored". The extractor passes through the model only here, once. Then go back to step 1.
Every field is optional except page. Pass several objects in queries to run several searches in one call.
keywords, free text, matched loosely.title, company, firstName, lastName, exact free text (the URL quotes them).geoUrn, bare id ("104688944") or bracketed. Resolve a city name with linkedin-geo-typeahead.network, degrees: "S" (2nd), "F" (1st), "O" (3rd+); combine as "F,S".page, required, 1-based. pages, how many pages from page (default 1, max 100).Limits (fixed, not configurable): ~10 people per page. Internal pool of 20 concurrent fetches with ~150ms jitter, 20 is the measured clean ceiling; above it LinkedIn silently returns truncated pages with no 429. Hard cap of 100 fetches per call; the excess is reported in skipped.
NEEDS_STORE → extractor missing or version-mismatched in this browser; run step 2 once, then retry step 1.page is required (per query, in results[].error) → that query object had no page.results with a live session → LinkedIn changed the SSR markup; the regex parser needs recapturing.{ "type": "json", "jsonPath": "count" }