/github-year-in-review
你在 GitHub 上的一年,浓缩成一段 45 秒可发布的影片。
与 /brag 是姊妹技能:brag 将项目转化为发布视频,而本技能将一个人的年度活动转化为视频。两者采用相同的渲染路径(HyperFrames)、相同的交付方式(海报帧嵌入为第 0 帧、共享文案)。区别在于:故事基于数据,因此数据和节奏逻辑以脚本形式提供,代理的工作是检查数字、润色文案、审阅帧并完成交付。
调用方式
/github-year-in-review
/github-year-in-review --user octocat --year 2025
/github-year-in-review --include-private --no-music
| 选项 | 默认值 |
|---|---|
--user | 当前已认证的 gh 用户 |
--year | 当前年份(12 月 31 日前标记为“至今”) |
--tz | 本机时区(用于小时维度的节奏数据) |
--include-private | 关闭:私有仓库仅计数,不显示名称 |
--no-music | 有可用曲目时默认开启音乐 |
--format | portrait 1080x1350(唯一支持的格式) |
输出位于当前目录下的 year-in-review-<user>-<year>/ 目录(如果已存在则添加时间戳)。<skill-dir> 是存放此文件的目录。
前置要求
gh 已认证(gh auth status)、Python 3.9+、Node 18+(需 npx hyperframes)、ffmpeg。需要 HyperFrames 领域技能(hyperframes-core、hyperframes-cli)用于检查和渲染。
步骤 1:数据收集
python3 <skill-dir>/scripts/collect.py --user <login> --year <year> --out <out>/year.json
对于活跃账户大约需要两分钟(提交历史会被采样用于时钟节奏)。它会写入:总计数据、每日日历、节奏(连续天数、时段、工作日)、热门仓库、编程语言、拉取请求、新建仓库、本年度获得的 Star 数,以及自动化:至少 60% 的采样提交共享同一消息模板的仓库(作为该用户身份提交的定时任务)。自动化数据不会纳入节奏和“工作”片段,而是成为独立的惊喜片段。
它还会收集可选的专题数据:代理(与编码 AI Agent 共同编写的提交,仅通过签名检测,加上通过脚注、应用作者和 claude/ 风格分支前缀标识的代理 PR)、本年度标记的版本发布,以及最大的已合并 PR。这些数据仅在数量达到阈值时才会形成独立片段(阈值定义见 references/story.md)。
门控: 在构建前读取 year.json 并进行合理性检查。标题总数应与该用户 GitHub 个人资料上显示的年度数量一致。如果发现异常(如检测器遗漏的机器人、不应显示名称的仓库),请修复收集器或使用 story.json 覆盖;切勿手动编辑数字到合成结果中。
步骤 2:故事脚本
python3 <skill-dir>/scripts/build.py <out>/year.json --out <out> \
[--music <track.mp3> --cues <track.music-cues.json>]
这将生成 <out>/story.json(片段、文案、时间)、<out>/composition/(影片文件)和 <out>/thumbnail/(用于封面的结尾定格画面,持续 1 秒,见步骤 4)。片段、其数据及降级方案详见 references/story.md。数据不足的片段会被删除,因此安静的一年会生成更短的影片,而非空影片。
然后读取 story.json,并在默认文案基础上尽可能具体地进行改进(字幕、典型角色名称和原因、工作片段引用)。规则:
- 每项声明必须与
year.json中的数据一致。不得虚构数字,不得使用数据不支持的最高级表述。 - 短句优先。文字字号较大是有意设计的;超过约 12 个单词的内容将无法在屏幕上停留足够长的时间。
- 除非使用
--include-private,否则不得显示私有仓库名称。 - 不使用表情符号、不出现长破折号、不采用“碾压一切”的语气。该内容将发布在 LinkedIn 上。
- 将代理和自动化描绘为用户领导的团队(“与……共同交付”、“与……共同编写”)。切勿贬低,切勿暗示工作成果不属于用户;代理计数仅为下界。
从编辑后的故事脚本重新构建:使用 --story <out>/story.json(时间将重新推导)。
音乐:如果已安装 /brag,其内置曲目和提示预设可直接使用(<brag-dir>/assets/music/*.mp3、<brag-dir>/assets/music/cues/*.music-cues.json)。优先选择长度至少与影片相同的曲目。使用 --cues 时,场景起始点会自动对齐最近的节拍。没有曲目时影片为静音,这也可以接受:LinkedIn 会自动静音播放。
步骤 3:检查、渲染、审阅
cd <out>/composition
npx hyperframes check # 门控:零错误;同时修复 line_unread 和 type-floor 警告
npx hyperframes render --quality draft --output ../draft.mp4
在每个片段中提取一个稳定帧,在渲染最终版本之前亲自检查每一帧。同时检查每个章节卡片在其命令输入完成后的画面(约进入卡片 2.5 秒处):完整命令必须显示在屏幕上,保持一行,墨水色文字置于深色背景上。
for t in $(jq -r '.beats[]|.start+.duration-0.5' ../story.json); do
ffmpeg -loglevel error -ss $t -i ../draft.mp4 -frames:v 1 ../frame-$t.png; done
检查以下问题:文字溢出或被裁剪、数字与 year.json 不符、空泛或无聊的片段、在手机尺寸下无法阅读的文字(将帧缩放到 540 像素宽进行测试)、第一帧视觉效果不佳。在 story.json 中修复原因(文案)或将其报告为技能 Bug;然后执行:
npx hyperframes render --quality high --output ../year-in-review.mp4
步骤 4:交付
海报帧:钩子的稳定帧(约 2.9 秒处)作为缩略图。将其嵌入为第 0 帧,以便所有平台都能显示(与 /brag 步骤 4 相同):
cd <out>
ffmpeg -ss 2.9 -i year-in-review.mp4 -frames:v 1 -q:v 2 poster.jpg
ffmpeg -y -i year-in-review.mp4 -i poster.jpg \
-filter_complex "[0:v][1:v]overlay=0:0:enable='eq(n,0)'[v]" \
-map "[v]" -map '0:a?' -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
-c:a aac -b:a 160k -movflags +faststart tmp.mp4 && mv tmp.mp4 year-in-review.mp4
封面图:影片结尾(年度网格、用户名、“<年份>,回顾。”)的干净定格画面,不包含幻灯片计数器、哈希标签、底部轨道或页脚栏。在 LinkedIn 上将其上传为视频的自定义缩略图(它符合 LinkedIn 预期的 4:5 宽高比),并可用于任何需要封面图像的地方:
cd <out>
npx hyperframes check thumbnail
npx hyperframes snapshot thumbnail --at 0.5 --no-end -o thumbnail/shots
cp thumbnail/shots/frame-00-at-0.5s.png thumbnail.png
门控: year-in-review.mp4 必须为 H.264、1080x1350、小于 50 MB、时长 25-75 秒(构建上限为 70 秒加上章节卡片);poster.jpg 存在;thumbnail.png 为 1080x1350 且您已亲自检查过(无计数器、无底部轨道、无裁剪)。
编写 <out>/share-copy.txt:以用户口吻写两三句简洁文案,基于最有力的事实(典型角色、一个数字、一个项目)。不要用一堆话题标签;最多两个。示例:“到目前为止,我在 GitHub 上的 2026 年:30 个新仓库、连续 44 天活跃、还有一个提交数量超过我本人的机器人。由 github-year-in-review 制作。”
告知用户视频、封面图、海报帧和共享文案的位置,以及一句话说明影片内容。
创作法则
- 第 0 帧就是帖子。 包含姓名、年份和头条数字,稳定呈现。
- 每个片段只提一个问题和一个焦点。 一个大数字或一个形状;其余均为标签。
- 身份重于清单。 影片以用户是谁(典型角色)结尾,而非数字总计。
- 诚实。 自动化会被明确标注,私有名称会被隐藏,不满一年的数据会标注“至今”。
- 静音可读。 每个片段在静音状态下也能理解。
- Git,而非 GitHub。 使用输入提示、哈希标签、差异片段和提交尾注;不使用 Logo、GitHub 绿色或 UI 界面元素。
- 独特外观。 严格遵守 references/story.md 中定义的总账风格;不要重新设计成 Wrapped 或 Unwrapped 风格。