cursor-move moves or renames folders while preserving the Cursor
workspace metadata and local chat history associated with their old paths.
Caution
This is an independent, alpha-quality utility that edits Cursor's private storage format. It is not affiliated with or supported by Cursor or Anysphere. Always inspect a dry run and keep the generated backup until the migrated workspaces have been verified.
Cursor identifies a workspace using both its absolute path and filesystem birth
time. Moving a project in Finder or with mv changes the path but does not
update Cursor's related records. The result can be missing chat history, empty
workspace state, or duplicate projects.
cursor-move performs the filesystem move and updates the relevant local
metadata as one guarded workflow. Cursor's User directory is discovered at:
| Platform | Default User directory |
|---|---|
| macOS | ~/Library/Application Support/Cursor/User |
| Linux | $XDG_CONFIG_HOME/Cursor/User or ~/.config/Cursor/User |
| Windows | %APPDATA%\Cursor\User |
Within it, the tool migrates workspaceStorage, globalStorage/storage.json,
globalStorage/state.vscdb, workspace-local state databases, and caches. It
also migrates ~/.cursor/projects when that directory exists.
All processing is local. The runtime has no network dependencies and does not upload workspace data or chat content.
- macOS, Linux, or Windows
- Python 3.12 or newer
- A local Cursor installation with an initialized
Userdata directory - Enough free disk space for one complete global-state database backup plus a safety margin
Cursor must be fully closed for a real migration. The command checks this before planning any changes and exits if Cursor is running or the process list cannot be inspected safely. After the check passes, do not open Cursor until the command reports that the migration is complete.
Install the published package as an isolated command-line tool with uv:
uv tool install cursor-moveOr use pipx:
pipx install cursor-moveInside an existing virtual environment, regular pip also works:
python -m pip install cursor-moveThe package is available from PyPI, and release notes are published on the GitHub Releases page.
To install the current source checkout instead, run uv tool install .. For
development without installation, use uv run cursor-move --help.
Always start with --dry-run. Dry runs may be performed while Cursor is open
because they do not change files or databases.
cursor-move --dry-run '/work/old-project' '/archive/new-project'After reviewing the plan, quit Cursor completely and run:
cursor-move '/work/old-project' '/archive/new-project'DESTINATION is the exact new path, not merely its parent directory.
With --regex, SOURCE is matched against complete absolute directory paths.
The destination may reference numbered or named capture groups using \1,
\g<name>, $1, or ${name}.
cursor-move --dry-run --regex \
'^/Users/me/Projects/(client-.+)$' \
'/Users/me/Archive/$1'The search root is inferred from the literal prefix of SOURCE. If it cannot
be inferred safely, specify it explicitly:
cursor-move --dry-run --regex --scan-root '/Users/me/Projects' \
'^/Users/me/Projects/(client-.+)$' \
'/Users/me/Archive/$1'Regexes always see absolute paths with forward slashes. On Windows, use that canonical form even though ordinary exact paths may use native backslashes:
cursor-move --dry-run --regex `
'^C:/Users/me/Projects/(client-.+)$' `
'C:/Users/me/Archive/$1'Matched folders are treated as move units, so their descendants are not also matched. The command rejects duplicate destinations, existing destinations, and attempts to move a directory inside itself before moving anything.
If a folder was already moved but Cursor metadata was not updated, pass its old
and current paths with --metadata-only:
cursor-move --metadata-only \
'/work/old-project' \
'/archive/new-project'--already-moved is retained as an alias. Metadata-only repair currently
accepts exact paths rather than regex transformations.
Override auto-detection when Cursor uses a custom or portable data directory:
cursor-move --cursor-user-dir '/custom/Cursor/User' \
'/work/old-project' '/work/new-project'Use --cursor-projects-dir when .cursor/projects also lives elsewhere.
Before changing the global SQLite database, cursor-move:
- refuses to run while Cursor is open;
- checks that enough free disk space is available;
- uses SQLite's online backup API, including committed WAL data;
- runs
PRAGMA quick_checkon the backup; and - aborts before modifying the source database if backup validation fails.
Validated backups are created beside the database with names such as:
state.vscdb.bak-1785330644
Keep the newest backup until Cursor opens normally and the expected workspaces
and chats are visible. To recover, fully quit Cursor, preserve the active
state.vscdb, state.vscdb-wal, and state.vscdb-shm together, then restore a
validated backup without reusing stale WAL/SHM files.
Cursor databases and logs may contain project paths, workspace data, account details, and chat content. Never attach them to a public issue without careful redaction.
- Cursor's internal schema is private and may change without notice.
- Multi-root
.code-workspacemetadata has not been comprehensively tested. - The operation spans the filesystem and several independent metadata stores; it cannot be fully atomic across all of them.
- The startup check cannot stop someone from launching Cursor midway through a long migration.
- Regex metadata-only recovery is not currently supported.
- Windows drive/UNC paths and Linux inode-based workspace IDs are implemented, but destructive migrations should still begin with a dry run and a retained backup on every new Cursor release.
- WSL, SSH, containers, network shares, and portable installations can place the GUI metadata somewhere other than the project filesystem; use the data directory belonging to the machine that runs the Cursor GUI.
Fully quit Cursor. On macOS use Cmd+Q; on Windows ensure Cursor.exe is gone;
on Linux ensure the Cursor binary or AppImage has exited. The check deliberately
fails closed when pgrep or PowerShell cannot inspect processes.
Free the amount reported by the command. Do not remove the only known-good backup to make room unless another validated copy exists.
Keep Cursor closed and rerun the affected exact path with --metadata-only.
Do not use a stale global database together with newer -wal or -shm files.
See CONTRIBUTING.md for setup and test commands and docs/design.md for storage details and failure invariants. Platform behavior and test boundaries are documented in docs/platforms.md.
MIT. See LICENSE.