mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-04-21 18:26:38 +02:00
115 lines
3.1 KiB
Go
115 lines
3.1 KiB
Go
package skillpackage
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// ExtractSkillMDFrontMatterYAML returns the YAML source inside the first --- ... --- block and the markdown body.
|
|
func ExtractSkillMDFrontMatterYAML(raw []byte) (fmYAML string, body string, err error) {
|
|
text := strings.TrimPrefix(string(raw), "\ufeff")
|
|
if strings.TrimSpace(text) == "" {
|
|
return "", "", fmt.Errorf("SKILL.md is empty")
|
|
}
|
|
lines := strings.Split(text, "\n")
|
|
if len(lines) < 2 || strings.TrimSpace(lines[0]) != "---" {
|
|
return "", "", fmt.Errorf("SKILL.md must start with YAML front matter (---) per Agent Skills standard")
|
|
}
|
|
var fmLines []string
|
|
i := 1
|
|
for i < len(lines) {
|
|
if strings.TrimSpace(lines[i]) == "---" {
|
|
break
|
|
}
|
|
fmLines = append(fmLines, lines[i])
|
|
i++
|
|
}
|
|
if i >= len(lines) {
|
|
return "", "", fmt.Errorf("SKILL.md: front matter must end with a line containing only ---")
|
|
}
|
|
body = strings.Join(lines[i+1:], "\n")
|
|
body = strings.TrimSpace(body)
|
|
fmYAML = strings.Join(fmLines, "\n")
|
|
return fmYAML, body, nil
|
|
}
|
|
|
|
// ParseSkillMD parses SKILL.md YAML head + body.
|
|
func ParseSkillMD(raw []byte) (*SkillManifest, string, error) {
|
|
fmYAML, body, err := ExtractSkillMDFrontMatterYAML(raw)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
var m SkillManifest
|
|
if err := yaml.Unmarshal([]byte(fmYAML), &m); err != nil {
|
|
return nil, "", fmt.Errorf("SKILL.md front matter: %w", err)
|
|
}
|
|
return &m, body, nil
|
|
}
|
|
|
|
type skillFrontMatterExport struct {
|
|
Name string `yaml:"name"`
|
|
Description string `yaml:"description"`
|
|
License string `yaml:"license,omitempty"`
|
|
Compatibility string `yaml:"compatibility,omitempty"`
|
|
Metadata map[string]any `yaml:"metadata,omitempty"`
|
|
AllowedTools string `yaml:"allowed-tools,omitempty"`
|
|
}
|
|
|
|
// BuildSkillMD serializes SKILL.md per agentskills.io.
|
|
func BuildSkillMD(m *SkillManifest, body string) ([]byte, error) {
|
|
if m == nil {
|
|
return nil, fmt.Errorf("nil manifest")
|
|
}
|
|
fm := skillFrontMatterExport{
|
|
Name: strings.TrimSpace(m.Name),
|
|
Description: strings.TrimSpace(m.Description),
|
|
License: strings.TrimSpace(m.License),
|
|
Compatibility: strings.TrimSpace(m.Compatibility),
|
|
AllowedTools: strings.TrimSpace(m.AllowedTools),
|
|
}
|
|
if len(m.Metadata) > 0 {
|
|
fm.Metadata = m.Metadata
|
|
}
|
|
head, err := yaml.Marshal(&fm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s := strings.TrimSpace(string(head))
|
|
out := "---\n" + s + "\n---\n\n" + strings.TrimSpace(body) + "\n"
|
|
return []byte(out), nil
|
|
}
|
|
|
|
func manifestTags(m *SkillManifest) []string {
|
|
if m == nil || m.Metadata == nil {
|
|
return nil
|
|
}
|
|
var out []string
|
|
if raw, ok := m.Metadata["tags"]; ok {
|
|
switch v := raw.(type) {
|
|
case []any:
|
|
for _, x := range v {
|
|
if s, ok := x.(string); ok && s != "" {
|
|
out = append(out, s)
|
|
}
|
|
}
|
|
case []string:
|
|
out = append(out, v...)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func versionFromMetadata(m *SkillManifest) string {
|
|
if m == nil || m.Metadata == nil {
|
|
return ""
|
|
}
|
|
if v, ok := m.Metadata["version"]; ok {
|
|
if s, ok := v.(string); ok {
|
|
return strings.TrimSpace(s)
|
|
}
|
|
}
|
|
return ""
|
|
}
|