diff --git a/extensions/browser/src/cli/browser-cli.test-support.ts b/extensions/browser/src/cli/browser-cli.test-support.ts index c8d58154f1ce..f96e63705649 100644 --- a/extensions/browser/src/cli/browser-cli.test-support.ts +++ b/extensions/browser/src/cli/browser-cli.test-support.ts @@ -20,14 +20,7 @@ export function createBrowserProgram(params?: { withGatewayUrl?: boolean }): { if (params?.withGatewayUrl) { browser.option("--url ", "Gateway WebSocket URL"); } - const parentOpts = (cmd: Command): BrowserParentOpts => { - for (let current: Command | null | undefined = cmd; current; current = current.parent) { - if (current.name() === "browser") { - return current.opts() as BrowserParentOpts; - } - } - return cmd.parent?.opts?.() as BrowserParentOpts; - }; + const parentOpts = (cmd: Command): BrowserParentOpts => cmd.optsWithGlobals(); return { program, browser, parentOpts }; } diff --git a/extensions/browser/src/cli/browser-cli.ts b/extensions/browser/src/cli/browser-cli.ts index fc71ba928874..dfa86160ae09 100644 --- a/extensions/browser/src/cli/browser-cli.ts +++ b/extensions/browser/src/cli/browser-cli.ts @@ -240,12 +240,7 @@ function resolveBrowserLazySubcommand(argv: string[]): string | null { } function resolveBrowserParentOpts(cmd: Command): BrowserParentOpts { - for (let current: Command | null | undefined = cmd; current; current = current.parent) { - if (current.name() === "browser") { - return current.opts() as BrowserParentOpts; - } - } - return cmd.parent?.opts?.() as BrowserParentOpts; + return cmd.optsWithGlobals(); } function registerLazyBrowserCommands( diff --git a/src/cli/program/help.ts b/src/cli/program/help.ts index 593bfd66654c..f19913a7a922 100644 --- a/src/cli/program/help.ts +++ b/src/cli/program/help.ts @@ -44,6 +44,23 @@ const EXAMPLES = [ ], ] as const; +export function formatProgramHelpOutput(str: string): string { + // Commander emits plain section labels; decorate them after command-specific help renders. + let output = str; + const isRootHelp = new RegExp( + `^Usage:\\s+${CLI_NAME_PATTERN}\\s+\\[options\\]\\s+\\[command\\]\\s*$`, + "m", + ).test(output); + if (isRootHelp && /^Commands:/m.test(output)) { + output = output.replace(/^Commands:/m, `Commands:\n ${theme.muted(ROOT_COMMANDS_HINT)}`); + } + + return output + .replace(/^Usage:/gm, theme.heading("Usage:")) + .replace(/^Options:/gm, theme.heading("Options:")) + .replace(/^Commands:/gm, theme.heading("Commands:")); +} + export function configureProgramHelp( program: Command, ctx: ProgramContext, @@ -92,29 +109,12 @@ export function configureProgramHelp( }, }); - const formatHelpOutput = (str: string) => { - // Commander emits plain section labels; decorate them after command-specific help renders. - let output = str; - const isRootHelp = new RegExp( - `^Usage:\\s+${CLI_NAME_PATTERN}\\s+\\[options\\]\\s+\\[command\\]\\s*$`, - "m", - ).test(output); - if (isRootHelp && /^Commands:/m.test(output)) { - output = output.replace(/^Commands:/m, `Commands:\n ${theme.muted(ROOT_COMMANDS_HINT)}`); - } - - return output - .replace(/^Usage:/gm, theme.heading("Usage:")) - .replace(/^Options:/gm, theme.heading("Options:")) - .replace(/^Commands:/gm, theme.heading("Commands:")); - }; - program.configureOutput({ writeOut: (str) => { - process.stdout.write(formatHelpOutput(str)); + process.stdout.write(formatProgramHelpOutput(str)); }, writeErr: (str) => { - process.stderr.write(formatHelpOutput(str)); + process.stderr.write(formatProgramHelpOutput(str)); }, outputError: (str, write) => write(formatCliParseErrorOutput(str, { argv: process.argv })), }); diff --git a/src/cli/program/preaction.ts b/src/cli/program/preaction.ts index ec9aefa3a7e2..e593896a2884 100644 --- a/src/cli/program/preaction.ts +++ b/src/cli/program/preaction.ts @@ -45,14 +45,6 @@ function shouldAllowInvalidConfigForAction(actionCommand: Command, commandPath: ); } -function getRootCommand(command: Command): Command { - let current = command; - while (current.parent) { - current = current.parent; - } - return current; -} - function getActionCommandPath(actionCommand: Command): string[] { const commandPath: string[] = []; let current: Command | null = actionCommand; @@ -64,11 +56,10 @@ function getActionCommandPath(actionCommand: Command): string[] { } function getCliLogLevel(actionCommand: Command): LogLevel | undefined { - const root = getRootCommand(actionCommand); - if (root.getOptionValueSource("logLevel") !== "cli") { + if (actionCommand.getOptionValueSourceWithGlobals("logLevel") !== "cli") { return undefined; } - const logLevel = root.getOptionValue("logLevel"); + const logLevel = actionCommand.optsWithGlobals<{ logLevel?: unknown }>().logLevel; return typeof logLevel === "string" ? (logLevel as LogLevel) : undefined; } diff --git a/src/cli/program/root-help.ts b/src/cli/program/root-help.ts index 69c9c143c67a..b59b503fd2d5 100644 --- a/src/cli/program/root-help.ts +++ b/src/cli/program/root-help.ts @@ -9,7 +9,7 @@ import { collectUniqueCommandDescriptors, } from "./command-descriptor-utils.js"; import { getCoreCliCommandDescriptors } from "./core-command-descriptors.js"; -import { configureProgramHelp } from "./help.js"; +import { configureProgramHelp, formatProgramHelpOutput } from "./help.js"; import { getSubCliEntries } from "./subcli-descriptors.js"; /** Options for rendering root help without fully registering the live CLI. */ @@ -60,17 +60,8 @@ async function buildRootHelpProgram(renderOptions?: RootHelpRenderOptions): Prom export async function renderRootHelpText(renderOptions?: RootHelpRenderOptions): Promise { const program = await buildRootHelpProgram(renderOptions); let output = ""; - const originalWrite = process.stdout.write.bind(process.stdout); - const captureWrite: typeof process.stdout.write = ((chunk: string | Uint8Array) => { - output += String(chunk); - return true; - }) as typeof process.stdout.write; - process.stdout.write = captureWrite; - try { - program.outputHelp(); - } finally { - process.stdout.write = originalWrite; - } + program.configureOutput({ writeOut: (chunk) => (output += formatProgramHelpOutput(chunk)) }); + program.outputHelp(); return output; }