<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: carlosp5751258-lgtm</title>
    <description>The latest articles on DEV Community by carlosp5751258-lgtm (@carlosp5751258lgtm).</description>
    <link>https://dev.to/carlosp5751258lgtm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4213804%2F83f2981e-44f8-4d3a-b383-cc69cb0da9bf.png</url>
      <title>DEV Community: carlosp5751258-lgtm</title>
      <link>https://dev.to/carlosp5751258lgtm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/carlosp5751258lgtm"/>
    <language>en</language>
    <item>
      <title>Formularios de issues en GitHub: deja de recibir reportes de error incompletos</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 21:12:32 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/formularios-de-issues-en-github-deja-de-recibir-reportes-de-error-incompletos-1p8</link>
      <guid>https://dev.to/carlosp5751258lgtm/formularios-de-issues-en-github-deja-de-recibir-reportes-de-error-incompletos-1p8</guid>
      <description>&lt;p&gt;"No funciona." Sin versión, sin pasos, sin logs. Si mantienes un repositorio, conoces ese issue. La solución es que GitHub obligue a dar la información antes de enviar: los &lt;strong&gt;issue forms&lt;/strong&gt;, formularios definidos en YAML que sustituyen al cuadro de texto libre.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dónde van
&lt;/h2&gt;

&lt;p&gt;Los formularios viven en &lt;code&gt;.github/ISSUE_TEMPLATE/&lt;/code&gt; de la rama por defecto, con extensión &lt;code&gt;.yml&lt;/code&gt;. Cada archivo es una opción que aparece al pulsar &lt;em&gt;New issue&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.github/
└── ISSUE_TEMPLATE/
    ├── bug_report.yml
    ├── feature_request.yml
    └── config.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Anatomía de un formulario
&lt;/h2&gt;

&lt;p&gt;Un formulario necesita &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt; y &lt;code&gt;body&lt;/code&gt;. Opcionalmente, &lt;code&gt;title&lt;/code&gt; (prefijo del título) y &lt;code&gt;labels&lt;/code&gt; (etiquetas que se aplican solas). Este es el inicio de un reporte de error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;🐛 Reporte de error&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Algo no funciona como debería.&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[Error]:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bug"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;por-clasificar"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;textarea&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pasos&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pasos para reproducirlo&lt;/span&gt;
      &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;1. Ejecuta `...`&lt;/span&gt;
        &lt;span class="s"&gt;2. Abre `...`&lt;/span&gt;
    &lt;span class="na"&gt;validations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;input&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;version&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Versión&lt;/span&gt;
      &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.4.2"&lt;/span&gt;
    &lt;span class="na"&gt;validations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los tipos de campo disponibles son cinco:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;markdown&lt;/code&gt;: texto informativo que no se envía (ideal para instrucciones).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;input&lt;/code&gt;: una línea (versión, URL).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;textarea&lt;/code&gt;: varias líneas. Con &lt;code&gt;render: shell&lt;/code&gt; el contenido se formatea como bloque de código, perfecto para logs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dropdown&lt;/code&gt;: lista de opciones, con &lt;code&gt;multiple: true&lt;/code&gt; si se pueden marcar varias.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;checkboxes&lt;/code&gt;: casillas; cada una puede ser obligatoria.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;validations.required: true&lt;/code&gt; impide enviar el formulario sin ese campo. Úsalo en lo imprescindible (pasos, versión, resultado esperado) y deja el resto opcional: un formulario demasiado largo hace que la gente abandone.&lt;/p&gt;

&lt;p&gt;Cada &lt;code&gt;id&lt;/code&gt; debe ser único dentro del formulario. Las etiquetas de &lt;code&gt;labels&lt;/code&gt; deberían existir en el repositorio; créalas antes en &lt;em&gt;Issues → Labels&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Un truco para logs
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;textarea&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logs&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Logs o mensajes de error&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Revisa que no incluya contraseñas ni tokens.&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shell&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La advertencia sobre tokens no es decorativa: mucha gente pega su configuración completa sin pensarlo.&lt;/p&gt;

&lt;h2&gt;
  
  
  config.yml: ordena la entrada
&lt;/h2&gt;

&lt;p&gt;Este archivo no es un formulario; controla la pantalla de selección:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;blank_issues_enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;contact_links&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;❓ Preguntas y ayuda&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/USUARIO/REPO/discussions&lt;/span&gt;
    &lt;span class="na"&gt;about&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Para dudas de uso, abre una discusión en lugar de un issue.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;🔒 Reportar una vulnerabilidad&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/USUARIO/REPO/security/advisories/new&lt;/span&gt;
    &lt;span class="na"&gt;about&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Reporta problemas de seguridad de forma privada.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con &lt;code&gt;blank_issues_enabled: false&lt;/code&gt; se oculta la opción de issue en blanco, y los &lt;code&gt;contact_links&lt;/code&gt; desvían preguntas y vulnerabilidades a sus canales. El enlace de seguridad funciona si activas &lt;em&gt;Private vulnerability reporting&lt;/em&gt; en &lt;em&gt;Settings → Security&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validar antes de hacer push
&lt;/h2&gt;

&lt;p&gt;Un error de YAML hace que el formulario no aparezca, sin aviso claro. Revisa la sintaxis en local:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import yaml,sys; [yaml.safe_load(open(f)) for f in sys.argv[1:]]; print('OK')"&lt;/span&gt; .github/ISSUE_TEMPLATE/&lt;span class="k"&gt;*&lt;/span&gt;.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Después del push, abre &lt;em&gt;New issue&lt;/em&gt; en tu repo y prueba cada formulario. Si alguno falta, GitHub suele mostrar el error al abrir el archivo &lt;code&gt;.yml&lt;/code&gt; en la interfaz web.&lt;/p&gt;

&lt;p&gt;Con esto, tu próximo "No funciona" llegará con versión, pasos y logs.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Este ejemplo es parte de las &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/plantillas-docs-github" rel="noopener noreferrer"&gt;Plantillas de Documentación para GitHub en Español&lt;/a&gt;&lt;/strong&gt;: paga lo que quieras, desde 2 USD. Más recursos gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>opensource</category>
      <category>tutorial</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Crea una skill de Claude Code para migraciones de base de datos sin sustos</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 21:07:13 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/crea-una-skill-de-claude-code-para-migraciones-de-base-de-datos-sin-sustos-3j95</link>
      <guid>https://dev.to/carlosp5751258lgtm/crea-una-skill-de-claude-code-para-migraciones-de-base-de-datos-sin-sustos-3j95</guid>
      <description>&lt;p&gt;Pide a cualquier asistente "renombra la columna &lt;code&gt;nombre&lt;/code&gt; a &lt;code&gt;nombre_completo&lt;/code&gt;" y lo más probable es que genere un &lt;code&gt;ALTER TABLE ... RENAME COLUMN&lt;/code&gt;. Funciona en tu máquina. En producción, el código que sigue desplegado busca &lt;code&gt;nombre&lt;/code&gt; y empieza a fallar hasta que termine el despliegue.&lt;/p&gt;

&lt;p&gt;En este tutorial vas a crear una &lt;strong&gt;skill&lt;/strong&gt; de Claude Code que le enseña a hacer migraciones seguras por defecto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué es una skill
&lt;/h2&gt;

&lt;p&gt;Una skill es una carpeta con un archivo &lt;code&gt;SKILL.md&lt;/code&gt; dentro de &lt;code&gt;.claude/skills/&lt;/code&gt; (en tu proyecto) o &lt;code&gt;~/.claude/skills/&lt;/code&gt; (para todos tus proyectos). El archivo empieza con un frontmatter YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;migraciones-bd&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Crea&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;revisa&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;migraciones&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;de&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;de&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;datos&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;seguras&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;para&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;producción&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(sin&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bloqueos&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;largos,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sin&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pérdida&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;de&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;datos,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;reversibles&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;compatibles&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;con&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;el&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;código&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;desplegado).&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Úsala&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cuando&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;el&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;usuario&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pida&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;crear&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;una&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;migración,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cambiar&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;el&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;esquema,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;renombrar&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;o&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;borrar&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;columnas,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;añadir&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;índices&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;o&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;revisar&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;una&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;migración&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;existente."&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La &lt;code&gt;description&lt;/code&gt; es la parte más importante: Claude Code la usa para decidir &lt;strong&gt;cuándo&lt;/strong&gt; cargar la skill. Escríbela con las frases que de verdad usas ("renombra esta columna", "añade un índice"). Debajo del frontmatter va el método, en Markdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  El método: expandir y contraer
&lt;/h2&gt;

&lt;p&gt;La idea central es que, durante un despliegue, conviven la versión actual del código y la nueva, así que el esquema debe servir a las dos. En la skill lo expresamos como una tabla que Claude puede consultar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Operación | Forma segura |
|---|---|
| Añadir columna | Nullable o con default constante. Luego rellenar en lotes. Luego &lt;span class="sb"&gt;`NOT NULL`&lt;/span&gt; si hace falta |
| Renombrar columna | Añadir nueva → escribir en ambas → rellenar → leer de la nueva → (otro despliegue) borrar la vieja |
| Borrar columna | Primero dejar de usarla en el código y desplegar; después borrarla en otra migración |
| Añadir índice | PostgreSQL: &lt;span class="sb"&gt;`CREATE INDEX CONCURRENTLY`&lt;/span&gt; (fuera de transacción) |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Así, ante "renombra la columna", Claude propone un cambio en fases y el orden de despliegue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Una checklist que obliga a pensar
&lt;/h2&gt;

&lt;p&gt;Las instrucciones vagas ("ten cuidado con las migraciones") no cambian nada. Las preguntas concretas sí:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; [ ] ¿Bloquea la tabla mucho tiempo (reescritura, índice no concurrente, validación completa)?
&lt;span class="p"&gt;-&lt;/span&gt; [ ] ¿Puede perder datos (&lt;span class="sb"&gt;`DROP`&lt;/span&gt;, truncar tipo, &lt;span class="sb"&gt;`NOT NULL`&lt;/span&gt; sin rellenar)?
&lt;span class="p"&gt;-&lt;/span&gt; [ ] ¿El código actualmente en producción sigue funcionando tras aplicarla?
&lt;span class="p"&gt;-&lt;/span&gt; [ ] ¿Tiene reversión y se ha probado &lt;span class="sb"&gt;`up`&lt;/span&gt; → &lt;span class="sb"&gt;`down`&lt;/span&gt; → &lt;span class="sb"&gt;`up`&lt;/span&gt; en local?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pide también el SQL real que generará el ORM (&lt;code&gt;alembic upgrade --sql&lt;/code&gt;, &lt;code&gt;python manage.py sqlmigrate&lt;/code&gt;): muchos problemas solo se ven ahí.&lt;/p&gt;

&lt;h2&gt;
  
  
  Límites explícitos
&lt;/h2&gt;

&lt;p&gt;Termina la skill con lo que nunca debe hacer sin tu permiso: ejecutar migraciones fuera de local, &lt;code&gt;migrate:fresh&lt;/code&gt;, &lt;code&gt;db:reset&lt;/code&gt;, &lt;code&gt;prisma migrate reset&lt;/code&gt; o cualquier &lt;code&gt;DROP&lt;/code&gt;. Una skill orienta a Claude, pero no es una barrera técnica: si quieres bloquear comandos de verdad, combínala con reglas &lt;code&gt;deny&lt;/code&gt; en &lt;code&gt;.claude/settings.json&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pruébala
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Guarda el archivo en &lt;code&gt;.claude/skills/migraciones-bd/SKILL.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Abre una sesión nueva de Claude Code.&lt;/li&gt;
&lt;li&gt;Pide: &lt;em&gt;"Necesito renombrar &lt;code&gt;usuarios.nombre&lt;/code&gt; a &lt;code&gt;nombre_completo&lt;/code&gt;"&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si la skill se activó, verás un plan en fases, la migración de la primera fase, el SQL generado y el orden de despliegue, en lugar de un &lt;code&gt;RENAME&lt;/code&gt; directo.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Este ejemplo es parte del &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/skills-avanzadas" rel="noopener noreferrer"&gt;Pack 8 Skills Avanzadas para Claude Code (en español)&lt;/a&gt;&lt;/strong&gt;: paga lo que quieras, desde 3 USD. Más recursos gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>database</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Un CLAUDE.md que Claude Code sí respeta: ejemplo real con Terraform</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 21:00:08 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/un-claudemd-que-claude-code-si-respeta-ejemplo-real-con-terraform-1ff1</link>
      <guid>https://dev.to/carlosp5751258lgtm/un-claudemd-que-claude-code-si-respeta-ejemplo-real-con-terraform-1ff1</guid>
      <description>&lt;p&gt;Claude Code lee el archivo &lt;code&gt;CLAUDE.md&lt;/code&gt; de la raíz de tu repositorio al iniciar cada sesión. Es su memoria de proyecto: lo que escribas ahí lo tendrá presente sin que lo repitas. El problema es que la mayoría de los &lt;code&gt;CLAUDE.md&lt;/code&gt; que veo son listas de buenas intenciones ("escribe código limpio", "sigue las buenas prácticas") que no cambian nada.&lt;/p&gt;

&lt;p&gt;Vamos a escribir uno que sí funcione, con un caso donde equivocarse cuesta caro: infraestructura con Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Comandos exactos, no descripciones
&lt;/h2&gt;

&lt;p&gt;Claude va a ejecutar comandos. Dale los que son seguros y dile exactamente cómo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Comandos (seguros, de solo lectura o locales)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Formato: &lt;span class="sb"&gt;`terraform fmt -recursive`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Validar: &lt;span class="sb"&gt;`terraform validate`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Lint: &lt;span class="sb"&gt;`tflint --recursive`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Plan: &lt;span class="sb"&gt;`terraform plan -out=tfplan`&lt;/span&gt; y revisar con &lt;span class="sb"&gt;`terraform show tfplan`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Valida el código" es ambiguo. &lt;code&gt;terraform validate&lt;/code&gt; no lo es. Si tu equipo usa OpenTofu, cámbialo por &lt;code&gt;tofu&lt;/code&gt;: los comandos son iguales.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Convenciones que el modelo no puede adivinar
&lt;/h2&gt;

&lt;p&gt;Claude ya sabe Terraform. Lo que no sabe es cómo lo usa &lt;strong&gt;tu&lt;/strong&gt; equipo. Escribe solo eso, y añade el porqué cuando la regla no sea obvia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`for_each`&lt;/span&gt; con claves estables en lugar de &lt;span class="sb"&gt;`count`&lt;/span&gt; (con &lt;span class="sb"&gt;`count`&lt;/span&gt;, reordenar la lista recrea recursos).
&lt;span class="p"&gt;-&lt;/span&gt; Para renombrar recursos usa bloques &lt;span class="sb"&gt;`moved {}`&lt;/span&gt; en vez de destruir y recrear.
&lt;span class="p"&gt;-&lt;/span&gt; Variables sensibles con &lt;span class="sb"&gt;`sensitive = true`&lt;/span&gt;; secretos desde el gestor de secretos, nunca en &lt;span class="sb"&gt;`.tfvars`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El motivo entre paréntesis importa: con él, Claude aplica la idea a casos que no listaste.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Un flujo con punto de parada
&lt;/h2&gt;

&lt;p&gt;La clave en IaC es decir dónde termina el trabajo de Claude y empieza el tuyo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Flujo&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Lee el módulo y el entorno afectados.
&lt;span class="p"&gt;2.&lt;/span&gt; Haz el cambio mínimo.
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="sb"&gt;`fmt`&lt;/span&gt; + &lt;span class="sb"&gt;`validate`&lt;/span&gt; + &lt;span class="sb"&gt;`tflint`&lt;/span&gt; + &lt;span class="sb"&gt;`plan`&lt;/span&gt;.
&lt;span class="p"&gt;4.&lt;/span&gt; Resume el plan: qué se crea, cambia y &lt;span class="gs"&gt;**destruye**&lt;/span&gt;. La aplicación la hace una persona.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El paso 4 convierte un &lt;code&gt;plan&lt;/code&gt; de 300 líneas en un resumen que puedes revisar en un minuto, y deja claro que el &lt;code&gt;apply&lt;/code&gt; no le corresponde.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. La sección "No hacer"
&lt;/h2&gt;

&lt;p&gt;Es la más importante y la que casi todos olvidan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## No hacer&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Nunca**&lt;/span&gt; ejecutar &lt;span class="sb"&gt;`terraform apply`&lt;/span&gt;, &lt;span class="sb"&gt;`terraform destroy`&lt;/span&gt;, &lt;span class="sb"&gt;`-auto-approve`&lt;/span&gt;,
  &lt;span class="sb"&gt;`terraform state rm/mv`&lt;/span&gt; ni &lt;span class="sb"&gt;`terraform import`&lt;/span&gt; sin aprobación explícita.
&lt;span class="p"&gt;-&lt;/span&gt; No editar el &lt;span class="sb"&gt;`.tfstate`&lt;/span&gt; a mano ni commitearlo.
&lt;span class="p"&gt;-&lt;/span&gt; Sí commitear &lt;span class="sb"&gt;`.terraform.lock.hcl`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un detalle: &lt;code&gt;CLAUDE.md&lt;/code&gt; guía el comportamiento, pero no es una barrera técnica. Para bloquear de verdad un comando, combínalo con reglas de permisos (&lt;code&gt;deny&lt;/code&gt;) en &lt;code&gt;.claude/settings.json&lt;/code&gt; o con un hook &lt;code&gt;PreToolUse&lt;/code&gt;. Lo ideal es usar las dos capas.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Mantenlo corto y vivo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Apunta a 30-80 líneas: todo se carga en cada sesión.&lt;/li&gt;
&lt;li&gt;Si corriges a Claude dos veces por lo mismo, esa corrección va al &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Usa &lt;code&gt;/memory&lt;/code&gt; dentro de Claude Code para editarlo sin salir de la sesión.&lt;/li&gt;
&lt;li&gt;En monorepos, pon un &lt;code&gt;CLAUDE.md&lt;/code&gt; por paquete (&lt;code&gt;infra/CLAUDE.md&lt;/code&gt;, &lt;code&gt;api/CLAUDE.md&lt;/code&gt;): Claude lo lee cuando trabaja en esa carpeta.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prueba rápida
&lt;/h2&gt;

&lt;p&gt;Después de escribirlo, abre una sesión nueva y pide: &lt;em&gt;"Añade un bucket S3 para logs en el entorno dev"&lt;/em&gt;. Si Claude formatea, valida, genera el plan, te lo resume y se detiene sin aplicar, tu &lt;code&gt;CLAUDE.md&lt;/code&gt; está funcionando.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Este ejemplo es parte de las &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/plantillas-claude-md" rel="noopener noreferrer"&gt;15 Plantillas CLAUDE.md por Stack para Claude Code (en español)&lt;/a&gt;&lt;/strong&gt;: paga lo que quieras, desde 3 USD. Más recursos gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>terraform</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Dockerfile multi-stage para Go: de 800 MB a menos de 20 MB</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 20:52:01 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/dockerfile-multi-stage-para-go-de-800-mb-a-menos-de-20-mb-32mc</link>
      <guid>https://dev.to/carlosp5751258lgtm/dockerfile-multi-stage-para-go-de-800-mb-a-menos-de-20-mb-32mc</guid>
      <description>&lt;p&gt;Si tu Dockerfile de Go empieza con &lt;code&gt;FROM golang&lt;/code&gt; y termina con &lt;code&gt;CMD ["./app"]&lt;/code&gt;, tu imagen probablemente pesa más de 800 MB: incluye el compilador, el código fuente y la caché de módulos. Nada de eso hace falta para ejecutar un binario. Con un build &lt;strong&gt;multi-stage&lt;/strong&gt; compilamos en una imagen y copiamos solo el binario a otra mínima.&lt;/p&gt;

&lt;h2&gt;
  
  
  El Dockerfile
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GO_VERSION=1.24&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/go/pkg/mod &lt;span class="se"&gt;\
&lt;/span&gt;    go mod download

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; TARGETOS&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; TARGETARCH&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/go/pkg/mod &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/root/.cache/go-build &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TARGETOS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TARGETARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    go build &lt;span class="nt"&gt;-trimpath&lt;/span&gt; &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-s -w"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /out/app .

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/static-debian12:nonroot&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /out/app /app&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; nonroot:nonroot&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/app"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Qué hace cada parte
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Dos &lt;code&gt;FROM&lt;/code&gt; = dos etapas.&lt;/strong&gt; La primera (&lt;code&gt;build&lt;/code&gt;) tiene Go completo. La segunda parte de una imagen casi vacía y solo recibe &lt;code&gt;/out/app&lt;/code&gt; con &lt;code&gt;COPY --from=build&lt;/code&gt;. Todo lo demás se descarta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;go.mod&lt;/code&gt; y &lt;code&gt;go.sum&lt;/code&gt; antes que el código.&lt;/strong&gt; Docker cachea cada instrucción. Si copias todo de una vez, cualquier cambio en un &lt;code&gt;.go&lt;/code&gt; invalida la descarga de módulos. Separándolos, &lt;code&gt;go mod download&lt;/code&gt; solo se repite cuando cambian tus dependencias.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;RUN --mount=type=cache&lt;/code&gt;.&lt;/strong&gt; Es una función de BuildKit (activado por defecto desde Docker 23). Monta una caché persistente durante el build sin guardarla en la imagen. La caché de compilación de Go (&lt;code&gt;/root/.cache/go-build&lt;/code&gt;) hace que recompilar tras un cambio pequeño tarde segundos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CGO_ENABLED=0&lt;/code&gt;.&lt;/strong&gt; Genera un binario estático que no depende de la libc del sistema. Es lo que permite usar una imagen final sin libc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;-trimpath -ldflags="-s -w"&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;-trimpath&lt;/code&gt; quita las rutas de tu máquina del binario y &lt;code&gt;-s -w&lt;/code&gt; elimina la tabla de símbolos y la información de depuración. El binario queda notablemente más chico.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;--platform=$BUILDPLATFORM&lt;/code&gt; + &lt;code&gt;TARGETOS&lt;/code&gt;/&lt;code&gt;TARGETARCH&lt;/code&gt;.&lt;/strong&gt; Go compila de forma cruzada sin esfuerzo. Con esto, el compilador corre nativo en tu máquina y genera el binario para la arquitectura que pidas, sin emular con QEMU:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker buildx build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64,linux/arm64 &lt;span class="nt"&gt;-t&lt;/span&gt; mi-servicio &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;distroless/static-debian12:nonroot&lt;/code&gt;.&lt;/strong&gt; Una imagen de unos 2 MB que contiene certificados CA (para llamar APIs HTTPS), datos de zona horaria y un usuario sin privilegios. No tiene shell ni gestor de paquetes: si alguien compromete tu app, no tiene herramientas para moverse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Un detalle importante
&lt;/h2&gt;

&lt;p&gt;Como distroless no tiene shell, no puedes usar &lt;code&gt;HEALTHCHECK CMD curl ...&lt;/code&gt;. Define el healthcheck en Kubernetes o compose apuntando a tu endpoint, o haz que tu binario acepte un flag como &lt;code&gt;-healthcheck&lt;/code&gt; y usa &lt;code&gt;HEALTHCHECK CMD ["/app", "-healthcheck"]&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compruébalo
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; mi-servicio &lt;span class="nb"&gt;.&lt;/span&gt;
docker images mi-servicio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para un servicio HTTP típico verás una imagen de entre 10 y 25 MB. Menos tamaño significa despliegues más rápidos, menos costo de almacenamiento y menos superficie de ataque.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Este ejemplo es parte del &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/dockerfiles-optimizados" rel="noopener noreferrer"&gt;Pack Dockerfiles Optimizados&lt;/a&gt;&lt;/strong&gt;: paga lo que quieras, desde 2 USD. Más recursos gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>go</category>
      <category>devops</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Valida Conventional Commits con un hook de git en bash (sin instalar nada)</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 20:48:56 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/valida-conventional-commits-con-un-hook-de-git-en-bash-sin-instalar-nada-487l</link>
      <guid>https://dev.to/carlosp5751258lgtm/valida-conventional-commits-con-un-hook-de-git-en-bash-sin-instalar-nada-487l</guid>
      <description>&lt;p&gt;Un historial con mensajes como "cambios", "arreglos" o "ahora sí" no sirve para nada cuando necesitas encontrar qué commit rompió producción. La convención &lt;strong&gt;Conventional Commits&lt;/strong&gt; resuelve esto con un formato simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tipo(alcance opcional): descripción
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por ejemplo: &lt;code&gt;feat(auth): agregar login con Google&lt;/code&gt; o &lt;code&gt;fix: corregir cálculo de impuestos&lt;/code&gt;. Además de ordenar el historial, permite generar changelogs y versiones semánticas de forma automática.&lt;/p&gt;

&lt;p&gt;La forma más directa de exigirlo es un hook &lt;code&gt;commit-msg&lt;/code&gt;: un script que git ejecuta justo después de que escribes el mensaje y que puede rechazar el commit. Lo vamos a hacer en bash puro, sin Node ni Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. El hook
&lt;/h2&gt;

&lt;p&gt;Crea una carpeta &lt;code&gt;.githooks/&lt;/code&gt; en tu repo y dentro un archivo &lt;code&gt;commit-msg&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;TIPOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert"&lt;/span&gt;
&lt;span class="nv"&gt;MAX_LARGO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;72

&lt;span class="c"&gt;# Git pasa como $1 la ruta del archivo con el mensaje.&lt;/span&gt;
&lt;span class="nv"&gt;encabezado&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'^#'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'/^[[:space:]]*$/d'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Deja pasar los mensajes que genera git (merge, fixup...).&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$encabezado&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^&lt;span class="o"&gt;(&lt;/span&gt;Merge|Revert&lt;span class="se"&gt;\ \"&lt;/span&gt;|fixup!|squash!&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;patron&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"^(&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TIPOS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)(&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="s2"&gt;[a-z0-9._/-]+&lt;/span&gt;&lt;span class="se"&gt;\)&lt;/span&gt;&lt;span class="s2"&gt;)?!?: [^[:space:]].*$"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$encabezado&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ &lt;span class="nv"&gt;$patron&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✖ Formato esperado: tipo(alcance): descripción"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  Tipos: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TIPOS&lt;/span&gt;&lt;span class="p"&gt;//|/, &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;encabezado&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; MAX_LARGO &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✖ Encabezado de &lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;encabezado&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; caracteres (máx. &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MAX_LARGO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Cómo funciona
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;$1&lt;/code&gt;&lt;/strong&gt;: git no te pasa el mensaje, sino la ruta a un archivo temporal que lo contiene. Por eso leemos el archivo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;grep -v '^#'&lt;/code&gt;&lt;/strong&gt;: cuando usas el editor, git agrega líneas de ayuda que empiezan con &lt;code&gt;#&lt;/code&gt;. Las quitamos, junto con las líneas vacías, y nos quedamos con la primera: el encabezado.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El patrón&lt;/strong&gt;: &lt;code&gt;(\([a-z0-9._/-]+\))?&lt;/code&gt; hace opcional el alcance, &lt;code&gt;!?&lt;/code&gt; permite marcar cambios incompatibles (&lt;code&gt;feat!: ...&lt;/code&gt;) y &lt;code&gt;: [^[:space:]]&lt;/code&gt; exige un espacio después de los dos puntos y una descripción real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;=~&lt;/code&gt; sin comillas en el patrón&lt;/strong&gt;: en bash, si pones comillas a la derecha de &lt;code&gt;=~&lt;/code&gt;, se compara como texto literal y no como expresión regular. Por eso el patrón vive en una variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Código de salida&lt;/strong&gt;: si el hook termina con algo distinto de 0, git cancela el commit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Activarlo para todo el equipo
&lt;/h2&gt;

&lt;p&gt;En lugar de copiar el script a &lt;code&gt;.git/hooks/&lt;/code&gt; (que no se versiona), dile a git que use tu carpeta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x .githooks/commit-msg
git config core.hooksPath .githooks
git add .githooks &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"chore: agregar hook commit-msg"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada persona del equipo solo ejecuta &lt;code&gt;git config core.hooksPath .githooks&lt;/code&gt; una vez después de clonar.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Probarlo
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;--allow-empty&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"cambios varios"&lt;/span&gt;        &lt;span class="c"&gt;# rechazado&lt;/span&gt;
git commit &lt;span class="nt"&gt;--allow-empty&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: agregar buscador"&lt;/span&gt; &lt;span class="c"&gt;# aceptado&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si alguna vez necesitas saltarlo, existe &lt;code&gt;git commit --no-verify&lt;/code&gt;, pero úsalo con moderación: el hook está ahí para cuidarte.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Este ejemplo es parte del &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/git-hooks-precommit" rel="noopener noreferrer"&gt;Pack Git Hooks y Pre-commit&lt;/a&gt;&lt;/strong&gt;: paga lo que quieras, desde 2 USD. Más recursos gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>git</category>
      <category>bash</category>
      <category>productivity</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Tu primer CI en GitHub Actions con matriz de versiones (explicado en español)</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 20:44:42 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/tu-primer-ci-en-github-actions-con-matriz-de-versiones-explicado-en-espanol-3l2h</link>
      <guid>https://dev.to/carlosp5751258lgtm/tu-primer-ci-en-github-actions-con-matriz-de-versiones-explicado-en-espanol-3l2h</guid>
      <description>&lt;p&gt;Si tu proyecto no tiene integración continua, cada push es una apuesta: "en mi máquina funciona". Con GitHub Actions puedes ejecutar tu linter y tus tests automáticamente en cada push y en cada pull request, gratis en repositorios públicos. En este tutorial armamos un workflow para Node.js que prueba tu código en dos versiones de Node a la vez, y explico cada línea para que lo adaptes a cualquier lenguaje.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Dónde va el archivo
&lt;/h2&gt;

&lt;p&gt;GitHub busca workflows en &lt;code&gt;.github/workflows/&lt;/code&gt; dentro de tu repositorio. Cualquier archivo &lt;code&gt;.yml&lt;/code&gt; ahí se convierte en un workflow. Crea &lt;code&gt;.github/workflows/node-ci.yml&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. El workflow completo
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Node.js CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workflow }}-${{ github.ref }}&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;20&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;22&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run lint&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Qué hace cada parte
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;on&lt;/code&gt;&lt;/strong&gt;: define cuándo se ejecuta. Aquí, en cada push a &lt;code&gt;main&lt;/code&gt; y en cada PR dirigido a &lt;code&gt;main&lt;/code&gt;. Si tu rama principal se llama &lt;code&gt;master&lt;/code&gt;, cámbialo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;concurrency&lt;/code&gt;&lt;/strong&gt;: si haces tres push seguidos a un PR, no tiene sentido terminar las dos primeras ejecuciones. Esta sección agrupa las ejecuciones por workflow y rama, y cancela las viejas. Ahorras minutos y obtienes feedback más rápido.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;permissions&lt;/code&gt;&lt;/strong&gt;: por defecto, el token del workflow puede tener permisos de escritura. Un CI solo necesita leer el código, así que lo limitamos a &lt;code&gt;contents: read&lt;/code&gt;. Si alguna dependencia maliciosa intenta usar el token, no podrá modificar tu repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;strategy.matrix&lt;/code&gt;&lt;/strong&gt;: el truco más útil. GitHub crea un job por cada valor de la matriz, en paralelo. Con &lt;code&gt;[20, 22]&lt;/code&gt; pruebas en ambas versiones LTS sin duplicar código. &lt;code&gt;fail-fast: false&lt;/code&gt; evita que, si falla Node 20, se cancele Node 22: quieres ver ambos resultados para saber si el problema depende de la versión.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;timeout-minutes&lt;/code&gt;&lt;/strong&gt;: el límite por defecto es de 6 horas. Un test colgado puede consumir tus minutos gratuitos. 15 minutos es un tope razonable para la mayoría de los proyectos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;cache: npm&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;setup-node&lt;/code&gt; guarda la caché de npm usando el hash de tu &lt;code&gt;package-lock.json&lt;/code&gt; como clave. Si el lockfile no cambia, la instalación es mucho más rápida.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;npm ci&lt;/code&gt; en lugar de &lt;code&gt;npm install&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;npm ci&lt;/code&gt; instala exactamente lo que dice el lockfile y falla si no coincide con &lt;code&gt;package.json&lt;/code&gt;. Es reproducible, que es justo lo que quieres en CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Siguiente paso: protege tu rama
&lt;/h2&gt;

&lt;p&gt;Una vez que el workflow pase, ve a &lt;em&gt;Settings &amp;gt; Branches&lt;/em&gt; (o &lt;em&gt;Rules&lt;/em&gt;) y exige que los checks pasen antes de hacer merge. A partir de ahí, nadie (ni tú con prisa un viernes) podrá fusionar código que rompe los tests.&lt;/p&gt;

&lt;p&gt;El mismo patrón sirve para Python (&lt;code&gt;setup-python&lt;/code&gt; o &lt;code&gt;setup-uv&lt;/code&gt; con matriz &lt;code&gt;["3.11", "3.12", "3.13"]&lt;/code&gt;, ¡entre comillas!) o Go (&lt;code&gt;setup-go&lt;/code&gt; con &lt;code&gt;go-version-file: go.mod&lt;/code&gt;).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Este ejemplo es parte del &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/github-actions-ci" rel="noopener noreferrer"&gt;Pack GitHub Actions CI en Español&lt;/a&gt;&lt;/strong&gt;: paga lo que quieras, desde 2 USD. Más recursos gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>devops</category>
      <category>node</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Cómo borrar ramas de git ya fusionadas sin miedo (script Bash con dry-run)</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 20:41:05 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/como-borrar-ramas-de-git-ya-fusionadas-sin-miedo-script-bash-con-dry-run-58ba</link>
      <guid>https://dev.to/carlosp5751258lgtm/como-borrar-ramas-de-git-ya-fusionadas-sin-miedo-script-bash-con-dry-run-58ba</guid>
      <description>&lt;p&gt;Si llevas unos meses en un proyecto, seguramente &lt;code&gt;git branch&lt;/code&gt; te devuelve una lista eterna: &lt;code&gt;fix-login&lt;/code&gt;, &lt;code&gt;feature/pagos&lt;/code&gt;, &lt;code&gt;prueba-2&lt;/code&gt;... La mayoría ya se fusionaron hace tiempo, pero nadie se atreve a borrarlas por miedo a perder trabajo. En este tutorial vamos a construir un script pequeño que las limpia de forma segura.&lt;/p&gt;

&lt;h2&gt;
  
  
  La idea clave: &lt;code&gt;--merged&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Git ya sabe qué ramas están fusionadas. Una rama está "fusionada" en &lt;code&gt;main&lt;/code&gt; cuando su último commit es alcanzable desde &lt;code&gt;main&lt;/code&gt;, es decir, borrarla no hace que se pierda ningún commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;--merged&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese comando funciona, pero su salida está pensada para humanos (marca la rama actual con &lt;code&gt;*&lt;/code&gt; y añade espacios). Para scripts es mejor &lt;code&gt;git for-each-ref&lt;/code&gt;, que permite elegir el formato exacto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="nt"&gt;-each-ref&lt;/span&gt; &lt;span class="nt"&gt;--merged&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;main &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'%(refname:short)'&lt;/span&gt; refs/heads/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora tenemos un nombre por línea, sin adornos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proteger las ramas importantes
&lt;/h2&gt;

&lt;p&gt;Nunca queremos borrar &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;develop&lt;/code&gt; ni la rama en la que estamos. Un truco limpio en Bash es rodear la lista con espacios y comparar con un patrón:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;
&lt;span class="nv"&gt;PROTEGIDAS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main master develop staging production"&lt;/span&gt;
&lt;span class="nv"&gt;actual&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git symbolic-ref &lt;span class="nt"&gt;--short&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; HEAD &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

es_protegida&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$actual&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="nv"&gt;$PROTEGIDAS&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;locales&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; rama&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;es_protegida &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$rama&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; locales+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$rama&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;git &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="nt"&gt;-each-ref&lt;/span&gt; &lt;span class="nt"&gt;--merged&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'%(refname:short)'&lt;/span&gt; refs/heads/&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ramas fusionadas en &lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;):"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Algunos detalles que vale la pena entender:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;set -euo pipefail&lt;/code&gt; hace que el script se detenga ante errores, variables sin definir o fallos dentro de una tubería.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;|| true&lt;/code&gt; en &lt;code&gt;symbolic-ref&lt;/code&gt; evita que el script muera si estás en "HEAD separado".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt; &amp;lt;(...)&lt;/code&gt; (sustitución de procesos) permite que el array &lt;code&gt;locales&lt;/code&gt; siga existiendo después del bucle; con una tubería &lt;code&gt;| while&lt;/code&gt; se perdería, porque el bucle correría en un subshell.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Borrar, pero con red de seguridad
&lt;/h2&gt;

&lt;p&gt;El último paso es borrar. Hay dos decisiones importantes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dry-run por defecto&lt;/strong&gt;: el script solo lista. Para borrar hay que pasar &lt;code&gt;--ejecutar&lt;/code&gt; y además confirmar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usar &lt;code&gt;git branch -d&lt;/code&gt;, no &lt;code&gt;-D&lt;/code&gt;&lt;/strong&gt;: la &lt;code&gt;-d&lt;/code&gt; minúscula se niega a borrar una rama que no esté fusionada. Aunque nuestra lógica fallara, git nos frena.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"¿Borrar estas ramas? [s/N] "&lt;/span&gt; resp &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^[sS]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cancelado."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;rama &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;git branch &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$rama&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El &lt;code&gt;--&lt;/code&gt; antes del nombre indica a git que lo que sigue no es una opción, por si alguna rama tuviera un nombre raro que empiece con guion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Un aviso sobre squash y rebase
&lt;/h2&gt;

&lt;p&gt;Si tu equipo fusiona los pull requests con "Squash and merge" o "Rebase and merge" en GitHub, los commits originales de la rama no llegan a &lt;code&gt;main&lt;/code&gt;, así que git no las detecta como fusionadas. Esas ramas tendrás que revisarlas a mano (o con &lt;code&gt;git cherry&lt;/code&gt;). No es un fallo del script: es la forma en que funcionan esos tipos de merge.&lt;/p&gt;

&lt;p&gt;Con estas pocas líneas puedes pasar de 40 ramas a 5 en un minuto, sin el miedo de perder trabajo.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Este script es parte del &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/scripts-bash-devs" rel="noopener noreferrer"&gt;Pack 12 Scripts Bash para Desarrolladores (Linux)&lt;/a&gt;&lt;/strong&gt;: paga lo que quieras, desde 2 USD. Más recursos gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>git</category>
      <category>bash</category>
      <category>linux</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Cómo pedirle a la IA que depure tu código (sin que adivine)</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 20:37:17 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/como-pedirle-a-la-ia-que-depure-tu-codigo-sin-que-adivine-fdo</link>
      <guid>https://dev.to/carlosp5751258lgtm/como-pedirle-a-la-ia-que-depure-tu-codigo-sin-que-adivine-fdo</guid>
      <description>&lt;p&gt;Si alguna vez pegaste un error en un asistente de IA y recibiste un "prueba esto" que no funcionó, seguido de otro "prueba esto otro", ya conoces el problema: el modelo salta directo a la solución sin entender la causa. El resultado es un ciclo de parches al azar.&lt;/p&gt;

&lt;p&gt;La buena noticia es que se arregla casi siempre con la forma en que pides las cosas. En este artículo te muestro dos prompts que uso para depurar y por qué funcionan.&lt;/p&gt;

&lt;h2&gt;
  
  
  El error más común: pedir el arreglo primero
&lt;/h2&gt;

&lt;p&gt;Un prompt como "arregla este error: TypeError..." invita al modelo a adivinar. No sabe tu versión del lenguaje, ni qué esperabas, ni qué ya intentaste. Y como le pediste un arreglo, te dará uno, aunque no tenga suficiente información.&lt;/p&gt;

&lt;p&gt;La alternativa es separar &lt;strong&gt;diagnóstico&lt;/strong&gt; de &lt;strong&gt;solución&lt;/strong&gt; y obligar a que el razonamiento ocurra antes del código.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt 1: diagnóstico a partir del stack trace
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Actúa como desarrollador senior de [LENGUAJE]. Te paso un error y su stack trace:
[ERROR_Y_STACK_TRACE]
Y el código relevante:
[CÓDIGO]
Explica en orden: 1) qué significa el error, 2) la línea exacta donde se origina
y por qué, 3) las 3 causas más probables ordenadas por probabilidad, 4) cómo
confirmar cada una con un experimento mínimo. No propongas el arreglo hasta
haber razonado la causa.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate en tres detalles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pide hipótesis ordenadas&lt;/strong&gt;, no una sola respuesta. Así ves el razonamiento y puedes descartar rápido lo que no aplica a tu caso.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pide un experimento por hipótesis.&lt;/strong&gt; Un &lt;code&gt;print&lt;/code&gt;, un assert o un test mínimo te dan evidencia real en lugar de confianza ciega.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prohíbe el arreglo prematuro.&lt;/strong&gt; Esa última frase cambia mucho la calidad de la respuesta.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reemplaza los marcadores con información completa: el stack trace entero (no solo la última línea) y el código de las funciones que aparecen en la traza.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt 2: cuando llevas horas atascado
&lt;/h2&gt;

&lt;p&gt;A veces el problema no es el error, sino que ya perdiste el método. Para esos momentos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Llevo [TIEMPO] atascado con este problema: [DESCRIPCIÓN]. Ya probé:
[LO_QUE_YA_PROBÉ]. No me des la solución directamente: ayúdame a construir un
plan de depuración con hipótesis numeradas, el experimento que confirma o
descarta cada una y el orden óptimo para ejecutarlos.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La clave está en &lt;code&gt;[LO_QUE_YA_PROBÉ]&lt;/code&gt;. Si no lo incluyes, el asistente te recomendará exactamente lo que ya hiciste. Si lo incluyes, se ve obligado a buscar en otra dirección. Y pedir "el orden óptimo" hace que priorice primero los experimentos baratos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Buenas prácticas que aplican a cualquier prompt de depuración
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Incluye versiones&lt;/strong&gt;: del lenguaje, del framework y del sistema operativo. Muchos bugs dependen de ellas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Di qué esperabas y qué obtuviste.&lt;/strong&gt; "No funciona" no es un síntoma.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce el código&lt;/strong&gt; al mínimo que reproduce el problema. Muchas veces encontrarás el bug tú mismo en ese proceso.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nunca pegues secretos&lt;/strong&gt;: tokens, contraseñas o variables de entorno reales. Reemplázalos por valores ficticios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verifica antes de confiar.&lt;/strong&gt; Ejecuta el experimento propuesto; no aceptes una explicación solo porque suena convincente.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Con este enfoque, la IA deja de ser una máquina de parches y se convierte en un compañero que piensa contigo el problema.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Estos dos prompts son parte de &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/100-prompts-programadores" rel="noopener noreferrer"&gt;100 Prompts para Programadores (en español)&lt;/a&gt;&lt;/strong&gt;: paga lo que quieras, desde 2 USD. Más recursos gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>debugging</category>
      <category>productivity</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Hooks en Claude Code: bloquea comandos peligrosos con un script de 20 líneas</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 20:33:33 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/hooks-en-claude-code-bloquea-comandos-peligrosos-con-un-script-de-20-lineas-3dln</link>
      <guid>https://dev.to/carlosp5751258lgtm/hooks-en-claude-code-bloquea-comandos-peligrosos-con-un-script-de-20-lineas-3dln</guid>
      <description>&lt;p&gt;Si usas Claude Code a diario, seguramente tienes reglas en tu &lt;code&gt;CLAUDE.md&lt;/code&gt; del tipo "nunca leas el &lt;code&gt;.env&lt;/code&gt;" o "no hagas &lt;code&gt;git push --force&lt;/code&gt;". El problema es que una instrucción en texto es una sugerencia: el modelo la sigue casi siempre, pero "casi" no basta cuando hablamos de secretos o de borrar carpetas.&lt;/p&gt;

&lt;p&gt;Para eso existen los &lt;strong&gt;hooks&lt;/strong&gt;: comandos de shell que Claude Code ejecuta automáticamente en momentos concretos. A diferencia de un prompt, un hook &lt;strong&gt;siempre&lt;/strong&gt; se ejecuta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los cuatro eventos que más vas a usar
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PreToolUse&lt;/code&gt;: antes de que Claude use una herramienta (Bash, Read, Edit...). Ideal para validar o bloquear.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PostToolUse&lt;/code&gt;: después de que la herramienta termina. Ideal para formatear o pasar el linter.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Notification&lt;/code&gt;: cuando Claude necesita tu atención.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Stop&lt;/code&gt;: cuando Claude termina de responder.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El hook recibe por entrada estándar un JSON con, entre otros campos, &lt;code&gt;tool_name&lt;/code&gt; y &lt;code&gt;tool_input&lt;/code&gt;. Y la regla clave: &lt;strong&gt;si el script sale con código 2, la acción se bloquea&lt;/strong&gt; y lo que escribas en &lt;code&gt;stderr&lt;/code&gt; le llega a Claude como explicación, para que busque otra alternativa.&lt;/p&gt;

&lt;h2&gt;
  
  
  El script
&lt;/h2&gt;

&lt;p&gt;Guárdalo como &lt;code&gt;.claude/hooks/proteger.py&lt;/code&gt; en tu proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Bloquea comandos destructivos y el acceso a archivos de secretos.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="n"&gt;datos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;herramienta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;entrada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

&lt;span class="n"&gt;PELIGROSOS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\brm\s+-[a-zA-Z]*r[a-zA-Z]*f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bgit\s+push\s+.*--force&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bmkfs\b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bdd\s+if=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;\s*/dev/sd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;herramienta&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;comando&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entrada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;patron&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;PELIGROSOS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;patron&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;comando&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bloqueado por política: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;comando&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;. Propón una alternativa &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;segura o pide al usuario que lo ejecute manualmente.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;herramienta&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Edit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ruta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entrada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file_path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(^|/)\.env(\.|$)|/secrets/|\.pem$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ruta&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Acceso denegado a archivo sensible: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ruta&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Registrarlo
&lt;/h2&gt;

&lt;p&gt;En &lt;code&gt;.claude/settings.json&lt;/code&gt; (o de forma interactiva con &lt;code&gt;/hooks&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash|Read|Edit|Write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python3 &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/hooks/proteger.py"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;matcher&lt;/code&gt; filtra por nombre de herramienta y &lt;code&gt;$CLAUDE_PROJECT_DIR&lt;/code&gt; apunta a la raíz del proyecto, así que funciona aunque Claude cambie de carpeta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pruébalo sin abrir Claude
&lt;/h2&gt;

&lt;p&gt;Como el hook solo lee JSON por stdin, puedes probarlo directamente en la terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"tool_name":"Bash","tool_input":{"command":"rm -rf build"}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | python3 .claude/hooks/proteger.py&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"salida=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# Bloqueado por política: 'rm -rf build'. ...&lt;/span&gt;
&lt;span class="c"&gt;# salida=2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Haz lo mismo con &lt;code&gt;{"tool_name":"Read","tool_input":{"file_path":"/app/.env"}}&lt;/code&gt; y con un comando inocente como &lt;code&gt;ls -la&lt;/code&gt; (este debe devolver 0).&lt;/p&gt;

&lt;h2&gt;
  
  
  Tres consejos
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No es un sandbox.&lt;/strong&gt; Un patrón puede esquivarse con un comando creativo. Combínalo con reglas &lt;code&gt;deny&lt;/code&gt; en los permisos y, si quieres autonomía total, trabaja dentro de un contenedor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mantén los hooks rápidos.&lt;/strong&gt; Se ejecutan en cada llamada a la herramienta.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revisa los hooks de repos ajenos.&lt;/strong&gt; Viajan con el proyecto y se ejecutan con tus permisos.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si una regla de tu &lt;code&gt;CLAUDE.md&lt;/code&gt; es crítica, conviértela en hook. Es la diferencia entre "por favor, no lo hagas" y "no puedes hacerlo".&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Este ejemplo es parte del &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/guia-claude-code" rel="noopener noreferrer"&gt;Guía Claude Code de Cero a Experto (PDF en español)&lt;/a&gt;&lt;/strong&gt;: paga lo que quieras, desde 3 USD. Más recursos gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>security</category>
      <category>espanol</category>
    </item>
    <item>
      <title>El CLAUDE.md perfecto para proyectos Python (plantilla lista para copiar)</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 20:15:54 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/el-claudemd-perfecto-para-proyectos-python-plantilla-lista-para-copiar-21ka</link>
      <guid>https://dev.to/carlosp5751258lgtm/el-claudemd-perfecto-para-proyectos-python-plantilla-lista-para-copiar-21ka</guid>
      <description>&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; es el archivo que Claude Code lee &lt;strong&gt;al empezar cada sesión&lt;/strong&gt;. Es tu oportunidad de decirle cómo se trabaja en &lt;em&gt;tu&lt;/em&gt; proyecto, para no repetirlo cada vez.&lt;/p&gt;

&lt;p&gt;El error típico es llenarlo de prosa. Lo que funciona: &lt;strong&gt;comandos exactos, convenciones concretas y prohibiciones claras&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plantilla
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md — Proyecto Python&lt;/span&gt;

&lt;span class="gu"&gt;## Entorno&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Gestor: &lt;span class="sb"&gt;`uv`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Ejecutar: &lt;span class="sb"&gt;`uv run python -m paquete`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Tests: &lt;span class="sb"&gt;`uv run pytest -q`&lt;/span&gt; (uno: &lt;span class="sb"&gt;`uv run pytest tests/test_x.py::test_y`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; Formato y lint: &lt;span class="sb"&gt;`uv run ruff format . &amp;amp;&amp;amp; uv run ruff check --fix .`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Tipos: &lt;span class="sb"&gt;`uv run mypy src/`&lt;/span&gt;

&lt;span class="gu"&gt;## Convenciones&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Type hints en toda función pública.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`pathlib`&lt;/span&gt; en vez de &lt;span class="sb"&gt;`os.path`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`logging.getLogger(__name__)`&lt;/span&gt;, nunca &lt;span class="sb"&gt;`print`&lt;/span&gt; en código de librería.

&lt;span class="gu"&gt;## Flujo&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Escribe o ajusta el test primero.
&lt;span class="p"&gt;2.&lt;/span&gt; Implementa.
&lt;span class="p"&gt;3.&lt;/span&gt; ruff + mypy + pytest antes de dar la tarea por terminada.

&lt;span class="gu"&gt;## No hacer&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; No usar &lt;span class="sb"&gt;`except:`&lt;/span&gt; sin tipo.
&lt;span class="p"&gt;-&lt;/span&gt; No commitear &lt;span class="sb"&gt;`.env`&lt;/span&gt; ni &lt;span class="sb"&gt;`data/raw/`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Por qué funciona
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Comandos copiables&lt;/strong&gt;: Claude no tiene que adivinar si usas &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;poetry&lt;/code&gt; o &lt;code&gt;uv&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El comando para un solo test&lt;/strong&gt; le ahorra ejecutar toda la suite en cada cambio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"No hacer"&lt;/strong&gt; previene los errores que más te molestan.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Extra: CLAUDE.md por carpetas
&lt;/h2&gt;

&lt;p&gt;Puedes tener un &lt;code&gt;CLAUDE.md&lt;/code&gt; en subcarpetas (por ejemplo &lt;code&gt;tests/CLAUDE.md&lt;/code&gt;) con reglas locales. Claude lo carga cuando trabaja con archivos de esa carpeta.&lt;/p&gt;

&lt;p&gt;Mantenlo corto: si pasa de ~100 líneas, probablemente algo debería ser una &lt;em&gt;skill&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Los hooks de seguridad están gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Si quieres todo listo (6 skills, 4 comandos, 5 plantillas CLAUDE.md, hooks e instalador), está el &lt;a href="https://claudecodees.gumroad.com/l/kit-claude-code-es" rel="noopener noreferrer"&gt;Kit Claude Code en Español&lt;/a&gt;: &lt;strong&gt;paga lo que quieras, desde 3 USD&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>python</category>
      <category>ai</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Tu primera skill de Claude Code en 10 minutos (en español)</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 20:15:21 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/tu-primera-skill-de-claude-code-en-10-minutos-en-espanol-5795</link>
      <guid>https://dev.to/carlosp5751258lgtm/tu-primera-skill-de-claude-code-en-10-minutos-en-espanol-5795</guid>
      <description>&lt;p&gt;Las &lt;strong&gt;skills&lt;/strong&gt; son la forma más sencilla de enseñarle a Claude Code a hacer una tarea &lt;em&gt;siempre igual&lt;/em&gt;: revisar código, escribir commits, generar tests… Son simples archivos Markdown y Claude decide solo cuándo usarlas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dónde van
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/skills/&amp;lt;nombre&amp;gt;/SKILL.md      # solo este proyecto
~/.claude/skills/&amp;lt;nombre&amp;gt;/SKILL.md    # todos tus proyectos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Anatomía de una skill
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;commits-convencionales&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Genera&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mensajes&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;de&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;commit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;siguiendo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Conventional&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Commits&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;partir&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;de&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;los&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cambios&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stage.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Úsala&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cuando&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;el&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;usuario&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pida&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;haz&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;commit&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;o&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;mensaje&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;de&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;commit&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;."&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Commits convencionales&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Lee &lt;span class="sb"&gt;`git diff --staged`&lt;/span&gt;.
&lt;span class="p"&gt;2.&lt;/span&gt; Elige tipo: feat, fix, refactor, docs, test, chore.
&lt;span class="p"&gt;3.&lt;/span&gt; Formato: &lt;span class="sb"&gt;`tipo(ámbito): resumen en imperativo, ≤ 72 caracteres`&lt;/span&gt;.
&lt;span class="p"&gt;4.&lt;/span&gt; Si el diff mezcla cambios no relacionados, propone dividirlo.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La línea clave es &lt;strong&gt;&lt;code&gt;description&lt;/code&gt;&lt;/strong&gt;: Claude la lee para decidir si la skill aplica. Escríbela pensando en &lt;em&gt;qué diría el usuario&lt;/em&gt; ("haz commit", "revisa mi código"), no en lo que hace internamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consejos que marcan la diferencia
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pasos numerados y verificables.&lt;/strong&gt; "Ejecuta los tests" es mejor que "asegúrate de que funcione".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Di qué NO hacer.&lt;/strong&gt; Por ejemplo: "no hagas &lt;code&gt;git add -A&lt;/code&gt; sin permiso".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Una skill, una tarea.&lt;/strong&gt; Si crece demasiado, divídela.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pruébala&lt;/strong&gt;: pide la tarea con tus propias palabras y comprueba que se activa.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Probarla
&lt;/h2&gt;

&lt;p&gt;Reinicia Claude Code y escribe: &lt;em&gt;"haz commit de esto"&lt;/em&gt;. Si la &lt;code&gt;description&lt;/code&gt; está bien escrita, verás que Claude sigue tus pasos al pie de la letra.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te sirvió?&lt;/strong&gt; Los hooks de seguridad están gratis en &lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Si quieres todo listo (6 skills, 4 comandos, 5 plantillas CLAUDE.md, hooks e instalador), está el &lt;a href="https://claudecodees.gumroad.com/l/kit-claude-code-es" rel="noopener noreferrer"&gt;Kit Claude Code en Español&lt;/a&gt;: &lt;strong&gt;paga lo que quieras, desde 3 USD&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>productivity</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Cómo evitar que Claude Code borre tu proyecto: hooks de seguridad en 5 minutos</title>
      <dc:creator>carlosp5751258-lgtm</dc:creator>
      <pubDate>Wed, 28 Oct 2026 20:02:56 +0000</pubDate>
      <link>https://dev.to/carlosp5751258lgtm/como-evitar-que-claude-code-borre-tu-proyecto-hooks-de-seguridad-en-5-minutos-2p0l</link>
      <guid>https://dev.to/carlosp5751258lgtm/como-evitar-que-claude-code-borre-tu-proyecto-hooks-de-seguridad-en-5-minutos-2p0l</guid>
      <description>&lt;p&gt;Los agentes de código son increíbles… hasta que ejecutan &lt;code&gt;git reset --hard&lt;/code&gt; sobre una tarde de trabajo sin commitear.&lt;br&gt;
Claude Code tiene una función poco conocida para evitarlo: &lt;strong&gt;los hooks&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué es un hook
&lt;/h2&gt;

&lt;p&gt;Un script que Claude Code ejecuta &lt;strong&gt;antes o después&lt;/strong&gt; de usar una herramienta. Si un hook &lt;code&gt;PreToolUse&lt;/code&gt;&lt;br&gt;
termina con &lt;strong&gt;código de salida 2&lt;/strong&gt;, la acción se cancela y Claude recibe tu mensaje de error, así que corrige su plan solo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hook 1: bloquear comandos destructivos
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.claude/hooks/bloquear.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="n"&gt;PATRONES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\brm\s+-[a-zA-Z]*[rR][a-zA-Z]*f?\s+(/|~|\$HOME|\*)(\s|$)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rm recursivo sobre /, ~ o *&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bgit\s+push\b.*(--force\b|-f\b)(?!-with-lease)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git push --force&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bgit\s+reset\s+--hard\b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git reset --hard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(curl|wget)[^|]*\|\s*(sudo\s+)?(ba|z)?sh\b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl | sh&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;motivo&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;PATRONES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bloqueado: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;motivo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hook 2: que no lea tu &lt;code&gt;.env&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Mismo patrón, pero mirando &lt;code&gt;tool_input.file_path&lt;/code&gt; en las herramientas &lt;code&gt;Read|Edit|Write&lt;/code&gt;&lt;br&gt;
y bloqueando &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;*.pem&lt;/code&gt;, &lt;code&gt;id_rsa&lt;/code&gt;… (y permitiendo &lt;code&gt;.env.example&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;
  
  
  Conectarlos
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python3 &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/hooks/bloquear.py"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reinicia Claude Code y escribe &lt;code&gt;/hooks&lt;/code&gt; para comprobar que está activo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Probarlo sin riesgo
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"tool_input":{"command":"rm -rf ~"}}'&lt;/span&gt; | python3 .claude/hooks/bloquear.py&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;   &lt;span class="c"&gt;# 2&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"tool_input":{"command":"npm test"}}'&lt;/span&gt;  | python3 .claude/hooks/bloquear.py&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;   &lt;span class="c"&gt;# 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Código completo
&lt;/h2&gt;

&lt;p&gt;Los dos hooks probados y una skill de code review en español, gratis: &lt;strong&gt;&lt;a href="https://github.com/carlosp5751258-lgtm/claude-code-es" rel="noopener noreferrer"&gt;https://github.com/carlosp5751258-lgtm/claude-code-es&lt;/a&gt;&lt;/strong&gt;.&lt;br&gt;
Si quieres la configuración completa (6 skills, comandos &lt;code&gt;/plan&lt;/code&gt; y &lt;code&gt;/pr&lt;/code&gt;, plantillas CLAUDE.md, autoformato),&lt;br&gt;
está el &lt;strong&gt;&lt;a href="https://claudecodees.gumroad.com/l/kit-claude-code-es" rel="noopener noreferrer"&gt;Kit Claude Code en Español&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>security</category>
      <category>espanol</category>
    </item>
  </channel>
</rss>
