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.ts 和 packages/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 大项目