Depois de cada resposta, o Haiku propõe três próximos pedidos em botões acima da caixa de mensagem (teclas 1-3)

Mods do Claude Code (function hooks) que uso todo dia. · Claude Code mods (function hooks) I use every day.
Seis mods, cada um resolvendo um problema que apareceu no uso real. A interface e as mensagens dos mods estão em português.
Precisa do Claude Code 2.1.287 ou mais novo.
/plugin marketplace add diegocamara89/claude-mods
/plugin install sonnet-por-padrao@claude-mods-diego
Troque sonnet-por-padrao pelo nome de qualquer mod da tabela. Para testar sem instalar: claude --plugin-dir ./mods/<nome>.
| Mod | O que faz | O que acessa | |
|---|---|---|---|
| sonnet-por-padrao | Subagente e agente de script (Workflow) criados sem modelo escrito rodam no Sonnet, em vez de herdar o Opus da conversa. Opus continua valendo quando escrito de propósito. | Reescreve o modelo do subagente e o script do Workflow; grava um registro das decisões (com a descrição de cada subagente) em LOCALAPPDATA no Windows ou TMPDIR//tmp fora dele. | |
| revisor-com-prova | Quando volta o resultado de um revisor (Codex, codex exec, agy, council), anexa ao resultado um lembrete: achado só vale com prova, reproduza antes de corrigir, "aprovado sem achados" é válido. | Só lê o comando; anexa texto ao resultado, que só o modelo vê. Os padrões agy.py e council.py são de skills do autor; sem elas, simplesmente não disparam. | |
| proximos-passos | Depois de cada resposta, o Haiku propõe três próximos pedidos em botões acima da caixa (teclas 1-3). /custos mostra os tokens medidos. | Chama o Haiku (cerca de 1 mil tokens por sugestão, medido); desenha a faixa. | |
| painel-vivo | Painel ao vivo, para ler de relance: uma manchete com Pendências, Atividade e Progresso; fila do que pede você (permissão, pergunta, travado) e avisos; trabalhadores em árvore (subagentes, agentes de Workflow, comandos, agy e Codex aninhados em quem os chamou), cada um com tempo, modelo, tokens e o que faz agora; outras sessões em andamento numa linha; cotas de 5 h e 7 dias. "Explicar" traduz um trabalho para leigo e alimenta um glossário (/glossario). | Envolve cada comando Bash e PowerShell para copiar a saída num arquivo local (tee, em LOCALAPPDATA ou TMPDIR) e marcar onde o agy grava os passos (AGY_EVENTS_DIR); apaga esses arquivos depois de 1 dia; se o rtk estiver instalado, passa o comando por ele antes; guarda um resumo de cada sessão no armazenamento do plugin para as outras sessões verem; lê o título da conversa no transcript com um script Python curto; chama o Haiku só no "Explicar". | |
| lixeira | Nega o apagar de vez e devolve ao Claude o comando que manda para a lixeira do sistema; barra comandos sem volta (git reset --hard, push --force, formatar, esvaziar lixeira, desligar ou reiniciar o computador, Format-Volume, Clear-Disk) até o usuário dizer sim (LIXEIRA_OK=1 no Bash, $env:LIXEIRA_OK=1; no PowerShell). Também pega o apagar pela esteira do PowerShell (`gci pasta \ | Remove-Item`). Temporários, caches e arquivos criados na conversa ficam livres. | Lê o comando e nega quando precisa. No Windows usa o lixeira.ps1 que vem junto; no macOS e no Linux sugere trash ou gio trash. |
| varredura-push | Antes de git push e de publicação pelo gh (no Bash e no PowerShell), confere os commits que vão subir procurando chave, token e senha. Barra o envio se achar. Liberação pontual com VARREDURA_OK=1. | Põe python varredura.py && antes do push. Usa o gitleaks se estiver no PATH; sem ele, regras de reserva. |
Revisor por LLM tende a achar defeito em trabalho correto quando o pedido manda "achar o que quebra": num estudo de 2026, o GPT-4o reprovou 73% de código correto com esse enquadramento, contra 26% com um pedido simples (arXiv 2603.00539). Do outro lado, quem recebe a crítica aceita sem conferir: diante de 48 relatórios falsos, o Claude Code rejeitou 1 (arXiv 2604.11950). Exigir prova reverteu isso para 85–96% de rejeição. O mod coloca essa regra exatamente onde ela faz falta: no resultado do revisor, e não em toda mensagem.
painel-vivo: a saída ao vivo usa bash (no Windows, o Git Bash que o Claude Code já usa); o nome das outras sessões precisa de python no PATH; feito para o app desktop (no terminal, desenha uma versão só texto). O Codex não informa tokens: o painel mostra só o modelo, quando o comando o escolhe.varredura-push: precisa de Python (python no Windows, python3 no macOS e no Linux).sonnet-por-padrao: não vê script de Workflow rodado a partir de arquivo (scriptPath) nem Workflow salvo por nome.revisor-com-prova: também dispara quando o agy é usado só para extrair dados; o lembrete começa com "se este resultado traz crítica".A leitura de comandos da lixeira (aspas, sudo/env, alvos seguros) veio de ideias do launch-codes, da OneWave AI (MIT).
Licença: MIT.
Six mods, each fixing a problem that came up in real use. The mods' UI and messages are in Portuguese.
Requires Claude Code 2.1.287 or later.
/plugin marketplace add diegocamara89/claude-mods
/plugin install sonnet-por-padrao@claude-mods-diego
Replace sonnet-por-padrao with any mod name from the table. To try one without installing: claude --plugin-dir ./mods/<name>.
| Mod | What it does | What it touches | |
|---|---|---|---|
| sonnet-por-padrao ("Sonnet by default") | Subagents and Workflow script agents created without an explicit model run on Sonnet instead of inheriting the session's Opus. An explicit opus is kept. | Rewrites the subagent's model and the Workflow script; logs its decisions (with each subagent's description) to LOCALAPPDATA on Windows or TMPDIR//tmp elsewhere. | |
| revisor-com-prova ("reviewer with evidence") | When a reviewer returns (Codex, codex exec, agy, council), attaches a reminder to the result: a finding counts only with evidence, reproduce before fixing, "approved with no findings" is valid. | Reads the command only; attaches model-only text to the result. The agy.py and council.py patterns come from the author's own skills; without them they simply never fire. | |
| proximos-passos ("next steps") | After each answer, Haiku proposes three next requests as buttons above the prompt (keys 1-3). /custos shows measured token use. | Calls Haiku (about 1k tokens per suggestion, measured); draws the band. | |
| painel-vivo ("live pane") | Glanceable live pane: a headline with Pending, Activity and Progress; a queue of what needs you (permission, question, stuck worker) and warnings; workers as a tree (subagents, Workflow agents, commands, agy and Codex nested under whoever called them), each with elapsed time, model, tokens and what it is doing now; other busy sessions in one line; 5-hour and 7-day rate limits. "Explicar" explains a job in plain language and feeds a glossary (/glossario). | Wraps each Bash and PowerShell command to copy its output to a local file (tee, in LOCALAPPDATA or TMPDIR) and to tell agy where to write its steps (AGY_EVENTS_DIR); deletes those files after 1 day; if rtk is installed, runs the command through it first; keeps a per-session summary in the plugin store so other sessions can see it; reads the conversation title from the transcript with a short Python script; calls Haiku only on "Explicar". | |
| lixeira ("trash") | Denies permanent deletes and hands Claude the command that moves the files to the system trash; blocks no-way-back commands (git reset --hard, push --force, format, emptying the trash, shutting down or restarting the computer, Format-Volume, Clear-Disk) until the user says yes (LIXEIRA_OK=1 in Bash, $env:LIXEIRA_OK=1; in PowerShell). It also catches deletes through a PowerShell pipeline (`gci folder \ | Remove-Item`). Temp folders, caches and files written in the session stay free. | Reads the command and denies when needed. On Windows it uses the bundled lixeira.ps1; on macOS and Linux it suggests trash or gio trash. |
| varredura-push ("push scan") | Before git push and gh publishing (in Bash and PowerShell), scans the commits about to leave for keys, tokens and passwords, and blocks the push on a hit. One-off override with VARREDURA_OK=1. | Prepends python varredura.py && to the push. Uses gitleaks when on PATH; fallback rules otherwise. |
LLM reviewers find defects in correct work when told to "find what breaks": in a 2026 study GPT-4o rejected 73% of correct code under that framing, against 26% with a plain prompt (arXiv 2603.00539). On the receiving side, critique gets accepted unchecked: given 48 false reports, Claude Code rejected 1 (arXiv 2604.11950). Requiring evidence moved that to 85–96% rejection. The mod puts the rule exactly where it is needed: on the reviewer's result, not on every message.
painel-vivo: live output uses bash (on Windows, the Git Bash Claude Code already uses); other sessions' names need python on PATH; built for the desktop app (the terminal gets a text-only version). Codex does not report tokens: the pane shows only its model, when the command picks one.varredura-push: needs Python (python on Windows, python3 on macOS and Linux).sonnet-por-padrao: does not see Workflow scripts run from a file (scriptPath) or saved Workflows run by name.revisor-com-prova: also fires when agy is used only for extraction; the reminder starts with "if this result carries critique".The command parsing in lixeira (quotes, sudo/env, safe targets) builds on ideas from launch-codes by OneWave AI (MIT).
License: MIT.
hooks/register.tsx 138 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { Opcao } from '../types'
5
6// Depois de cada resposta, o Haiku propõe 3 próximos pedidos (botões acima da caixa).
7// Medido em 02/10: o Haiku lê ~1 mil tokens; o modelo principal releria a conversa inteira (~850 mil).
8const opcoes = atom({ plugin: 'proximos-passos', key: 'opcoes' } as const, [])
9const gerando = atom({ plugin: 'proximos-passos', key: 'gerando' } as const, false)
10const nativa = atom({ plugin: 'proximos-passos', key: 'nativa' } as const, '')
11
12const SISTEMA = `Você sugere os próximos passos de uma conversa de trabalho entre um usuário e o assistente Claude.
13O usuário escreve curto e direto e decide o que fazer; o assistente executa. Escreva os pedidos no idioma em que o usuário escreve.
14Leia o último pedido do usuário e a resposta do assistente e proponha exatamente 3 próximos pedidos que o USUÁRIO mandaria agora.
15- Se a resposta terminou com uma pergunta ou com opções, as sugestões são as respostas plausíveis a ela (a recomendada primeiro).
16- Senão, os passos mais úteis a partir dali: continuar, conferir/testar, ou mudar de rumo.
17- Cada pedido escrito como o usuário escreveria, na primeira pessoa dele, imperativo curto ("sim, pode aplicar").
18- Nada genérico ("continue", "obrigado"). Nada que repita o que já foi feito.
19Responda APENAS um JSON válido, sem cercas de código: [{"rotulo": "<até 6 palavras>", "pedido": "<o texto que será enviado>"}, ...]`
20
21let ultimoPedido = ''
22
23// Tokens exatamente como a resposta da Anthropic informa (campo usage).
24async function medir($: any, modo: 'turno' | 'haiku', ms: number, u: any) {
25 const m = {
26 modo, ms,
27 novos: Number(u?.input_tokens ?? 0),
28 cache: Number(u?.cache_read_input_tokens ?? 0),
29 escrita: Number(u?.cache_creation_input_tokens ?? 0),
30 saida: Number(u?.output_tokens ?? 0),
31 }
32 const lista = ((await $.store.get('medicoes2')) as any[] | undefined) ?? []
33 await $.store.set('medicoes2', [...lista, m].slice(-600))
34}
35
36function ler(texto: string): Opcao[] {
37 const j = JSON.parse(String(texto).replace(/^[\s\S]*?(\[)/, '$1').replace(/\][^\]]*$/, ']'))
38 return Array.isArray(j)
39 ? j.filter((o: any) => o?.rotulo && o?.pedido).slice(0, 3)
40 .map((o: any) => ({ rotulo: String(o.rotulo).slice(0, 60), pedido: String(o.pedido) }))
41 : []
42}
43
44async function sugerir($: any, resposta: string) {
45 await update($, gerando, () => true)
46 let lista: Opcao[] = []
47 try {
48 const t0 = await $.clock.now()
49 const r: any = await $.model.complete({
50 model: 'haiku', system: SISTEMA, maxTokens: 300, timeoutMs: 15_000,
51 prompt: `Último pedido do usuário:\n${ultimoPedido.slice(0, 1000)}\n\nFim da resposta do assistente:\n${resposta.slice(-3000)}`,
52 })
53 await medir($, 'haiku', (await $.clock.now()) - t0, r.usage).catch(() => undefined)
54 if (r.isAnswered) lista = ler(r.text)
55 } catch {
56 /* erro, limite de tempo ou resposta fora do formato: sem sugestão desta vez */
57 } finally {
58 // Sempre desliga o "pensando", senão a faixa fica presa.
59 await update($, opcoes, () => lista)
60 await update($, gerando, () => false)
61 }
62}
63
64const fmt = (n: number) => Math.round(n).toLocaleString('pt-BR')
65
66export const register: Register = on => {
67 on('session.start', async ($, e, next) => {
68 await $.command.register({ name: 'custos', description: 'Tokens das sugestões comparados com uma resposta normal' })
69 // Um recarregamento no meio de uma sugestão deixaria o "pensando" ligado para sempre.
70 await update($, gerando, () => false)
71 return next(e)
72 })
73
74 on('command.run', { command: 'custos' }, async $ => {
75 const todas = (((await $.store.get('medicoes2')) as any[] | undefined) ?? []).filter(m => m.modo !== 'claude')
76 if (!todas.length) return { text: 'Ainda não há medições.' }
77 const nomes: Record<string, string> = { turno: 'Resposta normal do Claude (referência)', haiku: 'Sugestões pelo Haiku' }
78 const linhas: string[] = ['Médias por chamada, em tokens, como a Anthropic informa:']
79 for (const modo of ['turno', 'haiku']) {
80 const l = todas.filter(m => m.modo === modo)
81 if (!l.length) continue
82 const media = (k: string) => l.reduce((s, m) => s + Number(m[k] ?? 0), 0) / l.length
83 const ms = l.map(m => m.ms).filter(Boolean).sort((a, b) => a - b)
84 linhas.push(
85 `• ${nomes[modo]} (${l.length}): ${fmt(media('cache'))} lidos do cache · ${fmt(media('novos'))} novos · ${fmt(media('escrita'))} gravados no cache · ${fmt(media('saida'))} de saída` +
86 (ms.length ? ` · ${(ms[Math.floor(ms.length / 2)] / 1000).toFixed(1)} s` : ''),
87 )
88 }
89 return { text: linhas.join('\n') }
90 })
91
92 // Só para não repetir nos botões o que já está no Tab.
93 on('prompt.suggest', async ($, e: any, next) => {
94 if (e.origin?.kind === 'suggestion' && e.text) await update($, nativa, () => String(e.text))
95 return next(e)
96 })
97
98 on('prompt.submit', async ($, e, next) => {
99 ultimoPedido = e.text
100 await update($, opcoes, () => [])
101 await update($, nativa, () => '')
102 return next(e)
103 })
104
105 on('turn.complete', async ($, e, next) => {
106 const r = await next(e)
107 if (e.agentId || e.reason !== 'answer') return r
108 if (e.usage) await medir($, 'turno', e.durationMs, e.usage).catch(() => undefined)
109 // Mensagens automáticas de outros mods e respostas curtas não pedem sugestão.
110 // Mensagem automática de outro mod começa com "[nome-do-mod]".
111 if (/^\s*\[[\w-]+\]/.test(ultimoPedido) || e.answer.split(/\s+/).length < 15) return r
112 void sugerir($, e.answer)
113 return r
114 })
115
116 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
117 const n = await read($, nativa)
118 const mesmo = (a: string, b: string) => a.trim().toLowerCase() === b.trim().toLowerCase()
119 const botoes = (await read($, opcoes)).filter(o => !n || !mesmo(o.pedido, n))
120 const ocupado = await read($, gerando)
121 if (e.props.hasSurvey || e.props.isWorking || (!botoes.length && !ocupado)) return next(e)
122 const { Box, Button, Text } = $.ui.resolve(e)
123 const enviar = (o: Opcao) => async () => {
124 await update($, opcoes, () => [])
125 await $.prompt.submit({ text: o.pedido, asUser: true })
126 }
127 return (
128 <Box flexDirection="row" flexWrap="wrap" columnGap={2}>
129 {ocupado && !botoes.length && <Text dimColor>pensando nos próximos passos…</Text>}
130 {botoes.map((o, i) => (
131 <Button key={`op${i}`} label={o.rotulo} hotkey={String(i + 1)} {...(i === 0 ? { variant: 'primary' as const } : {})} onPress={enviar(o)} />
132 ))}
133 {botoes.length > 0 && <Button key="fechar" plain label="✕" role="dismiss" onPress={() => update($, opcoes, () => [])} />}
134 </Box>
135 )
136 })
137}
138types/index.d.ts 8 lines1export type Opcao = { rotulo: string; pedido: string }
2
3declare module 'claude-code' {
4 interface PluginState {
5 'proximos-passos': { opcoes: Opcao[]; gerando: boolean; nativa: string }
6 }
7}
8