Development
cli-toolchain - Claude MCP Skill
Apply modern CLI development toolchain patterns: Commander.js (default), oclif, Ink for Node.js command-line tools. Use when building CLI applications, choosing CLI frameworks, or discussing terminal UX.
SEO Guide: Enhance your AI agent with the cli-toolchain tool. This Model Context Protocol (MCP) server allows Claude Desktop and other LLMs to apply modern cli development toolchain patterns: commander.js (default), oclif, ink for node.js comm... Download and configure this skill to unlock new capabilities for your AI workflow.
Documentation
SKILL.md# CLI Toolchain
Modern command-line application development with Node.js and TypeScript.
## Recommended Stack: Commander.js (Default)
**Why Commander.js (2025):**
- Lightweight library (not a framework)
- Unopinionated (full control over structure)
- Standard in Node.js ecosystem (24M+ downloads/week)
- Excellent TypeScript support
- Simple API for argument parsing and subcommands
- Battle-tested (used by Vue CLI, Create React App, etc.)
```bash
# Install
npm install commander
# Create CLI entry point
```
```typescript
// bin/cli.ts
#!/usr/bin/env node
import { Command } from 'commander'
import { version } from '../package.json'
const program = new Command()
program
.name('my-cli')
.description('CLI tool for awesome things')
.version(version)
program
.command('create <name>')
.description('Create a new project')
.option('-t, --template <type>', 'Project template', 'default')
.action((name, options) => {
console.log(`Creating project: ${name}`)
console.log(`Template: ${options.template}`)
})
program.parse()
```
### When to Use Commander.js
ā
Standard CLIs with subcommands and options
ā
Want lightweight, minimal overhead
ā
Need full control over implementation
ā
Simple argument parsing requirements
ā
Most use cases (90%+)
## Alternative: oclif
**Enterprise-grade CLI framework:**
- Full framework (not just parsing)
- Plugin system
- Auto-generated documentation
- Testing utilities
- Used by Salesforce, Heroku CLIs
```bash
# Create new CLI with oclif
npx oclif generate my-cli
# Structure enforced by framework
my-cli/
āāā src/
ā āāā commands/ # Command files
ā āāā hooks/ # Lifecycle hooks
āāā test/
āāā package.json
```
### When to Use oclif
ā
Large CLIs with many commands (20+)
ā
Need plugin architecture
ā
Auto-documentation required
ā
Enterprise/team projects
ā
Want opinionated structure
## Alternative: Ink
**React for CLIs:**
- Build interactive UIs with React components
- Flexbox layout for terminal
- Rich, interactive experiences (dashboards, progress, forms)
```bash
# Install
npm install ink react
# Create interactive CLI
```
```tsx
// bin/ui.tsx
import React from 'react'
import { render, Box, Text } from 'ink'
const App = () => (
<Box flexDirection="column">
<Text color="green">ā Task completed</Text>
<Text>Processing...</Text>
</Box>
)
render(<App />)
```
### When to Use Ink
ā
Rich interactive UI needed (dashboards, loaders)
ā
Complex terminal layouts
ā
Team familiar with React
ā ļø Overkill for simple CLIs (use Commander.js)
## Toolchain Comparison
| | Commander.js | oclif | Ink |
|---|---|---|---|
| **Type** | Library | Framework | UI Library |
| **Setup** | Minimal | Scaffold | Manual |
| **Use Case** | General purpose | Large/complex | Interactive UI |
| **Learning Curve** | Low | Medium | Medium (React) |
| **Bundle Size** | Small | Large | Medium |
| **Flexibility** | High | Medium | High |
| **Documentation** | Good | Excellent | Good |
## Project Structure (Commander.js)
```
my-cli/
āāā src/
ā āāā commands/ # Command implementations
ā ā āāā create.ts
ā ā āāā build.ts
ā ā āāā deploy.ts
ā āāā utils/ # Shared utilities
ā ā āāā logger.ts
ā ā āāā config.ts
ā ā āāā spinner.ts
ā āāā types/ # TypeScript types
ā āāā index.ts # Main CLI entry
āāā bin/
ā āāā cli # Executable (symlink to dist)
āāā package.json
āāā tsconfig.json
```
## Essential Patterns
### Subcommands
```typescript
// src/index.ts
import { Command } from 'commander'
import { createCommand } from './commands/create'
import { buildCommand } from './commands/build'
const program = new Command()
program
.addCommand(createCommand)
.addCommand(buildCommand)
program.parse()
```
```typescript
// src/commands/create.ts
import { Command } from 'commander'
export const createCommand = new Command('create')
.description('Create a new project')
.argument('<name>', 'Project name')
.option('-t, --template <type>', 'Template type', 'default')
.action(async (name, options) => {
console.log(`Creating: ${name}`)
// Implementation
})
```
### Arguments & Options
```typescript
program
.command('deploy')
// Required argument
.argument('<app>', 'Application to deploy')
// Optional argument with default
.argument('[environment]', 'Environment', 'production')
// Boolean option
.option('-d, --dry-run', 'Dry run mode')
// Option with value
.option('-r, --region <region>', 'Deployment region', 'us-east-1')
// Option with choices
.option('-l, --log-level <level>', 'Log level', 'info')
.choices(['debug', 'info', 'warn', 'error'])
// Variadic option (multiple values)
.option('-e, --env <pairs...>', 'Environment variables')
.action((app, environment, options) => {
console.log({ app, environment, ...options })
})
```
### Interactive Prompts
```bash
# Install prompts library
npm install inquirer @types/inquirer
```
```typescript
import inquirer from 'inquirer'
program
.command('init')
.action(async () => {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: 'Project name:',
default: 'my-app',
},
{
type: 'list',
name: 'template',
message: 'Choose template:',
choices: ['React', 'Vue', 'Vanilla'],
},
{
type: 'confirm',
name: 'useTypeScript',
message: 'Use TypeScript?',
default: true,
},
])
console.log('Creating project with:', answers)
})
```
### Progress & Spinners
```bash
npm install ora chalk
```
```typescript
import ora from 'ora'
import chalk from 'chalk'
async function deploy() {
const spinner = ora('Deploying application...').start()
try {
await performDeploy()
spinner.succeed(chalk.green('Deployed successfully!'))
} catch (error) {
spinner.fail(chalk.red('Deployment failed'))
console.error(error)
process.exit(1)
}
}
```
### Configuration Files
```typescript
// src/utils/config.ts
import { cosmiconfigSync } from 'cosmiconfig'
export function loadConfig() {
const explorer = cosmiconfigSync('my-cli')
const result = explorer.search()
if (!result) {
return {} // Default config
}
return result.config
}
// Looks for:
// - .my-clirc
// - .my-clirc.json
// - .my-clirc.yaml
// - my-cli.config.js
// - "my-cli" field in package.json
```
## Essential Libraries
```bash
# Argument parsing (if not using Commander.js)
npm install yargs
# Interactive prompts
npm install inquirer
# Terminal styling
npm install chalk
# Progress indicators
npm install ora cli-progress
# Tables
npm install cli-table3
# File system utilities
npm install fs-extra
# Configuration loading
npm install cosmiconfig
```
## Testing Strategy
```bash
npm install --save-dev vitest @types/node
```
```typescript
// src/commands/create.test.ts
import { describe, it, expect, vi } from 'vitest'
import { execSync } from 'child_process'
describe('create command', () => {
it('creates project with default template', () => {
const output = execSync('node dist/index.js create test-app')
expect(output.toString()).toContain('Creating project: test-app')
})
it('uses specified template', () => {
const output = execSync('node dist/index.js create test-app --template react')
expect(output.toString()).toContain('Template: react')
})
})
```
## Publishing to npm
```json
// package.json
{
"name": "my-cli",
"version": "1.0.0",
"bin": {
"my-cli": "./dist/index.js"
},
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"prepublishOnly": "npm run build"
}
}
```
```bash
# Build
npm run build
# Test locally
npm link
my-cli --version
# Publish
npm publish
```
## Quality Gates Integration
```yaml
# .github/workflows/cli-ci.yml
name: CLI CI
on: [pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm ci
- run: npm run build
- run: npm test
publish:
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npm run build
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```
## Error Handling
```typescript
import chalk from 'chalk'
program
.command('risky-operation')
.action(async () => {
try {
await performOperation()
} catch (error) {
console.error(chalk.red('Error:'), error.message)
if (process.env.DEBUG) {
console.error(error.stack)
}
process.exit(1)
}
})
// Global error handler
program.exitOverride((err) => {
if (err.code === 'commander.unknownCommand') {
console.error(chalk.red('Unknown command. See --help for available commands.'))
process.exit(1)
}
throw err
})
```
## Performance Optimization
```typescript
// Lazy load heavy dependencies
program
.command('build')
.action(async () => {
// Only import when command is used
const { build } = await import('./commands/build')
await build()
})
// Use dynamic imports for optional features
if (options.analyze) {
const { analyze } = await import('./utils/analyzer')
await analyze()
}
```
## UX Best Practices
- **Clear help text**: Use `.description()` liberally
- **Sensible defaults**: Minimize required options
- **Consistent naming**: Use kebab-case for flags
- **Progress feedback**: Show spinners for long operations
- **Color sparingly**: Red for errors, green for success, yellow for warnings
- **Respect --quiet**: Suppress non-critical output
- **Support --help**: Always implement comprehensive help
- **Version info**: Include `--version` flag
## Recommendation Flow
```
New CLI tool:
āā Standard commands/options ā Commander.js ā
āā Large enterprise CLI (20+ commands) ā oclif
āā Rich interactive UI needed ā Ink
Combine approaches:
āā Commander.js + Ink ā Interactive commands when needed
āā Commander.js + inquirer ā Simple prompts
```
When agents design CLI tools, they should:
- Default to Commander.js for most use cases
- Use inquirer for interactive prompts
- Apply chalk sparingly for colored output
- Show ora spinners for long operations
- Load config with cosmiconfig
- Apply quality-gates skill for testing/CI
- Test on multiple OS (Linux, macOS, Windows)
- Publish to npm with proper bin configuration
- Lazy load heavy dependencies for performance
- Provide comprehensive --help documentationSignals
Information
- Repository
- phrazzld/claude-config
- Author
- phrazzld
- Last Sync
- 1/24/2026
- Repo Updated
- 1/17/2026
- Created
- 1/13/2026
Reviews (0)
No reviews yet. Be the first to review this skill!
Related Skills
upgrade-nodejs
Upgrading Bun's Self-Reported Node.js Version
cursorrules
CrewAI Development Rules
cn-check
Install and run the Continue CLI (`cn`) to execute AI agent checks on local code changes. Use when asked to "run checks", "lint with AI", "review my changes with cn", or set up Continue CI locally.
CLAUDE
CLAUDE.md
Related Guides
Bear Notes Claude Skill: Your AI-Powered Note-Taking Assistant
Learn how to use the bear-notes Claude skill. Complete guide with installation instructions and examples.
Mastering tmux with Claude: A Complete Guide to the tmux Claude Skill
Learn how to use the tmux Claude skill. Complete guide with installation instructions and examples.
OpenAI Whisper API Claude Skill: Complete Guide to AI-Powered Audio Transcription
Learn how to use the openai-whisper-api Claude skill. Complete guide with installation instructions and examples.