# claude-code-log: turning Claude Code transcript JSONL into readable HTML and Markdown

> This Python CLI converts Claude Code session transcripts into browsable HTML, Markdown and JSON, with a Textual TUI, watch mode, full-text search over an archive, token accounting and a folding theme for sub-agent branches. It is MIT licensed, runs on Python 3.10 or newer, and treats a failed upstream transcript format as an expected condition rather than an edge case.

**daaain/claude-code-log** — A Python CLI tool that converts Claude Code transcript JSONL files into readable HTML / Markdown format.

- Repository: https://github.com/daaain/claude-code-log
- Website: http://daaain.github.io/claude-code-log/
- Stars: 1,250 · Forks: 102
- Language: Python
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/daaain-claude-code-log

## The problem: your sessions are JSONL, and JSONL is unreadable

Claude Code writes a transcript of every session to disk as JSONL, one JSON object per line. That format is excellent for a program to stream and terrible for a person to read, and after a few weeks of work the archive under `~/.claude/projects/` contains a record of everything you asked and everything the agent did, in a form that resists skimming.

claude-code-log is a Python CLI tool that converts those transcript files into readable HTML and Markdown, plus JSON for tooling. The HTML output is described as clean and minimalist pages showing user prompts and assistant responses chronologically, and the project ships a real example generated from a sample of its own development.

The questions the README lists are the real motivations. How do I review all my conversations, what did I work on yesterday, how many tokens are my sessions using, what tools did the agent use in this project, what is the timeline of my project development, and how do I feed a past session back to a model for analysis or experience building. That last one is the most interesting, because it depends on a specific feature, the compact mode, that we will come back to.

The intended user is a heavy Claude Code user with a growing archive. Not a team with a shared transcript pipeline, and not someone who wants an agent observability product. The tool is a personal reader for a personal archive, and it is built with that in mind, including a cache and a resume action that opens the session back in Claude Code.

## The TUI is the main interface, not a wrapper around a converter

The interactive terminal UI is listed first among the key features, and the project ships a TUI demo, so it is worth understanding that this is a browsing tool first and a converter second.

Launching it is a flag on the same command:

```bash
# Launch TUI for all projects (default behavior)
claude-code-log --tui

# Launch TUI for specific project directory
claude-code-log /path/to/project --tui

# Launch TUI for specific Claude project
claude-code-log my-project --tui  # Automatically converts to ~/.claude/projects/-path-to-my-project
```

The last form is the useful one for daily use. The project key `my-project` resolves to the mangled directory name under `~/.claude/projects/`, which is the same encoding Claude Code uses, so you do not have to remember the flattened path.

Inside, the session listing shows session IDs, summaries, timestamps, message counts and token usage. Two details show real care. Smart summaries prefer Claude-generated summaries over the first user message when identifying a session, which means a session called "fix the flaky test" is labelled that way rather than by whatever prompt opened it. And working directory matching finds and opens the project matching your current directory, so launching the tool from inside a repository lands you in that repository's sessions.

The quick actions are the reason to use the TUI rather than the plain converter. `h` generates and opens the session HTML in a browser, `m` does the same for Markdown, `v` views the Markdown in an embedded viewer with a table of contents, and `c` resumes the session in Claude Code with `claude -r <sessionId>`. You can also archive a session with `a`, delete it with `d`, and restore an archived session back to a JSONL file with `r`.

That last pair is the one that makes the tool more than a reader. Archiving is destructive to the visible archive, and restoring it is the undo path, which means the tool takes a position on the lifecycle of your transcripts rather than leaving them untouched. The TUI is built on Textual, and the README notes a cache system with automatic cache validation for fast loading.

## Watch mode and the serve command for live sessions

A transcript is a growing file while a session is running, and that creates an awkward problem: any HTML you generated a minute ago is already out of date.

Two features address it. Watch mode re-converts as a session is written, so Markdown in an editor or in Obsidian stays current. The serve command does the same for the web output:

```bash
claude-code-log watch
claude-code-log serve --watch
```

The second makes an open session page grow as messages arrive while keeping your scroll position and folded sections. That last part is the detail that shows the feature was built by someone who actually reads transcripts. A page that re-renders and jumps to the top every time a message lands is worse than a static page, and preserving the scroll position and the folds is what makes a live view tolerable.

The serve command also carries the search feature. `claude-code-log serve` serves the archive over loopback with full-text search across every project and session, and every generated page also has in-page search. Serving over loopback is the right default for an archive that contains your prompts and the agent's tool output, and it is worth knowing that the archive is only as private as the machine serving it.

The feature walkthrough linked from the README is organised into six short chapters on the archive index and search, reading a session, folding and filtering, agents and forks, and live sessions, which is a good map of the feature surface if you want to understand what is actually implemented before running it.

## Folding, depth levels and a JavaScript filter engine

Three filtering mechanisms exist, and they operate at different levels.

The theme system is the most visible. `--theme minimal` gives a compact light and dark page in which sub-agents and rewind forks are branches you can fold away, interleave with the main conversation, or open side by side in columns. For a transcript of a large task with several sub-agents, the difference between a folded branch and an interleaved one is the difference between a readable document and a wall of text.

The depth levels control how far into the message hierarchy the renderer goes, with `--depth session|user|assistant|agent|tool|hook`. The compaction mode, `--compact`, merges repeated section headings. The README pairs that with `--format md` and gives the purpose away: the combination is meant for feeding past conversations back to a model for analysis or experience building. In other words, compaction does double duty, as a reading feature and as a preprocessing step for a machine.

Runtime message filtering is the third mechanism, and it works in an unusual way. Filtering is JavaScript powered, letting you show or hide message types such as user, assistant, system and tool use. The `quickjs-ng` dependency in the project metadata is an embedded JavaScript engine, so the filter expressions you write are evaluated by a JavaScript runtime compiled into the tool rather than by a Python expression parser. That gives you real language features in the filter, at the cost of a dependency most similar tools would not have.

Date range filtering is a fourth, simpler one, and it accepts natural language such as "today", "yesterday" or "last week". The `dateparser` dependency does the work. Rich message types are supported, including user and assistant messages, tool use and results, thinking content and images, and system commands such as `init` appear in expandable details with structured parsing.

## Running it: uvx for a quick look, the CLI for everything else

The quickstart is a single command, and it needs nothing installed:

```sh
uvx claude-code-log@latest --open-browser
```

That runs the tool from the published package and opens the result in a browser. It processes your entire Claude Code archive, which the README describes as the fastest way to see what the tool produces. `--open-browser` is a convenience flag, and the default behaviour, as the usage section says, is to process all projects.

The underlying command is `claude-code-log`, mapped in the packaging metadata to `claude_code_log.cli:main`, built with Hatchling and using Click for the command line interface. The package requires Python 3.10 or newer and is classified as operating system independent.

The dependency list explains the feature set. Click is the CLI, Pydantic validates the transcript data, Jinja2 renders the HTML templates, wenmode provides server-side Markdown rendering with syntax highlighting, Textual powers the TUI, dateparser handles the natural language date ranges, Pygments does the syntax highlighting, and quickjs-ng runs the JavaScript filter expressions.

There is also a `justfile` and a `mise.toml` for contributors, which run everything through `uv run`. The default recipe is `just --list`, and a `cli` recipe runs `uv run claude-code-log` with your arguments, so a contributor and an end user end up on the same code path.

## A test suite that treats rendering as a snapshot

The testing setup is unusually well documented in the project files, and it explains how the rendering stays stable as features are added.

Pytest runs with `-n auto --dist=worksteal` by default, and the configuration comment explains why: the default `load` distribution left workers idle while one slogged through a queue, so the project switched to worksteal to balance heavyweight integration tests across workers. There is a per-test wall-clock ceiling of 300 seconds, and the comment is blunt that nothing here is remotely slow, the slowest test measured 5.6 seconds and the slowest non-browser test 4.5 seconds, so the ceiling exists for the pathological case rather than the normal one.

The rendering tests are snapshot based, and the justfile records the reason in detail. The `update-snapshot` recipe purges stale bytecode, regenerates the snapshots serially with `-n0`, and then re-runs the test read-only to prove the committed file matches what the code renders. The comment notes that a parallel `--snapshot-update` once silently truncated around 6000 lines of the `.ambr` file, and that a healthy result is purely additive.

That is a small but telling piece of engineering culture. The tool is for reading conversations, and the test suite treats the rendered output as the contract.

The suite is also split by marker rather than run as one blob. `just test` runs unit and integration tests and excludes the TUI, browser and benchmark markers, and it passes `-p no:playwright` because unit tests never use the browser fixtures while the plugin import costs about a second per xdist worker on Windows. Separate recipes exist for the TUI tests, the browser tests that require Chromium, the integration tests with realistic JSONL data, and the benchmark tests which run serially for stable measurements.

## Upstream formats, experimental providers and the wrong-tool case

The tool sits on top of a format it does not control, and the project says so plainly. Experimental provider support is available for single-session exports from the Antigravity CLI, `agy`, at alpha status, and from the Codex CLI, `codex`, at beta status, using `--provider agy|codex --session-id <id>`. The note is explicit that these integrations may change as the upstream transcript formats evolve.

That is the central risk in this project, and it is not a flaw in the implementation. A transcript is a private format of another tool, and any change to it can break parsing. The project handles this by shipping a changelog, regenerating the example output on every docs build, and by labelling the non-Claude providers as alpha and beta rather than presenting them as finished. Version 1.7.0 was released on 2026-10-05, with 1.6.0 on 2026-08-31 and 1.5.0 on 2026-07-09, and the last push was on 2026-10-09, so the response to format changes has been fast so far.

The wrong-tool case is a shared or monitored setup. This is a personal reader for a personal archive, with a `serve` command bound to loopback and a browser that opens locally. It is not a team transcript store, not an audit trail for an agent fleet, and not something to expose on a network without thinking about what the transcripts contain, because they contain your prompts, your file paths and the output of every tool the agent ran.

The alternative approach is to read the JSONL directly, or to write a small script against the raw files. The difference in approach is that a script gives you exactly the fields you want and nothing else, while claude-code-log gives you the whole thing rendered, searchable and navigable. For one file, a script wins. For an archive of hundreds of sessions, the renderer, the search and the TUI are the reason the tool exists.

The licence is MIT, declared in the project metadata and the LICENSE file, with the two authors named as Daniel Demmel and Edward Z. Yang. For a personal tool that reads your own files, the terms are not a constraint.

## Conclusion

Adopt claude-code-log if you keep a large archive of agent sessions and need to read, search and share them, since it handles the whole `~/.claude/projects/` tree, keeps Markdown in an editor in sync through watch mode, and can hand a compacted transcript back to a model for analysis. Do not adopt it if you need a stable transcript schema, since the tool reads a format that upstream changes, and the Antigravity and Codex provider support is labelled alpha and beta respectively. Verify three things first: that the session directory you point it at is the one you intend to publish, because the HTML output contains your prompts, tool calls and any code you pasted, that the `serve` command is bound to loopback as documented before you put it on a network, and that the version you pin matches the transcript format your Claude Code installation writes.

## FAQ

### What does claude-code-log do with Claude Code transcripts?

It converts transcript JSONL files into readable HTML pages, Markdown and JSON for tooling, showing user prompts and assistant responses chronologically. It handles individual session files as well as the whole `~/.claude/projects/` hierarchy.

### How do I run claude-code-log without installing it?

Run `uvx claude-code-log@latest --open-browser`, which runs the published package and opens the generated pages in a browser. The default behaviour is to process all projects, and the `--tui` flag starts the terminal interface instead.

### Can claude-code-log search across all my Claude Code sessions?

Yes. `claude-code-log serve` serves the archive over loopback with full-text search across every project and session, and each generated page also has in-page search. There is also an in-app date range filter that accepts natural language such as "today" or "last week".

### Which other agent CLIs does claude-code-log support?

Experimental support exists for the Antigravity CLI with `--provider agy`, which is labelled alpha, and the Codex CLI with `--provider codex`, which is labelled beta. Both are limited to single-session exports identified by `--session-id`.

### What is the difference between --depth and --compact in claude-code-log?

`--depth session|user|assistant|agent|tool|hook` sets how deep into the message hierarchy the renderer goes, while `--compact` merges repeated section headings. The README notes that `--compact` pairs well with `--format md` for feeding past conversations back to a model for analysis.

## Sources

- [daaain/claude-code-log on GitHub](https://github.com/daaain/claude-code-log)
- [License: MIT](https://github.com/daaain/claude-code-log/blob/main/LICENSE)
- [Project website](http://daaain.github.io/claude-code-log/)
- [README](https://github.com/daaain/claude-code-log/blob/main/README.md)
- [Releases](https://github.com/daaain/claude-code-log/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/daaain-claude-code-log
