claude-replay normalizes seven agent transcript formats onto one renderer
Convert AI coding agent sessions (Claude Code, Cursor, Codex, Gemini, OpenCode, Kimi Code, Hermes) into self-contained, embeddable HTML replays
At a glance
- What is it?
- A dependency-free converter that turns agent session logs into a single self-contained HTML file, so a session can be emailed or embedded instead of screenshotted. Claude Code is the internal vocabulary every other format is mapped onto, the Docker image installs the published npm package rather than the local source, and secret redaction is a step on the export path only.
- Who is it for?
- claude-replay earns its place on the strength of one requirement: the output has to be a single file with no external dependencies, so it survives being emailed, attached to a bug report or embedded in documentation without a server. That constraint also explains a zero-dependency package with an esbuild step and Playwright tests.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 16 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Every format is mapped onto Claude Code's vocabulary
The location table is the fastest way to see what this project does. Seven sources, each with a different storage convention.
| Source | Transcript location | |---|---| | Claude Code | `~/.claude/projects/<project>/` | | Cursor | `~/.cursor/projects/<project>/agent-transcripts/<id>/` | | Codex CLI | `~/.codex/sessions/<date>/` | | Gemini CLI | `~/.gemini/tmp/<projectHash>/chats/` | | OpenCode | Export via `opencode export <sessionID>` | | Kimi Code | `~/.kimi-code/sessions/<project>/<session>/agents/<name>/wire.jsonl` | | Hermes Agent | `~/.hermes/state.db` (read live via SQLite) or `hermes sessions export` |
Note that OpenCode and Hermes are the two entries whose location is an action rather than a path, and the reason differs. OpenCode keeps sessions in SQLite, so a session has to be exported with `opencode export <sessionID>` and redirected into a JSONL file first. Hermes also stores state in SQLite, but there is a second route: on Node 22.5 and newer the live store is read directly, either by session ID, searching `~/.hermes/state.db` and `~/.hermes/profiles/*/state.db`, or by virtual path of the form `~/.hermes/state.db#session:<id>`.
Everything else is a plain file read. That makes the tool's tolerance for format drift a function of how those files are written rather than of any API.
Tool names are rewritten so one renderer can draw all of them
The renderer is built around Claude Code's presentation, and each adapter translates into it.
Codex CLI is mapped by tool name: `exec_command` and `apply_patch` become `Bash` and `Edit` or `Write`, so the same diff view and command preview render. Gemini CLI stores sessions as single JSON files rather than JSONL, with inline thinking blocks and tool calls, and its names are mapped the same way, with `run_shell_command` becoming `Bash` and `read_file` becoming `Read`. Hermes follows suit, with `terminal`, `patch` and `read_file` mapped to Claude Code equivalents and its reasoning blocks displayed as thinking.
The cost of a shared vocabulary is that a view can only show what the union of the sources supports. Kimi Code and OpenCode are described as rendering thinking blocks and tool calls natively, which reads as the opposite approach, so a session that mixes sources, and chaining is a supported operation, may render through whichever path each block came from.
The mapping also means a rename inside a vendor CLI shows up as a missing tool call rather than as an error. Nothing on the page describes what happens when an unknown tool name arrives.
Cursor sessions have no timestamps, so playback is paced
One source is missing the field the player needs most, and the project says so plainly.
Cursor transcripts do not include timestamps. Playback therefore uses paced timing by default, and the text points at a Timing modes section further down the page for the alternatives. That is a real difference between sources rather than a setting: a Claude Code replay advances on the clock recorded in the transcript, while a Cursor replay advances on a synthetic schedule that has to guess the pacing.
Everything else in the feature list is playback machinery built around that timeline: interactive playback with speed control, collapse and expand for tool calls and thinking blocks, bookmarks and chapters, multiple color themes, and a terminal-style bottom-to-top scroll. The file activity sidebar is the feature that matters most on that timeline, since it lists which files were touched and navigates to the tool call that touched each one.
For a transcript that is a record of an agent working, guessing the pauses is a visible approximation. The paced default is stated, which is more than the alternative would have been, but the visible text does not say how the pacing is derived.
No runtime dependencies, one HTML file out
The manifest is the reason the output can be a single file, and it is unusually spare.
The published surface is `bin/`, `src/` and `template/`. There are no dependencies at all under `dependencies`, only two dev entries: `esbuild` for the template build and `@playwright/test` for end-to-end runs. Tests are split the same way, with `node --test test/test-*.mjs` for units and `npx playwright test` for browser coverage, plus a `playwright.config.mjs` in the tree and a `.pre-commit-config.yaml`.
The build step is what turns the source into the artifact that gets inlined. `npm run build` runs a template build script, and `build:website` runs that plus a separate website build, so the demo page and the replay template are built by the same script family. The engine floor is Node 18 or newer, with the Hermes live-read path needing 22.5.
That floor has a consequence worth stating: the tool's behavior differs by Node version on one specific source, and the version requirement does not reflect it, since the package declares only `>=18`.
The Docker image installs from npm and binds every interface
The image is four lines.
FROM node:22-alpine
RUN npm install -g claude-replay
EXPOSE 7331
ENTRYPOINT ["claude-replay", "--host", "0.0.0.0"]Two things follow from those lines. The install is `npm install -g claude-replay`, so the image pulls whatever version the registry serves when the image is built; it does not copy the local `src/` or `bin/` directories, so a build from a checkout does not test that checkout. And the entrypoint passes `--host 0.0.0.0`, which binds the web editor on every interface inside the container.
The documented run publishes that port:
docker run --rm --init -p 7331:7331 \
-v ~/.claude/projects:/root/.claude/projects:ro \
ghcr.io/es617/claude-replayThe mount is read-only, which protects the transcripts from being written, and it is the transcript directory itself that is exposed. There is no authentication in front of the editor and no second container option for adding one, so the practical reading is that port 7331 should be treated as sensitive on any host that is reachable by anyone else, and the CLI variant that also mounts the working directory writes output to `/output` instead.
Redaction happens on export, not on the way to the browser
Secret redaction before export is listed as a feature, and the word doing the work is before.
The pipeline the page describes is: discover sessions, browse, edit and preview in the web editor, then export. Redaction belongs to that last step, which means the editor itself displays whatever the transcript contains. The same is true of the live mode, where `--serve --watch` is offered for monitoring agent sessions as they run, including on remote machines and containers, which is the feature most likely to be pointed at a box holding credentials.
There is a dedicated demonstration of the redaction behavior on the project site, a demo page whose name marks it as the redaction demo, and the online playground sits next to it. So the capability is real and worth looking at before you publish anything.
The other half of sharing is the file itself. The output is a single self-contained HTML file with no external dependencies, described as something you can email, host anywhere or embed in documentation, and embedding is listed as iframe support. A redacted export is safe to send; a live session view is not the same thing, and the page does not offer a redaction switch for the served path.
The editor is the default and session IDs are the currency
Running the tool with no arguments does not produce a file. It opens a browser-based editor that auto-discovers your Claude Code and Cursor sessions, where you can browse, edit, preview and export.
# Launch the web editor (default)
claude-replay
# Generate a replay by session ID (auto-finds the file)
claude-replay abc123def456 -o replay.html
# Or pass the full path
claude-replay ~/.claude/projects/-Users-me-myproject/session-id.jsonl -o replay.html
# Chain multiple sessions into one replay
claude-replay session1-id session2-id -o combined.htmlThe shortcut worth knowing is the bare ID. Passing one searches `~/.claude/projects/`, `~/.cursor/projects/`, `~/.codex/sessions/`, `~/.gemini/tmp/` and the Hermes SQLite databases under `~/.hermes/`, which spares you from constructing a project-hashed path. Passing a full path skips the search. Passing several IDs chains them into one file, which is how you would assemble a debugging session that was split across a restart.
The editor itself listens on a port you can change with `--port 8080`, and its session browser covers the same roots plus the Kimi Code session tree, where the page notes that each subagent has its own `agents/<name>/` directory and that the editor discovers the main session alongside any subagents.
Editorial conclusion
claude-replay earns its place on the strength of one requirement: the output has to be a single file with no external dependencies, so it survives being emailed, attached to a bug report or embedded in documentation without a server. That constraint also explains a zero-dependency package with an esbuild step and Playwright tests. Three things to know. Format support is uneven at the edges: OpenCode has to be exported from its SQLite store before it can be replayed, Kimi Code splits subagents into separate directories, and Cursor transcripts carry no timestamps, so their playback is paced rather than real. Second, treat the served editor as sensitive. The container entrypoint binds all interfaces, the documented run publishes the port with no authentication, and the mounted directory is your raw transcripts, while redaction applies only when you export. Third, the Docker image installs `claude-replay` from npm at build time, so the image contents follow the registry and not the commit you built from.
Frequently asked questions
Where does claude-replay look for agent sessions?
Passing a bare session ID searches ~/.claude/projects/, ~/.cursor/projects/, ~/.codex/sessions/, ~/.gemini/tmp/ and the Hermes SQLite databases under ~/.hermes/. The documented locations are ~/.claude/projects/<project>/, ~/.cursor/projects/<project>/agent-transcripts/<id>/, ~/.codex/sessions/<date>/, ~/.gemini/tmp/<projectHash>/chats/ and ~/.kimi-code/sessions/<project>/<session>/agents/<name>/wire.jsonl. A full path skips the search.
Can claude-replay replay an OpenCode or Hermes session?
Yes, with a difference between them. OpenCode keeps sessions in SQLite, so you export first with opencode export <sessionID> redirected into a JSONL file. Hermes can be read live on Node 22.5 and newer straight from ~/.hermes/state.db or ~/.hermes/profiles/*/state.db, by session ID or by a state.db#session:<id> virtual path, and exported files from hermes sessions export also work.
How do I run claude-replay without installing it globally?
Use npx claude-replay. The Docker route is ghcr.io/es617/claude-replay, which installs the published package on node:22-alpine, exposes 7331 and starts claude-replay with --host 0.0.0.0. Mounting ~/.claude/projects read-only and publishing the port gives you the web editor at http://localhost:7331.
What happens with Cursor transcripts that have no timestamps?
Playback uses paced timing by default, since Cursor transcripts do not include timestamps. The page refers to a Timing modes section for the other options. Playback features include speed control, collapse and expand for tool calls and thinking blocks, bookmarks and chapters, color themes, and a file activity sidebar that navigates to the tool call for each touched file.
Does claude-replay redact secrets before sharing a session?
Secret redaction before export is a listed feature, and the project site hosts a demo page devoted to it. Redaction is part of the export step, so the web editor and the --serve --watch live mode display what the transcript actually contains, and the Docker example publishes the editor on port 7331 with the raw project transcript directory mounted read-only.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/es617-claude-replay)