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 递归扫描 skillsDir,返回所有包含 SKILL.md 的目录的元数据。 func Discover(skillsDir string) ([]Meta, error) { var metas []Meta filepath.Walk(skillsDir, func(path string, info os.FileInfo, err error) error { if err != nil || info.IsDir() || info.Name() != "SKILL.md" { return nil } data, err := os.ReadFile(path) if err != nil { return nil } meta, err := parseMeta(data) if err != nil { return nil } meta.Path = filepath.Dir(path) metas = append(metas, meta) return nil }) 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. // 如果 skill 有完整内容,会注入到 prompt 中。 func ToXML(metas []Meta) string { var sb strings.Builder sb.WriteString("\n") for _, m := range metas { // 尝试加载完整 skill 内容 s, err := Load(m.Path) if err == nil && s.Body != "" { fmt.Fprintf(&sb, " \n %s\n \n%s\n \n \n", m.Name, m.Description, s.Body) } else { fmt.Fprintf(&sb, " \n %s\n \n", m.Name, m.Description) } } 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])) }