Guide
Every change an agent ever made to one file
Git blame tells you a line changed and by whom. It doesn’t tell you the conversation that changed it — the ask, the failed attempt, the reasoning. That’s in the session logs.
The question git can’t answer
Agent-written code has a strange provenance problem. The commit says you changed
useWebSocket.ts — but what actually happened is a conversation: you asked for a fix,
the agent read three files, tried an edit, hit a failing test, and tried again. When the same file
misbehaves two weeks later, the valuable context isn’t the diff (git has that) — it’s
why that diff: the constraint you stated, the approach that was rejected, the error that
forced the retry. All recorded, none of it in git — and usually scattered across several sessions,
sometimes across two agents working the same repo.
The manual way
Edits are tool_use records whose input carries a file_path, so:
# which session files mention the path at all
grep -rl "useWebSocket.ts" ~/.claude/projects/
# just the Edit/Write calls touching it, with timestamps
jq -r 'select(.message.content | type=="array")
| .timestamp as $ts | .message.content[]
| select(.type=="tool_use" and (.name=="Edit" or .name=="Write" or .name=="MultiEdit"))
| select(.input.file_path | test("useWebSocket")? )
| [$ts, .name, .input.file_path] | @tsv' \
~/.claude/projects/-Users-you-dev-webapp/*.jsonl | sort
That gets you a timeline of touches. What it doesn’t get you: the content of each
change (that’s in old_string/new_string, awkward to render from a
shell), whether the edit succeeded (the result is a separate tool_result line
you must join by tool_use_id), which edits belong to one conversation versus a resumed
copy of it, and any of the surrounding discussion. Ten minutes of jq per question is the realistic
price — workable once, unworkable as a habit. (Record anatomy, if you want to build the join:
the JSONL guide.)
What the indexed version looks like
With the history in a database, “one file’s story” becomes a screen: pick a path,
see every session that ever touched it — newest first, across agents — and expand each into its
edits as rendered diffs, in order, with a jump back into the full conversation at that
exact moment. The reverse filter also starts earning: “only files touched by sessions
matching reconnect” — or, from the search side,
path:useWebSocket.ts is:error for every failed edit to one file, ever.
npx turnlog demo.)The indexed way
Turnlog calls this the Files screen
Turnlog — free, MIT, 100% local — indexes your Claude Code and Codex
history and keeps the file→edits relation queryable: the Files screen for browsing,
path: in the search language, file_history as an MCP tool so
your agent can ask before it touches a file
with a past. Git blame, but for agent edits — with the conversation attached.
Related: searching your history · agent memory over MCP · the record format