3-3. Hook 이벤트
SessionStart·PreToolUse·Stop·ConfigChange 등 전체 hook 이벤트 레퍼런스
Hook 이벤트
각 이벤트는 Claude Code의 생명주기에서 Hook이 실행될 수 있는 시점에 해당합니다. 아래 섹션은 생명주기 순서대로 정렬되어 있으며, 세션 설정부터 에이전트 루프를 거쳐 세션 종료까지를 다룹니다. 각 섹션에서는 이벤트가 발생하는 시점, 지원하는 matcher, 수신하는 JSON 입력, 그리고 출력을 통한 동작 제어 방법을 설명합니다.
SessionStart
Claude Code가 새 세션을 시작하거나 기존 세션을 재개할 때 실행됩니다. 기존 이슈나 코드베이스의 최근 변경 사항과 같은 개발 컨텍스트를 로드하거나 환경 변수를 설정하는 데 유용합니다. 스크립트가 필요 없는 정적 컨텍스트의 경우 CLAUDE.md를 대신 사용하세요.
SessionStart는 모든 세션에서 실행되므로 이 Hook은 빠르게 유지하세요. type: "command" Hook만 지원됩니다.
matcher 값은 세션이 시작된 방식에 해당합니다:
| Matcher | 발생 시점 |
|---|---|
startup | 새 세션 |
resume | --resume, --continue, 또는 /resume |
clear | /clear |
compact | 자동 또는 수동 압축 |
SessionStart 입력
공통 입력 필드 외에, SessionStart Hook은 source, model, 그리고 선택적으로 agent_type을 수신합니다. source 필드는 세션이 시작된 방식을 나타냅니다: 새 세션의 경우 "startup", 재개된 세션의 경우 "resume", /clear 이후의 경우 "clear", 압축 이후의 경우 "compact"입니다. model 필드는 모델 식별자를 포함합니다. claude --agent <name>으로 Claude Code를 시작하면 agent_type 필드에 에이전트 이름이 포함됩니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "SessionStart",
"source": "startup",
"model": "claude-sonnet-4-6"
}
SessionStart 결정 제어
Hook 스크립트가 stdout에 출력하는 모든 텍스트는 Claude의 컨텍스트로 추가됩니다. 모든 Hook에서 사용 가능한 JSON 출력 필드 외에, 다음과 같은 이벤트별 필드를 반환할 수 있습니다:
| 필드 | 설명 |
|---|---|
additionalContext | Claude의 컨텍스트에 추가되는 문자열. 여러 Hook의 값이 연결됩니다 |
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "My additional context here"
}
}
환경 변수 유지
SessionStart Hook은 CLAUDE_ENV_FILE 환경 변수에 접근할 수 있으며, 이는 이후 Bash 명령에서 사용할 환경 변수를 유지할 수 있는 파일 경로를 제공합니다.
개별 환경 변수를 설정하려면 CLAUDE_ENV_FILE에 export 문을 작성하세요. 다른 Hook에서 설정한 변수를 보존하려면 추가 모드(>>)를 사용하세요:
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi
exit 0
설정 명령으로부터 모든 환경 변경 사항을 캡처하려면, 전후로 내보낸 변수를 비교하세요:
#!/bin/bash
ENV_BEFORE=$(export -p | sort)
# Run your setup commands that modify the environment
source ~/.nvm/nvm.sh
nvm use 20
if [ -n "$CLAUDE_ENV_FILE" ]; then
ENV_AFTER=$(export -p | sort)
comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi
exit 0
이 파일에 기록된 모든 변수는 세션 동안 Claude Code가 실행하는 모든 후속 Bash 명령에서 사용할 수 있습니다.
참고:
CLAUDE_ENV_FILE은 SessionStart Hook에서 사용할 수 있습니다. 다른 Hook 유형에서는 이 변수에 접근할 수 없습니다.
InstructionsLoaded
CLAUDE.md 또는 .claude/rules/*.md 파일이 컨텍스트에 로드될 때 발생합니다. 이 이벤트는 세션 시작 시 즉시 로드되는 파일에 대해 발생하고, 이후 파일이 지연 로드될 때 다시 발생합니다. 예를 들어 Claude가 중첩된 CLAUDE.md를 포함하는 하위 디렉토리에 접근하거나 paths: 프론트매터가 있는 조건부 규칙이 일치할 때입니다. 이 Hook은 차단이나 결정 제어를 지원하지 않습니다. 관찰 목적으로 비동기적으로 실행됩니다.
InstructionsLoaded는 matcher를 지원하지 않으며 모든 로드 발생 시 실행됩니다.
InstructionsLoaded 입력
공통 입력 필드 외에, InstructionsLoaded Hook은 다음 필드를 수신합니다:
| 필드 | 설명 |
|---|---|
file_path | 로드된 지침 파일의 절대 경로 |
memory_type | 파일의 범위: "User", "Project", "Local", 또는 "Managed" |
load_reason | 파일이 로드된 이유: "session_start", "nested_traversal", "path_glob_match", 또는 "include" |
globs | 파일의 paths: 프론트매터에 있는 경로 glob 패턴 (있는 경우). path_glob_match 로드 시에만 존재 |
trigger_file_path | 이 로드를 트리거한 파일의 경로 (지연 로드의 경우) |
parent_file_path | 이 파일을 포함한 상위 지침 파일의 경로 (include 로드의 경우) |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"permission_mode": "default",
"hook_event_name": "InstructionsLoaded",
"file_path": "/Users/my-project/CLAUDE.md",
"memory_type": "Project",
"load_reason": "session_start"
}
InstructionsLoaded 결정 제어
InstructionsLoaded Hook에는 결정 제어가 없습니다. 지침 로드를 차단하거나 수정할 수 없습니다. 이 이벤트는 감사 로깅, 컴플라이언스 추적, 또는 관찰 목적으로 사용하세요.
UserPromptSubmit
사용자가 프롬프트를 제출할 때, Claude가 처리하기 전에 실행됩니다. 이를 통해
프롬프트/대화를 기반으로 추가 컨텍스트를 추가하거나, 프롬프트를 검증하거나,
특정 유형의 프롬프트를 차단할 수 있습니다.
UserPromptSubmit 입력
공통 입력 필드 외에, UserPromptSubmit Hook은 사용자가 제출한 텍스트가 담긴 prompt 필드를 수신합니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "UserPromptSubmit",
"prompt": "Write a function to calculate the factorial of a number"
}
UserPromptSubmit 결정 제어
UserPromptSubmit Hook은 사용자 프롬프트의 처리 여부를 제어하고 컨텍스트를 추가할 수 있습니다. 모든 JSON 출력 필드를 사용할 수 있습니다.
종료 코드 0에서 대화에 컨텍스트를 추가하는 두 가지 방법이 있습니다:
- 일반 텍스트 stdout: stdout에 작성된 비-JSON 텍스트가 컨텍스트로 추가됩니다
additionalContext가 포함된 JSON: 더 세밀한 제어를 위해 아래 JSON 형식을 사용합니다.additionalContext필드가 컨텍스트로 추가됩니다
일반 stdout은 트랜스크립트에 Hook 출력으로 표시됩니다. additionalContext 필드는 더 조용하게 추가됩니다.
프롬프트를 차단하려면 decision이 "block"으로 설정된 JSON 객체를 반환하세요:
| 필드 | 설명 |
|---|---|
decision | "block"은 프롬프트 처리를 방지하고 컨텍스트에서 삭제합니다. 프롬프트를 진행하려면 생략하세요 |
reason | decision이 "block"일 때 사용자에게 표시됩니다. 컨텍스트에 추가되지 않습니다 |
additionalContext | Claude의 컨텍스트에 추가되는 문자열 |
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "My additional context here"
}
}
참고: 간단한 사용 사례에서는 JSON 형식이 필수가 아닙니다. 컨텍스트를 추가하려면 종료 코드 0과 함께 일반 텍스트를 stdout에 출력할 수 있습니다. 프롬프트를 차단하거나 더 구조화된 제어가 필요할 때 JSON을 사용하세요.
PreToolUse
Claude가 도구 매개변수를 생성한 후, 도구 호출을 처리하기 전에 실행됩니다. 도구 이름으로 매칭됩니다: Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch, WebSearch, 그리고 모든 MCP 도구 이름.
도구 사용을 허용, 거부, 또는 권한 요청하려면 PreToolUse 결정 제어를 사용하세요.
PreToolUse 입력
공통 입력 필드 외에, PreToolUse Hook은 tool_name, tool_input, tool_use_id를 수신합니다. tool_input 필드는 도구에 따라 다릅니다:
Bash
셸 명령을 실행합니다.
| 필드 | 타입 | 예시 | 설명 |
|---|---|---|---|
command | string | "npm test" | 실행할 셸 명령 |
description | string | "Run test suite" | 명령이 수행하는 작업에 대한 선택적 설명 |
timeout | number | 120000 | 선택적 타임아웃 (밀리초) |
run_in_background | boolean | false | 백그라운드에서 명령을 실행할지 여부 |
Write
파일을 생성하거나 덮어씁니다.
| 필드 | 타입 | 예시 | 설명 |
|---|---|---|---|
file_path | string | "/path/to/file.txt" | 작성할 파일의 절대 경로 |
content | string | "file content" | 파일에 작성할 내용 |
Edit
기존 파일에서 문자열을 교체합니다.
| 필드 | 타입 | 예시 | 설명 |
|---|---|---|---|
file_path | string | "/path/to/file.txt" | 편집할 파일의 절대 경로 |
old_string | string | "original text" | 찾아서 교체할 텍스트 |
new_string | string | "replacement text" | 교체할 텍스트 |
replace_all | boolean | false | 모든 항목을 교체할지 여부 |
Read
파일 내용을 읽습니다.
| 필드 | 타입 | 예시 | 설명 |
|---|---|---|---|
file_path | string | "/path/to/file.txt" | 읽을 파일의 절대 경로 |
offset | number | 10 | 읽기 시작할 선택적 줄 번호 |
limit | number | 50 | 읽을 선택적 줄 수 |
Glob
glob 패턴과 일치하는 파일을 찾습니다.
| 필드 | 타입 | 예시 | 설명 |
|---|---|---|---|
pattern | string | "**/*.ts" | 파일과 매칭할 glob 패턴 |
path | string | "/path/to/dir" | 검색할 선택적 디렉토리. 기본값은 현재 작업 디렉토리 |
Grep
정규 표현식으로 파일 내용을 검색합니다.
| 필드 | 타입 | 예시 | 설명 |
|---|---|---|---|
pattern | string | "TODO.*fix" | 검색할 정규 표현식 패턴 |
path | string | "/path/to/dir" | 검색할 선택적 파일 또는 디렉토리 |
glob | string | "*.ts" | 파일을 필터링할 선택적 glob 패턴 |
output_mode | string | "content" | "content", "files_with_matches", 또는 "count". 기본값은 "files_with_matches" |
-i | boolean | true | 대소문자 구분 없는 검색 |
multiline | boolean | false | 여러 줄 매칭 활성화 |
WebFetch
웹 콘텐츠를 가져와 처리합니다.
| 필드 | 타입 | 예시 | 설명 |
|---|---|---|---|
url | string | "https://example.com/api" | 콘텐츠를 가져올 URL |
prompt | string | "Extract the API endpoints" | 가져온 콘텐츠에 대해 실행할 프롬프트 |
WebSearch
웹을 검색합니다.
| 필드 | 타입 | 예시 | 설명 |
|---|---|---|---|
query | string | "react hooks best practices" | 검색 쿼리 |
allowed_domains | array | ["docs.example.com"] | 선택 사항: 이 도메인의 결과만 포함 |
blocked_domains | array | ["spam.example.com"] | 선택 사항: 이 도메인의 결과를 제외 |
Agent
서브에이전트를 생성합니다.
| 필드 | 타입 | 예시 | 설명 |
|---|---|---|---|
prompt | string | "Find all API endpoints" | 에이전트가 수행할 작업 |
description | string | "Find API endpoints" | 작업에 대한 간단한 설명 |
subagent_type | string | "Explore" | 사용할 특화된 에이전트 유형 |
model | string | "sonnet" | 기본값을 재정의할 선택적 모델 별칭 |
PreToolUse 결정 제어
PreToolUse Hook은 도구 호출의 진행 여부를 제어할 수 있습니다. 최상위 decision 필드를 사용하는 다른 Hook과 달리, PreToolUse는 hookSpecificOutput 객체 내부에 결정을 반환합니다. 이를 통해 세 가지 결과(허용, 거부, 또는 확인 요청)와 실행 전 도구 입력을 수정하는 기능을 포함한 더 풍부한 제어가 가능합니다.
| 필드 | 설명 |
|---|---|
permissionDecision | "allow"는 권한 시스템을 우회하고, "deny"는 도구 호출을 방지하며, "ask"는 사용자에게 확인을 요청합니다 |
permissionDecisionReason | "allow"와 "ask"의 경우 사용자에게 표시되지만 Claude에게는 표시되지 않습니다. "deny"의 경우 Claude에게 표시됩니다 |
updatedInput | 실행 전 도구의 입력 매개변수를 수정합니다. "allow"와 결합하여 자동 승인하거나, "ask"와 결합하여 수정된 입력을 사용자에게 표시합니다 |
additionalContext | 도구 실행 전 Claude의 컨텍스트에 추가되는 문자열 |
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "My reason here",
"updatedInput": {
"field_to_modify": "new value"
},
"additionalContext": "Current environment: production. Proceed with caution."
}
}
참고: PreToolUse는 이전에 최상위
decision과reason필드를 사용했지만, 이 이벤트에서는 더 이상 사용되지 않습니다. 대신hookSpecificOutput.permissionDecision과hookSpecificOutput.permissionDecisionReason을 사용하세요. 더 이상 사용되지 않는 값"approve"와"block"은 각각"allow"와"deny"에 매핑됩니다. PostToolUse 및 Stop과 같은 다른 이벤트는 현재 형식으로 최상위decision과reason을 계속 사용합니다.
PermissionRequest
사용자에게 권한 대화 상자가 표시될 때 실행됩니다.
사용자를 대신하여 허용하거나 거부하려면 PermissionRequest 결정 제어를 사용하세요.
PreToolUse와 동일한 값으로 도구 이름에 매칭됩니다.
PermissionRequest 입력
PermissionRequest Hook은 PreToolUse Hook과 마찬가지로 tool_name과 tool_input 필드를 수신하지만, tool_use_id는 포함되지 않습니다. 선택적 permission_suggestions 배열에는 사용자가 권한 대화 상자에서 일반적으로 볼 수 있는 "항상 허용" 옵션이 포함됩니다. 차이점은 Hook이 발생하는 시점입니다: PermissionRequest Hook은 사용자에게 권한 대화 상자가 표시되려고 할 때 실행되는 반면, PreToolUse Hook은 권한 상태와 관계없이 도구 실행 전에 실행됩니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PermissionRequest",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf node_modules",
"description": "Remove node_modules directory"
},
"permission_suggestions": [
{ "type": "toolAlwaysAllow", "tool": "Bash" }
]
}
PermissionRequest 결정 제어
PermissionRequest Hook은 권한 요청을 허용하거나 거부할 수 있습니다. 모든 Hook에서 사용 가능한 JSON 출력 필드 외에, Hook 스크립트는 다음 이벤트별 필드가 포함된 decision 객체를 반환할 수 있습니다:
| 필드 | 설명 |
|---|---|
behavior | "allow"는 권한을 부여하고, "deny"는 거부합니다 |
updatedInput | "allow" 전용: 실행 전 도구의 입력 매개변수를 수정합니다 |
updatedPermissions | "allow" 전용: 사용자가 "항상 허용" 옵션을 선택한 것과 동일하게 권한 규칙 업데이트를 적용합니다 |
message | "deny" 전용: Claude에게 권한이 거부된 이유를 알립니다 |
interrupt | "deny" 전용: true이면 Claude를 중지합니다 |
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}
}
PostToolUse
도구가 성공적으로 완료된 직후에 실행됩니다.
PreToolUse와 동일한 값으로 도구 이름에 매칭됩니다.
PostToolUse 입력
PostToolUse Hook은 도구가 이미 성공적으로 실행된 후에 발생합니다. 입력에는 도구에 전송된 인수인 tool_input과 반환된 결과인 tool_response가 모두 포함됩니다. 두 필드의 정확한 스키마는 도구에 따라 다릅니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "file content"
},
"tool_response": {
"filePath": "/path/to/file.txt",
"success": true
},
"tool_use_id": "toolu_01ABC123..."
}
PostToolUse 결정 제어
PostToolUse Hook은 도구 실행 후 Claude에게 피드백을 제공할 수 있습니다. 모든 Hook에서 사용 가능한 JSON 출력 필드 외에, Hook 스크립트는 다음 이벤트별 필드를 반환할 수 있습니다:
| 필드 | 설명 |
|---|---|
decision | "block"은 Claude에게 reason을 표시합니다. 동작을 진행하려면 생략하세요 |
reason | decision이 "block"일 때 Claude에게 표시되는 설명 |
additionalContext | Claude가 고려할 추가 컨텍스트 |
updatedMCPToolOutput | MCP 도구 전용: 도구의 출력을 제공된 값으로 교체합니다 |
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for Claude"
}
}
PostToolUseFailure
도구 실행이 실패할 때 실행됩니다. 이 이벤트는 오류를 발생시키거나 실패 결과를 반환하는 도구 호출에 대해 발생합니다. 실패를 로깅하거나, 알림을 보내거나, Claude에게 수정 피드백을 제공하는 데 사용합니다.
PreToolUse와 동일한 값으로 도구 이름에 매칭됩니다.
PostToolUseFailure 입력
PostToolUseFailure Hook은 PostToolUse와 동일한 tool_name과 tool_input 필드를 수신하며, 오류 정보가 최상위 필드로 포함됩니다:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolUseFailure",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite"
},
"tool_use_id": "toolu_01ABC123...",
"error": "Command exited with non-zero status code 1",
"is_interrupt": false
}
| 필드 | 설명 |
|---|---|
error | 무엇이 잘못되었는지 설명하는 문자열 |
is_interrupt | 실패가 사용자 중단으로 인한 것인지를 나타내는 선택적 불리언 |
PostToolUseFailure 결정 제어
PostToolUseFailure Hook은 도구 실패 후 Claude에게 컨텍스트를 제공할 수 있습니다. 모든 Hook에서 사용 가능한 JSON 출력 필드 외에, Hook 스크립트는 다음 이벤트별 필드를 반환할 수 있습니다:
| 필드 | 설명 |
|---|---|
additionalContext | 오류와 함께 Claude가 고려할 추가 컨텍스트 |
{
"hookSpecificOutput": {
"hookEventName": "PostToolUseFailure",
"additionalContext": "Additional information about the failure for Claude"
}
}
Notification
Claude Code가 알림을 보낼 때 실행됩니다. 알림 유형으로 매칭됩니다: permission_prompt, idle_prompt, auth_success, elicitation_dialog. 모든 알림 유형에 대해 Hook을 실행하려면 matcher를 생략하세요.
알림 유형에 따라 다른 핸들러를 실행하려면 별도의 matcher를 사용하세요. 이 구성은 Claude가 권한 승인이 필요할 때 권한별 알림 스크립트를 트리거하고, Claude가 유휴 상태일 때 다른 알림을 트리거합니다:
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/permission-alert.sh"
}
]
},
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/idle-notification.sh"
}
]
}
]
}
}
Notification 입력
공통 입력 필드 외에, Notification Hook은 알림 텍스트가 담긴 message, 선택적 title, 그리고 어떤 유형이 발생했는지를 나타내는 notification_type을 수신합니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Notification",
"message": "Claude needs your permission to use Bash",
"title": "Permission needed",
"notification_type": "permission_prompt"
}
Notification Hook은 알림을 차단하거나 수정할 수 없습니다. 모든 Hook에서 사용 가능한 JSON 출력 필드 외에, additionalContext를 반환하여 대화에 컨텍스트를 추가할 수 있습니다:
| 필드 | 설명 |
|---|---|
additionalContext | Claude의 컨텍스트에 추가되는 문자열 |
SubagentStart
Agent 도구를 통해 Claude Code 서브에이전트가 생성될 때 실행됩니다. 에이전트 유형 이름으로 필터링하는 matcher를 지원합니다 (내장 에이전트인 Bash, Explore, Plan, 또는 .claude/agents/의 커스텀 에이전트 이름).
SubagentStart 입력
공통 입력 필드 외에, SubagentStart Hook은 서브에이전트의 고유 식별자가 담긴 agent_id와 에이전트 이름(내장 에이전트인 "Bash", "Explore", "Plan", 또는 커스텀 에이전트 이름)이 담긴 agent_type을 수신합니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "SubagentStart",
"agent_id": "agent-abc123",
"agent_type": "Explore"
}
SubagentStart Hook은 서브에이전트 생성을 차단할 수 없지만, 서브에이전트에 컨텍스트를 주입할 수 있습니다. 모든 Hook에서 사용 가능한 JSON 출력 필드 외에, 다음을 반환할 수 있습니다:
| 필드 | 설명 |
|---|---|
additionalContext | 서브에이전트의 컨텍스트에 추가되는 문자열 |
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Follow security guidelines for this task"
}
}
SubagentStop
Claude Code 서브에이전트가 응답을 완료했을 때 실행됩니다. SubagentStart와 동일한 값으로 에이전트 유형에 매칭됩니다.
SubagentStop 입력
공통 입력 필드 외에, SubagentStop Hook은 stop_hook_active, agent_id, agent_type, agent_transcript_path, last_assistant_message를 수신합니다. agent_type 필드는 matcher 필터링에 사용되는 값입니다. transcript_path는 메인 세션의 트랜스크립트이며, agent_transcript_path는 중첩된 subagents/ 폴더에 저장된 서브에이전트 자체의 트랜스크립트입니다. last_assistant_message 필드는 서브에이전트의 최종 응답의 텍스트 내용을 포함하므로, 트랜스크립트 파일을 파싱하지 않고도 Hook에서 접근할 수 있습니다.
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../abc123.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "SubagentStop",
"stop_hook_active": false,
"agent_id": "def456",
"agent_type": "Explore",
"agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl",
"last_assistant_message": "Analysis complete. Found 3 potential issues..."
}
SubagentStop Hook은 Stop Hook과 동일한 결정 제어 형식을 사용합니다.
Stop
메인 Claude Code 에이전트가 응답을 완료했을 때 실행됩니다. 사용자 중단으로 인해 중지된 경우에는
실행되지 않습니다.
Stop 입력
공통 입력 필드 외에, Stop Hook은 stop_hook_active와 last_assistant_message를 수신합니다. stop_hook_active 필드는 Claude Code가 이미 Stop Hook의 결과로 계속 실행 중일 때 true입니다. Claude Code가 무한으로 실행되는 것을 방지하려면 이 값을 확인하거나 트랜스크립트를 처리하세요. last_assistant_message 필드는 Claude의 최종 응답의 텍스트 내용을 포함하므로, 트랜스크립트 파일을 파싱하지 않고도 Hook에서 접근할 수 있습니다.
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Stop",
"stop_hook_active": true,
"last_assistant_message": "I've completed the refactoring. Here's a summary..."
}
Stop 결정 제어
Stop과 SubagentStop Hook은 Claude의 계속 실행 여부를 제어할 수 있습니다. 모든 Hook에서 사용 가능한 JSON 출력 필드 외에, Hook 스크립트는 다음 이벤트별 필드를 반환할 수 있습니다:
| 필드 | 설명 |
|---|---|
decision | "block"은 Claude가 중지하는 것을 방지합니다. Claude가 중지되도록 하려면 생략하세요 |
reason | decision이 "block"일 때 필수입니다. Claude에게 계속해야 하는 이유를 알립니다 |
{
"decision": "block",
"reason": "Must be provided when Claude is blocked from stopping"
}
TeammateIdle
에이전트 팀 팀원이 자신의 차례를 마치고 유휴 상태가 되려고 할 때 실행됩니다. 통과하는 lint 검사 요구나 출력 파일 존재 확인과 같은 품질 게이트를 팀원이 작업을 중단하기 전에 적용하는 데 사용합니다.
TeammateIdle Hook이 종료 코드 2로 종료하면, 팀원은 stderr 메시지를 피드백으로 받고 유휴 상태 대신 작업을 계속합니다. 팀원을 재실행하는 대신 완전히 중지하려면 {"continue": false, "stopReason": "..."}가 포함된 JSON을 반환하세요. TeammateIdle Hook은 matcher를 지원하지 않으며 모든 발생 시 실행됩니다.
TeammateIdle 입력
공통 입력 필드 외에, TeammateIdle Hook은 teammate_name과 team_name을 수신합니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TeammateIdle",
"teammate_name": "researcher",
"team_name": "my-project"
}
| 필드 | 설명 |
|---|---|
teammate_name | 유휴 상태가 되려는 팀원의 이름 |
team_name | 팀 이름 |
TeammateIdle 결정 제어
TeammateIdle Hook은 팀원 동작을 제어하는 두 가지 방법을 지원합니다:
- 종료 코드 2: 팀원이 stderr 메시지를 피드백으로 받고 유휴 상태 대신 작업을 계속합니다.
- JSON
{"continue": false, "stopReason": "..."}:StopHook 동작과 일치하여 팀원을 완전히 중지합니다.stopReason은 사용자에게 표시됩니다.
이 예시는 팀원이 유휴 상태가 되기 전에 빌드 산출물이 존재하는지 확인합니다:
#!/bin/bash
if [ ! -f "./dist/output.js" ]; then
echo "Build artifact missing. Run the build before stopping." >&2
exit 2
fi
exit 0
TaskCompleted
작업이 완료로 표시될 때 실행됩니다. 이 이벤트는 두 가지 상황에서 발생합니다: 에이전트가 TaskUpdate 도구를 통해 작업을 명시적으로 완료로 표시할 때, 또는 에이전트 팀 팀원이 진행 중인 작업이 있는 상태에서 자신의 차례를 마칠 때입니다. 작업이 닫히기 전에 테스트 통과나 lint 검사와 같은 완료 기준을 적용하는 데 사용합니다.
TaskCompleted Hook이 종료 코드 2로 종료하면, 작업은 완료로 표시되지 않고 stderr 메시지가 모델에게 피드백으로 전달됩니다. 팀원을 재실행하는 대신 완전히 중지하려면 {"continue": false, "stopReason": "..."}가 포함된 JSON을 반환하세요. TaskCompleted Hook은 matcher를 지원하지 않으며 모든 발생 시 실행됩니다.
TaskCompleted 입력
공통 입력 필드 외에, TaskCompleted Hook은 task_id, task_subject, 그리고 선택적으로 task_description, teammate_name, team_name을 수신합니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TaskCompleted",
"task_id": "task-001",
"task_subject": "Implement user authentication",
"task_description": "Add login and signup endpoints",
"teammate_name": "implementer",
"team_name": "my-project"
}
| 필드 | 설명 |
|---|---|
task_id | 완료되는 작업의 식별자 |
task_subject | 작업 제목 |
task_description | 작업의 상세 설명. 없을 수 있음 |
teammate_name | 작업을 완료하는 팀원의 이름. 없을 수 있음 |
team_name | 팀 이름. 없을 수 있음 |
TaskCompleted 결정 제어
TaskCompleted Hook은 작업 완료를 제어하는 두 가지 방법을 지원합니다:
- 종료 코드 2: 작업은 완료로 표시되지 않고 stderr 메시지가 모델에게 피드백으로 전달됩니다.
- JSON
{"continue": false, "stopReason": "..."}:StopHook 동작과 일치하여 팀원을 완전히 중지합니다.stopReason은 사용자에게 표시됩니다.
이 예시는 테스트를 실행하고 실패하면 작업 완료를 차단합니다:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
# Run the test suite
if ! npm test 2>&1; then
echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
exit 2
fi
exit 0
ConfigChange
세션 중 구성 파일이 변경될 때 실행됩니다. 설정 변경을 감사하거나, 보안 정책을 적용하거나, 구성 파일에 대한 무단 수정을 차단하는 데 사용합니다.
ConfigChange Hook은 설정 파일, 관리형 정책 설정, 스킬 파일의 변경에 대해 발생합니다. 입력의 source 필드는 어떤 유형의 구성이 변경되었는지 알려주며, 선택적 file_path 필드는 변경된 파일의 경로를 제공합니다.
matcher는 구성 소스를 기준으로 필터링합니다:
| Matcher | 발생 시점 |
|---|---|
user_settings | ~/.claude/settings.json 변경 |
project_settings | .claude/settings.json 변경 |
local_settings | .claude/settings.local.json 변경 |
policy_settings | 관리형 정책 설정 변경 |
skills | .claude/skills/의 스킬 파일 변경 |
이 예시는 보안 감사를 위해 모든 구성 변경을 로깅합니다:
{
"hooks": {
"ConfigChange": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit-config-change.sh"
}
]
}
]
}
}
ConfigChange 입력
공통 입력 필드 외에, ConfigChange Hook은 source와 선택적으로 file_path를 수신합니다. source 필드는 어떤 구성 유형이 변경되었는지를 나타내며, file_path는 수정된 특정 파일의 경로를 제공합니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "ConfigChange",
"source": "project_settings",
"file_path": "/Users/.../my-project/.claude/settings.json"
}
ConfigChange 결정 제어
ConfigChange Hook은 구성 변경이 적용되는 것을 차단할 수 있습니다. 변경을 방지하려면 종료 코드 2 또는 JSON decision을 사용하세요. 차단되면 새 설정이 실행 중인 세션에 적용되지 않습니다.
| 필드 | 설명 |
|---|---|
decision | "block"은 구성 변경이 적용되는 것을 방지합니다. 변경을 허용하려면 생략하세요 |
reason | decision이 "block"일 때 사용자에게 표시되는 설명 |
{
"decision": "block",
"reason": "Configuration changes to project settings require admin approval"
}
policy_settings 변경은 차단할 수 없습니다. policy_settings 소스에 대해서도 Hook은 발생하므로 감사 로깅에 사용할 수 있지만, 차단 결정은 무시됩니다. 이는 기업 관리형 설정이 항상 적용되도록 보장합니다.
WorktreeCreate
claude --worktree를 실행하거나 서브에이전트가 isolation: "worktree"를 사용할 때, Claude Code는 git worktree를 사용하여 격리된 작업 복사본을 만듭니다. WorktreeCreate Hook을 구성하면 기본 git 동작을 대체하여 SVN, Perforce, Mercurial과 같은 다른 버전 관리 시스템을 사용할 수 있습니다.
Hook은 생성된 worktree 디렉토리의 절대 경로를 stdout에 출력해야 합니다. Claude Code는 이 경로를 격리된 세션의 작업 디렉토리로 사용합니다.
이 예시는 SVN 작업 복사본을 생성하고 Claude Code가 사용할 경로를 출력합니다. 리포지토리 URL을 자신의 것으로 교체하세요:
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
}
]
}
]
}
}
Hook은 stdin의 JSON 입력에서 worktree name을 읽고, 새 디렉토리에 새 복사본을 체크아웃한 다음, 디렉토리 경로를 출력합니다. 마지막 줄의 echo가 Claude Code가 worktree 경로로 읽는 것입니다. 경로에 간섭하지 않도록 다른 출력은 stderr로 리다이렉트하세요.
WorktreeCreate 입력
공통 입력 필드 외에, WorktreeCreate Hook은 name 필드를 수신합니다. 이는 새 worktree의 슬러그 식별자로, 사용자가 지정하거나 자동 생성됩니다 (예: bold-oak-a3f2).
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "WorktreeCreate",
"name": "feature-auth"
}
WorktreeCreate 출력
Hook은 생성된 worktree 디렉토리의 절대 경로를 stdout에 출력해야 합니다. Hook이 실패하거나 출력을 생성하지 않으면, worktree 생성이 오류와 함께 실패합니다.
WorktreeCreate Hook은 표준 허용/차단 결정 모델을 사용하지 않습니다. 대신 Hook의 성공 또는 실패가 결과를 결정합니다. type: "command" Hook만 지원됩니다.
WorktreeRemove
WorktreeCreate의 정리 대응입니다. 이 Hook은 worktree가 제거될 때 발생합니다. --worktree 세션을 종료하고 제거를 선택할 때, 또는 isolation: "worktree"를 사용하는 서브에이전트가 완료될 때입니다. git 기반 worktree의 경우 Claude가 git worktree remove로 자동으로 정리합니다. git이 아닌 버전 관리 시스템을 위해 WorktreeCreate Hook을 구성한 경우, 정리를 처리할 WorktreeRemove Hook과 짝을 이루세요. 없으면 worktree 디렉토리가 디스크에 남습니다.
Claude Code는 WorktreeCreate가 stdout에 출력한 경로를 Hook 입력의 worktree_path로 전달합니다. 이 예시는 해당 경로를 읽고 디렉토리를 제거합니다:
{
"hooks": {
"WorktreeRemove": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'jq -r .worktree_path | xargs rm -rf'"
}
]
}
]
}
}
WorktreeRemove 입력
공통 입력 필드 외에, WorktreeRemove Hook은 제거할 worktree의 절대 경로인 worktree_path 필드를 수신합니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "WorktreeRemove",
"worktree_path": "/Users/.../my-project/.claude/worktrees/feature-auth"
}
WorktreeRemove Hook에는 결정 제어가 없습니다. worktree 제거를 차단할 수는 없지만, 버전 관리 상태 제거나 변경 사항 아카이빙과 같은 정리 작업을 수행할 수 있습니다. Hook 실패는 디버그 모드에서만 로깅됩니다. type: "command" Hook만 지원됩니다.
PreCompact
Claude Code가 압축 작업을 실행하기 직전에 실행됩니다.
matcher 값은 압축이 수동으로 트리거되었는지 자동으로 트리거되었는지를 나타냅니다:
| Matcher | 발생 시점 |
|---|---|
manual | /compact |
auto | 컨텍스트 윈도우가 가득 찼을 때 자동 압축 |
PreCompact 입력
공통 입력 필드 외에, PreCompact Hook은 trigger와 custom_instructions를 수신합니다. manual의 경우 custom_instructions에는 사용자가 /compact에 전달한 내용이 포함됩니다. auto의 경우 custom_instructions는 비어 있습니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PreCompact",
"trigger": "manual",
"custom_instructions": ""
}
SessionEnd
Claude Code 세션이 종료될 때 실행됩니다. 정리 작업, 세션 통계 로깅, 또는 세션 상태 저장에 유용합니다. 종료 사유별로 필터링하는 matcher를 지원합니다.
Hook 입력의 reason 필드는 세션이 종료된 이유를 나타냅니다:
| 사유 | 설명 |
|---|---|
clear | /clear 명령으로 세션이 초기화됨 |
logout | 사용자가 로그아웃함 |
prompt_input_exit | 프롬프트 입력이 표시된 상태에서 사용자가 종료함 |
bypass_permissions_disabled | 권한 우회 모드가 비활성화됨 |
other | 기타 종료 사유 |
SessionEnd 입력
공통 입력 필드 외에, SessionEnd Hook은 세션이 종료된 이유를 나타내는 reason 필드를 수신합니다. 모든 값은 위의 사유 표를 참조하세요.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "SessionEnd",
"reason": "other"
}
SessionEnd Hook에는 결정 제어가 없습니다. 세션 종료를 차단할 수는 없지만, 정리 작업을 수행할 수 있습니다.