Files
ai-job-search/.agents/skills/linkedin-search/cli
Ayobami Adegoke 1c74a57c5e test(cli): pin the 429/5xx retry contract in all six portal CLIs (#246)
* test(cli): pin the 429/5xx retry contract in all six portal CLIs

The portal-skill contract requires backoff on 429/5xx, and every CLI
implements it - a retry loop with exponential delay and jitter - but
nothing verified the loops actually retry, stop retrying on plain
4xx, or give up after the documented attempt budget. A regression
here is invisible: a CLI that stops retrying still works on every
healthy request.

Each CLI gains tests/retry-backoff.test.ts, network-free, using the
request-timeout.test.ts pattern from #197 (import the fetch wrapper,
stub globalThis.fetch): a stubbed fetch counts attempts, and a
stubbed setTimeout fires immediately so the exhaustion case does not
sleep through the real 500ms -> 5s/8s backoff schedule (tests run in
milliseconds, not ~17s).

Three assertions per fetch wrapper, adapted to each CLI's documented
semantics:

- a 429 is retried and the next attempt's result is returned
- a plain 4xx is not retried (jobbank's fetchWithUA RETURNS the
  response for callers to handle - pinned as such; linkedin's
  htmlFetch returns "" on 404; freehire's apiGet returns null)
- persistent 5xx gives up after the initial attempt plus six
  retries (7 fetch calls) with the status in the error

freehire additionally pins its documented graceful-degradation
contract: a connection failure fails fast with no retry. jobdanmark
exercises both apiFetch and apiPost, which carry separate copies of
the loop that could drift apart.

Mutation-checked: changing maxRetries in jobindex makes the
exhaustion test fail, so the tests distinguish the current behavior
from a silently altered one.

Verified: bun test green in all six CLIs (jobindex 19, jobnet 20,
jobbank 20, jobdanmark 21, linkedin 21, freehire 31 - 0 fail);
tsc --noEmit clean in all six; python3 tools/lint_skills.py OK.

* test(jobindex): pin apiFetch's retry loop alongside htmlFetch's

Review parity gap: jobindex carries two separate copies of the retry
loop and only htmlFetch was exercised, so apiFetch's retry budget
could drift silently - the same situation jobdanmark's test already
handles for its apiFetch/apiPost pair.

apiFetch gets the same three assertions, adapted to its documented
semantics (JSON return on success, throw on plain 4xx): a 429 is
retried and the next attempt's parsed body returned, a 400 is not
retried, persistent 5xx gives up after the initial attempt plus six
retries (7 calls).

Mutation-checked on the new axis: changing apiFetch's maxRetries
(the file's first copy of the loop) fails its exhaustion test while
htmlFetch's tests stay green, so each wrapper is now pinned
independently.

Verified: bun test 30 pass / 0 fail (full jobindex suite);
tsc --noEmit clean.
2026-07-28 20:11:46 +02:00
..

linkedin-cli

CLI for searching jobs on LinkedIn's public job listings, for any country/region (and remote), across any sector.

Data source: LinkedIn jobs-guest endpoints (seeMoreJobPostings/search and jobPosting/<id>). Authentication: None required. Dependencies: None (plain bun + fetch). bun install is optional and only pulls dev type defs.

Personal use only. This uses LinkedIn's public job pages; automated access is against LinkedIn's Terms of Service. Keep volume low, don't use it commercially or for bulk data collection, and run it on your own responsibility.

Installation

cd .agents/skills/linkedin-search/cli
bun install   # optional — only installs TypeScript dev types

The CLI runs without any install because it has zero runtime dependencies.

Commands

Command Description
search Search for job listings (--location required)
detail Fetch full detail for a single job listing

search accepts --format json|table|plain (default json); detail accepts --format json|plain. All errors are written to stderr as { "error": "...", "code": "..." } with exit code 1.

Quick examples

# Software roles in Hyderabad, last 7 days
bun run src/cli.ts search -q "backend engineer" -l "Hyderabad, Telangana, India" --jobage 7 --format table

# Design roles in London
bun run src/cli.ts search -q "product designer" -l "London, United Kingdom" --format table

# Fully remote
bun run src/cli.ts search -q "technical writer" -l "Remote" --remote remote --format table

# Full detail for one job
bun run src/cli.ts detail 4426311357 --format plain

See ../SKILL.md for the full flag reference and the Terms-of-Service note.

Search flags

Flag Alias Description
--location -l Required. Place string, e.g. "Mumbai, Maharashtra, India", "Berlin, Germany", "Remote".
--query -q Keywords (title / skill / role). Recommended.
--jobage Posted within N days: 1, 7, 14, 30.
--remote remote | hybrid | onsite.
--page 1-indexed page (10 results/page).
--limit -n Cap results emitted.
--format json | table | plain.