我同时订了 Claude Pro 和 ChatGPT Plus,日常就是 Claude Code 和 Codex 来回切。切换本身不难,难的是「交接」:Codex 打开的时候对你的项目、做到哪一步、哪些路已经试过不行,一无所知。
这篇讲讲我是怎么把这个交接做成自动的,顺便拆一下两边的会话到底存在哪、长什么样。
1. Claude Code 的会话:本地 JSONL
Claude Code 每个会话都是本地的一个 JSONL 文件,在 ~/.claude/projects/<项目路径转义>/<session-id>.jsonl。一行一个事件:用户消息、助手回复、工具调用、工具结果、todo 更新等等。
这意味着你不需要任何 API 就能完整读到一个会话的来龙去脉。难点在于「读多少」:
- 原始文件里工具结果可能非常大(读整个文件、跑测试的完整输出),直接塞给另一个 agent 会把上下文撑爆。
- 所以要压缩:消息保留原文,工具调用只保留「调了什么、对什么、结果大意」,再加上当前 todo 和 git 状态。
2. Codex 的会话:用它自己的 app-server
Codex 也有本地的会话记录,但我没去解析它的文件格式,而是只走 Codex 自带的 app-server 接口:创建线程、往线程里发消息、读线程内容。好处是 Codex 升级改格式时不容易坏,而且写入走的是官方路径。
3. 双向同步:游标 + 只送一次
最开始我做的是「一次性导出」:Claude 写个 HANDOFF.md,Codex 读。问题是切回来的时候,Claude 不知道 Codex 干了什么,又得再导一次。
现在的做法:
- 一个 Claude 会话和一个 Codex 线程配成一对。
- 两边各记一个游标:上次同步到哪一条。
- 切过去的时候,只把游标之后的新进展压缩后发过去。
- 在 Claude 这边,用
UserPromptSubmithook:你发下一条消息时,自动把 Codex 那边的新进展附在前面。每条更新只送一次,通常几千 token。
结果就是两边像在做「同一个任务」,而不是两个各说各话的聊天。
4. 什么时候切:看额度
我切换最多的原因是 Claude 的 5 小时窗口撞了。所以还加了一个提醒:哪边的 5 小时额度到 90%,就提示该切了。
额度数据我没自己去抓,而是读你可能已经在用的工具写出来的数据:CodexBar、ccusage、abtop、claude-hud、ccstatusline 等等,装了哪个读哪个,不需要配置。
5. 工具
上面这些我做成了一个开源的 Mac 菜单栏小工具 ccx,菜单栏点一下 ⇄ 就交接,全部本地运行,不需要账号也没有服务器。
就算你不用它,上面的思路也可以自己实现:读 Claude 的 JSONL、压缩工具输出、记游标、只送增量。关键是让交接这一步足够便宜,不然你会懒得切,最后还是在那干等额度。