You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Commander.js的CLI工具子命令在Windows Git Bash中失效

背景

我正在开发一款名为solo的CLI工具,用于自动化工作中的常规流程,技术栈选用TypeScript、Node.js搭配Commander.js。该工具最终将支持跨平台,但目前处于早期原型阶段,主要适配Windows环境,我在Windows上仅通过Git Bash使用它。

目标

我希望将部分命令按逻辑分组,实现如下调用方式:

solo monorepo <subcommand> [options]
solo git <subcommand> [options]
solo aws <subcommand> [options]
# etc

目前我在实现单个子命令的最小示例时遇到了问题。

问题

我尝试定义demo命令及其inner子命令,期望通过solo demo inner调用,通过solo help demo inner查看帮助,但该功能无法正常工作。无论执行何种相关命令,要么触发demo命令的帮助信息,要么仅执行demo命令本身。

代码

index.ts

import chalk from 'chalk';
import { Command } from 'commander';
import { allCommands } from './commands';
import { CliCommandMetadata, createCommandExample, describeCliOption } from './commands/cli-option';

export function createCli(): Command {
  const program = new Command();

  program
    .name(`solo`)
    .description(`CLI tool for automating routine processes.`)
    .option(`--verbose`, `Provide verbose output.`)
    .version(`0.0.1`);

  const cmd = program
    .command('demo')
    .description('Demo command')
    .addHelpText('after', `DEMO CMD`);
    // .action(() => {
    //   console.log('demo cmd');
    // });

  const subCmd = cmd
    .command('inner')
    .description('Inner command')
    .addHelpText('after', `INNER CMD`)
    .action(() => {
      console.log('inner cmd');
    });

  return program;
}


//function registerCommand(programCommand: Command, commandMetadata: CliCommandMetadata) {
//  let command = programCommand
//    .command(commandMetadata.name)
//    .description(commandMetadata.description);
//
//  command = Object
//    .values(commandMetadata.options)
//    .reduce(
//      (command, option) => command.option(...describeCliOption(option)),
//      command,
//    );
//
//  command
//    .addHelpText('after', formatExample(createCommandExample(commandMetadata)))
//    .action((...args: [str: any, options: any]) => {
//      commandMetadata.impl.apply(null, args);
//    });
//
//  function formatExample(...lines: string[]): string {
//    return `
//Example:
//${lines.join('\n')}
//`;
//  }
//}

package.json

{
  "name": "solo",
  "version": "0.0.1",
  "description": "CLI tool for automating routine processes.",
  "main": "index.js",
  "bin": {
    "solo": "./dist/index.js"
  },
  "scripts": {
    "start": "ts-node index.ts",
    "start-alt": "node -r ts-node/register index.ts",
    "tsc": "tsc",
    "build": "npx tsc"
  },
  "dependencies": {
    "axios": "^1.7.5",
    "chalk": "^4.1.2",
    "cli-table3": "^0.6.5",
    "commander": "^12.1.0",
    "semver": "^7.6.3",
    "ts-node": "^10.9.2",
    "typescript": "^5.5.4"
  },
  "devDependencies": {
    "@types/node": "^22.5.0",
    "@types/semver": "^7.5.8"
  }
}

失效场景演示

以下是我尝试调用子命令或查看帮助时的输出:
无论执行何种操作,要么显示demo命令的帮助信息,要么仅触发demo命令本身。

$ solo demo inner

> solo@0.0.1 start
> ts-node index.ts demo

Usage: solo demo [options] [command]

Demo command

Options:
  -h, --help      display help for command

Commands:
  inner           Inner command
  help [command]  display help for command
DEMO CMD
$ solo demo help inner

> solo@0.0.1 start
> ts-node index.ts demo

Usage: solo demo [options] [command]

Demo command

Options:
  -h, --help      display help for command

Commands:
  inner           Inner command
  help [command]  display help for command
DEMO CMD
$ solo help

> solo@0.0.1 start
> ts-node index.ts help

Usage: solo [options] [command]

CLI tool for automating routine processes.

Options:
  --verbose                        Provide verbose output.
  -V, --version                    output the version number
  -h, --help                       display help for command

Commands:
  solo-check-health                Self-check the health of the CLI tool.
                                   Validates the presence and the correctness of the config.
  demo                             Demo command
  help [command]                   display help for command
$ solo help demo

> solo@0.0.1 start
> ts-node index.ts help

Usage: solo [options] [command]

CLI tool for automating routine processes.

Options:
  --verbose                        Provide verbose output.
  -V, --version                    output the version number
  -h, --help                       display help for command

Commands:
  solo-check-health                Self-check the health of the CLI tool.
                                   Validates the presence and the correctness of the config.
  demo                             Demo command
  help [command]                   display help for command
$ solo demo help

> solo@0.0.1 start
> ts-node index.ts demo

Usage: solo demo [options] [command]

Demo command

Options:
  -h, --help      display help for command

Commands:
  inner           Inner command
  help [command]  display help for command
DEMO CMD
$ solo demo inner help

> solo@0.0.1 start
> ts-node index.ts demo

Usage: solo demo [options] [command]

Demo command

Options:
  -h, --help      display help for command

Commands:
  inner           Inner command
  help [command]  display help for command
DEMO CMD

解决方案

问题出在两个核心点:npm脚本未完整传递参数,以及CLI缺少参数解析逻辑。

1. 修复npm脚本的参数传递

当前start脚本仅执行ts-node index.ts,后续的命令参数(如demo inner)未被传递给ts-node。修改package.json中的脚本,让它接收并传递所有参数:

"scripts": {
  "start": "ts-node index.ts \"$@\"",
  "start-alt": "node -r ts-node/register index.ts \"$@\"",
  // 其他脚本保持不变
}

修改后,执行solo demo inner时所有参数都会被正确传递给ts-node。

2. 添加CLI参数解析逻辑

createCli函数创建了Command实例,但未调用parse方法解析命令行参数。在index.ts文件末尾添加执行逻辑:

// 在index.ts文件末尾添加
const cli = createCli();
cli.parse(process.argv);

Commander.js必须调用parse方法才能处理传入的命令行参数,否则无法识别子命令。

3. 验证修复效果

修改后执行以下命令测试:

  • npm start demo inner:应输出inner cmd
  • npm start help demo inner:应显示inner子命令的帮助信息
  • 若要直接使用solo命令,先执行npm link将工具链接到全局,之后即可直接用solo demo inner调用。

内容的提问来源于stack exchange,提问作者Igor Soloydenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 23:23:11