package skill import ( "bytes" "fmt" "os" "path/filepath" "strings" "gopkg.in/yaml.v3" ) type Meta struct { Name string `yaml:"name"` Description string `yaml:"description"` Path string `yaml:"-"` } type Skill struct { Meta Body string // full SKILL.md body (instructions) } // Discover scans skillsDir and returns metadata for all valid skills. func Discover(skillsDir string) ([]Meta, error) { entries, err := os.ReadDir(skillsDir) if err != nil { return nil, err } var metas []Meta for _, e := range entries { if !e.IsDir() { continue } path := filepath.Join(skillsDir, e.Name(), "SKILL.md") data, err := os.ReadFile(path) if err != nil { continue } meta, err := parseMeta(data) if err != nil { continue } meta.Path = filepath.Join(skillsDir, e.Name()) metas = append(metas, meta) } return metas, nil } // Load returns a fully loaded skill including body. func Load(skillDir string) (*Skill, error) { data, err := os.ReadFile(filepath.Join(skillDir, "SKILL.md")) if err != nil { return nil, err } meta, err := parseMeta(data) if err != nil { return nil, err } meta.Path = skillDir body := extractBody(data) return &Skill{Meta: meta, Body: body}, nil } // ToXML generates XML for agent system prompts. func ToXML(metas []Meta) string { var sb strings.Builder sb.WriteString("\n") for _, m := range metas { fmt.Fprintf(&sb, " \n %s\n %s\n %s/SKILL.md\n \n", m.Name, m.Description, m.Path) } sb.WriteString("") return sb.String() } func parseMeta(data []byte) (Meta, error) { var meta Meta if !bytes.HasPrefix(data, []byte("---")) { return meta, fmt.Errorf("missing frontmatter") } parts := bytes.SplitN(data, []byte("---"), 3) if len(parts) < 3 { return meta, fmt.Errorf("invalid frontmatter") } return meta, yaml.Unmarshal(parts[1], &meta) } func extractBody(data []byte) string { parts := bytes.SplitN(data, []byte("---"), 3) if len(parts) < 3 { return string(data) } return strings.TrimSpace(string(parts[2])) }