on main help sign in
refactor(skills): use fastwalk to resolve symlinks (#1732)
d361a33Amolith
internal/skills/skills.go+21−2
@@ -10,7 +10,9 @@ import (
10 "path/filepath"
10 "path/filepath"
11 "regexp"
11 "regexp"
12 "strings"
12 "strings"
13+ "sync"
13
14
15+ "github.com/charlievieth/fastwalk"
14 "gopkg.in/yaml.v3"
16 "gopkg.in/yaml.v3"
15)
17)
16
18
@@ -110,17 +112,32 @@ func splitFrontmatter(content string) (frontmatter, body string, err error) {
110// Discover finds all valid skills in the given paths.
112// Discover finds all valid skills in the given paths.
111func Discover(paths []string) []*Skill {
113func Discover(paths []string) []*Skill {
112 var skills []*Skill
114 var skills []*Skill
115+ var mu sync.Mutex
113 seen := make(map[string]bool)
116 seen := make(map[string]bool)
114
117
115 for _, base := range paths {
118 for _, base := range paths {
119+ // We use fastwalk with Follow: true instead of filepath.WalkDir because
120+ // WalkDir doesn't follow symlinked directories at any depth—only entry
121+ // points. This ensures skills in symlinked subdirectories are discovered.
122+ // fastwalk is concurrent, so we protect shared state (seen, skills) with mu.
123+ conf := fastwalk.Config{
124+ Follow: true,
125+ ToSlash: fastwalk.DefaultToSlash(),
126+ }
116− filepath.WalkDir(base, func(path string, d os.DirEntry, err error) error {
127+ fastwalk.Walk(&conf, base, func(path string, d os.DirEntry, err error) error {
117 if err != nil {
128 if err != nil {
118 return nil
129 return nil
119 }
130 }
120− if d.IsDir() || d.Name() != SkillFileName || seen[path] {
131+ if d.IsDir() || d.Name() != SkillFileName {
132+ return nil
133+ }
134+ mu.Lock()
135+ if seen[path] {
136+ mu.Unlock()
121 return nil
137 return nil
122 }
138 }
123 seen[path] = true
139 seen[path] = true
140+ mu.Unlock()
124 skill, err := Parse(path)
141 skill, err := Parse(path)
125 if err != nil {
142 if err != nil {
126 slog.Warn("Failed to parse skill file", "path", path, "error", err)
143 slog.Warn("Failed to parse skill file", "path", path, "error", err)
@@ -131,7 +148,9 @@ func Discover(paths []string) []*Skill {
131 return nil
148 return nil
132 }
149 }
133 slog.Info("Successfully loaded skill", "name", skill.Name, "path", path)
150 slog.Info("Successfully loaded skill", "name", skill.Name, "path", path)
151+ mu.Lock()
134 skills = append(skills, skill)
152 skills = append(skills, skill)
153+ mu.Unlock()
135 return nil
154 return nil
136 })
155 })
137 }
156 }