animate-file-history - Animación del historial de archivos
Construye un GIF (o MP4) de cómo se veía algo a lo largo de la vida del repositorio. La solicitud está en lenguaje natural ($ARGUMENTS), así que el trabajo consiste en: determinar a qué archivos se refiere, recopilar cada versión de ellos desde git, renderizar cada una, descartar las versiones que se ven igual que la anterior y unir el resto en una animación.
El script incluido se encarga de recorrer git, renderizar, deduplicar visualmente y codificar:
scripts/animate-history.sh, relativo al directorio base de este skill - no al repositorio del usuario. A continuación, $SKILL representa ese directorio; llama al script por su ruta absoluta. --help lista todas las opciones. Tu parte es elegir los archivos y, para páginas, producir las capturas de pantalla.
1. Verificar las herramientas
command -v git ffmpeg rsvg-convert; ls "/Applications/Google Chrome.app" 2>/dev/null; command -v chromium google-chrome
ffmpeg es obligatorio. SVG necesita rsvg-convert (brew install librsvg / apt install librsvg2-bin) o Chrome/Chromium. --label (etiquetas) y páginas HTML necesitan Chrome/Chromium (CHROME=/ruta anula la detección). Si falta algo, indica qué y cómo instalarlo; no omitas versiones SVG en silencio.
2. Resolver la solicitud a archivos
Busca en el historial completo de la rama actual, no solo en el árbol de hoy; los logotipos se renombran, mueven y reexportan, y los archivos eliminados aún pertenecen a la historia:
git log --format= --name-only -- ':(glob,icase)**/*logo*' | sort -u
Busca por lo que la cosa se llama, no solo por la palabra del usuario. Un logotipo también vive en favicon*, apple-touch-icon*, *mark*, *lockup*, *wordmark*, *brand* y el propio nombre del producto; las versiones tempranas suelen estar solo en el favicon. Amplía la lista de globs en consecuencia y observa lo que devuelve.
Filtra a tipos renderizables: png jpg jpeg gif webp bmp svg para imágenes; html para páginas estáticas. Ignora la salida de compilación y copias empaquetadas (dist/, build/, node_modules/, .next/).
Luego agrupa las coincidencias en linajes - un linaje por animación:
- La misma obra de arte a través de renombres, movimientos o un cambio de formato (
logo.png→brand/logo.svg) es un linaje. Pasa todas sus rutas a una sola ejecución; el script las fusiona en una sola línea de tiempo por fecha de commit. - Las variantes que coexisten (
logo-dark.svg,logo-mono.svg,favicon.png, exportaciones con tamaño como[email protected]) son linajes separados. Por defecto, una animación por linaje, y solo anima las variantes que la solicitud plausiblemente significa (normalmente el logotipo principal, no todos los tamaños de favicon).
Si la agrupación es genuinamente ambigua (varios archivos "logo" no relacionados, o nada coincide), muestra la lista de candidatos con el recuento de commits (git log --follow --oneline -- <ruta> | wc -l) y pregunta antes de renderizar.
3a. Imágenes: ejecutar el script
"$SKILL/scripts/animate-history.sh" -o logo-history.gif --label <ruta> [<ruta-antigua> ...]
Opciones útiles: -s 512 o -s 1200x800 lienzo, -t 0.25 segundos por versión (predeterminado para imágenes; las capturas de pantalla de páginas vía --frames usan 0.8 por defecto; usa el valor del usuario si lo dio, ej. "0.5s por fotograma" → -t 0.5), --hold 1.5 segundos en el fotograma final, --bg '#0b0b0b' para logotipos diseñados para fondos oscuros, --keep DIR para conservar los fotogramas, -o x.mp4 para video, --skip <sha> (repetible) para omitir una versión que el usuario no quiere. Imprime keep/skip por commit y kept N of M versions (se mantuvieron N de M versiones) en stderr.
Las rutas son relativas al directorio actual (o -C DIR), como cualquier pathspec de git; ejecuta desde la raíz del repositorio y pásalas como las muestra la lista de historial anterior. El script sigue los renombres por sí mismo (git log --follow), por lo que la ruta actual suele ser suficiente a menos que el archivo se haya recreado con un nuevo nombre.
3b. Páginas: capturar cada versión, luego ejecutar el script en la carpeta
Una página necesita renderizarse en cada commit. Encuentra el origen de la página (index.html, app/page.tsx, src/pages/index.vue, ...) y lista los commits que la modificaron o lo que renderiza; el aspecto de una página de inicio también cambia a través de sus componentes y CSS:
git log --format='%H %cs' --reverse -- <archivo-página> <directorios de componentes/estilos>
Si son más de ~40 commits, muestrea (uno por semana o mes, más el primero y el último) y dílo. Las capturas van en una carpeta nombrada <AAAA-MM-DD>_<sha-corto>.png, para que los archivos se ordenen por fecha y el nombre sea el título.
-
HTML estático: sin compilación. Exporta el commit con
git archive <sha> | tar -x -C "$snap"y captura la pantalla del archivo con"$CHROME" --headless=new --hide-scrollbars --window-size=1280,800 --screenshot=... "file://$snap/<página>.html". Elimina$snapdespués y mantenlo fuera del repositorio. -
Página con framework (React, Next, Vue, Astro, ...): debe ejecutarse para renderizar. Usa
snapshot-commit.shpor commit:"$SKILL/scripts/snapshot-commit.sh" -C <repo> -r /<ruta> -f https://<sitio-producción> <sha> "$shots"Exporta el commit, instala el lockfile de ese commit (cacheado por lockfile), inicia el servidor de desarrollo (
-d 'comando con {port}'si no esnpm run dev -- --port {port}), y captura la pantalla a través de Playwright (scripts/screenshot-page.mjs, necesitaplaywright-corey Chrome - el propio del repositorio se encuentra viaPLAYWRIGHT_DIR). Espera a que la red esté inactiva y cada<img>, descarta el banner de cookies, e imprime una línea:ok,blank(en blanco),error(página de error como "no se pudo cargar en este navegador") ofail(fallo de instalación o servidor). Solookescribe un PNG. Pregunta antes de ejecutar esto si implica más de un puñado de instalaciones.
Verifica los resultados antes de animar, no confíes solo en ok:
- Nunca tomes prestado el
node_modulesactual. Los commits antiguos importan paquetes que el árbol actual eliminó (ej.react-router-dom), y la página se renderiza en blanco. Por eso el script instala por commit. - Commits
blank/error/fail: mira el registro de desarrollo, intenta una vez arreglar la causa (una variable de entorno faltante, un comando-dincorrecto,--ignore-scriptssaltando un paso de compilación necesario), de lo contrario omite el commit y di cuántos. - Imágenes rotas (
broken-images=Nen la líneaok, con las URLs): assets que el servidor de desarrollo no puede servir (rutas alojadas en plataforma como/__l5e/..., archivos gitignorados o subidos).-f <sitio-producción>los obtiene desde producción. Si las imágenes siguen rotas, dílo en el informe; no envíes un GIF lleno de cuadros vacíos en silencio. Arregla sobre la marcha en lugar de informar después. - Mira una hoja de contactos de los fotogramas (
ffmpeg -pattern_type glob -i '*.png' -vf scale=320:-1,tile=6x6 -frames:v 1 sheet.png) antes de la ejecución final.
Luego:
"$SKILL/scripts/animate-history.sh" --frames "$shots" -s 1280x800 --label -o home-page-history.gif
Las capturas idénticas se descartan igual que las imágenes idénticas, y también las que son completamente de un color plano (usa --keep-blank para conservarlas). Usa un tamaño de ventana fijo para cada captura, y prefiere -o x.mp4 para páginas largas o detalladas; los GIF de páginas completas se vuelven grandes.
3c. En varios repositorios
Cuando el logotipo vive en más de un repositorio (una app, un panel, un sitio web), ejecuta el script en cada repositorio con --keep <dir> y sin --label, luego copia cada fotograma conservado en una carpeta nombrada <AAAA-MM-DD>_<repo>-<sha-corto>.png; las líneas keep en stderr dan la fecha y sha de cada fotograma conservado en orden. Agrega -2, -3 a nombres que colisionan (varios archivos cambiaron en un commit). Luego ejecuta --frames <carpeta> --label -t 0.25 en la carpeta fusionada para una sola línea de tiempo a través de todos ellos.
4. Informe
Proporciona la ruta de salida, cuántas versiones se encontraron y cuántas llegaron a la animación, el rango de fechas y cualquier cosa omitida (no renderizable, compilaciones fallidas, muestreadas). Ábrelo si se pide (open x.gif). Escribe la salida donde el usuario quiera, por defecto en el directorio actual; nunca lo confirms a menos que se solicite.
Advertencias
- La deduplicación es sobre píxeles renderizados, no bytes. Un PNG reexportado o SVG reformateado se omite si se renderiza idénticamente; un desplazamiento de un píxel cuenta como cambio. Ese es el punto; no añadas un filtro a nivel de bytes encima.
- Los SVG que referencian archivos externos (fuentes,
<image href>) se renderizan sin ellos, ya que solo el SVG en sí se extrae del historial. El contenido incrustado está bien. - Las fuentes GIF animadas contribuyen solo con su primer fotograma.
- Los commits de fusión no se recorren -
git logmuestra los cambios del archivo en la rama que los hizo, que es lo que se desea para una línea de tiempo. - Los clones superficiales (CI,
--depth 1) no tienen historial. Verificagit rev-parse --is-shallow-repositoryy sugieregit fetch --unshallowsi imprimetrue. - Las etiquetas necesitan Chrome/Chromium porque las compilaciones de ffmpeg incluidas a menudo carecen de
drawtext.
Modelo y esfuerzo
El criterio aquí es pequeño (elegir archivos, agrupar linajes, elegir una muestra); el trabajo pesado recae en el script y los bucles de shell. Por defecto, usa Sonnet con esfuerzo bajo. Opus o mayor esfuerzo no aporta nada para esta tarea, y un fork o subagente es innecesario. Aumenta el esfuerzo solo cuando la agrupación de linajes sea genuinamente ambigua.
La mayoría de los tokens de una ejecución son el contexto heredado de la sesión (CLAUDE.md, listas de skills y herramientas, ~100k+), releído desde caché en cada turno, no el trabajo en sí. Una ejecución en una sesión sin carga cuesta mucho menos que una en una cargada.
Benchmarks
Mide con la transcripción de la sesión (suma usage en los turnos del asistente):
f=$(ls -t ~/.claude/projects/<directorio-proyecto>/*.jsonl | head -1)
jq -s '[.[]|select(.message.usage)|.message.usage]|{turns:length,out:(map(.output_tokens)|add),cache_read:(map(.cache_read_input_tokens)|add),cache_create:(map(.cache_creation_input_tokens)|add)}' "$f"
| Fecha | Modelo / esfuerzo | Tarea | Turnos | Tokens salida | Tokens lectura caché | Tokens creación caché | Resultado |
|---|---|---|---|---|---|---|---|
| 2026-10-23 | Sonnet 5.5 / bajo | Página de inicio de storra, solo descubrimiento (antes de renderizar) | 9 | 3.6k | 731k | 134k | Archivos + historial de ruta encontrados en ~7 llamadas |
Añade una fila por ejecución (ejecución completa, no solo descubrimiento); compara Opus / mayor esfuerzo solo si una ejecución Sonnet-bajo se equivoca en el linaje o el muestreo.