Skip to main content
skill CX Codex

cli-demo-generator — Claude Code skill: animated CLI GIFs

Records terminal commands through VHS and hands back an animated GIF. Useful for README demos and CLI documentation.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add daymade/claude-code-skills --skill cli-demo-generator -a claude-code
Open on GitHub

What cli-demo-generator does

The skill drives VHS, a tool that plays a tape file and captures the terminal as a GIF. You supply the commands, it writes the file. Four routes in: automated generation with auto_generate_demo.py, batch builds from a YAML config, interactive recording of a live session, and a hand-written tape when you want frame-level control.

Inside are Python scripts and tape templates. Automated generation picks its own pauses: 3 seconds for install, build, test and deploy, one second for ls, pwd and echo. Flags cover theme, font size, terminal width and height, plus --bootstrap for hidden setup and --filter to strip noisy output lines.

The VHS parser is where things break. Type strings reject $, \" and backticks, so any command carrying those has to be base64-encoded and decoded inside the tape. VHS itself is required (brew install charmbracelet/tap/vhs); gifsicle, asciinema and ffmpeg are optional, for speeding up a recording, capturing a live session and checking frames.

What it helps with

  • record a package install and build into one README GIF
  • generate several demos from a single YAML config
  • keep environment setup out of the viewer's sight
  • strip noisy progress lines from command output
  • speed up an existing recording without re-recording

Example prompts for the agent

  • Make a GIF demo: run npm install my-package, then npm run build, save it as demo.gif
  • Record an interactive terminal session to output.gif with the Catppuccin Latte theme
  • Build the demos from demos.yaml into ./gifs

How to install cli-demo-generator

1Skills CLI (Vercel)

npx skills add daymade/claude-code-skills --skill cli-demo-generator -a claude-code

2Manually: put the files in place

~/.claude/skills/cli-demo-generator/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/cli-demo-generator/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about cli-demo-generator

How do I install cli-demo-generator in Claude?

Run npx skills add daymade/claude-code-skills --skill cli-demo-generator -a claude-code. The skill lands at ~/.claude/skills/cli-demo-generator/SKILL.md.

What does cli-demo-generator do?

It records command-line sessions through VHS and saves them as an animated GIF. Built for README demos and CLI documentation.

Why does VHS fail to parse my command?

VHS Type strings reject $, \" and backticks. Encode a command containing them as base64 and decode it inside the tape file.

Source text

SKILL.md · MIT
---
name: cli-demo-generator
description: >-
  Generates professional animated CLI demos as GIFs using VHS terminal recordings. Use to create
  terminal demos, record CLI workflows as GIFs, generate animated documentation, build demo tapes
  for README files, or showcase any command-line tool visually: record terminal / VHS tape / demo
  GIF / animate my CLI.
---

# CLI Demo Generator

Create professional animated CLI demos. Four approaches, from fully automated to pixel-precise manual control.

## Quick Start

**Simplest path** — give commands, get GIF:

```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/auto_generate_demo.py \
  -c "npm install my-package" \
  -c "npm run build" \
  -o demo.gif
```

**Self-bootstrapping demo** — for repeatable recordings that clean their own state:

```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/auto_generate_demo.py \
  -c "npm install my-package" \
  -c "npm run build" \
  -o demo.gif \
  --bootstrap "npm uninstall my-package 2>/dev/null" \
  --speed 2
```

## Critical: VHS Parser Limitations

VHS `Type` strings cannot contain `$`, `\"`, or backticks. These cause parse errors:

```tape
# FAILS — VHS parser rejects special chars
Type "echo \"hello $USER\""
Type "claude() { command claude \"$@\"; }"
```

**Workaround: base64 encode the command**, decode at runtime:

```bash
# 1. Encode your complex command
echo 'claude() { command claude "$@" 2>&1 | grep -v "noise"; }' | base64
# Output: Y2xhdWRlKCkgey4uLn0K

# 2. Use in tape
Type "echo Y2xhdWRlKCkgey4uLn0K | base64 -d > /tmp/wrapper.sh && source /tmp/wrapper.sh"
```

This pattern is essential for output filtering, function definitions, and any command with shell special characters.

## Approaches

### 1. Automated Generation (Recommended)

```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/auto_generate_demo.py \
  -c "command1" -c "command2" \
  -o output.gif \
  --title "My Demo" \
  --theme "Catppuccin Latte" \
  --font-size 24 \
  --width 1400 --height 600
```

| Flag | Default | Description |
|------|---------|-------------|
| `-c` | required | Command to include (repeatable) |
| `-o` | required | Output GIF path |
| `--title` | none | Title shown at start |
| `--theme` | Dracula | VHS theme name |
| `--font-size` | 16 | Font size in pt |
| `--width` | 1400 | Terminal width px |
| `--height` | 700 | Terminal height px |
| `--bootstrap` | none | Hidden setup command (repeatable) |
| `--filter` | none | Regex pattern to filter from output |
| `--speed` | 1 | Playback speed multiplier (uses gifsicle) |
| `--no-execute` | false | Generate .tape only |

Smart timing: `install`/`build`/`test`/`deploy` → 3s, `ls`/`pwd`/`echo` → 1s, others → 2s.

### 2. Batch Generation

Create multiple demos from one config:

```yaml
# demos.yaml
demos:
  - name: "Install"
    output: "install.gif"
    commands: ["npm install my-package"]
  - name: "Usage"
    output: "usage.gif"
    commands: ["my-package --help", "my-package run"]
```

```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/batch_generate.py demos.yaml --output-dir ./gifs
```

### 3. Interactive Recording

Record a live terminal session:

```bash
bash ${CLAUDE_SKILL_DIR}/scripts/record_interactive.sh output.gif --theme "Catppuccin Latte"
# Type commands naturally, Ctrl+D when done
```

Requires asciinema (`brew install asciinema`).

### 4. Manual Tape File

For maximum control, write a tape directly. Templates in `assets/templates/`:

- `basic.tape` — simple command sequence
- `interactive.tape` — typing simulation
- `self-bootstrap.tape` — **self-cleaning demo with hidden setup** (recommended for repeatable demos)

## Advanced Patterns

These patterns come from production use. See `references/advanced_patterns.md` for full details.

### Self-Bootstrapping Demos

Demos that clean previous state, set up environment, and hide all of it from the viewer:

```tape
Hide
Type "cleanup-previous-state 2>/dev/null"
Enter
Sleep 2s
Type "clear"
Enter
Sleep 500ms
Show

Type "the-command-users-see"
Enter
Sleep 3s
```

The `Hide` → commands → `clear` → `Show` sequence is critical. `clear` wipes the terminal buffer so hidden commands don't leak into the GIF.

### Output Noise Filtering

Filter noisy progress lines from commands that produce verbose output:

```tape
# Hidden: create a wrapper function that filters noise
Hide
Type "echo <base64-encoded-wrapper> | base64 -d > /tmp/w.sh && source /tmp/w.sh"
Enter
Sleep 500ms
Type "clear"
Enter
Sleep 500ms
Show

# Visible: clean command, filtered output
Type "my-noisy-command"
Enter
Sleep 3s
```

### Frame Verification

After recording, verify GIF content by extracting key frames:

```bash
# Extract frames at specific positions
ffmpeg -i demo.gif -vf "select=eq(n\,100)" -frames:v 1 /tmp/frame.png -y 2>/dev/null

# View the frame (Claude can read images)
# Use Read tool on /tmp/frame.png to verify content
```

### Post-Processing Speed-Up

Use gifsicle to speed up recordings without re-recording:

```bash
# 2x speed (halve frame delay)
gifsicle -d2 original.gif "#0-" > fast.gif

# 1.5x speed
gifsicle -d4 original.gif "#0-" > faster.gif
```

### Template Placeholder Pattern

Keep tape files generic with placeholders, replace at build time:

```tape
# In tape file
Type "claude plugin marketplace add MARKETPLACE_REPO"

# In build script
sed "s|MARKETPLACE_REPO|$DETECTED_REPO|g" template.tape > rendered.tape
vhs rendered.tape
```

## Timing & Sizing Reference

| Context | Width | Height | Font | Duration |
|---------|-------|--------|------|----------|
| README/docs | 1400 | 600 | 16-20 | 10-20s |
| Presentation | 1800 | 900 | 24 | 15-30s |
| Compact embed | 1200 | 600 | 14-16 | 10-15s |
| Wide output | 1600 | 800 | 16 | 15-30s |

See `references/best_practices.md` for detailed guidelines.

## Troubleshooting

| Problem | Solution |
|---------|----------|
| VHS not installed | `brew install charmbracelet/tap/vhs` |
| gifsicle not installed | `brew install gifsicle` |
| GIF too large | Reduce dimensions, sleep times, or use `--speed 2` |
| Text wraps/breaks | Increase `--width` or decrease `--font-size` |
| VHS parse error on `$` or `\"` | Use base64 encoding (see Critical section above) |
| Hidden commands leak into GIF | Add `clear` + `Sleep 500ms` before `Show` |
| Commands execute before previous finishes | Increase `Sleep` duration |

## Tape File Syntax

`references/vhs_syntax.md` is the full VHS tape-file reference — every command,
setting, and key name, with the argument forms each accepts. Read it when writing
a tape by hand, when a tape parses but behaves unexpectedly, or when you need a
command this SKILL.md does not show an example of.

## Dependencies

**Required:** VHS (`brew install charmbracelet/tap/vhs`)

**Optional:** gifsicle (speed-up), asciinema (interactive recording), ffmpeg (frame verification), PyYAML (batch YAML configs)

© daymade · MIT · daymade/claude-code-skills

Similar skills

Everything for Writing and docs →

From the same set64

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor