Claude Code Save Conversation: Where Transcripts Live
Claude Code hit a $1B annualized run-rate six months after public launch: Anthropic's "fastest-growing product in the company's history" (Anthropic, 2025). I've used it daily across 27 project directories on this laptop. As of this morning, my ~/.claude/projects/ folder holds 122 JSONL transcripts for the blog repo alone, going back roughly four weeks.
That last number is the catch. Claude Code keeps your conversations locally, and deletes them after 30 days by default (Claude Code Docs, 2026). If you've ever closed a terminal and wished you could go back to "how did I solve that Postgres migration last month," that's the window you're losing.
This is the practical guide I wish I'd had: where transcripts actually live, the JSON schema, the built-in /resume and /export commands, five open-source tools for searching and exporting, and the redaction workflow I use before sharing a session externally.
the broader Claude Code production hardening guide that pairs with this archiving setup
Key Takeaways
- Claude Code stores every session as plaintext JSONL at
~/.claude/projects/<encoded-cwd>/<session-uuid>.jsonland auto-purges after 30 days unless you setcleanupPeriodDayshigher (Claude Code Docs, 2026).- Native commands cover resume and export:
/resumeopens the session picker,/export <file>writes the current conversation,/insightsanalyzes your history.- Trust in AI tools dropped to 29% in 2025, its lowest ever (Stack Overflow, 2025). Your own transcripts are the only ground truth about what the model actually did for you.
- Five OSS tools turn raw JSONL into searchable, shareable archives: ccusage (14.2k★), claude-code-transcripts, claude-code-log, claude-conversation-extractor, and claude-history.
Why Should You Save Claude Code Conversations at All?
84% of developers now use AI tools but only 29% trust their accuracy, the widest gap the Stack Overflow survey has ever recorded (Stack Overflow, 2025). If the model is wrong, the only audit trail you have is the transcript. There's no "git blame" for an agent's reasoning unless you keep the JSONL.
Three concrete reasons matter more than the privacy paranoia people usually lead with:
-
Debugging your own agent loops. When a subagent goes sideways (wrong tool, weird argument, runaway plan), the transcript shows the exact stdin/stdout that hooks saw. The first time I had a subagent silently loop on a
git statuscall, replaying the JSONL line by line was the only way I caught it. - Learning from your own patterns. Simon Willison reported personally accumulating 379 MB of JSONL (Simon Willison, 2025). At that volume, the transcripts become a personal prompt library: the prompts that actually worked, not the ones you think worked. A METR randomized trial of 16 experienced devs found they believed AI made them 20-24% faster while measurements showed they were 19% slower ((https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/), 2025). Reviewing your transcripts is one of the few honest ways to close that gap.
- Audit and review. Stack Overflow's 2025 data shows 72% of developers say "vibe coding" is not part of their professional work (Stack Overflow, 2025). Most of us review what the model produced. When a PR review asks "why this way?" the JSONL is the receipt.
Our finding: Across 122 saved sessions in this single project, I can
grepexactly when I first wired up the Hashnode adapter, what error message convinced me to drop it, and the verbatim prompt that fixed a stale-job recovery bug, none of which I'd remember without the JSONL on disk.
the subagent debugging context that benefits most from saved transcripts
Where Does Claude Code Save Conversations on Disk?
Claude Code writes every session as a JSON-Lines file under ~/.claude/projects/<encoded-cwd>/<session-uuid>.jsonl, with a sibling directory of the same UUID for any sidecar attachments (Claude Code Docs, 2026). The encoded working directory is just your full cwd with / replaced by -, so /Users/nishil/Documents/work/blogs becomes -Users-nishil-Documents-work-blogs. That naming is how the CLI knows which transcripts belong to the project you're standing in when you run claude or /resume.
Here's the actual layout from my machine right now:
~/.claude/projects/
├── -Users-nishil-Documents-work-blogs/ ← this blog's project dir
│ ├── 02a0ea76-3694-4307-b1da-65c17cee00a4.jsonl ← one full session
│ ├── 02a0ea76-3694-4307-b1da-65c17cee00a4/ ← sidecar dir (attachments)
│ ├── 02cf8a57-d0dc-4d37-b0fc-b242c2f46a1b.jsonl
│ ├── 035df874-52bf-43b7-b458-43b69bcf987f.jsonl
│ └── … (122 sessions total)
├── -Users-nishil-Documents-work-ats-resume-tailor/
├── -Users-nishil-Documents-work-claude-skills/
└── … (27 project directories)
~/.claude/
├── settings.json ← cleanupPeriodDays lives here
├── settings.local.json ← project-local overrides
├── hooks/ ← your PreToolUse/PostToolUse scripts
└── skills/ ← installed skills
Each .jsonl file is append-only: every user message, model response, tool call, hook attachment, and snapshot lands as one JSON object per line. That structure is what makes the format trivially grep-able and trivially streamable: no parser required for a first pass.
The 30-day cleanup is enforced by Claude Code itself, not your OS. To keep transcripts longer, edit ~/.claude/settings.json:
{
"cleanupPeriodDays": 365
}
Set it to a year if you want a real archive. Set it to 0 to disable auto-cleanup entirely (your filesystem becomes the only janitor).
A subtle thing most guides miss: /feedback transcripts have a separate, longer retention (Anthropic keeps them for 5 years to improve the product) (Claude Code Docs, 2026). Submitting feedback from inside a session is itself a form of "save," just one you don't control.
What's Inside a Claude Code Transcript File?
A Claude Code session file is JSON-Lines: each line is a self-contained JSON object with the metadata needed to replay the conversation. Sampling the first session in my blog project, I see five distinct line types, and every line carries sessionId, timestamp, cwd, gitBranch, and a UUID-chained parentUuid linking it to the previous turn. The chain is what makes the transcript a graph, not just a log.
The top-level keys you'll see most often:
| Field | Type | What it tells you |
|---|---|---|
type |
string |
user, assistant, attachment, permission-mode, or summary
|
sessionId |
UUID | Matches the filename |
parentUuid |
UUID | null | Points to the previous turn (null for the first message) |
timestamp |
ISO 8601 | Server-side time of the turn |
cwd |
path | Working directory when the turn happened |
gitBranch |
string | Git branch at the time — invaluable for retracing context |
version |
string | Claude Code CLI version (2.1.119, etc.) |
message |
object | The full Anthropic message payload (role, content blocks) |
attachment |
object | Hook output, snapshots, or tool sidecar data |
userType |
string |
external for you, internal for agent-spawned subagents |
isSidechain |
boolean |
true if this turn is from a dispatched subagent |
A minimal user-turn line looks like this:
{
"type": "user",
"sessionId": "02a0ea76-3694-4307-b1da-65c17cee00a4",
"parentUuid": "e2363e23-702d-4df6-9c39-036dd00f5d8b",
"uuid": "f7a2c1b3-...-...",
"timestamp": "2026-04-24T10:16:23.118Z",
"cwd": "/Users/nishil/Documents/work/blogs",
"gitBranch": "main",
"version": "2.1.119",
"message": {
"role": "user",
"content": [{"type": "text", "text": "Refactor the publish orchestrator…"}]
}
}
Knowing this schema is what unlocks the rest of the workflow. Once you can identify a type: "user" line and pull message.content[0].text, you can rebuild any session into Markdown with three lines of jq.
A few non-obvious wrinkles. The attachment lines carry hook output (stdin payload, stdout, stderr, exit code, duration), so for hook failures, that's where you look. The sibling directory next to each .jsonl (same UUID, no extension) holds binary attachments like image uploads and diffs. Subagent-spawned turns set isSidechain: true, which separates the main thread from delegated work. There's no compaction either: even a 200-turn session stays on disk as the full append-only log. My largest blog session is 4.8 MB; the project directory across 27 codebases is 612 MB, which compresses to 51 MB gzipped.
How Do You Use the Built-In /resume and /export Commands?
The native commands cover 80% of what most people need, and they ship with the CLI, so there's no install step. Claude Code's official command reference lists /resume, /continue (alias), /branch, /export, /insights, /rewind, /clear, and /compact (Claude Code Docs, 2026). The first two are how you walk back into a saved conversation.
The four that matter for save-and-reuse:
# Open the session picker for this project — arrow keys, Enter to resume
/resume
# Resume a specific session by UUID or name
/resume 02a0ea76-3694-4307-b1da-65c17cee00a4
# Branch the current conversation — original stays reachable via /resume
/branch experimenting-with-langgraph
# Export the current session to a file (Markdown by default)
/export ~/Desktop/blog-refactor-2026-05-15.md
# Surface patterns across your saved sessions — what tools you use most,
# where loops happened, where you re-prompted
/insights
/resume without an argument is the one I use most. It opens a TUI list of every saved session in the current cwd, with the first user message as the preview, exactly enough context to pick the right one.
The /branch command is the underrated sibling: it forks a conversation at the current turn so you can explore an alternative direction without losing the trunk. I use it when I want to try a riskier refactor that might burn the agent's context — branch, fail, return to the trunk. Cheaper than git stash because no files change.
/export writes the conversation as Markdown by default, ready to drop into a PR description or a postmortem. The output preserves tool calls, which is more than you get from copy-pasting the terminal.
/insights is the newest and most surprising: it runs an analysis pass over your saved sessions and surfaces patterns. The first time I ran it, it told me I was reflexively asking for "a quick fix" 38 times across one project, which was the exact prompt pattern producing the worst output.
How Do You grep Your Own Transcript History?
Because every transcript is a plain JSONL file, the shell is already enough. The patterns below are the ones I run weekly — copy them as a starting kit:
# Count saved sessions in the current project
ls ~/.claude/projects/$(pwd | sed 's#/#-#g')/*.jsonl | wc -l
# Search every transcript ever for a specific phrase
grep -l "Hashnode adapter" ~/.claude/projects/*/*.jsonl
# Pull just the user messages from one session, as plain text
jq -r 'select(.type=="user") | .message.content[0].text // empty' \
~/.claude/projects/-Users-nishil-Documents-work-blogs/02a0ea76*.jsonl
# Find every session where you touched a specific file
grep -l "lib/publish-orchestrator.ts" ~/.claude/projects/*/*.jsonl \
| xargs -I{} basename {} .jsonl
# Sort sessions by total token usage (rough proxy: file size)
du -h ~/.claude/projects/*/*.jsonl | sort -h | tail -10
# Reconstruct a session as plain Markdown in five lines of jq
jq -r '
select(.type=="user" or .type=="assistant")
| "### " + .type + " (" + .timestamp + ")\n\n"
+ ((.message.content[]?
| select(.type=="text") | .text) // "[tool call]")
' SESSION_UUID.jsonl > session.md
The pwd | sed 's#/#-#g' trick is the cheap way to find your current project's transcript folder without leaving the terminal. Pin those one-liners as shell aliases and you have a personal observability layer for Claude Code that costs zero dollars.
the broader pattern of using transcripts as audit evidence during agent code review
Which Open-Source Tools Turn Transcripts Into Real Archives?
Five OSS tools cover the gap between raw grep and a real searchable archive. All five are actively maintained as of May 2026, all five are MIT/Apache-licensed, and all five operate on the same ~/.claude/projects/ directory.
According to a 2026 GitHub stars snapshot, ccusage leads the category with ~14,200 stars (GitHub, 2026), an order of magnitude ahead of the other contenders. That gap reflects the practical priority most teams hit first: cost tracking. Once you've spent a month on Claude Code, you want to know where the tokens went.
What each one is actually for:
-
ccusage (TypeScript, 14.2k★) — Token usage and cost reports per day, per project, per session. The first install for anyone on a Pro/Team plan.
npx ccusage dailyis the entire onboarding. - claude-code-transcripts (Python, 1.5k★) — Converts JSONL into clean, paginated HTML. Mobile-friendly, deterministic output. Good for sharing a session as a link.
- claude-code-log (Python, 1.0k★) — JSONL → readable HTML + Markdown with filtering and token tracking. The best out-of-the-box "make my transcripts browsable" tool.
-
claude-conversation-extractor (Python, 563★) — Pulls conversations out of
~/.claude/projects/and writes Markdown (or JSON/HTML). Lightweight, no dependencies beyond stdlib. - claude-history (Rust, 267★) — Fuzzy search with a built-in TUI. The closest thing to "Spotlight for your Claude Code history."
My setup runs three of them: ccusage weekly for cost reporting, claude-code-log monthly to dump everything into HTML for offline review, and claude-history daily as a TUI when I need to find a specific past prompt.
A short selection guide. ccusage is operational: it answers "where did $400 of API spend go this month" but doesn't show content. claude-code-log and claude-code-transcripts overlap on the export side; both produce HTML, both work fine. claude-conversation-extractor is the right pick if you want zero dependencies and a one-shot Markdown dump. claude-history is the only one with a real TUI and fuzzy search.
All four read the same plaintext JSONL. The data is the moat, not the tools.
How Should You Redact a Transcript Before Sharing It?
Around 100,000+ LLM share-link conversations across ChatGPT, Claude, Copilot, and others were publicly indexed by search engines in 2024-2025 before vendors shut the experiments down (AI Incident Database #1186, 2025). Anthropic stopped Claude share-link transcripts from appearing in Google around September 10, 2025 (Obsidian Security, 2025) — but the lesson stands: a transcript is leaky. Before you paste one into a PR, a Slack channel, or a public gist, redact.
What to scrub:
-
cwdand absolute paths that reveal your username or project layout - API keys (look for
sk-,ghp_,AKIA, anything 40+ chars with no spaces) - Email addresses in user messages
- Internal repo names, customer IDs, ticket numbers
- The
gitBranchfield if it leaks unreleased project names
A pragmatic one-pass redaction with jq plus a regex stage:
# Step 1: extract user + assistant turns only (drop hook/permission noise)
jq -c 'select(.type=="user" or .type=="assistant")' session.jsonl > clean.jsonl
# Step 2: strip filesystem and git metadata, scrub obvious secrets
jq -c '. + {cwd: "[redacted]", gitBranch: "[redacted]"}' clean.jsonl \
| sed -E 's/(sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{20,})/[REDACTED-KEY]/g' \
| sed -E 's/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/[REDACTED-EMAIL]/g' \
> redacted.jsonl
# Step 3: convert to Markdown for the actual share
jq -r 'select(.type=="user" or .type=="assistant")
| "### " + .type + "\n\n"
+ ((.message.content[]? | select(.type=="text") | .text) // "")' \
redacted.jsonl > shareable.md
If you share transcripts often, wrap this in a shell function. The five minutes it takes once is worth more than the recovery effort after you paste a key into a public issue.
One more thing worth checking before you publish a redacted transcript: the assistant's responses sometimes echo your secrets back at you. If you pasted a DATABASE_URL mid-session and the model quoted it in a summary later, regex-scrubbing the input lines isn't enough. Grep the assistant content too, ideally with the same pattern set. A safer habit is to never paste real credentials into a session in the first place — use environment variable names as placeholders and let the agent reason about them abstractly.
My Own Setup: Archive, Index, and Never Lose a Session
I run a four-line cron job and one shell function. That's the entire system.
# crontab -e — nightly archive of yesterday's transcripts to an external drive
0 2 * * * rsync -a --delete \
~/.claude/projects/ /Volumes/Archive/claude-transcripts/
# ~/.zshrc — quick session search in the current project
ccsearch() {
local dir="$HOME/.claude/projects/$(pwd | sed 's#/#-#g')"
grep -l "$1" "$dir"/*.jsonl 2>/dev/null | while read -r f; do
echo " $(basename "$f" .jsonl)"
grep -o "\"text\":\"[^\"]*$1[^\"]*\"" "$f" | head -1
echo
done
}
# ~/.claude/settings.json — extend local retention from 30 days to one year
{
"cleanupPeriodDays": 365
}
The cleanupPeriodDays: 365 setting alone is the highest-leverage change in this entire post. Most people don't realize the 30-day default is destroying their data until they go looking for a session from last month and find an empty directory.
For monthly review, I run npx claude-code-log ~/.claude/projects/ and open the generated HTML index. That's when I learn things — which prompts I repeated, which subagents looped, which tools I never actually used. The HTML output is faster to scan than the TUI because you can Ctrl-F across every session at once.
The cron uses rsync rather than tar deliberately: incremental syncs are cheap for unchanged sessions, and the destination stays browsable. --delete mirrors source deletions, which is fine because cleanupPeriodDays: 365 means nothing is deleted for a year anyway. If you rotate laptops, point the same rsync at a VPS over SSH for an offsite copy.
Our finding: Of my 122 saved sessions in this blog project over four weeks, 19 of them were re-prompts of the same core question phrased three different ways. That ratio — roughly 15% wasted re-prompting — only became visible because I had the JSONL on disk to count.
Frequently Asked Questions
Does Anthropic see my Claude Code transcripts if I'm on a Pro plan?
Yes, for service operation. Anthropic stores conversation data server-side for up to 30 days after you delete a chat, retains opt-in training data for up to 5 years de-identified, and keeps policy-violation conversations for up to 2 years (Anthropic Privacy Center, 2026). Commercial plans (Work, Enterprise, Edu, Gov) are excluded from training by default (Anthropic, 2025). API logs were reduced to a 7-day retention window in September 2025 and are never used for training (Anthropic Privacy Center, 2025).
Where does Claude Code save conversations on Windows?
The cross-platform default is %USERPROFILE%\.claude\projects\ on Windows and ~/.claude/projects/ on macOS and Linux. The encoded-cwd directory naming is identical across platforms: slashes (and backslashes on Windows) get replaced with hyphens. Everything else in this guide (JSONL schema, /resume, cleanupPeriodDays) works the same.
How do I disable Claude Code's 30-day auto-delete?
Set cleanupPeriodDays in ~/.claude/settings.json to a higher number (365 for a year) or to 0 to disable cleanup entirely. The setting is documented in the official Data Usage reference (Claude Code Docs, 2026). Restart your session for the change to apply.
Can I export every saved session at once, not just the current one?
Yes. /export only handles the active session, but claude-code-log or claude-conversation-extractor (both linked above) walk every JSONL file in ~/.claude/projects/ and produce one Markdown or HTML file per session. Run them as a monthly cron job for a rolling archive.
What's the difference between /resume and /continue?
They're aliases. /continue is identical to /resume (Claude Code Docs, 2026). Use whichever feels more natural — the muscle memory matters more than the spelling.
Do hooks have access to the transcript file?
Yes. Every hook payload includes a transcript_path field pointing to the active session's JSONL (Claude Code Docs, 2026). A PostToolUse audit hook can append rich context to its own log, and a Stop hook can summarize the session before ending — the transcript is written in real time, so your hook reads everything up to the current event.
Conclusion
Claude Code's local-first transcript design is one of the most underused features in the CLI. Every session lands as plaintext JSONL, the format is grep-friendly, the schema is stable, and there's a healthy OSS ecosystem turning that data into searchable archives.
The whole workflow is four moves:
-
Save by setting
cleanupPeriodDayspast 30 so transcripts survive long enough to be useful. -
Search with
grep,jq, orclaude-historywhen you need to find a past prompt. -
Export with
/exportfor one session orclaude-code-logfor the whole archive. -
Reuse via
/resumeto jump back into prior context, or/branchto fork it.
Trust in AI tools is at an all-time low and adoption is at an all-time high (Stack Overflow, 2025). The transcript on your disk is the only ground truth you have. Set the retention up tonight; thank yourself in three months.
the next layer of the Claude Code stack to wire into your saved-session workflow
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "BlogPosting",
"headline": "Claude Code Save Conversation: Find & Export Transcripts",
"description": "Where Claude Code saves your conversations, the JSONL schema, and 5 OSS tools to grep, export, and reuse them before the 30-day auto-delete wipes them.",
"datePublished": "2026-06-02",
"dateModified": "2026-06-02",
"author": {
"@type": "Person",
"name": "Nishil Bhave"
},
"image": "https://maketocreate.com/images/generated/claude-code-save-conversation-export-guide-hero-v1-scattered.png",
"url": "https://maketocreate.com/claude-code-save-conversation-export-guide/",
"keywords": ["claude code save conversation", "claude code conversation history", "where does claude code save conversations", "claude code export conversation", "claude code transcripts", "claude code jsonl"]
},
{
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Does Anthropic see my Claude Code transcripts if I'm on a Pro plan?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes, for service operation. On consumer plans Anthropic stores conversation data server-side for up to 30 days after you delete a chat, retains opt-in training data for up to 5 years de-identified, and keeps policy-violation conversations for up to 2 years. Commercial plans (Work, Enterprise, Edu, Gov) are excluded from training by default, and API logs use a 7-day retention window and are never used for training."
}
},
{
"@type": "Question",
"name": "Where does Claude Code save conversations on Windows?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Claude Code saves conversations to %USERPROFILE%\.claude\projects\ on Windows and ~/.claude/projects/ on macOS and Linux. The encoded working-directory folder naming is identical across platforms: path separators are replaced with hyphens. The JSONL schema, /resume, and cleanupPeriodDays all work the same way."
}
},
{
"@type": "Question",
"name": "How do I disable Claude Code's 30-day auto-delete?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Set cleanupPeriodDays in ~/.claude/settings.json to a higher number, such as 365 for a year, or to 0 to disable cleanup entirely. The default is 30 days, after which Claude Code itself purges old transcripts. Restart your session for the change to apply."
}
},
{
"@type": "Question",
"name": "Can I export every saved session at once, not just the current one?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes. The built-in /export command only handles the active session, but open-source tools like claude-code-log and claude-conversation-extractor walk every JSONL file in ~/.claude/projects/ and produce one Markdown or HTML file per session. Run them as a monthly cron job for a rolling archive."
}
},
{
"@type": "Question",
"name": "What's the difference between /resume and /continue?",
"acceptedAnswer": {
"@type": "Answer",
"text": "They are aliases for the same command. /continue is identical to /resume, opening the saved-session picker for the current project directory. Use whichever you prefer."
}
},
{
"@type": "Question",
"name": "Do hooks have access to the transcript file?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes. Every hook payload includes a transcript_path field pointing to the active session's JSONL file. A PostToolUse hook can append context to its own log, and a Stop hook can summarize the session, since the transcript is written in real time up to the current event."
}
}
]
}
]
}



Top comments (0)