Claude Code 훅은 Claude Code가 수명 주기의 정해진 지점에서 실행하는 명령이므로, 모델이 선택하든 하지 않든 알림과 포맷터와 차단이 일어납니다. 문서의 알림은 ~/.claude/settings.json의 Notification 명령입니다. macOS는 osascript, Linux는 notify-send, Windows는 PowerShell MessageBox입니다. 읽기 전용 /hooks 메뉴에서 확인합니다. 이 파일은 이 머신에만 적용되고, 클라우드 세션은 이 파일을 읽지 않습니다. 공유 Prettier 훅은 프로젝트의 .claude/settings.json에서 PostToolUse와 matcher Edit|Write로 둡니다. 성공하면 채팅에는 아무것도 나오지 않고, Bash 명령이 다시 쓴 파일은 보지 못합니다. 편집을 멈추려면 Anthropic의 protect-files.sh가 Edit 또는 Write 전에 이유를 stderr에 쓰고 종료 코드 2로 나갑니다. 파괴적인 셸 명령을 멈추려면 block-rm.sh가 종료 코드 0으로 나가고 permissionDecision deny를 출력합니다. 종료 코드 2는 JSON이 allow라고 해도 PreToolUse를 막습니다. 종료 코드 0만으로는 그 호출을 승인하지 않습니다. PermissionRequest와 Notification은 종료 코드 2를 무시합니다. 시작하지 못한 command 훅이나, PreToolUse에서 시간 초과된 command, HTTP, MCP tool 훅은 도구 호출을 일반 권한 흐름에 남깁니다. 명령 자체는 사용자 계정의 파일 권한으로 실행됩니다.
훅은 모델이 선택하지 않아도 실행됩니다
Claude Code 훅은 Claude Code 수명 주기의 한 시점에 붙여 둔 셸 명령입니다. 그 시점이 오면 Claude Code가 매번 명령을 실행하므로, 모델이 실행을 선택하든 하지 않든 알림과 포맷터와 차단이 일어납니다. Anthropic의 훅 가이드가 레시피입니다. 훅 레퍼런스에는 이벤트 스키마, 종료 코드 표, JSON 필드가 있습니다.
핸들러 유형은 command, http, mcp_tool, prompt, agent 다섯 가지입니다. 아래 세 레시피는 command를 씁니다. 판단이 필요한 결정이면 prompt 훅이 훅 입력을 Claude 모델로 보냅니다. 가이드의 기본 모델은 Haiku입니다. agent 훅은 실험 기능이고 바뀔 수 있으며, 가이드는 프로덕션 워크플로에 command 훅을 쓰라고 합니다.
Anthropic은 GitHub의 Bash 명령 검증기도 완전한 참조 구현으로 링크합니다.
문서에 있는 데스크톱 알림부터 시작합니다
~/.claude/settings.json을 열고 Notification 훅을 추가합니다. 파일이 없으면 만듭니다. Anthropic의 첫 예제는 macOS의 osascript 디스플레이 알림이며, matcher는 비어 있고 “type”은 “command”입니다.
파일에 이미 hooks 키가 있으면 다른 이벤트 키 옆에 Notification을 넣습니다. 각 이벤트 이름은 그 hooks 객체 안의 키 하나입니다. 객체를 통째로 바꾸면 그 파일에 이미 설정된 훅이 모두 사라집니다.
/hooks를 입력해 등록을 확인합니다. 메뉴는 이벤트와 개수를 나열합니다. 훅을 선택하면 matcher, type, 소스 파일, 명령이 보입니다. 메뉴는 읽기 전용입니다. 훅을 추가하거나 바꾸거나 지우려면 JSON을 편집하거나 Claude에게 편집을 맡깁니다.
시험하려면 Esc를 누른 뒤, 상태 표시줄에 manual mode on이 나올 때까지 Shift+Tab을 누릅니다. 권한이 필요한 일을 요청한 다음 터미널에서 벗어납니다. macOS에서 osascript는 Script Editor를 거칩니다. Script Editor에 알림 권한이 없으면 명령은 조용히 실패하고, macOS는 권한을 부여하라고 묻지 않습니다. osascript -e ’display notification “test”’를 한 번 실행한 뒤 System Settings에서 Script Editor의 알림을 허용합니다.
~/.claude/settings.json
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
Linux와 Windows도 같은 Notification 이벤트를 씁니다
Linux와 Windows의 ~/.claude/settings.json에도 같은 Notification 훅, 빈 matcher, command 타입을 넣습니다. 바뀌는 것은 명령 문자열뿐입니다. Anthropic의 Linux 명령은 notify-send입니다. notify-send에는 데스크톱 알림 데몬이 필요하며, 헤드리스 서버와 SSH 세션, 대부분의 컨테이너에는 그 데몬이 없습니다. notify-send ‘Claude Code’ ’test’로 확인합니다. Debian이나 Ubuntu에서 명령이 없으면 패키지는 libnotify-bin입니다.
Anthropic의 Windows 명령은 PowerShell MessageBox를 엽니다. 대화상자이므로 터미널 뒤에 가릴 수 있습니다. PowerShell에서 먼저 명령을 시험합니다. WSL 안에서는 Windows interop을 통해 powershell.exe가 PATH에 있어야 합니다. Notification 훅은 차단할 수 없습니다. 종료 코드와 stderr는 무시됩니다.
플랫폼별 Anthropic Notification 명령입니다. macOS 예제와 같은 command 필드에 넣습니다.
| 플랫폼 | 명령 |
| — | — |
| macOS | osascript -e ‘display notification “Claude Code needs your attention” with title “Claude Code”’ |
| Linux | notify-send ‘Claude Code’ ‘Claude Code needs your attention’ |
| Windows | powershell.exe -Command “[System.Reflection.Assembly]::LoadWithPartialName(‘System.Windows.Forms’); [System.Windows.Forms.MessageBox]::Show(‘Claude Code needs your attention’, ‘Claude Code’)” |
받고 싶은 알림만 맞춥니다
빈 matcher는 모든 알림 유형에 반응합니다. permission_prompt는 Claude가 도구나 샌드박스된 명령의 네트워크 요청을 승인받아야 할 때 발생합니다. idle_prompt는 Claude가 응답을 끝낸 뒤 약 60초가 지났는데도 입력이 없으면 발생합니다. elicitation 대화상자는 permission_prompt와 같은, 약 6초의 관문을 씁니다.
터미널에서는 프롬프트가 나타난 순간 permission_prompt 타이머가 시작하고 약 6초를 기다립니다. 키를 누를 때마다 타이머가 미뤄집니다. Claude Desktop, VS Code 확장, 그리고 Agent SDK로 권한 요청에 응답하는 다른 호스트에서는 요청 뒤 약 6초가 되면 permission_prompt가 발생하고, 입력은 그 시점을 미루지 않습니다. 더 일찍 응답하거나 PermissionRequest 훅이 더 일찍 응답하면 그 Notification 훅은 실행되지 않습니다. 권한 순간 자체에는 PermissionRequest를 씁니다.
agent_needs_input은 에이전트 뷰가 열린 상태에서 백그라운드 세션이 입력을 기다리기 시작할 때, 또는 현재 세션이 팀원의 터미널 설정 질문을 던진 뒤 약 6초 동안 입력이 없을 때 발생합니다. agent_completed는 백그라운드 세션이 끝나거나 실패할 때, 그리고 agent view가 열려 있을 때만 발생합니다. quota_auto_resume_fired는 claude.ai 사용 한도가 작업을 멈춘 뒤 Claude Code가 그 작업을 다시 이어 갈 때 발생합니다. 시점은 한도가 초기화될 때이거나, 기다리는 동안 한 일이 사용량을 다시 쓸 수 있게 하면 그보다 빠릅니다. quota_auto_resume_stale은 컴퓨터가 약 30분을 넘게 잠든 사이에 그 한도가 초기화되었을 때 발생합니다. Claude Code는 Enter를 기다립니다. 더 짧은 잠에서는 작업을 이어 가고 대신 quota_auto_resume_fired를 발생시킵니다. quota_auto_resume_disabled는 Claude Code가 작업을 이어 가지 않고 기다림을 끝낼 때 발생합니다. Esc나 Ctrl+C를 누르거나 Don’t continue automatically를 고르면 발생하지 않습니다.
agent_needs_input과 agent_completed에는 Claude Code v2.1.198 이상이 필요합니다. agent_needs_input의 팀원 터미널 설정 경우는 v2.1.248 이상이 필요합니다. quota_auto_resume 매처 세 개에는 v2.1.234 이상이 필요합니다. 터미널에서 샌드박스된 명령의 네트워크 요청에 대한 permission_prompt에는 v2.1.246 이상이 필요합니다.
개인 알림은 사용자 파일에, 공유 훅은 프로젝트 파일에 둡니다
~/.claude/settings.json은 이 머신의 모든 프로젝트에 적용되고 공유할 수 없습니다. .claude/settings.json은 프로젝트 하나에 적용되고 커밋할 수 있습니다. .claude/settings.local.json은 프로젝트 하나에 적용되고, Claude Code가 그곳에 설정을 저장하면 gitignore됩니다. 훅 항목은 사용자, 프로젝트, 로컬 설정에 걸쳐 병합됩니다. 각 파일이 자기 훅을 더합니다. 프로젝트 포맷터가 사용자 알림 훅을 지우지는 않습니다. 같은 핸들러를 둘 이상의 설정 파일에 정의해도 한 번만 실행됩니다. 플러그인이나 스킬에 있는 그 핸들러의 사본은 별개로 남습니다. 한 파일 안에서 hooks 객체를 통째로 바꾸면, 그 파일에 설정된 다른 이벤트는 여전히 빠집니다.
클라우드 세션은 로컬 ~/.claude/settings.json을 읽지 않습니다. 그 파일의 개인 알림 훅은 이 머신에 남습니다.
command 훅은 사용자 계정의 권한으로 실행됩니다. 그 계정이 접근할 수 있는 파일은 수정하고, 지우고, 읽을 수 있습니다. 공유 프로젝트 파일에 커밋하기 전에 명령을 검토하고 시험합니다. 대화형 세션에서 Claude Code는 워크스페이스 신뢰 대화상자를 수락할 때까지 ~/.claude/settings.json을 포함한 모든 설정 파일의 훅을 보류합니다. claude -p 또는 SDK 세션에서 Claude Code는 그 폴더를 신뢰된 것으로 다루므로, .claude/settings.json에 커밋된 훅이 실행됩니다.
Anthropic이 말하는 훅 설정의 적용 범위입니다.
| 파일 | 범위 | 공유 |
| — | — | — |
| ~/.claude/settings.json | 이 머신의 모든 프로젝트 | 아니요 |
| .claude/settings.json | 프로젝트 하나 | 예, 커밋할 수 있음 |
| .claude/settings.local.json | 프로젝트 하나 | 아니요. Claude Code가 그곳에 설정을 저장하면 gitignore됩니다 |
Edit나 Write 뒤에 파일을 포맷합니다
Anthropic의 편집 후 포맷 레시피는 프로젝트 .claude/settings.json의 PostToolUse 훅이며 matcher는 Edit|Write입니다. 명령은 jq로 tool_input.file_path를 읽고 npx prettier –write에 넘깁니다. 그 파일에 이미 있는 이벤트 키 옆에 PostToolUse를 추가합니다.
훅 가이드의 Bash 예제는 jq를 씁니다. macOS에서는 brew install jq, Debian과 Ubuntu에서는 apt-get install jq로 설치합니다. 훅이 성공하면 Claude Code는 대화에 아무것도 보여 주지 않습니다. Anthropic의 확인 방법은 Claude에게 JavaScript 파일에 작은따옴표 문자열 한 줄을 추가하라고 한 뒤 파일을 여는 것입니다. Prettier 기본 설정이면 훅이 그 문자열을 큰따옴표로 바꿉니다.
PostToolUse는 도구가 성공한 뒤에 실행되므로 편집을 되돌리지 못합니다. Edit|Write는 Bash 명령이 다시 쓴 파일도 놓칩니다. 무엇이 썼든 특정 파일을 다시 포맷해야 하면 Anthropic은 FileChanged 훅을 가리킵니다. .envrc|.env 같은 FileChanged matcher는 |로 나눈 리터럴 파일 이름 목록이며, FileChanged는 쓰기를 막지 못합니다. 한 턴의 모든 변경에는 가이드가 Stop 검사나, git status –porcelain으로 수정·미추적 파일을 나열하는 스크립트의 Bash|PowerShell matcher를 가리킵니다. PowerShell 도구가 기본 셸인 Windows에서는 Bash만 맞는 훅이 실행되지 않습니다.
.claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
종료 코드 2로 편집을 막습니다
Anthropic의 protect-files 예제는 Edit|Write에 대한 PreToolUse 훅입니다. 스크립트는 tool_input.file_path를 읽고, .env, package-lock.json, .git/를 보호 부분 문자열로 보고, Blocked 메시지를 stderr에 쓴 뒤 종료 코드 2로 나갑니다. Claude Code는 그 Edit 또는 Write를 막고 메시지를 Claude에게 전달합니다. 스크립트 끝의 종료 코드 0은 결정이 없다는 보고이므로 일반 권한 흐름이 그대로 적용됩니다. 편집을 승인하지는 않습니다.
Windows에서 Write, Edit, Read는 file_path를 백슬래시로 전달합니다. 스크립트의 치환은 패턴 검사 전에 그것을 슬래시로 바꿉니다. 슬래시로 쓴 비교를 백슬래시 경로에 그대로 적용하면 편집이 통과합니다.
스크립트를 .claude/hooks/protect-files.sh로 저장합니다. macOS와 Linux에서는 chmod +x .claude/hooks/protect-files.sh를 실행합니다. 이 명령은 shell form이므로 경로를 따옴표로 감싸 프로젝트 .claude/settings.json에 등록합니다. Anthropic의 시험은 Claude에게 .env에 주석을 추가하라고 하는 것입니다. 편집은 실행 전에 차단됩니다.
PreToolUse에서 종료 코드 2는 스크립트가 allow라고 하는 JSON을 출력해도 차단합니다. stderr와 종료 코드 2를 쓰거나, 종료 코드 0과 JSON을 쓰고, 훅마다 한 방식을 고릅니다.
.claude/hooks/protect-files.sh
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Normalize Windows backslash separators so the patterns below match
FILE_PATH="${FILE_PATH//\\//}"
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0
Edit나 Write 전에 파일 가드를 등록합니다
이 PreToolUse 그룹을 프로젝트 .claude/settings.json에, 이미 있는 이벤트 키 옆에 추가합니다. $CLAUDE_PROJECT_DIR은 세션이 시작된 프로젝트 루트입니다. shell form에서는 공백이 있는 경로가 남도록 따옴표가 중요합니다.
시작하지 못하는 command 훅, 즉 파일이 없거나 실행할 수 없는 스크립트는 편집을 막지 않습니다. 셸은 127 같은 코드로 끝나고, 트랜스크립트에는 차단하지 않는 훅 오류가 보이며, 도구 호출은 계속됩니다. PreToolUse에서 시간 초과에 도달한 command, HTTP, MCP tool 훅도 호출을 일반 권한 흐름에 남깁니다. 이 훅들의 기본 시간은 600초입니다. 시작에 실패하거나 시간 초과된 가드는 문을 열어 두므로, 하드 블록은 여전히 권한 시스템에 둡니다. 레퍼런스가 말하는 예외는 PreToolUse에서 시간 초과를 넘긴 Agent SDK 콜백 훅입니다. 그 콜백은 도구 호출을 막습니다.
.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
JSON으로 파괴적인 셸 명령을 거부합니다
명령이 실행되기 전에 멈추려면 Bash에 PreToolUse를 씁니다. Anthropic 레퍼런스 예제는 프로젝트에 block-rm.sh를 저장하고 chmod +x로 실행 가능하게 만든 뒤, if 규칙을 Bash(rm *)로 등록합니다. 스크립트는 명령에 rm -rf가 있으면 permissionDecision deny를 출력합니다. 그 외에는 종료 코드 0으로 나가며, 일반 권한 흐름은 그대로 두고 명령을 승인하지 않습니다.
if 필드는 권한 규칙 하나를 담으며 매칭은 best-effort입니다. Claude Code가 Bash 입력이 어떤 명령을 실행하는지 알 수 없으면 패턴과 관계없이 훅을 실행합니다. 레퍼런스는 하드 allow나 deny에는 훅 대신 권한 시스템을 쓰라고 합니다. 훅이 실제로 반환한 permissionDecision deny는 bypassPermissions 모드와 –dangerously-skip-permissions에서도 도구를 막습니다. allow 결정은 설정의 deny 규칙을 느슨하게 만들지 않습니다.
PreToolUse에서 deny는 호출을 취소하고 permissionDecisionReason을 Claude에게 보냅니다. ask는 일반 프롬프트를 보여 줍니다. allow는 대화형 프롬프트를 건너뛰되 deny와 ask 규칙은 그대로 적용됩니다. defer는 비대화형 claude -p 실행에 있습니다. PreToolUse 형제 훅은 그중 하나가 deny를 반환한 뒤에도 계속 실행됩니다. 병합된 결정의 순서는 deny, defer, ask, allow입니다.
이 등록은 args를 빈 배열로 두며, 이것은 exec form입니다. Claude Code가 셸 없이 스크립트를 직접 실행합니다. Prettier 훅처럼 파이프나 &&가 필요하면 args를 생략합니다. PowerShell이 기본 셸인 Windows에서는 Bash|PowerShell도 맞춥니다. Bash만 맞는 훅은 거기서 실행되지 않습니다. 레퍼런스에는 Remove-Item용 PowerShell 핸들러가 따로 있습니다.
.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"args": []
}
]
}
]
}
}
deny를 출력하는 스크립트입니다
이것을 .claude/hooks/block-rm.sh로 저장하고 macOS와 Linux에서 chmod +x를 합니다. jq를 쓰므로 jq가 PATH에 있어야 합니다. rm -rf이면 JSON을 출력하고 종료 코드 0으로 나가므로, 호출을 거부하는 것은 JSON입니다. rm file.txt처럼 더 안전한 명령이면 출력 없이 종료 코드 0으로 나갑니다. 이 방식은 종료 코드 0을 유지합니다. 종료 코드 2를 더하면, 나중에 JSON을 allow로 고쳐도 차단됩니다.
PreToolUse는 EndConversation도 건너뛰고, @로 참조한 파일은 보지 못합니다. Claude Code는 프롬프트를 만드는 동안 그 내용을 넣으므로, 그 파일에 대해서는 Read 훅이 실행되지 않습니다.
.claude/hooks/block-rm.sh
#!/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
fi
종료 코드의 효과는 이벤트마다 다릅니다
종료 코드 0은 훅이 종료 코드로는 이의를 제기하지 않는다는 뜻입니다. PreToolUse에서는 일반 권한 흐름이 그대로 남습니다. 훅이 JSON 결정을 출력할 때도 종료 코드 0을 씁니다. 종료 코드 2는 차단할 수 있는 이벤트를 막습니다. 이유는, 그 JSON 결정이 차단하면 결정의 reason이고, 그렇지 않으면 stderr입니다. PermissionRequest는 종료 코드 2를 무시합니다. deny는 hookSpecificOutput.decision.behavior에서 와야 합니다. Notification 훅은 종료 코드와 stderr를 무시합니다. PostToolUse는 이미 실행된 도구를 되돌리지 못합니다. 거기에서 종료 코드 2는 일이 끝난 뒤 stderr를 Claude에게 보여 줍니다.
대부분의 이벤트에서 코드만으로 차단하는 종료 코드는 2뿐입니다. 유효한 결정 JSON이 없으면 종료 코드 1은 차단하지 않는 오류이고 동작은 계속됩니다. WorktreeCreate는 예외입니다. 0이 아닌 종료는 워크트리 생성을 실패시킵니다. 차단할 수 있는 이벤트에서 종료 코드 2와 JSON을 섞으면 종료 코드 2가 여전히 차단하고, permissionDecision allow는 그것을 뒤집지 못합니다. Claude Code v2.1.214 이전에는 종료 코드 2와 스키마 검증에 실패한 stdout JSON이 함께 있으면 차단하지 않았습니다. 현재 레퍼런스는 그 조합도 여전히 차단하며, Claude Code가 stderr를 이유로 쓴다고 말합니다.
셸 프로필이 JSON보다 먼저 글을 출력하면 stdout이 {로 시작하지 않고, Claude Code는 그것을 일반 텍스트로 다루며, 종료 코드 0에서는 결정이 무시됩니다. 가이드의 수정은 대화형 셸에서만 echo하는 것입니다. 시작하지 못한 command 훅과, PreToolUse에서 시간 초과된 command, HTTP, MCP tool 훅은 도구 호출을 일반 권한 흐름에 남깁니다.
- PreToolUse의 종료 코드 0은 일반 권한 흐름을 그대로 둡니다.
- 종료 코드 0과 JSON은 구조적 제어입니다. PreToolUse의 permissionDecision, PermissionRequest의 decision.behavior, Stop과 PostToolUse 같은 이벤트의 최상위 decision block입니다.
- 종료 코드 2는 stdout JSON이 allow라고 해도 PreToolUse를 막습니다.
- PostToolUse의 종료 코드 2는 도구가 이미 실행된 뒤 stderr를 Claude에게 보여 줍니다.
- PermissionRequest와 Notification은 종료 코드 2를 무시합니다.
- 유효한 결정 JSON이 없는 종료 코드 1은 차단하지 않습니다.
이 레시피의 이벤트에 대해 Anthropic 훅 레퍼런스가 말하는 종료 코드 2와 JSON의 효과입니다.
| 이벤트 | 종료 코드 2의 효과 | 결정을 내리는 JSON 필드 |
| — | — | — |
| PreToolUse | 도구 호출을 막음 | hookSpecificOutput.permissionDecision: allow, deny, ask, defer |
| PostToolUse | stderr를 Claude에게 보여 줌. 도구는 이미 실행됨 | 최상위 decision을 block으로 설정 |
| PermissionRequest | 무시됨. 권한 흐름은 계속됨 | hookSpecificOutput.decision.behavior: allow 또는 deny |
| Notification | 무시됨 | 없음. 이 이벤트에는 결정 제어가 없음 |
| Stop | 턴을 끝내는 대신 Claude가 계속 작업하게 함 | 최상위 decision을 block으로 설정 |
세 주기가 세션의 대부분을 덮습니다
훅 레퍼런스는 흔한 이벤트를 세 주기로 나눕니다. SessionStart와 SessionEnd는 세션마다 실행됩니다. UserPromptSubmit, Stop, StopFailure는 턴마다 실행됩니다. PreToolUse와 PostToolUse는 에이전트 루프 안의 도구 호출에서 실행되며, EndConversation은 둘 다 건너뜁니다. 포맷터는 PostToolUse에 둡니다. 차단은 PreToolUse에 둡니다. 데스크톱 알림은 Notification에 둡니다. Notification은 Claude Code가 알림을 보낼 때 발생하며, 위에서 말한 지연된 permission_prompt도 포함합니다.
같은 이벤트에 맞는 훅은 병렬로 끝까지 실행됩니다. deny 하나가 형제 로거를 멈추지는 않습니다. 여러 PreToolUse 훅이 같은 도구에 updatedInput을 반환하면 가장 늦게 끝난 것이 적용되고, 그 순서는 결정적이지 않습니다.
Anthropic의 Notification 명령을 ~/.claude/settings.json에 넣고 /hooks에서 확인합니다. Linux에서는 notify-send를, Windows에서는 PowerShell MessageBox 명령을 씁니다. jq를 설치한 뒤 프로젝트의 .claude/settings.json에 PostToolUse Edit|Write Prettier 명령을 추가합니다. protect-files.sh를 저장하고 macOS 또는 Linux에서 chmod +x를 한 다음, 따옴표로 감싼 $CLAUDE_PROJECT_DIR 경로로 PreToolUse에 등록합니다. block-rm.sh도 같은 방식으로 저장하고, if는 Bash(rm *), args는 빈 배열로 등록해 permissionDecision deny를 출력하게 합니다. 가드 스크립트가 시작에 실패하거나 시간 초과되면 도구 호출을 막지 않으므로, 권한 시스템에도 하드 deny를 함께 둡니다.