Last active
March 6, 2026 05:28
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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() |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
.zipexport and extract it.Example:
You should see a file like:
2. Run the Script
Place
claude_conversation_convertor.pyin the same folder and run:Optional output directory:
3. Output
Markdown files will be created:
Each conversation becomes a clean Markdown file.