How to Use Cli Developer Skill in Claude Code for DevOps Development
Purpose
This post demonstrates how to use the Cli Developer skill in Claude Code for efficient DevOps development and command-line tool creation.
Environment
- Claude Code CLI
- claude-skills plugin
- Node.js/Bun or Python for CLI tools
- Bash/Shell environment
What is Cli Developer?
The Cli Developer skill helps you build and maintain command-line interfaces and DevOps tools. When I need to create CLI tools, write shell scripts, or automate DevOps tasks, this skill provides specialized patterns and best practices.
The skill focuses on:
- CLI framework selection (Commander.js, Yargs, Click, argparse)
- Argument parsing and validation
- Help text and documentation generation
- Error handling and user feedback
- Subcommand architecture
- Configuration file management
- Interactive prompts and wizards
When should you use this skill? I use it when:
- Creating new CLI tools from scratch
- Adding features to existing command-line applications
- Refactoring CLI code for better maintainability
- Implementing DevOps automation scripts
- Setting up project scaffolding tools
Installation and Setup
To use the Cli Developer skill, you need the claude-skills plugin installed.
First, install the plugin globally:
npm install -g @jeffallan/claude-skillsOr using bun:
bun install -g @jeffallan/claude-skillsThen verify the installation:
claude-skills --versionThe skill activates automatically when you use specific trigger phrases in Claude Code. You don’t need to manually enable it.
Core Usage Patterns
Here are the common ways I invoke the Cli Developer skill:
Direct invocation:
- “Use cli-developer to create a tool for X”
- “I need to build a CLI application with cli-developer”
- “Help me design a command-line interface using cli-developer”
Implicit triggers:
- “Create a CLI tool that…”
- “Build a command-line script for…”
- “Add a subcommand to my CLI app…”
- “Refactor this CLI code…”
The skill understands CLI-specific terminology like:
- Arguments, flags, options
- Subcommands, commands
- Help text, usage information
- Configuration files
- Interactive prompts
Practical Examples
Example 1: Creating a Simple CLI Tool
When I needed to create a file organization tool, I used cli-developer to structure it properly.
My request:
Use cli-developer to create a CLI tool that organizes files by extension into foldersThe skill guided me through:
- Framework selection - Recommended Commander.js for Node.js
- Command structure - Design with main command and optional flags
- Argument parsing - Handle source directory and target options
- Error handling - Validate directories and permissions
Here’s the structure it helped me create:
#!/usr/bin/env node
import { Command } from 'commander';import path from 'path';import fs from 'fs/promises';
const program = new Command();
program .name('organize') .description('Organize files by extension into folders') .version('1.0.0') .argument('<source>', 'Source directory to organize') .option('-d, --dry-run', 'Show changes without applying', false) .option('-v, --verbose', 'Show detailed output', false) .action(async (source, options) => { const sourcePath = path.resolve(source); await organizeFiles(sourcePath, options); });
program.parse();The skill emphasized:
- Clear command name and description
- Version information
- Required argument with angle brackets
- Optional flags with descriptions
- Default values for options
Example 2: Building a Multi-Command CLI
For a DevOps automation tool, I needed multiple subcommands like deploy, build, and test.
My approach:
Use cli-developer to design a multi-command CLI tool for deployment automationThe skill suggested this structure:
mytool/├── bin/│ └── mytool.ts # Main entry point├── commands/│ ├── deploy.ts # Deploy command│ ├── build.ts # Build command│ └── test.ts # Test command├── lib/│ ├── config.ts # Config loading│ └── utils.ts # Shared utilities└── package.jsonThe main file:
#!/usr/bin/env node
import { Command } from 'commander';import { deploy } from './commands/deploy';import { build } from './commands/build';import { test } from './commands/test';
const program = new Command();
program.name('mytool').description('DevOps automation tool');
// Add subcommandsprogram.addCommand(deploy);program.addCommand(build);program.addCommand(test);
program.parse();Each subcommand file:
import { Command } from 'commander';
export const deploy = new Command('deploy') .description('Deploy application to environment') .argument('<env>', 'Target environment (dev|staging|prod)') .option('-t, --tag <version>', 'Docker image tag') .option('-f, --force', 'Skip confirmation prompts') .action(async (env, options) => { console.log(`Deploying to ${env}...`); // Deployment logic });The skill recommended this pattern because:
- Each command is isolated and testable
- Easy to add new commands
- Shared utilities in
lib/ - Clear separation of concerns
Example 3: Adding Interactive Prompts
When I needed to create a project scaffolding tool, I used cli-developer to add interactive prompts.
Use cli-developer to add interactive prompts to my CLI toolThe skill suggested using prompts or inquirer for Node.js:
import prompts from 'prompts';
async function promptUser() { const response = await prompts([ { type: 'text', name: 'projectName', message: 'Project name:', initial: 'my-project', validate: (name) => /^[a-z0-9-]+$/.test(name) || 'Invalid project name' }, { type: 'select', name: 'framework', message: 'Choose framework:', choices: [ { title: 'React', value: 'react' }, { title: 'Vue', value: 'vue' }, { title: 'Svelte', value: 'svelte' } ] }, { type: 'confirm', name: 'typescript', message: 'Use TypeScript?', initial: true } ]);
return response;}Key points the skill emphasized:
- Input validation for text fields
- Clear choices for select prompts
- Sensible defaults
- Type safety for responses
Best Practices
DO
1. Start with help text I write help text first, then implement features. This clarifies the CLI’s purpose before coding.
2. Use standard conventions
- Short flags:
-v,-h,-o - Long options:
--verbose,--help,--output - Required arguments:
<file> - Optional arguments:
[file]
3. Handle errors gracefully
try { await processFile(filePath);} catch (error) { if (error.code === 'ENOENT') { console.error(`Error: File not found: ${filePath}`); process.exit(1); } throw error;}4. Provide clear feedback
- Show progress for long operations
- Display success confirmations
- Explain errors with solutions
5. Support config files
// Check for config file: .mytoolrcconst configPath = path.join(process.cwd(), '.mytoolrc');let config = {};
try { const configContent = await fs.readFile(configPath, 'utf-8'); config = JSON.parse(configContent);} catch { // Use defaults if no config file}DON’T
1. Don’t skip validation Always validate user input:
- File paths exist
- Numbers are in valid ranges
- Enum values match allowed choices
2. Don’t create duplicate commands If a tool does similar things, use subcommands instead of separate CLIs.
3. Don’t hardcode values Use environment variables and config files:
const apiKey = process.env.MYTOOL_API_KEY || config.apiKey;4. Don’t ignore exit codes Return meaningful exit codes:
0- Success1- General error2- Invalid usage3- Network error
Common Patterns
Configuration Loading
I use this pattern for config file loading:
import path from 'path';import { homedir } from 'os';import fs from 'fs/promises';
export async function loadConfig() { // Check multiple locations in priority order const configPaths = [ path.join(process.cwd(), '.mytoolrc'), // Current directory path.join(homedir(), '.mytoolrc'), // Home directory path.join('/etc', 'mytool', 'config.json') // System-wide ];
for (const configPath of configPaths) { try { const content = await fs.readFile(configPath, 'utf-8'); return JSON.parse(content); } catch { continue; // Try next location } }
return {}; // No config found}Progress Indicators
For long-running operations, I show progress:
import { cliProgress } from 'cli-progress';
const bar = new cliProgress.SingleBar({ format: 'Processing |{bar}| {percentage}% | {value}/{total}'});
bar.start(totalFiles, 0);
for (let i = 0; i < files.length; i++) { await processFile(files[i]); bar.update(i + 1);}
bar.stop();Subcommand Registration
When I have many subcommands, I register them dynamically:
import fs from 'fs/promises';import path from 'path';
async function loadCommands(program: Command, commandsDir: string) { const files = await fs.readdir(commandsDir);
for (const file of files) { if (file.endsWith('.ts')) { const commandModule = await import(path.join(commandsDir, file)); program.addCommand(commandModule.default); } }}Related Skills
The Cli Developer skill works well with other claude-skills:
- security-review - Validate CLI tools handle sensitive data securely
- tdd-guide - Test CLI tools with proper coverage
- documentation - Generate help text and man pages
- github-action - Package CLI tools for CI/CD
Summary
In this post, I showed how to use the Cli Developer skill in Claude Code to create command-line tools and DevOps automation scripts. The key point is knowing when to invoke this skill and following its patterns for CLI architecture, argument parsing, and error handling.
The skill helps me avoid common mistakes like poor error handling, confusing command structures, and missing validation. By using it from the start, I build CLIs that are maintainable and user-friendly.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments