From fc83b3bd5306d2b9edb07686710b35e41b11bff3 Mon Sep 17 00:00:00 2001 From: Rudy Mizrahi Celekli <47457359+rudycelekli@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:22:32 -0400 Subject: [PATCH] fix(freehire-search): reject --description-format on detail instead of ignoring it (#648) * fix(freehire-search): reject --description-format on detail instead of ignoring it The flag was on detail's known-flag list, but runDetail never reads it and always prints the detail endpoint's HTML body stripped to plain text, so detail --description-format markdown passed the unknown-flag guard, exited 0 and printed text. Drop it from detail's list so it fails with UNKNOWN_FLAG before any request; search keeps the flag. Co-Authored-By: Claude Opus 5.5 * changelog: place the #648 entry under [Unreleased] Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 Co-authored-by: Mads Lorentzen --- .agents/skills/freehire-search/cli/src/cli.ts | 6 +- .../cli/tests/detail-flags.test.ts | 72 +++++++++++++++++++ CHANGELOG.md | 8 +++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 .agents/skills/freehire-search/cli/tests/detail-flags.test.ts diff --git a/.agents/skills/freehire-search/cli/src/cli.ts b/.agents/skills/freehire-search/cli/src/cli.ts index 9c01ee74..83f1be8e 100644 --- a/.agents/skills/freehire-search/cli/src/cli.ts +++ b/.agents/skills/freehire-search/cli/src/cli.ts @@ -131,13 +131,15 @@ function parseIntFlag(name: string, raw: string | boolean | string[]): number | // Long-form flag names each command accepts (parseFlags resolves the short // aliases q/n to these before validation). "help"/"h" pass so `search --help` -// still prints usage. +// still prints usage. detail takes no --description-format: it always prints +// the detail endpoint's HTML body stripped to text, so accepting the flag +// there ignored it silently. const KNOWN_FLAGS: Record> = { search: new Set([ "query", "category", "city", "company", "country", "facet", "format", "jobage", "limit", "page", "region", "remote", "seniority", "skill", "description-format", "no-description", "help", "h", ]), - detail: new Set(["format", "description-format", "help", "h"]), + detail: new Set(["format", "help", "h"]), } async function main(): Promise { diff --git a/.agents/skills/freehire-search/cli/tests/detail-flags.test.ts b/.agents/skills/freehire-search/cli/tests/detail-flags.test.ts new file mode 100644 index 00000000..32ef45e7 --- /dev/null +++ b/.agents/skills/freehire-search/cli/tests/detail-flags.test.ts @@ -0,0 +1,72 @@ +import { describe, expect, test } from "bun:test"; +import { runCLI } from "./helpers"; + +// `detail` listed --description-format among its known flags, but runDetail +// never reads it: the detail endpoint's HTML body is always stripped to plain +// text. So `detail --description-format markdown` passed the +// unknown-flag guard, exited 0 and printed plain text - the silently ignored +// flag that guard exists to prevent. The CLI runs against a loopback API +// through its supported FREEHIRE_API_URL override; requests are counted. +const JOB = { + public_slug: "backend-engineer-acme-ab12cd34", + source: "greenhouse", + external_id: "1", + url: "https://boards.example/acme/1", + title: "Backend Engineer", + company: "Acme", + company_slug: "acme", + location: "Berlin, Germany", + description: "

Build things

  • Go
  • Postgres
", + skills: ["go"], + regions: ["eu"], + countries: ["DE"], + cities: ["Berlin"], + posted_at: "2026-10-01T00:00:00Z", + created_at: "2026-10-01T00:00:00Z", + enrichment: {}, +}; + +async function detail(args: string[]) { + const requests: URL[] = []; + const server = Bun.serve({ + hostname: "127.0.0.1", + port: 0, + fetch(request) { + requests.push(new URL(request.url)); + return Response.json({ data: JOB }); + }, + }); + try { + const result = await runCLI(["detail", JOB.public_slug, ...args], { FREEHIRE_API_URL: server.url.origin }); + return { result, requests }; + } finally { + server.stop(true); + } +} + +describe("detail flags", () => { + for (const value of ["markdown", "text", "html"]) { + test(`--description-format ${value} is rejected as an unknown detail flag before any request`, async () => { + const { result, requests } = await detail(["--description-format", value]); + expect(result.exitCode).toBe(1); + expect(result.stdout).toBe(""); + const error = JSON.parse(result.stderr); + expect(error.code).toBe("UNKNOWN_FLAG"); + expect(error.error).toContain("--description-format"); + expect(requests).toHaveLength(0); + }); + } + + test("--format plain still prints the detail", async () => { + const { result, requests } = await detail(["--format", "plain"]); + expect(result.exitCode).toBe(0); + expect(requests).toHaveLength(1); + expect(requests[0].pathname).toBe(`/api/v1/jobs/${JOB.public_slug}`); + expect(result.stdout).toContain("Backend Engineer"); + }); + + test("search still validates its own --description-format", async () => { + const result = await runCLI(["search", "--description-format", "tekst"]); + expect(JSON.parse(result.stderr).code).toBe("BAD_ARG"); + }); +}); diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b5043fc..1275ee1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,14 @@ per-file diff commands. handler-level test that pastes the search fixture's own apply URL into `detail` with fetch stubbed; the off-host cases fail on master. +- **`freehire-search detail` rejects `--description-format` instead of ignoring it** + (#648, `.agents/skills/freehire-search/cli/src/cli.ts`, `tests/detail-flags.test.ts`) - the flag + was on `detail`'s known-flag list, but `runDetail` never reads it and always prints the detail + endpoint's HTML body stripped to plain text. So `detail --description-format markdown` + passed the unknown-flag guard, exited 0 and printed text, which is the silently ignored flag + that guard exists to prevent. `detail` now answers it with `UNKNOWN_FLAG` before any request; + `search`, where the flag is documented and used, is unchanged. + ## [1.7.3] - 2026-10-19 ### Added