Claude Code PreToolUse 훅이 등록 안 되는 문제 해결 (RTK)
Claude Code는 Bash 도구를 부르기 전에 PreToolUse 훅을 거치게 할 수 있다. 여기에 RTK 같은 토큰 절약 도구를 물려 두면, Claude가 실행하는 git status가 실행 전에 rtk git status로 바뀌고 압축된 출력만 컨텍스트에 들어간다. 분명 설정했는데 자꾸 안 먹는 것 같아서 원인을 따라가 봤다.
증상: 성공 메시지 아래 에러
~/.claude/settings.json의 PreToolUse에 RTK 훅(rtk hook claude)을 등록해 뒀는데, Claude가 돌리는 Bash 명령이 rtk로 바뀌는 경우가 별로 없었다. 환경은 macOS이고 RTK는 Homebrew로 설치했다(brew install rtk). 글로벌 훅을 다시 등록해 봤다.
$ rtk init -gRTK hook registered (global).
Command: rtk hook claude RTK.md: ~/.claude/RTK.md (10 lines) CLAUDE.md: @RTK.md reference addedrtk: Failed to parse ~/.claude/settings.json as JSON:trailing comma at line 235 column 3출력 첫 줄만 보면 성공한 것 같은데, 바로 아래에 JSON 파싱 에러가 같이 찍혀 있었다. ~/.claude/hooks 폴더를 열어 봐도 파일이 하나도 없었다.
원인: 쉼표 하나
처음엔 그럴듯한 원인부터 차례로 의심했다.
- Windows의 bash 문제. RTK 구버전 훅(
rtk-rewrite.sh)은 bash 스크립트라 네이티브 Windows에서는 돌지 않고 CLAUDE.md 안내문으로만 대신한다. 여기는 macOS라 해당하지 않았다. - PATH나 jq 문제. 훅을 돌리는 셸이
rtk나jq설치 경로(/opt/homebrew/bin)를 못 찾으면 의존성 확인이 조용히 실패할 수 있다. 그럴듯했지만 로그를 보니 아니었다.
진짜 원인은 ~/.claude/settings.json 235번째 줄의 trailing comma였다. JSON은 JSONC와 달리 배열이나 객체의 마지막 항목 뒤에 쉼표가 있으면 파싱이 통째로 실패한다.
"allow": [ "Bash(npm run *)", "Bash(git commit *)", "Bash(git commit *)"]RTK는 기존 설정을 읽어 PreToolUse 항목을 합쳐 넣으려다 이 파싱에서 실패했다. 그러니 “RTK hook registered”는 RTK.md와 CLAUDE.md 쪽만 성공했다는 뜻이었고, settings.json에는 훅이 들어가지 않았다.
hooks 폴더가 비어 있던 이유
이건 별개로 헷갈렸던 부분이다. 요즘 RTK는 예전처럼 bash 스크립트(rtk-rewrite.sh)를 따로 만들지 않고, rtk 바이너리의 hook claude 서브커맨드를 settings.json에 바로 등록한다. 구버전 기준으로는 hooks 폴더가 비어 있는 게 이상해 보이지만, 지금 버전에서는 정상이다.
해결: 쉼표를 지우고 다시 등록
먼저 백업하고 문제 줄 근처를 확인한 뒤, 위처럼 마지막 항목 뒤의 쉼표를 지웠다.
cp ~/.claude/settings.json ~/.claude/settings.json.baksed -n '225,240p' ~/.claude/settings.jsonJSON 문법을 검증하고 다시 등록했다. 이번엔 파싱 에러 없이 끝났고, settings.json에 훅이 들어간 것도 확인했다.
python3 -m json.tool < ~/.claude/settings.json > /dev/null && echo "OK"rtk init -ggrep -A 3 "rtk hook claude" ~/.claude/settings.json🧪 직접 해보기
훅이 명령을 실제로 바꾸는지 확인하기
hooks 폴더에 파일이 없는 게 정상이니, 훅 커맨드에 Claude Code가 보내는 것과 같은 입력을 직접 넣어 본다.
echo '{"tool_name":"Bash","tool_input":{"command":"git status"}}' | rtk hook claude{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecisionReason":"RTK auto-rewrite","updatedInput":{"command":"rtk git status"},"permissionDecision":"allow"}}updatedInput에 rtk git status가 들어 있으면 훅이 제대로 동작하는 것이다.
배운 점
- 도구가 낸 성공 메시지가 어느 단계까지의 성공인지 끝까지 읽는다. RTK.md와 CLAUDE.md 업데이트 성공이 훅 등록 성공은 아니었다.
- settings.json은 결국 JSON 파일이다. 쉼표 하나로 도구의 쓰기 작업 전체가 조용히 깨질 수 있으니, 손으로 고쳤다면
python3 -m json.tool로 한 번 검증한다. - 도구의 내부 구현은 버전이 바뀌며 달라진다(셸 스크립트에서 바이너리 서브커맨드로). 예전 문서나 가설에 기대기보다 훅에 입력을 직접 넣어 보는 게 가장 빠르고 정확했다.
- Claude Code의
hooks는model이나defaultMode같은 단일 값 설정과 달리, user · project · local 범위끼리 덮어쓰지 않고 합쳐진다. 같은 이벤트에 여러 범위로 훅을 넣으면 둘 다 돌 수 있으니, 예전에 넣어 둔 훅이 남아 있지 않은지 확인하고 한 곳에만 둔다.