Files
filip131311 3fff176a32 fix(cli): let a boolean flag take the value you typed (#699)
Fixes #586.

## The bug

`--includeChildren false` left the flag **true** and silently dropped
the word, so the tool ran with the opposite of what was asked. The
reporter spent a while chasing a filter bug that did not exist — the
output was simply the unfiltered kind.

Reproduced on 0.18.1, device-free, with a discriminator that shows the
flag's value directly:

```
$ argent run screenshot-diff --udid U --baselinePath A.png --currentPath B.png --captureCurrent=false
{ "summary": "Screenshot diff summary … pixel_mismatch: 1.79% …"      <-- false, diff runs

$ argent run screenshot-diff --udid U --baselinePath A.png --currentPath B.png --captureCurrent false
[Tool:screenshot-diff] Provide either currentPath or captureCurrent, not both.   <-- stayed TRUE
```

That message is the whole problem in one line: the user typed `false`,
and the tool refuses on the grounds that they asked to capture.

## Why the old behaviour existed, and why it does not need to

The parser deliberately declined to look ahead:

> `// Bare boolean flag: true. Don't consume next argv to avoid
surprising behavior when a positional follows.`

That protects a case which does not exist. `parseFlags` has exactly one
caller, and **`positional` is never read anywhere in the repo** — every
bare token is collected and dropped, for booleans and for every other
type. There is no positional to steal. The module header had documented
the space form as legal all along:

```
//   --name value          (string / number / integer / boolean-with-value)
```

This matters because several tool booleans **default to true**, so
`false` is the only way to say what you mean:
`screenshot.includeImageInContext`, `network-request.includeBody`,
`debugger-component-tree.onScreenOnly`,
`debugger-inspect-element.resolveSourceMaps`.

## What changed

A boolean flag consumes a following token when it is exactly the word
`true`/`false`, case-insensitively. `1`/`0` and everything else are left
alone — a bare number after a switch is more likely an unrelated
argument than a value, and guessing at an ambiguous token is what caused
this bug. `--flag -- false` still keeps the word positional.

Three consequences are handled here rather than left to bite later:

- **`argent run` now reports arguments it ignored.** This is what stops
`--flag 0` and `--flag yes` being the same silent inversion, in a syntax
users are now being taught works. stderr, so `--json` stays parseable.
- **`--no-flag true` / `--no-flag false` now name the positive form**
instead of quietly meaning false. One is a contradiction, the other a
double negative.
- **`--flag=True` used to throw** while `--flag True` would now work —
the inline coercion was case-sensitive and did not trim. Both paths now
share one helper, so a single word cannot get two answers one function
apart.

The **flow** and **lens** parsers take switches that accept no value at
all, and would have silently ignored the syntax their users just learned
elsewhere. They now refuse it — in the flow parser that also stops
`false` being swallowed as the flow name, since its first bare token is
the flow to run.

`--help` gains **one legend line** for tools with a boolean field,
rather than widening the flag column on every field of every tool:

```
  Booleans: --flag or --flag true sets true; --flag false, --flag=false, or --no-flag sets false.
```

It deliberately does not begin with `--`.
`scripts/e2e-full/lib/discover-tools.sh` reads any such line in the
`Flags:` section as a flag row and takes the first `--token` as its
name, so a legend starting with a flag would inject a phantom flag into
every tool model it builds. **There is a test pinning that shape**,
verified live: running the harness's awk over the new help output yields
exactly the real flags and no legend row.

## Verified live

```
--captureCurrent false  -> diff runs (was: "Provide either currentPath or captureCurrent, not both.")
--captureCurrent=false  -> unchanged
--captureCurrent 0      -> "Note: ignoring unused argument(s): 0. Pass values as --flag <value> or --flag=<value>."
screenshot --help       -> legend present; harness awk parses only the real flags
```

25 new tests; 8 confirmed to fail before and pass after. The rest pin
what deliberately did *not* change: bare flag still true, a following
flag or non-boolean word left alone, `1`/`0` still positional in the
space form, `--` escape, string and unknown fields untouched,
`--no-flag` semantics, boolean arrays, last-write-wins. The parser had
**zero** boolean coverage before — which is why this survived. Full CLI
suite 298 passing; lint, prettier and `typecheck:tests` clean.

## Scope and neighbours

Only `flag-parser.ts` (the schema-driven parser for `argent run`)
changes semantics. The other CLI parsers take fixed, default-off
switches where `false` has no coherent meaning; three of them already
reject a stray token outright, and the two that did not now do.
`flow.ts`'s deliberate rejection of `--flag=value` is untouched.

**#648** also edits `flag-parser.ts` (rewrites `renderFlagName`, inserts
helpers at the same anchor). Behaviourally compatible — it keeps boolean
→ bare `--name` — so this is a textual conflict only, and either order
works.

## Found while here, not fixed

`scripts/e2e-full/lib/discover-tools.sh:60-64` detects flag kinds with
`\bboolean\b` in awk, where `\b` is **backspace**, not a word boundary —
so it never matches and every non-enum flag is typed `unknown`. That
makes the `bad-type` validation case downstream unreachable.
Pre-existing and unrelated to this fix; filing separately.

---

> **Stacked on #648** (`filip/run-cli-validation-messages`). Both
rewrite the same region of `packages/argent-cli/src/flag-parser.ts` —
#648 factors out `flagNameFor`/`isJsonField` and rewrites
`renderFlagName`, this one adds `booleanWord` beside them and a boolean
legend to `formatSchemaUsage`. Merging them independently conflicts;
this branch is rebased on #648, so review only the top commit. GitHub
retargets it to `main` when #648 merges.

---------

Co-authored-by: Filip131311 <f.kaminski2000@gmail.com>
2026-08-03 12:18:11 +02:00

19 KiB