---
title: "Where Claude Code stores your session history, and how to keep it past 30 days"
description: "Claude Code writes every session to ~/.claude/projects as JSONL and deletes it after 30 days by default. The directory layout, the cleanupPeriodDays setting, how to find and resume old sessions, and how to search inside them."
canonical: https://claudoscope.com/blog/where-claude-code-stores-session-history
author: Liran Baba
datePublished: 2026-09-29
last_updated: 2026-09-29
---

# Where Claude Code stores your session history, and how to keep it past 30 days

*September 29, 2026 · Liran Baba · 7 min read*

The most common question I get after [the database password post](https://claudoscope.com/blog/found-database-password-in-claude-code-session.md) is not about secrets. It is "wait, where are these files, and why did last month's sessions disappear?"

Short answer: Claude Code writes every conversation to `~/.claude/projects/` as a JSONL file, one per session, and a startup sweep deletes anything older than 30 days unless you change one setting. Everything below is the longer answer, checked against the Claude Code docs on September 29, 2026.

## The directory layout

Each session lives at:

```
~/.claude/projects/<project>/<session-id>.jsonl
```

`<project>` is your working directory path with every non-alphanumeric character replaced by a hyphen. A session started in `/Users/liran/projects/claudoscope-website` lands in `~/.claude/projects/-Users-liran-projects-claudoscope-website/`. If the converted name runs past 200 characters, Claude Code truncates it and appends a hash of the full path.

Next to the transcript you may find:

- `<session-id>/subagents/`: transcripts for subagents spawned during the session. They age out with the parent.
- `<session-id>/tool-results/`: large tool outputs that were spilled to separate files, plus full-size copies of images returned by MCP tools.
- `<session-id>.jsonl.superseded-<timestamp>` or `.orphaned-<timestamp>-<suffix>.jsonl`: an earlier copy of the transcript that Claude Code set aside instead of overwriting. These do not appear in the session picker.
- `memory/`: auto memory for the project. The sweep leaves this alone unless it has been empty for the whole retention period.

Elsewhere under `~/.claude/`, `history.jsonl` holds every prompt you have typed with a timestamp and project path (that is what up-arrow recall and `Ctrl+R` search read), `file-history/<session>/` holds the pre-edit snapshots behind checkpoint restore, and `plans/` holds plan-mode output.

On Windows, `~/.claude` means `%USERPROFILE%\.claude`. To move all of it, set `CLAUDE_CONFIG_DIR`.

One thing that trips people up: this is the CLI's history only. The desktop app, claude.ai/code, and the VS Code extension each keep their own session list, and Cowork writes its transcripts under `~/Library/Application Support/Claude/` on a Mac. A session you started in the desktop app will not show up in `claude --resume`, and the CLI's 30-day sweep treats Desktop and Cowork transcripts differently, as described below.

## What is inside a session file

One JSON object per line. Each line is a message, a tool call, a tool result, or a metadata entry. The transcript includes everything Claude read during the session, which is how a `.env` file ends up sitting in plaintext next to your conversation.

Two warnings from experience. First, the format is internal to Claude Code and the docs say it changes between versions, so a script that parses these files can break on any release. If you only need the conversation, `/export` writes a readable text file and does not depend on the format. Second, if you do parse them, the JSONL contains in-progress streaming records with a null `stop_reason`. Sum every record's token counts and you will double-count. I shipped that bug in Claudoscope and did not notice until my cost estimates ran 1.5 to 2x over the invoice.

## The 30-day deletion

At startup, Claude Code runs a retention sweep that deletes files older than `cleanupPeriodDays`. The default is 30 days. The minimum is 1, and setting it to 0 fails validation rather than meaning "forever".

The sweep covers session transcripts, their subagent transcripts and spilled tool results, `plans/`, debug logs, the paste cache, `/insights` reports under `usage-data/`, and a handful of other directories. The docs have the full table.

Two exemptions matter. Transcripts of sessions you started or last continued in Claude Desktop or Cowork are kept at any age unless you set `desktopSessionCleanupPeriodDays`. And if your organization sets `cleanupPeriodDays` through managed settings, that value wins over whatever you put in your own file.

If Claude Code cannot safely determine the retention period, for example because a settings file will not parse, it pauses the sweep and shows a warning in `/status`. So a broken `settings.json` can quietly stop the deletion, and fixing the file will resume it.

## How to keep sessions longer

Add one key to `~/.claude/settings.json`:

```json
{
  "cleanupPeriodDays": 3650
}
```

That is ten years, which in practice means "until I delete them myself." Pick whatever suits you; the value is in days.

Two things to think about before you do. Transcripts are plain text and you can check how much you are holding with `du -sh ~/.claude/projects`. And everything Claude read stays on disk for as long as you keep them, credentials included, so a longer retention period is also a longer window for [something to leak](https://claudoscope.com/blog/found-database-password-in-claude-code-session.md). Scan them, or at least know that they are there.

If you would rather keep the default sweep and archive selectively, a `SessionEnd` hook can copy the transcript somewhere the sweep never looks. Hooks receive a JSON payload on stdin that includes `transcript_path`, so the script is short:

```bash
#!/bin/bash
# ~/.claude/hooks/archive-transcript.sh
path=$(jq -r '.transcript_path')
mkdir -p ~/claude-archive
cp "$path" ~/claude-archive/
```

Register it in `~/.claude/settings.json`:

```json
{
  "hooks": {
    "SessionEnd": [
      { "hooks": [ { "type": "command", "command": "~/.claude/hooks/archive-transcript.sh" } ] }
    ]
  }
}
```

The copy is a plain file outside `~/.claude`, so nothing in Claude Code will touch it again. Whether that is what you want depends on what is inside it; see the warning above.

## Finding a past session from the CLI

Claude Code stores sessions per project directory, and the tools for getting back to one are:

| I want to | Run |
| --- | --- |
| Reopen the last session in this directory | `claude --continue` |
| Pick from a list | `claude --resume`, or `/resume` inside a session |
| Resume a specific one | `claude --resume <name>`, `claude --resume <session-id>`, or `claude --resume <path-to.jsonl>` |
| Find the session that opened a pull request | `claude --from-pr <number>` |
| Name the current session so you can find it later | `/rename auth-refactor`, or start with `claude -n auth-refactor` |
| Save the conversation as text | `/export`, optionally with a filename |

Inside the picker, `/` starts a search, `Space` previews a session, `Ctrl+A` widens the list to every project on the machine, `Ctrl+W` to every worktree of the current repo, and `Ctrl+B` filters to the current git branch. Each row shows the name if you set one, otherwise a generated title from your first prompt, plus time since last activity, branch, and file size.

Sessions started with `claude -p` do not appear in the picker; you can still resume one by passing its id. And the picker can only show what is still on disk, which brings us back to the 30-day sweep: if a session is gone, `/resume` will not find it, and neither will anything else.

## Searching inside sessions

The picker searches names and first prompts, not the conversation body. When I need "the session where I set up the deploy pipeline" and cannot remember what I named it, I have three options.

`grep` works, because the files are text:

```bash
grep -l "deploy pipeline" ~/.claude/projects/*/*.jsonl
```

It is slow on a big corpus and the hits are raw JSON, but it answers the question.

[Claudoscope](https://claudoscope.com/) is the reason I stopped using grep for this. It indexes every session into a local SQLite cache keyed by file size and modification time, so a search across roughly 3,000 sessions and 74 projects comes back in under a second, and clicking a result opens the conversation with the tool calls folded away. There is a Files tab per session listing every file Claude edited with the diff for each edit, which is how I answer "which of the five agents changed this file" after the fact. Nothing leaves the machine; it reads the same directory this post is about.

If the MCP server is on, `search_sessions` and `get_session` expose the same index to Claude Code, so "find the session where I set up the deploy pipeline" is a prompt.

## Deleting what you do not want kept

The opposite problem has a command. `claude project purge <path>` deletes the transcripts, related state, and matching lines in `history.jsonl` for one project, and `--dry-run` shows what it would remove first:

```bash
claude project purge ~/work/my-repo --dry-run
claude project purge ~/work/my-repo
```

For a single session, deleting the `.jsonl` file is enough; the picker stops showing it. For everything, a short `cleanupPeriodDays` does the job on the next launch.

## Quick reference

| Question | Answer |
| --- | --- |
| Where are the files? | `~/.claude/projects/<project>/<session-id>.jsonl` |
| How long are they kept? | 30 days by default (`cleanupPeriodDays`), swept at startup |
| Keep them longer | `"cleanupPeriodDays": 3650` in `~/.claude/settings.json` |
| Move them | `CLAUDE_CONFIG_DIR` |
| Resume the last one | `claude --continue` |
| Find one by name | `claude --resume <name>` |
| Search the contents | `grep` the JSONL, or Claudoscope |
| Delete a project's history | `claude project purge <path>` |

## If you want the browsable version

Claudoscope is free, MIT licensed, and runs on macOS 14 or later with Apple Silicon:

```bash
brew tap cordwainersmith/claudoscope
brew install --cask claudoscope
```

It will not stop the 30-day sweep for you; that is Claude Code's setting and I did not want an app silently editing your `settings.json`. Set the key yourself, and Claudoscope will index whatever survives.

## About the author

Liran Baba builds and maintains Claudoscope, and has been running Claude Code daily across roughly 74 projects since well before there was anything to measure it with. More at [liranbaba.dev](https://liranbaba.dev) and [GitHub](https://github.com/cordwainersmith).

---

Claudoscope is an independent open-source project and is not affiliated with or endorsed by Anthropic.

## Sitemap

- [All pages](https://claudoscope.com/sitemap.md)
