whyso — git blame が見せないもの

git blame は誰が、いつ、何を変えたかを見せる。whyso はなぜ変えたかを見せる。


問題

コードの一行がなぜこうなっているのか知りたいとき、できることは git blame とコミットメッセージを見ることだ。

$ git blame internal/handler/page.go
a3f1b2c (parkjunwoo 2026-03-08) func CreatePage(c *gin.Context) {

誰が、いつ、何を変えたかは出てくる。なぜ変えたかは出てこない。

コミットメッセージに理由を書けばいいのでは?現実はこうだ:

fix: update handler
refactor: clean up
wip

良いコミットメッセージを書くのは規律の問題だ。チームのコンベンションを決めても、深夜3時にバグを直しながら「この変更の文脈と根拠をコミットメッセージに丁寧に記録しよう」と思う人は少ない。

しかし、AIコーディングの時代には状況が違う。変更の理由はすでに記録されている。


Claude Code のセッションデータ

Claude Code はすべての会話を ~/.claude/projects/ 以下に JSONL ファイルとして保存する。各レコードにはユーザーメッセージ、AI の応答、tool_use(Write、Edit、Bash)の呼び出し、そしてこれらをつなぐ parentUuid チェーンが含まれる。

ユーザー: "whyso validate コマンドの実装を始めて"
  → AI: Write internal/crosscheck/crosscheck.go
    → AI: Edit internal/crosscheck/crosscheck.go
      → AI: "crosscheck パッケージの初期生成 — SSaC↔OpenAPI operationId マッチング検証"

ファイルがなぜ生まれ、なぜ修正されたかが会話そのものに残っている。コミットメッセージより豊富で、意図的に書く必要もない。会話をすれば自動的に記録される。

whyso はこのデータをパースしてファイルごとの変更履歴を抽出する。


ファイルの生涯が見える

file: internal/crosscheck/crosscheck.go
created: 2026-03-08T14:23:01Z
history:
  - timestamp: 2026-03-08T14:23:01Z
    session: 09351222-d7be-41fe-994f-87c2d7067e5d
    user_request: "whyso validate コマンドの実装を始めて"
    answer: "crosscheck パッケージの初期生成 — SSaC↔OpenAPI operationId マッチング検証"
    tool: Write

  - timestamp: 2026-03-09T10:15:33Z
    session: 4e9b4e5e-3a50-43f2-be6e-e5db228ecc3b
    user_request: "x-sort カラムが DDL に存在するか検証を追加して"
    answer: "x-sort/x-filter カラム → DDL クロス検証を追加"
    tool: Edit

  - timestamp: 2026-03-10T09:41:22Z
    session: b2e43b4f-cb21-4286-975d-1eb9de8a16c0
    user_request: "Func スペックのクロス検証も追加して"
    answer: "@call ↔ Func spec 引数の数・型のクロス検証を追加"
    tool: Edit

なぜ作られ、どんなリクエストでどう進化したか。ファイル一つの全生涯が見える。


なぜこれが必要か

コードレビューの空白

PR をレビューするとき「この変更はなぜ?」という疑問が生まれる瞬間がある。コミットメッセージに理由がなければ、作者に聞かなければならない。作者が覚えていなければ、コードを読み返して推論するしかない。

AIコーディング環境では、この問いの答えがセッションデータにある。whyso はその答えを取り出してくれる。

オンボーディング

新しく加わったチームメンバーがコードベースを理解する最速の方法は、「このコードがなぜこうなったか」を知ることだ。git log は変更の時系列の並びで、ドキュメントは現在の状態のスナップショットだ。whyso のファイルごとの履歴はその間——意図の連鎖——を見せてくれる。

自己記録

一人で開発していても、3ヶ月前になぜこう書いたか思い出せない瞬間が来る。whyso は過去の自分とAIの間の会話を復元してくれる。コミットメッセージに書かなかった文脈がそこにある。


git blame との関係

whyso は git blame を置き換えない。補完する。

git blamewhyso
誰がO
いつOO
何を変えたかO(行単位)O(tool_use 単位)
なぜ変えたか△(コミットメッセージ)O(ユーザーリクエスト + AI の説明)
データソースgit 履歴Claude Code セッション

git blame がコミットベースの「公式記録」なら、whyso は会話ベースの「作業日誌」だ。公式記録に書けなかった文脈が作業日誌に残っている。


AI が自分の記録を読む構造

whyso で最も興味深いユースケースは、人間ではなくAI 自身が読むことだ。

Claude Code はセッションが終わるとすべてを忘れる。次のセッションで同じプロジェクトを開くと、昨日なぜそう書いたか、どんな選択肢を検討して捨てたか、ユーザーがどんな文脈でリクエストしたか——すべてない。コードを読んで推論するだけだ。

CLAUDE.md に一行追加することを想像してみよう:

ファイル修正前に必ず `whyso history <file>` を実行。履歴があれば読んでから修正。

この一行が変えるもの:

戻してはいけない変更を知る

ファイルを修正しようと読んでいたら、何かおかしく見えるコードがある。現在の状態だけ見ると「ミスじゃないか?」と直したくなる。しかし whyso の履歴に「ユーザーが明示的にこの構造をリクエストした。理由はパフォーマンスではなく可読性のため」と書いてあれば、触れない。

捨てた選択肢を再提案しない

前のセッションで「A の方法でやってみようか?」→「いや、B で行こう」という会話があったのに、新しいセッションの AI はそれを知らない。だからまた A を提案してしまう。ユーザーにとって苛立たしいことだ。whyso があれば、すでに検討して却下した方向を知っている。

連続した作業の文脈をつなぐ

「昨日のリファクタリングを続けて」と言われると、今は diff を見て推論しなければならない。whyso の履歴があれば、昨日のセッションのユーザーリクエストと AI の判断根拠をすぐに読める。推論ではなく事実に基づいた連続作業になる。

記録がセッションを超えて積み重なる

この構造になると、whyso の履歴がセッションを重ねるほど積み上がる。今のセッションで修正した理由も次のセッションの AI に伝わる。セッション間の記憶がない問題がファイル単位で解決される。別途のメモリシステムなしでも。

whyso に記録されているのは AI がやったことだ。しかし AI はそれを覚えていない。自分が作った記録が自分に最も必要な状況。 これが whyso が AI コーディングツールと組み合わさったときの独特な点だ。

人間にとって whyso は「良いコミットメッセージを自動的に残してくれるツール」だ。AI にとって whyso はセッションを超えた記憶だ。


使い方

インストール

go install github.com/park-jun-woo/whyso/cmd/whyso@latest

ファイル履歴の確認

# 単一ファイル
whyso history README.md

# ディレクトリ全体
whyso history internal/ --all

# ファイル構造をミラーリングして出力
whyso history . --all --output .file-histories/

# JSON 形式
whyso history README.md --format json

セッション一覧

whyso sessions

動作の仕組み

parentUuid チェーンの逆追跡

JSONL のすべてのレコードは uuidparentUuid でつながっている。AI がファイルを修正した tool_use から parentUuid チェーンを遡ると、その修正を引き起こしたユーザーの元のリクエストに到達する。

user message (uuid: A)          ← "x-sort カラムが DDL に存在するか検証を追加して"
  → assistant tool_use (parentUuid: A)
    → tool_result (parentUuid: B)
      → assistant Edit (parentUuid: C)  ← この修正の理由 = A

途中に tool_result タイプのメッセージが挟まっているため、type == "user" かつ content が文字列のものだけを抽出すれば、本当のユーザーリクエストを見つけられる。

設計上の決定

Write/Edit だけを追跡する。 Claude Code の Write と Edit tool_use はファイルパスと変更内容を明示的に含む。Bash コマンドのファイル操作(rmmvcp)はヒューリスティックで検出するが、Write/Edit が主な追跡対象だ。Claude Code はファイル修正時に Write/Edit を使うよう設計されているため、この二つのツールだけでほとんどの変更を捉えられる。

サブエージェントも含む。 Claude Code が複雑なタスクを処理する際にサブエージェントを生成することがある。サブエージェントのセッションは親セッションディレクトリ以下に保存される。whyso はこのサブエージェントセッションもパースして完全な履歴を構築する。

差分更新。 --output オプションでディレクトリに出力する際、すでにパース済みのセッションはスキップする。セッションが数百個に増えても、毎回全体を再パースしない。

数値

whyso プロジェクト自体の開発過程を whyso で追跡した結果:

項目数値
セッション数17
Write 呼び出し660
Edit 呼び出し1,415
Bash ファイル操作206
変更されたユニークファイル数480

17 セッションで 480 ファイルの変更履歴が抽出された。各ファイルに「なぜ作られ、なぜ変わったか」が記録されている。


限界

  • Claude Code 専用だ。 他の AI コーディングツールのセッションデータは形式が異なる。現在は Claude Code の JSONL 形式のみ対応している。
  • テキストファイルだけを追跡する。 Write/Edit tool_use はテキストファイルを対象とする。画像、バイナリファイルの変更は追跡しない。
  • セッションデータがローカルにある必要がある。 ~/.claude/projects/ にセッションファイルがあることが前提だ。削除した場合や別のマシンで作業した場合、その記録は存在しない。

まとめ

AI とともにコードを書く時代に、変更の理由はもはやコミットメッセージだけに頼る必要はない。会話そのものが記録であり、その記録からファイルごとの変更履歴を自動的に抽出できる。

git blame が「誰がいつ何を変えたか」を見せるなら、whyso は「なぜ変えたか」を見せる。

コード: github.com/park-jun-woo/whyso