AI 编辑后 package-lock / pnpm-lock 冲突

Agent 装 / 删了依赖,lockfile 和团队的对不上了。

Claude Code 或 Cursor 给你写一个新功能,顺手 npm install lodash 一条命令,package-lock.json 从 5000 行变成 5400 行。你 push 一下,CI 立刻红:“npm ci failed: lockfile out of sync”,或者同事 pull 之后 pnpm install --frozen-lockfile 跑不过。冲突文件一打开是 4000 行 <<<<<< / >>>>>>,手工对根本不可能。

本文给你”丢掉 AI 的脏 lockfile、保留 package.json 的合理新增、用项目指定的包管理器重新生成干净 lockfile”的最短路径,并解释为什么手工 resolve lockfile 是错的。

常见原因

按出现频率排序,前 3 个占 80%。

1. Agent 跑了 npm install foo 而项目用的是 pnpm / yarn

最经典:CLAUDE.md / AGENTS.md 没写包管理器,agent 默认 npm install,但项目用 pnpm。结果生成了一份 package-lock.json,同时 pnpm-lock.yaml 没更新;或者两个 lockfile 都存在,CI 直接拒绝。

ERR_PNPM_OUTDATED_LOCKFILE  Cannot install with "frozen-lockfile"
because package-lock.json was modified

如何判断:仓库里突然出现两个 lockfile(package-lock.json + pnpm-lock.yaml),或 lockfile 顶部的版本号变了。

2. AI 改了 package.json 但没重跑 install

Aider 或 Codex 在 package.json 加了 "axios": "^1.7.0",但因为权限或环境问题没真的执行 npm install,lockfile 完全没更新。CI 上 npm ci --frozen-lockfile 立刻挂。

npm error code EUSAGE
npm error `npm ci` can only install packages when your package.json
and package-lock.json are in sync.

如何判断package.json 里有的依赖在 lockfile 里完全找不到。

3. AI 在 main 已 push 新依赖时同步本地 install

你的分支落后于 main 3 个新依赖。AI 在本地跑 npm install whatever,npm 不止装了 whatever,还把你 lockfile 里其他过时的间接依赖都 hoist 重排了。结果 lockfile 里 1500 行无关变动,rebase 时冲突。

如何判断:lockfile diff 里大量你没装过的包出现增减。

4. AI 以为某个依赖没用就删了,其实是间接依赖

Agent 看 import 没有显式引用就把 tslib / regenerator-runtime / core-js 这类底层包从 dependencies 里删了。本地 dev 没事,prod build 里被传递依赖引用直接挂。

Module not found: Can't resolve 'tslib'

如何判断:删除的包是 tslib@babel/runtimecore-jsregenerator-runtime 这类底层运行时;或者 npm ls <pkg> 显示有多个父依赖。

5. AI 升级了一个包,触发 peer dep 大范围漂移

让 agent 把 react 从 18 升到 19,它顺手把 lockfile 里几十个 peer dep 一起锁到新版本。其中某个 lib 的 v19 兼容版本还没发,lockfile 锁了 “wanted but not installable”。

如何判断npm installERESOLVE could not resolve,或 lockfile 里出现红色 unresolved 节点。

6. 两份 lockfile 同时被改

monorepo 里 root lockfile + workspace lockfile 都存在,AI 只更新了其中一个。

如何判断pnpm-workspace.yamlpackage.jsonworkspaces 字段下多个 lockfile 时间戳不一致。

最短修复路径

核心原则:lockfile 永远不要手工 resolve,重新生成才对。

Step 1:把 lockfile 整个回退到 main 的版本

冲突来了别看 4000 行的 <<<<<<,直接:

git checkout origin/main -- package-lock.json
# 或者 pnpm / yarn
git checkout origin/main -- pnpm-lock.yaml
git checkout origin/main -- yarn.lock

monorepo 一次性全清:

git checkout origin/main -- $(git diff --name-only --diff-filter=U | grep -E '(package-lock\.json|pnpm-lock\.yaml|yarn\.lock)$')

这一步把”AI 那份脏 lockfile”彻底丢掉,只保留 package.json 的合理新增。

Step 2:审查 package.json 的真实变动

git diff origin/main -- package.json

逐行问自己三个问题:

  1. 这个新依赖真的需要吗?(agent 经常装”以防万一”的包)
  2. 版本号是不是 agent 随便写的 ^latest?换成项目惯用的 caret / tilde / pinned 风格
  3. 移到 dependencies 还是 devDependencies 对了吗?(typescript 类型一定要在 devDeps)

不要的依赖手工从 package.json 删掉。

Step 3:用项目指定的包管理器重新生成 lockfile

先确认项目用什么:

cat package.json | grep packageManager
# 例如 "packageManager": "pnpm@9.1.0"

然后只用对应的命令重新生成

包管理器重新生成命令
npmnpm install --package-lock-only
pnpmpnpm install --lockfile-only
yarn (classic)yarn install --mode=update-lockfile
bunbun install --frozen-lockfile=false

--*-only 标志保证只更新 lockfile、不真的下载,速度快几十倍且不污染 node_modules。

Step 4:本地用 frozen 模式验证一遍

rm -rf node_modules
pnpm install --frozen-lockfile  # 或 npm ci
npm test && npm run build

frozen-lockfile / npm ci 就是 CI 用的命令——本地能过,CI 才能过。

Step 5:如果 Step 4 报 peer dep 错

ERESOLVE could not resolve
While resolving: react-dom@19.0.0
Found: react@18.3.1

回到 package.json 把冲突方一起升级或降级:

# 用 agent 帮你查兼容范围
# 然后手动 pin:
npm pkg set dependencies.react=19.0.0 dependencies.react-dom=19.0.0
pnpm install --lockfile-only

或者用 overrides / resolutions 字段强制解决:

{
  "pnpm": {
    "overrides": {
      "react": "19.0.0"
    }
  }
}

预防建议

  • package.json 里固定 packageManager 字段(如 "packageManager": "pnpm@9.1.0"),Node ≥ 16.9 会强制用 Corepack
  • CLAUDE.md / .cursorrules / AGENTS.md 第一行写”包管理器是 pnpm,禁止 npm install / yarn add
  • pre-commit hook 拦截多 lockfile:检测到 package-lock.json + pnpm-lock.yaml 同时存在就拒绝 commit
  • agent 想装新依赖时让它先输出 “我要装 X 用于 Y”,你确认后再执行——Bash(npm install:*) 默认拒绝、白名单按需放
  • CI 用 npm ci --frozen-lockfile / pnpm install --frozen-lockfile,任何 lockfile 漂移都立刻挂掉,不让它进 main
  • 升级类任务用专门的 AI 依赖升级工作流,不要混在 feature 分支里

相关阅读

标签: #AI 编程 #排查 #排查