Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,54 @@ agent = create_deep_agent(

See our [quickstarts repo](https://github.com/langchain-ai/deepagents-quickstarts) for more examples.

### AGENTS.md Support

DeepAgents CLI automatically loads [AGENTS.md](https://agents.md/) configuration files, a standardized format for providing AI coding agents with project-specific context and instructions. This feature works alongside the existing `agent.md` files:

**What is AGENTS.md?**
- An open-source standard for AI agent configuration (20,000+ projects use it)
- Provides build steps, tests, coding conventions, and project-specific guidance
- Works across multiple AI coding agents (Cursor, Aider, GitHub Copilot, etc.)

**How it works in deepagents:**
- Automatically discovers AGENTS.md files hierarchically from your current directory
- Supports monorepos: nested AGENTS.md files provide subproject-specific context
- Combines with user and project `agent.md` files for comprehensive agent configuration
- Files are loaded from most specific (current directory) to least specific (near project root)

**Example structure:**
```
my-project/
β”œβ”€β”€ AGENTS.md # Not loaded (at project root)
β”œβ”€β”€ packages/
β”‚ β”œβ”€β”€ AGENTS.md # Loaded: General package guidelines
β”‚ └── package-a/
β”‚ β”œβ”€β”€ AGENTS.md # Loaded: Package A specific instructions
β”‚ └── src/ # ← You are here
```

When working in `packages/package-a/src/`, the agent automatically loads both AGENTS.md files (from `package-a` and `packages`), combining them with your personal and project agent.md preferences.

**Creating an AGENTS.md file:**
```markdown
# Project Name

## Build
Run `npm install` to install dependencies.
Run `npm run build` to build the project.

## Testing
Run `npm test` to run the test suite.
All tests must pass before committing.

## Code Style
- Use TypeScript for all new code
- Follow the existing code style
- Add JSDoc comments for public APIs
```

No configuration needed - just create an AGENTS.md file and deepagents will automatically include it in the agent's context.

### `tools`

Provide custom tools to your agent (in addition to [Built-in Tools](#built-in-tools)):
Expand Down
58 changes: 53 additions & 5 deletions libs/deepagents-cli/deepagents_cli/agent_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class AgentMemoryState(AgentState):
project_memory: NotRequired[str]
"""Project-specific context (loaded from project root)."""

agents_md_memory: NotRequired[str]
"""AGENTS.md context loaded hierarchically from current directory up to project root."""


class AgentMemoryStateUpdate(TypedDict):
"""A state update for the agent memory middleware."""
Expand All @@ -34,6 +37,9 @@ class AgentMemoryStateUpdate(TypedDict):
project_memory: NotRequired[str]
"""Project-specific context (loaded from project root)."""

agents_md_memory: NotRequired[str]
"""AGENTS.md context loaded hierarchically from current directory up to project root."""


# Long-term Memory Documentation
# Note: Claude Code loads CLAUDE.md files hierarchically and combines them (not precedence-based):
Expand All @@ -50,15 +56,23 @@ class AgentMemoryStateUpdate(TypedDict):

**User Memory Location**: `{agent_dir_absolute}` (displays as `{agent_dir_display}`)
**Project Memory Location**: {project_memory_info}
**AGENTS.md Support**: {agents_md_info}

Your system prompt is loaded from TWO sources at startup:
Your system prompt is loaded from THREE sources at startup:
1. **User agent.md**: `{agent_dir_absolute}/agent.md` - Your personal preferences across all projects
2. **Project agent.md**: Loaded from project root if available - Project-specific instructions
3. **AGENTS.md**: Standard AI agent configuration files loaded hierarchically from current directory

Project-specific agent.md is loaded from these locations (both combined if both exist):
- `[project-root]/.deepagents/agent.md` (preferred)
- `[project-root]/agent.md` (fallback, but also included if both exist)

AGENTS.md files follow the agents.md specification (https://agents.md/):
- Searched hierarchically from current working directory up to (but not including) project root
- Multiple AGENTS.md files are combined from most specific (current dir) to least specific
- Provides standardized project context, build steps, tests, and conventions for AI agents
- For monorepos, nested AGENTS.md files in subdirectories provide subproject-specific context

**When to CHECK/READ memories (CRITICAL - do this FIRST):**
- **At the start of ANY new session**: Check both user and project memories
- User: `ls {agent_dir_absolute}`
Expand Down Expand Up @@ -165,7 +179,11 @@ class AgentMemoryStateUpdate(TypedDict):

<project_memory>
{project_memory}
</project_memory>"""
</project_memory>

<agents_md>
{agents_md_memory}
</agents_md>"""


class AgentMemoryMiddleware(AgentMiddleware):
Expand Down Expand Up @@ -214,7 +232,7 @@ def before_agent(
) -> AgentMemoryStateUpdate:
"""Load agent memory from file before agent execution.

Loads both user agent.md and project-specific agent.md if available.
Loads user agent.md, project-specific agent.md, and AGENTS.md files if available.
Only loads if not already present in state.

Dynamically checks for file existence on every call to catch user updates.
Expand All @@ -224,7 +242,7 @@ def before_agent(
runtime: Runtime context.

Returns:
Updated state with user_memory and project_memory populated.
Updated state with user_memory, project_memory, and agents_md_memory populated.
"""
result: AgentMemoryStateUpdate = {}

Expand All @@ -242,6 +260,22 @@ def before_agent(
with contextlib.suppress(OSError, UnicodeDecodeError):
result["project_memory"] = project_path.read_text()

# Load AGENTS.md files if not already in state
if "agents_md_memory" not in state:
agents_md_files = self.settings.get_agents_md_files()
if agents_md_files:
agents_md_contents = []
for agents_md_path in agents_md_files:
with contextlib.suppress(OSError, UnicodeDecodeError):
content = agents_md_path.read_text()
# Add a header to indicate which file this is from
agents_md_contents.append(
f"# From: {agents_md_path}\n\n{content}"
)
if agents_md_contents:
# Combine all AGENTS.md files, separated by horizontal rules
result["agents_md_memory"] = "\n\n---\n\n".join(agents_md_contents)

return result

def _build_system_prompt(self, request: ModelRequest) -> str:
Expand All @@ -257,6 +291,7 @@ def _build_system_prompt(self, request: ModelRequest) -> str:
state = cast("AgentMemoryState", request.state)
user_memory = state.get("user_memory")
project_memory = state.get("project_memory")
agents_md_memory = state.get("agents_md_memory")
base_system_prompt = request.system_prompt

# Build project memory info for documentation
Expand All @@ -267,16 +302,28 @@ def _build_system_prompt(self, request: ModelRequest) -> str:
else:
project_memory_info = "None (not in a git project)"

# Build AGENTS.md info for documentation
if agents_md_memory:
agents_md_files = self.settings.get_agents_md_files()
num_files = len(agents_md_files)
if num_files == 1:
agents_md_info = f"Loaded 1 AGENTS.md file from `{agents_md_files[0].parent}`"
else:
agents_md_info = f"Loaded {num_files} AGENTS.md files hierarchically"
else:
agents_md_info = "No AGENTS.md files found"

# Build project deepagents directory path
if self.project_root:
project_deepagents_dir = str(self.project_root / ".deepagents")
else:
project_deepagents_dir = "[project-root]/.deepagents (not in a project)"

# Format memory section with both memories
# Format memory section with all memories
memory_section = self.system_prompt_template.format(
user_memory=user_memory if user_memory else "(No user agent.md)",
project_memory=project_memory if project_memory else "(No project agent.md)",
agents_md_memory=agents_md_memory if agents_md_memory else "(No AGENTS.md files found)",
)

system_prompt = memory_section
Expand All @@ -288,6 +335,7 @@ def _build_system_prompt(self, request: ModelRequest) -> str:
agent_dir_absolute=self.agent_dir_absolute,
agent_dir_display=self.agent_dir_display,
project_memory_info=project_memory_info,
agents_md_info=agents_md_info,
project_deepagents_dir=project_deepagents_dir,
)

Expand Down
55 changes: 55 additions & 0 deletions libs/deepagents-cli/deepagents_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,42 @@ def _find_project_agent_md(project_root: Path) -> list[Path]:
return paths


def _find_agents_md(start_path: Path | None = None, project_root: Path | None = None) -> list[Path]:
"""Find AGENTS.md files hierarchically from current directory up to project root.

Following the AGENTS.md specification:
- Searches from current working directory up to (but not including) project root
- For monorepos, nested AGENTS.md files take precedence over parent ones
- All found files are returned in order from most specific (current dir) to least (near project root)

Args:
start_path: Directory to start searching from. Defaults to current working directory.
project_root: Project root directory. Searching stops before reaching this. Defaults to None.

Returns:
List of paths to AGENTS.md files, ordered from most specific to least specific.
"""
current = Path(start_path or Path.cwd()).resolve()
agents_md_files = []

# If we have a project root, resolve it
if project_root:
project_root = project_root.resolve()

# Walk up the directory tree
for parent in [current, *list(current.parents)]:
# Stop before reaching project root (project root itself is not included)
if project_root and parent == project_root:
break

# Check for AGENTS.md in this directory
agents_md = parent / "AGENTS.md"
if agents_md.exists():
agents_md_files.append(agents_md)

return agents_md_files


@dataclass
class Settings:
"""Global settings and environment detection for deepagents-cli.
Expand Down Expand Up @@ -329,6 +365,25 @@ def ensure_project_skills_dir(self) -> Path | None:
skills_dir.mkdir(parents=True, exist_ok=True)
return skills_dir

def get_agents_md_files(self, start_path: Path | None = None) -> list[Path]:
"""Get all AGENTS.md files hierarchically from current directory to project root.

Following the AGENTS.md specification, this searches from the current working
directory (or start_path) up to (but not including) the project root, collecting
all AGENTS.md files found. Files are returned in order from most specific
(current directory) to least specific (nearest to project root).

For monorepos, this allows nested AGENTS.md files to provide subproject-specific
context while still including parent directory guidance.

Args:
start_path: Directory to start searching from. Defaults to current working directory.

Returns:
List of paths to AGENTS.md files, ordered from most specific to least specific.
"""
return _find_agents_md(start_path=start_path, project_root=self.project_root)


# Global settings instance (initialized once)
settings = Settings.from_environment()
Expand Down
Loading
Loading