你在 chat 里输 @utils.ts,让 Cursor 解释里面的 formatDate,结果它聊的内容你完全不认识——它读的是 packages/admin/utils.ts 而不是你期望的 packages/web/utils.ts。或者更怪:它讲了一个你三天前删掉的函数。这几乎从来不是模型胡说,而是三种情况之一:@ 选择器在多个同名文件里挑错、语义索引滞后于磁盘真实状态、或 symlink 让 Cursor 把两个路径当成同一个。
最快的修法(覆盖 90% 情况): 删掉当前 @ 标签,重新用全路径引用(写 @packages/web/utils.ts,别写 @utils.ts),并在 prompt 里加一句,让模型在回答前先把它读的文件路径 echo 出来。如果改名或删除之后它还在给陈旧内容,就到 Cursor Settings → Indexing 强制 resync。下面把这个问题拆成六种情况,并讲怎么让修复真正生效。
本文中的 Cursor 索引与 ignore 文件行为均按 2026 年 6 月核对。
先判断你属于哪一种
| 回答里的症状 | 最可能的原因 | 跳到 |
|---|---|---|
| 函数名对,但包/路径不对 | @ 选择器存了另一个同名文件 | 原因 1 |
| 讲的是你已改名或删除的代码 | git mv / git rm 后索引滞后于磁盘 | 原因 2 |
| 两个路径”是同一个文件” | 目录被 symlink | 原因 3 |
| @ 标签变灰 / 标 “missing” | 旧对话里挂着失效 attachment | 原因 4 |
| 讲的是测试数据而不是真实逻辑 | fixture 和源码同名 | 原因 5 |
| 你 Mac 上对,Linux CI 上错 | 大小写敏感差异 | 原因 6 |
常见原因
1. 同名文件多份,@ 选择器挑错
monorepo / 大型项目里 index.ts、types.ts、utils.ts、config.ts 等会有几十份。@ 自动补全按相似度排序,列表里你点了第二个、它其实存了第一个 path。
如何判断:
fd "utils.ts" . # 或 find . -name "utils.ts" -not -path "*/node_modules/*"
如果输出超过一行,就说明有歧义。
2. 索引陈旧——还在引用旧版本 / 已删除文件
你刚 git mv old.ts new.ts 或 git rm old.ts,索引还没更新;Cursor 仍按旧索引取出旧文件内容回答。
如何判断:打开 Cursor Settings → Indexing(新版把这里从 Features → Codebase Indexing 改了名),看已索引文件数和索引状态。如果文件数还停留在你改名之前,或你在磁盘上动过文件后状态一直没变,就是索引陈旧。
3. Symlink 让两个路径指向同一份
packages/shared symlink 到 ../shared,Cursor 索引可能两份都存、但读时按 symlink target,与你 @ 的路径不一致。
如何判断:
find . -type l -not -path "*/node_modules/*"
4. 文件改名后 chat 引用了旧 attachment
旧对话里 @ 的文件路径已经过期。继续在同一对话发 prompt,Cursor 还试图读旧路径,要么读到旧缓存要么读到同名新文件。
如何判断:上下文面板里 @ 文件的路径加灰显示 / 标 “missing”。
5. 测试 fixture 和源码同名
src/parser.ts 和 src/__tests__/fixtures/parser.ts,retrieval 把 fixture 排到前面,模型解释 fixture 的逻辑。
如何判断:context 面板里出现 fixtures/ / __tests__/ 下的同名文件。
6. 大小写敏感差异(macOS / Linux 差)
macOS APFS 默认不区分大小写,User.ts 和 user.ts 在 macOS 上是同一文件,在 Linux CI 上是两个。Cursor 索引按 OS 行为存,跨机器协作时混淆。
如何判断:fd -i "user.ts" . 看有几条命中(-i = 忽略大小写)。
动手前先确认
- 确认是 chat / Composer(Agent)/ Cmd+K 行内编辑哪个入口出问题。Cmd+K 直接基于光标位置,不会读错文件;所以如果只在 chat/Composer 里出问题,那就是检索或 @ 选择器的问题,不是编辑器的问题。
- 复现前先 commit 一次,避免 rename / 删 symlink 改动丢追踪。
- 记下 Cursor 版本(Help → About)、当前模型、是否在 monorepo 或 multi-root workspace。
需要收集的信息
- 你 @ 的文件名 + 模型回答里引用的内容。
fd <filename> .输出(列出所有同名文件)。Cursor Settings → Indexing里的文件数和索引状态。- Composer / chat 上下文面板截图,看实际加载了哪个路径。
最短修复路径
按”立刻消歧 → 系统改善”排序。
Step 1:删旧 @,用全路径重 attach
把当前 @ 删掉,重新输:
@packages/web/utils.ts
让 @ 解析全路径而不是只认文件名。如果路径里还有歧义,补到目录级:
@packages/web/src/utils.ts (而不是 @utils.ts)
在 monorepo 里,先缩小检索范围也有帮助:用 @folder(比如 @packages/web)把检索限制在一个包里,再去 @ 文件,而不是让 @codebase 把全仓库的同名文件都排一遍。
Step 2:在 prompt 里再次显式指认
I'm asking about the formatDate function in packages/web/utils.ts (NOT any other utils.ts in this repo).
Please confirm the file path you're reading before answering.
让模型先 echo 路径,错了你立刻发现。
Step 3:强制 resync 索引
可靠的触发方式是设置里的按钮,不是快捷键。打开 Cursor Settings → Indexing,点 resync / reindex 那个控件(老版本这一栏叫 Features → Codebase Indexing)。如果是大改动后要彻底重建(批量改名、切分支、大合并),命令面板里对应的是 Cmd+Shift+P → Reindex Codebase 和 Reindex Codebase (Rebuild)。
注意:老的 Cursor: Resync Index 命令在新版里已经并进了设置面板,所以如果在命令面板里找不到,直接输 reindex 搜。大仓库(几万个文件)重建可能要几分钟到几小时;索引到大约 80% 之前语义检索都用不了,所以等状态跑完再重新测你的 prompt。
Step 4:rename 消歧(最稳)
如果两份同名文件长期共存,给一个起独特名字:
git mv packages/admin/utils.ts packages/admin/adminUtils.ts
# 更新所有 import
rg "from .*admin/utils" --files-with-matches | xargs sed -i '' 's|admin/utils|admin/adminUtils|g'
模型再也不会混。
Step 5:清理过期 @ + 重开 chat
旧 chat 里的 @ 可能挂着失效路径。新开 chat(Cmd+L 重置 chat),重新 @,模型不会带过去的上下文残留。
Step 6:拆掉 symlink,或把重复路径剔出索引
如果一份目录被 symlink,作为重复路径冒出来,就把这份 symlink 副本剔出索引——但要用对 ignore 文件。截至 2026 年 6 月,两者行为不同:
.cursorindexingignore只把路径从语义检索里去掉;你仍然能@这个文件,agent 也仍然能读。symlink 重复用这个正合适:保留对规范路径的索引,把别名隐藏,两边都还够得到。.cursorignore会把路径从检索、Tab、Agent、Inline Edit 以及@引用里全部屏蔽。只有在你完全不想让 agent 碰这个路径时才用(密钥、构建产物)。
# 把 symlink 别名从索引里隐藏,但保留可 @ 引用
echo "packages/shared-link" >> .cursorindexingignore
# 长期更好的做法:用 npm/pnpm/yarn workspaces 替掉 symlink
改完任一文件后,再跑一遍 Step 3,让索引反映新的排除规则。
怎么确认已经修好
- 重新 @ 后,让模型把它加载的文件路径 echo 出来,确认和你想要的那条一致。
- 打开上下文面板,确认只有一个标签、指向完整正确的路径(没有变灰 / “missing” 的标签)。
- 从模型答案里取一段独特字符串,在你期望的文件里
grep/rg——应该能在那里找到、在同名诱饵文件里找不到。 - 如果是索引陈旧的修复,等 resync 状态显示完成后再跑同一 prompt;已删除/改名的引用应该不再出现。
如果还是没修好
- 把 prompt 缩到最小:只 @ 一个全路径文件 + 一句
what's in this file?,不带其他上下文。 - 回滚最近一次 rename 或
.cursorindexingignore/.cursorignore改动,排除它引入了新冲突。 - 确认文件没有被误放进
.cursorignore(它会彻底屏蔽@引用),而你本意是用.cursorindexingignore。 - 在 forum.cursor.com 搜 “wrong file context”;附上
fd输出和上下文面板截图。 - 抓
View → Output → Cursor的索引日志,贴到论坛的 Bug Reports 分类。
预防建议
- 重要文件尽量起独特名字,少用裸的
utils.ts/types.ts/index.ts。 - 大重构、rename 或切分支后,立即到
Cursor Settings → Indexing手动 resync,别等自动同步。 - @ 习惯用全路径,养成肌肉记忆,避免在选择器里选错那一行。
- monorepo 里先用
@folder缩范围,再用@file。 - 用 npm / pnpm / yarn workspaces 替掉 symlink,让两个路径永远不会指向同一个文件。
- 跨 macOS / Linux 的项目用大小写敏感文件系统(macOS 可用磁盘工具建一个 case-sensitive APFS volume)。
FAQ
为什么 Cursor 会读我几天前删掉的文件?
语义索引是对代码库的一份缓存 embedding,不是对磁盘的实时读取。git rm 或 git mv 之后,它会继续给旧内容,直到重新 resync。到 Cursor Settings → Indexing 强制 resync,再重新问一遍。
这是不是模型幻觉?
通常不是。幻觉是凭空编出根本不存在的代码;这个 bug 给的是真实代码,只是来自错的同名文件或陈旧索引。如果你能在仓库的某个文件里 grep 到模型给的片段,那就是检索/索引问题,不是幻觉。
.cursorignore 和 .cursorindexingignore 有什么区别?
.cursorignore 会把路径从检索、Tab、Agent、Inline Edit 以及 @ 引用里全部屏蔽——agent 根本够不到(终端和 MCP 工具是例外)。.cursorindexingignore 只把它从索引/检索里去掉;你仍能 @ 它,agent 也能按需读取。symlink 重复用 .cursorindexingignore。
Cmd+K 行内编辑会不会也有这个问题? 不会。Cmd+K 绑定你当前光标位置和文件,改的就是你所在的地方。读错文件这个问题只出现在 chat 和 Composer(Agent)的检索以及 @ 选择器上。
reindex 要多久?
小项目几秒就 resync 完。几万个文件的大仓库可能要几分钟到几小时,而且索引到大约 80% 之前语义检索都用不了。重新测之前,先看 Cursor Settings → Indexing 里的状态。