pr-walkthrough について
GitHub のプルリクエストを、社内向けのナレーション付き解説動画にするスキルです。変更の調査からナレーション作成、音声生成、字幕同期、コード中心の MP4 レンダリングまでを案内します。
主な機能
- 01PR のコミットと差分の確認
- 02分割ナレーションの作成
- 03セグメントごとの音声生成
- 04字幕付き解説 MP4 のレンダリング
活用シーン
- 01チームへの PR の説明
- 02コード中心の PR 概要の作成
- 03社内レビュー用の解説動画の共有
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tldraw/ tldraw --skill pr-walkthroughインストール後、Claude Code で/pr-walkthroughを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/manifest.mdscripts/generate-audio.shmake-video.shvideo/.gitignorebuild.mjshyperframes.jsonmeta.jsonrender.sh詳細
PR ウォークスルー動画
プルリクエスト用のナレーション付きウォークスルー動画を作成します。これは内部向けの成果物として設計されており、プルリクエストの作成者が作る loom 動画と同じメリットを提供します — コードの変更を順を追って説明し、何をなぜ行ったのかを解説することで、視聴者が PR を素早く理解できるようにします。
入力: GitHub のプルリクエスト URL(例: https://github.com/tldraw/tldraw/pull/7924)。PR 番号のみやその他の説明が与えられた場合は、その PR は tldraw/tldraw リポジトリにあるものとみなします。
出力: 1280×720(30 fps)の MP4 動画で、音声ナレーション、whisper でタイミング調整された黒背景に黄色の字幕、標準化されたイントロ/アウトロスライドを含み、out/pr-<number>-walkthrough.mp4 に保存されます。
すべての中間ファイル(音声、マニフェスト、スクリプト)は、このスキルディレクトリからの相対パスで tmp/pr-<number>/ に置かれます。このディレクトリは gitignore されています。最終的な .mp4 のみが out/ に置かれます。
./scripts や ./video を参照するコマンドは、このスキルディレクトリから実行してください。
基本方針
これは作成者の視点からのウォークスルーです。 目標は、PR の作成者が誰かと座って変更を順を追って説明するのと同じです — 特定のコードを示し、何がなぜ変わったのかを、理解が深まる順序で説明します。視聴者は、コードが何をするのか と 変更をどう捉えればよいのか の両方を理解して終えるべきです。
つまり:
- ナレーションがすべてを導きます。 まずウォークスルーのナレーションを、PR の継続的な説明として書きます。次に、発言内容を支えるために各時点で画面に何を表示すべきかを決めます。
- コードを示します。 既定のビジュアルはコードの差分またはソースファイルです。テキストスライドは例外(イントロ、短いつなぎ、アウトロ)であり、原則ではありません。ナレーションが関数について話しているとき、視聴者はその関数を見ているべきです。
- 変更を論理的な順序で順を追って説明します — 必ずしもファイル順やコミット順である必要はありません — ただし常に抽象的な説明ではなく、具体的なコードに紐づけます。
- 「何を」だけでなく「なぜ」を説明します。 画面のコードは何が変わったのかを示します。ナレーションはその理由を補います — なぜこのアプローチなのか、どんな問題を解決するのか、どのエッジケースを扱うのか。
ワークフロー
ステップ 1: PR を理解する
PR のコミット、差分、説明を読みます。物語の流れを理解します:
- これはどんな問題を解決するのか?
- アプローチは何か?
- 主要な仕組みは何か?
gh pr view <number> --json title,body,commits
git log main..HEAD --oneline
git diff main..HEAD --stat
生成されたファイルはスキップします。 差分を読むとき、手で編集されたものではなく自動生成されたファイルは無視します。これらはノイズを増やすだけでウォークスルーに有益な情報を与えません。このリポジトリでよくある例:
**/api-report.md、**/api-report.api.md— 生成された API サーフェスレポート**/*.api.json、**/temp/*.api.json— API extractor の出力apps/docs/content/reference/**— 生成されたリファレンスドキュメントpnpm-lock.yaml、package-lock.json— ロックファイル**/CHANGELOG.md— 自動生成された変更履歴- リポジトリのツールが再生成するあらゆるファイル(スナップショット、スキーマダンプ、バンドルされたアセット)
ファイルが生成されたものかどうか不明な場合は、「DO NOT EDIT」のようなヘッダーコメントがあるか、またはそれを生成するジェネレーターコマンドがリポジトリにあるかを確認してください。diff/code スライドで取り上げるファイルを選ぶ際には、これらを除外します。
ステップ 2: ナレーションを書く
ナレーションは、論理的なセグメントに分割した連続したテキストとして書きます。各セグメントはウォークスルーの一拍であり、概念、変更、または関連する変更のグループです。これを tmp/pr-<number>/SCRIPT.md として保存します。
ナレーションは、著者が同僚に PR を説明するように読めるべきです。「では、私たちがやっていることはこうです…中心的な問題は X でした…私が取ったアプローチは Y です…ここにあるこの関数を見てください…」
構成: イントロ → コンテキスト/問題 → コードのウォークスルー → まとめ。以下のスクリプト構成を参照してください。
コミットがシンプルでよく整理されている場合(多くの場合、名前に -clean を含むブランチ上にあります)、コミットメッセージと説明に従ってナレーションを導くことができます。そうでない場合は、コードを調べて独自のナラティブを作成してください。前の概念の上に積み重なる順序で概念を導入してください。
冗長さを避けてください。特にイントロと最初のコンテンツセグメントの間では。
ステップ 3: 音声とタイムスタンプを生成する
セグメントごとに 1 回の TTS 呼び出しで、セグメントごとの音声クリップを生成します。これにより、チャンク化、アライメント、分割が完全に不要になります。各セグメントは、1 回の信頼できる TTS 呼び出しには十分短いからです。
narration.json ファイルを書き、generate-audio.sh CLI ツールを実行します:
./scripts/generate-audio.sh narration.json tmp/pr-<number>/
API キー: リポジトリの .env ファイル(GEMINI_API_KEY)から自動的に取得されます。
ナレーション JSON フォーマット
{
"style": "Read the following walkthrough narration in a calm, steady, professional tone. Speak at a measured pace as if the author of a pull request were walking a colleague through the code changes.",
"voice": "Iapetus",
"slides": [
"This pull request adds group-aware binding resolution to the arrow tool...",
"The core problem was that arrow bindings broke when the target shape...",
"If you look at the getBindingTarget method in ArrowBindingUtil.ts..."
]
}
style— 音声ペルソナとペーシングの指示。短く具体的に保ってください。voice— Gemini の音声名(デフォルト:Iapetus)。slides— ナレーションテキストの配列。セグメントごとに 1 エントリです。
仕組み
- 各セグメントについて、スクリプトがプロンプトを構築します: スタイルの前置き + セグメントテキスト。
- セグメントごとに
gemini-3.1-flash-tts-previewへの 1 回の API 呼び出しで、WAV クリップを直接生成します。 - 各クリップは検証され(単語数に対する長さの健全性チェック)、出力が不正な場合は自動的に再試行されます。
- 各クリップの先頭と末尾の無音がトリミングされます。
出力: セグメントごとの音声クリップ(audio-00.wav、…)と、各音声ファイル名をその長さ(秒)にマッピングする durations.json ファイル。
依存関係: ffmpeg / ffprobe。標準ライブラリ以外に必要な Python パッケージはありません。
使用しないでください: ナレーションテキスト内で [pause long] や [pause medium] のようなマークアップタグを使わないでください。モデルがそれらを文字通り読み上げる可能性があります。
ステップ 4: マニフェストを書く
各クリップの長さについてステップ 3 の durations.json を読み、音声ファイルの隣に manifest.json を書きます。これはスライドごとに 1 エントリを順番にリストし、各エントリには type、audio、durationInSeconds が含まれます。
スライドの種類とそのフィールドについては references/manifest.md を参照してください。
ステップ 5: 動画をレンダリングする
render.sh スクリプトを実行します:
./video/render.sh \
tmp/pr-<number>/manifest.json \
out/pr-<number>-walkthrough.mp4
スクリプトの動作:
- 参照されている音声/画像ファイルを
video/assets/にコピーします。 - 各音声ファイルに対して whisper 文字起こしを実行 →
video/transcripts/audio-NN.json(冪等 — 音声が既存の文字起こしより新しい場合にのみ再文字起こしします)。 build.mjs <manifest>を実行してvideo/index.htmlを生成します — すべてのスライドに対して時間指定されたクリップ、トランジションとコードフォーカスパン用の GSAP タイムライン、whisper 文字起こしから導出された開始/終了時間を持つ黒地に黄色のキャプションクリップを備えたハイパーフレームコンポジションです。- コンポジションをリントし、
npx hyperframes renderで 1920×1080 のフレームをレンダリングします。 - 1280×720 / 30fps にダウンスケールし、ffmpeg(CRF 26 + AAC 96k)で再圧縮して、最終的な小さくてもシャープな MP4 にします。
依存関係: Node.js 22+、ffmpeg、Python 3(render.sh がマニフェストを解析するために使用)。hyperframes は npx --yes 経由で呼び出されるため、インストール手順は不要です。Whisper はローカルで実行されます(small.en モデル、初回ダウンロード時約 150MB)。
whisper によるキャプション同期
キャプションは、黒の塗りつぶされたピル上に黄色いテキストとして表示され、フレームの下部に固定されます。その開始/終了時刻は、5~7 語のチャンクにグループ化された単語レベルの whisper トランスクリプトから取得され、自然なポーズ(450ms を超えるギャップ = 文の境界)で早めに区切られます。1 つの含意: whisper はブランド名やコード名を発音通りに文字起こしします — 「tldraw」→「TL Draw」、「OverlayUtil」→「overlay util」。これはキャプションとしては許容できますが、後で build.mjs 内の置換テーブルを介して正規化できます。
ファイルサイズの調整項目
デフォルトのレンダリングは、8 分の動画で約 30~60 MB を目標とします。調整するには:
render.sh内の ffmpeg ダウンスケール手順における--crf <n>— 22 はほぼロスレス、26 がデフォルト、30 以上はかなり小さくなります。CRF 28~30 はドキュメント品質の結果を得るのに適した範囲です。- 1080p のハイパーフレームレンダリングでは
-q draft --crf 30を使用して中間ファイルを小さく保ちます(最終的なサイズはどのみちダウンスケールが支配的です)。
ファイル構成
最終出力はこのスキルディレクトリに置かれます。すべての中間ファイルは tmp/(gitignore 対象)に置かれます:
pr-walkthrough/
├── SKILL.md # This file
├── scripts/ # CLI tools (checked in)
│ └── generate-audio.sh # narration.json → per-slide WAVs + durations.json
├── video/ # Hyperframes project (checked in)
│ ├── hyperframes.json # hyperframes config
│ ├── meta.json # project meta
│ ├── build.mjs # manifest.json → index.html composition
│ ├── render.sh # manifest.json → 720p MP4 (full pipeline)
│ ├── assets/ # Auto-populated at render time (gitignored)
│ ├── transcripts/ # Whisper word-level JSON (gitignored, cached)
│ └── renders/ # Intermediate 1080p renders (gitignored)
├── out/ # Final outputs (gitignored)
│ └── pr-XXXX-walkthrough.mp4
└── tmp/ # Intermediate files (gitignored)
└── pr-XXXX/
├── SCRIPT.md # Narration script
├── narration.json # Input to generate-audio.sh
├── durations.json # Audio filename → duration in seconds
├── manifest.json # Input to render.sh
└── audio-XX.wav # Per-segment audio clips
API 設定
- Gemini API キー: プロジェクトルートの
.envファイルにGEMINI_API_KEYとして保存されます。TTS と音声アラインメントに使用されます。 - TTS モデル:
gemini-3.1-flash-tts-preview - TTS ボイス:
Iapetus(常に)
スクリプト構成
ウォークスルーは一貫したナラティブの流れに従います。すべてのセクションが独自のセグメントを必要とするわけではありません — PR の複雑さに基づいてセクションを結合したりスキップしたりしてください。目標は合計 8~12 セグメントで、その大半でコードを表示します。
イントロ(1 セグメント)
イントロカード: tldraw ロゴ + PR タイトル + 日付。ナレーションは、この PR が高レベルで何を行うかを位置づける 1 文にする必要があります。まだ詳細には踏み込みません。
マニフェストのスライドタイプ: intro。
コンテキスト(0~1 セグメント)
コードに踏み込む前の簡単な位置づけ。この PR の前はどのような状況だったのか? どのような問題やニーズがこの作業の動機になったのか? これは短く保ってください — コードウォークスルーが理解できる程度の枠組みで十分です。
- 具体的に述べる: 「ターゲットのシェイプがグループ内にあるときにアローバインディングが壊れた」のであって「バインディングに問題があった」ではありません
- 影響を受けるコードベースの領域を名指しする
最初の関連するコードを示しながらコンテキストを説明できる場合は、独立したコンテキストセグメントをスキップして、最初のコードセグメントに折り込みます。
マニフェストのスライドタイプ: text または diff(問題のあるコードを示す場合)。
コードウォークスルー(6~10 セグメント)
動画の大部分です。実際のコード変更をたどり、何が行われ、なぜ行われたのかを説明しながら、特定の diff とファイルを示します。
すべてのセグメントでコードを表示する必要があります。 変更には diff スライドを、変更されていない参照コードには code スライドを使用します。
ガイドライン:
- ファイルと関数を名指しする。 ナレーション付きの各セグメントは、少なくとも 1 つの特定のファイルまたは関数を参照する必要があります。
- diff を表示する。 各セグメントのビジュアルは、議論されている実際の diff であるべきです。
git diff main..HEAD -- path/to/fileを使用して diff を取得し、関連するハンクを抽出します。 - ファイルではなく理解の順に並べる。 理解を構築する順序で変更を提示します。新しい型があるファイルで定義され別のファイルで使用される場合は、まず定義を示します。
- 「何を」だけでなく「なぜ」を説明する。 diff は何が変更されたかを示します — ナレーションはその理由、扱うエッジケース、検討された代替案を補います。
- ボイラープレートはスキップするが、言及はする。 すべての import 変更や型エクスポートにセグメントを割くのではなく、ついでに触れます: 「
index.tsにもいくつかの型エクスポートが追加されています — それらは次に見る新しい型の再エクスポートにすぎません。」 - 関連する小さな変更をグループ化する。 3 つのファイルすべてに同じ 1 行の修正が入った場合、1 つのセグメントで 3 つすべてを扱えます。各ファイルを名前で言及してください。
まとめ(1 セグメント)
PR が達成したことを簡単に振り返ります。これは短い締めくくりです — 全体的な変更を要約する 1~2 文で、関連する場合は既知の制限やフォローアップ作業に言及します。
マニフェストのスライドタイプ: text。
アウトロ(1 セグメント、無音)
tldraw のロゴ、3 秒間の無音。常に最後のスライドとして含めてください。
マニフェストのスライドタイプ:outro、durationInSeconds: 3。
ナレーション作成のヒント
- コードについて具体的に述べる。 「バインドシステムが更新されました」ではなく、「
BindingUtil.tsでは、onAfterChangeハンドラーがグループの祖先をチェックするようになりました」と言いましょう。ファイル名や関数名を挙げることで、視聴者はナレーションと画面上の内容を結びつけられます。 - 各セグメント = 1 つの変更、または密接に関連する変更のグループ。 そのセグメントについて特定の diff を指し示せないなら、おそらく抽象的すぎます。
- 著者として書く。 トーンは説明的で自然に——自分の作業を誰かに順を追って説明するように。「ここでの主なポイントは……」や「難しい部分は……」で構いません。
- イントロと最初のコンテンツセグメントの間で冗長を避ける。
- 表示されないファイルにも言及する。 PR が 15 ファイルに触れていても、興味深いのが 6 つだけなら、残りについて簡単に触れましょう:「残りの変更は型のエクスポートとテストフィクスチャです。」
- ナレーション全体で 5〜7 分 を目指しましょう。
チェックリスト
- すべての PR コミットを読み、完全な diff を理解する
- SCRIPT.md にナレーションを書く(8〜12 セグメント)
- セグメントごとの音声を生成する(Iapetus ボイス)
- durations.json を読み、セグメントごとの長さを取得する
- スライドタイプ、diff/コード、音声参照を含む manifest.json を書く
- render.sh で動画をレンダリングする
- 最終出力を検証する:1280×720 / 30 fps、音声が同期されている、キャプションが読める、アウトロが存在する