Skip to Content
Guia do UsuárioRecursosHooks

Qwen Code Hooks

Overview

Os hooks do Qwen Code fornecem um mecanismo poderoso para estender e personalizar o comportamento do aplicativo Qwen Code. Os hooks permitem que os usuários executem scripts ou programas personalizados em pontos específicos do ciclo de vida do aplicativo, como antes da execução de uma ferramenta, após a execução de uma ferramenta, no início/fim da sessão e durante outros eventos importantes.

Os hooks são habilitados por padrão. Você pode desabilitar temporariamente todos os hooks definindo disableAllHooks como true no seu arquivo de configurações (no nível superior, junto com hooks):

{ "disableAllHooks": true, "hooks": { "PreToolUse": [...] } }

Isso desabilita todos os hooks sem excluir suas configurações.

What are Hooks?

Hooks são scripts ou programas definidos pelo usuário que são executados automaticamente pelo Qwen Code em pontos predefinidos do fluxo do aplicativo. Eles permitem que os usuários:

  • Monitorem e auditem o uso de ferramentas
  • Apliquem políticas de segurança
  • Injetem contexto adicional nas conversas
  • Personalizem o comportamento do aplicativo com base em eventos
  • Integrem-se com sistemas e serviços externos
  • Modifiquem entradas ou respostas de ferramentas programaticamente

Hook Types

O Qwen Code suporta quatro tipos de executores de hook:

TypeDescription
commandExecuta um comando de shell. Recebe JSON via stdin e retorna os resultados via stdout.
httpEnvia JSON como corpo de uma requisição POST para uma URL especificada. Retorna os resultados via corpo da resposta HTTP.
functionChama diretamente uma função JavaScript registrada (apenas hooks no nível da sessão).
promptUsa um LLM para avaliar a entrada do hook e retornar uma decisão.

Command Hooks

Os hooks de comando executam comandos via processos filhos. O JSON de entrada é passado via stdin, e a saída é retornada via stdout.

Configuration:

FieldTypeRequiredDescription
type"command"YesTipo do hook
commandstringYesComando a ser executado
namestringNoNome do hook (para logs)
descriptionstringNoDescrição do hook
timeoutnumberNoTimeout em milissegundos, padrão 60000
asyncbooleanNoSe deve ser executado de forma assíncrona em segundo plano
envRecord<string, string>NoVariáveis de ambiente
shell"bash" | "powershell"NoShell a ser usado
statusMessagestringNoMensagem de status exibida durante a execução

Example:

{ "hooks": { "PreToolUse": [ { "matcher": "WriteFile", "hooks": [ { "type": "command", "command": "$QWEN_PROJECT_DIR/.qwen/hooks/security-check.sh", "name": "security-check", "timeout": 10000 } ] } ] } }

HTTP Hooks

Os hooks HTTP enviam a entrada do hook como requisições POST para URLs especificadas. Eles suportam listas de permissão de URL, proteção contra SSRF no nível de DNS, interpolação de variáveis de ambiente e outros recursos de segurança.

Configuration:

FieldTypeRequiredDescription
type"http"YesTipo do hook
urlstringYesURL de destino
headersRecord<string, string>NoCabeçalhos da requisição (suporta interpolação de variáveis de ambiente)
allowedEnvVarsstring[]NoLista de permissão de variáveis de ambiente permitidas na URL/cabeçalhos
timeoutnumberNoTimeout em segundos, padrão 600
namestringNoNome do hook (para logs)
statusMessagestringNoMensagem de status exibida durante a execução
oncebooleanNoExecutar apenas uma vez por evento por sessão (apenas hooks HTTP)

Security Features:

  • Lista de permissão de URL: Configure os padrões de URL permitidos via allowedUrls
  • Proteção contra SSRF: Bloqueia IPs privados (10.x.x.x, 172.16-31.x.x, 192.168.x.x, etc.), mas permite endereços de loopback (127.0.0.1, ::1)
  • Validação de DNS: Valida a resolução de domínio antes das requisições para evitar ataques de DNS rebinding
  • Interpolação de Variáveis de Ambiente: Sintaxe ${VAR}, permite apenas variáveis na lista de permissão allowedEnvVars

Example:

{ "hooks": { "PreToolUse": [ { "matcher": "*", "hooks": [ { "type": "http", "url": "http://127.0.0.1:8080/hooks/pre-tool-use", "headers": { "Authorization": "Bearer ${HOOK_API_KEY}" }, "allowedEnvVars": ["HOOK_API_KEY"], "timeout": 10, "name": "remote-security-check" } ] } ] } }

Function Hooks

Os hooks de função chamam diretamente funções JavaScript/TypeScript registradas. Eles são usados internamente pelo sistema de Skills e atualmente não são expostos como uma API pública para usuários finais.

Note: Para a maioria dos casos de uso, use hooks de comando ou hooks HTTP em vez disso, que podem ser configurados em arquivos de configurações.

Prompt Hooks

Os hooks de prompt usam um LLM para avaliar a entrada do hook e retornar uma decisão. Isso é útil para tomar decisões inteligentes com base no contexto, como determinar se uma operação deve ser permitida ou bloqueada.

How it works:

  1. O JSON de entrada do hook é injetado no seu prompt usando o placeholder $ARGUMENTS
  2. O prompt é enviado para um LLM (padrão: seu modelo atual)
  3. O LLM retorna uma resposta JSON com a decisão
  4. O Qwen Code processa a decisão e continua ou bloqueia a execução de acordo

Configuration:

FieldTypeRequiredDescription
type"prompt"YesTipo do hook
promptstringYesPrompt enviado para o LLM. Use $ARGUMENTS para a entrada do hook
modelstringNoModelo a ser usado (padrão: seu modelo atual)
timeoutnumberNoTimeout em segundos, padrão 30
namestringNoNome do hook (para logs)
descriptionstringNoDescrição do hook
statusMessagestringNoMensagem de status exibida durante a execução

Response Format:

O LLM deve retornar um JSON com a seguinte estrutura:

{ "ok": true, "reason": "Explanation of the decision", "additionalContext": "Optional context to inject into the conversation" }
FieldDescription
oktrue para permitir/continuar, false para bloquear/parar
reasonObrigatório quando ok é false. Mostrado ao modelo para explicar o bloqueio
additionalContextOpcional. Contexto adicional para injetar na conversa ao permitir

Supported Events:

Os hooks de prompt podem ser usados com a maioria dos eventos de hook, incluindo:

  • PreToolUse - Avalia se deve permitir uma chamada de ferramenta
  • PostToolUse - Avalia os resultados da ferramenta e potencialmente injeta contexto
  • Stop - Determina se deve continuar ou parar
  • SubagentStop - Avalia os resultados do subagente
  • UserPromptSubmit - Avalia ou enriquece os prompts do usuário

Example: Stop Hook

{ "hooks": { "Stop": [ { "hooks": [ { "type": "prompt", "prompt": "You are evaluating whether Qwen Code should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.", "timeout": 30 } ] } ] } }

Quando ok é false, o Qwen Code continuará trabalhando e usará o reason como contexto para a próxima resposta.

Example: PreToolUse Hook

{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "prompt", "prompt": "Evaluate this tool call for security concerns. Tool input: $ARGUMENTS\n\nCheck for:\n- Dangerous commands (rm -rf, curl | sh, etc.)\n- Unauthorized access attempts\n- Data exfiltration patterns\n\nRespond with {\"ok\": true} if safe, or {\"ok\": false, \"reason\": \"concern\"} if blocked.", "model": "sonnet", "timeout": 30, "name": "security-evaluator" } ] } ] } }

Hook Events

Os hooks são disparados em pontos específicos durante uma sessão do Qwen Code. Diferentes eventos suportam diferentes matchers para filtrar as condições de disparo.

EventTriggered WhenMatcher Target
PreToolUseAntes da execução da ferramentaNome da ferramenta (WriteFile, ReadFile, Bash, etc.)
PostToolUseApós a execução bem-sucedida da ferramentaNome da ferramenta
PostToolUseFailureApós a falha na execução da ferramentaNome da ferramenta
UserPromptSubmitApós o usuário enviar o promptNenhum (sempre dispara)
SessionStartQuando a sessão inicia ou é retomadaOrigem (startup, resume, clear, compact)
SessionEndQuando a sessão terminaMotivo (clear, logout, prompt_input_exit, etc.)
StopQuando o Claude se prepara para concluir a respostaNenhum (sempre dispara)
SubagentStartQuando o subagente iniciaTipo de agente (Bash, Explorer, Plan, etc.)
SubagentStopQuando o subagente paraTipo de agente
PreCompactAntes da compactação da conversaGatilho (manual, auto)
NotificationQuando as notificações são enviadasTipo (permission_prompt, idle_prompt, auth_success)
PermissionRequestQuando o diálogo de permissão é exibidoNome da ferramenta
TodoCreatedQuando um novo item de todo é criadoNenhum (sempre dispara)
TodoCompletedQuando um item de todo é marcado como concluídoNenhum (sempre dispara)

Padrões de Matcher

matcher é uma expressão regular usada para filtrar condições de gatilho.

Tipo de EventoEventosSuporte a MatcherAlvo do Matcher
Eventos de FerramentaPreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest✅ RegexNome da ferramenta: WriteFile, ReadFile, Bash, etc.
Eventos de SubagenteSubagentStart, SubagentStop✅ RegexTipo de agente: Bash, Explorer, etc.
Eventos de SessãoSessionStart✅ RegexOrigem: startup, resume, clear, compact
Eventos de SessãoSessionEnd✅ RegexMotivo: clear, logout, prompt_input_exit, etc.
Eventos de NotificaçãoNotification✅ Correspondência exataTipo: permission_prompt, idle_prompt, auth_success
Eventos de CompactaçãoPreCompact✅ Correspondência exataGatilho: manual, auto
Eventos de TodoTodoCreated, TodoCompleted❌ NãoN/A
Eventos de PromptUserPromptSubmit❌ NãoN/A
Eventos de StopStop❌ NãoN/A

Sintaxe do Matcher:

  • String vazia "" ou "*" corresponde a todos os eventos desse tipo
  • Sintaxe padrão de regex suportada (por exemplo, ^Bash$, Read.*, (WriteFile|Edit))

Exemplos:

{ "hooks": { "PreToolUse": [ { "matcher": "^Bash$", "hooks": [ { "type": "command", "command": "echo 'bash check' >> /tmp/hooks.log" } ] }, { "matcher": "Write.*", "hooks": [ { "type": "command", "command": "echo 'write check' >> /tmp/hooks.log" } ] }, { "matcher": "*", "hooks": [ { "type": "command", "command": "echo 'all tools' >> /tmp/hooks.log" } ] } ], "SubagentStart": [ { "matcher": "^(Bash|Explorer)$", "hooks": [ { "type": "command", "command": "echo 'subagent check' >> /tmp/hooks.log" } ] } ] } }

Regras de Entrada/Saída

Estrutura de Entrada do Hook

Todos os hooks recebem entrada padronizada no formato JSON através do stdin (command) ou do corpo do POST (http).

Campos Comuns:

{ "session_id": "string", "transcript_path": "string", "cwd": "string", "hook_event_name": "string", "timestamp": "string" }

Campos específicos do evento são adicionados com base no tipo de hook. Ao executar em um subagente, agent_id e agent_type são incluídos adicionalmente.

Estrutura de Saída do Hook

A saída do hook é retornada via stdout (command) ou corpo da resposta HTTP (http) como JSON.

Comportamento do Código de Saída (Hooks de Comando):

Código de SaídaComportamento
0Sucesso. Analisa o JSON no stdout para controlar o comportamento.
2Erro de bloqueio. Ignora o stdout, passa o stderr como feedback de erro para o modelo.
OtherErro não bloqueante. O stderr é exibido apenas no modo de depuração, a execução continua.

Estrutura de Saída:

A saída do hook suporta três categorias de campos:

  1. Campos Comuns: continue, stopReason, suppressOutput, systemMessage
  2. Decisão de Nível Superior: decision, reason (usados por alguns eventos)
  3. Controle Específico do Evento: hookSpecificOutput (deve incluir hookEventName)
{ "continue": true, "decision": "allow", "reason": "Operation approved", "hookSpecificOutput": { "hookEventName": "PreToolUse", "additionalContext": "Additional context information" } }

Detalhes Individuais dos Eventos de Hook

PreToolUse

Propósito: Executado antes que uma ferramenta seja usada para permitir verificações de permissão, validação de entrada ou injeção de contexto.

Campos específicos do evento:

{ "permission_mode": "default | plan | auto_edit | yolo", "tool_name": "name of the tool being executed", "tool_input": "object containing the tool's input parameters", "tool_use_id": "unique identifier for this tool use instance (internal format, e.g., toolu_xxx)", "tool_call_id": "original API call ID from the LLM provider (e.g., call_xxx for OpenAI/Qwen) (optional)" }

Opções de Saída:

  • hookSpecificOutput.permissionDecision: “allow”, “deny” ou “ask” (OBRIGATÓRIO)
  • hookSpecificOutput.permissionDecisionReason: explicação para a decisão (OBRIGATÓRIO)
  • hookSpecificOutput.updatedInput: parâmetros de entrada da ferramenta modificados para usar em vez do original
  • hookSpecificOutput.additionalContext: informações de contexto adicionais

O valor de permissionDecision controla se a ferramenta é executada:

  • "allow" — executa a ferramenta sem o prompt de aprovação usual.
  • "deny" — bloqueia a ferramenta; ela não é executada e um erro é retornado ao modelo.
  • "ask" — pausa e pede ao usuário para confirmar a chamada da ferramenta na TUI antes que ela seja executada. Confirmar executa a ferramenta uma vez; recusar a cancela. Em contextos que não podem pedir confirmação — execuções headless (--prompt) e subagentes em segundo plano — "ask" reverte para "deny".

Nota: Embora campos padrão de saída de hook como decision e reason sejam tecnicamente suportados pela classe subjacente, a interface oficial espera o hookSpecificOutput com permissionDecision e permissionDecisionReason.

Exemplo de Saída:

{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Security policy blocks database writes", "additionalContext": "Current environment: production. Proceed with caution." } }

PostToolUse

Propósito: Executado após uma ferramenta ser concluída com sucesso para processar resultados, registrar ocorrências ou injetar contexto adicional.

Campos específicos do evento:

{ "permission_mode": "default | plan | auto_edit | yolo", "tool_name": "name of the tool that was executed", "tool_input": "object containing the tool's input parameters", "tool_response": "object containing the tool's response", "tool_use_id": "unique identifier for this tool use instance (internal format, e.g., toolu_xxx)", "tool_call_id": "original API call ID from the LLM provider (e.g., call_xxx for OpenAI/Qwen) (optional)" }

Opções de Saída:

  • decision: “allow”, “deny”, “block” (o padrão é “allow” se não especificado)
  • reason: motivo da decisão
  • hookSpecificOutput.additionalContext: informações adicionais a serem incluídas

Exemplo de Saída:

{ "decision": "allow", "reason": "Tool executed successfully", "hookSpecificOutput": { "additionalContext": "File modification recorded in audit log" } }

PostToolUseFailure

Propósito: Executado quando a execução de uma ferramenta falha para lidar com erros, enviar alertas ou registrar falhas.

Campos específicos do evento:

{ "permission_mode": "default | plan | auto_edit | yolo", "tool_use_id": "unique identifier for the tool use (internal format, e.g., toolu_xxx)", "tool_call_id": "original API call ID from the LLM provider (e.g., call_xxx for OpenAI/Qwen) (optional)", "tool_name": "name of the tool that failed", "tool_input": "object containing the tool's input parameters", "error": "error message describing the failure", "is_interrupt": "boolean indicating if failure was due to user interruption (optional)" }

Opções de Saída:

  • hookSpecificOutput.additionalContext: informações de tratamento de erro
  • Campos padrão de saída do hook

Exemplo de Saída:

{ "hookSpecificOutput": { "additionalContext": "Error: File not found. Failure logged in monitoring system." } }

UserPromptSubmit

Propósito: Executado quando o usuário envia um prompt para modificar, validar ou enriquecer a entrada.

Campos específicos do evento:

{ "prompt": "the user's submitted prompt text" }

Opções de Saída:

  • decision: “allow”, “deny”, “block” ou “ask”
  • reason: explicação legível por humanos para a decisão
  • hookSpecificOutput.additionalContext: contexto adicional para anexar ao prompt (opcional)

Nota: Como UserPromptSubmitOutput estende HookOutput, todos os campos padrão estão disponíveis, mas apenas additionalContext em hookSpecificOutput é especificamente definido para este evento.

Exemplo de Saída:

{ "decision": "allow", "reason": "Prompt reviewed and approved", "hookSpecificOutput": { "additionalContext": "Remember to follow company coding standards." } }

SessionStart

Propósito: Executado quando uma nova sessão é iniciada para realizar tarefas de inicialização.

Campos específicos do evento:

{ "permission_mode": "default | plan | auto_edit | yolo", "source": "startup | resume | clear | compact", "model": "the model being used", "agent_type": "the type of agent if applicable (optional)" }

Opções de Saída:

  • hookSpecificOutput.additionalContext: contexto a ser disponibilizado na sessão
  • Campos padrão de saída do hook

Exemplo de Saída:

{ "hookSpecificOutput": { "additionalContext": "Session started with security policies enabled." } }

SessionEnd

Propósito: Executado quando uma sessão termina para realizar tarefas de limpeza.

Campos específicos do evento:

{ "reason": "clear | logout | prompt_input_exit | bypass_permissions_disabled | other" }

Opções de Saída:

  • Campos padrão de saída do hook (tipicamente não usados para bloqueio)

Stop

Propósito: Executado antes que o Qwen conclua sua resposta para fornecer feedback final ou resumos.

Campos específicos do evento:

{ "stop_hook_active": "boolean indicating if stop hook is active", "last_assistant_message": "the last message from the assistant", "context_usage": "ratio of context window used (may exceed 1 when tokens exceed window; optional)", "context_limit": "context window size in tokens (optional)", "input_tokens": "prompt token count (may include output tokens depending on provider; optional)" }

Os campos context_usage, context_limit e input_tokens permitem que scripts de hook observem o uso do contexto e implementem estratégias de compactação personalizadas — por exemplo, um script que imprime um lembrete para executar /compact quando o uso excede um limite personalizado.

Opções de Saída:

  • decision: “allow”, “deny”, “block” ou “ask”
  • reason: explicação legível por humanos para a decisão
  • stopReason: feedback a ser incluído na resposta de parada
  • continue: defina como false para parar a execução
  • hookSpecificOutput.additionalContext: informações de contexto adicionais

Nota: Como StopOutput estende HookOutput, todos os campos padrão estão disponíveis, mas o campo stopReason é particularmente relevante para este evento.

Exemplo de Saída:

{ "decision": "block", "reason": "Must be provided when Qwen Code is blocked from stopping" }

StopFailure

Propósito: Executado quando o turno termina devido a um erro de API (em vez de Stop). Este é um evento fire-and-forget - a saída do hook e os códigos de saída são ignorados.

Campos específicos do evento:

{ "error": "rate_limit | authentication_failed | billing_error | invalid_request | server_error | max_output_tokens | unknown", "error_details": "detailed error message (optional)", "last_assistant_message": "the last message from the assistant before the error (optional)" }

Matcher: Faz a correspondência com o campo error. Por exemplo, "matcher": "rate_limit" será acionado apenas para erros de limite de taxa.

Opções de Saída:

  • None - O StopFailure é fire-and-forget. Toda a saída do hook e os códigos de saída são ignorados.

Tratamento de Código de Saída:

Exit CodeBehavior
AnyIgnorado (fire-and-forget)

Configuração de Exemplo:

{ "hooks": { "StopFailure": [ { "matcher": "rate_limit", "hooks": [ { "type": "command", "command": "/path/to/rate-limit-alert.sh", "name": "rate-limit-alerter" } ] } ] } }

Casos de Uso:

  • Monitoramento e alerta de limite de taxa
  • Registro de falhas de autenticação
  • Notificações de erro de faturamento
  • Coleta de estatísticas de erros

SubagentStart

Propósito: Executado quando um subagente (como a ferramenta Task) é iniciado para configurar o contexto ou as permissões.

Campos específicos do evento:

{ "permission_mode": "default | plan | auto_edit | yolo", "agent_id": "identifier for the subagent", "agent_type": "type of agent (Bash, Explorer, Plan, Custom, etc.)" }

Opções de Saída:

  • hookSpecificOutput.additionalContext: contexto inicial para o subagente
  • Campos de saída padrão do hook

Exemplo de Saída:

{ "hookSpecificOutput": { "additionalContext": "Subagent initialized with restricted permissions." } }

SubagentStop

Propósito: Executado quando um subagente termina para realizar tarefas de finalização.

Campos específicos do evento:

{ "permission_mode": "default | plan | auto_edit | yolo", "stop_hook_active": "boolean indicating if stop hook is active", "agent_id": "identifier for the subagent", "agent_type": "type of agent", "agent_transcript_path": "path to the subagent's transcript", "last_assistant_message": "the last message from the subagent" }

Opções de Saída:

  • decision: “allow”, “deny”, “block” ou “ask”
  • reason: explicação legível por humanos para a decisão

Exemplo de Saída:

{ "decision": "block", "reason": "Must be provided when Qwen Code is blocked from stopping" }

PreCompact

Propósito: Executado antes da compactação da conversa para preparar ou registrar a compactação.

Campos específicos do evento:

{ "trigger": "manual | auto", "custom_instructions": "custom instructions currently set" }

Opções de Saída:

  • hookSpecificOutput.additionalContext: contexto a ser incluído antes da compactação
  • Campos de saída padrão do hook

Exemplo de Saída:

{ "hookSpecificOutput": { "additionalContext": "Compacting conversation to maintain optimal context window." } }

PostCompact

Propósito: Executado após a conclusão da compactação da conversa para arquivar resumos ou rastrear o uso.

Campos específicos do evento:

{ "trigger": "manual | auto", "compact_summary": "the summary generated by the compaction process" }

Matcher: Faz a correspondência com o campo trigger. Por exemplo, "matcher": "manual" será acionado apenas para compactação manual via comando /compact.

Opções de Saída:

  • hookSpecificOutput.additionalContext: contexto adicional (apenas para registro)
  • Campos de saída padrão do hook (apenas para registro)

Nota: O PostCompact não está na lista oficial de eventos suportados pelo modo de decisão. O campo decision e outros campos de controle não produzem efeitos de controle - eles são usados apenas para fins de registro.

Tratamento de Código de Saída:

Exit CodeBehavior
0Sucesso - stdout mostrado ao usuário no modo verbose
OtherErro não bloqueante - stderr mostrado ao usuário no modo verbose

Configuração de Exemplo:

{ "hooks": { "PostCompact": [ { "matcher": "manual", "hooks": [ { "type": "command", "command": "/path/to/save-compact-summary.sh", "name": "save-summary" } ] } ] } }

Casos de Uso:

  • Arquivamento de resumos em arquivos ou bancos de dados
  • Rastreamento de estatísticas de uso
  • Monitoramento de mudanças de contexto
  • Registro de auditoria para operações de compactação

Notification

Propósito: Executado quando notificações são enviadas para personalizá-las ou interceptá-las.

Campos específicos do evento:

{ "message": "notification message content", "title": "notification title (optional)", "notification_type": "permission_prompt | idle_prompt | auth_success" }

Nota: O tipo elicitation_dialog está definido, mas não está implementado atualmente.

Opções de Saída:

  • hookSpecificOutput.additionalContext: informações adicionais a serem incluídas
  • Campos de saída padrão do hook

Exemplo de Saída:

{ "hookSpecificOutput": { "additionalContext": "Notification processed by monitoring system." } }

PermissionRequest

Propósito: Executado quando as caixas de diálogo de permissão são exibidas para automatizar decisões ou atualizar permissões.

Campos específicos do evento:

{ "permission_mode": "default | plan | auto_edit | yolo", "tool_name": "name of the tool requesting permission", "tool_input": "object containing the tool's input parameters", "permission_suggestions": "array of suggested permissions (optional)" }

Opções de Saída:

  • hookSpecificOutput.decision: objeto estruturado com detalhes da decisão de permissão:
    • behavior: “allow” ou “deny”
    • updatedInput: entrada da ferramenta modificada (opcional)
    • updatedPermissions: permissões modificadas (opcional)
    • message: mensagem a ser exibida ao usuário (opcional)
    • interrupt: se deve interromper o fluxo de trabalho (opcional)

Exemplo de Saída:

{ "hookSpecificOutput": { "decision": { "behavior": "allow", "message": "Permission granted based on security policy", "interrupt": false } } }

TodoCreated

Propósito: Executado quando um novo item de todo é criado via ferramenta todo_write. Permite validação, registro ou bloqueio da criação do todo.

Os hooks de todo são executados em duas fases:

  • validation: executada antes da persistência. Use esta fase apenas para validação; retornar block ou deny impede a gravação.
  • postWrite: executada após a persistência. Use esta fase para efeitos colaterais, como registro ou sincronização; block ou deny é ignorado nesta fase.

Campos específicos do evento:

{ "todo_id": "unique identifier for the todo item", "todo_content": "content/description of the todo item", "todo_status": "pending | in_progress | completed", "all_todos": "array of all todo items in the current list", "phase": "validation | postWrite" }

Opções de Saída:

  • decision: “allow”, “block” ou “deny”
  • reason: explicação legível por humanos para a decisão (obrigatória ao bloquear)

Comportamento de Bloqueio:

Durante a fase de validation, quando decision é block ou deny (código de saída 2), a criação do todo é impedida. A lista de todos permanece inalterada e o motivo é fornecido como feedback para o modelo.

Durante a fase de postWrite, o todo já foi persistido. Os hooks ainda podem retornar saída, mas block / deny não desfaz a gravação e não deve ser usado para validação.

Exemplo de Saída (Allow):

{ "decision": "allow", "reason": "Todo content validated successfully" }

Exemplo de Saída (Block):

{ "decision": "block", "reason": "Todo content too short. Minimum 5 characters required." }

Exemplo de Script de Hook:

#!/bin/bash # ~/.qwen/hooks/todo-validator.sh # Validates todo content before creation INPUT=$(cat) CONTENT=$(echo "$INPUT" | jq -r '.todo_content') # Check minimum length if [ ${#CONTENT} -lt 5 ]; then echo '{"decision": "block", "reason": "Todo content must be at least 5 characters"}' exit 2 fi # Block test-related todos if [[ "$CONTENT" =~ "test" ]]; then echo '{"decision": "block", "reason": "Test todos are not allowed in production"}' exit 2 fi echo '{"decision": "allow"}' exit 0

Configuração de Exemplo:

{ "hooks": { "TodoCreated": [ { "hooks": [ { "type": "command", "command": "$HOME/.qwen/hooks/todo-validator.sh", "name": "todo-validator", "timeout": 5000 } ] } ] } }

TodoCompleted

Propósito: Executado quando um item de todo é marcado como concluído. Permite validação, registro ou bloqueio da conclusão do todo.

Os hooks de todo são executados em duas fases:

  • validation: executada antes da persistência. Use esta fase apenas para validação; retornar block ou deny impede a gravação.
  • postWrite: executada após a persistência. Use esta fase para efeitos colaterais, como registro ou sincronização; block ou deny é ignorado nesta fase.

Campos específicos do evento:

{ "todo_id": "unique identifier for the todo item", "todo_content": "content/description of the todo item", "previous_status": "pending | in_progress (status before completion)", "all_todos": "array of all todo items in the current list", "phase": "validation | postWrite" }

Opções de Saída:

  • decision: “allow”, “block” ou “deny”
  • reason: explicação legível por humanos para a decisão (obrigatória ao bloquear)

Comportamento de Bloqueio:

Durante a fase de validation, quando decision é block ou deny (código de saída 2), a conclusão do todo é impedida. O item de todo permanece em seu status anterior e o motivo é fornecido como feedback para o modelo.

Durante a fase de postWrite, o todo já foi persistido. Os hooks ainda podem retornar saída, mas block / deny não desfaz a gravação e não deve ser usado para validação.

Exemplo de Saída (Allow):

{ "decision": "allow", "reason": "Todo completion approved" }

Exemplo de Saída (Block):

{ "decision": "block", "reason": "Cannot complete this todo until dependent tasks are finished." }

Exemplo de Script de Hook:

#!/bin/bash # ~/.qwen/hooks/todo-completion-validator.sh # Validates todo completion conditions INPUT=$(cat) TODO_ID=$(echo "$INPUT" | jq -r '.todo_id') ALL_TODOS=$(echo "$INPUT" | jq -r '.all_todos') # Check if there are incomplete dependent todos (example logic) INCOMPLETE_COUNT=$(echo "$ALL_TODOS" | jq '[.[] | select(.status != "completed")] | length') if [ "$INCOMPLETE_COUNT" -gt 5 ]; then echo '{"decision": "block", "reason": "Too many incomplete todos. Complete other tasks first."}' exit 2 fi echo '{"decision": "allow"}' exit 0

Configuração de Exemplo:

{ "hooks": { "TodoCompleted": [ { "hooks": [ { "type": "command", "command": "$HOME/.qwen/hooks/todo-completion-validator.sh", "name": "completion-validator", "timeout": 5000 } ] } ] } }

Casos de Uso:

  • Registro: Rastreia a criação e conclusão de todos para auditoria ou análise
  • Validação: Aplica padrões de qualidade de conteúdo (tamanho mínimo, palavras-chave obrigatórias)
  • Controle de Fluxo de Trabalho: Bloqueia a conclusão até que os pré-requisitos sejam atendidos
  • Integração: Sincroniza todos com sistemas externos de gerenciamento de tarefas (Jira, Trello, etc.)

Configuração de Hook

Os hooks são configurados nas configurações do Qwen Code, geralmente em .qwen/settings.json ou arquivos de configuração do usuário:

{ "hooks": { "PreToolUse": [ { "matcher": "^Bash$", "sequential": false, "hooks": [ { "type": "command", "command": "/path/to/security-check.sh", "name": "security-check", "description": "Run security checks before tool execution", "timeout": 30000 } ] } ], "SessionStart": [ { "hooks": [ { "type": "command", "command": "echo 'Session started'", "name": "session-init" } ] } ] } }

Execução de Hook

Execução Paralela vs Sequencial

  • Por padrão, os hooks são executados em paralelo para um melhor desempenho
  • Use sequential: true na definição do hook para forçar a execução dependente de ordem
  • Hooks sequenciais podem modificar a entrada para os hooks subsequentes na cadeia

Hooks Assíncronos

Apenas o tipo command suporta execução assíncrona. Definir "async": true executa o hook em segundo plano sem bloquear o fluxo principal.

Recursos:

  • Não pode retornar o controle de decisão (a operação já ocorreu)
  • Os resultados são injetados no próximo turno da conversa via systemMessage ou additionalContext
  • Adequado para auditoria, logging, testes em segundo plano, etc.

Exemplo:

{ "hooks": { "PostToolUse": [ { "matcher": "WriteFile|Edit", "hooks": [ { "type": "command", "command": "$QWEN_PROJECT_DIR/.qwen/hooks/run-tests-async.sh", "async": true, "timeout": 300000 } ] } ] } }
#!/bin/bash INPUT=$(cat) FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty') if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then exit 0; fi RESULT=$(npm test 2>&1) if [ $? -eq 0 ]; then echo "{\"systemMessage\": \"Tests passed after editing $FILE_PATH\"}" else echo "{\"systemMessage\": \"Tests failed: $RESULT\"}" fi

Modelo de Segurança

  • Os hooks são executados no ambiente do usuário com os privilégios do usuário
  • Hooks no nível do projeto requerem status de pasta confiável
  • Timeouts previnem que hooks fiquem travados (padrão: 60 segundos)

Boas Práticas

Exemplo 1: Hook de Validação de Segurança

Um hook PreToolUse que registra em log e potencialmente bloqueia comandos perigosos:

security_check.sh

#!/bin/bash # Read input from stdin INPUT=$(cat) # Parse the input to extract tool info TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name') TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input') # Check for potentially dangerous operations if echo "$TOOL_INPUT" | grep -qiE "(rm.*-rf|mv.*\/|chmod.*777)"; then echo '{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Security policy blocks dangerous command" } }' exit 2 # Blocking error fi # Log the operation echo "INFO: Tool $TOOL_NAME executed safely at $(date)" >> /var/log/qwen-security.log # Allow with additional context echo '{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "allow", "permissionDecisionReason": "Security check passed", "additionalContext": "Command approved by security policy" } }' exit 0

Configure em .qwen/settings.json:

{ "hooks": { "PreToolUse": [ { "hooks": [ { "type": "command", "command": "${SECURITY_CHECK_SCRIPT}", "name": "security-checker", "description": "Security validation for bash commands", "timeout": 10000 } ] } ] } }

Exemplo 2: Hook de Auditoria HTTP

Um hook HTTP PostToolUse que envia todos os registros de execução de ferramentas para um serviço de auditoria remoto:

{ "hooks": { "PostToolUse": [ { "matcher": "*", "hooks": [ { "type": "http", "url": "https://audit.example.com/api/tool-execution", "headers": { "Authorization": "Bearer ${AUDIT_API_TOKEN}", "Content-Type": "application/json" }, "allowedEnvVars": ["AUDIT_API_TOKEN"], "timeout": 10, "name": "audit-logger" } ] } ] } }

Exemplo 3: Hook de Validação de Prompt do Usuário

Um hook UserPromptSubmit que valida prompts do usuário em busca de informações sensíveis e fornece contexto para prompts longos:

prompt_validator.py

import json import sys import re # Load input from stdin try: input_data = json.load(sys.stdin) except json.JSONDecodeError as e: print(f"Error: Invalid JSON input: {e}", file=sys.stderr) exit(1) user_prompt = input_data.get("prompt", "") # Sensitive words list sensitive_words = ["password", "secret", "token", "api_key"] # Check for sensitive information for word in sensitive_words: if re.search(rf"\b{word}\b", user_prompt.lower()): # Block prompts containing sensitive information output = { "decision": "block", "reason": f"Prompt contains sensitive information '{word}'. Please remove sensitive content and resubmit.", "hookSpecificOutput": { "hookEventName": "UserPromptSubmit" } } print(json.dumps(output)) exit(0) # Check prompt length and add warning context if too long if len(user_prompt) > 1000: output = { "hookSpecificOutput": { "hookEventName": "UserPromptSubmit", "additionalContext": "Note: User submitted a long prompt. Please read carefully and ensure all requirements are understood." } } print(json.dumps(output)) exit(0) # No processing needed for normal cases exit(0)

Solução de Problemas

  • Verifique os logs da aplicação para detalhes da execução do hook
  • Verifique as permissões e a capacidade de execução do script do hook
  • Certifique-se de que a formatação JSON nas saídas do hook esteja correta
  • Use padrões de matcher específicos para evitar a execução não intencional de hooks
  • Use o modo --debug para ver informações detalhadas de correspondência e execução de hooks
  • Desative temporariamente todos os hooks: adicione "disableAllHooks": true nas configurações
Last updated on