Skip to content

Commit 5656b87

Browse files
committed
preselected SKILLs are supported
1 parent a7a62fb commit 5656b87

4 files changed

Lines changed: 90 additions & 0 deletions

File tree

version.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2026.05.10.01

zsmith/src/main/java/airhacks/zsmith/agent/boundary/Agent.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.List;
77
import java.util.Map;
88
import java.util.Objects;
9+
import java.util.Set;
910
import java.util.concurrent.ConcurrentHashMap;
1011
import java.util.concurrent.ExecutorService;
1112
import java.util.concurrent.Executors;
@@ -178,6 +179,10 @@ public Agent withSkills(String path) {
178179
return withSkills(new SkillStore(List.of(Path.of(path))));
179180
}
180181

182+
public Agent withSkillsNamed(String... names) {
183+
return withSkills(SkillStore.forAgent(this.name).filtered(Set.of(names)));
184+
}
185+
181186
public Agent withSkills(SkillStore store) {
182187
var catalog = store.catalog();
183188
var enrichedPrompt = catalog.isEmpty()

zsmith/src/main/java/airhacks/zsmith/skills/boundary/SkillStore.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.LinkedHashMap;
77
import java.util.List;
88
import java.util.Map;
9+
import java.util.Set;
910
import java.util.stream.Collectors;
1011

1112
import airhacks.zsmith.configuration.control.ZCfg;
@@ -28,6 +29,23 @@ public SkillStore(List<Path> searchDirectories) {
2829
Log.info("skills loaded: " + this.skills.size());
2930
}
3031

32+
SkillStore(Map<String, Skill> skills) {
33+
this.skills = new LinkedHashMap<>(skills);
34+
}
35+
36+
public SkillStore filtered(Set<String> names) {
37+
var retained = new LinkedHashMap<String, Skill>();
38+
for (var name : names) {
39+
var skill = this.skills.get(name);
40+
if (skill == null) {
41+
Log.warning("skill not found, skipped from filter: " + name);
42+
continue;
43+
}
44+
retained.put(name, skill);
45+
}
46+
return new SkillStore(retained);
47+
}
48+
3149
public static SkillStore forAgent(String agentName) {
3250
var userHome = System.getProperty("user.home");
3351
var dirs = List.of(

zsmith/src/test/java/SkillStoreTest.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.List;
66

77
import java.util.Objects;
8+
import java.util.Set;
89

910
import airhacks.zsmith.skills.boundary.SkillStore;
1011

@@ -17,6 +18,9 @@ void main() throws IOException {
1718
emptyDirectoryProducesEmptyCatalog();
1819
nonExistentDirectoryIsIgnored();
1920
loadReturnsNullForUnknownSkill();
21+
filteredKeepsOnlyNamedSkills();
22+
filteredIgnoresUnknownNames();
23+
filteredEmptySetProducesEmptyStore();
2024
}
2125

2226
void loadSkillWithFrontmatter() throws IOException {
@@ -160,6 +164,68 @@ void loadReturnsNullForUnknownSkill() throws IOException {
160164
}
161165
}
162166

167+
void filteredKeepsOnlyNamedSkills() throws IOException {
168+
var tempDir = Files.createTempDirectory("zunit-skillstore");
169+
try {
170+
writeSkill(tempDir, "alpha", "alpha description", "alpha content");
171+
writeSkill(tempDir, "beta", "beta description", "beta content");
172+
writeSkill(tempDir, "gamma", "gamma description", "gamma content");
173+
174+
var store = new SkillStore(List.of(tempDir));
175+
var filtered = store.filtered(Set.of("alpha", "gamma"));
176+
177+
assert filtered.allSkills().size() == 2 : "expected 2 skills but got " + filtered.allSkills().size();
178+
assert filtered.load("alpha") != null : "alpha should be present";
179+
assert filtered.load("gamma") != null : "gamma should be present";
180+
assert filtered.load("beta") == null : "beta should have been filtered out";
181+
} finally {
182+
deleteRecursively(tempDir);
183+
}
184+
}
185+
186+
void filteredIgnoresUnknownNames() throws IOException {
187+
var tempDir = Files.createTempDirectory("zunit-skillstore");
188+
try {
189+
writeSkill(tempDir, "alpha", "alpha description", "alpha content");
190+
191+
var store = new SkillStore(List.of(tempDir));
192+
var filtered = store.filtered(Set.of("alpha", "missing"));
193+
194+
assert filtered.allSkills().size() == 1 : "expected 1 skill but got " + filtered.allSkills().size();
195+
assert filtered.load("alpha") != null : "alpha should be present";
196+
assert filtered.load("missing") == null : "missing should not be present";
197+
} finally {
198+
deleteRecursively(tempDir);
199+
}
200+
}
201+
202+
void filteredEmptySetProducesEmptyStore() throws IOException {
203+
var tempDir = Files.createTempDirectory("zunit-skillstore");
204+
try {
205+
writeSkill(tempDir, "alpha", "alpha description", "alpha content");
206+
207+
var store = new SkillStore(List.of(tempDir));
208+
var filtered = store.filtered(Set.of());
209+
210+
assert filtered.allSkills().isEmpty() : "expected empty filtered store";
211+
assert "".equals(filtered.catalog()) : "expected empty catalog but got: " + filtered.catalog();
212+
} finally {
213+
deleteRecursively(tempDir);
214+
}
215+
}
216+
217+
static void writeSkill(Path baseDir, String name, String description, String content) throws IOException {
218+
var skillDir = baseDir.resolve(name);
219+
Files.createDirectories(skillDir);
220+
Files.writeString(skillDir.resolve("SKILL.md"), """
221+
---
222+
name: %s
223+
description: %s
224+
---
225+
%s
226+
""".formatted(name, description, content));
227+
}
228+
163229
static void deleteRecursively(Path path) throws IOException {
164230
try (var walk = Files.walk(path)) {
165231
walk.sorted(Comparator.reverseOrder())

0 commit comments

Comments
 (0)