Skip to content

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:

Terminal window
npm install -g @jeffallan/claude-skills

Or using bun:

Terminal window
bun install -g @jeffallan/claude-skills

Then verify the installation:

Terminal window
claude-skills --version

The 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 folders

The skill guided me through:

  1. Framework selection - Recommended Commander.js for Node.js
  2. Command structure - Design with main command and optional flags
  3. Argument parsing - Handle source directory and target options
  4. Error handling - Validate directories and permissions

Here’s the structure it helped me create:

index.ts
#!/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 automation

The 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.json

The main file:

mytool.ts
#!/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 subcommands
program.addCommand(deploy);
program.addCommand(build);
program.addCommand(test);
program.parse();

Each subcommand file:

commands/deploy.ts
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 tool

The skill suggested using prompts or inquirer for Node.js:

scaffold.ts
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: .mytoolrc
const 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 - Success
  • 1 - General error
  • 2 - Invalid usage
  • 3 - Network error

Common Patterns

Configuration Loading

I use this pattern for config file loading:

config.ts
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);
}
}
}

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