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:
Benjamin Jesuiter
2026-02-16 22:06:25 +01:00
committed by GitHub
parent 05a83b9e97
commit b25f334fa2
15 changed files with 288 additions and 69 deletions

View File

@@ -9,6 +9,7 @@ type SubCliRegistrar = (program: Command) => Promise<void> | void;
type SubCliEntry = {
name: string;
description: string;
hasSubcommands: boolean;
register: SubCliRegistrar;
};
@@ -31,10 +32,14 @@ const loadConfig = async (): Promise<OpenClawConfig> => {
return mod.loadConfig();
};
// Note for humans and agents:
// If you update the list of commands, also check whether they have subcommands
// and set the flag accordingly.
const entries: SubCliEntry[] = [
{
name: "acp",
description: "Agent Control Protocol tools",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../acp-cli.js");
mod.registerAcpCli(program);
@@ -42,7 +47,8 @@ const entries: SubCliEntry[] = [
},
{
name: "gateway",
description: "Gateway control",
description: "Run, inspect, and query the WebSocket Gateway",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../gateway-cli.js");
mod.registerGatewayCli(program);
@@ -51,6 +57,7 @@ const entries: SubCliEntry[] = [
{
name: "daemon",
description: "Gateway service (legacy alias)",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../daemon-cli.js");
mod.registerDaemonCli(program);
@@ -58,7 +65,8 @@ const entries: SubCliEntry[] = [
},
{
name: "logs",
description: "Gateway logs",
description: "Tail gateway file logs via RPC",
hasSubcommands: false,
register: async (program) => {
const mod = await import("../logs-cli.js");
mod.registerLogsCli(program);
@@ -67,6 +75,7 @@ const entries: SubCliEntry[] = [
{
name: "system",
description: "System events, heartbeat, and presence",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../system-cli.js");
mod.registerSystemCli(program);
@@ -74,7 +83,8 @@ const entries: SubCliEntry[] = [
},
{
name: "models",
description: "Model configuration",
description: "Discover, scan, and configure models",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../models-cli.js");
mod.registerModelsCli(program);
@@ -82,7 +92,8 @@ const entries: SubCliEntry[] = [
},
{
name: "approvals",
description: "Exec approvals",
description: "Manage exec approvals (gateway or node host)",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../exec-approvals-cli.js");
mod.registerExecApprovalsCli(program);
@@ -90,7 +101,8 @@ const entries: SubCliEntry[] = [
},
{
name: "nodes",
description: "Node commands",
description: "Manage gateway-owned node pairing and node commands",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../nodes-cli.js");
mod.registerNodesCli(program);
@@ -99,6 +111,7 @@ const entries: SubCliEntry[] = [
{
name: "devices",
description: "Device pairing + token management",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../devices-cli.js");
mod.registerDevicesCli(program);
@@ -106,7 +119,8 @@ const entries: SubCliEntry[] = [
},
{
name: "node",
description: "Node control",
description: "Run and manage the headless node host service",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../node-cli.js");
mod.registerNodeCli(program);
@@ -114,7 +128,8 @@ const entries: SubCliEntry[] = [
},
{
name: "sandbox",
description: "Sandbox tools",
description: "Manage sandbox containers for agent isolation",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../sandbox-cli.js");
mod.registerSandboxCli(program);
@@ -122,7 +137,8 @@ const entries: SubCliEntry[] = [
},
{
name: "tui",
description: "Terminal UI",
description: "Open a terminal UI connected to the Gateway",
hasSubcommands: false,
register: async (program) => {
const mod = await import("../tui-cli.js");
mod.registerTuiCli(program);
@@ -130,7 +146,8 @@ const entries: SubCliEntry[] = [
},
{
name: "cron",
description: "Cron scheduler",
description: "Manage cron jobs via the Gateway scheduler",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../cron-cli.js");
mod.registerCronCli(program);
@@ -138,7 +155,8 @@ const entries: SubCliEntry[] = [
},
{
name: "dns",
description: "DNS helpers",
description: "DNS helpers for wide-area discovery (Tailscale + CoreDNS)",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../dns-cli.js");
mod.registerDnsCli(program);
@@ -146,7 +164,8 @@ const entries: SubCliEntry[] = [
},
{
name: "docs",
description: "Docs helpers",
description: "Search the live OpenClaw docs",
hasSubcommands: false,
register: async (program) => {
const mod = await import("../docs-cli.js");
mod.registerDocsCli(program);
@@ -154,7 +173,8 @@ const entries: SubCliEntry[] = [
},
{
name: "hooks",
description: "Hooks tooling",
description: "Manage internal agent hooks",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../hooks-cli.js");
mod.registerHooksCli(program);
@@ -162,7 +182,8 @@ const entries: SubCliEntry[] = [
},
{
name: "webhooks",
description: "Webhook helpers",
description: "Webhook helpers and integrations",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../webhooks-cli.js");
mod.registerWebhooksCli(program);
@@ -171,6 +192,7 @@ const entries: SubCliEntry[] = [
{
name: "qr",
description: "Generate iOS pairing QR/setup code",
hasSubcommands: false,
register: async (program) => {
const mod = await import("../qr-cli.js");
mod.registerQrCli(program);
@@ -179,6 +201,7 @@ const entries: SubCliEntry[] = [
{
name: "clawbot",
description: "Legacy clawbot command aliases",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../clawbot-cli.js");
mod.registerClawbotCli(program);
@@ -186,7 +209,8 @@ const entries: SubCliEntry[] = [
},
{
name: "pairing",
description: "Pairing helpers",
description: "Secure DM pairing (approve inbound requests)",
hasSubcommands: true,
register: async (program) => {
// Initialize plugins before registering pairing CLI.
// The pairing CLI calls listPairingChannels() at registration time,
@@ -199,7 +223,8 @@ const entries: SubCliEntry[] = [
},
{
name: "plugins",
description: "Plugin management",
description: "Manage OpenClaw plugins and extensions",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../plugins-cli.js");
mod.registerPluginsCli(program);
@@ -209,7 +234,8 @@ const entries: SubCliEntry[] = [
},
{
name: "channels",
description: "Channel management",
description: "Manage connected chat channels (Telegram, Discord, etc.)",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../channels-cli.js");
mod.registerChannelsCli(program);
@@ -217,7 +243,8 @@ const entries: SubCliEntry[] = [
},
{
name: "directory",
description: "Directory commands",
description: "Lookup contact and group IDs (self, peers, groups) for supported chat channels",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../directory-cli.js");
mod.registerDirectoryCli(program);
@@ -225,7 +252,8 @@ const entries: SubCliEntry[] = [
},
{
name: "security",
description: "Security helpers",
description: "Security tools and local config audits",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../security-cli.js");
mod.registerSecurityCli(program);
@@ -233,7 +261,8 @@ const entries: SubCliEntry[] = [
},
{
name: "skills",
description: "Skills management",
description: "List and inspect available skills",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../skills-cli.js");
mod.registerSkillsCli(program);
@@ -241,7 +270,8 @@ const entries: SubCliEntry[] = [
},
{
name: "update",
description: "CLI update helpers",
description: "Update OpenClaw and inspect update channel status",
hasSubcommands: true,
register: async (program) => {
const mod = await import("../update-cli.js");
mod.registerUpdateCli(program);
@@ -250,6 +280,7 @@ const entries: SubCliEntry[] = [
{
name: "completion",
description: "Generate shell completion script",
hasSubcommands: false,
register: async (program) => {
const mod = await import("../completion-cli.js");
mod.registerCompletionCli(program);
@@ -261,6 +292,10 @@ export function getSubCliEntries(): SubCliEntry[] {
return entries;
}
export function getSubCliCommandsWithSubcommands(): string[] {
return entries.filter((entry) => entry.hasSubcommands).map((entry) => entry.name);
}
function removeCommand(program: Command, command: Command) {
const commands = program.commands as Command[];
const index = commands.indexOf(command);