/add-dir writes a persistent, bidirectional project→project link (.session-aliases) that merges an entire other project's sessions into /resume

Status Open
Maintainer reply ✓ Yes — bcherny
Activity 4 comments · opened Jul 9, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

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

  1. cd project-a && claude → /add-dir /abs/path/to/project-b → exit.
  2. cd project-a && claude --resume → the default view (not Ctrl+A) now lists all of project-b's sessions.
  3. cd project-b && claude --resume → lists project-a's sessions too (bidirectional).
  4. Survives restarts. cat ~/.claude/projects/<sanitized-a>/.session-aliases shows 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

  1. Granularity: docs promise "sessions … that added the current directory" (per-session); the code merges the whole project folder (per-project).
  2. Scope: a session-only /add-dir produces a permanent link.
  3. Direction: adding B while in A also makes A appear in B's picker — never requested.
  4. Visibility: stored in a hidden, misleadingly named file (.session-aliases reads 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.
  5. No pruning: links never expire; stale .session-aliases.bak files 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.

View original on GitHub ↗

3 Comments

bcherny collaborator · 1 month ago

Partially confirmed on 2.1.233 (Linux).

What still reproduces:

  • A session-only /add-dir ("Yes, for this session") still writes a persistent hidden link that survives the session.
  • Wholesale merge: after one /add-dir B from 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:

  • The bidirectional part: the link is now one-directional (adding B from A surfaces A's sessions in B's picker, but not B's in A's), and the link file is stored on the added project's side.

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

marcindulak · 26 days ago

For your information: the label reproduced on 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.

zeroarst · 25 days ago

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 B from A appends A's project folder to projects/<B>/.session-aliases, exactly as @bcherny describes. But two /add-dir calls 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-dir writes a link. In 2.1.278 the sole writer of .session-aliases is recordSessionAlias, and its only caller is the /add-dir command 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 launch
  • permissions.additionalDirectories in a settings file

That is a usable workaround for permanently related directories: put them in additionalDirectories and never /add-dir them. It also looks like an inconsistency worth settling in whichever direction you prefer. Relatedly, /add-dir returns early when the path is already a working directory, so once a directory is in additionalDirectories, re-running /add-dir on 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 sessionPersistenceDisabled launch 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 isAlias flag 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.

Showing cached comments. Read the full discussion on GitHub ↗