How to Auto-Generate AI Coding Agent Instructions from a Single AGENTS.md
Stop manually syncing .cursorrules, CLAUDE.md, and .copilot-instructions. Learn how to auto-generate AI coding agent instructions from a single AGENTS.md source of truth.
Maintaining customized instruction files for every AI coding assistant in your repository is a recipe for configuration drift. If you want to keep Cursor, Claude Code, GitHub Copilot, and Gemini Code Assist aligned with your codebase's architectural standards, you are likely maintaining a .cursorrules, a CLAUDE.md, a .github/copilot-instructions.md, and a GEMINI.md simultaneously.
When you update your testing strategy or migrate from Node.js to Bun, you have to rewrite those instructions in four different places. If you forget even one, your AI agents will start hallucinating deprecated patterns.
The solution is to auto-generate AI coding agent instructions from a single, unified source of truth: AGENTS.md.
Here is how to build a zero-dependency TypeScript compiler that parses a single master instruction file and spits out perfectly tailored, tool-specific configuration files for every coding agent on the market.
The End Result
You modify a single file, AGENTS.md, which contains your core architectural decisions, technology stack, and agent-specific optimizations. You run a single command (or let a pre-commit hook handle it):
bun run tools/compile-agents.tsInstantly, your repository's root is updated with target-specific files, stripped of irrelevant instructions to save context window tokens:
# Outputs generated automatically:
├── .cursorrules # Optimized for multi-file Composer edits
├── CLAUDE.md # Short, sharp CLI commands for Claude Code
├── GEMINI.md # Custom rules for Gemini Code Assist
└── .github/copilot-instructions.md # Embedded context for GitHub CopilotThe Architecture
To achieve this, we use conditional compilation comments within a standard Markdown document. This allows us to keep the master file readable as a standard document, while still allowing the compiler to selectively extract blocks based on the target agent's capabilities.
Why You Must Auto-Generate AI Coding Agent Instructions
Every AI coding tool handles context ingestion differently.
- Claude Code (
CLAUDE.md) expects strict, actionable CLI commands for building, linting, and running tests. It does not need high-level behavioral essays; it needs operational constraints. - Cursor (
.cursorrulesor.cursor/rules/) thrives on structural paradigms, rules for multi-file generation, and preventing typical LLM laziness. If you want to see how Cursor's internal logic has evolved to handle these rules, check out our deep-dive on /blog/2026-05-21-under-the-hood-of-cursor-composer-25-what-actually-changed-for-ai-coding-agents. - GitHub Copilot (
.github/copilot-instructions.md) is heavily constrained by context window limitations in inline completions. Bloating it with 500 lines of CLI commands ruins its performance. It needs tight, code-style rules.
If you attempt to feed a single, massive 2,000-line Markdown file to all of them, you waste expensive context tokens and confuse the models. By deciding to auto-generate AI coding agent instructions, you compile highly optimized, stripped-down instruction subsets targeted directly at each tool's unique execution model.
Step 1: Designing the AGENTS.md Schema
We will use HTML comments as compilation directives. This keeps our master file completely valid, standard Markdown that renders beautifully in GitHub or your IDE preview.
We will use two directives:
<!-- @only <agent1>,<agent2> -->- Only include this block for the specified agents.<!-- @exclude <agent1> -->- Exclude this block for the specified agents.<!-- @end -->- Closes the conditional block.
Here is what your master AGENTS.md looks like:
# Project Standards & Agent Instructions
This is the central source of truth for all AI coding assistants in this repository.
*Do not edit generated agent configs directly. Edit this file and run compilation.*
## Core Tech Stack
- Runtime: Bun (v1.1+)
- Language: TypeScript (Strict mode)
- Framework: Hono
<!-- @only cursor -->
## Cursor Composer Guidelines
- When refactoring, always use multi-file edits.
- Do not ask for permission before creating associated unit tests.
<!-- @end -->
<!-- @only claude -->
## Claude Code Commands
- Build project: `bun run build`
- Run tests: `bun test`
- Lint check: `bun run lint`
- Fix lint: `bun run lint --fix`
<!-- @end -->
<!-- @exclude copilot -->
## Complex Architecture Rules
- We use a strict Hexagonal Architecture.
- Domain models must have zero external dependencies.
- Infrastructure adapters must implement explicit domain interfaces.
<!-- @end -->
## Code Style
- Prefer functional programming patterns over classes.
- Avoid default exports; use named exports exclusively.Step 2: Writing the Compiler Engine
We will write a zero-dependency TypeScript script. This script can be executed natively using Bun, Deno, or ts-node. We will use Bun for this demonstration, but it is easily adaptable to any modern JS/TS runtime. For a look at how runtimes compare in 2026, check out /blog/2026-05-25-bun-vs-nodejs-vs-deno-the-definitive-2026-runtime-showdown.
Create a file named tools/compile-agents.ts:
import * as fs from 'fs';
import * as path from 'path';
interface AgentTarget {
name: string;
outputPath: string;
headerComment: string;
}
const TARGETS: AgentTarget[] = [
{
name: 'cursor',
outputPath: '.cursorrules',
headerComment: '# GENERATED FROM AGENTS.md - DO NOT EDIT DIRECTLY\n'
},
{
name: 'claude',
outputPath: 'CLAUDE.md',
headerComment: '<!-- GENERATED FROM AGENTS.md - DO NOT EDIT DIRECTLY -->\n'
},
{
name: 'copilot',
outputPath: '.github/copilot-instructions.md',
headerComment: '<!-- GENERATED FROM AGENTS.md - DO NOT EDIT DIRECTLY -->\n'
},
{
name: 'gemini',
outputPath: 'GEMINI.md',
headerComment: '<!-- GENERATED FROM AGENTS.md - DO NOT EDIT DIRECTLY -->\n'
}
];
function compile(sourcePath: string, target: AgentTarget): string {
if (!fs.existsSync(sourcePath)) {
throw new Error(`Source file not found at: ${sourcePath}`);
}
const content = fs.readFileSync(sourcePath, 'utf-8');
const lines = content.split('\n');
const outputLines: string[] = [];
// Track conditional compilation state
const skipStack: boolean[] = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
// Match @only directive
const onlyMatch = trimmed.match(/^<!--\s*@only\s+([a-zA-Z0-9,\s]+)\s*-->$/);
if (onlyMatch) {
const allowedAgents = onlyMatch[1].split(',').map(s => s.trim().toLowerCase());
const isAllowed = allowedAgents.includes(target.name);
skipStack.push(!isAllowed);
continue;
}
// Match @exclude directive
const excludeMatch = trimmed.match(/^<!--\s*@exclude\s+([a-zA-Z0-9,\s]+)\s*-->$/);
if (excludeMatch) {
const excludedAgents = excludeMatch[1].split(',').map(s => s.trim().toLowerCase());
const isExcluded = excludedAgents.includes(target.name);
skipStack.push(isExcluded);
continue;
}
// Match block end directive
if (trimmed.match(/^<!--\s*@end\s*-->$/)) {
if (skipStack.length === 0) {
console.warn(`Warning: Unexpected <!-- @end --> tag at line ${i + 1}`);
} else {
skipStack.pop();
}
continue;
}
// If any level of the skip stack is true, omit the line
const shouldSkip = skipStack.some(val => val === true);
if (!shouldSkip) {
outputLines.push(line);
}
}
if (skipStack.length > 0) {
console.warn(`Warning: Unclosed conditional compilation block in AGENTS.md`);
}
// Clean up trailing/leading whitespace and append warning header
const cleanedContent = outputLines.join('\n').trim();
return `${target.headerComment}\n${cleanedContent}\n`;
}
function main() {
const rootDir = process.cwd();
const sourceFile = path.join(rootDir, 'AGENTS.md');
console.log('Starting compilation of agent instructions...');
for (const target of TARGETS) {
try {
const compiledContent = compile(sourceFile, target);
const targetFullPath = path.join(rootDir, target.outputPath);
// Ensure target directory exists (e.g., .github/)
const targetDir = path.dirname(targetFullPath);
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
fs.writeFileSync(targetFullPath, compiledContent, 'utf-8');
console.log(`✅ Successfully compiled: ${target.outputPath}`);
} catch (error: any) {
console.error(`❌ Failed to compile target ${target.name}:`, error.message);
}
}
}
main();Step 3: Automating with Git Hooks
To guarantee your instruction files never drift out of sync, you should compile them automatically before every commit. This ensures that when a developer updates AGENTS.md, the generated files are updated in the same commit.
We can set this up easily using standard Git hooks. Create or edit .git/hooks/pre-commit:
#!/bin/sh
# Compile the agent files
bun run tools/compile-agents.ts
# Stage the auto-generated files if they were modified
git add .cursorrules CLAUDE.md .github/copilot-instructions.md GEMINI.mdMake the hook executable:
chmod +x .git/hooks/pre-commitNow, any changes to your core system instructions are automatically propagated and staged whenever you commit.
Deep Dive: Structuring High-Value Instructions
To get the most out of your compiled files, you need to write instructions that actually work. General advice like "write clean code" is a waste of context. Instead, write highly targeted directives.
For writing optimized system prompts for TypeScript applications, you can read our guide on /blog/2026-05-20-beyond-copy-paste-optimizing-gpt-4o-system-prompts-for-typescript.
Here is an example of highly effective, concrete rules structured inside our AGENTS.md format:
<!-- @only cursor,claude -->
## TypeScript & Async Patterns
- Always use `Promise.allSettled` instead of `Promise.all` when running parallel processes where partial success is acceptable. Refer to our async patterns guide when building high-throughput services.
- Never write explicit loops for array mutations; use map/filter/reduce.
<!-- @end -->(For more advanced async patterns to feed your agent compiler, read our analysis on /blog/2026-05-22-beyond-promiseall-advanced-typescript-async-await-patterns-for-high-throughput-s).
Common Gotchas & How to Avoid Them
1. Token Bloat in Inline Completions
If you include your entire system architecture inside the GitHub Copilot file, you will experience extreme latency during inline completions. Copilot sends these instructions with every single keypress context.
- Fix: Use
<!-- @exclude copilot -->liberally to strip heavy guides, architecture essays, and CLI commands from the Copilot target. Keep.github/copilot-instructions.mdunder 50 lines of strictly formatting rules.
2. Local AI Tool Ingestion Quirks
If you are running local LLMs using Continue or Ollama, they may not automatically read .cursorrules or CLAUDE.md.
- Fix: If you run a local stack, compile to
.continue/config.jsonsystem prompts directly. You can read more about setting up local environments on /blog/2026-05-27-escaping-rate-limits-the-ultimate-local-ai-coding-setup-with-ollama-and-continue.
3. Nested Directive Collisions
The regex compiler written above handles flat blocks. If you write nested blocks like this:
<!-- @only cursor -->
<!-- @exclude copilot -->
...
<!-- @end -->
<!-- @end -->The simple compiler's stack logic will execute correctly, but it adds unnecessary complexity and can lead to unclosed tag warnings. Keep your compilation directives flat and clean.
4. Git Conflicts on Generated Files
If multiple developers are working on different branches, auto-generated files can cause annoying merge conflicts.
- Fix: Ensure that the
compile-agents.tsscript runs deterministically (e.g., sorting keys or using strict formatting). Since the script outputs exact content from a single master file, resolving the conflict onAGENTS.mdand running the compiler will instantly resolve the conflicts on all downstream files.
Related Posts
Secure MCP Server Implementation: Exposing SaaS APIs to AI Agents Safely
Learn how to build a secure mcp server implementation in TypeScript. Control authentication, restrict access, and enforce server-side guardrails for AI agents.
Escaping Rate Limits: The Ultimate Local AI Coding Setup with Ollama and Continue
Tired of hitting Claude and Cursor rate limits? Learn how to configure a high-performance, private, local ai coding setup with ollama and continue for professional typescript development.
Stop Scraping, Start Serving: A Guide to Model Context Protocol Next.js Integration
Tired of LLM bots scraping your Next.js site? Learn how to build a native Model Context Protocol (MCP) server inside your Next.js App Router to serve structured data directly to AI agents.