/video-transcripts
Transcribe a supplied local or linked video with Gemini, or from its frames with a Claude subagent when Gemini fails, and check the transcript against one frame per second, when a recording's contents are needed as evidence, such as a bug recording on an issue, PR or comment, or
Claude Code
$ npx -y skills add udecode/dotai --skill video-transcripts --agent claude-code
Codex
$ npx skills add udecode/dotai --skill video-transcripts -a codex -y
Cursor
$ npx skills add udecode/dotai --skill video-transcripts -a cursor -y
Gemini CLI
$ npx skills add udecode/dotai --skill video-transcripts -a gemini-cli -y
GitHub Copilot
$ npx skills add udecode/dotai --skill video-transcripts -a github-copilot -y
OpenCode
$ npx skills add udecode/dotai --skill video-transcripts -a opencode -y
Windsurf
$ npx skills add udecode/dotai --skill video-transcripts -a windsurf -y
SKILL.md
video-transcripts.SKILL.mdname: video-transcripts
description: "Transcribe a supplied local or linked video with Gemini, or from its frames with a Claude subagent when Gemini fails, and check the transcript against one frame per second, when a recording's contents are needed as evidence, such as a bug recording on an issue, PR or comment, or a question about what a video shows."
Video Transcripts
Quick Start
Run the helper once per relevant video. `<skill>` is this skill's directory:
bash <skill>/scripts/generate_video_transcript.sh \
"https://uploads.linear.app/.../screen-recording.mov" \
--title "PDF preview hyperlinks trigger leave-page modal"
Or for a GitHub attachment:
bash <skill>/scripts/generate_video_transcript.sh \
"https://github.com/user-attachments/assets/..." \
--title "Slash menu loses selection after confirm"
Or for a Screencastify watch page:
bash <skill>/scripts/generate_video_transcript.sh \
"https://app.screencastify.com/watch/..." \
--title "Bulk send expands filtered recipients"
Or for a local file:
bash <skill>/scripts/generate_video_transcript.sh \
"/absolute/path/to/video.mov" \
--title "Preview hyperlink exits workflow"
For auth-gated Linear uploads, the helper automatically retries with cookies from the local Linear desktop app on macOS. For private GitHub asset URLs, it retries with `GITHUB_TOKEN`, `GH_TOKEN`, or `gh auth token` when available.
The helper also cuts the video into one frame per second and prints `Frames: <dir>` on stderr. `<dir>/frames/tSSSS.png` is the frame at second SSSS, so `[01:12]` is `t0072.png`, and each `<dir>/sheet-SSSS-SSSS.png` shows the nine seconds its name spans in one image. Pass `--frames <dir>` to choose the directory. For a local video without Gemini, run `bash <skill>/scripts/video_frames.sh <video> --output <dir>`.
When Gemini gives no transcript, such as with no API key, a refused upload or two failed models, the helper writes `<dir>/frames-brief.md`, prints its path and exits 3. Then run the [Frames fallback](#frames-fallback) for that video. Any other nonzero exit means the helper could not read or cut the video.
The transcript is a draft until Workflow step 3 checks it against the frames.
Use This When
- A GitHub or Linear issue, PR, or comment includes a screen recording.
- An attachment URL points to `uploads.linear.app`.
- An attachment URL points to a GitHub attachment or private GitHub asset host.
- A tracker links to a public Screencastify watch page.
- You need timeline-style transcript lines, not a vague summary.
- You want the result in this exact XML shape:
<video-transcripts>
<video-transcript title="...">
[00:00] (...)
</video-transcript>
</video-transcripts>
Workflow
A tracker comment or reply that anyone outside the team can read, such as one on a public repository, needs explicit message authority; one only the team can read follows the project's message rule. Prepare or return the transcript locally when publication is not authorized. Cache location and reuse rules below do not grant permission to post.
1. Run the helper once for each relevant video. When it exits 3, run the [Frames fallback](#frames-fallback) for that video. 2. Give each run a short, bug-focused `--title`. 3. Check the transcript against the frames before using it, whether Gemini or the Frames fallback wrote it. Gemini misreads formatting, mixes up windows and invents repeated steps, and it states a wrong line as firmly as a right one. Open every sheet of a clip under a minute; for a longer clip, open the sheet that holds each line a conclusion rests on and the sheets on either side. Open the full frame wherever a sheet is too small to read, such as bold or italic text. Where a frame and a line disagree, rewrite the line from the frame, and delete a line the frames contradict. Mark a line the frames can neither show nor contradict, such as speech, `(unverified)`. 4. For tracked work, the canonical shared cache should live in the tracker next to the evidence it describes. 5. If the video is in the issue or PR body, use a top-level tracker comment. 6. If the video is in a Linear comment, post the transcript cache as a reply to that specific comment. 7. If the video is in a GitHub issue or PR comment, use one dedicated top-level cache comment for that source comment's video set. GitHub has no replies, so keep cache comments separated by source container instead of merging unrelated comment videos together. 8. On GitHub, fold each cache comment into one collapsed block, so the thread shows one summary line until a reader opens it. The summary names the source container and how many clips the block holds, and ends with `, from frames` when the Frames fallback wrote the block. Inside the block, put a transcript source link, then the timestamp lines:
<details><summary>Transcripts of the 2 clips in the issue body</summary>
[[Transcript](link to video or comment if not available)]
[00:00] (...)
</details>
````
A Linear reply holds the same link and timestamp lines without the block.
Keep each source link on the line directly above its first timestamp line,
because a check that reads the cache looks for that pair.
9. When caching helper XML, strip the `<video-transcripts>` and
`<video-transcript ...>` wrapper lines and paste only timestamp lines after
the source link.
10. If there are multiple videos in the same source container, repeat the
`[[Transcript](...)]` source link plus timestamp lines for each video,
inside the one block, with a blank line between videos.
11. Build every cached line from the helper's output, or the Frames fallback's, as corrected against the frames, never from memory or the issue's own text.
12. Link `[[Transcript](...)]` to the video URL when available. If the video URL is not available or is unstable, link to the source comment that contains the video.
13. Fo
Read more
name: video-transcripts description: "Transcribe a supplied local or linked video with Gemini, or from its frames with a Claude subagent when Gemini fails, and check the transcript against one frame per second, when a recording's contents are needed as evidence, such as a bug recording on an issue, PR or comment, or a question about what a video shows."
Video Transcripts
Quick Start
Run the helper once per relevant video. `<skill>` is this skill's directory:
bash <skill>/scripts/generate_video_transcript.sh \ "https://uploads.linear.app/.../screen-recording.mov" \ --title "PDF preview hyperlinks trigger leave-page modal"
Or for a GitHub attachment:
bash <skill>/scripts/generate_video_transcript.sh \ "https://github.com/user-attachments/assets/..." \ --title "Slash menu loses selection after confirm"
Or for a Screencastify watch page:
bash <skill>/scripts/generate_video_transcript.sh \ "https://app.screencastify.com/watch/..." \ --title "Bulk send expands filtered recipients"
Or for a local file:
bash <skill>/scripts/generate_video_transcript.sh \ "/absolute/path/to/video.mov" \ --title "Preview hyperlink exits workflow"
For auth-gated Linear uploads, the helper automatically retries with cookies from the local Linear desktop app on macOS. For private GitHub asset URLs, it retries with `GITHUB_TOKEN`, `GH_TOKEN`, or `gh auth token` when available.
The helper also cuts the video into one frame per second and prints `Frames: <dir>` on stderr. `<dir>/frames/tSSSS.png` is the frame at second SSSS, so `[01:12]` is `t0072.png`, and each `<dir>/sheet-SSSS-SSSS.png` shows the nine seconds its name spans in one image. Pass `--frames <dir>` to choose the directory. For a local video without Gemini, run `bash <skill>/scripts/video_frames.sh <video> --output <dir>`.
When Gemini gives no transcript, such as with no API key, a refused upload or two failed models, the helper writes `<dir>/frames-brief.md`, prints its path and exits 3. Then run the [Frames fallback](#frames-fallback) for that video. Any other nonzero exit means the helper could not read or cut the video.
The transcript is a draft until Workflow step 3 checks it against the frames.
Use This When
- A GitHub or Linear issue, PR, or comment includes a screen recording.
- An attachment URL points to `uploads.linear.app`.
- An attachment URL points to a GitHub attachment or private GitHub asset host.
- A tracker links to a public Screencastify watch page.
- You need timeline-style transcript lines, not a vague summary.
- You want the result in this exact XML shape:
<video-transcripts> <video-transcript title="..."> [00:00] (...) </video-transcript> </video-transcripts>
Workflow
A tracker comment or reply that anyone outside the team can read, such as one on a public repository, needs explicit message authority; one only the team can read follows the project's message rule. Prepare or return the transcript locally when publication is not authorized. Cache location and reuse rules below do not grant permission to post.
1. Run the helper once for each relevant video. When it exits 3, run the [Frames fallback](#frames-fallback) for that video. 2. Give each run a short, bug-focused `--title`. 3. Check the transcript against the frames before using it, whether Gemini or the Frames fallback wrote it. Gemini misreads formatting, mixes up windows and invents repeated steps, and it states a wrong line as firmly as a right one. Open every sheet of a clip under a minute; for a longer clip, open the sheet that holds each line a conclusion rests on and the sheets on either side. Open the full frame wherever a sheet is too small to read, such as bold or italic text. Where a frame and a line disagree, rewrite the line from the frame, and delete a line the frames contradict. Mark a line the frames can neither show nor contradict, such as speech, `(unverified)`. 4. For tracked work, the canonical shared cache should live in the tracker next to the evidence it describes. 5. If the video is in the issue or PR body, use a top-level tracker comment. 6. If the video is in a Linear comment, post the transcript cache as a reply to that specific comment. 7. If the video is in a GitHub issue or PR comment, use one dedicated top-level cache comment for that source comment's video set. GitHub has no replies, so keep cache comments separated by source container instead of merging unrelated comment videos together. 8. On GitHub, fold each cache comment into one collapsed block, so the thread shows one summary line until a reader opens it. The summary names the source container and how many clips the block holds, and ends with `, from frames` when the Frames fallback wrote the block. Inside the block, put a transcript source link, then the timestamp lines:
<details><summary>Transcripts of the 2 clips in the issue body</summary> [[Transcript](link to video or comment if not available)] [00:00] (...) </details> ```` A Linear reply holds the same link and timestamp lines without the block. Keep each source link on the line directly above its first timestamp line, because a check that reads the cache looks for that pair. 9. When caching helper XML, strip the `<video-transcripts>` and `<video-transcript ...>` wrapper lines and paste only timestamp lines after the source link. 10. If there are multiple videos in the same source container, repeat the `[[Transcript](...)]` source link plus timestamp lines for each video, inside the one block, with a blank line between videos. 11. Build every cached line from the helper's output, or the Frames fallback's, as corrected against the frames, never from memory or the issue's own text. 12. Link `[[Transcript](...)]` to the video URL when available. If the video URL is not available or is unstable, link to the source comment that contains the video. 13. Fo
Shared skills that the pstack plugin does not cover: test audits (test-audit, adapted from openclaw's), screenshot walkthroughs and video transcripts, and pstack setup, sync and skill-drift audits (sync-pstack).
Other skills on dotai.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about video-transcripts.
- Which coding agents can use video-transcripts?
- video-transcripts installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

