Claude Code 훅에서 'allow'를 출력하고 오류 없이 무시되게 하는 방법
요약
Claude Code의 `PermissionRequest` 훅에서 권한 결정(`allow`/`deny`)을 할 때 문자열 대신 객체 구조를 사용해야 합니다. 이전 방식처럼 단순 문자열을 출력하면 유효성 검사에 실패하여 무시되므로, 반드시 정확한 JSON 객체 형식으로 출력을 수정해야 합니다.
핵심 포인트
- 권한 결정은 문자열이 아닌 {
- decision
- {behavior: "allow"}
- }와 같은 객체로 출력해야 함.
- 출력된 바이트를 테스트하여 실제 동작을 검증하는 습관이 중요함.
- 실제 허용(allow) 시나리오를 엔드투엔드로 증명하며 테스트할 것을 권장함.
당신은 PermissionRequest 훅을 작성했습니다. 이 훅은 'allow'를 결정하여 출력하고, 로그에는 'allowed'라고 표시되며, Claude Code는 평범한 프롬프트를 보여줍니다. 오류도 경고도 없습니다. 마치 질문에 대한 답을 당신에게 맡긴 훅처럼 보입니다.
저희가 그 버그를 배포했습니다. 몇 주 동안 모든 원격 장치에서 승인된 작업이, 설치할 때마다 무시되었습니다. 여기에는 단 한 줄의 원인이 있으며, 이를 잡아낼 수 있었던 두 가지 습관에 대해 설명합니다.
원인: Claude Code가 객체를 원하는 곳에 문자열을 사용함
이전 예제들은 결정을 문자열로 출력했습니다:
"decision": "allow"
Claude Code는 훅의 출력을 검증합니다. 문자열은 유효성 검사에 실패하며, 유효성 검사에 실패한 훅은 **비차단(non-blocking)**으로 처리됩니다. 즉, 권한 흐름은 마치 당신의 훅이 아무런 답변을 하지 않은 것처럼 계속 진행됩니다. CLI 자체의 검증기가 원하는 바를 알려줍니다:
PermissionRequest decision must be {"behavior": "allow"} or {"behavior": "deny", "message": "..."}
저희가 확인했을 때, 게시된 훅 문서는 여전히 문자열 형태를 보여주고 있었습니다. 검증기를 신뢰하세요.
대신 출력해야 할 내용
표준 출력(Standard output)은 답변만을 담고 그 외에는 아무것도 전달하지 않습니다. 로그는 표준 에러(standard error)로 전송됩니다.
허용(Allow):
{"hookSpecificOutput": {"hookEventName": "PermissionRequest",
"decision": {"behavior": "allow"}}}
거부(Deny), Claude가 읽고 행동할 수 있는 이유와 함께:
{"hookSpecificOutput": {"hookEventName": "PermissionRequest",
"decision": {"behavior": "deny", "message": "Not on main. Push to a branch."}}}
아무것도 전달하지 않음으로써 사람에게 맡기는 경우 (no decision):
{"hookSpecificOutput": {"hookEventName": "PermissionRequest"}}
이를 잡아낼 수 있었던 두 가지 습관
- 하나의 함수가 출력을 만들고, 테스트는 그것이 출력하는 정확한 바이트를 확인합니다.
decision이behavior를 가진 객체인지, 그리고 거부(deny) 시에message를 포함하는지 단언하세요. 당신이 출력하려는 것이 아니라, 실제로 무엇을 출력하는지 테스트하세요. - 실제 허용(allow) 사례 하나를 엔드투엔드로 증명합니다. 마커 파일을 생성하고, 세션에게 그 파일을 삭제하도록 요청한 다음, 당신의 훅을 통해 삭제를 승인하고, 파일이 사라졌는지 확인하세요. 만약 파일이 여전히 존재하고 일반적인 프롬프트가 나타났다면, 당신의 답변은 무시된 것입니다.
연결(Wiring), 간략히
~/.claude/settings.json 또는 플러그인의 hooks/hooks.json에서:
{"hooks": {"PermissionRequest": [{"hooks": [
{"type": "command", "command": "python3 /path/to/your-hook.py", "timeout": 86460}]}]}}
타임아웃은 초 단위입니다. 사람이 기다리는 훅은 길어야 합니다. 그렇지 않으면 그들이 읽는 동안 질문이 사라질 수 있습니다. 이벤트는 표준 입력(standard input)으로 JSON 형태로 도착하며, 프롬프트가
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기