你两年前就定了用 pnpm。仓库里是 pnpm-lock.yaml,CI 用 pnpm install --frozen-lockfile,Dockerfile 里 corepack enable && corepack prepare pnpm@9。Cursor 或 Claude Code 不管这些,它跑 npm install lodash,在 pnpm-lock.yaml 旁边生成一个 package-lock.json,hoist 方式与 pnpm 严格模式不一致,然后把两个都 commit 进去。CI 崩了,因为 pnpm workspace 配置不再匹配 npm 的扁平布局;之前能解析的 phantom dependency 现在失败;同事拉下来得到一坨乱七八糟的 node_modules。这是 AI 编码最常见的”小翻车”,而且会累积——两个锁文件一旦共存,后续每次安装都有风险。
常见原因
按出现频次排序。
1. AI 默认 npm,因为训练数据里 npm 最常见
公开的 Node 教程绝大多数用 npm install,模型对 npm 命令词汇训练深。如果项目没有显式信号,它默认就上 npm。
如何识别:仓库里明明有 pnpm-lock.yaml 或 yarn.lock,AI 还是建议 npm install <pkg>。
2. package.json 里缺 packageManager 字段
现代 Node 工具链(corepack)用 package.json 里的 "packageManager": "pnpm@9.x" 声明权威管理器。不写,AI 就没有机器可读的信号。
如何识别:cat package.json | grep packageManager 没输出。
3. AI 跑 npm 是为了”修”一个它误诊的问题
它看到缺依赖的报错,直接 npm install <pkg>,完全没考虑这会留下竞争锁文件——求快压过求对。
如何识别:某个本应修别的问题的 commit 里冒出来一个 package-lock.json。
4. 团队成员或环境里混用了多种管理器
一个人 npm,一个人 pnpm,CI 用 yarn。AI 继承这种不一致,根据当前打开文件所在目录看到什么用什么。
如何识别:版本历史里出现过多种锁文件;同事的 node_modules 布局各不相同。
5. AI 生成了绕过 workspace manager 的 npx
npx prisma generate 能跑,但不尊重 pnpm 的 workspace 结构。monorepo 里这会解析到与 workspace 锁定不同的 prisma 版本。
如何识别:AI 生成的脚本里出现 npx <tool>;本地和 CI 的工具版本不一致。
6. AI 给 package.json 加了”兼容性”脚本
有时 AI 会”贴心”地写一句 "scripts": { "install:npm": "npm install" }。任何同事跑这个脚本就会污染锁文件。
如何识别:package.json 的 scripts 里同时引用了 npm 和 pnpm/yarn。
开始之前
- 确定本项目的权威包管理器。还不清楚就先和团队定下——统一好就解决了一半。
- 检查 AI 暂存的改动里有没有
package-lock.json或多余的node_modules路径。 - 决定强制方式:
preinstall脚本、corepack、CI 检查,三者都上更稳。
需要收集的信息
- 仓库里都有哪些锁文件:
ls -la *.lock* *lock*.yaml *lock*.json。 package.json内容——特别是packageManager字段与engines。.npmrc、.pnpmrc、.yarnrc.yml(如有)。- CI 配置:流水线里用的是哪条 install 命令。
- Dockerfile / devcontainer 启动时做了什么。
- 出现错误锁文件或同时存在多种锁文件的近期 commit。
分步修复
按”先清当前烂摊子,再防再犯”排序。
第 1 步:删掉错误锁文件并按规重装
如果权威管理器是 pnpm,而冒出来了 package-lock.json:
rm -rf node_modules package-lock.json
pnpm install
git add package.json pnpm-lock.yaml
git rm package-lock.json 2>/dev/null
权威是 yarn 或 npm 的话,把命令对应替换即可。
第 2 步:在 package.json 里声明权威管理器
{
"packageManager": "pnpm@9.7.0",
"engines": {
"node": ">=20",
"pnpm": ">=9"
}
}
启用 corepack(corepack enable)后,在这个仓库里跑 npm install 会警告或失败。
第 3 步:加 preinstall 守卫
package.json 里:
{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
only-allow 是一个极小的包,用错管理器它会直接 abort。报错信息很明确:“Use pnpm, not npm.”
第 4 步:CI 加上重复锁文件检查
CI 里:
if [ -f "package-lock.json" ] || [ -f "yarn.lock" ]; then
echo "ERROR: Found a non-pnpm lockfile. This repo uses pnpm only."
exit 1
fi
带错误锁文件的 PR 直接合并失败。
第 5 步:在规则文件里显式告诉 AI
.cursorrules 或 CLAUDE.md:
This repo uses pnpm. Never use npm or yarn commands.
- Install: pnpm install
- Add package: pnpm add <pkg>
- Add dev dep: pnpm add -D <pkg>
- Remove: pnpm remove <pkg>
- Run script: pnpm run <script> or pnpm <script>
- Workspaces: pnpm --filter <pkg> <cmd>
Do NOT generate or run:
- npm install / npm ci / npm add
- yarn install / yarn add
- npx (use pnpm dlx instead if needed)
The preinstall hook will block npm/yarn anyway, but do not generate them.
直接把策略放进 AI 的工作上下文。
第 6 步:用 pnpm dlx / yarn dlx 替代 npx
一次性工具调用:
# 别用:
npx prisma generate
# 用:
pnpm dlx prisma generate
# 或者更好——固化成 dev dep 再用:
pnpm prisma generate
pnpm dlx 尊重 workspace 解析器。附赠:因为 pnpm 用 content-addressed store,它一般比 npx 还快。
第 7 步:用 corepack 锁住开发环境
README 与 Dockerfile 里都加上:
corepack enable
corepack prepare pnpm@9.7.0 --activate
这样每个开发环境和 CI runner 都是同一个 pnpm 版本。哪怕 AI 想”用 pnpm 试一下”也不会用到错版本。
验证
- 仓库里只剩一种锁文件(比如只有
pnpm-lock.yaml)。 - 在仓库里跑
npm install立即被only-allow拦下。 - 带错误锁文件的 PR 在 CI 里失败。
- 让 AI”加一个新包”,第一次就给出
pnpm add(不是npm install)。 - 全新克隆 +
pnpm install --frozen-lockfile能产出可用的node_modules。
长期预防
- 新仓库一律设
packageManager。这是最有效的单点护栏。 preinstall拦住错管理器——人和 AI 一起抓。- CI 既要检查重复锁文件,也要监控
package-lock.json是否突然增大(意外的 npm install 信号)。 - 通过规则文件一次性教育 AI,不要靠每次 prompt 内提醒。
- monorepo 优先 pnpm 或 yarn berry——npm workspaces 最弱,AI 的默认偏好对它伤害最大。
- 通过 corepack 锁版本号(不只是名字),避免漂移。
常见误区
- 不设
packageManager,寄希望于”反正pnpm-lock.yaml就在那 AI 应该看得到”——它看不到。 - 删了
package-lock.json却忘记加 preinstall 守卫——下一次会话 AI 又给你生成出来。 - AI 生成的脚本里用
npx调 monorepo 工具——会静默拿到错版本。 - 容忍某个同事”我就用 npm,pnpm 烦死人”——他们提交错锁文件,AI 之后会沿着他们的 commit 模仿。
- 两个锁文件都提交”以防万一”——一定会分歧,一定有一个是错的。
- CI 没开 corepack,导致 CI 的 pnpm 版本和本地不一样——症状和 AI 用错管理器几乎相同。
相关问题见 AI 锁文件冲突、AI 覆盖了环境变量、AI 代码本地通过但云端构建失败。
FAQ
Q:pnpm-lock.yaml 明明就在那,AI 为什么还是默认用 npm?
公开训练数据里 npm 占主导。锁文件的存在是 AI 可能注意到也可能忽略的弱信号,packageManager 字段才是强信号——务必设上。
Q:能不能 npm 和 pnpm 共用一个 package.json?
不行。它们 hoist 策略不同、peer dep 解析不同,产出的 node_modules 互不兼容。选一种,锁死。
Q:想从 npm 迁到 pnpm,该怎么 prompt AI?
“本项目正在从 npm 迁到 pnpm。请删 package-lock.json 和 node_modules,跑 pnpm import 转换锁文件。从现在起只用 pnpm。在 package.json 里加 packageManager: pnpm@9。”
Q:preinstall 守卫离线开发会不会出问题?
only-allow 是个本地小脚本,不需要联网。它只看 npm_config_user_agent 环境变量来判断,离线照样能用。