Skip to main content

Move folders while preserving Cursor workspace metadata and chat history

Project description

cursor-move

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 directly from a checkout with uv:

uv tool install .

Or run it without installing:

uv run cursor-move --help

The project name cursor-move was available on PyPI when this repository was prepared, but no PyPI release is assumed by these instructions.

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cursor_move-0.1.0.tar.gz (34.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cursor_move-0.1.0-py3-none-any.whl (20.0 kB view details)

Uploaded Python 3

File details

Details for the file cursor_move-0.1.0.tar.gz.

File metadata

  • Download URL: cursor_move-0.1.0.tar.gz
  • Upload date:
  • Size: 34.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cursor_move-0.1.0.tar.gz
Algorithm Hash digest
SHA256 098b874a166f88a8229990772b54b020bf2a53fca6e64afbbfae570da3dc2248
MD5 8a19b5ad041d432d379316ffda841537
BLAKE2b-256 cd5368ddc47156a4074c6babd46aa0e9efece06a73cc28b974e853cbddf7a3bb

See more details on using hashes here.

Provenance

The following attestation bundles were made for cursor_move-0.1.0.tar.gz:

Publisher: release.yml on NRB-Tech/cursor-move

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cursor_move-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cursor_move-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cursor_move-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ccce95a13fddd39ec7e028734b586f57895724129d44b24eaf8997b943451d83
MD5 511862cc8e65daae6f77c01384a0bcb1
BLAKE2b-256 ce23d01687214aa21d48fbee6c39089fbc32ee1777e23c3f16cbe25829f4a0a1

See more details on using hashes here.

Provenance

The following attestation bundles were made for cursor_move-0.1.0-py3-none-any.whl:

Publisher: release.yml on NRB-Tech/cursor-move

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page