4 min read
AI assisted

Sharing Claude Code Sessions via Symlinked .jsonl

Resume the same Claude Code session across accounts by pointing every projects directory at one shared physical path

Series — Multi-Account Claude Code Operations
  1. 1. Multi-Account Claude Code via CLAUDE_CONFIG_DIR
  2. 2. Sharing Claude Code Sessions via Symlinked .jsonl
  3. 3. Claude Code Settings Sync and Troubleshooting

Multi-account setup introduces a new problem: sessions created in account A don't appear in account B. Each account's resume list is built from its own projects directory. Switching accounts means losing the conversation context you were just in — which undermines half the value of running multiple accounts seamlessly.

The fix is to point every account's projects directory at the same physical path.

Claude Code builds its resumable-session list by scanning .jsonl files in the projects directory directly. To share that list across multiple accounts on the same machine, point each account's projects directory at the same physical directory via symlink.


How it works

When claude --resume opens the resume dialog, it scans the projects directory for every .jsonl file and builds the session list in memory. The physical location of the files is what matters — a symlink is transparent to the scan.

~/.claude-shared/projects/              <- physical directory (all .jsonl files live here)
├── -Users-jaesolshin/
│   └── *.jsonl
├── -private-tmp/
│   └── *.jsonl
└── ...

~/.claude/projects        -> symlink -> ~/.claude-shared/projects/
~/.claude-2/projects      -> symlink -> ~/.claude-shared/projects/
~/.claude-3/projects      -> symlink -> ~/.claude-shared/projects/
~/.claude-4/projects      -> symlink -> ~/.claude-shared/projects/

All accounts point at the same physical directory. A session created in one account is visible from any other — no sync step, no polling.


Directory layout and symlinks

The shared physical directory lives at ~/.claude-shared/projects/. Each account's projects path is replaced with a symlink pointing there:

~/.claude/
├── settings.json
├── history.jsonl
└── projects/    -> ~/.claude-shared/projects/

~/.claude-2/
├── settings.json
├── history.jsonl
└── projects/    -> ~/.claude-shared/projects/

~/.claude-shared/projects/
├── -Users-jaesolshin/
│   └── session-abc123.jsonl
└── -private-tmp/
    └── session-ghi789.jsonl

Migration

Run the initialization script once:

bash ~/.claude/scripts/sessions-shared-init.sh

The script runs three phases. Phase 1 rsyncs each account's existing sessions into ~/.claude-shared/projects/ with --ignore-existing to deduplicate:

for ACCT in ~/.claude ~/.claude-2 ~/.claude-3; do
    if [ -d "$ACCT/projects" ] && [ ! -L "$ACCT/projects" ]; then
        rsync -a --ignore-existing "$ACCT/projects/" ~/.claude-shared/projects/
    fi
done

Phase 2 backs up each original projects directory with a timestamp:

for ACCT in ~/.claude ~/.claude-[0-9]*; do
    BACKUP="$ACCT/projects.bak.$(date +%s)"
    mv "$ACCT/projects" "$BACKUP"
done

Phase 3 creates the symlinks:

for ACCT in ~/.claude ~/.claude-[0-9]*; do
    ln -sf ~/.claude-shared/projects "$ACCT/projects"
done

To roll back to independent per-account directories:

for ACCT in ~/.claude ~/.claude-[0-9]*; do
    [ -L "$ACCT/projects" ] || continue
    rm "$ACCT/projects"
    BACKUP=$(ls -td "$ACCT"/projects.bak.* 2>/dev/null | head -1)
    [ -d "$BACKUP" ] && mv "$BACKUP" "$ACCT/projects"
    echo "Restored $ACCT/projects"
done

To carry sessions created after the symlink into the restored directory:

rsync -a --update ~/.claude-shared/projects/ "$ACCT/projects/"

Session resume behavior

Because every account's projects path resolves to the same physical directory, the .jsonl scan returns an identical list regardless of which account is active. A session started under account 1 (claude -1) appears in the resume list when account 2 (claude -2) is launched.

# Start a session under account 1
claude -1
# ... do some work, then exit

# Switch to account 2 — the session from account 1 appears in the Resume list
claude -2

Session IDs are stable across accounts. A known ID can be resumed directly from any account:

claude --resume <session-id>

Operational notes

Ownership. On a single-user Mac, ~/.claude-shared/ is owned by the running user automatically. No ownership changes are needed.

Concurrent writes. Each session gets a unique .jsonl filename, so two processes writing simultaneously do not collide. External tools that maintain sessions-index.json can race, but --resume reads .jsonl directly and is unaffected.

Settings stay isolated. The shared directory contains only session conversation files. settings.json, CLAUDE.md, history.jsonl, commands/, hooks/, and memory/ all remain per-account — the symlink touches nothing outside projects/.

Memory sharing (optional). To extend the same pattern to memory/:

mkdir -p ~/.claude-shared/memory
rsync -a --ignore-existing ~/.claude/memory/ ~/.claude-shared/memory/

for ACCT in ~/.claude ~/.claude-[0-9]*; do
    rm -rf "$ACCT/memory"
    ln -sf ~/.claude-shared/memory "$ACCT/memory"
done

Without this step, memory remains per-account.


Series position

Ep1 covered CLAUDE_CONFIG_DIR account isolation. Ep3 covers settings synchronization with sync-claude.sh — distributing settings.json, CLAUDE.md, commands, and hooks across accounts while keeping per-account overrides intact.