ccRewind reads ~/.claude/projects/*.jsonl
, parses them, indexes them into a local SQLite database, and renders the result back as a searchable, browsable history. It never opens those source files for writing. Not a rename, not a metadata touch, not one byte.
That sentence is easy to state and easy to promise. It is worth explaining why the line is drawn exactly there, and what holding it actually costs.
The parsing rules that make ccRewind possible start from treating JSONL as a historical record: skip what you cannot read, preserve what you do not yet understand, never let your opinion of the data change the data. That principle has a natural extension. An archaeologist who reshapes what they dig up is not doing archaeology anymore. They are authoring a new site and calling it the old one.
The payoff is specific: because ccRewind never writes, a session file is exactly what it was on the day it was written, no matter how much time has passed or how many tools have looked at it since. That property has already paid for itself once. A subtle client-side bug caused certain tool calls to silently misbehave for a run of sessions. Weeks later, reconstructing what had happened depended on going back to the original, untouched session data. If the viewing tool had normalized, deduplicated, or otherwise reshaped that data on the way in, the reconstruction would have had to rule out its own side effects before trusting anything it found. An archive that might have edited the evidence is not an archive; it is a second suspect.
There is a more mundane reason underneath the philosophical one. A session file being viewed might not be finished. Claude Code could still be appending to it in another process. Writing to a file that something else is actively extending is a reliable way to produce corruption.
None of this is free. Refusing to write to the source pushes the cost somewhere else, and it is worth being honest about where.
A parallel database, not an augmented one. There is no column added to the source, no sidecar file next to it. Instead there is an entirely separate SQLite database that has to be built, migrated, and kept in sync by rescanning the filesystem from the outside.
Staleness has to be invented. If writing one flag back to an already-read file were allowed, "have I processed this yet" would be a solved problem. Since it is not allowed, ccRewind fingerprints instead: modification time, plus a per-session version counter that increments whenever the parser learns to extract something new. A sync pass compares those fingerprints against what is already indexed and decides what to reprocess.
const summaryStale =
existing.summaryVersion === null ||
existing.summaryVersion < SUMMARY_VERSION
That one comparison exists only because the alternative, a boolean written back into the JSONL itself, is off the table.
Disk, twice. The source history and its index both live on the same disk for the same conversations. In my data, the index alone runs to a bit under a gigabyte, sitting next to the JSONL it was built from.
Curation does not round-trip. Archiving a session or tagging it inside ccRewind creates metadata that exists only in ccRewind's own database. Rebuild the index, or point the tool at a fresh machine, and every conversation comes back exactly as it was, but every tag and archive flag is gone. The source was never an acceptable place to keep them, and the cost of that decision is that they are not durable anywhere else either.
"Never writes" is precise about what it covers, and the boundary deserves the same precision. ccRewind is not a tool that touches nothing on disk. It keeps its own database, its own cache, its own state, and it manages all of that freely. The constraint is narrower and, because it is narrower, it is also unambiguous: the one thing it never touches is the source of truth it did not create.
Kept exactly there, that boundary is what keeps the promise true: the file did not change because someone looked at it.
To see this in the code, ccRewind is open source: ccRewind on GitHub. The parsing rules that the read-only index is built on are in the previous post: Parsing Claude Code's JSONL: patterns for a schema that keeps moving.
Disclosure: this post was drafted with Claude and edited by the human who made every tradeoff in it.