Claude
Skills
Sign in
Back

cli-guidelines

Included with Lifetime
$97 forever

This skill should be used when the user asks to "build a CLI tool", "create command-line interface", "CLI best practices", "clig.dev guidelines", "help text patterns", "CLI arguments", "CLI error messages", "CLI output formatting", "human-first design", "CLI interactivity", "exit codes", "CLI configuration", "environment variables", or mentions building user-facing Nushell scripts, commands, or tools that should follow professional CLI design principles.

Designscripts

What this skill does


# CLI Guidelines for Nushell

Comprehensive reference for building excellent command-line interfaces in Nushell, based on [clig.dev](https://clig.dev) principles and adapted for Nushell's structured data paradigm. Great CLIs are not just functional - they are empathetic, discoverable, consistent, and robust.

## Why CLI Guidelines Matter

Command-line interfaces are often the primary way developers and power users interact with tools. A well-designed CLI:

- **Reduces cognitive load** - Users can focus on their tasks, not fighting the tool
- **Builds trust** - Consistent, predictable behavior creates confidence
- **Enables automation** - Scripts and pipelines work reliably
- **Scales knowledge** - Learning one well-designed CLI transfers to others

## Core Philosophy from clig.dev

The eight principles that guide excellent CLI design:

### 1. Human-First Design

**Humans come first, machines second.** Optimize for human understanding by default.

```nushell
# Human-friendly: structured output Nushell displays beautifully
def "files analyze" [path: path = "."] -> table {
    ls $path
    | where type == "file"
    | select name size modified
    | sort-by size --reverse
    | first 10
}

# Machine-readable when needed: one pipe away
# files analyze | to json
# files analyze | to csv
```

**Nushell advantage:** Tables and records are both human-readable AND machine-parseable. No need to choose.

### 2. Simple Parts That Work Together

**Do one thing well.** Build small, focused commands that compose via pipelines.

```nushell
# GOOD: Single responsibility, composable
export def "git branches-merged" [] {
    git branch --merged
    | lines
    | where $it !~ '\*'
    | str trim
}

# Can be composed: git branches-merged | each { git branch -d $in }

# BAD: Trying to do too much
export def "git-cleanup-everything" [] {
    # Fetches, prunes, deletes merged, deletes stale, garbage collects...
    # Violates single responsibility
}
```

### 3. Consistency Across Programs

**Users build mental models. Respect them.** Use standard flag names and behaviors.

| Short | Long | Purpose |
|-------|------|---------|
| `-h` | `--help` | Show help text |
| `-v` | `--verbose` | More detailed output |
| `-q` | `--quiet` | Suppress non-error output |
| `-f` | `--force` | Skip confirmations |
| `-n` | `--dry-run` | Preview without changes |
| `-o` | `--output` | Output file or destination |
| `-r` | `--recursive` | Include subdirectories |

```nushell
# Consistent across your entire toolset
export def "backup create" [
    source: path
    destination: path
    --verbose (-v)
    --quiet (-q)
    --force (-f)
    --recursive (-r)
    --dry-run (-n)
] {
    # Users learn once, apply everywhere
}
```

### 4. Saying Just Enough

**Balance silence and verbosity.** Default to useful information, not noise.

```nushell
# Default: Essential information only
export def "deploy status" [--verbose (-v)] {
    let status = get-deployment-status

    if $verbose {
        # Full details when requested
        $status | table --expand
    } else {
        # Concise default
        print $"Status: ($status.state) | Uptime: ($status.uptime)"
    }
}

# Silent success for scripts
export def "cache clear" [--quiet (-q)] {
    rm -rf ~/.cache/myapp

    if not $quiet {
        print "Cache cleared successfully"
    }
}
```

### 5. Ease of Discovery

**Users should learn your CLI by using it.** Make help accessible and thorough.

```nushell
# Nushell's def comments become built-in help
# Run: help task add

# Add a new task to the task list
#
# Creates a task with the given title and optional metadata.
# Tasks are stored in ~/.local/share/tasks/tasks.nuon
#
# Examples:
#   task add "Fix bug in parser"
#   task add "Review PR" --priority 5 --due 2024-03-15
#   task add "Weekly meeting" --tags ["recurring", "team"]
export def "task add" [
    title: string           # The task title (required)
    --priority (-p): int    # Priority level 1-5 (default: 3)
    --due (-d): datetime    # Due date in any parseable format
    --tags (-t): list<string>  # Tags for categorization
] {
    # Implementation
}

# Typing `task` alone shows available subcommands
export def "task" [] {
    print "Task management commands:"
    print ""
    print "  task add      - Create a new task"
    print "  task list     - Show all tasks"
    print "  task complete - Mark task as done"
    print "  task delete   - Remove a task"
    print ""
    print "Run 'help task <command>' for details"
}
```

### 6. Conversation as the Norm

**Treat CLI interaction as a dialogue.** Confirm destructive actions and show progress.

```nushell
# Confirmation for destructive actions
export def "data purge" [
    --force (-f)    # Skip confirmation
    --dry-run (-n)  # Show what would be deleted
] {
    let items = (ls data/ | length)

    if $dry_run {
        print $"Would delete ($items) items"
        return
    }

    if not $force {
        let confirm = input $"Delete ($items) items? This cannot be undone. [y/N] "
        if ($confirm | str downcase) != "y" {
            print "Aborted."
            return
        }
    }

    rm -rf data/*
    print $"Deleted ($items) items"
}

# Progress for long operations
export def "sync remote" [--verbose (-v)] {
    let files = (ls -r src/ | where type == "file")
    let total = ($files | length)

    $files | enumerate | each { |item|
        if $verbose {
            print -e $"Syncing [($item.index + 1)/($total)]: ($item.item.name)"
        }
        upload-file $item.item.name
    }

    print $"Synced ($total) files"
}
```

### 7. Robustness

**Handle edge cases gracefully. Fail fast with clear messages.**

```nushell
export def "file process" [path: path] {
    # Validate early with helpful messages
    if not ($path | path exists) {
        error make {
            msg: $"File not found: ($path)"
            help: "Check the path and try again. Use 'ls' to see available files."
        }
    }

    if ($path | path type) != "file" {
        error make {
            msg: $"Expected a file, got: ($path | path type)"
            help: "Use --recursive for directories"
        }
    }

    # Wrap risky operations
    try {
        open $path | process-contents
    } catch { |err|
        error make {
            msg: $"Failed to process ($path)"
            help: $"File may be corrupted or unsupported format.\nOriginal: ($err.msg)"
        }
    }
}
```

### 8. Empathy

**Remember: users are often stressed, debugging, or learning.** Be helpful.

```nushell
# Helpful error messages with actionable suggestions
export def "config validate" [path: path = "config.toml"] {
    if not ($path | path exists) {
        print $"(ansi red)Error:(ansi reset) Config file not found: ($path)"
        print ""
        print "To create a default config:"
        print $"  (ansi cyan)config init(ansi reset)"
        print ""
        print "Or specify a different path:"
        print $"  (ansi cyan)config validate /path/to/config.toml(ansi reset)"
        exit 1
    }

    # Validate with specific fix suggestions
    let errors = validate-config $path
    if ($errors | length) > 0 {
        print $"(ansi yellow)Found ($errors | length) issue(s):(ansi reset)"
        $errors | each { |e|
            print $"  Line ($e.line): ($e.message)"
            if ($e.suggestion | is-not-empty) {
                print $"    (ansi dim)Fix: ($e.suggestion)(ansi reset)"
            }
        }
    }
}
```

## Nushell's Natural Advantages

Nushell inherently supports many CLI guidelines through its design:

| CLI Guideline | Nushell Feature |
|---------------|-----------------|
| Structured output | Tables, records, lists - native data types |
| Type safety | Built-in type system validates input |
| Self-documenting | `def` comments become `help` output |
| Error handling | `try`/`catch` with rich error messages |
| Composability | Pipeline-native, structured data flows |
| Discoverability | Tab completion, `help` s

Related in Design