Skip to content

fix: Handle repeated CLI flags instead of silently dropping values#126

Merged
JoshMock merged 2 commits into
mainfrom
MattDevy/fix/issue-101
Apr 13, 2026
Merged

fix: Handle repeated CLI flags instead of silently dropping values#126
JoshMock merged 2 commits into
mainfrom
MattDevy/fix/issue-101

Conversation

@MattDevy
Copy link
Copy Markdown
Contributor

Summary

Fixes #101. Repeated CLI flags are now handled explicitly instead of silently keeping only the last value.

  • String options accumulate repeated values with comma separation: --index a --index b produces "a,b"
  • Non-string options (number, enum, object, array) error: --count 5 --count 10 produces "option --count cannot be specified more than once"
  • Boolean flags are unchanged (idempotent by nature)

Applies to both OptionDefinition options and schema-derived options. No changes to OptionDefinition, SchemaArgDefinition, ParsedResult, or any handler code.

Implementation

Two new helpers in factory.ts (~30 lines), using Commander's built-in getOptionValueSource() API inside parseArg callbacks:

  • stringAccumulator(cmd, attrName): on first CLI occurrence returns the value as-is; on subsequent occurrences comma-joins with the previous value
  • singleValueGuard(cmd, attrName, flagDisplay, innerParse?): errors via cmd.error() on repetition, otherwise delegates to the inner parser

Design decisions for reviewer awareness

  1. All string options accumulate, not just "known multi-value" ones. We cannot distinguish z.string() fields that accept comma-separated values (an Elasticsearch convention) from those that don't, since both are typed as string. Accumulating all strings is safe: Zod validation still runs, and if the ES API doesn't support multi-value, Elasticsearch itself returns a clear error. The key point is no data is silently lost, which was the original bug. If this is too permissive, an alternative is an explicit repeatable: true field on OptionDefinition/SchemaArgDefinition, but that adds config surface and requires every command author to opt in.

  2. Comma is the separator, not a configurable delimiter. Elasticsearch universally uses commas for multi-value path/query parameters (indices, fields, node IDs). A configurable separator would add complexity with no clear use case.

  3. --input-file is not guarded. It is registered without a parseArg callback. Commander already errors if the file doesn't exist, so double-specification is unlikely to be a real problem. Can add a guard as a follow-up.

  4. Global options (--json, --config-file, --use-context) are not guarded. They are registered on the root program in cli.ts, not through the factory. Guarding them would be a small follow-up change in cli.ts.

Test plan

  • 13 new tests in describe('repeated flags') covering:
    • String accumulation (single, double, triple values)
    • Default value not bleeding into accumulated result
    • Number/enum/object/array rejection
    • Boolean idempotence
    • Schema-derived options with and without Zod defaults
  • Full test suite passes (0 failures)
  • npm run build clean
  • Manual verification: --index idx-a --index idx-b produces "idx-a,idx-b" via --dry-run

)

Repeated string flags now accumulate with comma separation
(e.g. --index a --index b -> "a,b"). Repeated non-string flags
(number, enum, object, array) error with a clear message. Boolean
flags remain idempotent.

Uses Commander's getOptionValueSource() to detect repetition at
parse time, requiring no changes to OptionDefinition, ParsedResult,
or handler code.
@MattDevy MattDevy requested a review from JoshMock April 13, 2026 13:14
@JoshMock JoshMock merged commit 7617c57 into main Apr 13, 2026
16 checks passed
@JoshMock JoshMock deleted the MattDevy/fix/issue-101 branch April 13, 2026 17:38
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.

Repeated CLI flags silently use last value with no warning

2 participants