Claude Code の Token 消費をプロジェクト別に Recorder に蓄積する (Stop hook)
個人のエージェント・サブスクリプションで開発を進めると、各開発でどれだけトークンを消費しているかは意外と見えにくいもの。Claude Code の Stop hook を使い、開発(プロジェクト)ごとの Token 消費量を Recorder で見える化する手順です。
このレシピで実現できること
個人のエージェント・サブスクリプションで開発を進めるとき、それぞれの開発でどの程度のトークンを消費しているかは、意外と把握しづらいものです。Claude Code のセッション transcript はローカルにしか残らず、Anthropic Console でも API key 単位の合計しか見えません。
このレシピでは Claude Code の Stop hook を使い、開発(起動 dir =プロジェクト)ごとの Token 消費量を Recorder に蓄積して見える化します。session ごとに次の 4 つを任意の <MyPrefix> 配下に記録します。
<MyPrefix>/Input— 入力 token (素の prompt)<MyPrefix>/Output— 出力 token<MyPrefix>/CacheRead— prompt cache から再利用された token<MyPrefix>/CacheCreate— prompt cache に書き込んだ token
<MyPrefix> は Claude Code を起動する dir 1 つに対して 1 つ命名 します。System/Code/<app>/Claude/Tokens の形に揃えておくと、DORA レシピ で蓄積する Lines や Deploy 回数と同じツリーに並び、Recorder 上で「LOC 増加 vs Token 消費」「Deploy 回数 vs Token 消費」を 1 画面に重ねられます。これは Anthropic Console (API key 単位の Usage) では出せない切り口です。
注:
Input/Output/CacheRead/CacheCreateの 4 値は、Claude API レスポンスのusageオブジェクト (input_tokens/output_tokens/cache_read_input_tokens/cache_creation_input_tokens) をそのまま転記したものです。各フィールドの正確な定義 — とりわけinput_tokensが「最後の cache breakpoint より後ろの、cache に乗らなかった入力」だけを指すこと — はオリジナルの Anthropic Prompt caching ドキュメント を参照してください。サンプル出力でInputが小さく出るのはこの定義によるものです。
前提
- Claude Code がローカルにインストール済み
- Recorder のアカウント
jq(集計に使用)
なぜ hook で送るのか
Claude Code のセッション transcript は ローカルにしか存在しません (~/.claude/projects/<encoded-cwd>/<session-id>.jsonl)。各行に message.usage = {input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens} がタイムスタンプ付きで残っているので、ローカルから直接読んで Recorder に流すのが自然です。GitHub Actions の cron では到達できないデータ源です。
セットアップ手順
1. Recorder の PAT を発行
Settings → API Tokens で発行 → 文字列をその場でコピー → ~/.zshrc 等で export RECORDER_PAT="..." しておきます。
2. 集計対象の dir を事前確認する
Claude Code は launch cwd を / → - で置換して project dir を作ります。集計したい dir で 1 回 Claude Code を起動すると、~/.claude/projects/ の下に対応する dir が作られます。ls して basename を控えます:
ls ~/.claude/projects/
# 例: -Users-me-code-myapps ← この行を控える3. Stop hook script を配置し、対象 dir と metric prefix を反映する
~/bin/post_claude_tokens.sh を作成。case の 2 行だけ自分の値に書き換えれば、残りはそのままで動きます。
#!/usr/bin/env bash
set -euo pipefail
: "${RECORDER_PAT:?env RECORDER_PAT is not set}"
PAYLOAD=$(cat)
# PAYLOAD は生の制御文字を含み厳密な JSON でない場合があるため、jq に通さず
# transcript_path (値はパスなので " を含まない) だけ文字列抽出する
TRANSCRIPT=$(printf '%s' "$PAYLOAD" | grep -o '"transcript_path":"[^"]*"' | head -1 | sed 's/.*:"//; s/"$//')
PROJECT_BASENAME=$(basename "$(dirname "$TRANSCRIPT")")
# ─── ⬇ ここの 2 行だけ自分の環境に書き換える ⬇ ───
case "$PROJECT_BASENAME" in
-Users-me-code-myapps) # ← 手順 2 で確認した dir basename
METRIC_PREFIX="System/Code/myapps/Claude/Tokens" ;; # ← Recorder 上で使う prefix
*)
exit 0 ;;
esac
# ─── ⬆ ここまで ⬆ ───
read -r INPUT OUTPUT CREAD CCREATE <<<"$(jq -R -s -r '
[ split("\n")[]
| select(length > 0)
| (fromjson? // empty) # 制御文字混じりで壊れた行はスキップ
| .message.usage // empty
]
| reduce .[] as $u (
{input:0, output:0, cread:0, ccreate:0};
.input += ($u.input_tokens // 0)
| .output += ($u.output_tokens // 0)
| .cread += ($u.cache_read_input_tokens // 0)
| .ccreate += ($u.cache_creation_input_tokens // 0)
)
| "\(.input) \(.output) \(.cread) \(.ccreate)"
' "$TRANSCRIPT")"
post() {
local name=$1 value=$2
curl -sS -X POST https://api.recorder.nasebanal.com/api/v1/records \
-H "Authorization: Bearer $RECORDER_PAT" \
-H "Content-Type: application/json" \
-d "{\"name\":\"$name\",\"unit\":\"tokens\",\"data_type\":\"integer\",\"chart_type\":\"line\",\"value\":$value}"
}
post "$METRIC_PREFIX/Input" "$INPUT"
post "$METRIC_PREFIX/Output" "$OUTPUT"
post "$METRIC_PREFIX/CacheRead" "$CREAD"
post "$METRIC_PREFIX/CacheCreate" "$CCREATE"実行権限を付けます:
chmod +x ~/bin/post_claude_tokens.sh集計対象 dir を複数にしたい場合は case 文に arm を追加するだけです。
4. Claude Code の settings に Stop hook を登録
~/.claude/settings.json:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "bash ~/bin/post_claude_tokens.sh"
}
]
}
]
}
}5. 動作確認
手順 2 で控えた dir で 1 session 動かして終了させ、Recorder の「タグ一覧」に <MyPrefix>/{Input,Output,CacheRead,CacheCreate} が現れていれば成功です。
サンプル出力
数 session 動かすと、4 つの token 種別が Recorder 上で 1 つのツリーに並びます。System/Code/nasebanal/Claude/Tokens の実データを、桁の違う 2 つの軸に分けて見るとこうなります。
まず素の Input / Output (cache を経由しない token)。Output (緑) が数十万 token 規模まで伸びる一方、Input (赤) はほぼベースラインに張り付いています。入力の大半は prompt cache 側に回るため、生の入力 token は出力よりむしろ小さく出ます。

次に cache 側。CacheRead (緑) は数千万 token 規模まで達し、CacheCreate (赤) は相対的に小さく収まります。スケールが I/O 側の約 100 倍違うので別チャートにしています。コンテキストの大半が prompt cache から再利用されている = 実際に課金対象となる素の入力 token が小さく抑えられている、ということが読み取れます。

これは Anthropic Console の API key 単位 Usage では出せない、dir / アプリ別の内訳です。
⚠️ シークレットの取扱い
RECORDER_PATは~/.zshrc等でexportするだけにして、script に直書きしないでください- 漏洩が疑われたら Recorder Settings → API Tokens で失効 → 新規発行 → 環境変数を上書きしてください
- Stop hook は すべての Claude Code session で発火します。
caseにない project は*) exit 0でサイレントに弾く設計なので、未追跡の作業内容が Recorder に紛れ込むことはありません