diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/File/AgentFileSkillsSource.cs b/dotnet/src/Microsoft.Agents.AI/Skills/File/AgentFileSkillsSource.cs index 54a5dec10cc..430977bf276 100644 --- a/dotnet/src/Microsoft.Agents.AI/Skills/File/AgentFileSkillsSource.cs +++ b/dotnet/src/Microsoft.Agents.AI/Skills/File/AgentFileSkillsSource.cs @@ -162,7 +162,10 @@ private static void SearchDirectoriesForSkills(string directory, List re string skillFilePath = Path.Combine(directory, SkillFileName); if (File.Exists(skillFilePath)) { + // Once a SKILL.md is found, this directory is the skill root. + // Subdirectories are part of this skill and should not be treated as independent skill roots. results.Add(Path.GetFullPath(directory)); + return; } if (currentDepth >= MaxSkillDirectorySearchDepth) diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillLoaderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillLoaderTests.cs index 6eb561c5b69..b001b46504e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillLoaderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillLoaderTests.cs @@ -1199,4 +1199,27 @@ public async Task GetSkillsAsync_SymlinkedFileInRealDirectory_SkipsSymlinkedFile Assert.Equal("references/legit.md", skill.GetTestResources()![0].Name); } #endif + + [Fact] + public async Task GetSkillsAsync_NestedSkillMd_DoesNotTreatSubdirectoryAsIndependentSkillAsync() + { + // Arrange — parent has SKILL.md; subdirectory also has SKILL.md with a name + // matching its directory so it would pass validation if discovered. + // Only the parent should be discovered as a skill root. + string parentSkillDir = this.CreateSkillDirectory("parent-skill", "Parent skill", "Parent body."); + string childDir = Path.Combine(parentSkillDir, "child"); + Directory.CreateDirectory(childDir); + File.WriteAllText( + Path.Combine(childDir, "SKILL.md"), + "---\nname: child\ndescription: Child skill\n---\nChild body."); + + var source = new AgentFileSkillsSource(this._testRoot, s_noOpExecutor); + + // Act + var skills = await source.GetSkillsAsync(); + + // Assert — only the parent skill is discovered; the nested child is not an independent skill + Assert.Single(skills); + Assert.Equal("parent-skill", skills[0].Frontmatter.Name); + } }