🪝 Hooks
Execute scripts shell em pontos-chave do ciclo do agente —
antes de uma tool rodar (PreToolUse), depois de uma tool ter sucesso
(PostToolUse), ou ao terminar (Stop). O output é
injetado de volta no contexto do modelo: ele vê os erros de lint, typecheck
ou testes e corrige sozinho, sem intervenção humana.
🇬🇧 In short: hooks let you run shell scripts at key points in the agent loop. Output is fed back to the model so it can self-correct lint errors, type errors, and failing tests — no human in the loop required.
Como funciona
Eventos
Antes da tool executar
Se o script retornar exit code ≠ 0, a tool é bloqueada e o output vira mensagem de erro para o modelo.
- Bloquear comandos perigosos (
rm -rf) - Validar argumentos antes de escrever
- Garantir ambiente pronto
Após tool bem-sucedida
stdout+stderr do script são injetados no contexto — o modelo vê e pode corrigir no próximo turno.
- Lint / typecheck automático
- Formatar código (prettier, black)
- Rodar testes afetados
Ao chamar finish
Roda depois da verificação passar, antes de retornar o resultado. Sem contexto de tool — use {workspace}.
- Commit automático das mudanças
- Notificar via Slack / webhook
- Gerar changelog / release notes
Configuração
Crie .poly/hooks.json na raiz do projeto (ou ~/.polypus/hooks.json para configuração global):
{
"hooks": [
{
"event": "PostToolUse",
"on": ["write_file", "edit_file"],
"command": "npx tsc --noEmit 2>&1 | head -30",
"maxOutputChars": 2000,
"timeout": 30000
},
{
"event": "PostToolUse",
"on": ["write_file", "edit_file"],
"command": "npx eslint {path} --fix 2>&1",
"maxOutputChars": 1000
}
]
}{
"hooks": [
{
"event": "PostToolUse",
"on": ["write_file", "edit_file"],
"command": "ruff check {path} --fix 2>&1",
"maxOutputChars": 1000
},
{
"event": "PostToolUse",
"on": ["write_file"],
"command": "python -m pytest tests/ -x -q 2>&1 | tail -20",
"maxOutputChars": 2000,
"timeout": 60000
}
]
}{
"hooks": [
{
"event": "Stop",
"command": "cd {workspace} && git add -A && git diff --cached --quiet || git commit -m 'polypus: auto-commit'"
},
{
"event": "PreToolUse",
"on": ["run_command"],
"command": "echo '{tool}' | grep -q 'rm -rf' && exit 1 || exit 0"
}
]
}🔒 Segurança: hooks só são carregados de ~/.polypus/hooks.json ou .poly/hooks.json do workspace — nunca de subdiretórios do repositório. Isso evita que projetos de terceiros injetem comandos maliciosos.
Visibilidade no CLI e VSCode
Cada hook aparece na timeline de eventos em tempo real. Use --compact ou pressione h para colapsar.
Referência de campos
| Campo | Tipo | Padrão | Descrição |
|---|---|---|---|
| event | string | — | PreToolUse · PostToolUse · Stop |
| on | string | string[] | "*" | Nome(s) da tool. Não usado em Stop. |
| command | string | — | Comando shell. Suporta {path} {tool} {workspace} |
| timeout | number (ms) | 120 000 | Tempo máximo de execução |
| maxOutputChars | number | 1 000 | Máx chars capturados. Limite absoluto: 8 000 |
Substituições de variáveis
| Placeholder | Valor | Disponível em |
|---|---|---|
| {path} | Caminho do arquivo operado | PreToolUse, PostToolUse |
| {tool} | Nome da tool (ex.: write_file) | PreToolUse, PostToolUse |
| {workspace} | Diretório raiz do projeto | Todos |
Migração do formato antigo
Os campos afterWrite, afterEdit, afterTool e beforeCommand ainda funcionam mas estão deprecated — uma mensagem de aviso é exibida no stderr ao carregar.
⚠️ Formato deprecated ainda funciona mas não receberá novos recursos. Migre para o campo hooks para ter acesso a PreToolUse, Stop, matcher por tool e controle de timeout/output.
// Antes (deprecated)
{ "afterWrite": "npm run lint", "beforeCommand": { "deny": ["rm -rf"] } }
// Depois
{
"hooks": [
{ "event": "PostToolUse", "on": ["write_file", "edit_file"], "command": "npm run lint" },
{ "event": "PreToolUse", "on": ["run_command"], "command": "echo '{tool}' | grep -q 'rm -rf' && exit 1 || exit 0" }
]
}