Skip to main content

Claude/ChatGPT (or Cursor) Prompt to Set Project Rules So AI Matches Your Codebase

Set Cursor/Claude project rules — architecture, naming, errors, testing, style — so AI-generated code fits your codebase without manual correction.

Fill in the placeholders

Edit the values, then copy your finished prompt.

Your Prompt
prompt.txt
You are a senior TypeScript engineer writing the project-rules file that guides an AI coding assistant. Specify it tightly enough to build against — return real config and code, not vague guidance.

Context:
- Language: TypeScript
- Project: full-stack multi-tenant SaaS application
- Architecture: Clean Architecture with domain-driven design
- Error handling: Result type pattern, no throwing in the domain layer

Deliverables:
1. A rules block covering architecture boundaries, folder layout, and where new code belongs.
2. Naming conventions for variables, types, files, and constants — with examples.
3. The error-handling contract (throw vs Result, logging, user-facing messages).
4. A testing policy: what gets unit, integration, and E2E coverage, and the assertion style.
5. Style and typing rules (lint, formatter, strictness) plus three anti-patterns to reject.
6. A worked example: take one small task and show code that obeys every rule above.

Output as a ready-to-commit `.cursorrules` / project-rules file, fenced and copy-ready.

What this prompt does

This prompt establishes a persistent "project context header" inside Cursor AI, replacing the default generic assistant behavior with one that knows your specific conventions. By front-loading architecture, naming, error handling, testing approach, and style rules as structured fields, the model has unambiguous constraints before it sees your task — meaning it cannot silently guess at your conventions and guess wrong.

The template works because it separates rules from task. Most developers dump both into one instruction and get a response that follows neither well. Here, the bracketed variables become a configuration layer. Cursor processes the full prompt context before generating, so these rules apply with equal weight to every line it writes.

The result is code that already matches your pull-request standards on the first attempt — not after two rounds of review comments about naming or missing error boundaries.

When to use it

  • Setting up Cursor AI on a new project where the team needs consistent output from day one.
  • Onboarding a contractor who will use Cursor but should not need to learn your codebase conventions manually.
  • Working in a monorepo where different packages use different typing approaches (strict TypeScript vs. JSDoc, for example).
  • After a refactor that changed your architecture or naming conventions — update the rules block and all subsequent Cursor suggestions follow the new standard.
  • Any task where you have been frustrated by Cursor ignoring your existing patterns (class-based vs. functional components, dependency injection style, etc.).

Example output

Given these variables: language = TypeScript, project_type = NestJS REST API, architecture = repository pattern, no logic in controllers, naming = camelCase methods, PascalCase classes, kebab-case files, error_strategy = throw domain exceptions, catch only at the HTTP layer, test_approach = Jest unit tests, mock repositories, style_rules = no any, strict null checks, typing_approach = explicit return types on all public methods, task = create a UserService.createUser method:

// user.service.ts
import { Injectable } from '@nestjs/common';
import { UserRepository } from './user.repository';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from './user.entity';
import { UserAlreadyExistsException } from './exceptions/user-already-exists.exception';

@Injectable()
export class UserService {
  constructor(private readonly userRepository: UserRepository) {}

  async createUser(dto: CreateUserDto): Promise<User> {
    const existing = await this.userRepository.findByEmail(dto.email);
    if (existing) {
      throw new UserAlreadyExistsException(dto.email);
    }
    return this.userRepository.create(dto);
  }
}

No any, domain exception thrown (not a generic Error), logic kept out of the controller — exactly what the rules specified.

Pro tips

  • Fill every variable, even briefly. An empty [additional_rules] tells Cursor nothing; write none explicitly so it does not invent rules to fill the gap.
  • Match your [architecture] field to your actual folder structure. Saying "repository pattern" works only if you also mention where repositories live — otherwise Cursor may generate the right pattern in the wrong place.
  • Use [composition_preference] to resolve the biggest source of inconsistency. "Prefer composition over inheritance" or "prefer hooks over HOCs" eliminates the most common style disagreements in generated code.
  • Version-control the filled template. Store the completed prompt (with your actual conventions substituted) in a .cursor/rules/ directory (Cursor 0.43+) or in your team wiki. When conventions change, update one file and everyone's Cursor context stays current.
  • Chain this with a .cursorrules file for persistent context. This prompt works best for one-off task invocations; for always-on rules, paste the filled architecture/naming/style sections into .cursorrules at project root so Cursor loads them automatically on every file open.

Frequently Asked Questions

Do I need to re-paste this prompt every time I ask Cursor a question?
For one-off tasks, yes — paste the filled template before each request. For persistent enforcement, copy the architecture, naming, and style fields into a `.cursorrules` file at your project root (or a `.cursor/rules/*.mdc` file if you are on Cursor 0.43 or later). Cursor loads that file automatically, so you only need to paste the `[task]` portion each time.
What happens if I leave some bracketed variables blank or vague?
Cursor fills the gap with its own defaults, which may not match your codebase. If you have no preference for a field (say, composition vs. inheritance), write 'no preference' explicitly — this tells the model the field was considered, not forgotten, and prevents it from making an arbitrary choice.
Can I use this prompt for languages other than TypeScript or Python?
Yes — the template is language-agnostic. The `[language]` and `[typing_approach]` variables are the only language-specific fields. For PHP with strict types you might write: language = PHP 8.3, typing_approach = strict types declaration + return type hints on all methods. The architecture and naming fields work identically regardless of language.
Engr Mejba Ahmed

Need this built for real?

Engr Mejba Ahmed

AI Developer · Software Engineer

I'm Mejba — I design and ship production AI systems, automations, and full-stack apps. If you want this turned into a working solution for your team, let's talk.

More in AI Coding Assistants

Engr Mejba Ahmed

Engr Mejba Ahmed

AI assistant · trained on my work

👋

Hey there!

Quick Actions

WhatsApp Direct line to me

Chat on WhatsApp

+880 1723 741224 · Replies within the hour on working days

Popular Questions

Engr Mejba Ahmed is connected
Engr Mejba Ahmed is typing...
Engr Mejba Ahmed avatar

✉ Want me to follow up? Drop your email

Engr Mejba Ahmed avatar

📞 Connect Directly

Choose how you'd like to reach me

WhatsApp

+880 1723 741224

Email

mejba.13@gmail.com

✓ Details sent! I'll get back to you shortly.

Powered by OpenAI

335+

Blog Posts

25

AI Courses

63

Projects

Services & Expertise

Pricing & Process

Learning & Resources

Connect & Support