/github-year-in-review
Tu año en GitHub, como una película de 45 segundos que puedes publicar.
Hermano de /brag: brag convierte un proyecto en un video de lanzamiento, este convierte el año de una persona en uno. Mismo motor de renderizado (HyperFrames), misma entrega (póster horneado como fotograma 0, copia para compartir). La diferencia es que la historia son datos, por lo que los datos y la lógica de ritmo viajan como scripts y el trabajo del agente es revisar los números, afinar el texto, mirar los fotogramas y entregar.
Invocación
/github-year-in-review
/github-year-in-review --user octocat --year 2025
/github-year-in-review --include-private --no-music
| Opción | Por defecto |
|---|---|
--user | el usuario autenticado de gh |
--year | el año actual (etiquetado "hasta ahora" hasta el 31 Dic) |
--tz | la zona horaria de esta máquina (usada para el ritmo de hora del día) |
--include-private | desactivado: los repos privados se cuentan, nunca se nombran |
--no-music | música activa si hay una pista disponible |
--format | portrait 1080x1350 (único formato enviado) |
La salida va a year-in-review-<usuario>-<año>/ en el directorio actual (añade un sello de tiempo si existe). <skill-dir> es el directorio que contiene este archivo.
Requisitos
gh autenticado (gh auth status), Python 3.9+, Node 18+ con npx hyperframes, ffmpeg.
Las habilidades del dominio HyperFrames (hyperframes-core, hyperframes-cli) para revisar y renderizar.
Paso 1: Recopilar
python3 <skill-dir>/scripts/collect.py --user <login> --year <year> --out <out>/year.json
Unos dos minutos para una cuenta activa (el historial de commits se muestrea para el ritmo del reloj). Escribe totales, el calendario diario, el ritmo (racha, horas, días de la semana), repos principales, lenguajes, pull requests, nuevos repos, estrellas ganadas este año, y automatización: repos donde al menos el 60% de los commits muestreados comparten una plantilla de mensaje (un cron que hace commits como el usuario). La automatización se mantiene fuera del ritmo y los golpes de "trabajo" y se convierte en su propio golpe sorpresa.
También recopila los datos opcionales y temáticos: agentes (commits coescritos con agentes de codificación, detectados solo por firmas, además de PRs de agentes por pie de página, autor de aplicación y prefijos de rama estilo claude/), lanzamientos etiquetados este año, y el PR fusionado más grande. Estos se convierten en golpes solo cuando los números están presentes (umbrales en references/story.md).
Puerta: lee year.json y haz una verificación de cordura antes de construir. El total principal debe coincidir con el número en el perfil de GitHub del usuario para ese año. Si algo parece incorrecto (un bot que el detector no captó, un repo que no debería nombrarse), arregla el recolector o usa las anulaciones de story.json; nunca edites números a mano en la composición.
Paso 2: Historia
python3 <skill-dir>/scripts/build.py <out>/year.json --out <out> \
[--music <track.mp3> --cues <track.music-cues.json>]
Esto escribe <out>/story.json (golpes, texto, tiempos), <out>/composition/ (la película) y <out>/thumbnail/ (un fotograma de un segundo del cierre para la portada, ver paso 4). Los golpes, sus datos y alternativas están en references/story.md. Los golpes con datos escasos se descartan, así que un año tranquilo hace una película más corta, no una vacía.
Luego lee story.json y mejora el texto donde puedas ser más específico que los valores por defecto (subtítulos, nombre del arquetipo y razón, la cita del golpe de trabajo). Reglas:
- Cada afirmación debe ser verdadera según
year.json. Sin números inventados, sin superlativos que los datos no respalden. - Líneas cortas. El tipo es grande a propósito; cualquier cosa de más de unas 12 palabras no se mantendrá el tiempo suficiente.
- Sin nombres de repos privados a menos que se use
--include-private. - Sin emojis, sin guiones largos, sin registro de "arrasando". Va a LinkedIn.
- Enmarca agentes y automatización como un equipo que la persona dirigió ("enviado con", "coescrito con"). Nunca avergüences, nunca impliques que el trabajo fue menos suyo; los recuentos de agentes son cotas inferiores.
Reconstruye desde la historia editada con --story <out>/story.json (los tiempos se recalculan).
Música: si /brag está instalado, sus pistas empaquetadas y preajustes de indicaciones funcionan tal cual (<brag-dir>/assets/music/*.mp3, <brag-dir>/assets/music/cues/*.music-cues.json). Prefiere una pista al menos tan larga como la película. Con --cues, los inicios de escena se ajustan al golpe más cercano. Sin pista la película es silenciosa, lo cual está bien: LinkedIn reproduce automáticamente sin sonido.
Paso 3: Revisar, renderizar, mirar
cd <out>/composition
npx hyperframes check # puerta: cero errores; corrige también advertencias line_unread y type-floor
npx hyperframes render --quality draft --output ../draft.mp4
Extrae un fotograma establecido por cada golpe y mira cada uno tú mismo antes de renderizar la versión final. También mira cada tarjeta de capítulo después de que su comando haya escrito (aproximadamente 2.5 s dentro de la tarjeta): el comando completo debe estar en pantalla, una línea, tinta sobre fondo oscuro.
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
Busca: texto desbordado o recortado, un número que no coincida con year.json, un golpe vacío o aburrido, texto que no se leería en tamaño de teléfono (escala un fotograma a 540 px de ancho), un primer fotograma débil. Corrige la causa en story.json (texto) o repórtalo como un error del skill; luego:
npx hyperframes render --quality high --output ../year-in-review.mp4
Paso 4: Entregar
Póster: el fotograma establecido del gancho (aproximadamente 2.9 s) es la miniatura. Hornéalo como fotograma 0 para que todas las plataformas lo muestren (misma técnica que /brag paso 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
Miniatura: el cierre (cuadrícula del año, identificador, "<año>, in review.") como un fotograma limpio, sin contador de diapositivas, etiqueta de hash, barra de pie de página o CTA. Súbela como la miniatura personalizada del video en LinkedIn (coincide con la relación de aspecto 4:5 del video, que LinkedIn espera) y úsala en cualquier lugar donde se pida una imagen de portada:
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
Puerta: year-in-review.mp4 es H.264, 1080x1350, bajo 50 MB, 25-75 s (la construcción limita a 70 más tarjetas de capítulo); poster.jpg existe; thumbnail.png es 1080x1350 y lo has mirado (sin contadores, sin barra, nada recortado).
Escribe <out>/share-copy.txt: dos o tres frases simples en la voz del usuario, construidas a partir de los hechos verdaderos más fuertes (el arquetipo, un número, un proyecto). Sin muro de hashtags; como máximo dos. Ejemplo: "Mi 2026 en GitHub, hasta ahora: 30 repos nuevos, una racha de 44 días, y un bot que me superó en commits. Hecho con github-year-in-review."
Dile al usuario dónde están el video, la miniatura, el póster y la copia para compartir, y una línea sobre lo que dice la película.
Leyes creativas
- El fotograma 0 es la publicación. Nombre, año y el número principal, establecidos.
- Una pregunta, un punto focal por golpe. Número grande o una forma; todo lo demás es una etiqueta.
- Identidad sobre inventario. La película termina en quién fueron (arquetipo), no en un total.
- Honesto. La automatización se nombra, los nombres privados se ocultan, los años parciales dicen "hasta ahora".
- Legible sin sonido. Cada golpe tiene sentido con el sonido apagado.
- Git, no GitHub. Prompts escritos, etiquetas de hash, fragmentos de diff y pies de página; sin logotipos, sin verdes de GitHub ni cromo de interfaz.
- Aspecto original. La identidad del libro mayor en references/story.md; no lo rediseñes como Wrapped o Unwrapped.