feat: recursive skill discovery + tree-url type-root folding
- skills are found at any depth under the skills dir, so category-nested layouts (skills/<category>/<id>/SKILL.md) vendor correctly; the folder basename is the upstream id and the outermost SKILL.md wins over anything nested inside a skill folder - web-tree urls pointing directly at a type directory (.../tree/main/skills) now map that directory as the type root instead of nesting it under the default layout - verified against the real github.com/mattpocock/skills repository (36 skills across categories; include-selection by basename)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package discovery
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func writeTree(t *testing.T, root string, files map[string]string) {
|
||||
t.Helper()
|
||||
for rel, content := range files {
|
||||
p := filepath.Join(root, filepath.FromSlash(rel))
|
||||
os.MkdirAll(filepath.Dir(p), 0o755)
|
||||
os.WriteFile(p, []byte(content), 0o644)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanNestedSkillCategories(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
writeTree(t, dir, map[string]string{
|
||||
"skills/flat-skill/SKILL.md": "---\nname: Flat\n---\n",
|
||||
"skills/engineering/tdd/SKILL.md": "---\nname: TDD\n---\n",
|
||||
"skills/productivity/grill-me/SKILL.md": "---\nname: Grill Me\n---\n",
|
||||
"skills/engineering/tdd/reference.md": "extra artifact",
|
||||
"skills/deprecated/old/README.md": "no skill here",
|
||||
"commands/deploy.md": "---\nname: Deploy\n---\n",
|
||||
})
|
||||
|
||||
items, err := Scan(&FsTree{Root: dir}, Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := map[string]Item{}
|
||||
for _, it := range items {
|
||||
got[it.UpstreamID] = it
|
||||
}
|
||||
for _, want := range []string{"flat-skill", "tdd", "grill-me"} {
|
||||
it, ok := got[want]
|
||||
if !ok {
|
||||
t.Fatalf("skill %q not discovered (items: %+v)", want, items)
|
||||
}
|
||||
if it.Type != "skills" {
|
||||
t.Errorf("%q type = %q", want, it.Type)
|
||||
}
|
||||
}
|
||||
if _, ok := got["old"]; ok {
|
||||
t.Error("folder without SKILL.md must not be a skill")
|
||||
}
|
||||
tdd := got["tdd"]
|
||||
if tdd.RelPath != "skills/engineering/tdd" {
|
||||
t.Errorf("tdd RelPath = %q", tdd.RelPath)
|
||||
}
|
||||
if got["deploy"].Type != "commands" {
|
||||
t.Error("flat command types must still be discovered")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanOuterSkillWinsOverNested(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
writeTree(t, dir, map[string]string{
|
||||
"skills/bundle/SKILL.md": "---\nname: Bundle\n---\n",
|
||||
"skills/bundle/inner/SKILL.md": "---\nname: Inner\n---\n",
|
||||
"skills/bundle/inner/deep/SKILL.md": "---\nname: Deep\n---\n",
|
||||
})
|
||||
|
||||
items, err := Scan(&FsTree{Root: dir}, Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(items) != 1 || items[0].UpstreamID != "bundle" {
|
||||
t.Fatalf("outermost skill must win, got %+v", items)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user