Criar um mod
Peça ao Claude para escrever um mod do Claude Code a partir de uma descrição, ou escreva um você mesmo que conte chamadas de ferramentas e adicione um comando. Aprenda o loop de recarga e validação.
Um mod é um plugin do Claude Code com um arquivo de entrada, chamado de hooks module: um arquivo JavaScript ou TypeScript cujas funções o Claude Code chama quando eventos acontecem. Existem duas maneiras de fazer um:
- Peça ao Claude para escrever: descreva o que você quer em uma sessão do Claude Code
- Escreva você mesmo: siga o tutorial para aprender como o código de um mod funciona. Você não precisa de Node.js, um bundler ou uma etapa de compilação, porque o Claude Code carrega arquivos
.jse.tsdiretamente.
Se você ainda não decidiu se um mod é a ferramenta certa, leia primeiro a comparação na visão geral.
Mods requerem Claude Code v2.1.287 ou posterior. Em seu shell, execute claude --version para verificar. Para ver se mods podem ser carregados para você, consulte Verificar se mods podem ser carregados.
Peça ao Claude para um mod
Descreva o mod que você quer em uma sessão interativa do Claude Code, e Claude o escreve. Claude trabalha a partir de uma skill integrada chamada plugin-authoring, que diz a ele onde escrever o mod, quais eventos e métodos sua versão tem, e como o mod é carregado. Claude pode carregar a skill quando você pede um mod, ou você pode carregá-la você mesmo executando /plugin-authoring no prompt do Claude Code.
O mod é executado assim que você o aprova, exceto em sessões onde um mod que Claude escreve não pode ser carregado.
Descreva o mod
Peça pelo mod com suas próprias palavras, por exemplo make a mod that shows the current git branch above the prompt. Claude escreve o mod em um diretório próprio na pasta de mods da sessão, que é ~/.claude/dev-mods/ seguido pelo ID da sessão. O caminho completo de um mod se parece com ~/.claude/dev-mods/3f2a9c1e-5b7d-4e8a-9c21-6d0f4b8a7e13/git-branch/.
Nos modos de permissão default e acceptEdits, o Claude Code pergunta antes que Claude crie cada um dos arquivos do mod, porque ~/.claude é um caminho protegido. Aprove cada arquivo conforme ele aparecer.
Aprove o mod
Quando Claude salva o primeiro arquivo, o Claude Code pergunta se deve ativar o hot reloading para a sessão. O hot reloading executa os mods que Claude escreve nesta sessão e pega cada mudança posterior.
Escolha uma destas respostas:
- Enable for this session: os mods na pasta de mods da sessão são carregados quando a rodada termina, e recarregam no final de cada rodada que os altera. Sua resposta dura para a sessão, inclusive depois que você a retoma.
- Not now: nada é carregado por enquanto. Os arquivos permanecem onde Claude os escreveu, e os mods são carregados na próxima vez que essa sessão inicia. Para evitar que um mod seja carregado, delete seu diretório.
Verifique se o mod foi carregado
Execute /plugin no prompt do Claude Code e pressione Tab até que a aba Installed seja selecionada. Ela lista o mod, e você pode desativá-lo lá.
Experimente o mod
Use o que você pediu. Para o prompt de exemplo, o nome do branch atual aparece acima da caixa de prompt. Se o mod não fizer o que você queria, diga ao Claude o que mudar. O mod recarrega no final de cada rodada que altera seus arquivos, então você pode tentar a mudança assim que Claude terminar.
Use o mod em outras sessões
Um mod que Claude escreveu é carregado apenas na sessão que o criou, e o Claude Code deleta a pasta de mods dessa sessão uma vez que é mais antiga que cleanupPeriodDays. Para manter o mod, copie seu diretório para fora da pasta de mods para um lugar seu, como ~/mods/git-branch. Depois escolha como carregá-lo:
- Em uma sessão que você inicia: em seu shell, execute
claude --plugin-dir ~/mods/git-branch - Para outras pessoas: adicione-o a um marketplace para que possam instalá-lo
Sessões onde um mod que Claude escreve não pode ser carregado
Um mod que Claude escreve é carregado apenas depois que você o aprova, em um workspace confiável onde mods podem ser executados. Nestas sessões ele não é carregado:
- Ninguém está lá para aprovar: a sessão não pode mostrar um prompt, como em uma execução
claude -pou mododontAsk - O workspace não é confiável: você não aceitou o prompt de confiança para o diretório
- Mods estão parados: você iniciou com
--safe-modeou--bare, você definiudisableAllHooks, ou as configurações gerenciadas da sua organização bloqueiam
Escreva um mod você mesmo
Neste tutorial você constrói um mod chamado first-mod que conta as chamadas de ferramentas que Claude faz, mostra a contagem ao lado do spinner enquanto Claude trabalha, e adiciona um comando /tally que a imprime. Você então lê as declarações de tipo que o Claude Code escreve ao lado do seu mod e executa claude plugin validate. Juntas elas mostram os eventos e métodos que sua versão oferece e o que o Claude Code lê do seu código.
Esta gravação mostra o mod finalizado. O spinner conta chamadas de ferramentas, /tally imprime a contagem, e uma edição no código entra em efeito enquanto a sessão é executada:
Você escreve três arquivos:
first-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js
plugin.json: o manifest do pluginhooks.json: aponta para seu arquivo de códigoregister.js: seu código, chamado de hooks module
Crie o diretório do plugin
Crie os dois diretórios que contêm os arquivos:
mkdir -p first-mod/.claude-plugin first-mod/hooks
New-Item -ItemType Directory -Force first-mod\.claude-plugin, first-mod\hooks
Escreva o manifest
Um mod é um plugin, e um mod precisa de um manifest. O manifest deste mod não tem campos especiais. Salve isto como first-mod/.claude-plugin/plugin.json:
{
"name": "first-mod",
"version": "0.1.0",
"description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
"author": { "name": "Your Name" }
}
Diga ao Claude Code onde seu código está
Quando o Claude Code carrega um plugin, ele lê o hooks/hooks.json do plugin. A chave modules naquele arquivo dá o caminho para seu código, e tê-la é o que torna o plugin um mod. Liste um caminho, relativo a hooks.json. Aqui ele aponta para register.js, que você escreve no próximo passo.
Salve isto como first-mod/hooks/hooks.json:
{
"description": "The first-mod hooks module",
"modules": ["./register.js"]
}
Escreva o código
Este arquivo é o código do mod, chamado de hooks module. Quando o mod é carregado, o Claude Code chama a função register que o arquivo exporta e passa a ela uma função chamada on. Cada chamada a on registra um manipulador de evento, chamado de hook, para o evento que ele nomeia.
Salve isto como first-mod/hooks/register.js:
// The count, shared by the hooks below
let calls = 0
// Claude Code calls this once when the mod loads
export function register(on) {
// Runs when the session starts, before your first prompt
on('session.start', async ($, e, next) => {
// Add the /tally command
await $.command.register({
name: 'tally',
description: 'Show how many tool calls Claude has made',
})
// Let the session start as usual
return next(e)
})
// Runs each time Claude is about to use a tool
on('tool.call', async ($, e, next) => {
calls += 1
// Ask Claude Code to draw the interface again, so the new count shows
$.ui.invalidate('ui.render')
// Let the tool run as usual
return next(e)
})
// Runs when you type /tally, and only then, because of the matcher
on('command.run', { command: 'tally' }, async () => {
// The text to print in the transcript
return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
})
// Runs each time Claude Code draws the spinner
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
// Keep Claude Code's spinner, with the count added after its word
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}
O arquivo mantém uma contagem em calls e registra quatro hooks:
session.starté executado quando a sessão inicia, antes do seu primeiro prompt, e novamente cada vez que o mod recarrega. Ele adiciona o comando/tallyao Claude Code.tool.callé executado cada vez que Claude está prestes a usar uma ferramenta. Ele adiciona um acallse pede ao Claude Code para desenhar a interface novamente.command.runé executado quando você digita/tally. Ele retorna o texto a ser impresso.ui.renderé executado cada vez que o Claude Code desenha o spinner. Ele adiciona a contagem após a palavra do spinner.
Como o mod de exemplo funciona explica os três argumentos que cada hook recebe e o que cada um retorna.
Carregue o mod
Inicie o Claude Code com a flag --plugin-dir, que carrega um diretório de plugin para uma sessão sem instalá-lo:
claude --plugin-dir ./first-mod
Experimente o mod
Peça ao Claude para fazer algo que leve algumas chamadas de ferramentas, como list the files here and read the README. Enquanto Claude trabalha, a palavra do spinner é seguida por uma contagem que sobe, como em Thinking · tool calls: 2…. Quando Claude termina, digite /tally e pressione Enter. A transcrição mostra first-mod: Claude has made 2 tool calls since this mod loaded, com sua própria contagem. O Claude Code coloca o nome do plugin na frente do texto do comando.
Para verificar o comando sem uma sessão interativa, execute-o em modo não interativo:
claude -p "/tally" --plugin-dir ./first-mod
first-mod: Claude has made 0 tool calls since this mod loaded
Se /tally não estiver na lista de comandos, o módulo não foi carregado. Consulte Descubra por que um mod não faz nada.
Altere o código enquanto a sessão é executada
Deixe a sessão aberta. Em register.js, altere ' · tool calls: ' para ' · tools used: ' no hook ui.render e salve. A linha destacada é a que muda:
// Runs each time Claude Code draws the spinner
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
// Keep Claude Code's spinner, with the count added after its word
return next({ ...e, props: { ...e.props, suffix: ' · tools used: ' + calls + '…' } })
})
Uma linha na transcrição diz que first-mod recarregou e lista seus hooks, e o próximo spinner usa o novo texto, como em Thinking · tools used: 1….
Como o mod de exemplo funciona
Cada função que você passa a on é um hook, que é um manipulador de evento. O Claude Code passa a cada hook os mesmos três argumentos:
- A API de mods, chamada
$: cada método que um mod pode chamar para alcançar fora de si mesmo, em namespaces como$.uie$.command - O evento, chamado
e: a entrada do evento como dados simples, como o nome e argumentos de uma chamada de ferramenta - O próximo manipulador, chamado
next: uma função que passa o evento para os outros mods e depois para o comportamento próprio do Claude Code, e retorna o resultado
Os hooks em first-mod lidam com seus eventos das três maneiras que um hook pode:
- Observar: o hook
session.startregistra o comando, e o hooktool.callconta a chamada e pede um redesenho. Ambos retornamnext(e), então a sessão inicia e a ferramenta é executada como usual. - Responder: o hook
command.runretorna seu próprio resultado e nunca chamanext. O segundo argumento aon,{ command: 'tally' }, é um filtro, chamado de matcher, então o hook é executado apenas para/tally. - Reescrever: o hook
ui.renderchamanextcom uma cópia deecujosuffixcontém a contagem, então o Claude Code desenha seu spinner usual com seu texto após a palavra
O Claude Code observa um diretório carregado com --plugin-dir e hot-recarrega o hooks module quando um arquivo nele muda. Cada recarga executa register novamente, então calls volta a 0 e /tally começa a contar novamente. Para manter um valor entre recargas, consulte Manter estado.
Continue trabalhando em um mod
Uma vez que um mod é carregado, você pode fazer Claude alterá-lo, verificar seu código contra as definições de tipo para sua versão, listar os eventos e chamadas que o Claude Code encontra nele, e testá-lo.
Altere um mod com Claude
Para alterar um mod que você já tem, inicie a sessão com --plugin-dir apontado para o diretório do mod, para que o que Claude escreve seja carregado na mesma sessão:
claude --plugin-dir ./first-mod
Depois peça pela mudança, por exemplo add a /tally-reset command to this mod that sets the tally back to zero. Claude edita o hooks module, executa claude plugin validate, e corrige o que ele relata. Um diretório que você carrega com --plugin-dir é um caminho protegido, então nos modos default e acceptEdits você é solicitado a aprovar cada edição de Claude ao mod. A tabela de caminhos protegidos dá o resultado para os outros modos de permissão.
Os arquivos que Claude salva durante sua rodada recarregam quando a rodada termina, então você pode tentar /tally-reset assim que Claude terminar.
Obtenha definições de tipo para sua versão
Cada vez que o Claude Code carrega ou recarrega um mod de um diretório que você passa a --plugin-dir, ou um mod que Claude escreveu para você, ele escreve arquivos de declaração TypeScript, terminando em .d.ts, em .claude-plugin/types/ dentro do diretório do mod. Eles descrevem os eventos exatos, métodos da API de mods e elementos na versão do Claude Code que você está executando, então seu editor pode autocompletar e verificar tipos em seus hooks. Para navegar pelas declarações online, leia mods/types/claude-code.d.ts no repositório do Claude Code, cuja primeira linha nomeia a versão que a escreveu. O diretório contém estes arquivos:
| Caminho | O que declara |
|---|---|
claude-code/index.d.ts |
Cada evento e sua entrada e resultado, cada namespace e método da API de mods, e os elementos que cada superfície pode desenhar |
claude-code-tools/index.d.ts |
As entradas e resultados das ferramentas integradas, para que verificar e.tool === 'Bash' restrinja e |
claude-code-mcp/index.d.ts |
As entradas das ferramentas MCP que foram conectadas a última vez que você salvou um arquivo no mod |
index.d.ts em um diretório nomeado para um plugin |
O que esse plugin adiciona à API de mods. Há um diretório para cada plugin que seu plugin.json lista em dependencies. |
tsconfig.json |
Opções de compilador que se adequam a um hooks module |
Se seu mod não tem seu próprio tsconfig.json, o Claude Code adiciona um na raiz do mod que estende o gerado, então seu editor e tsc -p ./first-mod verificam tipos do mod sem mais configuração.
Os eventos e métodos podem mudar entre releases, então confie nesses arquivos sobre qualquer página, inclusive esta, quando discordarem.
claude-code/index.d.ts é a referência mais completa para sua compilação, com um comentário e um exemplo para cada método da API de mods. Para procurar algo, pesquise o arquivo por seu nome, como 'tool.call'.
Verifique o que o Claude Code lê do seu mod
Para ver seu mod da maneira que o Claude Code o vê, sem executar seu código ou iniciar uma sessão, use claude plugin validate. Ele verifica o manifest e executa a mesma análise estática no código-fonte do hooks module que o Claude Code executa quando carrega um mod. Em seu shell, execute-o no diretório do mod:
claude plugin validate ./first-mod
Para first-mod, a saída inclui estas linhas.
❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passed
A linha hooks: lista os eventos que seu módulo conecta, cada um com seu filtro entre chaves. A linha calls: lista cada método da API de mods que ele chama. Um módulo que lê ou define variáveis de ambiente também obtém linhas env reads: e env writes:, e um que usa $.state obtém state reads: e state writes:.
Se um evento que você pretendia conectar está faltando na primeira linha, o Claude Code não chamará esse hook também. A causa usual é um nome de evento digitado incorretamente, que o comando relata como um erro como "tool.calls" is not an event.
Siga estas regras para que a análise estática possa encontrar cada hook e chamada:
- Soletra cada chamada da API de mods completamente:
$, o namespace, depois o método, como em$.store.get('notes'). Você pode passar$para uma função declarada no nível superior do mesmo arquivo, e para uma função sua chamadaloadNotes, a linhacalls:então lê$.store.get (via loadNotes). Passar$para um método, uma função definida dentro do hook, ou uma função que você importa de outro de seus arquivos falha na validação. As funçõesreadeupdateque$.stateusa são as importações que podem levá-lo. Não atribua$ou um de seus namespaces a uma variável, desestruture-o, ou indexe-o com um nome computado.const ui = $.uifalha com$.ui is used as a value. - Escreva o nome do evento em cada chamada
oncomo um literal de string, como'tool.call'. Uma variável, ou um loop sobre uma lista de nomes, falha comthe event name passed to on() is not a string literal. - Dentro de
register, não declare uma segunda variável ou parâmetro chamadoon. A validação falha com"on" is declared again (shadowed). - Importe apenas de arquivos dentro do diretório do plugin, por caminho relativo. A única importação nua permitida é
claude-code, para tipos e alguns auxiliares. - Use declarações
importno topo do arquivo, como emimport { name } from './file.js'. Umimport()dinâmico falha coma dynamic import(); a hooks module imports its own files with an import declaration. - Escreva cada arquivo como um módulo ES, com
importe nãorequire. A referência lista as extensões de arquivo que o Claude Code carrega.
Teste o mod
Você pode escrever testes automatizados para um mod e executá-los de seu shell com claude plugin test, sem sessão, sign-in ou rede. Um teste levanta os eventos que seus hooks lidam e verifica o que os hooks fizeram.
Este teste levanta duas chamadas de ferramentas, executa /tally, e verifica que a resposta conta ambas. Salve-o como first-mod/tests/first-mod.test.ts:
import { expect, test } from 'claude-code/testing'
test('/tally reports the tool calls the mod has seen', async ($, on) => {
// Answer each tool call in Claude Code's place, so no tool runs
on('tool.call', () => ({ result: 'ok' }))
// Raise two tool calls, which the mod's tool.call hook counts
await $.tool.call({ tool: 'Bash', command: 'ls' })
await $.tool.call({ tool: 'Read', file_path: 'README.md' })
// Run /tally and check the text its hook returns
const answer = await $.command.run({ command: 'tally', args: '' })
expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})
Em seu shell, execute os testes do diretório first-mod:
claude plugin test
A saída nomeia cada teste e se passou, com tempos que variam de execução para execução:
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [22.87ms]
1 pass
0 fail
Ran 1 test across 1 file. [0.19s]
Teste um mod cobre stubbing de uma chamada de modelo ou da loja, e testes de temporizadores e desenhos.
Compartilhe seu mod
Um mod é um plugin, então você o versiona no manifest e as pessoas o instalam e atualizam com os comandos /plugin. Para dá-lo a outras pessoas, adicione-o a um marketplace.
Antes de fazer isso, verifique o name do plugin: claude plugin validate falha em um nome que parece um dos próprios da Anthropic, como um que começa com claude-. Os eventos e métodos podem mudar entre releases, então seu README é o lugar para dizer qual versão do Claude Code você testou.
Continue desenvolvendo contra o diretório com --plugin-dir, não contra uma cópia instalada. O Claude Code armazena em cache um plugin instalado por versão, então suas edições não chegam à cópia instalada até que você aumente a versão e instale novamente.
Próximos passos
- Desenhe na interface: abra um painel, desenhe acima do prompt, e adicione botões e campos de texto
- Reaja a eventos: conecte chamadas de ferramentas, prompts e rodadas
- Use a API de mods: adicione comandos e ferramentas, chame um modelo, e execute trabalho em um temporizador
- Teste um mod: stub do que o Claude Code responderia, e teste temporizadores e desenhos
- Solucione problemas de um mod: as razões pelas quais um mod não faz nada, e o log de depuração
- Leia a fonte de mods integrados: plugins completos, cada um com seu hooks module e testes