<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Callum Ward</title>
    <description>The latest articles on DEV Community by Callum Ward (@callum_5c4104e61f9bc).</description>
    <link>https://dev.to/callum_5c4104e61f9bc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2995266%2F52eb89e5-4f3f-4b2d-86a0-2a0b575c87b2.jpg</url>
      <title>DEV Community: Callum Ward</title>
      <link>https://dev.to/callum_5c4104e61f9bc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/callum_5c4104e61f9bc"/>
    <language>en</language>
    <item>
      <title>I built a CLI to sync Cursor chat history between machines</title>
      <dc:creator>Callum Ward</dc:creator>
      <pubDate>Wed, 04 Mar 2026 11:51:30 +0000</pubDate>
      <link>https://dev.to/callum_5c4104e61f9bc/i-built-a-cli-to-sync-cursor-chat-history-between-machines-2cnf</link>
      <guid>https://dev.to/callum_5c4104e61f9bc/i-built-a-cli-to-sync-cursor-chat-history-between-machines-2cnf</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;If you use &lt;a href="https://cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; across multiple machines — a laptop, a desktop, a VM over SSH — you've hit this: you switch devices and your entire conversation history is gone.&lt;/p&gt;

&lt;p&gt;All the context the AI had about your codebase, the decisions you made together, the thread of reasoning across dozens of messages — stuck on whichever machine you happened to be using.&lt;/p&gt;

&lt;p&gt;This matters because Cursor's agent mode builds up a deep understanding of your project over a conversation. Losing that context means starting from scratch, re-explaining your architecture, and burning credits on information the AI already had.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Happens
&lt;/h2&gt;

&lt;p&gt;Cursor stores all chat data in &lt;strong&gt;local SQLite databases&lt;/strong&gt;, not in the cloud. Even when you're connected to a remote server via SSH, the chats live on the machine running Cursor's UI.&lt;/p&gt;

&lt;p&gt;There's no built-in sync. No export. No way to move conversations between machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: cursaves
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Callum-Ward/cursaves" rel="noopener noreferrer"&gt;&lt;strong&gt;cursaves&lt;/strong&gt;&lt;/a&gt; is a CLI tool that exports your Cursor conversations to a private git repo and imports them on another machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install (once per machine)&lt;/span&gt;
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;git+https://github.com/Callum-Ward/cursaves.git

&lt;span class="c"&gt;# Set up sync repo (once per machine)&lt;/span&gt;
cursaves init &lt;span class="nt"&gt;--remote&lt;/span&gt; git@github.com:you/my-cursaves.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the workflow is two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Machine A: save your chats&lt;/span&gt;
cursaves push

&lt;span class="c"&gt;# Machine B: restore them&lt;/span&gt;
cursaves pull
&lt;span class="c"&gt;# Restart Cursor to see the imported chats&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Actually Does
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Push&lt;/strong&gt; reads Cursor's SQLite databases, exports each conversation as a self-contained JSON snapshot (compressed with gzip), commits to a git repo, and pushes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pull&lt;/strong&gt; fetches from git, writes the conversation data back into Cursor's databases, rewrites file paths to match the target machine, and registers the chats in the correct workspace.&lt;/p&gt;

&lt;p&gt;It handles the tricky parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project matching&lt;/strong&gt; — uses git remote URLs to identify projects, so the same repo at &lt;code&gt;/Users/alice/myapp&lt;/code&gt; and &lt;code&gt;/home/bob/myapp&lt;/code&gt; syncs correctly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path rewriting&lt;/strong&gt; — file references in chat metadata are automatically rewritten for the target machine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH workspaces&lt;/strong&gt; — Cursor stores SSH chats locally, so &lt;code&gt;cursaves&lt;/code&gt; lets you target specific workspaces with &lt;code&gt;-w&lt;/code&gt; or interactive selection with &lt;code&gt;-s&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety&lt;/strong&gt; — reads use a temp copy of the database, writes create backups first, and imports are blocked while Cursor is running&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Interactive Selection
&lt;/h2&gt;

&lt;p&gt;You don't have to push/pull everything. The &lt;code&gt;-s&lt;/code&gt; flag lets you pick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cursaves push -s

  Conversations in my-project  (5 total)

  #    Name                                      Msgs  Last Updated
  ---------------------------------------------------------------------------
  1    Refactor auth middleware                   1004  2026-03-03 17:39 UTC
  2    Fix deployment pipeline                    993  2026-03-02 17:19 UTC
  3    Add rate limiting to API                    55  2026-02-13 14:41 UTC

  Select chats to push (e.g. 1,3 or 1-3 or 'all') [all]:
  &amp;gt; 1,2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For pull, it shows each snapshot with its date and message count so you know exactly what you're importing.&lt;/p&gt;

&lt;h2&gt;
  
  
  SSH Remote Workflow
&lt;/h2&gt;

&lt;p&gt;This was the use case that motivated the whole project. When you SSH into a VM through Cursor, your chats are on your laptop — not the VM. If you switch to a different laptop, those chats are gone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all workspaces (local + SSH)&lt;/span&gt;
cursaves workspaces

&lt;span class="c"&gt;#    Type   Path                                     Host         Chats&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# 1  ssh    /home/user/repos/myapp                   prod-vm          3&lt;/span&gt;
&lt;span class="c"&gt;# 2  ssh    /home/user/repos/myapp                   dev-vm           5&lt;/span&gt;
&lt;span class="c"&gt;# 3  local  /Users/me/Projects/webapp                                 2&lt;/span&gt;

&lt;span class="c"&gt;# Push from a specific SSH workspace&lt;/span&gt;
cursaves push &lt;span class="nt"&gt;-w&lt;/span&gt; 1

&lt;span class="c"&gt;# On another machine, pull it back&lt;/span&gt;
cursaves pull &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; run &lt;code&gt;cursaves&lt;/code&gt; in a local terminal, not Cursor's integrated terminal (which runs on the remote).&lt;/p&gt;

&lt;h2&gt;
  
  
  Same-Machine Workspace Copying
&lt;/h2&gt;

&lt;p&gt;It's not just for syncing between machines. Cursor isolates chats per workspace — if you clone the same repo to a new directory, or open it from a different path, your previous conversations won't be there.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cursaves&lt;/code&gt; handles this too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Export from the old workspace&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/old/checkout
cursaves push

&lt;span class="c"&gt;# Import into the new workspace&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/new/checkout
cursaves pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No remote repo needed — &lt;code&gt;cursaves init&lt;/code&gt; without &lt;code&gt;--remote&lt;/code&gt; works for local-only use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;The tool needs to be fast since you'll run it frequently. Some numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot listing&lt;/strong&gt;: reads tiny metadata sidecar files instead of decompressing full snapshots — &lt;strong&gt;3ms&lt;/strong&gt; vs 15+ seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conversation listing&lt;/strong&gt;: single shared DB connection — &lt;strong&gt;800ms&lt;/strong&gt; for 5 conversations vs 4.7 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch DB writes&lt;/strong&gt;: imports 50K+ message entries in a single SQLite transaction instead of individual connections&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;You need Python 3.10+, &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt;, and git. No external Python dependencies. Tested with Cursor 2.6.11.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Install&lt;/span&gt;
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;git+https://github.com/Callum-Ward/cursaves.git

&lt;span class="c"&gt;# 2. Create a PRIVATE repo on GitHub for your chat data&lt;/span&gt;
&lt;span class="c"&gt;#    (snapshots contain your full conversations — keep it private)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Initialize on each machine&lt;/span&gt;
cursaves init &lt;span class="nt"&gt;--remote&lt;/span&gt; git@github.com:you/my-cursaves.git

&lt;span class="c"&gt;# 4. Start syncing&lt;/span&gt;
cursaves push   &lt;span class="c"&gt;# save&lt;/span&gt;
cursaves pull   &lt;span class="c"&gt;# restore (then restart Cursor)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also a &lt;code&gt;watch&lt;/code&gt; command that auto-syncs in the background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cursaves watch &lt;span class="nt"&gt;-p&lt;/span&gt; /path/to/project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How Cursor Stores Chats (for the curious)
&lt;/h2&gt;

&lt;p&gt;Cursor uses two SQLite databases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Workspace DB&lt;/strong&gt; (&lt;code&gt;workspaceStorage/{id}/state.vscdb&lt;/code&gt;) — sidebar metadata mapping conversation IDs to workspaces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global DB&lt;/strong&gt; (&lt;code&gt;globalStorage/state.vscdb&lt;/code&gt;) — actual message content stored as individual JSON entries keyed by &lt;code&gt;bubbleId:{composerId}:{messageId}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workspace DB entry needs a &lt;code&gt;type: "head"&lt;/code&gt; field and several metadata flags for Cursor to render it in the sidebar. The global DB can be 3+ GB and uses WAL mode. Cursor caches everything in memory at startup and never watches for external changes — which is why a full restart is required after import.&lt;/p&gt;

&lt;p&gt;I wrote up the full details in &lt;a href="https://github.com/Callum-Ward/cursaves/blob/main/docs/how-cursor-stores-chats.md" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; if you want to dig deeper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Callum-Ward/cursaves" rel="noopener noreferrer"&gt;github.com/Callum-Ward/cursaves&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: AGPL-3.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you find it useful, a star on the repo or &lt;a href="https://buymeacoffee.com/callumward" rel="noopener noreferrer"&gt;a coffee&lt;/a&gt; goes a long way. Issues and PRs welcome.&lt;/p&gt;

</description>
      <category>coding</category>
      <category>cursor</category>
      <category>cli</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
