Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Lm655hdgDo5xsSLNomgdsy
65 lines
2.8 KiB
Java
65 lines
2.8 KiB
Java
package com.ankurm.agentskills;
|
|
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.List;
|
|
|
|
import com.ankurm.agentskills.support.Transcript;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.junit.jupiter.api.io.TempDir;
|
|
import org.springaicommunity.agent.tools.SkillsTool;
|
|
import org.springaicommunity.agent.utils.Skills;
|
|
import org.springframework.ai.tool.ToolCallback;
|
|
|
|
/** What the loader does with an unknown name, a duplicate name and a SKILL.md with no description. */
|
|
class SkillLoadingEdgeCasesTest {
|
|
|
|
@TempDir
|
|
Path dir;
|
|
|
|
@Test
|
|
void recordWhatActuallyHappens() throws Exception {
|
|
Path dup = Files.createDirectories(dir.resolve("copy/release-notes"));
|
|
Files.writeString(dup.resolve("SKILL.md"),
|
|
"---\nname: release-notes\ndescription: A second skill that reuses an existing name.\n---\n\nSECOND COPY BODY\n");
|
|
Path nodesc = Files.createDirectories(dir.resolve("copy/no-description"));
|
|
Files.writeString(nodesc.resolve("SKILL.md"), "---\nname: no-description\n---\n\nBODY WITHOUT A DESCRIPTION\n");
|
|
|
|
try (Transcript t = new Transcript("03-loading-edge-cases.txt", "Unknown, duplicate and description-less skills")) {
|
|
ToolCallback bundled = SkillClients.bundledSkills();
|
|
|
|
t.line("== unknown skill name");
|
|
t.line("call Skill(command=\"does-not-exist\") returns: %s", oneLine(call(bundled, "does-not-exist")));
|
|
|
|
t.blank();
|
|
t.line("== the same name in two directories (classpath skills first, then the copy)");
|
|
ToolCallback both = SkillsTool.builder()
|
|
.addSkillsResource(new org.springframework.core.io.ClassPathResource("skills"))
|
|
.addSkillsDirectory(dir.resolve("copy").toString())
|
|
.build();
|
|
String loaded = call(both, "release-notes");
|
|
t.line("body served for release-notes contains the first copy ('Breaking') : %s", loaded.contains("Breaking"));
|
|
t.line("body served for release-notes contains the second copy : %s", loaded.contains("SECOND COPY BODY"));
|
|
|
|
t.blank();
|
|
t.line("== a SKILL.md with no description");
|
|
List<SkillsTool.Skill> skills = Skills.loadDirectory(dir.resolve("copy").toString());
|
|
skills.forEach(s -> t.line("loaded: name=%s frontMatterKeys=%s", s.name(), s.frontMatter().keySet()));
|
|
String listing = both.getToolDefinition().description();
|
|
t.line("'no-description' appears in the tool description : %s", listing.contains("no-description"));
|
|
|
|
t.blank();
|
|
t.line("== the allowed-tools key in sql-review's frontmatter");
|
|
t.line("echoed into the listing as <allowed-tools> : %s", bundled.getToolDefinition().description().contains("<allowed-tools>Bash</allowed-tools>"));
|
|
}
|
|
}
|
|
|
|
private static String call(ToolCallback tool, String command) {
|
|
return tool.call("{\"command\":\"" + command + "\"}");
|
|
}
|
|
|
|
private static String oneLine(String s) {
|
|
return s.replace("\n", "\\n");
|
|
}
|
|
}
|