package skill import ( "os" "path/filepath" "strings" "testing" ) func writeSkill(t *testing.T, root, dirName, frontmatter, body string) { t.Helper() dir := filepath.Join(root, dirName) if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } content := "---\n" + frontmatter + "---\n\n" + body if err := os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte(content), 0o644); err != nil { t.Fatal(err) } } func TestDiscover_ParsesFrontmatterAndBody(t *testing.T) { root := t.TempDir() writeSkill(t, root, "refactoring", "name: refactoring\ndescription: Vorgehen für größere Refactorings.\n", "# Refactoring\n\nSchritt 1: Tests lesen.\n") skills, err := Discover([]string{root}) if err != nil { t.Fatal(err) } if len(skills) != 1 { t.Fatalf("got %d skills, want 1", len(skills)) } s := skills[0] if s.Name != "refactoring" { t.Errorf("name = %q", s.Name) } if s.Description != "Vorgehen für größere Refactorings." { t.Errorf("description = %q", s.Description) } if !strings.Contains(s.Body, "Schritt 1: Tests lesen.") { t.Errorf("body missing expected content: %q", s.Body) } if strings.Contains(s.Body, "---") { t.Errorf("body should not contain frontmatter delimiters: %q", s.Body) } } func TestDiscover_LaterPathOverridesEarlierByName(t *testing.T) { global := t.TempDir() project := t.TempDir() writeSkill(t, global, "refactoring", "name: refactoring\ndescription: global version\n", "global body") writeSkill(t, project, "refactoring", "name: refactoring\ndescription: project version\n", "project body") skills, err := Discover([]string{global, project}) if err != nil { t.Fatal(err) } if len(skills) != 1 { t.Fatalf("got %d skills, want 1 (deduped by name)", len(skills)) } if skills[0].Description != "project version" { t.Errorf("description = %q, want project version to win (later path)", skills[0].Description) } } func TestDiscover_MissingPathIsSkippedNotError(t *testing.T) { skills, err := Discover([]string{"/does/not/exist/at/all"}) if err != nil { t.Fatal(err) } if len(skills) != 0 { t.Errorf("got %d skills, want 0", len(skills)) } } // TestRenderIndex_OmitsBody ist das M5-Fertig-Kriterium aus Abschnitt 6: ein // Skill wird erst bei Bedarf geladen — der Index darf nur Name+Description // enthalten, ein Token-Delta zum vollen Body muss messbar sein. func TestRenderIndex_OmitsBody(t *testing.T) { root := t.TempDir() longBody := strings.Repeat("Dies ist eine lange Anleitung. ", 200) // ~1400 Zeichen writeSkill(t, root, "refactoring", "name: refactoring\ndescription: kurze Beschreibung\n", longBody) skills, err := Discover([]string{root}) if err != nil { t.Fatal(err) } index := RenderIndex(skills) if strings.Contains(index, longBody) { t.Fatal("index must not contain the full skill body") } if !strings.Contains(index, "refactoring") || !strings.Contains(index, "kurze Beschreibung") { t.Errorf("index missing name/description: %q", index) } if len(index) >= len(longBody) { t.Errorf("index (%d bytes) should be far smaller than the body (%d bytes)", len(index), len(longBody)) } }