¿Qué hace transcribe-summarize?
Audio de entrada, transcripción de salida y, opcionalmente, un conjunto de notas de reunión. La ruta predeterminada se ejecuta completamente en la máquina del usuario.
Dos cosas hacen que esto sea diferente de llamar a Whisper por tu cuenta, y ambas surgieron de un día de medición, no de diseño:
- Whisper inventa habla sobre el silencio, y un modelo más grande no ayuda. En una llamada real con 40 segundos de silencio inicial,
large-v3produjo un segmento de 18 segundos con una palabra repetida 55 veces. Esta habilidad filtra esos segmentos y los conserva, junto con las métricas que los rechazaron, en un archivo adjunto para que la decisión sea auditable. - Limpiar el audio supera a elegir un modelo. Normalizar y eliminar silencios redujo los segmentos inventados de 2 a 0 y corrigió una palabra que tanto
large-v3comoturbohabían transcrito incorrectamente. Está activado por defecto.
Configuración
ffmpeg y ffprobe deben estar en el PATH — macOS brew install ffmpeg, Windows winget install Gyan.FFmpeg, Linux apt install ffmpeg.
Todo lo demás es biblioteca estándar. El backend trae su propia biblioteca, y solo la seleccionada, así que elígelo con uv run --with:
| backend | plataforma | especificación --with |
|---|---|---|
mlx-whisper — predeterminado en Apple Silicon | solo macOS arm64 | 'mlx-whisper>=0.4.2' |
faster-whisper — predeterminado en otros | macOS / Windows / Linux | 'faster-whisper>=1.2' |
parakeet — optativo | Apple Silicon verificado | 'parakeet-mlx' |
groq, openai, elevenlabs, gemini — optativos, red | cualquiera | nada que instalar |
Si falta la biblioteca, el error imprime el comando exacto. No lo adivines.
Ejecútalo
Declara la ayuda al principio de cada bloque de comando — el estado del shell no persiste entre llamadas a herramientas:
ts() {
UV="$(command -v uv || ls "$HOME/.local/bin/uv" "$HOME/.cargo/bin/uv" \
/opt/homebrew/bin/uv /usr/local/bin/uv 2>/dev/null | head -1)"
"$UV" run --with "$1" --script "$HOME/.claude/skills/transcribe-summarize/scripts/transcribe.py" "${@:2}"
}
# Apple Silicon
ts 'mlx-whisper>=0.4.2' reunión.m4a
# Windows / Linux
ts 'faster-whisper>=1.2' reunión.m4a --backend faster-whisper
# Sesga el decodificador hacia jerga conocida y corrige un nombre que siempre falla
ts 'mlx-whisper>=0.4.2' reunión.m4a --prompt 'Terragrunt, EKS' --replace 'north wind=Northwind'
Usa la ruta absoluta. Una ruta relativa solo se resuelve si el directorio de trabajo actual coincide con el directorio de la habilidad.
Contrato de salida
<directorio_salida>/
<nombre_base>.md la transcripción, con marcas [hh:mm:ss]
<nombre_base>-artefactos/
<nombre_base>.srt subtítulos
<nombre_base>.json cada segmento, incluidos los suprimidos
<nombre_base>.run.json backend, modelo, desplazamientos de recorte, recuento de guardia
--json imprime esas rutas en stdout como JSON. Reporta rutas absolutas al usuario; un archivo que tenga que buscar no es un entregable.
Las marcas de tiempo están en el reloj de la grabación original. El recorte acorta el audio durante la decodificación, y los desplazamientos se reasignan antes de escribir cualquier cosa, por lo que el archivo .srt se alinea con el archivo del usuario.
La regla de red — indícala, luego cúmplela
Un backend de red es accesible solo cuando el usuario lo solicita en esa invocación. Nunca como alternativa, nunca después de que falle un backend local, nunca porque sería más rápido. --backend auto no puede acceder a uno; no hay ruta de código.
Antes de enviar cualquier cosa, la herramienta imprime el proveedor, el endpoint, el tamaño del archivo, la duración y el costo estimado, luego espera. Usa --dry-run para mostrar al usuario ese bloque antes de comprometerse. En una sesión no interactiva, se rechaza una carga no confirmada.
Las claves provienen del entorno, una variable por backend, y de ningún otro lugar. Nunca aceptes una como bandera, nunca la repitas, nunca la escribas en un archivo.
| backend | clave | modelo | $/hora | diariza |
|---|---|---|---|---|
groq | GROQ_API_KEY | whisper-large-v3-turbo | 0.04 | no |
groq | GROQ_API_KEY | whisper-large-v3 (predeterminado) | 0.111 | no |
elevenlabs | ELEVENLABS_API_KEY | scribe_v2 | 0.22 | hasta 32 |
gemini | GEMINI_API_KEY | gemini-3.5-transcribe | 0.306 | hasta 3 |
openai | OPENAI_API_KEY | whisper-1 | 0.36 | no |
--backend gemini — las dos cosas que decir antes de ejecutarlo
ts '' /ruta/al/audio.m4a --backend gemini --dry-run # muestra el bloque, no envía nada
ts '' /ruta/al/audio.m4a --backend gemini # luego ejecútalo
Nada que instalar — es http.client de la biblioteca estándar, por lo que el argumento --with está vacío. Dos hechos pertenecen a la frase que le dices al usuario antes de ejecutarlo, porque ninguno es visible desde la línea de comandos:
- El audio se carga dos veces. Gemini no tiene un endpoint de una sola llamada: el archivo va primero a la API de Archivos de Google, y la solicitud de transcripción luego lleva solo el URI que regresó. Google conserva esa carga durante 48 horas. El bloque de divulgación imprime esto; no lo omitas.
- 30 minutos es el límite, no la hora que la propia página de Google menciona. La hora se aplica solo sin marcas de tiempo de palabras ni diarización, y este backend siempre solicita ambas — sin desplazamientos de palabras no hay reloj para asignar al archivo del usuario. Se rechaza una grabación más larga antes de que comience la carga.
No ofrezcas el modo smart de Gemini. No está conectado, a propósito. Si el usuario lo solicita — es la característica principal del modelo, y elimina palabras de relleno y reescribe las autocorrecciones habladas — la respuesta es que la API de Google rechaza las marcas de tiempo de palabras y las etiquetas de orador junto con él, por lo que no produciría un archivo .srt ni oradores, y cambia las palabras. La eliminación de relleno ocurre en el paso de notas en su lugar, donde se etiqueta como resumen y se verifica. Consulta references/backends.md.
El guardia de calidad
Activado por defecto. Los segmentos suprimidos permanecen en el archivo .json con la razón y las métricas; se eliminan del .md y .srt. --guard mark los mantiene en línea marcados, --guard off lo desactiva.
No se transfiere a todos los backends. Las reglas fuertes leen las métricas del decodificador de Whisper. faster-whisper, Groq y OpenAI devuelven las tres, por lo que el guardia es el mismo código. Parakeet, ElevenLabs y Gemini no devuelven ninguna y solo recurren a las reglas independientes del backend — la herramienta lo dice en tiempo de ejecución. Consulta references/backends.md.
Si el usuario pregunta por qué falta un tramo de transcripción, lee el archivo .json: la respuesta está ahí con los números que lo causaron.
Empezar desde una transcripción que ya tienes
La mayoría de las reuniones ya están transcritas por la plataforma, y a menudo no hay audio en absoluto. Teams y Zoom entregan un archivo .vtt (a veces .srt), y es ilegible como prosa: una pista cada pocos segundos, oraciones cortadas entre pistas, el nombre del orador repetido en cada línea, códigos de tiempo por todas partes.
Ese es un punto de entrada compatible. No se retranscribe nada — la ruta de audio se omite por completo:
"$HOME/.claude/skills/transcribe-summarize/scripts/normalise_transcript.py" reunión.vtt
# -> reunión.md, pistas unidas en párrafos, oradores atribuidos, un ancla por párrafo
Luego continúa exactamente como si la transcripción se hubiera decodificado aquí: léela, solicita los hechos de la reunión y escribe las notas debajo de references/notes-register.md.
--no-speakers elimina la atribución si las etiquetas de origen son incorrectas o no deseadas.
Es una conversión de formato y nada más. No se cambian palabras, no se elimina relleno, no se corrige gramática. Decidir lo que se dijo es el paso de resumen y tiene sus propias reglas. Si un usuario te entrega una transcripción desordenada y te pide que la "limpies", normaliza el formato aquí y haz el juicio en las notas — no reescribas silenciosamente las palabras de alguien en un documento que se supone que debe registrarlas.
Los oradores se mantienen en la transcripción y están prohibidos en las notas, lo que no es una contradicción: una transcripción es un artefacto de trabajo y puede llevar procedencia; las notas son un registro y no pueden. notes_check.py aún rechazará una etiqueta Speaker 2 sin formato en las notas.
Si alguien pasa un archivo .vtt a transcribe.py, se detiene y apunta aquí en lugar de intentar decodificarlo.
Archivos de video
.mp4 es lo que realmente es una descarga de Teams o Zoom, y funciona directamente — no es necesario extraer el audio primero. Verificado que funciona: .mp4, .mov, .mkv, .webm, además de todos los contenedores de audio (.m4a, .mp3, .wav, .flac, .opus). ffmpeg extrae la pista de audio; el video se ignora y nunca se carga.
Un archivo sin pista de audio — una grabación de pantalla silenciada — se rechaza de inmediato, antes de que se le ofrezca cualquier backend de red. Esa verificación se ejecuta antes de la puerta de salida específicamente para que un video silencioso nunca se cargue y facture.
La revisión es un paso, no un extra opcional
La salida de la transcripción es un borrador. Dilo y haz que el usuario la verifique.
La transcripción automática se equivoca con confianza de maneras que ningún umbral captura. Medido en audio real en este proyecto: large-v3 eliminó silenciosamente la corrección hablada de una cifra por parte de un orador, y todos los backends escucharon "board pack" como "board packs up". Ambos se leen perfectamente fluidos. El guardia de calidad captura invenciones sobre el silencio; no puede capturar una mala interpretación plausible, y tú tampoco puedes leer la salida.
Por lo tanto, el flujo de trabajo es transcribir → el usuario verifica → luego notas:
- El archivo
.mdde la transcripción termina con una sección "Vale la pena verificar" — marcas de tiempo clasificadas donde es más probable que valga la pena escuchar de nuevo (segmentos con menos confianza, cualquier cosa que contenga cifras). Señala al usuario explícitamente. - Los nombres, las cifras y las fechas son lo que hay que verificar. Son lo primero que se degrada y lo más costoso de tener incorrecto en un registro.
- Las correcciones van en el archivo
.md, a mano o con--replaceen una nueva ejecución. Luego regenera cualquier cosa construida a partir de él.
Nunca presentes una transcripción o un conjunto de notas como verificado. No escuchaste el audio; el usuario sí. Di lo que se decodificó, di dónde es menos seguro y deja que ellos confirmen.
Editar y regenerar
El archivo .md es la fuente. Edítalo, luego reconstruye el PDF — sin retranscripción:
"$HOME/.claude/skills/transcribe-summarize/scripts/notes_check.py" notas.md
"$HOME/.claude/skills/transcribe-summarize/scripts/render_pdf.py" notas.md
Ejecuta el verificador nuevamente después de cualquier edición. La edición manual es exactamente cuando se introduce una fuga de procedencia o una línea inferida de "próximos pasos".
Escribir las notas
Cuando el usuario quiera notas de reunión en lugar de una transcripción, tú las escribes — lee la transcripción y produce el documento tú mismo. Hay un scripts/summarize.py de respaldo que llama a un modelo de red, pero es para ejecutar este pipeline fuera de una sesión de Claude Code; dentro de una, es una carga inútil.
Pregunta primero — tres cosas que el audio no puede decirte
Casi ningún backend aquí identifica a los oradores. Todos los motores Whisper y Parakeet no devuelven ningún campo de orador. Dos excepciones: --backend elevenlabs (Scribe) diariza hasta 32 oradores, y --backend gemini hasta 3. Ambos devuelven etiquetas posicionales en lugar de nombres — speaker_0 y spk:0 respectivamente, los formatos medidos a partir de respuestas en vivo el 2026-09-04 y 2026-09-06 — y el registro prohíbe una etiqueta sin formato en un documento de notas, en cualquier formato. Así que te dicen cuántas personas hablaron y qué líneas van juntas; todavía no pueden decirte quién. La atribución proviene del usuario de cualquier manera; la diarización solo hace posible el mapeo desde la transcripción en lugar de desde la memoria.
Antes de escribir notas, pregunta por:
- Quién estuvo presente. Sin nombres, escribe las notas sin ninguna atribución — impersonal, "se indicó que…". Nunca inventes un nombre, un rol o una etiqueta posicional. Si la transcripción hace obvio quién habló (alguien es mencionado por su nombre), puedes atribuirlo; la ambigüedad significa ningún nombre.
- La fecha de la reunión. La fecha de hoy es un valor predeterminado, no un hecho. El audio a menudo se escribe días después de la conversación, y una fecha incorrecta en un registro factual es peor que ninguna fecha. Confírmala.
- Qué fue la reunión — un título en los propios términos de los participantes.
Pregunta por los tres en un solo mensaje, no uno a la vez, y ofrece la fecha de hoy como valor predeterminado para que el usuario solo tenga que corregirla. Si el usuario se niega a proporcionar asistentes, procede sin atribución y di que eso es lo que hiciste.
Antes de escribir una sola línea, lee references/notes-register.md. Es corto y es toda la especificación. No es un consejo de estilo; las reglas en él se aprendieron al eliminar secciones a mano de un documento real, y las razones son legales más que estéticas.
Las dos reglas que activa:
- Solo resumen factual. Lo que se dijo. Sin análisis, sin implicaciones, sin "próximos pasos", sin "lo que no se cubrió", sin llenar un vacío.
- Se lee como un informe de protocolo escrito por alguien que estuvo allí. Nunca menciona una grabación, una transcripción, audio, un modelo o cualquier artefacto del decodificador — sin códigos de tiempo, sin etiquetas
Speaker 2.
Luego verifícalo, cada vez — el registro no se aplica por sí mismo y un modelo al que se le pide resumir agregará análisis sin que se le solicite:
"$HOME/.claude/skills/transcribe-summarize/scripts/notes_check.py" notas.md
El código de salida 0 está limpio; el 1 lista archivo:línea para cada violación. Corrígelas antes de entregar el documento. Ejecútalo también en el PDF — una fuga que solo existe en el archivo renderizado aún cuenta.
La orientación es bienvenida, pero después de las notas y fuera del documento. Ofrece tu lectura en conversación, o escríbela en un archivo separado. Nunca la añadas.
El PDF
"$HOME/.claude/skills/transcribe-summarize/scripts/render_pdf.py" notas.md
Headless Chrome, Chromium o Edge, descubiertos automáticamente. Si ninguno está instalado, escribe el HTML, lo dice y sale con 0 — el HTML sigue siendo un entregable.
Procedencia
La decodificación local es mlx-whisper (MLX) o faster-whisper (CTranslate2); la preparación de audio es ffmpeg. Los backends de red llaman a los endpoints de transcripción de Groq y OpenAI directamente a través de urllib de la biblioteca estándar — sin SDK del proveedor.
Mantener la procedencia fuera de un documento de notas cambia lo que el documento dice, no lo que sucedió. Grabar una llamada sin consentimiento es un delito penal en muchas jurisdicciones; esta habilidad no facilita eso. Verifica que el usuario tenga autorización antes de procesar cualquier grabación.