cli-demo-recorder
Create demo videos for CLI and TUI tools. Use when user asks to "record a demo", "make a demo GIF", "create a demo video", "record my CLI tool", "asciinema demo", "demo recording script". Covers asciinema+agg+ffmpeg pipeline, tmux recording, multi-shell, and dual-purpose demo+test harnesses in Python, Rust, TypeScript, and Bash.
What this skill does
# CLI Demo Recorder
Record polished demo videos for CLI tools — either as **direct subprocess recordings** (CLI tools, no AI session) or **real TUI recordings** (interactive AI sessions via tmux).
**Use this skill when:** A demo GIF/video is needed for a CLI tool, plugin, or terminal application.
**Invoke with:** `/cli-demo-recorder` or "Help me record a demo for my CLI tool"
---
## Choose Your Pathway [Both]
Pick the pathway based on whether the tool has an interactive TUI session. Wrong choice → recording captures nothing useful.
| Tool type | Interactive TUI? | Uses AI/LLM? | Correct pathway |
|-----------|-----------------|--------------|-----------------|
| Pure CLI (aise, git, curl) | No | No | **CLI**: harness IS the recording |
| CLI + AI session (claude -p) | No TUI | Yes | **CLI**: verify output is useful |
| Plugin/hook for TUI tool | Via TUI | Yes | **TUI live**: tmux + pane |
| Plugin with hook-only acts | Via hook | No | **TUI scripted**: `run_hook()` + `--play` |
| Spawns interactive TUI | Yes | Maybe | **TUI live**: drive TUI via tmux |
| Batch/config tool | No | No | **CLI**: `capture_output=False` |
**WARNING**: Using `subprocess.run(capture_output=True)` for a CLI demo silences recording entirely — asciinema captures nothing. See CLI pathway for the correct pattern.
---
## How It Works [Both]
### Phase 1: Plan (15–30 min)
1. **Read the tool's docs first** — before writing a single act
2. **Choose 5–7 features** that are visible, immediate, and self-explanatory to newcomers
3. **Skip invisible features** (background daemons, auto-save without visible output)
4. **Choose your pathway** (see table above) — this determines the entire harness design
### Phase 2: Build the Harness (30–60 min)
- **CLI**: Python script that calls `subprocess.run(..., capture_output=False)`. asciinema records `python test_demo.py --run-acts`.
- **TUI scripted**: Python script that calls `run_hook()` directly for each act. asciinema records `python test_demo.py --play`.
- **TUI live**: Python script that creates a tmux session, sends prompts via `send-keys`, and asciinema attaches to that session.
### Phase 3: Record and Verify (10–30 min)
- **CLI**: `python tests/test_demo.py --record` → checks cast text fragments
- **TUI**: `python tests/test_demo.py --record` → parse JSONL for tool calls
### Phase 4: Convert [Both]
```bash
agg demo.cast demo.gif \
--theme dracula \
--font-size 14 \ # 14-16; smaller fits more content
--renderer fontdue \ # vector-quality anti-aliased text
--speed 0.75 \ # 0.75x — readable without pausing
--idle-time-limit 10 # 10s — preserves full banner display
# MP4: 4-strategy fallback (best compression first)
# Strategy 1: libx265 HEVC (tune=animation — ~50% smaller than libx264 at same quality)
ffmpeg -y -i demo.gif -movflags faststart \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
-c:v libx265 -preset slow -crf 28 -tune animation \
-pix_fmt yuv420p -tag:v hvc1 demo.mp4 2>/dev/null \
|| ffmpeg -y -i demo.gif -movflags faststart \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
-c:v libx264 -preset slow -crf 28 -tune animation \
-pix_fmt yuv420p demo.mp4 2>/dev/null \
|| ffmpeg -y -i demo.gif -movflags faststart \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
-c:v h264_videotoolbox -q:v 65 -pix_fmt yuv420p -color_range tv demo.mp4 2>/dev/null \
|| ffmpeg -y -i demo.gif -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
-pix_fmt yuv420p demo.mp4
```
**Important**: Convert GIF → MP4 (not cast → MP4). The GIF is already processed; going cast → MP4 directly misses the speed/idle adjustments from agg. Use `tune=animation` (not `tune=fast`) — terminal recordings have flat colors and sharp edges that match animation compression.
**Total:** ~75–120 minutes for a polished, verified demo.
---
## CLI Pathway [CLI Only]
### Architecture: Harness IS the Recording
For CLI tools, the Python harness runs as the command that asciinema records. No tmux, no pane attachment.
```
asciinema rec demo.cast --command "python test_demo.py --run-acts"
↑ harness runs here
→ harness types $ prompt, runs subprocess with capture_output=False
→ asciinema captures all stdout from the process
→ agg demo.cast demo.gif
```
```python
# ✅ CORRECT — output flows to terminal, asciinema captures it
subprocess.run("mytool subcommand", shell=True, capture_output=False, env=DEMO_ENV)
# ❌ WRONG for CLI demos — captures output into Python, asciinema sees nothing
result = subprocess.run(["mytool", "subcommand"], capture_output=True)
```
### Core Helpers [CLI Only]
```python
_TIMED = False # True only inside --run-acts (recording mode); _DEMO_WITH_TIMING also common
def pause(seconds: float) -> None:
"""No-op in pytest; sleeps during recording. Errors 1+2: timing ≠ spacing."""
if _TIMED:
time.sleep(seconds)
def _type(text: str, delay: float = 0.04) -> None:
if _TIMED:
for ch in text:
sys.stdout.write(ch); sys.stdout.flush(); time.sleep(delay)
else:
sys.stdout.write(text); sys.stdout.flush()
def _run(cmd: str) -> None:
"""Show typed $ prompt, then run command.
capture_output=False is CRITICAL — output must flow to terminal.
"""
_type(f"\n\033[1;32m$\033[0m ", delay=0)
_type(cmd + "\n", delay=0.045)
pause(0.3)
subprocess.run(cmd, env=DEMO_ENV, shell=True, capture_output=False, text=True)
def section(title: str) -> None:
"""Visual section divider between acts.
3 newlines BEFORE bar = visual gap from previous act (change this for spacing).
pause() durations = reading time (change separately for timing).
These are INDEPENDENT knobs — do not conflate them.
bar_len = max(68, len(title) + 6) prevents bars shorter than title.
"""
bar_len = max(68, len(title) + 6)
bar = "─" * bar_len
sys.stdout.write(f"\n\n\n\033[90m{bar}\033[0m\n")
sys.stdout.write(f"\033[1;96m {title}\033[0m\n")
sys.stdout.write(f"\033[90m{bar}\033[0m\n")
sys.stdout.flush()
```
### Intro Banner [CLI Only]
For CLI tools, the banner is a Python string printed directly to stdout:
```python
def banner() -> None:
W = 68 # compute padding on PLAIN text only — no ANSI codes inside len() math
def row(text: str = "", style: str = "") -> str:
content = (" " + text).ljust(W) # W visible chars; no ANSI in length
return f"\033[90m ║\033[0m{style}{content}\033[0m\033[90m║\033[0m"
# ❌ WRONG — ANSI codes inflate len(), misalign padding:
# bad = f"\033[1m{text}\033[0m".ljust(W)
lines = [
f"\033[90m ╔{'═'*W}╗\033[0m",
row("mytool — tagline here", "\033[1;96m"),
row(),
row("This demo shows:", "\033[90m"),
row(" 1. Feature one", "\033[90m"),
row(" 2. Feature two", "\033[90m"),
f"\033[90m ╚{'═'*W}╝\033[0m",
]
print("\n" + "\n".join(lines) + "\n")
```
### Privacy Isolation [CLI Only]
```python
# DEMO_DATA_DIR: committed synthetic fixtures (no real user data)
# TOOL_ISOLATION_VAR: env var that redirects the tool's data reads
# Examples: CLAUDE_CONFIG_DIR (aise), XDG_DATA_HOME, APP_DATA_DIR
DEMO_DATA_DIR = Path(__file__).parent / "tool-demo"
DEMO_ENV = {**os.environ, "TOOL_ISOLATION_VAR": str(DEMO_DATA_DIR)}
```
### Date-Shifting Fixtures [CLI Only]
Required when demo acts use `--since Nd`, `--after DATE`, or any time-relative filter.
```python
def create_dated_demo_dir() -> Path:
"""Copy DEMO_DATA_DIR to temp dir with timestamps shifted to near today.
Without this: fixtures from months ago → 0 results for --since 3d.
The committed fixture files are NEVER modified — only the temp copy is shifted.
Adapt _TS_RE and shift logic to match your tool's timestamp format.
"""
_TS_RE = re.compile(r'"timestamp":\s*"(\d{4}-\d{2}-\d{2}T[^"]+)"')
# find max timestamp in fixtures, compute delta to (today - 1 day)
tmp = Path(tempfile.mkRelated in Image & Video
watch
IncludedWatch a video (URL or local path). Downloads with yt-dlp, extracts auto-scaled frames with ffmpeg, pulls the transcript from captions (or Whisper API fallback), and hands the result to Claude so it can answer questions about what's in the video.
physical-ai-defect-image-generation
IncludedUse when the user wants to orchestrate defect image generation, run associated setup, or handle outputs on OSMO. The Day 0 path handles cold-start with USD-to-ROI, image-edit augmentation, and AnomalyGen to create initial PCBA datasets. The Day 1 path performs inference and labeling on real images. This skill helps with first-time asset setup, creation of finetuning checkpoints, and configuring deployment. Trigger keywords: defect image generation, dig workflow, dig pipeline, defect image detection workflow, aoi pipeline, aoi anomalygen, usd2roi anomalygen, day 0 pcba, day 1 pcba, day 1 real-photo alignment, day 1 manual roi, metal surface anomaly, glass defect, anomalygen finetune, setup_pcb, setup_metal, setup_glass, setup_pretrained, dig setup, dig datasets, dig pretrained checkpoint, dig image-edit endpoint.
accelint-react-best-practices
IncludedReact performance optimization and best practices. ALWAYS use this skill when working with any React code - writing components, hooks, JSX; refactoring; optimizing re-renders, memoization, state management; reviewing for performance; fixing hydration mismatches; debugging infinite re-renders, stale closures, input focus loss, animations restarting; preventing remounting; implementing transitions, lazy initialization, effect dependencies. Even simple React tasks benefit from these patterns. Covers React 19+ (useEffectEvent, Activity, ref props). Triggers - useEffect, useState, useMemo, useCallback, memo, inline components, nested components, components inside components, re-render, performance, hydration, SSR, Next.js, useDeferredValue, combined hooks.
elevenlabs-agents
IncludedBuild conversational AI voice agents with ElevenLabs Platform using React, JavaScript, React Native, or Swift SDKs. Configure agents, tools (client/server/MCP), RAG knowledge bases, multi-voice, and Scribe real-time STT. Use when: building voice chat interfaces, implementing AI phone agents with Twilio, configuring agent workflows or tools, adding RAG knowledge bases, testing with CLI "agents as code", or troubleshooting deprecated @11labs packages, Android audio cutoff, CSP violations, dynamic variables, or WebRTC config. Keywords: ElevenLabs Agents, ElevenLabs voice agents, AI voice agents, conversational AI, @elevenlabs/react, @elevenlabs/client, @elevenlabs/react-native, @elevenlabs/elevenlabs-js, @elevenlabs/agents-cli, elevenlabs SDK, voice AI, TTS, text-to-speech, ASR, speech recognition, turn-taking model, WebRTC voice, WebSocket voice, ElevenLabs conversation, agent system prompt, agent tools, agent knowledge base, RAG voice agents, multi-voice agents, pronunciation dictionary, voice speed control, elevenlabs scribe, @11labs deprecated, Android audio cutoff, CSP violation elevenlabs, dynamic variables elevenlabs, case-sensitive tool names, webhook authentication
humanizer
IncludedHumanize AI-generated text by detecting and removing patterns typical of LLM output. Rewrites text to sound natural, specific, and human. Uses 28 pattern detectors, 560+ AI vocabulary terms across 3 tiers, and statistical analysis (burstiness, type-token ratio, readability) for comprehensive detection. Use when asked to humanize text, de-AI writing, make content sound more natural/human, review writing for AI patterns, score text for AI detection, or improve AI-generated drafts. Covers content, language, style, communication, and filler categories.
generating-mermaid-diagrams
IncludedSalesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says "diagram", "visualize", "ERD", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG/SVG image output (use generating-visual-diagrams), or asks about non-Salesforce systems.