Skip to main content

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

SectionContent
OverviewProject purpose, stack, key decisions
Coding StandardsStyle (naming, formatting), patterns (error handling, async)
ArchitectureLayers (controller → service → repository), boundaries, dependency direction
File PatternsDirectory structure, naming (e.g., *.test.ts, *.spec.ts)
TestingFramework, TDD expectations, integration vs unit
Error HandlingTry/catch patterns, logging, user messages
Do NotAnti-patterns, forbidden practices
ReferencesLinks 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:

FormatExtensionUse Case
Markdown.mdSimple rules, no activation config
Markdown with frontmatter.mdcRules 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 only
  • src/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
---
FieldTypePurpose
descriptionstringWhat the rule does. Shown in UI. Used for relevance.
globsstringFile pattern. Activates when matching files in context.
alwaysApplybooleanIf 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**/*.ts
  • react-patterns.mdc**/*.tsx
  • api-conventions.mdcsrc/api/**/*.ts
  • test-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 — alwaysApply
  • logging.mdc — alwaysApply
  • security.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

AspectAGENTS.md.cursor/rules/SKILL.md
PurposeProject contextConventions, patternsTask-specific capability
LoadingAlwaysConditional (always, intelligent, file, manual)On-demand when relevant
ScopeProject-wideProject-specific, can be file-scopedReusable across projects
Typical size100-300 lines<500 lines per rule<5000 tokens
ContentOverview, architecture, dos/don'tsConcrete patterns, examplesProcess, templates, workflows
WhenEvery promptPer activation modeWhen 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

NeedChoiceRationale
"We use Express and Postgres"AGENTS.mdAlways relevant
"TypeScript error handling pattern"Rule (globs: **/*.ts)File-triggered
"How to write SDD specs"SkillTask-specific, on-demand
"Controllers must be thin"AGENTS.mdArchitecture, always
"API response format"Rule (globs: src/api/**)File-scoped
"Code review against spec"SkillTask-specific
"No console.log"AGENTS.md or RuleUniversal

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

  1. Open a .ts file → TypeScript rule should activate (check rule indicator if available)
  2. Open a *.controller.ts file → API rule should activate
  3. Open a *.test.ts file → Test rule should activate
  4. 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

  1. AGENTS.md is the project-level instruction manual and is default-loaded in many runtimes, providing foundational context for most prompts.

  2. AGENTS.md structure: Overview, Coding Standards, Architecture, File Patterns, Testing, Error Handling, Do Not, References. Keep it concise; reference detailed docs.

  3. Monorepo hierarchy: Root AGENTS.md for shared concerns; nested AGENTS.md in packages for package-specific conventions.

  4. Cursor Rules provide granular, conditional guidance. Four modes: Always Apply, Apply Intelligently, Apply to Specific Files, Apply Manually.

  5. .mdc format: description, globs, alwaysApply. Use globs for file-scoped activation.

  6. AGENTS.md vs rules vs skills: AGENTS.md = always, project-wide. Rules = conditional, file-scoped. Skills = on-demand, task-specific.

  7. Best practices: Rules under 500 lines, no codebase duplication, specific guidance, concrete examples, one concern per rule.


Chapter Quiz

  1. What is AGENTS.md, and how does its loading behavior vary across platforms? What are the implications for its length and content?

  2. 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?

  3. What are the four Cursor Rules activation modes? Give an example of when you would use each.

  4. What is the difference between .md and .mdc rule files? When would you use .mdc?

  5. 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."

  6. What are three best practices for writing rules? Give a concrete example for each.

  7. You have a rule that's 600 lines. What should you do? What are the trade-offs of splitting vs keeping it?

  8. Create a rule that applies only when editing test files. What glob would you use? What content would you include?