npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alexgsdev/claude-code-transcript-export

v0.1.9

Published

Extract Claude Code session logs into readable, committable markdown transcripts.

Readme

Claude Code Transcript Export (cctx)

Extract Claude Code session logs into readable, committable markdown transcripts.

Claude Code transcripts exist in exactly one place: JSONL files under ~/.claude/projects/, on the machine where the work happened. They are not in the claude.ai account export and never will be. They are also not durable — they are a cache, subject to pruning by a tool that does not consider them precious.

cctx moves them from a location that forgets into one that remembers.

Install

npm install -g @alexgsdev/claude-code-transcript-export

Installs the cctx command. Requires Node 22 or newer.

Use

From anywhere inside your project:

cctx init      # write .claude-export.yaml — its location marks the project root
cctx list      # see every session, with its UUID
cctx           # write transcripts

Output looks like this:

docs/sessions/
  index.md
  2026-08-29--consolidating-the-two-sites--0ba0029e.md
  images/
    0ba0029e-3f9c1a72.png

Each transcript carries YAML frontmatter (title, session id, dates, turn count, links to continued sessions) and the conversation as numbered, speaker-labelled turns.

Commands

| | | | --- | --- | | cctx / cctx extract | Write transcripts for this project | | cctx list | Every session with UUID, date, turn count and status | | cctx probe | Report the observed log schema | | cctx init | Write a starter config in the current directory |

Useful flags: --project <path> to work outside a configured project, --dry-run, --json, --keep-tools, --keep-thinking, --ignore-config.

How it decides things

A turn is one exchange, not one record. A single reply is spread across many records — text, a tool call, its result, more text. Those coalesce into one turn, because that is what a citation wants to name. The cost: turn numbers are stable only while the filter is.

Most user records are not you. Across the corpus this was built against, 775 of 10,600 user records are the human actually speaking — 7.3%. The rest are tool results the harness feeds back. Treating them all as turns would inflate a transcript fourteenfold.

Tool payloads and thinking are dropped by default. Anything authored in a Claude Code session lands in your working tree and is captured by the commit, so the payload is redundant with git while making up most of the bytes. What is unique to the transcript is the prose. Pass --keep-tools if you disagree.

Images are kept. Screenshots you paste exist nowhere but the log, so they are written alongside the transcript, named by a hash of their content.

So are files Claude sends you. A render Claude makes in /tmp and shows you with a caption never reaches your working tree. The transcript lists each sent file by path, with the image and the caption. The image comes from the log when Claude looked at it first, and otherwise is copied from where it was sent — once. A later run after /tmp is cleaned keeps that copy and the link to it.

So are the answers you give a question widget. When Claude asks a multiple-choice question, the option you picked — and anything you typed into "Other", or added as a note — is a tool result, and would fall under the rule above. It is also a decision the rest of the session proceeds on, recorded nowhere else. Those render as a turn of yours, question and answer, whatever --keep-tools says.

Dates are local. A session beginning 2026-08-25T00:13Z is 17:13 on 2026-08-24 where you were sitting, and so is the commit it produced.

A rerun over unchanged logs writes nothing. extracted: means the day a transcript's content last changed, not the day the tool last ran, so reruns do not produce a wall of one-line diffs.

Deletion is narrow. Renamed and newly-excluded transcripts are removed and reported. Nothing else, ever — the logs are ephemeral and this output is the durable copy, so a directory sync would delete precisely the transcripts that can no longer be regenerated. Orphaned images are reported but never deleted.

The index survives a machine move. Logs are not committed and transcripts are, so a new machine — a cloud dev box, a reinstall — has the transcripts and none of the logs behind them. Rows for those are read back out of the transcripts themselves rather than dropped, alternation counts included, and the index says how many. Their images stay off the orphan report for the same reason.

Configuration

.claude-export.yaml, at the project root. cctx init writes a commented starter. Every key is optional; see the specification for the full list.

Sessions are selected by UUID — titles are model-generated and change while you work, so they are not a stable selector. Run cctx list to get the ids.

A blocklist is permanent. The logs are a cache: a session you exclude today, whose log Claude Code prunes next month, is unrecoverable. cctx --ignore-config --out <scratch> captures everything if you want to look first.

On schema drift

The Claude Code log format is undocumented and it moves. This package was written against 84 logs on one machine, and during a single afternoon of drafting, two design decisions had to be reversed by observation.

The test suite guards against our regressions. It cannot detect Claude Code changing its format, because CI has no real logs to look at. A green badge here does not mean the parser still fits your logs.

Detecting that stays a manual act:

cctx probe

It reports every record and block type it sees, and names the ones this version does not handle. Run it when output looks wrong.

Library

import { discover, readSession, renderTranscript } from '@alexgsdev/claude-code-transcript-export';

Everything is pure unless its name says otherwise — nothing below the CLI layer reads the clock, the environment, or your home directory. See the specification §12.

Releasing

Work lands on develop; main is what has been released. Every merge to main publishes to npm. Changes are recorded as they are made with Changesets and collected into CHANGELOG.md at release time.

While developing. A PR to develop that changes src/, package.json or tsconfig.build.json must add a changeset:

npm run changeset

CI fails without one. Label the PR skip-changeset if nothing a user of the package would notice has changed.

To release:

  1. Branch from an up-to-date develop and apply the pending changesets:

    git switch develop && git pull
    git switch -c release-<version>
    npm run release:version

    This deletes the changesets, bumps the version in package.json and package-lock.json, and prepends an entry to CHANGELOG.md. Edit the entry if it reads badly, then commit.

  2. Open a PR from the release branch to develop and merge it.

  3. Open a PR from develop to main. CI fails unless the version went up, CHANGELOG.md has a section for it, the lockfile agrees, no changesets are left unapplied, and the tag does not exist yet.

  4. Merge. .github/workflows/release.yml runs the full suite, publishes to npm, and pushes the tag v<version>.

Do not tag a release by hand. The workflow treats an existing tag as proof the version is already published and skips the publish. If a release run fails partway, re-run it from the Actions tab: it skips a publish that already happened and only pushes the missing tag.

License

MIT