{"slug": "show-hn-migrating-from-claude-code-cli-to-claude-desktop", "title": "Show HN: Migrating from Claude Code CLI to Claude Desktop", "summary": "A developer released claude-profiles, an open-source CLI and Raycast extension that create isolated Claude Desktop accounts and move Claude Code session history between them by rebuilding the per-profile session index. The tool, installable via pip, adds commands including profile, list, import, migrate, backup, optimize, undo and inspect, and writes a rollback copy plus a run manifest under $XDG_STATE_HOME/claude-profiles/runs/ before any writing operation. The CLI supports macOS and Linux, while the Raycast extension is macOS only, and both read the same profile registry.", "body_md": "Run several isolated Claude Desktop accounts side by side, and move Claude Code session history between them.\n\nTwo parts:\n\n- **`claude-profiles`** , a CLI that creates profiles, launches them, and moves\nsession history into them. macOS and Linux.\n- A **Raycast extension** that does the profile half from the Raycast launcher.\nmacOS only. Both read the same profile registry.\n\nA new profile starts with an empty Claude Code session list even though every transcript is still on disk, because the list comes from a per-profile index and the transcripts live in one shared store. Most of this tool exists to build and move that index.\n\nInstall the CLI:\n\n```\npip install -e .\n```\n\nCreate a profile and sign in to it:\n\n```\nclaude-profiles profile add \"Personal\"\n```\n\nSigning in matters: it creates the `<account>/<org>` directory every migration\nwrites into. Quit the profile afterwards — every writing command refuses a\nprofile with a live Claude instance.\n\nBack up before moving anything:\n\n```\nclaude-profiles backup --all\n```\n\nSee which CLI sessions exist and which the profile already has:\n\n```\nclaude-profiles list --profile personal\n```\n\nImport the five most recent, then check them in the app:\n\n```\nclaude-profiles import --to personal --limit 5\n```\n\nIf anything looks wrong, reverse it:\n\n```\nclaude-profiles undo --last\n```\n\n`--to` and `--profile` take a registered profile id, a profile name, or a path.\n\n| Command | What it does | \n|---|---|\n| `profile add\\|list\\|open\\|rm` | create, launch and forget isolated profiles | \n| `list` | list Claude Code CLI sessions, marked by what an import would do | \n| `import` | generate index entries so CLI sessions appear in a profile | \n| `migrate` | copy the session index from one profile to another | \n| `backup` | snapshot profile session state, excluding runtime and credentials | \n| `optimize` | clear stale error state, connector caches and dead scratch paths | \n| `undo` | reverse any run that wrote something | \n| `inspect` | print the shape of an index entry or a transcript | \n\n`claude-profiles <command> --help` carries the detail. Start with `--dry-run` on\nanything that writes.\n\n| Mark | Meaning | \n|---|---|\n| `*` | already indexed in the target profile | \n| `+` | already there, absorbed into a later session as a prior CLI id | \n| `-` | no user content at all, always skipped | \n| `~` | only built-in slash commands, skipped by `--exclude-rote-commands` | \n| `s` | ran in a desktop scratch workspace, see `--scratch-sessions` | \n\nEvery command that writes takes a rollback copy first and records what it did in\na run manifest under `$XDG_STATE_HOME/claude-profiles/runs/`. `claude-profiles undo --list` shows them; `undo <run>` reverses one, whole or one record at a\ntime with `--only`.\n\n`import` additionally:\n\n- never opens a source transcript for writing, and records a SHA-256 before and after each import, failing if the file moved\n- writes nothing into `~/.claude` at all unless`--remap` is used, in which case\nthe rewritten copy is published under a**new** session id and the original\nstays addressable\n- refuses to import a subagent sidechain as a session\n- verifies after writing that no working-directory field in a generated entry still names the template session's folder\n- resets every per-session permission grant, so approvals given to one session never ride along to another\n\n- [Session storage](https://github.com/calebbarzee/claude-profiles/blob/main/docs/session-storage.md) — the two locations, project slugs,\nsubagents, scratch workspaces, and how duplicates are detected\n- [The session index](https://github.com/calebbarzee/claude-profiles/blob/main/docs/session-index.md) — field policy, permissions, and\nthe working-directory rule\n- [Linux](https://github.com/calebbarzee/claude-profiles/blob/main/docs/linux.md) — install, paths, and the running-window caveat\n- [Verifying on Linux](https://github.com/calebbarzee/claude-profiles/blob/main/docs/verify-on-linux.md) — the container test\n\n```\npython3 -m venv .venv && .venv/bin/pip install -e '.[dev]'\n.venv/bin/pytest && .venv/bin/ruff check . && .venv/bin/mypy claude_profiles\n```\n\nThe Raycast extension lives in `raycast/`:\n\n```\ncd raycast && npm install && npm run dev\n```\n\n`tools/` holds forensic scripts that document undocumented formats. Re-running\none after a Claude Desktop update tells you whether its finding still holds.\nThey are macOS-only.\n\n| Script | Establishes | \n|---|---|\n| `trace-subagents.sh` | the sidechain layout and the `promptId` join | \n| `permission-audit.sh` | where permission settings live, and their valid enums | \n| `sample-brand-color.sh` | the brand hex, sampled from an app icon | \n| `make-icon.sh` | rebuilds `raycast/assets/icon.png` from`swap_icon.svg` | \n\n`raycast/assets/icon.png` is tinted `#D97757`, the most common opaque non-white pixel in\nClaude Desktop's own icon. Rebuilding it needs `rsvg-convert` (`brew install librsvg`, or `apt install librsvg2-bin`). The source artwork is \"swap\" by Evan\nShuster, from the Noun Project.\n\nEach profile is a fully separate Claude account. There is no shared memory, chat history, or context between them. This makes switching faster; it does not merge accounts.\n\nClaude Desktop checks whether its `userData` path is the default location. Any\nprofile launched by this tool fails that check, which disables local pairing in\nthat instance. Your default profile, launched normally, is unaffected.\n\nThe index schema is undocumented and changes between app versions. Re-run the\nscripts in `tools/` after an update before trusting a large migration.\n\nMIT. See [LICENSE](https://github.com/calebbarzee/claude-profiles/blob/main/LICENSE).", "url": "https://wpnews.pro/news/show-hn-migrating-from-claude-code-cli-to-claude-desktop", "canonical_source": "https://github.com/calebbarzee/claude-profiles", "published_at": "2026-09-20 03:11:38+00:00", "updated_at": "2026-09-20 03:52:53.216047+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-products"], "entities": ["Claude Code", "Claude Desktop", "claude-profiles", "Raycast", "Anthropic"], "alternates": {"html": "https://wpnews.pro/news/show-hn-migrating-from-claude-code-cli-to-claude-desktop", "markdown": "https://wpnews.pro/news/show-hn-migrating-from-claude-code-cli-to-claude-desktop.md", "text": "https://wpnews.pro/news/show-hn-migrating-from-claude-code-cli-to-claude-desktop.txt", "jsonld": "https://wpnews.pro/news/show-hn-migrating-from-claude-code-cli-to-claude-desktop.jsonld"}}