AI 编程助手数据提取
将你自己的本地聊天记录从 AI 编程助手中提取为统一的 JSONL 格式——用于微调、个人数据分析,或只是在应用的本地数据库被清空之前备份多年对话。
功能特性
自动发现并提取完整对话历史,包括:
- 用户消息与助手回复
- 代码上下文(文件路径、选区、代码片段)
- 代码 diff / 建议的编辑(在工具记录了这些信息的情况下)
- 工具调用及其结果
- 时间戳、会话 ID、项目路径、模型名称——各工具存储中实际包含的内容
支持的来源
| # | 工具 | 存储方式 | 搜索位置 |
|---|---|---|---|
| 1 | Claude Code | JSONL,每个会话一个文件 | ~/.claude/projects/**/*.jsonl |
| 2 | Codex CLI | JSONL “rollout” 文件 | ~/.codex/sessions/**/rollout-*.jsonl |
| 3 | Cursor | SQLite (state.vscdb) | ~/…/Cursor/User/{global,workspace}Storage |
| 4 | Windsurf | SQLite,未公开的 schema(启发式) | ~/…/Windsurf/User/{global,workspace}Storage |
| 5 | Trae | SQLite + JSONL,未公开(启发式) | ~/…/Trae |
| 6 | Continue | JSON,每个会话一个文件 | ~/.continue/sessions/*.json |
| 7 | Gemini CLI | JSON,每个聊天一个文件 | ~/.gemini/tmp/<hash>/chats/*.json |
| 8 | OpenCode | JSON(session/message/part 树) | ~/.local/share/opencode/storage/ |
| 9 | Cline / Roo Code(新增) | JSON,每个任务一个文件夹 | <editor>/User/globalStorage/<ext-id>/tasks/ |
| 10 | Aider(新增) | 每个项目一个 Markdown 记录 | <project>/.aider.chat.history.md |
每个脚本都会自动搜索 macOS、Linux 和 Windows 的常见路径(~/Library/Application Support、~/.config、~/.local/share、%APPDATA%、%LOCALAPPDATA%)——你无需告诉它你用的是哪个操作系统。
为什么加入 Cline 和 Aider
它们是原列表遗漏的两个使用最广泛的 AI 编程工具,而且两者的存储结构确实与众不同(很有参考价值):
- Cline(及其分支 Roo Code)是最流行的开源自主编程 agent 扩展。它按任务存储原始的 Anthropic 格式消息数组,因此它的提取器也可以作为解析该格式最简单的示例,方便你以后添加自己的工具。
- Aider 是最流行的纯终端结对编程工具,它的结构与此处的其他工具完全不同:没有中央数据库,只有位于每个项目目录中的 Markdown 记录文件。收录它正是为了证明这套工具集可以泛化到"某个应用数据文件夹里的 SQLite 或 JSONL"之外。
快速开始
# 无依赖——仅使用标准库
python --version # 需要 3.9+,推荐 3.10+
# 交互式:从编号菜单中选择要提取的来源
python extract.py
# 或直接驱动
python extract.py --all
python extract.py --sources cursor,claude_code,aider
python extract.py --list # 只显示已安装的工具,不提取
python extract.py --all --merge # 同时生成 all_conversations.jsonl
# "提取全部"的简写
./extract_all.sh
CLI 参考
python extract.py [--all] [--sources ids] [--list] [--output-dir DIR]
[--search-path PATH ...] [--merge]
--all 提取所有支持的来源,无提示。
--sources ids 逗号分隔的来源 id(名称见上表,或用 --list 查看)。跳过菜单。
--list 报告每个来源的发现结果但不提取——快速、安全的预览。
--output-dir DIR JSONL 输出目录(默认:./extracted_data)
--search-path PATH 在常规操作系统位置之外额外搜索的目录。可重复使用。主要用于:
- Aider,它没有固定的应用数据文件夹,
需要知道你的项目在哪里
- 其他工具的非标准安装位置
--merge 提取完成后,将所有内容合并为 all_conversations.jsonl
每个提取器也仍然可以独立运行,与原工具集相同(在项目根目录下运行 python -m extractors.cursor,或 python extractors/cursor.py),这在调试单个来源时很方便。
输出格式
每次运行都会在 extracted_data/ 下创建带时间戳的文件:
extracted_data/
├── claude_code_conversations_20260816_143022.jsonl
├── cursor_conversations_20260816_143022.jsonl
├── aider_conversations_20260816_143022.jsonl
├── cline_conversations_20260816_143022.jsonl
└── ... 每个提取的来源一个文件,使用 --merge 时还有 all_conversations.jsonl
每行是一条 JSON 对话:
{
"messages": [
{
"role": "user",
"content": "How do I fix this TypeScript error?",
"code_context": [
{"file": "/Users/you/project/src/index.ts", "code": "const x: string = 123;"}
],
"timestamp": "2026-01-16T14:30:22Z"
},
{
"role": "assistant",
"content": "The error occurs because you're assigning a number to a string type...",
"tool_use": [{"name": "edit_file", "input": {"path": "src/index.ts"}}],
"timestamp": "2026-01-16T14:30:25Z"
}
],
"source": "cursor-composer",
"session_id": "c1a2b3...",
"project_path": "/Users/you/project",
"name": "TypeScript Type Error Fix",
"created_at": 1705414222000
}
字段因来源而略有不同(并非每个工具都记录 code_context、token 用量或 project_path)——messages、source 和 session_id 是你始终可以依赖的字段。
工作原理
- 检测操作系统并构建一个可能的数据根目录列表
(
Application Support、.config、.local/share、%APPDATA%等)。 - 在每个根目录中搜索工具的已知文件夹名。
- 读取存储——JSONL 逐行读取,SQLite 通过只读连接读取(因此运行中的应用不会阻塞我们),或读取 JSON 树,取决于具体工具。
- 规范化找到的内容为上述
messages[]schema。 - 写入 JSONL,每行一条对话,输出到
extracted_data/。
这里的一切都不会以写入模式打开数据库,且每个读取器都有包装保护,一个损坏或被锁定的文件不会让整个运行崩溃——你会得到部分结果并继续,而不是一个堆栈跟踪。
关于 Cursor、Windsurf 和 Trae 的说明
这三者都没有公开其存储 schema,而且 schema 已多次变更(仅 Cursor 就经历了至少三种形态:workspace ItemTable 聊天、内联 composer、拆分的 bubbleId composer)。cursor.py 提取器明确实现了所有三种已知形态。windsurf.py 和 trae.py 则使用通用启发式方法(extractors/common.py::heuristic_extract_chat_from_kv),扫描聊天相关的键并遍历解析后的 JSON,寻找形如 role + text 对的对象。这是尽力的最佳尝试,而非文档化格式——如果未来版本改变形态导致不再匹配,那也在预期之内;调整相应文件中的 KEY_HINTS 或提交 PR。
关于 Aider 的说明
Aider 没有中央会话存储——每个项目目录都有自己的 .aider.chat.history.md。默认情况下,本工具集会扫描你的主目录以及若干常见的项目根目录名(projects、code、dev、repos、workspace、src、Documents),最多深入 5 层目录,跳过 node_modules、.git 等类似目录。如果你的项目存放在其他地方,直接指向它们:
python extract.py --sources aider --search-path ~/client-work --search-path /mnt/data/repos
扩展:添加新来源
每个提取器都是一个具有相同双函数接口的小模块——复制最简单的一个(continue_ext.py 是很好的模板)并填入:
DISPLAY_NAME = "My Tool"
SOURCE_ID = "my_tool"
def find_installations(extra_paths: list[Path] | None = None) -> list[Path]:
"""Return the directories/files worth scanning."""
def extract(installations: list[Path]) -> list[dict]:
"""Return a list of conversation dicts matching the schema above."""
然后在 extract.py 的 REGISTRY 列表中注册它。extractors/common.py 包含 SQLite/JSON/JSONL 读取器以及你可能会用到的两个通用启发式方法。
隐私与安全
本工具从你自己用户账户下运行的工具中提取数据。在分享或用于训练之前:
- 扫描密钥:
pip install detect-secrets --break-system-packages detect-secrets scan extracted_data/*.jsonl - 检查专有代码、API 密钥和个人文件路径——
code_context和tool_use字段是最可能发现它们的地方。 - 不要将
extracted_data/提交到公共仓库(它已在.gitignore中)。如果包含客户或专有工作内容,请保存在加密存储中。
训练用例
from datasets import load_dataset
dataset = load_dataset("json", data_files="extracted_data/*.jsonl", split="train")
dataset = dataset.filter(lambda x: any(m["role"] == "assistant" for m in x["messages"]))
def format_chat(example):
return {"text": tokenizer.apply_chat_template(example["messages"], tokenize=False)}
dataset = dataset.map(format_chat)
故障排除
“No installation found”(未找到安装)——工具未安装、尚无聊天记录,或位于非标准位置。传入 --search-path 直接指向它,或检查 extractors/<tool>.py 的 SEARCH_DIRS / APP_DIR_NAMES 常量并添加你的路径。
Cursor/Windsurf 数据库被锁定——读取以 mode=ro 方式打开,专门为避免运行中的编辑器阻塞提取而设计,但如果仍看到错误,请关闭应用后重新运行。
Windsurf/Trae 找到安装但提取到 0 条对话——common.heuristic_extract_chat_from_kv 中的启发式键匹配未能识别当前的存储键。运行 --list 确认应用目录已被找到,然后直接检查 state.vscdb 的 ItemTable/cursorDiskKV 键(sqlite3 state.vscdb "SELECT key FROM ItemTable")并将匹配项添加到 KEY_HINTS。
免责声明
本工具集从你自己机器上安装的 AI 工具中提取你自己的数据。你需要负责:
- 拥有所提取数据的相应权利,
- 妥善处理任何敏感/专有信息,
- 遵守每个工具的服务条款,
- 在分享或用于训练输出之前扫描密钥。
许可证
MIT——自由使用,包括用于训练 ML 模型。