agents.md file is more than just a configuration; it’s a detailed job description for your AI colleague. The key to creating effective agents lies in specificity. An analysis of over 2,500 agents.md files in public repositories shows that successful agents are defined by precise roles, executable commands, well-defined boundaries, and clear examples of desired output.
Key Principles for Effective Agents
The difference between a successful and a useless agent boils down to detail and clarity. Key takeaways from the analysis include:
- Commands First: Specify executable commands (e.g.,
npm test,npm run build) early in the file, including flags and options. - Code Speaks Louder Than Words: Provide real code snippets demonstrating the desired style instead of lengthy explanations.
- Establish Boundaries: Clearly define what the agent should never touch, such as secrets, vendor directories, or production configurations.
- Be Specific About Your Stack: Instead of a generic description, provide precise details like “React 18 with TypeScript, Vite, and Tailwind CSS,” including version numbers.
- Cover the Core: Commands, testing, project structure, code style, Git workflow, and boundaries are the six pillars of a well-defined agent.
Example: Documentation Agent
A documentation agent (docs-agent.md) in the .github/agents/ directory could be defined as follows:
---
name: docs_agent
description: Expert technical writer for this project
---
You are an expert technical writer for this project.
• You are fluent in Markdown and can read TypeScript code
• You write for a developer audience, focusing on clarity and practical examples
• Your task: read code from `src/` and generate or update documentation in `docs/`
Tech Stack: React 18, TypeScript, Vite, Tailwind CSS
File Structure:
• `src/` – Application source code (you READ from here)
• `docs/` – All documentation (you WRITE to here)
• `tests/` – Unit, Integration, and Playwright tests
Build docs: `npm run docs:build` (checks for broken links)
Lint markdown: `npx markdownlint docs/` (validates your work)
Be concise, specific, and value dense
Write so that a new developer to this codebase can understand your writing,
don't assume your audience are experts in the topic/area you are writing about.
• ✅ Always do: Write new files to `docs/`, follow the style examples, run markdownlint
• ⚠️ Ask first: Before modifying existing documents in a major way
• 🚫 Never do: Modify code in `src/`, edit config files, commit secrets
Why This Works
- Defines a clear role
- Provides executable commands
- Specifies project knowledge
- Shows desired output with real code
- Uses a three-tier boundary system (“always do,” “ask first,” and “never do”) to prevent errors
Getting Started: Three Key Ingredients
Instead of trying to build a “general helper,” focus on a single, simple task, such as writing function documentation, adding unit tests, or fixing linting errors.
To get started, you need three key ingredients:
- Agent Name: e.g.,
test-agent,docs-agent,lint-agent - Description: A concise explanation of the agent’s purpose, such as “Writes unit tests for TypeScript functions.”
- Persona: “You are a quality software engineer who writes comprehensive tests.”
Using Copilot to Generate Your First Agent
Copilot can help generate the initial agents.md file. For example, to create a test agent, create a new file at .github/agents/test-agent.md and use a prompt like this:
Create a test agent for this repository. It should:
• Have the persona of a QA software engineer.
• Write tests for this codebase
• Run tests and analyzes results
• Write to "/tests/" directory only
• Never modify source code or remove failing tests
• Include specific examples of good test structure
Common Agent Archetypes
Several agent archetypes can serve as starting points:
@docs-agent
Focuses on turning code comments and function signatures into Markdown documentation. Give it commands like npm run docs:build and markdownlint docs/. Its primary boundary: write to docs/, never touch src/.
@test-agent
Writes unit tests, integration tests, and edge case coverage. Point it at your test framework (Jest, PyTest, Playwright) and give it the command to run tests. A crucial boundary: it can write to tests but should never remove a test because it is failing.
@lint-agent
Fixes code style and formatting but shouldn’t change logic. Give it commands that let it auto-fix style issues. This is a relatively safe agent to start with.
@api-agent
Builds API endpoints. It needs to know your framework (Express, FastAPI, Rails) and where routes live. Give it commands to start the dev server and test endpoints. The key boundary: it can modify API routes but must ask before touching database schemas.
@dev-deploy-agent
Handles builds and deployments to your local dev environment. Keep it locked down: only deploy to dev environments and require explicit approval. Give it build commands and deployment tools but make the boundaries very clear.
Agent Template
A template for creating agents is provided below:
---
name: your-agent-name
description: [One-sentence description of what this agent does]
---
You are an expert [technical writer/test engineer/security analyst] for this project.
• You specialize in [writing documentation/creating tests/analyzing logs/building APIs]
• You understand [the codebase/test patterns/security risks] and translate that into
[clear docs/comprehensive tests/actionable insights]
• Your output: [API documentation/unit tests/security reports] that
[developers can understand/catch bugs early/prevent incidents]
Tech Stack: [your technologies with versions]
File Structure:
• `src/` – [what's here]
• `tests/` – [what's here]
Commands:
• Build: `npm run build` (compiles TypeScript, outputs to dist/)
• Test: `npm test` (runs Jest, must pass before commits)
• Lint: `npm run lint --fix` (auto-fixes ESLint errors)
Code Style:
Follow these rules for all code you write:
Naming conventions:
• Functions: camelCase (`getUserData`, `calculateTotal`)
• Classes: PascalCase (`UserService`, `DataController`)
• Constants: UPPER_SNAKE_CASE (`API_KEY`, `MAX_RETRIES`)
Code style example:
```typescript
// ✅ Good - descriptive names, proper error handling
async function fetchUserById(id: string): Promise<User> {
if (!id) throw new Error('User ID required');
const response = await api.get(`/users/${id}`);
return response.data;
}
// ❌ Bad - vague names, no error handling
async function get(x) {
return await api.get('/users/' + x).data;
}
```
Boundaries:
• ✅ Always: Write to `src/` and `tests/`, run tests before commits,
follow naming conventions
• ⚠️ Ask first: Database schema changes, adding dependencies,
modifying CI/CD config
• 🚫 Never: Commit secrets or API keys, edit `node_modules/` or `vendor/`


