CLI Forge vs. meow

meow is a zero-dependency, minimalist CLI helper by Sindre Sorhus.

CLI Forge strengths

Shared strengths

meow strengths

  • Zero dependencies — Completely self-contained.
  • Simplicity — A single function call returns parsed results. No builder chain, no class hierarchy. Ideal for simple scripts.
  • ESM-native — ESM-only by design.

Side-by-side example

The same CLI — a greet command with hello and goodbye subcommands — implemented in both libraries. Note how meow requires manual subcommand dispatch since it has no built-in subcommand system.

import meow from 'meow';
import { pathToFileURL } from 'node:url';

// meow is a minimal parser — subcommands are handled manually
// by reading positional arguments from cli.input.

const cli = meow(
  `
  Usage
    $ greet <command>

  Commands
    hello    Say hello to someone
    goodbye  Say goodbye to someone

  Options
    --name        Name to greet (default: World)
    --uppercase   Print greeting in uppercase
    --formal      Use formal farewell
`,
  {
    // meow v14 requires import.meta for ESM package resolution.
    // Construct a compatible object from __filename for CJS contexts.
    importMeta: { url: pathToFileURL(__filename).href } as ImportMeta,
    flags: {
      name: { type: 'string', default: 'World' },
      uppercase: { type: 'boolean', default: false },
      formal: { type: 'boolean', default: false },
    },
  }
);

const [command] = cli.input;

if (command === 'hello') {
  const msg = `Hello, ${cli.flags.name}!`;
  console.log(cli.flags.uppercase ? msg.toUpperCase() : msg);
} else if (command === 'goodbye') {
  console.log(
    cli.flags.formal
      ? `Farewell, ${cli.flags.name}.`
      : `Bye, ${cli.flags.name}!`
  );
} else {
  cli.showHelp();
}
import { cli } from 'cli-forge';

cli('greet')
  .command('hello', {
    description: 'Say hello to someone',
    builder: (args) =>
      args
        .option('name', {
          type: 'string',
          description: 'Name to greet',
          default: 'World',
        })
        .option('uppercase', {
          type: 'boolean',
          description: 'Print greeting in uppercase',
          default: false,
        }),
    handler: (args) => {
      const msg = `Hello, ${args.name}!`;
      console.log(args.uppercase ? msg.toUpperCase() : msg);
    },
  })
  .command('goodbye', {
    description: 'Say goodbye to someone',
    builder: (args) =>
      args
        .option('name', {
          type: 'string',
          description: 'Name to bid farewell',
          default: 'World',
        })
        .option('formal', {
          type: 'boolean',
          description: 'Use formal farewell',
          default: false,
        }),
    handler: (args) => {
      console.log(
        args.formal ? `Farewell, ${args.name}.` : `Bye, ${args.name}!`
      );
    },
  })
  .forge();

Back to comparison overview · View all framework examples