/github-year-in-review
Votre année sur GitHub, sous forme d'un film de 45 secondes publiable.
Frère de /brag : brag transforme un projet en vidéo de lancement, celui-ci transforme l'année d'une personne en film. Même pipeline de rendu (HyperFrames), même livraison (poster intégré comme frame 0, texte de partage). La différence est que l'histoire est faite de données, donc les données et la logique des séquences sont livrées sous forme de scripts, et le travail de l'agent est de vérifier les chiffres, peaufiner le texte, examiner les frames, et livrer.
Invocation
/github-year-in-review
/github-year-in-review --user octocat --year 2025
/github-year-in-review --include-private --no-music
| Option | Valeur par défaut |
|---|---|
--user | l'utilisateur gh authentifié |
--year | l'année en cours (étiquetée "jusqu'à présent" jusqu'au 31 déc.) |
--tz | le fuseau horaire de cette machine (utilisé pour le beat de l'heure) |
--include-private | désactivé : les dépôts privés sont comptés, jamais nommés |
--no-music | musique activée si un fichier est disponible |
--format | portrait 1080x1350 (seul format livré) |
La sortie est placée dans year-in-review-<utilisateur>-<année>/ dans le répertoire courant (ajoute un horodatage si le dossier existe). <skill-dir> est le répertoire contenant ce fichier.
Prérequis
gh authentifié (gh auth status), Python 3.9+, Node 18+ avec npx hyperframes, ffmpeg. Les compétences HyperFrames (hyperframes-core, hyperframes-cli) pour la vérification et le rendu.
Étape 1 : Collecte
python3 <skill-dir>/scripts/collect.py --user <login> --year <annee> --out <sortie>/year.json
Environ deux minutes pour un compte actif (l'historique des commits est échantillonné pour le beat de l'horloge). Il écrit les totaux, le calendrier quotidien, le rythme (série, heures, jours de la semaine), les dépôts principaux, les langages, les pull requests, les nouveaux dépôts, les étoiles gagnées cette année, et l'automatisation : les dépôts où au moins 60 % des commits échantillonnés partagent un même modèle de message (un cron commitant en tant qu'utilisateur). L'automatisation est exclue du rythme et des beats "travail" et devient son propre beat surprise.
Il collecte aussi les données optionnelles et thématiques : agents (commits co-écrits avec des agents de codage, détectés uniquement par signatures, plus les PRs d'agents par pied de page, auteur de l'application et préfixes de branche de style claude/), versions taguées cette année, et la plus grosse PR fusionnée. Ces éléments deviennent des beats uniquement si les chiffres sont suffisants (seuils dans references/story.md).
Porte : lisez year.json et vérifiez-le avant de construire. Le total principal doit correspondre au nombre sur le profil GitHub de l'utilisateur pour cette année. Si quelque chose semble erroné (un bot que le détecteur a manqué, un dépôt qui ne devrait pas être nommé), corrigez le collecteur ou utilisez les surcharges de story.json ; ne modifiez jamais les chiffres à la main dans la composition.
Étape 2 : Histoire
python3 <skill-dir>/scripts/build.py <sortie>/year.json --out <sortie> \
[--music <morceau.mp3> --cues <morceau.music-cues.json>]
Ce script écrit <sortie>/story.json (beats, texte, minutage), <sortie>/composition/ (le film) et <sortie>/thumbnail/ (une image fixe d'une seconde de la fin pour la couverture, voir étape 4). Les beats, leurs données et leurs replis sont décrits dans references/story.md. Les beats avec des données trop faibles sont supprimés, donc une année calme produit un film plus court, pas vide.
Lisez ensuite story.json et améliorez le texte là où vous pouvez être plus précis que les valeurs par défaut (légendes, le nom et la raison de l'archétype, la citation du beat "travail"). Règles :
- Chaque affirmation doit être vraie par rapport à
year.json. Pas de chiffres inventés, pas de superlatifs non fondés. - Lignes courtes. Le texte est volontairement grand ; tout ce qui dépasse environ 12 mots ne tiendra pas assez longtemps.
- Pas de noms de dépôts privés sauf si
--include-private. - Pas d'emojis, pas de tirets longs, pas de ton "tu déchires". Cela va sur LinkedIn.
- Présentez les agents et l'automatisation comme une équipe que la personne a dirigée ("livré avec", "co-écrit avec"). Ne faites jamais honte, n'insinuez jamais que le travail était moins le leur ; les comptes d'agents sont des estimations basses.
Reconstruisez à partir de l'histoire modifiée avec --story <sortie>/story.json (le minutage est recalculé).
Musique : si /brag est installé, ses pistes fournies et ses presets de repères fonctionnent tels quels (<brag-dir>/assets/music/*.mp3, <brag-dir>/assets/music/cues/*.music-cues.json). Préférez une piste au moins aussi longue que le film. Avec --cues, les débuts de scène s'alignent sur le beat le plus proche. Sans piste, le film est muet, ce qui est acceptable : LinkedIn lit automatiquement en sourdine.
Étape 3 : Vérification, rendu, inspection
cd <sortie>/composition
npx hyperframes check # porte : zéro erreur ; corrigez aussi les avertissements line_unread et type-floor
npx hyperframes render --quality draft --output ../draft.mp4
Extrayez une frame stable par beat et examinez-les toutes vous-même avant de faire le rendu final. Examinez aussi chaque carte de chapitre après que sa commande a été tapée (environ 2,5 s après le début de la carte) : la commande complète doit être à l'écran, sur une ligne, affichée en clair sur fond sombre.
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
Recherchez : texte débordant ou coupé, un nombre qui ne correspond pas à year.json, un beat vide ou ennuyeux, un texte illisible à taille téléphone (mettez une frame à l'échelle 540 px de large), une première frame faible. Corrigez la cause dans story.json (texte) ou signalez-la comme un bug du skill ; ensuite :
npx hyperframes render --quality high --output ../year-in-review.mp4
Étape 4 : Livraison
Poster : la frame stable du hook (environ 2,9 s) est la vignette. Intégrez-la comme frame 0 pour que toutes les plateformes l'affichent (même technique que /brag étape 4) :
cd <sortie>
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
Miniature : la fin (grille d'année, pseudo, "<année>, en revue.") comme une image fixe propre, sans compteur de diapositives, étiquette de hachage, barre de pied ou CTA. Téléchargez-la comme miniature personnalisée de la vidéo sur LinkedIn (elle correspond au format 4:5 de la vidéo, attendu par LinkedIn) et utilisez-la partout où une image de couverture est demandée :
cd <sortie>
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
Porte : year-in-review.mp4 est en H.264, 1080x1350, moins de 50 Mo, 25-75 s (la construction plafonne à 70 plus les cartes de chapitre) ; poster.jpg existe ; thumbnail.png est en 1080x1350 et vous l'avez inspectée (pas de compteurs, pas de barre, rien de coupé).
Écrivez <sortie>/share-copy.txt : deux ou trois phrases simples dans la voix de l'utilisateur, construites à partir des faits réels les plus forts (l'archétype, un nombre, un projet). Pas de mur de hashtags ; deux au maximum. Exemple : "Mon 2026 sur GitHub, jusqu'à présent : 30 nouveaux dépôts, une série de 44 jours, et un bot qui a commit plus que moi. Réalisé avec github-year-in-review."
Indiquez à l'utilisateur où se trouvent la vidéo, la miniature, le poster et le texte de partage, ainsi qu'une ligne sur ce que raconte le film.
Lois créatives
- La frame 0 est l'accroche. Nom, année et le chiffre principal, stables.
- Une question, un point focal par beat. Un grand nombre ou une forme ; tout le reste est une étiquette.
- Identité plutôt qu'inventaire. Le film se termine sur qui ils étaient (archétype), pas sur un total.
- Honnête. L'automatisation est nommée, les noms privés sont masqués, les années partielles disent "jusqu'à présent".
- Lisible en muet. Chaque beat a du sens sans le son.
- Git, pas GitHub. Invites tapées, étiquettes de hachage, blocs de diff et pieds de page ; pas de logos, pas de verts GitHub ni chrome d'interface.
- Aspect original. L'identité du grand livre dans references/story.md ; ne la refondez pas en style Wrapped ou Unwrapped.