loop engineering
Claude Code 2.1: skills con hooks y subagentes para módulos Odoo 19
Claude Code 2.1.0 permite que una skill traiga sus propios hooks y se ejecute en un subagente. Armamos una skill que valida cada archivo de tu módulo Odoo 19 apenas Claude lo edita.
Focuz Academy · 27 de enero de 2026 · 7 min
El 7 de enero de 2026 se publicó en npm la versión 2.1.0 de Claude Code (registro de versiones). Su CHANGELOG es largo, pero hay tres cambios que modifican la forma de trabajar el loop de un agente de código:
- Hooks en el frontmatter de las skills (y de los agentes): la skill trae sus propias reglas de verificación.
context: fork: una skill puede ejecutarse en un subagente aparte, con el campoagentpara elegir el tipo.- Recarga en caliente: las skills creadas o modificadas en
~/.claude/skillso.claude/skillsquedan disponibles sin reiniciar la sesión.
Dos días después, la 2.1.3 unificó los slash commands y las skills "sin cambio de comportamiento", según el mismo changelog. Es decir: hoy una skill es la unidad para empaquetar conocimiento, permisos y verificaciones.
En este post armamos una skill para desarrollar módulos de Odoo 19 que se verifica sola.
Por qué importa para el loop
Un agente produce mejor código cuando recibe retroalimentación inmediata y objetiva. Antes, los hooks vivían en settings.json y aplicaban a todo el proyecto. Ahora, según la documentación de hooks, los hooks de una skill se registran cuando la skill se invoca, usando el mismo formato que en settings. Así, la verificación de Odoo se activa solo cuando invocas la skill (y, según esa misma documentación, sigue activa el resto de la sesión).
Paso 1: la skill con su hook
Crea .claude/skills/odoo-19/SKILL.md en tu repositorio de addons:
---
name: odoo-19
description: Convenciones para escribir y modificar módulos Odoo 19. Úsala al crear modelos, vistas o métodos ORM.
allowed-tools:
- Read
- Grep
- Glob
hooks:
PostToolUse:
- matcher: "Edit|Write"
hooks:
- type: command
command: "python3 \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/validar_odoo.py"
---
# Convenciones Odoo 19
- No uses record._cr, record._uid ni record._context: están obsoletos.
Usa self.env.cr, self.env.uid y self.env.context.
- No uses read_group en código nuevo: usa _read_group (backend)
o formatted_read_group (API pública).
- No importes desde odoo.osv: está obsoleto.
- Para manipular dominios, usa la API odoo.Domain.
- Marca con @api.private los métodos públicos que no deben exponerse por RPC.
Cada regla sale del changelog oficial del ORM de Odoo: en Odoo 19 se declararon obsoletos record._cr, record._context, record._uid y odoo.osv; en la versión Online 18.2 se declaró obsoleto read_group en favor de _read_group y formatted_read_group, y se agregó @api.private; en la 18.1 llegó la API odoo.Domain.
Detalles a notar:
allowed-toolsacepta listas YAML desde la 2.1.0.- El hook usa
$CLAUDE_PROJECT_DIR, la variable que Claude Code entrega a los comandos de hooks.
Paso 2: el script que valida
Crea .claude/hooks/validar_odoo.py. Claude Code envía al hook un JSON por la entrada estándar; en PostToolUse incluye tool_input.file_path, la ruta del archivo editado (referencia de PostToolUse).
import json
import re
import sys
import xml.etree.ElementTree as ET
datos = json.load(sys.stdin)
ruta = datos.get("tool_input", {}).get("file_path", "")
errores = []
if ruta.endswith(".py"):
with open(ruta, encoding="utf-8") as f:
codigo = f.read()
try:
compile(codigo, ruta, "exec")
except SyntaxError as e:
errores.append(f"Error de sintaxis en la línea {e.lineno}: {e.msg}")
for obsoleto in ("._cr", "._uid", "._context"):
if obsoleto in codigo:
errores.append(f"Usa self.env en lugar de {obsoleto} (obsoleto en Odoo 19)")
if re.search(r"\.read_group\(", codigo):
errores.append("read_group está obsoleto: usa _read_group o formatted_read_group")
elif ruta.endswith(".xml"):
try:
ET.parse(ruta)
except ET.ParseError as e:
errores.append(f"XML mal formado: {e}")
if errores:
print("\n".join(errores), file=sys.stderr)
sys.exit(2) # Claude Code muestra este mensaje a Claude
La clave es el código de salida 2. En PostToolUse la herramienta ya se ejecutó, así que no se bloquea nada: Claude Code le muestra el stderr a Claude, que corrige en el siguiente paso. Cualquier otro código distinto de 0 se trata como un error no bloqueante: la acción sigue y el mensaje no se le entrega a Claude como retroalimentación. Lo probamos con un .py que usaba self._cr y con un XML sin cerrar: ambos devolvieron 2 con su mensaje; un archivo correcto devolvió 0.
Es una verificación deliberadamente simple (busca texto, no analiza el AST). Su objetivo es cerrar el loop rápido; tus pruebas de Odoo siguen siendo la red de seguridad.
Paso 3: una segunda skill que corre en un subagente
Para revisar un módulo completo sin llenar tu contexto principal, usa context: fork. La documentación de skills indica que la skill se ejecuta en un subagente del tipo indicado en agent, que no ve tu historial: las instrucciones deben bastarse solas.
---
name: revisar-modulo-odoo
description: Revisa un módulo Odoo 19 y lista usos de APIs obsoletas.
context: fork
agent: Explore
---
Revisa el módulo indicado en los argumentos.
1. Lista los modelos (_name, _inherit) y sus archivos.
2. Busca record._cr, ._uid, ._context, read_group y "from odoo.osv".
3. Devuelve una tabla: archivo, línea, problema, reemplazo sugerido.
No edites archivos.
Según la documentación de subagentes, Claude usa Explore para buscar y entender código sin hacer cambios, y así los resultados de la exploración no llenan tu conversación principal: recibes el resumen, no las decenas de lecturas intermedias.
Otros cambios de la 2.1.x que te sirven
- Comodines en permisos de Bash en cualquier posición:
Bash(python3 *)oBash(git * main)(2.1.0). once: trueen un hook para que corra una sola vez (2.1.0).- Descubrimiento de skills en carpetas
.claude/skillsanidadas al trabajar en subdirectorios (2.1.6): útil si cada addon tiene su propia skill. - Los hooks
PreToolUsepueden devolveradditionalContextal modelo (2.1.9).
Ejercicio
- Copia la skill
odoo-19y el script en un repositorio de addons de prueba. - Abre Claude Code y pídele: "Agrega a res.partner un método que cuente sus pedidos usando read_group".
- Observa cómo el hook rechaza
read_groupy Claude lo reescribe con_read_group. - Ejecuta
/revisar-modulo-odoo mi_moduloy compara el resumen con lo que encontrarías a mano. - Reto: agrega al script una regla propia de tu equipo (por ejemplo, prohibir
sudo()sin comentario).
Fuentes: CHANGELOG de Claude Code, versiones en npm, documentación de skills, documentación de hooks, changelog del ORM de Odoo 19.
Guía gratuita: 5 agentes Python para Odoo
Casos de Junior a Senior, con código verificado contra Odoo 20.
Descargar →¿Empiezas desde cero? Lee qué es el agent loop y sigue las lecciones gratuitas.