You ask Composer for a date-formatting helper. It hands you function formatDate(d) {...}. Open src/utils/date.ts: a mature formatDate already lives there with timezone and locale handling. The model isn’t lazy — its embedding retrieval just didn’t surface the existing helper, so it “honestly” believed the repo had none and rewrote one.
Fix from both sides: get the model to look first, and make the existing helpers easier to find.
Common causes
1. Existing helper not in retrieval context
Repo has 10k files; Composer retrieval pulls top-K chunks. src/utils/date.ts didn’t match the prompt’s keywords well enough to make it in.
How to judge: ask “List every file you read” — if src/utils/date.ts isn’t there, it wasn’t seen.
2. Helper buried in a non-obvious path
packages/feature-a/internal/helpers/date.ts ranks low and the generic filename date.ts repeats everywhere.
How to judge: rg "function formatDate" yourself — count hits and locations.
3. Naming style mismatch
Your repo uses toLocaleDateString / renderDate / displayDate; the model defaults to formatDate. Semantic search partially helps but ranking stays low.
How to judge: list your repo’s actual naming vs the model’s proposal — style gap.
4. No centralized utility index
No src/utils/README.md, no .cursorrules listing helpers. The model has to discover from scratch every time.
How to judge: ls src/utils/ — no README.md / index.ts entry point.
5. Monorepo already has duplicates
The model isn’t only producing duplicates; the repo has multiple `formatDate’s already. The model sees one and adds a third.
How to judge: rg "function formatDate" --type ts — count occurrences in the repo.
6. Agent didn’t grep before writing
Agent mode has grep_search but the model doesn’t always call it. It writes first, searches never.
How to judge: Composer message tool-call chain has no grep_search / read_file step.
Before you start
- Identify which entry point: Composer / Cmd+K / chat. Cmd+K doesn’t do repo-wide search and will always duplicate.
- Commit before reproducing so later helper consolidation stays tracked.
- Note Cursor version and active model — opus is more inclined to grep first than sonnet.
Info to collect
- Full text of the new helper Composer produced.
- Paths + implementations of existing similar helpers in your repo.
- Screenshot of the message’s tool-call chain.
- Full
.cursorrules(any “reuse first” clause?). - Layout of
src/utils/or your equivalent.
Shortest fix path
“Fix this instance → systemic discoverability” order.
Step 1: Make the model grep before writing
Prompt template:
Before writing new code:
1. Use grep_search to find existing functions related to "date formatting" / "format date" / "render date".
2. List what you found with paths.
3. If a suitable one exists, use it and explain why.
4. Only if nothing suitable exists, write new — and propose a location.
Step 2: Utility README + index.ts as a manifest
src/utils/README.md:
# Utilities
| Function | File | Purpose |
|---|---|---|
| formatDate | date.ts | Locale-aware date formatting |
| formatCurrency | money.ts | Currency formatting with i18n |
| parseInvoice | invoice.ts | Parse invoice payloads |
| isValidEmail | validation.ts | RFC-5322 email check |
Then @src/utils/README.md in relevant Composer prompts so the model sees the manifest.
Step 3: Reuse rule in .cursorrules
# .cursorrules
- Before writing a new utility function, search src/utils/ and packages/shared/ for existing implementations.
- Common helpers live in:
- src/utils/date.ts (date / time formatting)
- src/utils/money.ts (currency)
- src/utils/validation.ts (input validation)
- If you must create a new helper, place it in src/utils/ and update src/utils/README.md.
- DO NOT inline helpers inside component files; extract to src/utils/.
Step 4: Push back when you see duplication
You wrote a new `formatDate` function, but src/utils/date.ts already has one with timezone handling.
Replace your implementation with `import { formatDate } from "@/utils/date"`.
Update any other duplicated helpers similarly.
Step 5: Repo-wide dedupe sweep
# Find functions with similar names
rg "^(export )?(async )?function (\w+)" --type ts -o -r '$3' | sort | uniq -c | sort -rn | head -30
# Find specifically formatDate duplicates
rg "function formatDate" --type ts
Consolidate to a single source; update call sites. Future AI then has one anchor instead of three.
Step 6: Give important helpers unique names
formatDate is too generic. Use formatInvoiceDate / formatRelativeDate / formatLocaleDate — retrieval precision goes up and the model is less likely to rewrite a same-name variant.
How to verify the fix
- Rerun the same prompt; the model should grep first, then speak — not write blind.
- Diff has noticeably fewer new helpers.
- A teammate running the same prompt sees the same behavior — confirms rules in team config.
If it still fails
- Reduce prompt to just “is there an X helper?” — verify the model can find it.
- Roll back the latest
.cursorruleschange to see which rule was working. - Search forum.cursor.com for “duplicate helper composer”; include prompt + tool-call screenshots.
- Grab View → Output → Cursor agent logs and post to Bug Reports.
Prevention
- One
utils/per package + README manifest — give the model an explicit entry point. .cursorrulesenforces “reuse first” + lists main helper paths.- Name important helpers with domain signal (
formatInvoiceDatebeatsformatDate). - PR review checklist: “Is this reusing the existing helper?” — block duplication.
- Quarterly utility dedupe sweep: merge near-duplicates, update README.