mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 16:46:24 +00:00
A general-purpose, field-agnostic job-search skill built on LinkedIn's public jobs-guest endpoints. Works for any market out of the box — location is an explicit required flag (no country default). Zero runtime dependencies (bun only); search + detail commands. Includes a personal-use / Terms-of-Service note (automated access is against LinkedIn's ToS — keep volume low, non-commercial). (Pairs with the .gitignore fix in #21, which lets skills under .agents/ be tracked.) Co-authored-by: Akhil Tripathi <kodabear@Akhils-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
58 lines
1.7 KiB
TypeScript
58 lines
1.7 KiB
TypeScript
import { DETAIL_URL, htmlFetch, parseJobDetail, writeError } from "../helpers.js"
|
|
|
|
export interface DetailOpts {
|
|
id: string
|
|
format: "json" | "plain"
|
|
}
|
|
|
|
/** Accept a raw job ID, a job-view URL, or a job URN. */
|
|
function normalizeId(input: string): string | null {
|
|
const urn = input.match(/urn:li:jobPosting:(\d+)/)
|
|
if (urn) return urn[1]
|
|
const url = input.match(/-(\d{6,})(?:\?|$)/) || input.match(/\/(\d{6,})(?:\?|$)/)
|
|
if (url) return url[1]
|
|
const bare = input.match(/^\d{6,}$/)
|
|
if (bare) return input
|
|
return null
|
|
}
|
|
|
|
export async function runDetail(opts: DetailOpts): Promise<number> {
|
|
const id = normalizeId(opts.id)
|
|
if (!id) {
|
|
writeError(`Could not parse a job ID from "${opts.id}"`, "BAD_ID")
|
|
return 1
|
|
}
|
|
try {
|
|
const html = await htmlFetch(`${DETAIL_URL}/${id}`)
|
|
if (!html) {
|
|
writeError("Job not found", "NOT_FOUND")
|
|
return 1
|
|
}
|
|
const job = parseJobDetail(html, id)
|
|
|
|
if (opts.format === "plain") {
|
|
const lines = [
|
|
job.title,
|
|
`${job.company || "—"} · ${job.location || "—"}`,
|
|
"",
|
|
job.seniority ? `Seniority: ${job.seniority}` : "",
|
|
job.employmentType ? `Employment: ${job.employmentType}` : "",
|
|
job.jobFunction ? `Function: ${job.jobFunction}` : "",
|
|
job.industries ? `Industries: ${job.industries}` : "",
|
|
"",
|
|
job.description || "(no description)",
|
|
"",
|
|
`URL: ${job.url}`,
|
|
job.applyUrl ? `Apply: ${job.applyUrl}` : "",
|
|
].filter((l) => l !== "")
|
|
process.stdout.write(lines.join("\n") + "\n")
|
|
} else {
|
|
process.stdout.write(JSON.stringify(job, null, 2) + "\n")
|
|
}
|
|
return 0
|
|
} catch (e) {
|
|
writeError(e instanceof Error ? e.message : String(e), "DETAIL_FAILED")
|
|
return 1
|
|
}
|
|
}
|