npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

claude-desktop-merge

v1.1.0

Published

Merge & sync Claude desktop-app sessions across accounts (macOS, zero-dependency, dry-run by default).

Downloads

40

Readme

claude-desktop-merge

Merge and sync Claude desktop app sessions across accounts — macOS, zero dependencies, dry-run by default.

When you log into a different Claude account, the desktop app shows an empty session list. Your conversations aren't gone — the app just scopes its session index per account. This tool reconciles that: it merges every account's session list into one canonical location and (optionally) symlinks the others to it so they stay in sync.

Not affiliated with Anthropic. It only reorganizes local files the desktop app already wrote; it never uploads anything and never touches your transcripts.

Demo

A dry run discovers every account, prints exactly what it would copy, and changes nothing until you add --apply. There's a full walkthrough page at docs/index.html — open it locally, or serve the docs/ folder with GitHub Pages.

$ npx claude-desktop-merge
claude-desktop-merge (macOS)
BASE: ~/Library/Application Support/Claude
Active account: be60c637

Discovered locations:
  1) be60c637/04127aae   (102 sessions)  [ACTIVE]
       · Refactor auth middleware
       · Wire up billing webhooks
  2) d8755814/f9c0ba6b   (47 sessions)
       · Fix flaky e2e test
       · Draft launch checklist

Choose CANONICAL location (number): 1
Choose SOURCE location(s) — 'a' for all others: a
1) MERGE only  2) MERGE then LINK — choose [1/2]: 1

PLAN
  Canonical: be60c637/04127aae
  Source d8755814/f9c0ba6b
    [claude-code-sessions] copy 47 files, 0 dirs; 0 conflicts
    [agent-mode]           copy 41 files, 6 dirs; 2 conflicts (kept canonical)

  Totals: 88 file(s) + 6 dir(s) to copy, 2 kept, 3 space(s) added

DRY RUN — no filesystem changes were made.
Re-run with --apply to execute the plan above.

Account/org IDs are shortened here for readability; the tool prints the full UUIDs.

Install (macOS)

Pick whichever you like — all three give you a claude-desktop-merge command and none of them require you to install anything first.

1. One-line installer — the simplest. Downloads the tool and puts it on your PATH; if you don't already have a runtime it installs Bun for you.

curl -fsSL https://raw.githubusercontent.com/sebryu/claude-desktop-merge/main/install.sh | bash

2. npm — if you already have Node:

npx claude-desktop-merge            # run once, nothing installed
# or install the command globally:
npm install -g claude-desktop-merge

3. From source — if you want to read or hack on it:

git clone https://github.com/sebryu/claude-desktop-merge.git
cd claude-desktop-merge
bun claude-desktop-merge.ts         # or: node claude-desktop-merge.ts   (Node >= 23.6)

Then run it — it's a dry run that changes nothing:

claude-desktop-merge                # discover accounts, print a plan
claude-desktop-merge --revert       # preview undoing a previous --link

That's the whole tool — one command; everything else is a flag, and nothing changes until you add --apply. See Usage for the flag table.

Uninstall. Installer: rm ~/.local/bin/claude-desktop-merge && rm -rf ~/.local/share/claude-desktop-merge. npm: npm uninstall -g claude-desktop-merge.


The storage model (why this is safe)

The macOS desktop app keeps two different things in two different places:

| | Location | Account-scoped? | |---|---|---| | Transcripts (the actual conversation) | ~/.claude/projects/<encoded-cwd>/<id>.jsonl | No — global, shared with the CLI | | Session index (pointer files: title, model, cwd, archive state) | ~/Library/Application Support/Claude/{claude-code-sessions,local-agent-mode-sessions}/<accountUuid>/<orgUuid>/local_<id>.json | Yes — one folder per account/org | | Routines (scheduled tasks: cron, model, enabled state) | …/{claude-code-sessions,local-agent-mode-sessions}/<accountUuid>/<orgUuid>/scheduled-tasks.json | Yes — one file per account/org, per tree |

Because the transcripts are global, switching accounts never loses a conversation — the desktop UI just stops listing it, since it reads a different <account>/<org> folder. This tool only reorganizes the small pointer files. It never reads, writes, moves, or deletes anything under ~/.claude or any .jsonl.

How it works — canonical merge, then link

  1. MERGE (non-destructive) — pick one canonical <account>/<org>. Copy the pointer files that exist only in other accounts into canonical. Union spaces.json and scheduled-tasks.json (your routines) by id — canonical wins on any collision. Archive flags ride along inside each pointer file.
  2. LINK (opt-in, --link) — back up each other account's folder to *.bak-<timestamp> and replace it with a symlink to canonical, so all accounts share one live set going forward.

The one rule that keeps it consistent: copy into canonical; symlink every other account at canonical. Never copy into a folder you then link away.

Pinned/grouped sessions live in a single global LevelDB keyed by session id, so they follow the merged sessions automatically — the tool reads it read-only to report which pins carry over, and never edits it.

Usage

claude-desktop-merge [flags]

Everything is a dry run until you pass --apply.

# 1. See what's there (interactive picker + plan, no changes)
claude-desktop-merge

# 2. Merge one account's sessions into another
claude-desktop-merge --canonical=<acct>/<org> --sources=<acct>/<org> --apply

# 3. Keep them in sync: quit the desktop app first, then link
claude-desktop-merge --canonical=<acct>/<org> --sources=<acct>/<org> --link --apply

# 4. Changed your mind? Undo the linking (quit the desktop app first)
claude-desktop-merge --revert            # dry-run: preview what gets restored
claude-desktop-merge --revert --apply    # restore the pre-symlink backups

| Flag | Meaning | |---|---| | --canonical=<a>/<o> | Target location that receives the merged sessions | | --sources=<a>/<o>[,...] | Source locations to merge from | | --all-others | Use every non-canonical location as a source | | --link | Also do Step B: back up each source folder and symlink it to canonical | | --revert | Undo a previous --link: restore each *.bak-<ts> backup over its symlink | | --apply | Execute. Without it, the tool is a dry run | | --yes, -y | Skip the confirmation prompt on --apply | | --log-file=<path> | Write the run log here (default: logs/desktop-merge-<ts>.log) | | --no-log | Disable file logging for this run | | --help, -h | Show help |

Reverting a link

--link is fully reversible. It leaves a *.bak-<timestamp> backup next to every folder it turns into a symlink, and --revert walks those backups to put things back:

claude-desktop-merge --revert          # dry-run: show which symlinks would be restored
claude-desktop-merge --revert --apply  # drop each symlink, move the newest backup back
  • It only ever removes a symlink. If a real directory has reappeared at the original path (e.g. the desktop app recreated it), that's reported as a conflict and left untouched — nothing is overwritten.
  • When several backups exist for the same folder, the newest is restored and older ones are left in place.
  • Quit the desktop app first, same as with --link.

The merged copies inside canonical are not touched by revert — reverting only undoes the symlinking (Step B), returning each account to its own independent folder.

Logging

Every run is mirrored to a log file (ANSI colors stripped) so you have a record of exactly what was planned and applied:

logs/desktop-merge-<timestamp>.log     # default, next to the script
  • --log-file=<path> writes the log somewhere else.
  • --no-log turns file logging off for that run.

The logs/ folder is git-ignored.

Safety

  • Dry-run by default — nothing changes without --apply.
  • Never overwrites canonical files; conflicts are reported and canonical is kept.
  • Backs up every folder before replacing it with a symlink (*.bak-<timestamp>), and --revert restores those backups.
  • Read-only on LevelDB; never touches ~/.claude transcripts.
  • Every run is logged to a file for an audit trail.
  • Quit the desktop app before --link or --revert so it doesn't recreate the folders mid-operation.

License

MIT