Building Your First CLI
Complete walkthrough of building a CLI with Rempts
Building Your First CLI
This comprehensive guide demonstrates creating a fully-featured CLI application using Rempts' file-based command system and type-safe architecture.
Prerequisites
- Bun runtime (v1.0 or later)
- Fundamental TypeScript proficiency
- Terminal/command line interface
Project Initialization
Bootstrap your CLI project using the intelligent scaffolding tool:
bunx @reliverse/rempts todo-cli
cd todo-cliThe scaffolding automatically provisions:
- Bun-optimized TypeScript configuration
- Rempts core dependencies with plugin ecosystem
- File-based command discovery structure
- Hot-reload development workflow
- Comprehensive testing infrastructure
Project Architecture
Rempts generates a production-ready layout optimized for Bun:
todo-cli/
├── cli.ts # Primary CLI entry point with async initialization
├── cmds/ # Command repository (essential for file-based discovery)
│ └── greet/
│ └── cmd.ts # Sample command implementation
├── dler.config.ts # Centralized Rempts configuration
├── package.json # Dependencies including @reliverse/rempts-core
├── tsconfig.json # TypeScript configuration for Bun compatibility
└── README.md # Generated project documentationImplementing Your First Command
Develop a comprehensive todo management CLI. Replace the default greet command with an add command featuring full type safety:
// cmds/add/cmd.ts
import { defineCommand, option } from '@reliverse/rempts-core'
import { z } from 'zod'
export default defineCommand({
name: 'add',
description: 'Register a new todo item to the task list',
options: {
task: option(
z.string().min(1),
{ description: 'Descriptive task content' }
),
priority: option(
z.enum(['low', 'medium', 'high']).default('medium'),
{ short: 'p', description: 'Urgency classification' }
),
due: option(
z.string().optional(),
{ short: 'd', description: 'Deadline specification' }
)
},
handler: async ({ flags, colors }) => {
console.log(colors.green('✓'), 'Task registered:', flags.task)
console.log(colors.dim(`Priority: ${flags.priority}`))
if (flags.due) {
console.log(colors.dim(`Deadline: ${flags.due}`))
}
}
})CLI Configuration
Configure your main CLI entry point with automatic command discovery:
// cli.ts
import { createCLI } from '@reliverse/rempts-core'
const cli = await createCLI({
name: 'todo',
version: '1.0.0',
description: 'Type-safe task management CLI'
})
// Commands automatically discovered from cmds/ directory
await cli.run()Development Workflow
bunx @reliverse/rempts devExecute your command to verify functionality:
bun run cli.ts add --task "Write documentation" --priority highExpanding Command Set
Implement a comprehensive list command with filtering capabilities:
// cmds/list.ts
import { defineCommand, option } from '@reliverse/rempts-core'
import { z } from 'zod'
export default defineCommand({
name: 'list',
description: 'Display all registered tasks',
alias: 'ls',
options: {
filter: option(
z.enum(['all', 'pending', 'completed']).default('all'),
{ short: 'f', description: 'Task status filter' }
),
sort: option(
z.enum(['priority', 'due', 'created']).default('created'),
{ short: 's', description: 'Result ordering' }
)
},
handler: async ({ flags, colors, spinner }) => {
const spin = spinner('Retrieving task collection...')
spin.start()
// Simulate data retrieval latency
await new Promise(resolve => setTimeout(resolve, 500))
const tasks = [
{ id: 1, task: 'Write documentation', priority: 'high', completed: false },
{ id: 2, task: 'Add tests', priority: 'medium', completed: false },
{ id: 3, task: 'Review PR', priority: 'low', completed: true }
]
spin.succeed('Task collection retrieved')
const filteredTasks = tasks.filter(task => {
if (flags.filter === 'pending') return !task.completed
if (flags.filter === 'completed') return task.completed
return true
})
console.log('\nRegistered tasks:\n')
filteredTasks.forEach(task => {
const statusIcon = task.completed
? colors.green('✓')
: colors.yellow('○')
const priorityIndicator = colors.dim(`[${task.priority}]`)
console.log(`${statusIcon} ${task.task} ${priorityIndicator}`)
})
}
})Interactive Command Implementation
Develop an interactive task completion command:
// cmds/complete.ts
import { defineCommand } from '@reliverse/rempts-core'
export default defineCommand({
name: 'complete',
description: 'Mark a task as successfully completed',
handler: async ({ prompt, colors }) => {
const taskCollection = [
{ id: 1, task: 'Write documentation', completed: false },
{ id: 2, task: 'Add tests', completed: false },
{ id: 3, task: 'Review PR', completed: false }
]
const pendingTasks = taskCollection.filter(task => !task.completed)
if (pendingTasks.length === 0) {
console.log(colors.yellow('No outstanding tasks remain!'))
return
}
const selectedTask = await prompt.select('Select completed task:', {
choices: pendingTasks.map(task => ({
value: task.id,
label: task.task
}))
})
console.log(colors.green('✓'), 'Task marked as completed!')
}
})Hierarchical Command Structure
Leverage Rempts' file-based routing to create nested command hierarchies:
// cmds/db/init.ts
import { defineCommand } from '@reliverse/rempts-core'
export default defineCommand({
name: 'init',
description: 'Initialize the database schema',
handler: async ({ colors }) => {
console.log(colors.green('✓'), 'Database schema initialized successfully')
}
})// cmds/db/backup.ts
import { defineCommand } from '@reliverse/rempts-core'
export default defineCommand({
name: 'backup',
description: 'Create database backup archive',
handler: async ({ spinner }) => {
const spin = spinner('Generating backup archive...')
spin.start()
await new Promise(resolve => setTimeout(resolve, 2000))
spin.succeed('Backup archive created: backup-2024-01-15.db')
}
})This creates the command structure:
todo db init # Initialize database
todo db backup # Create backupProduction Compilation
Generate optimized executables for distribution:
# Compile for current platform architecture
bunx @reliverse/rempts build
# Cross-compile for all supported platforms
bunx @reliverse/rempts build --all
# Generate standalone executable with no runtime dependencies
bunx @reliverse/rempts build --compileComprehensive Testing
Develop tests for your command implementations:
// cmds/add.test.ts
import { test, expect } from 'bun:test'
import { createTestCLI } from '@reliverse/rempts-test'
import addCommand from './add'
test('add command registers new task successfully', async () => {
const cli = createTestCLI()
expect(result.exitCode).toBe(0)
expect(result.output).toContain('Task registered: Test implementation')
})Execute the test suite:
bunx @reliverse/rempts testPlugin Integration
Augment your CLI with powerful plugins for configuration management and AI detection:
// cli.ts
import { createCLI } from '@reliverse/rempts-core'
import { configPlugin } from '@reliverse/rempts-plugin-config'
import { aiDetectPlugin } from '@reliverse/rempts-plugin-ai-detect'
const cli = await createCLI({
name: 'todo',
version: '1.0.0',
description: 'Type-safe task management CLI',
plugins: [
// Load configuration from .todorc.json or ~/.config/todo/config.json
configPlugin(),
// Detect execution within AI assistant environments
aiDetectPlugin({ verbose: true })
] as const
})
await cli.run()Now your commands can use plugin features:
// cmds/list.ts
handler: async ({ flags, colors, context }) => {
// Provide structured output for AI agents
if (context?.env.isAIAgent) {
console.log(JSON.stringify({ tasks }, null, 2))
} else {
// Human-friendly output
tasks.forEach(task => {
console.log(`${colors.green('✓')} ${task.name}`)
})
}
}Configuration Management
Tailor your CLI behavior through dler.config.ts:
import { defineConfig } from '@reliverse/rempts-core'
export default defineConfig({
name: 'todo',
version: '1.0.0',
commands: {
directory: './commands'
},
build: {
entry: './cli.ts',
outdir: './dist',
compile: true,
targets: ['darwin-arm64', 'linux-x64', 'windows-x64']
}
})Distribution Pipeline
When prepared to distribute your CLI globally:
-
Cross-platform compilation:
bunx @reliverse/rempts build --all -
Automated release creation:
bunx @reliverse/rempts release -
NPM publication:
npm publish
Next Steps
You've built a functional CLI! Here's what to explore next:
- Schema Validation - Advanced validation
- Interactive Prompts - Rich user interactions
- Testing - Comprehensive testing strategies
- Distribution - Publishing and deployment
Complete Example
Find the complete todo CLI example in the Rempts repository.