cli-tool-development

Build professional CLI tools with Node.js, commander, and Ink

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cli-tool-development" with this command: npx skills add autohandai/community-skills/autohandai-community-skills-cli-tool-development

CLI Tool Development

Project Structure

src/
  index.ts          # Entry point with shebang
  cli.ts            # Commander setup
  commands/         # Command handlers
  ui/               # Ink components (if interactive)
  utils/            # Helpers
  types.ts          # Type definitions

Commander.js Setup

#!/usr/bin/env node
import { Command } from 'commander';
import packageJson from '../package.json' with { type: 'json' };

const program = new Command();

program
  .name('mytool')
  .description('My awesome CLI tool')
  .version(packageJson.version);

program
  .command('init')
  .description('Initialize a new project')
  .option('-t, --template <name>', 'Template to use', 'default')
  .option('-f, --force', 'Overwrite existing files', false)
  .action(async (options) => {
    await initCommand(options);
  });

program.parseAsync();

User Feedback with Chalk & Ora

import chalk from 'chalk';
import ora from 'ora';

// Status messages
console.log(chalk.green('✓') + ' Operation complete');
console.log(chalk.red('✗') + ' Operation failed');
console.log(chalk.yellow('⚠') + ' Warning message');

// Progress spinner
const spinner = ora('Loading...').start();
try {
  await longOperation();
  spinner.succeed('Done!');
} catch (error) {
  spinner.fail('Failed');
}

Interactive Prompts with Enquirer

import enquirer from 'enquirer';

const { name } = await enquirer.prompt<{ name: string }>({
  type: 'input',
  name: 'name',
  message: 'Project name:',
  validate: (v) => v.length > 0 || 'Name required',
});

const { confirm } = await enquirer.prompt<{ confirm: boolean }>({
  type: 'confirm',
  name: 'confirm',
  message: 'Continue?',
  initial: true,
});

Ink for Rich TUI

import React, { useState } from 'react';
import { render, Box, Text, useInput } from 'ink';

function App() {
  const [selected, setSelected] = useState(0);
  const items = ['Option 1', 'Option 2', 'Option 3'];

  useInput((input, key) => {
    if (key.downArrow) setSelected(s => Math.min(s + 1, items.length - 1));
    if (key.upArrow) setSelected(s => Math.max(s - 1, 0));
    if (key.return) process.exit(0);
  });

  return (
    <Box flexDirection="column">
      {items.map((item, i) => (
        <Text key={i} color={i === selected ? 'cyan' : undefined}>
          {i === selected ? '>' : ' '} {item}
        </Text>
      ))}
    </Box>
  );
}

render(<App />);

Configuration Management

import fs from 'fs-extra';
import path from 'node:path';
import os from 'node:os';

const CONFIG_DIR = path.join(os.homedir(), '.mytool');
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');

async function loadConfig(): Promise<Config> {
  await fs.ensureDir(CONFIG_DIR);
  if (await fs.pathExists(CONFIG_FILE)) {
    return fs.readJson(CONFIG_FILE);
  }
  return getDefaultConfig();
}

async function saveConfig(config: Config): Promise<void> {
  await fs.writeJson(CONFIG_FILE, config, { spaces: 2 });
}

Error Handling

// Graceful exit handling
process.on('SIGINT', () => {
  console.log('\nCancelled');
  process.exit(0);
});

// Top-level error handler
try {
  await program.parseAsync();
} catch (error) {
  console.error(chalk.red('Error:'), error.message);
  process.exit(1);
}

Package.json Setup

{
  "bin": {
    "mytool": "./dist/index.js"
  },
  "files": ["dist"],
  "type": "module",
  "scripts": {
    "build": "tsup src/index.ts --format esm --dts",
    "dev": "tsx src/index.ts"
  }
}

Best Practices

  1. Add #!/usr/bin/env node shebang to entry file
  2. Support both flags (-f) and options (--force)
  3. Provide helpful error messages with suggestions
  4. Support --help and --version flags
  5. Use exit codes: 0 for success, 1 for error
  6. Support piping and stdin when appropriate
  7. Respect NO_COLOR environment variable

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

typescript-refactoring-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

python-fastapi-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

tailwind-ui-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

api-design-restful

No summary provided by upstream source.

Repository SourceNeeds Review