fix: show subcommand help on --help

This commit is contained in:
Peter Steinberger
2026-01-21 04:01:23 +00:00
parent d7440baef6
commit 833bbcd166
3 changed files with 37 additions and 5 deletions

View File

@@ -21,7 +21,7 @@ const { nodesAction, registerNodesCli } = vi.hoisted(() => {
vi.mock("../acp-cli.js", () => ({ registerAcpCli }));
vi.mock("../nodes-cli.js", () => ({ registerNodesCli }));
const { registerSubCliCommands } = await import("./register.subclis.js");
const { registerSubCliByName, registerSubCliCommands } = await import("./register.subclis.js");
describe("registerSubCliCommands", () => {
const originalArgv = process.argv;
@@ -78,4 +78,20 @@ describe("registerSubCliCommands", () => {
expect(registerNodesCli).toHaveBeenCalledTimes(1);
expect(nodesAction).toHaveBeenCalledTimes(1);
});
it("replaces placeholder when registering a subcommand by name", async () => {
process.argv = ["node", "clawdbot", "acp", "--help"];
const program = new Command();
program.name("clawdbot");
registerSubCliCommands(program, process.argv);
await registerSubCliByName(program, "acp");
const names = program.commands.map((cmd) => cmd.name());
expect(names.filter((name) => name === "acp")).toHaveLength(1);
await program.parseAsync(["node", "clawdbot", "acp"], { from: "user" });
expect(registerAcpCli).toHaveBeenCalledTimes(1);
expect(acpAction).toHaveBeenCalledTimes(1);
});
});

View File

@@ -19,9 +19,7 @@ const shouldRegisterPrimaryOnly = (argv: string[]) => {
};
const shouldEagerRegisterSubcommands = (argv: string[]) => {
if (isTruthyEnvValue(process.env.CLAWDBOT_DISABLE_LAZY_SUBCOMMANDS)) return true;
if (hasHelpOrVersion(argv)) return true;
return false;
return isTruthyEnvValue(process.env.CLAWDBOT_DISABLE_LAZY_SUBCOMMANDS);
};
const loadConfig = async (): Promise<ClawdbotConfig> => {
@@ -234,6 +232,15 @@ function removeCommand(program: Command, command: Command) {
}
}
export async function registerSubCliByName(program: Command, name: string): Promise<boolean> {
const entry = entries.find((candidate) => candidate.name === name);
if (!entry) return false;
const existing = program.commands.find((cmd) => cmd.name() === entry.name);
if (existing) removeCommand(program, existing);
await entry.register(program);
return true;
}
function registerLazyCommand(program: Command, entry: SubCliEntry) {
const placeholder = program.command(entry.name).description(entry.description);
placeholder.allowUnknownOption(true);

View File

@@ -10,6 +10,7 @@ import { assertSupportedRuntime } from "../infra/runtime-guard.js";
import { formatUncaughtError } from "../infra/errors.js";
import { installUnhandledRejectionHandler } from "../infra/unhandled-rejections.js";
import { enableConsoleCapture } from "../logging.js";
import { getPrimaryCommand, hasHelpOrVersion } from "./argv.js";
import { tryRouteCli } from "./route.js";
export function rewriteUpdateFlagArgv(argv: string[]): string[] {
@@ -47,7 +48,15 @@ export async function runCli(argv: string[] = process.argv) {
process.exit(1);
});
await program.parseAsync(rewriteUpdateFlagArgv(normalizedArgv));
const parseArgv = rewriteUpdateFlagArgv(normalizedArgv);
if (hasHelpOrVersion(parseArgv)) {
const primary = getPrimaryCommand(parseArgv);
if (primary) {
const { registerSubCliByName } = await import("./program/register.subclis.js");
await registerSubCliByName(program, primary);
}
}
await program.parseAsync(parseArgv);
}
function stripWindowsNodeExec(argv: string[]): string[] {