If you've lost track of a Claude Code session and need to recover a plan, a todo list, or a specific decision buried in the transcript, you can find it by searching the local .jsonl session files Claude Code stores on disk, then export that file into a readable Markdown document. This guide walks through locating the right session by agent or plan name, inspecting the raw transcript, and converting it into something you can actually read and search.
I ran into this last week while working on a multi-step product onboarding flow for an Etsy-style storefront. I'd planned out a whole approach in a Claude Code session days earlier, moved on to other work, and then needed to go back and check exactly what we'd agreed on. The session was still on disk, just buried inside a raw JSONL file with every tool call and tool result mixed in with the actual plan. Here's the workflow I used to dig it out.
Add line numbers if you'll need to reference specific spots later:
bash
nl -ba "$FILE" | less
Or jump straight to a line range you already suspect is relevant:
bash
nl -ba "$FILE" | sed -n '80,220p'
This works for a quick look, but raw JSONL is awkward to read for anything longer than a few lines, since every message is wrapped in tool call metadata and JSON escaping. For anything beyond a quick peek, export it.
Exporting the Session to Readable Markdown
This Python script reads the JSONL file line by line, pulls out the role and content of each message, and prints it as Markdown with clear ## USER / ## ASSISTANT headers. It also surfaces tool calls and tool results so you can see what Claude actually ran, not just what it said.
python
# File: export_session.pyimport json
file = "/home/matija/.claude/projects/-home-matija-etsy-slo/d54cb123-ae10-48f3-9e8f-bb805061cc88.jsonl"withopen(file, "r", encoding="utf-8") as f:
for line in f:
try:
obj = json.loads(line)
except Exception:
continue
msg = obj.get("message") or {}
role = msg.get("role")
content = msg.get("content")
ifnot role ornot content:
continueprint(f"\n\n## {role.upper()}\n")
ifisinstance(content, str):
print(content)
elifisinstance(content, list):
for item in content:
ifnotisinstance(item, dict):
continueif item.get("type") == "text":
print(item.get("text", ""))
elif item.get("type") == "tool_use":
print("\n```txt")
print("TOOL:", item.get("name"))
print(item.get("input"))
print("```")
elif item.get("type") == "tool_result":
print("\n```txt")
print("TOOL RESULT:")
result = item.get("content")
ifisinstance(result, str):
print(result[:4000])
else:
print(str(result)[:4000])
print("```")
Each tool result gets truncated to 4000 characters. That keeps massive file dumps or long command outputs from blowing up the export, while still leaving enough context to understand what happened at each step.
Run it and redirect the output into a Markdown file:
Open the Markdown export the same way you'd read any long document:
bash
less /home/matija/etsy-slo/multi-step-product-onboarding-session.md
Inside less, search forward with / followed by your term:
txt
/onboarding
Press n to jump to the next match. This is usually faster than scrolling once you know roughly what you're looking for.
Filtering Down to the Relevant Sections
A full session export can run thousands of lines once tool calls and results are included. To pull out just the parts related to your plan, grep the exported Markdown for the keywords that matter:
bash
grep -niE 'multi|step|product|onboarding|plan|todo|task|approach|implement' /home/matija/etsy-slo/multi-step-product-onboarding-session.md | head -100
Save the matches to their own file if you want something you can hand off or reference later without scrolling through the full export:
If you want to pick the conversation back up instead of just reading it, Claude Code can resume a session directly using its ID. Run the resume command from the same project directory the session was originally created in, since the working directory affects what Claude can see and access:
bash
cd /home/matija/etsy-slo
claude --resume d54cb123-ae10-48f3-9e8f-bb805061cc88
Using the Built-In Export Command
Claude Code also ships with a built-in export command you can run from inside an active session:
txt
/export session-name.md
This produces a readable Markdown export directly, without writing or running the Python script above. It's the quickest path when you're exporting the session you're currently in. The manual JSONL parsing approach earns its place when you need to recover a session you're no longer inside of, want full control over formatting, or need to script the export across many sessions at once.
Quick Reference Script
Here's the whole workflow as one block, ready to adapt for a different session:
bash
# File: export_and_view.sh
FILE="/home/matija/.claude/projects/-home-matija-etsy-slo/d54cb123-ae10-48f3-9e8f-bb805061cc88.jsonl"
OUT="/home/matija/etsy-slo/multi-step-product-onboarding-session.md"
python3 - <<'PY' > "$OUT"
import json
file = "/home/matija/.claude/projects/-home-matija-etsy-slo/d54cb123-ae10-48f3-9e8f-bb805061cc88.jsonl"
with open(file, "r", encoding="utf-8") as f:
for line in f:
try:
obj = json.loads(line)
except Exception:
continue
msg = obj.get("message") or {}
role = msg.get("role")
content = msg.get("content")
if not role or not content:
continueprint(f"\n\n## {role.upper()}\n")
if isinstance(content, str):
print(content)
elif isinstance(content, list):
for item in content:
if isinstance(item, dict) and item.get("type") == "text":
print(item.get("text", ""))
PY
less "$OUT"
Swap in your own FILE path and OUT destination, and this gives you a one-shot export-and-read command for any session.
FAQ
Where exactly does Claude Code store session files on disk?
Under ~/.claude/projects/<encoded-project-path>/<session-id>.jsonl, where the encoded project path replaces every slash in your project's absolute path with a dash.
How do I find a session if I only remember a rough description, not the exact agent name?
Grep with a broader set of keywords across ~/.claude/projects, ~/.claude/tasks, and ~/.claude/plans. Partial matches on distinctive words from the plan usually surface the right file.
Why does the export script truncate tool results to 4000 characters?
Long tool outputs, like full file dumps or verbose command logs, can make the exported Markdown unwieldy. Truncating keeps the file readable while preserving enough of each result to follow the session's logic.
Can I export a session I'm not currently inside of?
Yes. The Python script works against any .jsonl file on disk regardless of whether that session is active. The built-in /export command only works for the session you're currently running.
Does resuming a session require being in the original project folder?
Yes. Claude Code's resume command ties the session to the working directory it was created in, so running claude --resume from a different folder can change what Claude sees and is able to do.
Wrapping Up
Recovering a Claude Code plan you thought you'd lost comes down to three steps: locate the right .jsonl file by grepping for a name you remember, convert it into Markdown so it's actually readable, and search that export for the specific decision or task you need. Once you've done this a couple of times, the whole loop takes a couple of minutes, and you stop worrying about losing context between sessions.
Let me know in the comments if you have questions, and subscribe for more practical development guides.