CommunityProgramación y desarrollogithub.com

adamstallard/bug-hunter

Detect bugs en código recién escrito antes de confirmarlo. Ejecútalo antes de cada `git commit` que contenga código que tú o el usuario acaben de escribir.

¿Qué es bug-hunter?

Bug Hunter es una habilidad para agentes de IA (como Claude Code, Cursor o Codex) que automatiza la detección y corrección de errores en cambios de código antes de confirmarlos. Actúa sobre el diff staged, identifica candidatos a bugs mediante análisis estático y, para cada uno, escribe una prueba de regresión que falla demostrablemente en el código original y pasa tras la corrección. Un segundo agente refuta candidatos débiles, y solo los errores confirmados se corrigen. La habilidad admite hasta tres iteraciones consecutivas sin intervención del usuario, y genera un informe paso a paso. Es compatible con flujos donde otro agente gestiona las confirmaciones y debe ejecutarse siempre que se haya escrito código nuevo, no solo cuando se pide explícitamente.

Compatible con✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/adamstallard/shared-skills/tree/HEAD/.agents/skills/bug-hunter

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Bug Hunter

Una afirmación sin una prueba que falle es una conjetura. Todo lo que esta habilidad informa está probado por una prueba observada en rojo antes de la corrección y en verde después; todo lo que no puede probar se descarta. Esa es la idea principal, y la razón por la que vale la pena leer su resultado.

Edita el árbol de trabajo, añade archivos de prueba y, por defecto, realiza confirmaciones.

  • Cómo hacer cada paso — reference.md. Léelo antes de los Pasos 4–7, antes de escribir un informe y antes de confirmar.
  • Pruebas de mutación, Paso 3 al Paso 9 — mutation.md. Léelo antes de comenzar cualquier mutante.

El contrato

  1. Cada error corregido tiene una prueba de regresión que se observó en rojo en el código sin corregir y en verde después de la corrección.
  2. Cada candidato que no se pueda poner en rojo, o que una segunda opinión refute, se descarta — y su prueba se elimina, nunca se deja fallando.
  3. Cualquier cosa real pero fuera del alcance o no comprobable se informa, no se corrige.
  4. El conjunto completo de pruebas termina como mínimo igual que empezó.

Lista de verificación del flujo de trabajo

- [ ] Paso 0: Delimitar el cambio — solo diff staged
- [ ] Paso 1: Triaje — ¿merece la pena revisar esta confirmación?
- [ ] Paso 2: Línea base — ejecutar el conjunto de pruebas antes de tocar nada
- [ ] Paso 3: Encontrar candidatos (modelo fuerte, alcanzabilidad requerida); agentes de mutación junto a él donde las funciones modificadas tengan pruebas
- [ ] Paso 4: Probar — escribir la prueba, ejecutarla, exigir un rojo coincidente
- [ ] Paso 5: Refutar — una segunda opinión intenta matar cada error probado, y verifica cada prueba superviviente y afirmación de código muerto
- [ ] Paso 6: Corregir (pregunta de prevención, confinado al alcance)
- [ ] Paso 7: Verificar — nuevas pruebas en verde; el conjunto completo una vez, antes del Paso 9
- [ ] Paso 8: Iterar — una y otra vez, hasta 3, sin preguntar; nunca una 4ª
- [ ] Paso 9: Informar una vez al final; preguntar al usuario cada decisión abierta (selector — escrito no es preguntado); luego confirmar (con el tráiler de prose cuando esté instalado) o dejarlo para revisión — o, bajo "caller commits", terminar con el bloque de `scripts/caller-result.sh`

Nunca ejecutes esto desde dentro de un hook pre-commit de git. Escribe archivos, añade pruebas y confirma — todo lo cual lucha contra el bloqueo del índice de git. Se ejecuta antes de que se cree la confirmación, y la confirmación ocurre en el Paso 9.

Un hook compañero vigila las confirmaciones que nunca pasaron por aquí: después de cada comando pregunta a git si HEAD se movió y si la nueva confirmación lleva un tráiler Bug-hunter:. Por lo tanto, el tráiler es el único marcador que importa — sin variable de entorno, nada que recordar, y el registro vive en la propia confirmación donde git log puede auditarlo.

El hook también hace una segunda pregunta sobre cualquier tráiler que no sea una nota de omisión — 0 bugs found y aborted ... incluidos, no solo los que informan de una corrección: ¿coincide un tráiler Bug-hunter-Tree con el árbol que realmente aterrizó? Esa vinculación es lo que scripts/mint-trailer.sh produce cuando se ejecuta contra el árbol staged real, justo antes de la confirmación — consulta Paso 9 para saber cómo. Un tráiler Bug-hunter: puede ser un tráiler git real con la redacción exacta y seguir siendo una frase recordada por haber leído este archivo, en lugar de una producida por ejecutar cualquier parte del mismo — esa es la única forma que la comprobación de presencia de cadena no puede detectar, y para eso sirve esta vinculación.

Una confirmación que aterriza sin un tráiler Bug-hunter:, o con uno cuya vinculación Bug-hunter-Tree no coincide, se nombra en un informe, con el repositorio en el que se encuentra. El informe te envía a la sección siguiente y no da ningún comando para corregir la confirmación: el hook no puede ver dónde estás, y la confirmación que nombra a menudo no es tu HEAD.


Si eres un agente a punto de confirmar

Si otra habilidad hace tus confirmaciones y ejecuta esta habilidad antes de cada una de ellas, deja que esa habilidad comience desde "confirmar esto"; aún así, ejecútala para "comprobar errores en esto". Si este turno ya ha indicado un asunto de confirmación, usa ese asunto sin cambios. Solo añade tráilers.

Cada confirmación que crees pasa por esta habilidad y lleva un tráiler Bug-hunter:. No solo cuando se pide por su nombre — cada vez. Una confirmación con código recibe la caza. Una confirmación con solo documentación, especificaciones, pruebas o formato se salta la caza, no la habilidad. "Confirmar esto", "confirmar mis cambios", "terminar y confirmar", o tu propia decisión de confirmar después de terminar una tarea significan: preparar el cambio, ejecutar esta habilidad, luego confirmar. El triaje (Paso 1) es lo que decide si una confirmación determinada merece la pena revisarse, por lo que ejecutarla siempre es barato cuando no aplica.

Ejecútala tú mismo, en tu propia sesión. El agente que escribió el cambio ejecuta esta habilidad. No entregues toda la ejecución a un subagente que luego inicie buscadores, refutadores y agentes de mutación por su cuenta: la ejecución necesita un orquestador que permanezca vivo para recoger supervivientes y reanudar agentes, y un delegado anidado sigue terminando su turno mientras sus hijos se ejecutan. Delegar los pases individuales, como dicen los pasos, es cómo se supone que debe ejecutarse.

No evites esta habilidad

Un encabezado, no cinco reglas, porque un agente que busca una salida de una comprobación encuentra de forma fiable la costura entre reglas enunciadas por separado. Cada línea aquí se añadió después de que un agente sorteara la regla que cierra.

1. Di cuál estás haciendo, en voz alta, cada vez. Una línea en la respuesta que el usuario pueda ver: "ejecutando bug-hunter en N archivos", o "saltando bug-hunter — ". No en tu razonamiento — en la respuesta. Saltar sin decir nada parece exactamente como ejecutarlo y no encontrar nada. Una omisión declarada puede ser anulada en cuatro palabras; una no declarada es una decisión que tomaste por ellos. Si te saltas, la confirmación también lleva el tráiler — commit-with-trailer.sh -F msg.txt "skipped — <motivo>", como en Paso 9. Ambos, no solo uno. Bajo caller commits, la omisión va al script de resultado en su lugar (Cuando el caller confirma).

2. Nunca pidas permiso para ejecutarlo. El desencadenante es una propiedad del código, no un momento: si se ha escrito código desde la última pasada que volvió limpia, se debe una pasada. Antes de una confirmación, entre iteraciones, después de una corrección que el usuario pidió, después de una refactorización, después de una confirmación que ya aterrizó — no son casos separados, todos son "se escribió código". Una lista de momentos invita a buscar el momento que no está en ella.

3. Anunciar solo cuenta si ya has empezado. "Eso se ejecuta a continuación a menos que quieras detenerte aquí" · "Lo lanzaré si estás contento con ello" · "las correcciones necesitan su propia pasada — ¿quieres que lo ejecute?" Cada una termina el turno con la comprobación sin ejecutar, lo que devuelve la decisión tan claramente como preguntar. Si estás describiendo una ejecución en tiempo futuro, no la has empezado, y ese es el error.

4. Las iteraciones se ejecutan una tras otra, y el informe es debido. Hasta tres, sin revisión intermedia, y sin informe provisional — un informe entre iteraciones invita a detenerse temprano y hace que una ejecución inacabada parezca terminada. Consulta los Pasos 8 y 9.

Tres es el presupuesto, no una oferta inicial. No hay cuarta iteración. "¿quieres que ejecute una pasada más?" · "las correcciones de la iteración 3 nunca se revisaron — ¿debería hacer una 4ª?" Ambas devuelven una decisión que el presupuesto ya tomó, en el momento en que el usuario tiene más probabilidades de decir que no. Una ejecución que alcanza el límite termina en el informe de causas raíz (Paso 9) y una opción por causa, donde la primera opción es una nueva ejecución en la iteración 1 limitada a esa causa. Eso no es la regla 2 suspendida: una pasada sobre las correcciones de la iteración 3 realmente es debida, y sigue siendo debida — lo que se agotó es el presupuesto para tomarla dentro de esta ejecución, por lo que se convierte en una nueva ejecución y qué causa la recibe es decisión del usuario. Tampoco es la regla 3 invertida: dentro del presupuesto se debe una pasada y la empiezas sin preguntar; en el límite la ejecución ha terminado, y lo que queda es una decisión, ofrecida como opciones en lugar de como una pregunta abierta.

5. Estas no son razones. Cada una ha sido utilizada, por un modelo capaz, para saltarse esta habilidad en una confirmación que luego envió un error:

  • el cambio es pequeño — los cambios de una línea se equivocan todo el tiempo
  • confío en este código — también lo hacía cada autor de cada error que esto ha encontrado
  • el usuario está esperando / nos estamos moviendo rápido — ese es el momento en que se intercambia la calidad, que es para lo que existe esto
  • la sesión es larga y esto es caro — una ejecución que no encuentra nada es barata, el triaje detiene la mayoría de las confirmaciones antes de la parte cara, y las pasadas largas se ejecutan en segundo plano de todos modos (Paso 3)
  • ya lo ejecuté en código relacionado antes — no lo ejecutaste en esto
  • esto es la maquinaria de la propia habilidad, no código real — es código real, y los peores errores en la historia de esta habilidad han estado exactamente en esa maquinaria
  • el usuario está claramente mirando y objetaría si le importara — está mirando la salida, no la ausencia de ella

Crear errores y seguir adelante porque la comprobación era inconveniente es el comportamiento específico que esta habilidad existe para detener. Hacerlo a esta habilidad no es ironía, es simplemente el error.

De quién es la decisión sobre el costo. Una ejecución completa son minutos; una mala es media hora. Quienquiera que instaló esto ya sopesó una confirmación más lenta contra un error que llega a la rama principal. Un agente que decide silenciosamente que la comprobación es demasiado lenta hoy no ha hecho un juicio de ingeniería — ha anulado esa decisión en nombre de alguien que no está en la sala, en la dirección que resulta ser menos trabajo. Si el costo realmente importa ahora, dilo en una frase y deja que ellos elijan.

De quién es la decisión

Dos listas cortas, porque ambos fallos viven en el límite entre ellas: preguntar sobre la primera entrena al usuario a pasar por alto, y decidir la segunda por ellos es la opción 3 tomada en silencio.

Tuyas, nunca preguntadas. Si ejecutar y cuándo; si un candidato está probado, refutado o descartado; cómo corregir un error probado dentro del cambio; si iterar, hasta el límite; ejecutar el conjunto de pruebas; confirmar una ejecución que terminó limpia sin nada abierto — excepto bajo caller commits, donde la confirmación es del caller.

Del usuario, siempre preguntadas. Sobre qué actuar: cada causa raíz y cada recomendación (corregir ahora / anotarlo / saltar), cada entrada no resuelta de una cancelación, si una confirmación sigue adelante cuando algo informado-no-corregido cambia lo que significa (modo solo informe), y la parada del Paso 0 — qué ediciones divergentes staged y unstaged de un archivo conservar.

Cuando preguntes, usa el selector interactivo del anfitrión (en Claude Code, la herramienta AskUserQuestion): cualquier pregunta con opciones discretas pasa por él, una pregunta por decisión, en orden de gravedad, nunca enterrada en prosa al final de un informe largo. Texto plano es el recurso para anfitriones sin selector, no el valor predeterminado. El selector es también la única cosa aquí que es estructural en lugar de consultiva — un turno con un selector abierto no puede terminar hasta que el usuario responda — por lo que la siguiente regla importa.

Preguntado significa que el usuario lo vio. Una pregunta se hace cuando está en una respuesta que el usuario lee o un selector que responde — no cuando un agente la escribió. Si esta habilidad se ejecuta en un agente que informa a otro agente, su informe del Paso 9 y sus preguntas llegan a ese agente, y nada se ha preguntado todavía: el salto mueve el selector, no descarga la pregunta. Por lo tanto:

  • Si un agente que generaste ejecutó esto en su propio cambio, su informe es tu entrada del Paso 9, no tu salida del Paso 9. Cada decisión abierta en ese informe se pone frente al usuario en tu siguiente respuesta, antes que cualquier otra cosa — a través de tu selector, una pregunta por decisión, o el bloque numerado del informe textualmente donde no haya uno. "La ejecución se canceló" o "encontró tres causas raíz" sin opciones debajo es transmitir sobre el informe en lugar de entregarlo; si estás escribiendo esa frase, ese es el error. Mantener la decisión a través de un turno mientras se termina otro trabajo es la opción 3, tomada por ellos — el otro trabajo espera, o la respuesta lleva ambos.
  • Si estás ejecutando esto en un agente que informa a otro agente, no tienes un selector que llegue al usuario. Imprime el informe completo en su formato — no un resumen del mismo — con el pie de página de traspaso (reference.md) que le dice a quien lo tiene lo que todavía es suyo preguntar. Di claramente qué decisiones están abiertas y que no pudiste preguntarlas. Bajo caller commits, la lista de decisiones del bloque de resultado reemplaza el pie de página (Cuando el caller confirma).

Una ejecución no ha terminado hasta que sus preguntas están frente al usuario, sea cual sea el agente que las lleve allí.

Paso 0 — Delimitar

El cambio bajo revisión es el diff staged:

git diff --cached --stat
git diff --cached

Lee el archivo completo para cada archivo modificado, no solo los fragmentos — un error suele estar en la interacción entre el código nuevo y el código a su alrededor.

Si algún archivo aparece tanto en el diff staged como en el unstaged, detente. Estarías razonando sobre el índice mientras pruebas el árbol de trabajo, y discrepan. Dile al usuario qué archivos divergen y pídele que prepare o revierta esas ediciones. Nunca uses git stash para solucionar esto — el usuario tiene trabajo sin confirmar y esta habilidad está a punto de escribir archivos encima de él.

Los cambios unstaged en otros archivos están bien. Anótalos en el informe.

No busques errores en especificaciones, documentación o pruebas existentes. Los cambios en esos archivos siguen siendo contexto que vale la pena leer.

Paso 1 — Triaje

El triaje decide a partir del diff, no del mensaje de confirmación. Para un diff de hasta aproximadamente 300 líneas cambiadas, haz el triaje tú mismo, en el bucle en el que ya estás: un subagente cuesta más de iniciar que leer un diff de ese tamaño. Delega un diff más grande a un solo pase rápido en un modelo pequeño y barato. De cualquier manera, el pase recibe la lista de archivos, el diff y si el usuario pidió un punto de control (abajo). Pide uno de REVIEW o SKIP más una razón de una línea. No pidas un mensaje de confirmación primero, y no leas un prefijo de asunto como una señal — esta habilidad no impone ninguna convención de confirmación (reference.md).

SKIP cuando el diff muestra:

  • Nada fuera de documentación, especificaciones, pruebas, archivos de bloqueo, generación de código, refactorización mecánica o cambios de formato.
  • Solo metadatos del proyecto (.gitignore, Dockerfile, dependencias, configuraciones de CI/CD, archivos de configuración).
  • El cambio es trivial (por ejemplo, renombrar una variable local).

REVIEW en caso contrario. Si el triaje es REVIEW, procede con los pasos restantes. Si es SKIP, salta directamente al Paso 9.

Paso 2 — Línea base

Ejecuta el conjunto de pruebas completo antes de tocar nada:

# Ejemplo (ajusta según el proyecto):
make test 2>&1 || npm test 2>&1 || python -m pytest 2>&1

Anota cualquier fallo existente. Si el conjunto no se ejecuta limpio, puedes pausar y preguntar al usuario, o continuar y anotarlo en el informe.

Paso 3 — Encontrar candidatos

Usa un modelo fuerte para analizar el diff staged y encontrar candidatos a errores. El análisis debe ser alcanzable: cada candidato debe ser un camino ejecutable real en el código, no una posibilidad teórica. Para cada función modificada que tenga pruebas existentes, ejecuta un agente de mutación junto al análisis.

Anota cada candidato con:

  • Ubicación (archivo y número de línea)
  • Descripción del error potencial
  • Por qué es alcanzable (breve justificación)

Paso 4 — Probar

Para cada candidato, escribe una prueba que demuestre el error:

  1. Escribe la prueba en el archivo de prueba adecuado.
  2. Ejecuta la prueba contra el código sin corregir y confirma que falla (rojo).
  3. Si no falla, descarta el candidato y elimina la prueba.

Paso 5 — Refutar

Envía cada prueba que demostró un error a un agente separado (segunda opinión) que intente refutarla. Este agente:

  • Intenta encontrar una razón por la que la prueba no es válida (por ejemplo, prueba incorrecta, mala interpretación del código).
  • Si refuta el error, descarta la prueba y el candidato.
  • También verifica cualquier afirmación de código muerto.

Paso 6 — Corregir

Para cada error que sobrevive a la refutación:

  1. Pregúntate: "¿Cómo se puede prevenir este error de forma general en lugar de solo parchear esta aparición?"
  2. Aplica una corrección limitada al alcance del cambio staged.
  3. No arregles cosas fuera del alcance; repórtalas en su lugar.

Paso 7 — Verificar

Ejecuta todas las nuevas pruebas y confirma que están en verde. Luego ejecuta el conjunto de pruebas completo una vez para asegurarte de que no hay regresiones. Si falla alguna, detente y resuelve.

Paso 8 — Iterar

Si la ejecución es limpia (no se encontraron errores, o todos se corrigieron y verificaron), termina. Si quedan candidatos no probados, repite desde el Paso 3 hasta 3 veces en total. No preguntes al usuario entre iteraciones. Nunca hagas una cuarta iteración dentro de la misma ejecución.

Paso 9 — Informar y confirmar

Genera un informe que contenga:

  • Resumen de lo que se hizo.
  • Errores encontrados y corregidos (con pruebas de regresión).
  • Candidatos descartados y por qué.
  • Decisiones abiertas (cosas reales pero fuera del alcance o no comprobables).
  • Estado del conjunto de pruebas.

Si el informe tiene decisiones abiertas, presenta cada una al usuario a través del selector interactivo del anfitrión antes de confirmar. Las preguntas deben tener opciones discretas (por ejemplo, "Corregir ahora", "Anotar para después", "Saltar").

Confirmar: Una vez que todas las decisiones están tomadas, confirma:

scripts/commit-with-trailer.sh -m "<mensaje>"

Asegúrate de que el mensaje de confirmación incluya los tráilers relevantes, especialmente Bug-hunter: con el resultado (por ejemplo, Bug-hunter: 3 bugs found, 2 fixed, 1 reported).

Cuando el caller confirma

Si un caller (otra habilidad o agente) gestiona la confirmación, no intentes confirmar. En su lugar:

  1. Al final de tu informe, haz que tu salida sea un bloque con formato que scripts/caller-result.sh produce.
  2. Incluye las líneas de tráiler y cualquier decisión abierta que el caller deba utilizar.
  3. Deja el cambio staged; el caller se encargará de confirmar.

Este documento es parte de la habilidad Bug Hunter. Para más detalles, consulta reference.md y mutation.md.

Skills relacionados