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 fi

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. 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. 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. 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. 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. 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. These work in Cursor's .cursor/rules. The structure transfers to any tool that reads markdown rules. Adjust the globs to your stack. --- 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. --- 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. --- 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. 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. 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. 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. 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.
Key Takeaways
- •You just installed Cursor, opened a TypeScript file, and asked the agent to fix a bug
- •This story was reported by Dev.to, covering developments in the dev space.
- •AI advancements continue to reshape industries — read the full article on Dev.to for complete coverage.
📖 Continue reading the full article:
Read Full Article on Dev.to →


