Contrails:自动归档AI编程助手会话,打造可追溯的开发知识库
1. 项目概述与核心价值
如果你和我一样,在日常开发中重度依赖像 VS Code Copilot、Claude Code 或 Cursor 这类 AI 编程助手,那你一定遇到过这个痛点:一次精彩的、充满曲折推理的对话,在你关闭编辑器或开始新会话后,就彻底消失了。那些尝试过的错误路径、关键的自我修正、以及最终促成解决方案的“灵光一现”,全都留在了 AI 的短期记忆里,无法追溯。下次遇到类似问题时,你只能从头再来,或者花费大量时间在聊天记录里翻找。Contrails 就是为了解决这个问题而生的。它是一个开源桌面应用,名字来源于飞机在高空留下的“凝结尾迹”,寓意着为你的 AI 编程助手会话留下可追溯的“轨迹”。
简单来说,Contrails 会像一个安静的观察者,在后台实时监控你指定的项目目录。一旦检测到上述 AI 助手生成了新的会话文件(比如 VS Code Copilot 的 .jsonl 日志、Claude Code 的 .jsonl 转录文件,或是 Cursor 的 SQLite 数据库更新),它就会立刻解析这些文件,将其转换成清晰、易读的 Markdown 文档,并自动保存到你项目仓库的 contrails/ 目录下。这样一来,所有 AI 参与的思考过程、代码迭代和决策逻辑,都变成了项目历史的一部分,可以像普通文档一样被提交到 Git 中,供未来查阅和复用。
2. 核心功能与工作原理深度解析
Contrails 的核心价值在于其“无感”的自动化记录能力。它不是让你手动复制粘贴聊天记录,而是通过一套精巧的文件系统监控和解析引擎,自动完成捕获、转换和归档的全流程。下面我们来拆解它是如何为三种主流 AI 编程助手工作的。
2.1 对 VS Code Copilot 的支持机制
VS Code Copilot 的聊天会话数据,以事件流的形式存储在用户配置目录下的 workspaceStorage 中。每个 VS Code 工作区对应一个随机 ID 的文件夹,里面可能包含一个 chatSessions/ 子目录,存放着以会话 UUID 命名的 .jsonl 文件。
1. 项目发现与监控 当你通过 Contrails 添加一个项目时,它会扫描系统标准的 VS Code workspaceStorage 路径(macOS: ~/Library/Application Support/Code/User/workspaceStorage/ , Windows: %APPDATA%\Code\User\workspaceStorage\ , Linux: ~/.config/Code/User/workspaceStorage/ ),寻找包含 chatSessions/ 的文件夹。找到后,它会读取同目录下的 workspace.json 文件,来解析出对应的工作区路径和项目名称。添加成功后,Contrails 会使用 Go 的 fsnotify 库对这个 chatSessions/ 目录进行实时监控。
2. 智能增量处理与防误触 这里有一个非常实用的设计:为了避免在首次添加项目时,一次性处理大量历史会话文件导致卡顿,Contrails 会为每个项目记录一个 lastProcessedAt 时间戳。当项目首次被添加时,这个时间戳被设置为“现在”。此后, fsnotify 只会触发处理那些修改时间晚于 lastProcessedAt 的新文件或变更文件。如果你想处理所有历史会话,需要在项目详情页手动点击“Process All Now”。这个设计平衡了实时性和性能。
3. 复杂的事件日志解析 Copilot 的 .jsonl 文件是一种版本 3 的事件日志,并非最终状态的快照。Contrails 的解析器需要“重放”这些事件来构建完整的会话。日志中包含三种事件:
kind: 0: 初始状态快照。kind: 1: 对某个键路径的标量值修补(例如修改会话的customTitle)。kind: 2: 对数组的修补,包含拼接操作(例如向response数组追加新的消息片段)。
解析器会先“物化”出会话的最终状态。为了获得最准确的对话文本,它采用了一种双策略:对于已完成的请求,优先使用 result.metadata.toolCallRounds 作为权威文本来源。这是因为流式传输协议在处理数组拼接时可能会丢失文本片段,而 toolCallRounds 保存了完整的叙述。工具调用、文件编辑和思考块等细节,则从去重后的 response[] 数组中提取,并通过位置与 toolCallRounds 中的条目进行关联。对于仍在进行中的请求(没有 toolCallRounds ),解析器会回退到直接遍历 response[] 数组。
4. 输出与“康复”机制 解析后的会话会被写入 {项目路径}/contrails/ 目录下的 Markdown 文件。文件名优先使用会话的 customTitle ,如果未设置则回退到会话 UUID。Contrails 还实现了一个“康复”逻辑:在应用启动或执行“Process All Now”时,它会扫描 contrails/ 目录下所有以会话 UUID 命名的 .md 文件。如果发现对应的源 .jsonl 文件现在有了 customTitle ,它会自动重新处理并重命名该文件,确保归档的文档有一个友好的名称。
实操心得:文件删除处理 一个值得称赞的细节是,当源
.jsonl文件被删除时(比如 Copilot 自动清理旧会话),Contrails 不会删除对应的 Markdown 文件,而是在文件顶部添加一个明显的“此会话已被删除”的横幅标记。这保留了历史记录的连续性,让你知道曾经有过这段对话,只是源文件不见了。
2.2 对 Claude Code 的钩子与信号机制
Claude Code 的工作方式与 VS Code 扩展不同,它更像一个独立的桌面应用,会话数据存储在 ~/.claude/projects/ 目录下。Contrails 采用了“钩子+信号”的机制来捕获其会话结束事件。
1. 项目发现 Contrails 会扫描 ~/.claude/projects/ 目录,寻找包含 .jsonl 会话文件的文件夹。你也可以通过浏览任意磁盘目录来手动添加包含 Claude Code 项目的文件夹。
2. 钩子安装与守护 这是最精巧的部分。当你添加一个包含 Claude Code 源的项目时,Contrails 会向该项目的 {项目路径}/.claude/settings.local.json 文件中写入一个“Stop hook”。这个钩子是一个 JavaScript 函数,它会在 Claude Code 会话结束时被调用。钩子的作用是将当前会话的元数据(工作目录、转录文件路径、会话ID)写入一个信号文件,存放于 ~/contrails/hook-signals/ 目录下。
为了保证钩子持久有效,Contrails 内部运行着一个 HookEnforcer 守护进程,每 5 秒检查一次钩子文件是否存在且内容正确。如果发现钩子被删除或篡改(比如 Claude Code 更新时重置了配置),它会自动重新安装。只有当你从项目中显式移除 Claude Code 源时,钩子才会被清理。
3. 信号监听与处理 Contrails 启动一个 SignalWatcher ,使用 fsnotify 监控 ~/contrails/hook-signals/ 目录。一旦有新的信号文件出现, SignalWatcher 会读取文件内容,根据其中的工作目录路径匹配到已注册的 Contrails 项目,然后解析对应的 Claude Code 转录文件(.jsonl),并生成 Markdown 文档。
4. 解析与输出 Claude Code 的转录文件格式相对直接,每行是一个 JSON 对象,代表一条消息,包含 role (human/assistant)和 content 块数组(可能是 text、tool_use、tool_result、thinking 等)。Contrails 的解析器会按顺序提取完整的对话,包括工具调用、推理过程和文件编辑内容。输出 Markdown 的标题通常取自第一条用户消息。
注意事项:钩子的持久性 这个设计意味着,即使 Contrails 应用当时没有运行,钩子也已经安装。当下一次 Claude Code 会话结束且 Contrails 启动时,它仍然能处理之前积累的信号文件。但需要注意的是,如果信号文件生成时 Contrails 未运行,它无法进行实时处理,只会在下次启动后处理积压的信号。
2.3 对 Cursor 的双数据库解析策略
Cursor 作为基于 VS Code 的衍生品,其数据存储方式更为复杂,它使用了两个 SQLite 数据库来管理会话数据,因此 Contrails 的解析策略也最为独特。
1. 项目发现 类似于 VS Code,Contrails 会扫描 Cursor 的 workspaceStorage 目录,寻找每个工作区对应的 state.vscdb 文件,并通过 workspace.json 解析出工作区路径。
2. 双重监控与防抖 Cursor 的数据写入非常频繁。Contrails 需要同时监控两个位置:
- 工作区级数据库 :每个工作区下的
state.vscdb文件,主要存储 composer(会话)列表。 - 全局数据库 :位于 Cursor 配置目录下
globalStorage/中的state.vscdb文件。所有会话的详细消息内容(bubbles)都写在这里,无论哪个工作区发起的会话。
Contrails 使用 fsnotify 监控这两个路径。由于 Cursor 的写入是爆发式的,Contrails 引入了 2 秒的防抖机制:当检测到文件变更后,会等待 2 秒没有新事件才触发处理,以避免不必要的重复解析。
3. 复杂的 SQLite 数据关联解析 解析器需要关联两个数据库的信息:
- 从 工作区数据库 的
ItemTable中,读取composer.composerData键对应的值,获取该工作区下的 composer(会话)列表及其元数据(如会话名称)。 - 从 全局数据库 的
cursorDiskKV表中,提取两种关键数据:composerData:{composerId}:包含会话元数据和fullConversationHeadersOnly数组,这个数组提供了消息的权威顺序。bubbleId:{composerId}:{bubbleId}:存储每条消息(气泡)的详细内容,包括用户消息(type=1)、AI消息(type=2)以及其中包含的富媒体内容(如工具调用、思考块、代码片段)。这些内容通过capabilityType等字段编码,需要额外解码。
解析器会按照 fullConversationHeadersOnly 提供的顺序,依次读取对应的 bubble 数据,重建完整的对话流。
4. 输出 最终,解析出的会话会以同样的 Markdown 格式输出到项目的 contrails/ 目录。文件名优先使用 composer 的 name 字段。
踩坑记录:全局数据库是关键 最初尝试只解析工作区数据库时,会发现只能拿到会话列表,拿不到具体的对话内容。必须同时读取全局数据库的
cursorDiskKV表,才能拼凑出完整的对话。这是理解 Cursor 数据模型的关键。
3. 从零开始:安装、配置与核心工作流
理解了原理,我们来看看如何将它用起来。Contrails 提供了 macOS、Windows 和 Linux 三端的原生应用,开箱即用。
3.1 各平台安装详解
macOS
- 从 GitHub Releases 页面下载
Contrails-macos.zip。 - 解压后,将
contrails.app拖拽到“应用程序”文件夹。 - 关键一步 :在终端中执行
xattr -cr /Applications/contrails.app。这是因为 macOS 对未经过公证(Notarization)的开发者应用有安全限制,此命令可以移除这些限制属性。项目作者也坦诚地指出,苹果的公证服务每年需要 99 美元,这对独立开发者是一笔不小的开销,因此目前采用了这个变通方案。 - 之后就可以像启动普通应用一样启动 Contrails 了。
Windows
- 下载
Contrails-windows.zip并解压。 - 直接运行解压出的
contrails.exe即可。Windows 通常没有额外的安全限制。
Linux
- 下载
Contrails-linux.zip并解压。 - 在终端中,进入解压目录,执行
chmod +x contrails赋予可执行权限。 - 运行
./contrails。 - 依赖检查 :Contrails 基于 Wails 框架,需要 WebKit2GTK 和 GTK3 运行时库。在 Ubuntu/Debian 上,可以运行
sudo apt install libgtk-3-0 libwebkit2gtk-4.1-0来安装。大多数现代 Linux 桌面发行版已经预装了这些库。
3.2 核心工作流:添加与管理项目
安装并启动后,你会看到一个简洁的侧边栏和主界面。核心操作就是添加项目。
-
添加项目 :点击侧边栏的 “+” 按钮,会弹出“添加项目”对话框。Contrails 会自动扫描系统中已存在的、支持 AI 助手的工作区:
- VS Code/Cursor 工作区 :它会列出在
workspaceStorage中发现的所有有效工作区。 - Claude Code 项目 :它会列出在
~/.claude/projects/中发现的项目目录。 你也可以点击“浏览”手动选择磁盘上的任意文件夹作为项目根目录。
- VS Code/Cursor 工作区 :它会列出在
-
项目配置 :选择项目后,你需要为其命名(默认会使用文件夹名或工作区名),并确认输出目录(默认为
{项目路径}/contrails/)。一个项目可以同时包含多种 AI 助手源。例如,如果你在一个项目里既用 VS Code Copilot 又用 Claude Code,Contrails 会同时监控两者,并将所有会话统一归档到同一个contrails/目录下。 -
状态监控 :添加成功后,项目会出现在侧边栏列表。项目名称旁边有一个状态指示点:
- 绿色圆点 :表示正在监控中。
- 灰色圆点 :表示监控已暂停。 你可以通过右键菜单对项目进行“重命名”、“暂停/恢复监控”、“立即处理所有会话”或“移除”操作。
-
查看成果 :在主界面选择项目后,你可以看到该项目的详细信息,包括监控路径、输出路径、已发现的会话源类型等。所有生成的 Markdown 文件都会保存在你配置的输出目录(默认是
contrails/)下。文件命名清晰,内容结构良好,包含了完整的对话、代码差异和 AI 的思考过程。
3.3 输出文件管理与版本控制
生成的 Markdown 文件设计得非常友好,适合直接纳入版本控制。
- 目录结构 :所有会话文件都平铺在
contrails/目录下,没有多层嵌套,便于浏览。 - 文件命名 :优先使用会话标题(如
实现用户登录功能.md),无标题时使用会话ID(如session_abc123.md)。 - 内容格式 :Markdown 内容清晰地区分了用户消息、AI 回复、思考过程、工具调用和代码块。代码差异会以 diff 格式呈现,可读性极高。
- 提交建议 :强烈建议将
contrails/目录添加到你的.gitignore排除规则中,而是选择性地将有价值的会话记录提交到仓库。你可以将其视为一种“开发日志”或“决策记录”,只提交那些对理解项目关键决策有重要意义的对话。
4. 高级特性与实用技巧
除了核心的监控与转换功能,Contrails 还包含一些提升体验和可靠性的高级特性。
4.1 匿名遥测与隐私控制
Contrails 集成了 PostHog 用于收集匿名使用数据,目的是帮助开发者了解应用的整体使用情况(如活跃用户数、创建的会话轨迹数量、各助手的使用比例等)。
- 收集什么 :仅收集聚合指标,如“应用启动”、“项目添加”、“会话轨迹创建”。 绝不收集任何个人数据、文件内容、对话文本或项目路径 。
- 设备标识 :首次启动时会在本地生成一个随机 UUID 作为设备标识,存储在
~/.config/contrails/device_id。没有账户系统,无法关联到个人。 - 完全可控 :你可以在侧边栏底部找到一个“Telemetry on/off”开关,随时开启或关闭遥测。这个偏好设置会立即保存并在重启后生效。
- 故障安全 :所有遥测调用都是非阻塞的。如果 PostHog 服务不可用,事件会被静默丢弃,绝不会影响应用功能。
- 开发构建 :在本地开发构建(
wails dev)或没有注入 API 密钥的构建中,遥测功能会被完全禁用。
4.2 自动更新检查
Contrails 在启动时会自动检查 GitHub Releases 页面,看是否有新版本发布。如果检测到更新,界面会显示一个一键更新按钮。点击后,应用会自动下载新版应用包(如 macOS 的 .app 包)并进行原子化替换,然后重启应用。这确保了用户能方便地获取功能改进和错误修复。
4.3 配置与数据存储
了解数据存储位置有助于调试和备份。
- 项目列表 :保存在平台的标准配置目录下。
- macOS:
~/Library/Application Support/contrails/projects.json - Windows:
%APPDATA%/contrails/projects.json - Linux:
~/.config/contrails/projects.json
- macOS:
- 遥测设置与设备ID :
~/.config/contrails/settings.json:存储analyticsEnabled布尔值。~/.config/contrails/device_id:存储设备 UUID。
- Claude Code 信号文件 :临时信号文件存储在
~/contrails/hook-signals/,处理完成后会被清理。
4.4 故障排查与常见问题
在实际使用中,你可能会遇到一些小问题。以下是一些排查思路:
1. 添加项目后无反应,监控不到新会话。
- 检查路径权限 :确认 Contrails 有权限读取目标目录(如 VS Code 的
workspaceStorage)。 - 确认会话源 :在项目详情页检查是否成功识别出了预期的 AI 助手源(如 VS Code Copilot)。如果没有,可能是工作区存储路径不标准。
- 手动触发处理 :尝试在项目右键菜单点击“Process All Now”,看是否能处理出已有的历史会话。这可以测试解析功能是否正常。
- 查看日志 :Contrails 目前没有图形界面的日志窗口,但你可以通过命令行启动应用,在终端中查看输出信息,可能会包含错误提示。
2. Claude Code 会话没有被捕获。
- 检查钩子安装 :前往你的项目目录,查看
.claude/settings.local.json文件是否存在,以及其中是否包含 Contrails 写入的stop钩子函数。 - 检查信号目录 :查看
~/contrails/hook-signals/目录下是否有新生成的.json信号文件。如果有文件但没被处理,可能是 Contrails 的信号监控服务没有正常运行。 - 重启 Claude Code :有时 Claude Code 需要重启后才能加载新的钩子配置。
3. Cursor 会话内容不完整或顺序错乱。
- 等待防抖 :Cursor 写入频繁,Contrails 有 2 秒防抖。完成一次对话后,稍等几秒再检查输出文件。
- 全局数据库访问 :确保 Contrails 有权限读取 Cursor 的全局
globalStorage目录。在 Linux 上,可能需要检查 Flatpak 或 Snap 包安装的 Cursor 其数据目录是否在非标准位置。
4. 生成的 Markdown 文件名为会话ID而非标题。
- 这是正常现象。对于没有设置自定义标题的会话,Contrails 会使用会话ID作为文件名。如果后续该会话被赋予了标题(例如在 Copilot 聊天界面重命名),你可以在 Contrails 中对该项目执行“Process All Now”,触发“康复”逻辑,文件会被自动重命名。
5. 开发者指南:构建、贡献与扩展
Contrails 是一个开源项目,技术栈清晰(Go + React + TypeScript,使用 Wails 框架),非常适合开发者深入研究、自行构建甚至参与贡献。
5.1 本地开发环境搭建
前置条件
- Go ≥ 1.23
- Node.js ≥ 18
- Wails CLI :
go install github.com/wailsapp/wails/v2/cmd/wails@latest - Lefthook (用于 Git 钩子):
go install github.com/evilmartians/lefthook@latest
启动开发模式
# 克隆项目
git clone https://github.com/ThreePalmTrees/Contrails.git
cd Contrails
# 安装前端依赖(忽略可能存在的 postinstall 脚本)
cd frontend && yarn --ignore-scripts && cd ..
# 安装 Git 钩子,用于提交前自动运行代码检查和测试
lefthook install
# 启动开发服务器,支持前端热重载
wails dev
执行 wails dev 后,会打开一个开发窗口,前端代码的修改会实时刷新。
5.2 项目架构回顾
作为开发者,理解项目架构能帮助你快速定位代码。
-
后端 (Go) :
app.go: 组合根,协调项目 CRUD、驱动注册、处理分发。agent/包: 核心。定义了AgentDriver接口和SessionParser接口。vscode/,claudecode/,cursor/子包分别实现了对三种助手的支持。watcher.go: 封装fsnotify,负责文件系统监控。analytics.go和updater.go: 分别处理遥测和自动更新。runtime.go: 定义了Logger,EventEmitter等接口,便于测试时注入模拟实现。
-
前端 (React + TypeScript) :
useProjects.ts: 核心状态管理 Hook,加载项目、监听后端事件。ProjectList.tsx,ProjectDetail.tsx: 侧边栏项目和主详情视图组件。- 整体采用相对简单的组件结构,状态通过 Wails 的运行时与前端的绑定进行通信。
5.3 测试与构建
运行测试 项目包含针对各 Agent 解析器的单元测试,使用 t.TempDir() 进行文件系统隔离。
go test ./... -v
测试数据位于 testdata/fixtures/ 目录,按助手类型分文件夹存放。Cursor 的测试使用内存 SQLite 数据库。
构建发布版本 项目提供了针对三个平台的构建脚本,简化了流程。
# 构建开发版(无遥测,无更新检查)
./buildMacOS.sh
./buildWindows.sh
./buildLinux.sh # Linux 需要安装 GTK 和 WebKit 开发库
# 构建带版本号和遥测的发布版(CI/CD 使用)
./buildMacOS.sh 1.2.3 phc_your_posthog_key
./buildWindows.sh 1.2.3 phc_your_posthog_key
./buildLinux.sh 1.2.3 phc_your_posthog_key
构建脚本内部调用了 wails build ,并通过 -ldflags 注入版本号和 PostHog API 密钥。当版本号设为 "dev" 时,遥测和更新检查功能会被禁用。
5.4 如何贡献
项目欢迎贡献。一个有趣的特色是,它鼓励贡献者提交由 AI 编码助手生成的“会话轨迹”(即 contrails/ 目录下的记录),作为工作过程的证明。
- Fork 仓库 。
- 安装前置条件 并配置好开发环境。
- 创建特性分支 。
- 进行修改 。
- 提交更改 。配置的 Lefthook 会在提交前自动运行
yarn build(前端构建)、go vet和go test,确保代码质量。 - 包含会话轨迹 :将你开发此功能时 AI 助手的对话记录(项目中的
contrails/目录)一并提交,这很有趣地成为了“开发过程的元数据”。 - 发起 Pull Request 到主仓库的
main分支。
5.5 扩展支持新的 AI 助手
Contrails 的架构设计使得支持新的 AI 助手变得相对清晰。你需要:
- 在
agent/目录下创建一个新的子包(例如agent/githubcopilotchat)。 - 实现
agent/driver.go中定义的AgentDriver接口,主要负责该助手的会话发现、监控逻辑。 - 实现
agent/contrail.go中定义的SessionParser接口,负责将助手的原生数据格式解析为统一的ParsedSession结构。 - 在
app.go的驱动注册处,将新驱动的工厂函数注册进去。 - 在前端
AddProjectDialog.tsx中,添加对新助手类型的自动发现支持(如果需要)。
整个过程中,最复杂的部分通常是逆向工程目标助手的会话存储格式(JSON、SQLite 或其他),并将其映射到 Contrails 的通用消息模型。
更多推荐



所有评论(0)