fix(browser): accept --window after the leaf subcommand (#1850) - #1899
fix(browser): accept --window after the leaf subcommand (#1850)#1899Chen17-sq wants to merge 1 commit into
Conversation
…jackwener#1850) `--window <mode>` was declared only on the parent `browser` command, but enablePositionalOptions() means a parent option is only accepted *before* the leaf subcommand token. The natural agent-facing form opencli browser <session> open <url> --window background failed with `error: unknown option '--window'`, and `browser open --help` omitted `--window` so it looked unsupported on the leaf. Register `--window` on the browser's page-interaction leaves so it is also accepted in the trailing position. getBrowserWindowMode()/getCommandOption() already walk the parent chain, so the pre-subcommand form keeps working and the flag resolves wherever it binds; `--window` now also shows in each page leaf's `--help`. Scope it precisely: only true leaves (sub.commands.length === 0) get the flag. Group nodes (get/tab/dialog) are recursed into but never carry `--window` themselves, and the session/scaffold commands {bind,unbind,init,verify,close} are skipped at the browser root because they never route through browserAction()/getBrowserWindowMode() — declaring it there would be inert and misleading in `--help`. The skip is gated on isBrowserRoot so the unrelated leaf `browser tab close` (a real page leaf under the `tab` group) still gets `--window`. In structured help, options inherited from the namespace root are de-duplicated out of a command's own `command_options`, so `--window` continues to surface once under `namespace_options` rather than being repeated on every leaf.
47a2d91 to
246aaeb
Compare
|
Thanks for the broader analysis and tests. I compared this with #1926 because both PRs address #1850. I opened a replacement PR here: #1963 The replacement keeps your important UX point: users and agents naturally put I did not use the leaf-option mirroring approach because it is easier to get subtly wrong as the browser command tree changes. One concrete example: this PR skips root You are credited in the replacement commit as a co-author:
|
fix(browser): accept
--windowafter page-interaction leaf subcommands (#1850)Problem
opencli browser <session> open <url> --window background— the natural agent-facing form with the flag in the trailing position — failed witherror: unknown option '--window', andbrowser open --helpomitted--window, making it look unsupported on the leaf. The original fix made it work but went too far: it registered--windowon EVERY browser descendant, including commands where the flag is inert and misleading in--help.Root cause
src/cli.ts—--window <mode>was declared only on the parentbrowsercommand. WithenablePositionalOptions(), a parent option is only accepted before the leaf subcommand token, so the trailing-position form was rejected.addBrowserWindowOptionRecursively) blindly added--windowto every descendant: the session/scaffold commandsbind,unbind,init,verify,close(none route throughbrowserAction/getBrowserWindowMode, so the flag does nothing) and the group nodesget/tab/dialog(not real commands).Fix
--windowis a namespace option. It stays declared on the parentbrowsercommand (line ~878) so it parses in the pre-subcommand position and is surfaced once undernamespace_optionsin structured help.addBrowserWindowOptionRecursively(command, isBrowserRoot)now (a) skips the session set{bind,unbind,init,verify,close}but ONLY whenisBrowserRoot === true— gating onisBrowserRootso the unrelated leafbrowser tab close(a real page leaf under thetabgroup, which DOES route throughbrowserAction) is not wrongly skipped by the name collision; (b) recurses into group nodes (get/tab/dialog) withisBrowserRoot=falsewithout adding--windowto the group node itself; (c) adds--windowonly to true leaves (sub.commands.length === 0); (d) keeps the existing "already has--window" guard. Verified thateval/extract(andopen/click/state/get url/tab close) route throughbrowserAction→getBrowserWindowMode, so they correctly keep--window;init/verifyuse plain.action()and correctly lose it.command_options. Because--windowis now redeclared on each page leaf,src/help.tscompactCommanderCommandfilters options whoselongmatches the namespace root's own options out of each leaf'scommand_options. This is correct and retained — without it--windowwould appear twice (once undernamespace_options, once per leaf). The comment was clarified to state it removes namespace-inherited options from a leaf's own list.Design summary
--windowis a browser-namespace option. To accept it in the trailing position it is mirrored onto every page-interaction leaf (so commander parses it wherever it binds;getBrowserWindowMode/getCommandOptionwalk the parent chain to read the value). It is deduped out of structuredcommand_optionsso it shows once at the namespace level. Non-page commands —init/verify/bind/unbind/closeand theget/tab/dialoggroup nodes — are excluded so--helpstays honest.Tests
src/cli.test.ts: trailing-position parse assertswindowMode: 'background'reaches the connect call; the breadth test asserts--windowIS onclick/open/evaland nestedget url, is NOT oninit/verify, and is NOT on thegetgroup node.src/help.test.ts: a direct test builds the program, gets the browserevalleaf, callscommanderCommandHelpData(browser, leaf, {globalCommand}), and assertscommand_optionsexcludeswindow/sessionbut includes the leaf's ownframe, whilenamespace_optionsstill includeswindow; a second test confirms the non-browserauth statusleaf'scommand_optionsis unchanged (sanity that the dedup only removes inherited options).npx tsc --noEmitclean;npx vitest run src/cli.test.ts src/help.test.ts→ 175 passed; full suitenpm test→ 464 files / 5065 tests passed, 1 skipped, 0 failed.