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.

Updated August 2026

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.

Turnlog's file history: useWebSocket.ts selected from three touched files, showing the session that edited it with the colored diff and a view-in-session jump
One path, every session that touched it, diffs in order — with the jump back into context. (Sample sessions via 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