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 - Mobile Shell#
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.