Documentation Index
Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Hook은 Claude Code의 수명 주기에서 특정 지점에 자동으로 실행되는 사용자 정의 셸 명령, HTTP 엔드포인트 또는 LLM 프롬프트입니다. 이 참조를 사용하여 이벤트 스키마, 구성 옵션, JSON 입출력 형식, 비동기 hook, HTTP hook, MCP 도구 hook과 같은 고급 기능을 조회할 수 있습니다. 처음으로 hook을 설정하는 경우 대신 가이드부터 시작하세요.
Hook 수명 주기
Hook은 Claude Code 세션 중 특정 지점에서 실행됩니다. 이벤트가 발생하고 matcher가 일치하면 Claude Code는 이벤트에 대한 JSON 컨텍스트를 hook 핸들러에 전달합니다. 명령 hook의 경우 입력은 stdin에 도착합니다. HTTP hook의 경우 POST 요청 본문으로 도착합니다. 그러면 핸들러는 입력을 검사하고 조치를 취한 후 선택적으로 결정을 반환할 수 있습니다. 이벤트는 세 가지 주기로 발생합니다: 세션당 한 번 (SessionStart, SessionEnd), 턴당 한 번 (UserPromptSubmit, Stop, StopFailure), 에이전트 루프 내의 모든 도구 호출에서 (PreToolUse, PostToolUse):
아래 표는 각 이벤트가 언제 발생하는지 요약합니다. Hook 이벤트 섹션에서는 각 이벤트의 전체 입력 스키마와 결정 제어 옵션을 문서화합니다.
| Event | When it fires |
|---|
SessionStart | When a session begins or resumes |
Setup | When you start Claude Code with --init-only, or with --init or --maintenance in -p mode. For one-time preparation in CI or scripts |
UserPromptSubmit | When you submit a prompt, before Claude processes it |
UserPromptExpansion | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion |
PreToolUse | Before a tool call executes. Can block it |
PermissionRequest | When a permission dialog appears |
PermissionDenied | When a tool call is denied by the auto mode classifier. Return {retry: true} to tell the model it may retry the denied tool call |
PostToolUse | After a tool call succeeds |
PostToolUseFailure | After a tool call fails |
PostToolBatch | After a full batch of parallel tool calls resolves, before the next model call |
Notification | When Claude Code sends a notification |
SubagentStart | When a subagent is spawned |
SubagentStop | When a subagent finishes |
TaskCreated | When a task is being created via TaskCreate |
TaskCompleted | When a task is being marked as completed |
Stop | When Claude finishes responding |
StopFailure | When the turn ends due to an API error. Output and exit code are ignored |
TeammateIdle | When an agent team teammate is about to go idle |
InstructionsLoaded | When a CLAUDE.md or .claude/rules/*.md file is loaded into context. Fires at session start and when files are lazily loaded during a session |
ConfigChange | When a configuration file changes during a session |
CwdChanged | When the working directory changes, for example when Claude executes a cd command. Useful for reactive environment management with tools like direnv |
FileChanged | When a watched file changes on disk. The matcher field specifies which filenames to watch |
WorktreeCreate | When a worktree is being created via --worktree or isolation: "worktree". Replaces default git behavior |
WorktreeRemove | When a worktree is being removed, either at session exit or when a subagent finishes |
PreCompact | Before context compaction |
PostCompact | After context compaction completes |
Elicitation | When an MCP server requests user input during a tool call |
ElicitationResult | After a user responds to an MCP elicitation, before the response is sent back to the server |
SessionEnd | When a session terminates |
Hook이 어떻게 해결되는지
이러한 부분들이 어떻게 함께 작동하는지 보려면 파괴적인 셸 명령을 차단하는 이 PreToolUse hook을 고려하세요. matcher는 Bash 도구 호출로 좁혀지고 if 조건은 rm *과 일치하는 Bash 부명령으로 더 좁혀지므로 block-rm.sh는 두 필터가 모두 일치할 때만 생성됩니다:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"args": []
}
]
}
]
}
}
스크립트는 stdin에서 JSON 입력을 읽고 명령을 추출한 후 rm -rf를 포함하면 permissionDecision을 "deny"로 반환합니다:
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0 # allow the command
fi
이제 Claude Code가 Bash "rm -rf /tmp/build"를 실행하기로 결정했다고 가정합니다. 다음은 발생하는 일입니다:
이벤트 발생
PreToolUse 이벤트가 발생합니다. Claude Code는 도구 입력을 stdin의 hook에 JSON으로 전송합니다:{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
Matcher 확인
matcher "Bash"가 도구 이름과 일치하므로 이 hook 그룹이 활성화됩니다. matcher를 생략하거나 "*"를 사용하면 이벤트의 모든 발생에서 그룹이 활성화됩니다.
If 조건 확인
if 조건 "Bash(rm *)"은 rm -rf /tmp/build가 rm *과 일치하는 부명령이므로 일치하여 이 핸들러가 생성됩니다. 명령이 npm test였다면 if 검사가 실패하고 block-rm.sh는 절대 실행되지 않아 프로세스 생성 오버헤드를 피합니다. if 필드는 선택 사항입니다. 없으면 일치한 그룹의 모든 핸들러가 실행됩니다.
Hook 핸들러 실행
스크립트는 전체 명령을 검사하고 rm -rf를 찾으므로 stdout에 결정을 인쇄합니다:{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook"
}
}
명령이 rm file.txt와 같은 더 안전한 rm 변형이었다면 스크립트는 대신 exit 0을 실행하여 Claude Code에 도구 호출을 허용하고 추가 조치를 취하지 않도록 알립니다. Claude Code가 결과에 따라 행동
Claude Code는 JSON 결정을 읽고 도구 호출을 차단하며 Claude에 이유를 표시합니다.
아래 구성 섹션에서는 전체 스키마를 문서화하고, 각 hook 이벤트 섹션에서는 명령이 받는 입력과 반환할 수 있는 출력을 문서화합니다.
Hook은 JSON 설정 파일에서 정의됩니다. 구성에는 세 가지 중첩 수준이 있습니다:
- 응답할 hook 이벤트를 선택합니다 (예:
PreToolUse 또는 Stop)
- 발생 시기를 필터링할 matcher 그룹을 추가합니다 (예: “Bash 도구에만”)
- 일치할 때 실행할 하나 이상의 hook 핸들러를 정의합니다
주석이 달린 예제를 포함한 완전한 설명은 위의 Hook이 어떻게 해결되는지를 참조하세요.
이 페이지는 각 수준에 대해 특정 용어를 사용합니다: 수명 주기 지점에 대해 hook 이벤트, 필터에 대해 matcher 그룹, 실행되는 셸 명령, HTTP 엔드포인트, MCP 도구, 프롬프트 또는 에이전트에 대해 hook 핸들러. “Hook”은 일반 기능을 나타냅니다.
Hook 위치
hook을 정의하는 위치는 그 범위를 결정합니다:
| 위치 | 범위 | 공유 가능 |
|---|
~/.claude/settings.json | 모든 프로젝트 | 아니오, 머신에 로컬 |
.claude/settings.json | 단일 프로젝트 | 예, 리포지토리에 커밋 가능 |
.claude/settings.local.json | 단일 프로젝트 | 아니오, gitignored |
| 관리형 정책 설정 | 조직 전체 | 예, 관리자 제어 |
Plugin hooks/hooks.json | plugin이 활성화되었을 때 | 예, plugin과 함께 번들됨 |
| Skill 또는 agent frontmatter | 컴포넌트가 활성화되어 있는 동안 | 예, 컴포넌트 파일에서 정의됨 |
설정 파일 해결에 대한 자세한 내용은 설정을 참조하세요. 엔터프라이즈 관리자는 allowManagedHooksOnly를 사용하여 사용자, 프로젝트 및 plugin hook을 차단할 수 있습니다. 관리형 설정 enabledPlugins에서 강제 활성화된 plugin의 hook은 면제되므로 관리자는 조직 마켓플레이스를 통해 검증된 hook을 배포할 수 있습니다. Hook 구성을 참조하세요.
Matcher 패턴
matcher 필드는 hook이 발생할 때를 필터링합니다. matcher가 평가되는 방식은 포함된 문자에 따라 다릅니다:
| Matcher 값 | 평가 대상 | 예제 |
|---|
"*", "" 또는 생략됨 | 모두 일치 | 이벤트의 모든 발생에서 발생 |
문자, 숫자, _, |만 포함 | 정확한 문자열 또는 |로 구분된 정확한 문자열 목록 | Bash는 Bash 도구만 일치; Edit|Write는 두 도구 중 하나와 정확히 일치 |
| 다른 문자 포함 | JavaScript 정규식 | ^Notebook은 Notebook으로 시작하는 모든 도구와 일치; mcp__memory__.*는 memory 서버의 모든 도구와 일치 |
FileChanged 이벤트는 감시 목록을 구축할 때 이러한 규칙을 따르지 않습니다. FileChanged를 참조하세요.
각 이벤트 유형은 다른 필드에서 일치합니다:
| 이벤트 | Matcher가 필터링하는 것 | 예제 matcher 값 |
|---|
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied | 도구 이름 | Bash, Edit|Write, mcp__.* |
SessionStart | 세션이 시작된 방식 | startup, resume, clear, compact |
Setup | 설정을 트리거한 CLI 플래그 | init, maintenance |
SessionEnd | 세션이 종료된 이유 | clear, resume, logout, prompt_input_exit, bypass_permissions_disabled, other |
Notification | 알림 유형 | permission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_complete, elicitation_response |
SubagentStart | 에이전트 유형 | general-purpose, Explore, Plan 또는 사용자 정의 에이전트 이름 |
PreCompact, PostCompact | 압축을 트리거한 것 | manual, auto |
SubagentStop | 에이전트 유형 | SubagentStart와 동일한 값 |
ConfigChange | 구성 소스 | user_settings, project_settings, local_settings, policy_settings, skills |
CwdChanged | matcher 지원 없음 | 모든 디렉토리 변경에서 항상 발생 |
FileChanged | 감시할 리터럴 파일명 (FileChanged 참조) | .envrc|.env |
StopFailure | 오류 유형 | rate_limit, authentication_failed, oauth_org_not_allowed, billing_error, invalid_request, server_error, max_output_tokens, unknown |
InstructionsLoaded | 로드 이유 | session_start, nested_traversal, path_glob_match, include, compact |
UserPromptExpansion | 명령 이름 | skill 또는 명령 이름 |
Elicitation | MCP 서버 이름 | 구성된 MCP 서버 이름 |
ElicitationResult | MCP 서버 이름 | Elicitation과 동일한 값 |
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove | matcher 지원 없음 | 모든 발생에서 항상 발생 |
matcher는 Claude Code가 stdin의 hook에 전송하는 JSON 입력의 필드에 대해 실행됩니다. 도구 이벤트의 경우 해당 필드는 tool_name입니다. 각 hook 이벤트 섹션에서는 해당 이벤트의 전체 matcher 값 집합과 입력 스키마를 나열합니다.
이 예제는 Claude가 파일을 쓰거나 편집할 때만 linting 스크립트를 실행합니다:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "/path/to/lint-check.sh"
}
]
}
]
}
}
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, CwdChanged는 matcher를 지원하지 않으며 모든 발생에서 항상 발생합니다. 이러한 이벤트에 matcher 필드를 추가하면 자동으로 무시됩니다.
도구 이벤트의 경우 개별 hook 핸들러에서 if 필드를 설정하여 더 좁게 필터링할 수 있습니다. if는 권한 규칙 구문을 사용하여 도구 이름과 인수를 함께 일치시키므로 "Bash(git *)" 는 git * 패턴과 일치하는 모든 하위 명령에서 실행되고 "Edit(*.ts)"는 TypeScript 파일에만 실행됩니다.
MCP 도구 일치
MCP 서버 도구는 도구 이벤트 (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied)에서 일반 도구로 나타나므로 다른 도구 이름과 동일한 방식으로 일치시킬 수 있습니다.
MCP 도구는 mcp__<server>__<tool> 명명 패턴을 따릅니다. 예를 들어:
mcp__memory__create_entities: Memory 서버의 create entities 도구
mcp__filesystem__read_file: Filesystem 서버의 read file 도구
mcp__github__search_repositories: GitHub 서버의 search 도구
서버의 모든 도구와 일치하려면 서버 접두사에 .*를 추가합니다. .*는 필수입니다: mcp__memory와 같은 matcher는 문자와 밑줄만 포함하므로 정확한 문자열로 비교되고 도구와 일치하지 않습니다.
mcp__memory__.*는 memory 서버의 모든 도구와 일치합니다
mcp__.*__write.*는 모든 서버의 이름이 write로 시작하는 모든 도구와 일치합니다
이 예제는 모든 memory 서버 작업을 기록하고 모든 MCP 서버의 쓰기 작업을 검증합니다:
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__memory__.*",
"hooks": [
{
"type": "command",
"command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
}
]
},
{
"matcher": "mcp__.*__write.*",
"hooks": [
{
"type": "command",
"command": "/home/user/scripts/validate-mcp-write.py"
}
]
}
]
}
}
Hook 핸들러 필드
내부 hooks 배열의 각 객체는 hook 핸들러입니다: matcher가 일치할 때 실행되는 셸 명령, HTTP 엔드포인트, MCP 도구, LLM 프롬프트 또는 에이전트입니다. 다섯 가지 유형이 있습니다:
- 명령 hook (
type: "command"): 셸 명령을 실행합니다. 스크립트는 이벤트의 JSON 입력을 stdin에서 받고 종료 코드와 stdout을 통해 결과를 다시 전달합니다.
- HTTP hook (
type: "http"): 이벤트의 JSON 입력을 HTTP POST 요청으로 URL에 전송합니다. 엔드포인트는 명령 hook과 동일한 JSON 출력 형식을 사용하여 응답 본문을 통해 결과를 다시 전달합니다.
- MCP 도구 hook (
type: "mcp_tool"): 이미 연결된 MCP 서버의 도구를 호출합니다. 도구의 텍스트 출력은 명령 hook stdout처럼 처리됩니다.
- 프롬프트 hook (
type: "prompt"): Claude 모델에 단일 턴 평가를 위한 프롬프트를 전송합니다. 모델은 yes/no 결정을 JSON으로 반환합니다. 프롬프트 기반 hook을 참조하세요.
- 에이전트 hook (
type: "agent"): Read, Grep, Glob과 같은 도구를 사용하여 결정을 반환하기 전에 조건을 확인할 수 있는 subagent를 생성합니다. 에이전트 hook은 실험적이며 변경될 수 있습니다. 에이전트 기반 hook을 참조하세요.
공통 필드
이러한 필드는 모든 hook 유형에 적용됩니다:
| 필드 | 필수 | 설명 |
|---|
type | 예 | "command", "http", "mcp_tool", "prompt" 또는 "agent" |
if | 아니오 | "Bash(git *)" 또는 "Edit(*.ts)"와 같은 권한 규칙 구문을 사용하여 이 hook이 실행될 때를 필터링합니다. hook은 도구 호출이 패턴과 일치할 때만 생성되거나 Bash 명령이 너무 복잡하여 구문 분석할 수 없을 때 생성됩니다. 도구 이벤트에서만 평가됩니다: PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied. 다른 이벤트에서는 if가 설정된 hook이 절대 실행되지 않습니다. 권한 규칙과 동일한 구문을 사용합니다 |
timeout | 아니오 | 취소하기 전 초 단위. 기본값: command, http, mcp_tool의 경우 600; prompt의 경우 30; agent의 경우 60. UserPromptSubmit은 command, http, mcp_tool의 기본값을 30으로 낮춥니다 |
statusMessage | 아니오 | hook이 실행되는 동안 표시되는 사용자 정의 스피너 메시지 |
once | 아니오 | true인 경우 세션당 한 번만 실행된 후 제거됩니다. Skill 및 에이전트의 Hook에서 선언된 hook에만 적용됨; 설정 파일 및 에이전트 frontmatter에서는 무시됨 |
if 필드는 정확히 하나의 권한 규칙을 보유합니다. 규칙을 결합하기 위한 &&, || 또는 목록 구문이 없습니다. 여러 조건을 적용하려면 각각에 대해 별도의 hook 핸들러를 정의합니다. Bash의 경우 규칙은 선행 VAR=value 할당이 제거된 후 도구 입력의 각 하위 명령에 대해 일치하므로 if: "Bash(git push *)" 는 FOO=bar git push 및 npm test && git push 모두와 일치합니다. hook은 모든 하위 명령이 일치할 때 실행되며 명령이 너무 복잡하여 구문 분석할 수 없을 때 항상 실행됩니다.
명령 hook 필드
공통 필드 외에도 명령 hook은 이러한 필드를 허용합니다:
| 필드 | 필수 | 설명 |
|---|
command | 예 | 실행할 셸 명령. args와 함께 직접 생성할 실행 파일입니다. Exec 형식 및 셸 형식 참조 |
args | 아니오 | 인수 목록. 존재할 때 command는 실행 파일로 해결되고 args를 인수 벡터로 하여 직접 생성되며 셸이 관여하지 않습니다. Exec 형식 및 셸 형식 참조 |
async | 아니오 | true인 경우 차단하지 않고 백그라운드에서 실행됩니다. 백그라운드에서 hook 실행 참조 |
asyncRewake | 아니오 | true인 경우 백그라운드에서 실행되고 종료 코드 2에서 Claude를 깨웁니다. async를 의미합니다. hook의 stderr 또는 stderr이 비어 있으면 stdout이 Claude에 시스템 알림으로 표시되므로 장기 실행 백그라운드 실패에 반응할 수 있습니다 |
shell | 아니오 | 이 hook에 사용할 셸. "bash" (기본값) 또는 "powershell"을 허용합니다. "powershell"을 설정하면 Windows에서 PowerShell을 통해 명령을 실행합니다. CLAUDE_CODE_USE_POWERSHELL_TOOL이 필요하지 않습니다. hook이 PowerShell을 직접 생성하기 때문입니다. args가 설정되면 무시됩니다 |
Exec 형식 및 셸 형식
명령 hook은 args가 설정되면 exec 형식으로 실행되고 args가 생략되면 셸 형식으로 실행됩니다. hook이 경로 자리 표시자를 참조할 때마다 args를 설정하세요. 각 요소는 따옴표 없이 하나의 인수로 전달됩니다. 파이프 또는 &&와 같은 셸 기능이 필요하거나 두 가지 우려 사항이 모두 적용되지 않을 때 args를 생략합니다.
Exec 형식은 args가 있을 때 실행됩니다. Claude Code는 command를 PATH의 실행 파일로 해결하고 args를 인수 벡터로 하여 직접 생성합니다. 셸이 없으므로 각 args 요소는 작성된 그대로 정확히 하나의 인수이며 ${CLAUDE_PLUGIN_ROOT}와 같은 경로 자리 표시자는 command 및 각 args 요소로 일반 문자열로 대체됩니다. 아포스트로피, $, 백틱과 같은 특수 문자는 해석할 셸이 없으므로 그대로 전달됩니다. 어떤 플랫폼에서도 셸 토큰화가 발생하지 않습니다.
셸 형식은 args가 없을 때 실행됩니다. command 문자열은 셸로 전달됩니다: macOS 및 Linux에서는 sh -c, Windows에서는 Git Bash, Git Bash가 설치되지 않았을 때는 PowerShell입니다. shell 필드를 설정하여 명시적으로 선택합니다. 셸은 문자열을 토큰화하고 변수를 확장하며 파이프, &&, 리다이렉트, 글로브를 해석합니다.
Windows에서 exec 형식은 .exe와 같은 실제 실행 파일로 해결되는 command를 필요로 합니다. npm, npx, eslint 및 기타 도구가 node_modules/.bin에 설치하는 .cmd 및 .bat shim은 실행 파일이 아니며 셸 없이 생성될 수 없습니다. exec 형식으로 실행하려면 기본 스크립트를 node로 직접 호출합니다. 예를 들어 "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. node 더하기 스크립트 경로 패턴은 node.exe가 실제 바이너리이므로 모든 플랫폼에서 작동합니다. .cmd 또는 .bat shim을 이름으로 실행하려면 셸 형식을 사용합니다.
이 예제는 plugin과 함께 번들된 Node 스크립트를 실행합니다. Exec 형식은 해결된 스크립트 경로를 따옴표 없이 하나의 인수로 전달합니다:
{
"type": "command",
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js", "--fix"]
}
동등한 셸 형식은 공백이나 특수 문자가 있는 경로를 처리하기 위해 따옴표가 필요합니다:
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/format.js --fix"
}
두 형식 모두 동일한 경로 자리 표시자를 지원하며 생성된 프로세스에서 환경 변수 CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT, CLAUDE_PLUGIN_DATA로 내보내므로 스크립트는 시작 방식과 관계없이 process.env.CLAUDE_PLUGIN_ROOT를 읽을 수 있습니다. Plugin hook은 추가로 ${user_config.*} 값을 대체합니다. 사용자 구성을 참조하세요.
Exec 형식에서 command는 실행 파일 이름 또는 경로만입니다. command가 경로 구분자가 없는 bare 이름이고 args와 함께 공백을 포함하면 Claude Code는 경고를 기록합니다. 생성이 실패하기 때문입니다: node script.js라는 이름의 실행 파일이 없습니다. 추가 토큰을 args로 이동합니다. C:\Program Files\nodejs\node.exe와 같은 공백이 있는 절대 경로는 단일 유효한 실행 파일이며 경고를 트리거하지 않습니다.
HTTP hook 필드
공통 필드 외에도 HTTP hook은 이러한 필드를 허용합니다:
| 필드 | 필수 | 설명 |
|---|
url | 예 | POST 요청을 전송할 URL |
headers | 아니오 | 키-값 쌍으로 된 추가 HTTP 헤더. 값은 $VAR_NAME 또는 ${VAR_NAME} 구문을 사용한 환경 변수 보간을 지원합니다. allowedEnvVars에 나열된 변수만 해결됩니다 |
allowedEnvVars | 아니오 | 헤더 값으로 보간될 수 있는 환경 변수 이름 목록. 나열되지 않은 변수에 대한 참조는 빈 문자열로 바뀝니다. 환경 변수 보간이 작동하려면 필수입니다 |
Claude Code는 hook의 JSON 입력을 Content-Type: application/json과 함께 POST 요청 본문으로 전송합니다. 응답 본문은 명령 hook과 동일한 JSON 출력 형식을 사용합니다.
오류 처리는 명령 hook과 다릅니다: 2xx가 아닌 응답, 연결 실패, 시간 초과는 모두 실행을 계속하도록 허용하는 차단하지 않는 오류를 생성합니다. 도구 호출을 차단하거나 권한을 거부하려면 decision: "block" 또는 hookSpecificOutput이 permissionDecision: "deny"를 포함하는 JSON 본문이 있는 2xx 응답을 반환합니다.
이 예제는 PreToolUse 이벤트를 로컬 검증 서비스로 전송하고 MY_TOKEN 환경 변수의 토큰으로 인증합니다:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"timeout": 30,
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}
MCP 도구 hook 필드
공통 필드 외에도 MCP 도구 hook은 이러한 필드를 허용합니다:
| 필드 | 필수 | 설명 |
|---|
server | 예 | 구성된 MCP 서버의 이름. 서버는 이미 연결되어 있어야 합니다. hook은 OAuth 또는 연결 흐름을 트리거하지 않습니다 |
tool | 예 | 해당 서버에서 호출할 도구의 이름 |
input | 아니오 | 도구에 전달되는 인수. 문자열 값은 hook의 JSON 입력에서 ${path} 치환을 지원합니다 (예: "${tool_input.file_path}") |
도구의 텍스트 콘텐츠는 명령 hook stdout처럼 처리됩니다: 유효한 JSON 출력으로 구문 분석되면 결정으로 처리되고, 그렇지 않으면 일반 텍스트로 표시됩니다. 명명된 서버가 연결되지 않았거나 도구가 isError: true를 반환하면 hook은 차단하지 않는 오류를 생성하고 실행이 계속됩니다.
MCP 도구 hook은 Claude Code가 MCP 서버에 연결한 후 모든 hook 이벤트에서 사용 가능합니다. SessionStart 및 Setup은 일반적으로 서버가 연결을 완료하기 전에 발생하므로 이러한 이벤트의 hook은 첫 실행 시 “not connected” 오류를 예상해야 합니다.
이 예제는 각 Write 또는 Edit 후에 my_server MCP 서버의 security_scan 도구를 호출하고 편집된 파일의 경로를 전달합니다:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "mcp_tool",
"server": "my_server",
"tool": "security_scan",
"input": { "file_path": "${tool_input.file_path}" }
}
]
}
]
}
}
프롬프트 및 에이전트 hook 필드
공통 필드 외에도 프롬프트 및 에이전트 hook은 이러한 필드를 허용합니다:
| 필드 | 필수 | 설명 |
|---|
prompt | 예 | 모델에 전송할 프롬프트 텍스트. hook 입력 JSON에 대한 자리 표시자로 $ARGUMENTS 사용 |
model | 아니오 | 평가에 사용할 모델. 기본값은 빠른 모델 |
일치하는 모든 hook은 병렬로 실행되며 동일한 핸들러는 자동으로 중복 제거됩니다. 명령 hook은 명령 문자열과 args로 중복 제거되고 HTTP hook은 URL로 중복 제거됩니다. 핸들러는 현재 디렉토리에서 Claude Code의 환경으로 실행됩니다. $CLAUDE_CODE_REMOTE 환경 변수는 원격 웹 환경에서 "true"로 설정되고 로컬 CLI에서는 설정되지 않습니다.
경로별로 스크립트 참조
프로젝트 또는 plugin 루트를 기준으로 hook 스크립트를 참조하려면 이러한 자리 표시자를 사용합니다. hook이 실행될 때의 작업 디렉토리와 관계없이:
${CLAUDE_PROJECT_DIR}: 프로젝트 루트.
${CLAUDE_PLUGIN_ROOT}: plugin의 설치 디렉토리, plugin과 함께 번들된 스크립트의 경우. plugin 업데이트 시마다 변경됩니다.
${CLAUDE_PLUGIN_DATA}: plugin의 지속적 데이터 디렉토리, plugin 업데이트를 거쳐 유지되어야 하는 종속성 및 상태의 경우.
경로 자리 표시자를 참조하는 모든 hook에 대해 exec 형식을 선호합니다. Exec 형식은 각 args 요소를 셸 토큰화 없이 하나의 인수로 전달하므로 공백이나 특수 문자가 있는 경로는 따옴표가 필요하지 않습니다. 셸 형식에서는 각 자리 표시자를 큰따옴표로 감싸세요.
이 예제는 ${CLAUDE_PROJECT_DIR}을 사용하여 Write 또는 Edit 도구 호출 후 프로젝트의 .claude/hooks/ 디렉토리에서 스타일 검사기를 실행합니다:{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh",
"args": []
}
]
}
]
}
}
hooks/hooks.json에서 plugin hook을 정의하고 선택적 최상위 description 필드를 포함합니다. plugin이 활성화되면 해당 hook이 사용자 및 프로젝트 hook과 병합됩니다.이 예제는 plugin과 함께 번들된 형식 지정 스크립트를 실행합니다:{
"description": "Automatic code formatting",
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
"args": [],
"timeout": 30
}
]
}
]
}
}
plugin hook 생성에 대한 자세한 내용은 plugin 컴포넌트 참조를 참조하세요.
Skill 및 에이전트의 Hook
설정 파일 및 plugin 외에도 hook은 frontmatter를 사용하여 skill 및 subagent에서 직접 정의할 수 있습니다. 이러한 hook은 컴포넌트의 수명 주기로 범위가 지정되며 해당 컴포넌트가 활성화되어 있을 때만 실행됩니다.
모든 hook 이벤트가 지원됩니다. subagent의 경우 Stop hook은 subagent가 완료될 때 발생하는 이벤트이므로 자동으로 SubagentStop으로 변환됩니다.
Hook은 설정 기반 hook과 동일한 구성 형식을 사용하지만 컴포넌트의 수명으로 범위가 지정되고 완료될 때 정리됩니다.
이 skill은 각 Bash 명령 전에 보안 검증 스크립트를 실행하는 PreToolUse hook을 정의합니다:
---
name: secure-operations
description: Perform operations with security checks
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/security-check.sh"
---
에이전트는 YAML frontmatter에서 동일한 형식을 사용합니다.
/hooks 메뉴
Claude Code에서 /hooks를 입력하여 구성된 hook의 읽기 전용 브라우저를 엽니다. 메뉴는 구성된 hook 수가 있는 모든 hook 이벤트를 표시하고, matcher로 드릴다운할 수 있으며, 각 hook 핸들러의 전체 세부 정보를 표시합니다. 구성을 확인하거나, hook이 어느 설정 파일에서 왔는지 확인하거나, hook의 명령, 프롬프트 또는 URL을 검사하는 데 사용합니다.
메뉴는 다섯 가지 hook 유형을 표시합니다: command, prompt, agent, http, mcp_tool. 각 hook은 소스를 나타내는 [type] 접두사와 레이블이 지정됩니다:
User: ~/.claude/settings.json에서
Project: .claude/settings.json에서
Local: .claude/settings.local.json에서
Plugin: plugin의 hooks/hooks.json에서
Session: 현재 세션을 위해 메모리에 등록됨
Built-in: Claude Code에 의해 내부적으로 등록됨
hook을 선택하면 이벤트, matcher, 유형, 소스 파일, 전체 명령, 프롬프트 또는 URL을 표시하는 세부 정보 보기가 열립니다. 메뉴는 읽기 전용입니다: hook을 추가, 수정 또는 제거하려면 설정 JSON을 직접 편집하거나 Claude에 변경을 요청하세요.
Hook 비활성화 또는 제거
hook을 제거하려면 설정 JSON 파일에서 해당 항목을 삭제합니다.
모든 hook을 제거하지 않고 임시로 비활성화하려면 설정 파일에서 "disableAllHooks": true를 설정합니다. 구성에 유지하면서 개별 hook을 비활성화할 수 있는 방법은 없습니다.
disableAllHooks 설정은 관리형 설정 계층을 준수합니다. 관리자가 관리형 정책 설정을 통해 hook을 구성한 경우 사용자, 프로젝트 또는 로컬 설정에서 설정된 disableAllHooks는 해당 관리형 hook을 비활성화할 수 없습니다. 관리형 설정 수준에서 설정된 disableAllHooks만 관리형 hook을 비활성화할 수 있습니다.
설정 파일의 hook에 대한 직접 편집은 일반적으로 파일 감시자에 의해 자동으로 선택됩니다.
Hook 입출력
명령 hook은 stdin을 통해 JSON 데이터를 받고 종료 코드, stdout, stderr를 통해 결과를 전달합니다. HTTP hook은 POST 요청 본문으로 동일한 JSON을 받고 HTTP 응답 본문을 통해 결과를 전달합니다. 이 섹션에서는 모든 이벤트에 공통적인 필드와 동작을 다룹니다. Hook 이벤트 아래의 각 이벤트 섹션에는 특정 입력 스키마와 결정 제어 옵션이 포함됩니다.
macOS 및 Linux에서 명령 hook은 v2.1.139부터 제어 터미널 없이 자신의 세션에서 실행됩니다. hook 프로세스 및 모든 자식 프로세스는 /dev/tty를 열거나 Claude Code 인터페이스에 직접 이스케이프 시퀀스를 보낼 수 없습니다. Windows에는 /dev/tty가 없습니다. 모든 플랫폼에서 사용자에게 메시지를 표시하려면 JSON 출력에서 systemMessage를 반환합니다. 데스크톱 알림을 트리거하거나 창 제목을 설정하거나 벨을 울리려면 대신 terminalSequence를 반환합니다.
공통 입력 필드
Hook 이벤트는 각 hook 이벤트 섹션에서 문서화된 이벤트 특정 필드 외에 이러한 필드를 JSON으로 받습니다. 명령 hook의 경우 이 JSON은 stdin을 통해 도착합니다. HTTP hook의 경우 POST 요청 본문으로 도착합니다.
| 필드 | 설명 |
|---|
session_id | 현재 세션 식별자 |
transcript_path | 대화 JSON 경로 |
cwd | hook이 호출될 때의 현재 작업 디렉토리 |
permission_mode | 현재 권한 모드: "default", "plan", "acceptEdits", "auto", "dontAsk" 또는 "bypassPermissions". 모든 이벤트가 이 필드를 받는 것은 아닙니다: 각 이벤트의 JSON 예제를 확인하세요 |
effort | 활성 노력 수준을 보유하는 level 필드가 있는 객체: "low", "medium", "high", "xhigh" 또는 "max". 요청된 노력이 현재 모델이 지원하는 것을 초과하면 이는 모델이 실제로 사용한 다운그레이드된 수준이며 요청한 수준이 아닙니다. 객체는 상태 줄 effort 필드와 일치합니다. PreToolUse, PostToolUse, Stop, SubagentStop과 같은 도구 사용 컨텍스트 내에서 발생하는 이벤트에 대해 현재 모델이 노력 매개변수를 지원할 때 존재합니다. 수준은 $CLAUDE_EFFORT 환경 변수로 hook 명령 및 Bash 도구에서도 사용 가능합니다. |
hook_event_name | 발생한 이벤트의 이름 |
--agent로 실행하거나 subagent 내부에서 실행할 때 두 개의 추가 필드가 포함됩니다:
| 필드 | 설명 |
|---|
agent_id | subagent의 고유 식별자. hook이 subagent 호출 내부에서 발생할 때만 존재합니다. 이를 사용하여 subagent hook 호출을 메인 스레드 호출과 구별합니다. |
agent_type | 에이전트 이름 (예: "Explore" 또는 "security-reviewer"). 세션이 --agent를 사용하거나 hook이 subagent 내부에서 발생할 때 존재합니다. subagent의 경우 subagent의 유형이 세션의 --agent 값보다 우선합니다. 사용자 정의 subagent의 경우 이는 에이전트의 frontmatter에서 name 필드이며 파일명이 아닙니다. |
SessionStart hook만 model 필드를 받습니다. $CLAUDE_MODEL 환경 변수는 없습니다. hook 프로세스는 부모 환경을 상속하므로 셸에서 설정한 경우 $ANTHROPIC_MODEL을 읽을 수 있지만 세션 중에 /model로 모델을 전환할 때 해당 값은 변경되지 않습니다.
예를 들어 Bash 명령에 대한 PreToolUse hook은 stdin에서 다음을 받습니다:
{
"session_id": "abc123",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}
tool_name 및 tool_input 필드는 이벤트 특정입니다. 각 hook 이벤트 섹션에서는 해당 이벤트의 추가 필드를 문서화합니다.
종료 코드 출력
hook 명령의 종료 코드는 Claude Code에 작업을 진행할지, 차단할지 또는 무시할지를 알려줍니다.
종료 0은 성공을 의미합니다. Claude Code는 JSON 출력 필드에 대해 stdout을 구문 분석합니다. JSON 출력은 종료 0에서만 처리됩니다. 대부분의 이벤트에서 stdout은 디버그 로그에 기록되지만 트랜스크립트에는 표시되지 않습니다. 예외는 UserPromptSubmit, UserPromptExpansion, 및 SessionStart이며, 여기서 stdout은 Claude가 보고 작용할 수 있는 컨텍스트로 추가됩니다.
종료 2는 차단 오류를 의미합니다. Claude Code는 stdout과 그 안의 JSON을 무시합니다. 대신 stderr 텍스트가 Claude에 오류 메시지로 피드백됩니다. 효과는 이벤트에 따라 다릅니다: PreToolUse는 도구 호출을 차단하고 UserPromptSubmit은 프롬프트를 거부합니다. 전체 목록은 이벤트별 종료 코드 2 동작을 참조하세요.
다른 종료 코드는 대부분의 hook 이벤트에 대한 차단하지 않는 오류입니다. 트랜스크립트는 <hook name> hook error 알림을 표시하고 stderr의 첫 번째 줄을 표시하므로 --debug 없이도 원인을 식별할 수 있습니다. 실행이 계속되고 전체 stderr은 디버그 로그에 기록됩니다.
예를 들어 위험한 Bash 명령을 차단하는 hook 명령 스크립트:
#!/bin/bash
# stdin에서 JSON 입력을 읽고 명령을 확인합니다
command=$(jq -r '.tool_input.command' < /dev/stdin)
if [[ "$command" == rm* ]]; then
echo "Blocked: rm commands are not allowed" >&2
exit 2 # 차단 오류: 도구 호출이 방지됨
fi
exit 0 # 성공: 도구 호출이 진행됨
대부분의 hook 이벤트에서 종료 코드 2만 작업을 차단합니다. Claude Code는 종료 코드 1을 차단하지 않는 오류로 취급하고 작업을 진행합니다. 1이 기존 Unix 실패 코드이지만 말입니다. hook이 정책을 적용하려면 exit 2를 사용합니다. 예외는 WorktreeCreate이며, 0이 아닌 종료 코드는 worktree 생성을 중단합니다.
이벤트별 종료 코드 2 동작
종료 코드 2는 hook이 “멈춰, 이것을 하지 마”라고 신호하는 방식입니다. 효과는 이벤트에 따라 다릅니다. 일부 이벤트는 차단할 수 있는 작업을 나타내고 (아직 발생하지 않은 도구 호출처럼) 다른 이벤트는 이미 발생했거나 방지할 수 없는 것을 나타내기 때문입니다.
| Hook 이벤트 | 차단 가능? | 종료 코드 2에서 발생하는 것 |
|---|
PreToolUse | 예 | 도구 호출을 차단합니다 |
PermissionRequest | 예 | 권한을 거부합니다 |
UserPromptSubmit | 예 | 프롬프트 처리를 차단하고 프롬프트를 지웁니다 |
UserPromptExpansion | 예 | 확장을 차단합니다 |
Stop | 예 | Claude가 중지되는 것을 방지하고 대화를 계속합니다 |
SubagentStop | 예 | subagent가 중지되는 것을 방지합니다 |
TeammateIdle | 예 | 팀원이 유휴 상태가 되는 것을 방지합니다 (팀원이 계속 작업함) |
TaskCreated | 예 | 작업 생성을 롤백합니다 |
TaskCompleted | 예 | 작업이 완료로 표시되는 것을 방지합니다 |
ConfigChange | 예 | 구성 변경이 적용되는 것을 차단합니다 (policy_settings 제외) |
StopFailure | 아니오 | 출력과 종료 코드는 무시됩니다 |
PostToolUse | 아니오 | Claude에 stderr을 표시합니다 (도구가 이미 실행됨) |
PostToolUseFailure | 아니오 | Claude에 stderr을 표시합니다 (도구가 이미 실패함) |
PostToolBatch | 예 | 다음 모델 호출 전에 에이전트 루프를 중지합니다 |
PermissionDenied | 아니오 | 종료 코드와 stderr은 무시됩니다 (거부가 이미 발생함). JSON hookSpecificOutput.retry: true를 사용하여 모델이 재시도할 수 있음을 알립니다 |
Notification | 아니오 | 사용자에게만 stderr을 표시합니다 |
SubagentStart | 아니오 | 사용자에게만 stderr을 표시합니다 |
SessionStart | 아니오 | 사용자에게만 stderr을 표시합니다 |
Setup | 아니오 | 사용자에게만 stderr을 표시합니다 |
SessionEnd | 아니오 | 사용자에게만 stderr을 표시합니다 |
CwdChanged | 아니오 | 사용자에게만 stderr을 표시합니다 |
FileChanged | 아니오 | 사용자에게만 stderr을 표시합니다 |
PreCompact | 예 | 압축을 차단합니다 |
PostCompact | 아니오 | 사용자에게만 stderr을 표시합니다 |
Elicitation | 예 | elicitation을 거부합니다 |
ElicitationResult | 예 | 응답을 차단합니다 (작업이 거부됨) |
WorktreeCreate | 예 | 0이 아닌 종료 코드로 인해 worktree 생성이 실패합니다 |
WorktreeRemove | 아니오 | 실패는 디버그 모드에서만 기록됩니다 |
InstructionsLoaded | 아니오 | 종료 코드는 무시됩니다 |
HTTP 응답 처리
HTTP hook은 종료 코드와 stdout 대신 HTTP 상태 코드와 응답 본문을 사용합니다:
- 2xx 빈 본문: 성공, 종료 코드 0과 출력 없음과 동등
- 2xx 일반 텍스트 본문: 성공, 텍스트가 컨텍스트로 추가됨
- 2xx JSON 본문: 성공, 명령 hook과 동일한 JSON 출력 스키마를 사용하여 구문 분석됨
- 2xx가 아닌 상태: 차단하지 않는 오류, 실행이 계속됨
- 연결 실패 또는 시간 초과: 차단하지 않는 오류, 실행이 계속됨
명령 hook과 달리 HTTP hook은 상태 코드만으로 차단 오류를 신호할 수 없습니다. 도구 호출을 차단하거나 권한을 거부하려면 적절한 결정 필드를 포함하는 JSON 본문이 있는 2xx 응답을 반환합니다.
JSON 출력
종료 코드를 사용하면 허용 또는 차단할 수 있지만 JSON 출력은 더 세밀한 제어를 제공합니다. 종료 코드 2로 차단하는 대신 종료 0으로 JSON 객체를 stdout에 인쇄합니다. Claude Code는 해당 JSON에서 특정 필드를 읽어 차단, 허용 또는 사용자에게 에스컬레이션을 포함한 동작을 제어합니다.
hook당 하나의 접근 방식을 선택해야 합니다. 둘 다 선택하지 마세요: 종료 코드만 사용하여 신호하거나 종료 0으로 JSON을 인쇄하여 구조화된 제어를 합니다. Claude Code는 종료 0에서만 JSON을 처리합니다. 종료 2로 나가면 JSON은 무시됩니다.
hook의 stdout은 JSON 객체만 포함해야 합니다. 셸 프로필이 시작 시 텍스트를 인쇄하면 JSON 구문 분석을 방해할 수 있습니다. 문제 해결 가이드의 JSON 검증 실패를 참조하세요.
hook 출력 문자열 (additionalContext, systemMessage, 및 일반 stdout)은 10,000자로 제한됩니다. 이 제한을 초과하는 출력은 파일에 저장되고 미리보기 및 파일 경로로 바뀌며, 큰 도구 결과가 처리되는 방식과 동일합니다.
JSON 객체는 세 가지 종류의 필드를 지원합니다:
continue와 같은 범용 필드는 모든 이벤트에서 작동합니다. 이들은 아래 표에 나열되어 있습니다.
- **최상위
decision 및 reason**은 일부 이벤트에서 차단하거나 피드백을 제공하는 데 사용됩니다.
- **
hookSpecificOutput**은 더 풍부한 제어가 필요한 이벤트를 위한 중첩 객체입니다. 이벤트 이름으로 설정된 hookEventName 필드가 필요합니다.
| 필드 | 기본값 | 설명 |
|---|
continue | true | false인 경우 hook이 실행된 후 Claude가 완전히 중지됩니다. 모든 이벤트 특정 결정 필드보다 우선합니다 |
stopReason | 없음 | continue가 false일 때 사용자에게 표시되는 메시지. Claude에는 표시되지 않음 |
suppressOutput | false | true인 경우 디버그 로그에서 stdout을 숨깁니다 |
systemMessage | 없음 | 사용자에게 표시되는 경고 메시지 |
terminalSequence | 없음 | Claude Code가 사용자를 대신하여 내보낼 터미널 이스케이프 시퀀스 (예: 데스크톱 알림, 창 제목 또는 벨). OSC 0/1/2/9/99/777 및 BEL로 제한됩니다. 값에 허용 목록 외의 항목이 포함되면 필드는 무시됩니다. /dev/tty를 사용할 수 없는 hook 대신 이를 사용합니다 |
Claude를 이벤트 유형과 관계없이 완전히 중지하려면:
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }
터미널 알림 내보내기
terminalSequence 필드는 Claude Code v2.1.141 이상이 필요합니다.
Hook은 제어 터미널 없이 실행되므로 이스케이프 시퀀스를 /dev/tty에 직접 쓰는 것이 실패합니다. 대신 terminalSequence 필드에 이스케이프 시퀀스를 반환하면 Claude Code가 자신의 터미널 쓰기 경로를 통해 이를 내보냅니다. 이는 race-free이고 tmux 및 GNU screen 내에서 작동하며 /dev/tty가 없는 Windows에서도 작동합니다.
필드는 하나 이상의 허용 목록에 있는 이스케이프 시퀀스 문자열을 허용합니다:
- OSC
0, 1, 2: 창 및 아이콘 제목
- OSC
9: iTerm2, ConEmu, Windows Terminal, 및 WezTerm 알림 (9;4 작업 표시줄 진행률 포함)
- OSC
99: Kitty 알림
- OSC
777: urxvt, Ghostty, 및 Warp 알림
- 맨 BEL
시퀀스는 BEL 또는 ST로 종료될 수 있습니다. 허용 목록 외의 항목 (CSI 커서 및 색상 시퀀스, OSC 팔레트 시퀀스, OSC 8 하이퍼링크, OSC 52 클립보드 쓰기, 및 OSC 1337 포함)은 거부되고 필드는 무시됩니다.
아래 예제는 Notification hook에서 데스크톱 알림을 발생시킵니다. 이스케이프 시퀀스는 printf 8진수 이스케이프로 빌드되므로 제어 바이트가 셸 명령줄에 나타나지 않으며, jq -n --arg는 JSON 출력을 빌드하므로 알림 메시지의 따옴표, 백슬래시, 및 줄바꿈이 올바르게 이스케이프됩니다:
#!/bin/bash
# Notification hook: Claude Code가 주의가 필요할 때 데스크톱을 ping합니다.
input=$(cat)
title="Claude Code'
body=$(jq -r '.message // 'Needs your attention"' <<<"$input")
seq=$(printf '\033]777;notify;%s;%s\007' "$title" "$body")
jq -nc --arg seq "$seq" '{terminalSequence: $seq}'
{ "terminalSequence": "..." } 형태는 모든 셸 또는 언어에서 동일합니다. Windows에서는 PowerShell 또는 스크립트에서 이스케이프 문자열을 빌드하고 동일한 JSON 객체를 내보냅니다.
terminalSequence는 이전에 /dev/tty에 직접 이스케이프 시퀀스를 작성한 hook의 지원되는 대체입니다. 허용 목록은 커서를 이동하거나 색상을 변경할 수 없는 시퀀스로 제한되므로 hook은 화면상의 프롬프트를 손상시킬 수 없습니다.
Claude를 위한 컨텍스트 추가
additionalContext 필드는 hook에서 Claude의 컨텍스트 윈도우로 문자열을 전달합니다. Claude Code는 문자열을 시스템 미리 알림으로 래핑하고 hook이 발생한 지점에서 대화에 삽입합니다. Claude는 다음 모델 요청에서 미리 알림을 읽지만 인터페이스에 채팅 메시지로 나타나지 않습니다.
이벤트 이름과 함께 hookSpecificOutput 내에 additionalContext를 반환합니다:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "This file is generated. Edit src/schema.ts and run `bun generate` instead."
}
}
미리 알림이 나타나는 위치는 이벤트에 따라 다릅니다:
여러 hook이 동일한 이벤트에 대해 additionalContext를 반환하면 Claude는 모든 값을 받습니다. 값이 10,000자를 초과하면 Claude Code는 전체 텍스트를 세션 디렉토리의 파일에 쓰고 짧은 미리보기와 함께 파일 경로를 Claude에 전달합니다.
Claude가 현재 환경 상태 또는 방금 실행된 작업에 대해 알아야 할 정보에 additionalContext를 사용합니다:
- 환경 상태: 현재 분기, 배포 대상 또는 활성 기능 플래그
- 조건부 프로젝트 규칙: 방금 편집한 파일에 적용되는 테스트 명령, 이 worktree에서 읽기 전용인 디렉토리
- 외부 데이터: 사용자에게 할당된 열린 문제, 최근 CI 결과, 내부 서비스에서 가져온 콘텐츠
변경되지 않는 지침의 경우 CLAUDE.md를 선호합니다. 스크립트를 실행하지 않고 로드되며 정적 프로젝트 규칙의 표준 위치입니다.
명령형 시스템 지침이 아닌 사실 진술로 텍스트를 작성합니다. “배포 대상은 프로덕션입니다” 또는 “이 리포지토리는 bun test를 사용합니다”와 같은 표현은 프로젝트 정보로 읽힙니다. 대역 외 시스템 명령으로 표현된 텍스트는 Claude의 프롬프트 주입 방어를 트리거할 수 있으며, 이로 인해 Claude가 텍스트를 컨텍스트로 취급하는 대신 사용자에게 표시합니다.
주입되면 텍스트는 세션 트랜스크립트에 저장됩니다. PostToolUse 또는 UserPromptSubmit과 같은 중간 세션 이벤트의 경우 --continue 또는 --resume으로 재개하면 과거 턴에 대해 hook을 다시 실행하는 대신 저장된 텍스트를 재생하므로 타임스탬프 또는 커밋 SHA와 같은 값이 재개 시 오래됩니다. SessionStart hook은 source가 "resume"으로 설정된 재개 시 다시 실행되므로 컨텍스트를 새로 고칠 수 있습니다.
결정 제어
모든 이벤트가 JSON을 통해 동작을 차단하거나 제어하는 것을 지원하는 것은 아닙니다. 그렇게 하는 이벤트는 각각 다른 필드 집합을 사용하여 해당 결정을 표현합니다. hook을 작성하기 전에 이 표를 빠른 참조로 사용하세요:
| 이벤트 | 결정 패턴 | 주요 필드 |
|---|
| UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact | 최상위 decision | decision: "block", reason |
| TeammateIdle, TaskCreated, TaskCompleted | 종료 코드 또는 continue: false | 종료 코드 2는 stderr 피드백으로 작업을 차단합니다. JSON {"continue": false, "stopReason": "..."} 또한 팀원을 완전히 중지하여 Stop hook 동작과 일치합니다 |
| PreToolUse | hookSpecificOutput | permissionDecision (allow/deny/ask/defer), permissionDecisionReason |
| PermissionRequest | hookSpecificOutput | decision.behavior (allow/deny) |
| PermissionDenied | hookSpecificOutput | retry: true는 모델이 거부된 도구 호출을 재시도할 수 있음을 알립니다 |
| WorktreeCreate | 경로 반환 | 명령 hook은 stdout에 경로를 인쇄합니다; HTTP hook은 hookSpecificOutput.worktreePath를 반환합니다. hook 실패 또는 누락된 경로는 생성을 실패합니다 |
| Elicitation | hookSpecificOutput | action (accept/decline/cancel), content (form field values for accept) |
| ElicitationResult | hookSpecificOutput | action (accept/decline/cancel), content (form field values override) |
| WorktreeRemove, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, FileChanged | 없음 | 결정 제어 없음. 로깅 또는 정리와 같은 부작용에 사용됨 |
다음은 각 패턴의 실제 예입니다:
최상위 결정
PreToolUse
PermissionRequest
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact에서 사용됩니다. 유일한 값은 "block"입니다. 작업을 진행하도록 허용하려면 JSON에서 decision을 생략하거나 JSON 없이 종료 0으로 나갑니다:{
"decision": "block",
"reason": "Test suite must pass before proceeding"
}
더 풍부한 제어를 위해 hookSpecificOutput을 사용합니다: 허용, 거부, 요청 또는 연기. 실행 전에 도구 입력을 수정하거나 Claude를 위한 추가 컨텍스트를 주입할 수도 있습니다. 전체 옵션 집합은 PreToolUse 결정 제어를 참조하세요.{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Database writes are not allowed"
}
}
hookSpecificOutput을 사용하여 사용자를 대신하여 권한 요청을 허용하거나 거부합니다. 허용할 때 도구의 입력을 수정하거나 권한 규칙을 적용하여 사용자가 다시 프롬프트되지 않도록 할 수 있습니다. 전체 옵션 집합은 PermissionRequest 결정 제어를 참조하세요.{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}
}
Bash 명령 검증, 프롬프트 필터링, 자동 승인 스크립트를 포함한 확장 예제는 가이드의 자동화할 수 있는 것과 Bash 명령 검증기 참조 구현을 참조하세요.
Hook 이벤트
각 이벤트는 hook이 실행될 수 있는 Claude Code의 수명 주기의 지점에 해당합니다. 아래 섹션은 수명 주기와 일치하도록 정렬됩니다: 세션 설정에서 에이전트 루프를 거쳐 세션 종료까지. 각 섹션에서는 이벤트가 언제 발생하는지, 지원하는 matcher, 받는 JSON 입력, 출력을 통해 동작을 제어하는 방법을 설명합니다.
SessionStart
Claude Code가 새 세션을 시작하거나 기존 세션을 재개할 때 실행됩니다. 기존 문제나 코드베이스의 최근 변경 사항과 같은 개발 컨텍스트를 로드하거나 환경 변수를 설정하는 데 유용합니다. 스크립트가 필요하지 않은 정적 컨텍스트의 경우 CLAUDE.md를 사용하세요.
SessionStart는 모든 세션에서 실행되므로 이러한 hook을 빠르게 유지하세요. type: "command" 및 type: "mcp_tool" 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/...",
"hook_event_name": "SessionStart",
"source": "startup",
"model": "claude-sonnet-4-6"
}
SessionStart 결정 제어
hook 스크립트가 stdout에 인쇄하는 모든 텍스트는 Claude의 컨텍스트로 추가됩니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 이러한 이벤트 특정 필드를 반환할 수 있습니다:
| 필드 | 설명 |
|---|
additionalContext | Claude의 컨텍스트 시작 부분에 추가되는 문자열. 첫 번째 프롬프트 전에 추가됩니다. Claude를 위한 컨텍스트 추가를 참조하여 텍스트가 전달되는 방식과 포함할 내용을 확인하세요 |
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2"
}
}
이 이벤트에 대해 일반 stdout이 이미 Claude에 도달하므로 컨텍스트만 로드하는 hook은 JSON을 구축하지 않고 stdout에 직접 인쇄할 수 있습니다. suppressOutput과 같은 다른 필드와 컨텍스트를 결합해야 할 때 JSON 형식을 사용합니다.
환경 변수 유지
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)
# 환경을 수정하는 설정 명령을 실행합니다
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 명령에서 사용 가능합니다.
Setup
--init-only로 Claude Code를 시작하거나 print 모드 (-p)에서 --init 또는 --maintenance로 시작할 때만 발생합니다. 일반 시작 시에는 발생하지 않습니다. 일회성 종속성 설치 또는 CI 또는 스크립트에서 명시적으로 트리거하는 예약된 정리에 사용합니다. 일반 세션 시작과 별도입니다. 세션별 초기화의 경우 SessionStart를 대신 사용합니다.
matcher 값은 hook을 트리거한 CLI 플래그에 해당합니다:
| Matcher | 언제 발생하는지 |
|---|
init | claude --init-only 또는 claude -p --init |
maintenance | claude -p --maintenance |
--init-only는 Setup hook과 startup matcher가 있는 SessionStart hook을 실행한 다음 대화를 시작하지 않고 종료합니다. --init 및 --maintenance는 -p (print 모드)와 결합할 때만 Setup hook을 발생시킵니다; 대화형 세션에서 이 두 플래그는 현재 Setup hook을 발생시키지 않습니다.
Setup은 모든 시작 시 발생하지 않으므로 종속성이 설치된 plugin은 Setup만으로는 의존할 수 없습니다. 실제 패턴은 첫 사용 시 종속성을 확인하고 누락되면 설치하는 것입니다. 예를 들어 ${CLAUDE_PLUGIN_DATA}/node_modules를 테스트하고 없으면 npm install을 실행하는 hook 또는 skill입니다. 설치된 종속성을 저장할 위치는 지속적 데이터 디렉토리를 참조하세요.
Setup 입력
공통 입력 필드 외에도 Setup hook은 trigger 필드를 받으며, 이는 "init" 또는 "maintenance"로 설정됩니다:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "Setup",
"trigger": "init"
}
Setup 결정 제어
Setup hook은 차단할 수 없습니다. 종료 코드 2에서 stderr이 사용자에게 표시됩니다; 다른 0이 아닌 종료 코드에서 stderr은 --verbose로 시작할 때만 나타납니다. 두 경우 모두 실행이 계속됩니다. Claude의 컨텍스트에 정보를 전달하려면 JSON 출력에서 additionalContext를 반환합니다; 일반 stdout은 디버그 로그에만 작성됩니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 이러한 이벤트 특정 필드를 반환할 수 있습니다:
| 필드 | 설명 |
|---|
additionalContext | Claude의 컨텍스트에 추가되는 문자열. 여러 hook의 값이 연결됨 |
{
"hookSpecificOutput": {
"hookEventName": "Setup",
"additionalContext": "Dependencies installed: node_modules, .venv"
}
}
Setup hook은 CLAUDE_ENV_FILE에 액세스할 수 있습니다. 해당 파일에 작성된 변수는 SessionStart hook과 마찬가지로 세션의 후속 Bash 명령에 유지됩니다. type: "command" 및 type: "mcp_tool" hook만 지원됩니다.
InstructionsLoaded
CLAUDE.md 또는 .claude/rules/*.md 파일이 컨텍스트에 로드될 때 발생합니다. 이 이벤트는 세션 시작 시 즉시 로드된 파일에 대해 발생하고 나중에 파일이 지연 로드될 때 다시 발생합니다. 예를 들어 Claude가 중첩된 CLAUDE.md를 포함하는 하위 디렉토리에 액세스할 때 또는 paths: frontmatter가 있는 조건부 규칙이 일치할 때입니다. hook은 차단 또는 결정 제어를 지원하지 않습니다. 관찰성 목적으로 비동기적으로 실행됩니다.
matcher는 load_reason에 대해 실행됩니다. 예를 들어 "matcher": "session_start"를 사용하여 세션 시작 시에만 로드된 파일에 대해 발생하거나 "matcher": "path_glob_match|nested_traversal"을 사용하여 지연 로드에만 발생합니다.
InstructionsLoaded 입력
공통 입력 필드 외에도 InstructionsLoaded hook은 이러한 필드를 받습니다:
| 필드 | 설명 |
|---|
file_path | 로드된 명령 파일의 절대 경로 |
memory_type | 파일의 범위: "User", "Project", "Local" 또는 "Managed" |
load_reason | 파일이 로드된 이유: "session_start", "nested_traversal", "path_glob_match", "include" 또는 "compact". "compact" 값은 압축 이벤트 후 명령 파일이 다시 로드될 때 발생합니다 |
globs | 파일의 paths: frontmatter의 경로 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",
"hook_event_name": "InstructionsLoaded",
"file_path": "/Users/my-project/CLAUDE.md",
"memory_type": "Project",
"load_reason": "session_start"
}
InstructionsLoaded 결정 제어
InstructionsLoaded hook은 결정 제어가 없습니다. 명령 로드를 차단하거나 수정할 수 없습니다. 감사 로깅, 규정 준수 추적 또는 관찰성을 위해 이 이벤트를 사용합니다.
UserPromptSubmit
사용자가 프롬프트를 제출할 때, Claude가 처리하기 전에 실행됩니다. 이를 통해 프롬프트/대화를 기반으로 추가 컨텍스트를 추가하거나, 프롬프트를 검증하거나, 특정 유형의 프롬프트를 차단할 수 있습니다.
UserPromptSubmit hook은 command, http, mcp_tool 유형에 대해 기본 30초 시간 초과를 가지며, 이는 다른 이벤트에서 이러한 유형의 기본 600초보다 짧습니다. 이 hook은 모든 프롬프트 전에 실행되고 모델 처리가 완료될 때까지 차단하므로 stuck hook은 세션을 정지시킵니다. hook에 더 많은 시간이 필요하면 hook 항목에서 timeout 필드를 설정합니다.
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의 컨텍스트에 추가되는 문자열. Claude를 위한 컨텍스트 추가를 참조하세요 |
sessionTitle | 세션 제목을 설정합니다. 프롬프트 내용을 기반으로 세션을 자동으로 이름 지정하는 데 사용합니다 |
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "My additional context here",
"sessionTitle": "My session title"
}
}
JSON 형식은 간단한 사용 사례에는 필요하지 않습니다. 컨텍스트를 추가하려면 종료 코드 0으로 stdout에 일반 텍스트를 인쇄할 수 있습니다. 프롬프트를 차단하거나 더 구조화된 제어가 필요할 때 JSON을 사용합니다.
UserPromptExpansion
사용자가 입력한 slash 명령이 Claude에 도달하기 전에 프롬프트로 확장될 때 실행됩니다. 이를 사용하여 특정 명령이 직접 호출되는 것을 차단하거나, 특정 skill에 대한 컨텍스트를 주입하거나, 사용자가 호출하는 명령을 기록합니다. 예를 들어 deploy와 일치하는 hook은 승인 파일이 없으면 /deploy를 차단할 수 있고, review skill과 일치하는 hook은 팀의 review 체크리스트를 additionalContext로 추가할 수 있습니다.
이 이벤트는 PreToolUse가 다루지 않는 경로를 다룹니다: PreToolUse hook이 Skill 도구와 일치하면 Claude가 도구를 호출할 때만 발생하지만, /skillname을 직접 입력하면 PreToolUse를 우회합니다. UserPromptExpansion은 그 직접 경로에서 발생합니다.
command_name에서 일치합니다. matcher를 비워두어 모든 prompt 유형 slash 명령에서 발생하도록 합니다.
UserPromptExpansion 입력
공통 입력 필드 외에도 UserPromptExpansion hook은 expansion_type, command_name, command_args, command_source, 원본 prompt 문자열을 받습니다. expansion_type 필드는 skill 및 사용자 정의 명령의 경우 slash_command이거나 MCP 서버 프롬프트의 경우 mcp_prompt입니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../00893aaf.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "UserPromptExpansion",
"expansion_type": "slash_command",
"command_name": "example-skill",
"command_args": "arg1 arg2",
"command_source": "plugin",
"prompt": "/example-skill arg1 arg2"
}
UserPromptExpansion 결정 제어
UserPromptExpansion hook은 확장을 차단하거나 컨텍스트를 추가할 수 있습니다. 모든 JSON 출력 필드를 사용할 수 있습니다.
| 필드 | 설명 |
|---|
decision | "block"은 slash 명령이 확장되는 것을 방지합니다. 생략하여 진행하도록 허용 |
reason | decision이 "block"일 때 사용자에게 표시됩니다 |
additionalContext | 확장된 프롬프트와 함께 Claude의 컨텍스트에 추가되는 문자열. Claude를 위한 컨텍스트 추가를 참조하세요 |
{
"decision": "block",
"reason": "This slash command is not available",
"hookSpecificOutput": {
"hookEventName": "UserPromptExpansion",
"additionalContext": "Additional context for this expansion"
}
}
Claude가 도구 매개변수를 생성한 후 도구 호출을 처리하기 전에 실행됩니다. 도구 이름에서 일치합니다: Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch, WebSearch, AskUserQuestion, ExitPlanMode, 모든 MCP 도구 이름.
PreToolUse 결정 제어를 사용하여 도구 사용을 허용, 거부, 요청 또는 연기합니다.
공통 입력 필드 외에도 PreToolUse hook은 tool_name, tool_input, tool_use_id를 받습니다. tool_input 필드는 도구에 따라 다릅니다:
Bash
셸 명령을 실행합니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
command | 문자열 | "npm test" | 실행할 셸 명령 |
description | 문자열 | "Run test suite" | 명령이 수행하는 작업의 선택적 설명 |
timeout | 숫자 | 120000 | 선택적 시간 초과 (밀리초) |
run_in_background | 부울 | false | 명령을 백그라운드에서 실행할지 여부 |
Write
파일을 생성하거나 덮어씁니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
file_path | 문자열 | "/path/to/file.txt" | 쓸 파일의 절대 경로 |
content | 문자열 | "file content" | 파일에 쓸 내용 |
Edit
기존 파일의 문자열을 바꿉니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
file_path | 문자열 | "/path/to/file.txt" | 편집할 파일의 절대 경로 |
old_string | 문자열 | "original text" | 찾아 바꿀 텍스트 |
new_string | 문자열 | "replacement text" | 대체 텍스트 |
replace_all | 부울 | false | 모든 발생을 바꿀지 여부 |
Read
파일 내용을 읽습니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
file_path | 문자열 | "/path/to/file.txt" | 읽을 파일의 절대 경로 |
offset | 숫자 | 10 | 읽기를 시작할 선택적 줄 번호 |
limit | 숫자 | 50 | 읽을 선택적 줄 수 |
Glob
glob 패턴과 일치하는 파일을 찾습니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
pattern | 문자열 | "**/*.ts" | 파일과 일치시킬 glob 패턴 |
path | 문자열 | "/path/to/dir" | 검색할 선택적 디렉토리. 기본값은 현재 작업 디렉토리 |
Grep
정규식으로 파일 내용을 검색합니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
pattern | 문자열 | "TODO.*fix" | 검색할 정규식 패턴 |
path | 문자열 | "/path/to/dir" | 검색할 선택적 파일 또는 디렉토리 |
glob | 문자열 | "*.ts" | 파일을 필터링할 선택적 glob 패턴 |
output_mode | 문자열 | "content" | "content", "files_with_matches" 또는 "count". 기본값은 "files_with_matches" |
-i | 부울 | true | 대소문자를 구분하지 않는 검색 |
multiline | 부울 | false | 다중 줄 일치 활성화 |
WebFetch
웹 콘텐츠를 가져오고 처리합니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
url | 문자열 | "https://example.com/api" | 콘텐츠를 가져올 URL |
prompt | 문자열 | "Extract the API endpoints" | 가져온 콘텐츠에서 실행할 프롬프트 |
WebSearch
웹을 검색합니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
query | 문자열 | "react hooks best practices" | 검색 쿼리 |
allowed_domains | 배열 | ["docs.example.com"] | 선택적: 이러한 도메인의 결과만 포함 |
blocked_domains | 배열 | ["spam.example.com"] | 선택적: 이러한 도메인의 결과 제외 |
Agent
subagent를 생성합니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
prompt | 문자열 | "Find all API endpoints" | 에이전트가 수행할 작업 |
description | 문자열 | "Find API endpoints" | 작업의 짧은 설명 |
subagent_type | 문자열 | "Explore" | 사용할 특화된 에이전트의 유형 |
model | 문자열 | "sonnet" | 기본값을 재정의할 선택적 모델 별칭 |
PostToolUse에서 완료된 Agent 호출의 tool_response는 subagent의 최종 텍스트와 사용 원격 측정을 전달합니다. hook에서 subagent별 비용을 기록하려면 이러한 필드를 읽으세요:
| 필드 | 유형 | 예제 | 설명 |
|---|
status | 문자열 | "completed" | 동기 호출의 경우 "completed", run_in_background: true의 경우 "async_launched" |
agentId | 문자열 | "a4d2c8f1e0b3a297" | subagent 실행의 식별자 |
content | 배열 | [{"type": "text", "text": "Found 12 endpoints..."}] | subagent의 최종 텍스트 블록 |
totalTokens | 숫자 | 12450 | subagent의 턴 전체에서 청구된 총 토큰 |
totalDurationMs | 숫자 | 48211 | subagent 실행의 벽시계 기간 |
totalToolUseCount | 숫자 | 7 | subagent가 수행한 도구 호출 수 |
usage | 객체 | {"input_tokens": 8320, ...} | 유형별 토큰 분석: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens |
run_in_background: true 호출의 경우 도구는 subagent를 시작한 후 즉시 반환되므로 tool_response는 사용 필드를 전달하지 않습니다. status: "async_launched", agentId, description, prompt, outputFile이 있습니다.
AskUserQuestion
사용자에게 1~4개의 객관식 질문을 합니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
questions | 배열 | [{"question": "Which framework?", "header": "Framework", "options": [{"label": "React"}], "multiSelect": false}] | 제시할 질문, 각각 question 문자열, 짧은 header, options 배열, 선택적 multiSelect 플래그 |
answers | 객체 | {"Which framework?": "React"} | 선택적. 질문 텍스트를 선택한 옵션 레이블로 매핑합니다. 다중 선택 답변은 쉼표로 레이블을 결합합니다. Claude는 이 필드를 설정하지 않습니다. updatedInput을 통해 프로그래밍 방식으로 답변을 제공하세요 |
ExitPlanMode
Claude가 plan 모드를 떠나기 전에 계획을 제시하고 사용자에게 승인을 요청합니다. Claude는 도구를 호출하기 전에 계획을 파일에 디스크에 작성하므로 모델의 리터럴 tool_input은 allowedPrompts만 전달합니다. Claude Code는 hook에 전달하기 전에 계획 내용과 파일 경로를 주입합니다.
| 필드 | 유형 | 예제 | 설명 |
|---|
plan | 문자열 | "## Refactor auth\n1. Extract..." | Markdown의 계획 내용. 디스크의 계획 파일에서 주입됨 |
planFilePath | 문자열 | "/Users/.../plans/refactor-auth.md" | 계획 파일의 경로. 주입됨 |
allowedPrompts | 배열 | [{"tool": "Bash", "prompt": "run tests"}] | 선택적. Claude가 계획을 구현하기 위해 요청하는 prompt 기반 권한, 각각 tool 이름과 작업 범주를 설명하는 prompt |
PostToolUse에서 tool_response는 승인된 계획을 보유하는 plan 및 filePath 필드가 있는 객체이며, 내부 상태 플래그도 있습니다. 디스크에서 파일을 다시 읽는 대신 tool_response.plan에서 계획 내용을 읽으세요.
PreToolUse hook은 도구 호출 진행 여부를 제어할 수 있습니다. 최상위 decision 필드를 사용하는 다른 hook과 달리 PreToolUse는 hookSpecificOutput 객체 내에 결정을 반환합니다. 이는 더 풍부한 제어를 제공합니다: 네 가지 결과 (허용, 거부, 요청 또는 연기) 및 실행 전에 도구 입력을 수정하는 기능.
| 필드 | 설명 |
|---|
permissionDecision | "allow"는 권한 시스템을 우회합니다. "deny"는 도구 호출을 방지합니다. "ask"는 사용자에게 확인을 요청합니다. "defer"는 나중에 재개하도록 연기합니다. 권한 거부 및 요청 규칙은 hook이 반환하는 것과 관계없이 여전히 평가됩니다 |
permissionDecisionReason | "allow" 및 "ask"의 경우 사용자에게 표시되지만 Claude에는 표시되지 않습니다. "deny"의 경우 Claude에 표시됩니다. "defer"의 경우 무시됩니다 |
updatedInput | 실행 전에 도구의 입력 매개변수를 수정합니다. 전체 입력 객체를 바꾸므로 변경되지 않은 필드를 수정된 필드와 함께 포함합니다. "allow"와 결합하여 자동 승인하거나 "ask"와 결합하여 수정된 입력을 사용자에게 표시합니다. "defer"의 경우 무시됩니다 |
additionalContext | 도구 결과와 함께 Claude의 컨텍스트에 추가되는 문자열. "defer"의 경우 무시됩니다. Claude를 위한 컨텍스트 추가를 참조하세요 |
여러 PreToolUse hook이 다른 결정을 반환할 때 우선순위는 deny > defer > ask > allow입니다.
hook이 "ask"를 반환하면 사용자에게 표시되는 권한 프롬프트에는 hook이 어디에서 왔는지를 나타내는 레이블이 포함됩니다: 예를 들어 [User], [Project], [Plugin] 또는 [Local]. 이는 사용자가 어느 구성 소스가 확인을 요청하는지 이해하는 데 도움이 됩니다.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "My reason here",
"updatedInput": {
"field_to_modify": "new value"
},
"additionalContext": "Current environment: production. Proceed with caution."
}
}
AskUserQuestion 및 ExitPlanMode는 사용자 상호 작용이 필요하며 일반적으로 비대화형 모드에서 -p 플래그로 차단합니다. permissionDecision: "allow"를 updatedInput과 함께 반환하면 해당 요구 사항을 충족합니다: hook은 stdin에서 도구의 입력을 읽고 자신의 UI를 통해 답변을 수집하고 updatedInput에서 반환하여 도구가 프롬프트 없이 실행되도록 합니다. "allow"만 반환하는 것은 이러한 도구에 충분하지 않습니다. AskUserQuestion의 경우 원본 questions 배열을 에코백하고 각 질문의 텍스트를 선택한 답변으로 매핑하는 answers 객체를 추가합니다.
PreToolUse는 이전에 최상위 decision 및 reason 필드를 사용했지만 이 이벤트에는 더 이상 사용되지 않습니다. 대신 hookSpecificOutput.permissionDecision 및 hookSpecificOutput.permissionDecisionReason을 사용합니다. 더 이상 사용되지 않는 값 "approve" 및 "block"은 각각 "allow" 및 "deny"로 매핑됩니다. PostToolUse 및 Stop과 같은 다른 이벤트는 계속 최상위 decision 및 reason을 현재 형식으로 사용합니다.
도구 호출을 나중에 재개하도록 연기
"defer"는 Claude Code를 subprocess로 실행하고 JSON 출력을 읽는 Agent SDK 앱 또는 Claude Code 위에 구축된 사용자 정의 UI와 같은 통합을 위한 것입니다. 이를 통해 호출 프로세스가 Claude를 도구 호출에서 일시 중지하고 자신의 인터페이스를 통해 입력을 수집하고 중단된 위치에서 재개할 수 있습니다. Claude Code는 비대화형 모드에서 -p 플래그를 사용할 때만 이 값을 준수합니다. 대화형 세션에서는 경고를 기록하고 hook 결과를 무시합니다.
defer 값은 Claude Code v2.1.89 이상이 필요합니다. 이전 버전은 이를 인식하지 못하고 도구는 일반 권한 흐름을 통해 진행됩니다.
일반적인 경우는 AskUserQuestion 도구입니다: Claude가 사용자에게 뭔가를 묻고 싶지만 답변할 터미널이 없습니다. 왕복은 다음과 같이 작동합니다:
- Claude가
AskUserQuestion을 호출합니다. PreToolUse hook이 발생합니다.
- hook이
permissionDecision: "defer"를 반환합니다. 도구가 실행되지 않습니다. 프로세스는 stop_reason: "tool_deferred"로 종료되고 보류 중인 도구 호출이 트랜스크립트에 유지됩니다.
- 호출 프로세스는 SDK 결과에서
deferred_tool_use를 읽고 자신의 UI에서 질문을 표시하고 답변을 기다립니다.
- 호출 프로세스는
claude -p --resume <session-id>를 실행합니다. 동일한 도구 호출이 PreToolUse를 다시 발생시킵니다.
- hook이
permissionDecision: "allow"를 updatedInput의 답변과 함께 반환합니다. 도구가 실행되고 Claude가 계속됩니다.
deferred_tool_use 필드는 도구의 id, name, input을 전달합니다. input은 실행 전에 캡처된 도구 호출을 위해 Claude가 생성한 매개변수입니다:
{
"type": "result",
"subtype": "success",
"stop_reason": "tool_deferred",
"session_id": "abc123",
"deferred_tool_use": {
"id": "toolu_01abc",
"name": "AskUserQuestion",
"input": { "questions": [{ "question": "Which framework?", "header": "Framework", "options": [{"label": "React"}, {"label": "Vue"}], "multiSelect": false }] }
}
}
시간 초과 또는 재시도 제한이 없습니다. 세션은 재개할 때까지 디스크에 유지됩니다. 재개할 때 답변이 준비되지 않으면 hook이 "defer"를 다시 반환할 수 있고 프로세스는 동일한 방식으로 종료됩니다. 호출 프로세스는 결국 "allow" 또는 "deny"를 반환하여 루프를 끝낼 시기를 제어합니다.
"defer"는 Claude가 한 번에 단일 도구 호출을 만들 때만 작동합니다. Claude가 여러 도구 호출을 한 번에 만들면 "defer"는 경고와 함께 무시되고 도구는 일반 권한 흐름을 통해 진행됩니다. 제약이 존재하는 이유는 재개가 하나의 도구만 다시 실행할 수 있기 때문입니다: 다른 도구를 미해결 상태로 두지 않고 배치에서 하나의 호출을 연기할 방법이 없습니다.
연기된 도구가 재개할 때 더 이상 사용 가능하지 않으면 프로세스는 stop_reason: "tool_deferred_unavailable"과 is_error: true로 종료되고 hook이 발생하기 전에 종료됩니다. 이는 도구를 제공한 MCP 서버가 재개된 세션에 연결되지 않을 때 발생합니다. deferred_tool_use 페이로드는 여전히 포함되므로 어느 도구가 누락되었는지 식별할 수 있습니다.
--resume은 도구가 연기되었을 때 활성화된 권한 모드를 복원하므로 재개할 때 --permission-mode를 다시 전달할 필요가 없습니다. 예외는 plan 및 bypassPermissions이며, 이들은 절대 이월되지 않습니다. 재개할 때 --permission-mode를 명시적으로 전달하면 복원된 값을 재정의합니다.
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": "addRules",
"rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
"behavior": "allow",
"destination": "localSettings"
}
]
}
PermissionRequest 결정 제어
PermissionRequest hook은 권한 요청을 허용하거나 거부할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 hook 스크립트는 이러한 이벤트 특정 필드가 있는 decision 객체를 반환할 수 있습니다:
| 필드 | 설명 |
|---|
behavior | "allow"는 권한을 부여하고, "deny"는 거부합니다. 권한 거부 및 요청 규칙은 여전히 평가되므로 hook이 "allow"를 반환해도 일치하는 거부 규칙을 재정의하지 않습니다 |
updatedInput | "allow"만 해당: 실행 전에 도구의 입력 매개변수를 수정합니다. 전체 입력 객체를 바꾸므로 변경되지 않은 필드를 수정된 필드와 함께 포함합니다. 수정된 입력은 거부 및 요청 규칙에 대해 다시 평가됩니다 |
updatedPermissions | "allow"만 해당: 적용할 권한 업데이트 항목 배열, 예를 들어 허용 규칙 추가 또는 세션 권한 모드 변경 |
message | "deny"만 해당: Claude에 권한이 거부된 이유를 알립니다 |
interrupt | "deny"만 해당: true인 경우 Claude를 중지합니다 |
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}
}
권한 업데이트 항목
updatedPermissions 출력 필드와 permission_suggestions 입력 필드 모두 동일한 항목 객체 배열을 사용합니다. 각 항목에는 다른 필드를 결정하는 type과 변경이 작성되는 위치를 제어하는 destination이 있습니다.
type | 필드 | 효과 |
|---|
addRules | rules, behavior, destination | 권한 규칙을 추가합니다. rules는 {toolName, ruleContent?} 객체의 배열입니다. 전체 도구와 일치하려면 ruleContent를 생략합니다. behavior는 "allow", "deny" 또는 "ask"입니다 |
replaceRules | rules, behavior, destination | 주어진 behavior의 모든 규칙을 destination에서 제공된 rules로 바꿉니다 |
removeRules | rules, behavior, destination | 주어진 behavior의 일치하는 규칙을 제거합니다 |
setMode | mode, destination | 권한 모드를 변경합니다. 유효한 모드는 default, acceptEdits, dontAsk, bypassPermissions, plan입니다 |
addDirectories | directories, destination | 작업 디렉토리를 추가합니다. directories는 경로 문자열의 배열입니다 |
removeDirectories | directories, destination | 작업 디렉토리를 제거합니다 |
setMode와 bypassPermissions는 세션이 이미 bypass 모드를 사용 가능하게 시작된 경우에만 적용됩니다: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions, 또는 설정의 permissions.defaultMode: "bypassPermissions", 그리고 모드가 permissions.disableBypassPermissionsMode에 의해 비활성화되지 않은 경우입니다. 그렇지 않으면 업데이트는 작동하지 않습니다. bypassPermissions는 destination과 관계없이 defaultMode로 절대 유지되지 않습니다.
모든 항목의 destination 필드는 변경이 메모리에만 유지되는지 또는 설정 파일에 유지되는지를 결정합니다.
destination | 쓰기 대상 |
|---|
session | 메모리 전용, 세션이 끝나면 삭제됨 |
localSettings | .claude/settings.local.json |
projectSettings | .claude/settings.json |
userSettings | ~/.claude/settings.json |
hook은 받은 permission_suggestions 중 하나를 자신의 updatedPermissions 출력으로 에코할 수 있으며, 이는 사용자가 대화 상자에서 해당 “항상 허용” 옵션을 선택하는 것과 동등합니다.
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...",
"duration_ms": 12
}
| 필드 | 설명 |
|---|
duration_ms | 선택적. 도구 실행 시간 (밀리초). 권한 프롬프트 및 PreToolUse hook에 소요된 시간 제외 |
PostToolUse 결정 제어
PostToolUse hook은 도구 실행 후 Claude에 피드백을 제공할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 hook 스크립트는 이러한 이벤트 특정 필드를 반환할 수 있습니다:
| 필드 | 설명 |
|---|
decision | "block"은 Claude에 reason을 표시합니다. 생략하여 작업을 진행하도록 허용 |
reason | decision이 "block"일 때 Claude에 표시되는 설명 |
additionalContext | Claude의 컨텍스트에 도구 결과와 함께 추가되는 문자열. Claude를 위한 컨텍스트 추가를 참조하세요 |
updatedToolOutput | 도구의 출력을 제공된 값으로 바꿉니다. 값은 도구의 출력 형태와 일치해야 합니다 |
updatedMCPToolOutput | MCP 도구만 해당: 도구의 출력을 제공된 값으로 바꿉니다 |
아래 예제는 Bash 호출의 출력을 바꿉니다. 대체 값은 Bash 도구의 출력 형태와 일치합니다:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for Claude",
"updatedToolOutput": {
"stdout": "[redacted]",
"stderr": "",
"interrupted": false,
"isImage": false
}
}
}
updatedToolOutput은 Claude가 보는 것만 변경합니다. 도구는 hook이 발생할 때까지 이미 실행되었으므로 작성된 파일, 실행된 명령 또는 전송된 네트워크 요청은 이미 적용되었습니다. OpenTelemetry 도구 span 및 분석 이벤트와 같은 원격 측정도 hook이 실행되기 전에 원본 출력을 캡처합니다. 도구 호출을 실행 전에 방지하거나 수정하려면 PreToolUse hook을 대신 사용합니다.대체 값은 도구의 출력 형태와 일치해야 합니다. 기본 제공 도구는 일반 문자열이 아닌 구조화된 객체를 반환합니다. 예를 들어 Bash는 stdout, stderr, interrupted, isImage 필드가 있는 객체를 반환합니다. 기본 제공 도구의 경우 도구의 출력 스키마와 일치하지 않는 값은 무시되고 원본 출력이 사용됩니다. MCP 도구 출력은 스키마 검증 없이 통과됩니다. Claude가 필요한 오류 세부 정보를 제거하면 잘못된 가정으로 진행할 수 있습니다.
PostToolUseFailure
도구 실행이 실패할 때 실행됩니다. 이 이벤트는 오류를 throw하거나 실패 결과를 반환하는 도구 호출에 대해 발생합니다. 이를 사용하여 실패를 기록하고, 경고를 보내거나, 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,
"duration_ms": 4187
}
| 필드 | 설명 |
|---|
error | 무엇이 잘못되었는지 설명하는 문자열 |
is_interrupt | 선택적 부울로 실패가 사용자 중단으로 인한 것인지 여부를 나타냅니다 |
duration_ms | 선택적. 도구 실행 시간 (밀리초). 권한 프롬프트 및 PreToolUse hook에 소요된 시간 제외 |
PostToolUseFailure 결정 제어
PostToolUseFailure hook은 도구 실패 후 Claude에 컨텍스트를 제공할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 hook 스크립트는 이러한 이벤트 특정 필드를 반환할 수 있습니다:
| 필드 | 설명 |
|---|
additionalContext | Claude의 컨텍스트에 오류와 함께 추가되는 문자열. Claude를 위한 컨텍스트 추가를 참조하세요 |
{
"hookSpecificOutput": {
"hookEventName": "PostToolUseFailure",
"additionalContext": "Additional information about the failure for Claude"
}
}
PostToolBatch
배치의 모든 도구 호출이 해결된 후, Claude Code가 모델에 다음 요청을 보내기 전에 한 번 실행됩니다. PostToolUse는 도구당 한 번 발생하므로 Claude가 병렬 도구 호출을 만들 때 동시에 발생합니다. PostToolBatch는 전체 배치와 함께 정확히 한 번 발생하므로 단일 도구가 아닌 실행된 도구 집합에 따라 달라지는 컨텍스트를 주입하기에 적합한 위치입니다. 이 이벤트에는 matcher가 없습니다.
PostToolBatch 입력
공통 입력 필드 외에도 PostToolBatch hook은 배치의 모든 도구 호출을 설명하는 배열인 tool_calls를 받습니다:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolBatch",
"tool_calls": [
{
"tool_name": "Read",
"tool_input": {"file_path": "/.../ledger/accounts.py"},
"tool_use_id": "toolu_01...",
"tool_response": " 1\tfrom __future__ import annotations\n 2\t..."
},
{
"tool_name": "Read",
"tool_input": {"file_path": "/.../ledger/transactions.py"},
"tool_use_id": "toolu_02...",
"tool_response": " 1\tfrom __future__ import annotations\n 2\t..."
}
]
}
tool_response는 모델이 해당 tool_result 블록에서 받는 것과 동일한 내용을 포함합니다. 값은 도구가 내보낸 것과 정확히 같은 직렬화된 문자열 또는 콘텐츠 블록 배열입니다. Read의 경우 원본 파일 내용이 아닌 줄 번호가 접두사로 붙은 텍스트를 의미합니다. 응답이 클 수 있으므로 필요한 필드만 구문 분석합니다.
tool_response 형태는 PostToolUse와 다릅니다. PostToolUse는 도구의 구조화된 Output 객체를 전달합니다 (예: Write의 경우 {filePath: "...", success: true}). PostToolBatch는 모델이 보는 직렬화된 tool_result 콘텐츠를 전달합니다.
PostToolBatch 결정 제어
PostToolBatch hook은 Claude에 대한 컨텍스트를 주입할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 hook 스크립트는 이러한 이벤트 특정 필드를 반환할 수 있습니다:
| 필드 | 설명 |
|---|
additionalContext | 다음 모델 호출 전에 한 번 주입되는 컨텍스트 문자열. Claude를 위한 컨텍스트 추가를 참조하여 전달 세부 정보, 포함할 내용, 재개된 세션이 과거 값을 처리하는 방식을 확인하세요 |
{
"hookSpecificOutput": {
"hookEventName": "PostToolBatch",
"additionalContext": "These files are part of the ledger module. Run pytest before marking the task complete."
}
}
decision: "block" 또는 continue: false를 반환하면 다음 모델 호출 전에 에이전트 루프가 중지됩니다.
PermissionDenied
자동 모드 분류기가 도구 호출을 거부할 때 실행됩니다. 이 hook은 자동 모드에서만 발생합니다: 권한 대화 상자를 수동으로 거부할 때, PreToolUse hook이 호출을 차단할 때, 또는 deny 규칙이 일치할 때 실행되지 않습니다. 이를 사용하여 분류기 거부를 기록하고, 구성을 조정하거나, 모델이 도구 호출을 재시도할 수 있음을 알립니다.
도구 이름에서 일치합니다. PreToolUse와 동일한 값입니다.
PermissionDenied 입력
공통 입력 필드 외에도 PermissionDenied hook은 tool_name, tool_input, tool_use_id, reason을 받습니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "auto",
"hook_event_name": "PermissionDenied",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf /tmp/build",
"description": "Clean build directory"
},
"tool_use_id": "toolu_01ABC123...",
"reason": "Auto mode denied: command targets a path outside the project"
}
| 필드 | 설명 |
|---|
reason | 분류기가 도구 호출을 거부한 이유에 대한 설명 |
PermissionDenied 결정 제어
PermissionDenied hook은 모델이 거부된 도구 호출을 재시도할 수 있음을 알릴 수 있습니다. hookSpecificOutput.retry를 true로 설정한 JSON 객체를 반환합니다:
{
"hookSpecificOutput": {
"hookEventName": "PermissionDenied",
"retry": true
}
}
retry가 true일 때 Claude Code는 모델이 도구 호출을 재시도할 수 있음을 알리는 메시지를 대화에 추가합니다. 거부 자체는 역전되지 않습니다. hook이 JSON을 반환하지 않거나 retry: false를 반환하면 거부가 유지되고 모델은 원래 거부 메시지를 받습니다.
Notification
Claude Code가 알림을 보낼 때 실행됩니다. 알림 유형에서 일치합니다: permission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_complete, elicitation_response. matcher를 생략하여 모든 알림 유형에 대해 hook을 실행합니다.
별도의 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/...",
"hook_event_name": "Notification",
"message": "Claude needs your permission to use Bash",
"title": "Permission needed",
"notification_type": "permission_prompt"
}
Notification hook은 알림을 차단하거나 수정할 수 없습니다. 이들은 외부 서비스로 알림을 전달하는 것과 같은 부작용을 위한 것입니다. 모든 hook에 사용 가능한 JSON 출력 필드가 적용됩니다.
SubagentStart
Agent 도구를 통해 Claude Code subagent가 생성될 때 실행됩니다. 에이전트 유형 이름으로 필터링할 matcher를 지원합니다. 기본 제공 에이전트의 경우 이는 general-purpose, Explore, Plan과 같은 에이전트 이름입니다. 사용자 정의 subagent의 경우 이는 파일명이 아닌 에이전트의 frontmatter의 name 필드입니다.
SubagentStart 입력
공통 입력 필드 외에도 SubagentStart hook은 subagent의 고유 식별자가 있는 agent_id와 에이전트 이름이 있는 agent_type (general-purpose, Explore, Plan과 같은 기본 제공 에이전트 또는 사용자 정의 에이전트 이름)을 받습니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SubagentStart",
"agent_id": "agent-abc123",
"agent_type": "Explore"
}
SubagentStart hook은 subagent 생성을 차단할 수 없지만 subagent에 컨텍스트를 주입할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 다음을 반환할 수 있습니다:
| 필드 | 설명 |
|---|
additionalContext | subagent의 대화 시작 부분에 추가되는 문자열. 첫 번째 프롬프트 전에 추가됩니다. Claude를 위한 컨텍스트 추가를 참조하세요 |
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Follow security guidelines for this task"
}
}
SubagentStop
Claude Code subagent가 응답을 마쳤을 때 실행됩니다. 에이전트 유형에서 일치합니다. 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/ 폴더에 저장된 subagent의 자체 트랜스크립트입니다. last_assistant_message 필드는 subagent의 최종 응답의 텍스트 내용을 포함하므로 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과 동일한 결정 제어 형식을 사용합니다. 이들은 additionalContext를 지원하지 않습니다. decision: "block"을 reason과 함께 반환하면 subagent가 계속 실행되고 reason이 subagent의 다음 명령으로 전달됩니다. subagent가 반환한 후 부모 세션에 컨텍스트를 주입하려면 Agent 도구에서 PostToolUse hook을 대신 사용합니다.
TaskCreated
작업이 TaskCreate 도구를 통해 생성될 때 실행됩니다. 이를 사용하여 명명 규칙을 적용하거나, 작업 설명을 요구하거나, 특정 작업이 생성되는 것을 방지합니다.
TaskCreated hook이 코드 2로 종료되면 작업이 생성되지 않고 stderr 메시지가 모델에 피드백으로 피드백됩니다. 팀원을 다시 실행하는 대신 완전히 중지하려면 {"continue": false, "stopReason": "..."}이 있는 JSON을 반환합니다. TaskCreated hook은 matcher를 지원하지 않으며 모든 발생에서 발생합니다.
TaskCreated 입력
공통 입력 필드 외에도 TaskCreated 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": "TaskCreated",
"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 | 팀의 이름. 없을 수 있음 |
TaskCreated 결정 제어
TaskCreated hook은 작업 생성을 제어하는 두 가지 방법을 지원합니다:
- 종료 코드 2: 작업이 생성되지 않고 stderr 메시지가 모델에 피드백으로 피드백됩니다.
- JSON
{"continue": false, "stopReason": "..."}: 팀원을 완전히 중지하여 Stop hook 동작과 일치합니다. stopReason은 사용자에게 표시됩니다.
이 예제는 제목이 필수 형식을 따르지 않는 작업을 차단합니다:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then
echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&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": "..."}: 팀원을 완전히 중지하여 Stop hook 동작과 일치합니다. stopReason은 사용자에게 표시됩니다.
이 예제는 테스트를 실행하고 실패하면 작업 완료를 차단합니다:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
# 테스트 스위트를 실행합니다
if ! npm test 2>&1; then
echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
exit 2
fi
exit 0
Stop
메인 Claude Code 에이전트가 응답을 마쳤을 때 실행됩니다. 중지가 사용자 중단으로 인해 발생한 경우 실행되지 않습니다. API 오류는 StopFailure 대신 발생합니다.
/goal 명령은 세션 범위 prompt 기반 Stop hook의 기본 제공 바로 가기입니다. 조건이 유지될 때까지 Claude가 계속 작동하도록 하되 hook 구성을 작성하지 않으려는 경우 사용합니다.
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 | Claude가 중지되는 것이 차단될 때 필수입니다. Claude에 계속해야 하는 이유를 알립니다 |
{
"decision": "block",
"reason": "Must be provided when Claude is blocked from stopping"
}
StopFailure
Stop 대신 턴이 API 오류로 인해 종료될 때 실행됩니다. 출력과 종료 코드는 무시됩니다. 이를 사용하여 실패를 기록하고, 경고를 보내거나, Claude가 API 오류로 인해 응답을 완료할 수 없을 때 복구 조치를 취합니다.
StopFailure 입력
공통 입력 필드 외에도 StopFailure hook은 error, 선택적 error_details, 선택적 last_assistant_message를 받습니다. error 필드는 오류 유형을 식별하며 matcher 필터링에 사용됩니다.
| 필드 | 설명 |
|---|
error | 오류 유형: rate_limit, authentication_failed, oauth_org_not_allowed, billing_error, invalid_request, server_error, max_output_tokens 또는 unknown |
error_details | 사용 가능한 경우 오류에 대한 추가 세부 정보 |
last_assistant_message | 대화에 표시되는 렌더링된 오류 텍스트. Stop 및 SubagentStop과 달리 이 필드는 Claude의 대화형 출력을 보유하고 StopFailure의 경우 "API Error: Rate limit reached"와 같은 API 오류 문자열 자체를 포함합니다 |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "StopFailure",
"error": "rate_limit",
"error_details": "429 Too Many Requests",
"last_assistant_message": "API Error: Rate limit reached"
}
StopFailure hook은 결정 제어가 없습니다. 이들은 알림 및 로깅 목적으로만 실행됩니다.
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": "..."}: 팀원을 완전히 중지하여 Stop hook 동작과 일치합니다. stopReason은 사용자에게 표시됩니다.
이 예제는 팀원이 유휴 상태가 되도록 허용하기 전에 빌드 아티팩트가 존재하는지 확인합니다:
#!/bin/bash
if [ ! -f "./dist/output.js" ]; then
echo "Build artifact missing. Run the build before stopping." >&2
exit 2
fi
exit 0
ConfigChange
세션 중에 구성 파일이 변경될 때 실행됩니다. 이를 사용하여 설정 변경을 감사하고, 보안 정책을 적용하거나, 구성 파일에 대한 무단 수정을 차단합니다.
ConfigChange hook은 설정 파일, 관리형 정책 설정, skill 파일의 변경에 대해 발생합니다. 입력의 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/의 skill 파일 변경 |
이 예제는 보안 감사를 위해 모든 구성 변경을 기록합니다:
{
"hooks": {
"ConfigChange": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-config-change.sh",
"args": []
}
]
}
]
}
}
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/...",
"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 변경은 차단할 수 없습니다. Hook은 여전히 policy_settings 소스에 대해 발생하므로 감사 로깅에 사용할 수 있지만 모든 차단 결정은 무시됩니다. 이는 엔터프라이즈 관리 설정이 항상 적용되도록 보장합니다.
CwdChanged
세션 중에 작업 디렉토리가 변경될 때 실행됩니다. 예를 들어 Claude가 cd 명령을 실행할 때입니다. 이를 사용하여 디렉토리 변경에 반응합니다: 환경 변수를 다시 로드하고, 프로젝트 특정 도구 체인을 활성화하거나, 설정 스크립트를 자동으로 실행합니다. FileChanged와 쌍을 이루어 direnv와 같은 디렉토리별 환경을 관리하는 도구를 사용합니다.
CwdChanged hook은 CLAUDE_ENV_FILE에 액세스할 수 있습니다. 해당 파일에 작성된 변수는 SessionStart hook과 마찬가지로 세션의 후속 Bash 명령에 유지됩니다.
CwdChanged는 matcher를 지원하지 않으며 모든 디렉토리 변경에서 발생합니다.
CwdChanged 입력
공통 입력 필드 외에도 CwdChanged hook은 old_cwd 및 new_cwd를 받습니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project/src",
"hook_event_name": "CwdChanged",
"old_cwd": "/Users/my-project",
"new_cwd": "/Users/my-project/src"
}
CwdChanged 출력
모든 hook에 사용 가능한 JSON 출력 필드 외에도 CwdChanged hook은 watchPaths를 반환하여 FileChanged가 감시하는 파일 경로를 동적으로 설정할 수 있습니다:
| 필드 | 설명 |
|---|
watchPaths | 절대 경로의 배열. 현재 동적 감시 목록을 바꿉니다 (matcher 구성의 경로는 항상 감시됨). 새 디렉토리에 들어갈 때 빈 배열을 반환하는 것이 일반적입니다 |
CwdChanged hook은 결정 제어가 없습니다. 디렉토리 변경을 차단할 수 없습니다.
FileChanged
감시된 파일이 디스크에서 변경될 때 실행됩니다. 프로젝트 구성 파일이 수정될 때 환경 변수를 다시 로드하는 데 유용합니다.
matcher는 이 이벤트에 대해 두 가지 역할을 합니다:
- 감시 목록 구축: 값은
|로 분할되고 각 세그먼트는 작업 디렉토리의 리터럴 파일명으로 등록되므로 ".envrc|.env"는 정확히 이 두 파일을 감시합니다. 정규식 패턴은 여기서 유용하지 않습니다: ^\.env와 같은 값은 ^\.env라는 리터럴 이름의 파일을 감시합니다.
- hook 실행 필터링: 감시된 파일이 변경되면 동일한 값이 표준 matcher 규칙을 사용하여 변경된 파일의 basename에 대해 실행할 hook 그룹을 필터링합니다.
FileChanged hook은 CLAUDE_ENV_FILE에 액세스할 수 있습니다. 해당 파일에 작성된 변수는 SessionStart hook과 마찬가지로 세션의 후속 Bash 명령에 유지됩니다.
FileChanged 입력
공통 입력 필드 외에도 FileChanged hook은 file_path 및 event를 받습니다.
| 필드 | 설명 |
|---|
file_path | 변경된 파일의 절대 경로 |
event | 발생한 일: "change" (파일 수정), "add" (파일 생성) 또는 "unlink" (파일 삭제) |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "FileChanged",
"file_path": "/Users/my-project/.envrc",
"event": "change"
}
FileChanged 출력
모든 hook에 사용 가능한 JSON 출력 필드 외에도 FileChanged hook은 watchPaths를 반환하여 감시되는 파일 경로를 동적으로 업데이트할 수 있습니다:
| 필드 | 설명 |
|---|
watchPaths | 절대 경로의 배열. 현재 동적 감시 목록을 바꿉니다 (matcher 구성의 경로는 항상 감시됨). hook 스크립트가 변경된 파일을 기반으로 감시할 추가 파일을 발견할 때 사용합니다 |
FileChanged hook은 결정 제어가 없습니다. 파일 변경을 차단할 수 없습니다.
WorktreeCreate
claude --worktree를 실행하거나 subagent가 isolation: "worktree"를 사용할 때 Claude Code는 git worktree를 사용하여 격리된 작업 복사본을 생성합니다. WorktreeCreate hook을 구성하면 기본 git 동작을 대체하여 SVN, Perforce 또는 Mercurial과 같은 다른 버전 제어 시스템을 사용할 수 있습니다.
hook은 생성된 worktree 디렉토리의 절대 경로를 반환해야 합니다. Claude Code는 이 경로를 격리된 세션의 작업 디렉토리로 사용합니다. 명령 hook은 stdout에 경로를 인쇄합니다; HTTP hook은 hookSpecificOutput.worktreePath를 반환합니다.
이 예제는 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의 slug 식별자이며, 사용자가 지정하거나 자동 생성됩니다 (예: 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 출력
WorktreeCreate hook은 표준 허용/차단 결정 모델을 사용하지 않습니다. 대신 hook의 성공 또는 실패가 결과를 결정합니다. hook은 생성된 worktree 디렉토리의 절대 경로를 반환해야 합니다:
- 명령 hook (
type: "command"): stdout에 경로를 인쇄합니다.
- HTTP hook (
type: "http"): 응답 본문에서 { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }를 반환합니다.
hook이 실패하거나 경로를 생성하지 않으면 worktree 생성이 오류로 실패합니다.
WorktreeRemove
WorktreeCreate의 정리 대응. 이 hook은 worktree가 제거될 때 발생합니다. --worktree 세션을 종료하고 제거하도록 선택하거나 isolation: "worktree"를 가진 subagent가 완료될 때입니다. 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 실패는 디버그 모드에서만 기록됩니다.
PreCompact
Claude Code가 압축 작업을 실행하려고 하기 전에 실행됩니다.
matcher 값은 압축이 수동으로 또는 자동으로 트리거되었는지 나타냅니다:
| Matcher | 언제 발생하는지 |
|---|
manual | /compact |
auto | 컨텍스트 윈도우가 가득 찼을 때 자동 압축 |
종료 코드 2로 압축을 차단합니다. 수동 /compact의 경우 stderr 메시지가 사용자에게 표시됩니다. JSON "decision": "block"을 사용하여 차단할 수도 있습니다.
자동 압축 차단은 발생 시기에 따라 다른 효과를 가집니다. 컨텍스트 제한 전에 압축이 사전에 트리거된 경우 Claude Code는 이를 건너뛰고 대화가 압축되지 않은 상태로 계속됩니다. 컨텍스트 제한 오류를 복구하기 위해 압축이 트리거된 경우 기본 오류가 표시되고 현재 요청이 실패합니다.
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/...",
"hook_event_name": "PreCompact",
"trigger": "manual",
"custom_instructions": ""
}
PostCompact
Claude Code가 압축 작업을 완료한 후 실행됩니다. 이 이벤트를 사용하여 새로운 압축된 상태에 반응합니다. 예를 들어 생성된 요약을 기록하거나 외부 상태를 업데이트합니다.
PreCompact와 동일한 matcher 값이 적용됩니다:
| Matcher | 언제 발생하는지 |
|---|
manual | /compact 후 |
auto | 컨텍스트 윈도우가 가득 찼을 때 자동 압축 후 |
PostCompact 입력
공통 입력 필드 외에도 PostCompact hook은 trigger 및 compact_summary를 받습니다. compact_summary 필드는 압축 작업에서 생성된 대화 요약을 포함합니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "PostCompact",
"trigger": "manual",
"compact_summary": "Summary of the compacted conversation..."
}
PostCompact hook은 결정 제어가 없습니다. 압축 결과에 영향을 미칠 수 없지만 후속 작업을 수행할 수 있습니다.
SessionEnd
Claude Code 세션이 종료될 때 실행됩니다. 정리 작업, 세션 통계 로깅 또는 세션 상태 저장에 유용합니다. 종료 이유별로 필터링할 matcher를 지원합니다.
hook 입력의 reason 필드는 세션이 종료된 이유를 나타냅니다:
| 이유 | 설명 |
|---|
clear | /clear 명령으로 세션 지워짐 |
resume | 대화형 /resume을 통해 세션 전환됨 |
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/...",
"hook_event_name": "SessionEnd",
"reason": "other"
}
SessionEnd hook은 결정 제어가 없습니다. 세션 종료를 차단할 수 없지만 정리 작업을 수행할 수 있습니다.
SessionEnd hook의 기본 시간 초과는 1.5초입니다. 이는 세션 종료, /clear, 대화형 /resume을 통한 세션 전환 모두에 적용됩니다. hook에 더 많은 시간이 필요하면 hook 구성에서 timeout을 설정합니다. 전체 예산은 설정 파일의 가장 높은 hook별 timeout으로 자동으로 올라가며, 최대 60초입니다. plugin 제공 hook에 설정된 시간 초과는 예산을 올리지 않습니다. 예산을 명시적으로 재정의하려면 CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS 환경 변수를 밀리초 단위로 설정합니다.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude
Elicitation
MCP 서버가 작업 중 사용자 입력을 요청할 때 실행됩니다. 기본적으로 Claude Code는 사용자가 응답할 수 있는 대화형 대화 상자를 표시합니다. Hook은 이 요청을 가로채고 프로그래밍 방식으로 응답하여 대화 상자를 완전히 건너뛸 수 있습니다.
matcher 필드는 MCP 서버 이름과 일치합니다.
Elicitation 입력
공통 입력 필드 외에도 Elicitation hook은 mcp_server_name, message, 선택적으로 mode, url, elicitation_id, requested_schema 필드를 받습니다.
form 모드 elicitation (가장 일반적인 경우):
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Elicitation",
"mcp_server_name": "my-mcp-server",
"message": "Please provide your credentials",
"mode": "form",
"requested_schema": {
"type": "object",
"properties": {
"username": { "type": "string", "title": "Username" }
}
}
}
URL 모드 elicitation (브라우저 기반 인증):
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Elicitation",
"mcp_server_name": "my-mcp-server",
"message": "Please authenticate",
"mode": "url",
"url": "https://auth.example.com/login"
}
Elicitation 출력
대화 상자를 표시하지 않고 프로그래밍 방식으로 응답하려면 hookSpecificOutput이 있는 JSON 객체를 반환합니다:
{
"hookSpecificOutput": {
"hookEventName": "Elicitation",
"action": "accept",
"content": {
"username": "alice"
}
}
}
| 필드 | 값 | 설명 |
|---|
action | accept, decline, cancel | 요청을 수락, 거부 또는 취소할지 여부 |
content | 객체 | 제출할 form 필드 값. action이 accept일 때만 사용됨 |
종료 코드 2는 elicitation을 거부하고 stderr을 사용자에게 표시합니다.
ElicitationResult
사용자가 MCP elicitation에 응답한 후 실행됩니다. Hook은 응답을 관찰하고, 수정하거나, MCP 서버로 다시 전송되기 전에 차단할 수 있습니다.
matcher 필드는 MCP 서버 이름과 일치합니다.
ElicitationResult 입력
공통 입력 필드 외에도 ElicitationResult hook은 mcp_server_name, action, 선택적으로 mode, elicitation_id, content 필드를 받습니다.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "ElicitationResult",
"mcp_server_name": "my-mcp-server",
"action": "accept",
"content": { "username": "alice" },
"mode": "form",
"elicitation_id": "elicit-123"
}
ElicitationResult 출력
사용자의 응답을 재정의하려면 hookSpecificOutput이 있는 JSON 객체를 반환합니다:
{
"hookSpecificOutput": {
"hookEventName": "ElicitationResult",
"action": "decline",
"content": {}
}
}
| 필드 | 값 | 설명 |
|---|
action | accept, decline, cancel | 사용자의 작업을 재정의합니다 |
content | 객체 | form 필드 값을 재정의합니다. action이 accept일 때만 의미 있음 |
종료 코드 2는 응답을 차단하여 효과적인 작업을 decline으로 변경합니다.
프롬프트 기반 hook
명령, HTTP 및 MCP tool hook 외에도 Claude Code는 LLM을 사용하여 작업을 허용할지 차단할지 평가하는 프롬프트 기반 hook (type: "prompt")과 도구 액세스가 있는 에이전트 검증자를 생성하는 에이전트 hook (type: "agent")을 지원합니다. 모든 이벤트가 모든 hook 유형을 지원하는 것은 아닙니다.
다섯 가지 hook 유형 모두 (command, http, mcp_tool, prompt, agent)를 지원하는 이벤트:
PermissionRequest
PostToolBatch
PostToolUse
PostToolUseFailure
PreToolUse
Stop
SubagentStop
TaskCompleted
TaskCreated
UserPromptExpansion
UserPromptSubmit
command, http 및 mcp_tool hook을 지원하지만 prompt 또는 agent는 지원하지 않는 이벤트:
ConfigChange
CwdChanged
Elicitation
ElicitationResult
FileChanged
InstructionsLoaded
Notification
PermissionDenied
PostCompact
PreCompact
SessionEnd
StopFailure
SubagentStart
TeammateIdle
WorktreeCreate
WorktreeRemove
SessionStart 및 Setup은 command 및 mcp_tool hook을 지원합니다. http, prompt 또는 agent hook은 지원하지 않습니다.
프롬프트 기반 hook이 어떻게 작동하는지
프롬프트 기반 hook은 Bash 명령을 실행하는 대신:
- hook 입력과 프롬프트를 Claude 모델 (기본값 Haiku)로 전송합니다
- LLM은 결정을 포함하는 구조화된 JSON으로 응답합니다
- Claude Code는 결정을 자동으로 처리합니다
프롬프트 hook 구성
type을 "prompt"로 설정하고 command 대신 prompt 문자열을 제공합니다. $ARGUMENTS 자리 표시자를 사용하여 hook의 JSON 입력 데이터를 프롬프트 텍스트에 주입합니다. Claude Code는 결합된 프롬프트와 입력을 빠른 Claude 모델로 전송하며, 이는 JSON 결정을 반환합니다.
이 Stop hook은 Claude가 완료되기 전에 모든 작업이 완료되었는지 평가하도록 LLM에 요청합니다:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
}
]
}
]
}
}
| 필드 | 필수 | 설명 |
|---|
type | 예 | "prompt"여야 합니다 |
prompt | 예 | LLM으로 전송할 프롬프트 텍스트. hook 입력 JSON에 대한 자리 표시자로 $ARGUMENTS 사용. $ARGUMENTS가 없으면 입력 JSON이 프롬프트에 추가됩니다 |
model | 아니오 | 평가에 사용할 모델. 기본값은 빠른 모델 |
timeout | 아니오 | 초 단위 시간 초과. 기본값: 30 |
continueOnBlock | 아니오 | 프롬프트가 ok: false를 반환할 때 이유를 Claude에 다시 피드백하고 중지하는 대신 턴을 계속합니다. 기본값: false. 결과 decision: "block"에서 continue: true로 구현됩니다. 이벤트별 동작은 응답 스키마를 참조하세요 |
응답 스키마
LLM은 다음을 포함하는 JSON으로 응답해야 합니다:
{
"ok": true | false,
"reason": "Explanation for the decision"
}
| 필드 | 설명 |
|---|
ok | true는 작업을 허용하고 false는 decision: "block"을 생성합니다. 아래의 이벤트별 동작을 참조하세요 |
reason | ok가 false일 때 필수입니다. 차단 이유로 사용됩니다 |
ok: false에서 발생하는 상황은 이벤트에 따라 다릅니다:
Stop 및 SubagentStop: 이유는 Claude의 다음 명령으로 피드백되며 턴이 계속됩니다
PreToolUse: tool 호출이 거부되고 이유는 Claude에 tool 오류로 반환되며, 이는 명령 hook의 permissionDecision: "deny"와 동일합니다
PostToolUse: 기본적으로 턴이 끝나고 이유는 채팅에 경고 줄로 나타납니다. continueOnBlock: true를 설정하여 이유를 Claude에 다시 피드백하고 턴을 계속하는 대신 사용합니다
PostToolBatch, UserPromptSubmit 및 UserPromptExpansion: 턴이 끝나고 이유는 경고 줄로 나타납니다. 이러한 이벤트는 continue에 관계없이 decision: "block"에서 턴을 종료합니다
PostToolUseFailure, TaskCreated 및 TaskCompleted: 이유는 Claude에 tool 오류로 반환되며, PreToolUse와 유사합니다
PermissionRequest: ok: false는 효과가 없습니다. hook에서 승인을 거부하려면 hookSpecificOutput.decision.behavior: "deny"를 반환하는 명령 hook을 사용합니다
이벤트에 대해 더 세밀한 제어가 필요한 경우 결정 제어에 설명된 이벤트별 필드가 있는 명령 hook을 사용합니다.
예제: 다중 기준 Stop hook
이 Stop hook은 Claude가 중지하기 전에 세 가지 조건을 확인하는 자세한 프롬프트를 사용합니다. "ok"가 false이면 Claude는 제공된 이유를 다음 명령으로 받으며 계속 작업합니다. SubagentStop hook은 subagent가 중지해야 하는지 평가하는 동일한 형식을 사용합니다:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "You are evaluating whether Claude 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
}
]
}
]
}
}
에이전트 기반 hook
에이전트 hook은 실험적입니다. 동작 및 구성은 향후 릴리스에서 변경될 수 있습니다. 프로덕션 워크플로우의 경우 명령 hook을 선호합니다.
에이전트 기반 hook (type: "agent")은 프롬프트 기반 hook과 유사하지만 다중 턴 도구 액세스가 있습니다. 단일 LLM 호출 대신 에이전트 hook은 파일을 읽고, 코드를 검색하고, 코드베이스를 검사하여 조건을 확인할 수 있는 subagent를 생성합니다. 에이전트 hook은 프롬프트 기반 hook과 동일한 이벤트를 지원합니다.
에이전트 hook이 어떻게 작동하는지
에이전트 hook이 발생할 때:
- Claude Code는 프롬프트와 hook의 JSON 입력을 가진 subagent를 생성합니다
- subagent는 Read, Grep, Glob과 같은 도구를 사용하여 조사할 수 있습니다
- 최대 50턴 후 subagent는 구조화된
{ "ok": true/false } 결정을 반환합니다
- Claude Code는 프롬프트 hook과 동일한 방식으로 결정을 처리합니다
에이전트 hook은 검증이 hook 입력 데이터만으로 평가하는 것이 아니라 실제 파일이나 테스트 출력을 검사해야 할 때 유용합니다.
에이전트 hook 구성
type을 "agent"로 설정하고 prompt 문자열을 제공합니다. 구성 필드는 프롬프트 hook과 동일하지만 더 긴 기본 시간 초과가 있습니다:
| 필드 | 필수 | 설명 |
|---|
type | 예 | "agent"여야 합니다 |
prompt | 예 | 확인할 내용을 설명하는 프롬프트. hook 입력 JSON에 대한 자리 표시자로 $ARGUMENTS 사용 |
model | 아니오 | 사용할 모델. 기본값은 빠른 모델 |
timeout | 아니오 | 초 단위 시간 초과. 기본값: 60 |
응답 스키마는 프롬프트 hook과 동일합니다: 허용하려면 { "ok": true } 또는 차단하려면 { "ok": false, "reason": "..." }.
이 Stop hook은 Claude가 완료되기 전에 모든 단위 테스트가 통과하는지 확인합니다:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
"timeout": 120
}
]
}
]
}
}
백그라운드에서 hook 실행
기본적으로 hook은 완료될 때까지 Claude의 실행을 차단합니다. 배포, 테스트 스위트 또는 외부 API 호출과 같은 장기 실행 작업의 경우 "async": true를 설정하여 Claude가 계속 작업하는 동안 백그라운드에서 hook을 실행합니다. 비동기 hook은 차단하거나 Claude의 동작을 제어할 수 없습니다: decision, permissionDecision, continue와 같은 응답 필드는 효과가 없습니다. 제어했을 작업이 이미 완료되었기 때문입니다.
비동기 hook 구성
hook 구성에 "async": true를 추가하여 Claude를 차단하지 않고 백그라운드에서 실행합니다. 이 필드는 type: "command" hook에서만 사용 가능합니다.
이 hook은 모든 Write 도구 호출 후 테스트 스크립트를 실행합니다. Claude는 run-tests.sh가 최대 120초 동안 실행되는 동안 즉시 계속 작업합니다. 스크립트가 완료되면 출력이 다음 대화 턴에 전달됩니다:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "/path/to/run-tests.sh",
"async": true,
"timeout": 120
}
]
}
]
}
}
timeout 필드는 백그라운드 프로세스의 최대 시간을 초 단위로 설정합니다. 지정하지 않으면 비동기 hook은 동기 hook과 동일한 10분 기본값을 사용합니다.
비동기 hook이 어떻게 실행되는지
비동기 hook이 발생하면 Claude Code는 hook 프로세스를 시작하고 완료를 기다리지 않고 즉시 계속합니다. hook은 동기 hook과 동일한 JSON 입력을 stdin을 통해 받습니다.
백그라운드 프로세스가 종료된 후 hook이 additionalContext 필드가 있는 JSON 응답을 생성한 경우 해당 콘텐츠는 다음 대화 턴에서 Claude에 컨텍스트로 전달됩니다. systemMessage 필드는 Claude가 아닌 사용자에게 표시됩니다.
비동기 hook 완료 알림은 기본적으로 억제됩니다. 보려면 Ctrl+O로 자세한 모드를 활성화하거나 --verbose로 Claude Code를 시작합니다.
예제: 파일 변경 후 테스트 실행
이 hook은 Claude가 파일을 쓸 때마다 백그라운드에서 테스트 스위트를 시작한 후 테스트가 완료되면 결과를 Claude에 보고합니다. 이 스크립트를 프로젝트의 .claude/hooks/run-tests-async.sh에 저장하고 chmod +x로 실행 가능하게 만듭니다:
#!/bin/bash
# run-tests-async.sh
# stdin에서 hook 입력을 읽습니다
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# 소스 파일에 대해서만 테스트를 실행합니다
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
exit 0
fi
# 테스트를 실행하고 additionalContext를 통해 결과를 Claude에 보고합니다
RESULT=$(npm test 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
MSG="Tests passed after editing $FILE_PATH"
else
MSG="Tests failed after editing $FILE_PATH: $RESULT"
fi
jq -nc --arg msg "$MSG" '{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'
그런 다음 프로젝트 루트의 .claude/settings.json에 이 구성을 추가합니다. async: true 플래그를 사용하면 Claude가 테스트 실행 중에 계속 작업할 수 있습니다:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/run-tests-async.sh",
"args": [],
"async": true,
"timeout": 300
}
]
}
]
}
}
제한 사항
비동기 hook은 동기 hook과 비교하여 여러 제약이 있습니다:
type: "command" hook만 async를 지원합니다. 프롬프트 기반 hook은 비동기적으로 실행될 수 없습니다.
- 비동기 hook은 도구 호출을 차단하거나 결정을 반환할 수 없습니다. hook이 완료될 때까지 트리거 작업이 이미 진행되었습니다.
- Hook 출력은 다음 대화 턴에 전달됩니다. 세션이 유휴 상태이면 응답은 다음 사용자 상호 작용까지 기다립니다. 예외:
asyncRewake hook이 종료 코드 2로 종료되면 세션이 유휴 상태일 때도 Claude를 즉시 깨웁니다.
- 각 실행은 별도의 백그라운드 프로세스를 생성합니다. 동일한 비동기 hook의 여러 발생에 걸쳐 중복 제거가 없습니다.
보안 고려 사항
면책 조항
명령 hook은 시스템 사용자의 전체 권한으로 실행됩니다.
명령 hook은 전체 사용자 권한으로 셸 명령을 실행합니다. 사용자 계정이 액세스할 수 있는 모든 파일을 수정, 삭제 또는 액세스할 수 있습니다. 구성에 추가하기 전에 모든 hook 명령을 검토하고 테스트하세요.
보안 모범 사례
hook을 작성할 때 이러한 사례를 염두에 두세요:
- 입력 검증 및 살균: 입력 데이터를 맹목적으로 신뢰하지 마세요
- 항상 셸 변수를 따옴표로 감싸세요:
$VAR 대신 "$VAR" 사용
- 경로 순회 차단: 파일 경로에서
.. 확인
- 절대 경로 사용: 스크립트의 전체 경로를 지정하세요. exec 형식에서는
${CLAUDE_PROJECT_DIR}을 사용하고 경로는 따옴표가 필요하지 않습니다. shell 형식에서는 큰따옴표로 감싸세요
- 민감한 파일 건너뛰기:
.env, .git/, 키 등을 피하세요
Windows PowerShell 도구
Windows에서 개별 hook을 PowerShell에서 실행할 수 있습니다. 명령 hook에서 "shell": "powershell"을 설정합니다. Hook은 PowerShell을 직접 생성하므로 CLAUDE_CODE_USE_POWERSHELL_TOOL이 설정되어 있는지 여부와 관계없이 작동합니다. Claude Code는 pwsh.exe (PowerShell 7+)를 자동 감지하고 powershell.exe (5.1)로 폴백합니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"shell": "powershell",
"command": "Write-Host 'File written'"
}
]
}
]
}
}
Hook 디버그
Hook 실행 세부 정보, 일치한 hook, 종료 코드, 전체 stdout 및 stderr은 디버그 로그 파일에 기록됩니다. claude --debug-file <path>로 Claude Code를 시작하여 로그를 알려진 위치에 작성하거나 claude --debug를 실행하고 ~/.claude/debug/<session-id>.txt에서 로그를 읽습니다. --debug 플래그는 터미널에 인쇄하지 않습니다.
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 600000ms
[DEBUG] Hook command completed with status 0: <Your stdout>
더 세밀한 hook 일치 세부 정보를 보려면 CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose를 설정하여 hook matcher 수 및 쿼리 일치와 같은 추가 로그 줄을 확인합니다.
hook이 발생하지 않음, 무한 Stop hook 루프 또는 구성 오류와 같은 일반적인 문제 해결은 가이드의 제한 사항 및 문제 해결을 참조하세요. 더 광범위한 진단 안내는 /context, /doctor 및 설정 우선순위를 다루는 구성 디버그를 참조하세요.