Most Cursor users type requests the way they'd type a Google search. "Fix the bug in auth.ts." "Add a dark mode toggle." "Refactor the database layer." Then they're surprised when the output is a mess.
Cursor is not a search engine. It's a code generation system that responds to the quality of your instructions. The gap between a mediocre Cursor user and a great one isn't AI knowledge — it's prompting discipline.
Here's what actually changes your output quality.
The three modes and why they need different prompt styles
Cursor has three distinct interaction modes, and most people use only one of them for everything. That's a mistake. Each mode works differently and needs a different type of prompt.
Cmd+K (inline edit) — triggers a quick edit on selected code. The model sees your selection and your instruction. It's fast and scoped. Best for: targeted, single-purpose changes.
Chat panel — conversational interface with full project context. The model can see files you @ mention and files it's indexed. Best for: exploration, planning, understanding, multi-step tasks.
Agent mode — autonomous execution. The model can read files, write files, run terminal commands, and iterate. Best for: implementing a full feature, migrating code, writing test suites.
Using Agent mode for a one-line fix wastes time. Using Cmd+K to plan a full architecture doesn't give the model enough context. Match the mode to the task.
Cmd+K prompting: scope is everything
When you open the Cmd+K bar, you're working with a targeted model that sees your selection and nothing else (unless you've injected context). The most important thing you can do: be explicit about what to change and what to leave alone.
Weak prompt:
Fix the auth middleware
Strong prompt:
Refactor the token validation logic in this middleware to use the new verifyJWT helper. Do not change the error handling, the request/response types, or any logic outside of the token validation block.
The "do not change" constraint is the most underused part of Cmd+K prompting. Cursor will happily refactor things you didn't ask it to touch if you don't draw a boundary. Every time I've had Cursor break something I wasn't trying to change, it was because I didn't tell it what was off limits.
Other Cmd+K patterns that work:
- "Add JSDoc comments to each function. Don't change any logic."
- "Convert this callback-based function to async/await. Keep the same error handling behavior."
- "Extract this validation block into a separate function called
validateUserInput. Call it from where the validation currently is." - "Replace all
vardeclarations withconstorletas appropriate. Don't change anything else."
Short, scoped, explicit about what stays the same. That's the pattern.
Chat panel prompting: always include file context
The chat panel is where most people waste the most tokens and get the worst results. They ask Cursor questions without giving it context, then wonder why the answer doesn't fit their codebase.
The first habit to build: always @ mention the specific files relevant to your question.
Weak:
How should I add rate limiting to my API?
Strong:
Looking at @server.ts and @middleware/auth.ts — I want to add rate limiting to all routes under /api/v1. The auth middleware already runs before route handlers. Where should I add rate limiting, and what library would fit best given this setup?
The second prompt gives Cursor your actual code to reason about. The answer will be specific to your project, not a generic explanation of rate limiting that you then have to translate.
Second habit: frame questions as decisions, not searches. Instead of "what is the best way to handle this", ask "given this constraint, should I do X or Y, and why." Cursor gives better output when the decision space is bounded.
Third habit: ask for a plan before execution. For anything more than 5 lines of change, ask Cursor to outline its approach first. Read the plan. If it looks wrong, correct it before it writes 200 lines in the wrong direction.
Before writing any code: outline your approach to migrating these three API handlers from REST to GraphQL. List the files you'll touch, the changes you'll make to each, and any risks. Wait for my approval before writing code.
That last line — "wait for my approval" — is one of the most useful patterns in the chat panel.
Agent mode: write a task brief, not a question
Agent mode is the most powerful and the most commonly misused. People open Agent mode and type questions. Questions don't work well in Agent mode.
Agent mode is designed to execute tasks. Give it a task brief:
Weak:
Can you help me add user authentication to this Next.js app?
Strong:
Goal: Add email/password authentication to this Next.js 15 app using NextAuth.js v5.
Starting state: The app has no auth. User routes are currently unprotected. There's a PostgreSQL database configured in @lib/db.ts.
What to build:
- Sign up page at /auth/signup (email, password, confirm password)
- Sign in page at /auth/signin
- Protect all routes under /dashboard
- Store users in the existing DB using the User model in @lib/schema.ts
Constraints:
- Do not install any additional UI libraries — use the existing shadcn/ui components
- Do not change any existing route logic outside of adding the auth check
- Use bcrypt for password hashing
Do not start executing until you've confirmed you understand the scope.
That last line is critical for complex tasks. Without it, Agent mode will start modifying files immediately, and if it misunderstood your scope you'll have a mess to undo.
The structure of a good Agent mode prompt: Goal → Starting state → What to build → Constraints → Confirmation step.
The .cursorrules file: persistent instructions for your whole project
.cursorrules is a file you create in your project root. Cursor reads it on every prompt across all modes. Think of it as your project-level system prompt.
Most people either don't know it exists or put vague platitudes in it ("write clean code", "follow best practices"). Those do nothing.
What actually belongs in .cursorrules:
Tech stack specifics:
Stack: Next.js 15, TypeScript (strict mode), Tailwind CSS v4, Drizzle ORM with PostgreSQL.
Do not use Prisma. Do not use class components. Do not install additional UI libraries.
Code style rules your linter doesn't catch:
- All async functions must have explicit error handling (try/catch or .catch())
- Never use 'any' type in TypeScript — use 'unknown' or define proper types
- All database queries go through the repository layer in /lib/db/
- Component files: one component per file, filename matches component name
What not to touch:
Do not modify the /lib/auth.ts file — it's maintained separately.
Do not change the database schema files — migrations are managed manually.
How to respond:
When suggesting multi-file changes: list all affected files before writing code.
When fixing bugs: explain the root cause in one sentence before showing the fix.
A well-maintained .cursorrules file is worth more than any individual prompting trick. Every prompt you send benefits from it.
Context injection beyond @ mentions
Most Cursor users know about @ mentions. Fewer know about these:
@Docs — you can add documentation URLs to Cursor's context. If you're working with a library and want Cursor to reference the actual current docs, add the doc URL. Cursor will fetch and index it.
@Web — lets Cursor search the web for current information. Useful when you're working with APIs that have changed recently or want to check for library updates.
Codebase indexing — Cursor indexes your whole codebase. You can ask questions about patterns across files without @ mentioning every file. "Where in the codebase do we handle rate limit errors?" It'll find the relevant code.
Pasting snippets directly — for external code (a Stack Overflow answer, a library example, documentation snippet), paste it directly into the chat rather than asking Cursor to look it up. You control what context it sees.
What not to do
Don't ask for wholesale rewrites. "Refactor the entire authentication system" is the worst type of prompt. Break it into specific, small tasks. Cursor can execute large tasks, but it can't plan well-scoped large tasks from a vague instruction.
Don't ignore Cursor's questions. When Agent mode asks a clarifying question, answer it precisely. It's flagging uncertainty about scope. If you wave it off ("just do what you think is best"), you'll get a scope that doesn't match what you wanted.
Don't skip the review step. Cursor generates plausible-looking code that's sometimes wrong. Review every change before accepting, especially in Agent mode where it's modified multiple files. The diff view exists for a reason.
Don't fight the model on file structure. If you have specific opinions about where code should live, put them in .cursorrules. Correcting file structure after the fact costs more time than specifying it upfront.
A worked example: migrating an Express API to Fastify
Here's the sequence of prompts I used to migrate a 12-endpoint Express API to Fastify. The whole thing took about 90 minutes, most of which was review.
Prompt 1 (Chat panel — planning):
Looking at @server.ts, @routes/, and @middleware/ — I want to migrate from Express to Fastify. Before writing any code, give me a migration plan: what changes in each file, what Express patterns don't have direct Fastify equivalents, and what I should handle manually. List risks.
Prompt 2 (after reviewing the plan):
The plan looks good. Start with the server setup in @server.ts only. Migrate to Fastify, keep the same port configuration, keep the same middleware order. Do not touch any route files yet.
Prompts 3-8 (repeated for each route group):
Now migrate @routes/users.ts only. The Fastify server instance is set up in the updated server.ts. Use Fastify's schema validation instead of express-validator — the schema for each endpoint should match the existing validation logic exactly.
Final prompt:
Review @server.ts and all files in @routes/ — check that all routes are registered correctly, that error handling is consistent with Fastify patterns, and that no Express imports remain. List anything that looks wrong.
One migration, eight focused prompts. Each one scoped to a specific file or concern. No wholesale "rewrite everything" prompt anywhere.
For more on AI coding workflows, see the vibe coding prompting guide. For patterns that work across different AI coding tools, check the 50 vibe coding prompts post.



