DEV Community

DaymondHyper
DaymondHyper

Posted on

Cursor Rules: How to Stop Your AI Agent From Writing Slop

You just installed Cursor, opened a TypeScript file, and asked the agent to fix a bug. Ten seconds later it handed you a type SomeType = any and a @ts-ignore above the line that wouldn't compile. This is the moment most developers discover that AI coding agents are powerful but undisciplined.

The fix isn't a better model. It's rules.

Most AI coding agent best practices boil down to a single idea: tell the agent what good looks like before it starts typing. Cursor lets you define rules files in .cursor/rules/ that load alongside your project context and tell the agent how to behave. Claude Code has its own rules system, Windsurf has a rules directory, Copilot reads .github/copilot-instructions.md. Learn to configure cursor rules properly and your agent starts behaving like a careful senior engineer instead of an eager intern.

What cursor rules files are

Cursor rules are markdown files with a .mdc extension stored in .cursor/rules/ at your project root. Each file is a set of instructions the agent reads before it starts working. When a rule's conditions match the file being edited, the instruction is injected into the model's context window.

A cursor rules file has two parts: a YAML frontmatter block between --- markers, and a markdown body with the actual instructions.

How to configure cursor rules: the frontmatter fields

Three fields matter.

description (required). A short summary of what the rule enforces. Cursor surfaces this when you toggle rules, so make it specific.

globs (optional). File patterns the rule applies to. Without globs, the rule applies to everything, which wastes context and creates conflicts.

alwaysApply (optional). Set to true for rules that should load in every session, regardless of the files involved. Leave it false for rules that only trigger when matching files are touched.

Real example:

---
description: Enforce strict TypeScript, no any, no ts-ignore
globs: **/*.{ts,tsx}
alwaysApply: false
---

# Strict TypeScript

## Context
This codebase runs with `strict: true`. Using `any` disables the type system
for that value and lets bugs through at runtime. Banned types are a team
decision, not a style preference.

## Requirements
- Give function parameters and return values explicit types.
- Use `unknown` instead of `any` when a value's shape is not known yet, then
  narrow it with a type guard before using it.
- Model state with discriminated unions rather than long optional chaining
  chains.

## Anti-Patterns
-`any` type annotations. Use `unknown` and narrow.
-`@ts-ignore` or `@ts-expect-error` to silence the compiler. Fix the type.
-`as any` casts to force a value through.
Enter fullscreen mode Exit fullscreen mode

Note the structure of the body: Context, Requirements, Anti-Patterns. That pattern is the heart of what makes a rule actually work, and I'll come back to it below.

AGENTS.md and CLAUDE.md: how they differ from .cursor/rules

A few related standards do similar jobs, and the naming confuses everyone.

AGENTS.md is a cross-tool standard: a single markdown file at the repository root that documents how an agent should work in that repo. Think of it as a README written for AI agents. It covers build commands, project conventions, and "don't touch X". Because it's a plain file at the root, any tool that follows the spec picks it up.

CLAUDE.md is Claude Code's project memory file. The claude code rules system reads it at startup, along with files it references, and treats it as long-term context for the session. Older projects use a CLAUDE/ folder with sub-files; a single root file is the current recommendation.

.cursor/rules files differ from both in one important way: they are scoped and conditional. AGENTS.md and CLAUDE.md are read as context in full. Cursor rules only load when their globs and settings match the current task. That makes them the right tool for file-type-specific discipline, while AGENTS.md is the right tool for repo-wide onboarding information.

A pragmatic setup: put repository facts (commands, architecture, conventions) in AGENTS.md, then layer scoped rules on top for things like "TypeScript files never get any" or "every new API endpoint needs input validation". The tools are converging on the same idea under different filenames.

Common mistakes that make cursor rules useless

Most rule files fail for one of four reasons.

1. Rules are too vague. "Write clean code" is not a rule, it's a wish. The agent has no operational definition of "clean", so it ignores the instruction. Good rules name the exact behavior: "no any", "every function has an explicit return type".

2. No anti-patterns. Telling the agent what to do is only half of it. Models pattern-match strongly on negative examples. A rule that says "validate all input" but never shows what an invalid attempt looks like produces validation that is decorative. List the exact things you never want to see, with examples, and the agent stops producing them.

3. No globs. A rule about API endpoints that applies to every file gets loaded constantly and starts conflicting with other rules. Scope rules to the files they govern. A testing rule probably belongs on **/*.test.{ts,tsx}, not **/*.

4. Nobody checks that the agent obeyed. Cursor lets you toggle rules in the chat composer, and @-mentioning a rule or file forces it into context for a conversation. If a rule matters for a task, mention it explicitly instead of trusting that the model read all seventeen rule files. Then verify: review the diff for any and @ts-ignore, and send it back if the agent slipped.

There's also a context budget problem. Every rule that loads costs tokens. Twenty vague rules crowd out real project context and make the agent worse, not better. Prefer a few sharp rules over a pile of aspirational ones.

Copy-pasteable cursor rules examples

These work in Cursor's .cursor/rules. The structure transfers to any tool that reads markdown rules. Adjust the globs to your stack.

1. TypeScript strictness

---
description: No any, no ts-ignore in TypeScript files
globs: **/*.{ts,tsx}
alwaysApply: true
---

# Strict TypeScript

## Context
The project compiles with `strict: true`. Any, ts-ignore and unsafe casts are
banned because they disable compile-time checks and move failures to runtime.

## Requirements
- Write explicit types for parameters, return values, and exported variables.
- Reach for `unknown` when a value arrives from an untyped boundary, then
  narrow it with a type guard.
- Handle `null` and `undefined` explicitly. Prefer early returns over `!`.

## Anti-Patterns
-`any`, `as any`, or `as unknown as SomeType`.
-`@ts-ignore` and `@ts-expect-error`.
- ❌ Non-null assertion `!` on values that can actually be null.
Enter fullscreen mode Exit fullscreen mode

2. Tests that prove behavior

---
description: Test files must assert behavior, not cover lines
globs: "**/*.{test,spec}.{ts,tsx,js,jsx}"
alwaysApply: false
---

# Behavioral tests

## Context
Coverage percentages measure nothing if the assertions don't. A passing test
suite that never fails is a treadmill. Each test should demonstrate one
behavior.

## Requirements
- One behavior per test. Name it like `should reject expired tokens`.
- Assert on observable outcomes, not on how the code is wired internally.
- Follow arrange, act, assert, and keep the act section to one call.

## Anti-Patterns
- ❌ Tests with no assertions, or `expect(true).toBe(true)`.
- ❌ Mocking the system under test so heavily that the test proves nothing.
- ❌ Snapshot-only tests that would pass with obviously wrong output.
Enter fullscreen mode Exit fullscreen mode

3. Security at every boundary

---
description: Validate input and avoid common security traps
globs: "**/*.{ts,tsx,js,jsx,py,go}"
alwaysApply: false
---

# Security guardrails

## Context
Every input is untrusted until proven otherwise. The cheapest vulnerability is
the one you never ship, so apply these checks to new code automatically.

## Requirements
- Validate and sanitize all input at the service boundary, before use.
- Use parameterized queries or an ORM for any database access. Never build SQL
  by string concatenation.
- Escape output in HTML templates. Prefer the framework's built-in escaping.
- Redact secrets and tokens in logs. Never log request bodies wholesale.

## Anti-Patterns
- ❌ SQL assembled with template strings containing user input.
- ❌ Storing secrets in source code or config files that get committed.
- ❌ Trusting client-supplied values for authorization decisions.
Enter fullscreen mode Exit fullscreen mode

The full rule set and where to get it

The examples above are the foundation. Once you see the pattern, you'll want rules for more of your stack: frontend conventions, backend API design, database migrations, code review workflows, and project templates that ship with rules already wired in.

That's what AgentForge is: a pack of 24 opinionated, machine-readable rule files across seven categories (general, frontend, backend, testing, AI agents, DevOps, database). Every file uses the frontmatter format and Context / Requirements / Anti-Patterns structure shown above, with accurate globs and alwaysApply settings so rules load only when relevant. It comes with a one-command installer and project templates, and it's a one-time purchase: US$29 (A$45) at https://dedyclan.gumroad.com/l/agentforge.

Not sure the structure fits your workflow? A free sample pack with three rule files is available on the same page. Drop them into .cursor/rules/ and feel the difference on your next task.

FAQ

What is a cursor rules file?

A cursor rules file is a markdown document with the .mdc extension placed in the .cursor/rules/ directory. It contains YAML frontmatter (description, globs, alwaysApply) and a markdown body of instructions. Cursor injects the body into the agent's context when the file being worked on matches the rule's globs, or whenever the rule has alwaysApply: true.

How do I get Cursor to follow my rules?

Three things help. First, make rules specific with explicit anti-patterns, because vague instructions are easy to ignore. Second, @-mention the rule or file in the chat composer so it's forced into context for that conversation. Third, verify: review the agent's diff for banned patterns like any or @ts-ignore and send the change back until it complies.

What's the difference between AGENTS.md and .cursor/rules?

AGENTS.md is a plain markdown file at the repository root that any standards-compliant agent tool reads as context. It's best for repo-wide facts: build commands, architecture, conventions. .cursor/rules files are conditional and scoped by globs, which makes them better for fine-grained discipline like "no any in TypeScript files". The two complement each other.

Top comments (2)

Collapse
 
nyx533 profile image
Nyx533

@daymondhyper The root cause of slop is not bad prompting. It is that the agent has no stake in the output it produces. A human who writes slop for a living gets fired. An agent that writes slop for a living costs another inference cycle. The cursor rules are a good bandage but the fix is a feedback loop that makes writing slop more expensive for the agent than writing well. A cost signal that penalises repeated patterns in the output would get further than any style guide.

Collapse
 
officialmailkr profile image
오피셜메일

AGENTS.md에는 저장소 전체의 사실과 경계를 두고, 파일 유형별 규칙은 globs로 좁혀야 한다는 구분이 특히 실용적입니다. Context·Requirements·Anti-Patterns 구조가 모호한 ‘클린 코드’ 지시를 검증 가능한 기준으로 바꾸는 점도 좋았습니다.