/add-dir writes a persistent, bidirectional project→project link (.session-aliases) that merges an entire other project's sessions into /resume
Environment
- Claude Code v2.1.205 (native installer build)
- macOS, Darwin 23.6.0
- Reproduces in a plain terminal and in the VS Code integrated terminal
Summary
The docs state the picker shows, by default, "sessions from the current worktree, plus sessions started elsewhere that added the current directory with /add-dir."
In practice, once you /add-dir B while working in project A — even as a session-only add (not "remember", it doesn't touch settings.local.json) — Claude Code writes a persistent link into a hidden file, and the picker then merges all of B's sessions into A's picker (and, bidirectionally, all of A's into B's). It is per-project, not per-session; persistent, not session-scoped; and bidirectional.
Mechanism / root cause
~/.claude/projects/<sanitized-A>/.session-aliases is a newline-delimited list of other project folders whose sessions get merged into A's resume picker. /add-dir (and resuming a session from another directory) appends the other project's folder path to it. The picker (loadSameRepoMessageLogs → getStatOnlyLogsForWorktrees) then reads those folders wholesale.
Empirical proof via --debug-file, run from project A:
/resume: loading sessions for cwd=/…/project-a, worktrees=[/…/project-a]
/resume: found 600 session files on disk
Project A has 18 own sessions on disk. The other 582 are project B's entire history. Emptying A/.session-aliases and re-running → found 18. So a single shared /add-dir pulled in 582 unrelated sessions, of which exactly one had ever added the current directory.
Steps to reproduce
cd project-a && claude→/add-dir /abs/path/to/project-b→ exit.cd project-a && claude --resume→ the default view (not Ctrl+A) now lists all of project-b's sessions.cd project-b && claude --resume→ lists project-a's sessions too (bidirectional).- Survives restarts.
cat ~/.claude/projects/<sanitized-a>/.session-aliasesshows project-b's folder path.
(Sanitized folder = the project's absolute path with non-alphanumeric chars replaced by -.)
Expected
Per the docs, only the specific session(s) that actually added the current directory via /add-dir should surface — not the entire other project.
Problems this causes
- Granularity: docs promise "sessions … that added the current directory" (per-session); the code merges the whole project folder (per-project).
- Scope: a session-only
/add-dirproduces a permanent link. - Direction: adding B while in A also makes A appear in B's picker — never requested.
- Visibility: stored in a hidden, misleadingly named file (
.session-aliasesreads like Ctrl+R rename aliases, but it holds folder paths); not surfaced by/add-dir,/permissions,/config, or~/.claude.json; no UI to inspect or remove it. Effectively undebuggable. - No pruning: links never expire; stale
.session-aliases.bakfiles can even resurrect old links.
Suggested fix (take with a grain of salt, fix it however you want)
- Key the association by session, not by project folder. Transcripts already record every
cwd/added directory per line, so the picker can surface exactly the sessions that touched the current dir — self-cleaning, no side file needed. If a persisted index is wanted for performance, store foreign session references, not foreign project folders. - Don't persist a link for a session-only
/add-dir. - Make it one-directional (or make the reverse an explicit opt-in).
- Give it a management surface (related: #27935 requests
/remove-dir) and an honest filename.
Workaround
rm ~/.claude/projects/<sanitized-project>/.session-aliases
Not a duplicate
Distinct from the "shows ALL projects by default" regression (#46887, #46445, #46688, #46784). This is the specific /add-dir link that merges one other project's sessions wholesale, persistently, and bidirectionally, via .session-aliases.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Partially confirmed on 2.1.233 (Linux).
What still reproduces:
/add-dir("Yes, for this session") still writes a persistent hidden link that survives the session./add-dir Bfrom project A, B's default resume picker lists all of A's sessions — including a fresh A session that never added B. The docs promise only "sessions started elsewhere that added the current directory", so this over-broad merge is a bug we'll track.What no longer reproduces on 2.1.233:
There's still no built-in way to inspect or remove these links (related: #27935); deleting the hidden file remains the workaround.
🤖 Generated with Claude Code
For your information: the label
reproducedon this issue does not prevent it from becoming stale and eventually auto-closed. See https://github.com/anthropics/claude-code/issues/87647 for details, and consider upvoting that for visibility.Still present on v2.1.278 (Linux, WSL2). A few findings from reading the 2.1.278 bundle that aren't in the thread yet.
The one-directional form is easy to mistake for the old bidirectional bug.
/add-dir Bfrom A appends A's project folder toprojects/<B>/.session-aliases, exactly as @bcherny describes. But two/add-dircalls in opposite directions, made months apart in ordinary use, produce two one-directional links that are indistinguishable from the bidirectional bug at the UI. On this machine project A's file lists B and B's lists A, from two separate adds. Worth keeping in mind when triaging "it's bidirectional again" reports.Second data point on scale: 25 link files accumulated in one config dir, 8 in another. The project where I noticed it has 17 sessions of its own; the picker listed 89, the other 72 being an unrelated project's history.
Only the interactive
/add-dirwrites a link. In 2.1.278 the sole writer of.session-aliasesisrecordSessionAlias, and its only caller is the/add-dircommand handler. Both dialog options write it, "for this session" and "save to local settings" alike. Two other ways of adding working directories never reach it:claude --add-dir <path>at launchpermissions.additionalDirectoriesin a settings fileThat is a usable workaround for permanently related directories: put them in
additionalDirectoriesand never/add-dirthem. It also looks like an inconsistency worth settling in whichever direction you prefer. Relatedly,/add-dirreturns early when the path is already a working directory, so once a directory is inadditionalDirectories, re-running/add-diron it writes nothing.There is still no targeted opt-out. The recording call is gated only by the global session-persistence check (test env, the
sessionPersistenceDisabledlaunch option,CLAUDE_CODE_SKIP_PROMPT_HISTORY, nested-session marker). Each of those also disables transcripts and resume, so none works as a switch for this alone.Alias-sourced sessions bypass the Ctrl+W worktree filter. They carry an
isAliasflag that the worktree filter passes through unconditionally. Ctrl+B branch filtering does still apply to them. So there is no way to narrow them out of the default view once the link exists.