Este artigo de referência descreve os tipos de gancho disponíveis com exemplos, incluindo seus formatos de entrada e saída, práticas recomendadas de script e padrões avançados para registro em log, imposição de segurança e integrações externas. Para obter informações gerais sobre como criar ganchos, consulte Uso de ganchos com agentes do GitHub Copilot. Para obter um tutorial sobre como criar ganchos para a CLI, consulte Usando ganchos com a CLI do Copilot para execução previsível e compatível com políticas.
Tipos de gancho
Gancho de início da sessão
Executado quando uma nova sessão de agente começa ou ao retomar uma sessão existente.
**JSON de entrada:**
{
"timestamp": 1704614400000,
"cwd": "/path/to/project",
"source": "new",
"initialPrompt": "Create a new feature"
}
{
"timestamp": 1704614400000,
"cwd": "/path/to/project",
"source": "new",
"initialPrompt": "Create a new feature"
}
**Campos:**
*
timestamp: carimbo de data/hora unix em milissegundos
*
cwd: diretório de trabalho atual
*
source
"new" : (nova sessão), "resume" (sessão retomada) ou"startup"
*
initialPrompt: o prompt inicial do usuário (se fornecido)
**Saída:** Ignorado (sem valor de retorno sendo processado)
**Gancho de exemplo:**
{
"type": "command",
"bash": "./scripts/session-start.sh",
"powershell": "./scripts/session-start.ps1",
"cwd": "scripts",
"timeoutSec": 30
}
{
"type": "command",
"bash": "./scripts/session-start.sh",
"powershell": "./scripts/session-start.ps1",
"cwd": "scripts",
"timeoutSec": 30
}
**Script de exemplo (Bash):**
#!/bin/bash INPUT=$(cat) SOURCE=$(echo "$INPUT" | jq -r '.source') TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp') echo "Session started from $SOURCE at $TIMESTAMP" >> session.log
#!/bin/bash
INPUT=$(cat)
SOURCE=$(echo "$INPUT" | jq -r '.source')
TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp')
echo "Session started from $SOURCE at $TIMESTAMP" >> session.log
Gancho de fim de sessão
Executado quando a sessão do agente é concluída ou encerrada.
**JSON de entrada:**
{
"timestamp": 1704618000000,
"cwd": "/path/to/project",
"reason": "complete"
}
{
"timestamp": 1704618000000,
"cwd": "/path/to/project",
"reason": "complete"
}
**Campos:**
*
timestamp: carimbo de data/hora unix em milissegundos
*
cwd: diretório de trabalho atual
*
reason: um de "complete", "error", "abort", "timeout"ou "user_exit"
**Saída:** Ignorado
**Exemplo de script:**
#!/bin/bash INPUT=$(cat) REASON=$(echo "$INPUT" | jq -r '.reason') echo "Session ended: $REASON" >> session.log # Cleanup temporary files rm -rf /tmp/session-*
#!/bin/bash
INPUT=$(cat)
REASON=$(echo "$INPUT" | jq -r '.reason')
echo "Session ended: $REASON" >> session.log
# Cleanup temporary files
rm -rf /tmp/session-*
Gancho enviado pelo prompt do usuário
Executado quando o usuário envia um prompt para o agente.
**JSON de entrada:**
{
"timestamp": 1704614500000,
"cwd": "/path/to/project",
"prompt": "Fix the authentication bug"
}
{
"timestamp": 1704614500000,
"cwd": "/path/to/project",
"prompt": "Fix the authentication bug"
}
**Campos:**
*
timestamp: carimbo de data/hora unix em milissegundos
*
cwd: diretório de trabalho atual
*
prompt: o texto exato enviado pelo usuário
**Saída:** Ignorado (a modificação de prompts não recebe suporte atualmente em ganchos de cliente)
**Exemplo de script:**
#!/bin/bash INPUT=$(cat) PROMPT=$(echo "$INPUT" | jq -r '.prompt') TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp') # Log to a structured file echo "$(date -d @$((TIMESTAMP/1000))): $PROMPT" >> prompts.log
#!/bin/bash
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt')
TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp')
# Log to a structured file
echo "$(date -d @$((TIMESTAMP/1000))): $PROMPT" >> prompts.log
Gancho de uso de pré-ferramenta
Executado antes que o agente use qualquer ferramenta (como bash, , edit). view Esse é o gancho mais poderoso, pois pode aprovar ou negar execuções de ferramentas.
**JSON de entrada:**
{
"timestamp": 1704614600000,
"cwd": "/path/to/project",
"toolName": "bash",
"toolArgs": "{\"command\":\"rm -rf dist\",\"description\":\"Clean build directory\"}"
}
{
"timestamp": 1704614600000,
"cwd": "/path/to/project",
"toolName": "bash",
"toolArgs": "{\"command\":\"rm -rf dist\",\"description\":\"Clean build directory\"}"
}
**Campos:**
*
timestamp: carimbo de data/hora unix em milissegundos
*
cwd: diretório de trabalho atual
*
toolName: nome da ferramenta que está sendo invocada (como "bash", "edit", "view", "create")
*
toolArgs: cadeia de caracteres JSON que contém os argumentos da ferramenta
**JSON de saída (opcional):**
{
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive operations require approval"
}
{
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive operations require approval"
}
**Campos de saída:**
*
permissionDecision: ou "allow", "deny" ou "ask" (somente "deny" é processado no momento)
*
permissionDecisionReason: explicação compreensível por humanos para a decisão
**Gancho de exemplo para bloquear comandos perigosos:**
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
TOOL_ARGS=$(echo "$INPUT" | jq -r '.toolArgs')
# Log the tool use
echo "$(date): Tool=$TOOL_NAME Args=$TOOL_ARGS" >> tool-usage.log
# Check for dangerous patterns
if echo "$TOOL_ARGS" | grep -qE "rm -rf /|format|DROP TABLE"; then
echo '{"permissionDecision":"deny","permissionDecisionReason":"Dangerous command detected"}'
exit 0
fi
# Allow by default (or omit output to allow)
echo '{"permissionDecision":"allow"}'
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
TOOL_ARGS=$(echo "$INPUT" | jq -r '.toolArgs')
# Log the tool use
echo "$(date): Tool=$TOOL_NAME Args=$TOOL_ARGS" >> tool-usage.log
# Check for dangerous patterns
if echo "$TOOL_ARGS" | grep -qE "rm -rf /|format|DROP TABLE"; then
echo '{"permissionDecision":"deny","permissionDecisionReason":"Dangerous command detected"}'
exit 0
fi
# Allow by default (or omit output to allow)
echo '{"permissionDecision":"allow"}'
**Gancho de exemplo para impor permissões de arquivo:**
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
# Only allow editing specific directories
if [ "$TOOL_NAME" = "edit" ]; then
PATH_ARG=$(echo "$INPUT" | jq -r '.toolArgs' | jq -r '.path')
if [[ ! "$PATH_ARG" =~ ^(src/|test/) ]]; then
echo '{"permissionDecision":"deny","permissionDecisionReason":"Can only edit files in src/ or test/ directories"}'
exit 0
fi
fi
# Allow all other tools
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
# Only allow editing specific directories
if [ "$TOOL_NAME" = "edit" ]; then
PATH_ARG=$(echo "$INPUT" | jq -r '.toolArgs' | jq -r '.path')
if [[ ! "$PATH_ARG" =~ ^(src/|test/) ]]; then
echo '{"permissionDecision":"deny","permissionDecisionReason":"Can only edit files in src/ or test/ directories"}'
exit 0
fi
fi
# Allow all other tools
Gancho de uso pós-ferramenta
Executado depois que uma ferramenta conclui a execução (com êxito ou falha).
**Exemplo de JSON de entrada:**
{
"timestamp": 1704614700000,
"cwd": "/path/to/project",
"toolName": "bash",
"toolArgs": "{\"command\":\"npm test\"}",
"toolResult": {
"resultType": "success",
"textResultForLlm": "All tests passed (15/15)"
}
}
{
"timestamp": 1704614700000,
"cwd": "/path/to/project",
"toolName": "bash",
"toolArgs": "{\"command\":\"npm test\"}",
"toolResult": {
"resultType": "success",
"textResultForLlm": "All tests passed (15/15)"
}
}
**Campos:**
*
timestamp: carimbo de data/hora unix em milissegundos
*
cwd: diretório de trabalho atual
*
toolName: nome da ferramenta que foi executada
*
toolArgs: cadeia de caracteres JSON que contém os argumentos da ferramenta
*
toolResult: objeto de resultado que contém:
*
resultType: "success", "failure" ou "denied"
*
textResultForLlm: o texto do resultado mostrado ao agente
**Saída:** Ignorado (a modificação de resultado não tem suporte no momento)
**Script de exemplo que registra estatísticas de execução da ferramenta em um arquivo CSV:**
Esse script registra as estatísticas de execução da ferramenta em um arquivo CSV e envia um alerta de email quando uma ferramenta falha.
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
RESULT_TYPE=$(echo "$INPUT" | jq -r '.toolResult.resultType')
# Track statistics
echo "$(date),${TOOL_NAME},${RESULT_TYPE}" >> tool-stats.csv
# Alert on failures
if [ "$RESULT_TYPE" = "failure" ]; then
RESULT_TEXT=$(echo "$INPUT" | jq -r '.toolResult.textResultForLlm')
echo "FAILURE: $TOOL_NAME - $RESULT_TEXT" | mail -s "Agent Tool Failed" [email protected]
fi
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
RESULT_TYPE=$(echo "$INPUT" | jq -r '.toolResult.resultType')
# Track statistics
echo "$(date),${TOOL_NAME},${RESULT_TYPE}" >> tool-stats.csv
# Alert on failures
if [ "$RESULT_TYPE" = "failure" ]; then
RESULT_TEXT=$(echo "$INPUT" | jq -r '.toolResult.textResultForLlm')
echo "FAILURE: $TOOL_NAME - $RESULT_TEXT" | mail -s "Agent Tool Failed" [email protected]
fi
Ocorreu um erro no gancho
Executado quando ocorre um erro durante a execução do agente.
**Exemplo de JSON de entrada:**
{
"timestamp": 1704614800000,
"cwd": "/path/to/project",
"error": {
"message": "Network timeout",
"name": "TimeoutError",
"stack": "TimeoutError: Network timeout\n at ..."
}
}
{
"timestamp": 1704614800000,
"cwd": "/path/to/project",
"error": {
"message": "Network timeout",
"name": "TimeoutError",
"stack": "TimeoutError: Network timeout\n at ..."
}
}
**Campos:**
*
timestamp: carimbo de data/hora unix em milissegundos
*
cwd: diretório de trabalho atual
*
error: objeto de erro que contém:
*
message: mensagem de erro
*
name: tipo/nome de erro
*
stack: rastreamento de pilha (se disponível)
**Saída:** Ignorado (não há suporte para modificação de tratamento de erros no momento)
**Script de exemplo que extrai detalhes de erro para um arquivo de log:**
#!/bin/bash INPUT=$(cat) ERROR_MSG=$(echo "$INPUT" | jq -r '.error.message') ERROR_NAME=$(echo "$INPUT" | jq -r '.error.name') echo "$(date): [$ERROR_NAME] $ERROR_MSG" >> errors.log
#!/bin/bash
INPUT=$(cat)
ERROR_MSG=$(echo "$INPUT" | jq -r '.error.message')
ERROR_NAME=$(echo "$INPUT" | jq -r '.error.name')
echo "$(date): [$ERROR_NAME] $ERROR_MSG" >> errors.log
Práticas recomendadas de script
Leitura de dados de entrada
Este script de exemplo lê a entrada JSON de stdin em uma variável e, em seguida, usa jq para extrair os campos timestamp e cwd.
**Bash:**
#!/bin/bash # Read JSON from stdin INPUT=$(cat) # Parse with jq TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp') CWD=$(echo "$INPUT" | jq -r '.cwd')
#!/bin/bash
# Read JSON from stdin
INPUT=$(cat)
# Parse with jq
TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp')
CWD=$(echo "$INPUT" | jq -r '.cwd')
**PowerShell:**
# Read JSON from stdin $input = [Console]::In.ReadToEnd() | ConvertFrom-Json # Access properties $timestamp = $input.timestamp $cwd = $input.cwd
# Read JSON from stdin
$input = [Console]::In.ReadToEnd() | ConvertFrom-Json
# Access properties
$timestamp = $input.timestamp
$cwd = $input.cwd
Geração de JSON
Este script de exemplo mostra como produzir JSON válido a partir de seu script de gancho. Use jq -c no Bash para saída de linha única compacta ou ConvertTo-Json -Compress no PowerShell.
**Bash:**
#!/bin/bash
# Use jq to compact the JSON output to a single line
echo '{"permissionDecision":"deny","permissionDecisionReason":"Security policy violation"}' | jq -c
# Or construct with variables
REASON="Too dangerous"
jq -n --arg reason "$REASON" '{permissionDecision: "deny", permissionDecisionReason: $reason}'
#!/bin/bash
# Use jq to compact the JSON output to a single line
echo '{"permissionDecision":"deny","permissionDecisionReason":"Security policy violation"}' | jq -c
# Or construct with variables
REASON="Too dangerous"
jq -n --arg reason "$REASON" '{permissionDecision: "deny", permissionDecisionReason: $reason}'
**PowerShell:**
# Use ConvertTo-Json to compact the JSON output to a single line
$output = @{
permissionDecision = "deny"
permissionDecisionReason = "Security policy violation"
}
$output | ConvertTo-Json -Compress
# Use ConvertTo-Json to compact the JSON output to a single line
$output = @{
permissionDecision = "deny"
permissionDecisionReason = "Security policy violation"
}
$output | ConvertTo-Json -Compress
Tratamento de erros
Este exemplo de script demonstra como lidar com erros em scripts de gancho.
**Bash:**
#!/bin/bash set -e # Exit on error INPUT=$(cat) # ... process input ... # Exit with 0 for success exit 0
#!/bin/bash
set -e # Exit on error
INPUT=$(cat)
# ... process input ...
# Exit with 0 for success
exit 0
**PowerShell:**
$ErrorActionPreference = "Stop"
try {
$input = [Console]::In.ReadToEnd() | ConvertFrom-Json
# ... process input ...
exit 0
} catch {
Write-Error $_.Exception.Message
exit 1
}
$ErrorActionPreference = "Stop"
try {
$input = [Console]::In.ReadToEnd() | ConvertFrom-Json
# ... process input ...
exit 0
} catch {
Write-Error $_.Exception.Message
exit 1
}
Tratamento de tempos limite
Os ganchos têm um tempo limite padrão de 30 segundos. Para operações mais longas, aumente timeoutSec:
{
"type": "command",
"bash": "./scripts/slow-validation.sh",
"timeoutSec": 120
}
{
"type": "command",
"bash": "./scripts/slow-validation.sh",
"timeoutSec": 120
}
Padrões avançados
Vários ganchos do mesmo tipo
Você pode definir múltiplos hooks para o mesmo evento. Eles executam na ordem:
{
"version": 1,
"hooks": {
"preToolUse": [
{
"type": "command",
"bash": "./scripts/security-check.sh",
"comment": "Security validation - runs first"
},
{
"type": "command",
"bash": "./scripts/audit-log.sh",
"comment": "Audit logging - runs second"
},
{
"type": "command",
"bash": "./scripts/metrics.sh",
"comment": "Metrics collection - runs third"
}
]
}
}
{
"version": 1,
"hooks": {
"preToolUse": [
{
"type": "command",
"bash": "./scripts/security-check.sh",
"comment": "Security validation - runs first"
},
{
"type": "command",
"bash": "./scripts/audit-log.sh",
"comment": "Audit logging - runs second"
},
{
"type": "command",
"bash": "./scripts/metrics.sh",
"comment": "Metrics collection - runs third"
}
]
}
}
Lógica condicional em scripts
**Exemplo: bloquear somente ferramentas específicas**
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
# Only validate bash commands
if [ "$TOOL_NAME" != "bash" ]; then
exit 0 # Allow all non-bash tools
fi
# Check bash command for dangerous patterns
COMMAND=$(echo "$INPUT" | jq -r '.toolArgs' | jq -r '.command')
if echo "$COMMAND" | grep -qE "rm -rf|sudo|mkfs"; then
echo '{"permissionDecision":"deny","permissionDecisionReason":"Dangerous system command"}'
fi
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
# Only validate bash commands
if [ "$TOOL_NAME" != "bash" ]; then
exit 0 # Allow all non-bash tools
fi
# Check bash command for dangerous patterns
COMMAND=$(echo "$INPUT" | jq -r '.toolArgs' | jq -r '.command')
if echo "$COMMAND" | grep -qE "rm -rf|sudo|mkfs"; then
echo '{"permissionDecision":"deny","permissionDecisionReason":"Dangerous system command"}'
fi
Registro em log estruturado
**Exemplo: formato de linhas JSON**
#!/bin/bash
INPUT=$(cat)
TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp')
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
RESULT_TYPE=$(echo "$INPUT" | jq -r '.toolResult.resultType')
# Output structured log entry
jq -n \
--arg ts "$TIMESTAMP" \
--arg tool "$TOOL_NAME" \
--arg result "$RESULT_TYPE" \
'{timestamp: $ts, tool: $tool, result: $result}' >> logs/audit.jsonl
#!/bin/bash
INPUT=$(cat)
TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp')
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
RESULT_TYPE=$(echo "$INPUT" | jq -r '.toolResult.resultType')
# Output structured log entry
jq -n \
--arg ts "$TIMESTAMP" \
--arg tool "$TOOL_NAME" \
--arg result "$RESULT_TYPE" \
'{timestamp: $ts, tool: $tool, result: $result}' >> logs/audit.jsonl
Integração com sistemas externos
**Exemplo: enviar alertas para o Slack**
#!/bin/bash
INPUT=$(cat)
ERROR_MSG=$(echo "$INPUT" | jq -r '.error.message')
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
curl -X POST "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"Agent Error: $ERROR_MSG\"}"
#!/bin/bash
INPUT=$(cat)
ERROR_MSG=$(echo "$INPUT" | jq -r '.error.message')
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
curl -X POST "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"Agent Error: $ERROR_MSG\"}"
Exemplos de casos de uso
Trilha de auditoria de conformidade
Registre todas as ações do agente para requisitos de conformidade utilizando scripts de log:
{
"version": 1,
"hooks": {
"sessionStart": [{"type": "command", "bash": "./audit/log-session-start.sh"}],
"userPromptSubmitted": [{"type": "command", "bash": "./audit/log-prompt.sh"}],
"preToolUse": [{"type": "command", "bash": "./audit/log-tool-use.sh"}],
"postToolUse": [{"type": "command", "bash": "./audit/log-tool-result.sh"}],
"sessionEnd": [{"type": "command", "bash": "./audit/log-session-end.sh"}]
}
}
{
"version": 1,
"hooks": {
"sessionStart": [{"type": "command", "bash": "./audit/log-session-start.sh"}],
"userPromptSubmitted": [{"type": "command", "bash": "./audit/log-prompt.sh"}],
"preToolUse": [{"type": "command", "bash": "./audit/log-tool-use.sh"}],
"postToolUse": [{"type": "command", "bash": "./audit/log-tool-result.sh"}],
"sessionEnd": [{"type": "command", "bash": "./audit/log-session-end.sh"}]
}
}
Rastreamento do custo
Acompanhe o uso da ferramenta para alocação de custos:
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp')
USER=${USER:-unknown}
echo "$TIMESTAMP,$USER,$TOOL_NAME" >> /var/log/copilot/usage.csv
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
TIMESTAMP=$(echo "$INPUT" | jq -r '.timestamp')
USER=${USER:-unknown}
echo "$TIMESTAMP,$USER,$TOOL_NAME" >> /var/log/copilot/usage.csv
Garantia de qualidade do código
Impedir commits que violam os padrões de código:
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
if [ "$TOOL_NAME" = "edit" ] || [ "$TOOL_NAME" = "create" ]; then
# Run linter before allowing edits
npm run lint-staged
if [ $? -ne 0 ]; then
echo '{"permissionDecision":"deny","permissionDecisionReason":"Code does not pass linting"}'
fi
fi
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName')
if [ "$TOOL_NAME" = "edit" ] || [ "$TOOL_NAME" = "create" ]; then
# Run linter before allowing edits
npm run lint-staged
if [ $? -ne 0 ]; then
echo '{"permissionDecision":"deny","permissionDecisionReason":"Code does not pass linting"}'
fi
fi
Sistema de notificação
Enviar notificações sobre eventos importantes:
#!/bin/bash INPUT=$(cat) PROMPT=$(echo "$INPUT" | jq -r '.prompt') # Notify on production-related prompts if echo "$PROMPT" | grep -iq "production"; then echo "ALERT: Production-related prompt: $PROMPT" | mail -s "Agent Alert" [email protected] fi
#!/bin/bash
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt')
# Notify on production-related prompts
if echo "$PROMPT" | grep -iq "production"; then
echo "ALERT: Production-related prompt: $PROMPT" | mail -s "Agent Alert" [email protected]
fi