Cursor Composer 在大项目里晕头转向

小仓库 Composer 很好用,5 万文件 monorepo 里就丢线索——靠显式上下文,别赌索引。

Composer 不读整个仓库——它依赖嵌入检索抽出来的几十块片段。小项目里 top-K 已经覆盖全仓库;进到 1 万文件以上的 monorepo,同一个概念有十几个版本,检索只看到 1-2 个,模型用猜补空白。结果就是:改了 A 模块,引用 A 的 B 模块没动;推荐的 import 路径根本不存在;packages/old-app 里的旧模式混进 packages/new-app。

修这类问题不是改设置那么简单,得调整你和 Composer 的协作方式。

常见原因

1. 检索只命中 top-N,覆盖率不够

Composer 把仓库切成 200-500 行块,嵌入存到本地索引。问”修一下 auth”时,它只取 top 10-20 最相关块。10 万行 auth 子系统里,这点信息量等于让模型透过钥匙孔看屋子。

如何判断:让 Composer 答完后追问 “List exactly which files you read”,如果只列了 3-5 个、且漏掉你心里清楚相关的文件,就是检索覆盖不够。

2. Monorepo 多版本同名模式干扰

packages/web/utils/fetch.tspackages/admin/utils/fetch.ts 同名不同实现。嵌入检索按相似度排序,可能把 admin 的版本塞进 web 的 prompt 里,模型照抄。

如何判断:检查它给的代码里出现 import 路径是不是穿越了包边界(例如 web 里 import 了 admin 的内部模块)。

3. Working directory 设到仓库根

Cursor 默认把 workspace 根当 working directory,所有文件都在搜索面。设到 apps/web 子目录后,检索面缩小一个量级,质量立刻提升。

如何判断:左下角看当前 workspace 路径;如果是仓库根又是 monorepo,就太宽。

4. 没有 .cursorrules 给出”包边界”地图

没有 rules 文件时,模型不知道哪些路径属于哪个包、哪个是公开 API、哪个是内部。它只能从文件路径猜。

如何判断:仓库根 ls -la | grep cursor;如果没有 .cursorrules.cursor/rules/,就是缺。

5. 一条 prompt 跨多个包

“统一所有微服务的 logger” 这种跨包任务,Composer 没法保证每个包都被检索覆盖到。它会改它看到的几个,漏的那些静静过去。

如何判断:执行后用 grep -r "old_logger" . 看是否还有遗漏;漏一两个就是这种情况。

6. 索引过期或被 .cursorignore 大面积排除

如果索引上次完整跑是两周前,期间新增的目录在检索里完全不存在。.cursorignore 太激进(比如 **/*.test.ts)会让 Composer 失去测试参考。

如何判断:Settings → Features → Codebase Indexing,看 “Last indexed” 时间;查 .cursorignore 文件覆盖范围。

动手前先确认

  • 确认是 Composer 还是 Cmd+K 在出问题;Cmd+K 不走全仓检索、行为差很多。
  • 复现前先 commit 一次或开 branch,避免 apply 把未保存改动覆盖。
  • 记下 Cursor 版本和当前模型;不同模型对长上下文处理差异很大(opus/gpt-5 long 比 sonnet 长上下文稳)。

需要收集的信息

  • 仓库总文件数、行数、是 monorepo 还是单包。
  • 是否有 .cursorrules / .cursorignore,最近一次完整索引时间。
  • 用的模型、是否开 Max mode、Composer 输入框里 attached 的 @Files 列表截图。
  • 模型答完后追问 “Which files did you actually read?” 的回复。

最短修复路径

按收益排序,前两步通常立刻见效。

Step 1:手动 @Files 5-10 个最相关文件

最大杠杆动作。任务前在 Composer 输入框:

@target-file.ts
@reference-pattern-1.ts
@reference-pattern-2.ts
@target-file.test.ts
@types/relevant.ts

加上 prompt:“follow the pattern shown in reference-pattern-1.ts exactly”。

Step 2:把 working directory 设小

Cursor → File → Open Folder → 选 apps/web 而不是 monorepo 根。Composer 检索面立刻缩到一个包以内,pattern 混乱基本消失。

Step 3:给每个包写自己的 .cursorrules

packages/web/.cursorrules

This is the customer-facing web app.
- Imports MUST stay inside packages/web/* or packages/shared/*.
- Never import from packages/admin/* or packages/internal-tools/*.
- Use the Logger from packages/shared/logger, not console.log.
- API calls go through src/api/client.ts; do not call fetch() directly.

Cursor 会优先采纳子目录的 rules,包之间不会再串味。

Step 4:让 Composer 先回报上下文,再动手

把 prompt 写成两步:

Step 1: List exactly which files you've loaded into context and explain how each relates to the task. Do NOT write code yet.
Step 2: After I confirm, implement the change.

如果它列出来的文件不对,停手补 @Files;对了再让它继续。

Step 5:跨包任务拆成单包子任务

不要一句”统一所有微服务”。改成:

Task scope: only packages/auth-service for now.
Goal: replace all usage of old_logger with shared logger.

完成后切到下一个包重新开 Composer turn。

Step 6:让索引重跑

Settings → Features → Codebase Indexing → “Reset index” → 等 5-30 分钟跑完。同时检查 .cursorignore 别覆盖太广。

怎么确认已经修好

  • 同一个 prompt 在 working dir 收窄前后对比,看跨包污染是否消失。
  • 让队友打开同一个 workspace 用同样 prompt 复现,确认是 prompt/上下文设置层的修复。
  • 用 grep / lint 实际跑一遍,确认 import 路径不再穿越包边界。

如果还是没修好

  • 把 prompt 缩到最小:只改一个函数、只 @ 一个文件、明确路径。
  • 回滚最近一次 Cursor 升级或 .cursorrules 改动。
  • 在 forum.cursor.com 搜 “monorepo composer context”;附 Cursor 版本 + 仓库规模数字。
  • 抓 View → Output → Cursor 日志贴 Bug Reports 频道。

预防建议

  • 每个包都放一份 .cursorrules,明确包边界、约定、禁用 import。
  • 在每个包 README 里写一节”哪些文件改了要同步改哪些”,给模型做 anchoring。
  • 把 5-10 个核心参考文件加进 workspace 的 pinned tabs,Composer 检索会优先它们。
  • 大重构走 Claude Code 这种 CLI agent 做扫描,Cursor 做编辑器内 spot edit,配合而不是强求 Cursor 单挑。
  • 把”先列上下文再写代码”做成你团队的标准 prompt 模板。

相关阅读

标签: #排查 #Cursor #排查 #Composer 大项目