Chapter 17: AGENTS.md and Cursor Rules
Learning Objectives
By the end of this chapter, you will be able to:
- Explain AGENTS.md as the project-level instruction manual for AI agents
- Structure AGENTS.md with dos/don'ts, conventions, and file patterns
- Apply AGENTS.md hierarchy in monorepos with nested files
- Understand AGENTS.md loading behavior and platform differences
- Master Cursor Rules: .md and .mdc formats with YAML frontmatter
- Configure the four activation modes: Always Apply, Apply Intelligently, Apply to Specific Files, Apply Manually
- Organize rules for TypeScript, testing, and API patterns
- Distinguish .cursorrules (legacy) from .cursor/rules/
- Apply the decision framework: AGENTS.md vs rules vs skills
- Create a complete AI configuration: AGENTS.md + .cursor/rules/
- Apply best practices: rule length, avoiding duplication, specific guidance
AGENTS.md: The Project-Level Instruction Manual
AGENTS.md is a file at the project root that provides AI agents with project-level context. In many agent runtimes, AGENTS.md is loaded by default for most prompts; in others, loading can be scoped or conditional. Treat it as the first place agents look to understand your project, but verify behavior in your tooling.
Think of AGENTS.md as the onboarding document for AI: when an agent starts working on your codebase, it reads AGENTS.md to learn your conventions, architecture, and expectations. It answers: What stack do we use? What patterns do we follow? What must we never do?
Placement
AGENTS.md lives in the project root directory:
my-project/
├── AGENTS.md ← Here
├── src/
├── specs/
├── package.json
└── ...
Some platforms also support AGENTS.md in subdirectories for monorepo hierarchy (covered below).
Structure
AGENTS.md is plain Markdown. No YAML frontmatter. Structure it with clear sections:
# [Project Name] — AI Agent Guidelines
## Overview
[1-2 paragraphs: what this project is, its purpose, key technologies]
## Coding Standards
[Dos and don'ts, style, patterns]
## Architecture
[Layers, boundaries, conventions]
## File Patterns
[Where things go, naming conventions]
## Testing Conventions
[How to test, what to test, frameworks]
## Error Handling
[Patterns, logging, user-facing messages]
## Do Not
[Explicit anti-patterns, things to avoid]
## References
[Links to detailed docs, specs, constraints]
What to Include
| Section | Content |
|---|---|
| Overview | Project purpose, stack, key decisions |
| Coding Standards | Style (naming, formatting), patterns (error handling, async) |
| Architecture | Layers (controller → service → repository), boundaries, dependency direction |
| File Patterns | Directory structure, naming (e.g., *.test.ts, *.spec.ts) |
| Testing | Framework, TDD expectations, integration vs unit |
| Error Handling | Try/catch patterns, logging, user messages |
| Do Not | Anti-patterns, forbidden practices |
| References | Links to constitution, constraints, detailed docs |
Example AGENTS.md
# Acme API — AI Agent Guidelines
## Overview
Acme API is a RESTful backend for the Acme product. It uses Express.js, TypeScript,
and PostgreSQL. All endpoints require authentication unless explicitly public.
## Coding Standards
- Use TypeScript strict mode
- Prefer `async/await` over raw Promises
- Use `logger` (Winston) for all logging—never `console.log`
- Export types from `src/types/`; implementation from `src/`
## Architecture
- **Controllers**: Thin. Parse request, call service, return response. No business logic.
- **Services**: Business logic. Stateless. Call repositories for data.
- **Repositories**: Data access only. One per entity. No cross-repository logic.
- **Direction**: Controllers → Services → Repositories. Never reverse.
## File Patterns
- Controllers: `src/controllers/[entity].controller.ts`
- Services: `src/services/[entity].service.ts`
- Repositories: `src/repositories/[entity].repository.ts`
- Tests: `src/**/__tests__/*.test.ts` (colocated)
- Specs: `specs/features/[id]-[name].md`
## Testing Conventions
- TDD: tests before implementation
- Use Jest. Integration tests use Testcontainers for Postgres
- Mock only external APIs (payment, email). Use real DB for integration tests
- Test file: `[name].test.ts` next to source
## Error Handling
- Services throw domain errors (e.g., `UserNotFoundError`)
- Controllers catch and map to HTTP status (404, 400, 500)
- Log errors with context: `logger.error('Failed to create user', { error, userId })`
- Never expose stack traces to clients
## Do Not
- Do not put business logic in controllers
- Do not access the database from controllers (use repositories)
- Do not add new dependencies without team approval
- Do not log PII (emails, names) in plain text
- Do not use `any` in TypeScript
## References
- Constitution: `docs/constitution.md`
- Architecture constraints: `specs/constraints/architecture.md`
- API spec: `specs/api/`
AGENTS.md Hierarchy in Monorepos
In a monorepo, different packages may have different conventions. Use nested AGENTS.md files:
monorepo/
├── AGENTS.md # Root: shared conventions
├── packages/
│ ├── api/
│ │ └── AGENTS.md # API-specific: Express, REST
│ ├── web/
│ │ └── AGENTS.md # Web-specific: React, components
│ └── shared/
│ └── AGENTS.md # Shared: types, utilities
Loading behavior: When the agent works in packages/api/, it typically loads both root AGENTS.md and packages/api/AGENTS.md. The more specific (nested) file overrides or extends the root. Platform behavior varies—check your agent's documentation.
Best practice: Root AGENTS.md for cross-cutting concerns (logging, security, general patterns). Package AGENTS.md for package-specific stack and conventions.
Default-Loaded in Many Runtimes: Why It Matters
In many runtimes, AGENTS.md is loaded by default. This has implications:
Pros:
- Agents always have project context
- No activation logic—no risk of "forgetting" to load
- Consistent baseline for every conversation
Cons:
- Consumes context on every prompt
- Long AGENTS.md reduces space for other context
- May be difficult to "turn off" for irrelevant tasks, depending on platform controls
Implication: Keep AGENTS.md focused and concise. Put detailed content in referenced documents (constitution, constraints) that the agent loads when needed. AGENTS.md should be the index to project knowledge, not the entire knowledge base.
Cursor Rules: Granular, File-Scoped Guidance
Cursor Rules provide granular, conditional guidance. Unlike AGENTS.md (always on), rules can apply always, intelligently, to specific files, or only when manually invoked.
File Formats
Rules use two formats:
| Format | Extension | Use Case |
|---|---|---|
| Markdown | .md | Simple rules, no activation config |
| Markdown with frontmatter | .mdc | Rules with activation config (globs, alwaysApply) |
Rule Location
Rules live in .cursor/rules/:
.cursor/
├── rules/
│ ├── typescript-standards.mdc
│ ├── react-patterns.mdc
│ ├── api-conventions.mdc
│ └── test-patterns.mdc
└── skills/
└── ...
Four Activation Modes
1. Always Apply
The rule applies to every chat session, regardless of context.
---
description: Core coding standards for the project
alwaysApply: true
---
Use for: Universal conventions that apply to all code (error handling, logging, security basics).
2. Apply Intelligently
The agent decides when the rule is relevant based on the conversation and open files.
---
description: API design patterns and REST conventions
alwaysApply: false
---
When alwaysApply is false and no globs are specified, the agent uses the description to determine relevance.
3. Apply to Specific Files
The rule applies when the user has files open that match a glob pattern.
---
description: TypeScript conventions for this project
globs: "**/*.ts"
alwaysApply: false
---
When globs is set, the rule activates when matching files are in context. Examples:
**/*.ts— All TypeScript files**/*.test.ts— Test files onlysrc/api/**/*.ts— API layer only**/*.tsx— React components
4. Apply Manually
The user explicitly invokes the rule by @-mentioning it (e.g., @typescript-standards).
Use for: Rules that are relevant only for specific tasks and would add noise if always applied.
.mdc Frontmatter Reference
---
description: Brief description (shown in rule picker, used for intelligent activation)
globs: "**/*.ts" # Optional. File pattern. Rule applies when matching files are open.
alwaysApply: false # Optional. If true, applies to every session. Default: false
---
| Field | Type | Purpose |
|---|---|---|
description | string | What the rule does. Shown in UI. Used for relevance. |
globs | string | File pattern. Activates when matching files in context. |
alwaysApply | boolean | If true, rule loads every session. |
Note: If both globs and alwaysApply are set, alwaysApply typically takes precedence—the rule applies always.
Rule Organization Strategies
By File Type
One rule per language or file type:
typescript-standards.mdc—**/*.tsreact-patterns.mdc—**/*.tsxapi-conventions.mdc—src/api/**/*.tstest-patterns.mdc—**/*.test.ts
Pros: Clear scope. Agent loads only relevant rules. Cons: May need to duplicate cross-cutting content.
By Concern
One rule per concern:
error-handling.mdc— alwaysApplylogging.mdc— alwaysApplysecurity.mdc— alwaysApply
Pros: Single place for each concern. Cons: Many always-apply rules consume context.
Hybrid
Combine both: file-type rules for language-specific patterns, concern rules for cross-cutting (if not in AGENTS.md).
Example Rules
TypeScript Conventions
---
description: TypeScript coding standards and patterns
globs: "**/*.ts"
alwaysApply: false
---
# TypeScript Standards
## Error Handling
\`\`\`typescript
// ❌ BAD
try {
await fetchData();
} catch (e) {}
// ✅ GOOD
try {
await fetchData();
} catch (e) {
logger.error('Failed to fetch', { error: e });
throw new DataFetchError('Unable to retrieve data', { cause: e });
}
\`\`\`
## Null Safety
- Use optional chaining: `user?.profile?.name`
- Use nullish coalescing for defaults: `value ?? 'default'`
- Avoid non-null assertion (`!`) unless you have verified
## Async
- Prefer `async/await` over raw Promises
- Always handle rejections—no unhandled promise rejections
API Patterns
---
description: REST API conventions and patterns
globs: "src/api/**/*.ts"
alwaysApply: false
---
# API Conventions
## Endpoint Structure
- `GET /resources` — List
- `GET /resources/:id` — Get one
- `POST /resources` — Create
- `PUT /resources/:id` — Replace
- `PATCH /resources/:id` — Partial update
- `DELETE /resources/:id` — Delete
## Response Format
- Success: `{ data: T }` or `{ data: T[], meta?: { total, page } }`
- Error: `{ error: { code, message } }`
- Use HTTP status correctly: 200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error
## Validation
- Validate input with Zod (or project validator)
- Return 400 with field-level errors for validation failures
Test Patterns
---
description: Test structure and patterns
globs: "**/*.test.ts"
alwaysApply: false
---
# Test Patterns
## Structure
- Use `describe` for the unit under test
- Use `it` for each behavior
- Name tests: "should [expected behavior] when [condition]"
## Given/When/Then
\`\`\`typescript
it('should return 404 when user not found', async () => {
// Given
const userId = 'nonexistent';
userRepository.getById.mockResolvedValue(null);
// When
const result = await userService.getUser(userId);
// Then
expect(result).toBeNull();
});
\`\`\`
## Mocks
- Mock at boundaries (repositories, external APIs)
- Prefer integration tests with real DB for service layer
- Use Testcontainers for Postgres in integration tests
.cursorrules: Legacy Single File
Before .cursor/rules/, Cursor supported a single .cursorrules file in the project root. It served the same purpose: project-level guidance for the AI.
Current state: .cursorrules is legacy. New projects should use .cursor/rules/ with .mdc files. Benefits of rules over .cursorrules:
- Multiple focused rules vs one long file
- Conditional activation (globs, alwaysApply)
- Better organization
- Rule picker in UI
Migration: If you have .cursorrules, split it into focused rules in .cursor/rules/. Move file-type-specific content to rules with globs. Move universal content to an always-apply rule or AGENTS.md.
AGENTS.md vs .cursor/rules/ vs SKILL.md
| Aspect | AGENTS.md | .cursor/rules/ | SKILL.md |
|---|---|---|---|
| Purpose | Project context | Conventions, patterns | Task-specific capability |
| Loading | Always | Conditional (always, intelligent, file, manual) | On-demand when relevant |
| Scope | Project-wide | Project-specific, can be file-scoped | Reusable across projects |
| Typical size | 100-300 lines | <500 lines per rule | <5000 tokens |
| Content | Overview, architecture, dos/don'ts | Concrete patterns, examples | Process, templates, workflows |
| When | Every prompt | Per activation mode | When task matches |
Decision Framework
Is it project-wide and always relevant?
YES → AGENTS.md
NO ↓
Is it file-type or session triggered?
YES → .cursor/rules/
NO ↓
Is it a specialized task with substantial instructions?
YES → SKILL.md
NO → Rule or AGENTS.md
Examples
| Need | Choice | Rationale |
|---|---|---|
| "We use Express and Postgres" | AGENTS.md | Always relevant |
| "TypeScript error handling pattern" | Rule (globs: **/*.ts) | File-triggered |
| "How to write SDD specs" | Skill | Task-specific, on-demand |
| "Controllers must be thin" | AGENTS.md | Architecture, always |
| "API response format" | Rule (globs: src/api/**) | File-scoped |
| "Code review against spec" | Skill | Task-specific |
| "No console.log" | AGENTS.md or Rule | Universal |
Tutorial: Create a Complete AI Configuration
This tutorial walks you through creating AGENTS.md and .cursor/rules/ for a TypeScript API project.
Step 1: Create AGENTS.md
Create AGENTS.md at project root:
# TaskFlow API — AI Agent Guidelines
## Overview
TaskFlow API is a task management backend. Express + TypeScript + PostgreSQL.
All endpoints require JWT auth except /health and /login.
## Coding Standards
- TypeScript strict mode
- async/await over Promises
- Winston logger (never console.log)
- Barrel exports from index.ts per module
## Architecture
- Controllers: thin, parse/validate, call service, return
- Services: business logic, stateless
- Repositories: data access only
- Flow: Controller → Service → Repository
## File Patterns
- `src/controllers/`, `src/services/`, `src/repositories/`
- Tests: `__tests__/*.test.ts` colocated
- Specs: `specs/features/`
## Testing
- Jest. TDD: tests before implementation
- Integration tests: Testcontainers for Postgres
- Mock only external APIs
## Do Not
- Business logic in controllers
- DB access outside repositories
- PII in logs
- `any` in TypeScript
## References
- specs/constraints/architecture.md
- docs/constitution.md
Step 2: Create TypeScript Rule
Create .cursor/rules/typescript-standards.mdc:
---
description: TypeScript conventions
globs: "**/*.ts"
alwaysApply: false
---
# TypeScript Standards
- Prefer optional chaining and nullish coalescing
- Catch blocks must log and rethrow or handle
- No `any`—use `unknown` and narrow
- Async: always handle rejections
Step 3: Create API Rule
Create .cursor/rules/api-conventions.mdc:
---
description: REST API patterns
globs: "src/**/*.controller.ts"
alwaysApply: false
---
# API Conventions
- Use standard REST verbs and status codes
- Response: { data } for success, { error: { code, message } } for errors
- Validate with Zod before service calls
- Map service errors to HTTP status
Step 4: Create Test Rule
Create .cursor/rules/test-patterns.mdc:
---
description: Test structure and patterns
globs: "**/*.test.ts"
alwaysApply: false
---
# Test Patterns
- describe/it structure
- Given/When/Then in test bodies
- Mock repositories at service boundary
- Integration tests use real DB (Testcontainers)
Step 5: Verify Activation
- Open a
.tsfile → TypeScript rule should activate (check rule indicator if available) - Open a
*.controller.tsfile → API rule should activate - Open a
*.test.tsfile → Test rule should activate - Start a new chat → AGENTS.md is loaded (implicit)
Step 6: Test with a Prompt
Prompt: "Add a GET /tasks endpoint that returns the user's tasks. Follow project conventions."
Expected: The agent uses AGENTS.md (architecture, file patterns), the API rule (REST, response format), and the TypeScript rule (error handling, types). The implementation should match your conventions.
Best Practices
1. Keep Rules Under 500 Lines
Long rules consume context. Split into focused rules. Use AGENTS.md or reference files for extensive content.
2. Avoid Duplicating Codebase Content
Don't paste large code blocks from your codebase into rules. Reference files by path: "See src/utils/errors.ts for error types." The agent can read the file when needed.
3. Write Specific Guidance
Vague: "Write good tests." Specific: "Use Given/When/Then. Mock at repository boundary. Name tests 'should [behavior] when [condition]'."
4. Use Concrete Examples
Show good vs bad. Code examples anchor the agent.
5. One Concern Per Rule
Don't combine TypeScript + API + testing in one rule. Split. The agent loads only what's relevant.
6. Prefer File-Scoped Over Always-Apply
Always-apply rules consume context on every prompt. Use file-scoped (globs) when the rule applies only to certain files.
7. Keep AGENTS.md as Index
AGENTS.md should point to detailed docs, not contain them. "See docs/constitution.md for constitutional principles."
8. Use Consistent Terminology
Choose terms and stick to them. "Controller" not "handler" or "route handler" in one place and "controller" in another. Consistency helps the agent parse and apply.
9. Update Configuration When Conventions Change
When the team adopts a new pattern or drops an old one, update AGENTS.md and rules. Stale configuration produces stale output.
Common Configuration Mistakes
Mistake 1: AGENTS.md as Dump
Bad: AGENTS.md contains 50 pages of documentation pasted from the wiki.
Good: AGENTS.md is 1-2 pages with sections and links. "For API design, see docs/api-design.md."
Mistake 2: Too Many Always-Apply Rules
Bad: 10 rules with alwaysApply: true. Every prompt loads all of them.
Good: 1-2 always-apply rules for universal concerns. Use globs for the rest.
Mistake 3: Duplication Across AGENTS.md and Rules
Bad: Same error-handling pattern in AGENTS.md and typescript-standards.mdc.
Good: AGENTS.md says "See error handling in .cursor/rules/typescript-standards.mdc" or define once, reference elsewhere.
Mistake 4: Vague Rule Descriptions
Bad: description: "TypeScript stuff"
Good: description: "TypeScript conventions: error handling, null safety, async patterns. Applies when editing .ts files."
Mistake 5: Rules That Should Be Skills
Bad: A 300-line rule for "how to write specifications" that loads only when the user mentions "spec."
Good: A spec-writer skill that loads on-demand when the task matches.
Try With AI
Prompt 1: AGENTS.md Review
"Read our AGENTS.md. What would you add or change? Consider: Is anything missing for a new AI working on this project? Is anything redundant with our rules? Suggest a revised structure."
Prompt 2: Rule Activation Check
"I have these rules: [list rules with globs]. I'm about to edit src/services/user.service.ts. Which rules would activate? Why? What about src/controllers/user.controller.ts?"
Prompt 3: Configuration Audit
"Audit our AI configuration: AGENTS.md + .cursor/rules/. Check for: (1) duplication between AGENTS.md and rules, (2) rules that should be skills (task-specific), (3) missing rules for our main file types, (4) rules that are too long. Provide specific recommendations."
Prompt 4: Rule Creation
"Create a rule for [describe pattern]. It should apply when [describe trigger: file type, directory]. Include: description, globs, and 3-5 concrete examples of the pattern. Keep it under 80 lines."
Practice Exercises
Exercise 1: AGENTS.md for Your Project
Create or refine AGENTS.md for your current project. Include: Overview, Coding Standards, Architecture, File Patterns, Testing, Do Not, References. Keep it under 150 lines. Test by asking the agent to implement a small feature and checking if it follows the guidelines.
Expected outcome: An AGENTS.md that improves agent output quality for your project.
Exercise 2: Rules for Your Stack
Create .cursor/rules/ for your project's main file types. For each: (1) Choose globs, (2) Write description, (3) Add 3-5 concrete patterns with examples. Verify activation by opening matching files.
Expected outcome: A set of rules that activate correctly and improve output for each file type.
Exercise 3: Decision Framework Application
For each of these, decide: AGENTS.md, Rule, or Skill? Document your reasoning. (a) "We use React Query for server state." (b) "When writing specs, use the 10-component structure." (c) "API responses must be { data } or { error }." (d) "Review code against the spec before merging."
Expected outcome: A short document showing the decision for each and the rationale.
Key Takeaways
-
AGENTS.md is the project-level instruction manual and is default-loaded in many runtimes, providing foundational context for most prompts.
-
AGENTS.md structure: Overview, Coding Standards, Architecture, File Patterns, Testing, Error Handling, Do Not, References. Keep it concise; reference detailed docs.
-
Monorepo hierarchy: Root AGENTS.md for shared concerns; nested AGENTS.md in packages for package-specific conventions.
-
Cursor Rules provide granular, conditional guidance. Four modes: Always Apply, Apply Intelligently, Apply to Specific Files, Apply Manually.
-
.mdc format: description, globs, alwaysApply. Use globs for file-scoped activation.
-
AGENTS.md vs rules vs skills: AGENTS.md = always, project-wide. Rules = conditional, file-scoped. Skills = on-demand, task-specific.
-
Best practices: Rules under 500 lines, no codebase duplication, specific guidance, concrete examples, one concern per rule.
Chapter Quiz
-
What is AGENTS.md, and how does its loading behavior vary across platforms? What are the implications for its length and content?
-
How would you structure AGENTS.md for a monorepo with an API package and a web package? What goes in the root vs package-level file?
-
What are the four Cursor Rules activation modes? Give an example of when you would use each.
-
What is the difference between .md and .mdc rule files? When would you use .mdc?
-
Explain the decision framework for AGENTS.md vs Rule vs Skill. Apply it to: "We use Zod for validation" and "Generate a spec from this user story."
-
What are three best practices for writing rules? Give a concrete example for each.
-
You have a rule that's 600 lines. What should you do? What are the trade-offs of splitting vs keeping it?
-
Create a rule that applies only when editing test files. What glob would you use? What content would you include?