Synchronize project-specific Cursor Agent conversations across Windows devices and make imported chats appear in Cursor’s native Agent history.
The project is source-first and self-hosted:
- The GitHub repository contains the complete extension source.
- Every user or team connects the extension to their own Supabase project.
- The author’s database, keys, Sync Keys, and Cursor databases are not included.
- A prebuilt VSIX may be published for convenience, but it is not connected to any database by default.
Current tested version:
0.0.16
Platform tested: Windows
Release status: Public beta
Cursor stores Agent conversations locally. When the same Git project is opened on another computer, its old Agent chats normally do not appear there.
Cursor Team Chat Sync provides a controlled workflow:
Device A
↓
Detect current Git project
↓
Find exact Cursor conversations for that project
↓
Create and encrypt a conversation bundle locally
↓
Upload encrypted bundle to the team’s Supabase backend
↓
Device B downloads and decrypts the bundle locally
↓
Back up both Cursor databases
↓
Import missing conversations
↓
Update Cursor’s native Agent-history sidebar
- Detects the current project from its Git
origin - Creates a stable project ID that matches across devices
- Finds exact Cursor conversations associated with the project
- Encrypts bundles locally before upload
- Stores encrypted bundle versions in a self-hosted Supabase backend
- Uses one private Sync Key per project vault
- Imports chats into Cursor’s native Agent history
- Detects new, identical, and conflicting conversations
- Avoids duplicate imports
- Creates verified backups of both Cursor SQLite databases
- Restores both databases if a committed import later fails
- Preserves safe Cursor-generated UI metadata changes
- Supports repeated imports and sidebar repair
This repository does not provide a shared public database.
Each user, organization, or team must:
- Create a Supabase project
- Apply the included migrations
- Deploy the included
cursor-sync-apiEdge Function - Enter their own Supabase URL and publishable key in Cursor Settings
- Build the extension from source or install a generic prebuilt VSIX
The installed extension reads:
cursorTeamChatSync.supabaseUrl
cursorTeamChatSync.supabasePublishableKey
The extension does not read its database connection from a project .env file.
A .env file may be used for local Supabase development, but real .env files must never be committed.
The extension reads:
git remote get-url originIt normalizes common HTTPS and SSH formats.
These two remotes become the same identity:
https://github.com/example/acme-app.git
git@github.com:example/acme-app.git
Canonical identity:
github.com/example/acme-app
The extension then calculates:
SHA-256(canonical Git remote) = stable project ID
This allows different local paths to match:
C:\Users\Alice\Projects\acme-app
C:\Users\Bob\Desktop\acme-app
Both devices must use the same Git repository and the same origin remote.
Cloud synchronization requires a valid Git origin so the project can be matched safely across devices.
Check it with:
git remote -vA vault is the private cloud container used for one synchronized project.
1 project = 1 vault
A vault contains:
- Project metadata
- A stable project ID
- Encrypted bundle-version metadata
- Encrypted bundle files
- A hash used to verify access
One Supabase backend can contain many project vaults, but every project has its own Sync Key.
A Sync Key has this format:
CTS1.<vault-id>.<secret>.<checksum>
Its parts are:
vault-id— locates the correct vaultsecret— derives encryption and access credentials locallychecksum— detects an incorrectly copied key
No.
The first upload to a new vault creates one key:
Project → Vault → Sync Key
Later uploads normally reuse the same saved key:
Bundle version 1 → 3 chats
Bundle version 2 → 4 chats
Bundle version 3 → 7 chats
A new Sync Key is generated only when the user deliberately chooses:
Create New Sync Vault
Treat the Sync Key like a password.
Anyone who has the complete key may be able to access and decrypt that project’s cloud bundles.
The complete key must never be:
- committed to Git
- added to
.env.example - posted in an issue
- included in screenshots
- shared publicly
Saved keys use Cursor/VS Code SecretStorage.
Before upload:
- The extension builds an exact conversation bundle
- The bundle is encrypted locally
- The encrypted result is verified
- Only the encrypted bundle is uploaded
The backend stores:
Vault metadata
Project metadata
Bundle version metadata
Encrypted bundle file
The backend does not need the plaintext bundle.
The extension uses the Supabase publishable key as a client credential. Administrative database access remains inside the Edge Function.
Never place a Supabase secret key or service-role key in Cursor Settings.
This is the primary installation method for developers and self-hosted users.
Install:
- Windows 10 or Windows 11
- Cursor
- Git
- Python 3
- Node.js 20 or newer
- npm
- A Supabase account
Confirm the tools:
git --version
node --version
npm --version
py -3 --versiongit clone `
"https://github.com/RaoTabeed/cursor-team-chat-sync.git"
cd ".\cursor-team-chat-sync"npm ciCreate a new project in Supabase.
Record:
Project reference
Project URL
Publishable key
Do not copy the secret key or service-role key into the extension.
npx supabase loginnpx supabase link `
--project-ref "YOUR_PROJECT_REF"Example project URL:
https://YOUR_PROJECT_REF.supabase.co
The generated local linkage under supabase/.temp/ must not be committed.
Preview the migration:
npx supabase db push `
--dry-runApply it:
npx supabase db pushThe repository contains the required migrations under:
supabase/migrations/
They create the vault, project, bundle-version, device, exclusion, index, storage, and security structures required by the extension.
npx supabase functions deploy `
cursor-sync-api `
--no-verify-jwtThe project configuration also contains:
[functions.cursor-sync-api]
verify_jwt = falseThe function performs its own publishable-key and vault-access checks.
Supabase supplies server-side environment variables to the hosted function. The extension must never receive the service-role key.
npm run check
if ($LASTEXITCODE -ne 0) {
throw "TypeScript validation failed."
}Validate the Python workers:
py -3 -m py_compile `
".\scripts\apply_conversation_import.py"
if ($LASTEXITCODE -ne 0) {
throw "Import worker validation failed."
}
py -3 -m py_compile `
".\scripts\validate_conversation_bundle.py"
if ($LASTEXITCODE -ne 0) {
throw "Bundle validator validation failed."
}npm run compile
if ($LASTEXITCODE -ne 0) {
throw "Extension compilation failed."
}npx @vscode/vsce package `
--out ".\cursor-team-chat-sync-0.0.12.vsix"Confirm:
Test-Path `
".\cursor-team-chat-sync-0.0.12.vsix"Expected:
True
In Cursor:
- Press
Ctrl+Shift+P - Run:
Extensions: Install from VSIX...
- Select:
cursor-team-chat-sync-0.0.12.vsix
- Reload Cursor
Open Cursor Settings and configure:
cursorTeamChatSync.supabaseUrl
cursorTeamChatSync.supabasePublishableKey
You can also add them to your Cursor user settings.json:
{
"cursorTeamChatSync.supabaseUrl": "https://YOUR_PROJECT_REF.supabase.co",
"cursorTeamChatSync.supabasePublishableKey": "YOUR_SUPABASE_PUBLISHABLE_KEY"
}Use only the publishable client key.
Do not use:
Supabase secret key
Supabase service-role key
Database password
CTS1 Sync Key
inside settings.json.
A GitHub Release may include a prebuilt generic VSIX.
The prebuilt extension is still not connected to the author’s backend. After installation, every user must enter their own Supabase URL and publishable key in Cursor Settings.
The source code remains available so users can inspect, modify, compile, and package the extension themselves.
Recommended release model:
GitHub repository
→ complete source code
→ Supabase migrations
→ Edge Function
→ Python workers
→ setup documentation
GitHub Release
→ optional generic prebuilt VSIX
Open a local clone of the Git project in Cursor.
Confirm its remote:
git remote -vCreate the Agent conversations you want to synchronize.
Press Ctrl+Shift+P and run:
Cursor Team Chat Sync: Inspect Current Project
Confirm:
Git repository detected
Origin remote detected
Canonical remote created
Stable project ID created
Cursor workspace database matched
Run:
Cursor Team Chat Sync: Upload All Chats to Cloud
For the first upload choose:
Create New Sync Vault
The extension will:
- Detect the project
- Generate a vault
- Generate a CTS1 Sync Key
- Find project conversations
- Build the exact bundle
- Encrypt it locally
- Upload the encrypted bundle
- Save the key in SecretStorage
- Copy the new key to the clipboard
Store the key safely.
For later uploads choose:
Use Saved Sync Vault
Each later upload creates a new bundle version using the same project vault and key.
git clone `
"THE_SAME_GIT_REMOTE"
cd ".\PROJECT_FOLDER"Open the project in Cursor.
For a completely new clone:
- Create one temporary Agent chat
- Wait for Cursor’s response
- Close Cursor normally
- Reopen the same project
This allows Cursor to create a valid workspace database.
Run:
Cursor Team Chat Sync: Inspect Current Project
The stable project ID should match Device A.
Run:
Cursor Team Chat Sync: Import Chats from Cloud
Choose:
Paste Another Sync Key
or:
Use Saved Sync Key
Paste the complete CTS1 key when requested.
The extension downloads the latest encrypted bundle, decrypts it locally, and validates each conversation.
Possible classifications:
Missing locally → new
Already present safely → identical
Real record difference → conflict
When there are no conflicts, choose:
Import and Close Cursor
Cursor closes while the detached worker performs the database import.
Wait several seconds and reopen the same project.
The imported conversations should appear in native Agent history.
Cursor uses two SQLite databases.
%APPDATA%\Cursor\User\globalStorage\state.vscdb
It contains:
- conversation records
- message records
- composer data
- global composer headers
%APPDATA%\Cursor\User\workspaceStorage\<workspace-id>\state.vscdb
It contains project-specific sidebar metadata, including:
ItemTable
key: composer.composerData
field: allComposers
Updating both databases is required for imported chats to appear in Cursor’s native history.
Before modifying either database, the worker:
- Waits for Cursor’s extension-host process to exit
- Waits until both databases are writable
- Runs SQLite integrity checks
- Creates logical SQLite backups
- Verifies the backups
- Imports new records transactionally
- Updates global headers
- Updates workspace
allComposers - Verifies sidebar visibility
- Restores both backups if a committed import later fails
Do not manually delete Cursor databases to bypass an import problem.
Importing the same bundle again should report:
New conversations: 0
Identical conversations: <existing count>
Conflicts: 0
The command may show:
Repair Sidebar and Close Cursor
This verifies and repairs native sidebar metadata without creating duplicate conversations.
Example:
Initial upload
→ 3 chats
→ bundle version 1
Create one new chat on Device A
Upload again with the saved vault
→ 4 chats
→ bundle version 2
Import on Device B
→ 1 new
→ 3 identical
→ 0 conflicts
A new Sync Key is not generated for bundle version 2.
Run:
Cursor Team Chat Sync: Manage Cloud Bundle Versions
This allows an authorized user to inspect and delete old encrypted cloud bundle versions.
Deleting a cloud bundle does not delete chats already imported into Cursor.
Cursor Team Chat Sync: Upload All Chats to Cloud
Cursor Team Chat Sync: Import Chats from Cloud
Cursor Team Chat Sync: Manage Cloud Bundle Versions
Cursor Team Chat Sync: Copy Saved Sync Key
Cursor Team Chat Sync: Inspect Local Cursor Storage
Cursor Team Chat Sync: Inspect Current Project
Cursor Team Chat Sync: Inspect Current Project Databases
Cursor Team Chat Sync: Index Project Conversations
Cursor Team Chat Sync: Trace Project Conversation Storage
Cursor Team Chat Sync: Build Conversation Bundle Manifest
Cursor Team Chat Sync: Export Exact Conversation Bundle
Cursor Team Chat Sync: Export Encrypted Conversation Bundle
Cursor Team Chat Sync: Validate Encrypted Bundle for Import
Cursor Team Chat Sync: Import Encrypted Conversation Bundle
cursor-team-chat-sync/
├── src/
│ ├── cloud/ Supabase client and cloud services
│ ├── commands/ Cursor command implementations
│ ├── conversations/ Conversation indexing and tracing
│ ├── crypto/ Sync Key and bundle encryption
│ ├── cursor/ Cursor storage discovery
│ ├── database/ SQLite inspection
│ ├── export/ Exact bundle generation
│ ├── git/ Git inspection and normalization
│ ├── import/ Import validation and job scheduling
│ ├── logging/ Output-channel logging
│ ├── projects/ Stable project identity
│ └── runtime/ Python process execution
├── scripts/
│ ├── apply_conversation_import.py
│ ├── build_conversation_bundle_manifest.py
│ ├── export_conversation_bundle.py
│ ├── index_project_conversations.py
│ ├── inspect_cursor_database.py
│ ├── trace_project_conversation_storage.py
│ └── validate_conversation_bundle.py
├── supabase/
│ ├── functions/
│ │ └── cursor-sync-api/
│ ├── migrations/
│ └── config.toml
├── package.json
├── package-lock.json
└── tsconfig.json
node_modules/
out/
*.vsix
.env
.env.*
supabase/.temp/
supabase/.branches/
__pycache__/
*.pyc
*.vscdb
*.sqlite
*.db
*.backup
*.before-*
CTS1 keys
Cursor chat bundles
Cursor database backups
Safe files to commit include:
src/
scripts/
supabase/functions/
supabase/migrations/
supabase/config.toml
package.json
package-lock.json
tsconfig.json
README.md
.gitignore
.vscodeignore
.env.example
Install dependencies:
npm ciType-check:
npm run checkCompile:
npm run compileWatch:
npm run watchList files that would enter a VSIX:
npx @vscode/vsce lsPackage:
npx @vscode/vsce packageSet:
cursorTeamChatSync.supabaseUrl
cursorTeamChatSync.supabasePublishableKey
in Cursor Settings.
On both devices run:
git remote get-url originThen run:
Cursor Team Chat Sync: Inspect Current Project
Both devices must produce the same canonical remote and stable project ID.
- Open the project
- Create one temporary Agent chat
- Wait for the response
- Close Cursor
- Reopen the same project
- Retry the import
The extension stops before importing.
Review the Cursor Team Chat Sync Output panel.
A real message or required-record difference is treated as a conflict instead of being overwritten automatically.
Run the same cloud import again.
When the conversations are already present, choose:
Repair Sidebar and Close Cursor
This is expected.
The databases should not be edited while Cursor’s extension host is using them. Reopen Cursor after the detached worker finishes.
Before publishing or contributing:
[ ] No real CTS1 Sync Key
[ ] No Supabase secret key
[ ] No Supabase service-role key
[ ] No database password
[ ] No Cursor state.vscdb file
[ ] No plaintext chat bundle
[ ] No real .env file
[ ] No supabase/.temp folder
[ ] No old repair/finalize scripts
[ ] No backup source files
[ ] No generated Python bytecode
- Windows is the tested platform
- Cursor must close during database import
- The same Git
originis required for reliable cross-device matching - Real two-sided edits of the same conversation are not automatically merged
- Cursor’s internal database schema may change in future releases
- Each team must self-host and maintain its own Supabase backend
- This project is currently a public beta
Before publishing:
npm run check
npm run compile
py -3 -m py_compile `
".\scripts\apply_conversation_import.py"
py -3 -m py_compile `
".\scripts\validate_conversation_bundle.py"Review tracked files:
git status
git diff --check
git ls-filesCommit:
git add .
git commit `
-m "Release Cursor Team Chat Sync 0.0.12 public beta"Push:
git branch -M main
git push `
-u `
origin `
mainThe complete source code is the main release. A generic prebuilt VSIX may be attached separately to a GitHub Release for convenience.