luff 0.2.1

Print files with formatting
Documentation
# luff

Print files with formatting

[![License: AGPLv3](https://img.shields.io/crates/l/luff)](LICENSE)
[![Rust Version](https://img.shields.io/badge/rust-1.85%2B-orange.svg)](https://www.rust-lang.org)

## Features

- **Multiple Output Formats**: Markdown blocks, tree view
- **Smart File Discovery**: Directory walking with gitignore support
- **Glob Pattern Support**: Explicitly ignore files using bash-style patterns
- **Clipboard Integration**: Copy output directly to system clipboard
- **Config File Support**: YAML configuration with JSON Schema for IDE autocomplete
- **Security Hardened**: Protection against path traversal, symlink attacks, resource exhaustion
- **High Performance**: Upfront collection with minimal allocations, efficient I/O
- **Well Tested**: Unit tests, property-based tests, integration tests, E2E tests
- **Modular Architecture**: Clean separation of concerns, easy to extend

## Installation

### From Source

```bash
git clone https://github.com/owlroute/luff.git
cd luff
cargo install --path crates/luff
```

### Development Build

```bash
cargo build --release
./target/release/luff --help
```

## Usage

### Process Specific Files

Files can be passed as positional arguments or via the `-f` flag:

```bash
luff src/main.rs Cargo.toml
luff -f src/main.rs Cargo.toml
```

### Walk Current Directory

Print all files in the current directory:

```bash
luff
```

### Walk from Git Root

Start from the git repository root:

```bash
luff -g
```

### Include Ignored Files

Include files that `.gitignore` would normally skip:

```bash
luff --add
```

### Explicitly Ignore Files

Ignore files matching glob patterns (in addition to `.gitignore`):

```bash
luff --ignore "*.log" --ignore "target/**"
```

Multiple patterns can be passed to a single `--ignore` flag:

```bash
luff --ignore "*.log" "*.tmp" "*.bak"
```

### Include Hidden Files

Include dot-directories and dotfiles:

```bash
luff --dotfiles
```

Override a config file that enables dotfiles:

```bash
luff --no-dotfiles
```

### Tree Output Format

Display as a tree structure:

```bash
luff --format tree
```

### Limit Directory Depth

Only traverse up to N levels deep:

```bash
luff --max-depth 3
```

### Limit File Count

Cap the number of files collected during a directory walk:

```bash
luff --max-files 500
```

### Copy to Clipboard

Copy output to system clipboard (also prints to stdout):

```bash
luff -c
```

### Clipboard Only (No Stdout)

Copy to clipboard without printing to terminal:

```bash
luff -c -S
```

### Use a Config File

Load settings from a YAML config file:

```bash
luff --config luff.yaml
```

### Generate Config Schema

Generate a JSON Schema for config file validation and IDE autocomplete:

```bash
luff schema
luff schema /tmp/luff.schema.json
```

### Enable Debug Logging

Show detailed information about file processing:

```bash
luff -v
```

### Combined Options

```bash
# Walk git repo, include ignored files, include dotfiles, debug output
luff -g -a --dotfiles -v

# Process specific files with tree format
luff -f src/*.rs --format tree

# Walk with depth limit and copy to clipboard
luff --max-depth 2 -c

# Clipboard-only mode with tree format
luff --format tree -c -S
```

## Options

| Flag | Long                        | Description                                                     |
| ---- | --------------------------- | --------------------------------------------------------------- |
|      | `<FILE>...`                 | Process specific files (positional, disables directory walk)    |
| `-f` | `--files <FILE>...`         | Process specific files (flag form, disables directory walk)     |
|      | `--config <PATH>`           | Path to config file (YAML format)                               |
| `-g` | `--git`                     | Start from git repository root                                  |
|      | `--dotfiles`                | Include hidden files/directories                                |
|      | `--no-dotfiles`             | Exclude hidden files/directories (overrides config)             |
| `-a` | `--add`                     | Add files normally ignored by `.gitignore`                      |
| `-i` | `--ignore <PATTERN>...`     | Explicitly ignore files matching glob patterns                  |
| `-c` | `--clip`                    | Copy output to clipboard (also prints to stdout unless `-S`)    |
| `-S` | `--suppress-stdout`         | Suppress stdout output (useful with `-c` for clipboard-only)    |
| `-v` | `--verbose`                 | Enable debug logging                                            |
|      | `--format <FORMAT>`         | Output format: `markdown` (`md`), `tree` (default: `markdown`)  |
|      | `--max-depth <N>`           | Maximum directory depth (0 = unlimited, default: 0)             |
|      | `--max-files <N>`           | Maximum files to collect during walk                            |
|      | `--max-clipboard-mb <N>`    | Maximum clipboard size in MB (default: 100, hard limit: 1000)   |
| `-h` | `--help`                    | Print help information                                          |
| `-V` | `--version`                 | Print version information                                       |

### Subcommands

| Command  | Description                                                  |
| -------- | ------------------------------------------------------------ |
| `schema` | Generate JSON Schema for config files (for IDE autocomplete) |

## Output Modes

| Flags   | Behavior                                                 |
| ------- | -------------------------------------------------------- |
| (none)  | Stream to stdout (default, low memory)                   |
| `-c`    | Buffer entire output, write to stdout AND clipboard      |
| `-c -S` | Buffer entire output, copy to clipboard only (no stdout) |
| `-S`    | No output (edge case, allowed but not useful)            |

**Note**: Clipboard mode requires buffering the entire output in memory before writing. For large codebases, this may use significant memory. Use `--max-clipboard-mb` to control the limit.

## Architecture

### Key Design Decisions

- **Upfront Collection**: Directory walker collects file paths at construction to prevent output file inclusion
- **Shell Redirection Protection**: Skips empty files created within 2 seconds to avoid including `luff > output.txt` output
- **Printer Actor**: Bounded channel (capacity 16) to a dedicated writer thread for contention-free stdout
- **Zero-cost Abstractions**: Walker enum provides unified interface without runtime overhead
- **Security First**: All file operations validated against path traversal, size limits, symlink attacks
- **Explicit Error Handling**: No panics in library code, `Result` types throughout

## Development

### Prerequisites

- Rust 1.85 or later
- Git (for git-related functionality)
- Clipboard support (X11/Wayland on Linux, native on macOS/Windows)

### Building

```bash
cargo build
cargo build --release
cargo run -- --help
```

### Testing

```bash
cargo test
cargo test -- --nocapture
cargo test --test integration_test
cargo test --test e2e_test
```

### Benchmarking

```bash
cargo bench
cargo bench walker_small
```

### Code Quality

```bash
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt
cargo geiger
```

## Security

This project follows security best practices:

- **Path Validation**: All paths validated against traversal attacks
- **Size Limits**: Files limited to 100MB to prevent memory exhaustion
- **TOCTOU Protection**: File metadata re-checked after open
- **Symlink Safety**: Canonicalization with validation
- **Input Sanitization**: All user input validated
- **No Unsafe Code**: Pure safe Rust (verify with `cargo geiger`)
- **Root Containment**: File list mode prevents access outside project root
- **Timeout Protection**: File operations have 5-second timeout to prevent FIFO blocking

## Platform Support

- **Linux**: Full support (X11 or Wayland required for clipboard)
- **macOS**: Full support (native clipboard)
- **Windows**: Full support (native clipboard)
- **Headless**: Works without clipboard (graceful degradation)