Skip to content

fix: pass option values to subcommands verbatim - #6

Merged
bentruyman merged 1 commit into
mainfrom
fix/subcommand-option-values-verbatim
Aug 20, 2026
Merged

bentruyman merged 1 commit into
mainfrom
fix/subcommand-option-values-verbatim

Conversation

@bentruyman

Copy link
Copy Markdown
Owner

Problem

A parent command only tells mri about the options it declares, so every option belonging to a subcommand fell through to mri's numeric coercion on the way past:

  • --title "" parsed as the number 0
  • --title 007 parsed as 7
  • --title 1e3 parsed as 1000

reconstructArgv then stringified those coerced values back into an argv for the subcommand, so the subcommand received --title 0 with no way to know the value had ever been anything else.

A flat command was never affected — it declares its own options to mri and never goes through the reconstruction. That asymmetry is what made this hard to spot: the same option block behaves differently depending on whether it sits under a subcommand.

Fix

The coercion isn't reversible, so the fix is to not serialize through it at all. reconstructArgv is replaced by argvForSubcommand, which hands the subcommand the original tokens, minus this command's own options and the subcommand name. Only the subcommand knows the types of its own options, so it should see exactly what the user typed.

Details:

  • --opt=value carries its own value; --opt value consumes the next token, but only when the option actually takes one.
  • Everything after -- is passed through verbatim.
  • The parent's own options are still stripped and handed down through inheritedOptions as before.
  • Clustered short groups like -abc are passed through rather than guessed at — swallowing a flag and the token after it on a wrong guess is a worse failure than the subcommand reporting an unknown option.

Tests

New option values reach subcommands verbatim block covering empty strings, numeric-looking strings (007, 1e3, 0x10, 1.50, +5), parity with the flat-command path, inherited parent options, and --opt=value / boolean flags.

bun test — 278 pass, 0 fail. bun run typecheck clean.

🤖 Generated with Claude Code

A parent command only tells mri about the options it declares itself, so every
option belonging to a subcommand fell through to mri's numeric coercion on the
way past. `--title ""` parsed as the number 0, `--title 007` as 7, `--title 1e3`
as 1000. `reconstructArgv` then stringified those values back into an argv for
the subcommand, which received `--title 0` with no way to know it had ever been
anything else.

The coercion is not reversible, so the fix is not to serialize through it. The
subcommand now receives the original tokens, minus this command's own options
and the subcommand name — only the subcommand knows the types of its own
options, so it should see exactly what the user typed.

A flat command was never affected, because it declares its own options to mri
and never goes through the reconstruction. That asymmetry is what made this
hard to see: the same option block behaves differently depending on whether it
sits under a subcommand.

Clustered short groups like `-abc` are passed through rather than guessed at.
Swallowing a flag and the token after it on a wrong guess is a worse failure
than the subcommand reporting an unknown option.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bentruyman
bentruyman merged commit ecb8814 into main Aug 20, 2026
1 check passed
@bentruyman
bentruyman deleted the fix/subcommand-option-values-verbatim branch August 20, 2026 23:51
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.

1 participant