ccmv 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +159 -0
- package/LICENSE +21 -0
- package/README.md +114 -0
- package/bin/ccmv.js +5 -0
- package/docs/ARCHITECTURE.md +438 -0
- package/lib/claude.js +311 -0
- package/lib/cursor.js +582 -0
- package/lib/index.js +352 -0
- package/lib/logger.js +60 -0
- package/lib/utils.js +93 -0
- package/package.json +36 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# ccmv - Project Guidelines
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`ccmv` is a Node.js CLI tool that migrates project directories, updating all Claude Code and/or Cursor references when a project is moved to a new location.
|
|
6
|
+
|
|
7
|
+
**Supported configurations:**
|
|
8
|
+
- Claude Code + Cursor (both)
|
|
9
|
+
- Claude Code only
|
|
10
|
+
- Cursor only
|
|
11
|
+
|
|
12
|
+
## Project Structure
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
ccmv/
|
|
16
|
+
├── package.json # npm package config with bin entry
|
|
17
|
+
├── bin/
|
|
18
|
+
│ └── ccmv.js # CLI entry point
|
|
19
|
+
├── lib/
|
|
20
|
+
│ ├── index.js # Main orchestration logic
|
|
21
|
+
│ ├── logger.js # Colored console output
|
|
22
|
+
│ ├── utils.js # Path encoding, file URIs, hashing
|
|
23
|
+
│ ├── claude.js # Claude Code operations
|
|
24
|
+
│ └── cursor.js # Cursor editor operations
|
|
25
|
+
├── README.md # User documentation
|
|
26
|
+
└── CLAUDE.md # This file
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Technical Details
|
|
30
|
+
|
|
31
|
+
### Claude Code Data Locations
|
|
32
|
+
|
|
33
|
+
- `~/.claude/projects/{encoded-path}/` - Project-specific session data
|
|
34
|
+
- `~/.claude/history.jsonl` - Global history file
|
|
35
|
+
- Path encoding: `/`, `:`, spaces → `-`
|
|
36
|
+
|
|
37
|
+
### Cursor Data Locations
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
~/Library/Application Support/Cursor/User/
|
|
41
|
+
├── globalStorage/
|
|
42
|
+
│ ├── storage.json # profileAssociations.workspaces (path list)
|
|
43
|
+
│ └── state.vscdb # SQLite ItemTable: history.recentlyOpenedPathsList, repositoryTracker.paths
|
|
44
|
+
└── workspaceStorage/
|
|
45
|
+
└── {hash}/
|
|
46
|
+
├── workspace.json # {"folder": "file://..."} or {"workspace": "file://..."}
|
|
47
|
+
└── state.vscdb # Workspace-specific SQLite (ItemTable + cursorDiskKV)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- **Path format**: `file:///Users/foo/my%20project` (URL encoded)
|
|
51
|
+
- **SQLite update**: Uses `better-sqlite3` for database operations
|
|
52
|
+
|
|
53
|
+
### Cursor Workspace Hash (Critical)
|
|
54
|
+
|
|
55
|
+
Cursor/VSCode uses MD5 hash of `path + birthtime_ms` for workspaceStorage directory names:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
import { createHash } from 'node:crypto';
|
|
59
|
+
import { statSync } from 'node:fs';
|
|
60
|
+
|
|
61
|
+
const stat = statSync(path);
|
|
62
|
+
const hash = createHash('md5')
|
|
63
|
+
.update(path)
|
|
64
|
+
.update(String(stat.birthtime.getTime()))
|
|
65
|
+
.digest('hex');
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Key Points:**
|
|
69
|
+
- Uses Node.js native `fs.statSync().birthtime.getTime()` for consistent millisecond precision
|
|
70
|
+
- `mv` command preserves birthtime on same volume, so new path hash is predictable
|
|
71
|
+
- When project moves, ccmv renames workspace directory to match new hash
|
|
72
|
+
- Chat history is stored in `workspaceStorage/{hash}/state.vscdb` (cursorDiskKV table)
|
|
73
|
+
|
|
74
|
+
**Duplicate Workspace Handling:**
|
|
75
|
+
- Multiple workspace directories can point to the same path (from failed migrations or Cursor quirks)
|
|
76
|
+
- `mergeDuplicateWorkspaces()` finds all workspaces pointing to new path
|
|
77
|
+
- Keeps the workspace with the **larger** `state.vscdb` (contains more chat history)
|
|
78
|
+
|
|
79
|
+
### Key Modules
|
|
80
|
+
|
|
81
|
+
| Module | Purpose |
|
|
82
|
+
|--------|---------|
|
|
83
|
+
| `lib/index.js` | CLI parsing, orchestration, rollback handling |
|
|
84
|
+
| `lib/logger.js` | Colored log output functions |
|
|
85
|
+
| `lib/utils.js` | `encodePath`, `pathToFileUri`, `getWorkspaceHash`, `resolvePath` |
|
|
86
|
+
| `lib/claude.js` | Backup, rename, update, verify Claude data |
|
|
87
|
+
| `lib/cursor.js` | Detect, backup, update Cursor storage.json and SQLite DBs |
|
|
88
|
+
|
|
89
|
+
### Error Handling
|
|
90
|
+
|
|
91
|
+
- Validates all paths before making changes
|
|
92
|
+
- Requires at least one of Claude Code or Cursor data (fails if neither exists)
|
|
93
|
+
- Creates backups before any modifications
|
|
94
|
+
- Auto-rollback on any error during migration
|
|
95
|
+
- Cursor must not be running (checked before migration)
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
### Setup
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npm install
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Testing
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Create test project
|
|
109
|
+
mkdir -p /tmp/test-proj-a
|
|
110
|
+
mkdir -p ~/.claude/projects/-tmp-test-proj-a
|
|
111
|
+
echo '{"cwd":"/tmp/test-proj-a"}' > ~/.claude/projects/-tmp-test-proj-a/test.jsonl
|
|
112
|
+
|
|
113
|
+
# Test dry-run
|
|
114
|
+
node bin/ccmv.js --dry-run /tmp/test-proj-a /tmp/test-proj-b
|
|
115
|
+
|
|
116
|
+
# Test with npx (from package directory)
|
|
117
|
+
npx . --dry-run /tmp/test-proj-a /tmp/test-proj-b
|
|
118
|
+
|
|
119
|
+
# Cleanup
|
|
120
|
+
rm -rf /tmp/test-proj-* ~/.claude/projects/-tmp-test-proj-*
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Local Development
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Run directly
|
|
127
|
+
node bin/ccmv.js --help
|
|
128
|
+
|
|
129
|
+
# Or link globally for testing
|
|
130
|
+
npm link
|
|
131
|
+
ccmv --help
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Known Issues
|
|
135
|
+
|
|
136
|
+
- Cursor must be closed during migration (enforced by `checkCursorNotRunning`)
|
|
137
|
+
- **Cross-volume moves**: `mv` to different volume may not preserve birthtime, potentially breaking hash prediction (untested)
|
|
138
|
+
- On Linux, Cursor data is at `~/.config/Cursor/User/` instead of `~/Library/Application Support/`
|
|
139
|
+
|
|
140
|
+
### Troubleshooting Cursor Chat History Loss
|
|
141
|
+
|
|
142
|
+
If chat history is lost after migration:
|
|
143
|
+
|
|
144
|
+
1. **Check workspace directories**: Look at `~/Library/Application Support/Cursor/User/workspaceStorage/*/workspace.json` for entries pointing to your path
|
|
145
|
+
2. **Compare state.vscdb sizes**: Larger file usually has more chat data
|
|
146
|
+
3. **Calculate expected hash**:
|
|
147
|
+
```javascript
|
|
148
|
+
import { createHash } from 'node:crypto';
|
|
149
|
+
import { statSync } from 'node:fs';
|
|
150
|
+
|
|
151
|
+
const path = '/your/project/path';
|
|
152
|
+
const stat = statSync(path);
|
|
153
|
+
const hash = createHash('md5')
|
|
154
|
+
.update(path)
|
|
155
|
+
.update(String(stat.birthtime.getTime()))
|
|
156
|
+
.digest('hex');
|
|
157
|
+
console.log(hash);
|
|
158
|
+
```
|
|
159
|
+
4. **Check cursorDiskKV table**: Chat data is in `state.vscdb` under key `composer.composerData`
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Saqoosha
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# ccmv
|
|
2
|
+
|
|
3
|
+
Claude Code project directory migration tool.
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
When you move a project directory, Claude Code loses track of your conversation history because it's stored in `~/.claude/projects/` using path-encoded directory names.
|
|
8
|
+
|
|
9
|
+
## Solution
|
|
10
|
+
|
|
11
|
+
`ccmv` moves your project and updates all Claude Code and/or Cursor references automatically. Works with:
|
|
12
|
+
- Projects with both Claude Code and Cursor data
|
|
13
|
+
- Claude Code-only projects (no Cursor)
|
|
14
|
+
- Cursor-only projects (no Claude Code history)
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Using npm (recommended)
|
|
20
|
+
npm install -g ccmv
|
|
21
|
+
|
|
22
|
+
# Or using npx (no installation required)
|
|
23
|
+
npx ccmv --help
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
ccmv [OPTIONS] <old-path> <new-path>
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
--refs-only Only update references (don't move directory)
|
|
33
|
+
--dry-run Preview changes without executing
|
|
34
|
+
--keep-backup Keep backup after successful migration
|
|
35
|
+
--no-cursor Skip Cursor editor data updates
|
|
36
|
+
--quiet Suppress detailed output
|
|
37
|
+
--help Show help
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Examples
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Move project to new location
|
|
44
|
+
ccmv ~/projects/myapp ~/work/myapp
|
|
45
|
+
|
|
46
|
+
# Or using npx
|
|
47
|
+
npx ccmv ~/projects/myapp ~/work/myapp
|
|
48
|
+
|
|
49
|
+
# Preview what would happen
|
|
50
|
+
ccmv --dry-run ~/old-project ~/new-project
|
|
51
|
+
|
|
52
|
+
# Already moved? Just update refs
|
|
53
|
+
ccmv --refs-only /old/path /new/path
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## What It Does
|
|
57
|
+
|
|
58
|
+
1. Detects Claude Code and/or Cursor data for the project
|
|
59
|
+
2. Creates backup of existing data
|
|
60
|
+
3. Moves project directory (unless `--refs-only`)
|
|
61
|
+
4. Updates Claude Code data (if exists):
|
|
62
|
+
- Renames `~/.claude/projects/{encoded-path}/`
|
|
63
|
+
- Updates `cwd` field in all JSONL files
|
|
64
|
+
- Updates `~/.claude/history.jsonl`
|
|
65
|
+
5. Updates Cursor workspace data (if exists):
|
|
66
|
+
- Renames `workspaceStorage/{hash}/` directory
|
|
67
|
+
- Updates `storage.json` (profile associations)
|
|
68
|
+
- Updates `state.vscdb` (global SQLite database)
|
|
69
|
+
- Updates `workspaceStorage/*/workspace.json`
|
|
70
|
+
- Updates `workspaceStorage/*/state.vscdb`
|
|
71
|
+
6. Verifies migration success
|
|
72
|
+
7. Auto-rollback on any failure
|
|
73
|
+
|
|
74
|
+
**Note:** Migration requires at least one of Claude Code or Cursor data to exist. If neither exists, use regular `mv` command.
|
|
75
|
+
|
|
76
|
+
## How Claude Code Stores Data
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
~/.claude/
|
|
80
|
+
├── projects/
|
|
81
|
+
│ └── -Users-jane-myproject/ # Encoded from /Users/jane/myproject
|
|
82
|
+
│ ├── session-abc.jsonl # Contains "cwd":"/Users/jane/myproject"
|
|
83
|
+
│ └── subagents/
|
|
84
|
+
│ └── agent-xyz.jsonl
|
|
85
|
+
└── history.jsonl # Global history with cwd fields
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Path encoding: `/Users/jane/foo bar` → `-Users-jane-foo-bar`
|
|
89
|
+
|
|
90
|
+
## Cursor Data (Auto-detected)
|
|
91
|
+
|
|
92
|
+
If Cursor is installed, `ccmv` also updates:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
~/Library/Application Support/Cursor/User/
|
|
96
|
+
├── globalStorage/
|
|
97
|
+
│ ├── storage.json # Profile workspace associations
|
|
98
|
+
│ └── state.vscdb # SQLite: history, repository paths
|
|
99
|
+
└── workspaceStorage/
|
|
100
|
+
└── {hash}/
|
|
101
|
+
├── workspace.json # Workspace folder URI
|
|
102
|
+
└── state.vscdb # Workspace-specific data
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Note:** Cursor must be closed during migration to prevent data corruption.
|
|
106
|
+
|
|
107
|
+
## Requirements
|
|
108
|
+
|
|
109
|
+
- Node.js 18.0.0 or later
|
|
110
|
+
- macOS or Linux
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT
|