chore(freehire-search): point at freehire.me (domain migrated from freehire.dev) (#229)

freehire moved its primary domain from freehire.dev to freehire.me. Update the
freehire-search skill's default API base URL, help text, docs, and examples.

Backward-compatible: FREEHIRE_API_URL still overrides the base (self-hosting),
and normalizeSlug is host-agnostic so pasted freehire.dev/jobs/<slug> URLs still
resolve. The GitHub repo link (github.com/strelov1/freehire) is unchanged. All
27 CLI tests pass; the freehire.me API answers 200 for /jobs/search + /jobs/facets.
This commit is contained in:
Ilya Strelov
2026-07-23 10:17:10 +02:00
committed by GitHub
parent 7db231c680
commit 1ae66ad094
8 changed files with 28 additions and 28 deletions
+8 -8
View File
@@ -3,7 +3,7 @@ name: freehire-search
version: 1.0.0
description: >
Use this skill to search live software / tech / data / engineering job listings
across many countries and markets (and remote) via the freehire.dev aggregator's
across many countries and markets (and remote) via the freehire.me aggregator's
public API, or to look up a specific posting. It aggregates roles from ~50 ATS
platforms into one schema, so a single skill covers many markets — but its faceted
filtering (skills, category, seniority) is tuned tech-first, so scope triggers to
@@ -17,7 +17,7 @@ allowed-tools: Bash(bun run .agents/skills/freehire-search/cli/src/cli.ts *)
# freehire Search Skill
Search live job listings from the **[freehire.dev](https://freehire.dev)** job
Search live job listings from the **[freehire.me](https://freehire.me)** job
aggregator — an open-source IT job board that normalizes postings from ~50 ATS
platforms across many countries into one schema. No authentication, no API key,
and **zero runtime dependencies** — it runs with just `bun`. The market is chosen
@@ -40,10 +40,10 @@ coverage exists but is still maturing; don't rely on this skill for general
## ️ Hosted-service dependency (best-effort, no SLA)
This skill depends on a third-party hosted service, freehire.dev. Reads are
This skill depends on a third-party hosted service, freehire.me. Reads are
**public and unauthenticated** — the same zero-signup bar as `linkedin-search`.
**freehire.dev is a personal project but actively maintained; it runs on a
**freehire.me is a personal project but actively maintained; it runs on a
best-effort basis (no formal SLA).** If the API is unreachable, the CLI fails
gracefully — a non-zero exit with a clear error message — so an outage degrades
this source rather than breaking the surrounding workflow.
@@ -52,7 +52,7 @@ this source rather than breaking the surrounding workflow.
MIT-licensed repo — [`strelov1/freehire`](https://github.com/strelov1/freehire)
(Go + PostgreSQL + Meilisearch) — that stands up with one command via Docker
Compose (`make up` → API on `:8080`, same `/api/v1/...` paths). The skill honors a
base-URL env var, `FREEHIRE_API_URL` (default `https://freehire.dev`), so pointing
base-URL env var, `FREEHIRE_API_URL` (default `https://freehire.me`), so pointing
it at a local instance is a one-line change:
```bash
@@ -99,7 +99,7 @@ Facet filters (values come from freehire's controlled vocabularies; comma-separa
> **Location is a facet, not free text.** Unlike `linkedin-search`'s `--location`,
> freehire filters geography through the structured `--region`/`--country`/`--city`
> facets. Discover the live values for a market at
> [`/api/v1/jobs/facets`](https://freehire.dev/api/v1/jobs/facets) (append `?q=<role>`
> [`/api/v1/jobs/facets`](https://freehire.me/api/v1/jobs/facets) (append `?q=<role>`
> to scope it) — never invent facet values.
### Fetch full job detail
@@ -109,7 +109,7 @@ bun run .agents/skills/freehire-search/cli/src/cli.ts detail <slug|url> [--forma
```
`slug` is the `id` from a `search` result (e.g. `golang-zensar-2bxu6dxm`). You may
also pass a full `https://freehire.dev/jobs/<slug>` URL. Returns the full (HTML-stripped)
also pass a full `https://freehire.me/jobs/<slug>` URL. Returns the full (HTML-stripped)
description, skills, region/country, and — when the posting is enriched — seniority,
category, employment type, and salary.
@@ -163,7 +163,7 @@ dictionaries never guess). So:
## Notes
- Data is from freehire.dev's public API — no credentials required. Only per-user
- Data is from freehire.me's public API — no credentials required. Only per-user
tracking (apply/save) needs a key, and this skill deliberately does not touch it:
it is **search + detail only**.
- `id` in search results is the freehire `public_slug` — pass it as-is to `detail`.
+6 -6
View File
@@ -1,13 +1,13 @@
# freehire-cli
CLI for searching the [freehire.dev](https://freehire.dev) job aggregator across
CLI for searching the [freehire.me](https://freehire.me) job aggregator across
**many markets** (tech-focused), via its public JSON API.
**Data source**: freehire.dev REST API (`/api/v1/jobs/search`, `/api/v1/jobs/facets`, `/api/v1/jobs/{slug}`).
**Data source**: freehire.me REST API (`/api/v1/jobs/search`, `/api/v1/jobs/facets`, `/api/v1/jobs/{slug}`).
**Authentication**: None required — reads are public (only tracking mutations need a key, and those are out of scope here).
**Dependencies**: None (plain `bun` + `fetch`). `bun install` is optional and only pulls dev type defs.
> **Hosted-service dependency.** This skill talks to freehire.dev, a personal
> **Hosted-service dependency.** This skill talks to freehire.me, a personal
> project maintained on a **best-effort basis with no formal SLA**. If the API is
> unreachable the CLI exits non-zero with a clear error rather than hanging, so an
> outage degrades gracefully instead of breaking the caller. Point `FREEHIRE_API_URL`
@@ -25,7 +25,7 @@ The CLI runs without any install because it has zero runtime dependencies.
## Self-hosting / base URL
The base URL defaults to `https://freehire.dev` and is overridable with an env var:
The base URL defaults to `https://freehire.me` and is overridable with an env var:
```bash
FREEHIRE_API_URL=http://localhost:8080 bun run src/cli.ts search -q "go"
@@ -83,5 +83,5 @@ See `../SKILL.md` for the full flag reference and the hosted-dependency note.
Facet values come from freehire's controlled vocabularies. Discover the live
values (with counts) for a market at
[`/api/v1/jobs/facets`](https://freehire.dev/api/v1/jobs/facets), or narrow it,
e.g. `https://freehire.dev/api/v1/jobs/facets?q=react`.
[`/api/v1/jobs/facets`](https://freehire.me/api/v1/jobs/facets), or narrow it,
e.g. `https://freehire.me/api/v1/jobs/facets?q=react`.
@@ -1,7 +1,7 @@
{
"name": "freehire-cli",
"version": "1.0.0",
"description": "CLI for searching the freehire.dev job aggregator's public JSON API across many markets (tech-focused) — no authentication, zero runtime dependencies. Base URL is swappable via FREEHIRE_API_URL for self-hosting.",
"description": "CLI for searching the freehire.me job aggregator's public JSON API across many markets (tech-focused) — no authentication, zero runtime dependencies. Base URL is swappable via FREEHIRE_API_URL for self-hosting.",
"type": "module",
"main": "src/cli.ts",
"bin": {
@@ -1,10 +1,10 @@
#!/usr/bin/env bun
// Self-contained CLI for searching the freehire.dev aggregator's public JSON API.
// Self-contained CLI for searching the freehire.me aggregator's public JSON API.
// No external CLI framework and zero runtime dependencies, so it runs anywhere
// `bun` is available with nothing installed beyond the repo clone.
//
// Hosted-service dependency: reads are public (no API key), but they hit
// freehire.dev — a personal project maintained best-effort (no formal SLA). Point
// freehire.me — a personal project maintained best-effort (no formal SLA). Point
// FREEHIRE_API_URL at a self-hosted freehire backend to swap the source.
import { runSearch, type SearchOpts } from "./commands/search.js"
@@ -69,7 +69,7 @@ function commaList(raw: FlagValue): string[] {
.filter(Boolean)
}
const HELP = `freehire-cli — search the freehire.dev job aggregator (many markets, tech-focused)
const HELP = `freehire-cli — search the freehire.me job aggregator (many markets, tech-focused)
USAGE
bun run src/cli.ts search [-q "<keywords>"] [facet flags] [--format json|table|plain]
@@ -82,7 +82,7 @@ SEARCH FLAGS
--limit, -n <n> Results per page (API limit). Default 25.
--format <fmt> json (default) | table | plain.
FACET FILTERS (values from freehire.dev's controlled vocabularies; comma = OR)
FACET FILTERS (values from freehire.me's controlled vocabularies; comma = OR)
--region <codes> Macro-region: global, eu, us, apac, latam, cis, ... e.g. --region eu,us
--country <codes> ISO-3166 alpha-2, e.g. --country DE,GB
--city <names> City name(s), e.g. --city Berlin
@@ -95,7 +95,7 @@ FACET FILTERS (values from freehire.dev's controlled vocabularies; comma = OR)
DETAIL
<slug|url> A freehire public slug (from a search result's id/slug)
or a full https://freehire.dev/jobs/<slug> URL.
or a full https://freehire.me/jobs/<slug> URL.
EXAMPLES
bun run src/cli.ts search -q "backend engineer" --seniority senior --limit 10 --format table
@@ -1,10 +1,10 @@
// Data source: the freehire.dev public REST API (JSON, `{data, meta}` envelope).
// Data source: the freehire.me public REST API (JSON, `{data, meta}` envelope).
// Reads are unauthenticated — no API key, the same bar as linkedin-search — and
// unlike the HTML-scraping portals there is no markup to parse: we fetch JSON and
// reshape it into the portal-skill contract's result fields. The base URL is
// swappable via FREEHIRE_API_URL for self-hosting.
export const DEFAULT_BASE_URL = "https://freehire.dev"
export const DEFAULT_BASE_URL = "https://freehire.me"
/** API base URL: FREEHIRE_API_URL (for a self-hosted instance) or the default. */
export function baseUrl(): string {
@@ -16,7 +16,7 @@ export function writeError(error: string, code: string): void {
process.stderr.write(JSON.stringify({ error, code }) + "\n")
}
const UA = "freehire-search-skill/1.0 (+https://freehire.dev)"
const UA = "freehire-search-skill/1.0 (+https://freehire.me)"
/** The shared API response envelope: {data, meta, error}. */
export interface Envelope<T> {
@@ -93,7 +93,7 @@ describe("normalizeSlug", () => {
expect(normalizeSlug("golang-zensar-2bxu6dxm")).toBe("golang-zensar-2bxu6dxm");
});
test("extracts the slug from a /jobs/<slug> URL", () => {
expect(normalizeSlug("https://freehire.dev/jobs/golang-zensar-2bxu6dxm")).toBe("golang-zensar-2bxu6dxm");
expect(normalizeSlug("https://freehire.me/jobs/golang-zensar-2bxu6dxm")).toBe("golang-zensar-2bxu6dxm");
});
test("rejects a non-slug string", () => {
expect(normalizeSlug("not a slug!")).toBeNull();
@@ -1,8 +1,8 @@
# freehire.dev API reference
# freehire.me API reference
The endpoints, parameters, and response shapes this skill depends on. This is the
file to update if the freehire API changes. Base URL defaults to
`https://freehire.dev` and is overridable via the `FREEHIRE_API_URL` env var.
`https://freehire.me` and is overridable via the `FREEHIRE_API_URL` env var.
## Authentication