8.4 KiB
name, description
| name | description |
|---|---|
| aptabase | Aptabase 커밋 기준 토큰 기록 훅을 설치/검증/디버깅한다. 사용자가 "aptabase 설정", "aptabase 테스트", "토큰 기록 확인", "누적 토큰"을 요청할 때 사용. |
Aptabase 커밋 기준 토큰 기록
Claude 의 모든 응답 토큰을 프로젝트 단위로 누적하고, git 커밋이 발생하면 Aptabase 로 한 번에 flush 한 뒤 0 으로 리셋한다.
구조
.claude/hooks/
├── aptabase.json # 설정 (app_key, aptabase_host, user_name)
├── aptabase_common.py # 공통 유틸 (Python)
├── aptabase-accumulate.sh # Stop 훅 진입점
├── aptabase-accumulate.py # Stop 로직 — 토큰 누적만
├── aptabase-commit.sh # 커밋 flush 진입점 (Claude 훅 + git 훅 공용)
├── aptabase-commit.py # 커밋 감지 + Aptabase POST + 리셋
└── install-git-hook.sh # .git/hooks/post-commit 설치 스크립트
.claude/state/
└── aptabase-accum.json # 누적 상태 (자동 생성, git root 기준)
.git/hooks/
└── post-commit # install-git-hook.sh 가 설치 (모든 커밋 포착)
동작 방식
| 시점 | 훅 | 하는 일 |
|---|---|---|
| 응답 완료 | Stop → aptabase-accumulate.sh |
트랜스크립트 새 영역 파싱 → state.accum 에 토큰 더하기 (네트워크 없음) |
| Claude 의 Bash 툴 실행 후 | PostToolUse(Bash) → aptabase-commit.sh |
HEAD 변화 감지. 바뀌었으면 flush → POST → 리셋 |
| 모든 커밋 (IDE / 터미널 / GUI) | .git/hooks/post-commit → aptabase-commit.sh |
같은 로직. Claude 밖에서 이루어진 커밋도 포착 |
aptabase-commit.py 는 stdin 에 JSON 이 있으면 Claude 훅 모드, 비어 있으면 git 훅 모드로 동작한다. 두 경로가 중복 실행돼도 last_sent_commit 비교 덕에 이중 전송되지 않는다.
- 누적 범위:
<git-root>/.claude/state/aptabase-accum.json— 프로젝트(= git 저장소)마다 독립 - 커밋 감지: HEAD 해시 변화 기반 —
git commit,--amend,merge,cherry-pick,rebase모두 감지 - 첫 실행: 기존 HEAD 를
last_sent_commit에 기준점으로 기록만 하고 전송 안 함 (이전 작업 토큰이 0이라) - 전송 실패:
state유지 → 다음 Bash 호출에서 재시도 (누적값 + 새 커밋 병합)
1. 설정
.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 으로 전송할 사용자 식별자 |
2. settings.json 에 훅 등록
.claude/settings.json 의 hooks 에 두 항목 모두 등록:
{
"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" }
]
}
]
}
}
이미 다른 Stop / PostToolUse 훅이 있으면 같은 hooks 배열에 추가한다.
2-1. git post-commit 훅 설치 (필수)
Claude 외부에서 이루어진 커밋(IDE, 터미널, git GUI 등)도 포착하려면 .git/hooks/post-commit 을 설치해야 한다. 프로젝트 루트에서 한 번만 실행:
bash .claude/hooks/install-git-hook.sh
이미 .git/hooks/post-commit 이 존재하면 에러가 나면서 병합 방법을 안내한다. 기존 훅을 덮어쓰려면:
bash .claude/hooks/install-git-hook.sh --force # 기존 훅을 .bak 로 백업하고 덮어쓰기
제거:
bash .claude/hooks/install-git-hook.sh --uninstall
.git/hooks/는 버전 관리되지 않으므로 clone 할 때마다 재설치 필요하다. CI/자동 설정 스크립트에서 프로젝트 초기화 시 함께 실행하는 것을 권장한다.
3. 전송되는 payload
Aptabase 이벤트 이름은 claude_commit (고정).
systemProps
osName,osVersion,locale,appVersion,sdkVersion,isDebug
props
| 필드 | 출처 |
|---|---|
claude_oauth_id |
~/.claude/config.json 의 oauthAccount.emailAddress (없으면 anonymous) |
plan |
oauthAccount.subscriptionType (max/pro/team/enterprise) — API 키면 apikey |
user_name |
aptabase.json 의 user_name |
local_ip |
UDP connect 트릭으로 추출 (패킷 미전송) |
public_ip |
api.ipify.org / ifconfig.me (2초 타임아웃) |
commit_hash |
git rev-parse HEAD |
commit_message |
git log -1 --pretty=%B |
issue_number |
커밋 메시지에서 regex 추출 (FEAT-123, #123, fixes #45 등). 없으면 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 |
누적 세부 |
Aptabase 최상위
sessionId는 epoch+random 으로 매 이벤트 새로 생성된다. Claude 세션 UUID 는 사용하지 않는다.
4. 연결 테스트
단계 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}
}'
200 + {} 이면 Aptabase 수신 OK.
단계 2 — 훅 직접 실행 (Stop 누적):
echo '{"transcript_path":"","cwd":"'"$PWD"'","hook_event_name":"Stop"}' \
| bash .claude/hooks/aptabase-accumulate.sh
cat .claude/state/aptabase-accum.json
transcript_path 가 비어있으면 accum 은 0 그대로지만, state 파일은 생성돼야 한다.
단계 3 — 실제 동작 확인:
- Claude 와 대화해서 토큰 쌓기
cat .claude/state/aptabase-accum.json으로 누적 확인git commit실행- Aptabase 대시보드에서
claude_commit이벤트 확인 cat .claude/state/aptabase-accum.json—accum이 0 으로 리셋,last_sent_commit갱신됐는지 확인
5. 누적 상태 파일 포맷
<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..."
}
offsets: 트랜스크립트 JSONL 파일별로 이미 읽은 byte 위치. append-only 특성 덕에 중복 집계 방지- 수동 리셋이 필요하면
rm .claude/state/aptabase-accum.json
6. 트러블슈팅
누적이 안 쌓임 (accum 이 계속 0):
aptabase.json의enabled: trueapp_key,aptabase_host실제 값- settings.json 의 Stop 훅에
aptabase-accumulate.sh등록 transcript_path가 훅 입력 JSON 에 실제로 있는지- 해당 파일 읽기 권한
커밋했는데 Aptabase 에 안 뜸:
- settings.json 의 PostToolUse(Bash) 훅에
aptabase-commit.sh등록 last_sent_commit가 이미 현재 HEAD 와 같은지 (이미 전송된 상태)- curl 로 직접 POST 했을 때 200 이 오는지 (네트워크/인증 분리 검증)
python3 --version동작- git 저장소 안에서 동작 중인지 (
git rev-parse HEAD성공해야 함)
첫 커밋이 무시됨:
의도된 동작. last_sent_commit 가 빈 상태일 때는 현재 HEAD 를 기준점으로 기록만 하고 전송하지 않는다. 다음 커밋부터 전송된다.
전송 기준점 초기화:
jq '.last_sent_commit = ""' .claude/state/aptabase-accum.json > /tmp/_s && mv /tmp/_s .claude/state/aptabase-accum.json
일시 비활성:
aptabase.json 에서 enabled: false. 훅 등록은 유지해도 된다.