Cursor 对话导入:解析 SQLite 里的宝藏

0 阅读2分钟

本文面向:使用 Cursor 的开发者,想了解 ChatCrystal 如何从 Cursor 中提取对话数据。 预计阅读时间:8 分钟


Cursor 的对话存在哪

Cursor 基于 VS Code,对话数据存在 SQLite 数据库里,不是像 Claude Code 那样的独立 JSONL 文件。

数据分布在两个位置:

1. 工作区数据库(Composer 元数据)

~/.config/Cursor/User/workspaceStorage/<hash>/state.vscdb

每个工作区一个数据库,存储 Composer(对话)的元数据:ID、创建时间、项目路径等。

<hash> 是工作区路径的哈希值,workspace.json 里记录了实际的项目路径。

2. 全局数据库(对话内容)

~/.config/Cursor/User/globalStorage/state.vscdb

所有对话的实际内容(Bubble 数据)存在这个全局数据库里,通过 cursorDiskKV 表按 bubbleId:<composerId>:<bubbleId> 的 key 格式存储。

各平台路径

系统路径
Windows%APPDATA%\Cursor\User\
macOS~/Library/Application Support/Cursor/User/
Linux~/.config/Cursor/User/

导入时发生了什么

扫描 workspaceStorage/*/state.vscdb
  → 读取 composer.composerData 获取对话列表
  → 读取 workspace.json 获取项目路径
  → 从 globalStorage/state.vscdb 读取 bubble 数据
  → 解析每条 bubble(type=1 用户,type=2 助手)
  → 提取文本、思考过程、工具调用信息
  → 按时间排序
  → 写入 SQLite

Bubble 数据结构

Cursor 的每条消息是一个 Bubble,存储格式:

{
  "_v": 3,
  "type": 1,
  "text": "帮我看看这个组件的性能问题",
  "createdAt": "2026-05-10T10:30:00Z",
  "isAgentic": true,
  "toolResults": [...],
  "codeBlocks": [...],
  "allThinkingBlocks": [...]
}
字段说明
type1 = 用户消息,2 = 助手消息
text消息文本
isAgentic是否使用了 Agent 模式
toolResults工具调用结果
codeBlocks代码块
allThinkingBlocks思考过程

ChatCrystal 只提取有文本内容的消息,跳过空的助手消息(流式传输的中间状态)。

孤立对话的发现

有些对话的工作区已经被删除了,但全局数据库里的 Bubble 数据还在。ChatCrystal 会扫描全局数据库中所有 bubbleId:* 的 key,找出不在任何工作区 Composer 列表中的对话。

这些「孤立对话」也会被导入,不会丢失。

自定义数据目录

如果 Cursor 数据不在默认位置:

CURSOR_DATA_DIR=/path/to/cursor/User crystal import

常见问题

导入后看不到 Cursor 对话

  1. 确认 Cursor 有历史对话:打开 Cursor,查看 Composer 面板
  2. 检查路径是否正确:ls ~/.config/Cursor/User/workspaceStorage/(Linux)或对应平台路径
  3. 如果用了自定义 CURSOR_DATA_DIR,确认路径指向 User 目录

导入速度慢

Cursor 的 SQLite 数据库可能比较大(几百 MB),首次扫描需要遍历所有工作区。后续增量导入会快很多。

对话内容不完整

Cursor 的 Bubble 数据依赖 _v(schema 版本)字段。如果 Cursor 更新了数据格式,ChatCrystal 可能需要适配新版本。遇到格式警告时可以忽略,不影响已支持的格式。

下一步


项目地址:github.com/ZengLiangYi…