# Technical Documentation: pranshuparmar/witr

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/pranshuparmar/witr)

### Overview (`README.md`)

<div align="center">

# witr

### Why is this running?
Trace any process, port, container, or file back to the exact chain that started it —<br>
one command, machine-readable JSON, or an [interactive TUI](#3-interactive-mode-tui).

[](https://github.com/pranshuparmar/witr/releases/latest) [](#8-platform-support) <br> [](https://repology.org/project/witr/versions)

<a href="https://trendshift.io/repositories/18714" target="_blank"><img src="https://trendshift.io/api/badge/repositories/18714" alt="pranshuparmar/witr on Trendshift" width="250" height="55" /></a>
<a href="https://www.producthunt.com/products/witr?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-witr" target="_blank" rel="noopener noreferrer"><img alt="witr - Why is this running? Trace process, port, container or file. | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1211309&amp;theme=light&amp;t=1785480480150"></a>

### 🎮 [**Try witr in your browser →**](https://pranshuparmar.github.io/witr/)
*Investigate a simulated Linux box — a guided tutorial and free-play sandbox, no install required.*

<a href="https://pranshuparmar.github.io/witr/"><img width="1232" alt="witr's interactive TUI and CLI answering why a node process is running — the same systemd → PM2 → node chain in both" src="https://github.com/user-attachments/assets/dbe271ad-25e5-425b-b414-392d0c4eee37" /></a>

</div>

---

<div align="center">

[**Purpose**](#1-purpose) • [**Installation**](#2-installation) • [**TUI**](#3-interactive-mode-tui) • [**Flags**](#4-flags--options) • [**Core Concept**](#5-core-concept) • [**Examples**](#6-example-outputs)
<br>
[**Output Behavior**](#7-output-behavior) • [**Platforms**](#8-platform-support) • [**Success Criteria**](#9-success-criteria) • [**Sponsors**](#10-sponsors)

</div>

---

## 1. Purpose

**witr** exists to answer a single question:

> **Why is this running?**

When something is running on a system, whether it is a process, a service, or something bound to a port, there is always a cause. That cause is often indirect, non-obvious, or spread across multiple layers such as supervisors, containers, services, or shells.

Existing tools (`ps`, `top`, `lsof`, `ss`, `systemctl`, `docker ps`) expose state and metadata. They show _what_ is running, but leave the user to infer _why_ by manually correlating outputs across tools.

**witr** makes that causality explicit.

It explains **where a running thing came from**, **how it was started**, and **what chain of systems is responsible for it existing right now**, in a single, human-readable output or an **interactive TUI dashboard**.

> 📖 Curious how witr came to be? [Read the story](https://medium.com/@pranshu.parmar/witr-why-is-this-running-a9a97cbedd18) or browse the [Hacker News discussion](https://news.ycombinator.com/item?id=46392910).

---

## 2. Installation

witr is distributed as a single static binary for Linux, macOS, FreeBSD, and Windows.

witr is also independently packaged and maintained across multiple operating systems and ecosystems. An up-to-date overview of packaging status is available on [Repology](https://repology.org/project/witr/versions). Please note that community packages may lag GitHub releases due to independent review and validation.

> [!TIP]
> If you use a package manager (Homebrew, Conda, Winget, etc.), we recommend installing via that for easier updates. Otherwise, the install script is the quickest way to get started.

---

### 2.1 Quick Install

#### Unix (Linux, macOS & FreeBSD)

```bash
curl -fsSL https://raw.githubusercontent.com/pranshuparmar/witr/main/install.sh | bash
```

<details>
<summary>Script Details</summary>

The script will:
- Detect your operating system (`linux`, `darwin` or `freebsd`)
- Detect your CPU architecture (`amd64`, `arm64` or `loong64` on Linux)
- Download the latest released binary and man page
- Install it to `/usr/local/bin/witr`
- Install the man page to `/usr/local/share/man/man1/witr.1`
- Pass INSTALL_PREFIX to override default install path

</details>

#### Windows (PowerShell)

```powershell
irm https://raw.githubusercontent.com/pranshuparmar/witr/main/install.ps1 | iex
```

<details>
<summary>Script Details</summary>

The script will:
- Download the latest release (zip) and verify checksum.
- Extract `witr.exe` to `%LocalAppData%\witr\bin`.
- Add the bin directory to your User `PATH`.

</details>

---

### 2.2 Package Managers

<details>
<summary><strong>APT (Debian, Ubuntu & Derivatives)</strong> <a href="https://packages.debian.org/sid/witr"><img src="https://repology.org/badge/version-for-repo/debian_unstable/witr.svg?style=flat-square" alt="Debian"></a></summary>
<br>


You can install **witr** from the official Debian and Ubuntu repositories (Ubuntu 26.04+, Debian sid and later), as well as derivative distributions like Kali Linux, Devuan, and Raspbian:

```bash
sudo apt install witr
```

> Note: The apt-shipped version may lag the latest GitHub release. For the newest features, use the install script or another installation method.
</details>

<details>
<summary><strong>Homebrew (macOS & Linux)</strong> <a href="https://formulae.brew.sh/formula/witr"><img src="https://img.shields.io/homebrew/v/witr?style=flat-square" alt="Homebrew"></a></summary>
<br>


You can install **witr** using [Homebrew](https://brew.sh/) on macOS or Linux:

```bash
brew install witr
```
</details>

<details>
<summary><strong>MacPorts (macOS)</strong> <a href="https://ports.macports.org/port/witr/"><img src="https://repology.org/badge/version-for-repo/macports/witr.svg?style=flat-square" alt="MacPorts"></a></summary>
<br>


You can install **witr** using [MacPorts](https://www.macports.org/) on macOS:

```bash
sudo port install witr
```
</details>

<details>
<summary><strong>Conda (macOS, Linux & Windows)</strong> <a href="https://anaconda.org/conda-forge/witr"><img src="https://img.shields.io/conda/vn/conda-forge/witr?style=flat-square" alt="Conda"></a></summary>
<br>


You can install **witr** using [conda](https://docs.conda.io/en/latest/), [mamba](https://mamba.readthedocs.io/en/latest/), or [pixi](https://pixi.prefix.dev/latest/) on macOS, Linux, and Windows:

```bash
conda install -c conda-forge witr
# alternatively using mamba
mamba install -c conda-forge witr
# alternatively using pixi
pixi global install witr
```
</details>

<details>
<summary><strong>Arch Linux (AUR)</strong> <a href="https://aur.archlinux.org/packages/witr-bin"><img src="https://img.shields.io/aur/version/witr-bin?style=flat-square" alt="AUR"></a></summary>
<br>


On Arch Linux and derivatives, install from the [AUR package](https://aur.archlinux.org/packages/witr-bin):

```bash
yay -S witr-bin
# alternatively using paru
paru -S witr-bin
# or use your preferred AUR helper
```
</details>

<details>
<summary><strong>Winget (Windows)</strong> <a href="https://winstall.app/apps/PranshuParmar.witr"><img src="https://img.shields.io/winget/v/PranshuParmar.witr?style=flat-square" alt="Winget"></a></summary>
<br>


You can install **witr** via [winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/):

```powershell
winget install -e --id PranshuParmar.witr
```
</details>

<details>
<summary><strong>NPM (Cross-platform)</strong> <a href="https://www.npmjs.com/package/@pranshuparmar/witr"><img src="https://img.shields.io/npm/v/@pranshuparmar/witr?label=npm&color=blue&style=flat-square" alt="NPM"></a></summary>
<br>

You can install **witr** using [npm](https://www.npmjs.com/package/@pranshuparmar/witr):

```bash
npm install @pranshuparmar/witr
```
</details>

<details>
<summary><strong>FreeBSD Ports</strong> <a href="https://www.freshports.org/sysutils/witr/"><img src="https://repology.org/badge/version-for-repo/freebsd/witr.svg?style=flat-square" alt="FreeBSD Port"></a></summary>
<br>


You can install **witr** on FreeBSD from the [FreshPorts port](https://www.freshports.org/sysutils/witr/):

```bash
pkg install witr
# or
pkg install sysutils/witr
```

Or build from Ports:

```bash
cd /usr/ports/sysutils/witr/
make install clean
```
</details>

<details>
<summary><strong>Chocolatey (Windows)</strong> <a href="https://community.chocolatey.org/packages/witr"><img src="https://img.shields.io/chocolatey/v/witr?style=flat-square" alt="Chocolatey"></a></summary>

<br>


You can install **witr** using [Chocolatey](https://community.chocolatey.org):

```powershell
choco install witr
```
</details>

<details>
<summary><strong>Scoop (Windows)</strong> <a href="https://scoop.sh/#/apps?q=witr"><img src="https://img.shields.io/scoop/v/witr?bucket=main&style=flat-square" alt="Scoop"></a></summary>
<br>


You can install **witr** using [Scoop](https://scoop.sh):

```powershell
scoop install main/witr
```
</details>

<details>
<summary><strong>AOSC OS</strong> <a href="https://packages.aosc.io/packages/witr"><img src="https://repology.org/badge/version-for-repo/aosc/witr.svg?style=flat-square" alt="AOSC OS"></a></summary>
<br>


You can install **witr** from the [AOSC OS repository](https://packages.aosc.io/packages/witr):

```bash
oma install witr
```
</details>

<details>
<summary><strong>GNU Guix</strong> <a href="https://packages.guix.gnu.org/packages/witr/"><img src="https://repology.org/badge/version-for-repo/gnuguix/witr.svg?style=flat-square" alt="GNU Guix"></a></summary>
<br>


You can install **witr** from the [GNU Guix repository](https://packages.guix.gnu.org/packages/witr/):

```bash
guix install witr
```
</details>

<details>
<summary><strong>Uniget (Linux)</strong> <a href="https://github.com/uniget-org/tools/tree/main/tools/witr"><img src="https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2Funiget-org%2Ftools%2Fmain%2Ftools%2Fwitr%2Fmanifest.yaml&query=%24.version&label=uniget&style=flat-square&color=blue" alt="Uniget"></a></summary>
<br>

You can install **witr** using [uniget](https://uniget.dev/):

```bash
uniget install witr
```
</details>

<details>
<summary><strong>Aqua (macOS, Linux & Windows)</strong> <a href="https://github.com/aquaproj/aqua-registry/blob/main/pkgs/pranshuparmar/witr"><img src="https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2Faquaproj%2Faqua-registry%2Fmain%2Fpkgs%2Fpranshuparmar%2Fwitr%2Fpkg.yaml&query=%24.packages%5B0%5D.name&label=aqua&style=flat-square&color=blue" alt="Aqua"></a></summary>
<br>

You can install **witr** using [aqua](https://aquaproj.github.io/):

```bash
# Add package
aqua g -i pranshuparmar/witr

# Install package
aqua i pranshuparmar/witr
```
</details>

<details>
<summary><strong>Brioche (Linux)</strong> <a href="https://github.com/brioche-dev/brioche-packages/tree/main/packages/witr"><img src="https://img.shields.io/static/v1?label=brioche&message=v0.3.3&color=blue&style=flat-square" alt="Brioche"></a></summary>
<br>

You can install **witr** using [brioche](https://brioche.dev/):

```bash
brioche install -r witr
```
</details>

<details>
<summary><strong>Mise (macOS, Linux & Windows)</strong> <a href="https://github.com/pranshuparmar/witr/releases/latest"><img src="https://img.shields.io/github/v/release/pranshuparmar/witr?label=mise&style=flat-square" alt="Mise"></a></summary>
<br>

You can install **witr** using [mise](https://mise.jdx.dev/):

```bash
mise use github:pranshuparmar/witr
```
</details>

<details>
<summary><strong>Prebuilt Packages (deb, rpm, apk)</strong></summary>
<br>

**witr** provides native packages for major Linux distributions. You can download the latest `.deb`, `.rpm`, or `.apk` package from the [GitHub releases page](https://github.com/pranshuparmar/witr/releases/latest).

- Generic download command using `curl`:
  ```bash
  # Replace <package name with the actual package that you need>
  curl -LO https://github.com/pranshuparmar/witr/releases/latest/download/<package-name>
  ```

- **Debian/Ubuntu (.deb):**
  ```bash
  sudo dpkg -i ./witr-*.deb
  # Or, using apt for dependency resolution:
  sudo apt install ./witr-*.deb
  ```
- **Fedora/RHEL/CentOS (.rpm):**
  ```bash
  sudo rpm -i ./witr-*.rpm
  ```
- **Alpine Linux (.apk):**
  ```bash
  sudo apk add --allow-untrusted ./witr-*.apk
  ```
</details>

---

### 2.3 Source & Manual Installation

<details>
<summary><strong>Go (cross-platform)</strong></summary>
<br>

You can install the latest version directly from source:

```bash
go install github.com/pranshuparmar/witr/cmd/witr@latest
```

This will place the `witr` binary in your `$GOPATH/bin` or `$HOME/go/bin` directory. Make sure this directory is in your `PATH`.
</details>

<details>
<summary><strong>Manual Installation</strong></summary>
<br>

If you prefer manual installation, follow these simple steps for your platform:

**Unix (Linux, macOS, FreeBSD)**

```bash
# 1. Determine OS and Architecture
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
[ "$ARCH" = "x86_64" ] && ARCH="amd64"
[ "$ARCH" = "aarch64" ] && ARCH="arm64"
[ "$ARCH" = "loongarch64" ] && ARCH="loong64"

# 2. Download the binary
curl -fsSL "https://github.com/pranshuparmar/witr/releases/latest/download/witr-${OS}-${ARCH}" -o witr

# 3. Verify checksum (Optional)
curl -fsSL "https://github.com/pranshuparmar/witr/releases/latest/download/SHA256SUMS" -o SHA256SUMS
grep "witr-${OS}-${ARCH}" SHA256SUMS | (sha256sum -c - 2>/dev/null || shasum -a 256 -c - 2>/dev/null)
rm SHA256SUMS

# 4. Rename and install
chmod +x witr
sudo mkdir -p /usr/local/bin
sudo mv witr /usr/local/bin/witr

# 5. Install man page (Optional)
sudo mkdir -p /usr/local/share/man/man1
sudo curl -fsSL https://github.com/pranshuparmar/witr/releases/latest/download/witr.1 -o /usr/local/share/man/man1/witr.1
```

**Windows (PowerShell)**

```
/* Detailed source-code truncated for AI context efficiency. */
```
</details>

---

### 2.4 Run Without Installation

<details>
<summary><strong>Nix Flake</strong></summary>
<br>

If you use Nix, you can build **witr** from source and run without installation:

```bash
nix run github:pranshuparmar/witr -- --help
```

</details>

<details>
<summary><strong>Pixi</strong></summary>
<br>

If you use [pixi](https://pixi.prefix.dev/latest/), you can run without installation on Linux or macOS:

```bash
pixi exec witr --help
```
</details>

---

### 2.5 Other Operations

<details>
<summary><strong>Verify Installation</strong></summary>
<br>

```bash
witr --version
man witr
```
</details>

<details>
<summary><strong>Shell Completions</strong></summary>
<br>

`witr` supports tab completion for all flags. To enable it, add the appropriate line to your shell configuration:

**Bash**
```bash
echo 'eval "$(witr completion bash)"' >> ~/.bashrc
source ~/.bashrc
```

**Zsh**
```zsh
echo 'eval "$(witr completion zsh)"' >> ~/.zshrc
source ~/.zshrc
```

**Fish**
```fish
witr completion fish | source
# To make it permanent:
witr completion fish > ~/.config/fish/completions/witr.fish
```

**PowerShell**
```powershell
witr completion powershell | Out-String | Invoke-Expression
# To make it permanent, add the above line to your $PROFILE
```
</details>

<details>
<summary><strong>Uninstallation</strong></summary>
<br>

If you installed via a package manager (Homebrew, Conda, etc.), please use the respective uninstall command (e.g., `brew uninstall witr`).

To completely remove script/manual installation of **witr**:

**Unix (Linux, macOS, FreeBSD)**

```bash
sudo rm -f /usr/local/bin/witr
sudo rm -f /usr/local/share/man/man1/witr.1
```

**Windows**

```powershell
Remove-Item -Recurse -Force "$env:LocalAppData\witr"
```
</details>

---
 
## 3. Interactive Mode (TUI)

Running `witr` without any arguments or with the `-i` flag launches the **Interactive Mode (TUI)**. This provides a real-time, terminal-based dashboard with four tabs for exploring processes, ports, containers, and file locks. Combine `-i` with a target to open the TUI already focused on it: `witr -i -p 1234` selects that PID, `witr -i nginx` pre-fills the process filter, and `witr -i -o 5432` / `-c web` / `-f /path` open the Ports / Containers / Locks tab with the filter set.

### Key Features:
- **Processes Tab**: Live, sortable, filterable list of all running processes with a side panel showing the ancestry tree of the highlighted process.
- **Ports Tab**: Open/listening ports with the owning processes attached in a side panel. Toggle between LISTEN-only and ALL with `a`. Enter on an owner opens it, or the container a port is published for; an owner hidden from your user shows the user it runs as (run with sudo to see the process).
- **Containers Tab**: All running containers across Docker, Podman, nerdctl, K8s/crictl, Incus, LXC, LXD, and FreeBSD jails in one list - name, image, status, ports, command, plus a per-container detail view with mounts, networks, and compose project metadata.
- **Locks Tab**: System-wide file locks (POSIX/FLOCK on Linux, lsof-derived on macOS/FreeBSD). Press `a` to switch into "all open files" mode, where locked entries are merged with every interesting open fd; type into `/` to search across the merged set.
- **Process Details**: Deep-dive into a process to see its full ancestry tree, child processes, environment variables, working directory, sockets, file context, and more.
- **Process Actions**: Send signals (Kill, Terminate, Pause, Resume) or Renice processes directly from the UI (Unix only). Press `a` on a process in the list or in its detail view; the confirmation names the exact process before anything is sent.
- **Mouse Support**: Navigate, sort columns, and click rows using your mouse.
- **Adaptive Theme**: Colors adapt automatically to light and dark terminal backgrounds.
- **Auto-Refresh**: The process, port, container, and lock lists refresh automatically on an adaptive cadence (starts at 3 seconds, backing off under load).

---

## 4. Flags & Options

```
  -c, --container strings container(s) to look up (repeatable)
      --env              show environment variables for the process
  -x, --exact            use exact name matching (no substring search)
  -f, --file strings     file(s) held open by a process (repeatable)
  -h, --help             help for witr
  -i, --interactive      interactive mode (TUI)
      --json             show result as JSON
      --no-color         disable colorized output
  -p, --pid strings      pid(s) to look up (repeatable)
  -o, --port strings     port(s) to look up (repeatable)
  -s, --short            show only ancestry
  -t, --tree             show only ancestry as a tree
      --verbose          show extended process information
  -v, --version          version for witr
      --warnings         show only warnings
```

Positional arguments (without flags) are treated as process or service names. Multiple names can be passed. By default, name matching uses substring matching (fuzzy search). Use `--exact` to match only processes with the exact name.

All target flags (`--pid`, `--port`, `--file`, `--container`) are repeatable and can be mixed with each other and with positional name arguments. When multiple targets are provided, results are shown sequentially with labeled dividers. All output modes (standard, short, tree, JSON, env, warnings, verbose) work with multiple inputs.

The `--container` flag searches across Docker, Podman, nerdctl, K8s/crictl, Incus, LXC, LXD, and FreeBSD jails, and matches against container name, image, command, and compose project/service labels, or a container ID (full, short, or a prefix of at least 4 characters).

The TUI is launched if no arguments or relevant flags (`--pid`, `--port`, `--file`, `--container`) are provided, or if the `--interactive` flag is explicitly used. It needs a terminal: run without one (from a script, a pipe or CI) and witr exits with code 4 and asks for a target instead. An output mode with no target (`witr --json`, `--short`, `--tree`, `--warnings`, `--verbose` or `--env`) also exits with code 4 and asks for a target, rather than opening the TUI.

---

## 5. Core Concept

witr treats **everything as a process question**.

Ports, services, containers, and commands all eventually map to **PIDs**. Once a PID is identified, witr builds a causal chain explaining _why that PID exists_.

At its core, witr answers:

1. What is running?
2. How did it start?
3. What is keeping it running?
4. What context does it belong to?

---

## 6. Example Outputs

> 💡 Prefer learning by doing? The [interactive browser tutorial](https://pranshuparmar.github.io/witr/) walks you through outputs like these live on a simulated box — for a better feel of witr, no install required.

### 6.1 Name Based Query

```bash
witr node
```

```
Target      : node

Process     : node (pid 14233)
User        : pm2
Command     : node index.js
Started     : 2 days ago (Mon 2025-02-02 11:42:10 +05:30)

Why It Exists :
  systemd (pid 1) → pm2 (pid 5034) → node (pid 14233)

Source      : pm2

Working Dir : /opt/apps/expense-manager
Git Repo    : expense-manager (main)
Sockets     : 127.0.0.1:5001 (TCP | LISTENING, 2 connections)
              127.0.0.1:51744 → 127.0.0.1:5432 (TCP | ESTABLISHED)
```

---

### 6.2 Short Output

```bash
witr --port 5000 --short
```

```
systemd (pid 1) → PM2 v5.3.1: God (pid 1481580) → python (pid 1482060)
```

---

### 6.3 Tree Output

```bash
witr --pid 143895 --tree
```

```
systemd (pid 1)
  └─ init-systemd(Ub (pid 2)
    └─ SessionLeader (pid 143858)
      └─ Relay(143860) (pid 143859)
        └─ bash (pid 143860)
          └─ sh (pid 143886)
            └─ node (pid 143895)
              ├─ node (pid 143930)
              ├─ node (pid 144189)
              └─ node (pid 144234)
```

Note: _Tree view includes child processes (up to 10) and highlights the target process._

---

### 6.4 Multiple Matches

```bash
witr ng
```

```
Multiple matching processes found:

[1] nginx (pid 2311)
    nginx -g daemon off;
[2] nginx (pid 24891)
    nginx -g daemon off;
[3] ngrok (pid 14233)
    ngrok http 5000

Re-run with:
  witr --pid <pid>
```

To avoid substring matching and only find processes with an exact name, use the `--exact` flag:

```bash
witr nginx -x
```

---

### 6.5 File Based Query

```bash
witr --file /var/lib/dpkg/lock
```

Explains the process holding a file open.

---

### 6.6 Container Based Query

```bash
witr --container redis
```

Looks up a container by name, ID, image, command, or compose project/service across every detected runtime (Docker, Podman, nerdctl, K8s/crictl, Incus, LXC, LXD, FreeBSD jails). The output includes the container's image and, for Compose services, the Compose file and project directory on the host. Pass `--verbose` to include mounts.

---

### 6.7 Multiple Inputs

```bash
witr nginx --port 5432 --pid 1234
```

```
----- [name: nginx] -----
Target      : nginx
Process     : nginx (pid 2311)
...

----- [port: 5432] -----
Target      : postgres
Process     : postgres (pid 891)
...

----- [pid: 1234] -----
Target      : node
Process     : node (pid 1234)
...
```

All target flags are repeatable and can be mixed. Results appear in the order you typed them. All output modes (`--short`, `--tree`, `--json`, `--env`, `--warnings`, `--verbose`) work with multiple inputs.

---

## 7. Output Behavior

### 7.1 Output Principles

- Single screen by default (best effort)
- Deterministic ordering
- Narrative-style explanation
- Best-effort detection with explicit uncertainty

---

### 7.2 Exit Codes

witr returns meaningful exit codes for use in scripts, CI pipelines, and monitoring:

| Code | Meaning |
|------|---------|
| 0 | Clean: process found, no warnings |
| 1 | Warnings: process found but has one or more warnings |
| 2 | Not found: no matching process or service (also a port no process on this system holds, when witr runs as root or on Windows) |
| 3 | Permission denied: insufficient privileges (retry with sudo) |
| 4 | Invalid input: bad arguments or ambiguous match |
| 5 | Internal error: an unexpected failure occurred |
| 6 | Cause unknown: process found, but what started it can't be traced (its warnings say why). Not used on Windows, where this is routine |

With several targets, the most severe result wins. Cause unknown ranks above warnings but below every failure (2 to 5).

#### Example Usage:

```bash
witr nginx --short
case $? in
  0) echo "All clear" ;;
  1) echo "Warnings detected" ;;
  2) echo "Process not running" ;;
  3) echo "Need elevated privileges" ;;
  4) echo "Invalid input or ambiguous match" ;;
  5) echo "Internal error" ;;
  6) echo "Cannot tell what started it" ;;
esac
```

---

### 7.3 Standard Output Sections

#### Target

What the user asked about.

#### Process

Executable, PID, user, command, start time and restart count. The restart count comes from the managing system: a systemd unit, or the container runtime (Docker, Podman, nerdctl and Kubernetes), which also shows the container's restart policy, e.g. `Restarts : 12 (policy: unless-stopped)`.

On Windows the user carries the process's integrity level when it isn't the normal Medium: `(elevated)` for a process run as administrator, `(system integrity)`, `(low integrity)` or `(untrusted integrity)` for sandboxed ones. On Linux, a `Security` line shows the AppArmor profile or SELinux context confining the process; unconfined processes have none.

#### Why It Exists

A causal ancestry chain showing how the process came to exist.
This is the core value of witr.

When the process that started it has exited, the chain marks the break with `? (original parent exited)` (or `? (parent pid N exited)` when that parent's PID no longer exists or now belongs to an unrelated process) instead of crediting whatever adopted it.

#### Source

The primary system responsible for starting or supervising the process (best effort).

Examples:

- systemd unit with schedule info for timer-triggered services (Linux)
- launchd service with schedule/trigger details (macOS)
- SSH session (with remote IP and terminal)
- docker container
- pm2
- cron
- interactive shell (detects tmux/screen sessions)
- Snap/Flatpak sandbox (Linux)

Only **one primary source** is selected. If the original parent has exited and nothing about the process itself (its container, service unit, login session or app scope, or launchd job) explains it, the source is reported as `unknown` rather than guessed. On systems without such a service manager it stays `init`, with a note that init only adopted it.

#### Context (best effort)

- Working directory
- Git repository name and branch
- Container name / image (docker, podman, kubernetes, colima, containerd)
- Sockets: listeners first, each with the number of connections it accepted, then other sockets; a connection shows `local → remote`
- Public vs private bind

#### Warnings

Non‑blocking observations such as:

- Process is running as root
- Dangerous Linux capabilities on non-root processes (CAP_SYS_ADMIN, etc.)
- Process is listening on a public interface (0.0.0.0 / ::)
- Restarted multiple times (warning only if above threshold)
- Process is using high memory (>1GB RSS)
- Process has been running for over 90 days
- Deleted binary, library injection indicators (LD_PRELOAD, DYLD_*)
- Original parent process has exited, so what started the process can't be traced

---

### 7.4 JSON Output

`--json` is meant for scripts and tools, and its output is a contract:

- **Stable:** field names, what they mean, and their types. Releases may add fields, so ignore any you don't recognise.
- **Not part of the contract:** field order, whitespace, and the wording of human-readable text (`Error`, `Note`, `Description` and warning messages). Match on fields and exit codes, not on sentences.
- **Empty values** may be `null`, an empty list, or left out.
- **Breaking changes** (renaming, removing or retyping a field) only ship in a release whose notes call them out.

| Command | Output |
|---------|--------|
| `witr <target> --json` | The full report: `Target`, `Process`, `Ancestry`, `Source`, `Warnings` and the rest |
| `--short --json` | A list of `{PID, Command}` from the top of the chain to the process, with `PPID` and `ParentExited` where the process that started it has exited |
| `--tree --json` | `{Ancestry, Children}`, each a list of the same entries |
| `--warnings --json` | `{PID, Process, Command, Warnings}` |
| `--env --json` | `{PID, Process, Command, Env}` |
| A container whose processes aren't visible | `{Target, Runtime, ContainerID, ContainerName, Image, …, Note}` |
| A failed lookup | `{Target, Error}`, plus `Matches` when the target was ambiguous |
| Several targets | A list of the above, one entry per target, in the order given |

A test pins every field of every shape, so an accidental rename fails the build.

### 7.5 Using witr with AI Coding Agents

AI coding agents (Claude Code, Codex, Cursor and others) regularly run into ports that are already in use, leftover dev servers and confusing containers, and work around them by chaining `lsof`, `ps`, `netstat` and `docker ps`. witr answers the same questions in one command, and two things make it easy for an agent to use:

- **`--json`** prints the result as JSON: for a process, the full report (the process, its ancestry chain, the source that started it and any warnings). A failed or ambiguous lookup prints `{Target, Error}`, with the candidates in `Matches` when it is ambiguous, and several targets print an array.
- **Exit codes** say what happened without parsing any text (see [7.2 Exit Codes](#72-exit-codes)). Exit code `1` means the process was found and has warnings; it is not a failure.

Add a short note like this to your project's agent instructions (`AGENTS.md`, `CLAUDE.md` or similar):

```markdown
## Process and port debugging

Use `witr` instead of chaining lsof/ps/netstat/docker commands:

- Port already in use: `witr --port <PORT> --json`
- Unknown or stuck process: `witr <name> --json`, or `witr --pid <PID> --tree`
- Container: `witr --container <name or ID> --json`

Exit codes: 0 found, 1 found with warnings (not a failure), 2 not found,
3 permission denied (retry with sudo), 4 ambiguous name or bad input
(re-run with --pid), 5 internal error, 6 found but what started it can't
be traced. Run `witr --help` for all options.
```

#### Official agent skill

For fuller guidance, install the official skill: it tells an agent which command fits the situation, how to read the JSON and exit codes, and to ask before stopping anything witr finds. The `witr` binary still needs to be installed.

- **Claude Code:** run `/plugin marketplace add pranshuparmar/witr`, then `/plugin install witr@witr`.
- **Other agents that read Agent Skills, or a manual install:** copy [`plugins/witr/skills/witr`](plugins/witr/skills/witr) into the agent's skills folder (for Claude Code, `~/.claude/skills/`).

---

## 8. Platform Support

- **Linux** (x86_64, arm64, loong64) - Full feature support (`/proc`).
- **macOS** (x86_64, arm64) - Uses `ps`, `lsof`, `sysctl`, `pgrep`.
- **Windows** (x86_64, arm64) - Native Win32 APIs (ToolHelp32, PSAPI, Service Control Manager). No PowerShell or WMI dependency.
- **FreeBSD** (x86_64, arm64) - Uses `procstat`, `ps`, `lsof`.

---

### 8.1 Feature Compatibility Matrix

| Feature | Linux | macOS | Windows | FreeBSD | Notes |
|---------|:-----:|:-----:|:-------:|:-------:|-------|
| **Process Selection** |
| By Name | ✅ | ✅ | ✅ | ✅ | |
| By PID | ✅ | ✅ | ✅ | ✅ | |
| By Port | ✅ | ✅ | ✅ | ✅ | |
| By File | ✅ | ✅ | ✅ | ✅ | |
| By Container | ✅ | ✅ | ✅ | ✅ | Requires the runtime CLI on PATH (docker/podman/nerdctl/crictl/incus/lxc/lxc-ls/jls). |
| Multiple/mixed inputs | ✅ | ✅ | ✅ | ✅ | Repeatable flags, mixed types. |
| Exact Match | ✅ | ✅ | ✅ | ✅ | |
| Full command line | ✅ | ✅ | ✅ | ✅ | |
| Process start time | ✅ | ✅ | ✅ | ✅ | |
| Working directory | ✅ | ✅ | ✅ | ✅ | |
| Environment variables | ✅ | ⚠️ | ⚠️ | ✅ | macOS: SIP restrictions; Windows: protected processes inaccessible. |
| **Network** |
| Listening ports | ✅ | ✅ | ✅ | ✅ | |
| Connections (remote end) | ✅ | ✅ | ✅ | ✅ | Connections show their remote end; connections a listener accepted are counted on its row. |
| Bind addresses | ✅ | ✅ | ✅ | ✅ | |
| Port → PID resolution | ✅ | ✅ | ✅ | ✅ | |
| Port → Container fallback | ✅ | ✅ | ✅ | ✅ | Used when the port is owned by PID 1 via systemd socket activation or a container runtime. A port published by Docker's `docker-proxy`, Docker Desktop's forwarders (`com.docker.backend`, plus `wslrelay` on Windows), rootless Podman's `rootlessport` or `pasta`, or RootlessKit (rootless nerdctl and Docker), is explained by the container's own process when it is visible, and by this view otherwise. So is a port that no process holds but a Docker, Podman or nerdctl container publishes (published through firewall rules). In the TUI's Ports tab, opening such a port's owner shows the container. |
| **Service Detection** |
| Service Manager | ✅ | ✅ | ✅ | ✅ | Linux: systemd, macOS: launchd, Windows: Services, FreeBSD: rc.d |
| Service Description | ✅ | ✅ | ✅ | ✅ | Linux: `Description`, macOS: `Comment`, Windows: `Display Name`, FreeBSD: `rc` header |
| Configuration Source | ✅ | ✅ | ✅ | ✅ | Linux: Unit File, macOS: Plist, Windows: Registry Key, FreeBSD: Rc Script |
| Supervisor | ✅ | ✅ | ✅ | ✅ | |
| Containers | ✅ | ✅ | ✅ | ✅ | Docker (plus compose mappings), Podman, nerdctl, K8s (Kubepods/crictl), Containerd. Colima on macOS/Linux. Incus/LXC/LXD on Linux. Jails on FreeBSD. |
| SSH session detection | ✅ | ✅ | ✅ | ✅ | Detects remote IP and terminal. |
| tmux/screen detection | ✅ | ✅ | ❌ | ✅ | Shows session name in source. |
| Schedule detection | ✅ | ✅ | ❌ | ❌ | Linux: systemd timers, macOS: launchd intervals/calendar. |
| Restart count | ✅ | ⚠️ | ⚠️ | ❌ | systemd restarts on Linux; container restart count and policy wherever Docker, Podman, nerdctl or Kubernetes runs. |
| Exited parent detection | ✅ | ✅ | ⚠️ | ✅ | Marks where the process that started it exited. Windows shows the break but doesn't treat it as a finding (no exit code 6): its launchers routinely exit. |
| Snap/Flatpak detection | ✅ | ❌ | ❌ | ❌ | |
| **Health & Diagnostics** |
| CPU usage detection | ✅ | ✅ | ✅ | ✅ | |
| Memory usage detection | ✅ | ✅ | ✅ | ✅ | |
| Health status detection | ✅ | ✅ | ✅ | ✅ | |
| Open Files / Handles | ✅ | ✅ | ⚠️ | ✅ | Windows: count only. |
| File Locks | ✅ | ✅ | ❌ | ✅ | Linux: `/proc/locks`; macOS/FreeBSD: derived from `lsof`/`fstat`. |
| Deleted binary detection | ✅ | ✅ | ✅ | ✅ | Warns if executable is missing. |
| Capability warnings | ✅ | ❌ | ❌ | ❌ | Warns about dangerous capabilities on non-root processes. |
| Security context | ✅ | ❌ | ✅ | ❌ | Linux: AppArmor profile or SELinux context; Windows: integrity level (elevated, system, low). |
| **Context** |
| Git repo/branch detection | ✅ | ✅ | ✅ | ✅ | |
| **Interactive Mode (TUI)** |
| Processes Tab | ✅ | ✅ | ✅ | ✅ | |
| Ports Tab | ✅ | ✅ | ✅ | ✅ | |
| Containers Tab | ✅ | ✅ | ✅ | ✅ | |
| Locks Tab | ✅ | ✅ | ❌ | ✅ | Toggle (`a`) shows all open files. |
| Process Details | ✅ | ✅ | ✅ | ✅ | |
| Process Actions | ✅ | ✅ | ❌ | ✅ | |

**Legend:** ✅ Full support | ⚠️ Partial/limited support | ❌ Not available

---

### 8.2 Permissions Note

#### Linux/FreeBSD

witr inspects system directories which may require elevated permissions.

If you are not seeing the expected information, try running witr with sudo:

```bash
sudo witr [your arguments]
```

Without root, a port held by another user's process still shows which user it belongs to (`it belongs to postgres`), but not the process. With root, a port that no process on the system holds is reported as such (exit code 2): on WSL that's usually a process in another distro, since all distros share one network.

#### macOS

On macOS, witr uses `ps`, `lsof`, and `launchctl` to gather process information. Some operations may require elevated permissions:

```bash
sudo witr [your arguments]
```

Note: Due to macOS System Integrity Protection (SIP), some system process details may not be accessible even with sudo.

#### Windows

On Windows, witr talks directly to Win32 APIs (ToolHelp32, PSAPI, Service Control Manager) rather than spawning PowerShell or WMI, startup is fast and there's no `Get-CimInstance` hang. To see details for processes owned by other users or system services, you must run the terminal as **Administrator**.

```powershell
# Run in Administrator PowerShell
.\witr.exe [your arguments]
```

---

## 9. Success Criteria

witr is successful if:

- A user can answer "why is this running?" within seconds
- It reduces reliance on multiple tools
- Output is understandable under stress
- Users trust it during incidents

---

## 10. Sponsors

Special thanks to the people who supported **witr** ❤️

<p>
  <a href="https://github.com/timcolson" title="Tim Colson">
    <img src="https://images.weserv.nl/?url=github.com/timcolson.png&mask=circle&w=80&h=80" width="80">
  </a>
  <a href="https://github.com/R-Bose" title="Rijurekh Bose">
    <img src="https://images.weserv.nl/?url=github.com/R-Bose.png&mask=circle&w=80&h=80" width="80">
  </a>
</p>

---

### Overview (`docs/README.md`)

# witr playground

An interactive, zero-install, in-browser playground for [witr](../README.md). It
runs a **simulated Linux box** and lets visitors investigate it with real witr
commands — as a guided tutorial and as a free-form sandbox.

Nothing here touches the visitor's machine. Every process, port, container, and
lock is authored data; the terminal simulates witr, not a real shell.

**Live:** this folder *is* the site — GitHub Pages serves it directly (see
[Deployment](#deployment)) at `https://<owner>.github.io/witr/`.

> This directory also holds `cli/` — the generated `witr` man page and markdown
> reference (produced by `make docs`). That's unrelated to the playground; it
> just shares the Pages folder.

---

## What it does

- **Terminal-first.** A dependency-free terminal widget runs `witr …` against
  the simulated world and renders witr's real ANSI output. A handful of flavour
  commands (`ls`, `cat`, `ps`, `top`, `neofetch`, …) make the box feel real to
  poke at.
- **The incident (tutorial).** Each scenario opens with a cold open that plays
  itself, then hands the visitor a briefing and a left-side tracker that counts
  down as each task is resolved. On **webbox** the tasks are *informational* — a
  deploy dies with `EADDRINUSE`, and the visitor traces what's holding `:8000`,
  who owns the `dpkg` lock, and how heavy the Node app really is. witr supplies
  the **why**; the fix stays the operator's call (nothing is forced — the
  squatter is a teammate's `http.server`, the lock is a scheduled
  `unattended-upgrade`). On **devbox** the tasks are *fix-by-kill* — a
  `git index.lock` blocks every commit, a `python3` zombie needs reaping through
  its parent, and a stray `ffmpeg` is pinning the CPU; here `kill` actually
  removes the process and the tracker, constellation and TUI all reflect it live.
  Clearing all three is the finale, with the install command. Feature coverage
  (`--port`, `--file`, `--pid`, `--verbose`, the causal chain) falls out of the
  investigation; `--json`, `--tree`, `--env`, `--container`, and the TUI are
  optional side quests that tick off as they're tried.
- **Reactive world.** The loaded world is a mutable clone: `kill`/`pkill` remove
  processes (and their subtrees), which the engine, the constellation, the TUI,
  and the incident tracker all reflect live. **Reset** restores the pristine box.
- **Playground mode.** Free rein to type any witr command against the box, or
  switch scenarios (a production web box, a messy dev laptop).
- **Process constellation.** A three.js view of the machine. When a query
  resolves, the causal chain (`systemd → … → target`) lights up while everything
  else dims — the text says the chain, the map shows it. Nodes and the legend
  (pid 1 / listener / process / warning) are both clickable.
- **Interactive TUI.** `witr` with no arguments opens a live dashboard
  (Processes / Ports / Containers / Locks) with an ancestry side-panel — the
  same shape as witr's real bubbletea TUI.

## Fidelity

The whole point is that the playground never lies about what witr prints.

- `js/engine.js` is a faithful port of witr's output layer
  (`internal/output/*.go`) and app routing (`internal/app/app.go`).
- `fixtures/gen/` is a small Go program that renders **golden fixtures using
  witr's actual output package**. `scripts/check-fixtures.mjs` replays the JS
  engine over the same world (with a pinned clock) and asserts byte-for-byte
  equality. CI runs this on every change — if the engine drifts from witr, the
  build fails.

## Run it locally

Any static file server works (ES modules need `http://`, not `file://`):

```bash
cd docs
python3 -m http.server 8099
# open http://localhost:8099/
```

## Project layout

```
docs/
  index.html            page shell
  css/styles.css        terminal-first theme (dark + light)
  js/
    analytics.js        optional GoatCounter wrapper (no-ops when blocked)
    ansi.js             ANSI escape → HTML
    engine.js           faithful witr output engine  ← fidelity-critical
    parser.js           witr command-line parser
    shell.js            command routing + flavour commands
    terminal.js         dependency-free terminal widget
    map.js              three.js process constellation
    tui.js              interactive TUI dashboard
    tutorial.js         mission definitions + progression
    app.js              wires it all together
  worlds/               the simulated machines (single source of truth)
    webbox.json         production box (tutorial)
    devbox.json         dev laptop (sandbox)
  fixtures/             golden output from the real witr binary
    gen/main.go         generator (build-tagged: `-tags fixtures`)
  scripts/
    check-fixtures.mjs  engine ⇄ golden fixture diff
  vendor/
    three.module.min.js three.js r160 (vendored, MIT)
  cli/                  generated witr man page + markdown (from `make docs`)
  .nojekyll             serve files as-is (no Jekyll processing)
```

## Regenerating fixtures

Regenerate after changing a world file or witr's output format. The generator
is build-tagged, so it never affects the normal `go build ./...`:

```bash
# from the repo root
go run -tags fixtures ./docs/fixtures/gen
node docs/scripts/check-fixtures.mjs
```

Fixtures embed absolute timestamps and a pinned clock (`_meta.json`), so every
regeneration changes the timestamps — that's expected. The check uses the pinned
clock, so it stays deterministic.

## Analytics

The site counts visits and a handful of anonymous events (tutorial started /
completed, tasks resolved, TUI opened, install command copied) via
[GoatCounter](https://www.goatcounter.com/) — cookieless, no identifier stored
in the browser, and the dashboard is public:
<https://witr.goatcounter.com>. `js/analytics.js` is a thin wrapper that
silently no-ops when the counter script is blocked or unavailable, so the
playground never depends on it. A note in the page footer discloses this to
visitors.

## Adding a scenario

1. Add `worlds/<id>.json` (see the schema the existing worlds follow).
2. Add the id to `WORLD_IDS` in `js/app.js` and a card in `index.html`.
3. Optionally add fixtures for it in `fixtures/gen/main.go`.

## Deployment

The playground is served straight from this folder by GitHub Pages. Enable it
once:

**Settings → Pages → Build and deployment → Source: Deploy from a branch →
Branch: `main` / `/docs`.**

Every push to `main` then publishes automatically — no build step, no workflow.
`.nojekyll` is present so files (including `fixtures/_meta.json`) are served
verbatim. CI (`.github/workflows/playground.yml`) only runs the fidelity check.

## Credits

[three.js](https://threejs.org/) (r160, MIT) is vendored under `vendor/`. All
other code is part of witr and shares its license.

---

### Overview (`vendor/github.com/charmbracelet/bubbletea/README.md`)

# Bubble Tea

<p>
    <picture>
      <source media="(prefers-color-scheme: light)" srcset="https://stuff.charm.sh/bubbletea/bubble-tea-v2-light.png" width="308">
      <source media="(prefers-color-scheme: dark)" srcset="https://stuff.charm.sh/bubbletea/bubble-tea-v2-dark.png" width="312">
      <img src="https://stuff.charm.sh/bubbletea/bubble-tea-v2-light.png" width="308" />
    </picture>
    <br>
    <a href="https://github.com/charmbracelet/bubbletea/releases"><img src="https://img.shields.io/github/release/charmbracelet/bubbletea.svg" alt="Latest Release"></a>
    <a href="https://pkg.go.dev/github.com/charmbracelet/bubbletea?tab=doc"><img src="https://godoc.org/github.com/charmbracelet/bubbletea?status.svg" alt="GoDoc"></a>
    <a href="https://github.com/charmbracelet/bubbletea/actions"><img src="https://github.com/charmbracelet/bubbletea/actions/workflows/build.yml/badge.svg?branch=main" alt="Build Status"></a>
</p>

The fun, functional and stateful way to build terminal apps. A Go framework
based on [The Elm Architecture][elm]. Bubble Tea is well-suited for simple and
complex terminal applications, either inline, full-window, or a mix of both.

<p>
    <img src="https://stuff.charm.sh/bubbletea/bubbletea-example.gif" width="100%" alt="Bubble Tea Example">
</p>

Bubble Tea is in use in production and includes a number of features and
performance optimizations we’ve added along the way. Among those is
a framerate-based renderer, mouse support, focus reporting and more.

To get started, see the tutorial below, the [examples][examples], the
[docs][docs], the [video tutorials][youtube] and some common [resources](#libraries-we-use-with-bubble-tea).

[youtube]: https://charm.sh/yt

## By the way

Be sure to check out [Bubbles][bubbles], a library of common UI components for Bubble Tea.

<p>
    <a href="https://github.com/charmbracelet/bubbles"><img src="https://stuff.charm.sh/bubbles/bubbles-badge.png" width="174" alt="Bubbles Badge"></a>&nbsp;&nbsp;
    <a href="https://github.com/charmbracelet/bubbles"><img src="https://stuff.charm.sh/bubbles-examples/textinput.gif" width="400" alt="Text Input Example from Bubbles"></a>
</p>

---

## Tutorial

Bubble Tea is based on the functional design paradigms of [The Elm
Architecture][elm], which happens to work nicely with Go. It's a delightful way
to build applications.

This tutorial assumes you have a working knowledge of Go.

By the way, the non-annotated source code for this program is available
[on GitHub][tut-source].

[elm]: https://guide.elm-lang.org/architecture/
[tut-source]: https://github.com/charmbracelet/bubbletea/tree/main/tutorials/basics

### Enough! Let's get to it.

For this tutorial, we're making a shopping list.

To start we'll define our package and import some libraries. Our only external
import will be the Bubble Tea library, which we'll call `tea` for short.

```go
package main

// These imports will be used later on the tutorial. If you save the file
// now, Go might complain they are unused, but that's fine.
// You may also need to run `go mod tidy` to download bubbletea and its
// dependencies.
import (
    "fmt"
    "os"

    tea "github.com/charmbracelet/bubbletea"
)
```

Bubble Tea programs are comprised of a **model** that describes the application
state and three simple methods on that model:

- **Init**, a function that returns an initial command for the application to run.
- **Update**, a function that handles incoming events and updates the model accordingly.
- **View**, a function that renders the UI based on the data in the model.

### The Model

So let's start by defining our model which will store our application's state.
It can be any type, but a `struct` usually makes the most sense.

```go
type model struct {
    choices  []string           // items on the to-do list
    cursor   int                // which to-do list item our cursor is pointing at
    selected map[int]struct{}   // which to-do items are selected
}
```

### Initialization

Next, we’ll define our application’s initial state. In this case, we’re defining
a function to return our initial model, however, we could just as easily define
the initial model as a variable elsewhere, too.

```go
func initialModel() model {
	return model{
		// Our to-do list is a grocery list
		choices:  []string{"Buy carrots", "Buy celery", "Buy kohlrabi"},

		// A map which indicates which choices are selected. We're using
		// the  map like a mathematical set. The keys refer to the indexes
		// of the `choices` slice, above.
		selected: make(map[int]struct{}),
	}
}
```

Next, we define the `Init` method. `Init` can return a `Cmd` that could perform
some initial I/O. For now, we don't need to do any I/O, so for the command,
we'll just return `nil`, which translates to "no command."

```go
func (m model) Init() tea.Cmd {
    // Just return `nil`, which means "no I/O right now, please."
    return nil
}
```

### The Update Method

Next up is the update method. The update function is called when ”things
happen.” Its job is to look at what has happened and return an updated model in
response. It can also return a `Cmd` to make more things happen, but for now
don't worry about that part.

In our case, when a user presses the down arrow, `Update`’s job is to notice
that the down arrow was pressed and move the cursor accordingly (or not).

The “something happened” comes in the form of a `Msg`, which can be any type.
Messages are the result of some I/O that took place, such as a keypress, timer
tick, or a response from a server.

We usually figure out which type of `Msg` we received with a type switch, but
you could also use a type assertion.

For now, we'll just deal with `tea.KeyMsg` messages, which are automatically
sent to the update function when keys are pressed.

```go
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {

    // Is it a key press?
    case tea.KeyMsg:

        // Cool, what was the actual key pressed?
        switch msg.String() {

        // These keys should exit the program.
        case "ctrl+c", "q":
            return m, tea.Quit

        // The "up" and "k" keys move the cursor up
        case "up", "k":
            if m.cursor > 0 {
                m.cursor--
            }

        // The "down" and "j" keys move the cursor down
        case "down", "j":
            if m.cursor < len(m.choices)-1 {
                m.cursor++
            }

        // The "enter" key and the spacebar (a literal space) toggle
        // the selected state for the item that the cursor is pointing at.
        case "enter", " ":
            _, ok := m.selected[m.cursor]
            if ok {
                delete(m.selected, m.cursor)
            } else {
                m.selected[m.cursor] = struct{}{}
            }
        }
    }

    // Return the updated model to the Bubble Tea runtime for processing.
    // Note that we're not returning a command.
    return m, nil
}
```

You may have noticed that <kbd>ctrl+c</kbd> and <kbd>q</kbd> above return
a `tea.Quit` command with the model. That’s a special command which instructs
the Bubble Tea runtime to quit, exiting the program.

### The View Method

At last, it’s time to render our UI. Of all the methods, the view is the
simplest. We look at the model in its current state and use it to return
a `string`. That string is our UI!

Because the view describes the entire UI of your application, you don’t have to
worry about redrawing logic and stuff like that. Bubble Tea takes care of it
for you.

```go
func (m model) View() string {
    // The header
    s := "What should we buy at the market?\n\n"

    // Iterate over our choices
    for i, choice := range m.choices {

        // Is the cursor pointing at this choice?
        cursor := " " // no cursor
        if m.cursor == i {
            cursor = ">" // cursor!
        }

        // Is this choice selected?
        checked := " " // not selected
        if _, ok := m.selected[i]; ok {
            checked = "x" // selected!
        }

        // Render the row
        s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
    }

    // The footer
    s += "\nPress q to quit.\n"

    // Send the UI for rendering
    return s
}
```

### All Together Now

The last step is to simply run our program. We pass our initial model to
`tea.NewProgram` and let it rip:

```go
func main() {
    p := tea.NewProgram(initialModel())
    if _, err := p.Run(); err != nil {
        fmt.Printf("Alas, there's been an error: %v", err)
        os.Exit(1)
    }
}
```

## What’s Next?

This tutorial covers the basics of building an interactive terminal UI, but
in the real world you'll also need to perform I/O. To learn about that have a
look at the [Command Tutorial][cmd]. It's pretty simple.

There are also several [Bubble Tea examples][examples] available and, of course,
there are [Go Docs][docs].

[cmd]: https://github.com/charmbracelet/bubbletea/tree/main/tutorials/commands/
[examples]: https://github.com/charmbracelet/bubbletea/tree/main/examples
[docs]: https://pkg.go.dev/github.com/charmbracelet/bubbletea?tab=doc

## Debugging

### Debugging with Delve

Since Bubble Tea apps assume control of stdin and stdout, you’ll need to run
delve in headless mode and then connect to it:

```bash
# Start the debugger
$ dlv debug --headless --api-version=2 --listen=127.0.0.1:43000 .
API server listening at: 127.0.0.1:43000

# Connect to it from another terminal
$ dlv connect 127.0.0.1:43000
```

If you do not explicitly supply the `--listen` flag, the port used will vary
per run, so passing this in makes the debugger easier to use from a script
or your IDE of choice.

Additionally, we pass in `--api-version=2` because delve defaults to version 1
for backwards compatibility reasons. However, delve recommends using version 2
for all new development and some clients may no longer work with version 1.
For more information, see the [Delve documentation](https://github.com/go-delve/delve/tree/master/Documentation/api).

### Logging Stuff

You can’t really log to stdout with Bubble Tea because your TUI is busy
occupying that! You can, however, log to a file by including something like
the following prior to starting your Bubble Tea program:

```go
if len(os.Getenv("DEBUG")) > 0 {
	f, err := tea.LogToFile("debug.log", "debug")
	if err != nil {
		fmt.Println("fatal:", err)
		os.Exit(1)
	}
	defer f.Close()
}
```

To see what’s being logged in real time, run `tail -f debug.log` while you run
your program in another window.

## Libraries we use with Bubble Tea

- [Bubbles][bubbles]: Common Bubble Tea components such as text inputs, viewports, spinners and so on
- [Lip Gloss][lipgloss]: Style, format and layout tools for terminal applications
- [Harmonica][harmonica]: A spring animation library for smooth, natural motion
- [BubbleZone][bubblezone]: Easy mouse event tracking for Bubble Tea components
- [ntcharts][ntcharts]: A terminal charting library built for Bubble Tea and [Lip Gloss][lipgloss]

[bubbles]: https://github.com/charmbracelet/bubbles
[lipgloss]: https://github.com/charmbracelet/lipgloss
[harmonica]: https://github.com/charmbracelet/harmonica
[bubblezone]: https://github.com/lrstanley/bubblezone
[ntcharts]: https://github.com/NimbleMarkets/ntcharts

## Bubble Tea in the Wild

There are over [10,000 applications](https://github.com/charmbracelet/bubbletea/network/dependents) built with Bubble Tea! Here are a handful of ’em.

### Staff favourites

- [chezmoi](https://github.com/twpayne/chezmoi): securely manage your dotfiles across multiple machines
- [circumflex](https://github.com/bensadeh/circumflex): read Hacker News in the terminal
- [gh-dash](https://www.github.com/dlvhdr/gh-dash): a GitHub CLI extension for PRs and issues
- [Tetrigo](https://github.com/Broderick-Westrope/tetrigo): Tetris in the terminal
- [Signls](https://github.com/emprcl/signls): a generative midi sequencer designed for composition and live performance
- [Superfile](https://github.com/yorukot/superfile): a super file manager

### In Industry

- Microsoft Azure – [Aztify](https://github.com/Azure/aztfy): bring Microsoft Azure resources under Terraform
- Daytona – [Daytona](https://github.com/daytonaio/daytona): open source dev environment manager
- Cockroach Labs – [CockroachDB](https://github.com/cockroachdb/cockroach): a cloud-native, high-availability distributed SQL database
- Truffle Security Co. – [Trufflehog](https://github.com/trufflesecurity/trufflehog): find leaked credentials
- NVIDIA – [container-canary](https://github.com/NVIDIA/container-canary): a container validator
- AWS – [eks-node-viewer](https://github.com/awslabs/eks-node-viewer): a tool for visualizing dynamic node usage within an EKS cluster
- MinIO – [mc](https://github.com/minio/mc): the official [MinIO](https://min.io) client
- Ubuntu – [Authd](https://github.com/ubuntu/authd): an authentication daemon for cloud-based identity providers

### Charm stuff

- [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser, and online markdown stash
- [Huh?](https://github.com/charmbracelet/huh): an interactive prompt and form toolkit
- [Mods](https://github.com/charmbracelet/mods): AI on the CLI, built for pipelines
- [Wishlist](https://github.com/charmbracelet/wishlist): an SSH directory (and bastion!)

### There’s so much more where that came from

For more applications built with Bubble Tea see [Charm & Friends][community].
Is there something cool you made with Bubble Tea you want to share? [PRs][community] are
welcome!

## Contributing

See [contributing][contribute].

[contribute]: https://github.com/charmbracelet/bubbletea/contribute

## Feedback

We’d love to hear your thoughts on this project. Feel free to drop us a note!

- [Twitter](https://twitter.com/charmcli)
- [The Fediverse](https://mastodon.social/@charmcli)
- [Discord](https://charm.sh/chat)

## Acknowledgments

Bubble Tea is based on the paradigms of [The Elm Architecture][elm] by Evan
Czaplicki et alia and the excellent [go-tea][gotea] by TJ Holowaychuk. It’s
inspired by the many great [_Zeichenorientierte Benutzerschnittstellen_][zb]
of days past.

[elm]: https://guide.elm-lang.org/architecture/
[gotea]: https://github.com/tj/go-tea
[zb]: https://de.wikipedia.org/wiki/Zeichenorientierte_Benutzerschnittstelle
[community]: https://github.com/charm-and-friends/charm-in-the-wild

## License

[MIT](https://github.com/charmbracelet/bubbletea/raw/main/LICENSE)

---

Part of [Charm](https://charm.sh).

<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-banner-next.jpg" width="400"></a>

Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة

---

### Overview (`vendor/github.com/charmbracelet/colorprofile/README.md`)

# Colorprofile

<p>
    <a href="https://github.com/charmbracelet/colorprofile/releases"><img src="https://img.shields.io/github/release/charmbracelet/colorprofile.svg" alt="Latest Release"></a>
    <a href="https://pkg.go.dev/github.com/charmbracelet/colorprofile?tab=doc"><img src="https://godoc.org/github.com/charmbracelet/colorprofile?status.svg" alt="GoDoc"></a>
    <a href="https://github.com/charmbracelet/colorprofile/actions"><img src="https://github.com/charmbracelet/colorprofile/actions/workflows/build.yml/badge.svg" alt="Build Status"></a>
</p>

A simple, powerful—and at times magical—package for detecting terminal color
profiles and performing color (and CSI) degradation.

## Detecting the terminal’s color profile

Detecting the terminal’s color profile is easy.

```go
import "github.com/charmbracelet/colorprofile"

// Detect the color profile. If you’re planning on writing to stderr you'd want
// to use os.Stderr instead.
p := colorprofile.Detect(os.Stdout, os.Environ())

// Comment on the profile.
fmt.Printf("You know, your colors are quite %s.", func() string {
    switch p {
    case colorprofile.TrueColor:
        return "fancy"
    case colorprofile.ANSI256:
        return "1990s fancy"
    case colorprofile.ANSI:
        return "normcore"
    case colorprofile.Ascii:
        return "ancient"
    case colorprofile.NoTTY:
        return "naughty!"
    }
    return "...IDK" // this should never happen
}())
```

## Downsampling colors

When necessary, colors can be downsampled to a given profile, or manually
downsampled to a specific profile.

```go
p := colorprofile.Detect(os.Stdout, os.Environ())
c := color.RGBA{0x6b, 0x50, 0xff, 0xff} // #6b50ff

// Downsample to the detected profile, when necessary.
convertedColor := p.Convert(c)

// Or manually convert to a given profile.
ansi256Color := colorprofile.ANSI256.Convert(c)
ansiColor := colorprofile.ANSI.Convert(c)
noColor := colorprofile.Ascii.Convert(c)
noANSI := colorprofile.NoTTY.Convert(c)
```

## Automatic downsampling with a Writer

You can also magically downsample colors in ANSI output, when necessary. If
output is not a TTY ANSI will be dropped entirely.

```go
myFancyANSI := "\x1b[38;2;107;80;255mCute \x1b[1;3mpuppy!!\x1b[m"

// Automatically downsample for the terminal at stdout.
w := colorprofile.NewWriter(os.Stdout, os.Environ())
fmt.Fprintf(w, myFancyANSI)

// Downsample to 4-bit ANSI.
w.Profile = colorprofile.ANSI
fmt.Fprintf(w, myFancyANSI)

// Ascii-fy, no colors.
w.Profile = colorprofile.Ascii
fmt.Fprintf(w, myFancyANSI)

// Strip ANSI altogether.
w.Profile = colorprofile.NoTTY
fmt.Fprintf(w, myFancyANSI) // not as fancy
```

## Contributing

See [contributing][contribute].

[contribute]: https://github.com/charmbracelet/colorprofile/contribute

## Feedback

We’d love to hear your thoughts on this project. Feel free to drop us a note!

- [Twitter](https://twitter.com/charmcli)
- [The Fediverse](https://mastodon.social/@charmcli)
- [Discord](https://charm.sh/chat)

## License

[MIT](https://github.com/charmbracelet/bubbletea/raw/master/LICENSE)

---

Part of [Charm](https://charm.sh).

<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>

Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة

---

### Overview (`vendor/github.com/charmbracelet/lipgloss/README.md`)

# Lip Gloss

<p>
    <a href="https://stuff.charm.sh/lipgloss/lipgloss-mascot-2k.png"><img width="340" alt="Lip Gloss title treatment" src="https://github.com/charmbracelet/lipgloss/assets/25087/147cadb1-4254-43ec-ae6b-8d6ca7b029a1"></a><br>
    <a href="https://github.com/charmbracelet/lipgloss/releases"><img src="https://img.shields.io/github/release/charmbracelet/lipgloss.svg" alt="Latest Release"></a>
    <a href="https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
    <a href="https://github.com/charmbracelet/lipgloss/actions"><img src="https://github.com/charmbracelet/lipgloss/workflows/build/badge.svg" alt="Build Status"></a>
    <a href="https://www.phorm.ai/query?projectId=a0e324b6-b706-4546-b951-6671ea60c13f"><img src="https://stuff.charm.sh/misc/phorm-badge.svg" alt="phorm.ai"></a>
</p>

Style definitions for nice terminal layouts. Built with TUIs in mind.



Lip Gloss takes an expressive, declarative approach to terminal rendering.
Users familiar with CSS will feel at home with Lip Gloss.

```go

import "github.com/charmbracelet/lipgloss"

var style = lipgloss.NewStyle().
    Bold(true).
    Foreground(lipgloss.Color("#FAFAFA")).
    Background(lipgloss.Color("#7D56F4")).
    PaddingTop(2).
    PaddingLeft(4).
    Width(22)

fmt.Println(style.Render("Hello, kitty"))
```

## Colors

Lip Gloss supports the following color profiles:

### ANSI 16 colors (4-bit)

```go
lipgloss.Color("5")  // magenta
lipgloss.Color("9")  // red
lipgloss.Color("12") // light blue
```

### ANSI 256 Colors (8-bit)

```go
lipgloss.Color("86")  // aqua
lipgloss.Color("201") // hot pink
lipgloss.Color("202") // orange
```

### True Color (16,777,216 colors; 24-bit)

```go
lipgloss.Color("#0000FF") // good ol' 100% blue
lipgloss.Color("#04B575") // a green
lipgloss.Color("#3C3C3C") // a dark gray
```

...as well as a 1-bit ASCII profile, which is black and white only.

The terminal's color profile will be automatically detected, and colors outside
the gamut of the current palette will be automatically coerced to their closest
available value.

### Adaptive Colors

You can also specify color options for light and dark backgrounds:

```go
lipgloss.AdaptiveColor{Light: "236", Dark: "248"}
```

The terminal's background color will automatically be detected and the
appropriate color will be chosen at runtime.

### Complete Colors

CompleteColor specifies exact values for True Color, ANSI256, and ANSI color
profiles.

```go
lipgloss.CompleteColor{TrueColor: "#0000FF", ANSI256: "86", ANSI: "5"}
```

Automatic color degradation will not be performed in this case and it will be
based on the color specified.

### Complete Adaptive Colors

You can use `CompleteColor` with `AdaptiveColor` to specify the exact values for
light and dark backgrounds without automatic color degradation.

```go
lipgloss.CompleteAdaptiveColor{
    Light: CompleteColor{TrueColor: "#d7ffae", ANSI256: "193", ANSI: "11"},
    Dark:  CompleteColor{TrueColor: "#d75fee", ANSI256: "163", ANSI: "5"},
}
```

## Inline Formatting

Lip Gloss supports the usual ANSI text formatting options:

```go
var style = lipgloss.NewStyle().
    Bold(true).
    Italic(true).
    Faint(true).
    Blink(true).
    Strikethrough(true).
    Underline(true).
    Reverse(true)
```

## Block-Level Formatting

Lip Gloss also supports rules for block-level formatting:

```go
// Padding
var style = lipgloss.NewStyle().
    PaddingTop(2).
    PaddingRight(4).
    PaddingBottom(2).
    PaddingLeft(4)

// Margins
var style = lipgloss.NewStyle().
    MarginTop(2).
    MarginRight(4).
    MarginBottom(2).
    MarginLeft(4)
```

There is also shorthand syntax for margins and padding, which follows the same
format as CSS:

```go
// 2 cells on all sides
lipgloss.NewStyle().Padding(2)

// 2 cells on the top and bottom, 4 cells on the left and right
lipgloss.NewStyle().Margin(2, 4)

// 1 cell on the top, 4 cells on the sides, 2 cells on the bottom
lipgloss.NewStyle().Padding(1, 4, 2)

// Clockwise, starting from the top: 2 cells on the top, 4 on the right, 3 on
// the bottom, and 1 on the left
lipgloss.NewStyle().Margin(2, 4, 3, 1)
```

## Aligning Text

You can align paragraphs of text to the left, right, or center.

```go
var style = lipgloss.NewStyle().
    Width(24).
    Align(lipgloss.Left).  // align it left
    Align(lipgloss.Right). // no wait, align it right
    Align(lipgloss.Center) // just kidding, align it in the center
```

## Width and Height

Setting a minimum width and height is simple and straightforward.

```go
var style = lipgloss.NewStyle().
    SetString("What’s for lunch?").
    Width(24).
    Height(32).
    Foreground(lipgloss.Color("63"))
```

## Borders

Adding borders is easy:

```go
// Add a purple, rectangular border
var style = lipgloss.NewStyle().
    BorderStyle(lipgloss.NormalBorder()).
    BorderForeground(lipgloss.Color("63"))

// Set a rounded, yellow-on-purple border to the top and left
var anotherStyle = lipgloss.NewStyle().
    BorderStyle(lipgloss.RoundedBorder()).
    BorderForeground(lipgloss.Color("228")).
    BorderBackground(lipgloss.Color("63")).
    BorderTop(true).
    BorderLeft(true)

// Make your own border
var myCuteBorder = lipgloss.Border{
    Top:         "._.:*:",
    Bottom:      "._.:*:",
    Left:        "|*",
    Right:       "|*",
    TopLeft:     "*",
    TopRight:    "*",
    BottomLeft:  "*",
    BottomRight: "*",
}
```

There are also shorthand functions for defining borders, which follow a similar
pattern to the margin and padding shorthand functions.

```go
// Add a thick border to the top and bottom
lipgloss.NewStyle().
    Border(lipgloss.ThickBorder(), true, false)

// Add a double border to the top and left sides. Rules are set clockwise
// from top.
lipgloss.NewStyle().
    Border(lipgloss.DoubleBorder(), true, false, false, true)
```

For more on borders see [the docs][docs].

## Copying Styles

Just use assignment:

```go
style := lipgloss.NewStyle().Foreground(lipgloss.Color("219"))

copiedStyle := style // this is a true copy

wildStyle := style.Blink(true) // this is also true copy, with blink added

```

Since `Style` data structures contains only primitive types, assigning a style
to another effectively creates a new copy of the style without mutating the
original.

## Inheritance

Styles can inherit rules from other styles. When inheriting, only unset rules
on the receiver are inherited.

```go
var styleA = lipgloss.NewStyle().
    Foreground(lipgloss.Color("229")).
    Background(lipgloss.Color("63"))

// Only the background color will be inherited here, because the foreground
// color will have been already set:
var styleB = lipgloss.NewStyle().
    Foreground(lipgloss.Color("201")).
    Inherit(styleA)
```

## Unsetting Rules

All rules can be unset:

```go
var style = lipgloss.NewStyle().
    Bold(true).                        // make it bold
    UnsetBold().                       // jk don't make it bold
    Background(lipgloss.Color("227")). // yellow background
    UnsetBackground()                  // never mind
```

When a rule is unset, it won't be inherited or copied.

## Enforcing Rules

Sometimes, such as when developing a component, you want to make sure style
definitions respect their intended purpose in the UI. This is where `Inline`
and `MaxWidth`, and `MaxHeight` come in:

```go
// Force rendering onto a single line, ignoring margins, padding, and borders.
someStyle.Inline(true).Render("yadda yadda")

// Also limit rendering to five cells
someStyle.Inline(true).MaxWidth(5).Render("yadda yadda")

// Limit rendering to a 5x5 cell block
someStyle.MaxWidth(5).MaxHeight(5).Render("yadda yadda")
```

## Tabs

The tab character (`\t`) is rendered differently in different terminals (often
as 8 spaces, sometimes 4). Because of this inconsistency, Lip Gloss converts
tabs to 4 spaces at render time. This behavior can be changed on a per-style
basis, however:

```go
style := lipgloss.NewStyle() // tabs will render as 4 spaces, the default
style = style.TabWidth(2)    // render tabs as 2 spaces
style = style.TabWidth(0)    // remove tabs entirely
style = style.TabWidth(lipgloss.NoTabConversion) // leave tabs intact
```

## Rendering

Generally, you just call the `Render(string...)` method on a `lipgloss.Style`:

```go
style := lipgloss.NewStyle().Bold(true).SetString("Hello,")
fmt.Println(style.Render("kitty.")) // Hello, kitty.
fmt.Println(style.Render("puppy.")) // Hello, puppy.
```

But you could also use the Stringer interface:

```go
var style = lipgloss.NewStyle().SetString("你好，猫咪。").Bold(true)
fmt.Println(style) // 你好，猫咪。
```

### Custom Renderers

Custom renderers allow you to render to a specific outputs. This is
particularly important when you want to render to different outputs and
correctly detect the color profile and dark background status for each, such as
in a server-client situation.

```go
func myLittleHandler(sess ssh.Session) {
    // Create a renderer for the client.
    renderer := lipgloss.NewRenderer(sess)

    // Create a new style on the renderer.
    style := renderer.NewStyle().Background(lipgloss.AdaptiveColor{Light: "63", Dark: "228"})

    // Render. The color profile and dark background state will be correctly detected.
    io.WriteString(sess, style.Render("Heyyyyyyy"))
}
```

For an example on using a custom renderer over SSH with [Wish][wish] see the
[SSH example][ssh-example].

## Utilities

In addition to pure styling, Lip Gloss also ships with some utilities to help
assemble your layouts.

### Joining Paragraphs

Horizontally and vertically joining paragraphs is a cinch.

```go
// Horizontally join three paragraphs along their bottom edges
lipgloss.JoinHorizontal(lipgloss.Bottom, paragraphA, paragraphB, paragraphC)

// Vertically join two paragraphs along their center axes
lipgloss.JoinVertical(lipgloss.Center, paragraphA, paragraphB)

// Horizontally join three paragraphs, with the shorter ones aligning 20%
// from the top of the tallest
lipgloss.JoinHorizontal(0.2, paragraphA, paragraphB, paragraphC)
```

### Measuring Width and Height

Sometimes you’ll want to know the width and height of text blocks when building
your layouts.

```go
// Render a block of text.
var style = lipgloss.NewStyle().
    Width(40).
    Padding(2)
var block string = style.Render(someLongString)

// Get the actual, physical dimensions of the text block.
width := lipgloss.Width(block)
height := lipgloss.Height(block)

// Here's a shorthand function.
w, h := lipgloss.Size(block)
```

### Placing Text in Whitespace

Sometimes you’ll simply want to place a block of text in whitespace.

```go
// Center a paragraph horizontally in a space 80 cells wide. The height of
// the block returned will be as tall as the input paragraph.
block := lipgloss.PlaceHorizontal(80, lipgloss.Center, fancyStyledParagraph)

// Place a paragraph at the bottom of a space 30 cells tall. The width of
// the text block returned will be as wide as the input paragraph.
block := lipgloss.PlaceVertical(30, lipgloss.Bottom, fancyStyledParagraph)

// Place a paragraph in the bottom right corner of a 30x80 cell space.
block := lipgloss.Place(30, 80, lipgloss.Right, lipgloss.Bottom, fancyStyledParagraph)
```

You can also style the whitespace. For details, see [the docs][docs].

## Rendering Tables

Lip Gloss ships with a table rendering sub-package.

```go
import "github.com/charmbracelet/lipgloss/table"
```

Define some rows of data.

```go
rows := [][]string{
    {"Chinese", "您好", "你好"},
    {"Japanese", "こんにちは", "やあ"},
    {"Arabic", "أهلين", "أهلا"},
    {"Russian", "Здравствуйте", "Привет"},
    {"Spanish", "Hola", "¿Qué tal?"},
}
```

Use the table package to style and render the table.

```go
var (
    purple    = lipgloss.Color("99")
    gray      = lipgloss.Color("245")
    lightGray = lipgloss.Color("241")

    headerStyle  = lipgloss.NewStyle().Foreground(purple).Bold(true).Align(lipgloss.Center)
    cellStyle    = lipgloss.NewStyle().Padding(0, 1).Width(14)
    oddRowStyle  = cellStyle.Foreground(gray)
    evenRowStyle = cellStyle.Foreground(lightGray)
)

t := table.New().
    Border(lipgloss.NormalBorder()).
    BorderStyle(lipgloss.NewStyle().Foreground(purple)).
    StyleFunc(func(row, col int) lipgloss.Style {
        switch {
        case row == table.HeaderRow:
            return headerStyle
        case row%2 == 0:
            return evenRowStyle
        default:
            return oddRowStyle
        }
    }).
    Headers("LANGUAGE", "FORMAL", "INFORMAL").
    Rows(rows...)

// You can also add tables row-by-row
t.Row("English", "You look absolutely fabulous.", "How's it going?")
```

Print the table.

```go
fmt.Println(t)
```



> [!WARNING]
> Table `Rows` need to be declared before `Offset` otherwise it does nothing.

### Table Borders

There are helpers to generate tables in markdown or ASCII style:

#### Markdown Table

```go
table.New().Border(lipgloss.MarkdownBorder()).BorderTop(false).BorderBottom(false)
```

```
| LANGUAGE |    FORMAL    | INFORMAL  |
|----------|--------------|-----------|
| Chinese  | Nǐn hǎo      | Nǐ hǎo    |
| French   | Bonjour      | Salut     |
| Russian  | Zdravstvuyte | Privet    |
| Spanish  | Hola         | ¿Qué tal? |
```

#### ASCII Table

```go
table.New().Border(lipgloss.ASCIIBorder())
```

```
+----------+--------------+-----------+
| LANGUAGE |    FORMAL    | INFORMAL  |
+----------+--------------+-----------+
| Chinese  | Nǐn hǎo      | Nǐ hǎo    |
| French   | Bonjour      | Salut     |
| Russian  | Zdravstvuyte | Privet    |
| Spanish  | Hola         | ¿Qué tal? |
+----------+--------------+-----------+
```

For more on tables see [the docs](https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc) and [examples](https://github.com/charmbracelet/lipgloss/tree/master/examples/table).

## Rendering Lists

Lip Gloss ships with a list rendering sub-package.

```go
import "github.com/charmbracelet/lipgloss/list"
```

Define a new list.

```go
l := list.New("A", "B", "C")
```

Print the list.

```go
fmt.Println(l)

// • A
// • B
// • C
```

Lists have the ability to nest.

```go
l := list.New(
    "A", list.New("Artichoke"),
    "B", list.New("Baking Flour", "Bananas", "Barley", "Bean Sprouts"),
    "C", list.New("Cashew Apple", "Cashews", "Coconut Milk", "Curry Paste", "Currywurst"),
    "D", list.New("Dill", "Dragonfruit", "Dried Shrimp"),
    "E", list.New("Eggs"),
    "F", list.New("Fish Cake", "Furikake"),
    "J", list.New("Jicama"),
    "K", list.New("Kohlrabi"),
    "L", list.New("Leeks", "Lentils", "Licorice Root"),
)
```

Print the list.

```go
fmt.Println(l)
```

<p align="center">
<img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/0dc9f440-0748-4151-a3b0-7dcf29dfcdb0">
</p>

Lists can be customized via their enumeration function as well as using
`lipgloss.Style`s.

```go
enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1)

l := list.New(
    "Glossier",
    "Claire’s Boutique",
    "Nyx",
    "Mac",
    "Milk",
    ).
    Enumerator(list.Roman).
    EnumeratorStyle(enumeratorStyle).
    ItemStyle(itemStyle)
```

Print the list.

<p align="center">
<img width="600" alt="List example" src="https://github.com/charmbracelet/lipgloss/assets/42545625/360494f1-57fb-4e13-bc19-0006efe01561">
</p>

In addition to the predefined enumerators (`Arabic`, `Alphabet`, `Roman`, `Bullet`, `Tree`),
you may also define your own custom enumerator:

```go
l := list.New("Duck", "Duck", "Duck", "Duck", "Goose", "Duck", "Duck")

func DuckDuckGooseEnumerator(l list.Items, i int) string {
    if l.At(i).Value() == "Goose" {
        return "Honk →"
    }
    return ""
}

l = l.Enumerator(DuckDuckGooseEnumerator)
```

Print the list:

<p align="center">
<img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/157aaf30-140d-4948-9bb4-dfba46e5b87e">
</p>

If you need, you can also build lists incrementally:

```go
l := list.New()

for i := 0; i < repeat; i++ {
    l.Item("Lip Gloss")
}
```

## Rendering Trees

Lip Gloss ships with a tree rendering sub-package.

```go
import "github.com/charmbracelet/lipgloss/tree"
```

Define a new tree.

```go
t := tree.Root(".").
    Child("A", "B", "C")
```

Print the tree.

```go
fmt.Println(t)

// .
// ├── A
// ├── B
// └── C
```

Trees have the ability to nest.

```go
t := tree.Root(".").
    Child("macOS").
    Child(
        tree.New().
            Root("Linux").
            Child("NixOS").
            Child("Arch Linux (btw)").
            Child("Void Linux"),
        ).
    Child(
        tree.New().
            Root("BSD").
            Child("FreeBSD").
            Child("OpenBSD"),
    )
```

Print the tree.

```go
fmt.Println(t)
```

<p align="center">
<img width="663" alt="Tree Example (simple)" src="https://github.com/user-attachments/assets/5ef14eb8-a5d4-4f94-8834-e15d1e714f89">
</p>

Trees can be customized via their enumeration function as well as using
`lipgloss.Style`s.

```go
enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("63")).MarginRight(1)
rootStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("35"))
itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212"))

t := tree.
    Root("⁜ Makeup").
    Child(
        "Glossier",
        "Fenty Beauty",
        tree.New().Child(
            "Gloss Bomb Universal Lip Luminizer",
            "Hot Cheeks Velour Blushlighter",
        ),
        "Nyx",
        "Mac",
        "Milk",
    ).
    Enumerator(tree.RoundedEnumerator).
    EnumeratorStyle(enumeratorStyle).
    RootStyle(rootStyle).
    ItemStyle(itemStyle)
```

Print the tree.

<p align="center">
<img width="663" alt="Tree Example (makeup)" src="https://github.com/user-attachments/assets/06d12d87-744a-4c89-bd98-45de9094a97e">
</p>

The predefined enumerators for trees are `DefaultEnumerator` and `RoundedEnumerator`.

If you need, you can also build trees incrementally:

```go
t := tree.New()

for i := 0; i < repeat; i++ {
    t.Child("Lip Gloss")
}
```

---

## FAQ

<details>
<summary>
Why are things misaligning? Why are borders at the wrong widths?
</summary>
<p>This is most likely due to your locale and encoding, particularly with
regard to Chinese, Japanese, and Korean (for example, <code>zh_CN.UTF-8</code>
or <code>ja_JP.UTF-8</code>). The most direct way to fix this is to set
<code>RUNEWIDTH_EASTASIAN=0</code> in your environment.</p>

<p>For details see <a href="https://github.com/charmbracelet/lipgloss/issues/40">https://github.com/charmbracelet/lipgloss/issues/40.</a></p>
</details>

<details>
<summary>
Why isn't Lip Gloss displaying colors?
</summary>
<p>Lip Gloss automatically degrades colors to the best available option in the
given terminal, and if output's not a TTY it will remove color output entirely.
This is common when running tests, CI, or when piping output elsewhere.</p>

<p>If necessary, you can force a color profile in your tests with
<a href="https://pkg.go.dev/github.com/charmbracelet/lipgloss#SetColorProfile"><code>SetColorProfile</code></a>.</p>

```go
import (
    "github.com/charmbracelet/lipgloss"
    "github.com/muesli/termenv"
)

lipgloss.SetColorProfile(termenv.TrueColor)
```

_Note:_ this option limits the flexibility of your application and can cause
ANSI escape codes to be output in cases where that might not be desired. Take
careful note of your use case and environment before choosing to force a color
profile.

</details>

## What about [Bubble Tea][tea]?

Lip Gloss doesn’t replace Bubble Tea. Rather, it is an excellent Bubble Tea
companion. It was designed to make assembling terminal user interface views as
simple and fun as possible so that you can focus on building your application
instead of concerning yourself with low-level layout details.

In simple terms, you can use Lip Gloss to help build your Bubble Tea views.

[tea]: https://github.com/charmbracelet/tea

## Under the Hood

Lip Gloss is built on the excellent [Termenv][termenv] and [Reflow][reflow]
libraries which deal with color and ANSI-aware text operations, respectively.
For many use cases Termenv and Reflow will be sufficient for your needs.

[termenv]: https://github.com/muesli/termenv
[reflow]: https://github.com/muesli/reflow

## Rendering Markdown

For a more document-centric rendering solution with support for things like
lists, tables, and syntax-highlighted code have a look at [Glamour][glamour],
the stylesheet-based Markdown renderer.

[glamour]: https://github.com/charmbracelet/glamour

## Contributing

See [contributing][contribute].

[contribute]: https://github.com/charmbracelet/lipgloss/contribute

## Feedback

We’d love to hear your thoughts on this project. Feel free to drop us a note!

- [Twitter](https://twitter.com/charmcli)
- [The Fediverse](https://mastodon.social/@charmcli)
- [Discord](https://charm.sh/chat)

## License

[MIT](https://github.com/charmbracelet/lipgloss/raw/master/LICENSE)

---

Part of [Charm](https://charm.sh).

<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>

Charm热爱开源 • Charm loves open source

[docs]: https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc
[wish]: https://github.com/charmbracelet/wish
[ssh-example]: examples/ssh

---

### Overview (`vendor/github.com/clipperhouse/displaywidth/README.md`)

# displaywidth

A high-performance Go package for measuring the monospace display width of strings, UTF-8 bytes, and runes.

[](https://pkg.go.dev/github.com/clipperhouse/displaywidth)
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml)
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml)

## Install
```bash
go get github.com/clipperhouse/displaywidth
```

## Usage

```go
package main

import (
    "fmt"
    "github.com/clipperhouse/displaywidth"
)

func main() {
    width := displaywidth.String("Hello, 世界!")
    fmt.Println(width)

    width = displaywidth.Bytes([]byte("🌍"))
    fmt.Println(width)

    width = displaywidth.Rune('🌍')
    fmt.Println(width)
}
```

For most purposes, you should use the `String` or `Bytes` methods. They sum
the widths of grapheme clusters in the string or byte slice.

> Note: in your application, iterating over runes to measure width is likely incorrect;
the smallest unit of display is a grapheme, not a rune.

### Iterating over graphemes

If you need the individual graphemes:

```go
import (
    "fmt"
    "github.com/clipperhouse/displaywidth"
)

func main() {
    g := displaywidth.StringGraphemes("Hello, 世界!")
    for g.Next() {
        width := g.Width()
        value := g.Value()
        // do something with the width or value
    }
}
```

### Options

There is one option, `displaywidth.Options.EastAsianWidth`, which defines
how [East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous)
are treated.

When `false` (default), East Asian Ambiguous characters are treated as width 1.
When `true`, they are treated as width 2.

You may wish to configure this based on environment variables or locale.
 `go-runewidth`, for example, does so
 [during package initialization](https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L26C1-L45C2).

`displaywidth` does not do this automatically, we prefer to leave it to you.
You might do something like:

```go
var width displaywidth.Options // zero value is default

func init() {
    if os.Getenv("EAST_ASIAN_WIDTH") == "true" {
        width = displaywidth.Options{EastAsianWidth: true}
    }
    // or check locale, or any other logic you want
}

// use it in your logic
func myApp() {
    fmt.Println(width.String("Hello, 世界!"))
}
```

## Technical standards and compatibility

This package implements the Unicode East Asian Width standard
([UAX #11](https://www.unicode.org/reports/tr11/tr11-43.html)), and handles
[version selectors](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)),
and [regional indicator pairs](https://en.wikipedia.org/wiki/Regional_indicator_symbol)
(flags). We implement [Unicode TR51](https://www.unicode.org/reports/tr51/tr51-27.html)
for emojis. We are keeping an eye on
[emerging standards](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/).

`clipperhouse/displaywidth`, `mattn/go-runewidth`, and `rivo/uniseg` will
give the same outputs for most real-world text. Extensive details are in the
[compatibility analysis](comparison/COMPATIBILITY_ANALYSIS.md).

## Prior Art

[mattn/go-runewidth](https://github.com/mattn/go-runewidth)

[rivo/uniseg](https://github.com/rivo/uniseg)

[x/text/width](https://pkg.go.dev/golang.org/x/text/width)

[x/text/internal/triegen](https://pkg.go.dev/golang.org/x/text/internal/triegen)

## Benchmarks

```bash
cd comparison
go test -bench=. -benchmem
```

```
/* Detailed source-code truncated for AI context efficiency. */
```

Here are some notes on [how to make Unicode things fast](https://clipperhouse.com/go-unicode/).

---

### Overview (`vendor/github.com/clipperhouse/stringish/README.md`)

# stringish

A small Go module that provides a generic type constraint for “string-like”
data, and a utf8 package that works with both strings and byte slices
without conversions.

```go
type Interface interface {
	~[]byte | ~string
}
```

[](https://pkg.go.dev/github.com/clipperhouse/stringish/utf8)
[](https://github.com/clipperhouse/stringish/actions/workflows/gotest.yml)

## Install

```
go get github.com/clipperhouse/stringish
```

## Examples

```go
import (
    "github.com/clipperhouse/stringish"
    "github.com/clipperhouse/stringish/utf8"
)

s := "Hello, 世界"
r, size := utf8.DecodeRune(s)   // not DecodeRuneInString 🎉

b := []byte("Hello, 世界")
r, size = utf8.DecodeRune(b)    // same API!

func MyFoo[T stringish.Interface](s T) T {
    // pass a string or a []byte
    // iterate, slice, transform, whatever
}
```

## Motivation

Sometimes we want APIs to accept `string` or `[]byte` without having to convert
between those types. That conversion usually allocates!

By implementing with `stringish.Interface`, we can have a single API, and
single implementation for both types: one `Foo` instead of `Foo` and
`FooString`.

We have converted the
[`unicode/utf8` package](https://github.com/clipperhouse/stringish/blob/main/utf8/utf8.go)
as an example -- note the absence of`*InString` funcs. We might look at `x/text`
next.

## Used by

- clipperhouse/uax29: [stringish trie](https://github.com/clipperhouse/uax29/blob/master/graphemes/trie.go#L27), [stringish iterator](https://github.com/clipperhouse/uax29/blob/master/internal/iterators/iterator.go#L9), [stringish SplitFunc](https://github.com/clipperhouse/uax29/blob/master/graphemes/splitfunc.go#L21)

- [clipperhouse/displaywidth](https://github.com/clipperhouse/displaywidth)

## Prior discussion

- [Consideration of similar by the Go team](https://github.com/golang/go/issues/48643)

---

### Overview (`vendor/github.com/erikgeiser/coninput/README.md`)

# coninput
Go library for input handling using Windows Console API

---

### Overview (`vendor/github.com/inconshreveable/mousetrap/README.md`)

# mousetrap

mousetrap is a tiny library that answers a single question.

On a Windows machine, was the process invoked by someone double clicking on
the executable file while browsing in explorer?

### Motivation

Windows developers unfamiliar with command line tools will often "double-click"
the executable for a tool. Because most CLI tools print the help and then exit
when invoked without arguments, this is often very frustrating for those users.

mousetrap provides a way to detect these invocations so that you can provide
more helpful behavior and instructions on how to run the CLI tool. To see what
this looks like, both from an organizational and a technical perspective, see
https://inconshreveable.com/09-09-2014/sweat-the-small-stuff/

### The interface

The library exposes a single interface:

    func StartedByExplorer() (bool)

---

### Overview (`vendor/github.com/lucasb-eyer/go-colorful/README.md`)

go-colorful
===========

[](https://pkg.go.dev/github.com/lucasb-eyer/go-colorful)
[](https://goreportcard.com/report/github.com/lucasb-eyer/go-colorful)

A library for playing with colors in Go. Supports Go 1.13 onwards.

Why?
====
I love games. I make games. I love detail and I get lost in detail.
One such detail popped up during the development of [Memory Which Does Not Suck](https://github.com/lucasb-eyer/mwdns/),
when we wanted the server to assign the players random colors. Sometimes
two players got very similar colors, which bugged me. The very same evening,
[I want hue](http://tools.medialab.sciences-po.fr/iwanthue/) was the top post
on HackerNews' frontpage and showed me how to Do It Right™. Last but not
least, there was no library for handling color spaces available in go. Colorful
does just that and implements Go's `color.Color` interface.

What?
=====
Go-Colorful stores colors in RGB and provides methods from converting these to various color-spaces. Currently supported colorspaces are:

- **RGB:** All three of Red, Green and Blue in [0..1].
- **HSL:** Hue in [0..360], Saturation and Luminance in [0..1]. For legacy reasons; please forget that it exists.
- **HSV:** Hue in [0..360], Saturation and Value in [0..1]. You're better off using HCL, see below.
- **Hex RGB:** The "internet" color format, as in #FF00FF.
- **Linear RGB:** See [gamma correct rendering](http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/).
- **CIE-XYZ:** CIE's standard color space, almost in [0..1].
- **CIE-xyY:** encodes chromacity in x and y and luminance in Y, all in [0..1]
- **CIE-L\*a\*b\*:** A *perceptually uniform* color space, i.e. distances are meaningful. L\* in [0..1] and a\*, b\* almost in [-1..1].
- **CIE-L\*u\*v\*:** Very similar to CIE-L\*a\*b\*, there is [no consensus](http://en.wikipedia.org/wiki/CIELUV#Historical_background) on which one is "better".
- **CIE-L\*C\*h° (HCL):** This is generally the [most useful](http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/) one; CIE-L\*a\*b\* space in polar coordinates, i.e. a *better* HSV. H° is in [0..360], C\* almost in [0..1] and L\* as in CIE-L\*a\*b\*.
- **CIE LCh(uv):** Called `LuvLCh` in code, this is a cylindrical transformation of the CIE-L\*u\*v\* color space. Like HCL above: H° is in [0..360], C\* almost in [0..1] and L\* as in CIE-L\*u\*v\*.
- **HSLuv:** The better alternative to HSL, see [here](https://www.hsluv.org/) and [here](https://www.kuon.ch/post/2020-03-08-hsluv/). Hue in [0..360], Saturation and Luminance in [0..1].
- **HPLuv:** A variant of HSLuv. The color space is smoother, but only pastel colors can be included. Because the valid colors are limited, it's easy to get invalid Saturation values way above 1.0, indicating the color can't be represented in HPLuv because it's not pastel.

For the colorspaces where it makes sense (XYZ, Lab, Luv, HCl), the
[D65](http://en.wikipedia.org/wiki/Illuminant_D65) is used as reference white
by default but methods for using your own reference white are provided.

A coordinate being *almost in* a range means that generally it is, but for very
bright colors and depending on the reference white, it might overflow this
range slightly. For example, C\* of #0000ff is 1.338.

Unit-tests are provided.

Nice, but what's it useful for?
-------------------------------

- Converting color spaces. Some people like to do that.
- Blending (interpolating) between colors in a "natural" look by using the right colorspace.
- Generating random colors under some constraints (e.g. colors of the same shade, or shades of one color.)
- Generating gorgeous random palettes with distinct colors of a same temperature.

So which colorspace should I use?
=================================
It depends on what you want to do. I think the folks from *I want hue* are
on-spot when they say that RGB fits to how *screens produce* color, CIE L\*a\*b\*
fits how *humans perceive* color and HCL fits how *humans think* colors.

Whenever you'd use HSV, rather go for CIE-L\*C\*h°. for fixed lightness L\* and
chroma C\* values, the hue angle h° rotates through colors of the same
perceived brightness and intensity.

How?
====

### Installing
Installing the library is as easy as

```bash
$ go get github.com/lucasb-eyer/go-colorful
```

The package can then be used through an

```go
import "github.com/lucasb-eyer/go-colorful"
```

### Basic usage

Create a beautiful blue color using different source space:

```go
// Any of the following should be the same
c := colorful.Color{0.313725, 0.478431, 0.721569}
c, err := colorful.Hex("#517AB8")
if err != nil {
    log.Fatal(err)
}
c = colorful.Hsv(216.0, 0.56, 0.722)
c = colorful.Xyz(0.189165, 0.190837, 0.480248)
c = colorful.Xyy(0.219895, 0.221839, 0.190837)
c = colorful.Lab(0.507850, 0.040585,-0.370945)
c = colorful.Luv(0.507849,-0.194172,-0.567924)
c = colorful.Hcl(276.2440, 0.373160, 0.507849)
fmt.Printf("RGB values: %v, %v, %v", c.R, c.G, c.B)
```

And then converting this color back into various color spaces:

```go
hex := c.Hex()
h, s, v := c.Hsv()
x, y, z := c.Xyz()
x, y, Y := c.Xyy()
l, a, b := c.Lab()
l, u, v := c.Luv()
h, c, l := c.Hcl()
```

Note that, because of Go's unfortunate choice of requiring an initial uppercase,
the name of the functions relating to the xyY space are just off. If you have
any good suggestion, please open an issue. (I don't consider XyY good.)

### The `color.Color` interface
Because a `colorful.Color` implements Go's `color.Color` interface (found in the
`image/color` package), it can be used anywhere that expects a `color.Color`.

Furthermore, you can convert anything that implements the `color.Color` interface
into a `colorful.Color` using the `MakeColor` function:

```go
c, ok := colorful.MakeColor(color.Gray16{12345})
```

**Caveat:** Be aware that this latter conversion (using `MakeColor`) hits a
corner-case when alpha is exactly zero. Because `color.Color` uses pre-multiplied
alpha colors, this means the RGB values are lost (set to 0) and it's impossible
to recover them. In such a case `MakeColor` will return `false` as its second value.

### Comparing colors
In the RGB color space, the Euclidean distance between colors *doesn't* correspond
to visual/perceptual distance. This means that two pairs of colors which have the
same distance in RGB space can look much further apart. This is fixed by the
CIE-L\*a\*b\*, CIE-L\*u\*v\* and CIE-L\*C\*h° color spaces.
Thus you should only compare colors in any of these space.
(Note that the distance in CIE-L\*a\*b\* and CIE-L\*C\*h° are the same, since it's the same space but in cylindrical coordinates)



The two colors shown on the top look much more different than the two shown on
the bottom. Still, in RGB space, their distance is the same.
Here is a little example program which shows the distances between the top two
and bottom two colors in RGB, CIE-L\*a\*b\* and CIE-L\*u\*v\* space. You can find it in `doc/colordist/colordist.go`.

```go
package main

import "fmt"
import "github.com/lucasb-eyer/go-colorful"

func main() {
	c1a := colorful.Color{150.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0}
	c1b := colorful.Color{53.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0}
	c2a := colorful.Color{10.0 / 255.0, 150.0 / 255.0, 50.0 / 255.0}
	c2b := colorful.Color{99.9 / 255.0, 150.0 / 255.0, 10.0 / 255.0}

	fmt.Printf("DistanceRgb:       c1: %v\tand c2: %v\n", c1a.DistanceRgb(c1b), c2a.DistanceRgb(c2b))
	fmt.Printf("DistanceLab:       c1: %v\tand c2: %v\n", c1a.DistanceLab(c1b), c2a.DistanceLab(c2b))
	fmt.Printf("DistanceLuv:       c1: %v\tand c2: %v\n", c1a.DistanceLuv(c1b), c2a.DistanceLuv(c2b))
	fmt.Printf("DistanceCIE76:     c1: %v\tand c2: %v\n", c1a.DistanceCIE76(c1b), c2a.DistanceCIE76(c2b))
	fmt.Printf("DistanceCIE94:     c1: %v\tand c2: %v\n", c1a.DistanceCIE94(c1b), c2a.DistanceCIE94(c2b))
	fmt.Printf("DistanceCIEDE2000: c1: %v\tand c2: %v\n", c1a.DistanceCIEDE2000(c1b), c2a.DistanceCIEDE2000(c2b))
}
```

Running the above program shows that you should always prefer any of the CIE distances:

```bash
$ go run colordist.go
DistanceRgb:       c1: 0.3803921568627451	and c2: 0.3858713931171159
DistanceLab:       c1: 0.32048458312798056	and c2: 0.24397151758565272
DistanceLuv:       c1: 0.5134369614199698	and c2: 0.2568692839860636
DistanceCIE76:     c1: 0.32048458312798056	and c2: 0.24397151758565272
DistanceCIE94:     c1: 0.19799168128511324	and c2: 0.12207136371167401
DistanceCIEDE2000: c1: 0.17274551120971166	and c2: 0.10665210031428465
```

It also shows that `DistanceLab` is more formally known as `DistanceCIE76` and
has been superseded by the slightly more accurate, but much more expensive
`DistanceCIE94` and `DistanceCIEDE2000`.

Note that `AlmostEqualRgb` is provided mainly for (unit-)testing purposes. Use
it only if you really know what you're doing. It will eat your cat.

### Blending colors
Blending is highly connected to distance, since it basically "walks through" the
colorspace thus, if the colorspace maps distances well, the walk is "smooth".

Colorful comes with blending functions in RGB, HSV and any of the LAB spaces.
Of course, you'd rather want to use the blending functions of the LAB spaces since
these spaces map distances well but, just in case, here is an example showing
you how the blendings (`#fdffcc` to `#242a42`) are done in the various spaces:



What you see is that HSV is really bad: it adds some green, which is not present
in the original colors at all! RGB is much better, but it stays light a little
too long. LUV and LAB both hit the right lightness but LAB has a little more
color. HCL works in the same vein as HSV (both cylindrical interpolations) but
it does it right in that there is no green appearing and the lightness changes
in a linear manner.

While this seems all good, you need to know one thing: When interpolating in any
of the CIE color spaces, you might get invalid RGB colors! This is important if
the starting and ending colors are user-input or random. An example of where this
happens is when blending between `#eeef61` and `#1e3140`:



You can test whether a color is a valid RGB color by calling the `IsValid` method
and indeed, calling IsValid will return false for the redish colors on the bottom.
One way to "fix" this is to get a valid color close to the invalid one by calling
`Clamped`, which always returns a nearby valid color. Doing this, we get the
following result, which is satisfactory:



The following is the code creating the above three images; it can be found in `doc/colorblend/colorblend.go`

```
/* Detailed source-code truncated for AI context efficiency. */
```

#### Generating color gradients
A very common reason to blend colors is creating gradients. There is an example
program in [doc/gradientgen.go](doc/gradientgen/gradientgen.go); it doesn't use any API
which hasn't been used in the previous example code, so I won't bother pasting
the code in here. Just look at that gorgeous gradient it generated in HCL space:



### Getting random colors
It is sometimes necessary to generate random colors. You could simply do this
on your own by generating colors with random values. By restricting the random
values to a range smaller than [0..1] and using a space such as CIE-H\*C\*l° or
HSV, you can generate both random shades of a color or random colors of a
lightness:

```go
random_blue := colorful.Hcl(180.0+rand.Float64()*50.0, 0.2+rand.Float64()*0.8, 0.3+rand.Float64()*0.7)
random_dark := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), rand.Float64()*0.4)
random_light := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), 0.6+rand.Float64()*0.4)
```

Since getting random "warm" and "happy" colors is quite a common task, there
are some helper functions:

```go
colorful.WarmColor()
colorful.HappyColor()
colorful.FastWarmColor()
colorful.FastHappyColor()
```

The ones prefixed by `Fast` are faster but less coherent since they use the HSV
space as opposed to the regular ones which use CIE-L\*C\*h° space. The
following picture shows the warm colors in the top two rows and happy colors
in the bottom two rows. Within these, the first is the regular one and the
second is the fast one.



Don't forget to initialize the random seed! You can see the code used for
generating this picture in `doc/colorgens/colorgens.go`.

### Getting random palettes
As soon as you need to generate more than one random color, you probably want
them to be distinguishable. Playing against an opponent which has almost the
same blue as I do is not fun. This is where random palettes can help.

These palettes are generated using an algorithm which ensures that all colors
on the palette are as distinguishable as possible. Again, there is a `Fast`
method which works in HSV and is less perceptually uniform and a non-`Fast`
method which works in CIE spaces. For more theory on `SoftPalette`, check out
[I want hue](http://tools.medialab.sciences-po.fr/iwanthue/theory.php). Yet
again, there is a `Happy` and a `Warm` version, which do what you expect, but
now there is an additional `Soft` version, which is more configurable: you can
give a constraint on the color space in order to get colors within a certain *feel*.

Let's start with the simple methods first, all they take is the amount of
colors to generate, which could, for example, be the player count. They return
an array of `colorful.Color` objects:

```go
pal1, err1 := colorful.WarmPalette(10)
pal2 := colorful.FastWarmPalette(10)
pal3, err3 := colorful.HappyPalette(10)
pal4 := colorful.FastHappyPalette(10)
pal5, err5 := colorful.SoftPalette(10)
```

Note that the non-fast methods *may* fail if you ask for way too many colors.
Let's move on to the advanced one, namely `SoftPaletteEx`. Besides the color
count, this function takes a `SoftPaletteSettings` object as argument. The
interesting part here is its `CheckColor` member, which is a boolean function
taking three floating points as arguments: `l`, `a` and `b`. This function
should return `true` for colors which lie within the region you want and `false`
otherwise. The other members are `Iteration`, which should be within [5..100]
where higher means slower but more exact palette, and `ManySamples` which you
should set to `true` in case your `CheckColor` constraint rejects a large part
of the color space.

For example, to create a palette of 10 brownish colors, you'd call it like this:

```go
func isbrowny(l, a, b float64) bool {
    h, c, L := colorful.LabToHcl(l, a, b)
    return 10.0 < h && h < 50.0 && 0.1 < c && c < 0.5 && L < 0.5
}
// Since the above function is pretty restrictive, we set ManySamples to true.
brownies := colorful.SoftPaletteEx(10, colorful.SoftPaletteSettings{isbrowny, 50, true})
```

The following picture shows the palettes generated by all of these methods
(sourcecode in `doc/palettegens/palettegens.go`), in the order they were presented, i.e.
from top to bottom: `Warm`, `FastWarm`, `Happy`, `FastHappy`, `Soft`,
`SoftEx(isbrowny)`. All of them contain some randomness, so YMMV.



Again, the code used for generating the above image is available as [doc/palettegens/palettegens.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/palettegens/palettegens.go).

### Sorting colors

Sorting colors is not a well-defined operation.  For example, {dark blue, dark red, light blue, light red} is already sorted if darker colors should precede lighter colors but would need to be re-sorted as {dark red, light red, dark blue, light blue} if longer-wavelength colors should precede shorter-wavelength colors.

Go-Colorful's `Sorted` function orders a list of colors so as to minimize the average distance between adjacent colors, including between the last and the first.  (`Sorted` does not necessarily find the true minimum, only a reasonably close approximation.)  The following picture, drawn by [doc/colorsort/colorsort.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/colorsort/colorsort.go), illustrates `Sorted`'s behavior:



The first row represents the input: a slice of 512 randomly chosen colors.  The second row shows the colors sorted in CIE-L\*C\*h° space, ordered first by lightness (L), then by hue angle (h), and finally by chroma (C).  Note that distracting pinstripes permeate the colors.  Sorting using *any* color space and *any* ordering of the channels yields a similar pinstriped pattern.  The third row of the image was sorted using Go-Colorful's `Sorted` function.  Although the colors do not appear to be in any particular order, the sequence at least appears smoother than the one sorted by channel.


### Using linear RGB for computations
There are two methods for transforming RGB⟷Linear RGB: a fast and almost precise one,
and a slow and precise one.

```go
r, g, b := colorful.Hex("#FF0000").FastLinearRgb()
```

TODO: describe some more.

### Want to use some other reference point?

```go
c := colorful.LabWhiteRef(0.507850, 0.040585,-0.370945, colorful.D50)
l, a, b := c.LabWhiteRef(colorful.D50)
```

### Reading and writing colors from databases

The type `HexColor` makes it easy to store colors as strings in a database. It
implements the [https://godoc.org/database/sql#Scanner](database/sql.Scanner)
and [database/sql/driver.Value](https://godoc.org/database/sql/driver.Value)
interfaces which provide automatic type conversion.

Example:

```go
var hc HexColor
_, err := db.QueryRow("SELECT '#ff0000';").Scan(&hc)
// hc == HexColor{R: 1, G: 0, B: 0}; err == nil
```

FAQ
===

### Q: I get all f!@#ed up values! Your library sucks!
A: You probably provided values in the wrong range. For example, RGB values are
expected to reside between 0 and 1, *not* between 0 and 255. Normalize your colors.

### Q: Lab/Luv/HCl seem broken! Your library sucks!
They look like this:

<img height="150" src="https://user-images.githubusercontent.com/3779568/28646900-6548040c-7264-11e7-8f12-81097a97c260.png">

A: You're likely trying to generate and display colors that can't be represented by RGB,
and thus monitors. When you're trying to convert, say, `HCL(190.0, 1.0, 1.0).RGB255()`,
you're asking for RGB values of `(-2105.254  300.680  286.185)`, which clearly don't exist,
and the `RGB255` function just casts these numbers to `uint8`, creating wrap-around and
what looks like a completely broken gradient. What you want to do, is either use more
reasonable values of colors which actually exist in RGB, or just `Clamp()` the resulting
color to its nearest existing one, living with the consequences:
`HCL(190.0, 1.0, 1.0).Clamp().RGB255()`. It will look something like this:

<img height="150" src="https://user-images.githubusercontent.com/1476029/29596343-9a8c62c6-8771-11e7-9026-b8eb8852cc4a.png">

[Here's an issue going in-depth about this](https://github.com/lucasb-eyer/go-colorful/issues/14),
as well as [my answer](https://github.com/lucasb-eyer/go-colorful/issues/14#issuecomment-324205385),
both with code and pretty pictures. Also note that this was somewhat covered above in the
["Blending colors" section](https://github.com/lucasb-eyer/go-colorful#blending-colors).

### Q: In a tight loop, conversion to Lab/Luv/HCl/... are slooooow!
A: Yes, they are.
This library aims for correctness, readability, and modularity; it wasn't written with speed in mind.
A large part of the slowness comes from these conversions going through `LinearRgb` which uses powers.
I implemented a fast approximation to `LinearRgb` called `FastLinearRgb` by using Taylor approximations.
The approximation is roughly 5x faster and precise up to roughly 0.5%,
the major caveat being that if the input values are outside the range 0-1, accuracy drops dramatically.
You can use these in your conversions as follows:

```go
col := // Get your color somehow
l, a, b := XyzToLab(LinearRgbToXyz(col.LinearRgb()))
```

If you need faster versions of `Distance*` and `Blend*` that make use of this fast approximation,
feel free to implement them and open a pull-request, I'll happily accept.

The derivation of these functions can be followed in [this Jupyter notebook](doc/LinearRGB Approximations.ipynb).
Here's the main figure showing the approximation quality:



More speed could be gained by using SIMD instructions in many places.
You can also get more speed for specific conversions by approximating the full conversion function,
but that is outside the scope of this library.
Thanks to [@ZirconiumX](https://github.com/ZirconiumX) for starting this investigation,
see [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18) for details.

### Q: Why would `MakeColor` ever fail!?
A: `MakeColor` fails when the alpha channel is zero. In that case, the
conversion is undefined. See [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21)
as well as the short caveat note in the ["The `color.Color` interface"](README.md#the-colorcolor-interface)
section above.

Who?
====

This library was developed by Lucas Beyer with contributions from
Bastien Dejean (@baskerville), Phil Kulak (@pkulak), Christian Muehlhaeuser (@muesli), and Scott Pakin (@spakin).

It is now maintained by makeworld (@makew0rld).


## License

This repo is under the MIT license, see [LICENSE](LICENSE) for details.

---

### Overview (`vendor/github.com/mattn/go-isatty/README.md`)

# go-isatty

[](http://godoc.org/github.com/mattn/go-isatty)
[](https://codecov.io/gh/mattn/go-isatty)
[](https://coveralls.io/github/mattn/go-isatty?branch=master)
[](https://goreportcard.com/report/mattn/go-isatty)

isatty for golang

## Usage

```go
package main

import (
	"fmt"
	"github.com/mattn/go-isatty"
	"os"
)

func main() {
	if isatty.IsTerminal(os.Stdout.Fd()) {
		fmt.Println("Is Terminal")
	} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
		fmt.Println("Is Cygwin/MSYS2 Terminal")
	} else {
		fmt.Println("Is Not Terminal")
	}
}
```

## Installation

```
$ go get github.com/mattn/go-isatty
```

## License

MIT

## Author

Yasuhiro Matsumoto (a.k.a mattn)

## Thanks

* k-takata: base idea for IsCygwinTerminal

    https://github.com/k-takata/go-iscygpty

---

### Overview (`vendor/github.com/mattn/go-localereader/README.md`)

# go-localereader

CodePage decoder for Windows

## Usage

```
io.Copy(os.Stdout, localereader.NewAcpReader(bytes.Reader(bytesSjis)))
```

## Installation

```
$ go get github.com/mattn/go-localereader
```

## License

MIT

## Author

Yasuhiro Matsumoto (a.k.a. mattn)

---

### Overview (`vendor/github.com/mattn/go-runewidth/README.md`)

go-runewidth
============

[](https://github.com/mattn/go-runewidth/actions?query=workflow%3Atest)
[](https://codecov.io/gh/mattn/go-runewidth)
[](http://godoc.org/github.com/mattn/go-runewidth)
[](https://goreportcard.com/report/github.com/mattn/go-runewidth)

Provides functions to get fixed width of the character or string.

Usage
-----

```go
runewidth.StringWidth("つのだ☆HIRO") == 12
```


Author
------

Yasuhiro Matsumoto

License
-------

under the MIT License: http://mattn.mit-license.org/2013

---

### Overview (`vendor/github.com/muesli/ansi/README.md`)

# ansi

[](https://github.com/muesli/ansi/releases)
[](https://github.com/muesli/ansi/actions)
[](https://coveralls.io/github/muesli/ansi?branch=master)
[](https://goreportcard.com/report/muesli/ansi)
[](https://pkg.go.dev/github.com/muesli/ansi)

Raw ANSI sequence helpers

## ANSI Writer

```go
import "github.com/muesli/ansi"

w := ansi.Writer{Forward: os.Stdout}
w.Write([]byte("\x1b[31mHello, world!\x1b[0m"))
w.Close()
```

## Compressor

The ANSI compressor eliminates unnecessary/redundant ANSI sequences.

```go
import "github.com/muesli/ansi/compressor"

w := compressor.Writer{Forward: os.Stdout}
w.Write([]byte("\x1b[31mHello, world!\x1b[0m"))
w.Close()
```

---

### Overview (`vendor/github.com/muesli/cancelreader/README.md`)

# CancelReader

[](https://github.com/muesli/cancelreader/releases)
[](https://pkg.go.dev/github.com/muesli/cancelreader)
[](/LICENSE)
[](https://github.com/muesli/cancelreader/actions)
[](https://goreportcard.com/report/muesli/cancelreader)

A cancelable reader for Go

This package is based on the fantastic work of [Erik Geiser](https://github.com/erikgeiser)
in Charm's [Bubble Tea](https://github.com/charmbracelet/bubbletea) framework.

## Usage

`NewReader` returns a reader with a `Cancel` function. If the input reader is a
`File`, the cancel function can be used to interrupt a blocking `Read` call.
In this case, the cancel function returns true if the call was canceled
successfully. If the input reader is not a `File`, the cancel function does
nothing and always returns false.

```go
r, err := cancelreader.NewReader(file)
if err != nil {
    // handle error
    ...
}

// cancel after five seconds
go func() {
    time.Sleep(5 * time.Second)
    r.Cancel()
}()

// keep reading
for {
    var buf [1024]byte
    _, err := r.Read(buf[:])

    if errors.Is(err, cancelreader.ErrCanceled) {
        fmt.Println("canceled!")
        break
    }
    if err != nil {
        // handle other errors
        ...
    }

    // handle data
    ...
}
```

## Implementations

- The Linux implementation is based on the epoll mechanism
- The BSD and macOS implementation is based on the kqueue mechanism
- The generic Unix implementation is based on the posix select syscall

## Caution

The Windows implementation is based on WaitForMultipleObject with overlapping
reads from CONIN$. At this point it only supports canceling reads from
`os.Stdin`.

---

### Overview (`vendor/github.com/muesli/termenv/README.md`)

<p align="center">
    <img src="https://stuff.charm.sh/termenv.png" width="480" alt="termenv Logo">
    <br />
    <a href="https://github.com/muesli/termenv/releases"><img src="https://img.shields.io/github/release/muesli/termenv.svg" alt="Latest Release"></a>
    <a href="https://godoc.org/github.com/muesli/termenv"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
    <a href="https://github.com/muesli/termenv/actions"><img src="https://github.com/muesli/termenv/workflows/build/badge.svg" alt="Build Status"></a>
    <a href="https://coveralls.io/github/muesli/termenv?branch=master"><img src="https://coveralls.io/repos/github/muesli/termenv/badge.svg?branch=master" alt="Coverage Status"></a>
    <a href="https://goreportcard.com/report/muesli/termenv"><img src="https://goreportcard.com/badge/muesli/termenv" alt="Go ReportCard"></a>
    <br />
    <img src="https://github.com/muesli/termenv/raw/master/examples/hello-world/hello-world.png" alt="Example terminal output">
</p>

`termenv` lets you safely use advanced styling options on the terminal. It
gathers information about the terminal environment in terms of its ANSI & color
support and offers you convenient methods to colorize and style your output,
without you having to deal with all kinds of weird ANSI escape sequences and
color conversions.

## Features

- RGB/TrueColor support
- Detects the supported color range of your terminal
- Automatically converts colors to the best matching, available colors
- Terminal theme (light/dark) detection
- Chainable syntax
- Nested styles

## Installation

```bash
go get github.com/muesli/termenv
```

## Usage

```go
output := termenv.NewOutput(os.Stdout)
```

`termenv` queries the terminal's capabilities it is running in, so you can
safely use advanced features, like RGB colors or ANSI styles. `output.Profile`
returns the supported profile:

- `termenv.Ascii` - no ANSI support detected, ASCII only
- `termenv.ANSI` - 16 color ANSI support
- `termenv.ANSI256` - Extended 256 color ANSI support
- `termenv.TrueColor` - RGB/TrueColor support

Alternatively, you can use `termenv.EnvColorProfile` which evaluates the
terminal like `ColorProfile`, but also respects the `NO_COLOR` and
`CLICOLOR_FORCE` environment variables.

You can also query the terminal for its color scheme, so you know whether your
app is running in a light- or dark-themed environment:

```go
// Returns terminal's foreground color
color := output.ForegroundColor()

// Returns terminal's background color
color := output.BackgroundColor()

// Returns whether terminal uses a dark-ish background
darkTheme := output.HasDarkBackground()
```

### Manual Profile Selection

If you don't want to rely on the automatic detection, you can manually select
the profile you want to use:

```go
output := termenv.NewOutput(os.Stdout, termenv.WithProfile(termenv.TrueColor))
```

## Colors

`termenv` supports multiple color profiles: Ascii (black & white only),
ANSI (16 colors), ANSI Extended (256 colors), and TrueColor (24-bit RGB). Colors
will automatically be degraded to the best matching available color in the
desired profile:

`TrueColor` => `ANSI 256 Colors` => `ANSI 16 Colors` => `Ascii`

```go
s := output.String("Hello World")

// Supports hex values
// Will automatically degrade colors on terminals not supporting RGB
s.Foreground(output.Color("#abcdef"))
// but also supports ANSI colors (0-255)
s.Background(output.Color("69"))
// ...or the color.Color interface
s.Foreground(output.FromColor(color.RGBA{255, 128, 0, 255}))

// Combine fore- & background colors
s.Foreground(output.Color("#ffffff")).Background(output.Color("#0000ff"))

// Supports the fmt.Stringer interface
fmt.Println(s)
```

## Styles

You can use a chainable syntax to compose your own styles:

```go
s := output.String("foobar")

// Text styles
s.Bold()
s.Faint()
s.Italic()
s.CrossOut()
s.Underline()
s.Overline()

// Reverse swaps current fore- & background colors
s.Reverse()

// Blinking text
s.Blink()

// Combine multiple options
s.Bold().Underline()
```

## Template Helpers

`termenv` provides a set of helper functions to style your Go templates:

```go
// load template helpers
f := output.TemplateFuncs()
tpl := template.New("tpl").Funcs(f)

// apply bold style in a template
bold := `{{ Bold "Hello World" }}`

// examples for colorized templates
col := `{{ Color "#ff0000" "#0000ff" "Red on Blue" }}`
fg := `{{ Foreground "#ff0000" "Red Foreground" }}`
bg := `{{ Background "#0000ff" "Blue Background" }}`

// wrap styles
wrap := `{{ Bold (Underline "Hello World") }}`

// parse and render
tpl, err = tpl.Parse(bold)

var buf bytes.Buffer
tpl.Execute(&buf, nil)
fmt.Println(&buf)
```

Other available helper functions are: `Faint`, `Italic`, `CrossOut`,
`Underline`, `Overline`, `Reverse`, and `Blink`.

## Positioning

```go
// Move the cursor to a given position
output.MoveCursor(row, column)

// Save the cursor position
output.SaveCursorPosition()

// Restore a saved cursor position
output.RestoreCursorPosition()

// Move the cursor up a given number of lines
output.CursorUp(n)

// Move the cursor down a given number of lines
output.CursorDown(n)

// Move the cursor up a given number of lines
output.CursorForward(n)

// Move the cursor backwards a given number of cells
output.CursorBack(n)

// Move the cursor down a given number of lines and place it at the beginning
// of the line
output.CursorNextLine(n)

// Move the cursor up a given number of lines and place it at the beginning of
// the line
output.CursorPrevLine(n)
```

## Screen

```go
// Reset the terminal to its default style, removing any active styles
output.Reset()

// RestoreScreen restores a previously saved screen state
output.RestoreScreen()

// SaveScreen saves the screen state
output.SaveScreen()

// Switch to the altscreen. The former view can be restored with ExitAltScreen()
output.AltScreen()

// Exit the altscreen and return to the former terminal view
output.ExitAltScreen()

// Clear the visible portion of the terminal
output.ClearScreen()

// Clear the current line
output.ClearLine()

// Clear a given number of lines
output.ClearLines(n)

// Set the scrolling region of the terminal
output.ChangeScrollingRegion(top, bottom)

// Insert the given number of lines at the top of the scrollable region, pushing
// lines below down
output.InsertLines(n)

// Delete the given number of lines, pulling any lines in the scrollable region
// below up
output.DeleteLines(n)
```

## Session

```go
// SetWindowTitle sets the terminal window title
output.SetWindowTitle(title)

// SetForegroundColor sets the default foreground color
output.SetForegroundColor(color)

// SetBackgroundColor sets the default background color
output.SetBackgroundColor(color)

// SetCursorColor sets the cursor color
output.SetCursorColor(color)

// Hide the cursor
output.HideCursor()

// Show the cursor
output.ShowCursor()

// Copy to clipboard
output.Copy(message)

// Copy to primary clipboard (X11)
output.CopyPrimary(message)

// Trigger notification
output.Notify(title, body)
```

## Mouse

```go
// Enable X10 mouse mode, only button press events are sent
output.EnableMousePress()

// Disable X10 mouse mode
output.DisableMousePress()

// Enable Mouse Tracking mode
output.EnableMouse()

// Disable Mouse Tracking mode
output.DisableMouse()

// Enable Hilite Mouse Tracking mode
output.EnableMouseHilite()

// Disable Hilite Mouse Tracking mode
output.DisableMouseHilite()

// Enable Cell Motion Mouse Tracking mode
output.EnableMouseCellMotion()

// Disable Cell Motion Mouse Tracking mode
output.DisableMouseCellMotion()

// Enable All Motion Mouse mode
output.EnableMouseAllMotion()

// Disable All Motion Mouse mode
output.DisableMouseAllMotion()
```

## Bracketed Paste

```go
// Enables bracketed paste mode
termenv.EnableBracketedPaste()

// Disables bracketed paste mode
termenv.DisableBracketedPaste()
```

## Terminal Feature Support

### Color Support

- 24-bit (RGB): alacritty, foot, iTerm, kitty, Konsole, st, tmux, vte-based, wezterm, Ghostty, Windows Terminal
- 8-bit (256): rxvt, screen, xterm, Apple Terminal
- 4-bit (16): Linux Console

### Control Sequences

<details>
<summary>Click to show feature matrix</summary>

| Terminal         | Query Color Scheme | Query Cursor Position | Set Window Title | Change Cursor Color | Change Default Foreground Setting | Change Default Background Setting | Bracketed Paste | Extended Mouse (SGR) | Pixels Mouse (SGR-Pixels) |
| ---------------- | :----------------: | :-------------------: | :--------------: | :-----------------: | :-------------------------------: | :-------------------------------: | :-------------: | :------------------: | :-----------------------: |
| alacritty        |         ✅         |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ❌             |
| foot             |         ✅         |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ✅             |
| kitty            |         ✅         |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ✅             |
| Konsole          |         ✅         |          ✅           |        ✅        |         ❌          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ❌             |
| rxvt             |         ❌         |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ✅        |          ❌          |            ❌             |
| urxvt            |         ❌         |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ❌             |
| screen           |      ⛔[^mux]      |          ✅           |        ✅        |         ❌          |                ❌                 |                ✅                 |       ❌        |          ❌          |            ❌             |
| st               |         ✅         |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ❌             |
| tmux             |      ⛔[^mux]      |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ❌             |
| vte-based[^vte]  |         ✅         |          ✅           |        ✅        |         ✅          |                ✅                 |                ❌                 |       ✅        |          ✅          |            ❌             |
| wezterm          |         ✅         |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ✅             |
| xterm            |         ✅         |          ✅           |        ✅        |         ❌          |                ❌                 |                ❌                 |       ✅        |          ✅          |            ❌             |
| Linux Console    |         ❌         |          ✅           |        ⛔        |         ❌          |                ❌                 |                ❌                 |       ❌        |          ❌          |            ❌             |
| Apple Terminal   |         ✅         |          ✅           |        ✅        |         ❌          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ❌             |
| iTerm            |         ✅         |          ✅           |        ✅        |         ❌          |                ❌                 |                ❌                 |       ✅        |          ✅          |            ❌             |
| Windows cmd      |         ❌         |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ❌        |          ❌          |            ❌             |
| Windows Terminal |         ❌         |          ✅           |        ✅        |         ✅          |                ✅                 |                ✅                 |       ✅        |          ✅          |            ❌             |

[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal.
[^mux]: Unavailable as multiplexers (like tmux or screen) can be connected to multiple terminals (with different color settings) at the same time.

You can help improve this list! Check out [how to](ansi_compat.md) and open an issue or pull request.

</details>

### System Commands

<details>
<summary>Click to show feature matrix</summary>

| Terminal         | Copy to Clipboard (OSC52) | Hyperlinks (OSC8) | Notifications (OSC777) |
| ---------------- | :-----------------------: | :---------------: | :--------------------: |
| alacritty        |            ✅             |  ✅[^alacritty]   |           ❌           |
| foot             |            ✅             |        ✅         |           ✅           |
| kitty            |            ✅             |        ✅         |           ✅           |
| Konsole          |       ❌[^konsole]        |        ✅         |           ❌           |
| rxvt             |            ❌             |        ❌         |           ❌           |
| urxvt            |        ✅[^urxvt]         |        ❌         |           ✅           |
| screen           |            ✅             |    ❌[^screen]    |           ❌           |
| st               |            ✅             |        ❌         |           ❌           |
| tmux             |            ✅             |     ❌[^tmux]     |           ❌           |
| vte-based[^vte]  |         ❌[^vte]          |        ✅         |           ❌           |
| wezterm          |            ✅             |        ✅         |           ❌           |
| xterm            |            ✅             |        ❌         |           ❌           |
| Linux Console    |            ⛔             |        ⛔         |           ❌           |
| Apple Terminal   |        ✅[^apple]         |        ❌         |           ❌           |
| iTerm            |            ✅             |        ✅         |           ❌           |
| Windows cmd      |            ❌             |        ❌         |           ❌           |
| Windows Terminal |            ✅             |        ✅         |           ❌           |

[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal. OSC52 is not supported, see [issue#2495](https://gitlab.gnome.org/GNOME/vte/-/issues/2495).
[^urxvt]: Workaround for urxvt not supporting OSC52. See [this](https://unix.stackexchange.com/a/629485) for more information.
[^konsole]: OSC52 is not supported, for more info see [bug#372116](https://bugs.kde.org/show_bug.cgi?id=372116).
[^apple]: OSC52 works with a [workaround](https://github.com/roy2220/osc52pty).
[^tmux]: OSC8 is not supported, for more info see [issue#911](https://github.com/tmux/tmux/issues/911).
[^screen]: OSC8 is not supported, for more info see [bug#50952](https://savannah.gnu.org/bugs/index.php?50952).
[^alacritty]: OSC8 is supported since [v0.11.0](https://github.com/alacritty/alacritty/releases/tag/v0.11.0)

</details>

## Platform Support

`termenv` works on Unix systems (like Linux, macOS, or BSD) and Windows. While
terminal applications on Unix support ANSI styling out-of-the-box, on Windows
you need to enable ANSI processing in your application first:

```go
    restoreConsole, err := termenv.EnableVirtualTerminalProcessing(termenv.DefaultOutput())
    if err != nil {
        panic(err)
    }
    defer restoreConsole()
```

The above code is safe to include on non-Windows systems or when os.Stdout does
not refer to a terminal (e.g. in tests).

## Color Chart



You can find the source code used to create this chart in `termenv`'s examples.

## Related Projects

- [reflow](https://github.com/muesli/reflow) - ANSI-aware text operations
- [Lip Gloss](https://github.com/charmbracelet/lipgloss) - style definitions for nice terminal layouts 👄
- [ansi](https://github.com/muesli/ansi) - ANSI sequence helpers

## termenv in the Wild

Need some inspiration or just want to see how others are using `termenv`? Check
out these projects:

- [Bubble Tea](https://github.com/charmbracelet/bubbletea) - a powerful little TUI framework 🏗
- [Glamour](https://github.com/charmbracelet/glamour) - stylesheet-based markdown rendering for your CLI apps 💇🏻‍♀️
- [Glow](https://github.com/charmbracelet/glow) - a markdown renderer for the command-line 💅🏻
- [duf](https://github.com/muesli/duf) - Disk Usage/Free Utility - a better 'df' alternative
- [gitty](https://github.com/muesli/gitty) - contextual information about your git projects
- [slides](https://github.com/maaslalani/slides) - terminal-based presentation tool

## Feedback

Got some feedback or suggestions? Please open an issue or drop me a note!

- [Twitter](https://twitter.com/mueslix)
- [The Fediverse](https://mastodon.social/@fribbledom)

## License

[MIT](https://github.com/muesli/termenv/raw/master/LICENSE)

---

### Overview (`vendor/github.com/rivo/uniseg/README.md`)

# Unicode Text Segmentation for Go

[](https://pkg.go.dev/github.com/rivo/uniseg)
[](https://goreportcard.com/report/github.com/rivo/uniseg)

This Go package implements Unicode Text Segmentation according to [Unicode Standard Annex #29](https://unicode.org/reports/tr29/), Unicode Line Breaking according to [Unicode Standard Annex #14](https://unicode.org/reports/tr14/) (Unicode version 15.0.0), and monospace font string width calculation similar to [wcwidth](https://man7.org/linux/man-pages/man3/wcwidth.3.html).

## Background

### Grapheme Clusters

In Go, [strings are read-only slices of bytes](https://go.dev/blog/strings). They can be turned into Unicode code points using the `for` loop or by casting: `[]rune(str)`. However, multiple code points may be combined into one user-perceived character or what the Unicode specification calls "grapheme cluster". Here are some examples:

|String|Bytes (UTF-8)|Code points (runes)|Grapheme clusters|
|-|-|-|-|
|Käse|6 bytes: `4b 61 cc 88 73 65`|5 code points: `4b 61 308 73 65`|4 clusters: `[4b],[61 308],[73],[65]`|
|🏳️‍🌈|14 bytes: `f0 9f 8f b3 ef b8 8f e2 80 8d f0 9f 8c 88`|4 code points: `1f3f3 fe0f 200d 1f308`|1 cluster: `[1f3f3 fe0f 200d 1f308]`|
|🇩🇪|8 bytes: `f0 9f 87 a9 f0 9f 87 aa`|2 code points: `1f1e9 1f1ea`|1 cluster: `[1f1e9 1f1ea]`|

This package provides tools to iterate over these grapheme clusters. This may be used to determine the number of user-perceived characters, to split strings in their intended places, or to extract individual characters which form a unit.

### Word Boundaries

Word boundaries are used in a number of different contexts. The most familiar ones are selection (double-click mouse selection), cursor movement ("move to next word" control-arrow keys), and the dialog option "Whole Word Search" for search and replace. They are also used in database queries, to determine whether elements are within a certain number of words of one another. Searching may also use word boundaries in determining matching items. This package provides tools to determine word boundaries within strings.

### Sentence Boundaries

Sentence boundaries are often used for triple-click or some other method of selecting or iterating through blocks of text that are larger than single words. They are also used to determine whether words occur within the same sentence in database queries. This package provides tools to determine sentence boundaries within strings.

### Line Breaking

Line breaking, also known as word wrapping, is the process of breaking a section of text into lines such that it will fit in the available width of a page, window or other display area. This package provides tools to determine where a string may or may not be broken and where it must be broken (for example after newline characters).

### Monospace Width

Most terminals or text displays / text editors using a monospace font (for example source code editors) use a fixed width for each character. Some characters such as emojis or characters found in Asian and other languages may take up more than one character cell. This package provides tools to determine the number of cells a string will take up when displayed in a monospace font. See [here](https://pkg.go.dev/github.com/rivo/uniseg#hdr-Monospace_Width) for more information.

## Installation

```bash
go get github.com/rivo/uniseg
```

## Examples

### Counting Characters in a String

```go
n := uniseg.GraphemeClusterCount("🇩🇪🏳️‍🌈")
fmt.Println(n)
// 2
```

### Calculating the Monospace String Width

```go
width := uniseg.StringWidth("🇩🇪🏳️‍🌈!")
fmt.Println(width)
// 5
```

### Using the [`Graphemes`](https://pkg.go.dev/github.com/rivo/uniseg#Graphemes) Class

This is the most convenient method of iterating over grapheme clusters:

```go
gr := uniseg.NewGraphemes("👍🏼!")
for gr.Next() {
	fmt.Printf("%x ", gr.Runes())
}
// [1f44d 1f3fc] [21]
```

### Using the [`Step`](https://pkg.go.dev/github.com/rivo/uniseg#Step) or [`StepString`](https://pkg.go.dev/github.com/rivo/uniseg#StepString) Function

This avoids allocating a new `Graphemes` object but it requires the handling of states and boundaries:

```go
str := "🇩🇪🏳️‍🌈"
state := -1
var c string
for len(str) > 0 {
	c, str, _, state = uniseg.StepString(str, state)
	fmt.Printf("%x ", []rune(c))
}
// [1f1e9 1f1ea] [1f3f3 fe0f 200d 1f308]
```

### Advanced Examples

The [`Graphemes`](https://pkg.go.dev/github.com/rivo/uniseg#Graphemes) class offers the most convenient way to access all functionality of this package. But in some cases, it may be better to use the specialized functions directly. For example, if you're only interested in word segmentation, use [`FirstWord`](https://pkg.go.dev/github.com/rivo/uniseg#FirstWord) or [`FirstWordInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstWordInString):

```go
str := "Hello, world!"
state := -1
var c string
for len(str) > 0 {
	c, str, state = uniseg.FirstWordInString(str, state)
	fmt.Printf("(%s)\n", c)
}
// (Hello)
// (,)
// ( )
// (world)
// (!)
```

Similarly, use

- [`FirstGraphemeCluster`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeCluster) or [`FirstGraphemeClusterInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeClusterInString) for grapheme cluster determination only,
- [`FirstSentence`](https://pkg.go.dev/github.com/rivo/uniseg#FirstSentence) or [`FirstSentenceInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstSentenceInString) for sentence segmentation only, and
- [`FirstLineSegment`](https://pkg.go.dev/github.com/rivo/uniseg#FirstLineSegment) or [`FirstLineSegmentInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstLineSegmentInString) for line breaking / word wrapping (although using [`Step`](https://pkg.go.dev/github.com/rivo/uniseg#Step) or [`StepString`](https://pkg.go.dev/github.com/rivo/uniseg#StepString) is preferred as it will observe grapheme cluster boundaries).

If you're only interested in the width of characters, use [`FirstGraphemeCluster`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeCluster) or [`FirstGraphemeClusterInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeClusterInString). It is much faster than using [`Step`](https://pkg.go.dev/github.com/rivo/uniseg#Step), [`StepString`](https://pkg.go.dev/github.com/rivo/uniseg#StepString), or the [`Graphemes`](https://pkg.go.dev/github.com/rivo/uniseg#Graphemes) class because it does not include the logic for word / sentence / line boundaries.

Finally, if you need to reverse a string while preserving grapheme clusters, use [`ReverseString`](https://pkg.go.dev/github.com/rivo/uniseg#ReverseString):

```go
fmt.Println(uniseg.ReverseString("🇩🇪🏳️‍🌈"))
// 🏳️‍🌈🇩🇪
```

## Documentation

Refer to https://pkg.go.dev/github.com/rivo/uniseg for the package's documentation.

## Dependencies

This package does not depend on any packages outside the standard library.

## Sponsor this Project

[Become a Sponsor on GitHub](https://github.com/sponsors/rivo?metadata_source=uniseg_readme) to support this project!

## Your Feedback

Add your issue here on GitHub, preferably before submitting any PR's. Feel free to get in touch if you have any questions.

---

### Overview (`vendor/github.com/spf13/cobra/README.md`)

<div align="center">
<a href="https://cobra.dev">
<img width="512" height="535" alt="cobra-logo" src="https://github.com/user-attachments/assets/c8bf9aad-b5ae-41d3-8899-d83baec10af8" />
</a>
</div>

Cobra is a library for creating powerful modern CLI applications.

<a href="https://cobra.dev">Visit Cobra.dev for extensive documentation</a> 


Cobra is used in many Go projects such as [Kubernetes](https://kubernetes.io/),
[Hugo](https://gohugo.io), and [GitHub CLI](https://github.com/cli/cli) to
name a few. [This list](site/content/projects_using_cobra.md) contains a more extensive list of projects using Cobra.

[](https://github.com/spf13/cobra/actions?query=workflow%3ATest)
[](https://pkg.go.dev/github.com/spf13/cobra)
[](https://goreportcard.com/report/github.com/spf13/cobra)
[](https://gophers.slack.com/archives/CD3LP1199)
<hr>
<div align="center" markdown="1">
   <sup>Supported by:</sup>
   <br>
   <br>
   <a href="https://www.warp.dev/cobra">
      <img alt="Warp sponsorship" width="400" src="https://github.com/user-attachments/assets/ab8dd143-b0fd-4904-bdc5-dd7ecac94eae">
   </a>

### [Warp, the AI terminal for devs](https://www.warp.dev/cobra)
[Try Cobra in Warp today](https://www.warp.dev/cobra)<br>

</div>
<hr>

# Overview

Cobra is a library providing a simple interface to create powerful modern CLI
interfaces similar to git & go tools.

Cobra provides:
* Easy subcommand-based CLIs: `app server`, `app fetch`, etc.
* Fully POSIX-compliant flags (including short & long versions)
* Nested subcommands
* Global, local and cascading flags
* Intelligent suggestions (`app srver`... did you mean `app server`?)
* Automatic help generation for commands and flags
* Grouping help for subcommands
* Automatic help flag recognition of `-h`, `--help`, etc.
* Automatically generated shell autocomplete for your application (bash, zsh, fish, powershell)
* Automatically generated man pages for your application
* Command aliases so you can change things without breaking them
* The flexibility to define your own help, usage, etc.
* Optional seamless integration with [viper](https://github.com/spf13/viper) for 12-factor apps

# Concepts

Cobra is built on a structure of commands, arguments & flags.

**Commands** represent actions, **Args** are things and **Flags** are modifiers for those actions.

The best applications read like sentences when used, and as a result, users
intuitively know how to interact with them.

The pattern to follow is
`APPNAME VERB NOUN --ADJECTIVE`
    or
`APPNAME COMMAND ARG --FLAG`.

A few good real world examples may better illustrate this point.

In the following example, 'server' is a command, and 'port' is a flag:

    hugo server --port=1313

In this command we are telling Git to clone the url bare.

    git clone URL --bare

## Commands

Command is the central point of the application. Each interaction that
the application supports will be contained in a Command. A command can
have children commands and optionally run an action.

In the example above, 'server' is the command.

[More about cobra.Command](https://pkg.go.dev/github.com/spf13/cobra#Command)

## Flags

A flag is a way to modify the behavior of a command. Cobra supports
fully POSIX-compliant flags as well as the Go [flag package](https://golang.org/pkg/flag/).
A Cobra command can define flags that persist through to children commands
and flags that are only available to that command.

In the example above, 'port' is the flag.

Flag functionality is provided by the [pflag
library](https://github.com/spf13/pflag), a fork of the flag standard library
which maintains the same interface while adding POSIX compliance.

# Installing
Using Cobra is easy. First, use `go get` to install the latest version
of the library.

```
go get -u github.com/spf13/cobra@latest
```

Next, include Cobra in your application:

```go
import "github.com/spf13/cobra"
```

# Usage
`cobra-cli` is a command line program to generate cobra applications and command files.
It will bootstrap your application scaffolding to rapidly
develop a Cobra-based application. It is the easiest way to incorporate Cobra into your application.

It can be installed by running:

```
go install github.com/spf13/cobra-cli@latest
```

For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md)

For complete details on using the Cobra library, please read [The Cobra User Guide](site/content/user_guide.md).

# License

Cobra is released under the Apache 2.0 license. See [LICENSE.txt](LICENSE.txt)

---

### Overview (`vendor/github.com/spf13/pflag/README.md`)

[](https://travis-ci.org/spf13/pflag)
[](https://goreportcard.com/report/github.com/spf13/pflag)
[](https://godoc.org/github.com/spf13/pflag)

## Description

pflag is a drop-in replacement for Go's flag package, implementing
POSIX/GNU-style --flags.

pflag is compatible with the [GNU extensions to the POSIX recommendations
for command-line options][1]. For a more precise description, see the
"Command-line flag syntax" section below.

[1]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html

pflag is available under the same style of BSD license as the Go language,
which can be found in the LICENSE file.

## Installation

pflag is available using the standard `go get` command.

Install by running:

    go get github.com/spf13/pflag

Run tests by running:

    go test github.com/spf13/pflag

## Usage

pflag is a drop-in replacement of Go's native flag package. If you import
pflag under the name "flag" then all code should continue to function
with no changes.

``` go
import flag "github.com/spf13/pflag"
```

There is one exception to this: if you directly instantiate the Flag struct
there is one more field "Shorthand" that you will need to set.
Most code never instantiates this struct directly, and instead uses
functions such as String(), BoolVar(), and Var(), and is therefore
unaffected.

Define flags using flag.String(), Bool(), Int(), etc.

This declares an integer flag, -flagname, stored in the pointer ip, with type *int.

``` go
var ip *int = flag.Int("flagname", 1234, "help message for flagname")
```

If you like, you can bind the flag to a variable using the Var() functions.

``` go
var flagvar int
func init() {
    flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
}
```

Or you can create custom flags that satisfy the Value interface (with
pointer receivers) and couple them to flag parsing by

``` go
flag.Var(&flagVal, "name", "help message for flagname")
```

For such flags, the default value is just the initial value of the variable.

After all flags are defined, call

``` go
flag.Parse()
```

to parse the command line into the defined flags.

Flags may then be used directly. If you're using the flags themselves,
they are all pointers; if you bind to variables, they're values.

``` go
fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)
```

There are helper functions available to get the value stored in a Flag if you have a FlagSet but find
it difficult to keep up with all of the pointers in your code.
If you have a pflag.FlagSet with a flag called 'flagname' of type int you
can use GetInt() to get the int value. But notice that 'flagname' must exist
and it must be an int. GetString("flagname") will fail.

``` go
i, err := flagset.GetInt("flagname")
```

After parsing, the arguments after the flag are available as the
slice flag.Args() or individually as flag.Arg(i).
The arguments are indexed from 0 through flag.NArg()-1.

The pflag package also defines some new functions that are not in flag,
that give one-letter shorthands for flags. You can use these by appending
'P' to the name of any function that defines a flag.

``` go
var ip = flag.IntP("flagname", "f", 1234, "help message")
var flagvar bool
func init() {
	flag.BoolVarP(&flagvar, "boolname", "b", true, "help message")
}
flag.VarP(&flagVal, "varname", "v", "help message")
```

Shorthand letters can be used with single dashes on the command line.
Boolean shorthand flags can be combined with other shorthand flags.

The default set of command-line flags is controlled by
top-level functions.  The FlagSet type allows one to define
independent sets of flags, such as to implement subcommands
in a command-line interface. The methods of FlagSet are
analogous to the top-level functions for the command-line
flag set.

## Setting no option default values for flags

After you create a flag it is possible to set the pflag.NoOptDefVal for
the given flag. Doing this changes the meaning of the flag slightly. If
a flag has a NoOptDefVal and the flag is set on the command line without
an option the flag will be set to the NoOptDefVal. For example given:

``` go
var ip = flag.IntP("flagname", "f", 1234, "help message")
flag.Lookup("flagname").NoOptDefVal = "4321"
```

Would result in something like

| Parsed Arguments | Resulting Value |
| -------------    | -------------   |
| --flagname=1357  | ip=1357         |
| --flagname       | ip=4321         |
| [nothing]        | ip=1234         |

## Command line flag syntax

```
--flag    // boolean flags, or flags with no option default values
--flag x  // only on flags without a default value
--flag=x
```

Unlike the flag package, a single dash before an option means something
different than a double dash. Single dashes signify a series of shorthand
letters for flags. All but the last shorthand letter must be boolean flags
or a flag with a default value

```
// boolean or flags where the 'no option default value' is set
-f
-f=true
-abc
but
-b true is INVALID

// non-boolean and flags without a 'no option default value'
-n 1234
-n=1234
-n1234

// mixed
-abcs "hello"
-absd="hello"
-abcs1234
```

Flag parsing stops after the terminator "--". Unlike the flag package,
flags can be interspersed with arguments anywhere on the command line
before this terminator.

Integer flags accept 1234, 0664, 0x1234 and may be negative.
Boolean flags (in their long form) accept 1, 0, t, f, true, false,
TRUE, FALSE, True, False.
Duration flags accept any input valid for time.ParseDuration.

## Mutating or "Normalizing" Flag names

It is possible to set a custom flag name 'normalization function.' It allows flag names to be mutated both when created in the code and when used on the command line to some 'normalized' form. The 'normalized' form is used for comparison. Two examples of using the custom normalization func follow.

**Example #1**: You want -, _, and . in flags to compare the same. aka --my-flag == --my_flag == --my.flag

``` go
func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
	from := []string{"-", "_"}
	to := "."
	for _, sep := range from {
		name = strings.Replace(name, sep, to, -1)
	}
	return pflag.NormalizedName(name)
}

myFlagSet.SetNormalizeFunc(wordSepNormalizeFunc)
```

**Example #2**: You want to alias two flags. aka --old-flag-name == --new-flag-name

``` go
func aliasNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
	switch name {
	case "old-flag-name":
		name = "new-flag-name"
		break
	}
	return pflag.NormalizedName(name)
}

myFlagSet.SetNormalizeFunc(aliasNormalizeFunc)
```

## Deprecating a flag or its shorthand
It is possible to deprecate a flag, or just its shorthand. Deprecating a flag/shorthand hides it from help text and prints a usage message when the deprecated flag/shorthand is used.

**Example #1**: You want to deprecate a flag named "badflag" as well as inform the users what flag they should use instead.
```go
// deprecate a flag by specifying its name and a usage message
flags.MarkDeprecated("badflag", "please use --good-flag instead")
```
This hides "badflag" from help text, and prints `Flag --badflag has been deprecated, please use --good-flag instead` when "badflag" is used.

**Example #2**: You want to keep a flag name "noshorthandflag" but deprecate its shortname "n".
```go
// deprecate a flag shorthand by specifying its flag name and a usage message
flags.MarkShorthandDeprecated("noshorthandflag", "please use --noshorthandflag only")
```
This hides the shortname "n" from help text, and prints `Flag shorthand -n has been deprecated, please use --noshorthandflag only` when the shorthand "n" is used.

Note that usage message is essential here, and it should not be empty.

## Hidden flags
It is possible to mark a flag as hidden, meaning it will still function as normal, however will not show up in usage/help text.

**Example**: You have a flag named "secretFlag" that you need for internal use only and don't want it showing up in help text, or for its usage text to be available.
```go
// hide a flag by specifying its name
flags.MarkHidden("secretFlag")
```

## Disable sorting of flags
`pflag` allows you to disable sorting of flags for help and usage message.

**Example**:
```go
flags.BoolP("verbose", "v", false, "verbose output")
flags.String("coolflag", "yeaah", "it's really cool flag")
flags.Int("usefulflag", 777, "sometimes it's very useful")
flags.SortFlags = false
flags.PrintDefaults()
```
**Output**:
```
  -v, --verbose           verbose output
      --coolflag string   it's really cool flag (default "yeaah")
      --usefulflag int    sometimes it's very useful (default 777)
```


## Supporting Go flags when using pflag
In order to support flags defined using Go's `flag` package, they must be added to the `pflag` flagset. This is usually necessary
to support flags defined by third-party dependencies (e.g. `golang/glog`).

**Example**: You want to add the Go flags to the `CommandLine` flagset
```go
import (
	goflag "flag"
	flag "github.com/spf13/pflag"
)

var ip *int = flag.Int("flagname", 1234, "help message for flagname")

func main() {
	flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
	flag.Parse()
}
```

### Using pflag with go test
`pflag` does not parse the shorthand versions of go test's built-in flags (i.e., those starting with `-test.`).
For more context, see issues [#63](https://github.com/spf13/pflag/issues/63) and [#238](https://github.com/spf13/pflag/issues/238) for more details.

For example, if you use pflag in your `TestMain` function and call `pflag.Parse()` after defining your custom flags, running a test like this:
```bash
go test /your/tests -run ^YourTest -v --your-test-pflags
```
will result in the `-v` flag being ignored. This happens because of the way pflag handles flag parsing, skipping over go test's built-in shorthand flags.
To work around this, you can use the `ParseSkippedFlags` function, which ensures that go test's flags are parsed separately using the standard flag package.

**Example**: You want to parse go test flags that are otherwise ignore by `pflag.Parse()`
```go
import (
	goflag "flag"
	flag "github.com/spf13/pflag"
)

var ip *int = flag.Int("flagname", 1234, "help message for flagname")

func main() {
	flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
    flag.ParseSkippedFlags(os.Args[1:], goflag.CommandLine)
	flag.Parse()
}
```

## More info

You can see the full reference documentation of the pflag package
[at godoc.org][3], or through go's standard documentation system by
running `godoc -http=:6060` and browsing to
[http://localhost:6060/pkg/github.com/spf13/pflag][2] after
installation.

[2]: http://localhost:6060/pkg/github.com/spf13/pflag
[3]: http://godoc.org/github.com/spf13/pflag

---

### Overview (`vendor/github.com/xo/terminfo/README.md`)

# About terminfo [![GoDoc][1]][2]

Package `terminfo` provides a pure-Go implementation of reading information
from the terminfo database.

`terminfo` is meant as a replacement for `ncurses` in simple Go programs.

## Installing

Install in the usual Go way:

```sh
$ go get -u github.com/xo/terminfo
```

## Using

Please see the [GoDoc API listing][2] for more information on using `terminfo`.

```
/* Detailed source-code truncated for AI context efficiency. */
```

[1]: https://godoc.org/github.com/xo/terminfo?status.svg
[2]: https://godoc.org/github.com/xo/terminfo

---

### Overview (`vendor/go.yaml.in/yaml/v3/README.md`)

go.yaml.in/yaml
===============

YAML Support for the Go Language


## Introduction

The `yaml` package enables [Go](https://go.dev/) programs to comfortably encode
and decode [YAML](https://yaml.org/) values.

It was originally developed within [Canonical](https://www.canonical.com) as
part of the [juju](https://juju.ubuntu.com) project, and is based on a pure Go
port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) C library to
parse and generate YAML data quickly and reliably.


## Project Status

This project started as a fork of the extremely popular [go-yaml](
https://github.com/go-yaml/yaml/)
project, and is being maintained by the official [YAML organization](
https://github.com/yaml/).

The YAML team took over ongoing maintenance and development of the project after
discussion with go-yaml's author, @niemeyer, following his decision to
[label the project repository as "unmaintained"](
https://github.com/go-yaml/yaml/blob/944c86a7d2/README.md) in April 2025.

We have put together a team of dedicated maintainers including representatives
of go-yaml's most important downstream projects.

We will strive to earn the trust of the various go-yaml forks to switch back to
this repository as their upstream.

Please [contact us](https://cloud-native.slack.com/archives/C08PPAT8PS7) if you
would like to contribute or be involved.


## Compatibility

The `yaml` package supports most of YAML 1.2, but preserves some behavior from
1.1 for backwards compatibility.

Specifically, v3 of the `yaml` package:

* Supports YAML 1.1 bools (`yes`/`no`, `on`/`off`) as long as they are being
  decoded into a typed bool value.
  Otherwise they behave as a string.
  Booleans in YAML 1.2 are `true`/`false` only.
* Supports octals encoded and decoded as `0777` per YAML 1.1, rather than
  `0o777` as specified in YAML 1.2, because most parsers still use the old
  format.
  Octals in the `0o777` format are supported though, so new files work.
* Does not support base-60 floats.
  These are gone from YAML 1.2, and were actually never supported by this
  package as it's clearly a poor choice.


## Installation and Usage

The import path for the package is *go.yaml.in/yaml/v3*.

To install it, run:

```bash
go get go.yaml.in/yaml/v3
```


## API Documentation

See: <https://pkg.go.dev/go.yaml.in/yaml/v3>


## API Stability

The package API for yaml v3 will remain stable as described in [gopkg.in](
https://gopkg.in).


## Example

```go
package main

import (
	"fmt"
	"log"

	"go.yaml.in/yaml/v3"
)

var data = `
a: Easy!
b:
  c: 2
  d: [3, 4]
`

// Note: struct fields must be public in order for unmarshal to
// correctly populate the data.
type T struct {
	A string
	B struct {
		RenamedC int   `yaml:"c"`
		D	[]int `yaml:",flow"`
	}
}

func main() {
	t := T{}

	err := yaml.Unmarshal([]byte(data), &t)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Printf("--- t:\n%v\n\n", t)

	d, err := yaml.Marshal(&t)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Printf("--- t dump:\n%s\n\n", string(d))

	m := make(map[interface{}]interface{})

	err = yaml.Unmarshal([]byte(data), &m)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Printf("--- m:\n%v\n\n", m)

	d, err = yaml.Marshal(&m)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Printf("--- m dump:\n%s\n\n", string(d))
}
```

This example will generate the following output:

```
--- t:
{Easy! {2 [3 4]}}

--- t dump:
a: Easy!
b:
  c: 2
  d: [3, 4]


--- m:
map[a:Easy! b:map[c:2 d:[3 4]]]

--- m dump:
a: Easy!
b:
  c: 2
  d:
  - 3
  - 4
```


## License

The yaml package is licensed under the MIT and Apache License 2.0 licenses.
Please see the LICENSE file for details.

---

### Overview (`vendor/github.com/aymanbagabas/go-osc52/v2/README.md`)

# go-osc52

<p>
    <a href="https://github.com/aymanbagabas/go-osc52/releases"><img src="https://img.shields.io/github/release/aymanbagabas/go-osc52.svg" alt="Latest Release"></a>
    <a href="https://pkg.go.dev/github.com/aymanbagabas/go-osc52/v2?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
</p>

A Go library to work with the [ANSI OSC52](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands) terminal sequence.

## Usage

You can use this small library to construct an ANSI OSC52 sequence suitable for
your terminal.


### Example

```go
import (
  "os"
  "fmt"

  "github.com/aymanbagabas/go-osc52/v2"
)

func main() {
  s := "Hello World!"

  // Copy `s` to system clipboard
  osc52.New(s).WriteTo(os.Stderr)

  // Copy `s` to primary clipboard (X11)
  osc52.New(s).Primary().WriteTo(os.Stderr)

  // Query the clipboard
  osc52.Query().WriteTo(os.Stderr)

  // Clear system clipboard
  osc52.Clear().WriteTo(os.Stderr)

  // Use the fmt.Stringer interface to copy `s` to system clipboard
  fmt.Fprint(os.Stderr, osc52.New(s))

  // Or to primary clipboard
  fmt.Fprint(os.Stderr, osc52.New(s).Primary())
}
```

## SSH Example

You can use this over SSH using [gliderlabs/ssh](https://github.com/gliderlabs/ssh) for instance:

```go
var sshSession ssh.Session
seq := osc52.New("Hello awesome!")
// Check if term is screen or tmux
pty, _, _ := s.Pty()
if pty.Term == "screen" {
  seq = seq.Screen()
} else if isTmux {
  seq = seq.Tmux()
}
seq.WriteTo(sshSession.Stderr())
```

## Tmux

Make sure you have `set-clipboard on` in your config, otherwise, tmux won't
allow your application to access the clipboard [^1].

Using the tmux option, `osc52.TmuxMode` or `osc52.New(...).Tmux()`, wraps the
OSC52 sequence in a special tmux DCS sequence and pass it to the outer
terminal. This requires `allow-passthrough on` in your config.
`allow-passthrough` is no longer enabled by default
[since tmux 3.3a](https://github.com/tmux/tmux/issues/3218#issuecomment-1153089282) [^2].

[^1]: See [tmux clipboard](https://github.com/tmux/tmux/wiki/Clipboard)
[^2]: [What is allow-passthrough](https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it)

## Credits

* [vim-oscyank](https://github.com/ojroques/vim-oscyank) this is heavily inspired by vim-oscyank.

---

### Overview (`vendor/github.com/godbus/dbus/v5/README.md`)

dbus
----

dbus is a simple library that implements native Go client bindings for the
D-Bus message bus system.

### Features

* Complete native implementation of the D-Bus message protocol
* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections)
* Subpackages that help with the introspection / property interfaces

### Installation

This packages requires Go 1.12 or later. It can be installed by running the command below:

```
go get github.com/godbus/dbus/v5
```

### Usage

The complete package documentation and some simple examples are available at
[godoc.org](http://godoc.org/github.com/godbus/dbus). Also, the
[_examples](https://github.com/godbus/dbus/tree/master/_examples) directory
gives a short overview over the basic usage. 

#### Projects using godbus
- [fyne](https://github.com/fyne-io/fyne) a cross platform GUI in Go inspired by Material Design.
- [fynedesk](https://github.com/fyne-io/fynedesk) a full desktop environment for Linux/Unix using Fyne.
- [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API.
- [iwd](https://github.com/shibumi/iwd) go bindings for the internet wireless daemon "iwd".
- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
- [playerbm](https://github.com/altdesktop/playerbm) a bookmark utility for media players.

Please note that the API is considered unstable for now and may change without
further notice.

### License

go.dbus is available under the Simplified BSD License; see LICENSE for the full
text.

Nearly all of the credit for this library goes to github.com/guelfey/go.dbus.

---

### Overview (`vendor/github.com/russross/blackfriday/v2/README.md`)

Blackfriday
[![Build Status][BuildV2SVG]][BuildV2URL]
[![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL]
===========

Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
is paranoid about its input (so you can safely feed it user-supplied
data), it is fast, it supports common extensions (tables, smart
punctuation substitutions, etc.), and it is safe for all utf-8
(unicode) input.

HTML output is currently supported, along with Smartypants
extensions.

It started as a translation from C of [Sundown][3].


Installation
------------

Blackfriday is compatible with modern Go releases in module mode.
With Go installed:

    go get github.com/russross/blackfriday/v2

will resolve and add the package to the current development module,
then build and install it. Alternatively, you can achieve the same
if you import it in a package:

    import "github.com/russross/blackfriday/v2"

and `go get` without parameters.

Legacy GOPATH mode is unsupported.


Versions
--------

Currently maintained and recommended version of Blackfriday is `v2`. It's being
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
documentation is available at
https://pkg.go.dev/github.com/russross/blackfriday/v2.

It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`.

Version 2 offers a number of improvements over v1:

* Cleaned up API
* A separate call to [`Parse`][4], which produces an abstract syntax tree for
  the document
* Latest bug fixes
* Flexibility to easily add your own rendering extensions

Potential drawbacks:

* Our benchmarks show v2 to be slightly slower than v1. Currently in the
  ballpark of around 15%.
* API breakage. If you can't afford modifying your code to adhere to the new API
  and don't care too much about the new features, v2 is probably not for you.
* Several bug fixes are trailing behind and still need to be forward-ported to
  v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
  tracking.

If you are still interested in the legacy `v1`, you can import it from
`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found
here: https://pkg.go.dev/github.com/russross/blackfriday.


Usage
-----

For the most sensible markdown processing, it is as simple as getting your input
into a byte slice and calling:

```go
output := blackfriday.Run(input)
```

Your input will be parsed and the output rendered with a set of most popular
extensions enabled. If you want the most basic feature set, corresponding with
the bare Markdown specification, use:

```go
output := blackfriday.Run(input, blackfriday.WithNoExtensions())
```

### Sanitize untrusted content

Blackfriday itself does nothing to protect against malicious content. If you are
dealing with user-supplied markdown, we recommend running Blackfriday's output
through HTML sanitizer such as [Bluemonday][5].

Here's an example of simple usage of Blackfriday together with Bluemonday:

```go
import (
    "github.com/microcosm-cc/bluemonday"
    "github.com/russross/blackfriday/v2"
)

// ...
unsafe := blackfriday.Run(input)
html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
```

### Custom options

If you want to customize the set of options, use `blackfriday.WithExtensions`,
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.

### `blackfriday-tool`

You can also check out `blackfriday-tool` for a more complete example
of how to use it. Download and install it using:

    go get github.com/russross/blackfriday-tool

This is a simple command-line tool that allows you to process a
markdown file using a standalone program.  You can also browse the
source directly on github if you are just looking for some example
code:

* <https://github.com/russross/blackfriday-tool>

Note that if you have not already done so, installing
`blackfriday-tool` will be sufficient to download and install
blackfriday in addition to the tool itself. The tool binary will be
installed in `$GOPATH/bin`.  This is a statically-linked binary that
can be copied to wherever you need it without worrying about
dependencies and library versions.

### Sanitized anchor names

Blackfriday includes an algorithm for creating sanitized anchor names
corresponding to a given input text. This algorithm is used to create
anchors for headings when `AutoHeadingIDs` extension is enabled. The
algorithm has a specification, so that other packages can create
compatible anchor names and links to those anchors.

The specification is located at https://pkg.go.dev/github.com/russross/blackfriday/v2#hdr-Sanitized_Anchor_Names.

[`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday/v2#SanitizedAnchorName) exposes this functionality, and can be used to
create compatible links to the anchor names generated by blackfriday.
This algorithm is also implemented in a small standalone package at
[`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
that want a small package and don't need full functionality of blackfriday.


Features
--------

All features of Sundown are supported, including:

*   **Compatibility**. The Markdown v1.0.3 test suite passes with
    the `--tidy` option.  Without `--tidy`, the differences are
    mostly in whitespace and entity escaping, where blackfriday is
    more consistent and cleaner.

*   **Common extensions**, including table support, fenced code
    blocks, autolinks, strikethroughs, non-strict emphasis, etc.

*   **Safety**. Blackfriday is paranoid when parsing, making it safe
    to feed untrusted user input without fear of bad things
    happening. The test suite stress tests this and there are no
    known inputs that make it crash.  If you find one, please let me
    know and send me the input that does it.

    NOTE: "safety" in this context means *runtime safety only*. In order to
    protect yourself against JavaScript injection in untrusted content, see
    [this example](https://github.com/russross/blackfriday#sanitize-untrusted-content).

*   **Fast processing**. It is fast enough to render on-demand in
    most web applications without having to cache the output.

*   **Thread safety**. You can run multiple parsers in different
    goroutines without ill effect. There is no dependence on global
    shared state.

*   **Minimal dependencies**. Blackfriday only depends on standard
    library packages in Go. The source code is pretty
    self-contained, so it is easy to add to any project, including
    Google App Engine projects.

*   **Standards compliant**. Output successfully validates using the
    W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional.


Extensions
----------

In addition to the standard markdown syntax, this package
implements the following extensions:

*   **Intra-word emphasis supression**. The `_` character is
    commonly used inside words when discussing code, so having
    markdown interpret it as an emphasis command is usually the
    wrong thing. Blackfriday lets you treat all emphasis markers as
    normal characters when they occur inside a word.

*   **Tables**. Tables can be created by drawing them in the input
    using a simple syntax:

    ```
    Name    | Age
    --------|------
    Bob     | 27
    Alice   | 23
    ```

*   **Fenced code blocks**. In addition to the normal 4-space
    indentation to mark code blocks, you can explicitly mark them
    and supply a language (to make syntax highlighting simple). Just
    mark it like this:

        ```go
        func getTrue() bool {
            return true
        }
        ```

    You can use 3 or more backticks to mark the beginning of the
    block, and the same number to mark the end of the block.

    To preserve classes of fenced code blocks while using the bluemonday
    HTML sanitizer, use the following policy:

    ```go
    p := bluemonday.UGCPolicy()
    p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
    html := p.SanitizeBytes(unsafe)
    ```

*   **Definition lists**. A simple definition list is made of a single-line
    term followed by a colon and the definition for that term.

        Cat
        : Fluffy animal everyone likes

        Internet
        : Vector of transmission for pictures of cats

    Terms must be separated from the previous definition by a blank line.

*   **Footnotes**. A marker in the text that will become a superscript number;
    a footnote definition that will be placed in a list of footnotes at the
    end of the document. A footnote looks like this:

        This is a footnote.[^1]

        [^1]: the footnote text.

*   **Autolinking**. Blackfriday can find URLs that have not been
    explicitly marked as links and turn them into links.

*   **Strikethrough**. Use two tildes (`~~`) to mark text that
    should be crossed out.

*   **Hard line breaks**. With this extension enabled newlines in the input
    translate into line breaks in the output. This extension is off by default.

*   **Smart quotes**. Smartypants-style punctuation substitution is
    supported, turning normal double- and single-quote marks into
    curly quotes, etc.

*   **LaTeX-style dash parsing** is an additional option, where `--`
    is translated into `&ndash;`, and `---` is translated into
    `&mdash;`. This differs from most smartypants processors, which
    turn a single hyphen into an ndash and a double hyphen into an
    mdash.

*   **Smart fractions**, where anything that looks like a fraction
    is translated into suitable HTML (instead of just a few special
    cases like most smartypant processors). For example, `4/5`
    becomes `<sup>4</sup>&frasl;<sub>5</sub>`, which renders as
    <sup>4</sup>&frasl;<sub>5</sub>.


Other renderers
---------------

Blackfriday is structured to allow alternative rendering engines. Here
are a few of note:

*   [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown):
    provides a GitHub Flavored Markdown renderer with fenced code block
    highlighting, clickable heading anchor links.

    It's not customizable, and its goal is to produce HTML output
    equivalent to the [GitHub Markdown API endpoint](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode),
    except the rendering is performed locally.

*   [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
    but for markdown.

*   [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex):
    renders output as LaTeX.

*   [bfchroma](https://github.com/Depado/bfchroma/): provides convenience
    integration with the [Chroma](https://github.com/alecthomas/chroma) code
    highlighting library. bfchroma is only compatible with v2 of Blackfriday and
    provides a drop-in renderer ready to use with Blackfriday, as well as
    options and means for further customization.

*   [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.

*   [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style


TODO
----

*   More unit testing
*   Improve Unicode support. It does not understand all Unicode
    rules (about what constitutes a letter, a punctuation symbol,
    etc.), so it may fail to detect word boundaries correctly in
    some instances. It is safe on all UTF-8 input.


License
-------

[Blackfriday is distributed under the Simplified BSD License](LICENSE.txt)


   [1]: https://daringfireball.net/projects/markdown/ "Markdown"
   [2]: https://golang.org/ "Go Language"
   [3]: https://github.com/vmg/sundown "Sundown"
   [4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func"
   [5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"

   [BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2
   [BuildV2URL]: https://travis-ci.org/russross/blackfriday
   [PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2
   [PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2

---

### Overview (`vendor/golang.org/x/sys/unix/README.md`)

# Building `sys/unix`

The sys/unix package provides access to the raw system call interface of the
underlying operating system. See: https://godoc.org/golang.org/x/sys/unix

Porting Go to a new architecture/OS combination or adding syscalls, types, or
constants to an existing architecture/OS pair requires some manual effort;
however, there are tools that automate much of the process.

## Build Systems

There are currently two ways we generate the necessary files. We are currently
migrating the build system to use containers so the builds are reproducible.
This is being done on an OS-by-OS basis. Please update this documentation as
components of the build system change.

### Old Build System (currently for `GOOS != "linux"`)

The old build system generates the Go files based on the C header files
present on your system. This means that files
for a given GOOS/GOARCH pair must be generated on a system with that OS and
architecture. This also means that the generated code can differ from system
to system, based on differences in the header files.

To avoid this, if you are using the old build system, only generate the Go
files on an installation with unmodified header files. It is also important to
keep track of which version of the OS the files were generated from (ex.
Darwin 14 vs Darwin 15). This makes it easier to track the progress of changes
and have each OS upgrade correspond to a single change.

To build the files for your current OS and architecture, make sure GOOS and
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
your specific system. Running `mkall.sh -n` shows the commands that will be run.

Requirements: bash, go

### New Build System (currently for `GOOS == "linux"`)

The new build system uses a Docker container to generate the go files directly
from source checkouts of the kernel and various system libraries. This means
that on any platform that supports Docker, all the files using the new build
system can be generated at once, and generated files will not change based on
what the person running the scripts has installed on their computer.

The OS specific files for the new build system are located in the `${GOOS}`
directory, and the build is coordinated by the `${GOOS}/mkall.go` program. When
the kernel or system library updates, modify the Dockerfile at
`${GOOS}/Dockerfile` to checkout the new release of the source.

To build all the files under the new build system, you must be on an amd64/Linux
system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
system. Running `mkall.sh -n` shows the commands that will be run.

Requirements: bash, go, docker

## Component files

This section describes the various files used in the code generation process.
It also contains instructions on how to modify these files to add a new
architecture/OS or to add additional syscalls, types, or constants. Note that
if you are using the new build system, the scripts/programs cannot be called normally.
They must be called from within the docker container.

### asm files

The hand-written assembly file at `asm_${GOOS}_${GOARCH}.s` implements system
call dispatch. There are three entry points:
```
  func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
  func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
  func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
```
The first and second are the standard ones; they differ only in how many
arguments can be passed to the kernel. The third is for low-level use by the
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
let it know that a system call is running.

When porting Go to a new architecture/OS, this file must be implemented for
each GOOS/GOARCH pair.

### mksysnum

Mksysnum is a Go program located at `${GOOS}/mksysnum.go` (or `mksysnum_${GOOS}.go`
for the old system). This program takes in a list of header files containing the
syscall number declarations and parses them to produce the corresponding list of
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
constants.

Adding new syscall numbers is mostly done by running the build on a sufficiently
new installation of the target OS (or updating the source checkouts for the
new build system). However, depending on the OS, you may need to update the
parsing in mksysnum.

### mksyscall.go

The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
hand-written Go files which implement system calls (for unix, the specific OS,
or the specific OS/Architecture pair respectively) that need special handling
and list `//sys` comments giving prototypes for ones that can be generated.

The mksyscall.go program takes the `//sys` and `//sysnb` comments and converts
them into syscalls. This requires the name of the prototype in the comment to
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
prototype can be exported (capitalized) or not.

Adding a new syscall often just requires adding a new `//sys` function prototype
with the desired arguments and a capitalized name so it is exported. However, if
you want the interface to the syscall to be different, often one will make an
unexported `//sys` prototype, and then write a custom wrapper in
`syscall_${GOOS}.go`.

### types files

For each OS, there is a hand-written Go file at `${GOOS}/types.go` (or
`types_${GOOS}.go` on the old system). This file includes standard C headers and
creates Go type aliases to the corresponding C types. The file is then fed
through godef to get the Go compatible definitions. Finally, the generated code
is fed though mkpost.go to format the code correctly and remove any hidden or
private identifiers. This cleaned-up code is written to
`ztypes_${GOOS}_${GOARCH}.go`.

The hardest part about preparing this file is figuring out which headers to
include and which symbols need to be `#define`d to get the actual data
structures that pass through to the kernel system calls. Some C libraries
preset alternate versions for binary compatibility and translate them on the
way in and out of system calls, but there is almost always a `#define` that can
get the real ones.
See `types_darwin.go` and `linux/types.go` for examples.

To add a new type, add in the necessary include statement at the top of the
file (if it is not already there) and add in a type alias line. Note that if
your type is significantly different on different architectures, you may need
some `#if/#elif` macros in your include statements.

### mkerrors.sh

This script is used to generate the system's various constants. This doesn't
just include the error numbers and error strings, but also the signal numbers
and a wide variety of miscellaneous constants. The constants come from the list
of include files in the `includes_${uname}` variable. A regex then picks out
the desired `#define` statements, and generates the corresponding Go constants.
The error numbers and strings are generated from `#include <errno.h>`, and the
signal numbers and strings are generated from `#include <signal.h>`. All of
these constants are written to `zerrors_${GOOS}_${GOARCH}.go` via a C program,
`_errors.c`, which prints out all the constants.

To add a constant, add the header that includes it to the appropriate variable.
Then, edit the regex (if necessary) to match the desired constant. Avoid making
the regex too broad to avoid matching unintended constants.

### internal/mkmerge

This program is used to extract duplicate const, func, and type declarations
from the generated architecture-specific files listed below, and merge these
into a common file for each OS.

The merge is performed in the following steps:
1. Construct the set of common code that is identical in all architecture-specific files.
2. Write this common code to the merged file.
3. Remove the common code from all architecture-specific files.


## Generated files

### `zerrors_${GOOS}_${GOARCH}.go`

A file containing all of the system's generated error numbers, error strings,
signal numbers, and constants. Generated by `mkerrors.sh` (see above).

### `zsyscall_${GOOS}_${GOARCH}.go`

A file containing all the generated syscalls for a specific GOOS and GOARCH.
Generated by `mksyscall.go` (see above).

### `zsysnum_${GOOS}_${GOARCH}.go`

A list of numeric constants for all the syscall number of the specific GOOS
and GOARCH. Generated by mksysnum (see above).

### `ztypes_${GOOS}_${GOARCH}.go`

A file containing Go types for passing into (or returning from) syscalls.
Generated by godefs and the types file (see above).

---

### Overview (`vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md`)

An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode 17.

[](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)



## Quick start

```
go get "github.com/clipperhouse/uax29/v2/graphemes"
```

```go
import "github.com/clipperhouse/uax29/v2/graphemes"

text := "Hello, 世界. Nice dog! 👍🐶"

tokens := graphemes.FromString(text)

for tokens.Next() {                     // Next() returns true until end of data
	fmt.Println(tokens.Value())         // Do something with the current grapheme
}
```

_A grapheme is a “single visible character”, which might be a simple as a single letter, or a complex emoji that consists of several Unicode code points._

## Conformance

We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-36.html#Tests29).




## APIs

### If you have a `string`

```go
text := "Hello, 世界. Nice dog! 👍🐶"

tokens := graphemes.FromString(text)

for tokens.Next() {                     // Next() returns true until end of data
	fmt.Println(tokens.Value())         // Do something with the current grapheme
}
```

### If you have an `io.Reader`

`FromReader` embeds a [`bufio.Scanner`](https://pkg.go.dev/bufio#Scanner), so just use those methods.

```go
r := getYourReader()                        // from a file or network maybe
tokens := graphemes.FromReader(r)

for tokens.Scan() {                         // Scan() returns true until error or EOF
	fmt.Println(tokens.Text())              // Do something with the current grapheme
}

if tokens.Err() != nil {                    // Check the error
	log.Fatal(tokens.Err())
}
```

### If you have a `[]byte`

```go
b := []byte("Hello, 世界. Nice dog! 👍🐶")

tokens := graphemes.FromBytes(b)

for tokens.Next() {                     // Next() returns true until end of data
	fmt.Println(tokens.Value())         // Do something with the current grapheme
}
```

### Benchmarks

On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second, and no allocations.

```
goos: darwin
goarch: arm64
pkg: github.com/clipperhouse/uax29/graphemes/comparative
cpu: Apple M2
BenchmarkGraphemes/clipperhouse/uax29-8   	  171895 ns/op	   203.39 MB/s     0 B/op	     0 allocs/op
BenchmarkGraphemes/rivo/uniseg-8          	 1980475 ns/op	    17.65 MB/s     0 B/op	     0 allocs/op
```

### Invalid inputs

Invalid UTF-8 input is considered undefined behavior. We test to ensure that bad inputs will not cause pathological outcomes, such as a panic or infinite loop. Callers should expect “garbage-in, garbage-out”.

Your pipeline should probably include a call to [`utf8.Valid()`](https://pkg.go.dev/unicode/utf8#Valid).

---

### Cli Witr (`docs/cli/witr.md`)

## witr

Why is this running?

### Synopsis

witr explains why a process or port is running by tracing its ancestry.

```
witr [process name...] [flags]
```

### Examples

```

  # Inspect a running process by name
  witr nginx

  # Look up a process by PID
  witr --pid 1234

  # Find the process listening on a specific port
  witr --port 5432

  # Find the process holding a file open
  witr --file /var/lib/dpkg/lock

  # Inspect a container by name
  witr --container redis

  # Inspect a process by name with exact matching (no fuzzy search)
  witr bun --exact

  # Show the full process ancestry (who started whom)
  witr postgres --tree

  # Show only warnings (suspicious env, arguments, parents)
  witr docker --warnings

  # Display only environment variables of the process
  witr node --env

  # Short, single-line output (useful for scripts)
  witr sshd --short

  # Disable colorized output (CI or piping)
  witr redis --no-color

  # Output machine-readable JSON
  witr chrome --json

  # Show extended process information (memory, I/O, file descriptors)
  witr mysql --verbose

  # Combine flags: inspect port, show environment variables, output JSON
  witr --port 8080 --env --json

  # Multiple inputs
  witr nginx node
  witr --port 8080 --port 3000
  witr --pid 1234 --pid 5678

  # Mixed inputs
  witr nginx --pid 1234 --port 8080

```

### Options

```
  -c, --container strings   container(s) to look up (repeatable)
      --env                 show environment variables for the process
  -x, --exact               use exact name matching (no substring search)
  -f, --file strings        file(s) held open by a process (repeatable)
  -h, --help                help for witr
  -i, --interactive         interactive mode (TUI)
      --json                show result as JSON
      --no-color            disable colorized output
  -p, --pid strings         pid(s) to look up (repeatable)
  -o, --port strings        port(s) to look up (repeatable)
  -s, --short               show only ancestry
  -t, --tree                show only ancestry as a tree
      --verbose             show extended process information
      --warnings            show only warnings
```

---

### Worlds Devbox.Json (`docs/worlds/devbox.json`)

{
  "id": "devbox",
  "name": "Developer laptop",
  "hostname": "devbox",
  "promptUser": "pranshu",
  "promptHost": "devbox",
  "kernel": "6.10.4-arch1-1",
  "distro": "Arch Linux",
  "arch": "x86_64",
  "memTotalBytes": 17179869184,
  "description": "A messy dev laptop: VS Code, a Vite dev server, a docker-compose shop stack, a zombie, a runaway ffmpeg, and a git lock nobody released.",
  "processes": [
    { "pid": 1, "cpuPercent": 0.1, "memory": { "vms": 178257920, "rss": 13631488, "shared": 8388608 }, "ppid": 0, "command": "systemd", "cmdline": "/sbin/init", "user": "root", "startedAgo": 259200,
      "source": { "type": "init", "name": "systemd", "description": "PID 1, the system and service manager" } },
    { "pid": 700, "cpuPercent": 0.0, "memory": { "vms": 16777216, "rss": 8388608, "shared": 6291456 }, "ppid": 1, "command": "sshd", "cmdline": "sshd: /usr/sbin/sshd -D [listener]", "user": "root", "startedAgo": 259200,
      "sockets": [ { "address": "0.0.0.0", "port": 22, "protocol": "TCP", "state": "LISTEN" } ],
      "source": { "type": "systemd", "name": "sshd.service", "unitFile": "/usr/lib/systemd/system/sshd.service", "description": "OpenSSH Daemon" } },
    { "pid": 900, "cpuPercent": 0.3, "memory": { "vms": 1520435200, "rss": 54525952, "shared": 20971520 }, "ppid": 1, "command": "containerd", "cmdline": "/usr/bin/containerd", "user": "root", "startedAgo": 259200,
      "source": { "type": "systemd", "name": "containerd.service", "description": "containerd container runtime" } },
    { "pid": 950, "cpuPercent": 0.8, "memory": { "vms": 2097152000, "rss": 102760448, "shared": 35651584 }, "ppid": 1, "command": "dockerd", "cmdline": "/usr/bin/dockerd -H fd://", "user": "root", "startedAgo": 259200,
      "source": { "type": "systemd", "name": "docker.service", "unitFile": "/usr/lib/systemd/system/docker.service", "description": "Docker Application Container Engine" } },

    { "pid": 2200, "cpuPercent": 0.0, "memory": { "vms": 25165824, "rss": 11534336, "shared": 7340032 }, "ppid": 1, "command": "systemd", "cmdline": "/lib/systemd/systemd --user", "user": "pranshu", "startedAgo": 86400,
      "source": { "type": "systemd", "name": "user@1000.service", "description": "User Manager for UID 1000" } },
    { "pid": 2260, "cpuPercent": 1.2, "memory": { "vms": 94371840, "rss": 19922944, "shared": 12582912 }, "ppid": 2200, "command": "pipewire", "cmdline": "/usr/bin/pipewire", "user": "pranshu", "startedAgo": 86400 },
    { "pid": 2280, "cpuPercent": 0.5, "memory": { "vms": 754974720, "rss": 71303168, "shared": 31457280 }, "ppid": 2200, "command": "gnome-terminal-", "cmdline": "/usr/lib/gnome-terminal-server", "user": "pranshu", "startedAgo": 82800,
      "source": { "type": "systemd", "name": "gnome-terminal-server.service", "description": "GNOME Terminal Server" } },
    { "pid": 2281, "cpuPercent": 0.0, "memory": { "vms": 12582912, "rss": 6291456, "shared": 4194304 }, "ppid": 2280, "command": "bash", "cmdline": "bash", "user": "pranshu", "startedAgo": 82800, "workingDir": "/home/pranshu/projects/shop" },
    { "pid": 2400, "cpuPercent": 3.1, "ppid": 2281, "command": "node", "cmdline": "node /home/pranshu/projects/shop/node_modules/.bin/vite",
      "user": "pranshu", "startedAgo": 5400, "workingDir": "/home/pranshu/projects/shop", "gitRepo": "shop", "gitBranch": "feat/checkout",
      "sockets": [ { "address": "::", "port": 5173, "protocol": "TCP", "state": "LISTEN" }, { "address": "127.0.0.1", "port": 5173, "protocol": "TCP", "state": "LISTEN" } ],
      "threadCount": 7, "fdCount": 61, "fdLimit": 1024,
      "memory": { "vms": 987654321, "rss": 268435456, "shared": 33554432 },
      "env": ["NODE_ENV=development", "VITE_API_URL=http://localhost:8080", "PATH=/usr/local/bin:/usr/bin:/bin"],
      "source": { "type": "shell", "name": "bash", "description": "Started from an interactive shell (pid 2281) via npm run dev" } },
    { "pid": 2455, "cpuPercent": 1.4, "memory": { "vms": 67108864, "rss": 35651584, "shared": 6291456 }, "ppid": 2400, "command": "esbuild", "cmdline": "/home/pranshu/projects/shop/node_modules/@esbuild/linux-x64/bin/esbuild --service=0.23.0 --ping", "user": "pranshu", "startedAgo": 5400 },

    { "pid": 3400, "cpuPercent": 1.8, "memory": { "vms": 1258291200, "rss": 199229440, "shared": 83886080 }, "ppid": 2200, "command": "code", "cmdline": "/usr/share/code/code --unity-launch", "user": "pranshu", "startedAgo": 79200,
      "source": { "type": "systemd", "name": "app-code@autostart.service", "description": "Visual Studio Code" } },
    { "pid": 3410, "cpuPercent": 0.0, "memory": { "vms": 293601280, "rss": 44040192, "shared": 31457280 }, "ppid": 3400, "command": "code", "cmdline": "/usr/share/code/code --type=zygote", "user": "pranshu", "startedAgo": 79200 },
    { "pid": 3455, "cpuPercent": 2.6, "memory": { "vms": 713031680, "rss": 123731968, "shared": 54525952 }, "ppid": 3400, "command": "code", "cmdline": "/usr/share/code/code --type=gpu-process", "user": "pranshu", "startedAgo": 79200 },
    { "pid": 3470, "cpuPercent": 4.2, "memory": { "vms": 1572864000, "rss": 356515840, "shared": 99614720 }, "ppid": 3400, "command": "code", "cmdline": "/usr/share/code/code --type=renderer", "user": "pranshu", "startedAgo": 79200 },
    { "pid": 3520, "cpuPercent": 2.1, "memory": { "vms": 1310720000, "rss": 272629760, "shared": 73400320 }, "ppid": 3400, "command": "code", "cmdline": "/usr/share/code/code --type=extensionHost", "user": "pranshu", "startedAgo": 79200 },
    { "pid": 3560, "cpuPercent": 1.7, "memory": { "vms": 943718400, "rss": 178257920, "shared": 41943040 }, "ppid": 3520, "command": "node", "cmdline": "node /home/pranshu/.vscode/extensions/typescript/tsserver.js", "user": "pranshu", "startedAgo": 79200,
      "source": { "type": "shell", "name": "code", "description": "TypeScript language server spawned by a VS Code extension host (pid 3520)" } },

    { "pid": 6001, "memory": { "vms": 2411724800, "rss": 492830720, "shared": 41943040 }, "ppid": 2281, "command": "ffmpeg", "cmdline": "ffmpeg -i input.mov -c:v libx264 -preset veryslow out.mp4",
      "user": "pranshu", "startedAgo": 900, "health": "high-cpu", "cpuPercent": 98.4, "workingDir": "/home/pranshu/videos",
      "threadCount": 16,
      "warnings": ["Process is using high CPU (>2h total)"],
      "source": { "type": "shell", "name": "bash", "description": "Started from an interactive shell (pid 2281)" } },

    { "pid": 6100, "cpuPercent": 0.0, "memory": { "vms": 10485760, "rss": 4194304, "shared": 3145728 }, "ppid": 2281, "command": "build.sh", "cmdline": "/bin/bash ./build.sh", "user": "pranshu", "startedAgo": 600, "workingDir": "/home/pranshu/projects/shop" },
    { "pid": 6120, "ppid": 6100, "command": "python3", "cmdline": "<defunct>", "user": "pranshu", "startedAgo": 480, "health": "zombie",
      "warnings": ["Process is a zombie (defunct)"],
      "source": { "type": "shell", "name": "build.sh", "description": "Child of a shell script (pid 6100) that never called wait()" } },

    { "pid": 7300, "cpuPercent": 0.0, "memory": { "vms": 188743680, "rss": 15728640, "shared": 9437184 }, "ppid": 2281, "command": "git", "cmdline": "git commit -m wip", "user": "pranshu", "startedAgo": 300, "workingDir": "/home/pranshu/projects/shop",
      "lockedFiles": ["/home/pranshu/projects/shop/.git/index.lock (WRITE)"],
      "source": { "type": "shell", "name": "bash", "description": "Started from an interactive shell (pid 2281)" } }
  ],
  "containers": [
    { "runtime": "docker", "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90",
      "name": "shop-web-1", "image": "nginx:1.27-alpine", "command": "nginx -g 'daemon off;'",
      "state": "running", "status": "Up 2 hours", "health": "",
      "createdAgo": 7300, "startedAgo": 7200, "networks": "shop_default", "mounts": "/home/pranshu/projects/shop/nginx.conf:/etc/nginx/nginx.conf:ro",
      "ports": "0.0.0.0:8080->80/tcp", "composeProject": "shop", "composeService": "web",
      "composeConfigFile": "/home/pranshu/projects/shop/docker-compose.yml", "composeWorkingDir": "/home/pranshu/projects/shop" },
    { "runtime": "docker", "id": "b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90a1",
      "name": "shop-api-1", "image": "node:20-alpine", "command": "node server.js",
      "state": "running", "status": "Up 2 hours (healthy)", "health": "healthy",
      "createdAgo": 7300, "startedAgo": 7200, "networks": "shop_default", "mounts": "shop_api-data:/data",
      "ports": "0.0.0.0:3000->3000/tcp", "composeProject": "shop", "composeService": "api",
      "composeConfigFile": "/home/pranshu/projects/shop/docker-compose.yml", "composeWorkingDir": "/home/pranshu/projects/shop" },
    { "runtime": "docker", "id": "c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2",
      "name": "shop-db-1", "image": "postgres:16-alpine", "command": "postgres",
      "state": "running", "status": "Up 2 hours (healthy)", "health": "healthy",
      "createdAgo": 7300, "startedAgo": 7200, "networks": "shop_default", "mounts": "shop_db-data:/var/lib/postgresql/data",
      "ports": "127.0.0.1:5432->5432/tcp", "composeProject": "shop", "composeService": "db",
      "composeConfigFile": "/home/pranshu/projects/shop/docker-compose.yml", "composeWorkingDir": "/home/pranshu/projects/shop" },
    { "runtime": "docker", "id": "d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3",
      "name": "shop-cache-1", "image": "redis:7-alpine", "command": "redis-server",
      "state": "restarting", "status": "Restarting (1) 8 seconds ago", "health": "",
      "createdAgo": 7300, "startedAgo": 30, "networks": "shop_default", "mounts": "",
      "ports": "", "composeProject": "shop", "composeService": "cache",
      "composeConfigFile": "/home/pranshu/projects/shop/docker-compose.yml", "composeWorkingDir": "/home/pranshu/projects/shop" }
  ],
  "locks": [
    { "pid": 2400, "process": "node", "path": "/home/pranshu/projects/shop/node_modules/.vite/deps/_metadata.json", "type": "FLOCK", "mode": "WRITE" },
    { "pid": 7300, "process": "git", "path": "/home/pranshu/projects/shop/.git/index.lock", "type": "FLOCK", "mode": "WRITE" }
  ],
  "socketOverrides": {
    "5173": { "state": "LISTEN", "explanation": "A process is accepting new connections on this port.", "workaround": "" },
    "8080": { "state": "LISTEN", "explanation": "A socket was found for the port, but the owning process runs inside a container namespace.", "workaround": "This port is published by the docker container shop-web-1." }
  },
  "fs": {
    "/home/pranshu": ["projects/", "videos/", ".vscode/", ".config/", ".bashrc"],
    "/home/pranshu/projects": ["shop/"],
    "/home/pranshu/projects/shop": ["src/", "node_modules/", "docker-compose.yml", "package.json", "build.sh", ".git/"],
    "/home/pranshu/videos": ["input.mov", "out.mp4"],
    "/": ["bin/", "boot/", "dev/", "etc/", "home/", "opt/", "proc/", "root/", "run/", "srv/", "usr/", "var/"]
  },
  "files": {
    "/home/pranshu/projects/shop/docker-compose.yml": "services:\n  web:   { image: nginx:1.27-alpine, ports: ['8080:80'] }\n  api:   { image: node:20-alpine, command: node server.js, ports: ['3000:3000'] }\n  db:    { image: postgres:16-alpine, ports: ['127.0.0.1:5432:5432'] }\n  cache: { image: redis:7-alpine }\n",
    "/home/pranshu/projects/shop/build.sh": "#!/bin/bash\n# fires off a helper and forgets to wait for it -> zombie\npython3 ./tools/prewarm.py &\nnpm run build\n"
  }
}

---

### Worlds Webbox.Json (`docs/worlds/webbox.json`)

{
  "id": "webbox",
  "name": "Web server box",
  "hostname": "webbox",
  "promptUser": "deploy",
  "promptHost": "webbox",
  "kernel": "6.8.0-45-generic",
  "distro": "Ubuntu 24.04.1 LTS",
  "arch": "x86_64",
  "memTotalBytes": 4294967296,
  "description": "A small production box: an Nginx front-end, a PM2-managed Node app, Postgres, a Compose stack (an API and its Redis cache), a backup left running after its SSH session closed, and a stuck apt lock.",
  "processes": [
    {
      "pid": 1, "cpuPercent": 0.1, "memory": { "vms": 176160768, "rss": 13631488, "shared": 8388608 }, "ppid": 0, "command": "systemd", "cmdline": "/sbin/init",
      "user": "root", "startedAgo": 1814400,
      "source": { "type": "init", "name": "systemd", "description": "PID 1, the system and service manager" }
    },
    { "pid": 402, "cpuPercent": 0.2, "memory": { "vms": 67108864, "rss": 16777216, "shared": 12582912 }, "ppid": 1, "command": "systemd-journald", "cmdline": "/lib/systemd/systemd-journald", "user": "root", "startedAgo": 1814400,
      "source": { "type": "systemd", "name": "systemd-journald.service", "unitFile": "/lib/systemd/system/systemd-journald.service", "description": "Journal Service" } },
    { "pid": 455, "cpuPercent": 0.0, "memory": { "vms": 25165824, "rss": 8388608, "shared": 6291456 }, "ppid": 1, "command": "systemd-logind", "cmdline": "/lib/systemd/systemd-logind", "user": "root", "startedAgo": 1814400,
      "source": { "type": "systemd", "name": "systemd-logind.service", "unitFile": "/lib/systemd/system/systemd-logind.service", "description": "User Login Management" } },
    { "pid": 690, "cpuPercent": 0.0, "memory": { "vms": 16777216, "rss": 4194304, "shared": 3145728 }, "ppid": 1, "command": "cron", "cmdline": "/usr/sbin/cron -f -P", "user": "root", "startedAgo": 1814400,
      "source": { "type": "systemd", "name": "cron.service", "unitFile": "/lib/systemd/system/cron.service", "description": "Regular background program processing daemon" } },

    { "pid": 711, "cpuPercent": 0.0, "memory": { "vms": 16777216, "rss": 8388608, "shared": 6291456 }, "ppid": 1, "command": "sshd", "cmdline": "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups",
      "user": "root", "startedAgo": 1814400,
      "sockets": [ { "address": "0.0.0.0", "port": 22, "protocol": "TCP", "state": "LISTEN" }, { "address": "::", "port": 22, "protocol": "TCP", "state": "LISTEN" } ],
      "capabilities": ["CAP_NET_BIND_SERVICE", "CAP_SYS_CHROOT"],
      "source": { "type": "systemd", "name": "ssh.service", "unitFile": "/lib/systemd/system/ssh.service", "description": "OpenBSD Secure Shell server" } },
    { "pid": 40122, "cpuPercent": 0.0, "memory": { "vms": 18874368, "rss": 10485760, "shared": 7340032 }, "ppid": 711, "command": "sshd", "cmdline": "sshd: deploy [priv]", "user": "root", "startedAgo": 10800 },
    { "pid": 40140, "cpuPercent": 0.1, "memory": { "vms": 18874368, "rss": 7340032, "shared": 5242880 }, "ppid": 40122, "command": "sshd", "cmdline": "sshd: deploy@pts/0", "user": "deploy", "startedAgo": 10800 },
    { "pid": 40141, "cpuPercent": 0.0, "memory": { "vms": 12582912, "rss": 5242880, "shared": 3145728 }, "ppid": 40140, "command": "bash", "cmdline": "-bash", "user": "deploy", "startedAgo": 10800,
      "workingDir": "/home/deploy",
      "source": { "type": "ssh", "name": "sshd", "description": "Interactive shell from an SSH login session (sshd pid 40140)" } },
    { "pid": 8123, "cpuPercent": 0.1, "memory": { "vms": 33554432, "rss": 23068672, "shared": 8388608 }, "ppid": 40141, "command": "python3", "cmdline": "python3 -m http.server 8000", "user": "deploy",
      "startedAgo": 2400, "workingDir": "/home/deploy/share", "forked": "forked",
      "sockets": [ { "address": "0.0.0.0", "port": 8000, "protocol": "TCP", "state": "LISTEN" } ],
      "warnings": ["Process is listening on a public interface"],
      "source": { "type": "shell", "name": "bash", "description": "Backgrounded from an interactive shell (pid 40141)" } },

    { "pid": 1201, "cpuPercent": 0.0, "memory": { "vms": 67108864, "rss": 12582912, "shared": 5242880 }, "ppid": 1, "command": "nginx", "cmdline": "nginx: master process /usr/sbin/nginx -g daemon off;",
      "user": "root", "startedAgo": 518400,
      "sockets": [ { "address": "0.0.0.0", "port": 80, "protocol": "TCP", "state": "LISTEN" }, { "address": "0.0.0.0", "port": 443, "protocol": "TCP", "state": "LISTEN" } ],
      "capabilities": ["CAP_NET_BIND_SERVICE"],
      "source": { "type": "systemd", "name": "nginx.service", "unitFile": "/lib/systemd/system/nginx.service", "description": "A high performance web server and a reverse proxy server" } },
    { "pid": 1202, "cpuPercent": 0.3, "memory": { "vms": 71303168, "rss": 17825792, "shared": 5242880 }, "ppid": 1201, "command": "nginx", "cmdline": "nginx: worker process", "user": "www-data", "startedAgo": 518400 },
    { "pid": 1203, "cpuPercent": 0.2, "memory": { "vms": 71303168, "rss": 16777216, "shared": 5242880 }, "ppid": 1201, "command": "nginx", "cmdline": "nginx: worker process", "user": "www-data", "startedAgo": 518400 },

    { "pid": 5034, "cpuPercent": 0.4, "memory": { "vms": 650117120, "rss": 65011712, "shared": 23068672 }, "ppid": 1, "command": "PM2 v5.3.1: God", "cmdline": "PM2 v5.3.1: God Daemon (/home/deploy/.pm2)",
      "user": "deploy", "startedAgo": 432000,
      "source": { "type": "systemd", "name": "pm2-deploy.service", "unitFile": "/etc/systemd/system/pm2-deploy.service", "description": "PM2 process manager for user deploy" } },
    { "pid": 14233, "cpuPercent": 2.3, "ppid": 5034, "command": "node", "cmdline": "node /srv/expense-manager/index.js",
      "user": "deploy", "startedAgo": 172800,
      "workingDir": "/srv/expense-manager", "gitRepo": "expense-manager", "gitBranch": "main",
      "sockets": [
        { "address": "127.0.0.1", "port": 5000, "protocol": "TCP", "state": "LISTEN" },
        { "address": "127.0.0.1", "port": 5000, "protocol": "TCP", "state": "ESTABLISHED", "remoteAddress": "127.0.0.1", "remotePort": 49822 },
        { "address": "127.0.0.1", "port": 5000, "protocol": "TCP", "state": "ESTABLISHED", "remoteAddress": "127.0.0.1", "remotePort": 49836 },
        { "address": "127.0.0.1", "port": 51744, "protocol": "TCP", "state": "ESTABLISHED", "remoteAddress": "127.0.0.1", "remotePort": 5432 }
      ],
      "threadCount": 11, "fdCount": 34, "fdLimit": 1024,
      "memory": { "vms": 1198765568, "rss": 187650048, "shared": 41943040 },
      "io": { "readBytes": 15728640, "writeBytes": 4194304, "readOps": 2140, "writeOps": 512 },
      "env": [
        "NODE_ENV=production",
        "PORT=5000",
        "PM2_HOME=/home/deploy/.pm2",
        "PM2_USAGE=CLI",
        "DATABASE_URL=postgres://app@127.0.0.1:5432/expenses",
        "REDIS_URL=redis://127.0.0.1:6379",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "PWD=/srv/expense-manager"
      ],
      "source": { "type": "systemd", "name": "pm2-deploy.service", "unitFile": "/etc/systemd/system/pm2-deploy.service", "description": "PM2 process manager for user deploy (app: expense-manager)", "details": { "NRestarts": "3" } } },
    { "pid": 14290, "cpuPercent": 0.6, "memory": { "vms": 786432000, "rss": 37748736, "shared": 14680064 }, "ppid": 40141, "command": "ngrok", "cmdline": "ngrok http 5000", "user": "deploy", "startedAgo": 3000,
      "forked": "forked", "workingDir": "/home/deploy",
      "sockets": [ { "address": "127.0.0.1", "port": 4040, "protocol": "TCP", "state": "LISTEN" } ],
      "source": { "type": "shell", "name": "bash", "description": "Started from an interactive shell (pid 40141)" } },

    { "pid": 1450, "cpuPercent": 0.4, "memory": { "vms": 1468006400, "rss": 50331648, "shared": 20971520 }, "ppid": 1, "command": "containerd", "cmdline": "/usr/bin/containerd", "user": "root", "startedAgo": 1814400,
      "source": { "type": "systemd", "name": "containerd.service", "unitFile": "/lib/systemd/system/containerd.service", "description": "containerd container runtime" } },
    { "pid": 1480, "cpuPercent": 0.7, "memory": { "vms": 1992294400, "rss": 96468992, "shared": 33554432 }, "ppid": 1, "command": "dockerd", "cmdline": "/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock", "user": "root", "startedAgo": 1814400,
      "sockets": [ { "address": "0.0.0.0", "port": 2375, "protocol": "TCP", "state": "LISTEN" } ],
      "source": { "type": "systemd", "name": "docker.service", "unitFile": "/lib/systemd/system/docker.service", "description": "Docker Application Container Engine" } },

    { "pid": 2010, "cpuPercent": 0.5, "memory": { "vms": 230686720, "rss": 27262976, "shared": 16777216 }, "ppid": 1, "command": "postgres", "cmdline": "/usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/16/main",
      "user": "postgres", "startedAgo": 518400,
      "sockets": [ { "address": "127.0.0.1", "port": 5432, "protocol": "TCP", "state": "LISTEN" } ],
      "source": { "type": "systemd", "name": "postgresql@16-main.service", "unitFile": "/lib/systemd/system/postgresql@.service", "description": "PostgreSQL Cluster 16-main" } },
    { "pid": 2011, "cpuPercent": 0.3, "memory": { "vms": 232783872, "rss": 18874368, "shared": 10485760 }, "ppid": 2010, "command": "postgres", "cmdline": "postgres: 16/main: checkpointer", "user": "postgres", "startedAgo": 518400 },
    { "pid": 2012, "cpuPercent": 0.2, "memory": { "vms": 231735296, "rss": 12582912, "shared": 10485760 }, "ppid": 2010, "command": "postgres", "cmdline": "postgres: 16/main: background writer", "user": "postgres", "startedAgo": 518400 },
    { "pid": 2013, "cpuPercent": 0.1, "memory": { "vms": 231735296, "rss": 11534336, "shared": 10485760 }, "ppid": 2010, "command": "postgres", "cmdline": "postgres: 16/main: walwriter", "user": "postgres", "startedAgo": 518400 },

    { "pid": 33871, "cpuPercent": 3.8, "memory": { "vms": 125829120, "rss": 60817408, "shared": 18874368 }, "ppid": 1, "command": "unattended-upgr", "cmdline": "/usr/bin/python3 /usr/bin/unattended-upgrade --download-only",
      "user": "root", "startedAgo": 240,
      "lockedFiles": ["/var/lib/dpkg/lock-frontend (WRITE)", "/var/lib/dpkg/lock (WRITE)"],
      "source": { "type": "systemd", "name": "apt-daily-upgrade.service", "unitFile": "/lib/systemd/system/apt-daily-upgrade.service", "description": "Daily apt upgrade and clean activities" } },

    { "pid": 3120, "cpuPercent": 0.0, "memory": { "vms": 1252261888, "rss": 12582912, "shared": 8388608 }, "ppid": 1, "command": "containerd-shim", "cmdline": "/usr/bin/containerd-shim-runc-v2 -namespace moby -id 7c41e0b9a2d3f4e5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9 -address /run/containerd/containerd.sock",
      "user": "root", "startedAgo": 432000,
      "source": { "type": "systemd", "name": "containerd.service", "unitFile": "/lib/systemd/system/containerd.service", "description": "containerd container runtime" } },
    { "pid": 3141, "cpuPercent": 0.4, "memory": { "vms": 268435456, "rss": 88080384, "shared": 12582912 }, "ppid": 3120, "command": "gunicorn", "cmdline": "/usr/local/bin/python /usr/local/bin/gunicorn app:app --bind 0.0.0.0:8080 --workers 2",
      "user": "root", "startedAgo": 432000, "workingDir": "/app",
      "container": "docker: expense-manager-api-1", "containerId": "7c41e0b9a2d3f4e5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
      "warnings": ["Process is running as root", "Container has no healthcheck configured"],
      "source": { "type": "container", "name": "docker" } },

    { "pid": 27310, "cpuPercent": 6.2, "memory": { "vms": 25165824, "rss": 6291456, "shared": 3145728 }, "ppid": 1, "parentExited": true, "command": "rsync", "cmdline": "rsync -a --partial /srv/uploads/ backup@10.0.4.20:/backups/uploads/",
      "user": "deploy", "startedAgo": 5400, "workingDir": "/home/deploy",
      "source": { "type": "systemd", "name": "session-3.scope", "description": "Session 3 of User deploy" } },
    { "pid": 27311, "cpuPercent": 1.1, "memory": { "vms": 16777216, "rss": 7340032, "shared": 5242880 }, "ppid": 27310, "command": "ssh", "cmdline": "ssh -l backup 10.0.4.20 rsync --server -logDtpre.iLsfxCIvu --partial . /backups/uploads/",
      "user": "deploy", "startedAgo": 5400, "workingDir": "/home/deploy" }
  ],
  "containers": [
    {
      "runtime": "docker", "id": "3f9a1c2b7d4e8a5c1f0b2d3e4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b",
      "name": "expense-manager-cache-1", "image": "redis:7-alpine",
      "command": "docker-entrypoint.sh redis-server --appendonly yes",
      "state": "running", "status": "Up 5 days (healthy)", "health": "healthy",
      "createdAgo": 432600, "startedAgo": 432000,
      "networks": "expense-manager_default", "mounts": "expense-manager_redis-data:/data",
      "ports": "127.0.0.1:6379->6379/tcp",
      "composeProject": "expense-manager", "composeService": "cache",
      "composeConfigFile": "/srv/expense-manager/docker-compose.yml",
      "composeWorkingDir": "/srv/expense-manager",
      "restartCount": 2, "restartPolicy": "always"
    },
    {
      "runtime": "docker", "id": "7c41e0b9a2d3f4e5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
      "name": "expense-manager-api-1", "image": "expense-manager-api:1.4.2",
      "command": "gunicorn app:app --bind 0.0.0.0:8080 --workers 2",
      "state": "running", "status": "Up 5 days",
      "createdAgo": 432060, "startedAgo": 432000,
      "networks": "expense-manager_default",
      "ports": "0.0.0.0:8081->8080/tcp",
      "composeProject": "expense-manager", "composeService": "api",
      "composeConfigFile": "/srv/expense-manager/docker-compose.yml",
      "composeWorkingDir": "/srv/expense-manager",
      "restartCount": 1, "restartPolicy": "unless-stopped",
      "pid": 3141
    }
  ],
  "locks": [
    { "pid": 33871, "process": "unattended-upgr", "path": "/var/lib/dpkg/lock-frontend", "type": "FLOCK", "mode": "WRITE" },
    { "pid": 33871, "process": "unattended-upgr", "path": "/var/lib/dpkg/lock", "type": "POSIX", "mode": "WRITE" },
    { "pid": 2010, "process": "postgres", "path": "/var/lib/postgresql/16/main/postmaster.pid", "type": "FLOCK", "mode": "WRITE" },
    { "pid": 14233, "process": "node", "path": "/srv/expense-manager/.pid.lock", "type": "FLOCK", "mode": "WRITE" }
  ],
  "socketOverrides": {
    "5000": { "state": "LISTEN", "explanation": "A process is accepting new connections on this port.", "workaround": "" },
    "8000": { "state": "LISTEN", "explanation": "A process is accepting new connections on this port.", "workaround": "This is a throwaway python http.server bound to 0.0.0.0 — stop it with: kill 8123" }
  },
  "fs": {
    "/home/deploy": ["share/", "app/", ".pm2/", ".bashrc", ".ssh/", "notes.txt"],
    "/home/deploy/share": ["report.pdf", "backup.tar.gz"],
    "/srv/expense-manager": ["index.js", "package.json", "ecosystem.config.js", "docker-compose.yml", ".git/", "node_modules/"],
    "/": ["bin/", "boot/", "dev/", "etc/", "home/", "opt/", "proc/", "root/", "run/", "srv/", "usr/", "var/"]
  },
  "files": {
    "/home/deploy/notes.txt": "Things to fix on webbox:\n- that python http.server on :8000 is still up (pid 8123). kill it.\n- pm2 restarted node 3x last week — check the logs\n- apt keeps grabbing the dpkg lock at odd hours (unattended-upgrades)\n- move redis off the default compose network\n",
    "/srv/expense-manager/ecosystem.config.js": "module.exports = {\n  apps: [{\n    name: 'expense-manager',\n    script: 'index.js',\n    cwd: '/srv/expense-manager',\n    env: { NODE_ENV: 'production', PORT: 5000 }\n  }]\n};\n",
    "/srv/expense-manager/docker-compose.yml": "services:\n  cache:\n    image: redis:7-alpine\n    command: redis-server --appendonly yes\n    ports:\n      - '127.0.0.1:6379:6379'\n    volumes:\n      - redis-data:/data\nvolumes:\n  redis-data:\n"
  }
}

---

--- METRICS ---
- Files Extracted: 29
- Estimated Token Budget: ~54590 tokens
- Canonical Reference: https://codewiki.google/github.com/pranshuparmar/witr
