You ask Cursor to “replace every useAuth with useSession across the project,” it touches 3 files, and grep -r useAuth src/ still shows 17 hits. That’s not laziness — Cursor literally cannot “see” the other 17 files. They were excluded from the codebase index, the workspace was opened at the wrong level, or your prompt never pointed at the right directory. Cursor (and Windsurf, Cline, and other IDE-embedded agents) can only edit files that are both in the codebase index and in the chat’s context window. Miss either and the file is invisible. This article walks through diagnosis and the exact commands to bring the full project back into Cursor’s view.
Common causes
Ordered by hit rate, highest first.
1. Indexing excluded critical directories
Cursor’s codebase index is shaped by three layers: .gitignore, .cursorignore, and Cursor’s own heuristics (huge files, dot-directories). The classic miss is .gitignore excluding src/packages/ or apps/web/ (a habit from older monorepo setups), which silently removes them from the index too.
How to spot it: In Cursor, Cmd+Shift+P → “Cursor Settings” → Features → Codebase Indexing. Compare “Files Indexed” against git ls-files | wc -l. A gap larger than 30% means lots of files are being skipped.
2. Workspace opened at the wrong level
You opened Cursor in ~/projects/myapp/frontend/, but the backend and shared types live in ~/projects/myapp/backend/ and ~/projects/myapp/shared/. Cursor only indexes the current workspace root, so cross-package refactors silently miss the other halves.
How to spot it: Look at the workspace name in the lower-left. If it’s a subdirectory rather than the monorepo root, you’re hitting this.
3. The prompt never pinned the files
Even with a complete index, on long chats Cursor selects context by relevance + token budget. Abstract prompts like “refactor the auth flow” rely on embedding retrieval, which skips files with non-obvious names or thin docstrings.
How to spot it: Expand the “Context used” panel at the bottom of Cursor’s reply. It lists which files were actually read. Diff that against the files you expected to change.
4. .gitignore hides files the agent still needs
Things like src/generated/ or prisma/client/ are build outputs but semantically critical (the agent needs them to know field names). Once they’re in .gitignore they’re also out of the index, and the agent ends up guessing types.
How to spot it: grep -r "from.*generated" src/. If the code imports heavily from a generated dir but .gitignore excludes it, Cursor can’t see it.
5. Huge files are auto-skipped
Cursor skips files > 500KB or > 5000 lines by default (to keep retrieval fast). If your schema, migration file, or i18n dictionary is a single giant file, the agent will behave as if it doesn’t exist.
How to spot it: find src -type f -size +500k. Paste any hits into the chat and ask Cursor “do you see this file in your index?”
Shortest path to fix
Ordered by ROI. The first three usually solve it.
Step 1: Audit the codebase index scope
Open Cursor → Cmd+Shift+P → “Cursor Settings” → Features → Codebase Indexing. Check:
- Files Indexed: compare to
git ls-files | wc -l - Status: must be Synced (not Indexing, not Stale)
- Ignore Rules: shows the effective
.gitignore+.cursorignore
If Files Indexed is too low, temporarily comment out suspect ignore lines and click “Resync Index”. You can also force a clean rebuild from the shell:
# Quit Cursor, then wipe the workspace index cache
rm -rf ~/Library/Application\ Support/Cursor/User/workspaceStorage/*/cursorIndex
# Relaunch Cursor — it will rebuild from zero
Step 2: Reopen the workspace at the monorepo root
If you opened a subdirectory, use File → Open Folder and pick the root. Or from the shell:
cd ~/projects/myapp # monorepo root
cursor . # relaunch Cursor at the root
After reopening, type @Folder frontend and @Folder backend in chat to verify both halves are reachable.
Step 3: Pin files and folders explicitly in the prompt
Don’t write “modify the auth logic.” Write:
@File src/lib/auth.ts @File src/hooks/useAuth.ts @Folder src/components/auth
Replace useAuth with useSession. Only modify the files I @-ed plus
their direct callers. After you finish, list every file path you changed.
@File / @Folder / @Symbol are hard pins — much more reliable than embedding retrieval. Claude Code users do the equivalent by hard-coding “always read these files first” in CLAUDE.md.
Step 4: Maintain a lean .cursorignore
.cursorignore syntax matches .gitignore but only affects Cursor. A reasonable template:
# Build output
dist/
build/
.next/
.astro/
out/
# Dependencies
node_modules/
vendor/
# Caches and logs
.cache/
.turbo/
*.log
coverage/
# Large generated data (case-by-case)
**/*.snap
public/locales/*.json
Do not include src/generated/ or similar semantically-required directories — the agent needs them to reason about types.
Step 5: Split files that exceed the index threshold
If a file is > 5000 lines, the agent will almost always skip it. Split i18n bundles, schemas, and route tables by module:
# Example: split a 40k-line src/i18n/en.json by namespace
node scripts/split-i18n.mjs src/i18n/en.json src/i18n/en/
Update imports, then Resync Index.
Prevention
- Keep a minimal
.cursorignoreat the repo root — only build output and dependencies, neversrc/generated/, schemas, or migrations - Before a large change, write “files I expect to be touched: a.ts, b.ts, c.ts” and have the agent confirm it can @-reach all of them
- For monorepos, always open Cursor at the repo root and scope work with
@Folder packages/fooinstead of opening a subpackage - Split any single file over 3000 lines; for files that genuinely can’t be split (like auto-generated schemas), call them out in CLAUDE.md /
.cursorrules - After a major dependency upgrade or directory reshuffle, manually click “Resync Codebase Index” instead of waiting for Cursor to notice