CLI Guidelines - Designing Great Command-Line Interfaces
CLI Guidelines
Comprehensive guide for creating user-friendly command-line tools:
Core Design Principles:
Human-First Design:
- Helpful by Default: Show useful information without being asked
- Fail Gracefully: Clear error messages with actionable suggestions
- Consistent Behavior: Follow established conventions
- Progressive Disclosure: Start simple, allow complexity when needed
Error Handling Best Practices:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Bad error message
error: invalid input
# Good error message
error: expected a number, got "hello"
Try: mycommand --count 5
# Even better with suggestions
error: unknown flag '--hep'
Did you mean '--help'?
# Excellent with context
error: cannot connect to database
database: postgresql://localhost:5432/myapp
reason: connection refused
help: is PostgreSQL running? try 'brew services start postgresql'
|
Guidelines by Category:
Arguments and Flags:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Prefer flags over positional arguments for clarity
# Good
deploy --environment staging --version 1.2.3
# Less clear
deploy staging 1.2.3
# Support both short and long forms
grep -r --recursive # Both work
grep -R --recursive # Consistent
# Use consistent flag naming
--verbose / -v # Standard conventions
--quiet / -q
--help / -h
--version / -V (capital for version)
|
Output and Feedback:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Show progress for long operations
Downloading... [████████████████████████████] 100% (15.2 MB/s)
# Provide machine-readable output options
mycommand --format json
mycommand --format csv
mycommand --format table # Human-readable default
# Color coding (but respect NO_COLOR)
✓ Success: Database backup completed
⚠ Warning: Low disk space
✗ Error: Connection failed
# Quiet modes for scripts
mycommand --quiet # No output on success
mycommand --silent # Minimal output
|
Configuration and Environment:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Configuration hierarchy (most to least specific)
1. Command-line flags
2. Environment variables
3. Configuration files
4. Defaults
# Environment variable naming
MYAPP_DATABASE_URL
MYAPP_LOG_LEVEL
MYAPP_CONFIG_PATH
# Config file locations (XDG spec)
~/.config/myapp/config.yaml # Primary
~/.myapp.yaml # Fallback
/etc/myapp/config.yaml # System-wide
|
Advanced Features:
Interactive Elements:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # Example: Interactive confirmation
import click
@click.command()
@click.option('--force', is_flag=True, help='Skip confirmation')
def delete_database(force):
if not force:
click.confirm('This will delete all data. Continue?', abort=True)
with click.progressbar(tables, label='Dropping tables') as bar:
for table in bar:
drop_table(table)
click.secho('✓ Database deleted', fg='green')
# Tab completion support
@click.command()
@click.argument('environment', type=click.Choice(['dev', 'staging', 'prod']))
def deploy(environment):
"""Deploy to specified environment."""
pass
|
Documentation Integration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # Multi-level help
mycommand --help # Overview
mycommand deploy --help # Subcommand help
mycommand --help-advanced # Advanced options
# Examples in help text
Usage: git-deploy [OPTIONS] ENVIRONMENT
Deploy application to specified environment.
Examples:
git-deploy staging
git-deploy prod --version 1.2.3
git-deploy dev --force --no-backup
Options:
--version TEXT Specific version to deploy
--force Skip safety checks
--no-backup Skip database backup
--help Show this message and exit.
|
Parinfer - Revolutionary Lisp Editing
Parinfer - simpler Lisp editing
Innovative approach to editing Lisp code that infers parentheses from
indentation:
The Parentheses Problem:
Traditional Lisp Editing Challenges:
1
2
3
4
5
6
7
8
9
10
11
| ;; Traditional Lisp - parentheses can be overwhelming
(defn factorial [n]
(if (<= n 1)
1
(* n (factorial (- n 1)))))
;; Easy to have mismatched parens
(defn broken [x]
(if (> x 0)
(inc x)
(dec x)) ; Missing closing paren somewhere?
|
Parinfer Modes:
Indent Mode:
- Indentation drives structure: Parentheses inferred from indentation
- Writing-focused: Natural for entering new code
- Familiar: Works like Python or other indentation-based languages
1
2
3
4
5
6
7
8
9
10
11
| ;; Type this (without worrying about parens):
defn factorial [n]
if (<= n 1)
1
* n (factorial (- n 1))
;; Parinfer automatically adds parens:
(defn factorial [n]
(if (<= n 1)
1
(* n (factorial (- n 1)))))
|
Paren Mode:
- Parentheses drive structure: Indentation adjusted to match parens
- Editing-focused: Good for modifying existing code
- Traditional: Familiar to experienced Lisp programmers
1
2
3
4
5
6
7
8
9
10
11
| ;; Move a closing paren:
(defn example [x]
(if (> x 0)
(inc x))
(dec x))
;; Parinfer adjusts indentation:
(defn example [x]
(if (> x 0)
(inc x)
(dec x)))
|
Smart Features:
Cursor-Based Inference:
1
2
3
4
5
6
7
8
| ;; Cursor position affects paren inference
;; Cursor at end of line:
(map inc [1 2 3|])
;; Result: (map inc [1 2 3])
;; Cursor in middle:
(map inc [1 2| 3])
;; Different grouping possible based on context
|
Tab Stops:
1
2
3
4
5
6
7
8
9
| ;; Smart tab stops for alignment
(let [x 1
y 2 ; Aligned automatically
long 3])
;; Function arguments align naturally
(function arg1
arg2 ; Aligned with first arg
arg3)
|
Editor Integration:
Available Implementations:
- Atom: Lisp editing with Parinfer integration
- VS Code: Parinfer extension for modern editor
- Vim/Neovim: Multiple Parinfer plugins available
- Emacs: Parinfer packages for the classic Lisp editor
- Sublime Text: Community-maintained Parinfer support
Configuration Example (VS Code):
1
2
3
4
5
6
| {
"parinfer.defaultMode": "indent",
"parinfer.forceBalance": true,
"parinfer.previewCursorScope": true,
"parinfer.dimParens": true
}
|
Benefits for Lisp Learning:
Reduced Cognitive Load:
- Focus on Logic: Less mental energy spent on parentheses
- Visual Structure: Indentation makes nesting obvious
- Error Prevention: Automatic balancing prevents common mistakes
- Gentle Learning Curve: Familiar indentation-based editing
Before/After Comparison:
1
2
3
4
5
6
7
8
9
10
11
12
13
| ;; Without Parinfer - manual paren management
(defn process-items [items]
(map (fn [item]
(if (valid? item)
(transform item)
(default-value))) items))
;; With Parinfer - focus on structure
defn process-items [items]
map (fn [item]
if (valid? item)
transform item
default-value) items
|
Mosh: the mobile shell
Robust, responsive terminal application that handles intermittent connectivity:
Problems with Traditional SSH:
Common SSH Issues:
- Connection Drops: WiFi disconnections kill sessions
- Lag: Every keystroke waits for round-trip
- IP Changes: Moving between networks breaks connections
- Firewall Issues: NAT and firewall complications
Mosh Solutions:
Key Technologies:
State Synchronization Protocol (SSP):
- Client and server maintain synchronized terminal state
- Only sends differences, not full screen updates
- Works over UDP for better handling of packet loss
Predictive Echo:
- Shows typed characters immediately
- Underlines predictions until confirmed by server
- Reduces perceived latency dramatically
Roaming Support:
1
2
3
4
5
6
7
| # Traditional SSH breaks when IP changes
ssh user@server
# WiFi → 4G transition = broken connection
# Mosh maintains connection across IP changes
mosh user@server
# WiFi → 4G → different WiFi = seamless transition
|
Technical Architecture:
Connection Process:
1
2
3
4
5
6
7
8
| # 1. Mosh client connects via SSH
mosh user@server
# 2. SSH launches mosh-server on remote host
# 3. mosh-server chooses UDP port and prints connection info
# 4. SSH connection terminates
# 5. Client connects directly via UDP
# 6. State synchronization begins
|
State Synchronization:
Client State: "hello world"
Server State: "hello world"
User types: "hello world!"
Client State: "hello world!" (immediately shown)
Server State: "hello world" (until network packet arrives)
Network packet arrives:
Server State: "hello world!" (synchronized)
Advanced Features:
Predictive Text Display:
# User types quickly:
$ git commit -m "fix bug"
^^^^^^^^^^^^^^^^^^ (underlined = predicted)
# Once server confirms:
$ git commit -m "fix bug"
^^^^^^^^^^^^^^^^^^ (normal display = confirmed)
# If server differs:
$ git commit -m "fix bug"
^^^ (server had different response)
Firewall and NAT Traversal:
1
2
3
4
5
6
7
8
| # Mosh uses UDP port range (default 60000-61000)
# Configure firewall to allow:
iptables -A INPUT -p udp --dport 60000:61000 -j ACCEPT
# Or specify port range:
mosh --server="mosh-server new -p 2222" user@server
# Works through NAT (unlike SSH X11 forwarding)
|
Installation and Usage:
Installation:
1
2
3
4
5
6
7
8
9
10
11
| # Ubuntu/Debian (both client and server needed)
sudo apt install mosh
# macOS
brew install mosh
# CentOS/RHEL
sudo yum install mosh
# Client connects to server (server auto-installed via SSH)
mosh user@hostname
|
Advanced Options:
1
2
3
4
5
6
7
8
9
10
| # Specify SSH port
mosh --ssh="ssh -p 2222" user@server
# Set prediction mode
mosh --predict=always user@server # Always predict
mosh --predict=never user@server # Never predict
mosh --predict=adaptive user@server # Default
# Specify colors (for 256-color support)
mosh --colors=256 user@server
|
ripgrep-all (rga) - Search Inside Documents
rga: ripgrep, but also search in PDFs, E-Books, Office documents, zip, tar.gz, etc.
Extended version of ripgrep that searches inside various file formats:
Supported File Types:
1
2
3
4
5
6
7
| # Search inside compressed archives
rga "search term" archive.zip
rga "function name" backup.tar.gz
rga "config setting" bundle.tar.xz
# Nested archives supported
rga "password" archive.zip/nested.tar.gz/document.pdf
|
1
2
3
4
5
6
7
8
9
10
11
12
| # Office documents
rga "quarterly report" presentation.pptx
rga "budget analysis" spreadsheet.xlsx
rga "project timeline" document.docx
# PDF documents
rga "machine learning" research_paper.pdf
rga "installation guide" manual.pdf
# E-books
rga "character development" novel.epub
rga "design patterns" programming_book.mobi
|
1
2
3
4
5
6
7
8
9
10
| # Image text extraction (OCR)
rga "street sign" photo.jpg
rga "license plate" security_footage.png
# Subtitle files
rga "dramatic scene" movie.mkv # Searches embedded subtitles
rga "dialogue" subtitles.srt
# Database files
rga "user_table" database.sqlite
|
Advanced Usage:
Adapter Configuration:
1
2
3
4
5
6
7
8
9
10
11
| # List available adapters
rga --rga-list-adapters
# Use specific adapter
rga --rga-adapters=zip,tar "search term"
# Disable slow adapters (like OCR)
rga --rga-adapters=-tesseract "search term"
# Cache results for faster repeated searches
rga --rga-cache-max-blob-len=10M "search term"
|
Integration with ripgrep Options:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Case-insensitive search
rga -i "Search Term" documents/
# Show context lines
rga -C 3 "important phrase" reports/
# Search specific file types only
rga -t pdf "research data" papers/
# JSON output for processing
rga --json "api_key" config_files/
# Parallel processing
rga -j 8 "performance" large_archive.zip
|
Caching Strategy:
1
2
3
4
5
6
| # Enable caching for large files
export RGA_CACHE_DIR=~/.cache/rga
rga --rga-cache-max-blob-len=100M "search term"
# Preprocess large archives
rga --rga-cache-compression-level=1 "index term" huge_backup.tar.gz
|
Resource Management:
1
2
3
4
5
6
7
8
| # Limit memory usage for OCR
rga --rga-adapters=-tesseract "text" images/
# Parallel processing limits
rga -j 4 "search term" documents/ # Use 4 threads max
# Skip large files
rga --max-filesize=50M "config" archive.zip
|
Practical Applications:
Log Analysis:
1
2
3
4
5
6
| # Search compressed log archives
rga "ERROR" logs.tar.gz
rga -A 5 -B 5 "database timeout" application_logs.zip
# Find configuration in backups
rga "database.*password" system_backup.tar.gz
|
Research and Documentation:
1
2
3
4
5
6
7
8
9
10
11
| # Academic research
rga -i "neural network" papers/*.pdf
rga "methodology" thesis_sources.zip
# Code archaeology
rga "deprecated function" legacy_code.tar.gz
rga "TODO.*security" project_archive.zip
# Compliance and auditing
rga "personal.*data" document_archive.tar.gz
rga "license.*agreement" contracts.zip
|
Troubleshooting Script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #!/bin/bash
# search-everywhere.sh - comprehensive search tool
search_term="$1"
directory="${2:-.}"
echo "Searching for '$search_term' in $directory"
echo "======================================="
echo "Regular files:"
rg "$search_term" "$directory"
echo -e "\nDocuments and archives:"
rga "$search_term" "$directory"
echo -e "\nCase-insensitive search:"
rga -i "$search_term" "$directory"
echo -e "\nWith context:"
rga -C 2 "$search_term" "$directory"
|
These tools represent modern approaches to common development and system
administration tasks - creating user-friendly CLIs, making complex code editing
more accessible, handling unreliable network connections, and searching through
diverse file formats efficiently.