Windows에서 Claude Code hook이 작동하지 않는데, 그 이유를 아무도 알려주지 않습니다
요약
Windows 환경에서 Claude Code 플러그인의 SessionStart 훅이 작동하지 않는 문제를 분석한 글입니다. 작성자는 초기에는 매처 문제나 일반적인 셸 오류로 추정했으나, 근본 원인은 Windows의 기본 셸(cmd.exe)이 bash 스크립트를 해석할 수 없어 훅 실행이 비정상 종료되기 때문임을 밝혀냈습니다.
핵심 포인트
- Windows 환경에서 Claude Code 플러그인 훅은 cmd.exe가 bash를 처리하지 못해 실패합니다.
- PreToolUse 같은 다른 훅들은 cmd 내장 명령어 사용으로 정상 작동할 수 있습니다.
- Bash 도구의 작동 여부는 훅 러너가 사용하는 OS 셸 경로와는 별개입니다.
제 플러그인 중 하나는 SessionStart hook을 가지고 있습니다. 이 훅은 활성 브랜드 키트(brand kit)를 컨텍스트에 로드하여, 사용자가 매 세션마다 색상과 글꼴을 다시 설명할 필요 없이 일반 요청만으로도 이미 알고 있게 합니다.
macOS에서는 작동합니다. 하지만 Windows에서는 아무 일도 일어나지 않습니다. 오류 메시지도, 경고 메시지도 없고, 스크립트 기록(transcript)에 남는 흔적조차 없습니다. 그저 조용히 존재하지 않는 것입니다.
잘못된 방향까지 포함하여 전체 과정을 설명하겠습니다. 왜냐하면 이 잘못된 과정이 흥미로운 부분이기 때문입니다.
증상
훅은 다음과 같이 선언됩니다:
{
"hooks": {
"SessionStart": [
...
여기에는 아무런 문제가 없습니다. 플러그인은 활성화되어 있고, 스크립트도 존재하며, 실행 가능합니다. Mac에서는 매 세션마다 작동합니다.
잘못된 이론
먼저 훅이 아예 호출되고 있는지 확인했습니다. Claude Code는 훅이 발동될 때 기록을 저장하므로 감사(audit)가 가능합니다. 실패한 세션을 살펴보니:
SessionStart:startup기록: 0PreToolUse:Bash기록: 12
따라서 SessionStart는 한 번도 실행되지 않았지만, 다른 훅은 열두 번 실행되었습니다. 그리고 같은 앱 버전의 형제 세션에서는 실제로 플러그인이 방출하는 정확한 라인을 담은 SessionStart 기록이 있었습니다. 즉, 훅 자체는 작동합니다. 단지 여기서는 호출되지 않은 것입니다.
이것은 합리적인 이론으로 이어졌습니다: 매처(matcher) 때문일 것이라고 생각했습니다. startup|resume가 모든 세션 소스(startup, resume, clear, compact)와 일치하지 않기 때문에, 데스크톱 앱이 일치하는 이벤트를 생성하지 않는 경로를 통해 세션을 만들 수도 있다는 것입니다.
그래서 매처를 제거하여 네 가지 모두에서 작동하도록 했습니다. 배포하고 Windows 기기를 업데이트했습니다.
여전히 아무것도 없습니다.
이것이 유용한 부분입니다. 이론은 그럴듯했고, 증거를 설명했지만 틀렸습니다. 기록이 0이라는 것은
| Check | 결과 |
|---|---|
기본 셸 (COMSPEC) | C:\WINDOWS\system32\cmd.exe |
| ... | |
따라서 Claude Code는 bash "...session-start.sh"를 cmd.exe에 전달하고, cmd는 bash를 해석할 수 없어 명령이 오류가 나고, 훅은 비정상 종료(non-zero)되며 사라집니다. 아무도 모르게요. |
상충되는 것처럼 보이는 두 가지 점
이 두 가지 모두 제가 bash가 정상적으로 작동한다고 믿게 만들었습니다. 하지만 사실 둘 다 확인 사살입니다.
Bash 도구는 작동합니다. 저는 세션에서 하루 종일 bash 명령을 실행할 수 있습니다. 하지만 이 도구는 훅 러너(hook runner)가 OS 셸과 시스템 PATH를 사용해서 찾는 경로와 다른 방식으로 자체 Git Bash를 찾습니다. "내 세션에서 bash가 작동한다"는 말은 훅이 그것을 찾을 수 있는지에 대해서는 아무것도 알려주지 않습니다.
다른 훅도 실행됩니다. 제 PreToolUse 훅은 echo를 실행합니다. 이것은 echo가 cmd 내장 명령어(built-in)이기 때문에 bash가 전혀 필요 없고 작동합니다. 오직 bash를 호출하는 훅들만 실패하며, 제가 가진 모든 플러그인에서 그렇습니다.
이것은 예외적인 경우가 아닌 기본 설치 환경입니다
Git for Windows는 Git\cmd를 PATH에 추가할 뿐, Git\bin을 추가하지 않습니다. 이것이 표준 설치 옵션입니다. Git\bin을 추가하는 것은 설치 프로그램에서 명시적으로 경고하는 옵션인데, 왜냐하면 GNU의 find와 sort가 Windows 기본 명령어보다 먼저 오게 되어 다른 도구들을 망가뜨리기 때문입니다.
따라서 기본 Windows 설정은 다음과 같습니다: bash는 설치되어 있지만, PATH에 등록되어 있지 않습니다. 훅이 bash로 외부 명령을 내보내는 모든 플러그인들은 대다수의 Windows 사용자들에게 아무런 효과도 없으며, 어디에도 오류 메시지가 나타나지 않습니다.
작동하지 않는 것을 포함한 해결책들
PATH에 Git\bin 추가하기. 작동합니다. 이것은 Git이 경고하는 것이기도 하며, 기기별 설정(per machine)이기 때문에 사용자님만 고치고 다른 사람은 못 고칩니다.
절대 경로 하드코딩하기. 안 됩니다. 단 하나의 올바른 경로는 없습니다. macOS는 bash를 /bin/bash에 설치하고, /usr/bin/bash가 아닙니다. 대부분의 Linux는 /usr/bin/bash를 사용합니다. Windows는 설치 프로그램마다 다른 경로를 가집니다. 게다가 cmd는 POSIX 경로를 해석할 수 없으므로, /usr/bin/bash는 거기서 무의미합니다.
대신 sh를 사용하세요. 게다가 Windows에는 sh도 없습니다. 둘 다 동일한 Git Bash 레이어에서 옵니다. 그리고 제 스크립트는 set -euo pipefail을 사용하는데, pipefail은 POSIX 표준이 아니므로 dash(Debian 및 Ubuntu의 /bin/sh) 환경에서 실행하면 즉시 오류가 발생합니다. 이는 Windows 문제를 Linux 문제로 바꾸는 셈입니다.
조건문을 명령어에 넣으세요. 매력적이지만, 후크 명령어는 Windows에서는 cmd.exe에서, Unix에서는 sh에서 실행되며, 이들의 조건문 구문은 공통점이 없습니다. 두 환경의 경로를 확인하는 단일 문자열은 존재하지 않습니다.
node를 사용하세요. 이것이 작동하는 방법입니다. Node의 Windows 설치 프로그램은 node를 기본적으로 PATH에 추가하는데, 이는 Git 설치 프로그램이 bash에 대해 하지 않는 정확한 동작입니다. 따라서 후크를 Node로 포팅하거나, 기존의 bash 스크립트를 유지하되 후보 위치들을 탐색하여 존재하는 것을 실행하는 작은 Node 런처를 추가해야 합니다:
// candidates, first hit wins
const candidates = [
'bash', // on PATH (mac/linux, or Git\bin on PATH)
...
이것은 보장이 아닙니다. 만약 node도 후크 실행기의 PATH에 없다면, 처음 시작점으로 돌아갑니다. 하지만 기본적으로 깨져 있는 의존성을 제거해 줍니다.
실제 버그
PATH 문제는 패키징상의 사소한 결함입니다. 진짜 문제는 후크가 실패했지만 아무에게도 알려주지 않았다는 것입니다.
명령어가 오류를 일으켰습니다. 플러그인이 조용히 기능을 잃어버린 것입니다. 트랜스크립트 어디에도, 경고 메시지도, 로그 라인도, 종료 코드도 사용자나 저에게 노출되지 않았습니다. 외부적으로 볼 때, 이는 후크가 등록조차 되지 않은 것과 구별할 수 없으며, 이것이 바로 제가 잘못된 이론에 하루를 낭비한 이유입니다.
후크를 사용하는 하네스(harness)를 구축한다면: 실패 사례들을 노출시키세요. 조용히 죽는 후크는 존재하지 않는 후크보다 더 나쁩니다. 왜냐하면 적어도 누락된 후크는 눈에 보이기 때문입니다.
그리고 후크를 사용하는 플러그인을 작성한다면: 무언가를 가정하기 전에 Windows 환경에서 where.exe bash를 실행해 보세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기