Cursor 一直 indexing 怎么办:真正管用的 .cursorignore 方案

Cursor 卡在 indexing 或反复重索引?多半是 dev server 不停改写的某个 build/缓存目录。这篇给出 .cursorignore 模板、watcherExclude 修复,以及如何确认索引真的健康。

打开一个稍微大点的项目,Cursor 状态栏永远停在 Indexing 5% 半小时不动;或者跑完了又立刻重跑,整台机器风扇拉满。Cursor 并没有卡死。截至 2026 年 6 月,它默认就会遵守你的 .gitignore,外加一份内置默认忽略列表(node_modules/、lockfile、.env*.git/、二进制文件),所以变慢的情况几乎总是某个默认列表没覆盖到、或被别的配置覆盖掉的目录:dev server 每次保存都重写的构建输出目录、某个缓存目录,或者 monorepo 某个子包的产物。文件监听器看到持续不断的变更,永远稳定不下来。

最快修复: 把那个吵闹的 build/缓存目录加进 .cursorignore,把同一路径加进 VS Code 的 files.watcherExclude,然后打开 Cursor Settings > IndexingResync Index。详细步骤和完整模板见下文。

先判断你属于哪一类

动手改之前,先把你的症状对到原因上。

症状最可能的原因跳转
Indexing 卡在低百分比,一直不动在扫描一个巨大目录(vendored 依赖、生成代码、漏进来的 node_modules原因 1
索引跑完后,只要 npm run dev 开着就每隔几秒重跑构建产物 / 热重载写入反复触发监听器原因 2
一停掉 dev server 死循环立刻停止同上原因 2
单仓库正常,只有 monorepo 出问题子包产物(apps/web/.nextpackages/ui/dist)叠加原因 3
哪怕是个小仓库也一直 Setting up indexing...服务端或账号问题,不是你的文件原因 4
开了 Privacy Mode 后一直转圈Privacy / 旧设置挡住了 embeddings 上传原因 4

常见原因

1. 默认列表没覆盖到的大目录

Cursor 的内置列表已经会跳过 node_modules/,但 vendored 依赖(vendor/third_party/)、生成的 SDK/protobuf、ML 权重(.pt.safetensors),或者因为符号链接、奇怪的 workspace 根目录被重新算进来的 node_modules,都落在它之外。每一个都比你的源代码大两个数量级。

如何判断:数一数可疑目录里的文件。

# Cursor 可能要遍历的文件总数
find . -type f -not -path './.git/*' | wc -l
# 最常见的元凶
find node_modules -type f 2>/dev/null | wc -l

如果项目总数到了几十万,而 Cursor 一直停在 Indexing,就说明有目录需要忽略。健康的源代码树通常在 <= 30000 个文件以内。

2. 构建产物反复触发监听器(死循环)

这是现在最常见的一类。开了热重载后,dist/.next/.svelte-kit/.turbo/ 每次保存都被重写。Cursor 收到变更事件就重新索引,形成 “索引完 -> 检测到变化 -> 重索引” 的死循环,把一个 CPU 核心占满。这里有两样东西都要忽略:索引(让文件不被重新 embedding)和文件监听器(让 Cursor 根本不被叫醒)。

如何判断npm run dev 跑着的时候 Indexing 每隔几秒就冒出来一次,一停掉 dev server 就安静了。

3. Monorepo 子包产物

apps/web/.nextapps/api/distpackages/ui/storybook-static 单看每个都不大,但加起来能到几百万文件。根目录一份 .cursorignore 里写裸的文件夹名(dist/)只匹配根目录;嵌套的副本需要用 glob。

如何判断

find . -type d \( -name dist -o -name .next -o -name build -o -name .turbo \) -not -path '*/node_modules/*'

返回三条以上且没有 ignore 覆盖,就是它。

4. 服务端或 Privacy Mode(不是你的文件)

如果哪怕是个小仓库也一直显示 Setting up indexing...,那问题就不在文件上。代码库索引会把 embeddings 上传到 Cursor 的服务器;旧的 Privacy Mode 设置和偶发的后端故障都会挡住这一步。先看 Cursor 状态页,再检查 Cursor Settings > General > Privacy。语义搜索大约在完成度 80% 时才可用,所以卡在接近结尾的位置,也可能是服务端的 embedding 队列在排队,而不是本地扫描。

最短修复路径

Step 1:写一份”通杀”的 .cursorignore

在项目根创建或编辑 .cursorignore。它用 gitignore 语法,所以 ** 可以跨目录匹配。下面这些条目是在 Cursor 内置默认之外的补充(node_modules/ 其实不写也行,因为它本来就是默认项,但写上去意图更清楚,同时也把它从 @ 引用里硬挡掉):

# 依赖(默认已覆盖 node_modules,这里写出来表明意图)
node_modules/
vendor/
third_party/

# 构建产物(用 ** 在任意层级匹配,照顾 monorepo)
**/dist/
**/build/
**/out/
**/.next/
**/.astro/
**/.svelte-kit/
**/.nuxt/
**/.output/
**/storybook-static/

# 缓存
**/.cache/
**/.turbo/
**/.vercel/
**/.parcel-cache/
**/.pytest_cache/
**/__pycache__/

# 测试覆盖率与大文件
**/coverage/
**/.nyc_output/
*.safetensors
*.pt

# 日志
*.log

.cursorignore 会同时阻止文件被索引以及被 Agent、Tab、@ 引用访问。如果你只想把某个目录排除出索引、但仍然能在 @ 引用或拖进 chat 时被读到(遗留代码、大型 fixture),那就改放进 .cursorindexingignore,语法相同。注意 Cursor 把这一机制称为 best-effort:它并不绝对保证被忽略的文件永远不会被发给模型。

Step 2:别让文件监听器把 Cursor 叫醒

.cursorignore 能让文件不进索引,但 Cursor 底层的 VS Code 文件监听器仍然会在每次构建写入时触发,而这正是死循环不停的根源。把这些吵闹路径同样加进 Cursor Settings > settings.json 里的 files.watcherExclude(Cmd/Ctrl+Shift+P -> Preferences: Open User Settings (JSON)):

{
  "files.watcherExclude": {
    "**/node_modules/**": true,
    "**/dist/**": true,
    "**/.next/**": true,
    "**/.turbo/**": true,
    "**/.cache/**": true,
    "**/coverage/**": true
  }
}

在热重载项目上,真正终结重索引死循环的就是这一个设置。

Step 3:强制重建索引

改完 .cursorignore 不会自动重建。最快的办法是用命令面板(Cmd/Ctrl+Shift+P -> Reindex Codebase),或者从 UI 触发(这个菜单已经从旧的 “Features” 标签页改名了):

Cursor Settings > Indexing  ->  Resync Index

想连服务端状态一起清掉,就在同一面板上点 Delete Index,再点 Resync Index。对付特别顽固的死循环,最彻底的重置是退出 Cursor 后清掉本地 workspace 索引缓存:

# 先退出 Cursor,然后:
# macOS
rm -rf ~/Library/Application\ Support/Cursor/User/workspaceStorage/*/cursorIndex
# Linux
rm -rf ~/.config/Cursor/User/workspaceStorage/*/cursorIndex
# Windows (PowerShell)
Remove-Item -Recurse -Force "$env:APPDATA\Cursor\User\workspaceStorage\*\cursorIndex"
# 重新打开 Cursor

Step 4:把构建产物统一放进已忽略的目录

如果索引跑完了又被 dev server 触发重跑,确认你的工具链是把所有输出都写进已被忽略的目录,而不是散落在源代码旁边:

// vite.config.ts
export default {
  build: {
    outDir: 'dist',       // 被 **/dist/ 匹配
  },
  cacheDir: '.cache/vite', // 被 **/.cache/ 匹配
}

如何确认真的修好了

  1. 重启 Cursor 重新打开项目,状态栏先短暂显示 Indexing 0%
  2. 中型项目 1-3 分钟内到 Indexed;大型 monorepo 5-10 分钟。语义搜索约在 80% 时就开始可用,所以还没到 100% AI 就能用了别慌。
  3. 打开 Cursor Settings > Indexing & Docs > View included files(或看 Indexing 面板上的 “Files Indexed” 计数)。健康的中型项目大致在 500030000 个文件。还停在 100000+ 说明有 ignore 规则没匹配上,回头检查你的 glob。
  4. 启动 npm run dev,盯着状态栏看一分钟。如果 Indexing 不再冒出来,说明 watcher exclude 生效了。

预防建议

  • 创建仓库时就 commit .cursorignore,并和 .gitignore 同步维护。
  • build/缓存目录一律用 **/ 这样的 glob,才能连 monorepo 嵌套副本一起命中,而不只是根目录。
  • 把这些吵闹路径一次性写进 files.watcherExclude,之后就不用再管了。
  • 装新依赖、换构建工具、加代码生成器之后,主动点一次 Resync Index,别等死循环。
  • 想让某个目录退出搜索、但仍能按需读取时,用 .cursorindexingignore(而不是 .cursorignore)。

常见问题

Cursor 既然读 .gitignore,我还需要 .cursorignore 吗?node_modules 和 lockfile 来说通常不用,因为它们在内置默认列表里。你需要它来处理那些没被 gitignore、但仍然很大或很吵的东西(vendored 代码、生成的 SDK、你提交进仓库的构建目录),以及把敏感文件从 Agent 那里硬挡掉。

.cursorignore.cursorindexingignore 有什么区别? .cursorignore 会把文件挡在索引以及所有 AI 访问之外(Tab、Agent、@ 引用)。.cursorindexingignore 只把它移出索引;你仍然可以 @ 引用它或拖进 chat。遗留代码和偶尔要引用的 fixture 用后者。

为什么我每次保存文件索引就重启? 你的 dev server 在重写一个仍然被监听的 build/缓存目录。把该路径同时加进 .cursorignorefiles.watcherExclude(Step 2)。真正能停掉叫醒的是 watcherExclude 那一条。

哪怕小仓库也卡在 Setting up indexing...,怎么办? 这几乎都是服务端问题,不是你的文件。看 status.cursor.com,确认你已登录,再看 Cursor Settings > General > Privacy,因为旧的 Privacy Mode 设置会挡住 embeddings 上传。然后 Delete IndexResync Index

“Files Indexed” 应该显示多少? 一个典型应用大致在 500030000。如果你看到 100000+,说明有 ignore 规则没匹配上,通常是一个裸的 dist/ 没能命中 monorepo 的嵌套副本。换成 **/dist/

相关阅读

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