Claude
Skills
Sign in
Back

terminal-concepts

Included with Lifetime
$97 forever

Comprehensive guide for building CLI and TUI applications - terminal internals, design principles, and battle-tested patterns When building CLI/TUI apps, implementing argument parsing, handling terminal input/output, escape codes, buffering, signals, or asking about terminal development concepts

Design

What this skill does


# Terminal Concepts for Developers

## Overview

This agent provides comprehensive guidance for **building and developing** terminal applications (CLI tools and TUIs). Learn how terminals work, design principles from proven programs, and practical patterns for creating robust, user-friendly command-line applications.

**Philosophy**: Focus on timeless concepts and learn from battle-tested programs like git, vim, tmux, less, ripgrep, and fzf.

**Target Audience**: Developers at all levels building terminal applications in any language.

## What Developers Need to Know

Building terminal applications requires understanding several interconnected concepts:

1. **Terminal Fundamentals**: How TTYs, PTYs, streams, and buffering work
2. **CLI Design**: User-facing interface principles (arguments, output, errors)
3. **TUI Architecture**: Interactive full-screen applications
4. **Common Pitfalls**: Issues developers face and how to avoid them

## Learning from Proven Programs

The best way to understand terminal application design is to study programs that have stood the test of time:

- **git**: Consistent subcommand interface, porcelain vs plumbing separation
- **vim**: Modal editing, robust state management
- **tmux**: Client-server architecture, session management
- **less**: Pager pattern, search and navigation
- **ripgrep**: Sensible defaults, performance-focused design
- **fzf**: Interactive filtering, minimal interface
- **cargo**: Clear progress indicators, helpful error messages

Throughout this guide, we'll reference these programs to illustrate concepts.

---

# Part 1: Terminal Fundamentals for Developers

## Understanding the Terminal Stack

When you build a terminal application, you're working within a layered system:

```
┌─────────────────────────────────┐
│   Terminal Emulator             │ ← User sees (renders text, sends input)
│   (iTerm2, Alacritty, etc.)     │
└─────────────────────────────────┘
              ↕ (PTY)
┌─────────────────────────────────┐
│   Shell (bash, zsh, fish)       │ ← Interprets commands
└─────────────────────────────────┘
              ↕ (fork/exec)
┌─────────────────────────────────┐
│   Your Program                  │ ← Your CLI/TUI application
└─────────────────────────────────┘
```

### TTYs and PTYs

**TTY** (Teletypewriter): Originally physical devices, now refers to the terminal driver in the operating system.

**PTY** (Pseudo-Terminal): A pair of virtual devices that emulate a TTY:

- **Master side**: Terminal emulator reads/writes here
- **Slave side**: Your program sees this as stdin/stdout/stderr

**Why This Matters for Developers**:

- Your program receives input filtered through the TTY driver
- Some control characters are intercepted by the OS (Ctrl-C, Ctrl-Z)
- Buffering behavior differs between TTY and pipes
- You need to detect if you're running in a TTY vs pipe

### Terminal Modes: Cooked vs Raw

**Cooked Mode** (Canonical Mode):

- Default mode for most programs
- OS provides line editing (backspace, Ctrl-U, Ctrl-W)
- Input delivered to your program line-by-line after Enter
- Control characters handled by OS (Ctrl-C sends SIGINT)

**Raw Mode**:

- Character-by-character input (no line buffering)
- Your program receives every keypress including Ctrl-C
- Required for TUIs (vim, less, htop)
- Your program must implement all input handling

**When to Use Each**:

- **Cooked mode**: Normal CLI tools (git, cargo, npm)
- **Raw mode**: Interactive TUIs, line editors

### Standard Streams and File Descriptors

Every process has three standard streams:

| Stream | FD  | Purpose                   | Examples                        |
| ------ | --- | ------------------------- | ------------------------------- |
| stdin  | 0   | Input from user or pipe   | Reading commands, file content  |
| stdout | 1   | Primary output            | Results, listings, JSON         |
| stderr | 2   | Errors, logs, diagnostics | Error messages, warnings, debug |

**Critical Design Principle**: Separate stdout and stderr properly.

**Why This Matters**:

```bash
# User wants to pipe your output
your-tool | jq .         # Only works if output goes to stdout

# User wants to capture errors
your-tool 2> errors.log  # Only works if errors go to stderr
```

**Examples from Proven Programs**:

- **git**: Errors to stderr, porcelain output to stdout
- **ripgrep**: Matches to stdout, warnings to stderr
- **cargo**: Build status to stderr, `--message-format=json` to stdout

### Detecting TTY vs Pipe

Your program must detect its output destination to format appropriately:

**Concept** (language-agnostic):

```
if isatty(stdout):
    # Human is watching - use colors, progress bars
    enable_colors()
    show_progress()
else:
    # Piped to another program - plain output
    disable_colors()
    no_progress()
```

**Real-world examples**:

- `ls --color=auto`: Colors only for TTY
- `git status`: Full output for TTY, shorter for pipes
- `ripgrep`: Colors and summaries for TTY, plain matches for pipes

**C Implementation**:

```c
#include <unistd.h>

if (isatty(STDOUT_FILENO)) {
    // stdout is a TTY
}
```

**Rust Implementation**:

```rust
use std::io::IsTerminal;

if std::io::stdout().is_terminal() {
    // stdout is a TTY
}
```

**Python Implementation**:

```python
import sys

if sys.stdout.isatty():
    # stdout is a TTY
```

**Go Implementation**:

```go
import "golang.org/x/term"

if term.IsTerminal(int(os.Stdout.Fd())) {
    // stdout is a TTY
}
```

---

## ASCII Control Characters

### The 33 Control Characters

Control characters are created by holding Ctrl and pressing a key. There are 33 total:

- Ctrl-A through Ctrl-Z (26 characters)
- Plus 7 more: Ctrl-@, Ctrl-[, Ctrl-\, Ctrl-], Ctrl-^, Ctrl-\_, Ctrl-?

### Three Categories

**1. OS-Handled (Terminal Driver Intercepts)**:

| Key     | ASCII | Name | Function                      |
| ------- | ----- | ---- | ----------------------------- |
| Ctrl-C  | 3     | ETX  | Sends SIGINT (interrupt)      |
| Ctrl-D  | 4     | EOT  | EOF when line is empty        |
| Ctrl-Z  | 26    | SUB  | Sends SIGTSTP (suspend)       |
| Ctrl-S  | 19    | XOFF | Freezes output (flow control) |
| Ctrl-Q  | 17    | XON  | Resumes output                |
| Ctrl-\  | 28    | FS   | Sends SIGQUIT                 |

**2. Keyboard Literals**:

| Key       | ASCII | Name | Usage                     |
| --------- | ----- | ---- | ------------------------- |
| Enter     | 13    | CR   | Line terminator           |
| Tab       | 9     | HT   | Tab character             |
| Backspace | 127   | DEL  | Delete previous character |
| Ctrl-H    | 8     | BS   | Often same as backspace   |

**3. Application-Specific** (Your Program Can Define):

| Key      | Common Usage                         |
| -------- | ------------------------------------ |
| Ctrl-A   | Move to line start (readline, emacs) |
| Ctrl-E   | Move to line end                     |
| Ctrl-W   | Delete word backwards                |
| Ctrl-U   | Delete line                          |
| Ctrl-K   | Kill to end of line                  |
| Ctrl-R   | Reverse search (shells)              |
| Ctrl-L   | Clear screen                         |
| Ctrl-P/N | Previous/Next (history navigation)   |

### What Developers Can and Cannot Intercept

**In Cooked Mode**:

- OS handles: Ctrl-C, Ctrl-D, Ctrl-Z, Ctrl-S, Ctrl-Q
- You see: Ctrl-A, Ctrl-E, Ctrl-W (already processed by line editing)

**In Raw Mode** (TUIs):

- You can intercept almost everything including Ctrl-C
- **Exception**: Ctrl-Z often still suspends (OS-level)
- You must handle all line editing yourself

**Best Practice**: Respect user expectations. Don't redefine Ctrl-C unless you have a very good reason (and document it clearly).

### Limited Modifier Combinations

Unlike GUI applications, terminals have severe limitations:

- **Only 33 Ctrl combinations** total
- Ctrl-Shift-X **doesn't exist** as a distinct character
- Ctrl-[number] combinations limited
- Alt/Meta combinations inconsistent ac

Related in Design