Skip to content

Instantly share code, notes, and snippets.

@erbanku
Last active March 6, 2026 05:28
Show Gist options
  • Select an option

  • Save erbanku/f0accfc7798620aeb616f40f9fefa5aa to your computer and use it in GitHub Desktop.

Select an option

Save erbanku/f0accfc7798620aeb616f40f9fefa5aa to your computer and use it in GitHub Desktop.
Created by Cactus with ♥ using GitHub-CLI at Fri 03/06/2026 13:25 +08:00 (1772774756)
import json
import os
import re
import argparse
from datetime import datetime
def parse_time(ts: str):
try:
return datetime.fromisoformat(ts.replace("Z", "+00:00"))
except Exception:
return None
def fmt_time(ts: str):
t = parse_time(ts)
return t.strftime("%Y-%m-%d %H:%M") if t else ts
def slugify(title: str):
title = title.strip()
title = re.sub(r"[^\w\s-]", "", title)
title = re.sub(r"\s+", "-", title)
return title.lower() or "conversation"
def filename_from_conv(conv):
t = parse_time(conv.get("created_at", ""))
date = t.strftime("%Y-%m-%d") if t else "unknown-date"
title = conv.get("name") or "conversation"
return f"{date}-{slugify(title)}.md"
def clean_text(t: str):
if not t:
return ""
t = t.replace("\r\n", "\n").strip()
return t
def render_message(msg):
role = msg.get("sender")
time = fmt_time(msg.get("created_at", ""))
text = clean_text(msg.get("text"))
if role == "human":
lines = [f"> **Human — {time}**", ""]
for l in text.split("\n"):
lines.append(f"> {l}")
lines.append("")
return "\n".join(lines)
if role == "assistant":
lines = [f"**Assistant — {time}**", "", text, ""]
return "\n".join(lines)
return ""
def render_conversation(conv):
title = conv.get("name") or "Untitled Conversation"
created = fmt_time(conv.get("created_at", ""))
lines = []
lines.append(f"# {title}")
lines.append("")
lines.append(f"_Started: {created}_")
lines.append("")
lines.append("---")
lines.append("")
msgs = sorted(
conv.get("chat_messages", []),
key=lambda m: m.get("created_at", "")
)
for m in msgs:
lines.append(render_message(m))
return "\n".join(lines).strip() + "\n"
def main():
parser = argparse.ArgumentParser()
parser.add_argument("input_json")
parser.add_argument("-o", "--outdir", default="markdown_conversations")
args = parser.parse_args()
os.makedirs(args.outdir, exist_ok=True)
with open(args.input_json, "r", encoding="utf-8") as f:
data = json.load(f)
for conv in data:
name = filename_from_conv(conv)
path = os.path.join(args.outdir, name)
md = render_conversation(conv)
with open(path, "w", encoding="utf-8") as f:
f.write(md)
if __name__ == "__main__":
main()
@erbanku
Copy link
Author

erbanku commented Mar 6, 2026

Claude Chat History → Markdown

Convert exported Claude chat history JSON into readable Markdown files (one file per conversation).

1. Export Claude Data

In Claude:

Settings → Privacy / Data → Export Data

Download the .zip export and extract it.

Example:

unzip claude-export.zip

You should see a file like:

conversations.json

2. Run the Script

Place claude_conversation_convertor.py in the same folder and run:

python claude_conversation_convertor.py conversations.json

Optional output directory:

python claude_conversation_convertor.py conversations.json -o markdown_conversations

3. Output

Markdown files will be created:

markdown_conversations/
├── 2025-01-10-my-chat.md
├── 2025-01-11-python-help.md

Each conversation becomes a clean Markdown file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment