- Go 100%
| .gitignore | ||
| .golangci.yml | ||
| convert.go | ||
| convert_test.go | ||
| gist.go | ||
| gist_test.go | ||
| go.mod | ||
| main.go | ||
| README.md | ||
| record.go | ||
claude2md
Convert a Claude Code session .jsonl transcript to readable Markdown.
Session logs live in ~/.claude/projects/<slugged-cwd>/<session-id>.jsonl.
Install
go install code.bas.es/marcus/claude2md@latest
Or build in-tree: go build -o claude2md .
Usage
claude2md ~/.claude/projects/-home-marcus-Source-springchick/<session-id>.jsonl
claude2md -o transcript.md session.jsonl
claude2md --no-thinking --max-result 500 session.jsonl -o short.md
cat session.jsonl | claude2md - > out.md
Default output path is the input with .md instead of .jsonl. - means
stdin/stdout.
| Flag | Default | Meaning |
|---|---|---|
-o |
<input>.md |
Output file; - for stdout |
-images |
<output>-images/ |
Directory for extracted images |
-no-images |
off | Leave placeholders instead of writing image files |
-no-thinking |
off | Omit thinking blocks |
-no-tools |
off | Omit tool calls and their results |
-max-result |
2000 |
Truncate tool inputs/results to N chars (0 = unlimited) |
-q |
off | No summary line on stderr |
-gist |
off | Publish as a secret gist, images included; prints the URL |
-gist-public |
off | Make that gist public instead |
-gist-desc |
Claude Code transcript — <file> |
Gist description |
-gist-open |
off | Open the gist in a browser |
Publishing to a gist
claude2md -gist session.jsonl # secret gist, prints URL
claude2md -gist -gist-public -no-thinking s.jsonl # public, no thinking blocks
claude2md -gist -o keep.md session.jsonl # publish and keep a local copy
Secret by default — transcripts carry source, paths, and command output. Secret only means unguessable, not private: anyone with the URL can read it.
Needs the gist CLI, logged in via
gist --login. The gist API is text-only, so images cannot go through it;
when a transcript has screenshots claude2md also needs git with push access
to git@gist.github.com (the same SSH key as GitHub). It then:
- creates the gist with a stub,
- clones its git remote, commits the markdown plus every image, pushes,
- links the images at
gist.githubusercontent.com/<user>/<id>/raw/<name>.
If the owner is missing from the returned URL, gh api user supplies it.
Without images only step 1 runs, and neither git nor gh is needed.
Nothing is written locally in -gist mode unless you pass -o; images go to
a temp dir that is removed on exit.
Output shape
## 👤 User/## 🤖 Assistantheadings with local-time stamps.- Records that stream one assistant response (shared
requestId) collapse under a single heading. - Thinking blocks and tool results go in
<details>so the conversation reads straight through. - Tool results attach to the call that produced them rather than appearing as user turns.
- Base64 images are written out as files and linked; they are the bulk of a
transcript's size (a 17 MB
.jsonlis typically ~350 KB of Markdown plus a few MB of screenshots). - Bookkeeping records (
mode,ai-title,attachment,file-history-*, …) are dropped.