[FEATURE] Project-local session storage: keep transcripts in the project instead of ~/.claude/projects/<path-slug>
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
<!-- To the triage bot and to whoever triages this by hand: please read the "Why this is filed again" section below before marking this a duplicate. Every prior filing of this request is CLOSED and LOCKED, so this is the only way left to raise it, and it is what those locks explicitly instruct. -->
Problem Statement
Claude Code stores a project's session transcripts in ~/.claude/projects/<slug-of-absolute-cwd>/, where the slug is the absolute working-directory path with separators replaced by hyphens. The path is the primary key. That has four consequences that a project-local store would not have:
- The history is not portable with the project. Move, rename, or re-clone the checkout and every past session for it becomes unreachable — the transcripts still exist, but under a slug nobody will ever compute again. Tracked from the bug side in #5768 (60 👍, open since 2025-08), #83146, #86713 and #33636.
- The history does not travel between machines. Working on the same repository from a laptop and a workstation gives two disjoint, unmergeable histories, because the two absolute paths differ.
- Git worktrees fragment it. Each worktree of the same repository is a different absolute path and therefore a different project, so sessions silently split across stores (#34437, 45 👍; #91780). For anyone whose normal workflow is one worktree per feature branch, the history shatters by construction.
- The slug is not injective, so different projects can collide in one store. Non-ASCII and punctuation normalisation maps distinct paths onto the same directory: #93960, #91735, #88218, #70674, #29471. A project-relative store has no encoding step and therefore no collision class.
None of these is a coding error in the slug function — they are all downstream of storing per-project data outside the project, keyed by a path. Every individual bug above gets fixed in isolation and the next one arrives.
The two things users actually want and cannot have today: keeping a project's conversation history with the project (backup, archival, audit, handover), and optionally sharing it with the team the way .claude/settings.json, .claude/commands/ and CLAUDE.md are already shared.
Proposed Solution
A setting that puts the session store inside the project, next to the project configuration that already lives there:
// .claude/settings.json or .claude/settings.local.json
{
"sessionStorage": "local" // "global" (default) | "local"
}
With "local":
- Transcripts are written to
<project>/.claude/sessions/instead of~/.claude/projects/<slug>/. - Session discovery (
--continue,--resume,/resume, the IDE and desktop session lists) reads the project-local store for that project, in addition to the global one, so nothing already recorded disappears. - On first use in a project,
.claude/sessions/is added to.gitignoreunless the user opts into committing it — the default should be private-by-default, with sharing an explicit choice. - Nothing about the JSONL format changes; only the location does.
Why a setting rather than a change of default: the global store is right for throwaway work in /tmp, for directories that are not repositories, and for anyone who does not want conversation text on a work filesystem. This should be opt-in per project, resolved through the normal settings precedence.
Secondary, and cheap: whichever store a session lives in, resolve it by session id as well as by path, so a session found in a project-local store stays resumable after the checkout moves. That is the durable fix behind #5768.
Alternative Solutions
CLAUDE_CONFIG_DIR is the only documented lever today, and it is the wrong shape for this. It relocates the entire home-directory tree — "settings, session history, and plugins", per the settings docs — so pointing it at a project means that project gets its own credentials, plugin set, skills and user settings too. Per-project use via direnv therefore costs a re-login per project and loses every global customisation. It also still has to be set identically on every machine, so it does not solve portability, and it carries its own trap: a ~ in the value is treated as a cwd-relative path and writes the whole config tree into a literal ./~/ inside the project (#78988).
/cd rebinds a running session to another directory. It helps after the fact, for one session, if you remember to use it before the move. It does not make a project's accumulated history portable.
Third-party tooling. People have written tools to copy transcripts between slug directories (claude-cp, referenced in #1516). That such tools exist, and that #90221 asks for a supported way to compute the slug because external tools keep breaking on it, is evidence the current location is load-bearing in places it was not designed to be.
Symlinking ~/.claude/projects/<slug> into the repo works until the path changes, which is the case this request is about.
Priority
High - Significant impact on productivity
Feature Category
Configuration and settings
Use Case Example
A multi-module Kotlin Multiplatform repository, developed with one git worktree per feature branch, on Linux, Claude Code 2.1.278:
git worktree add ../repo-feature-ios feature/ios-appand start Claude Code there. Several long sessions accumulate — design decisions, rejected approaches, the reasoning behind a workaround that is now a comment in the code.- The branch merges; the worktree is removed. Every one of those sessions is now orphaned under
~/.claude/projects/-home-user-dev-repo-feature-ios/, which no future working directory will ever produce again. - A colleague picks up the follow-up work in the main checkout. None of that history is visible to them, and none of it is visible to me from the main checkout either, although it is the same repository and the same branch history.
- With
"sessionStorage": "local", the transcripts sit in.claude/sessions/in the worktree. They move, archive, or get committed with the branch like anything else in the repo, and/resumein the checkout finds the work that belongs to that checkout.
The same holds for the ordinary single-checkout case: reorganising ~/projects into ~/work/client/ costs the entire history of every project moved, with no warning and no migration path.
Additional Context
Why this is filed again. This request has been made at least five times and has never once been read on its merits — each filing was auto-closed as a duplicate, and the targets are themselves closed:
| Issue | Filed | Outcome |
|---|---|---|
| #4050 Project-specific session storage configuration | 2025-07-21 | bot: duplicate of #4120 → closed, locked |
| #4120 Todos should be project-specific | 2025-07-22 | bot: duplicate of #4050 → closed. The two were closed as duplicates of each other |
| #9084 Optional local .claude_history file | 2025-10-07 | autoclosed after 60 days of inactivity |
| #9306 Project-Local Conversation History Storage | 2025-10-10 | duplicate of #9084, which then went stale |
| #20622 Allow session storage in project directory | 2026-01-24 | duplicate of #9306 — already closed at the time |
| #22387 …NO DUPLICATE | 2026-02-01 | duplicate of #20622. The author had written in the body: "if you close it again because some assumed duplication I give up." |
All six are locked. Every one of them proposed the same "sessionStorage": "local" key, independently. None has a single reply from a maintainer, and none accumulated more than one 👍 — not for lack of interest, but because none stayed open long enough to collect any. The dedup chain manufactured the appearance of a settled question out of six people asking the same unanswered one.
Each of those locked issues ends with: "If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant." This is that issue.
Related open issues this would address or reduce: #5768 (60 👍) · #25947 (44 👍, the same request for memory files, still open and still active) · #34437 (45 👍, worktrees) · #83146 · #86713 · #91780 · #93960 · #91735 · #88218 · #90221 · #78988. #1455 (431 👍, XDG) is adjacent but orthogonal: it asks where the global directory lives, not whether per-project data should be in the project.
Prior art: every tool that keeps per-project state keeps it in the project — .git/, node_modules/, .venv/, .idea/, .vscode/. Claude Code already does this for CLAUDE.md, .claude/settings.json, .claude/commands/, .claude/agents/ and .claude/skills/. Session transcripts are the conspicuous exception, and they are the per-project data users most want to keep.
Environment: Claude Code 2.1.278, Linux 6.8.0.