AI 编辑后 package-lock / pnpm-lock 冲突怎么修

Agent 装 / 删了依赖,lockfile 和团队的对不上了。别手工 resolve——直接回退重新生成,下面是每种包管理器的准确命令。

Claude Code 或 Cursor 给你写新功能,顺手一条 npm install lodashpackage-lock.json 从 5000 行变成 5400 行。你 push 一下 CI 立刻红:npm ci can only install packages when your package.json and package-lock.json are in sync,或者同事 pull 之后 pnpm install --frozen-lockfile 跑不过。打开冲突文件一看是 4000 行 <<<<<< / >>>>>>,手工对根本不可能。

最快的修法(先做这个): lockfile 永远不要手工 resolve。把它整个回退到 main,只保留 package.json 的合理改动,再用项目实际用的包管理器重新生成一份干净 lockfile:

git checkout origin/main -- package-lock.json   # 或 pnpm-lock.yaml / yarn.lock / bun.lock
npm install --package-lock-only                  # 用你自己的包管理器只重生 lockfile(命令见下表)
rm -rf node_modules && npm ci                     # 用 frozen 模式验证对得上

后面会讲:怎么判断自己落在 6 种故障里的哪一种、每种包管理器准确的重生命令(npm / pnpm / yarn / bun,其中三个在过去一年里改过 flag 或行为),以及为什么手工 resolve lockfile 永远是错的。

你属于哪一种?

动手之前先把症状对到原因上。前 3 种大约占 80%。

你看到的症状分类根本原因
现在同时有两个 lockfile(package-lock.json + pnpm-lock.yaml1agent 用错了包管理器
某个依赖在 package.json 里有、lockfile 里没有2改了 package.json 但没跑 install
lockfile diff 里冒出几十个你没碰过的包3分支落后 main 时跑了 install
prod 里某个底层包报 Module not found4agent 删了一个间接依赖
install 报 ERESOLVE / ERR_PNPM_PEER_DEP_ISSUES5升版本触发 peer dep 连锁
monorepo 两份 lockfile 只改了一份6workspace 只更新了一半

常见原因(详解)

1. agent 跑了 npm install foo,但项目用的是 pnpm / yarn / bun

最经典的一种。CLAUDE.md / AGENTS.md / .cursorrules 里没钉死包管理器,agent 默认 npm install,但仓库用的是 pnpm。结果你有了一份新的 package-lock.json,外加一份没更新的 pnpm-lock.yaml,CI 直接拒绝。pnpm 10/11 的 frozen 安装报错长这样:

 ERR_PNPM_OUTDATED_LOCKFILE  Cannot proceed with the frozen installation.
The lockfile (pnpm-lock.yaml) is outdated and is not up to date with package.json

如何判断:本该只有一个 lockfile 的地方出现了两个,或者 lockfile 顶部的 lockfileVersion 字段变了。

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

Aider 或 Codex 在 dependencies 里加了 "axios": "^1.7.0",但因为沙箱或权限被拦,install 根本没真的执行,lockfile 一点没动。CI 上 npm ci 立刻挂:

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

如何判断package.json 里有的依赖,在 lockfile 里完全搜不到。拿包名去 grep lockfile,没命中就是这一种。

3. AI 在分支落后 main 新依赖时同步本地 install

你的分支落后 main 3 个新依赖。agent 在本地跑 npm install something-else,npm 不止装了这一个——它把它认为过时的几十个间接依赖一并重新解析、重新锁定。结果 lockfile 里出现 1500 行无关变动,rebase 时直接炸。

如何判断:lockfile diff 里大量你没装过的包出现增减。git diff origin/main -- pnpm-lock.yaml | grep -c '^+' 返回的数字远大于你实际改动的那一两个依赖。

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,它顺手把相关的 peer 依赖库一起重新锁定。其中某个库还没发 React 19 兼容版本,于是 lockfile 里出现了一条解不开的条目。

如何判断:install 报 ERESOLVE could not resolve(npm)或 ERR_PNPM_PEER_DEP_ISSUES(pnpm)。

6. monorepo 里两份 lockfile 只改了一份

root lockfile 和某个 workspace 的 lockfile 都存在,agent 只更新了其中一个。(现代 pnpm 和 bun 整个 workspace 共用一份 root lockfile,所以这主要坑老式的 npm/yarn-workspaces,或者有嵌套独立 install 的仓库。)

如何判断:在 pnpm-workspace.yamlpackage.jsonworkspaces 字段下,多份 lockfile 的修改时间对不上。

还有一种不怪你

如果 npm ci 在你没改过的 lockfile 上挂了,可能是某个间接依赖在 lockfile 提交之后发了新补丁版本。比如 @types/node 发了个新 patch,就能让一份本来合法的 lockfile 被 npm ci 拒绝。修法一样——npm install --package-lock-only 重新生成再提交——但根因在上游,不在 agent。

最短修复路径

核心原则:lockfile 冲突永远不要手工 resolve,重新生成才对。 lockfile 是派生产物;手工拼接它的冲突标记只会得到一份”两边的依赖树都对不上”的文件——完整性 hash 校不过,npm ci 照样拒。

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

别去看 4000 行的 <<<<<<,直接拿 main 那份:

git checkout origin/main -- package-lock.json
# 或者按你的包管理器:
git checkout origin/main -- pnpm-lock.yaml
git checkout origin/main -- yarn.lock
git checkout origin/main -- bun.lock

monorepo 一次性把所有冲突的 lockfile 全清掉:

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

这一步把”AI 那份脏 lockfile”彻底丢掉,只保留 package.json 的合理新增(它们是单独追踪的)。

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

git diff origin/main -- package.json

对每一个新增依赖问三件事:

  1. 这个依赖真的需要吗?(agent 经常装”以防万一”的包)
  2. 版本范围符合你仓库的风格吗(caret / tilde / 固定版本),还是 agent 随手写了 *latest 这种松散的?
  3. 该放 dependencies 还是 devDependencies?(类型包——任何 @types/*——和构建工具都该在 devDeps)

不要的依赖,在重新生成之前手工从 package.json 删掉。

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

先确认仓库要用哪个包管理器:

grep '"packageManager"' package.json
# 例如 "packageManager": "pnpm@11.7.0"

然后只用对应那一条 lockfile-only 命令。这些 flag 最近改过,所以一定要用你这个包管理器的那条(2026 年 6 月核对):

包管理器重新生成命令说明
npmnpm install --package-lock-only只更新 package-lock.json,不动 node_modules
pnpmpnpm install --lockfile-only只写 pnpm-lock.yaml。pnpm 10.9 起它会重新解析整张依赖图,所以 diff 会偏大但是正确的。
Yarn Berry (v2+)yarn install --mode=update-lockfileBerry 专属 flag;更新 yarn.lock 而不做完整下载。
Yarn Classic (v1)yarn install(然后提交 yarn.lockClassic 没有 lockfile-only 模式;普通 install 会重新生成并写入 yarn.lock
bunbun install --lockfile-only写文本格式的 bun.lock(Bun 1.2 起的默认;老的二进制 bun.lockb 已是 legacy)。

这些 lockfile-only flag 只更新 lockfile、不往 node_modules 里装东西——快很多,也不会污染目录。

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

完全复现 CI 的动作:

rm -rf node_modules
pnpm install --frozen-lockfile   # 或:npm ci / yarn install --immutable / bun install --frozen-lockfile
npm test && npm run build

--frozen-lockfile / npm ci / --immutable 都拒绝改动 lockfile,一旦有漂移就直接报错——跟 CI 用的是同一道闸。本地能过,CI 才能过。

Step 5:如果 Step 4 报 peer dep 错

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

回到 package.json,把冲突双方一起升或一起降,然后重新生成:

npm pkg set dependencies.react=19.0.0 dependencies.react-dom=19.0.0
npm install --package-lock-only

如果那个 peer 确实还没有兼容版本,用 overrides(npm/pnpm)或 resolutions(yarn)强制解决——但把它当临时补丁,上游正式版本一发就删掉:

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

怎么确认修好了

满足下面三条就算修好:

  1. 项目只剩一个 lockfile(跑 ls *lock* */*lock* 2>/dev/null——pnpm-lock.yaml 旁边不应该还有个 package-lock.json)。
  2. 干净的 frozen 安装能过:rm -rf node_modules && pnpm install --frozen-lockfile(或 npm ci)退出码为 0。
  3. mainpackage.json diff 里只有你想要的依赖,且没有任何 lockfile 冲突标记(git grep -n '<<<<<<<' 没有输出)。

预防建议

  • package.json 里固定 packageManager 字段(如 "packageManager": "pnpm@11.7.0")。注意:只有在启用了 Corepackcorepack enable)时它才会被强制执行——而且从 Node.js 25(2025 年 10 月)起 Corepack 不再随 Node 自带,要单独安装。光有这个字段只是个提示、不是保证;务必配合下面的 CI 检查。
  • CLAUDE.md / .cursorrules / AGENTS.md 第一行写:“包管理器是 pnpm,禁止 npm install / yarn add。”
  • 加一个 pre-commit hook,检测到 package-lock.json + pnpm-lock.yaml 同时存在就拒绝提交。
  • agent 想装新依赖时,让它先输出”我要装 X、用于 Y”,你确认后再执行——Bash(npm install:*) 默认拒绝、白名单按需放行。
  • CI 必须跑 npm ci / pnpm install --frozen-lockfile / yarn install --immutable,任何漂移在进 main 之前就挂掉。这是唯一真正管用的强制手段。
  • 升级类任务走专门的 AI 依赖升级工作流,别混进 feature 分支里。

常见问题

为什么不能直接手工 merge lockfile 冲突? lockfile 是从你的 package.json 加上 registry 的依赖图生成的。从 <<<<<<< 块的两边各挑几行,得到的是一份”两个分支实际都解析不出来的树”——完整性 hash 对不上,npm ci 会拒。永远重新生成。

npm ci 挂了,可我根本没改 lockfile,为什么? 大概率是某个间接依赖在你提交 lockfile 之后发了新补丁(比如新的 @types/*)。跑 npm install --package-lock-only 对着当前已发布的版本重新锁定,再提交。少数情况是 lockfile 由跟 CI 不同的 npm 大版本生成的——对齐 packageManager 字段即可。

这个仓库到底用哪个包管理器? 先看 grep '"packageManager"' package.json,再看存在哪个 lockfile:package-lock.json(npm)、pnpm-lock.yaml(pnpm)、yarn.lock(yarn)、bun.lock 或旧的 bun.lockb(bun)。如果不止一个,那就是分类 1——把错的那个删掉。

agent 删了一个包,现在 prod build 找不到它,要恢复吗? 要。如果 npm ls <pkg> 显示有别的包依赖它,说明它是个真实需求(往往是间接依赖)。把它加回 dependencies 再重新生成。这类坑正是”只凭 grep import 就删依赖”不安全的原因。

lockfile 到底要不要提交? 应用项目:要,一定提交,这样每次安装(包括 CI)都可复现。发布的库:lockfile 不会被使用方用到,但提交它仍能让你自己的 CI 保持确定性。

pnpm 重新生成的行数比我预期多很多,是 bug 吗? 不是。pnpm 10.9 起,--lockfile-only 会重新解析整张依赖图、而不只是改动那一个包,所以 diff 会偏大。只要随后 pnpm install --frozen-lockfile 能过,这份 lockfile 就是对的。

相关阅读

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