home / skills / dicklesworthstone / meta_skill / building-glamorous-tuis

building-glamorous-tuis skill

/skills/building-glamorous-tuis

This skill helps you build elegant terminal UIs with Charmbracelet, transforming shell, Go CLI, or SSH apps into polished experiences.

npx playbooks add skill dicklesworthstone/meta_skill --skill building-glamorous-tuis

Review the files below or copy the command above to add this skill to your agents.

Files (6)
SKILL.md
7.1 KB
---
name: building-glamorous-tuis
description: >-
  Build beautiful terminal UIs with Charmbracelet. For SHELL/BASH scripts: use
  Gum (prompts, spinners, selection), VHS (recording), Mods (AI), Freeze
  (screenshots), Glow (markdown). For GO applications: use Bubble Tea
  (framework), Bubbles (components), Lip Gloss (styling), Huh (forms), Glamour
  (markdown), Harmonica (animation). For INFRASTRUCTURE: Wish (SSH apps), Soft
  Serve (Git), teatest (testing). Trigger on: shell scripts needing UI, Go CLI
  tools, terminal dashboards, SSH applications, or "make this CLI prettier."
---

# Building Glamorous TUIs with Charmbracelet

## Quick Router

**What are you building?**

| Context | Solution | Reference |
|---------|----------|-----------|
| **Shell/Bash script** | Gum, VHS, Mods, Freeze | [→ Shell Scripts](#for-shell-scripts) |
| **Go CLI/TUI** | Bubble Tea + Lip Gloss | [→ Go TUI](#for-go-applications) |
| **SSH-accessible app** | Wish + Bubble Tea | [→ Infrastructure](#for-infrastructure) |
| **Recording demos** | VHS | [→ Shell Scripts](#vhs-quick-start) |
| **Testing TUIs** | teatest | [→ Infrastructure](#for-infrastructure) |

---

## For Shell Scripts

**No Go required.** Gum gives you all the UI primitives.

```bash
brew install gum
```

### Essential Gum Commands

```bash
# Input
NAME=$(gum input --placeholder "Your name")

# Selection (single)
COLOR=$(gum choose "red" "green" "blue")

# Selection (multi)
ITEMS=$(gum choose --no-limit "a" "b" "c")

# Fuzzy filter (from stdin)
BRANCH=$(git branch | gum filter)

# Confirmation
gum confirm "Continue?" && echo "yes"

# Spinner
gum spin --title "Working..." -- long-command

# Styled output
gum style --border rounded --padding "1 2" "Hello"

# File picker
FILE=$(gum file .)
```

### Quick Recipe: Git Commit

```bash
TYPE=$(gum choose "feat" "fix" "docs" "refactor")
MSG=$(gum input --placeholder "commit message")
gum confirm "Commit?" && git commit -m "$TYPE: $MSG"
```

### VHS Quick Start

Record terminal → GIF:

```bash
brew install vhs

cat > demo.tape << 'EOF'
Output demo.gif
Set Theme "Catppuccin Mocha"
Type "echo hello"
Enter
Sleep 1s
EOF

vhs demo.tape
```

### Other Shell Tools

| Tool | Install | One-liner |
|------|---------|-----------|
| **Mods** | `brew install mods` | `git diff \| mods "review this"` |
| **Glow** | `brew install glow` | `glow README.md` |
| **Freeze** | `brew install freeze` | `freeze code.go -o screenshot.png` |

**[Full Shell Reference →](references/shell-scripts.md)**

---

## For Go Applications

### Instant Start

```bash
go get github.com/charmbracelet/bubbletea \
       github.com/charmbracelet/lipgloss
```

### Minimal TUI (Copy & Run)

```go
package main

import (
    "fmt"
    tea "github.com/charmbracelet/bubbletea"
    "github.com/charmbracelet/lipgloss"
)

var highlight = lipgloss.NewStyle().Foreground(lipgloss.Color("212")).Bold(true)

type model struct {
    items  []string
    cursor int
}

func (m model) Init() tea.Cmd { return nil }

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "q", "ctrl+c":
            return m, tea.Quit
        case "up", "k":
            if m.cursor > 0 { m.cursor-- }
        case "down", "j":
            if m.cursor < len(m.items)-1 { m.cursor++ }
        case "enter":
            fmt.Printf("Selected: %s\n", m.items[m.cursor])
            return m, tea.Quit
        }
    }
    return m, nil
}

func (m model) View() string {
    s := ""
    for i, item := range m.items {
        if i == m.cursor {
            s += highlight.Render("▸ "+item) + "\n"
        } else {
            s += "  " + item + "\n"
        }
    }
    return s + "\n(↑/↓ move, enter select, q quit)"
}

func main() {
    m := model{items: []string{"Option A", "Option B", "Option C"}}
    tea.NewProgram(m).Run()
}
```

### Library Cheat Sheet

| Need | Library | Example |
|------|---------|---------|
| TUI framework | `bubbletea` | `tea.NewProgram(model).Run()` |
| Components | `bubbles` | `list.New()`, `textinput.New()` |
| Styling | `lipgloss` | `style.Foreground(lipgloss.Color("212"))` |
| Forms (simple) | `huh` | `huh.NewInput().Title("Name").Run()` |
| Markdown | `glamour` | `glamour.Render(md, "dark")` |
| Animation | `harmonica` | `harmonica.NewSpring()` |

### Quick Patterns

**Styled Output (no TUI):**
```go
style := lipgloss.NewStyle().Foreground(lipgloss.Color("205")).Bold(true)
fmt.Println(style.Render("Hello!"))
```

**Simple Form (no TUI):**
```go
var name string
huh.NewInput().Title("Your name?").Value(&name).Run()
```

**Confirmation:**
```go
var ok bool
huh.NewConfirm().Title("Delete?").Value(&ok).Run()
```

**[Full Go TUI Reference →](references/go-tui.md)**
**[Component API →](references/component-catalog.md)**
**[Advanced Patterns →](references/advanced-patterns.md)**

---

## For Infrastructure

### Wish: SSH Apps

Serve TUI over SSH:

```go
s, _ := wish.NewServer(
    wish.WithAddress(":2222"),
    wish.WithHostKeyPath(".ssh/key"),
    wish.WithMiddleware(
        bubbletea.Middleware(handler),
        logging.Middleware(),
    ),
)
s.ListenAndServe()
```

Connect: `ssh localhost -p 2222`

### Other Infrastructure

| Tool | Purpose | Install |
|------|---------|---------|
| **Soft Serve** | Self-hosted Git | `brew install soft-serve` |
| **Pop** | Send email | `brew install pop` |
| **Skate** | Key-value store | `brew install skate` |
| **Melt** | SSH key backup | `brew install melt` |
| **Wishlist** | SSH gateway | `go install github.com/charmbracelet/wishlist` |

### Testing TUIs

```go
tm := teatest.NewTestModel(t, model)
tm.Send(tea.KeyMsg{Type: tea.KeyEnter})
tm.Type("hello")
teatest.WaitFor(t, tm, func(b []byte) bool {
    return strings.Contains(string(b), "expected")
})
```

**[Full Infrastructure Reference →](references/infrastructure.md)**

---

## Decision Guide

```
Is it a shell script?
├─ Yes → Use Gum
│        Need recording? → VHS
│        Need AI? → Mods
│
└─ No (Go application)
   │
   ├─ Just need styled output?
   │  └─ Lip Gloss only (no Bubble Tea)
   │
   ├─ Need simple prompts/forms?
   │  └─ Huh standalone
   │
   ├─ Need full interactive TUI?
   │  └─ Bubble Tea + Bubbles + Lip Gloss
   │
   └─ Need SSH access?
      └─ Wish + Bubble Tea
```

---

## When NOT to Use Charm

- **Output is piped:** `mytool | grep` → plain text
- **CI/CD:** No terminal → use flags/env vars
- **One simple prompt:** Maybe `fmt.Scanf` is fine

**Escape hatch:**
```go
if !term.IsTerminal(os.Stdin.Fd()) || os.Getenv("NO_TUI") != "" {
    runPlainMode()
    return
}
```

---

## All References

| Reference | Contents |
|-----------|----------|
| [Shell Scripts](references/shell-scripts.md) | Gum, VHS, Mods, Freeze, Glow - complete |
| [Go TUI](references/go-tui.md) | Bubble Tea patterns, debugging, anti-patterns |
| [Infrastructure](references/infrastructure.md) | Wish, Soft Serve, teatest, x/term |
| [Component Catalog](references/component-catalog.md) | All Bubbles components API |
| [Advanced Patterns](references/advanced-patterns.md) | Theming, layouts, production architecture |

Overview

This skill teaches how to build beautiful, practical terminal UIs using the Charmbracelet ecosystem for both shell scripts and Go applications. It maps the right tools for common needs—Gum, VHS, Mods, Freeze and Glow for shell; Bubble Tea, Bubbles, Lip Gloss and friends for Go; Wish and teatest for SSH and testing. You get clear patterns, short recipes, and decision guidance to pick the simplest, robust path for your CLI or TUI.

How this skill works

The skill inspects the target environment (shell script vs compiled Go binary vs SSH service) and recommends the minimal library or toolset to achieve a polished UI. For shell workflows it provides Gum-based primitives and VHS for demos; for Go it supplies Bubble Tea app patterns, component usage, styling with Lip Gloss, forms via Huh, markdown rendering, and animation. It also covers serving TUIs over SSH with Wish and testing with teatest.

When to use it

  • You have a shell or Bash script and want friendly prompts, spinners, or styled output without Go.
  • You are building a Go CLI that benefits from an interactive list, form, or full-screen TUI.
  • You need to record or demo terminal sessions as GIFs or videos (VHS).
  • You must expose a TUI over SSH or embed it in remote workflows.
  • You want repeatable TUI tests or CI-safe fallbacks for non-interactive environments.

Best practices

  • Choose the simplest tool that meets the need: Gum for scripts, Lip Gloss alone for styled prints, Bubble Tea for full interactivity.
  • Detect non-terminal environments and provide a plain-mode path (flags/env) to avoid breaking pipes or CI.
  • Keep UI components small and stateless where possible; use Bubble Tea models to centralize state and commands.
  • Use VHS for polished demos and Freeze for screenshots to document UX quickly.
  • Write unit and integration tests with teatest to validate key interactions and render output deterministically.

Example use cases

  • Interactive git commit helper in Bash using Gum choose + input + confirm.
  • A Go-based dashboard listing services with Bubble Tea, styled via Lip Gloss and animated hints from Harmonica.
  • An SSH-accessible admin tool using Wish to serve a Bubble Tea TUI for remote management.
  • Recording onboarding demos as GIFs with VHS and publishing them alongside docs using Glow-rendered markdown.
  • Automated TUI tests that simulate keypresses and assert output with teatest in CI.

FAQ

When should I not use a TUI?

Avoid TUIs when output is intended to be piped, when running in CI/non-interactive shells, or for trivial single prompts—use plain text flags or stdin instead.

Do I need Go to get nicer shell prompts?

No. Gum provides rich prompts, spinners, file pickers and styled blocks for shell scripts without any Go dependency.