AGENTS.md Patterns Guide
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
Table of Contents
- What is AGENTS.md
- Tool Compatibility via Symlinks
- DRY Pattern: Reference crefft
- File Placement Strategies
- Essential Sections
- Templates by Project Type
- Integration with Other Tools
- Maintenance Best Practices
- Anti-Patterns
- Complete Example Templates
What is AGENTS.md
Definition
AGENTS.md is a markdown file that serves as a structured briefing document for AI assistants working with a codebase1. It provides essential context that AI assistants cannot easily infer from code alone.
Core Principles
An AGENTS.md file MUST:
- Be concise - Provide necessary context without overwhelming detail (200-500 lines ideal)
- Be actionable - Include concrete commands and examples
- Be discoverable - Placed where AI assistants can find it (repository root)
- Be version-controlled - Tracked in git alongside code
- Be current - Updated as the project evolves
An AGENTS.md file MUST NOT:
- Replace comprehensive documentation - It’s a briefing, not a manual
- Include secrets - Never include credentials or sensitive data
- Be auto-generated - Manual curation ensures quality
- Duplicate style guides - Link to external guides instead
Why AGENTS.md Matters
The Context Problem: AI assistants lack persistent memory. Without AGENTS.md, they must infer project structure, guess at conventions, and discover commands through trial and error. This wastes time, introduces errors, and creates inconsistency.
The Solution: A well-crafted AGENTS.md accelerates onboarding, ensures consistency, prevents mistakes, and preserves tribal knowledge.
Tool Compatibility via Symlinks
AI coding tools disagree about filenames. Codex, GitHub Copilot, and Cursor
read AGENTS.md natively234; Claude Code reads CLAUDE.md5,
Gemini CLI reads GEMINI.md6, and Aider reads nothing it has not been
told to read7. To support multiple tools with a single source of truth,
MUST use symlinks:
Recommended Setup
# AGENTS.md is the canonical file
# Codex, GitHub Copilot and Cursor already discover it - no symlink needed
# Symlink only for tools whose default filename differs
ln -s AGENTS.md CLAUDE.md # Claude Code
ln -s AGENTS.md GEMINI.md # Gemini CLI
On Windows a symlink needs Administrator rights or Developer Mode, so a
CLAUDE.md whose only content is the import @AGENTS.md MUST be used
there instead5. Aider is not covered by either file: it MUST be
pointed at the file explicitly, as described under
Aider.
Repository Structure
my-project/
├── AGENTS.md <- Canonical file (edit this one)
├── CLAUDE.md <- Symlink -> AGENTS.md
├── GEMINI.md <- Symlink -> AGENTS.md
├── README.md <- Human documentation
├── src/
└── tests/
Why Symlinks
| Approach | Pros | Cons |
|---|---|---|
| Symlinks | Single source of truth, no drift | Requires symlink support |
| Duplicates | Works everywhere | Content drift, maintenance burden |
| Single name | Simple | Some tools won’t find it |
Symlinks are the recommended approach because they ensure all tools read identical instructions while requiring only one file to maintain.
Tool Discovery
Each tool has one default filename, its own hierarchy, and its own way of being pointed at a different file. The table below was checked against vendor documentation on 2026-09-08.
| Tool | Loaded by default | AGENTS.md support | Hierarchy and precedence | Imports | How to inspect what loaded |
|---|---|---|---|---|---|
| Codex2 | AGENTS.override.md, else AGENTS.md | Native | ~/.codex first, then project root down to the working directory; nearer files override; truncated at project_doc_max_bytes (32 KiB) | No | codex --ask-for-approval never "Summarize the current instructions." |
| GitHub Copilot3 | .github/copilot-instructions.md | Native, in any directory | Nearest AGENTS.md in the directory tree wins; .github/instructions/*.instructions.md add path-scoped rules | No | Expand the references list on a Copilot Chat response |
| Cursor4 | AGENTS.md, .cursor/rules/*.mdc | Native, project root and subdirectories | Team → Project → User rules; nested AGENTS.md files combine, nearer files take precedence | @file references | Customize → Rules lists every rule and its status |
| Claude Code5 | CLAUDE.md, .claude/CLAUDE.md, CLAUDE.local.md, .claude/rules/*.md | Not read; import or symlink it | Root down to the working directory, concatenated; subdirectory files load when Claude reads those directories | @AGENTS.md | /context, then read Memory files |
| Gemini CLI6 | GEMINI.md | Add AGENTS.md to context.fileName in settings.json | ~/.gemini/GEMINI.md, then workspace directories and their parents, then just-in-time files as tools touch directories | @file.md | /memory show |
| Aider7 | Nothing | aider --read AGENTS.md, or read: in .aider.conf.yml | Not applicable; every file is loaded explicitly | No | Aider prints Added AGENTS.md to the chat on start-up |
Why This Matters
A wrong entry in this table fails silently: the file is committed, the team
believes the tool is reading it, and the tool never loads it. Creating
CLAUDE.md does nothing for Aider, and creating AGENTS.md does nothing for
Claude Code or for an unconfigured Gemini CLI. MUST confirm discovery with
the inspection command in the last column before relying on a file.
DRY Pattern: Reference crefft
Project AGENTS.md files MUST reference crefft as the source of truth for coding standards. This creates a “DRY for AI” pattern where:
- crefft contains canonical style guides, tooling choices, and conventions
- Project AGENTS.md contains only project-specific context
Why This Matters
Without central standards, each project’s AGENTS.md would duplicate:
- Language style guides (naming, formatting, patterns)
- Framework conventions (architecture, testing approaches)
- Tooling configuration (linters, formatters, CI)
This leads to drift, inconsistency, and maintenance burden.
Required Standards Section
Every project AGENTS.md MUST begin with a Standards section:
## Standards
This project follows [crefft](https://github.com/agh/crefft):
| Concern | Guide |
| ------- | ----- |
| Python | [guides/languages/python.md][python] |
| Django | [guides/frameworks/django.md][django] |
| Testing | [guides/process/testing.md][testing] |
[python]: https://github.com/agh/crefft/blob/main/guides/languages/python.md
[django]: https://github.com/agh/crefft/blob/main/guides/frameworks/django.md
[testing]: https://github.com/agh/crefft/blob/main/guides/process/testing.md
**Do not duplicate crefft guidance here.** This file contains only
project-specific context.
What Belongs in crefft vs Project AGENTS.md
| In crefft | In Project AGENTS.md |
|---|---|
| Python naming conventions | This project’s directory structure |
| Django architecture patterns | Commands to run this project |
| Testing strategies | Environment variables needed |
| Tool versions and configs | Project-specific pitfalls |
| RFC 2119 requirements | Architecture decisions unique to this project |
Benefits
- Single source of truth - Update once, benefit everywhere
- Reduced maintenance - Project files stay small and focused
- Consistency - All projects follow identical standards
- AI efficiency - AI can learn crefft once, apply everywhere
File Placement Strategies
Single Project Repository
For a single project, MUST place AGENTS.md at repository root:
my-project/
├── AGENTS.md <- AI context here
├── CLAUDE.md <- Symlink
├── GEMINI.md <- Symlink
├── README.md <- Human documentation
├── src/
└── tests/
Monorepo with Multiple Projects
For monorepos, MUST use hierarchical placement:
monorepo/
├── AGENTS.md <- Monorepo-level context
├── CLAUDE.md <- Symlink
├── GEMINI.md <- Symlink
├── services/
│ ├── api/
│ │ └── AGENTS.md <- API-specific context
│ └── worker/
│ └── AGENTS.md <- Worker-specific context
└── libs/
└── shared/
└── AGENTS.md <- Shared library context
Hierarchical Rules:
- Root AGENTS.md contains: Monorepo structure, cross-cutting commands, shared conventions
- Child AGENTS.md contains: Service-specific context, commands, conventions
- Child files link back to parent for shared information
Microservices Architecture
Each repository MUST have its own AGENTS.md. Include cross-service references:
## Related Services
- **user-service**: Authentication/authorization
- Repository: https://github.com/org/user-service
- Dependencies: We depend on their User model
Essential Sections
Every AGENTS.md MUST contain these sections:
1. Overview
Provide high-level context in 3-5 lines:
## Overview
API gateway for e-commerce platform. Routes requests to microservices,
handles authentication, and provides rate limiting.
**Stack:** Node.js 20, TypeScript 5.3, Express 4.18, Redis 7.2
**Purpose:** Centralized entry point for web, mobile, and third-party clients
2. Commands
Provide executable commands with expected outcomes:
## Commands
### Development
```bash
npm install # Install dependencies
npm run dev # Start dev server (port 3000, hot reload)
npm run typecheck # Type checking (strict mode)
```
### Testing
```bash
npm test # Run all tests
npm test -- --coverage # Coverage report (requires 80%)
```
MUST include actual runnable commands with prerequisites.
3. Project Structure
Explain non-obvious directory organization:
## Project Structure
```text
src/
├── api/ # Express route handlers
├── services/ # Business logic (DI-injectable)
├── models/ # TypeORM entities
└── middleware/ # Express middleware
tests/
├── unit/ # Unit tests (fast, isolated)
└── integration/ # Integration tests (DB required)
```
**Key Files:**
- `src/server.ts` - Application entry point
- `.env.example` - Required environment variables
4. Architecture
Document patterns and decisions:
## Architecture
**Dependency Injection:** Using tsyringe. All services **MUST** be
injectable.
**Repository Pattern:** All database access **MUST** go through repositories.
**Data Flow:**
```text
Client -> Middleware -> Controller -> Service -> Repository -> Database
```
5. Code Style
Document conventions not captured in linters:
## Code Style
Follows [crefft TypeScript Guide](../../guides/languages/typescript.md)
**Naming:**
- Files: `kebab-case.ts`
- Classes: `PascalCase`
- Functions: `camelCase`
**Import Ordering:**
```typescript
// 1. Node built-ins
// 2. External dependencies
// 3. Internal modules
// 4. Types
```
6. Common Pitfalls
Warn about known issues:
## Common Pitfalls
### Database Connections
**ISSUE:** TypeORM doesn't automatically release connections
**SOLUTION:**
```typescript
const queryRunner = dataSource.createQueryRunner();
try {
await queryRunner.startTransaction();
// ... queries
await queryRunner.commitTransaction();
} finally {
await queryRunner.release(); // Always release!
}
```
7. Testing Strategy
Guide AI in writing tests:
## Testing Strategy
- **Unit:** Jest with mocked dependencies (80% coverage minimum)
- **Integration:** Testcontainers for database tests
- **E2E:** Supertest for API tests (critical paths only)
**Example:**
```typescript
describe('UserService', () => {
it('should hash password on creation', async () => {
const user = await service.create({ password: 'secret' });
expect(user.password).not.toBe('secret');
});
});
```
8. Environment Configuration
Document required variables:
## Environment Configuration
```bash
# Required
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
JWT_SECRET=your-secret-key-min-32-chars
# Optional
LOG_LEVEL=info # debug | info | warn | error
FEATURE_NEW_UI=false # Feature flags
```
**Secrets Management:** Use AWS Secrets Manager in production. Never commit
secrets.
Templates by Project Type
Python (FastAPI)
# AGENTS.md - [Project Name]
## Overview
[1-2 sentence description]
**Stack:** Python 3.12, FastAPI 0.109, SQLAlchemy 2.0, PostgreSQL 16
## Commands
```bash
# Development
uv sync
uv run uvicorn app.main:app --reload
# Testing
uv run pytest --cov=src --cov-fail-under=80
# Database
uv run alembic upgrade head
```
## Structure
```text
src/
├── api/ # FastAPI routers
├── models/ # SQLAlchemy models
├── schemas/ # Pydantic schemas
└── services/ # Business logic
```
## Code Style
Follows [crefft Python Guide](../../guides/languages/python.md)
**Type Hints:** **MUST** use on all functions with
`from __future__ import annotations`
## Common Pitfalls
**Async/Await:** Never mix sync and async code. Use `AsyncSession` not
`Session`.
## Environment
```bash
DATABASE_URL=postgresql+asyncpg://user:pass@localhost/dbname
SECRET_KEY=your-secret-key
```
TypeScript (Express)
# AGENTS.md - [Project Name]
## Overview
[1-2 sentence description]
**Stack:** Node.js 20, TypeScript 5.3, Express 4.18, PostgreSQL 16
## Commands
```bash
# Development
npm install
npm run dev
# Testing
npm test -- --coverage
```
## Structure
```text
src/
├── controllers/ # Request handlers
├── services/ # Business logic
├── models/ # TypeORM entities
└── middleware/ # Express middleware
```
## Code Style
Follows [crefft TypeScript Guide](../../guides/languages/typescript.md)
**Naming:** Files: `kebab-case.ts`, Classes: `PascalCase`, Functions:
`camelCase`
## Common Pitfalls
**Promise Handling:** Always use try/catch in async route handlers and pass
errors to `next()`.
## Environment
```bash
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
JWT_SECRET=your-secret-key
```
Go (Gin)
# AGENTS.md - [Project Name]
## Overview
[1-2 sentence description]
**Stack:** Go 1.22, Gin 1.9, PostgreSQL 16, GORM 1.25
## Commands
```bash
# Development
go mod download
go run cmd/server/main.go
# Testing
go test ./...
go test -race ./... # Race detection
```
## Structure
```text
cmd/server/ # Application entrypoint
internal/
├── api/ # HTTP handlers
├── service/ # Business logic
└── repository/ # Data access
```
## Code Style
Follows [crefft Go Guide](../../guides/languages/go.md)
**Error Handling:** Always wrap errors: `fmt.Errorf("get user: %w", err)`
## Common Pitfalls
**Goroutine Leaks:** Use context cancellation. **Pointer Receivers:** Use for
methods that modify state.
## Environment
```bash
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
JWT_SECRET=your-secret-key
```
Rails
# AGENTS.md - [Project Name]
## Overview
[1-2 sentence description]
**Stack:** Ruby 3.3, Rails 7.1, PostgreSQL 16, Sidekiq 7
## Commands
```bash
# Development
bundle install
rails db:setup
rails server
# Testing
rails test
COVERAGE=true rails test
```
## Structure
```text
app/
├── controllers/ # Request handlers
├── models/ # ActiveRecord models
├── services/ # Service objects
└── jobs/ # Background jobs
```
## Code Style
Follows [crefft Rails Guide](../../guides/frameworks/rails.md)
**Service Objects:** Use for complex business logic outside controllers.
## Common Pitfalls
**N+1 Queries:** Use `includes()` for eager loading. **Mass Assignment:** Use
strong parameters.
## Environment
```bash
DATABASE_URL=postgresql://user:pass@localhost/dbname
REDIS_URL=redis://localhost:6379/0
SECRET_KEY_BASE=your-secret-key
```
Django
# AGENTS.md - [Project Name]
## Overview
[1-2 sentence description]
**Stack:** Python 3.12, Django 5.1, PostgreSQL 16, Celery 5.4, Redis 7
## Commands
```bash
# Development
uv sync
uv run python manage.py migrate
uv run python manage.py runserver
# Testing
uv run pytest --cov=apps --cov-fail-under=80
# Linting
uv run ruff check . --fix
uv run mypy apps/
```
## Structure
```text
config/
├── settings/ # Split settings (base, local, production)
│ ├── base.py
│ └── local.py
└── urls.py
apps/
├── users/ # User management app
│ ├── models.py
│ ├── views.py
│ ├── services.py # Business logic
│ └── selectors.py # Query logic
└── core/ # Shared utilities
```
## Code Style
Follows [crefft Django Guide](../../guides/frameworks/django.md)
**Fat Models, Thin Views:** Business logic in services, not views.
**Selectors Pattern:** Query logic in selectors, not views or models.
## Common Pitfalls
**N+1 Queries:** Use `select_related()` and `prefetch_related()`. Check with
django-debug-toolbar.
**Signals:** Use sparingly. Prefer explicit service calls for business logic.
**Async Views:** Use `async def` only with async ORM operations. Don't mix
sync/async.
## Environment
```bash
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
SECRET_KEY=your-secret-key-min-50-chars
CELERY_BROKER_URL=redis://localhost:6379/0
```
Flask
# AGENTS.md - [Project Name]
## Overview
[1-2 sentence description]
**Stack:** Python 3.12, Flask 3.1, SQLAlchemy 2.0, PostgreSQL 16, Celery 5.4
## Commands
```bash
# Development
uv sync
uv run flask db upgrade
uv run flask run --debug
# Testing
uv run pytest --cov=src --cov-fail-under=80
# Linting
uv run ruff check . --fix
```
## Structure
```text
src/
├── __init__.py # Application factory
├── models/ # SQLAlchemy models
├── routes/ # Blueprint route handlers
├── services/ # Business logic
├── schemas/ # Marshmallow/Pydantic schemas
└── extensions.py # Flask extensions (db, migrate, etc.)
tests/
├── conftest.py # Fixtures
└── test_*.py
```
## Code Style
Follows [crefft Flask Guide](../../guides/frameworks/flask.md)
**Application Factory:** Always use `create_app()` pattern.
**Blueprints:** Organize routes by domain (auth, users, api).
## Common Pitfalls
**Circular Imports:** Use application factory and import extensions from
`extensions.py`.
**Request Context:** Access `current_app` and `g` only within request context.
**SQLAlchemy Sessions:** Use `db.session` from Flask-SQLAlchemy, not raw
sessions.
## Environment
```bash
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
SECRET_KEY=your-secret-key
FLASK_ENV=development
```
Rust (Axum)
# AGENTS.md - [Project Name]
## Overview
[1-2 sentence description]
**Stack:** Rust 1.83, Axum 0.8, SQLx 0.8, PostgreSQL 16, Tokio 1.43
## Commands
```bash
# Development
cargo build
cargo run
# Testing
cargo test
cargo test -- --nocapture # See output
# Linting
cargo clippy -- -D warnings
cargo fmt --check
# Database
sqlx database create
sqlx migrate run
```
## Structure
```text
src/
├── main.rs # Entry point, server setup
├── app.rs # Router and state setup
├── config.rs # Configuration loading
├── handlers/ # Request handlers
│ ├── mod.rs
│ └── users.rs
├── models/ # Domain models
├── repositories/ # Database access (SQLx)
├── services/ # Business logic
└── error.rs # Error types and handling
migrations/ # SQLx migrations
tests/ # Integration tests
```
## Code Style
Follows [crefft Axum Guide](../../guides/frameworks/axum.md)
**Extractors:** Use typed extractors for request data. Validate with
`validator` crate.
**Error Handling:** Use `thiserror` for domain errors, implement
`IntoResponse`.
**State:** Use `AppState` with `Arc` for shared state.
## Common Pitfalls
**Async Runtime:** Always use `#[tokio::main]`. Don't block the runtime with
sync I/O.
**SQLx Compile-Time Checks:** Run `cargo sqlx prepare` before CI builds
without database.
**Ownership in Handlers:** Clone `Arc<AppState>` fields, don't fight the
borrow checker.
**Tower Middleware Order:** Middleware wraps in reverse order. Add logging
last (runs first).
## Environment
```bash
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
JWT_SECRET=your-secret-key-min-32-chars
RUST_LOG=info,tower_http=debug
```
Next.js
# AGENTS.md - [Project Name]
## Overview
[1-2 sentence description]
**Stack:** Next.js 15, React 19, TypeScript 5.7, PostgreSQL 16, Prisma 6
## Commands
```bash
# Development
pnpm install
pnpm dev
# Testing
pnpm test
pnpm test:e2e
# Linting
pnpm lint
pnpm typecheck
# Database
pnpm prisma migrate dev
pnpm prisma generate
```
## Structure
```text
app/
├── (auth)/ # Auth route group (shared layout)
│ ├── login/
│ └── register/
├── (dashboard)/ # Dashboard route group
│ └── settings/
├── api/ # API routes
│ └── users/
├── layout.tsx # Root layout
└── page.tsx # Home page
components/
├── ui/ # Shadcn/UI components
└── features/ # Feature-specific components
lib/
├── db.ts # Prisma client
├── auth.ts # Auth.js config
└── actions/ # Server Actions
```
## Code Style
Follows [crefft Next.js Guide](../../guides/frameworks/nextjs.md)
**Server Components:** Default. Only add `'use client'` when needed.
**Server Actions:** Use for mutations. Validate with Zod.
**Data Fetching:** Fetch in Server Components, not useEffect.
## Common Pitfalls
**Hydration Mismatch:** Don't use `Date.now()` or `Math.random()` in Server
Components.
**Client/Server Boundary:** Can't pass functions or classes from Server to
Client Components.
**Caching:** `fetch()` caches by default. Use `{ cache: 'no-store' }` for
dynamic data.
**Server Actions Security:** Always validate input. Actions are public
endpoints.
## Environment
```bash
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
NEXTAUTH_SECRET=your-secret-key
NEXTAUTH_URL=http://localhost:3000
```
Integration with Other Tools
Cursor
Cursor reads AGENTS.md from the project root and from subdirectories, and
combines nested files with parent files, so no Cursor-specific file is needed
for project context4. Reach for .cursor/rules/*.mdc only when a rule must
be scoped rather than always applied:
- AGENTS.md: project context, commands, architecture; the root file applies broadly, a nested file applies when working in its directory tree
- .cursor/rules/*.mdc: rules attached by glob, selected by description, or
invoked manually with
@rule-name
A project rule is an .mdc file with frontmatter. A plain .md file in
.cursor/rules/ is ignored because it carries no frontmatter4:
---
globs: src/components/**/*.tsx
alwaysApply: false
---
- Use named exports, not default exports
- See AGENTS.md for project-wide conventions
.cursorrules is legacy. Cursor still reads a root .cursorrules, but
documents it as pending deprecation and gives a migration path: copy the
content into a rule, set the rule type to Always Apply, and delete the
file8. New projects MUST NOT create .cursorrules. Start from
configs/cursor/rules/project-rules.mdc.template
instead.
Aider
Aider discovers no instruction file by filename. Conventions MUST be loaded explicitly, either per invocation or once in configuration7:
aider --read AGENTS.md src/users/service.ts
# .aider.conf.yml
read: AGENTS.md
# Multiple files
# read: [AGENTS.md, CONVENTIONS.md]
--read and read: mark the file read-only and let it be cached when prompt
caching is enabled7. Aider prints Added AGENTS.md to the chat at start-up;
if that line is missing, nothing was loaded. Keep AGENTS.md concise
(< 1000 lines) so it fits in context alongside the files being edited.
IDE Integration
Add to .vscode/settings.json:
{
"files.associations": {
"AGENTS.md": "markdown",
"CLAUDE.md": "markdown",
"GEMINI.md": "markdown"
}
}
Maintenance Best Practices
When to Update
MUST update when:
- Architecture changes (new patterns, refactoring)
- Commands change (new scripts, tool updates)
- Conventions change (style guide updates)
- New pitfalls discovered (production issues)
- Major dependencies change (framework upgrades)
Update Process
- Review quarterly - Schedule regular reviews
- Track in git - Commit AGENTS.md changes with code changes
- Test with AI - Verify AI assistants understand updates
- Keep concise - Remove outdated content, don’t just append
Automated Checks
Add to CI/CD:
# .github/workflows/lint.yml
- name: Check AGENTS.md exists
run: |
if [ ! -f AGENTS.md ]; then
echo "AGENTS.md not found"
exit 1
fi
- name: Validate markdown
run: npx markdownlint-cli2@0.23.2 AGENTS.md
- name: Check for secrets
env:
GITLEAKS_VERSION: 8.30.1
run: |
base="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}"
curl -sSfL -O "${base}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
curl -sSfL -O "${base}/gitleaks_${GITLEAKS_VERSION}_checksums.txt"
sha256sum --check --ignore-missing "gitleaks_${GITLEAKS_VERSION}_checksums.txt"
tar -xzf "gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" gitleaks
./gitleaks dir AGENTS.md --redact --no-banner --exit-code 1
- name: Verify symlinks
run: |
if [ -f CLAUDE.md ] && [ ! -L CLAUDE.md ]; then
echo "CLAUDE.md should be a symlink to AGENTS.md"
exit 1
fi
Locally the same scan runs from the pinned Gitleaks pre-commit hook in configs/pre-commit/.pre-commit-config.yaml9.
Why a Scanner, Not grep
A hand-written pattern such as grep -r "password.*=.*[^example]" AGENTS.md
does not do what it appears to do. [^example] is a negated character class:
one character that is not e, x, a, m, p, or l. It never means “not
the word example.” The pattern is also case-sensitive and knows only the word
password, so it misses every other credential shape.
Measured on the same fixtures, grep against Gitleaks v8.30.19:
AGENTS.md content | grep | Gitleaks |
|---|---|---|
PASSWORD= with a high-entropy value | missed | flagged |
api_token=ghp_... GitHub token | missed | flagged |
| Live-shaped AWS key pair | missed | flagged |
-----BEGIN RSA PRIVATE KEY----- block | missed | flagged |
password="example" | false positive | clean |
password=example-value | false positive | clean |
DATABASE_URL=postgresql://user:password@host:5432/db | clean | clean |
AWS documentation keys (AKIAIOSFODNN7EXAMPLE) | missed | clean |
Token line ending # gitleaks:allow | missed | clean |
Two consequences follow. Intentional placeholders MUST be marked with a
trailing # gitleaks:allow comment or listed in .gitleaksignore, rather than
worded to dodge a regex. And --redact MUST stay on: without it a detected
credential is printed in full into CI logs that are often world-readable.
No scanner is complete. Gitleaks scores generic assignments on entropy, so a
short weak literal such as password=hunter2 passes. Scanning is a backstop;
the rule that credentials never enter AGENTS.md still belongs to review.
Anti-Patterns
1. The Novel (15,000 lines)
Problem: AGENTS.md becomes comprehensive documentation replacement
Solution:
- Keep under 1000 lines (200-500 ideal)
- Link to comprehensive docs
- Focus on what AI needs to know
2. The Stale Guide
Problem: Outdated commands and information
Solution:
- Review quarterly
- Update with major changes
- Add to PR checklist
3. The Secret Leaker
Problem: Contains credentials or sensitive data
Solution:
- Use placeholder values
- Reference
.env.example - Never commit secrets
4. The Copy-Paste Disaster
Problem: Duplicates README, docs, style guides
Solution:
- Link to external guides
- Summarize key points
- Document only deviations
5. The Vague Guide
Problem: Lacks specific, actionable information
Solution:
- Provide specific commands
- Include code examples
- Be concrete and actionable
6. The Assumed Knowledge
Problem: Assumes AI knows project-specific context
Solution:
- Be explicit about patterns
- Include examples
- Don’t assume shared context
7. The Auto-Generated Mess
Problem: Auto-generated from code comments
Solution:
- Manually curate AGENTS.md
- Focus on high-level patterns
- Link to API docs for details
8. The Kitchen Sink
Problem: Includes everything tangentially related
Solution:
- Be selective
- Include only essential information
- Link to comprehensive sources
9. The Duplicate Files
Problem: Separate CLAUDE.md, GEMINI.md, etc. with different content
Solution:
- Use single AGENTS.md as source of truth
- Create symlinks for tool compatibility
- Never duplicate content across files
Complete Example Templates
Minimal Example (200 lines)
# AGENTS.md - Todo API
## Overview
Simple REST API for todo list management. FastAPI backend with SQLite
database.
**Stack:** Python 3.12, FastAPI 0.109, SQLAlchemy 2.0, SQLite
## Commands
```bash
# Development
uv sync
uv run uvicorn app.main:app --reload # Port 8000
# Testing
uv run pytest
# Linting
uv run ruff check . --fix
```
## Structure
```text
app/
├── main.py # FastAPI entry point
├── models.py # SQLAlchemy models
├── schemas.py # Pydantic schemas
└── database.py # DB connection
tests/ # Pytest tests
```
## Key Patterns
**Async by default:**
```python
@app.get("/todos")
async def get_todos(db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Todo))
return result.scalars().all()
```
**Validation:** All inputs use Pydantic schemas. Never accept raw dicts.
## Common Issues
**Database locking:** SQLite doesn't handle concurrent writes. Use PostgreSQL
for production.
**Async session:** Always use `AsyncSession`, not `Session`.
## Environment
```bash
DATABASE_URL=sqlite:///./todos.db
ENVIRONMENT=development
```
Medium Example (500 lines)
See complete microservices e-commerce example in Python Template section.
Key additions for medium projects:
- Service Dependencies: Document how services interact
- Event Flow: Show event-driven communication
- Common Pitfalls: More detailed with distributed systems issues
- Testing Strategy: Contract tests, integration tests
- Monitoring: Metrics, logs, tracing
Large Projects
For large projects (> 5 services), split into:
- Main AGENTS.md (500 lines) - Overview, quick start, architecture
- Service-specific AGENTS.md (200-300 lines each)
- External docs links
Main AGENTS.md structure:
# AGENTS.md - [Platform Name]
## Overview
[High-level description]
## Quick Start
```bash
docker-compose up -d
npm run dev:all
```
## Architecture
[System-level architecture diagram]
## Service Navigation
- **API Gateway:** [services/api-gateway/AGENTS.md](services/api-gateway/AGENTS.md)
- **User Service:** [services/users/AGENTS.md](services/users/AGENTS.md)
- **Order Service:** [services/orders/AGENTS.md](services/orders/AGENTS.md)
## Cross-Cutting Concerns
[Shared patterns, auth, logging, monitoring]
## External Documentation
- [Full Docs](https://docs.example.com)
- [API Reference](https://api.example.com/docs)
See Also
- Testing Guide - Testing strategies
- CI/CD Guide - Continuous integration
- Markdown Guide - Markdown style guide
- Language Guides - Python, TypeScript, Go, Ruby
References
Footnotes
-
AGENTS.md - The cross-tool convention this guide documents ↩
-
Codex AGENTS.md - Instruction chain, override files, and discovery precedence ↩ ↩2
-
GitHub Copilot repository instructions -
copilot-instructions.md, path-specific instructions, andAGENTS.md↩ ↩2 -
Cursor rules - Project rules,
AGENTS.mdsupport, and rule precedence ↩ ↩2 ↩3 ↩4 -
Claude Code memory - How Claude Code discovers
CLAUDE.md,.claude/rules/, and@AGENTS.mdimports ↩ ↩2 ↩3 -
Gemini CLI context files - Hierarchical
GEMINI.mdloading and thecontext.fileNamesetting ↩ ↩2 -
Aider conventions - Loading a conventions file with
--readorread:in.aider.conf.yml↩ ↩2 ↩3 ↩4 -
Cursor rules help - Migration path from the legacy
.cursorrulesfile ↩ -
Gitleaks - Secret scanner used by crefft’s pre-commit configuration ↩ ↩2