Function hooks de Claude Code: qué son y cómo usarlos
Los hooks de Claude Code que ya conoces son scripts de shell: se disparan en un evento, reciben un JSON por la entrada estándar y devuelven un código de salida. Sirven, pero se quedan cortos en cuanto quieres hacer algo más que aprobar o bloquear. Los function hooks cambian esa historia: convierten cada hook en una función de TypeScript que se compone con las demás como si fuera middleware de Express o Koa. Es una propuesta en fase de early access (issue #91870 del repositorio oficial), y en cuanto la pruebas entiendes por qué está dando que hablar.
Vengo de leerme el paper de arquitectura que Anthropic ha publicado junto a la propuesta, ver las demos y montar un plugin de ejemplo. Y sí: esto es otra liga.
Antes de nada, un aviso honesto. Function hooks todavía no es una feature estable. Es una propuesta que Anthropic ha puesto sobre la mesa detrás de un flag experimental, y en el propio issue lo dicen sin rodeos: “the response from the community likely dictates whether this ships or not”. O sea, que salga adelante depende del feedback. Así que léete esto como “a dónde van los hooks”, no como “esto ya está en tu terminal para producción”.
En este post vas a encontrar:
- Qué problema real resuelven los function hooks frente a los hooks de shell
- El modelo mental que hay detrás: middleware componible al estilo Koa
- La firma
($, e, next)explicada pieza a pieza - Las cinco formas de colocar tu lógica en la cadena
- Un ejemplo de implementación completo, paso a paso, que puedes copiar
- Cómo se dibuja UI propia y cómo esto llega a enterprise
Vamos al lío.
¿Qué son los function hooks y en qué se diferencian de los hooks normales? ¶
Los function hooks son un quinto tipo de hook para Claude Code, escrito como una función de TypeScript (o JavaScript) que se ejecuta dentro del motor, no en un proceso de shell aparte.
Hoy, según la documentación pública, un hook vive en un hooks.json y puede ser de cuatro tipos: command, prompt, agent y http. La propuesta añade el function. La diferencia de fondo la resume muy bien el paper de arquitectura de Alice Poteat (Anthropic): un hook de shell es un contrato con un entorno; un function hook es un contrato con el motor.
¿Y eso qué significa en la práctica? Que el hook deja de ser una caja negra que recibe texto y devuelve un número, y pasa a tener acceso tipado a lo que está pasando dentro de Claude Code: la llamada a una herramienta, el prompt que acabas de enviar, lo que se va a dibujar en pantalla.
Si nunca has tocado los hooks clásicos, en el post sobre los comandos secretos de Claude Code tienes la sección de /hooks con el modelo de siempre (PreToolUse, PostToolUse, Stop y compañía). Este artículo asume que ya sabes para qué sirven y va un paso más allá.
Los hooks de shell tienen límites que cualquiera que los haya usado en serio reconoce. No pueden reescribir el prompt que has escrito. No pueden añadir contexto desde la base de conocimiento de tu empresa. No pueden dibujar un botón, una fila de estado o una barra. No pueden preguntarte nada. No pueden registrar herramientas nuevas ni editar la descripción de las que ya existen. Y no tienen memoria: un hook no se entera de lo que hizo el anterior, ni de lo que pasó en la sesión de ayer.
Los function hooks resuelven toda esa lista. De un plumazo.
🔑 El salto no es “hooks más potentes”. Es pasar de un script que vigila la puerta a una capa de middleware que envuelve el motor entero y puede leer, reescribir, bloquear, dibujar y recordar.
¿Por qué necesitas control determinista en Claude Code? ¶
Porque el prompt no basta. Y esto lo sabes si llevas un tiempo trabajando con agentes.
Pones en tu CLAUDE.md una regla clarísima: “nunca ejecutes comandos destructivos contra la base de datos”. Funciona… hasta que la ventana de contexto se llena. Según avanza la sesión, las instrucciones del principio pierden peso, el modelo se despista, tú sueltas un prompt vago un viernes por la tarde y de repente Claude interpreta mal y lanza algo que no debía.
El prompt es una recomendación. Un hook es una ley.
Esa es toda la motivación de los hooks, clásicos o de función: añadir control determinista. Lo que pasa por un hook pasa siempre, esté lleno o vacío el contexto, sea bueno o malo tu prompt. Si quieres exprimir la ventana de contexto por otro lado, tenemos una guía con 21 técnicas para ahorrar tokens en Claude Code que ataca el problema desde la gestión del contexto. Pero para las reglas que no se pueden saltar, la respuesta son los hooks.
Los function hooks suben el listón porque ese control determinista ahora puede ser inteligente: no solo “bloquea o deja pasar”, sino “reescribe esto”, “cachea aquello”, “pregúntame antes”, “dibuja el estado del deploy mientras tanto”.
Antes de domar los hooks, ten Claude Code a punto
Si acabas de sentarte a programar con IA, el curso-juego te lleva de la idea a tu primer proyecto en 17 paradas: elegir tu agente, montar tu CLAUDE.md y entender la ventana de contexto, con la voz de Dani Primo.
Entra en el curso gratis →¿Cómo funciona el modelo middleware de los function hooks? ¶
Un function hook es middleware al estilo Koa. Si has escrito middleware de Express alguna vez, ya lo entiendes: cada capa recibe el control, hace lo suyo, y decide si pasa el testigo a la siguiente con next() o si corta la cadena ahí mismo.
En Express escribes algo así para bloquear peticiones sin cabecera de autorización:
// Middleware clásico de Express
function auth(req, res, next) {
if (!req.headers.authorization) {
return res.status(401).send("No autorizado")
}
next() // pasa a la siguiente capa
}
En Claude Code, un function hook tiene exactamente la misma forma. Cada hook recibe tres argumentos, siempre los mismos: $, e y next.
on("tool.call", ($, e, next) => {
if (e.tool === "Bash" && e.command === "rm -rf /") {
return { deny: "Comando destructivo bloqueado por el hook" }
}
return next(e) // deja seguir al resto de la cadena
})
Este es el “hola mundo” de los function hooks, sacado literalmente del paper de arquitectura. En vez de mirar una petición HTTP, miras una llamada a una herramienta. En vez de res.status(401), devuelves { deny: "..." }. En vez de next(), llamas a next(e). Misma música, distinto escenario.
El detalle bonito: la cadena se pliega. Si registras tres hooks sobre el mismo evento (A, B y C), lo que ocurre por dentro es A(B(C(⊥))). El primero que registras queda “por encima” y envuelve a todos los demás. El último de la pila, el que hace el trabajo de verdad (la implementación por defecto del motor), es el que menos autoridad tiene. Registrarse antes es tener más poder, porque envuelves más.
¿Qué son $, e y next en un function hook? ¶
Son los tres argumentos que recibe cada hook, y entender qué hace cada uno es entender el sistema entero.
$ es la interfaz del motor. Es todo lo que un hook puede ver o hacer: leer un fichero, llamar al modelo, dibujar en pantalla, hablar por los altavoces. Es un objeto de nouns (sustantivos), y cada uno es un objeto de eventos: $.noun.event(input). Por ejemplo $.tool.call, $.ui.log, $.fs.read. Y aquí viene lo importante: $ es la única puerta. El entorno donde se ejecuta un plugin no tiene sistema de ficheros ni red por su cuenta, así que lo que hizo un plugin es exactamente las llamadas que hizo sobre $. Nada más. Eso hace que los efectos de un plugin sean auditables de forma estática: claude plugin validate sabe qué toca un plugin antes de ejecutar una sola línea.
e es el evento. Es el argumento de la llamada, un valor plano e inmutable. En un tool.call, e trae el nombre de la herramienta y sus argumentos (e.tool, e.command…). Como es inmutable, no lo modificas en el sitio: si quieres cambiar algo, le pasas a next una copia con el campo cambiado.
next es la continuación. Llamar a next(e) ejecuta el resto de la cadena y devuelve una promesa con el resultado final. Puedes llamarlo una vez, muchas o ninguna. Y trae extras muy útiles colgados:
| Propiedad | Qué te da |
|---|---|
next(e) |
Ejecuta el resto de la cadena con e y resuelve al resultado final |
next.signal |
Un AbortSignal por dispatch: te avisa cuando el evento termina o se cancela, para que pares trabajos que dejaste flotando |
next.is(type, e) |
Un type predicate para saber si este dispatch es tal evento; útil bajo el comodín * |
next.event |
El nombre del evento, para un hook que escucha todo y quiere registrar o hacer un switch |
next.origin |
El plugin que originó este dispatch, o engine. Oro puro para logs |
La regla mental que propone el paper: $ guarda lo que es del mundo; next guarda lo que es de este dispatch concreto.
💡 Si solo te llevas una idea de esta sección:
$es qué puedes hacer,ees qué está pasando, ynextes “sigue adelante”. Con eso ya lees cualquier function hook.
Entender cómo se compone el middleware de un agente es de las cosas que más rendimiento te dan ahora mismo. Cada domingo mando a +6.700 developers lo que voy aprendiendo sobre trabajar con IA en el desarrollo real. Gratis, desde 2018.
Quiero esa dinamita 🧨¿Cuáles son las cinco formas de colocar tu lógica en la cadena? ¶
Cada hook decide dónde se ejecuta su lógica respecto al punto donde ocurre la acción de verdad. El paper las llama las cinco colocaciones, y son el patrón que vas a repetir una y otra vez. Todas sobre tool.call:
| Colocación | Qué hace | La forma |
|---|---|---|
| before | Trabaja antes y deja seguir al resto | $.ui.log("va a ejecutar " + e.tool); return next(e) |
| after | Ejecuta la acción y luego accede a su resultado | const r = await next(e); $.ui.log(e.tool + " ejecutado"); return r |
| during | Arranca el resto y trabaja en paralelo | const p = next(e); $.ui.log("ejecutando " + e.tool); return p |
| instead | Anula todo lo de debajo | return { deny: "aquí no se ejecutan herramientas" } |
| modifying | Reenvía un evento modificado | return next({ ...e, timeout: 30 }) |
Fíjate en la fila modifying, porque es la que abre las puertas más interesantes. Como e es inmutable, reescribir un evento es tan simple como pasar a next una copia con un campo cambiado. Con eso puedes, por ejemplo, sustituir npm por pnpm en cualquier comando antes de que se ejecute, o subir un timeout, o redirigir un fetch a través de tu proxy.
Y instead es la que usas para bloquear, cachear o sustituir una herramienta entera. ¿Quieres que Claude Code use tu MCP de búsqueda favorito en lugar del WebSearch de serie? Interceptas el evento, y si tienes la API key configurada, devuelves tú los resultados sin llamar nunca a la implementación de debajo. Un cortocircuito limpio.
⚠️ Cuidado con el orden: un hook tiene que asumir que cualquier hook por encima de él puede cambiar su resultado o no llamarlo nunca. No des por hecho que tu
next(e)se ejecuta siempre.
Ejemplo de implementación: un plugin guardián paso a paso ¶
Vamos a montar algo real. Un plugin que hace tres cosas: bloquea comandos destructivos, cambia npm por pnpm sin que te enteres, y deja una nota en el transcript cada vez que se ejecuta una herramienta. Los tres patrones (instead, modifying y after) en un solo fichero.
Paso 1: la estructura del plugin. Un function hook vive dentro de un plugin normal de Claude Code. La novedad está en la carpeta hooks/:
guardian-plugin/
├── .claude-plugin/
│ └── plugin.json # manifiesto del plugin (nombre, versión...)
└── hooks/
├── hooks.json # aquí declaras el módulo
└── guardian.ts # aquí va tu lógica
Paso 2: declarar el módulo en hooks/hooks.json. Junto a las entradas de hooks de shell que ya tuvieras, añades una clave nueva, modules, apuntando a tu fichero:
{
"modules": ["./guardian.ts"]
}
Eso es todo lo que hace falta para que el motor sepa que hay un module de funciones que cargar. Los hooks de tipo command, prompt, agent o http que tuvieras en ese mismo hooks.json siguen funcionando al lado, sin tocarlos.
Paso 3: escribir el módulo. Un module de hooks exporta una única función, register, que recibe el registrador on. Dentro registras tantos hooks como quieras:
// hooks/guardian.ts
export function register(on) {
// 1. instead — bloquear comandos destructivos (con matcher por herramienta)
on("tool.call", { tool: "Bash" }, ($, e, next) => {
const peligroso = /rm\s+-rf|drop\s+database|mkfs|dd\s+if=/i
if (peligroso.test(e.command)) {
return { deny: `Comando bloqueado por guardian: ${e.command}` }
}
return next(e)
})
// 2. modifying — reescribir npm por pnpm antes de ejecutar
on("tool.call", { tool: "Bash" }, ($, e, next) => {
if (e.command.startsWith("npm ")) {
const reescrito = e.command.replace(/^npm /, "pnpm ")
$.ui.log(`guardian: reescribo "${e.command}" -> "${reescrito}"`)
return next({ ...e, command: reescrito })
}
return next(e)
})
// 3. after — dejar constancia de cada herramienta ejecutada
on("tool.call", async ($, e, next) => {
const resultado = await next(e)
$.ui.log(`guardian: ${e.tool} ejecutado`)
return resultado
})
}
Tres hooks, tres colocaciones distintas, un solo fichero. El primero usa un matcher ({ tool: "Bash" }) para que el motor solo lo invoque en llamadas a Bash y de paso estreche el tipo de e. El segundo reescribe el evento con la técnica modifying. El tercero envuelve la ejecución y anota el resultado después.
Paso 4: activar el flag y cargar el plugin. Los function hooks van detrás de una variable de entorno. Arrancas Claude Code así:
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude
Al activarlo aparece una skill integrada nueva, plugin-authoring, pensada justo para esto: “write or debug a Claude Code plugin made of function hooks”. Es decir, puedes pedirle a Claude Code que te escriba o depure el propio plugin. Muy meta.
Paso 5: probar y recargar. Cuando edites el module, no hace falta reiniciar la sesión: con /reload plugins se recarga en caliente. En las demos oficiales el módulo se recarga a medida que escribes. Escribes una función, y el motor hace el resto.
Ahora pídele a Claude Code que ejecute npm install y verás en el transcript la nota de que el guardián lo reescribió a pnpm. Pídele un rm -rf de algo y verás el bloqueo. Sin haber tocado tu CLAUDE.md.
🛡️ Un plugin de function hooks es un plugin normal: se comparte con el equipo por un repositorio de Git como cualquier otro. Lo que antes eran reglas frágiles repartidas por varios CLAUDE.md ahora es un paquete versionado que se instala y se audita.
¿Cómo se dibuja interfaz propia con los function hooks? ¶
Aquí está, para mucha gente, la parte más golosa. Dibujar en Claude Code es un evento más: ui.render. Y como cualquier evento, se engancha y se filtra igual.
Claude Code dibuja una fila por cada llamada a herramienta usando un componente llamado ToolUse. Puedes interceptar ese render y envolverlo con lo tuyo:
on("ui.render", { component: "ToolUse" }, async ($, e, next) => {
const { Row, Badge } = $.ui.resolve(e)
const rendered = await next(e)
return (
<Row>
{rendered}
<Badge text={`${e.props.output.length} caracteres`} />
</Row>
)
})
Lo que ves ahí es JSX de verdad. $.ui.resolve(e) te da los elementos nativos de la superficie donde estás dibujando, next(e) te devuelve el render que haría el motor por su cuenta, y tú lo envuelves añadiendo un badge con el número de caracteres de la salida.
La palabra clave es superficie. El evento trae e.surface, que puede ser "terminal", "desktop" o el de un artifact. El mismo hook sirve en todas: escribes un componente y se dibuja donde toque, sea la terminal con Ink, el escritorio con el DOM o Slack con Block Kit. One text, every surface, como se dijo en las demos.
¿Y los botones? Los elementos como Button o Input aceptan manejadores (onPress, onHover, onInput…). Cuando alguien pulsa, se levanta un evento ui.press que —lo has adivinado— cualquier otro plugin puede interceptar antes, después o en lugar del manejador original. Cross-plugin de serie.
Con esto se explican las demos que circulan por ahí: una fila junto al prompt que muestra el estado del deploy de Vercel (queued, building, ready) con el tiempo que lleva; un panel con datos en vivo; una confirmación antes de que Claude envíe una newsletter. Cosas que con un hook de shell eran, sencillamente, imposibles.
Domina la herramienta a fondo
Los hooks son una pieza: monta el Claude Code entero
Dos horas y media en directo con CLAUDE.md, hooks, agentes, MCP (Sequential Thinking, Context7, GitHub) y los workflows EPCC y TDD aplicados de verdad, con el contexto que la documentación oficial no termina de explicar.
Asomarme a la masterclass →Masterclass en directo · 2h30 · Acceso con Web Reactiva Premium
¿Cómo se construye la interfaz $ y qué es el “fold”? ¶
Esta es la parte más conceptual, pero merece la pena porque explica de dónde sale la memoria y por qué el sistema es tan extensible.
Cada método de $ es enganchable. Y los eventos del motor son, literalmente, el motor llamándose a sí mismo sobre $: la REPL llama a $.prompt.submit, el bucle de queries llama a $.tool.call, un sitio de render llama a $.ui.render. Eventos y $ son la misma cosa vista desde dos lados.
¿Y cómo nace $? Se construye una vez, al arrancar, ejecutando una cadena especial: engine.create. El plugin core, registrado el último, es el que añade las primitivas (ficheros, red, modelo, terminal, permisos). Y cada plugin puede añadir sus propios nouns encima. Por ejemplo, así es como un plugin añade un $.store:
on("engine.create", async ($, e, next) => {
const below = await next(e)
return { ...below, store: createStore(below) }
})
Ese $.store es la respuesta a la vieja limitación de “los hooks de shell no tienen memoria”. Ahora hay un almacén de variables que el siguiente hook, o el siguiente turno, pueden leer. Y hay una variante persistente que sobrevive a reinicios y sesiones.
El caso de uso que más impresiona: redactar secretos. Un plugin intercepta lo que entra en la sesión, detecta una API key, la guarda en el store y la sustituye en el transcript por un identificador. Claude nunca ve el secreto. Pero cuando llega el momento de hacer una petición que lo necesita, el hook reescribe el identificador por el valor real justo en esa llamada. El secreto nunca toca el historial. Es la misma idea que el Agent Proxy de Infisical, pero montada dentro de Claude Code con un puñado de líneas.
Para el tipado, todo encaja con declaration merging de TypeScript. Un plugin declara lo que añade y el tipo se propaga:
declare module "claude-code" {
interface EngineInterface { cache: Cache }
interface Cache { evict(input: { key: string }): Promise<void> }
}
Como un evento es un método de $, esa misma declaración tipa on("cache.evict", ...): e es su argumento y el resultado es su tipo de retorno. El autocompletado te sale gratis.
¿Sirve esto para equipos y entornos enterprise? ¶
Sí, y de una forma sorprendentemente elegante: el control de la organización es, otra vez, un plugin.
Como el orden de registro define la autoridad, una empresa que quiere mandar pone su plugin primero. Al estar arriba del todo, ve cada evento antes que nadie y cada resultado después que nadie, y nada de lo que hay debajo puede saltárselo. Con eso montas lo que quieras:
- Allowlist / denylist de plugins: un hook sobre
plugin.registerdecide qué plugins pueden siquiera cargarse. - Blast-door: un hook sobre
engine.createque deja pasar solo los nouns permitidos y bloquea el resto. - Auditoría de cumplimiento: para sectores como salud o pagos, un hook sobre el comodín
*que registra absolutamente todo:
on("*", ($, e, next) => {
$.ui.log(`${next.origin} llamó a ${next.event} en ${Date.now()}`)
return next(e)
})
Ese hook, al estar en un plugin antepuesto, ve cada llamada que hace cada plugin. Un rastro de auditoría completo en cuatro líneas. La seguridad no es una feature aparte que Anthropic tuvo que añadir: cae por su propio peso de la estructura del álgebra. El cumplimiento es orden de plugins.
Estas piezas (hooks, plugins, agentes, MCP) se mueven cada semana y cuesta seguirles el ritmo. En la newsletter seleccionamos 12 recursos cada domingo para que no tengas que rastrearlo tú. Ya somos +6.700.
Apúntate gratis →Hooks de shell vs. function hooks: ¿cuál uso? ¶
No es que unos sustituyan a otros. Los de shell siguen ahí y para muchas cosas son suficientes. Esta tabla te ayuda a decidir:
| Capacidad | Hooks de shell | Function hooks |
|---|---|---|
| Bloquear una acción | Sí (exit code 2) | Sí ({ deny }) |
| Reescribir el input | No | Sí (modifying) |
| Cachear / cortocircuitar | No | Sí (instead) |
| Dibujar UI (filas, botones, paneles) | No | Sí (ui.render) |
| Preguntar al usuario | No | Sí |
| Añadir o sustituir herramientas | No | Sí |
| Memoria entre hooks y sesiones | No | Sí ($.store) |
| Componer varios hooks entre sí | Limitado | Sí (Koa middleware) |
| Lenguaje | Shell / cualquier binario | TypeScript / JavaScript |
| Estado | Estable, documentado | Propuesta (early access) |
La lectura rápida: si lo que necesitas es un formateo tras escribir un fichero o un bloqueo simple, un hook de shell te vale y es estable hoy. Si necesitas reescribir, recordar, dibujar o preguntar, ahí es donde los function hooks no tienen rival. Y si te montas flujos con varios agentes, échale un ojo a cómo se coordinan en Agent Teams, que es otra pieza del mismo puzle de hacer a Claude Code más controlable.
¿Por qué toda la API parece sacada de la web? ¶
Porque lo está, y es una decisión de diseño consciente. En la sección de filosofía del paper hay una frase que resume el espíritu: “Rotlaust tre fell” (un árbol sin raíces cae). El sistema se apoya entero en vocabulario que ya conoces.
El next viene de Koa. El objeto de evento y el AbortSignal, del DOM. El declaration merging, de TypeScript. El JSX, de React y compañía. Los workers y el structured clone, de la plataforma web. Y hasta el $, que tiene una historia larga que se remonta a jQuery. La web es la lingua franca, y por eso, si eres desarrollador web, la curva de aprendizaje es mucho más corta de lo que parece a primera vista.
No estás aprendiendo un DSL raro. Estás escribiendo middleware con JSX. Eso ya lo sabes hacer.
TL;DR ¶
- 🧩 Los function hooks convierten los hooks de Claude Code en middleware de TypeScript componible al estilo Koa, con la firma
($, e, next). - 🔌 Se activan con
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claudey viven en un plugin, declarando el module enhooks/hooks.jsoncon la clavemodules. - 🎛️ Cinco colocaciones (before, after, during, instead, modifying) te dejan bloquear, reescribir, cachear y sustituir herramientas con
return next(e)oreturn { deny }. - 🎨 Dibujan UI propia con el evento
ui.rendery JSX que funciona en terminal, escritorio y Slack por igual, y tienen memoria real con$.store. - ⚠️ Es una propuesta en early access (issue #91870): no la lleves a producción todavía, pero entiende hacia dónde van los hooks.
Preguntas frecuentes sobre los function hooks de Claude Code ¶
¿Qué son los function hooks de Claude Code?
Son un quinto tipo de hook propuesto para Claude Code, escrito como una función de TypeScript o JavaScript que se ejecuta dentro del motor como middleware. A diferencia de los hooks de shell (command, prompt, agent, http), pueden reescribir eventos, dibujar interfaz, recordar estado entre sesiones y componerse entre sí al estilo Koa/Express.
¿Cómo se activan los function hooks?
Arrancando Claude Code con la variable de entorno CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1, es decir, CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude. Al hacerlo se habilita también una skill integrada, plugin-authoring, para escribir y depurar plugins de function hooks.
¿Los function hooks ya están disponibles en la versión estable?
No. Son una propuesta en fase de early access, documentada en el issue #91870 del repositorio de Claude Code. Anthropic ha dicho que la respuesta de la comunidad decidirá si la feature se lanza, así que conviene tratarla como experimental y no llevarla a entornos de producción todavía.
¿Qué significan $, e y next en un function hook?
$ es la interfaz del motor: todo lo que un hook puede ver o hacer ($.tool.call, $.ui.log, $.fs.read). e es el evento, un valor inmutable con los datos de la llamada. next es la continuación: next(e) ejecuta el resto de la cadena y devuelve el resultado.
¿Cómo bloqueo un comando peligroso con un function hook?
Registrando un hook sobre tool.call que devuelva { deny: "razón" } en vez de llamar a next(e). Por ejemplo, comprobar si e.command contiene rm -rf y devolver un deny con el motivo. Ese resultado le llega al modelo como el error de la herramienta.
¿Puedo reescribir un comando antes de que se ejecute?
Sí, con la colocación “modifying”. Como el evento e es inmutable, le pasas a next una copia con el campo cambiado: return next({ ...e, command: nuevoComando }). Es la forma de, por ejemplo, sustituir npm por pnpm o redirigir peticiones por un proxy.
¿Cómo dibujo interfaz propia en Claude Code?
Enganchando el evento ui.render y filtrando por componente, como on("ui.render", { component: "ToolUse" }, ...). Dentro usas $.ui.resolve(e) para obtener los elementos nativos y devuelves JSX. El mismo hook funciona en terminal, escritorio y Slack porque el render es nativo de cada superficie.
¿Los function hooks tienen memoria entre sesiones?
Sí. Un plugin puede añadir un $.store durante la construcción de la interfaz (engine.create). Hay una variante en memoria, que ven los hooks siguientes y el turno siguiente, y una variante persistente que sobrevive a reinicios y sesiones. Es la gran diferencia frente a los hooks de shell, que no tienen estado.
¿Puedo compartir un plugin de function hooks con mi equipo?
Sí, porque un function hook vive dentro de un plugin normal de Claude Code. Se versiona y se distribuye por un repositorio de Git como cualquier otro plugin, de modo que todo el equipo instala las mismas reglas deterministas.
¿Sirven los function hooks para control enterprise?
Sí. Una organización coloca su plugin el primero en el orden de registro, con lo que ve cada evento antes que nadie y nada por debajo puede saltárselo. Con eso se montan allowlists de plugins (hook sobre plugin.register), restricción de capacidades (hook sobre engine.create) y auditoría completa (hook sobre el comodín *).
Fuentes ¶
- Function Hooks — issue #91870 del repositorio de Claude Code
- Function Hooks: Core Architecture (Alice Poteat, Anthropic, agosto 2026), documento adjunto a la propuesta
- Documentación oficial de hooks de Claude Code
- Guía de hooks de Claude Code
¿Y tú, qué regla de tu CLAUDE.md convertirías en un function hook determinista si mañana esto llegara a la versión estable? Porque ese es el ejercicio que de verdad merece la pena hacer.
🧨 Última oprtunidad para recibir la dinamita que mereces sobre programación con IA el próximo domingo: Suscríbete gratis a Web Reactiva en https://webreactiva.com/newsletter
12 recursos para developers cada domingo en tu bandeja de entrada
Además de una skill práctica bien explicada, trucos para mejorar tu futuro profesional y una pizquita de humor útil para el resto de la semana. Gratis.