Pular para o conteúdo
MÓDULO 4.3

🔍 Depurar e as pegadinhas

Um mod que "não faz nada" quase sempre já recebeu o motivo: o engine pulou um hook, recusou uma árvore ou nem carregou o módulo, e escreveu isso em algum lugar. Este módulo ensina onde olhar e passa pelas pegadinhas que o inema-mods já pagou para aprender.

6
Tópicos
~35
Minutos
Avançado
Nível
Prático
Tipo
0 de 60%
Versão conferida: as mensagens citadas aqui estão na referência oficial do Claude Code 2.1.289; as pegadinhas, no docs/COMO-FAZER-UM-MOD.md do inema-mods (05/10/2026). O texto exato de um log pode mudar entre versões: guie-se pelo sentido.
1

Ligue o --debug

A referência oficial dá o conselho em uma linha: rode com claude --debug enquanto desenvolve. O log de depuração tem uma linha para cada vez que algo deu errado e para cada resultado que o engine recusou.

Sem o debug você ainda vê parte disso, mas só em certas sessões. A tabela mostra onde cada sinal aparece.

🆕 Novo aqui? Log de depuração e linha apagada

O log de depuração é o registro detalhado que o --debug liga. A linha apagada (dim) é um aviso discreto que aparece na própria conversa, com o nome do plugin, o evento e o motivo, uma vez só, enquanto a sessão recarrega a pasta do mod a quente.

Tipo de sessãoOnde aparece o aviso
Interativa com --plugin-dir (pasta vigiada)linha apagada na conversa, uma vez; e o log de depuração
Plugin instalado por marketplacesó no log de depuração, como <plugin>: <linha>
claude -p com saída em textomódulo que não carregou: uma vez no stderr, com o motivo
claude -p com saída jsonsó no log de depuração

O que olhar na tabela: o lugar mais completo é sempre o log. Desenvolva com --plugin-dir e --debug juntos e você vê os dois sinais.

⚠️ Instalado por marketplace não avisa na conversa

A linha apagada só aparece enquanto a sessão recarrega a pasta a quente. Um mod instalado pelo marketplace que falha fica mudo na tela: o único sinal está no log. Se um mod instalado "parou de funcionar", reabra com claude --debug antes de qualquer outra coisa.

🪵
--debug

cada ocorrência

📂
--plugin-dir

aviso na conversa

⚡
-p

stderr no carregamento

✅
validate

antes de carregar

2

Leia a linha de hook pulado

Um hook que lança erro, estoura o orçamento de tempo ou devolve uma forma errada é pulado. A cadeia segue sem ele: os hooks de baixo e o núcleo rodam no lugar, ou vale o último resultado do next que ele chegou a chamar.

Por isso o sintoma é silencioso: o Claude Code continua normal, só que sem o seu mod. A exceção é o .catch do registro, que responde no lugar do hook que falhou (módulo 2.2).

plugin Achama next(e) o seu hookthrow TypeError núcleoroda no lugar a cadeia desvia do hook pulado log: nome do erro + tamanho da mensagem (sem o texto)

Como ler o desenho: o X vermelho não derruba a sessão; a seta passa por cima. O que sobra é a linha pontilhada no log. Pela referência, ela traz o nome do erro e o tamanho da mensagem no lugar do texto; o texto do primeiro erro, cortado, fica na linha apagada da conversa.

✓ Causas comuns de hook pulado

  • ✓ Mock ou noun que não responde e lança
  • ✓ $.command.register recusado por nome repetido
  • ✓ Laço longo que passa do orçamento do dispatch
  • ✓ Resposta com forma errada para o evento

✗ Não conclua

  • ✗ "O evento não dispara" antes de ler o log
  • ✗ "Foi o outro mod" sem rodar só o seu
  • ✗ Que o erro some sozinho no próximo reload

💡 Um .catch por comando registrado

Visto ao vivo no inema-mods: se a pessoa já tem uma skill ou comando com o mesmo nome, o $.command.register é recusado e o erro derruba o hook session.start inteiro, levando junto tudo o que vinha depois. Registre cada comando com o seu próprio .catch(...) e escolha nomes pouco comuns.

3

Corrija um módulo que não carrega

Pior que um hook pulado é um módulo que nem carrega: nenhum hook entra, nenhum comando aparece. As causas conhecidas são poucas e quase sempre estruturais, não de lógica.

O claude plugin validate lê o manifesto e o código como o engine vai ler e relata o que o engine recusaria, antes de você abrir uma sessão. Rode-o primeiro, sempre.

CausaFonteCorreção
import() dinâmicoreference.mdimport estático de arquivo do mod
Extensão fora da listareference.md.ts .tsx .jsx .js .mjs .cjs .mts .cts
require, Node, DOMpegadinha 2tudo pelo $
$ passado a função internapegadinha 1função no topo do arquivo
Contrato só com export {}pegadinha 13exportar ao menos um tipo
✗ Não carrega (pegadinha 1)
export const register: Register = on => {
  // função que recebe $, DENTRO do register
  const avisar = async ($: EngineInterface, t: string) =>
    $.ui.toast(t)
  on('session.start', async ($, e, next) => {
    await avisar($, 'mod pronto')
    return next(e)
  })
}
✓ Carrega
// no TOPO do arquivo
async function avisar($: EngineInterface, t: string) {
  $.ui.toast(t)
}

export const register: Register = on => {
  on('session.start', async ($, e, next) => {
    await avisar($, 'mod pronto')
    return next(e)
  })
}

⚠️ Fora isso, $ é sempre $.<noun>.<método>

A pegadinha 1 completa diz: $ só pode ser passado para função declarada no topo do arquivo; fora isso, use sempre a forma $.<noun>.<método>(…). É por isso que todos os mods do inema-mods (e este curso desde a trilha 2) declaram medir, registrar, aplicarModo acima do register.

4

Corrija uma árvore recusada

Quando um ui.render devolve uma árvore que não valida (um elemento que a superfície não tem, uma prop que ele não aceita, um filho onde não cabe filho), o engine não desenha a árvore. Ele desenha a dele no lugar e registra o motivo.

No log de depuração a linha começa assim, seguida do motivo:

ui.render (<Component>): a hook returned a tree that does not validate

Numa sessão que recarrega a pasta do mod a quente, a conversa mostra também, uma vez por carga, componente e motivo:

<plugin>: ui.render (<Component>) refused: <reason>; the engine drew its own
ui.renderdevolve a árvore tabela da superfícievalida? sim: desenha a sua árvore não: o engine desenha a dele faixa: nothing was drawn painel: the pane was closed

Como ler o desenho: o caminho vermelho não quebra a sessão, mas o seu desenho some. Segundo a referência, quando o engine não tem o que desenhar, a faixa fica vazia e a linha termina em nothing was drawn; um painel é fechado e a linha termina em the pane was closed.

✓ Como corrigir

  • ✓ Leia o motivo e o tipo das props do elemento
  • ✓ Estreite por e.surface antes de usar Raster
  • ✓ No teste, ui.drawn() rejeita com a recusa

✗ Recusas clássicas

  • ✗ Raster ou Image fora do terminal
  • ✗ Svg no terminal
  • ✗ Input ou Select no mobile
5

Percorra as pegadinhas do kit INEMA

O docs/COMO-FAZER-UM-MOD.md do inema-mods lista 23 pegadinhas que custaram tempo na construção dos 18 mods. Abaixo, o sentido de cada uma, agrupado. O número é o do arquivo (lá a 17 aparece por último).

GrupoPegadinhas (resumo)
Carregar1 $ só em função do topo · 2 sem import(), require, Node, DOM · 13 contrato exporta ao menos um tipo · 21 item do PluginState é objeto literal ali mesmo
Testes3 nada responde por baixo: mock cada noun · 4 prompt.submit pede { text, origin, wait: false } · 5 FsStat exige isLink · 14 mock.store(on, {…}) · 15 turn.start precisa de mock; turn.step se lê com for await · 18 mocks antes do $ · 19 fs.list com mtimeMs; outros nouns de sessão precisam de mock · 23 datas com meio-dia UTC
Estado e tempo7 $.clock.now(), nunca Date.now() · 8 estado do desenho em $.state; render nunca escreve · 16 $.command.run num hook que o turno espera fica na fila: use $.clock.after(0, …) · 17 timers do every morrem no reload (recriação no session.start não confirmada)
Tela9 painel sem pedido só vira barra lateral com tela larga · 10 terminal sem Svg; grade é um Raster · 11 a faixa é uma só: some a sua linha ao next(e) · 12 key em Text some · 20 barra que funcione no desktop: █░ com cor
Ferramentas6 sucesso = deny === undefined && isError !== true · 22 recusa de permissão chega como isError

🆕 Novo aqui? "Visto ao vivo"

  • • $.command.register recusado por nome repetido derruba o session.start (tópico 2).
  • • Comandos de mod respondem sem chamar o modelo: claude -p vira teste de fumaça barato (tópico 6).
  • • O que um comando grava em $.store persiste de verdade: depois de testar ao vivo, volte ao padrão.
  • • $.ui.ask pode se resolver sozinho por ausência: opção segura primeiro (módulo 4.1).

💡 Leia o original

Esta tabela resume. O texto completo, com os exemplos e as regras do kit (mod nunca chama modelo nem rede, mensagens em português, tudo configurável em userConfig), está em github.com/inematds/inema-mods, arquivo docs/COMO-FAZER-UM-MOD.md.

6

Faça um teste de fumaça com claude -p

Os testes provam a lógica. O teste de fumaça prova que o mod carrega de verdade numa sessão real. Um comando de mod responde sem chamar o modelo, então custa zero de cota.

O inema-mods junta os três controles anteriores num script só, scripts/checar-mod.sh: validate, tsc contra os tipos do engine e test. O Rafa roda os dois antes de dizer "pronto".

1

validate

Manifesto e código lidos como o engine lê.

2

tsc

Nome de evento, campo e método conferidos contra os tipos da sua versão.

3

test

O comportamento, com o mundo em memória (módulo 4.2).

4

fumaça com claude -p

Uma sessão de verdade carrega a pasta e responde o comando.

🎯 Objetivo: provar que o recibo-sessao carrega e responde

No terminal, dentro da pasta inema-mods. Troque <caminho do tsc> pelo seu (ex.: o de um node_modules/.bin):

TSC=<caminho do tsc> scripts/checar-mod.sh mods/recibo-sessao
claude -p "/recibo" --plugin-dir mods/recibo-sessao

Saída real (05/10/2026, Claude Code 2.1.289):

== recibo-sessao
  validate: ok
  tsc: ok
  test: ok (6 linhas de aprovação)

recibo-sessao: Nesta sessão: nenhum arquivo criado ou alterado.
Como verificar: as três linhas do checar-mod.sh dizem ok, e o claude -p devolve a linha do recibo prefixada com o nome do plugin. Se em vez disso aparecer no stderr um aviso de que o módulo não carregou, volte ao tópico 3.

✓ Fumaça boa

  • ✓ Um comando que só lê estado e responde texto
  • ✓ Resultado conhecido de antemão (sessão nova, nada feito)
  • ✓ Roda em segundos e não grava nada

✗ Fumaça ruim

  • ✗ Um prompt que faz o modelo trabalhar (gasta cota)
  • ✗ Comando que grava em $.store sem voltar ao padrão
  • ✗ Comando que depende de $.ui.ask (no -p ele rejeita)

Teste rápido (opcional): o mod do Rafa abre com --plugin-dir, mas nenhum hook roda e o comando não aparece. A função contar($, e) está declarada dentro do register. Qual a causa provável?

🎓 Resumo do módulo

✓
--debug com --plugin-dir — log completo e aviso na conversa.
✓
Hook que falha é pulado — o log guarda nome do erro e tamanho.
✓
Módulo que não carrega é estrutural — import(), extensão, $ em função interna.
✓
Árvore recusada: o engine desenha a dele — leia o motivo na linha do log.
✓
checar-mod.sh e claude -p — antes de dizer "pronto".

Próximo módulo:

4.4 — Distribuir e projeto final