# CLI Tools: Build Command Line Apps

# 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

```bash
npm init -y
npm install commander inquirer chalk
```

### 2. Simple Command Structure

```javascript
// 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:**
```bash
node cli.js hello World
# Output: Hello, World!
```

### 3. Commands with Options

```javascript
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:**
```bash
node cli.js create myapp --template react --git
```

### 4. Interactive Prompts

```javascript
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

```javascript
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

```javascript
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

```javascript
// 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

```javascript
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

```javascript
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

```javascript
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

| Feature | Benefit |
|---------|---------|
| **Commander** | Elegant argument parsing, subcommands, options |
| **Inquirer** | Interactive prompts, validation, conditional logic |
| **Chalk** | Colored output for better UX |
| **Validation** | Input validation prevents errors |
| **Subcommands** | Organize complex CLIs hierarchically |
| **Async/Await** | Handle async operations cleanly |

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