1package skills
2
3import (
4 "embed"
5 "io/fs"
6 "log/slog"
7 "path/filepath"
8)
9
10// BuiltinPrefix is the path prefix for builtin skill files. It is used by
11// the View tool to distinguish embedded files from disk files.
12const BuiltinPrefix = "crush://skills/"
13
14//go:embed builtin/*
15var builtinFS embed.FS
16
17// BuiltinFS returns the embedded filesystem containing builtin skills.
18func BuiltinFS() embed.FS {
19 return builtinFS
20}
21
22// DiscoverBuiltin finds all valid skills embedded in the binary.
23func DiscoverBuiltin() []*Skill {
24 var discovered []*Skill
25
26 fs.WalkDir(builtinFS, "builtin", func(path string, d fs.DirEntry, err error) error {
27 if err != nil {
28 return nil
29 }
30 if d.IsDir() || d.Name() != SkillFileName {
31 return nil
32 }
33
34 content, err := builtinFS.ReadFile(path)
35 if err != nil {
36 slog.Warn("Failed to read builtin skill file", "path", path, "error", err)
37 return nil
38 }
39
40 skill, err := ParseContent(content)
41 if err != nil {
42 slog.Warn("Failed to parse builtin skill file", "path", path, "error", err)
43 return nil
44 }
45
46 // Set paths using the crush prefix. Strip the leading "builtin/"
47 // so the path is relative to the embedded root
48 // (e.g., "crush://skills/crush-config/SKILL.md").
49 relPath, _ := filepath.Rel("builtin", path)
50 relPath = filepath.ToSlash(relPath)
51 skill.SkillFilePath = BuiltinPrefix + relPath
52 skill.Path = BuiltinPrefix + filepath.Dir(relPath)
53 skill.Builtin = true
54
55 if err := skill.Validate(); err != nil {
56 slog.Warn("Builtin skill validation failed", "path", path, "error", err)
57 return nil
58 }
59
60 slog.Debug("Successfully loaded builtin skill", "name", skill.Name, "path", skill.SkillFilePath)
61 discovered = append(discovered, skill)
62 return nil
63 })
64
65 return discovered
66}