diff --git a/CHANGELOG.md b/CHANGELOG.md index b75821a6..452c4d0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added support for `gpt-5.2` model from OpenAI - Added `django-crontask` integration and scheduler service scaffolding for periodic tasks. - Added GitHub CLI (`gh`) tool support in Git platform middleware for GitHub operations. +- Added support for `.agents/skills` directory as an additional location for project-specific skills, providing more flexibility in organizing skills alongside existing `.daiv/skills`, `.cursor/skills`, and `.claude/skills` directories. ### Changed diff --git a/README.md b/README.md index 64c8a75a..46d7f6c8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ DAIV is an open-source automation assistant designed to enhance developer produc - βš™οΈ **Configurable Behavior**: A `.daiv.yml` file in your repo's default branch lets you tailor DAIV's features (like toggling auto-issue addressing). - ⚑ **Slash Commands**: Command-based interactions for common tasks on issues and merge requests, such as help to list available commands, cloning issues to multiple repositories, etc. - πŸ”§ **MCP Tools**: Supporting [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) tools to extend the capabilities of the agents. -- 🎯 **Agent Skills**: Modular, reusable capabilities that give agents domain-specific expertise. Define custom workflows in your repository's `.daiv/skills/` directory, or use builtin skills like changelog maintenance and AGENTS.md generation. +- 🎯 **Agent Skills**: Modular, reusable capabilities that give agents domain-specific expertise. Define custom workflows in your repository's `.daiv/skills/`, `.agents/skills/`, `.cursor/skills/`, or `.claude/skills/` directory, or use builtin skills like changelog maintenance and AGENTS.md generation. - πŸ“¦ **Sandbox**: Running commands in a secure sandbox to allow the agents to perform actions on the codebase, such as installing/updating dependencies, generating translations, etc. with your own docker image. - πŸ“Š **Monitoring**: Monitoring the behavior of the agents to allow you to analyze the performance and identify potential issues. - πŸ€– **Providers Support**: [OpenAI](https://openai.com/api/), [Anthropic](https://www.anthropic.com/api), [Gemini](https://ai.google.dev/gemini) and [OpenRouter](https://openrouter.ai/) are the supported LLM providers. diff --git a/daiv/automation/agent/constants.py b/daiv/automation/agent/constants.py index 3832683f..42b8db39 100644 --- a/daiv/automation/agent/constants.py +++ b/daiv/automation/agent/constants.py @@ -9,9 +9,10 @@ DAIV_SKILLS_PATH = ".daiv/skills" CURSOR_SKILLS_PATH = ".cursor/skills" CLAUDE_CODER_SKILLS_PATH = ".claude/skills" +AGENTS_SKILLS_PATH = ".agents/skills" # Paths where the skills are stored in repository. -SKILLS_SOURCES = [DAIV_SKILLS_PATH, CURSOR_SKILLS_PATH, CLAUDE_CODER_SKILLS_PATH] +SKILLS_SOURCES = [DAIV_SKILLS_PATH, CURSOR_SKILLS_PATH, CLAUDE_CODER_SKILLS_PATH, AGENTS_SKILLS_PATH] # Path where the memory is stored in repository. DAIV_MEMORY_PATH = ".daiv/AGENTS.md" diff --git a/docs/ai-agents/skills.md b/docs/ai-agents/skills.md index f38b84d6..9e941baf 100644 --- a/docs/ai-agents/skills.md +++ b/docs/ai-agents/skills.md @@ -58,7 +58,16 @@ This architecture ensures minimal context usageβ€”agents only know Skills exist ## Skills Directory Structure -Skills live in the `.daiv/skills/` directory at the root of your repository: +Skills can be placed in any of the following directories at the root of your repository: + +- `.daiv/skills/` (default) +- `.agents/skills/` +- `.cursor/skills/` +- `.claude/skills/` + +DAIV automatically scans all these directories and loads skills from each. + +Example structure: ``` your-repository/ @@ -68,11 +77,13 @@ your-repository/ β”‚ β”‚ β”œβ”€β”€ SKILL.md # Required: YAML frontmatter + instructions β”‚ β”‚ β”œβ”€β”€ checklist.md # Optional: supporting documentation β”‚ β”‚ └── review.py # Optional: helper script -β”‚ β”œβ”€β”€ web-research/ -β”‚ β”‚ β”œβ”€β”€ SKILL.md -β”‚ β”‚ └── search_helper.py β”‚ └── creating-agents-md-file/ # Builtin skill (auto-copied) β”‚ └── SKILL.md +β”œβ”€β”€ .agents/ +β”‚ └── skills/ +β”‚ └── web-research/ +β”‚ β”œβ”€β”€ SKILL.md +β”‚ └── search_helper.py β”œβ”€β”€ src/ └── README.md ``` @@ -86,7 +97,7 @@ DAIV includes builtin Skills that are automatically loaded to the `.daiv/skills/ | `generating-agents-md` | Generates or updates an AGENTS.md file reflecting repository structure and conventions | Issues | | `skills-creator` | Creates a new Skill in the `.daiv/skills/` directory | Issues | -You can override builtin skills by creating a Skill with the same name in your project's `.daiv/skills/` directory. +You can override builtin skills by creating a Skill with the same name in any of your project's skills directories. --- diff --git a/tests/unit_tests/automation/agent/middlewares/test_skills.py b/tests/unit_tests/automation/agent/middlewares/test_skills.py index 3533eb14..9268cdd1 100644 --- a/tests/unit_tests/automation/agent/middlewares/test_skills.py +++ b/tests/unit_tests/automation/agent/middlewares/test_skills.py @@ -429,3 +429,46 @@ async def test_skill_tool_appends_named_arguments_when_missing_placeholder(self) assert isinstance(result, Command) messages = result.update["messages"] assert messages[1].content.endswith("\n\n$ARGUMENTS: --flag=1") + + async def test_discovers_skills_from_multiple_sources(self, tmp_path: Path): + from deepagents.backends.filesystem import FilesystemBackend + + repo_name = "repoX" + builtin = tmp_path / "builtin_skills" + (builtin / "skill-one").mkdir(parents=True) + (builtin / "skill-one" / "SKILL.md").write_text(_make_skill_md(name="skill-one", description="builtin one")) + + # Create skills in different source directories + daiv_skill = tmp_path / repo_name / ".daiv" / "skills" / "daiv-skill" + daiv_skill.mkdir(parents=True) + (daiv_skill / "SKILL.md").write_text(_make_skill_md(name="daiv-skill", description="from daiv")) + + agents_skill = tmp_path / repo_name / ".agents" / "skills" / "agents-skill" + agents_skill.mkdir(parents=True) + (agents_skill / "SKILL.md").write_text(_make_skill_md(name="agents-skill", description="from agents")) + + cursor_skill = tmp_path / repo_name / ".cursor" / "skills" / "cursor-skill" + cursor_skill.mkdir(parents=True) + (cursor_skill / "SKILL.md").write_text(_make_skill_md(name="cursor-skill", description="from cursor")) + + backend = FilesystemBackend(root_dir=tmp_path, virtual_mode=True) + middleware = SkillsMiddleware( + backend=backend, + sources=[f"/{repo_name}/.daiv/skills", f"/{repo_name}/.agents/skills", f"/{repo_name}/.cursor/skills"], + ) + runtime = _make_runtime(repo_working_dir=str(tmp_path / repo_name)) + + with patch("automation.agent.middlewares.skills.BUILTIN_SKILLS_PATH", builtin): + result = await middleware.abefore_agent({"messages": [HumanMessage(content="hello")]}, runtime, Mock()) + + assert result is not None + skills = {skill["name"]: skill for skill in result["skills_metadata"]} + assert set(skills) == {"skill-one", "daiv-skill", "agents-skill", "cursor-skill"} + assert skills["skill-one"]["description"] == "builtin one" + assert skills["skill-one"]["metadata"]["is_builtin"] is True + assert skills["daiv-skill"]["description"] == "from daiv" + assert "is_builtin" not in skills["daiv-skill"]["metadata"] + assert skills["agents-skill"]["description"] == "from agents" + assert "is_builtin" not in skills["agents-skill"]["metadata"] + assert skills["cursor-skill"]["description"] == "from cursor" + assert "is_builtin" not in skills["cursor-skill"]["metadata"]