Cursor 老是读错文件:3 个原因 + 修复路径

Cursor 把旧 / 生成 / vendored 文件拉进 context——靠索引和 ignore 修。

你让 Cursor 修一个 React 组件,它却引用了 dist/components/Foo.js(已编译的旧版)里的 prop 名,导致 patch 完全不对。或者你问”我们怎么实现 rate limit 的?“,它回答的是 vendor/express-rate-limit/ 里的源码,而不是你自己写的中间件。这都是 Cursor 把”不该读的文件”塞进了 context window——构建产物、vendored 第三方库、备份文件、生成代码。这篇给出诊断方法和让 Cursor 只看源码的清理步骤。

常见原因

按出现频率从高到低排序。

1. 生成文件和源码混在一起

dist/build/.next/coverage/ 没被 ignore,Cursor 把它们一并索引。embedding 检索时,编译后的 JS 经常因为”包含一样的关键字”被判定相关,反而压过真正的 src/ 源码。

如何判断:执行后看 Cursor 回复底部的 “Context used”,里面出现 dist/build/.next/out/.astro/ 任意一个路径就是命中。

2. Vendored 第三方库放在仓库里

旧式 Node / PHP 项目里 vendor/ 直接 commit;前端项目偶尔有 public/vendor/jquery-3.5.1.min.js。这些代码体量大、命中率高,把 agent 的 context 挤满。

如何判断du -sh vendor/ public/vendor/ third_party/ 2>/dev/null,若任一目录 > 5MB 又被索引,就是元凶。

3. 完全没有 .cursorignore

很多人以为 .gitignore 就够了,但 .gitignore 不管未被 git 追踪的本地文件——比如 .env.localscratch.mdold-backup-2024.tsx。Cursor 看见就会索引。

如何判断:仓库根执行 ls -la | grep -E "(scratch|backup|old|\.bak|\.orig)",列出来的文件如果不在 .cursorignore 里,都是潜在污染源。

4. 备份与 .bak / .orig 文件

merge 冲突遗留的 Foo.tsx.orig、IDE 自动保存的 Foo.tsx~、或者你手动复制的 Foo-old.tsx。这些文件和当前版本只差几行,embedding 算它们高度相关,agent 经常把旧逻辑搬回来。

如何判断find . -type f \( -name "*.orig" -o -name "*.bak" -o -name "*~" -o -name "*-old.*" \) | head -20

5. 测试 fixtures / snapshot 抢戏

__snapshots__/fixtures/mocks/ 经常和真实代码”语义雷同但行为不同”。改业务逻辑时,agent 把 snapshot 里的旧字符串误认为是当前代码的真实状态。

如何判断:在 “Context used” 里看到 __snapshots__.snap 文件,且你的 prompt 跟测试无关,就是。

最短修复路径

Step 1:建一个能拦住常见污染源的 .cursorignore

在仓库根创建或扩充 .cursorignore

# 构建产物
dist/
build/
out/
.next/
.astro/
.svelte-kit/
.nuxt/

# 依赖与 vendored
node_modules/
vendor/
public/vendor/
third_party/

# 缓存、覆盖率、日志
.cache/
.turbo/
.parcel-cache/
coverage/
*.log

# 备份与编辑器临时文件
*.bak
*.orig
*~
*-old.*
*.swp

# 压缩 / 打包文件
*.min.js
*.min.css
*.bundle.js
*.bundle.css

# 测试 snapshot(按需)
**/__snapshots__/
**/*.snap

# 本地 scratch
scratch.md
TODO.local.md

根据栈微调,原则是”AI 看到没价值/会误导的,全屏蔽”。

Step 2:强制 Cursor 重建索引

.cursorignore 改完不会立刻生效,必须 resync:

Cmd+Shift+P → "Cursor: Resync Index"

或者更彻底地:

# 关闭 Cursor,然后
rm -rf ~/Library/Application\ Support/Cursor/User/workspaceStorage/*/cursorIndex
# 重启 Cursor

重建完成后,Settings → Codebase Indexing 显示的 “Files Indexed” 数应该明显下降。

Step 3:在 prompt 里用 @File / @Folder 锁定要参考的文件

即使 ignore 干净了,长 chat 仍会偶发误检索。最稳妥的做法是显式钉:

@Folder src/components/auth @File src/lib/auth.ts

修改 AuthForm 组件的 onSubmit。只读 / 改我 @ 出来的文件,
不要参考任何 dist/、build/ 或 *.min.js 的内容。

加一句反向约束(“不要参考 X”)通常比只加正向约束更有效。

Step 4:定期清理仓库里的”考古层”

每月(或加到 pre-commit hook)跑一次清扫:

# 列出所有可能被 agent 误读的文件
find . -type f \( \
  -name "*.bak" -o -name "*.orig" -o -name "*~" \
  -o -name "*-old.*" -o -name "*-backup.*" \
  -o -name "*.swp" \
\) -not -path "./node_modules/*" -not -path "./.git/*"

# 确认无用后删
find . -type f \( -name "*.bak" -o -name "*.orig" -o -name "*~" \) \
  -not -path "./node_modules/*" -delete

合并冲突后第一时间 git status 看是否产生了 .orig,立刻删。

Step 5:验证 agent 现在读对了

让 Cursor 自己列读取的文件:

列出你为了回答上一个 prompt 读了哪些文件,完整路径,按读取
顺序。不要包含 dist / vendor / *.min.js。如果有,告诉我哪个 @ 把它们带进来了。

回答里若仍出现脏路径,说明 ignore 不完整,回 Step 1 补。

预防建议

  • 新仓库初始化时就 commit .cursorignore,模板和 .gitignore 同步维护
  • 生成产物(dist/build/coverage/)永远放仓库根独立目录,不要散落在 src/
  • vendored 第三方代码放 third_party/ 并加进 .cursorignore,不要直接放仓库根或 public/
  • 解决 merge 冲突后立刻 find . -name "*.orig" -delete,避免污染索引
  • 在 CLAUDE.md / .cursorrules 里写一句 “Treat dist/, build/, vendor/, and .min. as build artifacts. Never read them.”

相关阅读

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