Skip to content

Reject unknown flags instead of dropping them silently - #18

Open
EvilFreelancer wants to merge 1 commit into
mainfrom
fix/strict-unknown-flags
Open

Reject unknown flags instead of dropping them silently#18
EvilFreelancer wants to merge 1 commit into
mainfrom
fix/strict-unknown-flags

Conversation

@EvilFreelancer

Copy link
Copy Markdown
Owner

Fixes #17.

Problem

Dynamic API commands never go through yargs - runApiCommand parses argv by hand. Any flag the spec did not declare was silently discarded, or attached as a JSON body field to a request that cannot carry one. ocli widgets_id --id widget-1 --expand parts (instead of --$expand) sent GET /widgets/widget-1 with no querystring, printed a normal-looking response and exited 0. As the reporter noted in a follow-up comment, this was not $-specific: --bogus value behaved the same way. Built-in commands had the same hole because yargs was not in strict mode.

Fix

  • new pure Layer 2 module src/command-args.ts: findUnknownFlags validates parsed flags against CliCommand.options, formatUnknownFlagsError renders the message, and the suggestion picks the closest declared name (normalized match first, then edit distance <= 2), so --expand points at --$expand;
  • runApiCommand calls it after parsing and before building the request, so nothing is sent when a flag is wrong;
  • .strict() on the yargs tree covers commands, profiles, use, onboard;
  • .locale("en") so yargs' own messages do not switch language with the environment - Unknown argument: qeury was coming out localized.

One case stays permissive on purpose: when an operation accepts a body (POST, PUT, PATCH, DELETE) and the spec declares no request body, undeclared flags are still forwarded as JSON body fields. That passthrough is the only way to call endpoints whose payload is undocumented, and it is covered by existing tests. Once the spec declares body properties or formData parameters, those names become the full list of accepted flags.

Behavior

$ ocli widgets_id -p expandbug --id widget-1 --expand parts
Unknown option: --expand (did you mean --$expand?). Run 'ocli widgets_id --help' to see available options.
$ echo $?
1

$ ocli widgets_id -p expandbug --id widget-1 --bogus value
Unknown option: --bogus. Run 'ocli widgets_id --help' to see available options.

$ ocli commands -p expandbug --qeury widget
Unknown argument: qeury

$ ocli widgets_id -p expandbug --id widget-1 --$expand parts
{ "id": "widget-1", "name": "Example Widget", "parts": ["bolt", "nut"] }

Verified end-to-end against a local mock server reproducing the issue: before the change the wrong flag returned "parts": null with exit 0, after it the request is never sent.

Tests

  • tests/command-args.test.ts - 14 new unit tests (unknown flag detection, $-prefix and typo suggestions, free-form body exception, message formatting);
  • tests/cli.test.ts - 6 new tests covering the reported scenario, the declared-body case, the correctly spelled --$expand, the retained body passthrough, and strict mode on commands.

Full suite: 139 passed, 11 suites. npm run build clean. README, skills/ocli-api/SKILL.md, and examples/skill-ocli-api.md document the new behavior; the architecture rule is updated in both .claude and .cursor trees.

🤖 Generated with Claude Code

Dynamic API commands are parsed by hand, not by yargs, and every flag that
the spec did not declare was either swallowed or attached as a JSON body
field to a request that could not carry one. A typo like --expand instead
of --$expand produced a 200 OK with missing data and exit code 0.

Add a pure Layer 2 module command-args.ts that validates parsed flags
against CliCommand.options and suggests the closest declared name, and call
it from runApiCommand before the request is built. Body-capable operations
whose spec declares no request body keep forwarding undeclared flags as
body fields - that passthrough is the only way to reach undocumented
payloads. Enable yargs .strict() so built-in commands reject unknown
arguments too, and pin the yargs locale to English so those messages do not
change with the environment.

Fixes #17

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: unknown/misspelled flags are silently dropped instead of erroring

1 participant