
git blametells you who changed what and when. whyso tells you why.
The problem
When you want to understand why a line of code looks the way it does, your options are git blame and the commit message.
$ git blame internal/handler/page.go
a3f1b2c (parkjunwoo 2026-03-08) func CreatePage(c *gin.Context) {
Who, when, what — all there. Why — nowhere to be found.
You could just write better commit messages, right? Here’s what they actually look like:
fix: update handler
refactor: clean up
wip
Writing good commit messages is a matter of discipline. No matter how strict your team conventions are, not many people think “I should carefully document the context and rationale for this change” while fixing a bug at 3am.
But in the age of AI coding, something is different. The reason for every change is already recorded.
Claude Code session data
Claude Code stores every conversation as JSONL files under ~/.claude/projects/. Each record contains the user message, the AI response, tool_use calls (Write, Edit, Bash), and the parentUuid chain that links them all together.
User: "Start implementing the whyso validate command"
→ AI: Write internal/crosscheck/crosscheck.go
→ AI: Edit internal/crosscheck/crosscheck.go
→ AI: "Initial crosscheck package — validates SSaC↔OpenAPI operationId matching"
Why a file was created, why it was modified — it’s all there in the conversation itself. Richer than any commit message, and you never have to write it intentionally. If you had the conversation, the record is automatic.
whyso parses this data and extracts a per-file change history.
A file’s entire life
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: "Start implementing the whyso validate command"
answer: "Initial crosscheck package — validates SSaC↔OpenAPI operationId matching"
tool: Write
- timestamp: 2026-03-09T10:15:33Z
session: 4e9b4e5e-3a50-43f2-be6e-e5db228ecc3b
user_request: "Add validation for x-sort columns existing in DDL"
answer: "Added x-sort/x-filter column → DDL cross-validation"
tool: Edit
- timestamp: 2026-03-10T09:41:22Z
session: b2e43b4f-cb21-4286-975d-1eb9de8a16c0
user_request: "Add Func spec cross-validation too"
answer: "Added @call ↔ Func spec argument count/type cross-validation"
tool: Edit
Why it was created, what requests drove its evolution. The complete life of a single file, laid out.
Why this matters
The missing piece in code review
There’s always that moment in a PR review when you ask yourself: “why was this changed?” If the commit message doesn’t say, you ask the author. If the author doesn’t remember, you re-read the code and try to reconstruct the reasoning.
In an AI coding environment, the answer to that question lives in the session data. whyso surfaces it.
Onboarding
The fastest way for a new team member to understand a codebase is to know why the code became what it is. git log is a chronological list of changes. Documentation is a snapshot of the current state. whyso’s per-file history fills the gap between them — the chain of intent.
Your own memory
Even when working solo, there comes a moment three months later when you have no idea why you wrote something that way. whyso restores the conversation between your past self and the AI. The context you never put in a commit message is right there.
How it relates to git blame
whyso doesn’t replace git blame. It complements it.
| git blame | whyso | |
|---|---|---|
| Who | Yes | — |
| When | Yes | Yes |
| What changed | Yes (line level) | Yes (tool_use level) |
| Why it changed | Partial (commit message) | Yes (user request + AI explanation) |
| Data source | git history | Claude Code sessions |
If git blame is the “official record” based on commits, whyso is the “work log” based on conversation. The context that never made it into the official record is in the work log.
When the AI reads its own history
The most interesting use case for whyso isn’t humans reading it — it’s the AI reading it.
Claude Code forgets everything when a session ends. Open the same project in a new session, and there’s no trace of why you wrote something yesterday, which options were considered and discarded, or what context was behind the user’s request. The AI reads the code and infers. That’s all it can do.
Now imagine adding one line to CLAUDE.md:
Always run `whyso history <file>` before editing a file. If history exists, read it first.
That one line changes everything:
It knows what not to undo
You’re about to edit a file and something in the code looks suspicious — like a mistake. On its own, the AI might want to “fix” it. But if whyso history says “user explicitly requested this structure, not for performance but for readability,” it leaves it alone.
It doesn’t re-propose rejected options
A previous session had the conversation: “should we go with approach A?” → “no, let’s do B.” A new session has no idea. So it proposes A again. Frustrating. With whyso, it knows which directions have already been evaluated and rejected.
It picks up where the last session left off
“Continue the refactoring from yesterday” currently means reading the diff and inferring. With whyso history, it reads the user’s intent and the AI’s reasoning from the previous session directly. Continuation based on facts, not inference.
The record compounds across sessions
Under this setup, whyso’s history accumulates with every session. The reason for a change made today gets passed to the AI in the next session. The problem of no memory between sessions gets solved at the file level — no separate memory system required.
What whyso records is what the AI did. But the AI doesn’t remember doing it. The record it created is exactly what it needs most. That’s the unique thing that happens when whyso meets AI coding tools.
For humans, whyso is “a tool that automatically leaves good commit messages.” For AI, whyso is memory across sessions.
Usage
Install
go install github.com/park-jun-woo/whyso/cmd/whyso@latest
File history
# Single file
whyso history README.md
# Entire directory
whyso history internal/ --all
# Mirror file structure to output directory
whyso history . --all --output .file-histories/
# JSON format
whyso history README.md --format json
Session list
whyso sessions
How it works
Tracing the parentUuid chain
Every record in the JSONL has a uuid and a parentUuid. Starting from a tool_use that modified a file, tracing the parentUuid chain backwards leads to the original user message that triggered the change.
user message (uuid: A) ← "Add validation for x-sort columns in DDL"
→ assistant tool_use (parentUuid: A)
→ tool_result (parentUuid: B)
→ assistant Edit (parentUuid: C) ← reason for this edit = A
Since tool_result messages are interspersed in the chain, filtering for records where type == "user" and content is a string surfaces the real user requests.
Design decisions
Only Write and Edit are tracked. Claude Code’s Write and Edit tool_use calls explicitly include the file path and the change. File manipulation via Bash commands (rm, mv, cp) is detected heuristically, but Write and Edit are the primary targets. Since Claude Code is designed to use Write and Edit for file modifications, these two tools capture the vast majority of changes.
Sub-agents are included. When Claude Code spawns sub-agents for complex tasks, their sessions are stored under the parent session directory. whyso parses these sub-agent sessions to build a complete history.
Incremental updates. When using --output to write to a directory, already-parsed sessions are skipped. Even if you accumulate hundreds of sessions, it doesn’t re-parse everything from scratch each time.
Numbers
Tracking whyso’s own development with whyso:
| Metric | Count |
|---|---|
| Sessions | 17 |
| Write calls | 660 |
| Edit calls | 1,415 |
| Bash file operations | 206 |
| Unique files changed | 480 |
17 sessions. Change history extracted for 480 files. Every file recording why it was created and why it changed.
Limitations
- Claude Code only. Other AI coding tools use different session formats. Currently only Claude Code’s JSONL format is supported.
- Text files only. Write and Edit tool_use calls target text files. Changes to images and binary files are not tracked.
- Session data must be local. Session files need to exist at
~/.claude/projects/. Work done on another machine, or sessions that have been deleted, won’t appear.
Summary
In the age of writing code with AI, the reason behind a change no longer has to live only in commit messages. The conversation itself is the record, and per-file change history can be extracted from it automatically.
git blame shows you who changed what and when. whyso shows you why.