discoverSkills walks the given skills directories and returns metadata for every valid skill it finds. Body and supporting file lists are NOT included; chatd fetches those on demand via read_skill. Missing directories or individual errors are silently skipped.
(skillsDirs []string, metaFile string)
| 315 | // via read_skill. Missing directories or individual errors are |
| 316 | // silently skipped. |
| 317 | func discoverSkills(skillsDirs []string, metaFile string) []codersdk.ChatMessagePart { |
| 318 | seen := make(map[string]struct{}) |
| 319 | var parts []codersdk.ChatMessagePart |
| 320 | |
| 321 | for _, skillsDir := range skillsDirs { |
| 322 | entries, err := os.ReadDir(skillsDir) |
| 323 | if err != nil { |
| 324 | continue |
| 325 | } |
| 326 | |
| 327 | for _, entry := range entries { |
| 328 | if !entry.IsDir() { |
| 329 | continue |
| 330 | } |
| 331 | |
| 332 | metaPath := filepath.Join(skillsDir, entry.Name(), metaFile) |
| 333 | f, err := os.Open(metaPath) |
| 334 | if err != nil { |
| 335 | continue |
| 336 | } |
| 337 | raw, err := io.ReadAll(io.LimitReader(f, maxSkillMetaBytes+1)) |
| 338 | _ = f.Close() |
| 339 | if err != nil { |
| 340 | continue |
| 341 | } |
| 342 | if int64(len(raw)) > maxSkillMetaBytes { |
| 343 | raw = raw[:maxSkillMetaBytes] |
| 344 | } |
| 345 | |
| 346 | name, description, _, err := workspacesdk.ParseSkillFrontmatter(string(raw)) |
| 347 | if err != nil { |
| 348 | continue |
| 349 | } |
| 350 | |
| 351 | // The directory name must match the declared name. |
| 352 | if name != entry.Name() { |
| 353 | continue |
| 354 | } |
| 355 | if !workspacesdk.SkillNamePattern.MatchString(name) { |
| 356 | continue |
| 357 | } |
| 358 | |
| 359 | // First occurrence wins across directories. |
| 360 | if _, ok := seen[name]; ok { |
| 361 | continue |
| 362 | } |
| 363 | seen[name] = struct{}{} |
| 364 | |
| 365 | skillDir := filepath.Join(skillsDir, entry.Name()) |
| 366 | parts = append(parts, codersdk.ChatMessagePart{ |
| 367 | Type: codersdk.ChatMessagePartTypeSkill, |
| 368 | SkillName: name, |
| 369 | SkillDescription: description, |
| 370 | SkillDir: skillDir, |
| 371 | ContextFileSkillMetaFile: metaFile, |
| 372 | }) |
| 373 | } |
| 374 | } |