Skip to main content
예제가 포함된 빠른 시작 가이드는 hook으로 작업 자동화를 참조하세요.
Hook은 Claude Code의 수명 주기에서 특정 지점에 자동으로 실행되는 사용자 정의 셸 명령, HTTP 엔드포인트, MCP 도구 호출, LLM 프롬프트 또는 서브에이전트입니다. Claude Code는 터미널의 세션, IDE 확장 프로그램, 데스크톱 앱, 클라우드 세션을 포함하여 실행되는 모든 곳에서 동일한 hook 이벤트를 발생시킵니다. 이 참조를 사용하여 이벤트 스키마, 구성 옵션, JSON 입출력 형식, 비동기 hook, HTTP hook, MCP 도구 hook과 같은 고급 기능을 조회할 수 있습니다.

Hook 수명 주기

Claude Code는 세션 중 특정 지점에서 hook을 실행합니다. 이벤트가 발생하고 matcher가 일치하면 Claude Code는 이벤트에 대한 JSON 컨텍스트를 hook 핸들러에 전달합니다. 명령 hook의 경우 입력은 stdin에 도착합니다. HTTP hook의 경우 POST 요청 본문으로 도착합니다. 그러면 핸들러는 입력을 검사하고 조치를 취한 후 선택적으로 결정을 반환할 수 있습니다. 이벤트는 세 가지 주기로 발생합니다:
  • 세션당 한 번: SessionStart 및 SessionEnd
  • 턴당 한 번: UserPromptSubmit, Stop 및 StopFailure
  • 에이전트 루프 내의 모든 도구 호출에서: PreToolUse 및 PostToolUse(단, EndConversation 호출은 제외되며, 이는 둘 다 건너뜁니다)
선택적 Setup에서 SessionStart로 시작하여 턴당 루프(UserPromptSubmit, 슬래시 명령에 대한 UserPromptExpansion, 중첩된 에이전트 루프(PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), Stop 또는 StopFailure), TeammateIdle, PreCompact, PostCompact, SessionEnd를 거쳐 진행되는 hook 수명 주기 다이어그램. Elicitation 및 ElicitationResult는 MCP 도구 실행 내에 중첩되고, PermissionDenied는 PermissionRequest의 부분 분기(자동 모드 거부용), WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged, DirectoryAdded는 독립적인 비동기 이벤트이며, PreModelSwitch는 요청된 모델 전환 전에 실행되는 독립적인 순차 이벤트이고, PostModelSwitch는 세션의 모델이 변경된 후에 실행되는 독립적인 비동기 이벤트이며, MessageDisplay는 어시스턴트 메시지 텍스트가 스트리밍되는 동안 실행되는 표시 전용 이벤트입니다선택적 Setup에서 SessionStart로 시작하여 턴당 루프(UserPromptSubmit, 슬래시 명령에 대한 UserPromptExpansion, 중첩된 에이전트 루프(PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), Stop 또는 StopFailure), TeammateIdle, PreCompact, PostCompact, SessionEnd를 거쳐 진행되는 hook 수명 주기 다이어그램. Elicitation 및 ElicitationResult는 MCP 도구 실행 내에 중첩되고, PermissionDenied는 PermissionRequest의 부분 분기(자동 모드 거부용), WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged, DirectoryAdded는 독립적인 비동기 이벤트이며, PreModelSwitch는 요청된 모델 전환 전에 실행되는 독립적인 순차 이벤트이고, PostModelSwitch는 세션의 모델이 변경된 후에 실행되는 독립적인 비동기 이벤트이며, MessageDisplay는 어시스턴트 메시지 텍스트가 스트리밍되는 동안 실행되는 표시 전용 이벤트입니다
아래 표는 각 이벤트가 언제 발생하는지 요약합니다. Hook 이벤트 섹션에서는 각 이벤트의 전체 입력 스키마와 결정 제어 옵션을 문서화합니다.

Hook이 어떻게 해결되는지

파괴적인 셸 명령을 차단하는 이 PreToolUse hook을 고려하세요.
matcher는 Bash 도구 호출로 좁혀지고 if 조건은 rm *과 일치하는 Bash 부명령으로 더 좁혀지므로 block-rm.sh는 두 필터가 모두 일치할 때만 생성됩니다:
스크립트는 stdin에서 JSON 입력을 읽고 명령을 추출한 후 rm -rf를 포함하면 permissionDecision을 "deny"로 반환합니다. 프로젝트의 .claude/hooks/block-rm.sh에 저장하고 Claude Code가 실행할 수 있도록 chmod +x .claude/hooks/block-rm.sh로 실행 가능하게 만듭니다:
이 스크립트는 이 페이지의 다른 Bash 예제처럼 JSON 입력을 파싱하므로 jq를 사용합니다. 따라서 이들을 시도하기 전에 jq를 설치하고 PATH에 있는지 확인하세요.
이제 Claude Code가 macOS/Linux 구성에 대해 Bash "rm -rf /tmp/build"를 실행하기로 결정했다고 가정합니다. 다음은 발생하는 일입니다:
Hook 해결 다이어그램: PreToolUse가 발생하고, matcher가 Bash 일치를 확인한 후 if 조건이 Bash(rm *) 일치를 확인합니다. 둘 다 일치하면 hook 명령이 실행되고 permissionDecision deny를 반환하므로 도구 호출이 차단되고 Claude Code가 계속됩니다. 어느 검사도 일치하지 않으면 hook이 건너뛰어지고 도구 호출이 진행되도록 허용됩니다.Hook 해결 다이어그램: PreToolUse가 발생하고, matcher가 Bash 일치를 확인한 후 if 조건이 Bash(rm *) 일치를 확인합니다. 둘 다 일치하면 hook 명령이 실행되고 permissionDecision deny를 반환하므로 도구 호출이 차단되고 Claude Code가 계속됩니다. 어느 검사도 일치하지 않으면 hook이 건너뛰어지고 도구 호출이 진행되도록 허용됩니다.
1

이벤트 발생

PreToolUse 이벤트가 발생합니다. Claude Code는 도구 입력을 stdin의 hook에 JSON으로 전송합니다:
2

Matcher 확인

matcher "Bash"가 도구 이름과 일치하므로 이 hook 그룹이 활성화됩니다. matcher를 생략하거나 "*"를 사용하면 이벤트의 모든 발생에서 그룹이 활성화됩니다.
3

If 조건 확인

if 조건 "Bash(rm *)"은 rm -rf /tmp/build가 rm *과 일치하는 부명령이므로 일치하여 이 핸들러가 생성됩니다. 명령이 npm test였다면 if 검사가 실패하고 block-rm.sh는 절대 실행되지 않아 프로세스 생성 오버헤드를 피합니다. if 필드는 선택 사항입니다. 없으면 일치한 그룹의 모든 핸들러가 실행됩니다.
4

Hook 핸들러 실행

스크립트는 전체 명령을 검사하고 rm -rf를 찾으므로 stdout에 결정을 인쇄합니다:
명령이 rm file.txt와 같은 더 안전한 rm 변형이었다면 스크립트는 대신 exit 0을 실행합니다. 출력이 없는 종료 코드 0은 hook이 보고할 결정이 없다는 의미이므로 도구 호출은 일반적인 권한 흐름을 통해 계속됩니다. hook은 호출을 거부할 수 있지만 침묵을 유지하는 것은 이를 승인하지 않습니다.
5

Claude Code가 결과에 따라 행동

Claude Code는 JSON 결정을 읽고 도구 호출을 차단하며 Claude에 이유를 표시합니다.
아래 구성 섹션에서는 전체 스키마를 문서화하고, 각 hook 이벤트 섹션에서는 명령이 받는 입력과 반환할 수 있는 출력을 문서화합니다.

구성

Hook은 JSON 설정 파일에서 정의됩니다. 구성은 세 가지 중첩 수준을 가집니다:
  1. 응답할 hook 이벤트를 선택합니다(예: PreToolUse 또는 Stop).
  2. 실행 시기를 필터링할 matcher 그룹을 추가합니다(예: “Bash 도구에만 해당”).
  3. 일치할 때 실행할 하나 이상의 hook 핸들러를 정의합니다.
주석이 달린 예제를 포함한 완전한 설명은 위의 hook이 어떻게 해결되는지를 참조하십시오.
이 페이지는 각 수준에 대해 특정 용어를 사용합니다: 라이프사이클 포인트에 대해 hook 이벤트, 필터에 대해 matcher 그룹, 실행되는 셸 명령, HTTP 엔드포인트, MCP 도구, 프롬프트 또는 에이전트에 대해 hook 핸들러. “Hook”은 일반적인 기능을 나타냅니다.

Hook 위치

hook을 정의하는 위치에 따라 범위가 결정됩니다: 클라우드 세션은 로컬 ~/.claude/settings.json을 읽지 않습니다. 거기의 hook은 리포지토리에서 나옵니다. 즉, 한 리포지토리가 있는 세션의 .claude/settings.json과 모든 세션에서 선언하는 플러그인, 그리고 조직의 서버 관리 설정에서 나옵니다. 자체 호스팅 환경에서 Claude Code는 또한 운영자가 실행기 호스트의 ~/.claude/에서 시드한 hook을 실행하고, Claude Code가 적용하는 관리형 소스 중 하나인 실행기 이미지의 관리형 설정 파일에서 hook을 실행합니다. 기본적으로 서버 관리 설정이나 MDM 전달 Claude Code 정책이 관리형 계층을 제공하지 않을 때만 해당됩니다. 클라우드 세션에 도달하는 파일에 대해서는 설정에서 이월되는 항목을 참조하십시오. 설정 파일 해결에 대한 자세한 내용은 설정을 참조하십시오. 설정 파일, 관리형 정책 설정 및 플러그인의 Hook도 서브에이전트 내에서 실행됩니다. 서브에이전트가 도구를 호출할 때, PreToolUse 및 PostToolUse와 같은 도구 이벤트는 주 대화에서 구성된 것과 동일한 hook을 실행하며, 입력은 서브에이전트를 식별하는 agent_id 및 agent_type 공통 입력 필드를 전달합니다. 엔터프라이즈 관리자는 allowManagedHooksOnly를 사용하여 실행되는 hook을 제한할 수 있습니다:
  • 사용자, 프로젝트, 로컬 및 플러그인 hook이 차단됩니다. 관리형 설정 enabledPlugins에서 강제 활성화된 플러그인의 Hook은 제외됩니다.
  • Claude Code는 또한 statusLine, fileSuggestion 및 subagentStatusLine 설정을 관리형 설정으로 좁힙니다.
  • Claude Code는 또한 disableCommandPluginSources가 명시적으로 false로 설정되지 않은 한 command 소스가 있는 플러그인을 비활성화합니다. 여기에는 관리형 설정 enabledPlugins에서 강제 활성화된 플러그인이 포함됩니다. command 소스는 Claude Code v2.1.229 이상이 필요합니다.
  • Claude Code는 또한 disableCommandPluginSources가 명시적으로 false로 설정되지 않은 한 마켓플레이스 headersHelper 명령을 차단합니다. 단, 관리형 설정 자체가 선언하는 마켓플레이스는 제외됩니다.
allowManagedHooksOnly에서 실행되는 항목을 참조하십시오. Hook 항목은 각 설정 수준에서 서로를 대체하지 않고 병합됩니다: 사용자, 프로젝트 및 로컬 설정은 관리형 항목을 제거하지 않고 자신의 hook을 추가하며, disableAllHooks 설정은 관리형 hook을 비활성화할 수 없습니다. HTTP hook 허용 목록은 관리형 정책 설정을 포함한 모든 소스의 hook에 적용됩니다:
  • allowedHttpHookUrls: 모든 설정 수준에서 정의되면 Claude Code는 URL이 병합된 허용 목록과 일치하는 경우에만 HTTP hook 핸들러를 실행합니다.
  • httpHookAllowedEnvVars: 정의되면 Claude Code는 해당 목록의 환경 변수만 hook 헤더에 보간합니다.

Matcher 패턴

matcher 필드는 hook이 실행되는 시기를 필터링합니다. matcher가 평가되는 방식은 포함된 문자에 따라 다릅니다: 정규 표현식 경로의 matcher는 JavaScript의 RegExp.prototype.test로 테스트되며, 값의 어디든지 일치하면 성공합니다. Edit.*는 Edit과 NotebookEdit 모두와 일치합니다. 전체 문자열 일치가 필요한 경우 ^Edit$와 같이 패턴을 ^ 및 $로 래핑하십시오. 쉼표 구분 기호와 주변 공백 허용은 Claude Code v2.1.191 이상이 필요합니다. 정확한 일치 집합의 하이픈은 Claude Code v2.1.195 이상이 필요합니다. 이전 버전에서는 code-reviewer와 같은 하이픈이 있는 이름이 앵커 없는 정규 표현식으로 평가되므로 senior-code-reviewer에 대해서도 실행됩니다. 해당 버전에서 해당 이름만 일치하도록 ^code-reviewer$로 앵커하십시오. FileChanged 및 StopFailure는 문자, 숫자, _ 및 |만 포함하는 더 좁은 정확한 일치 집합을 사용합니다. matcher에 하이픈, 공백 또는 쉼표가 있으면 이 두 이벤트에 대해 정규 표현식 경로에 유지되며, |만 대안을 구분합니다. matcher 지원이 있는 다른 모든 이벤트는 | 또는 ,를 허용합니다. FileChanged 이벤트는 감시 목록을 작성할 때 이러한 규칙을 따르지 않습니다. FileChanged를 참조하십시오. 각 이벤트 유형은 다른 필드에서 일치합니다: StopFailure에서 cloud_credential_error와 일치하려면 Claude Code v2.1.267 이상이 필요합니다. 이는 자격 증명 로드 실패를 server_error 또는 unknown 대신 해당 값으로 보고하는 첫 번째 버전입니다. 대부분의 이벤트에서 Claude Code는 stdin의 hook으로 보내는 JSON 입력의 필드에 대해 matcher를 평가합니다. 도구 이벤트의 경우 해당 필드는 tool_name입니다. PreModelSwitch 및 PostModelSwitch의 경우 Claude Code는 PreModelSwitch 아래에 설명된 대로 to_model에서 파생된 정규 이름에 대해 matcher를 평가합니다. 각 hook 이벤트 섹션은 matcher 값의 전체 집합과 해당 이벤트의 입력 스키마를 나열합니다. 이 예제는 Claude가 파일을 쓰거나 편집할 때만 린팅 스크립트를 실행합니다:
matcher 지원이 없는 이벤트에 matcher 필드를 추가하면 자동으로 무시됩니다. 도구 이벤트의 경우 개별 hook 핸들러에서 if 필드를 설정하여 더 좁게 필터링할 수 있습니다. if는 권한 규칙 구문을 사용하여 도구 이름과 인수를 함께 일치시키므로 "Bash(git *)"는 Bash 입력의 모든 하위 명령이 git *과 일치할 때 실행되고 "Edit(*.ts)"는 TypeScript 파일에만 실행됩니다.

MCP 도구 일치

MCP 서버 도구는 도구 이벤트(PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied)에서 일반 도구로 나타나므로 다른 도구 이름과 동일한 방식으로 일치시킬 수 있습니다. MCP 도구는 mcp__<server>__<tool> 패턴을 따릅니다. 예를 들어:
  • mcp__memory__create_entities: Memory 서버의 엔티티 생성 도구
  • mcp__filesystem__read_file: Filesystem 서버의 파일 읽기 도구
  • mcp__github__search_repositories: GitHub 서버의 검색 도구
서버의 모든 도구와 일치하려면 서버 접두사에 .*를 추가합니다. .*는 필수입니다: mcp__memory 또는 mcp__brave-search와 같은 matcher는 정확한 일치 문자만 포함하므로 정확한 문자열로 비교되며 도구와 일치하지 않습니다.
  • mcp__memory__.*는 memory 서버의 모든 도구와 일치합니다.
  • mcp__brave-search__.*는 이름에 하이픈이 포함된 서버의 모든 도구와 일치합니다.
  • mcp__.*__write.*는 모든 서버의 이름이 write로 시작하는 모든 도구와 일치합니다.
정확한 일치 집합의 하이픈은 Claude Code v2.1.195 이상이 필요합니다. 이전 버전에서는 mcp__brave-search와 같은 하이픈이 있는 접두사가 앵커 없는 정규 표현식으로 평가되어 해당 서버의 모든 도구와 일치합니다. mcp__brave-search__.* 형식은 모든 버전에서 작동합니다. 플러그인 번들 MCP 서버의 도구는 플러그인 이름을 포함하는 범위 지정 서버 세그먼트를 사용합니다: mcp__plugin_<plugin-name>_<server-name>__<tool>. 베어 서버 키에 대해 작성된 matcher는 이러한 도구에 대해 실행되지 않습니다. db 키 아래에 서버를 번들하는 my-plugin이라는 플러그인의 경우 query 도구는 mcp__plugin_my-plugin_db__query로 나타나므로 해당 서버의 모든 도구에 대한 matcher는 mcp__plugin_my-plugin_db__.*입니다. 핸들러의 if 필드에서 동일한 범위 지정 도구 이름을 사용합니다. 범위 지정 이름이 작성되는 방식에 대해서는 플러그인 제공 MCP 서버를 참조하십시오. 이 예제는 모든 메모리 서버 작업을 기록하고 모든 MCP 서버의 쓰기 작업을 검증합니다:

Hook 핸들러 필드

내부 hooks 배열의 각 객체는 hook 핸들러입니다: matcher가 일치할 때 실행되는 셸 명령, HTTP 엔드포인트, MCP 도구, LLM 프롬프트 또는 에이전트입니다. 다섯 가지 유형이 있습니다:
  • 명령 hook (type: "command"): 셸 명령을 실행합니다. 스크립트는 stdin의 이벤트 JSON 입력을 수신하고 종료 코드 및 stdout을 통해 결과를 다시 전달합니다.
  • HTTP hook (type: "http"): 이벤트의 JSON 입력을 HTTP POST 요청으로 URL에 보냅니다. 엔드포인트는 명령 hook과 동일한 JSON 출력 형식을 사용하여 응답 본문을 통해 결과를 다시 전달합니다.
  • MCP 도구 hook (type: "mcp_tool"): 이미 연결된 MCP 서버의 도구를 호출합니다. 도구의 텍스트 출력은 명령 hook stdout처럼 처리됩니다.
  • 프롬프트 hook (type: "prompt"): Claude 모델에 단일 턴 평가를 위한 프롬프트를 보냅니다. 모델은 결정을 JSON으로 반환합니다. 프롬프트 기반 hook을 참조하십시오.
  • 에이전트 hook (type: "agent"): Read, Grep 및 Glob과 같은 도구를 사용하여 조건을 확인한 후 결정을 반환할 수 있는 서브에이전트를 생성합니다. 에이전트 hook은 실험적이며 변경될 수 있습니다. 에이전트 기반 hook을 참조하십시오.
일치하는 모든 hook은 병렬로 실행됩니다. 동일한 핸들러를 둘 이상의 설정 파일에서 정의하면 한 번 실행됩니다. 플러그인 또는 스킬의 동일한 핸들러 복사본은 별도로 유지됩니다. 핸들러는 Claude Code의 환경이 있는 현재 디렉토리에서 실행됩니다. 예를 들어 다른 셸이 세션 중간에 삭제한 worktree 또는 임시 디렉토리와 같이 현재 디렉토리가 더 이상 존재하지 않으면 Claude Code는 다음 중 여전히 존재하는 첫 번째 디렉토리에서 명령 hook을 실행합니다: 세션이 시작된 디렉토리, 프로젝트 루트, 홈 디렉토리 또는 시스템 임시 디렉토리. Claude Code는 디버그 로그에서 폴백 디렉토리의 이름을 지정하는 경고를 기록합니다. $CLAUDE_CODE_REMOTE 환경 변수는 원격 웹 환경에서 "true"이고 로컬 CLI에서 설정되지 않습니다. Claude Code v2.1.199 이상은 로컬 세션이 활성 Remote Control 연결을 가지는 동안 $CLAUDE_CODE_BRIDGE_SESSION_ID를 Remote Control 세션 ID로 설정합니다.

공통 필드

이 필드는 모든 hook 유형에 적용됩니다: if 필드는 정확히 하나의 권한 규칙을 보유합니다. 규칙을 결합하기 위한 &&, || 또는 목록 구문이 없습니다. 여러 조건을 적용하려면 각각에 대해 별도의 hook 핸들러를 정의하십시오. 파일 도구의 if 조건에서 "Edit(src/**)" 같은 단일 세그먼트 디렉토리 패턴은 작업 디렉토리의 src 디렉토리와 그 아래의 파일만 일치합니다. 작업 디렉토리 아래의 모든 깊이에서 src라는 디렉토리와 일치하려면 "Edit(**/src/**)" 형식으로 작성하십시오. v2.1.214 이전에는 "Edit(src/**)" 작업 디렉토리 아래의 모든 깊이에서 src라는 디렉토리와 일치했습니다. Bash 패턴의 경우 hook 명령이 실행되는지 여부는 패턴의 형태와 Claude가 호출하는 Bash 명령에 따라 다릅니다. 선행 VAR=value 할당은 일치하기 전에 제거됩니다. Claude Code가 Bash 입력이 실행하는 명령을 결정할 수 없으면 패턴에 관계없이 hook을 실행합니다. if 필터는 최선의 노력이므로 하드 허용 또는 거부를 적용하려면 hook 대신 권한 시스템을 사용하십시오.

명령 hook 필드

공통 필드 외에도 명령 hook은 다음 필드를 허용합니다: 명령 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을 이름으로 실행하려면 셸 형식을 사용합니다.
이 예제는 플러그인과 함께 번들된 Node 스크립트를 실행합니다. Exec 형식은 해결된 스크립트 경로를 따옴표 없이 하나의 인수로 전달합니다:
동등한 셸 형식은 공백이나 특수 문자가 있는 경로를 처리하기 위해 따옴표가 필요합니다:
두 형식 모두 동일한 경로 자리 표시자를 지원하며, 둘 다 생성된 프로세스에서 CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT 및 CLAUDE_PLUGIN_DATA를 환경 변수로 내보내므로 스크립트는 시작 방식에 관계없이 process.env.CLAUDE_PLUGIN_ROOT를 읽을 수 있습니다. 플러그인 hook은 추가로 ${user_config.*} 값을 exec 형식에서만 대체합니다: 값은 일반 문자열로 command 및 각 args 요소로 대체되므로 셸이 다시 파싱하지 않습니다. command가 ${user_config.*}를 참조하는 셸 형식 플러그인 hook은 실행되는 대신 오류로 실패합니다. 셸 형식 hook에서 옵션 값을 사용하려면 $CLAUDE_PLUGIN_OPTION_<KEY> 환경 변수(예: webhook_url 옵션의 경우 $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL)를 읽거나 args를 설정하여 hook을 exec 형식으로 전환합니다. v2.1.207 이전에는 셸 형식 플러그인 hook 명령도 ${user_config.*}를 대체했습니다.
Exec 형식에서 command는 실행 파일 이름 또는 경로만입니다. command가 경로 구분 기호가 없는 베어 이름이고 args와 함께 공백을 포함하면 Claude Code는 경고를 기록합니다. 생성이 실패하기 때문입니다: node script.js라는 실행 파일이 없습니다. 추가 토큰을 args로 이동합니다. C:\Program Files\nodejs\node.exe와 같은 공백이 있는 절대 경로는 단일 유효한 실행 파일이며 경고를 트리거하지 않습니다.

HTTP hook 필드

공통 필드 외에도 HTTP hook은 다음 필드를 허용합니다: Claude Code는 hook의 JSON 입력을 Content-Type: application/json을 사용하여 POST 요청 본문으로 보냅니다. 응답 본문은 명령 hook과 동일한 JSON 출력 형식을 사용합니다. 오류 처리는 명령 hook과 다릅니다. HTTP 응답 처리를 참조하십시오. 이 예제는 PreToolUse 이벤트를 로컬 검증 서비스로 보내고 MY_TOKEN 환경 변수의 토큰으로 인증합니다:

MCP 도구 hook 필드

공통 필드 외에도 MCP 도구 hook은 다음 필드를 허용합니다: Claude Code는 도구의 텍스트 콘텐츠를 명령 hook stdout과 동일한 방식으로 읽으며, 종료 코드 0 아래의 파싱 규칙을 따릅니다. 명명된 서버가 연결되지 않았거나 도구가 isError: true를 반환하면 hook은 차단하지 않는 오류를 생성하고 실행이 계속됩니다. 이 예제는 각 Write 또는 Edit 후에 my_server MCP 서버의 security_scan 도구를 호출하고 편집된 파일의 경로를 전달합니다:
mcp_tool hook은 Claude Code가 세션의 MCP 서버를 hook에 사용 가능하게 만든 후에만 실행될 수 있습니다. SessionStart 및 Setup은 그 시점 이전에 실행될 수 있습니다:
  • 시작 시: SessionStart는 --continue 또는 --resume으로 시작할 때를 포함하여 서버를 사용 가능하기 전에 실행됩니다. Claude Code는 도구를 호출하지 않고 이벤트의 mcp_tool hook을 건너뛰고 디버그 로그는 mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context)를 기록합니다.
  • 실행 중인 세션의 나중: /clear 또는 압축 후 SessionStart는 서버가 이미 사용 가능한 상태에서 다시 실행되고 mcp_tool hook이 실행됩니다.
  • Setup에서: Setup은 항상 서버를 사용 가능하기 전에 실행되므로 Claude Code는 매번 mcp_tool hook을 건너뛰고 SessionStart를 명명하는 동일한 메시지를 기록합니다.
예를 들어 이 구성은 matcher가 없는 SessionStart hook에서 my_server MCP 서버의 load_context 도구를 호출하므로 모든 SessionStart 소스에 적용됩니다:
claude를 실행하면 Claude Code는 이 hook을 건너뛰고 load_context를 호출하지 않으며 no MCP client context 메시지를 디버그 로그에 씁니다. 동일한 세션에서 /clear를 실행하면 hook이 실행되고 load_context를 호출합니다. type: "command" hook은 SessionStart에서 실행되므로 세션이 첫 번째 턴에서 필요한 모든 항목에 하나를 사용합니다.

프롬프트 및 에이전트 hook 필드

공통 필드 외에도 프롬프트 및 에이전트 hook은 다음 필드를 허용합니다:

경로별 스크립트 참조

프로젝트 또는 플러그인 루트를 기준으로 hook 스크립트를 참조하려면 이 자리 표시자를 사용합니다. hook이 실행될 때의 작업 디렉토리와 관계없이:
  • ${CLAUDE_PROJECT_DIR}: 세션이 시작된 프로젝트 루트. Claude Code는 또한 stdio MCP 서버 및 플러그인 LSP 서버의 환경에서 이 변수를 설정합니다.
  • ${CLAUDE_PLUGIN_ROOT}: 플러그인과 함께 번들된 스크립트의 플러그인 설치 디렉토리. 업데이트 전반에 걸쳐 경로가 어떻게 작동하는지에 대해서는 플러그인 환경 변수를 참조하십시오.
  • ${CLAUDE_PLUGIN_DATA}: 플러그인 업데이트를 통해 유지되어야 하는 종속성 및 상태의 플러그인 영구 데이터 디렉토리.
Worktree는 다릅니다. Claude가 세션 중에 worktree에 들어가면 Claude Code는 ${CLAUDE_PROJECT_DIR}을 원래 위치에 유지하고 worktree 경로를 다른 방식으로 hook에 전달합니다:
  • ${CLAUDE_PROJECT_DIR}은 제자리에 유지됩니다: 여전히 세션이 시작된 프로젝트 루트를 가리키므로 ${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh와 같은 명령은 여전히 주 체크아웃에서 스크립트를 실행합니다.
  • cwd는 Claude를 따릅니다: Claude가 worktree에 들어간 후 hook의 입력 JSON의 cwd 필드는 worktree 루트이고, Claude가 cd를 실행한 후 새 디렉토리입니다. hook이 Claude가 작업 중인 디렉토리를 알아야 할 때 읽습니다.
경로 자리 표시자를 참조하는 모든 hook에 대해 exec 형식을 선호합니다. 셸 형식에서 각 자리 표시자를 큰따옴표로 래핑합니다.
이 예제는 ${CLAUDE_PROJECT_DIR}을 사용하여 모든 Write 또는 Edit 도구 호출 후 프로젝트의 .claude/hooks/ 디렉토리에서 스타일 검사기를 실행합니다:

스킬 및 에이전트의 Hook

설정 파일 및 플러그인 외에도 hook은 frontmatter를 사용하여 스킬 및 서브에이전트에서 직접 정의될 수 있으며, 설정 기반 hook과 동일한 구성 형식입니다. Claude Code가 등록된 상태를 유지하는 기간은 구성 요소에 따라 다릅니다:
  • 서브에이전트 hook: Claude Code는 해당 서브에이전트가 실행되는 동안만 실행하고 완료되면 제거합니다. Claude Code는 여기서 Stop hook을 SubagentStop으로 변환합니다. 서브에이전트가 완료될 때 실행되는 이벤트입니다.
  • 스킬 hook: Claude Code는 사용자 또는 Claude가 스킬을 호출할 때 등록하고 스킬의 자신의 턴 이후 턴뿐만 아니라 세션의 나머지 부분에서 실행을 계속합니다. 첫 번째 성공적인 실행 후 hook을 제거하려면 대신 once: true를 설정하십시오.
이 스킬은 각 Bash 명령 전에 보안 검증 스크립트를 실행하는 PreToolUse hook을 정의합니다:
서브에이전트는 YAML frontmatter에서 동일한 형식을 사용합니다. 프로젝트 스킬의 Frontmatter hook은 설정 파일의 hook과 동일한 작업 공간 신뢰 규칙을 따릅니다. Claude Code는 사용자 또는 Claude가 스킬을 호출할 때 등록합니다. 폴더를 신뢰하지 않은 -p 실행 포함. 프로젝트 서브에이전트의 Frontmatter hook은 에이전트 파일이 나온 폴더에 대해 작업 공간 신뢰 대화를 수락한 후에만 실행됩니다. -p 세션은 수락으로 계산되지 않습니다. 폴더를 신뢰하기 전에 실행되는 항목은 설정 파일 규칙과 비교하고 서브에이전트 페이지는 어떤 범위가 제외되는지 나열합니다. v2.1.218 이전에는 이러한 hook이 신뢰하지 않은 폴더에서 실행될 수 있었습니다.

/hooks 메뉴

Claude Code에서 /hooks를 입력하여 구성된 hook의 읽기 전용 브라우저를 엽니다. 메뉴는 구성된 hook 수를 포함하는 모든 hook 이벤트를 표시하고, matcher로 드릴다운하고, 각 hook 핸들러의 전체 세부 정보를 표시합니다. 구성을 확인하고, hook이 어느 설정 파일에서 나왔는지 확인하거나, hook의 명령, 프롬프트 또는 URL을 검사하는 데 사용합니다. 메뉴는 다섯 가지 hook 유형을 모두 표시합니다: command, prompt, agent, http 및 mcp_tool. 각 hook은 [type] 접두사와 정의된 위치를 나타내는 소스로 레이블이 지정됩니다:
  • User Settings: ~/.claude/settings.json에서
  • Project Settings: .claude/settings.json에서
  • Local Settings: .claude/settings.local.json에서
  • Plugin Hooks: 플러그인의 hooks/hooks.json에서
  • Session Hooks: 현재 세션에 대해 메모리에 등록됨
Hook을 선택하면 이벤트, matcher, 유형, 소스 파일 및 전체 명령, 프롬프트 또는 URL을 표시하는 세부 정보 보기가 열립니다. 메뉴는 읽기 전용입니다: hook을 추가, 수정 또는 제거하려면 설정 JSON을 직접 편집하거나 Claude에게 변경을 요청하십시오.

Hook 비활성화 또는 제거

hook을 제거하려면 설정 JSON 파일에서 항목을 삭제합니다. hook을 제거하지 않고 일시적으로 모든 hook을 비활성화하려면 설정 파일에서 "disableAllHooks": true를 설정합니다. Claude Code는 설정 우선 순위가 적용된 후 남은 값을 읽으므로 프로젝트의 .claude/settings.json의 "disableAllHooks": false는 사용자 설정의 true를 재정의합니다. 프로젝트의 설정이 무엇이든 한 번 실행에 대해 hook을 끄려면 --settings '{"disableAllHooks": true}'를 전달합니다. 이는 프로젝트 및 로컬 설정보다 우선합니다. 구성에 유지하면서 개별 hook을 비활성화할 방법이 없습니다. disableAllHooks 설정은 관리형 설정 계층을 존중합니다. 관리자가 관리형 정책 설정을 통해 hook을 구성한 경우 사용자, 프로젝트 또는 로컬 설정에서 설정된 disableAllHooks는 해당 관리형 hook을 비활성화할 수 없습니다. 관리형 설정 수준에서 설정된 disableAllHooks만 관리형 hook을 비활성화할 수 있습니다. 각 수준의 전체 범위에 대해서는 disableAllHooks를 참조하십시오. 설정 파일의 hook에 대한 직접 편집은 일반적으로 파일 감시자에 의해 자동으로 선택됩니다.

Hook 입출력

명령 hook은 stdin을 통해 JSON 데이터를 받고 종료 코드, stdout, stderr를 통해 결과를 전달합니다. HTTP hook은 POST 요청 본문으로 동일한 JSON을 받고 HTTP 응답 본문을 통해 결과를 전달합니다. 이 섹션에서는 모든 이벤트에 공통적인 필드와 동작을 다룹니다. Hook 이벤트 아래의 각 이벤트 섹션에는 특정 입력 스키마와 결정 제어 옵션이 포함됩니다. macOS 및 Linux에서 명령 hook은 제어 터미널 없이 자신의 세션에서 실행됩니다. hook 프로세스 및 모든 자식 프로세스는 /dev/tty를 열거나 Claude Code 인터페이스에 직접 이스케이프 시퀀스를 보낼 수 없습니다. Windows에는 /dev/tty가 없습니다. 모든 플랫폼에서 사용자에게 메시지를 표시하려면 JSON 출력에서 systemMessage를 반환합니다. 일부 이벤트는 이를 버리거나 다른 곳에 전달하며, 각 이벤트의 섹션에서 그렇게 말합니다. 데스크톱 알림을 트리거하거나 창 제목을 설정하거나 벨을 울리려면 대신 terminalSequence를 반환합니다.

공통 입력 필드

Hook 이벤트는 각 hook 이벤트 섹션에서 문서화된 이벤트 특정 필드 외에 이러한 필드를 JSON으로 받습니다. 명령 hook의 경우 이 JSON은 stdin을 통해 도착합니다. HTTP hook의 경우 POST 요청 본문으로 도착합니다. --agent로 실행하거나 subagent 내부에서 실행할 때 두 개의 추가 필드가 포함됩니다: SessionStart hook만 model 필드를 받을 수 있으며, Claude Code가 항상 포함하지는 않습니다. PreModelSwitch 및 PostModelSwitch hook은 대신 from_model 및 to_model을 받으므로 PostModelSwitch hook을 사용하여 세션 중에 모델이 변경될 때 모델을 따릅니다. $CLAUDE_MODEL 환경 변수는 없습니다. hook은 셸에서 설정한 경우 $ANTHROPIC_MODEL을 읽을 수 있지만, 세션 중에 /model로 모델을 전환할 때 해당 값은 변경되지 않습니다. hook 프로세스는 부모 환경을 상속하며, CLAUDE_CODE_SUBPROCESS_ENV_SCRUB가 1로 설정된 경우 Claude Code가 모든 서브프로세스에서 제거하는 OTEL_* 내보내기 변수와 제거하는 변수를 제외합니다. 예를 들어 Bash 명령에 대한 PreToolUse hook은 stdin에서 다음을 받습니다:
tool_name, tool_input, tool_use_id 필드는 이벤트 특정입니다. 각 hook 이벤트 섹션에서는 해당 이벤트의 추가 필드를 문서화합니다.

종료 코드 출력

hook 명령의 종료 코드는 Claude Code에 작업을 진행할지, 차단할지 또는 무시할지를 알려줍니다. 종료 코드는 단독으로 작동하지 않습니다. Claude Code는 모든 종료 코드에서 JSON 출력 필드를 stdout에서 읽으며, 표준 결정 모델을 사용하는 이벤트의 경우 스키마 검증을 통과하는 구문 분석된 객체가 코드와 함께 적용됩니다. Exit 2의 차단은 JSON이 재정의할 수 없는 유일한 결과입니다. 두 개의 표가 이벤트별 예외를 소유합니다: 이벤트별 종료 코드 2 동작은 각 이벤트에 대해 종료 코드가 수행하는 작업을 말하고, 결정 제어는 각 이벤트가 수행하는 결정 필드를 말합니다. systemMessage와 같은 범용 필드는 대부분의 이벤트에서 작동하며 JSON 출력 표에 나열됩니다.

종료 코드 0

종료 0은 성공을 의미하며, JSON을 인쇄하여 구조화된 제어를 할 때 의도된 종료 코드입니다. 대부분의 이벤트에서 Claude Code는 stdout을 디버그 로그에 기록하고 트랜스크립트에는 표시하지 않습니다. 예외는 UserPromptSubmit, UserPromptExpansion, SessionStart, PostModelSwitch이며, 여기서 Claude Code는 일반 텍스트 stdout을 Claude가 보고 작용할 수 있는 컨텍스트로 추가합니다. Claude Code가 stdout을 JSON 출력 또는 일반 텍스트로 읽는지 여부는 주변 공백을 무시하고 시작 및 종료 방식에 따라 달라집니다:
  • {로 시작하고 }로 끝남: Claude Code는 이를 JSON으로 구문 분석합니다. 출력이 각각 자체적으로 JSON으로 구문 분석되는 두 줄 이상이고 필드를 설정하는 JSON 출력 객체가 없는 경우 Claude Code는 전체 출력을 일반 텍스트로 취급합니다. 이러한 줄 중 하나가 필드를 설정하면 전체 출력은 아래에 설명된 구문 분석 실패입니다.
  • {로 시작하지만 }로 끝나지 않음: Claude Code는 이를 일반 텍스트로 취급합니다.
  • 다른 것으로 시작: Claude Code는 이를 일반 텍스트, JSON 배열 또는 포함된 따옴표 JSON 문자열로 취급합니다.
표준 결정 모델을 사용하는 이벤트의 경우 스키마 검증에 실패하는 구문 분석된 객체로 종료 0은 차단하지 않는 오류입니다: 작업이 진행되고 트랜스크립트는 검증 메시지와 함께 <hook name> hook error 알림을 표시합니다. 2 이외의 다른 종료 코드에서도 동일한 일이 발생하는 반면 종료 2는 여전히 차단합니다. 표준 결정 모델을 사용하는 이벤트의 경우 Claude Code가 stdout을 JSON으로 구문 분석하려고 시도하고 실패하면 2 이외의 모든 종료 코드에서 차단하지 않는 오류를 보고합니다. 트랜스크립트는 구문 분석 메시지와 함께 <hook name> hook error 알림을 표시합니다. 일반 텍스트 stdout을 컨텍스트로 추가하는 이벤트에서 Claude Code는 텍스트를 추가하지 않습니다. v2.1.248 이전에 Claude Code는 해당 stdout을 일반 텍스트로 취급했습니다. 종료 0으로 나가는 hook의 stderr은 디버그 로그로만 가며 트랜스크립트로는 가지 않으며 Claude는 이를 보지 못합니다. 직접 읽으려면 디버그 로깅을 활성화합니다. PostToolUse 또는 PostToolUseFailure hook에서 Claude에 경고를 표시하려면 대신 종료 2로 나가서 Claude가 stderr을 보도록 합니다. 도구는 이미 실행되었습니다.

종료 코드 2

종료 2는 차단 오류를 의미합니다. 차단할 수 있는 이벤트에서 종료 2는 JSON을 인쇄하는지 여부와 관계없이 차단합니다: JSON permissionDecision의 "allow"도 이를 재정의할 수 없습니다. Claude Code는 여전히 stdout에서 유효한 JSON 출력을 읽습니다. Elicitation 및 ElicitationResult에서 종료 2 hook의 hookSpecificOutput은 무시됩니다. 차단 메시지는 차단 결정을 하는 JSON의 이유이며, 그렇지 않으면 stderr 텍스트입니다. 차단이 수행하는 작업은 이벤트에 따라 다릅니다: PreToolUse는 도구 호출을 차단하고 UserPromptSubmit은 프롬프트를 거부합니다. 이벤트별 종료 코드 2 동작은 모든 이벤트의 효과를 나열하며, 각 이벤트의 섹션은 메시지가 어디로 가는지 말합니다. JSON 출력 스키마 검증에 실패하는 JSON을 인쇄하면서 종료 2로 나가는 hook은 여전히 차단합니다: Claude Code는 stderr을 차단 이유로 사용하고 검증 실패를 디버그 로그에 기록합니다. v2.1.214 이전에 Claude Code는 해당 조합을 차단하지 않는 오류로 취급했으며 작업이 진행되었습니다. 이 스크립트는 rm 명령을 차단하고 다른 모든 명령을 일반 권한 흐름으로 남깁니다:

다른 종료 코드

다른 종료 코드는 대부분의 hook 이벤트에 대해 자체적으로 차단하지 않습니다. 발생하는 일은 stdout에 따라 다릅니다:
  • 스키마 검증을 통과하는 구문 분석된 객체를 사용하면 표준 결정 모델을 사용하는 이벤트의 경우 Claude Code는 종료 코드를 무시하고 JSON만 결과를 결정합니다:
    • 이벤트가 지원하는 각 필드는 permissionDecision, additionalContext, updatedInput, systemMessage를 포함하여 수행되며 hook은 오류로 보고되지 않습니다.
    • 결정 제어는 이벤트별 결정 필드를 나열합니다. systemMessage와 같은 범용 필드는 JSON 출력 표를 따릅니다.
  • 스키마 검증에 실패하는 구문 분석된 객체를 사용하면 표준 결정 모델을 사용하는 이벤트의 경우 종료 0과 동일한 차단하지 않는 오류입니다: 작업이 진행되고 <hook name> hook error 알림은 검증 메시지를 전달합니다.
  • Claude Code가 JSON으로 구문 분석하려고 시도하고 실패하는 stdout을 사용하면 Claude Code는 표준 결정 모델을 사용하는 이벤트에 대해 종료 0과 동일한 차단하지 않는 오류를 보고합니다. 작업이 진행되고 알림은 구문 분석 메시지를 전달합니다.
  • Claude Code가 일반 텍스트로 취급하는 stdout을 사용하거나 빈 stdout을 사용하면 대부분의 hook 이벤트에 대해 차단하지 않는 오류입니다: 작업이 진행되고 트랜스크립트는 <hook name> hook error 알림을 표시한 후 stderr의 첫 번째 줄을 Failed with non-blocking status code:로 접두사를 붙여 표시합니다. 전체 stderr을 캡처하려면 디버그 로깅을 활성화합니다.
표준 결정 모델 외부의 이벤트는 이벤트별 표에서 자신의 행을 유지합니다: WorktreeCreate는 JSON이 무엇을 말하든 0이 아닌 종료 코드에서 생성을 실패하고, StopFailure와 같이 hook 출력을 완전히 버리는 이벤트는 모든 종료 코드에서 JSON을 무시하며, terminalSequence와 같은 부작용 필드는 여전히 발생합니다. 시작할 수 없는 hook은 동일한 차단하지 않는 버킷에 도착합니다. 스크립트 경로가 존재하지 않거나 실행 가능하지 않으면 셸은 127과 같은 코드로 종료되고 인터프리터의 메시지와 함께 동일한 알림을 봅니다. 예를 들어 Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory. 대부분의 hook 이벤트에서 작업이 진행됩니다. 정책 hook을 설정할 때 첫 번째 실행에서 이 알림을 확인합니다: settings.json의 오타 경로는 게이트를 조용히 비활성화합니다.
대부분의 hook 이벤트에서 종료 코드 2는 코드만으로 차단하는 유일한 종료 코드입니다. stdout에 유효한 JSON이 없으면 Claude Code는 종료 코드 1을 차단하지 않는 오류로 취급하고 작업을 진행합니다. 1이 기존 Unix 실패 코드이지만 말입니다. hook이 정책을 적용하려면 exit 2를 사용합니다. worktree 이벤트는 다릅니다: WorktreeCreate의 0이 아닌 종료 코드는 worktree 생성을 중단하고, WorktreeRemove의 0이 아닌 종료 코드는 디렉토리가 여전히 존재하면 worktree 제거를 실패하게 합니다.

시간 초과

async: true로 실행하는 명령 hook을 제외하고 Claude Code는 timeout에 도달하는 command, http, mcp_tool hook을 취소하고 hook의 출력을 버리므로 대부분의 이벤트에서 시간 초과된 hook은 결정을 렌더링하지 않습니다. PreModelSwitch에서 시간 초과에서 취소된 hook은 모델 전환을 차단합니다. PreToolUse에서 두 hook 패밀리는 다릅니다:

이벤트별 종료 코드 2 동작

종료 코드 2는 hook이 “멈춰, 이것을 하지 마”라고 신호하는 방식입니다. 효과는 이벤트에 따라 다릅니다. 일부 이벤트는 차단할 수 있는 작업을 나타내기 때문입니다 (아직 발생하지 않은 도구 호출처럼) 그리고 다른 이벤트는 이미 발생했거나 방지할 수 없는 것을 나타냅니다. SessionStart, SubagentStart, PostModelSwitch의 경우 Claude Code는 종료 코드 2 stderr을 트랜스크립트에 <hook name> hook error 알림으로 렌더링하며, 차단하지 않는 오류와 동일한 방식입니다. Claude는 이를 보지 못하며 세션 또는 subagent는 진행됩니다. SubagentStart의 경우 알림은 부모 대화가 아닌 subagent의 자신의 트랜스크립트에 나타납니다.

HTTP 응답 처리

HTTP hook은 종료 코드와 stdout 대신 HTTP 상태 코드와 응답 본문을 사용합니다. 아래의 결과는 대부분의 이벤트에 적용됩니다. 이벤트별 표에서 자신의 실패 계약을 가진 이벤트 (예: WorktreeCreate)는 실패한 HTTP hook에도 해당 계약을 적용합니다:
  • 2xx 빈 본문: 성공, 종료 코드 0과 출력 없음과 동등
  • 2xx JSON 객체 본문: 명령 hook과 동일한 JSON 출력 스키마를 사용하여 구문 분석됩니다. 스키마 검증에 실패하는 본문은 차단하지 않는 오류입니다
  • 2xx 일반 텍스트와 같은 다른 본문: 차단하지 않는 오류, 2xx가 아닌 상태와 동일하게 처리됩니다. Claude Code는 텍스트를 Claude의 컨텍스트에 추가하지 않습니다
  • 2xx가 아닌 상태: 차단하지 않는 오류, 실행이 계속됨
  • 연결 실패: 차단하지 않는 오류, 실행이 계속됨
  • 시간 초과: 시간 초과 아래에 설명된 대로 hook이 취소됩니다
명령 hook과 달리 HTTP hook은 상태 코드만으로 차단 오류를 신호할 수 없습니다. 도구 호출을 차단하거나 권한을 거부하려면 적절한 결정 필드를 포함하는 JSON 본문이 있는 2xx 응답을 반환합니다.

JSON 출력

종료 코드는 차단하거나 침묵할 수 있지만 JSON 출력은 더 세밀한 제어를 제공합니다. 종료 코드 2로 차단하는 대신 종료 0으로 JSON 객체를 stdout에 인쇄합니다. Claude Code는 해당 JSON에서 특정 필드를 읽어 차단, 허용 또는 사용자에게 에스컬레이션을 포함한 동작을 제어합니다. 결정 제어는 차단, 허용 또는 에스컬레이션을 위한 필드를 나열합니다.
hook당 하나의 접근 방식을 선택합니다: 종료 코드만 사용하여 신호하거나 종료 0으로 JSON을 인쇄하여 구조화된 제어를 합니다. 둘을 섞으면 종료 2는 차단 효과를 유지하고 Claude Code는 여전히 JSON 필드를 읽으며, Elicitation 예외가 종료 코드 2 아래에 기록됩니다.
hook의 stdout은 JSON 객체만 포함해야 합니다. 셸 프로필이 시작 시 텍스트를 인쇄하면 JSON 구문 분석을 방해할 수 있습니다. 문제 해결 가이드의 Hook JSON이 효과가 없음을 참조하세요. hook의 additionalContext, systemMessage, initialUserMessage 문자열 및 일반 stdout은 10,000자로 제한됩니다:
  • 범위: Claude Code는 각 문자열을 자체적으로 측정하며, 동일한 이벤트에 대해 여러 hook이 실행되는 경우에도 마찬가지입니다. JSON 출력의 경우 각 필드는 별도로 측정되며, 일반 stdout은 전체적으로 측정됩니다.
  • 제한 초과: Claude Code는 출력을 세션 디렉토리의 파일에 저장하고 파일 경로와 최대 처음 2,000자의 미리보기로 바꿉니다. 큰 유효한 Bash 결과는 출력 제한 아래에 설명된 동일한 방식으로 처리됩니다. 이 Bash 상한과 달리 이 제한에는 이를 높이기 위한 설정이나 환경 변수가 없습니다.
  • 파일 읽기: Claude Code는 Claude에 파일을 읽도록 요청하지 않으므로 Claude가 항상 봐야 할 항목은 제한 내에 유지하세요.
JSON 객체는 세 가지 종류의 필드를 지원합니다:
  • continue와 같은 범용 필드는 아래 표에 나열됩니다. 모든 이벤트가 이들을 허용하지만 일부 이벤트는 이들을 버리거나 systemMessage를 트랜스크립트 이외의 다른 곳에 전달합니다. 각 이벤트의 섹션에서 그렇게 말합니다. terminalSequence는 터미널 알림 내보내기 아래에 나열된 예외를 제외하고 이러한 이벤트에서도 작동합니다.
  • **최상위 decision 및 reason**은 일부 이벤트에서 차단하거나 피드백을 제공하는 데 사용됩니다.
  • **hookSpecificOutput**은 더 풍부한 제어가 필요한 이벤트를 위한 중첩 객체입니다. 이벤트 이름으로 설정된 hookEventName 필드가 필요합니다.
Claude를 완전히 중지하려면:
PreToolUse 및 PostToolUse hook의 경우 도구 호출이 실패하거나 Claude가 여전히 응답을 스트리밍하는 동안 완료되어도 중지가 적용됩니다.

터미널 알림 내보내기

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 포함)은 거부되고 필드는 무시됩니다. Claude Code는 hook의 출력을 처리할 때 시퀀스 자체를 기록하므로 필드는 systemMessage 및 continue를 버리는 이벤트 (예: Notification 및 StopFailure)에서 작동합니다. 두 가지 제한이 있습니다:
  • Claude Code는 대화형 세션에서만 시퀀스를 기록하고 인터페이스가 화면에 있을 때만 기록합니다. -p 플래그를 사용한 비대화형 모드 및 Agent SDK에서 필드를 무시합니다.
  • WorktreeCreate 명령 hook은 Claude Code가 stdout을 worktree 경로로 읽기 때문에 JSON을 반환할 수 없습니다. HTTP WorktreeCreate hook은 JSON을 반환하고 필드를 포함할 수 있습니다.
아래 예제는 Notification hook에서 데스크톱 알림을 발생시킵니다. 이스케이프 시퀀스는 printf 8진수 이스케이프로 빌드되므로 제어 바이트가 셸 명령줄에 나타나지 않으며, jq -n --arg는 JSON 출력을 빌드하므로 알림 메시지의 따옴표, 백슬래시, 줄바꿈이 올바르게 이스케이프됩니다:
{ "terminalSequence": "..." } 형태는 모든 셸 또는 언어에서 동일합니다.

Claude를 위한 컨텍스트 추가

additionalContext 필드는 hook에서 Claude의 컨텍스트 윈도우로 문자열을 전달합니다. Claude Code는 문자열을 시스템 미리 알림으로 래핑하고 hook이 발생한 지점에서 대화에 삽입합니다. Claude는 다음 모델 요청에서 미리 알림을 읽지만 인터페이스에 채팅 메시지로 나타나지 않습니다. 이벤트 이름과 함께 hookSpecificOutput 내에 additionalContext를 반환합니다:
미리 알림이 나타나는 위치는 이벤트에 따라 다릅니다: 여러 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"으로 설정된 재개 시 다시 실행되거나 "fork"로 설정된 경우 --fork-session을 추가했으므로 컨텍스트를 새로 고칠 수 있습니다.

결정 제어

모든 이벤트가 JSON을 통해 동작을 차단하거나 제어하는 것을 지원하는 것은 아닙니다. 그렇게 하는 이벤트는 각각 다른 필드 집합을 사용하여 해당 결정을 표현합니다. hook을 작성하기 전에 이 표를 빠른 참조로 사용하세요: 일부 이벤트는 또한 허용 또는 차단하는 것이 아니라 콘텐츠를 다시 작성할 수 있습니다:
  • PreToolUse: hookSpecificOutput 바로 아래의 updatedInput은 실행 전에 도구의 인수를 바꿉니다. PreToolUse 결정 제어 참조
  • PermissionRequest: decision 객체 내의 updatedInput. PermissionRequest 결정 제어 참조
  • PostToolUse: updatedToolOutput은 도구의 결과를 바꿉니다. PostToolUse 결정 제어 참조
  • UserPromptSubmit: 프롬프트를 바꿀 수 없습니다. additionalContext를 옆에만 주입합니다
편집 또는 변환 사용 사례의 경우 아웃바운드 도구 입력에 대해 PreToolUse에서 가로채고 인바운드 도구 결과에 대해 PostToolUse에서 가로채세요. 다음은 각 패턴의 실제 예입니다:
유일한 값은 "block"입니다. 작업을 진행하도록 허용하려면 JSON에서 decision을 생략하거나 JSON 없이 종료 0으로 나갑니다:
Bash 명령 검증, 프롬프트 필터링, 자동 승인 스크립트를 포함한 확장 예제는 가이드의 자동화할 수 있는 것과 Bash 명령 검증기 참조 구현을 참조하세요.

Hook 이벤트

각 이벤트는 hook이 실행될 수 있는 Claude Code의 수명 주기의 지점에 해당합니다. 아래 섹션은 수명 주기와 일치하도록 정렬됩니다: 세션 설정에서 에이전트 루프를 거쳐 세션 종료까지. 각 섹션에서는 이벤트가 언제 발생하는지, 지원하는 matcher, 받는 JSON 입력, 출력을 통해 동작을 제어하는 방법을 설명합니다.

SessionStart

Claude Code가 새 세션을 시작하거나 기존 세션을 재개할 때 실행됩니다. 기존 문제나 코드베이스의 최근 변경 사항과 같은 개발 컨텍스트를 로드하거나 환경 변수를 설정하는 데 유용합니다. 스크립트가 필요하지 않은 정적 컨텍스트의 경우 CLAUDE.md를 사용하세요. SessionStart는 모든 세션에서 실행되므로 이러한 hook을 빠르게 유지하세요. type: "command" 및 type: "mcp_tool" hook만 지원됩니다. MCP tool hook 필드를 참조하여 mcp_tool hook이 언제 실행되는지 확인하세요. matcher 값은 세션이 시작된 방식에 해당합니다: v2.1.214 이전에는 포크된 세션이 소스 "resume"을 보고했습니다. 대화형 세션을 시작하거나 --continue 또는 --resume으로 시작 시 대화를 재개하거나 /clear를 실행할 때 SessionStart hook은 백그라운드에서 실행됩니다. 바로 입력할 수 있으며 재개한 대화는 hook을 기다리지 않고 나타납니다. Claude의 첫 번째 응답은 여전히 hook이 완료될 때까지 기다리므로 해당 컨텍스트가 Claude에 도달합니다. 세션 내에서 /resume으로 대화를 전환할 때 전환은 hook이 완료될 때까지 기다립니다. 백그라운드 hook이 여전히 실행 중인 동안 /clear를 실행하거나 다른 대화로 전환하면 반환하는 것이 세션에 적용되지 않습니다. 시작 시에도 동일한 대기가 적용됩니다 (재개된 세션 포함): SessionStart hook이 여전히 실행 중인 동안 전송하는 프롬프트는 완료될 때까지 Claude에 도달하지 않습니다. 대기 중에 Esc를 눌러 프롬프트를 입력으로 다시 가져올 수 있습니다. Hook은 계속 실행됩니다.

SessionStart 입력

공통 입력 필드 외에도 SessionStart hook은 source 및 선택적으로 model, agent_type, session_title을 받습니다: source가 "resume" 또는 "fork"이고 트랜스크립트에 Claude의 응답이 최소 하나 포함되어 있을 때 SessionStart hook은 아래의 네 필드도 받습니다. Hook은 이를 사용하여 첫 번째 요청 전에 오래된 대화를 재개하는 비용을 보고할 수 있습니다 (예: systemMessage에서). 이 필드는 Claude Code v2.1.251 이상이 필요합니다. 이 예제는 마지막 응답 90분 후에 재개된 세션의 입력을 보여줍니다:

SessionStart 결정 제어

Claude Code는 일반 텍스트로 처리하는 stdout을 Claude의 컨텍스트에 추가합니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 이러한 이벤트 특정 필드를 반환할 수 있습니다:
이 이벤트에 대해 일반 stdout이 이미 Claude에 도달하므로 컨텍스트만 로드하는 hook은 JSON을 구축하지 않고 stdout에 직접 인쇄할 수 있습니다. sessionTitle과 같은 다른 필드와 컨텍스트를 결합해야 할 때 JSON 형식을 사용합니다. SessionStart hook이 skill을 설치하거나 업데이트할 때 reloadSkills를 사용합니다. Skill 발견은 일반적으로 SessionStart hook이 완료되기 전에 실행되므로 hook이 ~/.claude/skills/ 또는 .claude/skills/에 작성하는 파일은 그렇지 않으면 다음 세션에만 나타납니다. 이 예제는 공유 skill 리포지토리를 동기화하고 다시 스캔을 요청합니다:
리포지토리 URL은 자리 표시자입니다; 자신의 skill 리포지토리로 바꾸세요. 자리 표시자를 사용하면 복제가 실패하고 stderr에 fatal: 메시지를 인쇄합니다. 종료 코드 0으로 종료되는 SessionStart hook의 stderr은 정보 전용이므로 reloadSkills 요청은 여전히 적용됩니다.

환경 변수 유지

SessionStart hook은 CLAUDE_ENV_FILE 환경 변수에 액세스할 수 있으며, 이는 후속 Bash 명령에 대한 환경 변수를 유지할 수 있는 파일 경로를 제공합니다. 개별 환경 변수를 설정하려면 CLAUDE_ENV_FILE에 export 문을 작성합니다. 다른 hook에서 설정한 변수를 유지하려면 추가 (>>)를 사용합니다:
설정 명령의 환경 변경을 모두 캡처하려면 내보낸 변수를 이전과 이후에 비교합니다:
CLAUDE_ENV_FILE은 SessionStart, Setup, CwdChanged, FileChanged hook에 사용 가능합니다. 다른 hook 유형은 이 변수에 액세스할 수 없습니다.

Setup

--init-only로 Claude Code를 시작하거나 비대화형 모드에서 -p 플래그와 함께 --init 또는 --maintenance로 시작할 때만 발생합니다. 일반 시작 시에는 발생하지 않습니다. 일회성 종속성 설치 또는 CI 또는 스크립트에서 명시적으로 트리거하는 예약된 정리에 사용합니다. 일반 세션 시작과 별도입니다. 세션별 초기화의 경우 대신 SessionStart를 사용합니다. matcher 값은 hook을 트리거한 CLI 플래그에 해당합니다: claude --init-only를 실행하면 Claude Code는 Setup hook과 startup matcher가 있는 SessionStart hook을 실행한 다음 대화를 시작하지 않고 종료합니다. -p로 대화를 시작하거나 계속할 때 프롬프트도 제공해야 합니다 (인수로 또는 stdin에 파이프됨). SessionStart hook이 initialUserMessage를 제공하거나 연기된 도구 호출로 세션을 재개할 때 프롬프트를 건너뛸 수 있습니다. 성공 시 --init-only는 터미널에 아무것도 인쇄하지 않습니다. hook이 실행되었는지 확인하려면 claude --debug-file <path> --init-only로 시작하고 <path>를 로그 파일 위치로 바꾸고 Setup 및 SessionStart hook 항목에 대한 로그를 확인합니다. Setup은 모든 시작 시 발생하지 않으므로 종속성이 설치된 plugin은 Setup만으로는 의존할 수 없습니다. 실제 패턴은 첫 사용 시 종속성을 확인하고 누락되면 설치하는 것입니다. 예를 들어 ${CLAUDE_PLUGIN_DATA}/node_modules를 테스트하고 없으면 npm install을 실행하는 hook 또는 skill입니다. 지속적 데이터 디렉토리를 참조하여 설치된 종속성을 저장할 위치를 확인하세요. plugin을 마켓플레이스를 통해 배포하는 경우 이 패턴이 필요하지 않을 수 있습니다: Claude Code는 plugin을 캐시할 때 적격 Node.js 패키지 종속성을 자동으로 설치합니다.

Setup 입력

공통 입력 필드 외에도 Setup hook은 trigger 필드를 받으며, 이는 "init" 또는 "maintenance"로 설정됩니다:

Setup 결정 제어

Setup hook은 차단할 수 없습니다; 모든 종료 코드에서 실행이 계속됩니다. 모든 종료 코드에서 Claude Code는 Setup hook의 JSON 출력 필드 (예: systemMessage, continue, hookSpecificOutput.additionalContext)를 삭제합니다. -p를 사용하면 Setup hook의 stdout, stderr, 종료 코드는 --output-format stream-json --verbose로 시작할 때만 hook_response 이벤트로 실행 출력에 나타납니다. Setup hook은 CLAUDE_ENV_FILE에 액세스할 수 있습니다. 해당 파일에 작성된 변수는 SessionStart hook과 마찬가지로 세션의 후속 Bash 명령에 유지됩니다. type: "command" hook만 Setup에서 실행됩니다. type: "mcp_tool" hook은 MCP tool hook 필드에서 설명한 대로 Setup에서 항상 건너뜁니다.

InstructionsLoaded

CLAUDE.md 또는 .claude/rules/*.md 파일이 컨텍스트에 로드될 때 발생합니다. 이 이벤트는 세션 시작 시 즉시 로드된 파일에 대해 발생하고 나중에 파일이 지연 로드될 때 다시 발생합니다. 예를 들어 Claude가 중첩된 CLAUDE.md를 포함하는 하위 디렉토리에 액세스할 때 또는 paths: frontmatter가 있는 조건부 규칙이 일치할 때입니다. hook은 차단 또는 결정 제어를 지원하지 않습니다. 관찰성 목적으로 비동기적으로 실행됩니다. 이 이벤트는 Claude가 Project instructions 설정을 통해 AGENTS.md를 직접 읽을 때는 발생하지 않습니다. CLAUDE.md가 AGENTS.md를 가져올 때는 다른 가져온 파일과 마찬가지로 load_reason이 include로 설정되어 발생하며, CLAUDE.md가 이에 대한 symlink일 때는 일반 CLAUDE.md 로드로 발생합니다. matcher는 load_reason에 대해 실행됩니다. 예를 들어 "matcher": "session_start"를 사용하여 세션 시작 시에만 로드된 파일에 대해 발생하거나 "matcher": "path_glob_match|nested_traversal"을 사용하여 지연 로드에만 발생합니다.

InstructionsLoaded 입력

공통 입력 필드 외에도 InstructionsLoaded hook은 이러한 필드를 받습니다:

InstructionsLoaded 결정 제어

InstructionsLoaded hook은 결정 제어가 없습니다. 명령 로드를 차단하거나 수정할 수 없습니다. Claude Code는 이들의 JSON 출력 필드 (예: systemMessage, continue)를 삭제합니다. 감사 로깅, 규정 준수 추적 또는 관찰성을 위해 이 이벤트를 사용합니다.

UserPromptSubmit

사용자가 프롬프트를 제출할 때, Claude가 처리하기 전에 실행됩니다. 이를 통해 프롬프트/대화를 기반으로 추가 컨텍스트를 추가하거나, 프롬프트를 검증하거나, 특정 유형의 프롬프트를 차단할 수 있습니다. UserPromptSubmit hook은 command, http, mcp_tool 유형에 대해 기본 30초 시간 초과를 가지며, 이는 대부분의 다른 이벤트에서 이러한 유형의 기본 600초보다 짧습니다. 이 hook은 모든 프롬프트 전에 실행되고 완료될 때까지 모델 처리를 차단하므로 stuck hook은 세션을 정지시킵니다. hook에 더 많은 시간이 필요하면 hook 항목에서 timeout 필드를 설정합니다. async: true로 실행하는 명령 hook을 제외하고, 시간 초과에 도달한 UserPromptSubmit 명령, HTTP 또는 MCP 도구 hook은 취소되고 additionalContext를 포함한 출력은 삭제됩니다. 프롬프트는 해당 컨텍스트 없이 여전히 Claude에 전달됩니다. 트랜스크립트에는 hook 이름, 발생한 시간 초과, 출력이 삭제되었음을 나타내는 알림이 표시됩니다. Agent SDK callback hook이 UserPromptSubmit에서 시간 초과에 도달하면 hook의 이름과 시간 초과를 나타내는 메시지로 프롬프트를 차단합니다. 왜냐하면 callback은 실패하더라도 통과시켜서는 안 되는 정책 게이트로 작동할 수 있기 때문입니다. 세션이 계속됩니다. v2.1.208 이전에는 callback 시간 초과가 실행 오류로 턴을 종료했습니다.

UserPromptSubmit 입력

공통 입력 필드 외에도 UserPromptSubmit hook은 사용자가 제출한 텍스트를 포함하는 prompt 필드를 받습니다.

UserPromptSubmit 결정 제어

UserPromptSubmit hook은 사용자 프롬프트 처리 여부를 제어하고 컨텍스트를 추가할 수 있습니다. 모든 JSON 출력 필드를 사용할 수 있습니다. 종료 코드 0에서 대화에 컨텍스트를 추가하는 두 가지 방법이 있습니다:
  • 일반 텍스트 stdout: Claude Code는 일반 텍스트로 처리하는 stdout을 Claude의 컨텍스트에 추가합니다
  • additionalContext가 있는 JSON: 더 많은 제어를 위해 아래 JSON 형식을 사용합니다. additionalContext 필드는 컨텍스트에 추가됩니다
둘 다 가시적인 트랜스크립트 항목을 생성하지 않습니다. 일반 stdout과 additionalContext 값은 각각 hook의 이름으로 시작하는 시스템 알림으로 주입됩니다; Claude는 둘 다 읽습니다. 전달을 확인하려면 디버그 로그를 확인하세요. 프롬프트를 차단하려면 decision을 "block"으로 설정한 JSON 객체를 반환합니다: 종료 코드 2로 차단하는 hook은 reason과 동일한 방식으로 라우팅됩니다: 차단 메시지는 stderr 텍스트를 사용자에게 표시하고 컨텍스트에 추가되지 않습니다.

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입니다.

UserPromptExpansion 결정 제어

UserPromptExpansion hook은 확장을 차단하거나 컨텍스트를 추가할 수 있습니다. 모든 JSON 출력 필드를 사용할 수 있습니다. 종료 코드 2로 차단하는 hook은 reason과 동일한 방식으로 라우팅됩니다: 차단 메시지는 stderr 텍스트를 사용자에게 표시합니다.

MessageDisplay

어시스턴트 메시지가 화면으로 스트리밍되는 동안 실행됩니다. Claude Code는 메시지를 증분으로 표시합니다: 새로 완료된 줄의 배치가 렌더링될 준비가 될 때마다 hook이 한 번 실행되고 Claude Code는 hook의 대체 텍스트를 그 자리에 렌더링합니다. 긴 메시지는 여러 호출을 생성합니다; 짧은 메시지는 하나만 생성할 수 있습니다. MessageDisplay를 사용하여:
  • markdown을 제거하여 최소한의 표시
  • Agent SDK 애플리케이션이 사용자에게 표시하는 텍스트 변환
  • Claude의 응답에서 API 키 또는 내부 호스트명 제거
Claude Code는 각 배치를 hook이 반환할 때까지 보유하므로 hook을 빠르게 유지하세요. hook이 실패하거나 시간 초과되면 Claude Code는 원본 텍스트를 표시합니다. 이 이벤트의 기본 시간 초과는 10초입니다; hook에 더 많은 시간이 필요하면 hook 항목에서 timeout 필드를 설정합니다. MessageDisplay는 표시 전용입니다: 대체 텍스트는 화면에 렌더링되는 것만 변경합니다. 트랜스크립트와 Claude가 보는 것은 원본 텍스트를 유지하므로 Claude는 대체를 보지 못하고 verbose 모드는 원본을 표시합니다. hook은 어시스턴트 메시지 텍스트만 받으므로 도구 결과와 입력한 텍스트는 변경되지 않은 상태로 렌더링됩니다. MessageDisplay는 matcher를 지원하지 않으며 텍스트를 스트리밍하는 모든 어시스턴트 메시지에 대해 발생합니다; 도구 호출 전용 응답과 같이 텍스트가 없는 메시지는 이를 트리거하지 않습니다. 비대화형 실행 (Agent SDK 쿼리 및 claude -p 포함)에서 MessageDisplay는 줄의 배치당 한 번이 아닌 어시스턴트 메시지당 한 번 실행됩니다. 단일 호출은 메시지가 완료된 후 도착하고 전체 메시지 텍스트를 전달합니다: index는 0, final은 true, delta는 전체 메시지를 보유합니다. 각 메시지에 대해 delta 텍스트를 수집하는 hook은 두 모드 모두에서 동일한 총 텍스트를 받습니다.

MessageDisplay 입력

공통 입력 필드 외에도 MessageDisplay hook은 턴과 메시지의 식별자, 이 호출이 메시지 내에서의 위치, delta의 새 텍스트를 받습니다. 배치 경계는 텍스트가 스트리밍되는 방식에 따라 다르므로 줄이 특정 방식으로 그룹화될 것으로 예상하기보다는 index 및 final을 사용하여 메시지를 통한 진행 상황을 추적합니다.

MessageDisplay 출력

모든 hook에 사용 가능한 JSON 출력 필드 외에도 MessageDisplay hook은 displayContent를 반환하여 화면의 delta를 바꿀 수 있습니다: MessageDisplay hook은 결정 제어가 없습니다. 메시지를 차단하거나 트랜스크립트에 저장되거나 Claude에 전송되는 것을 변경할 수 없습니다. Claude Code는 이들의 JSON 출력에서 displayContent를 작동하고 systemMessage 및 continue를 삭제합니다. 이 예제는 Claude의 응답에서 markdown 형식을 제거하여 일반 텍스트 표시를 합니다. 스크립트는 stdin에서 각 배치를 읽고 delta에서 굵은 마커와 인라인 코드 백틱을 제거하고 결과를 displayContent로 반환합니다.
설정 파일에서 이벤트에 대한 명령 hook을 등록합니다:
이 스크립트를 프로젝트의 .claude/hooks/plain-display.sh에 저장하고 chmod +x로 실행 가능하게 만듭니다:
markdown이 없는 배치는 변경되지 않은 상태로 통과합니다. 스크립트가 실패하면 (예: jq가 누락된 경우) Claude Code는 원본 텍스트를 표시하고 디버그 출력에서만 실패를 기록하며 세션에서는 기록하지 않습니다.

PreToolUse

Claude가 도구 매개변수를 생성한 후 도구 호출을 처리하기 전에 실행됩니다. EndConversation을 제외한 모든 도구 이름에서 일치합니다: Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion, ExitPlanMode와 같은 기본 제공 도구, 그리고 모든 MCP 도구 이름. 특정 파일이 디스크에서 변경될 때 (어떤 것이 작성했든) hook을 실행하려면 파일 편집 도구를 이름으로 일치시키는 대신 FileChanged를 사용합니다. PreToolUse와 달리 Claude Code는 FileChanged hook을 변경 후에 실행하고 결정 제어가 없으므로 쓰기를 차단할 수 없습니다.
PreToolUse는 Claude가 도구를 호출할 때만 실행됩니다. 프롬프트에서 @로 참조하는 파일은 도구 호출 없이 추가됩니다: Claude Code는 프롬프트를 구축하는 동안 해당 내용을 삽입하므로 Read와 일치하는 hook을 포함하여 PreToolUse hook이 발생하지 않습니다. 특정 경로를 @ 참조에서 차단하려면 Read 거부 규칙을 대신 사용하세요.PreToolUse는 또한 EndConversation에 대해 발생하지 않습니다.
PreToolUse 결정 제어를 사용하여 도구 사용을 허용, 거부, 요청 또는 연기합니다. Agent SDK callback hook이 PreToolUse에서 시간 초과를 초과하면 도구 호출을 차단하고 Claude는 시간 초과를 이름으로 지정하는 오류 결과를 받습니다. 다른 hook이 반환한 명시적 거부가 여전히 우선합니다.

PreToolUse 입력

공통 입력 필드 외에도 PreToolUse hook은 tool_name, tool_input, tool_use_id를 받습니다. MCP 도구의 경우 입력은 또한 서버의 name과 서버의 정의가 어디에서 왔는지를 나타내는 source가 있는 객체인 mcp_server를 전달합니다. source 값에는 plugin, sdk, user, project와 같은 구성 범위가 포함됩니다. McpServerProvenance는 Agent SDK 참조에서 모두 나열하고 인식하지 못하는 것을 처리하는 방법을 설명합니다. name 또는 mcp__<server>__ 도구 이름 접두사가 아닌 source를 기반으로 신뢰 결정을 내립니다. mcp_server 필드는 Claude Code v2.1.274 이상이 필요합니다. 파일 도구 Write, Edit, Read의 경우 tool_input.file_path는 항상 절대 경로입니다:
  • Claude Code는 hook이 실행되기 전에 ~ 및 상대 경로를 확장하므로 경로와 일치하는 hook은 ~ 또는 동일한 경로의 상대 철자를 통해 우회될 수 없습니다
  • Windows에서 경로는 $PWD가 /c/project처럼 보이는 Git Bash에서 hook이 실행되더라도 백슬래시 구분 기호로 도착합니다
  • 정방향 슬래시로 작성된 비교 (예: /src/ 검사)는 백슬래시 경로와 절대 일치하지 않으며 hook이 차단할 것이 없는 것처럼 도구 호출이 진행됩니다
  • 비교 전에 구분 기호를 정규화합니다: Bash에서 FILE_PATH="${FILE_PATH//\\//}", Python에서 file_path.replace("\\", "/"), 그런 다음 ^로 고정하지 않고 /src/와 같은 경로 세그먼트와 일치합니다 (경로는 절대 경로이므로)
Windows의 Write 호출은 다음을 전달합니다:
tool_input 필드는 도구에 따라 다릅니다: 셸 명령을 실행합니다. Bash 명령이 Git 리포지토리의 파일을 변경할 때 Claude Code는 변경된 내용을 기록할 수 있습니다. bashEditDiffEnabled 설정이 기록을 켤 때 모든 권한 모드에서 기록합니다; 해당 설정의 항목은 어떤 파일이 이를 설정할 수 있는지 말합니다. 그렇지 않으면 자동 모드 및 bypassPermissions 모드에서만 기록하고 Claude Code가 Bash를 통해 파일을 편집하도록 지시할 때만 기록합니다. bashEditDiffEnabled를 false로 설정하여 기록을 끕니다. 백그라운드 명령과 읽기 전용 명령은 diff를 전달하지 않습니다. PostToolUse hook은 tool_response.bashEditDiff에서 변경된 파일을 받습니다. 목록은 명령이 실행되는 동안 리포지토리 아래에서 변경된 내용을 다룹니다. Git이 무시하는 파일과 하위 모듈의 파일은 나열되지 않습니다. Claude Code v2.1.269 이상이 필요합니다.
목록은 최선의 노력이며 공개 베타입니다. Claude Code는 변경을 놓칠 수 있고, 동시에 다른 프로세스가 변경한 파일을 포함할 수 있으며, 크기 제한에서 중지할 수 있습니다. 필드 형태가 변경될 수 있습니다. 정책을 적용하지 않고 검토할 내용을 찾기 위해 목록을 사용합니다.
changedFiles 및 files는 명령이 변경한 내용을 나열합니다; 나머지 필드는 해당 목록이 얼마나 완전하고 신뢰할 수 있는지를 말합니다. PowerShell 명령을 실행합니다. PowerShell 도구의 가용성을 플랫폼별로 참조하세요. 필드는 Bash 도구와 일치하며 command 문자열에 명령이 있습니다: 셸 명령을 검사하는 hook에서 Bash|PowerShell과 일치하여 두 도구를 모두 다룹니다:
  • Windows에서 PowerShell 도구가 활성화된 곳이면 Claude는 PowerShell을 기본 셸로 취급하고 셸 명령을 통해 라우팅합니다.
  • Git Bash가 없는 Windows에서 도구는 자동으로 활성화되고 Claude Code는 Bash 도구를 등록하지 않습니다.
  • Bash만 일치하는 hook은 거기서 절대 발생하지 않습니다.
파일을 생성하거나 덮어씁니다. 기존 파일의 문자열을 바꿉니다. 파일 내용을 읽습니다. glob 패턴과 일치하는 파일을 찾습니다. 정규식으로 파일 내용을 검색합니다. 웹 콘텐츠를 가져오고 처리합니다. 웹을 검색합니다. subagent를 생성합니다. foreground Agent 호출이 완료되면 PostToolUse hook은 subagent의 결과와 실행 원격 측정을 tool_response에서 받습니다. 이 필드를 읽어 실행을 검사합니다; subagent 전체의 토큰 및 비용 롤업의 경우 토큰 및 비용 카운터를 query_source "subagent"로 필터링하여 사용합니다 (totalTokens 및 usage는 최종 요청만 다룹니다): Claude Code v2.1.271 이상에서 SubagentHandback 도구로 실행되는 subagent는 해당 도구를 통해 보고서를 전달하며 텍스트로 반환하지 않습니다. 완료된 결과의 content 필드는 보고서 자체가 아닌 손 전달에 대한 짧은 메모를 전달합니다. 보고서를 읽으려면 SubagentHandback과 일치하는 PreToolUse 또는 PostToolUse hook을 일치시키고 tool_input.message를 읽습니다. 백그라운드 subagent의 경우 도구는 작업이 백그라운드로 이동할 때 반환되므로 tool_response는 사용 필드를 전달하지 않습니다: 백그라운드 시작은 즉시 반환되고 foreground 작업이 실행 중 백그라운드로 이동하면 해당 전환에서 반환됩니다. status: "async_launched", agentId, description, prompt, outputFile, resolvedModel이 있습니다. 완료된 응답에서 resolvedModel은 subagent가 시작된 모델의 이름을 지정하며, 이는 tool_input의 model 값과 다를 수 있습니다. 비동기 시작된 응답에서 resolvedModel은 에이전트가 백그라운드로 이동할 때 사용 중인 모델의 이름을 지정하므로 백그라운드 이동 전에 발생한 교환이 반영됩니다. 백그라운드 시간 resolvedModel 동작 및 modelsUsed는 Claude Code v2.1.212 이상이 필요합니다. 사용자에게 1~4개의 객관식 질문을 합니다. Claude가 plan 모드를 떠나기 전에 계획을 제시하고 사용자에게 승인을 요청합니다. Claude는 도구를 호출하기 전에 계획을 파일에 디스크에 작성하므로 모델의 리터럴 tool_input은 일반적으로 비어 있습니다. Claude Code는 hook에 전달하기 전에 계획 내용과 파일 경로를 주입합니다. PostToolUse에서 tool_response는 승인된 계획을 보유하는 plan 및 filePath 필드가 있는 객체이며, 내부 상태 플래그도 있습니다. 디스크에서 파일을 다시 읽는 대신 tool_response.plan에서 계획 내용을 읽으세요.

PreToolUse 결정 제어

PreToolUse hook은 도구 호출 진행 여부를 제어할 수 있습니다. 최상위 decision 필드를 사용하는 다른 hook과 달리 PreToolUse는 hookSpecificOutput 객체 내에 결정을 반환합니다. 이는 더 풍부한 제어를 제공합니다: 네 가지 결과 (허용, 거부, 요청 또는 연기) 및 실행 전에 도구 입력을 수정하는 기능. 여러 PreToolUse hook이 다른 결정을 반환할 때 우선순위는 deny > defer > ask > allow입니다. 종료 코드 2로 차단하는 hook은 "deny"와 동일한 방식으로 라우팅됩니다: Claude는 stderr 메시지를 거부 이유로 봅니다. hook이 "ask"를 반환하면 사용자에게 표시되는 권한 프롬프트에는 hook이 어디에서 왔는지를 나타내는 레이블이 포함됩니다: 설정 파일 또는 에이전트 frontmatter의 hook의 경우 [settings], plugin의 hook의 경우 [plugin:<name>], skill의 hook의 경우 [skill]. 이는 사용자가 어느 구성 소스가 확인을 요청하는지 이해하는 데 도움이 됩니다. hook의 "ask"는 또한 자동 모드에서 권한 프롬프트를 강제합니다: 분류기는 여전히 도구 호출을 거부할 수 있지만 hook이 요청한 프롬프트를 표시하지 않고 승인할 수 없습니다. v2.1.211 이전에는 분류기가 샌드박스 외부에서 실행되는 Bash 명령을 hook이 요청한 프롬프트를 표시하지 않고 승인할 수 있었습니다; 분류기는 여전히 해당 명령에 자신의 안전 규칙을 적용했고 hook "deny"는 항상 준수되었습니다.
비대화형 모드에서 -p 플래그를 사용하면 Claude Code는 AskUserQuestion 및 ExitPlanMode를 실행에 권한 호스트가 있을 때만 제공합니다 (예: Agent SDK canUseTool callback). 이 도구는 사용자 상호 작용이 필요합니다. permissionDecision: "allow"를 updatedInput과 함께 반환하면 해당 요구 사항을 충족합니다: hook은 stdin에서 도구의 입력을 읽고 자신의 UI를 통해 답변을 수집하고 updatedInput에서 반환하여 도구가 프롬프트 없이 실행되도록 합니다. "allow"만 반환하는 것은 이러한 도구에 충분하지 않습니다. AskUserQuestion의 경우 원본 questions 배열을 에코백하고 각 질문의 텍스트를 선택한 답변으로 매핑하는 answers 객체를 추가합니다. v2.1.199부터 _meta["anthropic/requiresUserInteraction"]로 표시된 MCP 도구는 더 엄격합니다: hook은 updatedInput이 있거나 없이 "allow"로 승인 프롬프트를 건너뛸 수 없습니다. Claude Code는 hook이 도구가 필요한 상호 작용을 수집했는지 확인할 수 없기 때문입니다.
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 결과를 무시합니다. 일반적인 경우는 AskUserQuestion 도구입니다: Claude가 사용자에게 뭔가를 묻고 싶지만 답변할 터미널이 없습니다. -p 실행은 권한 호스트가 있을 때만 AskUserQuestion을 제공합니다 (예: --permission-prompt-tool으로 전달하는 MCP 도구). 왕복은 다음과 같이 작동합니다:
  1. Claude가 AskUserQuestion을 호출합니다. PreToolUse hook이 발생합니다.
  2. hook이 permissionDecision: "defer"를 반환합니다. 도구가 실행되지 않습니다. 프로세스는 stop_reason: "tool_deferred"로 종료되고 보류 중인 도구 호출이 트랜스크립트에 유지됩니다.
  3. 호출 프로세스는 SDK 결과에서 deferred_tool_use를 읽고 자신의 UI에서 질문을 표시하고 답변을 기다립니다.
  4. 호출 프로세스는 동일한 권한 호스트와 함께 claude -p --resume <session-id>를 실행합니다. 동일한 도구 호출이 PreToolUse를 다시 발생시킵니다.
  5. hook이 permissionDecision: "allow"를 updatedInput의 답변과 함께 반환합니다. 도구가 실행되고 Claude가 계속됩니다.
deferred_tool_use 필드는 도구의 id, name, input을 전달합니다. input은 실행 전에 캡처된 도구 호출을 위해 Claude가 생성한 매개변수입니다:
시간 초과 또는 재시도 제한이 없습니다. 세션은 재개할 때까지 디스크에 유지됩니다 (retention sweep 규칙에 따라 cleanupPeriodDays 보존 스윕에 의해 30일 후 기본적으로 삭제됨). 재개할 때 답변이 준비되지 않으면 hook이 "defer"를 다시 반환할 수 있고 프로세스는 동일한 방식으로 종료됩니다. 호출 프로세스는 결국 "allow" 또는 "deny"를 반환하여 루프를 끝낼 시기를 제어합니다. "defer"는 Claude가 한 번에 단일 도구 호출을 만들 때만 작동합니다. Claude가 여러 도구 호출을 한 번에 만들면 "defer"는 경고와 함께 무시되고 도구는 일반 권한 흐름을 통해 진행됩니다. 제약이 존재하는 이유는 재개가 하나의 도구만 다시 실행할 수 있기 때문입니다: 다른 도구를 미해결 상태로 두지 않고 배치에서 하나의 호출을 연기할 방법이 없습니다. 연기된 도구가 재개할 때 더 이상 사용 가능하지 않으면 프로세스는 stop_reason: "tool_deferred_unavailable"과 is_error: true로 종료되고 hook이 발생하기 전에 종료됩니다. 이는 도구를 제공한 MCP 서버가 재개된 세션에 연결되지 않을 때 발생합니다. deferred_tool_use 페이로드는 여전히 포함되므로 어느 도구가 누락되었는지 식별할 수 있습니다.
plan 모드에서 연기된 세션을 재개하려면 --permission-prompt-tool을 --resume과 함께 전달하여 Claude Code가 승인을 위해 계획을 제시할 수 있도록 합니다. 없으면 Claude Code는 plan 모드를 복원하지 않습니다. Claude Code v2.1.246 이상이 필요합니다.-p로 재개할 때 Claude Code는 저장된 다른 권한 모드를 복원하지 않습니다. 새 claude -p 실행이 시작할 권한 모드로 실행을 시작하므로 연기된 세션이 사용한 경우 --permission-mode 또는 --dangerously-skip-permissions를 다시 전달합니다. claude --resume <session-id>로 -p 없이 재개할 때 Claude Code는 재개 시 권한 모드에 나열된 예외를 제외하고 저장된 권한 모드를 복원합니다.

PermissionRequest

Claude Code가 도구 사용 권한을 요청하려고 할 때 실행됩니다. 비대화형 모드의 백그라운드 서브에이전트처럼 프롬프트를 표시할 수 없는 세션에서도 Claude Code는 이 hook을 실행하며, 어떤 hook도 결정을 반환하지 않으면 도구 호출을 거부합니다. PermissionRequest 결정 제어를 사용하여 사용자를 대신하여 허용하거나 거부합니다. Claude가 도구 사용 권한을 요청하는 순간 신호가 필요할 때 이 이벤트를 사용합니다. Claude Code는 프롬프트가 약 6초 동안 기다린 후에야 permission_prompt 유형의 Notification hook을 실행합니다. Claude Code는 샌드박스 명령의 네트워크 요청에 대해서는 PermissionRequest hook을 실행하지 않습니다. 해당 프롬프트에 대한 신호를 받으려면 permission_prompt 알림 유형을 사용합니다. 도구 이름에서 일치합니다. PreToolUse와 동일한 값입니다.

PermissionRequest 입력

PermissionRequest hook은 PreToolUse hook과 같은 tool_name 및 tool_input 필드를 받지만 tool_use_id는 없습니다. MCP 도구의 경우 mcp_server 객체도 받습니다. 선택적 permission_suggestions 배열에는 Claude Code가 이 요청에 대해 제안하는 권한 업데이트 (예: 허용 규칙 추가 또는 권한 모드 변경)가 포함됩니다. permission_suggestions 배열은 권한 대화 상자에서 보는 옵션의 정확한 목록이 아닙니다. 각 권한 대화 상자는 자신의 옵션을 구축하기 때문입니다. 파일 편집과 같은 일부 대화 상자는 배열을 읽지 않고 요청 자체에서 옵션을 파생합니다. 배열을 읽는 대화 상자는 여전히 allowManagedPermissionRulesOnly가 규칙 저장 옵션을 숨길 때와 같이 배열에 제안이 있는 옵션을 보류할 수 있습니다. 또한 제안 항목이 없는 옵션을 제공할 수 있습니다 (예: Yes, and switch to auto mode, 권한 업데이트를 통하지 않고 권한 모드를 직접 변경하는 것). PreToolUse hook은 권한 상태와 관계없이 모든 도구 호출 전에 실행됩니다. PermissionRequest hook은 Claude Code가 권한을 요청하려고 할 때만 실행되거나 프롬프트할 수 없는 경우 자동 거부할 때만 실행됩니다. 둘 다 EndConversation에 대해 발생하지 않습니다.

PermissionRequest 결정 제어

PermissionRequest hook은 권한 요청을 허용하거나 거부할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 hook 스크립트는 이러한 이벤트 특정 필드가 있는 decision 객체를 반환할 수 있습니다: decision 객체 없이 종료 코드 2로 종료하는 hook은 권한 흐름을 변경하지 않으며, 해당 stderr은 삭제됩니다. decision 객체만 요청을 허용하거나 거부할 수 있습니다.

권한 업데이트 항목

updatedPermissions 출력 필드와 permission_suggestions 입력 필드 모두 동일한 항목 객체 배열을 사용합니다. 각 항목에는 다른 필드를 결정하는 type과 변경이 작성되는 위치를 제어하는 destination이 있습니다.
setMode와 bypassPermissions는 세션이 이미 bypass 모드를 사용 가능하게 시작된 경우에만 적용됩니다: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions, 또는 사용자, --settings, 또는 관리형 설정의 permissions.defaultMode: "bypassPermissions". 그렇지 않으면 업데이트는 작동하지 않습니다. permissions.disableBypassPermissionsMode가 모드를 비활성화하거나 세션이 제한된 모드로 시작될 때도 업데이트는 작동하지 않습니다.bypassPermissions는 destination과 관계없이 defaultMode로 절대 유지되지 않습니다.
모든 항목의 destination 필드는 변경이 메모리에만 유지되는지 또는 설정 파일에 유지되는지를 결정합니다. hook은 받은 permission_suggestions 중 하나를 자신의 updatedPermissions 출력으로 에코할 수 있으며, 이는 사용자가 대화 상자에서 해당 옵션을 선택하는 것과 동등합니다.

PostToolUse

도구가 성공적으로 완료된 직후 실행됩니다. 도구 이름에서 일치합니다. PreToolUse와 동일한 값입니다. 더 광범위하게 일치할 때 도구 이름이 올바른 필터가 아닙니다:
  • 모든 도구가 성공적으로 완료된 후 hook을 실행하려면 matcher를 생략하거나 "*"로 설정합니다. Hook은 git status --porcelain을 실행하여 변경된 내용을 자체적으로 발견할 수 있으며, 이는 git diff가 놓치는 추적되지 않은 파일도 나열합니다. 도구 호출이 실패하는 경우 PostToolUseFailure에 동일한 hook을 추가합니다.
  • 특정 파일이 변경될 때 hook을 실행하려면 (어떤 것이 작성했든) FileChanged를 사용합니다. Claude Code는 Edit|Write와 일치하는 PostToolUse hook을 실행하지 않습니다 (Bash 명령 또는 Claude Code 외부의 프로세스가 동일한 파일을 다시 작성할 때).

PostToolUse 입력

PostToolUse hook은 도구가 이미 성공적으로 실행된 후에 발생합니다. 입력에는 도구에 전송된 인수인 tool_input과 반환한 결과인 tool_response가 모두 포함됩니다. 둘 다의 정확한 스키마는 도구에 따라 다릅니다. 파일 도구 tool_input 경로는 PreToolUse와 동일한 형식으로 도착합니다: 항상 절대 경로, 플랫폼의 기본 구분 기호 포함 (Windows에서는 백슬래시). MCP 도구의 경우 입력은 또한 mcp_server 객체를 전달합니다.

PostToolUse 결정 제어

PostToolUse hook은 도구 실행 후 Claude에 피드백을 제공할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 hook 스크립트는 이러한 이벤트 특정 필드를 반환할 수 있습니다: 아래 예제는 Bash 호출의 출력을 바꿉니다. 대체 값은 Bash 도구의 출력 형태와 일치합니다:
updatedToolOutput은 Claude가 보는 것만 변경합니다. 도구는 hook이 발생할 때까지 이미 실행되었으므로 작성된 파일, 실행된 명령 또는 전송된 네트워크 요청은 이미 적용되었습니다. OpenTelemetry 도구 span 및 분석 이벤트와 같은 원격 측정도 hook이 실행되기 전에 원본 출력을 캡처합니다. 도구 호출을 실행 전에 방지하거나 수정하려면 PreToolUse hook을 대신 사용합니다.대체 값은 도구의 출력 형태와 일치해야 합니다. 기본 제공 도구는 일반 문자열이 아닌 구조화된 객체를 반환합니다. 예를 들어 Bash는 stdout, stderr, interrupted, isImage 필드가 있는 객체를 반환합니다. 기본 제공 도구의 경우 도구의 출력 스키마와 일치하지 않는 값은 무시되고 원본 출력이 사용됩니다. MCP 도구 출력은 스키마 검증 없이 통과됩니다. Claude가 필요한 오류 세부 정보를 제거하면 잘못된 가정으로 진행할 수 있습니다.

자동 모드 분류기를 위한 결과 주석

classifierContext를 반환하여 Claude가 아닌 자동 모드 분류기에 도구 호출 결과에 대한 짧은 메모를 보냅니다. 분류기는 도구 결과 자체를 절대 받지 않으므로 이 필드는 분류기에 호출이 반환한 내용을 알려주는 지원되는 방법입니다. 필드는 Claude Code v2.1.236 이상이 필요합니다. 아래 예제는 분류기에 쿼리의 출력이 어디에서 왔는지를 알립니다:
분류기가 메모에 부여하는 가중치는 hook을 구성한 위치에 따라 다릅니다:
  • Claude Code에서 구성된 Hook: 설정 파일, plugin, skill, 에이전트 frontmatter의 hook의 경우 분류기는 메모를 검증되지 않은 애플리케이션 제공 컨텍스트로 취급합니다. 메모는 사용자 의도를 절대 설정하지 않으며, 메모가 승인 또는 요청을 주장하면 분류기는 대화에서 해당 주장을 확인합니다
  • In-process Agent SDK callback: 애플리케이션이 hook을 TypeScript SDK callback으로 등록하고 라이브 세션 중에 메모를 반환할 때 분류기는 메모에서 전달된 사용자 진술을 사용자 의도로 가중치를 부여할 수 있습니다. 그러한 진술은 분류기가 메시지에서 수락할 사용자 진술을 충족할 수 있지만 자신의 메시지가 들어올릴 수 없는 차단을 절대 들어올리지 않습니다. 세션이 재개된 후 Claude Code는 복원된 메모를 검증되지 않은 컨텍스트로 취급합니다. 두 그룹의 hook이 동일한 호출에 주석을 달 때 분류기는 결합된 메모를 검증되지 않은 것으로 취급합니다
Claude Code는 메모를 전달할 때 이러한 제한을 적용합니다:
  • 길이: Claude Code는 한 도구 호출에 대한 메모를 2,000자로 제한하고 나머지를 자릅니다. 제한은 해당 호출에 응답하는 모든 hook에서 공유됩니다
  • 동기 응답만: Claude Code는 백그라운드에서 실행되는 hook의 응답에서 필드를 무시합니다 (해당 응답이 도구 결과를 기록한 후 도착하기 때문)
  • 분류기가 기록하지 않는 호출: 분류기의 트랜스크립트는 파일 읽기 및 검색과 같은 읽기 전용 조회를 생략합니다. Claude Code는 해당 호출에 첨부된 메모를 삭제합니다
  • 재작성과의 상호 작용: 메모가 updatedToolOutput으로 바꾸는 출력을 설명할 때 두 필드를 동일한 hook 응답에서 반환합니다. Claude Code는 해당 재작성이 거부되거나 다른 hook의 재작성이 이를 대체할 때 메모를 삭제합니다. Claude Code는 재작성 없이 반환하는 메모를 전달합니다 (다른 hook의 재작성이 이를 대체하더라도)
분류기는 classifierContext에 배치하는 콘텐츠를 애플리케이션 호스팅 세션의 정보로 읽으므로 신뢰할 수 없는 도구 출력 또는 제3자 텍스트를 복사하지 마세요. 메모를 이 호출에 대한 짧은 주장 (예: 출처에 대한 사실 또는 이에 대한 사용자 진술)으로 유지합니다; 필드를 사용하여 관련 없는 메시지 또는 이벤트 스트림을 전달하지 마세요.

PostToolUseFailure

도구 실행이 실패할 때 실행됩니다: 도구가 오류를 throw하거나 MCP 도구가 오류 결과를 반환합니다. 이를 사용하여 실패를 기록하고, 경고를 보내거나, Claude에 수정 피드백을 제공합니다. 도구 이름에서 일치합니다. PreToolUse와 동일한 값입니다.
이 이벤트는 실행 전에 거부된 도구 호출에 대해 발생하지 않습니다: 알 수 없는 도구 이름, 스키마 또는 도구 특정 검증에 실패한 입력, 또는 권한 거부. 검증 거부는 tool_use_error 결과로 반환되고 hook이 실행되기 전에 발생하므로 PreToolUse 또는 이 이벤트를 발생시키지 않습니다. 권한 거부는 PreToolUse를 발생시키지만 이 이벤트는 발생시키지 않습니다; PermissionDenied를 참조하세요.

PostToolUseFailure 입력

PostToolUseFailure hook은 PostToolUse와 동일한 tool_name 및 tool_input 필드를 받으며, 오류 정보는 최상위 필드로 받습니다. MCP 도구의 경우 mcp_server 객체도 받습니다. 예를 들어 실패한 npm test 명령은 다음을 전달할 수 있습니다:
error 문자열은 일반적으로 Claude가 실패한 도구의 결과로 받는 것과 동일한 텍스트입니다. 형식은 도구 및 실패에 따라 다릅니다. tool_name, is_interrupt, 첫 번째 줄 Exit code N에 hook을 키합니다; 나머지 문자열을 표시 텍스트로 취급하고 안정적인 형식이 아닙니다.
  • Bash 및 PowerShell의 경우 실행되고 종료된 명령은 첫 번째 줄 Exit code N을 생성하고 명령이 생성한 모든 출력을 stdout과 stderr이 인터리브된 하나의 블록으로 생성합니다
  • 페이로드는 또한 Claude Code가 셸 프로세스 자체를 시작할 수 없을 때와 같이 종료 코드 줄이 없는 베어 실패 메시지를 전달할 수 있습니다
  • Claude Code는 ... [N characters truncated] ... 마커 주위에 긴 문자열을 중간 자르고 Command timed out after 2m 0s와 같은 자신의 줄을 삽입할 수 있습니다

PostToolUseFailure 결정 제어

PostToolUseFailure hook은 도구 실패 후 Claude에 컨텍스트를 제공할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 hook 스크립트는 이러한 이벤트 특정 필드를 반환할 수 있습니다:

PostToolBatch

배치의 모든 도구 호출이 해결된 후, Claude Code가 모델에 다음 요청을 보내기 전에 한 번 실행됩니다. PostToolUse는 도구당 한 번 발생하므로 Claude가 병렬 도구 호출을 만들 때 동시에 발생합니다. PostToolBatch는 전체 배치와 함께 정확히 한 번 발생하므로 단일 도구가 아닌 실행된 도구 집합에 따라 달라지는 컨텍스트를 주입하기에 적합한 위치입니다. 이 이벤트에는 matcher가 없습니다.

PostToolBatch 입력

공통 입력 필드 외에도 PostToolBatch hook은 배치의 모든 도구 호출을 설명하는 배열인 tool_calls를 받습니다:
tool_response는 모델이 해당 tool_result 블록에서 받는 것과 동일한 콘텐츠를 포함합니다. 값은 도구가 내보낸 것과 정확히 같은 직렬화된 문자열 또는 콘텐츠 블록 배열입니다. Read의 경우 원본 파일 내용이 아닌 줄 번호가 접두사로 붙은 텍스트를 의미합니다. 응답이 클 수 있으므로 필요한 필드만 구문 분석합니다.
tool_response 형태는 PostToolUse와 다릅니다. PostToolUse는 도구의 구조화된 Output 객체를 전달합니다 (예: Write의 경우 {filePath: "...", type: "create"}). PostToolBatch는 모델이 보는 직렬화된 tool_result 콘텐츠를 전달합니다.

PostToolBatch 결정 제어

PostToolBatch hook은 Claude에 대한 컨텍스트를 주입할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 hook 스크립트는 이러한 이벤트 특정 필드를 반환할 수 있습니다:
decision: "block" 또는 continue: false를 반환하면 다음 모델 호출 전에 에이전트 루프가 중지됩니다. 차단 메시지는 JSON reason 또는 stopReason, 또는 종료 코드 2의 stderr에서 나옵니다. 트랜스크립트에 경고로 표시되고 대화에 유지되므로 Claude는 대화가 계속될 때 이를 봅니다.

PermissionDenied

자동 모드가 도구 호출을 거부할 때 실행됩니다 (분류기 판정이 없어서 거부할 때 포함 (자동 모드가 작업의 안전성을 결정할 수 없음 또는 응답이 구문 분석되지 않았을 때). 이 hook은 자동 모드에서만 발생합니다: 권한 대화 상자를 수동으로 거부할 때, PreToolUse hook이 호출을 차단할 때, 또는 deny 규칙이 일치할 때 실행되지 않습니다. 이를 사용하여 거부를 기록하고, 구성을 조정하거나, 모델이 도구 호출을 재시도할 수 있음을 알립니다. 도구 이름에서 일치합니다. PreToolUse와 동일한 값입니다.

PermissionDenied 입력

공통 입력 필드 외에도 PermissionDenied hook은 tool_name, tool_input, tool_use_id, reason을 받습니다. MCP 도구의 경우 mcp_server 객체도 받습니다.

PermissionDenied 결정 제어

PermissionDenied hook은 모델이 거부된 도구 호출을 재시도할 수 있음을 알릴 수 있습니다. hookSpecificOutput.retry를 true로 설정한 JSON 객체를 반환합니다:
retry가 true일 때 Claude Code는 모델이 도구 호출을 재시도할 수 있음을 알리는 메시지를 대화에 추가합니다. 거부 자체는 역전되지 않습니다. hook이 JSON을 반환하지 않거나 retry: false를 반환하면 거부가 유지되고 모델은 원래 거부 메시지를 받습니다. 분류기가 작업에 대한 판정을 생성하지 않았을 때 Claude Code는 retry: true를 무시합니다: 응답이 구문 분석되지 않았거나 자동 모드와 별개의 안전 검사가 분류기의 요청을 거부했습니다. 이러한 거부의 경우 Claude Code는 이미 거부 메시지에서 나중에 재시도할지 또는 계속할지를 모델에 알립니다.

Notification

Claude Code가 알림을 보낼 때 실행됩니다. 알림 유형에서 일치합니다. 생략하여 모든 알림 유형에 대해 hook을 실행합니다. 데스크톱 알림이 꺼져 있어도 이 hook 이벤트를 받습니다: preferredNotifChannel 설정 (예: notifications_disabled)은 알림 방식만 변경하고 hook이 실행되는지 여부는 변경하지 않습니다. agent_needs_input 및 agent_completed 유형은 Claude Code v2.1.198 이상이 필요합니다. quota_auto_resume_fired, quota_auto_resume_stale, quota_auto_resume_disabled 유형은 Claude Code v2.1.234 이상이 필요합니다. 터미널 세션에서 샌드박스 명령의 네트워크 요청에 대한 permission_prompt는 Claude Code v2.1.246 이상이 필요합니다. 팀원의 터미널 설정 질문에 대한 agent_needs_input은 Claude Code v2.1.248 이상이 필요합니다.
permission_prompt, idle_prompt, elicitation_dialog, elicitation_url_dialog 유형은 데스크톱 알림과 시간을 공유하므로 터미널 세션에서는 터미널에서 멀리 있는 것처럼 보일 때만 표시됩니다:
  • 약 6초 동안 입력하지 않으면 permission_prompt를 예상합니다. 타이머는 권한 프롬프트가 나타날 때 시작되고 각 키 입력이 이를 연기합니다. Claude가 도구 사용 권한을 요청할 때 즉시 hook을 실행하려면 대신 PermissionRequest를 사용합니다.
  • Claude가 응답을 마친 후 약 60초 후에 idle_prompt를 예상하고 입력하지 않은 경우에만. Claude Code는 claude.ai 사용 제한 재설정을 기다리는 동안 idle_prompt를 보내지 않습니다. 대기가 자체적으로 끝나면 quota_auto_resume_* 유형 중 하나가 대신 발생합니다.
  • elicitation 양식의 경우 elicitation_dialog를 예상하거나 약 6초 동안 입력하지 않은 경우 브라우저 URL 요청의 경우 elicitation_url_dialog. 둘 다 permission_prompt와 동일한 6초 게이트를 공유합니다: 타이머는 대화 상자가 나타날 때 시작되고 각 키 입력이 이를 연기합니다.
권한 요청 또는 elicitation이 다른 대화 상자가 화면에 있는 동안 도착하면 동일한 6초 게이트를 유지하고 요청이 도착할 때부터 시간이 지정됩니다. 해당 알림은 요청이 여전히 열린 대화 상자 뒤에서 기다리는 동안 도달할 수 있습니다.
Claude Code는 권한 요청을 Agent SDK의 canUseTool callback으로 보내는 세션에서 permission_prompt를 다르게 시간합니다 (Claude Desktop 및 VS Code 확장이 Claude Code를 호스팅하는 방식):
  • Claude가 권한을 요청한 후 약 6초 후에 permission_prompt를 예상합니다. Claude Code는 입력하는 동안 이를 연기하지 않습니다.
  • 또는 PermissionRequest hook이 더 빨리 답변하면 Claude Code는 permission_prompt를 실행하지 않습니다.
  • CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS를 1로 설정하여 이 세션에서 permission_prompt를 끕니다.
v2.1.233 이전에는 permission_prompt가 이 세션에서 발생하지 않았습니다. 별도의 matcher를 사용하여 알림 유형에 따라 다른 핸들러를 실행합니다. 이 구성은 Claude가 권한 승인이 필요할 때 권한 특정 경고 스크립트를 트리거하고 Claude가 유휴 상태일 때 다른 알림을 트리거합니다:

Notification 입력

공통 입력 필드 외에도 Notification hook은 알림 텍스트가 있는 message, 선택적 title, 발생한 유형을 나타내는 notification_type을 받습니다.
Notification hook은 알림을 차단하거나 수정할 수 없습니다. Claude Code는 이들의 systemMessage 및 continue 필드를 삭제하지만 여전히 terminalSequence를 내보냅니다 (데스크톱 알림 예제가 의존하는 것). Notification hook은 외부 서비스로 알림을 전달하는 것과 같은 부작용을 위한 것입니다.

SubagentStart

Claude가 Agent 도구로 subagent를 생성할 때, Claude가 subagent를 재개할 때, 그리고 in-process agent team 팀원이 새 메시지를 처리할 때마다 실행됩니다. 에이전트 유형 이름으로 필터링할 matcher를 지원합니다. 기본 제공 에이전트의 경우 이는 general-purpose, Explore, Plan과 같은 에이전트 이름입니다. 사용자 정의 subagent의 경우 이는 파일명이 아닌 에이전트의 frontmatter의 name 필드입니다. plugin에서 제공하는 subagent의 경우 에이전트 유형은 my-plugin:reviewer와 같은 plugin 범위 식별자이며, 파일명이 아닙니다. 콜론은 plugin 범위 이름을 정규식 경로에 배치하므로 정확한 일치를 위해 matcher를 ^ 및 $로 고정합니다: ^my-plugin:reviewer$.

SubagentStart 입력

공통 입력 필드 외에도 SubagentStart hook은 subagent의 고유 식별자가 있는 agent_id와 matcher가 필터링하는 에이전트 이름이 있는 agent_type을 받습니다.
SubagentStart hook은 subagent 생성을 차단할 수 없지만 subagent에 컨텍스트를 주입할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 다음을 반환할 수 있습니다:
hook이 동일한 subagent에 대해 다시 실행될 때 Claude Code는 subagent의 컨텍스트가 이전 실행의 복사본을 이미 보유하지 않을 때만 반환된 컨텍스트를 주입합니다. 시작 시 주입된 복사본은 subagent의 prompt cache를 그대로 두고 제자리에 유지됩니다. 자동 압축이 해당 복사본을 삭제한 후 Claude Code는 다음 실행의 컨텍스트를 다시 주입합니다.

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은 트랜스크립트 파일을 구문 분석하지 않고도 액세스할 수 있습니다. Claude Code v2.1.271 이상에서 SubagentHandback 도구로 실행되는 subagent는 해당 도구를 통해 보고서를 전달하며 텍스트로 반환하지 않습니다. last_assistant_message 필드는 subagent의 닫는 텍스트 (있는 경우)를 보유하며, 이는 전달된 보고서가 아닙니다. 보고서는 해당 호출의 message 입력이며, PreToolUse 또는 PostToolUse hook이 SubagentHandback과 일치할 때 tool_input.message로 받습니다. SubagentStop hook은 또한 Stop 입력에서 설명한 background_tasks 및 session_crons 배열을 받습니다. 두 배열 모두 subagent가 아닌 부모 세션으로 범위가 지정됩니다.
SubagentStop hook은 Stop hook과 동일한 결정 제어 형식을 사용합니다. 이들은 hookSpecificOutput.additionalContext를 지원하며 hookEventName을 "SubagentStop"으로 설정하여 subagent를 계속 실행하는 비오류 피드백을 제공합니다. decision: "block"을 reason과 함께 반환하면 subagent가 계속 실행되고 reason이 subagent의 다음 명령으로 전달됩니다. 종료 코드 2로 차단하는 hook은 stderr 메시지를 동일한 방식으로 전달합니다. subagent가 반환한 후 부모 세션에 컨텍스트를 주입하려면 Agent 도구에서 PostToolUse hook을 대신 사용합니다.

TaskCreated

작업이 TaskCreate 도구를 통해 생성될 때 실행됩니다. 이를 사용하여 명명 규칙을 적용하거나, 작업 설명을 요구하거나, 특정 작업이 생성되는 것을 방지합니다. Task 도구가 없는 세션에서는 이 이벤트가 발생하지 않습니다. TaskCreated hook은 matcher를 지원하지 않으며 모든 발생에서 발생합니다.

TaskCreated 입력

공통 입력 필드 외에도 TaskCreated hook은 task_id, task_subject, 선택적으로 task_description, teammate_name, team_name을 받습니다.

TaskCreated 결정 제어

TaskCreated hook은 작업 생성을 차단하는 두 가지 방법을 지원합니다. 어느 쪽이든 Claude Code는 작업을 삭제하고 메시지를 도구의 오류로 Claude에 반환합니다.
  • 종료 코드 2: Claude Code는 stderr 텍스트를 메시지로 반환합니다.
  • JSON {"decision": "block", "reason": "..."}: Claude Code는 reason을 메시지로 반환합니다.
이 예제는 제목이 필수 형식을 따르지 않는 작업을 차단합니다:

TaskCompleted

작업이 완료로 표시될 때 실행됩니다. 이는 두 가지 상황에서 발생합니다: 모든 에이전트가 TaskUpdate 도구를 통해 명시적으로 작업을 완료로 표시할 때 또는 agent team 팀원이 진행 중인 작업으로 자신의 턴을 마칠 때입니다. 이를 사용하여 작업이 닫히기 전에 테스트 통과 또는 lint 검사와 같은 완료 기준을 적용할 수 있습니다. TaskCompleted hook은 matcher를 지원하지 않으며 모든 발생에서 발생합니다.

TaskCompleted 입력

공통 입력 필드 외에도 TaskCompleted hook은 task_id, task_subject, 선택적으로 task_description, teammate_name, team_name을 받습니다.

TaskCompleted 결정 제어

TaskCompleted hook은 작업 완료를 제어하는 두 가지 방법을 지원합니다:
  • 종료 코드 2: 작업이 완료로 표시되지 않고 stderr 메시지가 모델에 피드백으로 피드백됩니다.
  • JSON {"continue": false, "stopReason": "..."}: 팀원을 완전히 중지하여 Stop hook 동작과 일치합니다. stopReason은 사용자에게 표시됩니다. TaskUpdate 도구가 이벤트를 트리거했을 때 Claude Code는 continue: false를 무시합니다; 종료 코드 2는 여전히 완료를 차단합니다.
이 예제는 테스트를 실행하고 실패하면 작업 완료를 차단합니다:

Stop

메인 Claude Code 에이전트가 응답을 마쳤을 때 실행됩니다. 중지가 사용자 중단으로 인해 발생한 경우 실행되지 않습니다. API 오류는 대신 StopFailure를 발생시킵니다.
/goal 명령은 세션 범위 prompt 기반 Stop hook의 기본 제공 바로 가기입니다. 조건이 유지될 때까지 Claude가 계속 작동하도록 하되 hook 구성을 작성하지 않으려는 경우 사용합니다.

Stop 입력

공통 입력 필드 외에도 Stop hook은 stop_hook_active, last_assistant_message, background_tasks, session_crons를 받습니다. stop_hook_active 필드는 Claude Code가 이미 stop hook의 결과로 계속되고 있을 때 true입니다. 이 값을 확인하거나 트랜스크립트를 처리하여 Claude Code가 무한정 실행되는 것을 방지합니다. Claude Code는 8번 연속 차단 후 hook을 재정의하고 턴을 종료합니다. last_assistant_message 필드는 Claude의 최종 응답의 텍스트 내용을 포함하므로 hook은 트랜스크립트 파일을 구문 분석하지 않고도 액세스할 수 있습니다. 방금 완료된 턴에 대해 작동하는 hook (예: 읽기 전용 또는 알림 hook)의 경우 트랜스크립트 파일에서 읽는 대신 이 필드를 사용하세요: 트랜스크립트 파일은 모든 버전에서 Stop 시간에 최종 메시지를 포함하도록 보장되지 않습니다. background_tasks 및 session_crons 배열을 통해 hook은 “세션이 완료됨”과 “세션이 백그라운드 작업이 깨어날 때까지 일시 중지됨”을 구분할 수 있습니다. 작업 레지스트리에 도달할 수 있을 때 두 배열이 모두 존재하며, 진행 중이거나 예약된 것이 없을 때 비어 있습니다. background_tasks의 각 항목은 하나의 진행 중인 작업을 설명하며 이러한 필드를 사용합니다: session_crons의 각 항목은 CronCreate, ScheduleWakeup, /loop에서 소싱된 하나의 세션 범위 예약된 깨어남을 설명합니다: 이 예제는 하나의 진행 중인 셸 작업과 하나의 반복 cron이 있는 Stop 입력을 보여줍니다:

Stop 결정 제어

Stop 및 SubagentStop hook은 Claude가 계속할지 여부를 제어할 수 있습니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 hook 스크립트는 이러한 이벤트 특정 필드를 반환할 수 있습니다: 종료 코드 2로 차단하는 hook은 reason과 동일한 방식으로 라우팅됩니다: Claude는 stderr 메시지를 계속해야 하는 이유로 받습니다.
additionalContext를 사용하면 hook이 설계대로 작동하고 Claude에 지침을 제공할 때 (예: “완료하기 전에 테스트 스위트를 실행하세요”). 대화를 decision: "block"과 동일한 루프 보호를 통해 계속하지만 트랜스크립트는 이를 Stop hook feedback으로 표시하고 hook 오류 알림이 표시되지 않습니다:

StopFailure

Stop 대신 턴이 API 오류로 인해 종료될 때 실행됩니다. Claude Code는 hook의 출력과 종료 코드를 무시하며 terminalSequence 제외. 이를 사용하여 실패를 기록하고, 경고를 보내거나, Claude가 API 오류로 인해 응답을 완료할 수 없을 때 복구 조치를 취합니다.

StopFailure 입력

공통 입력 필드 외에도 StopFailure hook은 error, 선택적 error_details, 선택적 last_assistant_message를 받습니다. error 필드는 오류 유형을 식별하며 matcher 필터링에 사용됩니다.
StopFailure hook은 결정 제어가 없습니다. 이들은 알림 및 로깅 목적으로만 실행됩니다.

TeammateIdle

agent team 팀원이 자신의 턴을 마친 후 유휴 상태가 되려고 할 때 실행됩니다. 이를 사용하여 lint 검사 통과 또는 출력 파일 존재 확인과 같은 팀원이 작업을 중지하기 전에 품질 게이트를 적용합니다. TeammateIdle hook은 matcher를 지원하지 않으며 모든 발생에서 발생합니다.

TeammateIdle 입력

공통 입력 필드 외에도 TeammateIdle hook은 teammate_name 및 team_name을 받습니다.

TeammateIdle 결정 제어

TeammateIdle hook은 팀원 동작을 제어하는 두 가지 방법을 지원합니다:
  • 종료 코드 2: 팀원은 stderr 메시지를 피드백으로 받고 유휴 상태가 되는 대신 계속 작업합니다.
  • JSON {"continue": false, "stopReason": "..."}: 팀원을 완전히 중지하여 Stop hook 동작과 일치합니다. stopReason은 사용자에게 표시됩니다.
이 예제는 팀원이 유휴 상태가 되도록 허용하기 전에 빌드 아티팩트가 존재하는지 확인합니다:

ConfigChange

세션 중에 구성 파일이 변경될 때 실행됩니다. 이를 사용하여 설정 변경을 감사하고, 보안 정책을 적용하거나, 구성 파일에 대한 무단 수정을 차단합니다. Claude Code는 설정 파일, 관리형 정책 파일, skill 파일의 변경에 대해 ConfigChange hook을 실행합니다. 관리형 정책의 경우 managed-settings.json 또는 managed-settings.d/의 파일이 변경될 때만 실행합니다. 서버 관리 설정과 macOS 관리 기본 설정 또는 Windows 레지스트리 정책 변경을 적용하고 실행하지 않습니다. WSL에서 wslInheritsWindowsSettings를 사용하면 정책 폴링 중에 변경된 Windows 측 관리 설정 파일도 실행하지 않고 적용합니다. matcher는 구성 소스에서 필터링합니다: 이 예제는 보안 감사를 위해 모든 구성 변경을 기록합니다:

ConfigChange 입력

공통 입력 필드 외에도 ConfigChange hook은 source 및 선택적으로 file_path를 받습니다. source 필드는 어떤 구성 유형이 변경되었는지 나타내고 file_path는 수정된 특정 파일의 경로를 제공합니다.

ConfigChange 결정 제어

ConfigChange hook은 구성 변경이 적용되는 것을 차단할 수 있습니다. 종료 코드 2 또는 JSON decision을 사용하여 변경을 방지합니다. 차단되면 새 설정이 실행 중인 세션에 적용되지 않습니다.
policy_settings 변경은 차단할 수 없습니다. Hook은 여전히 policy_settings 소스에 대해 발생하므로 감사 로깅에 사용할 수 있지만 모든 차단 결정은 무시됩니다. 이는 엔터프라이즈 관리 설정이 항상 적용되도록 보장합니다. Claude Code는 서버 관리 설정이 도착하거나 새로 고쳐질 때 ConfigChange hook을 실행하지 않습니다. Claude Code는 ConfigChange hook의 JSON 출력에서 차단 결정을 작동하고 systemMessage 및 continue를 삭제합니다. 차단된 변경은 reason이 있거나 종료 코드 2의 stderr이 있는지 여부에 관계없이 메시지를 표시하지 않습니다. Claude Code는 디버그 로그에만 줄을 작성합니다.

CwdChanged

셸 명령이 메인 대화에서 작업 디렉토리를 변경할 때 실행됩니다 (예: Claude가 cd 명령을 실행할 때). 이를 사용하여 디렉토리 변경에 반응합니다: 환경 변수를 다시 로드하고, 프로젝트 특정 도구 체인을 활성화하거나, 설정 스크립트를 자동으로 실행합니다. FileChanged와 쌍을 이루어 direnv와 같은 디렉토리별 환경을 관리하는 도구를 사용합니다. CwdChanged hook은 CLAUDE_ENV_FILE에 액세스할 수 있습니다. 해당 파일에 작성된 변수는 SessionStart hook과 마찬가지로 세션의 후속 Bash 명령에 유지됩니다. CwdChanged는 matcher를 지원하지 않으며 모든 디렉토리 변경에서 발생합니다.

CwdChanged 입력

공통 입력 필드 외에도 CwdChanged hook은 old_cwd 및 new_cwd를 받습니다.

CwdChanged 출력

모든 hook에 사용 가능한 JSON 출력 필드 외에도 CwdChanged hook은 watchPaths를 반환하여 FileChanged가 감시하는 파일 경로를 동적으로 설정할 수 있습니다: CwdChanged hook은 결정 제어가 없습니다. 디렉토리 변경을 차단할 수 없습니다. Claude Code는 이들의 JSON 출력에서 watchPaths 및 systemMessage를 읽고 continue를 삭제합니다. 대화형 세션에서 systemMessage를 짧은 터미널 알림으로 표시합니다. 메시지는 SDK 메시지 스트림에 도달하지 않습니다.

DirectoryAdded

/add-dir 명령으로 또는 SDK 클라이언트가 register_repo_root 제어 요청으로 mid-session에 작업 디렉토리를 추가한 후 실행됩니다. 새로 추가된 리포지토리를 준비하는 데 사용합니다 (예: 종속성 설치). Claude Code는 다음의 경우 이 이벤트를 발생시키지 않습니다:
  • 시작 시 --add-dir 플래그로 디렉토리를 전달합니다; SessionStart가 이러한 디렉토리를 다룹니다
  • /permissions Workspace 탭에서 디렉토리를 추가합니다
  • 이미 작업 디렉토리이거나 그 안에 있는 디렉토리를 추가합니다
Claude Code는 샌드박스 및 권한 상태를 새로 고친 후 DirectoryAdded를 발생시키므로 샌드박스 도구는 hook이 실행될 때 새 디렉토리를 이미 봅니다. Hook 명령 자체는 샌드박스되지 않은 상태로 실행됩니다. Claude Code는 hook을 기다리지 않습니다: 추가가 즉시 완료되고 hook은 600초 기본 시간 초과로 백그라운드에서 실행됩니다. matcher는 디렉토리가 추가된 방식에 따라 필터링합니다:

DirectoryAdded 입력

공통 입력 필드 외에도 DirectoryAdded hook은 directory 및 source를 받습니다.
DirectoryAdded hook은 결정 제어가 없습니다. 추가를 차단할 수 없으며, 이는 hook이 실행될 때 이미 완료되었습니다. Claude Code는 이들의 JSON 출력에서 continue 필드를 삭제하고 소스별로 나머지를 다르게 표시합니다:
  • slash_command: Claude Code는 hook의 systemMessage를 Claude에 다음 대화 턴의 컨텍스트로 전달하며, 사용자에게 표시하지 않습니다. 실패한 hook의 개수가 트랜스크립트에 나타납니다. 전체 실패 출력은 디버그 로그로 이동합니다
  • register_repo_root: Claude Code는 systemMessage 출력과 실패 출력을 디버그 로그에만 작성합니다

FileChanged

감시된 파일이 디스크에서 변경될 때 실행됩니다. Claude Code는 파일 시스템 감시자로 변경을 감지하므로 어떤 것이 파일을 변경했든 hook이 실행됩니다: Edit 또는 Write 도구 호출, Claude가 Bash로 실행하는 스크립트, 또는 Claude Code 외부의 프로세스. 일반적인 사용은 프로젝트 구성 파일이 수정될 때 환경 변수를 다시 로드하는 것입니다. 이 이벤트의 matcher는 두 가지 역할을 합니다:
  • 감시 목록 구축: 값은 |로 분할되고 각 세그먼트는 작업 디렉토리의 리터럴 파일명으로 등록되므로 ".envrc|.env"는 정확히 이 두 파일을 감시합니다. 정규식 패턴은 여기서 유용하지 않습니다: ^\.env와 같은 값은 ^\.env라는 리터럴 이름의 파일을 감시합니다.
  • hook 실행 필터링: 감시된 파일이 변경되면 동일한 값이 표준 matcher 규칙을 사용하여 변경된 파일의 basename에 대해 실행할 hook 그룹을 필터링합니다.
이 예제는 data.csv의 줄 끝을 정규화합니다 (Bash 명령 또는 외부 스크립트가 파일을 다시 작성한 후 포함):
hook은 JSON 입력의 file_path 필드에서 변경된 파일의 절대 경로를 stdin에서 읽습니다. grep 가드는 perl이 제거하는 것과 동일한 것을 테스트합니다 (줄 끝의 CR). 정규화 후 실행은 파일을 건드리지 않고 종료되므로 루프가 없습니다. 더 느슨한 가드는 perl -i가 대체하지 않을 때도 파일을 다시 작성하고 Claude Code가 모든 다시 작성 후 hook을 다시 실행하므로 무한 루프를 생성합니다. /path/to/normalize-line-endings.sh에 이 스크립트를 저장하고 실행 가능하게 만듭니다:
hook이 작동하는지 확인하려면 Claude에 Bash 명령으로 data.csv에 CRLF 줄을 추가하도록 요청합니다. Claude Code는 hook을 실행하고 파일은 LF 끝으로 끝납니다. 미리 이름을 지정할 수 없는 파일을 감시하려면 hook에서 watchPaths를 반환하여 감시 목록을 동적으로 업데이트합니다. Claude Code는 무언가가 감시할 파일을 이름 지정할 때만 감시자를 시작하므로 matcher가 최소 하나의 파일을 이름 지정하는 FileChanged 그룹으로 목록을 시드하거나 SessionStart 또는 CwdChanged hook이 watchPaths를 반환합니다. matcher는 감시된 파일이 변경될 때 실행할 hook 그룹을 필터링하므로 동적 경로를 처리하는 그룹에 생략된 matcher를 제공합니다 (모든 감시된 파일과 일치하고 감시 목록에 아무것도 추가하지 않음). "*" matcher도 모든 파일과 일치하지만 Claude Code는 다른 값처럼 감시 목록에 *라는 리터럴 파일을 등록합니다. FileChanged hook은 CLAUDE_ENV_FILE에 액세스할 수 있습니다. 해당 파일에 작성된 변수는 SessionStart hook과 마찬가지로 세션의 후속 Bash 명령에 유지됩니다.

FileChanged 입력

공통 입력 필드 외에도 FileChanged hook은 file_path 및 event를 받습니다.

FileChanged 출력

모든 hook에 사용 가능한 JSON 출력 필드 외에도 FileChanged hook은 watchPaths를 반환하여 감시되는 파일 경로를 동적으로 업데이트할 수 있습니다: FileChanged hook은 결정 제어가 없습니다. 파일 변경을 차단할 수 없습니다. Claude Code는 이들의 JSON 출력에서 watchPaths 및 systemMessage를 읽고 continue를 삭제합니다. 대화형 세션에서 systemMessage를 짧은 터미널 알림으로 표시합니다. 메시지는 SDK 메시지 스트림에 도달하지 않습니다.

WorktreeCreate

claude --worktree를 실행하거나 subagent가 isolation: "worktree"를 사용할 때 또는 백그라운드 세션을 위해 Claude Code가 자신의 worktree에서 격리할 때 실행됩니다. 기본적으로 Claude Code는 git worktree로 격리된 작업 복사본을 생성합니다. WorktreeCreate hook을 구성하면 기본 git 동작을 대체하여 SVN, Perforce 또는 Mercurial과 같은 다른 버전 제어 시스템을 사용할 수 있습니다. hook은 생성된 worktree 디렉토리의 절대 경로를 반환해야 합니다. Claude Code는 이 경로를 격리된 세션의 작업 디렉토리로 사용합니다. WorktreeCreate 출력을 참조하여 각 hook 유형이 경로를 반환하는 방식을 확인하세요. hook이 기본 동작을 완전히 대체하므로 .worktreeinclude는 처리되지 않습니다. .env와 같은 로컬 구성 파일을 새 worktree에 복사해야 하면 hook 스크립트 내에서 수행합니다. 이 예제는 SVN 작업 복사본을 생성하고 Claude Code가 사용할 경로를 인쇄합니다. 리포지토리 URL을 자신의 것으로 바꾸세요:
hook은 stdin의 JSON 입력에서 worktree name을 읽고, 새 디렉토리로 신선한 복사본을 체크아웃하고, 디렉토리 경로를 인쇄합니다. 마지막 줄의 echo는 Claude Code가 worktree 경로로 읽는 것입니다. 다른 모든 출력을 stderr로 리디렉션하여 경로를 방해하지 않도록 합니다.

WorktreeCreate 입력

공통 입력 필드 외에도 WorktreeCreate hook은 name 필드를 받습니다. 이는 새 worktree의 slug 식별자이며, 사용자가 지정하거나 자동 생성됩니다 (예: bold-oak-a3f2).

WorktreeCreate 출력

WorktreeCreate hook은 표준 허용/차단 결정 모델을 사용하지 않습니다. 대신 hook의 성공 또는 실패가 결과를 결정합니다. hook은 생성된 worktree 디렉토리의 절대 경로를 반환해야 합니다:
  • 명령 hook (type: "command"): stdout의 마지막 비어 있지 않은 줄로 경로를 인쇄합니다. Claude Code는 경로를 읽기 전에 ANSI 이스케이프 코드를 제거하므로 셸 시작 배너가 echo 전에 인쇄되면 무시됩니다. 다른 모든 hook 출력을 stderr로 리디렉션합니다.
  • HTTP hook (type: "http"): 응답 본문에서 { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }를 반환합니다.
hook이 실패하거나 경로를 생성하지 않으면 worktree 생성이 오류로 실패합니다. Claude Code는 hook이 실행된 디렉토리에 대해 상대 경로를 해결하고 . 또는 .. 세그먼트를 축소합니다. 결과 경로가 Claude Code가 들어갈 수 있는 디렉토리가 아니면 세션은 경로를 이름으로 지정하는 오류를 인쇄하고 코드 1로 종료됩니다. Claude Code는 . 또는 .. 세그먼트를 포함하는 절대 경로를 거부하고 리포지토리 루트 아래의 symlink를 통과하는 모든 경로를 거부합니다 (리포지토리에 커밋된 symlink가 worktree를 그 밖으로 리디렉션할 수 있기 때문). 오류는 거부된 구성 요소를 이름으로 지정합니다. 리포지토리 내부의 symlink를 통과하지 않는 정규화된 경로를 반환합니다. v2.1.216 이전에는 worktree 생성이 경로를 검사 없이 따랐습니다.

WorktreeRemove

worktree가 제거될 때 실행됩니다. 이는 WorktreeCreate의 정리 대응입니다. 이 hook은 다음의 경우 발생합니다:
  • --worktree 세션을 종료하고 제거하도록 선택합니다
  • isolation: "worktree"를 가진 subagent가 완료됩니다
  • 백그라운드 세션을 삭제합니다 (hook이 생성한 worktree)
git 기반 worktree의 경우 Claude Code는 git worktree remove로 정리를 자동으로 처리합니다. git이 아닌 버전 제어 시스템에 대해 WorktreeCreate hook을 구성한 경우 정리를 처리하려면 WorktreeRemove hook과 쌍을 이루세요. 없으면 worktree 디렉토리가 디스크에 남아 있습니다. Claude Code는 WorktreeRemove hook의 JSON 출력 필드 (예: systemMessage, continue)를 삭제합니다. 백그라운드 세션 삭제의 경우 Claude Code는 hook을 실행하기 전에 저장된 worktree 경로를 확인하고 symlink이거나 리포지토리 루트 아래의 symlink를 통과하는 경로를 거부합니다. 여전히 파일을 포함하는 worktree에 대해서는 agent view에서 삭제를 확인할 때만 hook이 실행됩니다; 그러한 worktree의 경우 claude rm은 대신 세션과 worktree를 유지합니다. v2.1.216 이전에는 hook이 이러한 검사 없이 저장된 경로에서 실행되었습니다. Claude Code는 WorktreeCreate가 반환한 경로를 hook 입력의 worktree_path로 전달합니다. 이 예제는 해당 경로를 읽고 디렉토리를 제거합니다:

WorktreeRemove 입력

공통 입력 필드 외에도 WorktreeRemove hook은 제거되는 worktree의 절대 경로인 worktree_path 필드를 받습니다.
WorktreeRemove hook의 종료 코드가 결과를 결정합니다. hook이 0이 아닌 코드로 종료되고 worktree_path의 디렉토리가 여전히 존재한 후 제거가 실패합니다:
  • worktree는 디스크에 유지되고 hook의 명령과 stderr은 디버그 로그로 이동합니다.
  • 백그라운드 세션을 삭제하는 경우 세션도 유지됩니다. agent view의 거부 메시지는 hook이 어떻게 끝났는지 (예: exited 1)를 보고하고 stderr의 시작을 인용하고 다시 삭제하면 디렉토리를 제거하는지 여부를 말합니다.

PreCompact

Claude Code가 압축 작업을 실행하려고 하기 전에 실행됩니다. matcher 값은 압축이 수동으로 또는 자동으로 트리거되었는지 나타냅니다: 종료 코드 2로 압축을 차단합니다. 수동 /compact의 경우 stderr 메시지가 사용자에게 표시됩니다. JSON "decision": "block"을 사용하여 차단할 수도 있습니다. 자동 압축 차단은 발생 시기에 따라 다른 효과를 가집니다. 컨텍스트 제한 전에 압축이 사전에 트리거된 경우 Claude Code는 이를 건너뛰고 대화가 압축되지 않은 상태로 계속됩니다. 컨텍스트 제한 오류를 복구하기 위해 압축이 트리거된 경우 기본 오류가 표시되고 현재 요청이 실패합니다. Claude Code는 PreCompact hook의 systemMessage 및 continue 필드를 삭제합니다.

PreCompact 입력

공통 입력 필드 외에도 PreCompact hook은 trigger 및 custom_instructions를 받습니다. manual의 경우 custom_instructions는 사용자가 /compact에 전달하는 것을 포함하고 아무것도 전달하지 않으면 null입니다. auto의 경우 custom_instructions는 null입니다.

PostCompact

Claude Code가 압축 작업을 완료한 후 실행됩니다. 이 이벤트를 사용하여 새로운 압축된 상태에 반응합니다 (예: 생성된 요약을 기록하거나 외부 상태를 업데이트). Claude Code는 PostCompact hook의 systemMessage 및 continue 필드를 삭제합니다. PreCompact와 동일한 matcher 값이 적용됩니다:

PostCompact 입력

공통 입력 필드 외에도 PostCompact hook은 trigger 및 compact_summary를 받습니다. compact_summary 필드는 압축 작업에서 생성된 대화 요약을 포함합니다.
PostCompact hook은 결정 제어가 없습니다. 압축 결과에 영향을 미칠 수 없지만 후속 작업을 수행할 수 있습니다.

PreModelSwitch

Claude Code가 사용자 또는 클라이언트가 요청한 모델 전환을 적용하기 전에 실행됩니다. 이를 사용하여 전환을 차단하거나, 확인을 요청하거나, 전환이 발생하기 전에 비용을 표시합니다. PreModelSwitch는 Claude Code v2.1.251 이상이 필요합니다. Claude Code는 이 요청에 대해 실행합니다:
  • /model <name> 및 /model 선택기
  • Option+P 또는 Alt+P 모델 선택기
  • /config의 Model 설정
  • fast mode를 켤 때 (모델을 변경하는 경우)
  • Agent SDK 호스트 또는 Remote Control의 set_model 요청 또는 apply_flag_settings 요청의 모델 변경
Claude Code는 자동 모델 폴백과 같이 Claude Code가 자체적으로 수행하는 전환에 대해 PreModelSwitch hook을 실행하지 않습니다. 이러한 변경은 PostModelSwitch에만 도달합니다. Claude Code는 matcher를 세션이 전환되는 모델의 정규 이름과 비교하고 [1m] 접미사를 무시합니다. opus, 날짜 모델 ID, Amazon Bedrock 모델 ID와 같은 공급자 특정 ID와 같은 별칭은 모두 해결되는 하나의 정규 이름과 일치하므로 claude-opus-5는 Opus 5의 모든 철자를 다룹니다. Claude Code가 대상의 정규 이름을 결정할 수 없을 때 (예: LLM gateway만 알고 있는 사용자 정의 모델 ID) matcher와 관계없이 모든 PreModelSwitch hook을 실행합니다. 차단하는 hook은 입력에서 to_model을 확인해야 합니다. matcher를 정확한 이름, |로 분리된 목록 (예: claude-opus-4-6|claude-opus-5) 또는 정규식 (예: .*opus.*)으로 작성합니다. 이 예제는 정확한 이름 matcher를 사용하고 hook 입력에서 to_model을 확인하므로 Opus 4.6으로의 전환을 거부하고 다른 대상을 허용합니다:
명령은 jq로 to_model을 확인합니다:
hook이 작동하는지 확인하려면 다른 모델을 실행하는 세션에서 /model claude-opus-4-6을 실행합니다. Claude Code는 현재 모델을 유지하고 PreModelSwitch hook이 전환을 차단했음을 보고하며 메시지를 이유로 표시합니다.

PreModelSwitch 입력

공통 입력 필드 외에도 PreModelSwitch hook은 아래 표의 필드를 받습니다. 마지막 다섯 개는 새 모델로 대화를 다시 전송하는 비용을 설명하므로 hook은 전환이 발생하기 전에 해당 수치를 표시할 수 있습니다. 이 예제는 Sonnet 5를 실행하는 세션에서 /model opus에 대한 입력을 보여줍니다:

PreModelSwitch 결정 제어

PreModelSwitch hook은 전환을 취소하거나, 사용자에게 확인을 요청하거나, 진행하도록 허용할 수 있습니다. 종료 코드 2 또는 최상위 decision: "block"은 전환을 취소합니다. 더 세밀한 제어를 위해 PreToolUse처럼 hookSpecificOutput 객체에서 permissionDecision 및 permissionDecisionReason을 반환합니다. PreModelSwitch는 "allow", "deny", "ask"를 수락합니다. "defer", updatedInput, additionalContext는 수락하지 않습니다. 아래 표는 두 필드를 설명합니다: "ask" 프롬프트는 대화형 세션에서 /model만 표시할 수 있습니다. 비대화형 모드 (-p 플래그), /config, set_model 요청을 포함한 다른 모든 표면에서 Claude Code는 "ask"를 거부로 취급합니다. 이 예제는 사용자에게 확인을 요청하고 context_tokens의 토큰 수를 인용합니다:
여러 PreModelSwitch hook이 다른 결정을 반환할 때 우선순위는 deny > ask > allow입니다. Claude Code는 결정과 관계없이 hook이 반환하는 모든 systemMessage를 사용자에게 표시하므로 비용 보고 hook은 {"systemMessage": "..."} 및 종료 0을 반환할 수 있습니다. 시간 초과 전에 응답하지 않는 PreModelSwitch hook은 전환을 차단합니다. 대조적으로 PreToolUse에서는 시간 초과된 명령 hook이 도구 호출이 계속되도록 허용합니다. 이 이벤트의 기본 시간 초과는 30초입니다. PreModelSwitch는 command, http, mcp_tool hook만 실행하므로 prompt 및 agent 기본값은 적용되지 않습니다. 0 또는 2 이외의 코드로 종료하고 JSON 결정을 인쇄하지 않는 hook은 차단하지 않습니다: Claude Code는 stderr을 표시하고 다른 종료 코드에서 설명한 대로 전환을 적용합니다.

PostModelSwitch

세션의 모델이 변경된 후 실행됩니다. 모든 CLAUDE.md를 편집하지 않고 특정 모델에 적용되는 모델 특정 지침을 Claude에 제공하는 데 사용합니다 (예: 조직 전체 명령). PostModelSwitch는 Claude Code v2.1.251 이상이 필요합니다. 차단할 수 없습니다 (모델이 이미 변경되었기 때문). Claude Code는 다음 변경 후 PostModelSwitch hook을 실행합니다:
  • 사용자 또는 클라이언트가 요청한 전환
  • 자동 모델 폴백 (세션의 모델을 변경)
  • opusplan과 같은 설정이 plan 모드에 들어가거나 나갈 때
  • Claude Code가 세션을 재개할 때 모델을 복원합니다
Claude Code는 폴백 모델 체인의 모델이 턴을 제공할 때 PostModelSwitch hook을 실행하지 않습니다 (해당 대체는 한 턴 지속되고 세션의 모델을 변경하지 않음). matcher는 PreModelSwitch와 동일한 규칙을 따릅니다: Claude Code는 세션이 전환되는 모델의 정규 이름과 비교합니다. 이 예제는 세션의 모델이 Opus 모델로 변경될 때마다 지침을 추가합니다:
hook이 작동하는지 확인하려면 다른 모델을 실행하는 세션에서 Opus 모델로 전환합니다 (예: Sonnet 세션에서 /model opus 실행). 그런 다음 Claude에 현재 모델에 대한 지침이 무엇인지 물어봅니다.

PostModelSwitch 입력

PostModelSwitch hook은 PreModelSwitch와 동일한 필드를 받으며 hook_event_name은 "PostModelSwitch"로 설정되고 두 개의 추가 source 값이 있습니다: Claude Code가 자체적으로 수행한 변경의 경우 "auto" (자동 폴백 또는 기타 변경), 세션을 재개할 때 복원된 모델의 경우 "resume". requested_model은 source가 "auto"일 때 null입니다. source가 "resume"일 때 Claude Code가 복원한 저장된 모델 설정입니다.

PostModelSwitch 결정 제어

Claude Code는 hook의 일반 텍스트 stdout을 종료 0에서 가져오거나 JSON 출력에서 additionalContext를 가져오고 전환 후 다음 요청과 함께 Claude에 전달합니다. 모든 hook에 사용 가능한 JSON 출력 필드 외에도 다음을 반환할 수 있습니다: 다음 프롬프트를 보낸 후 5초 이내에 hook이 완료되지 않으면 Claude Code는 해당 출력 없이 그 요청을 보내고 대신 그다음 요청에 첨부합니다. 다음 요청 전에 모델이 여러 번 변경되면 Claude Code는 마지막 전환의 대상 모델에 대한 출력만 전달합니다.

SessionEnd

Claude Code 세션이 종료될 때 실행됩니다. 정리 작업, 세션 통계 로깅 또는 세션 상태 저장에 유용합니다. 종료 이유별로 필터링할 matcher를 지원합니다. hook 입력의 reason 필드는 세션이 종료된 이유를 나타냅니다:

SessionEnd 입력

공통 입력 필드 외에도 SessionEnd hook은 세션이 종료된 이유를 나타내는 reason 필드를 받습니다. 모든 값은 위의 이유 표를 참조하세요.
SessionEnd hook은 결정 제어가 없습니다. 세션 종료를 차단할 수 없지만 정리 작업을 수행할 수 있습니다. Claude Code는 이들의 JSON 출력 필드 (예: systemMessage)를 삭제합니다. SessionEnd hook의 기본 시간 초과는 1.5초입니다. 이는 세션 종료, /clear, 대화형 /resume을 통한 세션 전환 모두에 적용됩니다. hook에 더 많은 시간이 필요하면 hook 구성에서 timeout을 설정합니다. 전체 예산은 설정 파일의 가장 높은 hook별 timeout으로 자동으로 올라가며, 최대 60초입니다. plugin 제공 hook에 설정된 시간 초과는 예산을 올리지 않습니다. 예산을 명시적으로 재정의하려면 CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS 환경 변수를 밀리초 단위로 설정합니다. 설정한 값은 또한 자신의 timeout이 없는 각 hook의 시간 초과가 됩니다. 이 예제는 예산을 5초로 설정합니다:
v2.1.268 이전에는 CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS가 전체 예산만 올렸고 자신의 timeout이 없는 hook은 여전히 1.5초 후 취소되었습니다.

Elicitation

MCP 서버가 작업 중 사용자 입력을 요청할 때 실행됩니다. 기본적으로 Claude Code는 사용자가 응답할 수 있는 대화형 대화 상자를 표시합니다. Hook은 이 요청을 가로채고 프로그래밍 방식으로 응답하여 대화 상자를 완전히 건너뛸 수 있습니다. matcher 필드는 MCP 서버 이름과 일치합니다.

Elicitation 입력

공통 입력 필드 외에도 Elicitation hook은 mcp_server_name, message, 선택적으로 mode, url, elicitation_id, requested_schema 필드를 받습니다. form 모드 elicitation (가장 일반적인 경우):
URL 모드 elicitation (브라우저 기반 인증):

Elicitation 출력

대화 상자를 표시하지 않고 프로그래밍 방식으로 응답하려면 hookSpecificOutput이 있는 JSON 객체를 반환합니다:
종료 코드 2는 elicitation을 거부합니다. Claude Code는 stderr 메시지를 어디에도 표시하지 않습니다. Claude Code는 Elicitation hook의 JSON 출력에서 hookSpecificOutput을 작동하고 systemMessage 및 continue를 삭제합니다.

ElicitationResult

사용자가 MCP elicitation에 응답한 후 실행됩니다. Hook은 응답을 관찰하고, 수정하거나, MCP 서버로 다시 전송되기 전에 차단할 수 있습니다. matcher 필드는 MCP 서버 이름과 일치합니다.

ElicitationResult 입력

공통 입력 필드 외에도 ElicitationResult hook은 mcp_server_name, action, 선택적으로 mode, elicitation_id, content 필드를 받습니다.

ElicitationResult 출력

사용자의 응답을 재정의하려면 hookSpecificOutput이 있는 JSON 객체를 반환합니다:
종료 코드 2는 응답을 차단하여 효과적인 작업을 decline으로 변경합니다. Claude Code는 stderr 메시지를 어디에도 표시하지 않습니다. Claude Code는 ElicitationResult hook의 JSON 출력에서 hookSpecificOutput을 작동하고 systemMessage 및 continue를 삭제합니다.

프롬프트 기반 hook

명령, HTTP 및 MCP tool hook 외에도 Claude Code는 LLM을 사용하여 작업을 허용할지 차단할지 평가하는 프롬프트 기반 hook (type: "prompt")과 도구 액세스가 있는 에이전트 검증자를 생성하는 에이전트 hook (type: "agent")을 지원합니다. 모든 이벤트가 모든 hook 유형을 지원하는 것은 아닙니다. 다섯 가지 hook 유형 모두 (command, http, mcp_tool, prompt, agent)를 지원하는 이벤트:
  • PermissionDenied
  • PermissionRequest
  • PostToolBatch
  • PostToolUse
  • PostToolUseFailure
  • PreToolUse
  • Stop
  • SubagentStop
  • TaskCompleted
  • TaskCreated
  • TeammateIdle
  • UserPromptExpansion
  • UserPromptSubmit
command, http 및 mcp_tool hook을 지원하지만 prompt 또는 agent는 지원하지 않는 이벤트:
  • ConfigChange
  • CwdChanged
  • DirectoryAdded
  • Elicitation
  • ElicitationResult
  • FileChanged
  • InstructionsLoaded
  • MessageDisplay
  • Notification
  • PostCompact
  • PostModelSwitch
  • PreCompact
  • PreModelSwitch
  • SessionEnd
  • StopFailure
  • SubagentStart
  • WorktreeCreate
  • WorktreeRemove
SessionStart 및 Setup은 command 및 mcp_tool hook을 지원하며, MCP tool hook 필드는 해당 mcp_tool hook이 실행되는 시기를 설명합니다. http, prompt 또는 agent hook은 지원하지 않습니다.

프롬프트 기반 hook이 어떻게 작동하는지

프롬프트 기반 hook은 Bash 명령을 실행하는 대신:
  1. hook 입력과 프롬프트를 Claude 모델 (기본값 Haiku)로 전송합니다
  2. LLM은 결정을 포함하는 구조화된 JSON으로 응답합니다
  3. Claude Code는 결정을 자동으로 처리합니다

프롬프트 hook 구성

type을 "prompt"로 설정하고 command 대신 prompt 문자열을 제공합니다. $ARGUMENTS 자리 표시자를 사용하여 hook의 JSON 입력 데이터를 프롬프트 텍스트에 주입합니다. 이 Stop hook은 Claude가 완료되기 전에 모든 작업이 완료되었는지 평가하도록 LLM에 요청합니다:

응답 스키마

LLM은 다음을 포함하는 JSON으로 응답해야 합니다:
ok: false에서 발생하는 상황은 이벤트에 따라 다릅니다:
  • Stop 및 SubagentStop: 이유는 Claude의 다음 명령으로 피드백되며 턴이 계속됩니다. 응답이 impossible: true도 설정하지 않는 한, 이 경우 Claude Code는 중지를 허용하고 턴이 종료됩니다
  • PreToolUse: tool 호출이 거부됩니다. 기본적으로 턴이 끝나고 거부 이유가 채팅에 경고 줄로 나타납니다. continueOnBlock: true를 설정하여 이유를 Claude에 tool 오류로 반환하여 조정하고 계속할 수 있도록 합니다. 이는 명령 hook의 permissionDecision: "deny"와 동일합니다. v2.1.210 이전에는 거부 이유가 Claude에 tool 오류로 반환되었고 턴이 계속되었습니다
  • PostToolUse: 기본적으로 턴이 끝나고 이유는 채팅에 경고 줄로 나타납니다. 대신 continueOnBlock: true를 설정하여 이유를 Claude에 다시 피드백하고 턴을 계속합니다
  • PostToolBatch, UserPromptSubmit 및 UserPromptExpansion: 턴이 끝나고 이유는 경고 줄로 나타납니다. 이러한 이벤트는 continue에 관계없이 decision: "block"에서 턴을 종료합니다
  • PostToolUseFailure 및 TaskCreated: 이유는 Claude에 tool 오류로 반환되며 턴이 계속됩니다. continueOnBlock에 관계없이
  • TaskCompleted: 턴 중에 작업이 완료됨으로 표시되어 발생할 때 이유는 Claude에 tool 오류로 반환되며 턴이 계속됩니다. continueOnBlock에 관계없이. 팀원이 중지되어 발생할 때 TeammateIdle처럼 동작하며 기본적으로 팀원을 중지합니다
  • TeammateIdle: 기본적으로 팀원이 중지되고 이유는 경고 줄로 나타납니다. continueOnBlock: true를 설정하여 이유를 팀원에게 다시 피드백하고 계속 작업하도록 유지합니다
  • PermissionRequest: ok: false는 효과가 없습니다. hook에서 승인을 거부하려면 hookSpecificOutput.decision.behavior: "deny"를 반환하는 명령 hook을 사용합니다
  • PermissionDenied: ok: false는 거부가 이미 발생했기 때문에 효과가 없습니다. 이 이벤트가 읽는 유일한 출력은 hookSpecificOutput.retry이며, 프롬프트 및 에이전트 hook은 이를 설정할 수 없습니다. 이들은 이 이벤트에서 실행되지만 출력은 버려집니다. retry를 반환하려면 명령 hook을 사용합니다
이벤트에 대해 더 세밀한 제어가 필요한 경우 결정 제어에 설명된 이벤트별 필드가 있는 명령 hook을 사용합니다.

중지하기 전에 여러 조건 확인

이 Stop hook은 Claude가 중지하기 전에 세 가지 조건을 확인하는 자세한 프롬프트를 사용합니다. SubagentStop hook은 subagent가 중지해야 하는지 평가하는 동일한 형식을 사용합니다. 모델이 조건이 아직 충족되지 않았기 때문에 "ok": false를 반환하면 Claude는 제공된 이유를 다음 명령으로 받으며 계속 작업합니다:

에이전트 기반 hook

에이전트 hook은 실험적입니다. 동작 및 구성은 향후 릴리스에서 변경될 수 있습니다. 프로덕션 워크플로우의 경우 명령 hook을 선호합니다.
에이전트 기반 hook (type: "agent")은 프롬프트 기반 hook과 유사하지만 다중 턴 도구 액세스가 있습니다. 단일 LLM 호출 대신 에이전트 hook은 파일을 읽고, 코드를 검색하고, 코드베이스를 검사하여 조건을 확인할 수 있는 subagent를 생성합니다. 에이전트 hook은 프롬프트 기반 hook과 동일한 이벤트를 지원합니다.

에이전트 hook이 어떻게 작동하는지

에이전트 hook이 발생할 때:
  1. Claude Code는 프롬프트와 hook의 JSON 입력을 가진 subagent를 생성합니다
  2. subagent는 Read, Grep, Glob과 같은 도구를 사용하여 조사할 수 있습니다
  3. 최대 50턴 후 subagent는 구조화된 { "ok": true/false } 결정을 반환합니다
  4. Claude Code는 ok가 true이면 작업을 허용합니다. ok가 false이면 Claude Code는 응답 스키마에 나열된 해당 이벤트에서 continueOnBlock: true를 사용하는 프롬프트 hook과 동일한 방식으로 블록을 처리합니다
에이전트 hook은 검증이 hook 입력 데이터만으로 평가하는 것이 아니라 실제 파일이나 테스트 출력을 검사해야 할 때 유용합니다.

에이전트 hook 구성

type을 "agent"로 설정하고 hook 입력 JSON에 대한 자리 표시자로 $ARGUMENTS를 사용하여 prompt 문자열을 제공합니다. 구성 필드는 프롬프트 hook과 동일하지만 에이전트 hook은 60초의 더 긴 기본 시간 초과를 가지며 continueOnBlock 필드가 없습니다. 응답 스키마는 허용하려면 { "ok": true }이거나 차단하려면 { "ok": false, "reason": "..." }입니다. ok: false일 때 Claude Code는 동일한 이벤트에서 continueOnBlock: true를 사용하는 프롬프트 hook을 처리하는 방식으로 에이전트 hook을 처리합니다. 에이전트 hook은 continueOnBlock 필드가 없으며 프롬프트 hook의 impossible 필드를 지원하지 않습니다. 이 Stop hook은 Claude가 완료되기 전에 모든 단위 테스트가 통과하는지 확인합니다:

백그라운드에서 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가 실행되는 동안 즉시 계속 작업합니다. 스크립트가 완료되면 출력이 다음 대화 턴에 전달됩니다:
비동기 hook이 백그라운드에서 실행되면 Claude Code는 timeout을 적용하지 않습니다. Claude Code는 여전히 asyncRewake로 실행하는 hook에 timeout을 적용합니다. Claude Code는 비동기 hook의 결과를 세션이 실행되는 동안에만 전달합니다:
  • -p 플래그가 있는 비대화형 모드에서 Claude Code는 종료 시 여전히 실행 중인 비동기 hook을 종료하고 결과를 cancelled로 완료합니다
  • hook의 작업이 claude -p 세션을 초과해야 하는 경우 완전히 분리된 프로세스를 시작합니다

비동기 hook이 어떻게 실행되는지

비동기 hook이 발생하면 Claude Code는 hook 프로세스를 시작하고 완료를 기다리지 않고 즉시 계속합니다. hook은 동기 hook과 동일한 JSON 입력을 stdin을 통해 받습니다. 백그라운드 프로세스가 종료된 후 Claude Code는 hook의 JSON 응답에서 additionalContext 및 systemMessage 필드를 다음 대화 턴에서 Claude에 전달합니다. 동기 hook의 systemMessage와 달리 두 필드 모두 사용자에게 표시되지 않습니다. Claude Code는 JSON 응답을 동기 hook과 동일한 출력 스키마에 대해 검증하고, systemMessage가 문자열이 아닌 경우와 같이 값의 유형이 잘못된 필드를 전달하지 않고 삭제합니다. --debug로 실행하여 삭제된 각 필드의 이름을 지정하는 경고를 확인합니다. v2.1.202 이전에는 비동기 hook의 잘못된 형식의 JSON 출력이 세션을 충돌시킬 수 있었고, 세션이 재개될 때마다 충돌이 반복되었습니다. 비동기 hook 완료 알림은 기본적으로 억제됩니다. 보려면 Ctrl+O로 자세한 모드를 활성화하거나 --verbose로 Claude Code를 시작합니다.

파일 변경 후 테스트 실행

이 hook은 Claude가 파일을 쓸 때마다 백그라운드에서 테스트 스위트를 시작한 후 테스트가 완료되면 결과를 Claude에 보고합니다. 이 스크립트를 프로젝트의 .claude/hooks/run-tests-async.sh에 저장하고 chmod +x로 실행 가능하게 만듭니다:
그런 다음 프로젝트 루트의 .claude/settings.json에 이 구성을 추가합니다. async: true 플래그를 사용하면 Claude가 테스트 실행 중에 계속 작업할 수 있습니다:

제한 사항

비동기 hook은 동기 hook과 비교하여 여러 제약이 있습니다:
  • Hook 출력은 다음 대화 턴에 전달됩니다. 세션이 유휴 상태이면 응답은 다음 사용자 상호 작용까지 기다립니다. 예외: asyncRewake hook이 종료 코드 2로 종료되면 세션이 유휴 상태일 때도 Claude를 즉시 깨웁니다.
  • 각 실행은 별도의 백그라운드 프로세스를 생성합니다. 동일한 비동기 hook의 여러 발생에 걸쳐 중복 제거가 없습니다.

보안 고려 사항

면책 조항

명령 hook은 전체 사용자 권한으로 셸 명령을 실행합니다. 사용자 계정이 액세스할 수 있는 모든 파일을 수정, 삭제 또는 액세스할 수 있습니다. 구성에 추가하기 전에 모든 hook 명령을 검토하고 테스트하세요.

작업 공간 신뢰

Claude Code는 설정 파일에서 hook을 실행하기 전에 작업 공간 신뢰를 확인합니다. 신뢰할 수 있는 것으로 간주되는 것은 세션 유형에 따라 다릅니다:
  • 대화형 세션: Claude Code는 사용자의 ~/.claude/settings.json을 포함한 모든 설정 파일의 hook을 보류하며, 폴더에 대한 작업 공간 신뢰 대화를 수락하거나 신뢰가 확장되는 상위 디렉토리에 대해 수락할 때까지 보류합니다
  • -p 또는 SDK 세션: Claude Code는 대화를 표시하지 않으며 폴더를 신뢰할 수 있는 것으로 취급하므로, 리포지토리의 .claude/settings.json에 커밋된 hook은 신뢰한 적이 없는 폴더에서 실행됩니다
작성하지 않은 리포지토리에 대해 claude -p를 스크립팅하기 전에 해당 .claude/ 설정 파일을 검토하고, --bare로 시작하거나, --settings '{"disableAllHooks": true}'를 사용하여 해당 실행에 대해 hook을 비활성화하세요. 프로젝트 서브에이전트의 frontmatter hook은 설정 파일 hook보다 더 엄격한 규칙을 따릅니다. 폴더를 신뢰하기 전에 실행되는 것은 세션 유형별로 각 종류의 리포지토리 콘텐츠를 나열합니다.

보안 모범 사례

hook을 작성할 때 이러한 사례를 염두에 두세요:
  • 입력 검증 및 살균: 입력 데이터를 맹목적으로 신뢰하지 마세요
  • 항상 셸 변수를 따옴표로 감싸세요: $VAR 대신 "$VAR" 사용
  • 경로 순회 차단: 파일 경로에서 .. 확인
  • 절대 경로 사용: 스크립트의 전체 경로를 지정하세요. exec 형식에서는 ${CLAUDE_PROJECT_DIR}을 사용하고 경로는 따옴표가 필요하지 않습니다. shell 형식에서는 큰따옴표로 감싸세요
  • 민감한 파일 건너뛰기: .env, .git/, 키 등을 피하세요

Windows PowerShell 도구

Windows에서 명령 hook에 "shell": "powershell"을 설정하여 PowerShell에서 개별 hook을 실행할 수 있습니다. Claude Code는 PowerShell 7 이상의 실행 파일인 pwsh.exe를 자동 감지하고 Windows PowerShell 5.1의 powershell.exe로 폴백합니다.
PowerShell 셸 형식 명령에서 프로젝트 루트를 참조하려면 ${CLAUDE_PROJECT_DIR} 또는 $env:CLAUDE_PROJECT_DIR을 작성합니다. v2.1.198부터 Claude Code는 hook이 settings.json, 플러그인 또는 스킬에 정의되어 있는지 여부와 관계없이 PowerShell 셸 형식 명령에서 ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} 및 ${CLAUDE_PLUGIN_DATA} 자리 표시자를 PowerShell의 ${env:NAME} 형식으로 다시 작성합니다. PowerShell은 구문 분석 후 내보낸 환경에서 값을 확인하므로 자리 표시자는 큰따옴표로 묶인 문자열 내에서는 작동하지만 PowerShell이 변수를 확장하지 않는 작은따옴표로 묶인 문자열 내에서는 작동하지 않습니다. v2.1.198 이전에는 이 다시 쓰기가 플러그인 hook에만 적용되었습니다. 이전 버전에서는 settings.json hook이 $env: 형식이나 exec 형식이 필요하며, 여기서 ${CLAUDE_PROJECT_DIR}은 hook이 정의된 위치와 관계없이 각 args 요소에서 대체됩니다. PowerShell hook에서 $CLAUDE_PROJECT_DIR의 단순한 형식을 작성하지 마십시오. PowerShell은 이를 정의되지 않은 로컬 변수로 구문 분석하고 $null로 확인하므로 스크립트 경로가 프로젝트 루트 접두사 없이 남습니다. Claude Code는 해당 형식을 다시 작성하지 않으며 대신 디버그 로그에 경고를 기록합니다. 아래 예제는 모든 버전에서 작동하는 $env: 형식으로 프로젝트 스크립트를 실행하는 settings.json hook을 보여줍니다:

Hook 디버그

Hook 실행 세부 정보는 디버그 로그 파일에 기록됩니다. claude --debug-file <path>로 Claude Code를 시작하여 로그를 알려진 위치에 작성하거나 claude --debug를 실행하고 ~/.claude/debug/<session-id>.txt에서 로그를 읽습니다. --debug 플래그는 터미널에 인쇄하지 않습니다. 예를 들어, Write에서 hook-ran을 인쇄하는 명령을 가진 PostToolUse hook은 다음과 같은 항목을 생성합니다:
더 세밀한 hook 일치 세부 정보를 보려면 CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose를 설정하여 hook matcher 수 및 쿼리 일치와 같은 추가 로그 줄을 확인합니다. hook이 발생하지 않음, 무한 Stop hook 루프 또는 구성 오류와 같은 일반적인 문제 해결은 가이드의 제한 사항 및 문제 해결을 참조하세요. /context, /doctor 및 설정 우선순위를 다루는 더 광범위한 진단 안내는 구성 디버그를 참조하세요.