|
1 | 1 | # assets/hero.tape — generates the README hero GIF with VHS (charmbracelet/vhs). |
2 | 2 | # |
3 | | -# Run from the repo root: |
4 | | -# vhs assets/hero.tape # writes assets/hero.gif |
| 3 | +# Regenerate (from the repo root): |
| 4 | +# mise run demo:hero # builds the dev CLI, then runs this tape |
5 | 5 | # |
6 | | -# Prereqs (vhs 0.11.0 + jq are pinned in mise's [tools]): |
7 | | -# - `agent-tty` on PATH (npm i -g agent-tty, or `npm run build && npm link`) |
8 | | -# - `jq` |
9 | | -# It uses the fast, browser-free `libghostty-vt` renderer so the GIF stays short |
10 | | -# (no Chromium boot). For a crisper, tighter, more "terminal" look, tune |
11 | | -# FontFamily (must be installed!), FontSize, LineHeight, LetterSpacing, and |
12 | | -# Padding below, plus the per-step Sleeps. The session is sized to 72x18 so a |
13 | | -# 26pt font fills the frame without the 80-col default wrapping. Regenerate, |
14 | | -# then replace the HERO DEMO comment block in README.md with: |
15 | | -#  |
| 6 | +# That task `depends` on `build` and puts vhs/ffmpeg/tmux/jq on PATH. To run the |
| 7 | +# tape directly instead, build first and provide those tools yourself: |
| 8 | +# npm run build && vhs assets/hero.tape |
| 9 | +# |
| 10 | +# Why build first: this hero shows `agent-tty dashboard`, which is unreleased — |
| 11 | +# a globally-installed `agent-tty` (e.g. `npm i -g agent-tty`) won't have it. So |
| 12 | +# the hidden setup below points PATH at THIS checkout's freshly-built CLI |
| 13 | +# (dist/cli/main.js) instead of `Require agent-tty`. It also needs the |
| 14 | +# dashboard's optional renderer `@coder/libghostty-vt-node` (a normal `npm i` |
| 15 | +# fetches it; check with `agent-tty doctor`). |
| 16 | +# |
| 17 | +# Prereqs (jq + tmux are pinned in mise's [tools]; tmux >= 3.1 for `-l 50%`): |
| 18 | +# - `npm run build` has been run in this checkout |
| 19 | +# - `jq`, `tmux` |
| 20 | +# - `ttyd` (VHS needs it): mise installs it on Linux, but it has no macOS |
| 21 | +# binary — on macOS run `brew install ttyd` yourself (mise finds it on PATH). |
| 22 | +# |
| 23 | +# Structure: the hidden setup builds a clean tmux split (left = operator shell, |
| 24 | +# right = idle shell). The panes and the agent-tty session all run `bash --norc` |
| 25 | +# (with a minimal `$ ` prompt on the panes via PS1) so they — and the dashboard's |
| 26 | +# live mirror — stay free of personal prompt clutter; `--norc` is what keeps the |
| 27 | +# user's interactive shell config (e.g. zsh `%{…%}` prompt escapes) out of the |
| 28 | +# frame. The visible recording then *types* `agent-tty dashboard` into the right |
| 29 | +# pane (so viewers see how it's launched), hops back to the left with the tmux |
| 30 | +# prefix, and drives a session whose changes the dashboard mirrors live. |
| 31 | +# AGENT_TTY_HOME is exported before tmux so the server and both panes share it. |
| 32 | +# (For a minimal `$ ` prompt in the mirror too, add `--env 'PS1=$ '` to create.) |
| 33 | +# |
| 34 | +# TUNING (do a visual pass after regenerating): Width/Height/FontSize and the |
| 35 | +# split percentage (`-l 50%`) trade off readability of the two panes. The split |
| 36 | +# is 50/50; FontSize is 18 so the longest CLI lines fit one un-split half — bump |
| 37 | +# the font only if you also widen the frame, or the operator-pane lines wrap. |
| 38 | +# The session defaults to 80x24, a touch wider than the half-width dashboard |
| 39 | +# pane, so its mirror clips to the top-left (where the `echo` output lands); add |
| 40 | +# `--cols/--rows` to `create` for a tighter, fully-visible mirror. FontFamily fallbacks if FiraCode |
| 41 | +# isn't installed: "Menlo", "SF Mono", "JetBrains Mono". Keep the README hero |
| 42 | +# pointing at ./assets/hero.gif: |
| 43 | +#  |
16 | 44 |
|
17 | 45 | Output assets/hero.gif |
18 | 46 |
|
19 | | -Require agent-tty |
20 | 47 | Require jq |
| 48 | +Require tmux |
21 | 49 |
|
22 | 50 | Set Shell bash |
23 | 51 | # Use a font that's actually installed (VHS silently falls back to an ugly |
24 | 52 | # default otherwise). FiraCode Nerd Font Mono is on this machine and reads clean. |
25 | | -# Bulletproof alternatives: "Menlo", "SF Mono", "Monaco", "JetBrains Mono". |
26 | 53 | Set FontFamily "FiraCode Nerd Font Mono" |
27 | | -Set FontSize 26 |
28 | | -Set Width 1280 |
29 | | -Set Height 640 |
30 | | -Set Padding 16 # tighter frame (was 28) |
31 | | -Set LineHeight 1.0 # tight lines; nudge to ~1.15 if they touch |
32 | | -Set LetterSpacing 0 # no extra tracking |
33 | | -Set Theme "Catppuccin Mocha" # any VHS theme works; try "Dracula", "Nord" |
| 54 | +# 18pt (not 20) so each half of the 50/50 split is wide enough for the longest |
| 55 | +# CLI lines (the `create … | jq` and `run --no-wait …` lines) without wrapping. |
| 56 | +Set FontSize 18 |
| 57 | +Set Width 1920 |
| 58 | +Set Height 720 |
| 59 | +Set Padding 16 |
| 60 | +Set LineHeight 1.0 |
| 61 | +Set LetterSpacing 0 |
| 62 | +Set Theme "Catppuccin Mocha" |
34 | 63 | Set TypingSpeed 40ms |
35 | 64 | Set PlaybackSpeed 1.0 |
36 | 65 |
|
37 | | -# --- hidden setup: isolated home + fast native renderer, then a clean screen --- |
| 66 | +# --- hidden setup: dev CLI on PATH + a clean tmux split (operator | idle) --- |
| 67 | +# AGENT_TTY_HOME, PATH, and PS1 are exported BEFORE tmux so the server and both |
| 68 | +# `bash --norc` panes inherit them. The split runs idle shells; the dashboard is |
| 69 | +# launched visibly below. kill-server first makes regeneration idempotent. |
38 | 70 | Hide |
39 | 71 | Type "export AGENT_TTY_HOME=$(mktemp -d) AGENT_TTY_RENDERER=libghostty-vt" Enter |
40 | | -Type "clear" Enter |
| 72 | +Type "export HERO_BIN=$(mktemp -d)" Enter |
| 73 | +Type 'chmod +x dist/cli/main.js && ln -sf "$PWD/dist/cli/main.js" "$HERO_BIN/agent-tty"' Enter |
| 74 | +Type 'export PATH="$HERO_BIN:$PATH"' Enter |
| 75 | +Type "export PS1='$ '" Enter |
| 76 | +Type "tmux -L hero kill-server 2>/dev/null; true" Enter |
| 77 | +Type "tmux -f /dev/null -L hero new-session -d -s hero 'bash --norc' \; set -g status off \; split-window -h -l 50% -t hero 'bash --norc' \; select-pane -t hero.0 \; attach -t hero" Enter |
41 | 78 | Show |
42 | 79 |
|
43 | | -Sleep 800ms |
44 | | -Type "# open a long-lived terminal session" Enter |
| 80 | +Sleep 1000ms |
| 81 | +Type "# drive a real terminal session with plain CLI calls" Enter |
45 | 82 | Sleep 500ms |
46 | | -Type 'SID=$(agent-tty create --json --cols 72 --rows 18 -- bash | jq -r .result.sessionId)' Enter |
| 83 | +Type 'SID=$(agent-tty create --json -- bash --norc | jq -r .result.sessionId)' Enter |
47 | 84 | Sleep 1200ms |
48 | 85 |
|
49 | | -Type "# run a command inside it" Enter |
| 86 | +Type "# open the dashboard on the right to watch it live →" Enter |
| 87 | +Sleep 600ms |
| 88 | +# hop to the right pane with the tmux prefix (Ctrl+B then o = next pane) |
| 89 | +Ctrl+B |
| 90 | +Type "o" |
| 91 | +Sleep 500ms |
| 92 | +Type "agent-tty dashboard --all" Enter |
| 93 | +Sleep 2200ms |
| 94 | +# hop back to the left pane to keep driving the session |
| 95 | +Ctrl+B |
| 96 | +Type "o" |
| 97 | +Sleep 600ms |
| 98 | + |
| 99 | +Type "# run a command inside it — watch the dashboard mirror it live →" Enter |
50 | 100 | Sleep 500ms |
51 | 101 | Type 'agent-tty run "$SID" "echo hello from agent-tty"' Enter |
52 | | -Sleep 1500ms |
| 102 | +Sleep 2200ms |
53 | 103 |
|
54 | | -Type "# wait for the screen — no sleeps, no grep" Enter |
| 104 | +Type "# fire a slow command, then wait for its OUTPUT — no sleeps, no polling →" Enter |
55 | 105 | Sleep 500ms |
56 | | -Type 'agent-tty wait "$SID" --text "hello from agent-tty"' Enter |
57 | | -Sleep 1500ms |
| 106 | +# Two things make the wait meaningful here: |
| 107 | +# 1. $RANDOM is single-quoted so the SESSION expands it — the echoed command |
| 108 | +# line shows the literal "$RANDOM", not a number. |
| 109 | +# 2. We therefore wait on a DIGIT after the colon (--regex), NOT the phrase |
| 110 | +# "your random number is:". That phrase is already on screen from the echoed |
| 111 | +# command, so a --text wait would match instantly and prove nothing; the |
| 112 | +# digits only appear once the command actually prints, after the 6s sleep. |
| 113 | +Type "agent-tty run --no-wait $SID 'sleep 6; echo your random number is: $RANDOM'" Enter |
| 114 | +# `wait` is typed right after the fire (no comment between) so the bulk of the 6s |
| 115 | +# sleep elapses WHILE wait blocks — the deterministic wait is visible on camera. |
| 116 | +Sleep 400ms |
| 117 | +Type 'agent-tty wait "$SID" --regex "random number is: [0-9]+"' Enter |
| 118 | +Sleep 5000ms |
58 | 119 |
|
59 | | -Type "# inspect the rendered screen as text you can diff" Enter |
| 120 | +Type "# and snapshot the result as text you can diff" Enter |
60 | 121 | Sleep 500ms |
61 | 122 | Type 'agent-tty snapshot "$SID" --format text' Enter |
62 | 123 | Sleep 2800ms |
63 | 124 |
|
64 | | -Type "# screenshots, asciicasts and WebM export come from the same session" Enter |
65 | | -Sleep 1200ms |
66 | | - |
67 | | -# --- hidden teardown --- |
| 125 | +# --- hidden teardown: stays hidden to the end, so the GIF's last frame is the |
| 126 | +# split — NOT the bare outer shell that `kill-server` drops back to (a trailing |
| 127 | +# `Show` here flashes that shell + a "[server exited]" line, which looks ugly) --- |
68 | 128 | Hide |
69 | 129 | Type 'agent-tty destroy "$SID" >/dev/null 2>&1' Enter |
70 | | -Show |
71 | | -Sleep 500ms |
72 | | - |
73 | | -# ----------------------------------------------------------------------------- |
74 | | -# ALTERNATIVE — dogfood it: record the loop with agent-tty itself, then convert. |
75 | | -# Drive the same create/run/wait/snapshot sequence, then: |
76 | | -# agent-tty record export "$SID" --format webm --out demo.webm |
77 | | -# ffmpeg -i demo.webm -vf "fps=12,scale=1200:-1:flags=lanczos" assets/hero.gif |
78 | | -# This makes the hero GIF literally the tool's own output ("recorded by the tool |
79 | | -# it documents"), at the cost of the Chromium-backed ghostty-web render path. |
80 | | -# ffmpeg isn't in mise's [tools] (it can't be cross-locked on Linux via conda); |
81 | | -# install it yourself for this path (brew install ffmpeg / apt-get install ffmpeg). |
| 130 | +Type "tmux -L hero kill-server" Enter |
| 131 | +# kill-server returns to the outer shell, which still holds these vars; clean up |
| 132 | +# the temp home + bin so repeated local runs don't litter $TMPDIR. |
| 133 | +Type 'rm -rf "$AGENT_TTY_HOME" "$HERO_BIN"' Enter |
| 134 | +# Hidden settle time so the cleanup completes before the tape ends (no Show). |
| 135 | +Sleep 1s |
0 commit comments