Skip to content

Repository files navigation

cursor-move

PyPI Python CI

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.

Why this exists

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.

Requirements

  • macOS, Linux, or Windows
  • Python 3.12 or newer
  • A local Cursor installation with an initialized User data 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.

Installation

Install the published package as an isolated command-line tool with uv:

uv tool install cursor-move

Or use pipx:

pipx install cursor-move

Inside an existing virtual environment, regular pip also works:

python -m pip install cursor-move

The 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.

Usage

Always start with --dry-run. Dry runs may be performed while Cursor is open because they do not change files or databases.

Move one folder

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.

Move several folders with a regex

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.

Repair metadata after a completed move

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.

Portable and custom installations

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.

Safety and recovery

Before changing the global SQLite database, cursor-move:

  1. refuses to run while Cursor is open;
  2. checks that enough free disk space is available;
  3. uses SQLite's online backup API, including committed WAL data;
  4. runs PRAGMA quick_check on the backup; and
  5. 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.

Limitations

  • Cursor's internal schema is private and may change without notice.
  • Multi-root .code-workspace metadata 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.

Troubleshooting

“Cursor is running, or its process state could not be checked safely”

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.

“Not enough free disk space for a safe SQLite backup”

Free the amount reported by the command. Do not remove the only known-good backup to make room unless another validated copy exists.

The folders moved but metadata migration failed

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.

Development

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.

License

MIT. See LICENSE.

About

Move folders while preserving Cursor workspace metadata and chat history

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages