Files
recordingtest/.claude/hooks/usage.md
2026-04-07 20:35:45 +09:00

9.5 KiB

common/.claude/hooks 사용법

Claude 의 모든 응답 토큰을 프로젝트 단위로 누적하고, git 커밋이 일어나면 Aptabase 로 한 번에 전송하는 훅 묶음.


파일 구성

.claude/hooks/
├── aptabase.json              # 설정 (app_key, aptabase_host, user_name, enabled)
├── aptabase_common.py         # 공통 유틸 (설정/IP/git/state/transcript/POST)
├── aptabase-accumulate.sh     # Stop 훅 진입점
├── aptabase-accumulate.py     # 트랜스크립트 → state.accum 누적 (네트워크 없음)
├── aptabase-commit.sh         # 커밋 flush 진입점 (Claude 훅 + git 훅 공용)
├── aptabase-commit.py         # HEAD 변화 감지 → POST → 리셋
└── install-git-hook.sh        # .git/hooks/post-commit 설치 스크립트

.claude/state/
└── aptabase-accum.json        # 누적 상태 (자동 생성, git root 기준)

설치 (프로젝트 1회)

1. 훅 파일 복사

# 프로젝트 루트에서
cp -r path/to/common/.claude/hooks .claude/hooks

2. aptabase.json 채우기

.claude/hooks/aptabase.json:

{
  "enabled": true,
  "app_key": "A-SH-XXXXXXXXXX",
  "aptabase_host": "https://aptabase.example.com",
  "user_name": "kim"
}
설명
enabled false 면 모든 훅이 즉시 종료 (일시 비활성)
app_key Aptabase App Key (self-hosted 는 A-SH- 접두사)
aptabase_host Aptabase 인스턴스 base URL
user_name props.user_name 으로 전송할 사용자 식별자

3. settings.json 에 Claude 훅 등록

.claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          { "type": "command", "command": "bash .claude/hooks/aptabase-accumulate.sh" }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "bash .claude/hooks/aptabase-commit.sh" }
        ]
      }
    ]
  }
}

기존 훅이 있으면 같은 hooks 배열에 추가.

4. git post-commit 훅 설치 (필수)

Claude 외부(IDE, 터미널, git GUI)에서 이루어진 커밋도 포착하려면 반드시 실행:

bash .claude/hooks/install-git-hook.sh

옵션:

bash .claude/hooks/install-git-hook.sh --force      # 기존 post-commit 을 .bak 로 백업 후 덮어쓰기
bash .claude/hooks/install-git-hook.sh --uninstall  # 제거

.git/hooks/ 는 버전 관리되지 않으므로 clone 할 때마다 재설치 필요.


동작 흐름

Claude 응답 완료 ──┐
                  │
                  ▼
        [aptabase-accumulate.sh]
          └─ 트랜스크립트 JSONL 의 새 byte 영역만 파싱
          └─ assistant 메시지의 usage 를 state.accum 에 누적
          └─ 네트워크 호출 없음

git commit (Claude 의 Bash 툴 또는 외부 도구) ──┐
                                               │
                     ┌─────────────────────────┴────────┐
                     ▼                                  ▼
          [PostToolUse(Bash)]                 [.git/hooks/post-commit]
           └─ aptabase-commit.sh               └─ aptabase-commit.sh
                     │                                  │
                     └──────────────┬───────────────────┘
                                    ▼
                    [aptabase-commit.py]
                     ├─ git rev-parse HEAD
                     ├─ last_sent_commit 과 같으면 return (중복 방지)
                     ├─ 다르면:
                     │   ├─ 최신 트랜스크립트 flush (Claude 훅 모드)
                     │   ├─ commit_message / issue_number / repository 수집
                     │   ├─ claude_oauth_id / plan / local_ip / public_ip 수집
                     │   ├─ POST {aptabase_host}/api/v0/event
                     │   ├─ 성공: state.accum = 0, last_sent_commit = HEAD
                     │   └─ 실패: state 유지, 다음 호출에서 재시도

전송되는 이벤트

이벤트 이름: claude_commit (고정)

props 필드

필드 출처
claude_oauth_id ~/.claude/config.json 의 OAuth 이메일 (없으면 anonymous)
plan 구독 플랜 (max / pro / team / enterprise / apikey / unknown)
user_name aptabase.jsonuser_name
local_ip UDP connect 트릭 (패킷 미전송)
public_ip api.ipify.orgifconfig.me (2초 타임아웃)
commit_hash git rev-parse HEAD
commit_message git log -1 --pretty=%B
issue_number 커밋 메시지에서 regex 추출. 없으면 null
repository owner/repo (remote URL) 또는 디렉터리명
repository_url git config --get remote.origin.url
total_tokens 누적 합계
input_tokens, cache_creation_tokens, cache_read_tokens, output_tokens 누적 세부

이슈 번호 추출 패턴 (우선순위 순):

  • FEAT-123, BUG-45, FIX-7, TASK-9, PROJ-100, ISSUE-12
  • closes #45, fixes #12, resolves #3, GH-8
  • #123

검증

1. Aptabase 도달성 확인 (curl)

APP_KEY=$(jq -r .app_key .claude/hooks/aptabase.json)
HOST=$(jq -r .aptabase_host .claude/hooks/aptabase.json)
curl -i -X POST "$HOST/api/v0/event" \
  -H "Content-Type: application/json" \
  -H "App-Key: $APP_KEY" \
  -H "User-Agent: ClaudeCodeHook/test" \
  -d '{
    "timestamp":"2026-04-07T00:00:00.000Z",
    "sessionId":"manual-test",
    "eventName":"claude_commit",
    "systemProps":{"osName":"Test","sdkVersion":"manual"},
    "props":{"commit_message":"manual test","total_tokens":1}
  }'

HTTP/1.1 200 + {} 이면 성공.

2. Stop 훅 누적 확인

Claude 와 대화 후:

cat .claude/state/aptabase-accum.json

accum.input_tokens, output_tokens 등이 0 이 아니면 정상 동작.

3. 커밋 훅 수동 실행

bash .claude/hooks/aptabase-commit.sh < /dev/null

Aptabase 대시보드에서 이벤트 확인 → accum 이 0 으로 리셋되면 성공.

4. E2E 테스트

# 1. Claude 와 대화 → 토큰 누적
# 2. cat .claude/state/aptabase-accum.json  (accum 비어있지 않음 확인)
git commit --allow-empty -m "test: aptabase hook #999"
# 3. Aptabase 대시보드에서 claude_commit 이벤트 확인
# 4. cat .claude/state/aptabase-accum.json  (accum 리셋 확인)

누적 상태 파일

<git-root>/.claude/state/aptabase-accum.json:

{
  "accum": {
    "input_tokens": 1234,
    "cache_creation_tokens": 5678,
    "cache_read_tokens": 9012,
    "output_tokens": 345
  },
  "offsets": {
    "C:\\Users\\...\\projects\\d--foo\\session-uuid.jsonl": 45678
  },
  "last_sent_commit": "abc123def..."
}
  • accum: 현재 누적 중인 토큰 (커밋 시 리셋됨)
  • offsets: 트랜스크립트 JSONL 별로 이미 읽은 byte 위치 (append-only 특성 덕에 중복 집계 방지)
  • last_sent_commit: 마지막으로 Aptabase 에 전송한 HEAD 해시 (중복 전송 방지)

수동 리셋: rm .claude/state/aptabase-accum.json


엣지 케이스

상황 동작
첫 설치 후 첫 Bash 호출 last_sent_commit 가 빈 상태 → 현재 HEAD 를 기준점으로 기록만. 전송 안 함
네트워크 실패 state 유지. 다음 호출에서 재시도 (누적값 + 새 커밋 병합)
Claude 가 Bash 로 커밋 PostToolUse(Bash).git/hooks/post-commit 둘 다 발동. last_sent_commit 덕에 한 번만 전송
외부(IDE)에서 커밋 .git/hooks/post-commit 만 발동
git 저장소 밖에서 실행 git rev-parse HEAD 실패 → 조용히 종료
OAuth 로그인 안 됨 (API 키) claude_oauth_id = "anonymous", plan = "apikey"
amend, rebase, cherry-pick HEAD 해시가 바뀌므로 모두 포착

트러블슈팅

누적이 안 쌓임 (accum 이 계속 0)

  1. aptabase.jsonenabled: true
  2. app_key, aptabase_host 실제 값인지
  3. settings.json 의 Stop 훅에 aptabase-accumulate.sh 등록되어 있는지
  4. transcript_path 가 훅 입력 JSON 에 실제로 있는지 (Claude Code 버전 확인)
  5. 해당 트랜스크립트 파일 읽기 권한

커밋했는데 Aptabase 에 안 뜸

  1. settings.json 의 PostToolUse(Bash) 훅 등록 확인
  2. .git/hooks/post-commit 이 실제로 있는지: cat .git/hooks/post-commit
  3. last_sent_commit 이 이미 현재 HEAD 와 같은지 (이미 전송됨)
  4. curl 로 직접 POST 했을 때 200 이 오는지 (네트워크/인증 분리 검증)
  5. python3 --version 동작
  6. git 저장소 안에서 실행 중인지

첫 커밋이 무시됨 의도된 동작. 첫 실행 시 last_sent_commit 를 현재 HEAD 로 기록하고 종료. 다음 커밋부터 전송.

전송 기준점 초기화 (디버깅용)

jq '.last_sent_commit = ""' .claude/state/aptabase-accum.json > /tmp/_s && mv /tmp/_s .claude/state/aptabase-accum.json

일시 비활성 aptabase.jsonenabledfalse 로. 훅 등록은 유지해도 된다.


제거

# 1. git post-commit 훅 제거
bash .claude/hooks/install-git-hook.sh --uninstall

# 2. settings.json 에서 Stop / PostToolUse(Bash) 훅 엔트리 제거

# 3. 파일 삭제 (선택)
rm -rf .claude/hooks .claude/state/aptabase-accum.json