Building CLI applications with Node.js, Inquirer, and Commander
Build command-line interface applications using Node.js and JavaScript/TypeScript. Focus on Inquirer for interactive prompts, Commander for argument parsing, and Chalk for terminal styling.
When building Node.js CLI tools, automation scripts, scaffolding tools, or interactive command-line applications.
| Package | Purpose |
|---|---|
@inquirer/prompts | Interactive prompts |
commander | CLI argument parsing |
chalk | Terminal string styling |
yargs | CLI builder |
oclif |
| Enterprise CLI framework |
picocolors | Fast color library |
npm install @inquirer/prompts commander chalk
# or
pnpm add @inquirer/prompts commander chalk
import { input, select, confirm, checkbox } from '@inquirer/prompts';
// Simple input
const name = await input({
message: "What's your name?",
default: "Anonymous"
});
// Selection
const framework = await select({
message: "Choose a framework:",
choices: [
{ name: "React", value: "react" },
{ name: "Vue", value: "vue" },
{ name: "Svelte", value: "svelte" }
]
});
// Confirmation
const proceed = await confirm({
message: "Do you want to continue?",
default: true
});
// Multiple selection
const features = await checkbox({
message: "Select features:",
choices: ["Auth", "Database", "Testing", "Deployment"],
default: ["Auth", "Testing"]
});
import { input, select, confirm } from '@inquirer/prompts';
import { cyan, bold } from 'chalk';
async function createProject() {
console.log(bold.cyan('Project Creator'));
console.log('-------------------\n');
const name = await input({
message: 'Project name:',
validate: (value) => /^[a-z0-9-]+$/.test(value)
? true
: 'Use lowercase letters, numbers, and hyphens only'
});
const template = await select({
message: 'Choose a template:',
choices: [
{ name: 'JavaScript', value: 'js' },
{ name: 'TypeScript', value: 'ts' },
{ name: 'Node.js API', value: 'api' }
]
});
const useTesting = await confirm({
message: 'Include testing framework?',
default: true
});
console.log(`\nCreating ${name} with ${template} template...`);
if (useTesting) {
console.log('Adding testing support...');
}
console.log(bold.green('Done!'));
}
createProject().catch(console.error);
import inquirer from 'inquirer';
const answers = await inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Project name:',
default: 'my-project'
},
{
type: 'list',
name: 'framework',
message: 'Choose framework:',
choices: ['React', 'Vue', 'Angular', 'Svelte']
},
{
type: 'confirm',
name: 'typescript',
message: 'Use TypeScript?',
default: true
},
{
type: 'checkbox',
name: 'features',
message: 'Select features:',
choices: ['Router', 'State Management', 'Testing', 'CSS Framework']
}
]);
import { Command } from 'commander';
const program = new Command();
program
.name('myapp')
.description('My awesome CLI application')
.version('1.0.0');
program
.command('init')
.description('Initialize a new project')
.argument('<name>', 'Project name')
.option('-t, --template <type>', 'Project template', 'default')
.action((name, options) => {
console.log(`Initializing ${name} with ${options.template} template`);
});
program
.command('build')
.description('Build the project')
.option('-p, --prod', 'Production build')
.option('-w, --watch', 'Watch mode')
.action((options) => {
console.log('Building...', options);
});
program.parse();
import { Command } from 'commander';
const program = new Command();
// config group
program
.command('config')
.description('Configuration commands')
.addCommand(
new Command('set')
.argument('<key>', 'Config key')
.argument('<value>', 'Config value')
.action((key, value) => {
console.log(`Setting ${key} = ${value}`);
})
)
.addCommand(
new Command('get')
.argument('<key>', 'Config key')
.action((key) => {
console.log(`Getting ${key}`);
})
)
.addCommand(
new Command('list')
.action(() => {
console.log('Listing all config');
})
);
program.parse();
program
.command('deploy')
.option('-e, --env <env>', 'Environment', 'production')
.option('-r, --replicas <count>', 'Number of replicas', '1')
.option('--dry-run', 'Dry run mode')
.option('-v, --verbose', 'Verbose output')
.action((options) => {
console.log('Deploying to:', options.env);
console.log('Replicas:', options.replicas);
if (options.dryRun) console.log('DRY RUN MODE');
if (options.verbose) console.log('Verbose output enabled');
});
import chalk from 'chalk';
// Text styling
console.log(chalk.red('Error message'));
console.log(chalk.green('Success!'));
console.log(chalk.yellow('Warning'));
console.log(chalk.blue('Info'));
// Combinations
console.log(chalk.bold.cyan('Title'));
console.log(chalk.bgRed.white('Highlighted'));
console.log(chalk.underline('Underlined'));
// Template literals
console.log(`
${chalk.green('✓')} Success: Operation completed
${chalk.red('✗')} Error: Something went wrong
${chalk.yellow('⚠')} Warning: Check configuration
`);
// Custom themes
const theme = {
title: chalk.bold.blue,
subtitle: chalk.cyan,
success: chalk.green,
error: chalk.red,
warning: chalk.yellow
};
console.log(theme.title('My Application'));
console.log(theme.subtitle('Version 1.0.0'));
// RGB
console.log(chalk.rgb(255, 128, 0)('Orange text'));
// Hex
console.log(chalk.hex('#FF8800')('Orange text'));
// Custom gradient
console.log(chalk.hex('#FF0000')('Red') +
chalk.hex('#FFFF00')('Yellow') +
chalk.hex('#00FF00')('Green'));
#!/usr/bin/env node
import { Command } from 'commander';
import { input, select, confirm } from '@inquirer/prompts';
import chalk from 'chalk';
import fs from 'fs/promises';
import path from 'path';
const program = new Command();
program
.name('project-cli')
.description('Project scaffolding CLI')
.version('1.0.0');
// init command with prompts
program
.command('init')
.description('Initialize a new project')
.action(async () => {
console.log(chalk.bold.cyan('\n Project Creator \n'));
const projectName = await input({
message: 'Project name:',
validate: (v) => v.length > 0 || 'Name is required'
});
const template = await select({
message: 'Choose template:',
choices: [
{ name: 'JavaScript (ESM)', value: 'js' },
{ name: 'TypeScript', value: 'ts' },
{ name: 'Node.js API', value: 'api' }
]
});
const addTesting = await confirm({
message: 'Add testing?',
default: true
});
console.log(chalk.yellow('\nCreating project...'));
// Create project structure
await fs.mkdir(projectName, { recursive: true });
const pkg = {
name: projectName,
version: '0.1.0',
type: 'module',
scripts: {
test: addTesting ? 'node --test' : undefined
}
};
await fs.writeFile(
path.join(projectName, 'package.json'),
JSON.stringify(pkg, null, 2)
);
console.log(chalk.green(`\n✓ Project ${projectName} created!`));
});
program.parse();
import { jest } from '@jest/globals';
// Mock inquirer for testing
jest.mock('@inquirer/prompts', () => ({
input: jest.fn().mockResolvedValue('test-project'),
select: jest.fn().mockResolvedValue('ts'),
confirm: jest.fn().mockResolvedValue(true)
}));
test('creates project with prompts', async () => {
await createProject();
expect(fs.mkdir).toHaveBeenCalledWith('test-project', expect.any(Object));
});
# Bash
myapp completion bash > /etc/bash_completion.d/myapp
# Zsh
myapp completion zsh > ~/.zsh/completions/_myapp
# Fish
myapp completion fish > ~/.config/fish/completions/myapp.fish
"type": "module") for modern importsvalidate option--no-color flagprocess.exit(1) for errors#!/usr/bin/env node