Claude Codeの会話ログはどこにある?開いて中身を数えてみた(Windows)

先週やったはずの修正を、どのプロジェクトのどのタイミングでやったのか思い出せなくなった。ターミナルは閉じてしまっているし、gitのコミットには「なぜそう直したか」が残っていない。あのときAIと何を話して、どういう理由でその実装に落ち着いたのかを確認したかった。

Claude Codeは会話を全部ディスクに書いている。--resume で過去のセッションを一覧できるのは知っていたが、あれは選択画面であって中身を読むものではない。実体のファイルを直接開いてみたら、想像よりずっと生々しいものが入っていた。

この記事は、その置き場所と中身の話。Windows環境で、実際に117セッション・505MBぶんを開いて数えた結果を書く。先に全体像を出しておく。

セッションログの置き場所と、1ファイルの中身

置き場所は .claude\projects、ただし環境変数で動く

既定では、ホームディレクトリ直下のここにある。

%USERPROFILE%\.claude\projects\

Git Bashなら ~/.claude/projects/、PowerShellなら $env:USERPROFILE\.claude\projects で届く。

ここで最初につまずいた。私の環境ではこのフォルダの中身が妙に少なく、最近やった作業がほとんど入っていなかった。原因は環境変数だった。

> $env:CLAUDE_CONFIG_DIR
C:\Users\<ユーザー名>\.claude-acc2

CLAUDE_CONFIG_DIR が設定されていると、設定もログもまるごとそちらへ移る。私はアカウントを複数使い分けるために切り替えていたので、.claude.claude-acc2 の2か所にログが分かれていた。同じような使い方をしている人は、片方だけ見て「ログが無い」と判断しないほうがいい。

まず全部の置き場所を出すなら、これで足りる。

Get-ChildItem $env:USERPROFILE -Directory -Filter ".claude*" |
  ForEach-Object { Join-Path $_.FullName "projects" } |
  Where-Object { Test-Path $_ }

フォルダ名は「作業ディレクトリのパスを潰したもの」

projects の下は、プロジェクトごとのフォルダに分かれている。名前が独特で、最初は暗号に見えた。

C--Users-<ユーザー名>-Documents-kabe-tech
C--Users-<ユーザー名>-Documents-AIWorks-skeleton-check
C--Users-<ユーザー名>--claude

規則は単純で、Claude Codeを起動したときの作業ディレクトリの絶対パスから、区切り文字をぜんぶ - に置き換えたものになっている。C:\ の部分はコロンとバックスラッシュがそれぞれ潰れるので C-- になる。3つめの例が --claude と2つ続いているのは、元が C:\Users\<ユーザー名>\.claude で、先頭のドットも - に化けているから。

つまり同じリポジトリでも、起動した場所が違えば別フォルダに記録される。私は一度これで「ログが消えた」と焦ったことがあって、実際にはサブディレクトリから起動したぶんが別の名前で並んでいただけだった。

フォルダの中身は、セッション1本につき1ファイル。ファイル名はセッションのUUIDで、拡張子は .jsonl

8a0a492d-cff4-48f5-9efa-9a62c1f9aab6.jsonl

中身はJSON Lines。1行が1イベント

.jsonl は「1行に1つのJSONが入っているテキストファイル」という素朴な形式。普通のJSONと違って全体を読み込まなくても1行ずつ処理できるので、あとで集計するときに助かる。

1行を取り出して整形するとこうなる(値は短く刈ってある)。

{
  "type": "user",
  "message": { "role": "user", "content": "..." },
  "uuid": "e938cd80-...",
  "parentUuid": null,
  "timestamp": "2026-07-04T07:54:06.207Z",
  "cwd": "C:\\Users\\<ユーザー名>\\Documents\\kabe-tech",
  "sessionId": "8a0a492d-...",
  "gitBranch": "main",
  "version": "2.1.200"
}

cwdgitBranch が毎行入っているのは、後から見るときにかなりありがたい。「この会話はどのブランチでやっていたのか」が分かる。

type にはいろいろな値が入る。手元でいちばん大きかった1ファイル(50.5MB)の内訳を数えると、こうなっていた。

type 件数 何のイベントか
assistant 980 AIの応答。ツール呼び出しもここに入る
user 480 ユーザーの発言と、ツールの実行結果
last-prompt 134 直近プロンプトの記録
mode / ai-title 133 / 133 モード切替と自動タイトル
permission-mode 130 権限モードの変化
system 98 システム側の通知
attachment 77 添付・参照ファイル
file-history-snapshot 54 編集前ファイルの退避
queue-operation 26 キュー操作

読むときに引っかかるのが user の扱いで、ここには人間の発言だけでなくツールの実行結果も入っている。ロールとしては user だが、中身は tool_result ブロックの配列になっている。人間が何を言ったかを数えたいときは、ここを弾かないと桁が変わる。

私の全ログでは、user タイプの10,713件のうち9,395件(88%)がツールの実行結果だった。純粋な人間の発言は814件しかない。

1セッション平均4.3MB、最大は50MB

サイズも数えた。117セッションで505MB、1セッションあたりの平均が4.3MB。最大のものは1ファイルで50.5MBあった。

テキストしか入っていないのにこの大きさになるのは、ツールの実行結果がまるごと保存されているから。ファイルを1本読めばその全文が、コマンドを1回叩けばその標準出力が、そのまま行として積まれていく。会話の見た目の長さと、ファイルの大きさは全然比例しない。

裏を返すと、ログには読んだファイルの中身がそのまま残っている。設定ファイルを開いた回があれば、その内容も入っている。この記事を書くにあたって手元のログを集計したが、公開するのは件数と割合だけにして、生の行は一切外に出していない。ログを誰かに見せる予定があるなら、ここは本当に気をつけたほうがいい。

目当てのセッションを探す

日付とプロジェクトで絞るだけなら、ファイルの更新時刻で十分だった。

Get-ChildItem "$env:CLAUDE_CONFIG_DIR\projects" -Recurse -Filter *.jsonl |
  Sort-Object LastWriteTime -Descending |
  Select-Object -First 10 @{n='日時';e={$_.LastWriteTime}},
                          @{n='MB';e={[math]::Round($_.Length/1MB,1)}},
                          @{n='プロジェクト';e={$_.Directory.Name}}

内容で探したいときは、typeuserisMeta が付いていない行だけを見るのが早い。スラッシュコマンドの実行やシステムからの注入もすべて user 型で入ってくるので、素直に全文検索すると自分が書いた覚えのない文字列が大量に引っかかる。

この形式に寄りかかりすぎないほうがいい

ひとつ断っておくと、このファイル形式は公開仕様として保証されているものではない。各行に version が入っていること自体が、形が変わりうる前提を示している。実際、手元のログでも古いセッションと新しいセッションでフィールドが微妙に違う。

だから「ログを常時パースして何かを自動化する」という作り方は、私はおすすめしない。バージョンが上がった日に静かに壊れる。あくまで後から人間が見返すための記録、あるいは今回のように一度きりの集計に使う素材として扱うのが安全だと思う。

次にやること

置き場所と形式が分かると、次に気になるのは「で、実際どれだけAIに働かせていたのか」になる。117セッションを全部パースして、指示の数・ツールの実行回数・やり直しの頻度まで数えた結果は別の記事にまとめた。集計スクリプトもそちらに置いてある。

まずは自分のログのサイズを見るところからでいい。上のPowerShellを1行流すだけで、思っていたより長い時間をAIと過ごしていたことが分かるはずだ。