Claude Code 알림 훅 시스템 for macOS
claude-notify는 Claude Code 의 hooks 시스템과 통합되는 알림 매니저입니다. 시스템 상태(터미널 활성화, 화면 잠금 등)에 따라 알림 채널을 자동으로 선택하여 적절한 시점에 사용자에게 알림을 전달합니다.
- 스마트 알림 라우팅: 시스템 상태를 감지하여 최적의 알림 채널 자동 선택
- 터미널 활성화 → 알림 스킵 (이미 화면 보고 있음)
- 화면 잠금 → ntfy 푸시 (모바일)
- 터미널 비활성 → terminal-notifier (로컬 macOS 알림)
- 다중 알림 채널: terminal-notifier(로컬)와 ntfy(모바일 푸시) 어댑터 지원
- 커스터마이징 가능: JSON 설정으로 알림 타입별 채널, 메시지 템플릿 설정
- 환경변수 오버라이드: 설정 파일 없이 환경변수로도 커스터마이징 가능
Claude Code 플러그인으로 설치하면 훅 등록이 자동으로 됩니다. ~/.claude/settings.json 을 직접 편집할 필요가 없습니다.
# 1. 마켓플레이스 등록 (최초 1회)
/plugin marketplace add socoolbear/cc-marketplace
# 2. 설치
/plugin install claude-notify@socoolbear-cc-marketplace
# 3. ntfy 토픽 설정 + 동작 확인
/claude-notify:notify-setup문제가 생기면 /claude-notify:notify-doctor 로 진단할 수 있습니다.
사전 요구사항
| 항목 | 용도 | 설치 |
|---|---|---|
terminal-notifier |
macOS 로컬 알림 | brew install terminal-notifier |
| Node.js 18+ | 훅 실행 | Claude Code 사용자면 대부분 이미 있습니다 |
bun install
make install # dist/claude-notify.mjs → ~/.local/bin/claude-notify빌드에는 Bun 이 필요하지만, 산출물은 shebang 이 붙은 Node 스크립트라 실행에는 Bun 이 필요하지 않습니다.
이 경로로 설치하면 훅은 직접 등록해야 합니다 — 아래 Claude Code 훅 설정 참고.
~/.config/claude-notify/config.json 파일을 생성하여 알림 동작을 커스터마이징할 수 있습니다.
설정 파일을 생성하지 않으면 다음의 기본값이 적용됩니다:
{
"ntfy": {
"server": "https://ntfy.sh",
"topic": ""
},
"terminal_notifier": {
"enabled": true
},
"log": {
"enabled": false,
"level": "info"
},
"skip_when_active": true
}ntfy 토픽에는 기본값이 없습니다. 잘 알려진 토픽명을 기본값으로 두면 설정하지 않은 사용자의 알림 내용이 공개 토픽으로 새어나가기 때문입니다. 토픽이 비어 있으면 ntfy 발송을 건너뛰고 terminal-notifier 로만 알립니다. /claude-notify:notify-setup 이나 NTFY_TOPIC 으로 토픽을 정하세요.
{
"ntfy": {
"server": "https://ntfy.sh",
"topic": "my-claude-topic",
"token": "your-optional-auth-token"
},
"terminal_notifier": {
"enabled": true
},
"log": {
"enabled": true,
"level": "debug"
},
"skip_when_active": true,
"notifications": {
"permission_prompt": {
"enabled": true,
"title": "Claude Code",
"message_template": "권한 요청: {message}",
"channels": ["terminal-notifier", "ntfy"]
},
"idle_prompt": {
"enabled": true,
"title": "Claude Code",
"message_template": "유휴 시간 알림: {message}",
"channels": ["ntfy"]
},
"auth_success": {
"enabled": true,
"title": "Claude Code",
"message_template": "인증 성공: {message}",
"channels": ["terminal-notifier"]
},
"elicitation_dialog": {
"enabled": true,
"title": "Claude Code",
"message_template": "{message}",
"channels": ["terminal-notifier", "ntfy"]
},
"stop": {
"enabled": true,
"title": "Claude Code",
"message_template": "세션 종료: {reason}",
"channels": ["terminal-notifier"]
}
}
}설정 파일 없이 환경변수로도 설정할 수 있습니다:
# ntfy 설정
export NTFY_SERVER="https://ntfy.sh"
export NTFY_TOPIC="my-topic"
export NTFY_TOKEN="my-token"
# 로깅 설정
export CLAUDE_NOTIFY_LOG=true
export CLAUDE_NOTIFY_LOG_LEVEL=debug
# claude-notify 실행
echo '{"hook_event_name":"Notification","notification_type":"permission_prompt","message":"test"}' | claude-notify| 환경변수 | 용도 |
|---|---|
NTFY_SERVER / NTFY_TOPIC / NTFY_TOKEN |
ntfy 설정 오버라이드 |
CLAUDE_NOTIFY_LOG / CLAUDE_NOTIFY_LOG_LEVEL |
파일 로깅 활성화와 레벨 |
CLAUDE_NOTIFY_FORCE |
강제 발송 모드 — 터미널 활성 스킵과 채널 축소를 무시하고 설정된 모든 채널로 발송합니다. 설치 직후 동작 확인용입니다 |
# 터미널을 보고 있어도 두 채널 모두로 발송해 동작 확인
echo '{"hook_event_name":"Notification","notification_type":"permission_prompt","message":"연결 테스트"}' \
| CLAUDE_NOTIFY_FORCE=true claude-notify플러그인으로 설치했다면 이 절은 건너뛰세요. 플러그인의
hooks/hooks.json이 훅을 자동으로 등록합니다. 플러그인과 수동 훅을 함께 두면 알림이 두 번 갑니다.
수동 설치 시에는 ~/.claude/settings.json 을 다음과 같이 수정하세요:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "~/.local/bin/claude-notify"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "~/.local/bin/claude-notify"
}
]
}
]
}
}그러면 Claude Code에서 다음 이벤트 발생 시 자동으로 claude-notify가 실행됩니다:
| 이벤트 | 발생 시점 |
|---|---|
Notification |
권한 요청, 유휴 대기, 인증 성공, 사용자 입력 대화 |
Stop |
세션 종료 |
Claude Code의 훅으로 설정된 경우, 사용자가 개입할 필요 없이 자동으로 실행됩니다.
# Notification 이벤트 테스트
echo '{
"hook_event_name": "Notification",
"notification_type": "permission_prompt",
"message": "API 권한이 필요합니다",
"session_id": "session-123",
"timestamp": "2025-02-03T10:00:00Z"
}' | claude-notify
# Stop 이벤트 테스트
echo '{
"hook_event_name": "Stop",
"session_id": "session-123",
"stop_reason": "completed",
"timestamp": "2025-02-03T10:05:00Z"
}' | claude-notifyCLAUDE_NOTIFY_LOG=true CLAUDE_NOTIFY_LOG_LEVEL=debug echo '{...}' | claude-notify
cat ~/.config/claude-notify/notify.log# 1. 저장소 클론 및 의존성 설치
git clone <repository>
cd claude-notify
bun install
# 2. 개발 모드 실행
bun run dev
# 3. 코드 테스트
echo '{...}' | bun run dev# Node 실행용 단일 파일 번들 (dist/claude-notify.mjs)
bun run build
# Bun 단일 실행 바이너리 (선택 — Node 없이 쓰고 싶을 때)
bun run build:binary
# 개발 모드 (핫 리로드 없음)
bun run dev
# 테스트 실행
bun test
# 린트 체크 (Biome)
bun run lint
# 자동 포맷팅 (Biome)
bun run fmt
# Makefile 명령어
make build # dist/claude-notify.mjs 번들 생성
make build-binary # Bun 단일 실행 바이너리
make install # ~/.local/bin/claude-notify 로 설치
make plugin-sync # 번들을 cc-marketplace 플러그인으로 복사
make test # 테스트 실행
make clean # 빌드 산출물 삭제make plugin-sync 의 복사 위치는 CC_MARKETPLACE 로 바꿀 수 있습니다:
make plugin-sync CC_MARKETPLACE=~/some/other/cc-marketplacesrc/
├── index.ts # 엔트리포인트: stdin에서 JSON 읽고 이벤트 라우팅
├── types.ts # TypeScript 타입 정의 (HookInput, Config 등)
├── config.ts # 설정 파일 로드 (기본값 머징 포함)
├── logger.ts # 파일 로깅 (토글 가능)
├── handlers/
│ ├── notification.ts # Notification 이벤트 처리 및 스마트 채널 선택
│ ├── stop.ts # Stop 이벤트 처리
│ └── index.ts # 핸들러 재공급(barrel export)
├── adapters/
│ ├── base.ts # Adapter 인터페이스
│ ├── terminal-notifier.ts # macOS 네이티브 알림
│ ├── ntfy.ts # ntfy.sh HTTP API 클라이언트
│ └── index.ts # 어댑터 재공급(barrel export)
└── utils/
├── env.ts # 환경변수 읽기, HOME fallback, 강제 모드 판정
├── exec.ts # execFile 기반 외부 명령 실행 (셸 미경유)
├── channel-selector.ts # 시스템 상태 → 알림 채널 결정
├── sanitize.ts # 훅 입력 검증, 제어 문자 제거
├── state-detector.ts # 화면 잠금/터미널 상태 감지
├── terminal-detector.ts # 터미널 앱 Bundle ID 관리
└── index.ts # 유틸리티 재공급(barrel export)
외부 명령은 모두 utils/exec.ts 의 runCommand 를 거칩니다. execFile 로 인자를 배열 전달하므로 셸을 경유하지 않고, 따라서 명령 주입이 성립하지 않습니다.
이 프로젝트는 다음 가이드라인을 따릅니다:
- Early return: 중첩 조건문 대신 조기 반환 사용
- Immutability: 불변 패턴 선호
- Optional chaining:
?.적극 활용 - Nullish coalescing:
??사용 (||대신) - Path alias:
@/prefix를 사용한 절대 경로 임포트
Biome으로 자동 포맷팅:
bun run fmt- 타입 정의:
src/types.ts에 새로운 타입 추가 - 구현: 적절한 핸들러/어댑터 파일에 로직 구현
- 테스트:
tests/*.test.ts파일에 테스트 작성 - 포맷 및 린트:
bun run fmt && bun run lint
플러그인으로 설치했다면 /claude-notify:notify-doctor 가 아래 항목을 한 번에 점검합니다.
# Homebrew로 설치
brew install terminal-notifier
# 설치 확인
which terminal-notifier- 토픽이 설정돼 있는지부터 확인하세요. 기본값이 없으므로 토픽이 비어 있으면 ntfy 발송을 건너뜁니다. 로그에 다음이 남습니다:
[WARN] Ntfy: 토픽이 설정되지 않아 발송을 건너뜁니다 (/notify-setup 또는 NTFY_TOPIC 설정)
- ntfy 토픽이 유효한지 확인:
curl -d "test message" https://ntfy.sh/your-topic- 설정 파일의 토픽 이름 확인:
grep topic ~/.config/claude-notify/config.json- 로그 활성화하여 디버깅:
CLAUDE_NOTIFY_LOG=true CLAUDE_NOTIFY_LOG_LEVEL=debug echo '{...}' | claude-notify
tail -f ~/.config/claude-notify/notify.log- 설정이 정상적으로 로드되는지 확인:
CLAUDE_NOTIFY_LOG=true echo '{"hook_event_name":"Notification","notification_type":"permission_prompt","message":"test"}' | claude-notify- 터미널이 활성화되어 있지는 않은지 확인 (skip_when_active 확인):
{
"skip_when_active": false
}- 강제 모드로 라우팅 판정을 우회해 발송 자체가 되는지 확인:
echo '{"hook_event_name":"Notification","notification_type":"permission_prompt","message":"test"}' \
| CLAUDE_NOTIFY_FORCE=true CLAUDE_NOTIFY_LOG=true CLAUDE_NOTIFY_LOG_LEVEL=debug claude-notify- 알림이 두 번 온다면 플러그인과 수동 훅이 함께 등록돼 있는지 확인:
grep -n "claude-notify" ~/.claude/settings.json| 항목 | 경로 |
|---|---|
| 설정 파일 | ~/.config/claude-notify/config.json |
| 로그 파일 | ~/.config/claude-notify/notify.log |
| 빌드 산출물 | dist/claude-notify.mjs |
| 수동 설치 위치 | ~/.local/bin/claude-notify |
| 플러그인 실행 파일 | <플러그인 루트>/bin/claude-notify.mjs |
MIT License - 자유롭게 사용, 수정, 배포 가능합니다.