Move or rename Claude Code projects without losing conversation history or settings.
Claude Code stores project context at ~/.claude/projects/ using a path derived from the project directory. Session files, settings, and history entries all contain the absolute path. Moving or renaming the project directory breaks these references. claude -c says "no conversations to continue".
Updates all path references so Claude Code keeps working after a move or rename:
- Session files (
.jsonl) and subagent sessions - Sessions index (
fullPath,projectPath) - Project settings (
permissions.allow, hooks,additionalDirectories) - MCP config (
.mcp.json) - History index (
~/.claude/history.jsonl) - Global project directory (
~/.claude/projects/{encoded}/) - Project trust and tool permissions (
~/.claude.json) - Sub-projects nested inside the moved directory
A backup is created automatically before each operation — one archive per project, so a batch produces one per move.
cargo install --path .
ccmv myapp ~/work/
# moves to ~/work/myappWorks like mv. If the target is an existing directory, the project is moved into it. An explicit target path works too:
ccmv ~/projects/myapp ~/work/myappRenaming is just a move to a sibling path:
ccmv ~/projects/old-name ~/projects/new-nameSeveral sources and a target directory, again like mv:
ccmv ~/projects/api ~/projects/web ~/projects/cli ~/work/For longer lists, read the moves from a tab-separated file — one SOURCE<TAB>TARGET per line, # comments and blank lines skipped:
ccmv --batch moves.tsv# moves.tsv
/home/user/projects/api /home/user/work/backend/api
/home/user/projects/web /home/user/work/frontend/web
- reads the plan from standard input, which is where globbing pays off:
for p in ~/projects/*/; do
printf '%s\t%s\n' "${p%/}" "$HOME/work/$(basename "$p")"
done | ccmv --batch -Unlike positional arguments, a batch line states its target outright — the source name is not appended.
The whole plan is checked before the first write. Rejected are duplicate sources, two sources mapping to one target, chains (a → b together with b → c), and any two moves whose paths nest one inside the other. All problems are reported at once, with line numbers, so a 120-line plan does not need 120 runs to fix.
Two limitations worth knowing:
- Paths containing tabs are not supported. A line with a stray tab aborts the batch rather than guessing which two fields were meant.
- A batch is not a transaction. Validation and the pre-flight check catch what they can before anything is written, but a failure partway through leaves the earlier moves applied. The backups are what gets you back.
ccmv -n myapp ~/work/Shows which files would be updated and how many path replacements each contains. Nothing is modified.
For a batch, -n prints the plan: one line per move plus the global directory it takes with it. Both the move list and the file list stop after ten entries; -v prints them whole.
ccmv backup ~/projects/myapp
ccmv restore ~/.claude/backups/ccmv/...-20260318-213553.tar.gzBackups contain session files, settings, and MCP config as a .tar.gz archive. The project directory itself is not included (use git for that).
ccmv --session-only ~/myapp/.claude/worktrees/feature-x ~/myappMoves only the session data — global projects/{encoded}/, jsonl cwd fields, sessions index, history.jsonl, and the ~/.claude.json trust key. Neither project directory is touched.
Use case: point a session started in a git worktree back at the parent repo, then drop the worktree. The source path does not need to still exist on disk; only its session data under ~/.claude/ is required.
| Flag | Short | Description |
|---|---|---|
--batch <FILE> |
Read SOURCE<TAB>TARGET lines from a file; - reads stdin |
|
--dry-run |
-n |
Preview changes, don't modify anything |
--verbose |
-v |
Print every move and every updated file, not just the first ten |
--force |
Overwrite if target already has Claude Code data | |
--no-backup |
Skip automatic backup | |
--session-only |
Migrate only session data, leave project directories untouched |
Flags go before positional arguments: ccmv -n source target
ccmv scans the current state, computes the target state, and applies only what needs to change. This makes operations idempotent. If a migration was interrupted or the directory was already moved manually, running ccmv again picks up where it left off.
Claude Code encodes project paths by replacing all non-alphanumeric characters (except -) with -:
/home/user/my_project -> -home-user-my-project
/home/user/.config/app -> -home-user--config-app
~/.claude/projects/{encoded}/(sessions, subagents, index).claude/inside the project (settings, hooks).mcp.json~/.claude.json(project trust and permissions)
Stored at ~/.claude/backups/ccmv/{encoded}-{timestamp}.tar.gz.
cargo test # unit + E2E tests
cargo clippy # pedantic lints enabledE2E tests use isolated temp directories and never touch your real ~/.claude/.
MIT