最近在搭建新项目的时候,我给自己写了套一键初始化脚本,把 Codex、OpenSpec 和 Matt Pocock Skills 这三样东西一次性配好。以前每开一个新仓库,我得先装 CLI、登录认证、配置模型、初始化规范目录、再手动把 skill 文件复制进去,至少折腾半小时;现在跑一条命令,三分钟左右就能进入“直接跟 AI 对话开工”的状态。
这个流程解决的核心问题很简单:AI 编程工具越来越强,但“让 AI 在正确规范下干活”这件事,手动配置起来又碎又容易漏。Codex 负责执行,OpenSpec 负责流程约束,Matt Pocock Skills 负责给模型注入 TypeScript 领域的编码最佳实践——三样东西单独用都不难,难的是每次新建项目都把它们正确地拼在一起。这篇文章我把脚本的设计思路、完整实现和踩坑记录都写出来,适合正在用 Codex 做项目、或者想在团队里统一 AI 开发流程的开发者参考。
1. 为什么要把初始化流程做成脚本
1.1 三个工具各自解决什么问题
这个组合看起来名字很多,其实分工非常清楚。
- Codex:OpenAI 出的命令行编程代理,可以在本地终端里读代码、改代码、执行命令、跑测试。它相当于你的“AI 协作者”,但它本身只提供协作能力,并不知道你的项目应该按什么规范来。
- OpenSpec:一套规范驱动的 AI 开发工作流。要求你在动手写代码之前,先把要做的功能写成一个 spec(需求说明),再由 spec 生成 plan(实施计划),最后才进入编码。这个流程能避免 AI 一上来就乱改代码、改完发现方向错了。
- Matt Pocock Skills:Matt Pocock 是 TypeScript 社区的知名教育者,他维护了一套给 AI 编程工具用的 skills。skills 本质上是一些 Markdown 文件(SKILL.md),里面写清楚了在某种场景下应该遵守的编码习惯、常见陷阱、检查清单。对 TypeScript/React 项目帮助尤其明显。
三者组合之后,等价于你拿到了一名“知道代码库情况、按规范推进、并且懂 TypeScript 最佳实践”的 AI 工程师。这句话听起来很玄,其实拆开看每个环节都很朴实:Codex 负责动手,OpenSpec 负责控制节奏,Skills 负责补充领域知识。少了任何一个,另外两个都会打折扣。
为了验证这个判断,我自己做过 AB 对比:同样的需求“给博客加一个标签归档页”,直接让 Codex 改,它可能会直接开始在 pages 下面新建文件;而先走 OpenSpec 的 spec → plan 流程,它会把数据结构、路由设计、页面入口先在文档里说清楚,再动手。后者虽然多花了五分钟在文档上,但后面几乎没有返工。
1.2 手动初始化到底痛在哪
很多人以为“初始化”就是装个 CLI 那么简单,实际远不止。
第一,流程步骤多且顺序敏感。你得先装好 Codex 并登录认证,然后才能用它;OpenSpec 要在项目根目录初始化才会生成 openspec 目录;Skills 文件要放进 Codex 能扫描到的目录(通常是.codex/skills),放错位置等于白放。步骤一多,人就会漏。
第二,配置漂移。上个月用 Codex 的时候,可能还只需要指向某个默认模型就够了;下个月你又给项目加了 OpenSpec,还需要在配置里开启 skills 支持。每个人的全局配置、每个项目的局部配置经常不一样,团队里十个人就有十种环境,光“把环境对齐”这件事就能耗掉大半天。
第三,新手上手成本高。如果你的团队来了个新人,你给他一份 README,里面写“先装 Codex,再跑 openspec init,然后把 skills 克隆到 .codex/skills,最后把 codex.json 改成 XXX”。他大概率会在某个环节卡住,尤其在认证、模型选择这些地方反复纠结。而一个脚本把所有这些判断都包进去,他只管执行。
所以当我连续手动初始化了五六个项目之后,我确定了一件事:这个流程值得做成脚本。不是因为我懒,而是因为“可重复的手工劳动”本身就是技术债,会持续吃掉我和队友的时间。
1.3 自动化之后的收益
我做了一个简单对比,手动做一套流程和脚本一键做一套,差异如下:
| 对比维度 | 手动操作 | 一键脚本 |
|---|---|---|
| 耗时 | 20-40 分钟 | 2-3 分钟 |
| 一致性 | 靠记忆,容易漏步骤 | 固定流程,每次一致 |
| 可审计 | 不知道当时配了什么 | 日志完整,可追溯 |
| 可传播 | 需要口头/文档教学 | 脚本即文档 |
| 出问题恢复 | 手工排查、重来 | 重新执行脚本即可 |
这个表格我实际用了两个月,目前感觉收益最明显的是“可传播”。脚本本身就成了项目的 onboarding 入口,新人拉完仓库先跑一下脚本,环境就绪,剩下的只需要读 PROMPT.md。对个人开发者来说,这个收益可能没有团队场景那么明显,但半年后你回头再维护这个仓库时,脚本能帮你快速恢复“当时是怎么搭起来”的记忆。
2. 脚本设计思路与整体拆解
2.1 脚本要完成的五步流程
我在设计脚本的时候,把整个初始化过程抽象成了五个步骤,并且严格控制顺序:
- 检查依赖环境:确认 Node.js、OpenSpec CLI、Codex CLI 是否安装,版本是否满足要求。缺了就给出安装命令提示,而不是直接报错退出。
- 写入 Codex 配置:生成项目级 codex.json,指定模型、审批策略、skills 目录等。
- 初始化 OpenSpec:调用 openspec init 生成目录结构,同时创建一个初始的 project 文档。
- 安装 Matt Pocock Skills:将技能文件同步到
.codex/skills目录。 - 生成启动上下文:把项目名、规范说明、推荐工作流写进 PROMPT.md,方便每次启动 Codex 时直接引用。
为什么是这个顺序?因为每一步都依赖前一步的产物。Codex 是执行引擎,必须先能跑;OpenSpec 是工作流约束,必须在写代码前就位;Skills 是领域知识,要放进 Codex 的扫描范围;最后生成 PROMPT.md 是为了把前三步串成“人怎么开始用”的入口。如果先装 Skills 再初始化 OpenSpec,OpenSpec 初始化过程理论上不会碰 skills 目录,但如果你连续跑多次脚本,顺序不固定就会导致状态不可预期。
2.2 目录结构与约定
脚本执行完,项目根目录应该长这样:
your-project/ ├── .codex/ │ ├── config.json │ └── skills/ │ ├── typescript/ │ │ └── SKILL.md │ └── react/ │ └── SKILL.md ├── openspec/ │ ├── projects/ │ │ └── initial-plan.md │ └── specs/ ├── AGENTS.md └── PROMPT.md这里有几个细节必须说明。
.codex/config.json是 Codex CLI 的项目级配置。放在项目里比写进用户全局配置更好,因为不同项目可能用不同模型、不同 skills 组合,项目内部配置文件可以跟着仓库走,团队其他人 clone 下来就自然继承。openspec/是 OpenSpec 初始化生成的目录,projects/下面放当前进行中的计划,specs/下面放已经确认的需求文档。这个结构不要乱改,OpenSpec 的 CLI 会依赖这个约定。
AGENTS.md是给 AI 代理看的项目说明文件,Codex 会优先读取这个文件来了解项目规则。脚本会把最基础的项目语言、代码风格、命令约定写进去。PROMPT.md是启动提示词,它不是给 AI 看的系统文件,而是给人看的“接下来怎么用”。我习惯在里面写清楚:第一步先让 Codex 读 openspec/ 下的计划,第二步让 Codex 按计划执行,第三步跑测试验证。这样新上手的人也知道怎么跟 AI 协作。
2.3 幂等与安全设计
脚本最大的敌人是“重复执行后状态变乱”。所以我做了几点设计:
- 脚本开头加
set -euo pipefail。这个组合的意思分别是:遇到错误立即退出、使用未定义变量时退出、管道中任一命令失败则失败。它能避免脚本在某个命令失败后继续乱跑。 - 每个步骤都做“存在性检查”。比如
.codex/config.json已经存在时,先问用户要不要覆盖;skills 目录里已有同名 skill 时,跳过而不是覆盖;openspec 目录已经初始化过,就不再重复执行 init。 - 设计临时文件清理机制。比如初始化过程中生成的临时 plan 草稿,用
trap ... EXIT在脚本退出时清理。这样就算中间报错,也不会在项目里留下一堆垃圾文件。
这些看着都是小细节,但恰恰是脚本能“反复跑、放心跑”的关键。我见过太多一键脚本只能在干净环境跑一次,第二次就各种报错,就是因为没有做幂等处理。
3. 核心实现:一步步写出脚本
3.1 环境检查与依赖引导
脚本第一步是检查依赖。我写了一个通用函数:
check_cmd() { local cmd="$1" local hint="$2" if command -v "$cmd" >/dev/null 2>&1; then echo "[OK] $cmd 已安装" else echo "[MISSING] 缺少 $cmd" echo "提示: $hint" exit 1 fi } check_cmd node "请先安装 Node.js 18+,推荐用 nvm 安装。" check_cmd codex "请先安装 Codex CLI,参照官方安装文档。" check_cmd openspec "请先安装 OpenSpec CLI,参照官方文档。"command -v是 shell 里最可靠的可执行文件检测方式,比which兼容性更好。我故意把安装提示写成“指引你去官方文档”,而不是在脚本里偷偷安装。原因很简单:安装方式是全局环境的事,脚本不应该替用户做修改全局 PATH 或安装全局包的决定。如果脚本发现了 Mac 上缺 Homebrew,最好的做法是提示,而不是直接把它装上。
如果你还需要某个最低版本,可以在检测后追加版本比较。比如 OpenSpec 要求 Node 18+,可以这样:
node_major=$(node -p "process.versions.node.split('.')[0]") if [ "$node_major" -lt 18 ]; then echo "Node.js 版本过低,需要 18+,当前 $node_major" exit 1 fi这个细节对实际使用很重要,因为很多旧项目环境装的是 Node 14 或 16,Codex CLI 和 OpenSpec 跑不起来,报错信息又不好懂。
3.2 写入 Codex 配置
Codex 项目的配置其实有两种承载方式:一是用户全局配置(~/.codex/config.toml),二是项目级配置。我在脚本里选择生成项目级codex.json,理由刚才说过——项目配置跟着仓库走,方便团队共享。
配置内容大概长这样:
{ "model": "gpt-5.6-sol", "approval_policy": "on-request", "skills": { "enabled": true, "directories": [".codex/skills"] } }这份 JSON 我按最小可用原则来写,字段含义说明一下:
model:指定 Codex 后端跑的模型。不同账号、不同订阅能访问的模型不一样,报错“the 'gpt-5.6-sol' model is not supported”时,通常就是账号没权限,可以改成你有权限的模型名。approval_policy:审批策略。on-request表示涉及敏感操作时征求你同意,适合日常开发;如果你希望 AI 全自动跑测试和命令,可以改成never或对应放行策略。skills:启用 Codex 的技能系统,并把目录指向项目内的.codex/skills。这里要注意,不同版本的 Codex 配置字段命名可能不同,我用的这个skills.directories是当前版本里比较常见的写法。如果你用的版本字段不一样,以官方文档为准。
还有一个经常被忽略的点:Codex 除了读项目配置,还会读项目根的AGENTS.md。这个文件对代理的行为影响非常大,我会在脚本里生成一份最基础的模板:
# AGENTS.md ## 项目说明 这是一个 TypeScript 项目,使用 Node.js 22 + pnpm 管理依赖。 ## 命令 - 安装依赖:pnpm install - 开发启动:pnpm dev - 测试:pnpm test ## 编码约定 - 遵循 Matt Pocock 的 TypeScript 最佳实践。 - 修改前先阅读 openspec/ 下的对应 spec。有了这个文件,Codex 一进项目就知道“该用什么包管理器、测试怎么跑、代码风格偏好”,这些信息如果只写在 PROMPT.md 里,你可能每次都要手动粘贴,放在 AGENTS.md 里就是自动加载。
3.3 OpenSpec 初始化
OpenSpec 我理解为“给 AI 开发加的一道流程约束”。它的核心思路很朴素:人先想清楚要什么,把它写成 spec,再把 spec 拆成 plan,最后才让代理去执行。脚本里初始化 OpenSpec 的步骤大概是这样:
# 如果 openspec 目录不存在再初始化 if [ ! -d "openspec" ]; then echo "初始化 OpenSpec 目录..." openspec init else echo "检测到 openspec 目录已存在,跳过 init。" fi # 确保初始计划文件存在 mkdir -p openspec/projects if [ ! -f "openspec/projects/initial-plan.md" ]; then cat > openspec/projects/initial-plan.md <<'EOF' # 当前计划 > 这个文件用于记录当前 AI 正在执行的计划。 > 每次新任务开始前,先在这里更新你的 plan。 ## 任务目标 (待补充) ## 执行步骤 1. 先阅读 openspec/specs/ 下的需求说明。 2. 拆解为实现步骤。 3. 完成编码后运行测试验证。 EOF fi这里有一个容易踩的坑:openspec init在某些版本里是交互式命令,会问你问题。在脚本里直接跑,如果它等待输入,脚本就会卡住。我试过两种处理方式:
- 用管道喂一个换行:
echo "" | openspec init,有时能跳过默认选项。 - 显式传参跳过交互:
openspec init --yes或--no-interactive,不同版本参数不一样。
如果你的 OpenSpec 版本不支持非交互参数,最简单稳妥的做法是:不调用 init,而是手动创建openspec/{projects,specs}目录并放一个初始文件。因为 OpenSpec 的核心是目录结构约定,只要目录结构对了,绝大多数子命令都能正常工作。这个方案虽然“土”一点,但在脚本里最不容易翻车。
3.4 接入 Matt Pocock Skills
Matt Pocock Skills 本质上就是一组 SKILL.md 文件。你要做的只有两件事:把它放到 Codex 能扫到的地方,确保 SKILL.md 的格式符合 Codex 的 skill 解析规则。
脚本里我采用的策略是:先确认远端仓库的最新版本,再把它 clone 到临时目录,然后把需要的技能复制到.codex/skills下。简化后的脚本逻辑是这样的:
SKILLS_SOURCE="${1:-$HOME/.cache/ai-skills}" if [ -d "$SKILLS_SOURCE" ]; then echo "使用本地 skills 缓存: $SKILLS_SOURCE" else echo "克隆 Matt Pocock Skills 仓库..." git clone --depth 1 https://github.com/mattpocock/ai-skills.git "$SKILLS_SOURCE" fi mkdir -p .codex/skills for skill_dir in "$SKILLS_SOURCE"/skills/*/; do skill_name=$(basename "$skill_dir") if [ -f "$skill_dir/SKILL.md" ]; then cp -R "$skill_dir" ".codex/skills/$skill_name" echo "已安装 skill: $skill_name" else echo "跳过 $skill_name:缺少 SKILL.md" fi done这里有个很重要的设计:只复制包含 SKILL.md 的目录。因为 Codex 的 skill 加载机制是扫描目录下的 SKILL.md 文件,如果目录里没有这个文件,复制过去也不会被加载,还会干扰 Codex 的目录扫描。
你也许想问:为什么不直接用软链接指向克隆下来的仓库?我实际试过,软链接在本地跑没问题,可一旦你把仓库同步到 CI 或者换一台机器,链接就断了,而且在 Windows 上创建软链接经常需要管理员权限。所以脚本里我坚持用“复制”,虽然会占用一点磁盘空间,但胜在稳定。
关于仓库地址、目录布局,不同作者维护的版本可能有差异,使用前建议先看一眼仓库结构。Matt Pocock 的这套 skills 核心是 TypeScript/React 方向,文件结构就是典型的skills/<name>/SKILL.md形式,所以脚本按这个约定来扫基本没问题。如果你要接入其他 skills 集合,改一下for skill_dir的路径就行。
3.5 生成启动提示词 PROMPT.md
最后一步是为“人机协作”准备一个入口。我习惯生成一个 PROMPT.md,里面写清楚三件事:
- 这个项目是什么。
- 接下来开发时遵循哪个流程(先 spec 再 plan 再编码)。
- 第一个任务从哪里开始。
脚本生成的初始版本类似:
# 项目启动提示词 你是一名资深 TypeScript 工程师。请遵循以下工作流: 1. 先阅读 AGENTS.md 和 openspec/ 下的计划与说明。 2. 如果有新需求,先按 OpenSpec 格式新增 spec,再拆 plan。 3. 开始编码前,先调用合适的 skill(如 typescript、react)。 4. 改完代码后运行测试并说明改动影响。 当前第一个任务:为项目初始化一个可运行的 hello-world 入口, 同时跑通 pnpm dev 和 pnpm test。这个文件在脚本里用 heredoc 写入。写完之后,脚本会打印一段使用说明,告诉你下一步可以直接在终端运行codex,让它先读PROMPT.md。
我在实际使用时,会先把PROMPT.md内容粘贴进第一轮 Codex 对话,或者把文件路径告诉 Codex,让它先读。这样能显著减少第一轮对话的“磨叽”,直接进入干活状态。
4. 常见问题与排查技巧实录
4.1 codex 认证与模型不可用
这个组合里,Codex 的登录认证和环境配置是最容易出问题的一环。我自己遇到并帮别人排查过这些:
第一类是登录态丢失或认证失败。表现是运行codex时提示需要登录,或者 OAuth 流程在浏览器里转一圈回来后还是失败。处理方式通常可以先把本地已有的凭证目录备份并清理掉,再重新登录。注意不同操作系统这个目录位置不一样,不要乱删。
第二类是模型不可用报错,尤其是报the 'gpt-5.6-sol' model is not supported when using codex with a chatgpt account。这句报错字面意思是“你用的是 ChatGPT 账号,但这个模型不允许你这么用”。这里核心问题是:Codex 可以用 ChatGPT 订阅账号登录,也可以用 API Key 认证,但两者能用的模型集合不一样。如果你没权限用默认模型,最简单的办法是改配置里model字段,换成一个当前账号可用的模型名。
第三类是希望能接入第三方兼容端点的情况。如果你手头没有官方 API,但想用其他提供兼容接口的服务,可以在配置里指定base_url之类的方式指向兼容端点。具体能不能用,取决于你选的模型服务是否兼容 Codex 的请求格式。这块涉及各家服务商的配置方式不同,建议在对应用户手册里查看,配置思路其实跟改model是一样的。
4.2 OpenSpec 在脚本里卡交互
这是脚本化最常遇到的坑。openspec init在交互式终端里很友好,但在脚本里会卡住。前面说了两种解法,我再补充一个细节:如果实在没有非交互参数,可以先printf 'n\n' | openspec init之类的管道喂字符,但是不同版本的问题数量不同,这种喂法很不稳定。我的最终建议是:脚本里不调用 init,手动创建目录结构即可。OpenSpec 本身对目录结构的要求非常明确,你只要按约定的目录结构创建空目录和初始文件,后续openspec子命令基本都能正常工作。
还有个相关的小坑:OpenSpec 的 spec 文件是 Markdown 格式,里面 frontmatter 有时包含id、status等字段。如果你手动创建初始文件,最好保留 frontmatter 模板,否则后续工具解析可能报错。把模板写死在脚本里,比每次手动敲一遍靠谱得多。
4.3 Skills 加载不掉
明明已经把 skills 复制到.codex/skills下了,但 Codex 好像完全没反应。我先列一下我排查过的可能性:
- 目录放错了。Codex 扫描的是项目级
.codex/skills,不是你自己建的skills/。如果两者名字相似,很容易搞混。 - SKILL.md 格式不对。Codex 对这种文件有解析要求,如果 frontmatter 里缺了必要字段,整个 skill 会被跳过。复制完之后可以手动打开一个 SKILL.md 看看。
- 配置没开 skills。也就是 codex.json 里
skills.enabled为false,或者没配置directories。这种情况下你复制多少文件都没用。 - 改了配置没重启。Codex CLI 通常启动时会加载配置,如果你在它运行的过程中改了 skills 目录,当前会话不会自动生效,退出重进就好了。
我见过最坑的一种情况:用户把 skills 放到了全局的.codex/skills,又同时在项目里放了另一套同名 skill,两边冲突时行为就变得很难预测。所以我的建议是:同一时刻只在一个位置维护 skills,脚本里固定用项目级目录,更符合项目自包含的原则。
4.4 跨平台脚本差异
这个脚本按“bash + Unix 工具链”写的,在 macOS 和 Linux 上直接跑没问题。但如果你在 Windows 上开发,建议用 Git Bash 或者 WSL 来执行,不要直接依赖 CMD 或 PowerShell,因为脚本里用了cp -R、command -v这类 Unix 风格命令。
还有两个跨平台细节值得注意:
- macOS 自带的
sed -i和 Linux GNU sed 的写法不同。如果脚本里要做文本替换,建议用sed -i.bak这种带备份后缀的写法,两端兼容。 read交互在 Windows 的 Git Bash 里有时表现不同。如果你脚本里要跟用户交互,建议把交互选项做成环境变量判断,默认值给好,用户可以直接回车跳过,别强制等待输入。
我的脚本里其实只保留了极少量的read交互:检测到已有配置时问一句“是否覆盖”。其他地方全部无交互,这样放到 CI 环境里也不会卡住。
4.5 问题速查表
最后整理一张速查表,方便大家直接对着查:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
codex提示未登录 | 凭证缺失或过期 | 重新执行登录流程 |
| 报错模型不支持 | 账号权限、模型名不对 | 修改model字段 |
| openspec 命令卡住 | 交互式命令在脚本中等待输入 | 手动创建目录,或传非交互参数 |
| skill 老是不生效 | 目录位置/格式/配置问题 | 按 4.3 检查清单逐项排查 |
| 脚本在非 Unix 环境报错 | 使用了 Unix 命令 | 用 Git Bash 或 WSL 执行 |
| 二次运行报目录已存在 | 脚本没有做存在性判断 | 加上如果存在就跳过的逻辑 |
这张表不是完整的排障手册,但覆盖了我自己踩过的绝大部分问题。如果你在实战中遇到新问题,我的建议是先看日志输出,再对照每一步脚本在做什么,通常很快能定位到是环境问题还是配置问题。
5. 结束与扩展方向
脚本我放在自己的 dotfiles 仓库里,每次新项目只用两条命令:一条从仓库拉下来,一条执行初始化。半年下来我大概跑了十几次,最大的体会是这个东西的价值不在于“自动化”本身,而在于它把一套容易漏步骤、靠记忆维护的流程,变成了可版本管理、可团队复制的约定。AI 开发工具更新很快,配置项会变、指令格式会变,但“先检查环境、再写配置、再建约束、再注入技能、最后给上下文”这个骨架是稳定的,值得你为它写一次脚本。
如果你也想做类似的工具,我最后再给一个建议:脚本保持小和弱依赖,不要把所有配置都塞进去。它只需要负责“把目录、配置、技能文件放到位”,剩下真正复杂的逻辑,交给 Codex 和 OpenSpec 本身去处理。后续想扩展的话,可以考虑把它做成pnpm create模板、加一个 CI 检查步骤来验证配置是否有效,或者针对不同技术栈生成不同的 skills 子集。先跑通,再变重,这个顺序比一上来追求大而全要稳得多。