把 Codex Home 从隐藏目录搬出来,用 Git 管理 AI 工作流资产
最近开始认真考虑一个问题:Codex 里沉淀下来的东西,应该怎么长期保存?
一开始,做法很朴素:把一些重要目录打成 zip,上传到云盘。这能兜住「电脑坏了怎么办」「换设备怎么办」这类问题,但不适合长期维护:
- 每次备份都是一个新的压缩包,不容易看差异。
- 只知道「备份了」,但不知道具体变了什么。
- 想回到某个历史版本,需要先下载、解压、人工比较。
- 想在多台设备之间同步,也不够自然。
压缩包更像一个快照,不像一个持续演进的工作区。后来又回到了一个很老但很好用的答案:Git。
先分清资产和运行态
Codex 的本地状态通常在 ~/.codex 下面。这个目录有点像一个 AI 工具的用户主目录:里面既有真正值得保存的长期资产,也有大量只属于当前机器、当前登录态和当前进程的运行状态。
值得保存的是可读、可审查、可迁移的工作流资产:
codex-home/
AGENTS.md
global/AGENTS.md
agents_references/
rules/
skills/
tools/
env/global/AGENTS.md 是 Codex 全局运行规则,比如希望 Codex 用什么语气、怎么协作、遇到某类任务先看什么引用。
agents_references/ 放全局规则引用的细节文档。全局入口只保留触发条件和路径,长规则拆到这里,能减少每次会话一上来就读进大量细节。
rules/ 是更细的命令或执行规则,适合长期稳定、跨任务复用的边界。
skills/ 是最重要的一类。Skill 会把一套可复用流程固化下来,让 Codex 下次遇到类似任务时不用从零开始理解。比如文档整理流程、某类调试流程、某个工具链的操作规范,都可以变成 Skill。
tools/ 放可复用的小工具、脚本或 MCP server 源码。这里保存源码、README、lockfile 和必要配置,不保存依赖安装目录和运行缓存。
env/ 只适合私有仓库。它可以保存内部工具需要的环境变量文件,但不适合作为公开模板直接分发。如果仓库会公开,应该改成 .env.example 或文档说明,真实 env 留在本机。
这些内容的共同点是:它们能被人读懂,也能通过 Git 看到演进历史。
不该提交的是运行态:
auth.json
config.toml
sessions/
archived_sessions/
browser-profiles/
cache/
plugins/cache/
tmp/
.tmp/
*.sqlite
*.sqlite-shm
*.sqlite-wal
logs/
worktrees/
generated_images/
shell_snapshots/
node_modules/
.venv/
__pycache__/auth.json、browser-profiles/ 可能包含登录态或凭证,不能进 Git。sessions/ 和 archived_sessions/ 体积会越来越大,也可能包含大量上下文和内部信息。*.sqlite、cache/、.tmp/、logs/ 都强依赖当前机器和当前版本,同步到另一台设备上价值不大,风险不少。
这里的边界很简单:Git 保存的是「可迁移能力」,不是「运行现场」。
为什么不再把整个 ~/.codex 变成仓库
最直接的想法是:
cd ~/.codex
git init然后配一个白名单 .gitignore,默认忽略所有内容,只放行明确要同步的文件。这个方案能工作,但长期维护会有两个问题。
第一个问题是隐藏目录不顺手。想经常用编辑器打开它,看看 Skill 写了什么,检查某条规则有没有过期,隐藏目录当然也能打开,但不如放在一个明确的代码目录里自然。
第二个问题更实际:~/.codex 不是纯源码目录。Codex 会持续往里面写登录态、sqlite、session、缓存、插件缓存、浏览器 profile 和临时状态。哪怕 .gitignore 很严格,人也会一直站在一个运行态目录里维护长期资产,心智负担会变高。
早期我也试过另一种更粗的做法:把 ~/.codex 整个迁到 ~/code/codex-home,再让 ~/.codex 指向它:
~/.codex -> ~/code/codex-home这比直接在隐藏目录里维护舒服,但它仍然把运行态和可迁移资产放在同一个真实目录里。维护一段时间后,更清楚的做法是保留 ~/.codex 作为运行目录,只把需要长期维护的文件和目录软链出去。
现在的结构:运行态留在 ~/.codex
现在推荐的是细粒度软链。
真实仓库放在日常代码目录:
~/code/codex-home/Codex 继续使用默认运行目录:
~/.codex/两边通过少数软链接连起来:
~/.codex/AGENTS.md -> ~/code/codex-home/global/AGENTS.md
~/.codex/agents_references -> ~/code/codex-home/agents_references
~/.codex/rules -> ~/code/codex-home/rules
~/.codex/skills -> ~/code/codex-home/skills
~/.codex/env -> ~/code/codex-home/env
~/.codex/tools -> ~/code/codex-home/tools这样,Codex 仍然从熟悉的位置读取规则、Skill 和工具;人维护的是一个普通 Git 仓库。登录态、会话、sqlite、浏览器 profile、插件缓存和临时目录继续留在 ~/.codex,不跟着仓库漂移。
这也是为什么现在不再推荐把整个 ~/.codex 都软链出去。整目录软链解决了「目录不好找」的问题,但没有真正拆开「长期资产」和「运行现场」。
skills/ 有一个小例外,值得单独说一下。Codex 可能会在 ~/.codex/skills/.system/ 下放系统生成的 Skill,这些内容不是我手写的工作流资产,也不应该提交到自己的仓库。
如果只把自己写的 Skill 一个个链接进去,通常碰不到它;如果为了省心,把整个 skills/ 目录链接到仓库里,.system/ 就可能出现在仓库工作区里。这个时候不需要把整个方案推倒,只要在仓库 .gitignore 里忽略:
skills/.system/换句话说,真正需要隔离的是系统生成内容。它和人工维护内容刚好落在同一个目录层级;把 .system/ 当成运行态处理,仍然可以保留整个 skills/ 目录链接的便利。
AGENTS.md 要拆成两层
把 Codex 资产放进一个仓库后,会遇到一个容易忽略的歧义:仓库根目录也可以有 AGENTS.md,而 Codex 自己的 home 目录也有 AGENTS.md。
如果仓库根目录的 AGENTS.md 既被当成 Codex 全局运行规则,又被当成 codex-home 这个仓库的项目说明,读起来会很别扭。人在维护仓库时看到它,会以为这是仓库规则;Codex 在别的项目里读到它时,又应该把它理解成全局协作规则。两种语义混在同一个文件里,后面一定会误解。
更清楚的分法是:
codex-home/
AGENTS.md # 只描述这个仓库怎么维护、怎么迁移
global/AGENTS.md # Codex 全局运行规则然后让 Codex home 里的入口指向全局规则:
~/.codex/AGENTS.md -> ~/code/codex-home/global/AGENTS.md这样,根 AGENTS.md 只回答仓库问题:这个仓库保存什么、不保存什么、新机器怎么链接、提交前怎么检查。global/AGENTS.md 只回答 Codex 行为问题:默认语言、协作方式、遇到 Figma 或浏览器任务时读什么 Skill。
global/AGENTS.md 里引用细节文档时,路径也应该写成 Codex home 视角:
${CODEX_HOME:-$HOME/.codex}/agents_references/browser-channel.md不要写成简单的 agents_references/browser-channel.md。全局规则会在很多项目里生效,引用路径必须能从 Codex home 找到,而不是依赖当前工作目录刚好是 codex-home。
当前机器怎么迁移
如果一台机器已经在使用 Codex,迁移时不要一上来就动登录态和 session。更稳的顺序是:
- 在日常代码目录里创建或 clone 私有仓库。
- 把长期资产整理进仓库。
- 备份
~/.codex里同名文件或目录。 - 用软链接把仓库里的资产挂回
~/.codex。 - 启动 Codex,确认规则和 Skill 能正常读取。
- 最后再提交和推送仓库。
准备仓库目录这一步仍然可以手动做:
mkdir -p ~/code/codex-home/global
cd ~/code/codex-home
git init
git branch -M main再把已有资产放进仓库。不同机器的情况不一样,下面只表达路径关系,真正执行前先看清自己本机是否已有对应目录:
cp ~/.codex/AGENTS.md ~/code/codex-home/global/AGENTS.md
cp -R ~/.codex/rules ~/code/codex-home/rules
cp -R ~/.codex/skills ~/code/codex-home/skills如果已经有 agents_references/、tools/ 或私有 env/,也可以按同样方式放进仓库。没有就先不创建空目录。
真正把仓库挂回 Codex home 时,现在更推荐走一个跨平台入口:
node tools/setup-codex-home.mjs --repo "$HOME/code/codex-home" --dry-run
node tools/setup-codex-home.mjs --repo "$HOME/code/codex-home"Windows PowerShell 里仍然是同一个脚本,只是路径写法不同:
node .\tools\setup-codex-home.mjs --repo "$HOME\Desktop\code\codex-home" --dry-run
node .\tools\setup-codex-home.mjs --repo "$HOME\Desktop\code\codex-home"--dry-run 只打印计划,不改文件。确认它会备份哪些旧路径、创建哪些链接以后,再跑真实命令。
这个脚本内部做几件事:
- 已经是正确链接时直接跳过。
- 已有普通文件或错误链接时,先备份成
.local-backup.<timestamp>。 - 缺失的可选目录会跳过,不为了凑结构创建空目录。
AGENTS.md、agents_references、rules、skills、env、tools都按同一份映射关系处理。skills/.system/继续按本机运行态处理,只保留在工作区并被.gitignore忽略。
迁移成功后做几个检查:
test -f ~/.codex/skills/skill-creator/SKILL.md
git -C ~/code/codex-home ls-files | rg '(^|/)(auth|sessions|browser-profiles|cache|tmp|logs|worktrees|generated_images|.*\.sqlite)'第一条用来确认 Skill 可读。第二条用来确认 Git 没有跟踪运行态或敏感文件;正常情况下应该没有输出。链接本身是否正确,脚本最后也会打印 verify 结果。
平台差异收进脚本里
跨平台方案的核心是同一个脚本入口。脚本内部按平台选择正确的文件系统原语。
macOS 和 Linux 上,文件、目录都可以走普通 symlink。Windows 上要更细一点:目录优先用 junction;文件优先用 symbolic link,如果当前权限不允许创建文件符号链接,再退到同盘 hard link。
原生 Windows 路线现在是可以成立的。Codex 官方文档里,Windows 桌面应用和 CLI 可以在 PowerShell 里配合 Windows sandbox 使用;不需要为了 Codex 本身强行套一层虚拟机。真正要考虑的是链接类型、权限和本机文件系统。
Git Bash 适合日常跑很多类 Unix 命令,但它没有改变底层文件系统。把 macOS 那段 ln -s 原样交给 Git Bash,最后生成什么仍然会受到 Windows 权限、MSYS/Git 配置和目标类型影响。迁移这种长期动作,交给 Node 脚本显式分支更稳。
Windows 上可以这样检查:
Get-Item "$HOME\.codex\AGENTS.md" | Format-List FullName,LinkType,Target
Test-Path "$HOME\.codex\skills\skill-creator\SKILL.md"
git -C $Repo ls-files | rg 'auth|session|sqlite|browser-profiles|cache|tmp|logs|worktrees|generated_images|node_modules|\.venv'如果 AGENTS.md 最后是 hard link,LinkType 不一定像 symbolic link 那样直观;这时重点看两边内容是否同步、Git 是否只跟踪仓库里的那份文件。
那 WSL 合适吗
WSL2 更像一条 Linux 运行路线,适合工具链本来就在 Linux 的工作流。
如果你的项目、本地工具链、包管理器、测试脚本本来就更偏 Linux,或者原生 Windows sandbox 在当前公司设备策略下跑不顺,Codex 的 WSL 文档 也建议选择 WSL2。这个时候就不要把仓库放在 /mnt/c/... 里来回跨文件系统访问,直接放在 WSL 的 home 目录,比如:
mkdir -p ~/code
git clone <private-repo-url> ~/code/codex-homeWSL 里也有自己的 ~/.codex。如果 Windows 原生 Codex 和 WSL 里的 Codex 都要用同一套资产,我会让两边各自 clone 同一个私有仓库,再用 Git 同步,而不是让 Windows 的 ~/.codex 和 WSL 的 ~/.codex 互相指。这样边界更清楚:Windows 运行态留在 Windows,WSL 运行态留在 Linux,真正共享的是 Git 里的工作流资产。
在另一台机器 fork 或 clone
把这个仓库拆出来,真正的价值就在这里:换设备时不用搬整个 ~/.codex,只要 clone 私有仓库,再把可迁移资产链接回 Codex home。
新机器上先让 Codex 自己创建默认目录,或者手动准备空目录:
mkdir -p ~/.codex然后 clone 私有仓库:
git clone <private-repo-url> ~/code/codex-home再运行跨平台脚本:
cd ~/code/codex-home
node tools/setup-codex-home.mjs --repo "$PWD" --dry-run
node tools/setup-codex-home.mjs --repo "$PWD"如果新机器已有自己写过的 Skill 或规则,不要直接删备份目录。先把确实要保留的内容合并进 ~/code/codex-home,再删除 .local-backup.<timestamp>。
链接完成后,再执行登录或打开桌面应用:
codex loginCodex 会在新机器上重新生成登录态、sqlite、session、插件缓存、浏览器 profile 和其他运行态。这是预期行为。新机器真正继承的是你的工作方式和可复用能力,而不是旧机器的所有运行痕迹。
为什么主入口改成 Node
Shell 示例很适合解释路径关系,但这套流程要长期在 macOS、Windows 和 WSL 之间复用,主入口就不应该绑定某个 shell。
这里我会偏向 Node。Node 的 fsPromises.symlink(target, path, type) 明确支持 Windows 上的 file、dir、junction 类型,官方文档 也写清楚了 junction 只能指向目录,并且目标路径会被规范成绝对路径。做目录链接时,可以把 Windows 分支写得很直接:
import { symlink } from 'node:fs/promises';
const type = process.platform === 'win32' ? 'junction' : undefined;
await symlink(targetDir, linkPath, type);Python 标准库也能做跨平台路径处理,pathlib、os.symlink、os.link 都能覆盖一部分需求。但 Windows junction 这件事,Python 标准库没有 Node 那种直接的 type: "junction" 参数;如果要做得完整,通常还要额外调用 Windows 命令、PowerShell,或者封装 Win32 API。不是不能做,只是对这个场景来说,Node 更省心。
无论用哪种语言,脚本都应该按「可重复执行」设计:正确链接不动,错误链接先备份,缺失目录跳过或创建明确需要的父目录,任何凭证、session、sqlite、缓存都不搬。
这个选型本身也值得单独写一篇。更完整的判断,我放到了把 Codex Home 迁移做成跨平台 Node 脚本。
迁移后要扫掉旧机器绝对路径
还有一类问题和链接无关,但迁移到多设备后很容易暴露:Skill、规则或工具脚本里写死了旧机器路径。
我会在迁移后扫一遍:
rg -n '/Users/|/home/|C:\\Users\\' AGENTS.md global agents_references rules skills tools env扫出来不代表一定要改。比如某篇文档里为了举例写了 /Users/alice/...,可以保留。但如果是全局规则、Skill 触发说明或工具脚本里的真实工作路径,就应该尽量换成环境变量或相对稳定的入口:
${CODEX_HOME:-$HOME/.codex}
${CODE_DIR:-$HOME/Desktop/code}
${SUGO_WEB_REPO}
${FRONTEND_AI_KNOWLEDGE_REPO}这样仓库迁到 Windows、macOS 或 WSL 时,真正需要改的是本机 env,而不是到处翻 Skill 和脚本。
私有仓库也要检查边界
远端仓库是私有的,不代表可以把所有东西都推上去。私有仓库降低了公开泄露风险,但不能消除误提交凭证、登录态和大体积运行缓存的风险。
我会把仓库分成三类:
- 可以提交:
global/AGENTS.md、agents_references/、rules/、skills/、可复用工具源码、README、lockfile。 - 私有仓库才可以提交:内部工具 env、只给自己使用的私有 Skill、带个人工作流偏好的全局规则。
- 不要提交:
auth.json、config.toml、session、浏览器 profile、sqlite、日志、缓存、插件缓存、依赖安装目录、构建产物。
提交前至少做两件事:
git status --short
git ls-files | rg 'auth|session|sqlite|browser-profiles|cache|tmp|logs|worktrees|generated_images|node_modules|\.venv'第一条看本次提交会包含什么,第二条看 Git 已经跟踪了什么。第二条只要有可疑输出,就先停下来检查。
这个方案解决什么
这个方案不是为了「备份一个目录」。
它解决的是:把 AI 工具的个人工作流资产,从一个隐藏的运行状态目录里拿出来,变成一个可读、可审查、可版本管理、可迁移的仓库。
最终结构是:
~/code/codex-home/ # 私有 Git 仓库,方便人维护
~/.codex/ # Codex 运行目录,保留登录态和运行现场
~/.codex/AGENTS.md -> ~/code/codex-home/global/AGENTS.md
~/.codex/agents_references -> ~/code/codex-home/agents_references
~/.codex/rules -> ~/code/codex-home/rules
~/.codex/skills -> ~/code/codex-home/skills
~/.codex/env -> ~/code/codex-home/env
~/.codex/tools -> ~/code/codex-home/tools压缩包适合兜底,Git 适合长期演进,细粒度软链接适合兼顾工具默认路径和人的维护体验。
这样一来,Codex 还是那个 Codex;只是它的长期能力终于有了一个清楚、可见、能版本管理的家。