Mods e hooks no Claude Code: diferenças e quando usar

Mods e hooks no Claude Code: diferenças e quando usar

Entender a diferença entre mods e hooks no Claude Code é o primeiro passo para personalizar a ferramenta sem escolher o mecanismo errado. Os dois reagem a eventos da sessão, porém rodam em lugares diferentes e têm poderes bem distintos.

Desde a versão 2.1.287, lançada em outubro de 2026, os mods vêm ativados por padrão no terminal e no app desktop. Por isso, a pergunta mais comum agora é simples: quando continuar com o hook de settings e quando partir para um mod?

Neste guia, você vai ver como cada mecanismo funciona, um mesmo bloqueio escrito das duas formas e um critério objetivo para decidir. Ao final, a escolha entre mods e hooks no Claude Code deixa de ser intuição e passa a seguir uma regra clara.

O que são mods e hooks no Claude Code

Antes de comparar, vale alinhar o vocabulário, pois a própria documentação usa a palavra hook em dois sentidos. Na prática, os mods e hooks no Claude Code compartilham o conceito de evento, mas não a forma de execução.

A documentação oficial chama o primeiro tipo de settings hook. Já o segundo tipo é o hook de um mod, ou seja, uma função registrada dentro de um plugin.

Settings hooks: scripts fora do processo

Um settings hook é um comando de shell, uma requisição HTTP, uma chamada de ferramenta MCP, um prompt ou um agente que roda em um ponto do ciclo de vida da sessão. Você declara esse hook em um arquivo JSON de configuração, como o settings.json.

A cada evento, o Claude Code inicia o processo, envia um JSON pela entrada padrão e lê a resposta pela saída padrão ou pelo código de saída. Em seguida, o processo termina e nada fica em memória.

Os eventos mais usados são PreToolUse, PostToolUse, UserPromptSubmit, Stop e SessionStart. Além deles, existem eventos para compactação, subagentes, worktrees, notificações e troca de modelo.

O escopo depende do arquivo onde a configuração mora. O ~/.claude/settings.json vale para todos os projetos, enquanto o .claude/settings.json fica no repositório e pode ser compartilhado com o time.

Mods: funções dentro do processo

Um mod é um plugin cujo comportamento mora em um módulo JavaScript ou TypeScript. O Claude Code carrega esse módulo uma única vez, e ele permanece vivo durante toda a sessão.

Dessa forma, o mod consegue guardar estado, desenhar interface e chamar o próprio Claude Code de volta. Por exemplo, ele abre um painel, registra um comando de barra, executa um processo ou oferece uma ferramenta nova para o modelo.

Se você quer construir o primeiro do zero, o artigo como criar seu primeiro mod mostra o passo a passo completo.

Como os settings hooks funcionam

A configuração tem três níveis: o evento, um grupo com matcher e a lista de handlers. O matcher filtra pelo nome da ferramenta, e o campo opcional if usa a sintaxe das regras de permissão para refinar ainda mais.

Entre mods e hooks no Claude Code, este é o modelo mais antigo e também o mais fácil de adotar em times que já têm scripts prontos.

O handler devolve uma decisão de duas maneiras. Primeiro, pelo código de saída, em que o valor 2 bloqueia a ação. Depois, por um JSON com campos como permissionDecision, additionalContext ou updatedInput.

Exemplo de settings hook que bloqueia rm -rf

Este trecho do .claude/settings.json executa um script apenas quando o Bash recebe um comando que começa com rm:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

O script lê o JSON da entrada padrão, extrai o comando e nega a execução quando encontra rm -rf:

#!/bin/bash
# lê o comando enviado pelo Claude Code
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
else
  exit 0  # sem decisão, segue o fluxo normal de permissões
fi

Repare que o script não sabe nada sobre a sessão anterior nem sobre o próximo evento. Cada chamada nasce e morre isolada, o que torna o modelo simples e previsível.

Um detalhe importa bastante: o código de saída 1 não bloqueia nada. Portanto, se o hook existe para impor uma política, use sempre exit 2 ou o JSON com deny.

Outro ponto de atenção é o tempo de execução. O UserPromptSubmit roda antes de cada prompt e tem timeout padrão de 30 segundos, então um script lento trava a sessão inteira.

Ainda assim, quem compara mods e hooks no Claude Code costuma subestimar o quanto esse modelo resolve sozinho. Linters, formatadores, auditoria e bloqueios simples cabem perfeitamente em um script de poucas linhas.

Como um mod funciona por dentro

A pasta de um mod é um plugin comum, com o manifesto em .claude-plugin/plugin.json. Além disso, o arquivo hooks/hooks.json aponta para um único módulo na chave modules:

{
  "modules": ["./guard.mjs"]
}

Aqui começa a parte em que mods e hooks no Claude Code se distanciam de verdade.

O módulo exporta a função register(on, options). Dentro dela, cada chamada a on(event, matcher?, hook) adiciona um hook com a assinatura ($, e, next).

O parâmetro $ é a API de mods, e traz os dados do evento e next repassa o evento adiante. Em outras palavras, tudo o que o mod faz fora do próprio código passa obrigatoriamente pelo $.

A cadeia de middleware com next

Para quem vem do .NET, entender a cadeia é a chave para comparar mods e hooks no Claude Code.

Os hooks de mods formam uma cadeia, como um middleware de ASP.NET Core ou Express. O seu hook roda, chama next(e), os outros plugins recebem o evento e, no fim, o Claude Code executa o comportamento padrão.

Com isso, um hook de mod tem três movimentos possíveis:

  • Observar: chama await next(e), olha o resultado e devolve sem alterar nada.
  • Reescrever: chama next({ ...e, command: safer }) com uma versão modificada do evento.
  • Responder: devolve { deny: "..." } sem chamar next, assumindo o evento por conta própria.

O mesmo bloqueio de rm -rf fica assim como mod:

// hooks/guard.mjs
export function register(on) {
  on("tool.call", { tool: "Bash" }, async ($, e, next) => {
    const command = String(e.command ?? "");
    if (command.includes("rm -rf")) {
      // responde o evento sem repassar para o Claude Code
      return { deny: "Destructive command blocked by guard mod" };
    }
    return next(e);
  });
}

O resultado parece igual ao do settings hook, contudo a execução muda bastante. Aqui não existe processo novo, não existe JSON pela entrada padrão e o hook tem acesso a todo o contexto da sessão.

Estado que sobrevive ao hot reload

O mod Token Weather, apresentado no blog oficial, mostra bem essa diferença. Ele lê o uso da janela de contexto após cada turno e desenha uma previsão do tempo acima do prompt.

Variáveis de módulo parecem a escolha óbvia para guardar o histórico, porém cada salvamento recarrega o arquivo e zera tudo. Por isso, o exemplo usa $.state, que o host mantém durante a sessão inteira:

// o host guarda este valor, então ele sobrevive ao hot reload
const readings = { plugin: "token-weather", key: "readings" };

async function takeReading($) {
  const { context } = await $.session.usage();
  if (!context?.window) return;
  const { value: history = [] } = await $.state.get(readings);
  await $.state.set(readings, [...history, context.percent].slice(-12));
}

Além disso, um $.state.get feito durante um ui.render inscreve o desenho, e cada $.state.set posterior redesenha a interface sozinho. Um settings hook simplesmente não tem onde guardar esse histórico nem onde exibi-lo.

Para declarar esse valor, o plugin aponta no manifesto para um arquivo .d.ts com a interface PluginState. Se a declaração faltar, o validador acusa o erro e já indica a correção.

Diferenças entre mods e hooks no Claude Code

A diferença central entre mods e hooks no Claude Code está no lugar onde o código roda. O settings hook vive fora do processo e conversa por texto, enquanto o mod vive dentro do processo e conversa por chamadas de função.

Essa escolha de arquitetura explica quase todo o resto. Afinal, quem roda dentro do processo consegue desenhar, manter estado e reescrever eventos com riqueza de detalhes.

Tabela comparativa de mods e hooks no Claude Code

Critério Settings hook Mod
Onde roda Processo externo, a cada evento Dentro do Claude Code, carregado uma vez
Linguagem Qualquer script, HTTP, MCP ou prompt JavaScript ou TypeScript
Configuração Entrada no settings.json Plugin com hooks.json e módulo
Estado Nenhum entre chamadas $.state e variáveis do módulo
Interface Não desenha nada Painéis, faixa acima do prompt, spinner e botões
O que altera Permite, bloqueia, ajusta argumentos e adiciona contexto Chamadas de ferramenta, prompts, turnos, comandos e interface
Timeout padrão 600 segundos para comandos 10 segundos por despacho, fora das chamadas ao $
Sessões na nuvem Rodam Rodam, mas não desenham

Segundo a documentação, o hook de um mod tem 10 segundos de tempo próprio por despacho. No entanto, o tempo gasto esperando dentro de uma chamada ao $ não entra nessa conta.

Esse detalhe permite segurar uma chamada enquanto o usuário decide. O mod Blast Radius usa exatamente esse truque para abrir um painel com os botões Proceed e Cancel antes de um rm -rf.

Onde cada um roda

Os settings hooks disparam no terminal, nas extensões de IDE, no app desktop e nas sessões na nuvem. Já os hooks de um mod rodam em todos esses lugares, mas o desenho aparece apenas no terminal e na aba Code do app desktop.

Na extensão do VS Code, no claude -p e no Agent SDK, o mod executa a lógica normalmente sem exibir nada. Além disso, sessões WSL no app desktop não carregam plugins, então nenhum mod roda ali.

Quando usar mods e hooks no Claude Code

A regra prática para escolher entre mods e hooks no Claude Code é começar pelo mais simples que resolve. A própria documentação sugere comparar antes de escrever um mod, porque muitas vezes um settings hook já basta.

Escolha um settings hook quando

O settings hook é a melhor opção quando você precisa permitir, bloquear ou registrar um evento com um script que já existe. Rodar um linter após cada Edit, por exemplo, é um caso clássico.

Ele também faz sentido quando o time não usa JavaScript, pois qualquer linguagem serve. Da mesma forma, um endpoint HTTP interno de auditoria se encaixa perfeitamente no type: "http".

Por fim, prefira o settings hook quando a política precisa valer em qualquer superfície, inclusive em sessões na nuvem. Só lembre que a nuvem não lê o seu ~/.claude/settings.json local. Nesse caso, a simplicidade do modelo de texto vira vantagem.

Escolha um mod quando

O mod entra em cena quando você quer um painel, uma faixa acima do prompt ou um comando de barra que responde na hora, sem gastar um turno do Claude. O Replay Theater, por exemplo, registra o comando /replay para navegar pelas edições do último turno.

Ele também é a escolha certa quando dois eventos precisam compartilhar dados. Um hook pode contar chamadas de ferramenta enquanto outro mostra a contagem ao lado do spinner.

Além disso, só o mod consegue reescrever partes da interface que o próprio Claude Code desenha, como a linha de uma chamada de ferramenta, o spinner ou a caixa de perguntas.

Na dúvida entre mods e hooks no Claude Code, faça uma pergunta: o usuário precisa ver ou clicar em algo?

Se a resposta for sim, o mod é o caminho. Funções nativas como o /diff e o suporte ao AGENTS.md já são mods, e o código fica público no repositório oficial.

Segurança de mods e hooks no Claude Code

Em termos de risco, mods e hooks no Claude Code exigem cuidados diferentes. O módulo de um mod roda em um ambiente próprio, sem DOM e sem Node, mas isso não é um sandbox de segurança.

Através do $, o mod lê e grava arquivos, inicia processos, acessa a rede e vê cada prompt da sessão com as suas permissões. Ele pode até aprovar uma chamada que uma regra ask perguntaria ou que um PreToolUse seu bloqueou.

Por outro lado, um mod não consegue alterar o prompt de permissão. Além disso, o comando claude plugin validate lista os eventos e as chamadas do mod antes da instalação, o que facilita a revisão.

Nenhum dos dois substitui as regras de permissão para bloqueios rígidos. O próprio Blast Radius lê apenas o texto do comando, então aliases, $(...) e scripts que chamam rm passam despercebidos.

Para desligar tudo de uma vez, a configuração "disableAllHooks": true interrompe os mods instalados e os settings hooks. Já a flag --safe-mode desativa as personalizações apenas na sessão atual.

Em empresas, administradores controlam mods e hooks no Claude Code por meio de managed settings. O mod nativo sec-default protege o que a organização gerencia contra mods instalados pelo usuário.

Testar e validar mods e hooks no Claude Code

O ciclo de teste também muda entre mods e hooks no Claude Code. Para settings hooks, o menu /hooks mostra o que está configurado, e o log de depuração registra cada execução.

Para mods, o fluxo é mais completo. Primeiro, o claude plugin validate confere o manifesto, o contrato de tipos do $.state e as chamadas ao $.

Em seguida, o claude plugin test executa arquivos *.test.ts contra o runtime real. Nesse caso, os hooks registrados no teste rodam depois do mod e simulam as respostas do Claude Code, como no teste oficial do Token Weather:

import { describe, expect, test } from "claude-code/testing";

describe("token-weather", () => {
  test("the band follows the context window", async ($, on) => {
    // simula o que o Claude Code responderia para o uso da sessão
    on("session.usage", () => ({
      value: { startedAt: 0, rateLimits: [], context: { tokens: 36_100, window: 200_000, percent: 18 } },
    }));
    on("session.start", ($, e) => ({ cwd: e.cwd }));

    await $.session.start({ surface: "terminal", isInteractive: true, cwd: "/work" } as any);
    const ui = await $.ui.mount({
      plugin: "token-weather",
      surface: "terminal",
      component: "AbovePrompt",
      props: { hasSurvey: false, isWorking: false, maxRows: 10, bodyColumns: 120 },
    } as any);
    expect(await ui.find({ type: "Text", text: /Clear/ })).toBeDefined();
    await ui.unmount();
  });
});

Um settings hook não tem equivalente nativo para esse tipo de teste. Na prática, você testa o script isoladamente, enviando um JSON de exemplo pela entrada padrão.

Durante o desenvolvimento, claude --plugin-dir ./guard carrega o mod e recarrega o módulo a cada salvamento. Quando um desenho não aparece, claude --debug mostra se o hook devolveu uma árvore inválida.

As declarações de tipos que o Claude Code gera em .claude-plugin/types/ são a referência oficial para a sua versão. Portanto, confie nelas antes de qualquer exemplo copiado da internet.

Conclusão

Os dois mecanismos não competem, eles se completam. Os mods e hooks no Claude Code podem até morar no mesmo plugin, ao lado de skills e servidores MCP.

Use o settings hook para políticas simples, scripts existentes e integração com qualquer linguagem. Já o mod fica para interface, estado entre eventos, comandos instantâneos e reescrita de comportamento.

Em resumo, comece pelo hook de settings e migre para um mod quando precisar ver algo na tela ou lembrar algo entre eventos. Para se aprofundar, a visão geral oficial dos mods e a referência de hooks cobrem cada evento em detalhe.

Os exemplos completos de Token Weather, Blast Radius e Replay Theater estão no repositório claude-code-playground. Vale clonar e testar com --plugin-dir antes de escrever o seu.

FAQ

Mods substituem os settings hooks?

Não, os settings hooks continuam funcionando ao lado dos mods. A documentação recomenda o settings hook quando um script já resolve o problema.

Preciso saber TypeScript para criar um mod?

Não necessariamente, porque o Claude Code escreve o mod a partir de uma descrição. Mesmo assim, ler o código gerado ajuda a revisar o que ele faz.

Qual versão do Claude Code suporta mods?

No terminal, a versão 2.1.287 ou superior. No app desktop, os mods funcionam a partir da versão 2.1.286 embutida no app.

Mods funcionam em sessões na nuvem?

Os hooks do mod rodam quando o plugin chega à sessão na nuvem. Porém, nada do que o mod desenha aparece nesse ambiente.

Como desligo todos os mods de uma vez?

Inicie com --safe-mode para uma sessão ou defina "disableAllHooks": true nas configurações. Os mods nativos do Claude Code continuam ativos nos dois casos.

Glossário

Termo Significado
Mod Plugin com funções JavaScript ou TypeScript que rodam dentro do Claude Code
Settings hook Comando, requisição HTTP, ferramenta MCP, prompt ou agente executado em um evento do ciclo de vida
next(e) Função que repassa o evento para os próximos plugins e para o comportamento padrão
$ API de mods, única ponte para arquivos, processos, rede, modelos e interface
$.state Armazenamento do host que sobrevive ao hot reload durante a sessão
Hot reload Recarga do módulo a cada salvamento, sem reiniciar a sessão
PreToolUse Evento de settings hook disparado antes de uma chamada de ferramenta
tool.call Evento de mod disparado quando o Claude pede para executar uma ferramenta
AbovePrompt Faixa da interface logo acima do campo de prompt, onde um mod pode desenhar

Fontes