Throughout this series, we've generated scripts for specific tasks. Now it's time to tie everything together into cohesive, professional CLI tools that feel native to your workflow. This final guide covers building custom management CLIs, API wrappers, interactive admin tools, and the patterns that make command-line tools a joy to use.
Prerequisites
- Claude Code installed (see Part 1)
- Familiarity with bash scripting
- Understanding of your infrastructure from previous parts
The Server Management CLI
Let's build a comprehensive server management tool that wraps common operations:
Create a professional CLI tool called "srv" for server management with:
Commands:
- srv status Show system overview
- srv services List and manage systemd services
- srv logs [service] View and search logs
- srv docker Docker management subcommands
- srv backup Trigger and manage backups
- srv deploy [app] Deploy applications
- srv ssh [host] SSH to managed hosts
- srv secrets Manage encrypted secrets
- srv update Update system packages
- srv firewall Firewall management
Features:
- Bash completion
- Colored output with consistent styling
- Configuration file for customization
- Help text for all commands
- Dry-run mode for dangerous operations
- Logging of all operations
Make it modular so commands can be added easily.#!/bin/bash
# =============================================================================
# srv - Server Management CLI
# =============================================================================
set -uo pipefail
# Version
VERSION="1.0.0"
# Paths
SRV_HOME="${SRV_HOME:-$HOME/.srv}"
SRV_CONFIG="$SRV_HOME/config"
SRV_LOG="$SRV_HOME/srv.log"
SRV_COMMANDS="$SRV_HOME/commands"
# Create directories
mkdir -p "$SRV_HOME" "$SRV_COMMANDS"
# Colors
export RED='\033[0;31m'
export GREEN='\033[0;32m'
export YELLOW='\033[0;33m'
export BLUE='\033[0;34m'
export CYAN='\033[0;36m'
export BOLD='\033[1m'
export DIM='\033[2m'
export NC='\033[0m'
# Configuration defaults
DRY_RUN="${DRY_RUN:-false}"
VERBOSE="${VERBOSE:-false}"
NO_COLOR="${NO_COLOR:-false}"
# Disable colors if requested or not a terminal
if [[ "$NO_COLOR" == "true" ]] || [[ ! -t 1 ]]; then
RED='' GREEN='' YELLOW='' BLUE='' CYAN='' BOLD='' DIM='' NC=''
fi
# Load config if exists
[[ -f "$SRV_CONFIG" ]] && source "$SRV_CONFIG"
# -----------------------------------------------------------------------------
# Utility Functions (exported for subcommands)
# -----------------------------------------------------------------------------
srv_log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" >> "$SRV_LOG"
if [[ "$VERBOSE" == "true" ]] || [[ "$level" == "ERROR" ]]; then
case "$level" in
INFO) echo -e "${BLUE}ℹ${NC} $message" ;;
OK) echo -e "${GREEN}✓${NC} $message" ;;
WARN) echo -e "${YELLOW}⚠${NC} $message" ;;
ERROR) echo -e "${RED}✗${NC} $message" >&2 ;;
esac
fi
}
srv_info() { srv_log "INFO" "$@"; }
srv_ok() { srv_log "OK" "$@"; }
srv_warn() { srv_log "WARN" "$@"; }
srv_error() { srv_log "ERROR" "$@"; }
srv_header() {
echo -e "\n${BOLD}${BLUE}━━━ $* ━━━${NC}\n"
}
srv_confirm() {
local prompt="${1:-Continue?}"
if [[ "$DRY_RUN" == "true" ]]; then
echo -e "${YELLOW}[DRY-RUN]${NC} Would prompt: $prompt"
return 0
fi
read -p "$prompt [y/N] " -n 1 -r
echo
[[ $REPLY =~ ^[Yy]$ ]]
}
srv_run() {
# Execute command with logging and dry-run support
local cmd="$*"
srv_info "Running: $cmd"
if [[ "$DRY_RUN" == "true" ]]; then
echo -e "${YELLOW}[DRY-RUN]${NC} $cmd"
return 0
fi
eval "$cmd"
}
# Export utilities for subcommands
export -f srv_log srv_info srv_ok srv_warn srv_error srv_header srv_confirm srv_run
export SRV_HOME SRV_CONFIG SRV_LOG DRY_RUN VERBOSE
# -----------------------------------------------------------------------------
# Help
# -----------------------------------------------------------------------------
show_help() {
cat << EOF
${BOLD}srv${NC} - Server Management CLI v$VERSION
${BOLD}USAGE:${NC}
srv [OPTIONS] <command> [args]
${BOLD}OPTIONS:${NC}
-n, --dry-run Show what would be done without executing
-v, --verbose Verbose output
-h, --help Show this help
--version Show version
--no-color Disable colored output
${BOLD}COMMANDS:${NC}
status System status overview
services [action] Manage systemd services
logs [service] View and search logs
docker <cmd> Docker management
backup [action] Backup operations
deploy <app> Deploy applications
ssh <host> SSH to managed hosts
secrets <action> Manage secrets
update Update system packages
firewall <action> Firewall management
config Edit configuration
help <command> Help for specific command
${BOLD}EXAMPLES:${NC}
srv status
srv services restart nginx
srv logs nginx --since 1h --grep error
srv docker ps
srv backup run
srv deploy myapp --branch main
srv --dry-run update
${BOLD}CONFIGURATION:${NC}
Config file: $SRV_CONFIG
Log file: $SRV_LOG
Run 'srv help <command>' for detailed help on any command.
EOF
}
# -----------------------------------------------------------------------------
# Built-in Commands
# -----------------------------------------------------------------------------
srv_cmd_status() {
if [[ "${1:-}" == "--help" ]]; then
echo "Usage: srv status"
echo "Show system status overview"
return
fi
srv_header "System Status"
# Host info
echo -e "${BOLD}Host:${NC} $(hostname) ($(uname -r))"
echo -e "${BOLD}Uptime:${NC} $(uptime -p)"
# Load
local load=$(cat /proc/loadavg | cut -d' ' -f1-3)
local cpus=$(nproc)
echo -e "${BOLD}Load:${NC} $load (${cpus} CPUs)"
# Memory
local mem_info=$(free -h | awk '/Mem:/ {print $3 "/" $2 " (" int($3/$2*100) "%)"}')
echo -e "${BOLD}Memory:${NC} $mem_info"
# Disk
echo -e "\n${BOLD}Disk Usage:${NC}"
df -h -x tmpfs -x devtmpfs -x overlay | awk 'NR==1 || $5+0 > 50'
# Services
local failed=$(systemctl --failed --no-pager --no-legend 2>/dev/null | wc -l)
if [[ $failed -gt 0 ]]; then
echo -e "\n${RED}${BOLD}Failed Services: $failed${NC}"
systemctl --failed --no-pager --no-legend 2>/dev/null | head -5
else
echo -e "\n${GREEN}${BOLD}All services healthy${NC}"
fi
# Docker
if command -v docker &>/dev/null && docker info &>/dev/null; then
local containers=$(docker ps -q | wc -l)
echo -e "\n${BOLD}Docker:${NC} $containers running"
fi
}
# ... Additional commands continue in main script
main() {
# Parse global options then run command
run_command "$@"
}
main "$@"Docker Management Subcommand
Create a modular subcommand for Docker operations:
#!/bin/bash
# =============================================================================
# srv docker - Docker management subcommand
# =============================================================================
source "${SRV_HOME:-$HOME/.srv}/commands/.common" 2>/dev/null || true
show_help() {
cat << EOF
Usage: srv docker <command> [args]
Commands:
ps List containers (default)
logs <container> View container logs
shell <container> Open shell in container
restart <container> Restart container
stop <container> Stop container
start <container> Start container
stats Show resource usage
clean Clean up unused resources
pull Pull latest images for running containers
compose <args> Run docker compose commands
Examples:
srv docker ps
srv docker logs nginx --follow
srv docker shell webapp
srv docker clean
srv docker compose up -d
EOF
}
cmd_ps() {
srv_header "Docker Containers"
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}\t{{.Ports}}" | head -20
# Show unhealthy
local unhealthy=$(docker ps --filter "health=unhealthy" --format "{{.Names}}" 2>/dev/null)
if [[ -n "$unhealthy" ]]; then
echo -e "\n${RED}${BOLD}Unhealthy:${NC}"
echo "$unhealthy"
fi
}
cmd_logs() {
local container="$1"
shift || { srv_error "Container name required"; return 1; }
local follow=""
local tail="100"
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--follow) follow="-f"; shift ;;
-n|--tail) tail="$2"; shift 2 ;;
*) break ;;
esac
done
docker logs $follow --tail "$tail" "$container"
}
cmd_shell() {
local container="$1"
[[ -z "$container" ]] && { srv_error "Container name required"; return 1; }
# Try bash first, fall back to sh
docker exec -it "$container" bash 2>/dev/null || \
docker exec -it "$container" sh
}
cmd_clean() {
srv_header "Docker Cleanup"
echo "Current disk usage:"
docker system df
echo ""
srv_confirm "Remove unused containers, images, and volumes?" || return
srv_run docker system prune -af --volumes
srv_ok "Cleanup complete"
}
cmd_stats() {
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"
}
# Main
case "${1:-ps}" in
--help|-h) show_help ;;
ps) shift; cmd_ps "$@" ;;
logs) shift; cmd_logs "$@" ;;
shell|exec) shift; cmd_shell "$@" ;;
restart) shift; srv_confirm "Restart $1?" && docker restart "$1" ;;
stop) shift; srv_confirm "Stop $1?" && docker stop "$1" ;;
start) shift; docker start "$1" ;;
stats) shift; cmd_stats "$@" ;;
clean|prune) shift; cmd_clean "$@" ;;
pull) docker ps --format "{{.Image}}" | sort -u | xargs -I{} docker pull {} ;;
compose) shift; docker compose "$@" ;;
*) srv_error "Unknown command: $1"; show_help ;;
esacBackup Management Subcommand
Handle backup operations with status, listing, and verification:
#!/bin/bash
# =============================================================================
# srv backup - Backup management subcommand
# =============================================================================
BACKUP_CONFIG="${BACKUP_CONFIG:-/etc/srv/backup.conf}"
BACKUP_DIR="${BACKUP_DIR:-/var/backups}"
show_help() {
cat << EOF
Usage: srv backup <command> [args]
Commands:
run Run backup now
status Show backup status
list List available backups
restore <backup> Restore from backup
verify Verify backup integrity
schedule Show/edit backup schedule
Configuration: $BACKUP_CONFIG
EOF
}
cmd_status() {
srv_header "Backup Status"
echo "Last backup runs:"
# Check database backup
if [[ -d "$BACKUP_DIR/databases/daily" ]]; then
local last_db=$(ls -t "$BACKUP_DIR/databases/daily" 2>/dev/null | head -1)
if [[ -n "$last_db" ]]; then
echo -e " ${GREEN}✓${NC} Database: $last_db"
else
echo -e " ${RED}✗${NC} Database: No backups found"
fi
fi
# Show disk usage
echo ""
echo "Backup storage usage:"
du -sh "$BACKUP_DIR"/* 2>/dev/null | while read -r line; do
echo " $line"
done
}
cmd_verify() {
srv_header "Verifying Backups"
echo "Checking backup file integrity..."
find "$BACKUP_DIR" -name "*.sha256" -type f | while read -r checksum_file; do
local backup_file="${checksum_file%.sha256}"
if [[ -f "$backup_file" ]]; then
if sha256sum -c "$checksum_file" &>/dev/null; then
echo -e " ${GREEN}✓${NC} $(basename "$backup_file")"
else
echo -e " ${RED}✗${NC} $(basename "$backup_file") - CHECKSUM FAILED"
fi
fi
done
}
cmd_schedule() {
srv_header "Backup Schedule"
echo "Current cron jobs:"
crontab -l 2>/dev/null | grep -E "(backup|Backup)" || echo " No backup cron jobs found"
echo ""
echo "Systemd timers:"
systemctl list-timers --all 2>/dev/null | grep -i backup || echo " No backup timers found"
}
# Main
case "${1:-status}" in
--help|-h) show_help ;;
run) shift; /opt/scripts/backup-databases.sh && /opt/scripts/backup-files.sh ;;
status) shift; cmd_status "$@" ;;
list|ls) find "$BACKUP_DIR" -name "*.tar.zst*" -type f | sort -r | head -20 ;;
restore) shift; srv_warn "Restore from: $1"; /opt/scripts/restore.sh "$1" ;;
verify) shift; cmd_verify "$@" ;;
schedule) shift; cmd_schedule "$@" ;;
*) srv_error "Unknown command: $1"; show_help ;;
esacDeployment Subcommand
Automate application deployments with rollback support:
#!/bin/bash
# =============================================================================
# srv deploy - Application deployment subcommand
# =============================================================================
APPS_DIR="${APPS_DIR:-/opt/apps}"
deploy_app() {
local app="$1"
local branch="${BRANCH:-main}"
local rollback="${ROLLBACK:-false}"
local app_dir="$APPS_DIR/$app"
if [[ ! -d "$app_dir" ]]; then
srv_error "App not found: $app"
return 1
fi
srv_header "Deploying $app"
cd "$app_dir" || return 1
# Rollback
if [[ "$rollback" == "true" ]]; then
srv_warn "Rolling back $app"
srv_run git checkout HEAD~1
srv_run docker compose up -d
srv_ok "Rollback complete"
return
fi
# Show current state
local current_commit=$(git rev-parse --short HEAD 2>/dev/null)
echo "Current: $current_commit"
# Pull updates
srv_info "Deploying branch: $branch"
srv_run git fetch origin
srv_run git checkout "$branch"
srv_run git pull origin "$branch"
local new_commit=$(git rev-parse --short HEAD 2>/dev/null)
echo "Deploying: $new_commit"
# Check for docker-compose
if [[ -f "docker-compose.yml" ]] || [[ -f "compose.yml" ]]; then
srv_info "Pulling images..."
srv_run docker compose pull
srv_info "Starting containers..."
srv_run docker compose up -d --remove-orphans
# Wait and check health
sleep 5
local unhealthy=$(docker compose ps --filter "health=unhealthy" -q 2>/dev/null | wc -l)
if [[ "$unhealthy" -gt 0 ]]; then
srv_error "Unhealthy containers detected!"
if srv_confirm "Rollback to previous version?"; then
srv_run git checkout "$current_commit"
srv_run docker compose up -d
fi
return 1
fi
srv_ok "Deployment successful: $current_commit → $new_commit"
else
srv_warn "No docker-compose.yml found"
fi
}
# Parse arguments and run
APP=""
BRANCH="main"
ROLLBACK=false
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h) echo "Usage: srv deploy <app> [--branch <branch>] [--rollback]"; exit 0 ;;
--branch|-b) BRANCH="$2"; shift 2 ;;
--rollback) ROLLBACK=true; shift ;;
*) APP="$1"; shift ;;
esac
done
[[ -z "$APP" ]] && { srv_error "App name required"; exit 1; }
deploy_app "$APP"Bash Completion
Make the CLI feel professional with tab completion:
# Bash completion for srv command
_srv_completions() {
local cur prev words cword
_init_completion || return
local commands="status services logs docker backup deploy ssh secrets update firewall config help"
local docker_commands="ps logs shell restart stop start stats clean pull compose"
local backup_commands="run status list restore verify schedule"
local services_actions="list status start stop restart enable disable logs"
local firewall_actions="status allow deny list reset"
case $cword in
1)
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
;;
2)
case "${words[1]}" in
docker)
COMPREPLY=($(compgen -W "$docker_commands" -- "$cur"))
;;
backup)
COMPREPLY=($(compgen -W "$backup_commands" -- "$cur"))
;;
services)
COMPREPLY=($(compgen -W "$services_actions" -- "$cur"))
;;
firewall)
COMPREPLY=($(compgen -W "$firewall_actions" -- "$cur"))
;;
deploy)
local apps=$(ls -1 /opt/apps 2>/dev/null)
COMPREPLY=($(compgen -W "$apps" -- "$cur"))
;;
logs|ssh)
local services=$(systemctl list-units --type=service --state=running --no-pager --no-legend | awk '{print $1}' | sed 's/.service$//')
COMPREPLY=($(compgen -W "$services" -- "$cur"))
;;
esac
;;
3)
case "${words[1]}" in
docker)
case "${words[2]}" in
logs|shell|restart|stop|start)
local containers=$(docker ps --format '{{.Names}}' 2>/dev/null)
COMPREPLY=($(compgen -W "$containers" -- "$cur"))
;;
esac
;;
esac
;;
esac
}
complete -F _srv_completions srv# Add to ~/.bashrc or /etc/bash_completion.d/
source /path/to/srv-completion.bashAPI Wrapper for External Services
Create a unified interface for commonly used APIs like Cloudflare, Discord, and Healthchecks.io:
#!/bin/bash
# =============================================================================
# API Wrapper - Unified interface for external APIs
# =============================================================================
set -uo pipefail
API_CONFIG="${API_CONFIG:-$HOME/.config/api-wrapper/config}"
API_CACHE="${API_CACHE:-$HOME/.cache/api-wrapper}"
mkdir -p "$API_CACHE"
[[ -f "$API_CONFIG" ]] && source "$API_CONFIG"
# Rate limiting
check_rate_limit() {
local api="$1"
local limit="${2:-60}"
local now=$(date +%s)
# Check and update rate limit file
# Returns 1 if limit exceeded
}
# Generic API request with retries
api_request() {
local method="$1"
local url="$2"
local data="${3:-}"
local headers=("${@:4}")
local retries=3
local attempt=1
while [[ $attempt -le $retries ]]; do
local response=$(curl -s -w "\n%{http_code}" -X "$method" \
"${headers[@]/#/-H }" \
${data:+-d "$data"} \
"$url")
local http_code=$(echo "$response" | tail -1)
local body=$(echo "$response" | sed '$d')
case "$http_code" in
2*) echo "$body"; return 0 ;;
429) sleep $((5 * attempt)) ;;
5*) sleep 5 ;;
*) echo "$body" >&2; return 1 ;;
esac
((attempt++))
done
}
# Cloudflare API
cf_api() {
api_request "${2:-GET}" \
"https://api.cloudflare.com/client/v4$1" \
"${3:-}" \
"Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
"Content-Type: application/json"
}
cf_list_zones() { cf_api "/zones" | jq -r '.result[] | "\(.id)\t\(.name)"'; }
cf_purge_cache() { cf_api "/zones/$1/purge_cache" "POST" '{"purge_everything":true}'; }
# Discord webhooks
discord_send() {
local message="$1"
local color="${2:-}"
local data=$(jq -n --arg msg "$message" '{content: $msg}')
api_request "POST" "$DISCORD_WEBHOOK_URL" "$data" "Content-Type: application/json"
}
# Healthchecks.io
healthcheck_ping() {
curl -fsS -m 10 --retry 5 "$1${2:+/$2}" > /dev/null
}
# Main CLI
case "${1:-}" in
cf|cloudflare) shift; cf_api "$@" ;;
discord) shift; discord_send "$@" ;;
hc|healthcheck) shift; healthcheck_ping "$@" ;;
*) echo "Usage: api-wrapper <cf|discord|hc> <command>" ;;
esacInteractive Server Dashboard
Build a TUI dashboard for real-time server monitoring:
#!/bin/bash
# =============================================================================
# Terminal Dashboard - Real-time server monitoring
# =============================================================================
set -uo pipefail
REFRESH_INTERVAL="${REFRESH_INTERVAL:-5}"
# Colors and terminal
RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m'
BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m'
draw_bar() {
local percent="$1" width="${2:-20}" color="$GREEN"
[[ $percent -gt 75 ]] && color=$YELLOW
[[ $percent -gt 90 ]] && color=$RED
local filled=$((percent * width / 100))
local empty=$((width - filled))
printf "["
printf "$color"
printf '%*s' "$filled" '' | tr ' ' '█'
printf "$NC"
printf '%*s' "$empty" '' | tr ' ' '░'
printf "] %3d%%\n" "$percent"
}
section_system() {
echo -e "${BOLD}${BLUE}━━━ System ━━━${NC}"
# CPU
local cpu_idle=$(top -bn1 | grep "Cpu(s)" | awk '{print $8}' | cut -d. -f1)
printf "CPU "; draw_bar "$((100 - ${cpu_idle:-0}))" 25
# Memory
local mem=$(free | awk '/Mem:/ {printf "%.0f", $3/$2*100}')
printf "MEM "; draw_bar "$mem" 25
# Disk
local disk=$(df / | awk 'NR==2 {gsub(/%/,""); print $5}')
printf "DISK "; draw_bar "$disk" 25
echo "Load: $(cat /proc/loadavg | cut -d' ' -f1-3)"
echo "Up: $(uptime -p | sed 's/up //')"
}
section_docker() {
echo -e "\n${BOLD}${BLUE}━━━ Docker ━━━${NC}"
if docker info &>/dev/null 2>&1; then
local running=$(docker ps -q | wc -l)
echo "Containers: $running running"
docker stats --no-stream --format " {{.Name}}\t{{.CPUPerc}}\t{{.MemPerc}}" | head -5
else
echo "Docker not available"
fi
}
section_services() {
echo -e "\n${BOLD}${BLUE}━━━ Services ━━━${NC}"
local failed=$(systemctl --failed --no-pager --no-legend 2>/dev/null | wc -l)
if [[ $failed -gt 0 ]]; then
echo -e "${RED}Failed: $failed${NC}"
else
echo -e "${GREEN}All services healthy${NC}"
fi
}
render() {
clear
echo -e "${BOLD}${CYAN}╔═══════════════════════════════════════════════════════════╗"
echo -e "║ SERVER DASHBOARD - $(hostname) ║"
echo -e "║ $(date '+%Y-%m-%d %H:%M:%S') ║"
echo -e "╚═══════════════════════════════════════════════════════════╝${NC}"
section_system
section_docker
section_services
echo -e "\n${DIM}Refresh: ${REFRESH_INTERVAL}s | Press 'q' to quit${NC}"
}
# Main loop
while true; do
render
if read -rsn1 -t "$REFRESH_INTERVAL" key; then
[[ "$key" == "q" ]] && break
fi
doneInstallation Script
Tie everything together with an installer:
#!/bin/bash
# =============================================================================
# srv CLI Installation Script
# =============================================================================
set -euo pipefail
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
SRV_HOME="${SRV_HOME:-$HOME/.srv}"
echo "Installing srv CLI..."
# Create directories
mkdir -p "$SRV_HOME/commands"
mkdir -p "$HOME/.config/api-wrapper"
# Install main executable
cp ./srv "$INSTALL_DIR/srv"
chmod +x "$INSTALL_DIR/srv"
# Install subcommands
for cmd in srv-docker srv-backup srv-deploy; do
if [[ -f "./$cmd" ]]; then
cp "./$cmd" "$SRV_HOME/commands/"
chmod +x "$SRV_HOME/commands/$cmd"
fi
done
# Create default config
if [[ ! -f "$SRV_HOME/config" ]]; then
cat > "$SRV_HOME/config" << 'EOF'
# srv CLI Configuration
EDITOR="${EDITOR:-vim}"
APPS_DIR="/opt/apps"
BACKUP_DIR="/var/backups"
# DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/xxx/yyy"
EOF
fi
# Install completion
if [[ -f "./srv-completion.bash" ]]; then
cp "./srv-completion.bash" "$SRV_HOME/"
echo "source $SRV_HOME/srv-completion.bash" >> "$HOME/.bashrc"
fi
echo ""
echo "✓ Installation complete!"
echo ""
echo "Usage:"
echo " srv status # System overview"
echo " srv docker ps # Docker containers"
echo " srv help # Full help"
echo ""
echo "Reload your shell: source ~/.bashrc"Tips & Quick Reference
Tips for Building CLI Tools
- Consistent interface — Use the same patterns for all commands
- Fail gracefully — Check for dependencies, provide helpful errors
- Document everything — Every command should have --help
- Add completion — Tab completion makes a huge difference
- Log operations — Keep a record for debugging
- Dry-run mode — Let users preview dangerous operations
- Color thoughtfully — Highlight important info, don't decorate
Quick Reference: CLI Building Prompts
| Need | Prompt Pattern |
|---|---|
| Main CLI | "Create CLI tool called [name] with commands for [operations]" |
| Subcommand | "Add subcommand [name] to handle [operations] with [options]" |
| Completion | "Generate bash completion for [tool] supporting [commands]" |
| API wrapper | "Create wrapper for [API] with [auth] and [operations]" |
| Dashboard | "Build terminal dashboard showing [metrics] with [refresh rate]" |
| Installer | "Generate install script that sets up [components] at [paths]" |
Series Conclusion
Over these ten parts, you've learned to use Claude Code for:
- Server hardening and initial setup
- OpenStack infrastructure automation with Terraform
- Docker Compose generation for any application stack
- Monitoring pipelines with Prometheus and Grafana
- GitOps workflows with Ansible and CI/CD
- AI model deployment for local inference
- Backup and disaster recovery automation
- Security hardening and compliance checking
- Log analysis and troubleshooting workflows
- Custom CLI tools that tie everything together
The key insight: Claude Code isn't just for generating one-off scripts. It's a conversational partner for building complete, professional infrastructure tooling. Describe what you need, iterate on the output, and build a personalized toolkit that matches exactly how you work.
