package services import ( "fmt" "os" "path/filepath" "strings" "sproutclaw-web/internal/models" ) // ListSkills returns all skills (enabled and disabled) under agentDir. func ListSkills(agentDir string) []models.SkillInfo { var skills []models.SkillInfo skills = append(skills, scanSkillsDir(filepath.Join(agentDir, "skills"), true)...) skills = append(skills, scanSkillsDir(filepath.Join(agentDir, "skills-disabled"), false)...) return skills } func scanSkillsDir(dir string, enabled bool) []models.SkillInfo { entries, err := os.ReadDir(dir) if err != nil { return nil } var skills []models.SkillInfo for _, e := range entries { if !e.IsDir() { continue } skillPath := filepath.Join(dir, e.Name()) name, desc := parseSkillMD(filepath.Join(skillPath, "SKILL.md")) if name == "" { name = e.Name() } skills = append(skills, models.SkillInfo{ Name: name, Description: desc, Enabled: enabled, Toggleable: true, Path: skillPath, }) } return skills } // ToggleSkill moves a skill between skills/ and skills-disabled/ directories. func ToggleSkill(agentDir, skillPath string, enable bool) error { skillsDir := filepath.Join(agentDir, "skills") disabledDir := filepath.Join(agentDir, "skills-disabled") if !isUnderDir(skillPath, skillsDir) && !isUnderDir(skillPath, disabledDir) { return fmt.Errorf("skill path not in managed directories") } name := filepath.Base(skillPath) var src, dst string if enable { src = filepath.Join(disabledDir, name) dst = filepath.Join(skillsDir, name) } else { src = filepath.Join(skillsDir, name) dst = filepath.Join(disabledDir, name) } if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil { return err } return os.Rename(src, dst) } func isUnderDir(path, dir string) bool { return strings.HasPrefix(filepath.Clean(path), filepath.Clean(dir)) }