CLI: improve command descriptions in help output (#18486)
* CLI: clarify config vs configure descriptions * CLI: improve top-level command descriptions * CLI: make direct command help more descriptive * CLI: add commands hint to root help * CLI: show root help hint in implicit help output * CLI: add help example for command-specific help * CLI: tweak root subcommand marker spacing * CLI: mark clawbot as subcommand root in help * CLI: derive subcommand markers from registry metadata * CLI: escape help regex CLI name
This commit is contained in:
committed by
GitHub
parent
05a83b9e97
commit
b25f334fa2
@@ -2,12 +2,23 @@ import type { Command } from "commander";
|
||||
import type { ProgramContext } from "./context.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { isRich, theme } from "../../terminal/theme.js";
|
||||
import { escapeRegExp } from "../../utils.js";
|
||||
import { formatCliBannerLine, hasEmittedCliBanner } from "../banner.js";
|
||||
import { replaceCliName, resolveCliName } from "../cli-name.js";
|
||||
import { getCoreCliCommandsWithSubcommands } from "./command-registry.js";
|
||||
import { getSubCliCommandsWithSubcommands } from "./register.subclis.js";
|
||||
|
||||
const CLI_NAME = resolveCliName();
|
||||
const CLI_NAME_PATTERN = escapeRegExp(CLI_NAME);
|
||||
const ROOT_COMMANDS_WITH_SUBCOMMANDS = new Set([
|
||||
...getCoreCliCommandsWithSubcommands(),
|
||||
...getSubCliCommandsWithSubcommands(),
|
||||
]);
|
||||
const ROOT_COMMANDS_HINT =
|
||||
"Hint: commands suffixed with * have subcommands. Run <command> --help for details.";
|
||||
|
||||
const EXAMPLES = [
|
||||
["openclaw models --help", "Show detailed help for the models command."],
|
||||
[
|
||||
"openclaw channels login --verbose",
|
||||
"Link personal WhatsApp Web and show QR + connection logs.",
|
||||
@@ -51,18 +62,36 @@ export function configureProgramHelp(program: Command, ctx: ProgramContext) {
|
||||
sortSubcommands: true,
|
||||
sortOptions: true,
|
||||
optionTerm: (option) => theme.option(option.flags),
|
||||
subcommandTerm: (cmd) => theme.command(cmd.name()),
|
||||
subcommandTerm: (cmd) => {
|
||||
const isRootCommand = cmd.parent === program;
|
||||
const hasSubcommands = isRootCommand && ROOT_COMMANDS_WITH_SUBCOMMANDS.has(cmd.name());
|
||||
return theme.command(hasSubcommands ? `${cmd.name()} *` : cmd.name());
|
||||
},
|
||||
});
|
||||
|
||||
const formatHelpOutput = (str: string) => {
|
||||
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) => {
|
||||
const colored = str
|
||||
.replace(/^Usage:/gm, theme.heading("Usage:"))
|
||||
.replace(/^Options:/gm, theme.heading("Options:"))
|
||||
.replace(/^Commands:/gm, theme.heading("Commands:"));
|
||||
process.stdout.write(colored);
|
||||
process.stdout.write(formatHelpOutput(str));
|
||||
},
|
||||
writeErr: (str) => {
|
||||
process.stderr.write(formatHelpOutput(str));
|
||||
},
|
||||
writeErr: (str) => process.stderr.write(str),
|
||||
outputError: (str, write) => write(theme.error(str)),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user