agentleFS
calebnewtonusccalebnewtonusc/Chewbacca/skills/demo/SKILL.md

demo

Skill8 starsChanged 3 months agoMITView on GitHub

Record a product demo video by reading the product's own code and the craft of demo-making, not by guessing at either. Use when the user asks to record a demo, make a demo video, film the product, show the product working, or produce a launch clip. Also use when a demo already recorded is wrong, boring, or missed the point of the product. Needs the repo and a running instance.

26 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use spec, implement and 3 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • SentrySkill

    spec

    Produce a verified spec — problem, desired outcome, and acceptance criteria — before building. Use when given a GitHub/Linear issue link, when starting from a fuzzy in-conversation idea, or asked to "figure out what to do" / "scope this" / "what should I build" — whenever the *what* isn't yet pinned down. Grills the ambiguities, checks the premise against the code, and produces acceptance criteria you sign off on — then hands to design-first.

    Also in 4 projects that use this

  • Hugging FaceSkill

    implement

    Make tests pass. Invoke after /write-tests produces failing tests.

    Also in 4 projects that use this

  • SentrySkill

    review

    Fixture review skill.

    Also in 4 projects that use this

  • AWSSkill

    ship

    Finalize dev work on an issue branch — update docs, push, and open a PR.

    Also in 3 projects that use this

  • NVIDIASkill

    research

    Before designing a feature, research how comparable libraries solve the problem, check best practices, known pitfalls, and upstream changes. Use when starting a new feature, investigating an unfamiliar area, or the user asks to research something.

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

8 rules. 0 are common, 7 are distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 24 edited versions we read keep these.

  • 7

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Run against a real site it produced this:

    line 15
  2. Distinctive:

    No CTA found, so it scrolled.

    line 22
  3. Distinctive:

    What is the single moment that shows it?: Not the homepage. The moment the thing pays off: the search returning cooks, the file becoming a …

    line 116
  4. Distinctive:

    What is the shortest path from load to that moment?: Every beat that is not on that path is cut.

    line 119
  5. Very specific:

    After the export, pull four beat frames and look again.

    line 180
  6. Distinctive:

    A camera cut must be a content cut: Never cut mid-idle.

    line 196
  7. Distinctive:

    Cut on the action, resume on the loaded page: No spinners, no blur-up.

    line 197
  8. Distinctive:

    When the thing to show is a web page that already plays on its own (a WebGL scene, a scripted demo, a scroll-synced story), a screen …

    line 212

“Common” is measured: kept by at least 6 in 10 of the 24 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Record a demo of the product, from its own source

The failure this exists to fix

Cap's bundled cap-demo skill scouts the page at runtime and scores every anchor and button to pick a "CTA": +3 if the text matches /features|pricing|docs/, +2 for a background colour, -5 for "sign up". Nothing in that scoring has any knowledge of the product it is pointed at.

Run against a real site it produced this:

STORY=scroll cta=none accent=null
cursor track synthesized: 182 moves, 0 clicks

No CTA found, so it scrolled. Zero clicks in a product demo. Nothing was demonstrated, because nothing in that pipeline knows what the product does.

You are not in that position. You have the repo. Read it, decide what the demo should show, and write the storyboard yourself. That is the whole skill.

Before any of that: you do not know how to make a demo video

You know how to drive a UI and run an encoder. Those are the mechanics. A demo video is a craft with its own rules, and the first version of this skill had none of them, so the first demo it produced was four taps on a tab bar. That is a screen recording. Caleb's response was "there's more to UX than just the nav bar lol", and he was right.

Research the craft before producing the artifact. For a demo that means watching people who make them for a living, not reasoning from first principles about what a demo probably is. The rules below came from doing that once, and they are here so the research does not have to be repeated. If the work drifts outside them, go watch two more and add what you learn.

The rules, from practitioners who have made hundreds

One to three features. Never more. Stated as the biggest mistake people make: "include way too many features... otherwise people will just switch off." This is the rule most likely to be overridden by the person asking, who will say "show everything". Showing everything is the failure mode with a name.

A storyline, not a tour. The demos that work have "a clear narrative... not jumping around between different aspects of the product." Ask what one job the viewer watches somebody complete. Most products already have this written down in their own onboarding copy; Silo's how-it-works screen says "Three taps to dinner", which is the storyboard, pre-written by the founder.

Cut the login. "I often see people record their login process, but it's not really something that people find that interesting."

Show it with data in it. A populated product is the whole advantage a demo has over a free trial. Empty states are not a feature.

Do not over-zoom. "It can actually be a little bit dizzying... most people really want to see the context about what you're interacting with." Zoom for one specific thing, then pull back out.

Describe the benefit, not the action. Not "now we click the filter": "you see only what is still open near you."

Increase complexity over time. Open on the simplest possible beat and build.

End on a CTA. Somebody who reached the end is the most engaged viewer the product will get that week.

When the brief and the craft disagree

"Show me all the screens" is a reasonable thing to ask for and a bad demo. Say so in one line, make the narrative version, and offer the full screen tour as a separate artifact if it is still wanted. Do not silently deliver either one.

What to read, in order of how much it is worth

1. The e2e specs, which are where you should always start. e2e/, tests/, cypress/, playwright/, *.spec.ts, *.cy.ts. These are the single best source in any repo and almost nobody thinks to use them: a passing e2e test is a recorded script of the exact journey the team already decided is the product, written with the real selectors, in the real order, with the real fixture data. A demo plan is an e2e spec with dwell times.

rg -l "test\(|it\(|describe\(" --glob "**/*.{spec,cy,e2e}.{ts,tsx,js}" .
rg "getByTestId|data-testid|getByRole" -N --glob "**/*.spec.ts" . | head -40

2. data-testid attributes. Stable by contract, which is exactly what a storyboard needs. A class name is a refactor away from breaking the demo.

rg -o 'data-testid="[^"]+"' -N . | sort -u

3. The routes. app/**/page.tsx, pages/, the router config. They are the product's own table of contents, and the order they were built in usually is the order they should be shown in.

4. The landing copy and the README. What the product claims to do is what the demo has to deliver. If the hero says "find a home cook near you" and the demo never finds a cook, the demo is wrong no matter how good it looks.

5. Recent commits. git log --oneline -30. For a launch clip, the feature that just shipped is usually the point.

Then decide the story, out loud, before writing any JSON

Answer these three in one line each. If you cannot, read more:

  • What does this product do for someone? One sentence, their words.
  • What is the single moment that shows it? Not the homepage. The moment the thing pays off: the search returning cooks, the file becoming a chart, the message arriving.
  • What is the shortest path from load to that moment? Every beat that is not on that path is cut. You have 12 seconds.

Write the plan

{
  "url": "http://localhost:3000",
  "beats": [
    { "do": "aim", "selector": "h1", "label": "hero" },
    { "do": "dwell", "ms": 1200, "label": "settle" },
    {
      "do": "type",
      "selector": "[data-testid=search]",
      "text": "tacos",
      "label": "search"
    },
    {
      "do": "click",
      "selector": "[data-testid=search-submit]",
      "label": "run_search"
    },
    { "do": "scroll", "selector": "[data-testid=results]", "label": "results" },
    { "do": "idle", "ms": 1800, "label": "land" }
  ]
}

Beats: aim (mark a landmark for the 3D shot, moves nothing), dwell, move, click, type, scroll (by selector or by pixels), idle.

Common keys: label names the beat in the timeline, ms is the glide or dwell duration, settle is the pause after, text on a click disambiguates by visible text, waitFor: false on a click that does not navigate.

A missing selector is a hard error, on purpose. A demo that silently skips its own climax is worse than one that stops and tells you the selector moved.

Run it

demo-shoot plan.json <outDir> <slug>
python3 ~/.claude/skills/cap-demo/lib/treat.py <outDir> <slug> [--bg-gradient FROM,TO]

demo-shoot.mjs writes <slug>.cap and <slug>.timeline.json in Cap's own format, so Cap's treat.py does the 3D camera, gradient, cursor and music unchanged. That stage is Cap's work and is good; only the scouting was the problem.

Frame-QA between the stages. Do not skip this.

ffmpeg -y -ss <t> -i <outDir>/<slug>.cap/content/segments/segment-0/display.mp4 \
  -frames:v 1 /tmp/raw-<t>.png

Then look at the frames. Check the right content is captured, no cookie banner leaked, the click landed somewhere that proves the product works, and the tail is clean. Reshoot before spending an export.

After the export, pull four beat frames and look again. Check the fps first, because cap record --detach engages late and the true rate is often ~58, not 60. Passing --quality hd on a 58fps capture buys judder:

ffprobe -v error -select_streams v:0 -show_entries stream=nb_frames,duration \
  -of csv=p=0 <outDir>/<slug>.cap/content/segments/segment-0/display.mp4

Set the brand gradient by eye. getComputedStyle lies on pastel and gradient-heavy sites: one that reads white with lavender accents samples as black. Look at the raw frame and pass --bg-gradient.

The editorial rules, which are Cap's and are right

  • 12 seconds, hard. Past that the tail is shaved evenly and the ending is lost. demo-shoot.mjs refuses a plan that budgets over it.
  • A camera cut must be a content cut. Never cut mid-idle.
  • Cut on the action, resume on the loaded page. No spinners, no blur-up.
  • One motion system. The 3D shot carries the emphasis. Never stack 2D zoom segments on top.
  • Aim at content, not the container. Point at the hero, the clicked thing, the destination header.

Requirements

macOS on Apple Silicon. cap on PATH with Screen Recording granted, node, python3, ffmpeg. npm install once in ~/.claude/skills/cap-demo for playwright-core. The product must be running and reachable at the plan's url, which is usually a local dev server you start first.

Web scenes that animate themselves: render, do not record

When the thing to show is a web page that already plays on its own (a WebGL scene, a scripted demo, a scroll-synced story), a screen recording is the wrong tool: it drops frames whenever the laptop is busy and captures real time. page-render runs the page on a fake clock and screenshots it one subframe at a time, so a 60fps render of a heavy shader comes out at 60fps anyway, with real motion blur (4 subframes per frame, blended by ffmpeg).

# one frame per second, tiled: check this before every full render
page-render http://127.0.0.1:4173/ sheet.png --preview 1 --duration 12 \
  --setup "document.querySelector('#scene').scrollIntoView()"
# then the clip, cropped to one element, at 2x
page-render http://127.0.0.1:4173/ clip.mp4 --duration 12 --scale 2 --clip "#stage"

It holds timers, performance.now, requestAnimationFrame and every CSS animation on the same virtual clock; tests/page_render.sh proves two renders match byte for byte. Cost: about 25 s of wall time per second of 2x video. The method is @twoclipping's code-only motion template (in the brain at research/motion-template); the story rules above still decide what to show.

Native apps: iOS Simulator, not a browser

demo-shoot.mjs drives Playwright, so it only works on something a browser can open. An Expo or React Native app is not that. Check app.json first: Silo lists platforms: ["ios","android"], so expo start --web refuses outright.

The path that works, learned by doing it badly first:

Boot a simulator that already has the app. A native build is minutes and can fail. Look before building:

find ~/Library/Developer/CoreSimulator/Devices -name "*.app" -path "*Bundle*" | grep -i <app>
xcrun simctl boot <udid> && open -a Simulator
xcrun simctl launch <udid> <bundle.id>

Deep links are a trap. xcrun simctl openurl booted myapp://feed raises an "Open in App?" system dialog every single time, so it cannot be used to jump between screens in a recording. Navigate by tapping.

Wheel scroll does nothing. peekaboo scroll sends wheel events and a simulator wants a touch drag. Use a swipe, and note the flags are --from-coords / --to-coords, not --from:

peekaboo swipe --from-coords 233,720 --to-coords 233,400 --duration 420 --steps 22 --app Simulator

Clicking by element label is unreliable here. peekaboo see reads the whole accessibility tree inside the simulator, which is genuinely useful for discovering labels, but clicking --on elem_N landed one element low every time. Calibrate coordinates instead, from the window bounds in peekaboo list windows --app Simulator:

screen_x = win.x + device_pt_x * (win.width / device_pt_width)

VERIFY THE WINDOW EXISTS BEFORE EVERY COORDINATE CLICK. This is the one that did damage. Mid-session the simulator shut down, its window vanished, and the next clicks at y=885 landed on the Dock and launched applications on the user's real machine. A coordinate click is a click on the desktop when the thing you meant to hit is gone.

cap record windows --json | grep -q '"id": *<winid>' || { echo "window gone"; exit 1; }

Never send keystrokes with System Events. osascript -e 'tell application "System Events" to keystroke "..."' goes to the frontmost macOS app, not the device, and it shut the simulator down mid-run. Text entry into a React Native field is the one unsolved piece: taps and swipes work, typing does not. xcrun simctl pbcopy fails with "Unable to connect to device pasteboard". idb-companion is installed and is the next thing to try (idb ui text).

Record and export without treat.py, since its 12s ceiling and its editorial cut are built for the browser storyboard:

cap record start --detach --window <winid> --fps 60 --path out.cap
cap record stop --path out.cap
cap export out.cap --output out.mp4
ffmpeg -y -i out.mp4 -vf "crop=500:1008:0:72" -c:v libx264 -crf 18 final.mp4

That crop removes the Simulator's macOS title bar and keeps the phone bezel, which reads as a product shot rather than a screen grab.

Demoing Chewbacca itself: driving another Claude tab

A demo of this kit is usually a demo of an agent doing something, which means the footage needs a second Claude visibly working. claude-tab drives one.

claude-tab new                         open a fresh tab, verified
claude-tab ask "<prompt>" --timeout 300   send, then block until it finishes
claude-tab status                      idle | busy | absent

ask is the piece that makes a back-and-forth possible. Before it existed every attempt used a fixed sleep, and the recording made that way stopped while the other Claude was still thinking, so the clip had no payoff in it. A real turn on this machine took 29.4s; guessing that number is how you lose the ending.

How it knows. The message input's placeholder is the run state: it reads "Queue another message…" while a turn is in flight and "⌘ Esc to focus or unfocus Claude" the moment it finishes. The field is exposed to the accessibility tree with description: "Message input", so it is found by description and clicked by element id rather than by coordinate.

Opening a tab is a keybinding, not a click. It used to be a derived click at a measured offset from the window's top-right corner. That broke the first time the VS Code toolbar gained an icon: every button shifted left, the click landed on a neighbour, and new reported "the tab did not change". claude-tab new now presses ctrl+alt+cmd+n, bound to claude-vscode.editor.open (the command the icon itself runs), and writes that binding into VS Code's keybindings.json if it is missing. It still verifies a tab appeared instead of trusting it.

Two constraints worth knowing before planning a shot:

  • Only the frontmost tab's state is published. status reports whichever Claude tab is on top, never one hidden behind it. Drive one at a time.
  • FILM A SEPARATE WINDOW, NOT A SIBLING TAB. Caleb's rule, 2026-09-16: "When making a video interacting with chewbacca, ALWAYS operate with it in a different tab." A sibling tab is not enough on its own, because VS Code raises whichever tab is producing output: every tool call the recording session makes pulls focus off the tab being filmed and onto itself. Open the demo in its own window with ctrl+alt+cmd+w (claude-vscode.window.open) and record that window id. Then the camera and the performance cannot steal focus from each other.
  • window() picks the largest window, which is the wrong one once a second window exists. With a demo window open, pass the id explicitly rather than letting it guess.
  • send refuses a tab that is mid-turn. The session running the tool is itself a Claude tab in the same window and is busy for as long as it runs, so without that guard a send issued before new succeeded types the prompt into its own input box. --force queues deliberately.

Record the VS Code window the same way as any other: cap record start --detach --window <id>, and resolve the id with claude-tab window, which picks the largest window because VS Code also publishes 1512x37 menu-bar strips.

When there is no repo

Fall back to cap-demo <url>, Cap's deterministic pipeline, and say plainly that it is the ~70% version. Do not pretend a scouted demo read the product.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.