Skip to content

fix(args): honor explicit value on boolean flags (--flag=false)#179

Open
Osamaali313 wants to merge 2 commits into
MiniMax-AI:mainfrom
Osamaali313:fix/boolean-flag-explicit-value
Open

fix(args): honor explicit value on boolean flags (--flag=false)#179
Osamaali313 wants to merge 2 commits into
MiniMax-AI:mainfrom
Osamaali313:fix/boolean-flag-explicit-value

Conversation

@Osamaali313

Copy link
Copy Markdown

Problem

parseFlags sets a boolean flag to true the moment it matches the flag, before looking at an inline =value:

if (schema.booleans.has(camelKey)) {
  (flags as Record<string, unknown>)[camelKey] = true;  // value ignored
  i++;
  continue;
}

So an explicit value is silently discarded and the opposite of what the user typed is applied:

parseFlags(['--quiet=false'], GLOBAL_OPTIONS).quiet     // => true  (expected false)
parseFlags(['--no-color=0'], GLOBAL_OPTIONS).noColor    // => true  (expected false)

A user trying to disable a flag via --flag=false ends up enabling it.

Fix

Honor an explicit value: --flag=false / --flag=0 / --flag=no / --flag=off (case-insensitive) → false; a bare --flag and --flag=true (or any other value) stay true. This fixes the inversion with no behavior change for existing usage (bare flags and =true are unchanged), and never consumes a following token (--flag false still treats false as a positional, as before).

Tests

Adds two parseFlags cases (test/args.test.ts): a bare boolean flag is true, and an explicit =false/=0/=true is honored. The boolean-with-=value path was previously untested, which is how the inversion went unnoticed. bun test test/args.test.ts passes (5/5); the changed files are eslint-clean.

parseFlags set a boolean flag to `true` as soon as it matched the flag,
before inspecting an inline `=value`. So `--flag=false` (or `--flag=0`)
was silently set to `true` -- the opposite of what the user typed -- and
the value was discarded.

Honor an explicit value: `--flag=false`/`0`/`no`/`off` -> false; a bare
`--flag` and `--flag=true` (or any other value) stay true, so there is no
behavior change for existing usage.

Adds parseFlags tests for the bare-flag and explicit-value cases.
Copilot AI review requested due to automatic review settings June 15, 2026 19:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Updates flag parsing to better handle boolean flags when an explicit value is provided (e.g., --flag=false) and adds regression tests to prevent boolean values being silently forced to true.

Changes:

  • Add a --verbose boolean flag to the test schema and new tests covering boolean parsing behavior.
  • Update parseFlags to interpret explicit boolean values like false/0/no/off as false.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
test/args.test.ts Adds tests for bare boolean flags and explicit boolean values.
src/args.ts Changes boolean flag parsing to respect explicit false-like values.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/args.ts Outdated
Comment on lines +117 to +122
// A bare boolean flag (`--flag`) is true. Honour an explicit value
// such as `--flag=false` / `--flag=0` instead of silently forcing the
// flag to true and discarding what the user typed.
(flags as Record<string, unknown>)[camelKey] =
value === undefined ||
!['false', '0', 'no', 'off'].includes(value.trim().toLowerCase());
Comment thread src/args.ts Outdated
Comment on lines +120 to +122
(flags as Record<string, unknown>)[camelKey] =
value === undefined ||
!['false', '0', 'no', 'off'].includes(value.trim().toLowerCase());
Comment thread test/args.test.ts Outdated
Comment on lines +34 to +39
it('honours an explicit value on a boolean flag instead of forcing true', () => {
// Regression: `--flag=false` / `--flag=0` were silently set to true.
expect(parseFlags(['--verbose=false'], OPTIONS).verbose).toBe(false);
expect(parseFlags(['--verbose=0'], OPTIONS).verbose).toBe(false);
expect(parseFlags(['--verbose=true'], OPTIONS).verbose).toBe(true);
});
Address PR review:
- hoist the recognised true/false token sets to module-level Sets instead
  of allocating an array on every boolean-flag parse
- accept common spellings (true/1/yes/on, false/0/no/off; case- and
  whitespace-insensitive) and throw a clear error for an unrecognised
  explicit value (e.g. `--flag=maybe`, `--flag=`) so a typo cannot
  silently enable a flag, consistent with numeric-flag validation
- expand tests to cover no/off, case/whitespace, true-like values, and the
  rejection path
@Osamaali313

Copy link
Copy Markdown
Author

Thanks @copilot — all three are good calls; addressed in 9f1f093:

  1. Per-parse allocation. Hoisted the recognised values to module-level Sets (BOOLEAN_TRUE_VALUES, BOOLEAN_FALSE_VALUES) and check membership against those constants — no array allocation per boolean-flag parse.

  2. Unknown/empty explicit values. The parser now validates explicit boolean values: it accepts true/1/yes/on and false/0/no/off, and throws a clear error for anything else (--verbose=maybe, --verbose=) — Flag --verbose requires a boolean value (e.g. true/false), got "maybe".. This mirrors the existing numeric-flag validation, so a typo can no longer silently enable a flag.

  3. Test coverage. Expanded the tests to cover no/off, case- and whitespace-insensitivity (--verbose= OFF , --verbose=False/TRUE), the full true-like set, and the new rejection path. bun test test/args.test.ts → 7 pass; eslint clean on the changed files.

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.

2 participants