Metadata-Version: 2.4
Name: cursor-move
Version: 0.1.0
Summary: Move folders while preserving Cursor workspace metadata and chat history
Project-URL: Homepage, https://github.com/NRB-Tech/cursor-move
Project-URL: Repository, https://github.com/NRB-Tech/cursor-move.git
Project-URL: Issues, https://github.com/NRB-Tech/cursor-move/issues
Project-URL: Changelog, https://github.com/NRB-Tech/cursor-move/blob/main/CHANGELOG.md
Author: NRB Tech Ltd
License-Expression: MIT
License-File: LICENSE
Keywords: cursor,linux,macos,migration,sqlite,windows,workspace
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Utilities
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# 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](https://docs.astral.sh/uv/):

```sh
uv tool install .
```

Or run it without installing:

```sh
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

```sh
cursor-move --dry-run '/work/old-project' '/archive/new-project'
```

After reviewing the plan, quit Cursor completely and run:

```sh
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}`.

```sh
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:

```sh
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:

```powershell
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`:

```sh
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:

```sh
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:

```text
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](CONTRIBUTING.md) for setup and test commands and
[docs/design.md](docs/design.md) for storage details and failure invariants.
Platform behavior and test boundaries are documented in
[docs/platforms.md](docs/platforms.md).

## License

MIT. See [LICENSE](LICENSE).
