diff --git a/.agents/skills/linkedin-search/SKILL.md b/.agents/skills/linkedin-search/SKILL.md index 2a9b7bd..f533f1f 100644 --- a/.agents/skills/linkedin-search/SKILL.md +++ b/.agents/skills/linkedin-search/SKILL.md @@ -64,7 +64,7 @@ bun run .agents/skills/linkedin-search/cli/src/cli.ts detail [--format `id` is the job ID from `search` results (e.g. `4426311357`). You may also pass a full LinkedIn `jobs/view/...` URL or a `urn:li:jobPosting:...` URN. Returns the full description, -seniority, employment type, job function, industries, and apply link. +seniority, employment type, job function, and industries. ## Usage examples diff --git a/.agents/skills/linkedin-search/cli/src/commands/detail.ts b/.agents/skills/linkedin-search/cli/src/commands/detail.ts index c07a8ed..ed710b8 100644 --- a/.agents/skills/linkedin-search/cli/src/commands/detail.ts +++ b/.agents/skills/linkedin-search/cli/src/commands/detail.ts @@ -43,7 +43,6 @@ export async function runDetail(opts: DetailOpts): Promise { job.description || "(no description)", "", `URL: ${job.url}`, - job.applyUrl ? `Apply: ${job.applyUrl}` : "", ].filter((l) => l !== "") process.stdout.write(lines.join("\n") + "\n") } else { diff --git a/.agents/skills/linkedin-search/cli/src/helpers.ts b/.agents/skills/linkedin-search/cli/src/helpers.ts index 1d6da1f..8b58e1f 100644 --- a/.agents/skills/linkedin-search/cli/src/helpers.ts +++ b/.agents/skills/linkedin-search/cli/src/helpers.ts @@ -63,7 +63,6 @@ export interface JobDetail extends JobCard { employmentType: string | null jobFunction: string | null industries: string | null - applyUrl: string | null } /** @@ -228,9 +227,6 @@ export function parseJobDetail(html: string, id: string): JobDetail { criteria[clean(cm[1]).toLowerCase()] = clean(cm[2]) } - const applyMatch = html.match(/class="topcard__link[^"]*"[^>]*href="([^"]+)"/i) - const applyUrl = applyMatch ? decodeHtmlEntities(applyMatch[1]).split("?")[0] : null - return { id, title: title ? clean(title) : "(untitled)", @@ -244,7 +240,6 @@ export function parseJobDetail(html: string, id: string): JobDetail { employmentType: criteria["employment type"] ?? null, jobFunction: criteria["job function"] ?? null, industries: criteria["industries"] ?? null, - applyUrl, } } diff --git a/.agents/skills/linkedin-search/cli/tests/parsing.test.ts b/.agents/skills/linkedin-search/cli/tests/parsing.test.ts index 4cfd98d..28edb70 100644 --- a/.agents/skills/linkedin-search/cli/tests/parsing.test.ts +++ b/.agents/skills/linkedin-search/cli/tests/parsing.test.ts @@ -86,6 +86,19 @@ describe("decodeHtmlEntities (via parseJobCards)", () => { }); }); +describe("parseJobDetail dropped fields", () => { + test("emits no applyUrl field", () => { + // The extraction regex assumed class-before-href and never matched + // LinkedIn's real markup (null on every live posting), and a fixed + // version would only capture the job-view URL - a duplicate of `url`. + // The field is dropped rather than fixed (review finding F19, + // 2026-08-19). This test pins the removal so it does not quietly + // return as a broken or redundant field. + const job = parseJobDetail("", "1"); + expect("applyUrl" in job).toBe(false); + }); +}); + describe("decodeHtmlEntities (via parseJobDetail)", () => { test("decodes hex entities inside the job title", () => { const html = `

Señor Engineer

`; diff --git a/CHANGELOG.md b/CHANGELOG.md index cab21d0..1daebbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,11 @@ per-file diff commands. ### Changed +- **`linkedin-search detail` drops the `applyUrl` field** - the extraction regex + assumed `class=` before `href=` and never matched LinkedIn's real markup (`null` on + every live posting since the markup ordering differs), and fixing the regex would only + capture the job-view URL, a duplicate of the record's own `url`. The field and the + SKILL.md "apply link" claim are removed; a test pins the removal. - **`jobdanmark-search` search output drops presentation-only keys** - `coverImage`, `companyLogo`, `companyLogoSvgMarkup`, `overlayColor`, and `silhouetteLogo` were ~40% of a live payload (a 30-result response shrinks from ~30k to ~20k chars), fed into