Agents & MCPDeveloper skill

Training Video Generator

Turns project documentation into training videos: AI-written scripts, terminal demo plans, TTS narration, and an FFmpeg merge. One-click automation on macOS via the kanopi Claude Code plugin.

What it does

Your setup steps live in a document new people skim once, then ask about in chat anyway. Filming a walkthrough sounds simple until you sit down to it: what goes on screen, in what order, what you say over it, and how it all becomes one file.

With this skill active, the assistant reads your documentation and plans the video before anything is recorded. It writes a scene-by-scene sheet with the on-screen action, the line spoken over it and how long it runs, a demo plan with the exact commands to type on camera, the narration as plain text any voice tool can read, and the commands that merge recording and audio.

You get the script, the recording plan, one narration file per scene and the merge commands. The recording is yours, so the polish depends on your takes. On a Mac the terminal recording can be automated; on Windows and Linux you record it and the rest is the same.

You end up with a beat-by-beat script, one narration file per scene, a recording plan and the FFmpeg commands that build the video.

When to reach for it

Worth it for onboarding, for a tool people keep using wrong, or for showing a release to people who will never read the notes. It pays off most when the steps are stable and the same questions keep coming back.

Skip it when the process changes every few weeks, because the video goes out of date faster than you can re-record it, and skip it when a six-line written answer already does the job.

What's inside

training-video-generator/
├── SKILL.md
└── references/
    ├── examples.md
    └── ffmpeg-and-tts.md
SKILL.md
---
name: training-video-generator
description: Turns project documentation into a training video. Produces a beat-by-beat script pulled from the docs, a per-beat screen demo plan with exact commands, a narration script formatted for any text-to-speech tool, and the FFmpeg commands that merge the recording with the audio. Use to build an onboarding video from a README, a walkthrough of an internal tool, or a release demo.
---

# Training Video Generator

You turn written documentation into a video someone can watch instead of read. You produce everything except the pixels: the script, the demo plan, the narration, and the exact commands that assemble the final file.

**What is automated, and where.** The script, demo plan, narration text and merge commands work on any operating system. One-click terminal recording is macOS only, because it drives iTerm2 through AppleScript. On Windows and Linux you record with a scripted terminal recorder or a screen recorder, and every other stage is identical. Say which path you are on before you promise anyone a finished file.

## Workflow

1. **Scope.** One video, one job. Name the viewer, the outcome, and the length.
2. **Mine the docs.** Pull the real steps out of the README, runbook or wiki. Anything not in the docs gets confirmed before it goes on screen.
3. **Beat sheet.** The scene table: beat, on-screen action, narration line, seconds.
4. **Demo plan.** Exact commands or clicks per beat, in run order, with the state each beat starts from.
5. **Narration script.** One plain-text block per beat, written to be spoken, ready to paste into any text-to-speech tool.
6. **Record.** macOS fast path, scripted terminal recorder, or manual screen capture.
7. **Merge.** Normalise each clip, reconcile it with its narration, concatenate.
8. **Review.** Watch it once end to end at full speed with the sound on.

```text
Training video checklist
[ ] Viewer, outcome and target length written down
[ ] Every on-screen step verified against the docs or a real run
[ ] Beat sheet complete: action + narration + seconds for every beat
[ ] Demo plan lists exact commands and the starting state
[ ] Narration timed at about 145 words a minute against each beat
[ ] Recorded beat by beat, not as one long take
[ ] Clips normalised to one resolution, frame rate and codec
[ ] Audio and video length reconciled per beat before concatenating
[ ] Captions generated from the narration script
[ ] Final file watched end to end with sound
```

## 1. Scope the video

Answer three questions before writing a word of script:

- **Who is watching?** A new hire on day one and a developer adding their fifth feature need different videos. Pick one.
- **What can they do afterwards that they could not do before?** One outcome, stated as an action: "run the test suite locally", "deploy a hotfix", "add a new integration".
- **How long?** Five to eight minutes is the working ceiling for a how-to. Past that, attention drops and the file goes stale faster, because a longer video touches more things that change.

One video, one outcome. If the docs cover setup, deployment and troubleshooting, that is three videos with three beat sheets, not one long one. Say so rather than making a twenty-minute file nobody finishes.

## 2. Mine the docs

Read the whole doc set before proposing anything. Then:

- List the candidate topics with a one-line outcome each, and recommend an order. The reader picks; you do not guess.
- Follow the shortest path that works. Documentation branches to cover every environment; a video takes one route and mentions the others in a sentence.
- Verify every command. A command that fails on camera is the most expensive mistake in this pipeline, because you discover it during the edit and re-record everything after it.
- Flag steps that cannot be filmed: anything needing real credentials, anything that costs money, anything that takes twenty minutes. Each one becomes a cut, a pre-prepared starting state, or a title card that says what happened.
- Note what the docs get wrong. If the README says `npm start` and the script is actually `npm run dev`, fix the doc as its own change. Do not narrate a correction the docs contradict.

## 3. The beat sheet

The beat sheet is the deliverable everything else hangs off. One row per beat:

| # | Beat | On screen | Narration | Sec |
| --- | --- | --- | --- | --- |
| 3 | Install | Terminal: `npm install`, dependency output scrolls | "Install the dependencies. This takes about a minute the first time, and it is cached after that." | 14 |

Rules that keep it usable:

- **A beat is one idea and one screen state.** If the narration needs "and then", split it in two.
- **Eight to twenty seconds each.** Shorter reads as a jump cut. Longer is somebody reading their own terminal aloud.
- **Time it by word count.** Spoken narration runs at roughly 145 words a minute, about 2.4 words a second. Thirty words is a twelve-second beat. Add time for anything that happens on screen without words, such as an install that runs for fifteen seconds.
- **Open with the outcome, close with the next step.** Beat one tells the viewer what they will be able to do. The last beat says where to go when something breaks.
- **Number the beats and never renumber them.** File names, audio clips and cut points all reference these numbers. Insert `3a` rather than shifting everything down.

## 4. The demo plan

The beat sheet says what appears. The demo plan says exactly how to make it appear, precisely enough that someone who has never used the tool can record it.

For each beat, write down:

- **Starting state.** Which directory, which branch, what is already installed, what the terminal shows before the beat begins.
- **Exact input.** The literal command, or the literal sequence of clicks. No paraphrasing.
- **Expected result.** What appears if it worked, so the recorder knows whether to keep the take.
- **Reset.** What to undo before the next beat, if anything.

Set the stage once, for every beat:

- 1920x1080, terminal font at 18pt or larger. Text that is comfortable on your monitor is unreadable on a phone.
- A clean prompt with no personal paths, no host names, and no branch you have to explain.
- A scratch directory with seeded fixture data, not your real work tree.
- Notifications off, second monitor cleared, browser tabs closed to the ones in shot.

## 5. The narration script

Write one plain-text block per beat, saved so each block can be sent to a speech tool on its own.

```text
beat-03.txt
Install the dependencies. This takes about a minute the first time, and it is
cached after that.
```

The rules that separate narration from prose:

- **Plain text only.** No markdown, no bullets, no headings inside a block. Some engines read asterisks aloud; most of them pause in the wrong places around them.
- **Never read a command out character by character.** The screen already shows it. The narration says why it is being run. "Point the tool at your config file", not "tilde slash dot config slash tool dot yaml".
- **Short sentences.** A comma is a short pause and a full stop is a long one. Dashes and ellipses behave differently in every engine, so use a full stop instead.
- **Expand an abbreviation the first time you use it**, then use it freely.
- **One audio file per beat**, named for the beat: `audio/beat-03.m4a`. This is what lets you re-record a single line six months later without touching the video.
- **Read it aloud yourself first.** Anything you stumble over, the engine will mangle.

Any speech tool works because the script is plain text. Hosted voices sound better; the `say` command built into macOS is free and fine for an internal video; your own voice recorded against the same script is better than both if you are comfortable on a microphone.

## 6. Recording

### macOS fast path

The `kanopi/training-video-generator` plugin for Claude Code automates the recording stage by driving iTerm2 through AppleScript, so the terminal types the commands for you. It also wraps the whole pipeline in five commands: `/video-topics`, `/video-script`, `/video-automate`, `/video-narrate` and `/video-merge`.

```text
/plugin marketplace add kanopi/claude-toolbox
/plugins install training-video-generator@claude-toolbox
```

Be straight about the requirements: **it needs macOS, iTerm2, and permission for AppleScript to control it.** FFmpeg is required on every platform. The plugin is worth installing if you are on a Mac and expect to make more than one video. Everyone else uses the same beat sheet with the paths below, and loses nothing except the automation.

### Scripted terminal recording, any operating system

VHS records a terminal from a script instead of from your hands. It runs on macOS, Linux and Windows, and needs `ttyd` and `ffmpeg` installed. A tape file per beat keeps the recording reproducible:

```text
# beat-03.tape
Output raw/beat-03.mp4
Set Shell "bash"
Set FontSize 22
Set Width 1920
Set Height 1080
Type "npm install"
Sleep 500ms
Enter
Sleep 14s
```

```bash
vhs beat-03.tape
```

This beats recording by hand for anything terminal-shaped: no typos on camera, identical pacing on every take, and the tape file lives in git next to the docs it came from. Re-run it after the tool changes and the beat is re-recorded in seconds.

### Screen recording for everything else

For a graphical app, use OBS Studio on any platform: 1920x1080 canvas and output, 30fps, capture the single window rather than the whole desktop, and one hotkey per beat so each take is its own file. QuickTime on macOS and `Win+Alt+R` on Windows both record a single window and are enough for a short video.

### Web app screens

Playwright records video for a whole browser context, which is the cleanest way to film a web UI without a cursor wandering off:

```js
const context = await browser.newContext({
  viewport: { width: 1920, height: 1080 },
  recordVideo: { dir: 'raw/', size: { width: 1920, height: 1080 } },
});
const page = await context.newPage();
await page.goto('http://localhost:5173/settings');
await page.getByLabel('Display name').fill('Ada L');
await page.getByRole('button', { name: 'Save changes' }).click();
await page.getByText('Saved').waitFor();
await context.close();          // the video is written on close
```

The output is `.webm`, which the normalise step below converts. Move slowly and wait on real signals rather than timers, exactly as you would in a test.

## 7. Merge

Four steps. Run them per beat, then once at the end.

**Normalise every clip** so the concatenation at the end can copy streams instead of re-encoding:

```bash
ffmpeg -y -i raw/beat-03.webm \
  -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,fps=30" \
  -an -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p \
  build/beat-03-video.mp4
```

**Reconcile the two lengths.** Narration and footage never match exactly. Measure both, then pad the shorter one:

```bash
ffprobe -v error -show_entries format=duration -of csv=p=0 build/beat-03-video.mp4   # 12.400
ffprobe -v error -show_entries format=duration -of csv=p=0 audio/beat-03.m4a         # 14.100
```

Narration longer than the video, by 1.7 seconds, so hold the last frame:

```bash
ffmpeg -y -i build/beat-03-video.mp4 -i audio/beat-03.m4a \
  -filter_complex "[0:v]tpad=stop_mode=clone:stop_duration=1.7[v]" \
  -map "[v]" -map 1:a -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p \
  -c:a aac -b:a 160k build/beat-03.mp4
```

If the video is the longer one instead, pad the audio with silence by the difference: `-af "apad=pad_dur=1.7"` on the audio input, same command otherwise. Never use `-shortest` here; it silently truncates whichever stream carries the content you care about.

**Concatenate.** Because every beat was encoded identically, this is a stream copy and takes seconds:

```bash
printf "file 'beat-01.mp4'\nfile 'beat-02.mp4'\nfile 'beat-03.mp4'\n" > build/beats.txt
ffmpeg -y -f concat -safe 0 -i build/beats.txt -c copy build/training-video.mp4
```

**Watch it.** At full speed, with sound, start to finish. Listen for narration that lands before or after the action it describes; that is the one defect the commands cannot catch.

## Example

Input: a twelve-line `CONTRIBUTING.md` that says clone the repo, run `make setup`, run `make test`, and open a pull request against `develop`. Request: "a two-minute video so new contributors stop asking how to run the tests."

Beat sheet:

| # | Beat | On screen | Narration | Sec |
| --- | --- | --- | --- | --- |
| 1 | Promise | Title card: "Run the test suite in three commands" | "By the end of this you will have the test suite running on your own machine, in about three minutes." | 8 |
| 2 | Clone | Terminal: `git clone git@github.com:acme/ledger.git && cd ledger` | "Clone the repository and move into it. Use the SSH URL, because the setup step pushes a branch later." | 12 |
| 3 | Setup | `make setup`, dependency output scrolling | "One command installs the dependencies and creates a local database. It takes about two minutes the first time." | 14 |
| 4 | Test | `make test`, green summary line, then a zoom on `84 passed` | "Run the suite. Eighty-four tests, and they should all pass on a clean checkout. If any fail here, the problem is your environment, not your code." | 16 |
| 5 | Next | `git checkout -b my-change` on screen, docs link on the card | "Branch off develop and you are ready to work. The contributing guide covers the pull request checklist." | 11 |

Demo plan for beat 3: start in `~/demo` with no `ledger` directory and Docker already running; type `make setup`; expect the output to end with `database ready`; cut the middle ninety seconds of dependency output down to four seconds in the edit. Reset by deleting the directory before re-recording.

Total: 61 seconds of narration across five beats, so roughly a two-minute video once the pauses and the trimmed install are counted. That fits the request, which is why the beat sheet is where you check the length rather than after recording.

## Rules

- Never show a command you have not run. Verify first, film second.
- The narration explains why; the screen shows what. If both say the same thing, cut the narration.
- Record per beat. A single long take means one flubbed line costs you the whole video.
- Keep the beat sheet, the tape files and the narration text in the repository next to the docs they came from. A video whose source is lost cannot be updated and will be wrong within a year.
- Generate captions from the narration script rather than from speech recognition. You already have exact text, and some viewers will only ever read it.
- Do not narrate anything the docs do not support. If you had to work something out to make the demo run, that discovery belongs in the docs first.

## Edge cases

- **The docs are wrong or out of date.** Stop and fix the docs as a separate change, then script from the corrected version. Filming a wrong instruction multiplies it.
- **A step needs real credentials or a paid action.** Pre-prepare the state off camera and cover the gap with a title card that says what happened. Never film a real secret, and check the recording for one before publishing.
- **A step takes twelve minutes.** Film the start and the finished result, and cut between them. Say the real duration in the narration so nobody thinks their machine has hung.
- **There is nothing to show.** If the task is entirely conceptual, a video adds cost and no clarity. Say so and offer a written walkthrough instead.
- **No budget for hosted speech.** The script is the deliverable either way. Use the free system voice, or record yourself against the same text.
- **The tool changes.** Per-beat files are the whole point: re-run one tape, regenerate one audio clip, re-concatenate. Budget for this from the start.
- **Someone asks for a thirty-minute video.** Propose a series with one outcome each, and show the beat sheets. Nobody watches a thirty-minute how-to to the end.
- **You are on Windows and want the one-click flow.** It does not exist for your platform. The scripted terminal path gets you the same output with one extra command per beat; do not imply otherwise.

## Files

- `references/examples.md` - two complete builds, beat sheet to finished file: a CLI onboarding video and an internal web-app how-to, each with full narration lines and the exact merge commands used. Read it before writing your first beat sheet.
- `references/ffmpeg-and-tts.md` - the production toolbox: captions from the narration script, loudness matching across clips, zooming so small text stays readable, title cards, and choosing a speech engine. Read it when the merge stage is not giving you a file you would show anyone.

## Attribution

The pipeline shape and the macOS automation come from [kanopi/training-video-generator](https://github.com/kanopi/training-video-generator). The cross-platform recording paths, the timing rules and the merge commands here are ours.

Install it

Claude Code (this project)

Unzip the download into .claude/skills/. The folder is already named right.

Claude Code (every project)

Unzip into ~/.claude/skills/ to use it everywhere.

claude.ai

Upload the zip under Settings, then Capabilities, then Skills (paid plans).

How to call it

Just describe the task. The description line inside the skill does the matching. Or call it directly:

/training-video-generator turn these docs into a training videomake an onboarding video from the READMEwrite a script for a demo video of our toolI need a walkthrough video for new team membersturn our setup guide into a narrated video

Works beyond Claude

Skills use the open Agent Skills format. The exact same folder works in OpenAI Codex, Gemini CLI, Cursor, GitHub Copilot and other tools that read the format.

Using a plain chat app? Paste SKILL.md into ChatGPT or Gemini as instructions. You lose auto-triggering, but the method still works.

agentskills.io

Pattern credit: kanopi/training-video-generator

Related skills