diff --git a/README.md b/README.md index ac78191..5096015 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ tmux attach -t my-agent | `pluk patterns --cli=claude` | Show loaded patterns for a CLI | | `pluk --help` / `-h` | Show usage (works after any subcommand) | +Unknown or misspelled `--flags` on any command exit 2 (`unknown flag --name`) instead of being silently ignored. + ### Attach Flags | Flag | What it does | diff --git a/changelog.d/fixed-346.md b/changelog.d/fixed-346.md new file mode 100644 index 0000000..764ce18 --- /dev/null +++ b/changelog.d/fixed-346.md @@ -0,0 +1 @@ +- `pluk` now rejects unknown or misspelled `--flags` (e.g. `--captur=50`, `--entr`) with an error (exit 2) instead of silently ignoring them. diff --git a/src/cli.ts b/src/cli.ts index 07ab9db..6c932f0 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -69,6 +69,17 @@ const BOOL_FLAGS = new Set([ 'no-open', 'dangerous', 'json', 'enter', 'literal', ]); +const KNOWN_FLAGS = new Set([ + 'help', ...VALUE_FLAGS, ...BOOL_FLAGS, 'capture', 'diagnostics', +]); + +function rejectUnknownFlag(name: string): void { + if (!KNOWN_FLAGS.has(name)) { + console.error(`${paintStderr(ANSI_RED, 'Error:')} unknown flag --${name} (see pluk --help)`); + process.exit(2); + } +} + function parseArgs(args: string[]): { positional: string[]; flags: Record } { const positional: string[] = []; const flags: Record = {}; @@ -79,6 +90,7 @@ function parseArgs(args: string[]): { positional: string[]; flags: Record 0) { const name = arg.slice(2, eqIdx); const value = arg.slice(eqIdx + 1); + rejectUnknownFlag(name); if (BOOL_FLAGS.has(name) && value !== 'true' && value !== 'false') { console.error(`${paintStderr(ANSI_RED, 'Error:')} --${name} is a boolean flag: use --${name} or --${name}=false`); process.exit(2); @@ -86,6 +98,7 @@ function parseArgs(args: string[]): { positional: string[]; flags: Record`); process.exit(2); diff --git a/test/cli-error-paths.test.js b/test/cli-error-paths.test.js index 5e50b9a..f544e99 100644 --- a/test/cli-error-paths.test.js +++ b/test/cli-error-paths.test.js @@ -346,3 +346,16 @@ test('bare boolean switches are still accepted', () => { assert.equal(r.status, 0); assert.doesNotMatch(r.stderr, /requires a value/); }); + +test('unknown or misspelled --flags exit 2 with an unknown-flag error', () => { + for (const [args, flag] of [ + [['watch', 's', '--captur=50'], 'captur'], + [['send', 's', '--text=hi', '--entr'], 'entr'], + [['watch', 's', '--'], ''], + [['watch', 's', '--=x'], ''], + ]) { + const r = spawnSync(process.execPath, [CLI, ...args], { encoding: 'utf8' }); + assert.equal(r.status, 2, args.join(' ')); + assert.match(r.stderr, new RegExp(`unknown flag --${flag}`)); + } +});