diff --git a/src/cli/program/command-registry.test.ts b/src/cli/program/command-registry.test.ts index 7f87bc5a7bf..627a26a2d04 100644 --- a/src/cli/program/command-registry.test.ts +++ b/src/cli/program/command-registry.test.ts @@ -20,8 +20,12 @@ vi.mock("./register.maintenance.js", () => ({ }, })); -const { getCoreCliCommandNames, registerCoreCliByName, registerCoreCliCommands } = - await import("./command-registry.js"); +const { + getCoreCliCommandNames, + getCoreCliCommandsWithSubcommands, + registerCoreCliByName, + registerCoreCliCommands, +} = await import("./command-registry.js"); vi.mock("./register.status-health-sessions.js", () => ({ registerStatusHealthSessionsCommands: (program: Command) => { @@ -40,6 +44,7 @@ const testProgramContext: ProgramContext = { describe("command-registry", () => { const createProgram = () => new Command(); + const namesOf = (program: Command) => program.commands.map((command) => command.name()); const withProcessArgv = async (argv: string[], run: () => Promise) => { const prevArgv = process.argv; @@ -57,6 +62,17 @@ describe("command-registry", () => { expect(names).toContain("agents"); }); + it("returns only commands that support subcommands", () => { + const names = getCoreCliCommandsWithSubcommands(); + expect(names).toContain("config"); + expect(names).toContain("memory"); + expect(names).toContain("agents"); + expect(names).toContain("browser"); + expect(names).not.toContain("agent"); + expect(names).not.toContain("status"); + expect(names).not.toContain("doctor"); + }); + it("registerCoreCliByName resolves agents to the agent entry", async () => { const program = createProgram(); const found = await registerCoreCliByName(program, testProgramContext, "agents"); @@ -78,7 +94,17 @@ describe("command-registry", () => { const program = createProgram(); registerCoreCliCommands(program, testProgramContext, ["node", "openclaw", "doctor"]); - expect(program.commands.map((command) => command.name())).toEqual(["doctor"]); + expect(namesOf(program)).toEqual(["doctor"]); + }); + + it("does not narrow to the primary command when help is requested", () => { + const program = createProgram(); + registerCoreCliCommands(program, testProgramContext, ["node", "openclaw", "doctor", "--help"]); + + const names = namesOf(program); + expect(names).toContain("doctor"); + expect(names).toContain("status"); + expect(names.length).toBeGreaterThan(1); }); it("treats maintenance commands as top-level builtins", async () => { @@ -102,9 +128,19 @@ describe("command-registry", () => { await program.parseAsync(["node", "openclaw", "status"]); }); - const names = program.commands.map((command) => command.name()); + const names = namesOf(program); expect(names).toContain("status"); expect(names).toContain("health"); expect(names).toContain("sessions"); }); + + it("replaces placeholders when loading a grouped entry by secondary command name", async () => { + const program = createProgram(); + registerCoreCliCommands(program, testProgramContext, ["node", "openclaw", "doctor"]); + expect(namesOf(program)).toEqual(["doctor"]); + + const found = await registerCoreCliByName(program, testProgramContext, "dashboard"); + expect(found).toBe(true); + expect(namesOf(program)).toEqual(["doctor", "dashboard", "reset", "uninstall"]); + }); });