training-video
Skill2 starsChanged 33 days agoMITView on GitHub
Turn a training guide (docs/training/<App>/<NN-Topic>/<guide>.md, made with the training-guide skill) into a narrated video. Each step shows its screenshot, a step label, and captions that follow an ElevenLabs voice reading the step. Voice clips are cached, so a re-render costs nothing. Use when the user asks for a training video, a walkthrough video, a narrated guide, or "video from the guide".
- Reads credentials
In this repository4 other files it gives its agents.
- AGENTS.mdAGENTS.md
- motion-videoSkill
- motion-web-videoSkill
- training-guideSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 5 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 19
Every step is ### N. Title, then its text, then its screenshot on its own line.
- Distinctive: line 23
No secrets on any screenshot.
- Very specific: line 28
Read every text. Shorten long scenes.
- Distinctive: line 53
No parentheses, slashes or arrows: the voice reads them oddly.
- Distinctive: line 55
Never read a secret, a key or a personal name.
- Very specific: line 58
Read the whole script aloud once, or run $R voice $G --local and $R render $G for a free draft with the system voice.
- Very specific: line 60
Commit script.json, so the narration is reviewed like the guide.
- Distinctive: line 62
Only changed scenes cost characters on a re-run: the voice cache is keyed by the text.
- Distinctive: line 90
Only after the user says the video is right.
- Very specific: line 98
If your site deploy deletes old files from the bucket, exclude media/*, or a deploy removes the videos.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Training video from a guide
The guide is the source. The video says what the guide says, step by step, over the guide's own screenshots. Fix wording in the guide, not in the video. Run everything from your repository root.
R="uv run --frozen --with pillow==12.3.0 --with imageio-ffmpeg==0.6.0 python ${CLAUDE_SKILL_DIR}/scripts/guide_video.py"
G=docs/training/<App>/<NN-Topic>/<guide>.mduv brings Pillow and an ffmpeg binary on first use (Windows and Mac). Nothing else to install.
1. Check the guide first
- Every step is
### N. Title, then its text, then its screenshot on its own line. Text above a screenshot is read over that screenshot. - The screenshots are current. A screenshot that shows an old state (a status that is now different, a removed button) goes wrong in the video too: retake it first.
- No secrets on any screenshot. A published video is served at a public URL.
2. Script: $R script $G
Writes <topic>/video/<guide>/script.json: one scene per screenshot, plus a title scene and a
"Check that it worked" scene. It prints the scene count and the characters of narration.
- Read every
text. Shorten long scenes; spoken lines should be short, plain sentences. Editscript.jsondirectly, or fix the guide and runscriptagain (that overwrites your edits). - Tables and code blocks are not read aloud.
3. Write the narration (before any voice)
script copies the guide's written sentences. Written text read aloud sounds like a manual, so
rewrite each scene's text in script.json as speech. Do this after the guide is final:
running script again overwrites it (restore with git checkout -- <script.json>).
Each scene's text is both the voice and the burned-in caption. Write it so it reads well on
screen and sounds right aloud.
| Scene | Write |
| Title | One line on what the viewer will be able to do, then one line on what they need. About 15 seconds. |
| Each shot | Say what to look at on this screenshot, then the one action. One point per scene. Name the button as it appears ("select Create" → "select Create"). |
| A card without a screenshot | Only what the next shot needs. If it adds nothing, delete the scene. |
| Check that it worked | Say what the viewer sees when it worked, in two or three sentences. script cuts this scene at 600 characters, often mid-sentence: always rewrite it. |
| Closing | Add a last card: where to find the written guide, and one trap to avoid. |
Rules
- Sentences of at most 15 words, active voice, one instruction each.
- About 150 spoken words a minute. A shot scene is 10 to 25 seconds, about 25 to 60 words. A scene over 70 words: split it or cut it.
- Speak what the screen shows. Don't read IDs, URLs, file paths or task keys aloud. Say "the task" or "the pull request".
- Spell names as written (Acme Board, GitHub, Copilot), because the caption shows the same text. If the voice says a name wrong, reword around it. Never respell it.
- No parentheses, slashes or arrows: the voice reads them oddly. Write "Claude or Gemini", not "Claude/Gemini".
- Numbers as people say them: "three", "two minutes", "up to five per scan".
- Never read a secret, a key or a personal name.
Check before the voice step
- Read the whole script aloud once, or run
$R voice $G --localand$R render $Gfor a free draft with the system voice. - Count the characters (
scriptprints them; recount after edits). Tell the user the count before the first ElevenLabs run. - Commit
script.json, so the narration is reviewed like the guide.
Only changed scenes cost characters on a re-run: the voice cache is keyed by the text.
4. Voice: $R voice $G
- ElevenLabs, voice Eric (
cjVigY5qzO86Huf0OWal,eleven_multilingual_v2). To change it, setvoiceinscript.json; list voices with the account's/v1/voices. - The key:
$ELEVENLABS_API_KEY. Never print it or write it to a file. - Cache: each clip is keyed by voice + model + text, stored locally in
voice/, and also ins3://$TRAINING_VIDEO_BUCKET/media/training/voice/when that variable is set. Only new or changed lines call ElevenLabs. The output says how many were made and how many came from cache. - Cost: ElevenLabs bills per character. Tell the user the count from step 2 before a first run. The free tier has 10,000 characters a month; a 4-minute guide is about 3,400.
--localuses the free system voice (Windows System.Speech, macOSsay, Linuxespeak-ng). Use it to check the pipeline without spending characters. Captions are then spread by length, not by word timing.
5. Render
- Draft:
$R render $G→<guide>-draft.mp4, 1280×720. - Final:
$R render $G --final→<guide>.mp4, 1920×1080, plus<guide>.vttcaptions andposter.png. - Captions are burned in and change with the voice's own word timings. The
.vtthas the same lines for players that show subtitles. - Look at it before reporting. Pull two frames and view them:
ffmpeg -ss 3 -i <mp4> -frames:v 1 f1.png(the ffmpeg path:uv run --frozen --with imageio-ffmpeg==0.6.0 python -c "import imageio_ffmpeg as f;print(f.get_ffmpeg_exe())"). Then open the mp4 for the user to watch. - Check the voice is in the file (a video can look right and be silent):
ffmpeg -i <mp4> -t 20 -af volumedetect -f null - 2>&1 | grep -E "Audio:|mean_volume". You need anAudio: aacstream and a mean volume above about −40 dB. Silence (−91 dB) or no audio stream means the voice step failed: rerunvoice, thenrender.
6. Publish: $R publish $G (optional)
Only after the user says the video is right. It needs an S3 bucket behind a website.
- Set
TRAINING_VIDEO_BUCKET(for exampleexample-training-media) andTRAINING_VIDEO_BASE_URL(for examplehttps://www.example.com).TRAINING_VIDEO_PREFIXdefaults tomedia/training. The AWS CLI usesAWS_PROFILEandAWS_REGION. - Uploads the mp4,
.vttand poster tos3://$TRAINING_VIDEO_BUCKET/media/training/<App>/<NN-Topic>/<guide>.*, served from$TRAINING_VIDEO_BASE_URL/media/training/.... - Adds a Watch the video: line under the guide's title (replaced on a later publish).
- If your site deploy deletes old files from the bucket, exclude
media/*, or a deploy removes the videos. - A narrated video is never embedded as muted autoplay. Browsers autoplay only muted video,
so the voice is never heard. Embed it stopped, with
controls, notmuted, and the publishedposter(the.pngbeside the mp4); the visitor's play starts it with sound. Silent loops (autoPlay muted loop) are only for clips with no voice. - Check it on the page, not only the file: in the browser, the
<video>must readmuted=false controls=true, and after a playcurrentTimemust move.
What is committed
script.json and the .vtt are committed with the guide. The mp4, poster.png and voice/ are
not (add them to .gitignore); keep them in S3 or another store.
Report
- The video's URL (or local path before publish), its length and size.
- Characters sent to ElevenLabs this run, and how many clips came from cache.
- Any screenshot that looked out of date in the frames you checked.
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
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.

