Skip to main content

Command Palette

Search for a command to run...

CLI Tools: Build Command Line Apps

Learn: CLI Tools: Build Command Line Apps

Updated
β€’4 min readβ€’View as Markdown
T

Welcome to TopperBlog! πŸ‘‹

I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.

🎯 What I Write About: β€’ AI/ML Engineering & LLMs β€’ Web3 & Blockchain Development
β€’ System Design & Architecture β€’ Interview Preparation (FAANG) β€’ Freelancing & Remote Work β€’ Modern Tech Stacks (Next.js, React, Rust, TypeScript) β€’ Performance Optimization & Best Practices

πŸ’Ό Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.

πŸ“š 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.

🌐 Let's connect and grow together in this amazing tech journey!

#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering

CLI Tools: Build Command Line Apps with Commander and Inquirer

Problem

Building command-line applications requires handling complex argument parsing, interactive prompts, and user input validation. Without proper libraries, developers must manually parse process arguments, manage state, and create interactive experiencesβ€”leading to repetitive, error-prone code.

Solution

Commander.js provides elegant command parsing and option handling, while Inquirer.js enables interactive prompts and user input collection. Together, they create professional CLI applications with minimal boilerplate.

Code Examples

1. Basic Setup and Installation

npm init -y
npm install commander inquirer chalk

2. Simple Command Structure

// cli.js
const { program } = require('commander');
const inquirer = require('inquirer');
const chalk = require('chalk');

program
  .name('mytool')
  .description('A powerful CLI application')
  .version('1.0.0');

program
  .command('hello <name>')
  .description('Greet someone')
  .action((name) => {
    console.log(chalk.blue(`Hello, ${name}!`));
  });

program.parse(process.argv);

Usage:

node cli.js hello World
# Output: Hello, World!

3. Commands with Options

program
  .command('create <project>')
  .description('Create a new project')
  .option('-t, --template <type>', 'Project template', 'basic')
  .option('-g, --git', 'Initialize git repository')
  .action((project, options) => {
    console.log(chalk.green(`βœ“ Creating project: ${project}`));
    console.log(`  Template: ${options.template}`);
    if (options.git) {
      console.log('  Git initialized');
    }
  });

Usage:

node cli.js create myapp --template react --git

4. Interactive Prompts

program
  .command('setup')
  .description('Interactive setup wizard')
  .action(async () => {
    const answers = await inquirer.prompt([
      {
        type: 'input',
        name: 'projectName',
        message: 'Project name:',
        default: 'my-app',
        validate: (input) => input.length > 0 || 'Name required'
      },
      {
        type: 'list',
        name: 'framework',
        message: 'Choose framework:',
        choices: ['React', 'Vue', 'Angular', 'Svelte']
      },
      {
        type: 'checkbox',
        name: 'features',
        message: 'Select features:',
        choices: ['TypeScript', 'ESLint', 'Prettier', 'Testing']
      },
      {
        type: 'confirm',
        name: 'install',
        message: 'Install dependencies?',
        default: true
      }
    ]);

    console.log(chalk.green('\nβœ“ Configuration:'));
    console.log(JSON.stringify(answers, null, 2));
  });

5. Conditional Prompts

program
  .command('deploy')
  .description('Deploy application')
  .action(async () => {
    const answers = await inquirer.prompt([
      {
        type: 'list',
        name: 'environment',
        message: 'Deploy to:',
        choices: ['staging', 'production']
      },
      {
        type: 'input',
        name: 'version',
        message: 'Version number:',
        when: (answers) => answers.environment === 'production',
        validate: (input) => /^\d+\.\d+\.\d+$/.test(input) || 'Use semver format'
      },
      {
        type: 'confirm',
        name: 'backup',
        message: 'Create backup?',
        when: (answers) => answers.environment === 'production',
        default: true
      }
    ]);

    console.log(chalk.yellow(`\n→ Deploying to ${answers.environment}...`));
    // Deployment logic
  });

6. Subcommands

const dbCommand = program
  .command('db')
  .description('Database operations');

dbCommand
  .command('migrate')
  .description('Run migrations')
  .action(() => {
    console.log(chalk.blue('Running migrations...'));
  });

dbCommand
  .command('seed')
  .description('Seed database')
  .option('-f, --force', 'Force seed')
  .action((options) => {
    console.log(chalk.blue('Seeding database...'));
  });

dbCommand
  .command('reset')
  .description('Reset database')
  .action(async () => {
    const { confirm } = await inquirer.prompt([
      {
        type: 'confirm',
        name: 'confirm',
        message: chalk.red('This will delete all data. Continue?'),
        default: false
      }
    ]);

    if (confirm) {
      console.log(chalk.green('Database reset'));
    }
  });

7. Complete Project Generator Example

// cli.js - Full example
const { program } = require('commander');
const inquirer = require('inquirer');
const chalk = require('chalk');
const fs = require('fs');
const path = require('path');

program
  .name('scaffold')
  .description('Project scaffolding tool')
  .version('1.0.0');

program
  .command('generate')
  .description('Generate new project')
  .action(async () => {
    console.log(chalk.cyan('\nπŸš€ Project Generator\n'));

    const answers = await inquirer.prompt([
      {
        type: 'input',
        name: 'name',
        message: 'Project name:',
        validate: (input) => {
          if (!input) return 'Name required';
          if (!/^[a-z0-9-]+$/.test(input)) return 'Use lowercase and hyphens';
          return true;
        }
      },
      {
        type: 'list',
        name: 'type',
        message: 'Project type:',
        choices: ['web', 'api', 'library', 'cli']
      },
      {
        type: 'checkbox',
        name: 'tools',
        message: 'Development tools:',
        choices: [
          { name: 'TypeScript', checked: true },
          { name: 'ESLint', checked: true },
          { name: 'Jest', checked: false },
          { name: 'Docker', checked: false }
        ]
      },
      {
        type: 'confirm',
        name: 'git',
        message: 'Initialize git?',
        default: true
      }
    ]);

    // Create project structure
    const projectPath = path.join(process.cwd(), answers.name);

    if (!fs.existsSync(projectPath)) {
      fs.mkdirSync(projectPath, { recursive: true });
    }

    // Create package.json
    const packageJson = {
      name: answers.name,
      version: '1.0.0',
      type: answers.type,
      tools: answers.tools
    };

    fs.writeFileSync(
      path.join(projectPath, 'package.json'),
      JSON.stringify(packageJson, null, 2)
    );

    console.log(chalk.green(`\nβœ“ Project created at ${projectPath}`));
    console.log(chalk.gray(`\nNext steps:`));
    console.log(chalk.gray(`  cd ${answers.name}`));
    console.log(chalk.gray(`  npm install`));
  });

program.parse(process.argv);

8. Error Handling and Validation

program
  .command('config <key> <value>')
  .description('Set configuration')
  .action((key, value) => {
    try {
      if (!key || !value) {
        throw new Error('Key and value required');
      }

      // Validate key format
      if (!/^[a-z.]+$/.test(key)) {
        throw new Error('Invalid key format');
      }

      console.log(chalk.green(`βœ“ Config set: ${key} = ${value}`));
    } catch (error) {
      console.error(chalk.red(`βœ— Error: ${error.message}`));
      process.exit(1);
    }
  });

9. Progress and Spinners

const ora = require('ora');

program
  .command('build')
  .description('Build project')
  .action(async () => {
    const spinner = ora('Building...').start();

    try {
      await new Promise(resolve => setTimeout(resolve, 2000));
      spinner.succeed('Build complete');
      console.log(chalk.green('βœ“ Ready for deployment'));
    } catch (error) {
      spinner.fail('Build failed');
      console.error(chalk.red(error.message));
    }
  });

10. Help and Documentation

program
  .command('help-custom')
  .description('Show detailed help')
  .action(() => {
    console.log(chalk.cyan(`
╔════════════════════════════════════════╗
β•‘         CLI Tool Documentation         β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Commands:
  create <name>    Create new project
  setup            Interactive setup
  deploy           Deploy application
  db <action>      Database operations

Options:
  -h, --help       Show help
  -v, --version    Show version

Examples:
  $ mytool create myapp --template react
  $ mytool setup
  $ mytool deploy --environment production
    `));
  });

program.parse(process.argv);

Key Takeaways

FeatureBenefit
CommanderElegant argument parsing, subcommands, options
InquirerInteractive prompts, validation, conditional logic
ChalkColored output for better UX
ValidationInput validation prevents errors
SubcommandsOrganize complex CLIs hierarchically
Async/AwaitHandle async operations cleanly

These tools transform CLI development from tedious to enjoyable, enabling professional applications with minimal code.