Codex Agent 输出与 Prettier 冲突:6 个原因 + format-in-loop 模板

Codex 写完文件、Prettier 保存时重排,diff 变 5 倍。修法是把 Prettier 写进 agent 循环、AGENTS.md 风格对齐 .prettierrc。

Codex 写出一份看着干净的 patch。你在编辑器里打开文件,Prettier-on-save 把每一行重排——引号翻转、加上 trailing comma、加分号或去分号、断行重布。git diff 变成实际改动的两倍长。更糟的是 agent 后续想继续接它自己写的代码时,对不上自己原本的行。Pre-commit hook fail,CI lint 报错。“改了 10 行”变成 300 行。

这不是 Codex 的 bug,是握手没做好。Agent 不知道有 .prettierrc,或者知道但 prompt 没加载它,或者跑了 format 但用的是默认设置不是项目设置。修法是让 Prettier 成为 agent”完成”定义的一部分,并把相关规则喂进 agent 的风格提示,让它第一稿就已经符合 Prettier 会产出的样子。

常见原因

按命中率排序。

1. Agent 没跑 Prettier,编辑器跑了

Agent 改完保存、宣布”done”。在你机器上编辑器 on-save Prettier 又重排了一遍。看着干净的输出最后一秒被改了形。

如何判断git diff 比 agent 报告的大很多。跑 prettier --check <file>——会有不少行 fail。

2. Agent 跑的 Prettier 和项目的不是同一个

pnpm prettier --write vs npx prettier@3.2 --write vs 全局 prettier——不同版本产出不同。Codex 用了全局装的,但项目锁的是 3.0.x。

如何判断:项目里跑 npx prettier --version 和 agent 跑的不一样。Diff 是清一色的格式差(比如全部 '")。

3. AGENTS.md 风格提示和 .prettierrc 不一致

AGENTS.md 说”用单引号”,但 .prettierrc"singleQuote": false。Agent 听 AGENTS.md、保存时 Prettier 赢——两边在打架。

如何判断:手动对比 AGENTS.md 的风格主张和 .prettierrc / prettier.config.js。任何不一致 = 永远的吵闹 diff。

4. Prettier 和 ESLint 两个格式化器混用

ESLint 配 eslint-plugin-prettier + 独立 on-save Prettier,对 arrow paren、JSX trailing comma 排序略有不同。Agent 挑一个,工具链挑另一个。

如何判断:取决于最后跑哪个工具文件会不一样。跑 pnpm prettier --check . && pnpm eslint .——如果 prettier --write 后两个都过,但 eslint --fix 又把行改回去,就是有冲突。

5. Agent 输出了 Prettier 会改写的不可见字符

智能引号、不间断空格、en-dash——Codex 有时在 comment 或 string 里塞这些。Prettier(或某些插件)会标准化。

如何判断cat -A <file> | grep -E '\xc2\xa0|\xe2\x80' 能找到非 ASCII。对比 agent 输出文本。

6. 文件有 // prettier-ignore 指令

现有代码用 // prettier-ignore 保留手对齐的块。Agent 在块内部改——Prettier 尊重 ignore,但 agent 的对齐和原对齐对不上。

如何判断grep -rn "prettier-ignore" <files> 列出保护区,对照 agent 编辑的区域。

动手前先确认

  • 存快照:git stashgit diff > before.patch,方便清晰对比。
  • 记 Prettier 版本:cat package.json | grep '"prettier"'npx prettier --version
  • 记编辑器的 Prettier 集成:VSCode 的 editor.formatOnSave + prettier.requireConfig 设置。

需要收集的信息

  • .prettierrc / prettier.config.js / package.json#prettier 内容。
  • AGENTS.md 里关于风格的章节。
  • 保存时被重排的具体文件(对比 git diff 行数和 agent 实际写的)。
  • 用的编辑器 + 扩展(VSCode + Prettier 扩展等)。
  • 是否装了 prettier-eslinteslint-config-prettier

最短修复路径

按收益从高到低。

Step 1:把 Prettier 写进 agent 的”完成”循环

任务 prompt 里:

每次改完文件,跑:
  pnpm prettier --write <被改文件>

确认:pnpm prettier --check <被改文件>  返回 0。
非 0 就修完再说完成。

Agent 最后一动和编辑器会产出的一样——保存时零重排。

Step 2:AGENTS.md 风格主张对齐 .prettierrc

打开 .prettierrc(或 prettier.config.js),每个选项翻译到 AGENTS.md:

## 格式化(与 .prettierrc 一致 —— 不要偏离)

- printWidth: 100
- semi: false       → 行尾不加分号
- singleQuote: true → 用单引号
- trailingComma: "all"
- arrowParens: "always" → (x) => x 不是 x => x
- bracketSpacing: true → { foo } 不是 {foo}

Codex 的第一稿就和 Prettier 输出一致——消除 diff 膨胀。

Step 3:钉死 agent 调用的 Prettier 版本

强制走项目锁的版本:

每次改完跑:
  pnpm exec prettier --write <files>
  # 不要:npx prettier --write   (可能拉到别的版本)
  # 不要:prettier --write        (可能用全局装的)

pnpm exec / yarn dlx / npm exec 走项目 lock 的 Prettier。

Step 4:彻底解决 ESLint + Prettier 冲突

如果还没装就先装 eslint-config-prettier

pnpm add -D eslint-config-prettier

ESLint config 最后 extends 它:

extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier']

这条会禁用所有和 Prettier 冲突的 ESLint 规则,eslint --fixprettier --write 输出一致。

Step 5:清理 agent 输出里的不可见字符

任务 prompt 加一段:

输出规则:
- 代码里只用 ASCII(不要智能引号、不要 en-dash、不要 nbsp)
- 注释:标准 ASCII " ' - --
- 字符串字面量:仅按用户字面要求

偏执一点的话改完跑一遍 sanitizer:

sed -i 's/\xc2\xa0/ /g; s/\xe2\x80\x9c/"/g; s/\xe2\x80\x9d/"/g' <files>

Step 6:显式处理 prettier-ignore 区域

任务 prompt 里列出受保护区:

以下块是 `// prettier-ignore` 保护区——按原对齐保留,非必要不要改:

- src/data/timezones.ts:42-78
- src/config/menus.tsx:120-180

必须改的话,缩进 / 对齐按字节对齐到现有的。

Step 7:可选——agent 跑期间关编辑器的 format-on-save

如果实在让 agent 的 Prettier 对不齐编辑器的,暂时:

"editor.formatOnSave": false

会话结束时自己跑 Prettier。把编辑器从隐藏 actor 拿掉。

怎么确认已经修好

  • Agent 输出的 git diff:行数应该和 agent 报告一致,不是 5 倍。
  • Agent 完成后立刻 pnpm prettier --check <files> 返回 0。
  • 在编辑器打开文件、什么都不动、保存:零变更。
  • Pre-commit hook(lint-staged + prettier --check)第一次就过。

长期预防

  • AGENTS.md 格式化章节从 .prettierrc 自动生成,它们就不会漂。
  • 每个 agent 任务模板都包含”改完文件先 prettier 再完成”。
  • Agent tool 调用里用 pnpm exec(或同类),不用全局 prettier
  • eslint-config-prettier,ESLint 不会和 Prettier 吵。
  • 加 CI job 跑 prettier --check PR 检查——merge 之前抓 drift。
  • 想要 ASCII 纯度,加 pre-commit hook 拒收 .ts / .js 里的非 ASCII。

常见坑

  • Agent 改完之后整个项目跑 prettier --write——掩盖 agent 真正改了哪些行、diff 膨胀。
  • 改了 AGENTS.md 风格章节但忘了改 .prettierrc(或反过来)——下个月又开始打架。
  • 信任 npx prettier 不走 pnpm exec——版本锁定被静默忽略。
  • editor.formatOnSave 开着的时候去”只是看看”agent diff——你一点进文件就被重排了。
  • 把 trailing comma 不一致当美观问题忽略——它打断 git blame、之后每个 diff 都膨胀。

常见 FAQ

Q:我把 Prettier 加进 agent 循环了,编辑器保存还在重排,为什么?

看编辑器设置里 prettier.requireConfig——为 false 时编辑器用默认设置、忽略 .prettierrc。设成 true 就只在找到 config 时跑 Prettier。

Q:我们团队用 Biome / dprint,不是 Prettier,修法一样吗?

一样——把 Prettier 命令换成 biome format --writedprint fmt。结构性修法(formatter 进 agent 循环 + 风格提示对齐 config)完全一样。

Q:能让 Codex 直接别格式化吗?

可以,但你得有一套清晰的”agent 之外统一格式化”流程。Commit 时 diff 还是会吵。不如让 agent 第一稿就对齐 Prettier 产出。

Q:.prettierignore 呢?

同样的——确保 AGENTS.md 列出 agent 不该碰的 pattern,和 .prettierignore 一致。否则 agent 改了一个生成文件、Prettier 拒绝格式化——又是不一致。

相关阅读

标签: #Codex #agent #排查 #prettier #formatting