이 참조 문서에서는 입력 및 출력 형식, 스크립트 모범 사례 및 로깅, 보안 적용 및 외부 통합에 대한 고급 패턴을 포함하여 예제와 함께 사용 가능한 후크 형식에 대해 설명합니다. 후크를 만드는 방법에 대한 일반적인 내용은 GitHub Copilot 에이전트와 후크 활용을 참조하세요. CLI에 대한 후크를 만드는 방법에 대한 자습서는 예측 가능한 정책 준수를 위해 Copilot CLI와 연결고리 사용을 참조하세요.
후크 형식
세션 시작 후크
새 에이전트 세션이 시작되거나 기존 세션을 다시 시작할 때 실행됩니다.
**입력 JSON:**
{
"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"
}
**필드:**
*
timestamp: Unix 타임스탬프(밀리초)
*
cwd: 현재 작업 디렉터리
*
source
"new" : (새 세션), "resume" (다시 시작된 세션) 또는"startup"
*
initialPrompt: 사용자의 초기 프롬프트(제공된 경우)
**출력:** 무시됨(반환 값이 처리되지 않음)
**후크 예:**
{
"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
}
**예제 스크립트(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
세션 끝 후크
에이전트 세션이 완료되거나 종료될 때 실행됩니다.
**입력 JSON:**
{
"timestamp": 1704618000000,
"cwd": "/path/to/project",
"reason": "complete"
}
{
"timestamp": 1704618000000,
"cwd": "/path/to/project",
"reason": "complete"
}
**필드:**
*
timestamp: Unix 타임스탬프(밀리초)
*
cwd: 현재 작업 디렉터리
*
reason: 중 "complete"하나, "error", "abort", "timeout"또는 "user_exit"
**출력:** 무시
**스크립트 예**
#!/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-*
사용자 프롬프트 제출 후크
사용자가 에이전트에 프롬프트를 제출할 때 실행됩니다.
**입력 JSON:**
{
"timestamp": 1704614500000,
"cwd": "/path/to/project",
"prompt": "Fix the authentication bug"
}
{
"timestamp": 1704614500000,
"cwd": "/path/to/project",
"prompt": "Fix the authentication bug"
}
**필드:**
*
timestamp: Unix 타임스탬프(밀리초)
*
cwd: 현재 작업 디렉터리
*
prompt: 사용자가 제출한 정확한 텍스트
**출력:** 무시됨(고객 후크에서 현재 지원되지 않는 프롬프트 수정)
**스크립트 예**
#!/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
사전 도구 사용 후크
에이전트가 모든 도구(예: bash, edit``view)를 사용하기 전에 실행됩니다. 도구 실행을 승인하거나 거부할 수 있는 가장 강력한 후크입니다.
**입력 JSON:**
{
"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\"}"
}
**필드:**
*
timestamp: Unix 타임스탬프(밀리초)
*
cwd: 현재 작업 디렉터리
*
toolName: 호출되는 도구의 이름(예: "bash", "edit", "view", "create")
*
toolArgs: 도구의 인수를 포함하는 JSON 문자열
**출력 JSON(선택 사항):**
{
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive operations require approval"
}
{
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive operations require approval"
}
**출력 필드:**
*
permissionDecision: "allow", "deny", 또는 "ask" (현재는 "deny"만 처리됨)
*
permissionDecisionReason: 결정에 대한 사람이 읽을 수 있는 설명
**위험한 명령을 차단하는 후크 예제:**
#!/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"}'
**파일 권한을 적용하는 후크 예제:**
#!/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
사후 도구 사용 후크
도구가 실행을 완료한 후 실행됩니다(성공 여부 또는 실패 여부).
**입력 JSON 예제:**
{
"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)"
}
}
**필드:**
*
timestamp: Unix 타임스탬프(밀리초)
*
cwd: 현재 작업 디렉터리
*
toolName: 실행된 도구의 이름
*
toolArgs: 도구의 인수를 포함하는 JSON 문자열
*
toolResult: 다음을 포함하는 결과 개체:
*
resultType: , "success"``"failure"또는"denied"
*
textResultForLlm: 에이전트에 표시되는 결과 텍스트
**출력:** 무시됨(결과 수정은 현재 지원되지 않음)
**CSV 파일에 도구 실행 통계를 기록하는 예제 스크립트:**
이 스크립트는 도구 실행 통계를 CSV 파일에 기록하고 도구가 실패하면 이메일 경고를 보냅니다.
#!/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
오류 발생 후크
에이전트를 실행하는 동안 오류가 발생할 때 실행됩니다.
**입력 JSON 예제:**
{
"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 ..."
}
}
**필드:**
*
timestamp: Unix 타임스탬프(밀리초)
*
cwd: 현재 작업 디렉터리
*
error: 다음을 포함하는 오류 개체입니다.
*
message: 오류 메시지
*
name: 오류 유형/이름
*
stack: 스택 추적(사용 가능한 경우)
**출력:** 무시됨(오류 처리 수정은 현재 지원되지 않음)
**로그 파일에 오류 세부 정보를 추출하는 예제 스크립트:**
#!/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
스크립트 모범 사례
읽기 입력
이 예제 스크립트는 표준 입력(stdin)에서 JSON 입력을 변수에 읽은 다음, jq를 사용하여 timestamp 및 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
JSON 출력
이 예제 스크립트는 후크 스크립트에서 유효한 JSON을 출력하는 방법을 보여 줍니다. Bash에서는 jq -c를 사용하여 압축된 단일 행 출력을 생성하거나 PowerShell에서는 ConvertTo-Json -Compress을/를 사용합니다.
**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
오류 처리
이 스크립트 예제에서는 후크 스크립트에서 오류를 처리하는 방법을 보여 줍니다.
**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
}
타임아웃 처리
후크의 기본 시간 제한은 30초입니다. 긴 작업을 위해 timeoutSec을(를) 증가시킵니다.
{
"type": "command",
"bash": "./scripts/slow-validation.sh",
"timeoutSec": 120
}
{
"type": "command",
"bash": "./scripts/slow-validation.sh",
"timeoutSec": 120
}
고급 패턴
동일한 유형의 여러 후크
동일한 이벤트에 대해 여러 후크를 정의할 수 있습니다. 순서대로 실행됩니다.
{
"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"
}
]
}
}
스크립트의 조건부 논리
**예: 특정 도구만 차단**
#!/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
구조적 로깅
**예: 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
외부 시스템과 통합
**예: 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\"}"
사용 사례 예
준수 감사 내역
로그 스크립트를 활용하여 규정 준수 요구 사항에 대한 모든 에이전트 작업을 기록합니다.
{
"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"}]
}
}
비용 추적
비용 할당에 대한 도구 사용 현황 추적:
#!/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
코드 품질 적용
코드 표준을 위반하는 커밋 방지:
#!/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
알림 시스템
중요한 이벤트에 대한 알림 보내기:
#!/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