mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 08:36:25 +00:00
fix(portals)!: reject unknown flags in all six CLIs
Silently discarded flags produced silently wrong results: jobdanmark with --query (its real flag is --text) returned all 13,862 jobs as if they matched, exit 0, empty stderr - indistinguishable from a real result set. The four bunli CLIs get an argv preflight built from each command's own options object; linkedin and freehire validate parsed flags against per-command known sets. help/version still pass, and add-portal.md's existing bogus-flag-exits-1 contract now holds for the reference implementations contributors copy. One linkedin pin updated: "--jobage-minutes -5" now fails as UNKNOWN_FLAG (the stray -5 token) rather than BAD_ARG - same loud-failure invariant, earlier gate. Review finding F13 (2026-08-19), decision approved by Mads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d4e0c64c3c
commit
3bfd525cc4
@@ -118,6 +118,17 @@ function parseIntFlag(name: string, raw: string | boolean | string[]): number |
|
||||
return val
|
||||
}
|
||||
|
||||
// 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.
|
||||
const KNOWN_FLAGS: Record<string, Set<string>> = {
|
||||
search: new Set([
|
||||
"query", "category", "city", "company", "country", "facet", "format", "jobage", "limit",
|
||||
"page", "region", "remote", "seniority", "skill", "description-format", "help", "h",
|
||||
]),
|
||||
detail: new Set(["format", "description-format", "help", "h"]),
|
||||
}
|
||||
|
||||
async function main(): Promise<number> {
|
||||
const argv = process.argv.slice(2)
|
||||
const flags = parseFlags(argv)
|
||||
@@ -128,6 +139,25 @@ async function main(): Promise<number> {
|
||||
return cmd ? 0 : 1
|
||||
}
|
||||
|
||||
// Reject unknown flags instead of silently discarding them: a discarded
|
||||
// filter changes what the search returns with no error (a wrong flag name
|
||||
// once returned an entire portal's database as if it matched the query).
|
||||
// add-portal.md's contract requires a bogus flag to exit 1 with a JSON
|
||||
// error on stderr.
|
||||
const knownFlags = KNOWN_FLAGS[cmd]
|
||||
if (knownFlags) {
|
||||
for (const key of Object.keys(flags)) {
|
||||
if (key === "_" || knownFlags.has(key)) continue
|
||||
process.stderr.write(
|
||||
JSON.stringify({
|
||||
error: `unknown flag --${key} for '${cmd}' - flags are never silently ignored, because a discarded filter changes what the search returns; see --help for the supported flags`,
|
||||
code: "UNKNOWN_FLAG",
|
||||
}) + "\n",
|
||||
)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd === "search") {
|
||||
const fmt = (flags.format as string) || "json"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user