
Cli Building
- 179 installs
- 20 repo stars
- Updated March 21, 2026
- siviter-xyz/dot-agent
Use cli-building for development tasks
About
cli-building: A skill for development. This provides functionality for development workflows.
- cli-building
Cli Building by the numbers
- 179 all-time installs (skills.sh)
- +1 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #2,217 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/siviter-xyz/dot-agent --skill cli-buildingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 179 |
|---|---|
| repo stars | ★ 20 |
| Last updated | March 21, 2026 |
| Repository | siviter-xyz/dot-agent ↗ |
What it does
Use cli-building for development tasks
Files
CLI Building
Guidelines for building command-line interfaces with modern patterns and best practices.
When to Use
- Creating new CLI tools or commands
- Building interactive terminal applications
- Adding commands to existing projects
- Implementing command-line interfaces
- Working with CLI frameworks
Core Principles
- Async-first: All I/O operations should be async/await, avoid blocking operations
- Composable commands: Commands should be modular and reusable, use command composition
- Strategy pattern: Use strategy pattern for branching workflows or task-based commands
- Output formatting: Proper formatting with unicode symbols and color support
Framework Selection
TypeScript/JavaScript
stricli (@bloomberg/stricli, recommended for modern async-first CLIs):
- Built for async/await from ground up
- Type-safe command definitions with full type inference
- Lazy loading for startup performance
- Zero dependencies
oclif (alternative):
- Mature framework with extensive features
- Plugin system
- Good for complex CLIs
Python
cyclopts (recommended for async-first):
- Modern async-first CLI framework
- Type-safe with excellent async support
- Clean API design
typer (when fully async support available):
- Based on Python type hints
- Clean and intuitive
- Good for simple to medium complexity CLIs
Command Architecture
Composable Commands
Design commands as reusable modules:
- Shared command utilities
- Command middleware
- Reusable command modules
- Command composition patterns
Strategy Pattern
Use strategy pattern for:
- Workflow branching
- Task-based commands
- Dynamic command routing
- Conditional command execution
Output Formatting
- No emojis: Do not use emojis unless explicitly directed
- Unicode symbols: Use unicode symbols (✓, ✗, →, ⚠) for status indicators
- Color support: Use color libraries, never hardcoded ANSI codes
- NO_COLOR: Always respect
NO_COLORenvironment variable - Formatting: Use formatting for better readability (bold, dim, etc.)
Async Patterns
Async-First Design
All I/O should be async:
- File operations: use async file APIs
- Network requests: use async HTTP clients
- Process execution: use async process APIs
- Database operations: use async database clients
Error Handling
Handle async errors properly:
- Use try/catch with await
- Handle promise rejections
- Provide clear error messages
- Exit with appropriate codes
Command Structure
Basic Command
// stricli example
import { createCli } from '@bloomberg/stricli';
async function myCommand() {
// Async implementation
}
const cli = createCli({
name: 'my-cli',
commands: {
'my-command': myCommand
}
});
cli.run();# cyclopts example
from cyclopts import App
app = App()
@app.default
async def my_command():
# Async implementation
pass
if __name__ == '__main__':
app()Best Practices
1. Async by default: All operations should be async 2. Composable design: Build reusable command modules 3. Strategy pattern: Use for workflow branching 4. Proper formatting: Unicode symbols and color with NO_COLOR support 5. Error handling: Clear error messages and exit codes 6. Type safety: Use TypeScript types or Python type hints 7. Testing: Test commands in isolation
References
For detailed guidance, see:
references/async-patterns.md- Async/await best practicesreferences/composable-commands.md- Command composition patternsreferences/strategy-pattern.md- Strategy pattern for workflowsreferences/output-formatting.md- Output formatting guidelinesreferences/frameworks.md- Framework comparisons and selection
Async Patterns
Async/await best practices for CLI development.
Async-First Design
All I/O operations should be async:
File Operations:
- Use
fs/promisesin Node.js - Use
aiofilesin Python - Avoid blocking file I/O
Network Requests:
- Use async HTTP clients (fetch, axios, httpx)
- Handle timeouts properly
- Use connection pooling
Process Execution:
- Use async process APIs
- Handle streams asynchronously
- Proper cleanup on exit
Error Handling
Try/Catch with Await:
try {
const result = await asyncOperation();
} catch (error) {
// Handle error
}Promise Rejections:
asyncOperation()
.catch(error => {
// Handle rejection
});Error Propagation:
- Let errors bubble up when appropriate
- Catch and transform for user-facing messages
- Exit with appropriate codes
Concurrent Operations
Parallel Execution:
const results = await Promise.all([
operation1(),
operation2(),
operation3()
]);Sequential with Results:
const result1 = await operation1();
const result2 = await operation2(result1);
const result3 = await operation3(result2);Best Practices
1. Always use async/await: Avoid mixing promises and callbacks 2. Handle errors: Use try/catch or .catch() 3. Avoid blocking: Never use blocking I/O in async functions 4. Cleanup: Ensure proper cleanup in finally blocks 5. Timeouts: Add timeouts for long-running operations
Composable Commands
Command composition patterns for modular CLI design.
Command Modules
Create reusable command modules:
// shared/commands/base.ts
export function createBaseCommand(options) {
return async function baseCommand() {
// Shared behavior
return options.handler();
};
}
// commands/user.ts
import { createBaseCommand } from '../shared/commands/base';
export const userCommand = createBaseCommand({
handler: async () => {
// User-specific logic
}
});Command Middleware
Use middleware for shared behavior:
function withAuth(command) {
return {
...command,
async handler(args) {
await checkAuth();
return command.handler(args);
}
};
}Command Composition
Compose commands from smaller parts:
const createCommand = compose(
withAuth,
withLogging,
withErrorHandling
);
const myCommand = createCommand({
name: 'my-command',
handler: async () => { /* ... */ }
});Shared Utilities
Extract common functionality:
// utils/output.ts
export async function printTable(data) {
// Shared table printing logic
}
// commands/list.ts
import { printTable } from '../utils/output';
export async function listCommand() {
const data = await fetchData();
await printTable(data);
}Best Practices
1. Extract common patterns: Create reusable utilities 2. Use middleware: For cross-cutting concerns 3. Compose commands: Build complex commands from simple ones 4. Share utilities: Common functionality in shared modules 5. Keep focused: Each command module should have single responsibility
CLI Frameworks
Framework comparisons and selection guidelines.
TypeScript/JavaScript
stricli (@bloomberg/stricli)
Best for: Modern async-first CLIs
Features:
- Built for async/await from ground up
- Type-safe command definitions with full type inference
- Excellent TypeScript support
- Lazy loading for performance
- Zero dependencies
- ESM and CommonJS support
When to use:
- New projects
- Async-heavy operations
- Type safety important
- Need lazy loading for startup performance
oclif
Best for: Complex CLIs with plugins
Features:
- Mature and feature-rich
- Plugin system
- Good documentation
- Large ecosystem
When to use:
- Complex CLI requirements
- Need plugin system
- Existing oclif ecosystem
Python
cyclopts
Best for: Async-first Python CLIs
Features:
- Modern async-first design
- Type-safe with type hints
- Clean API
- Excellent async support
When to use:
- New async-first projects
- Type safety important
- Modern Python (3.8+)
typer
Best for: Type-hint based CLIs
Features:
- Based on Python type hints
- Clean and intuitive
- Good for simple to medium complexity
- Built on click
When to use:
- Simple to medium complexity
- Type hints preferred
- When full async support available
Selection Guidelines
Choose stricli/cyclopts when:
- Async-first design is priority
- Type safety is important
- Modern framework preferred
Choose oclif/typer when:
- Need mature ecosystem
- Plugin system required
- Simpler requirements
Migration
From other frameworks:
- Evaluate async requirements
- Check type safety needs
- Consider plugin requirements
- Test with existing codebase
Output Formatting
Guidelines for CLI output formatting with unicode symbols and color.
Unicode Symbols
Use unicode symbols for status indicators:
- ✓ (checkmark) - Success
- ✗ (cross) - Failure
- → (arrow) - Progress/next step
- ⚠ (warning) - Warning
Color Libraries
Never use hardcoded ANSI codes. Use color libraries:
TypeScript:
chalk- Popular, well-maintainedkleur- Lightweight alternativecolors- Simple API
Python:
rich- Full-featured formattingcolorama- Cross-platform colorsclick.style()- Built into click
NO_COLOR Support
Always respect NO_COLOR environment variable:
import chalk from 'chalk';
const supportsColor = !process.env.NO_COLOR;
const success = supportsColor ? chalk.green('✓') : '✓';import os
from rich.console import Console
console = Console(no_color=os.getenv('NO_COLOR'))
console.print('[green]✓[/green] Success')Formatting Guidelines
Status Messages:
console.log(chalk.green('✓') + ' Operation successful');
console.log(chalk.red('✗') + ' Operation failed');
console.log(chalk.yellow('⚠') + ' Warning message');Progress Indicators:
console.log(chalk.blue('→') + ' Processing...');Tables and Lists:
- Use consistent spacing
- Align columns properly
- Use dim for secondary information
Best Practices
1. No emojis: Unless explicitly directed 2. Unicode symbols: Use for status indicators 3. Color libraries: Never hardcoded ANSI 4. NO_COLOR: Always check and respect 5. Consistent style: Use same symbols/colors throughout 6. Accessibility: Ensure readable without color
Strategy Pattern
Using strategy pattern for CLI workflow branching.
Workflow Branching
Use strategy pattern for different workflows:
interface WorkflowStrategy {
execute(): Promise<void>;
}
class BuildWorkflow implements WorkflowStrategy {
async execute() {
// Build workflow logic
}
}
class DeployWorkflow implements WorkflowStrategy {
async execute() {
// Deploy workflow logic
}
}
function selectWorkflow(type: string): WorkflowStrategy {
switch (type) {
case 'build': return new BuildWorkflow();
case 'deploy': return new DeployWorkflow();
default: throw new Error('Unknown workflow');
}
}Task-Based Commands
Use strategy for task selection:
const taskStrategies = {
init: new InitTask(),
build: new BuildTask(),
test: new TestTask(),
};
const task = taskStrategies[taskName];
if (task) {
await task.execute();
}Dynamic Routing
Route commands based on context:
class CommandRouter {
private strategies: Map<string, WorkflowStrategy>;
route(command: string, context: Context): WorkflowStrategy {
const strategy = this.strategies.get(command);
if (!strategy) {
throw new Error(`Unknown command: ${command}`);
}
return strategy;
}
}Best Practices
1. Separate strategies: Each strategy in own module 2. Common interface: All strategies implement same interface 3. Factory pattern: Use factory to create strategies 4. Context passing: Pass context to strategies 5. Error handling: Handle strategy selection errors