Files
gstack/make-pdf/test/cli-args.test.ts
T
Garry TanandClaude Fable 5 a118fd0c89 fix(make-pdf): boolean flags no longer swallow the next positional argument
Fixes #2514. The parser treated any non-flag token after a flag as its value,
so `$P generate --toc essay.md` ate essay.md as --toc's value and failed with
"missing input" — the skill's own documented usage only worked when two
boolean flags happened to be adjacent. BOOLEAN_FLAGS enumerates the no-value
flags; value flags (--watermark, --to, --title, ...) are unchanged. main()
now runs behind import.meta.main so tests import the parser directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 09:19:12 -07:00

48 lines
1.9 KiB
TypeScript

/**
* #2514: boolean flags (--toc, --cover, ...) must never swallow the next
* positional argument. The parser treated ANY following non-flag token as a
* flag value, so `$P generate --toc essay.md` ate essay.md as --toc's value
* and the skill's own documented invocations failed with "missing input".
*/
import { describe, test, expect } from "bun:test";
import { parseArgs, BOOLEAN_FLAGS } from "../src/cli";
// parseArgs slices argv from index 2 (node/bun + script path).
const parse = (...args: string[]) => parseArgs(["bun", "cli.ts", ...args]);
describe("#2514 boolean flags do not swallow positionals", () => {
test("--toc before the input keeps the input positional", () => {
const r = parse("generate", "--toc", "essay.md");
expect(r.command).toBe("generate");
expect(r.flags.toc).toBe(true);
expect(r.positional).toEqual(["essay.md"]);
});
test("the skill's documented usage parses: --cover --toc essay.md essay.pdf", () => {
const r = parse("generate", "--cover", "--toc", "essay.md", "essay.pdf");
expect(r.flags.cover).toBe(true);
expect(r.flags.toc).toBe(true);
expect(r.positional).toEqual(["essay.md", "essay.pdf"]);
});
test("value flags still consume their value", () => {
const r = parse("generate", "--watermark", "DRAFT", "memo.md");
expect(r.flags.watermark).toBe("DRAFT");
expect(r.positional).toEqual(["memo.md"]);
});
test("--to consumes its format value", () => {
const r = parse("generate", "--to", "html", "doc.md");
expect(r.flags.to).toBe("html");
expect(r.positional).toEqual(["doc.md"]);
});
test("every registered boolean flag is covered by the set", () => {
// The generate command's no-value flags per commands.ts + usage text.
for (const f of ["cover", "toc", "no-chapter-breaks", "quiet", "verbose", "allow-network"]) {
expect(BOOLEAN_FLAGS.has(f)).toBe(true);
}
});
});