
Developer Tools
- 66 installs
- 19 repo stars
- Updated January 20, 2026
- miles990/claude-software-skills
Helps with ai & agent building tasks.
About
developer-tools is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- developer-tools
- AI & Agent Building
- AI-coding skill
Developer Tools by the numbers
- 66 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #5,968 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/miles990/claude-software-skills --skill developer-toolsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 66 |
|---|---|
| repo stars | ★ 19 |
| Last updated | January 20, 2026 |
| Repository | miles990/claude-software-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Developer Tools
Overview
Building command-line interfaces, SDKs, and tools that enhance developer experience.
---
CLI Development
CLI Framework (Commander)
#!/usr/bin/env node
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import inquirer from 'inquirer';
const program = new Command();
program
.name('myctl')
.description('CLI tool for managing resources')
.version('1.0.0');
// Simple command
program
.command('init')
.description('Initialize a new project')
.option('-t, --template <name>', 'Template to use', 'default')
.option('-d, --directory <path>', 'Target directory', '.')
.action(async (options) => {
const spinner = ora('Initializing project...').start();
try {
await initProject(options.template, options.directory);
spinner.succeed(chalk.green('Project initialized successfully!'));
} catch (error) {
spinner.fail(chalk.red(`Failed: ${error.message}`));
process.exit(1);
}
});
// Interactive command
program
.command('create <name>')
.description('Create a new resource')
.action(async (name) => {
const answers = await inquirer.prompt([
{
type: 'list',
name: 'type',
message: 'Select resource type:',
choices: ['api', 'worker', 'database'],
},
{
type: 'input',
name: 'description',
message: 'Description:',
},
{
type: 'confirm',
name: 'public',
message: 'Make it public?',
default: false,
},
]);
await createResource(name, answers);
console.log(chalk.green(`Created ${answers.type}: ${name}`));
});
// Subcommands
const configCmd = program.command('config').description('Manage configuration');
configCmd
.command('set <key> <value>')
.description('Set a config value')
.action(async (key, value) => {
await setConfig(key, value);
console.log(`Set ${key}=${value}`);
});
configCmd
.command('get <key>')
.description('Get a config value')
.action(async (key) => {
const value = await getConfig(key);
console.log(value);
});
configCmd
.command('list')
.description('List all config values')
.action(async () => {
const config = await getAllConfig();
console.table(config);
});
// Global options
program
.option('--debug', 'Enable debug mode')
.option('--json', 'Output as JSON')
.hook('preAction', (thisCommand) => {
if (thisCommand.opts().debug) {
process.env.DEBUG = 'true';
}
});
program.parse();CLI Output Formatting
import Table from 'cli-table3';
import boxen from 'boxen';
// Table output
function printTable(data: Array<Record<string, any>>, columns: string[]) {
const table = new Table({
head: columns.map((c) => chalk.bold(c)),
style: { head: ['cyan'] },
});
data.forEach((row) => {
table.push(columns.map((col) => row[col] ?? ''));
});
console.log(table.toString());
}
// JSON output
function printJson(data: any) {
console.log(JSON.stringify(data, null, 2));
}
// Box output
function printBox(message: string, title?: string) {
console.log(
boxen(message, {
padding: 1,
margin: 1,
borderStyle: 'round',
title,
titleAlignment: 'center',
})
);
}
// Progress bar
import cliProgress from 'cli-progress';
async function withProgress<T>(
items: T[],
fn: (item: T) => Promise<void>,
label: string
) {
const bar = new cliProgress.SingleBar({
format: `${label} |{bar}| {percentage}% | {value}/{total}`,
barCompleteChar: '\u2588',
barIncompleteChar: '\u2591',
});
bar.start(items.length, 0);
for (const item of items) {
await fn(item);
bar.increment();
}
bar.stop();
}---
SDK Development
TypeScript SDK
// sdk/index.ts
export class MyServiceClient {
private baseUrl: string;
private apiKey: string;
constructor(options: { apiKey: string; baseUrl?: string }) {
this.apiKey = options.apiKey;
this.baseUrl = options.baseUrl || 'https://api.example.com';
}
private async request<T>(
method: string,
path: string,
data?: any
): Promise<T> {
const response = await fetch(`${this.baseUrl}${path}`, {
method,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiKey}`,
},
body: data ? JSON.stringify(data) : undefined,
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new ApiError(response.status, error.message || 'Request failed');
}
return response.json();
}
// Resource: Users
users = {
list: (params?: ListUsersParams) =>
this.request<PaginatedResponse<User>>('GET', '/users?' + qs(params)),
get: (id: string) => this.request<User>('GET', `/users/${id}`),
create: (data: CreateUserInput) =>
this.request<User>('POST', '/users', data),
update: (id: string, data: UpdateUserInput) =>
this.request<User>('PUT', `/users/${id}`, data),
delete: (id: string) => this.request<void>('DELETE', `/users/${id}`),
};
// Resource: Projects
projects = {
list: () => this.request<Project[]>('GET', '/projects'),
get: (id: string) => this.request<Project>('GET', `/projects/${id}`),
create: (data: CreateProjectInput) =>
this.request<Project>('POST', '/projects', data),
};
}
// Error class
export class ApiError extends Error {
constructor(
public status: number,
message: string,
public code?: string
) {
super(message);
this.name = 'ApiError';
}
}
// Types
export interface User {
id: string;
email: string;
name: string;
createdAt: string;
}
export interface CreateUserInput {
email: string;
name: string;
}
export interface UpdateUserInput {
name?: string;
}
export interface PaginatedResponse<T> {
data: T[];
meta: {
page: number;
limit: number;
total: number;
};
}
// Usage
const client = new MyServiceClient({ apiKey: 'sk_...' });
const users = await client.users.list({ page: 1, limit: 10 });
const user = await client.users.create({ email: 'test@example.com', name: 'Test' });SDK with Retry and Rate Limiting
import pRetry from 'p-retry';
import pThrottle from 'p-throttle';
class RobustClient {
private throttle = pThrottle({
limit: 100,
interval: 60000, // 100 requests per minute
});
private async requestWithRetry<T>(
fn: () => Promise<T>,
options?: { retries?: number }
): Promise<T> {
return pRetry(
async () => {
try {
return await fn();
} catch (error) {
if (error instanceof ApiError) {
// Don't retry client errors
if (error.status >= 400 && error.status < 500) {
throw new pRetry.AbortError(error);
}
}
throw error;
}
},
{
retries: options?.retries ?? 3,
onFailedAttempt: (error) => {
console.log(
`Attempt ${error.attemptNumber} failed. ${error.retriesLeft} retries left.`
);
},
}
);
}
private request = this.throttle(async <T>(
method: string,
path: string,
data?: any
): Promise<T> => {
return this.requestWithRetry(async () => {
const response = await fetch(`${this.baseUrl}${path}`, {
method,
headers: this.getHeaders(),
body: data ? JSON.stringify(data) : undefined,
});
// Handle rate limiting
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const delay = retryAfter ? parseInt(retryAfter) * 1000 : 60000;
await sleep(delay);
throw new Error('Rate limited, retrying...');
}
if (!response.ok) {
throw new ApiError(response.status, await response.text());
}
return response.json();
});
});
}---
API Documentation
OpenAPI Spec Generation
import { generateOpenApi } from '@ts-rest/open-api';
import { contract } from './contract';
const openApiDocument = generateOpenApi(contract, {
info: {
title: 'My API',
version: '1.0.0',
description: 'API for managing resources',
},
servers: [
{ url: 'https://api.example.com', description: 'Production' },
{ url: 'https://staging-api.example.com', description: 'Staging' },
],
});
// Export for documentation tools
export { openApiDocument };Interactive Documentation
// Using Swagger UI React
import SwaggerUI from 'swagger-ui-react';
import 'swagger-ui-react/swagger-ui.css';
function ApiDocs() {
return (
<SwaggerUI
url="/api/openapi.json"
docExpansion="list"
defaultModelsExpandDepth={3}
/>
);
}
// Or Redoc
import { RedocStandalone } from 'redoc';
function ApiDocs() {
return <RedocStandalone specUrl="/api/openapi.json" />;
}---
Developer Portal
// API key management component
function ApiKeyManager() {
const [keys, setKeys] = useState<ApiKey[]>([]);
async function createKey(name: string) {
const key = await api.createApiKey({ name });
// Show the secret only once
showModal({
title: 'API Key Created',
content: (
<div>
<p>Save this key - it won't be shown again:</p>
<code className="bg-gray-100 p-2 block">{key.secret}</code>
</div>
),
});
setKeys([...keys, key]);
}
async function revokeKey(keyId: string) {
await api.revokeApiKey(keyId);
setKeys(keys.filter((k) => k.id !== keyId));
}
return (
<div>
<h2>API Keys</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Created</th>
<th>Last Used</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{keys.map((key) => (
<tr key={key.id}>
<td>{key.name}</td>
<td>{formatDate(key.createdAt)}</td>
<td>{key.lastUsedAt ? formatDate(key.lastUsedAt) : 'Never'}</td>
<td>
<button onClick={() => revokeKey(key.id)}>Revoke</button>
</td>
</tr>
))}
</tbody>
</table>
<button onClick={() => createKey(prompt('Key name:') || 'Unnamed')}>
Create New Key
</button>
</div>
);
}---
Related Skills
- [[api-design]] - API design patterns
- [[documentation]] - Technical writing
- [[automation-scripts]] - Build automation
#!/usr/bin/env node
/**
* CLI Boilerplate Template
* Usage: Foundation for building Node.js CLI tools
*
* Features:
* - Command structure with subcommands
* - Flag/option parsing
* - Interactive prompts
* - Config file support
* - Colored output
* - Progress indicators
*
* Install dependencies:
* npm install commander chalk ora inquirer conf
* npm install -D @types/node @types/inquirer
*/
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import inquirer from 'inquirer';
import Conf from 'conf';
// ===========================================
// Configuration
// ===========================================
const config = new Conf<{
apiKey?: string;
defaultFormat: string;
verbose: boolean;
}>({
projectName: 'my-cli',
defaults: {
defaultFormat: 'json',
verbose: false,
},
});
// ===========================================
// CLI Setup
// ===========================================
const program = new Command();
program
.name('my-cli')
.description('CLI tool description')
.version('1.0.0')
.option('-v, --verbose', 'Enable verbose output')
.option('-c, --config <path>', 'Path to config file')
.hook('preAction', (thisCommand) => {
const opts = thisCommand.opts();
if (opts.verbose) {
config.set('verbose', true);
}
});
// ===========================================
// Commands
// ===========================================
// Init command
program
.command('init')
.description('Initialize configuration')
.action(async () => {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'apiKey',
message: 'Enter your API key:',
validate: (input: string) => input.length > 0 || 'API key is required',
},
{
type: 'list',
name: 'format',
message: 'Default output format:',
choices: ['json', 'yaml', 'table'],
default: 'json',
},
]);
config.set('apiKey', answers.apiKey);
config.set('defaultFormat', answers.format);
console.log(chalk.green('✓ Configuration saved'));
});
// Run command with subcommands
const run = program.command('run').description('Run operations');
run
.command('task <name>')
.description('Run a named task')
.option('-f, --force', 'Force execution')
.option('--dry-run', 'Preview without executing')
.action(async (name: string, options: { force?: boolean; dryRun?: boolean }) => {
const spinner = ora(`Running task: ${name}`).start();
try {
if (options.dryRun) {
spinner.info(`Would run task: ${name}`);
return;
}
// Simulate async operation
await sleep(2000);
spinner.succeed(`Task ${chalk.bold(name)} completed`);
} catch (error) {
spinner.fail(`Task failed: ${(error as Error).message}`);
process.exit(1);
}
});
run
.command('batch')
.description('Run batch operations')
.option('-p, --parallel <n>', 'Parallel execution count', '4')
.action(async (options: { parallel: string }) => {
const items = ['item1', 'item2', 'item3'];
const parallel = parseInt(options.parallel, 10);
console.log(chalk.blue(`Processing ${items.length} items (${parallel} parallel)`));
for (const item of items) {
const spinner = ora(`Processing ${item}`).start();
await sleep(500);
spinner.succeed();
}
console.log(chalk.green('\n✓ Batch completed'));
});
// Config command
const configCmd = program.command('config').description('Manage configuration');
configCmd
.command('get <key>')
.description('Get config value')
.action((key: string) => {
const value = config.get(key);
if (value !== undefined) {
console.log(value);
} else {
console.log(chalk.yellow(`Config key "${key}" not found`));
}
});
configCmd
.command('set <key> <value>')
.description('Set config value')
.action((key: string, value: string) => {
config.set(key, value);
console.log(chalk.green(`✓ Set ${key}=${value}`));
});
configCmd
.command('list')
.description('List all config values')
.action(() => {
const all = config.store;
console.log(JSON.stringify(all, null, 2));
});
configCmd
.command('reset')
.description('Reset configuration')
.action(async () => {
const { confirm } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: 'Reset all configuration?',
default: false,
},
]);
if (confirm) {
config.clear();
console.log(chalk.green('✓ Configuration reset'));
}
});
// ===========================================
// Output Helpers
// ===========================================
function log(message: string, level: 'info' | 'warn' | 'error' | 'success' = 'info') {
const prefix = {
info: chalk.blue('ℹ'),
warn: chalk.yellow('⚠'),
error: chalk.red('✗'),
success: chalk.green('✓'),
};
console.log(`${prefix[level]} ${message}`);
}
function table(data: Record<string, unknown>[], columns: string[]) {
const widths = columns.map((col) => {
const maxLen = Math.max(col.length, ...data.map((row) => String(row[col] || '').length));
return maxLen;
});
// Header
const header = columns.map((col, i) => col.padEnd(widths[i])).join(' | ');
console.log(chalk.bold(header));
console.log('-'.repeat(header.length));
// Rows
for (const row of data) {
const line = columns.map((col, i) => String(row[col] || '').padEnd(widths[i])).join(' | ');
console.log(line);
}
}
// ===========================================
// Utilities
// ===========================================
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function isVerbose(): boolean {
return config.get('verbose') ?? false;
}
function debug(message: string) {
if (isVerbose()) {
console.log(chalk.gray(`[DEBUG] ${message}`));
}
}
// ===========================================
// Error Handling
// ===========================================
process.on('uncaughtException', (error) => {
console.error(chalk.red(`\nFatal error: ${error.message}`));
if (isVerbose()) {
console.error(error.stack);
}
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
console.error(chalk.red(`\nUnhandled rejection: ${reason}`));
process.exit(1);
});
// ===========================================
// Run CLI
// ===========================================
program.parse();
// Show help if no command provided
if (!process.argv.slice(2).length) {
program.outputHelp();
}
// Export for testing
export { program, config, log, table, debug };
Developer Tools Templates
Templates for building developer tools and CLIs.
Files
| Template | Purpose |
|---|---|
cli-boilerplate.ts | Node.js CLI foundation |
Usage
Quick Start
# Create project
mkdir my-cli && cd my-cli
npm init -y
# Install dependencies
npm install commander chalk ora inquirer conf
npm install -D typescript @types/node @types/inquirer tsx
# Copy template
cp templates/cli-boilerplate.ts ./src/cli.ts
# Run
npx tsx src/cli.ts --help
# Build for distribution
npx tsc
chmod +x dist/cli.jsCLI Features
| Feature | Library |
|---|---|
| Commands | Commander.js |
| Colors | Chalk |
| Spinners | Ora |
| Prompts | Inquirer |
| Config | Conf |
Command Structure
my-cli
├── init # Initialize configuration
├── run
│ ├── task <name> # Run named task
│ └── batch # Batch operations
└── config
├── get <key> # Get config value
├── set <key> <value>
├── list # Show all config
└── reset # Reset configExample Usage
# Initialize
my-cli init
# Run task
my-cli run task deploy --dry-run
my-cli run task build --force
# Batch processing
my-cli run batch --parallel 8
# Configuration
my-cli config get apiKey
my-cli config set format yaml
my-cli config list
# Global options
my-cli --verbose run task test
my-cli --config ./custom.json initAdding Commands
// Simple command
program
.command('hello <name>')
.description('Say hello')
.option('-l, --loud', 'Shout')
.action((name, options) => {
const greeting = `Hello, ${name}!`;
console.log(options.loud ? greeting.toUpperCase() : greeting);
});
// Command group
const db = program.command('db').description('Database operations');
db.command('migrate')
.description('Run migrations')
.action(async () => { /* ... */ });
db.command('seed')
.description('Seed database')
.action(async () => { /* ... */ });Interactive Prompts
// Text input
const { name } = await inquirer.prompt([
{ type: 'input', name: 'name', message: 'Your name:' }
]);
// Selection
const { choice } = await inquirer.prompt([
{
type: 'list',
name: 'choice',
message: 'Select option:',
choices: ['Option A', 'Option B', 'Option C']
}
]);
// Confirmation
const { proceed } = await inquirer.prompt([
{ type: 'confirm', name: 'proceed', message: 'Continue?', default: false }
]);
// Password
const { secret } = await inquirer.prompt([
{ type: 'password', name: 'secret', message: 'API Key:' }
]);Progress Indicators
// Spinner
const spinner = ora('Loading...').start();
await doWork();
spinner.succeed('Done!');
// spinner.fail('Error!');
// spinner.warn('Warning');
// spinner.info('Info');
// Progress updates
spinner.text = 'Processing item 5/10...';Output Helpers
// Colored messages
console.log(chalk.green('Success!'));
console.log(chalk.red('Error!'));
console.log(chalk.yellow('Warning'));
console.log(chalk.blue('Info'));
console.log(chalk.bold('Bold text'));
console.log(chalk.dim('Dimmed'));
// Table output
table([
{ name: 'Alice', role: 'Admin' },
{ name: 'Bob', role: 'User' },
], ['name', 'role']);Package.json Setup
{
"name": "my-cli",
"version": "1.0.0",
"type": "module",
"bin": {
"my-cli": "./dist/cli.js"
},
"scripts": {
"build": "tsc",
"start": "tsx src/cli.ts",
"prepublishOnly": "npm run build"
}
}Distribution
# Local testing
npm link
my-cli --help
# Publish to npm
npm publish
# Users install globally
npm install -g my-cliTesting
import { program } from './cli';
describe('CLI', () => {
it('runs task command', async () => {
await program.parseAsync(['node', 'cli', 'run', 'task', 'test']);
// assertions...
});
});Related skills
AI & Agent Buildingagents