Files
moltbot/src/cli/program/parent-default-help.test.ts
hclsys ba80695bba fix(cli): exit 0 when invoking parent commands without a subcommand (#73077)
Several `openclaw <parent>` commands (channels, plugins, approvals, devices,
cron, mcp) were exiting with code 1 when invoked bare, while printing the
same help-style content that `<parent> --help` produces (which exits 0).
This broke `&&` chains and surfaced a misleading
`ELIFECYCLE Command failed with exit code 1.` line under pnpm.

Add a small `applyParentDefaultHelpAction(cmd)` helper in
`src/cli/program/parent-default-help.ts` that attaches a default action
which prints the parent's own help and sets `process.exitCode = 0`. The
helper is a no-op when the parent already has its own action (e.g.
`agents` defaulting to `agents list`), so existing intentional defaults
are preserved.

Apply it to the six core parents listed in #73077.
2026-04-28 02:40:44 +01:00

45 lines
1.7 KiB
TypeScript

import { Command } from "commander";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { applyParentDefaultHelpAction } from "./parent-default-help.js";
describe("applyParentDefaultHelpAction (#73077)", () => {
let originalExitCode: NodeJS.Process["exitCode"];
beforeEach(() => {
originalExitCode = process.exitCode;
process.exitCode = undefined;
});
afterEach(() => {
process.exitCode = originalExitCode;
});
function buildParent(): Command {
const program = new Command();
program.exitOverride();
const parent = program.command("parent").description("test parent");
parent.exitOverride();
parent.command("list").action(() => {});
parent.command("status").action(() => {});
return parent;
}
it("invokes parent help and exits 0 when invoked without subcommand", async () => {
const parent = buildParent();
const helpSpy = vi.spyOn(parent, "outputHelp").mockImplementation(() => {});
applyParentDefaultHelpAction(parent);
await parent.parent!.parseAsync(["node", "test", "parent"]);
expect(helpSpy).toHaveBeenCalledTimes(1);
expect(process.exitCode).toBe(0);
});
it("still routes through subcommand actions when one is invoked", async () => {
const parent = buildParent();
const listAction = vi.fn();
parent.commands.find((c) => c.name() === "list")!.action(listAction);
const helpSpy = vi.spyOn(parent, "outputHelp").mockImplementation(() => {});
applyParentDefaultHelpAction(parent);
await parent.parent!.parseAsync(["node", "test", "parent", "list"]);
expect(listAction).toHaveBeenCalledTimes(1);
expect(helpSpy).not.toHaveBeenCalled();
});
});