把 Codex Home 从隐藏目录搬出来,用 Git 管理 AI 工作流资产

最近开始认真考虑一个问题:Codex 里沉淀下来的东西,应该怎么长期保存?

一开始,做法很朴素:把一些重要目录打成 zip,上传到云盘。这样能解决「电脑坏了怎么办」「换设备怎么办」这类问题,但很快就会遇到新的不舒服:

  • 每次备份都是一个新的压缩包,不容易看差异。
  • 只知道「备份了」,但不知道具体变了什么。
  • 想回到某个历史版本,需要先下载、解压、人工比较。
  • 想在多台设备之间同步,也不够自然。

这些问题都不是压缩包擅长解决的。它更像一个快照,而不是一个持续演进的工作区。

后来又回到了一个很老但很好用的答案:Git。

Codex 里到底有哪些值得保存的东西

Codex 的本地状态通常在 ~/.codex 下面。

这个目录有点像一个 AI 工具的「用户主目录」。里面既有真正值得保存的长期资产,也有大量只属于当前机器的运行状态。

值得保存的大概是这些:

AGENTS.md
rules/
skills/
memories/MEMORY.md
memories/memory_summary.md
memories/extensions/ad_hoc/notes/

它们分别对应几类东西。

AGENTS.md 是全局行为规则。比如希望 Codex 用什么语气、怎么协作、回答时注意什么,这些都可以放在这里。

rules/ 是更细的规则文件。它们通常用于补充一些本机长期有效的执行边界。

skills/ 是最重要的一类。Skill 会把一套可复用流程固化下来,让 Codex 下次遇到类似任务时不用从零开始理解。比如文档整理流程、某类调试流程、某个工具链的操作规范,都可以变成 Skill。

memories/MEMORY.mdmemories/memory_summary.md 更像长期记忆索引。它们不应该替代真实资料,但能帮助 Codex 在新会话里快速知道:哪些经验已经沉淀过,应该去哪里找。

memories/extensions/ad_hoc/notes/ 适合放那些主动补充的长期规则或偏好。

这些内容的共同点是:它们是可读的、可迁移的、可以被人审查的。Git 很适合管理它们。

不该被 Git 管的东西

~/.codex 里也有很多东西不适合提交。

比如:

auth.json
sessions/
archived_sessions/
browser-profiles/
cache/
.tmp/
tmp/
*.sqlite
*.sqlite-shm
*.sqlite-wal
logs/
worktrees/
generated_images/
installation_id

这些东西的问题不一样。

auth.jsonbrowser-profiles/ 可能包含登录态或凭证,不能进 Git。

sessions/archived_sessions/ 体积会越来越大,也可能包含大量上下文和内部信息。它们对于「换设备后继续使用 Codex」并没有那么关键。

*.sqlitecache/.tmp/logs/ 都是运行态。它们强依赖当前机器、当前版本、当前进程状态。同步到另一台设备上,价值不大,风险不少。

这里有一个很重要的边界:Git 应该保存的是「可迁移能力」,不是「运行现场」。

第一个想法:直接把 ~/.codex 变成仓库

最直接的想法是:

cd ~/.codex
git init

然后配一个白名单 .gitignore,默认忽略所有内容,只放行明确要同步的文件。

大概像这样:

*

!.gitignore
!AGENTS.md

!rules/
!rules/**

!skills/
!skills/**

!memories/
memories/*
!memories/MEMORY.md
!memories/memory_summary.md
!memories/extensions/
memories/extensions/*
!memories/extensions/ad_hoc/
memories/extensions/ad_hoc/*
!memories/extensions/ad_hoc/notes/
!memories/extensions/ad_hoc/notes/**

这个方案能工作。

另一台设备初始化时,也可以直接:

git clone <private-repo-url> ~/.codex
codex login

如果 ~/.codex 已经存在,就先备份:

mv ~/.codex ~/.codex.bak.$(date +%F-%H%M%S)
git clone <private-repo-url> ~/.codex
codex login

但这个方案有一个体验问题:~/.codex 是隐藏目录,平时不太顺手。

想经常用编辑器打开它,看看 Skill 写了什么,检查某条记忆有没有过期,或者把某个 Skill 复制出来给别人参考。隐藏目录虽然不是不能打开,但总归不如放在一个明确的代码目录里自然。

更舒服的做法:把 Codex Home 放到代码目录

更好的办法是把 Codex Home 放到一个显式目录,比如:

~/code/codex-home

然后让 ~/.codex 指向它。

mv ~/.codex ~/.codex.bak.$(date +%F-%H%M%S)
git clone <private-repo-url> ~/code/codex-home
ln -s ~/code/codex-home ~/.codex
codex login

这样有几个好处。

第一,目录变得好找。它就在日常代码目录下,可以直接用编辑器打开。

第二,Git 管理更自然。仓库根目录就是 ~/code/codex-home,不用总是意识到自己在一个隐藏目录里操作。

第三,迁移体验也更清楚。新设备上先 clone 到 ~/code/codex-home,再建立 ~/.codex 软链接即可。

第四,Codex 仍然可以继续从 ~/.codex 读取数据。对 Codex 来说路径没有变;对人来说,真实文件已经搬到了更容易观察的位置。

这其实是一个很常见的做法:让程序继续使用它熟悉的默认路径,但把真实数据放到更适合人维护的位置。

CODEX_HOME 也能改,但桌面应用更适合软链接

Codex CLI 支持 CODEX_HOME

也就是说,纯 CLI 场景里可以这样启动:

CODEX_HOME=~/code/codex-home codex

这样 Codex 会从指定目录读取配置、Skill 和其他 home 数据。

不过如果平时主要使用桌面应用,情况会复杂一点。图形界面启动的应用不一定继承你在 shell 里设置的环境变量。你当然可以想办法给桌面应用注入环境变量,但这会让维护复杂度上升。

因此对桌面应用来说,软链接更稳:

~/.codex -> ~/code/codex-home

Codex 仍然读默认位置。Git 仓库仍然在好找的位置。两边都舒服。

私有仓库也要白名单

即使仓库是私有的,也不建议放开所有文件。

私有不等于可以把登录态、缓存和本机数据库全部推上去。原因有两个。

第一,风险不必要。凭证、浏览器 profile、session 这类东西没有必要离开当前设备。

第二,噪声会淹没真正有价值的变更。你真正想看的是「新增了哪个 Skill」「改了哪条规则」「记忆摘要有什么变化」,而不是一堆 sqlite wal 文件和缓存文件在变。

所以 .gitignore 应该从第一天就用白名单模式。

提交前可以用一条命令做保险检查:

git ls-files | rg 'auth|session|sqlite|browser-profiles|cache|tmp|logs|worktrees'

如果有输出,就先停下来检查。

换设备时会发生什么

用这个方案换设备,大致是:

git clone <private-repo-url> ~/code/codex-home
ln -s ~/code/codex-home ~/.codex
codex login

然后 Codex 会重新生成那些不进 Git 的运行态:

auth.json
sessions/
browser-profiles/
cache/
*.sqlite
logs/
tmp/

这很正常。

换设备真正要迁移的是你的工作方式和能力,不是旧机器的所有运行痕迹。

新的设备应该继承:

  • 你写过的 Skill。
  • 你长期使用的规则。
  • 你希望 Codex 记住的偏好。
  • 可读的记忆索引。

新的设备应该重新生成:

  • 登录态。
  • 浏览器授权。
  • 临时会话。
  • 缓存和数据库。

这个边界一旦想清楚,迁移就会简单很多。

这个方案解决什么

这个方案不是为了「备份一个目录」。

它解决的是:把 AI 工具的个人工作流资产,从一个隐藏的运行状态目录里拿出来,变成一个可读、可审查、可版本管理、可迁移的仓库。

压缩包适合兜底。
Git 适合长期演进。
软链接适合兼顾工具默认路径和人的维护体验。

最后形成的结构大概是:

~/code/codex-home/        # Git 仓库,方便人维护
~/.codex -> ~/code/codex-home

仓库里只跟踪可迁移资产:

AGENTS.md
rules/
skills/
memories/MEMORY.md
memories/memory_summary.md
memories/extensions/ad_hoc/notes/
.gitignore

其它运行态留在工作区,但不进 Git。

这样一来,Codex 还是那个 Codex;只是它的长期能力终于有了一个清楚、可见、能版本管理的家。