| name | demo-recording |
| description | Record a demo of a web application or terminal session. Uses Playwright for browser recordings and VHS for terminal recordings. Offers to attach the resulting GIF/video as a PR comment.
|
| user-invocable | true |
Demo Recording Skill
Record a demo of a running application or terminal session.
Workflow
-
Determine the type of demo.
- Browser demo: recording interactions in a web application.
- Terminal demo: recording a terminal session (commands, output).
-
Ask if the local dev environment is already running (for browser demos).
- If yes, ask for the URL (default:
http://localhost:3000).
- If no, ask the user how to start it, then run the provided command and
wait for the server to be ready before proceeding.
-
Ask what flow/steps to demonstrate.
Get a clear description of the interactions to script (pages to visit,
buttons to click, text to type, etc.).
-
Record the demo.
Browser demo (Playwright)
Install Playwright and Chromium to a temp directory:
npm install --prefix /tmp/pw playwright
npx --prefix /tmp/pw playwright install chromium
Create a script (e.g., /tmp/pw/demo-record.mjs) that performs the
described interactions with video recording enabled:
import { chromium } from '/tmp/pw/node_modules/playwright/index.mjs';
const url = process.argv[2] || 'http://localhost:3000';
const browser = await chromium.launch();
const context = await browser.newContext({
recordVideo: { dir: '/tmp/pw/', size: { width: 1600, height: 900 } },
viewport: { width: 1600, height: 900 }
});
const page = await context.newPage();
await page.goto(url, { waitUntil: 'networkidle' });
await page.waitForTimeout(3000);
await context.close();
await browser.close();
Run with:
node /tmp/pw/demo-record.mjs "<URL>"
Tips:
- Add
page.waitForTimeout(2000–3000) pauses between actions so the
recording is easy to follow.
- For JS-heavy apps (Grafana, SPAs), use longer waits after navigation.
- Use
waitUntil: 'networkidle' for every page.goto() call.
Terminal demo (VHS)
Create a .tape file describing the session:
Output demo.gif
Set FontSize 14
Set Width 1200
Set Height 600
Type "command here"
Enter
Sleep 2s
Run with:
vhs demo.tape
If VHS is not available, fall back to asciinema + agg:
asciinema rec demo.cast
agg demo.cast demo.gif
-
Convert to GIF if needed (for browser recordings that output .webm):
ffmpeg -i /tmp/pw/*.webm -vf "fps=15,scale=1280:-1:flags=lanczos" -loop 0 demo.gif
If the GIF is too large (>10MB), reduce fps or scale:
ffmpeg -i /tmp/pw/*.webm -vf "fps=10,scale=800:-1:flags=lanczos" -loop 0 demo.gif
-
Show the result to the user using the read tool on the resulting GIF/video
file so they can verify it looks correct.
-
Ask if the user wants the recording added as a comment on the current
pull request.
- If yes, determine the current PR number:
gh pr view --json number -q .number
- Never commit the GIF to the repository. Always upload it as a GitHub
comment attachment and reference the resulting URL:
gh pr comment <number> --body "## Demo" --edit-last 2>/dev/null || true
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
ASSET_URL=$(curl -sS \
-H "Authorization: token $(gh auth token)" \
-H "Accept: application/json" \
-F "file=@demo.gif;type=image/gif" \
"https://uploads.github.com/repos/${REPO}/issues/${PR_NUMBER}/comments/assets" \
| jq -r '.[0].href // .[0].url')
gh pr comment <number> --body "## Demo
"
- Do NOT use
git add, git commit, or git push for demo GIFs.
They bloat the repository and are not source code.
Notes
- Use Chromium, not Firefox. The Playwright Firefox build has compatibility
issues with JS-heavy apps (e.g., Grafana fails to load application files).
- Use the programmatic API with ESM imports from
/tmp/pw/node_modules/playwright/index.mjs.
- Install to
/tmp/pw to avoid polluting the project directory.
- Keep recordings short and focused (aim for < 30 seconds).
- Clean up intermediate files (
.webm, scripts) after the final GIF is produced,
unless the user says otherwise.
- If both VHS and asciinema are unavailable for terminal demos, inform the user
and suggest installing one via nix (
pkgs.vhs or pkgs.asciinema).