본문으로 건너뛰기

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 -g
RTK hook registered (global).
Command: rtk hook claude
RTK.md: ~/.claude/RTK.md (10 lines)
CLAUDE.md: @RTK.md reference added
rtk: Failed to parse ~/.claude/settings.json as JSON:
trailing comma at line 235 column 3

출력 첫 줄만 보면 성공한 것 같은데, 바로 아래에 JSON 파싱 에러가 같이 찍혀 있었다. ~/.claude/hooks 폴더를 열어 봐도 파일이 하나도 없었다.

원인: 쉼표 하나

처음엔 그럴듯한 원인부터 차례로 의심했다.

  1. Windows의 bash 문제. RTK 구버전 훅(rtk-rewrite.sh)은 bash 스크립트라 네이티브 Windows에서는 돌지 않고 CLAUDE.md 안내문으로만 대신한다. 여기는 macOS라 해당하지 않았다.
  2. PATH나 jq 문제. 훅을 돌리는 셸이 rtk나 jq 설치 경로(/opt/homebrew/bin)를 못 찾으면 의존성 확인이 조용히 실패할 수 있다. 그럴듯했지만 로그를 보니 아니었다.

진짜 원인은 ~/.claude/settings.json 235번째 줄의 trailing comma였다. JSON은 JSONC와 달리 배열이나 객체의 마지막 항목 뒤에 쉼표가 있으면 파싱이 통째로 실패한다.

~/.claude/settings.json
"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.bak
sed -n '225,240p' ~/.claude/settings.json

JSON 문법을 검증하고 다시 등록했다. 이번엔 파싱 에러 없이 끝났고, settings.json에 훅이 들어간 것도 확인했다.

검증하고 다시 등록
python3 -m json.tool < ~/.claude/settings.json > /dev/null && echo "OK"
rtk init -g
grep -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 범위끼리 덮어쓰지 않고 합쳐진다. 같은 이벤트에 여러 범위로 훅을 넣으면 둘 다 돌 수 있으니, 예전에 넣어 둔 훅이 남아 있지 않은지 확인하고 한 곳에만 둔다.
GitHub에서 고치기
esc

최근 글

↑↓ 이동↵ 열기검색 페이지에서 보기 ›