Troca o apagar de vez pela lixeira do sistema e barra comandos sem volta (git reset --hard, push --force, formatar) até o sim do usuário

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.ts 132 lines1import type { Register } from 'claude-code'
2
3// Troca o apagar de vez pela lixeira do sistema e barra o que não tem volta.
4// Ideias de leitura de comando (aspas, sudo/env, alvos seguros) do launch-codes, OneWave AI (MIT):
5// https://github.com/OneWave-AI/claude-code-mods/tree/main/launch-codes
6const LIBERA = /\bLIXEIRA_OK=1\b/
7
8// Temporários e caches: apagar ali não perde trabalho.
9const SEGURO = /(^|[\\/])\$?\{?(temp|tmp|scratchpad|node_modules|__pycache__|\.pytest_cache|\.cache|\.mypy_cache|\.ruff_cache|\.next|\.turbo|dist|coverage)([\\/]|$)/i
10
11const SEM_VOLTA: [RegExp, string][] = [
12 [/\bformat(\.com)?\s+[a-z]:/i, 'formatar disco'],
13 [/\bdiskpart\b/i, 'diskpart'],
14 [/\bClear-RecycleBin\b|\$Recycle\.Bin/i, 'esvaziar a lixeira'],
15 [/\bgit\b[^;&|\n]*\breset\b[^;&|\n]*--hard\b/, 'git reset --hard'],
16 [/\bgit\b[^;&|\n]*\bpush\b[^;&|\n]*(--force(?!-with-lease)\b|\s-f\b)/, 'git push --force'],
17 [/\bgit\b[^;&|\n]*\bclean\b[^;&|\n]*\s-[a-z]*f/, 'git clean -f'],
18 [/\b(shred|sdelete)\b|\bcipher\s+\/w/i, 'apagar sem recuperação'],
19 [/\bmkfs\b|\bdd\b[^;\n]*\bof=/, 'sobrescrever disco'],
20 [/\bshutdown(\.exe)?\s+[/-][srgp]\b|\b(Restart|Stop)-Computer\b/i, 'desligar ou reiniciar o computador'],
21 [/\bFormat-Volume\b|\bClear-Disk\b|\bInitialize-Disk\b/i, 'formatar ou zerar disco'],
22]
23const LIBERAR = 'repita com `$env:LIXEIRA_OK=1;` na frente (PowerShell) ou `LIXEIRA_OK=1 ` na frente (Bash), ' +
24 'só com o sim do usuário nesta conversa. Nunca por conta própria.'
25
26type Palavra = { w: string } | { op: string }
27
28// Divide em palavras e operadores respeitando aspas: texto entre aspas não vira comando.
29function palavras(linha: string): Palavra[] {
30 const out: Palavra[] = []
31 let w = '', tem = false
32 const fecha = () => { if (tem) out.push({ w }); w = ''; tem = false }
33 for (let i = 0; i < linha.length; i++) {
34 const c = linha[i]
35 if (c === "'" || c === '"') {
36 const fim = linha.indexOf(c, i + 1)
37 w += linha.slice(i + 1, fim === -1 ? linha.length : fim); tem = true
38 i = fim === -1 ? linha.length : fim
39 } else if (c === ' ' || c === '\t') fecha()
40 else if (c === '\n' || c === ';' || c === '&' || c === '|' || c === '(' || c === ')') { fecha(); out.push({ op: c }) }
41 else { w += c; tem = true }
42 }
43 fecha()
44 return out
45}
46
47// Cada comando com o operador que veio antes dele ('' no começo): a esteira (|) passa o alvo adiante.
48function comandos(linha: string): { op: string; ws: string[] }[] {
49 const out: { op: string; ws: string[] }[] = []
50 let atual: string[] = [], op = ''
51 for (const p of palavras(linha)) {
52 if ('op' in p) { if (atual.length) out.push({ op, ws: atual }); atual = []; op = p.op }
53 else atual.push(p.w)
54 }
55 if (atual.length) out.push({ op, ws: atual })
56 return out
57}
58
59const PULA = new Set(['sudo', 'env', 'command', 'exec', 'nohup', 'time', 'xargs'])
60function programa(ws: string[]) {
61 let i = 0
62 while (i < ws.length && (PULA.has(ws[i]) || /^[A-Za-z_]\w*=/.test(ws[i]))) i++
63 return { nome: (ws[i] ?? '').split(/[\\/]/).pop()!.toLowerCase().replace(/\.exe$/, ''), args: ws.slice(i + 1) }
64}
65
66// Alvos de cada comando que apaga, nas duas sintaxes (Bash e PowerShell/cmd).
67export function alvosQueApagam(cmd: string): string[] {
68 const alvos: string[] = []
69 const semFlagDe = (args: string[]) => args.filter(a => !/^(-|\/[a-z]$)/i.test(a))
70 let anterior: string[] = []
71 for (const { op, ws } of comandos(cmd)) {
72 const { nome, args } = programa(ws)
73 let semFlag = semFlagDe(args)
74 const ant = anterior
75 anterior = ws
76 if (['rm', 'unlink', 'remove-item', 'ri', 'del', 'erase', 'rd', 'rmdir'].includes(nome)) {
77 if (nome === 'rmdir' && !args.some(a => /^(\/s|-r|-recurse)$/i.test(a))) continue // rmdir simples só remove pasta vazia
78 // Esteira do PowerShell (gci X | Remove-Item): o alvo é o caminho do comando anterior.
79 if (!semFlag.length && op === '|') semFlag = semFlagDe(programa(ant).args).length ? semFlagDe(programa(ant).args) : ['.']
80 alvos.push(...semFlag)
81 } else if (nome === 'find' && args.includes('-delete')) alvos.push(args[0] ?? '.')
82 else if (['powershell', 'pwsh', 'cmd', 'bash', 'sh'].includes(nome)) {
83 const i = args.findIndex(a => /^(-c|-command|\/c)$/i.test(a))
84 if (i >= 0 && args[i + 1]) alvos.push(...alvosQueApagam(args.slice(i + 1).join(' ')))
85 }
86 }
87 return alvos
88}
89
90// Arquivos que o Claude criou nesta conversa: apagar o próprio rascunho é livre.
91const criados = new Set<string>()
92const norm = (p: string) => p.replace(/\\/g, '/').replace(/^\/([a-z])\//i, '$1:/').toLowerCase()
93
94export const register: Register = on => {
95 on('tool.call', async ($, e, next) => {
96 const a: any = e
97 if (e.tool === 'Write' && a.file_path) {
98 const r = await next(e)
99 criados.add(norm(String(a.file_path)))
100 return r
101 }
102 if (e.tool !== 'Bash' && e.tool !== 'PowerShell') return next(e)
103 const cmd = String(a.command ?? '').replace(/<<-?\s*['"]?(\w+)['"]?[^\n]*\n[\s\S]*?\n\s*\1\s*(?=\n|$)/g, '')
104 if (LIBERA.test(cmd)) return next(e)
105
106 const semVolta = SEM_VOLTA.filter(([re]) => re.test(cmd)).map(([, nome]) => nome)
107 if (semVolta.length) {
108 $.ui.toast(`lixeira: barrado (${semVolta.join(', ')})`)
109 return {
110 deny: `lixeira barrou um comando sem volta: ${semVolta.join(', ')}.\n` +
111 `Explique ao usuário o que o comando faria e peça o sim dele. Com o sim explícito, ${LIBERAR}`,
112 }
113 }
114
115 const perigosos = alvosQueApagam(cmd).filter(t => t && !SEGURO.test(t) && !criados.has(norm(t)))
116 if (!perigosos.length) return next(e)
117 $.ui.toast('lixeira: troquei o apagar pela lixeira')
118 const lista = perigosos.map(t => `"${t}"`).join(' ')
119 // Windows: script que vem junto com o mod. macOS e Linux: o comando de lixeira do sistema.
120 const windows = (await $.env.get('OS')) === 'Windows_NT'
121 const script = `${String($.plugin.root).replace(/\\/g, '/')}/lixeira.ps1`
122 const como = windows
123 ? `Mande para a Lixeira do Windows (dá para restaurar):\npowershell -NoProfile -ExecutionPolicy Bypass -File "${script}" ${lista}\n`
124 : `Mande para a lixeira do sistema (dá para restaurar): trash ${lista} (macOS 14+ ou pacote trash-cli) ou gio trash ${lista} (Linux).\n`
125 return {
126 deny: `lixeira: este comando apagaria de vez ${perigosos.slice(0, 5).join(', ')}${perigosos.length > 5 ? '…' : ''}.\n` +
127 como +
128 `Coringa (*) ou variável no caminho: troque pelo caminho real antes. Se o usuário pediu apagar de vez, ${LIBERAR}`,
129 }
130 })
131}
132