fix(cli): align root command descriptions (#100670)

Co-authored-by: AmirF194 <amirfathi@fastinfer.org>
This commit is contained in:
Vincent Koc
2026-07-05 23:29:40 -07:00
committed by GitHub
parent ef269f2689
commit c8ddcd116c
3 changed files with 94 additions and 30 deletions

View File

@@ -8,17 +8,17 @@ export type CoreCliCommandDescriptor = NamedCommandDescriptor;
const coreCliCommandCatalog = defineCommandDescriptorCatalog([
{
name: "crestodian",
description: "Open the interactive setup and repair assistant",
description: "Open the ring-zero setup and repair helper",
hasSubcommands: false,
},
{
name: "setup",
description: "Initialize local config and an agent workspace",
description: "Alias for openclaw onboard",
hasSubcommands: false,
},
{
name: "onboard",
description: "Interactive onboarding for gateway, workspace, and skills",
description: "Guided setup for auth, models, Gateway, workspace, channels, and skills",
hasSubcommands: false,
},
{
@@ -29,7 +29,7 @@ const coreCliCommandCatalog = defineCommandDescriptorCatalog([
{
name: "config",
description:
"Non-interactive config helpers (get/set/unset/file/validate). Default: starts guided setup.",
"Non-interactive config helpers (get/set/patch/unset/file/schema/validate). Run without subcommand for guided setup.",
hasSubcommands: true,
},
{
@@ -44,7 +44,7 @@ const coreCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "doctor",
description: "Diagnose and repair config, Gateway, plugin, and channel problems",
description: "Health checks + quick fixes for the gateway and channels",
hasSubcommands: false,
},
{
@@ -64,12 +64,12 @@ const coreCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "message",
description: "Send, read, and manage channel messages",
description: "Send, read, and manage messages and channel actions",
hasSubcommands: true,
},
{
name: "mcp",
description: "Manage OpenClaw MCP config and channel bridge",
description: "Manage OpenClaw mcp.servers config and channel bridge",
hasSubcommands: true,
parentDefaultHelp: true,
},
@@ -80,22 +80,22 @@ const coreCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "agent",
description: "Run one agent turn via the Gateway",
description: "Run an agent turn via the Gateway (use --local for embedded)",
hasSubcommands: false,
},
{
name: "agents",
description: "Manage isolated agents (workspaces, auth, routing)",
description: "Manage isolated agents (workspaces + auth + routing)",
hasSubcommands: true,
},
{
name: "status",
description: "Show Gateway, channel, model, and recent-session status",
description: "Show channel health and recent session recipients",
hasSubcommands: false,
},
{
name: "health",
description: "Fetch detailed health from the running Gateway",
description: "Fetch health from the running gateway",
hasSubcommands: false,
},
{
@@ -110,7 +110,7 @@ const coreCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "tasks",
description: "Inspect durable background tasks and flows",
description: "Inspect durable background tasks and TaskFlow state",
hasSubcommands: true,
},
] as const satisfies ReadonlyArray<CoreCliCommandDescriptor>);

View File

@@ -0,0 +1,64 @@
// Root help renders catalog placeholders while command help and completion use
// registered Commander commands. Keep those user-facing descriptions aligned.
import { Command } from "commander";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
getCoreCliCommandDescriptors,
getCoreCliCommandNames,
registerCoreCliByName,
} from "./command-registry-core.js";
import { createProgramContext } from "./context.js";
import { getSubCliEntries, registerSubCliByName } from "./register.subclis.js";
describe("root command descriptions", () => {
beforeEach(() => {
vi.stubEnv("OPENCLAW_ENABLE_PRIVATE_QA_CLI", "");
});
afterEach(() => {
vi.unstubAllEnvs();
});
it("keeps catalog placeholders and registered commands in sync", async () => {
const program = new Command().name("openclaw");
const ctx = createProgramContext();
const argv = ["node", "openclaw", "completion"];
for (const name of getCoreCliCommandNames()) {
await registerCoreCliByName(program, ctx, name, argv);
}
for (const entry of getSubCliEntries()) {
await registerSubCliByName(program, entry.name, argv, { purpose: "completion" });
}
const registeredCommands = new Map<string, { command: Command; registeredAsAlias: boolean }>();
for (const command of program.commands) {
registeredCommands.set(command.name(), { command, registeredAsAlias: false });
for (const alias of command.aliases()) {
registeredCommands.set(alias, { command, registeredAsAlias: true });
}
}
const descriptors = [...getCoreCliCommandDescriptors(), ...getSubCliEntries()];
const missing: string[] = [];
const mismatches: string[] = [];
for (const descriptor of descriptors) {
const registered = registeredCommands.get(descriptor.name);
if (!registered) {
missing.push(descriptor.name);
continue;
}
if (
!registered.registeredAsAlias &&
registered.command.description() !== descriptor.description
) {
mismatches.push(
`${descriptor.name}\n catalog: ${descriptor.description}\n registered: ${registered.command.description()}`,
);
}
}
expect(missing, "catalog entries with no registered command or alias").toEqual([]);
expect(mismatches, "root help vs registered command description drift").toEqual([]);
});
});

View File

@@ -7,31 +7,31 @@ import { isPrivateQaCliEnabled } from "./private-qa-cli.js";
export type SubCliDescriptor = NamedCommandDescriptor;
const subCliCommandCatalog = defineCommandDescriptorCatalog([
{ name: "acp", description: "Run and manage ACP-backed coding agents", hasSubcommands: true },
{ name: "acp", description: "Run an ACP bridge backed by the Gateway", hasSubcommands: true },
{
name: "gateway",
description: "Run, inspect, and query the OpenClaw Gateway",
description: "Run, inspect, and query the WebSocket Gateway",
hasSubcommands: true,
},
{
name: "daemon",
description: "Manage the Gateway service (legacy alias)",
description: "Manage the Gateway service (launchd/systemd/schtasks)",
hasSubcommands: true,
},
{ name: "logs", description: "Tail Gateway logs locally or via RPC", hasSubcommands: false },
{ name: "logs", description: "Tail gateway file logs via RPC", hasSubcommands: false },
{
name: "system",
description: "System events, heartbeat, and presence",
description: "System tools (events, heartbeat, presence)",
hasSubcommands: true,
},
{
name: "models",
description: "List, scan, and set model providers",
description: "Model discovery, scanning, and configuration",
hasSubcommands: true,
},
{
name: "infer",
description: "Run provider-backed model, media, search, and embedding commands",
description: "Run provider-backed inference commands through a stable CLI surface",
hasSubcommands: true,
},
{
@@ -57,12 +57,12 @@ const subCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "nodes",
description: "Pair nodes and run node-host commands through the Gateway",
description: "Manage gateway-owned nodes (pairing, status, invoke, and media)",
hasSubcommands: true,
},
{
name: "devices",
description: "Device pairing + token management",
description: "Device pairing and auth tokens",
hasSubcommands: true,
parentDefaultHelp: true,
},
@@ -73,12 +73,12 @@ const subCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "sandbox",
description: "Manage sandbox containers for agent isolation",
description: "Manage sandbox containers (Docker-based agent isolation)",
hasSubcommands: true,
},
{
name: "worktrees",
description: "Manage isolated git worktrees for agent tasks",
description: "Create, inspect, restore, and clean up managed worktrees",
hasSubcommands: true,
parentDefaultHelp: true,
},
@@ -104,7 +104,7 @@ const subCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "cron",
description: "Schedule and inspect Gateway background jobs",
description: "Manage cron jobs (via Gateway)",
hasSubcommands: true,
parentDefaultHelp: true,
},
@@ -140,7 +140,7 @@ const subCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "qr",
description: "Generate mobile pairing QR/setup code",
description: "Generate a mobile pairing QR code and setup code",
hasSubcommands: false,
},
{
@@ -155,13 +155,13 @@ const subCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "plugins",
description: "Install, enable, disable, and inspect plugins",
description: "Manage OpenClaw plugins and extensions",
hasSubcommands: true,
parentDefaultHelp: true,
},
{
name: "channels",
description: "Add, remove, login, and inspect messaging channels",
description: "Manage connected chat channels and accounts",
hasSubcommands: true,
parentDefaultHelp: true,
},
@@ -172,17 +172,17 @@ const subCliCommandCatalog = defineCommandDescriptorCatalog([
},
{
name: "security",
description: "Security tools and local config audits",
description: "Audit local config and state for common security foot-guns",
hasSubcommands: true,
},
{
name: "secrets",
description: "Audit, apply, and reload SecretRef-backed credentials",
description: "Secrets runtime controls",
hasSubcommands: true,
},
{
name: "skills",
description: "List, inspect, and install agent skills",
description: "List and inspect available skills",
hasSubcommands: true,
},
{