package knowledge import ( "fmt" "os" "path/filepath" "sort" "strings" ) type Skill struct { Name string Content string Source string } func LoadSoul(path string) (string, error) { b, err := os.ReadFile(path) if err != nil { return "", fmt.Errorf("read soul file failed: %w", err) } content := strings.TrimSpace(string(b)) if content == "" { return "", fmt.Errorf("soul file is empty") } return content, nil } func LoadSkillSet(dir string) ([]Skill, error) { entries, err := os.ReadDir(dir) if err != nil { return nil, fmt.Errorf("read skills dir failed: %w", err) } skillDirs := make([]string, 0) for _, entry := range entries { if entry.IsDir() { skillDirs = append(skillDirs, entry.Name()) } } sort.Strings(skillDirs) out := make([]Skill, 0, len(skillDirs)) for _, skillDir := range skillDirs { file := filepath.Join(dir, skillDir, "skill.md") b, err := os.ReadFile(file) if err != nil { if os.IsNotExist(err) { continue } return nil, fmt.Errorf("read skill file failed: %w", err) } content := strings.TrimSpace(string(b)) if content == "" { continue } name := extractSkillName(skillDir, content) out = append(out, Skill{ Name: name, Content: content, Source: file, }) } if len(out) == 0 { return nil, fmt.Errorf("no valid skills loaded from %s (expected: skills//skill.md)", dir) } return out, nil } func extractSkillName(fileName, markdown string) string { for _, line := range strings.Split(markdown, "\n") { line = strings.TrimSpace(line) if !strings.HasPrefix(line, "#") { continue } title := strings.TrimSpace(strings.TrimLeft(line, "#")) title = strings.TrimSpace(strings.TrimPrefix(title, "Skill:")) title = strings.TrimSpace(strings.TrimPrefix(title, "skill:")) if title != "" { return title } } base := strings.TrimSuffix(fileName, filepath.Ext(fileName)) if base == "" { return fileName } return base }