package common import ( "os" "path/filepath" "strings" "github.com/spf13/afero" "gopkg.in/yaml.v3" "github.com/lukaszraczylo/harness-sync/internal/fsx" ) // Doc mirrors canonical.Skill / canonical.Agent without importing them. type Doc struct { Name string Description string Body string Path string } // ParseFrontmatter extracts the name and description fields from a markdown // file's YAML frontmatter block. Returns ("", "") when there is no frontmatter. func ParseFrontmatter(b []byte) (name, description string) { s := string(b) if !strings.HasPrefix(s, "---\n") { return "", "" } end := strings.Index(s[4:], "\n---") if end == -1 { return "", "" } var meta struct { Name string `yaml:"name"` Description string `yaml:"description"` } _ = yaml.Unmarshal([]byte(s[4:4+end]), &meta) return meta.Name, meta.Description } // ImportMarkdownTreeFS walks dir on fs and returns one Doc per matching file. // When requiredFilename is non-empty (e.g. "SKILL.md"), only files with that // exact basename are included; otherwise any *.md. func ImportMarkdownTreeFS(fs fsx.FS, dir, requiredFilename string) ([]Doc, error) { var docs []Doc if !DirExistsFS(fs, dir) { return docs, nil } err := afero.Walk(fs, dir, func(p string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } if requiredFilename != "" && filepath.Base(p) != requiredFilename { return nil } if requiredFilename == "" && !strings.HasSuffix(p, ".md") { return nil } body, err := afero.ReadFile(fs, p) if err != nil { return err } name, desc := ParseFrontmatter(body) if name == "" { name = strings.TrimSuffix(filepath.Base(p), ".md") } rel, _ := filepath.Rel(dir, p) docs = append(docs, Doc{ Name: name, Description: desc, Body: string(body), Path: rel, }) return nil }) return docs, err } // ImportMarkdownTree walks dir and returns one Doc per markdown file. When // requiredFilename is non-empty (e.g. "SKILL.md"), only files with that exact // basename are included; otherwise any *.md. func ImportMarkdownTree(dir, requiredFilename string) ([]Doc, error) { return ImportMarkdownTreeFS(fsx.OS(), dir, requiredFilename) }