Documentation
¶
Overview ¶
Package skills discovers reusable instruction "skills" stored on disk as */SKILL.md files. Each skill is a directory containing a SKILL.md whose optional YAML-ish frontmatter carries a name/description and whose markdown body is the skill content the model can pull in on demand (PRD F15).
The loader is deliberately dependency-free: frontmatter is hand-parsed (no YAML library) and malformed files are skipped rather than failing the whole load, so a single bad skill never hides the good ones.
Index ¶
- Constants
- Variables
- func AgentsDir(env map[string]string) string
- func DefaultDir(env map[string]string) string
- func DiscoveryRoots(env map[string]string, pluginRoots []string) []string
- func GlobalRoots(primary string) []string
- func ListFromRoots(dirs []string) ([]Skill, []DuplicateName, error)
- func LoadFromRoots(dirs []string) ([]Skill, []DuplicateName, error)
- func ReadLock(dir string) (map[string]LockEntry, error)
- func Remove(dir string, name string) error
- type DuplicateName
- type GitRunner
- type InstallOptions
- type InstallResult
- type LockEntry
- type Skill
- type SkillInfo
Constants ¶
const LockFileName = "skills.lock"
LockFileName is the name of the per-directory lockfile that maps an installed skill name to the source it was installed from and the content hash recorded at install time.
Variables ¶
var ErrNameClash = errors.New("a different skill with that name is already installed")
ErrNameClash is returned when an install would overwrite a skill that was installed from a DIFFERENT source, unless InstallOptions.Force is set. It is a safety guard so a remote source can never silently shadow a skill the user already trusts.
Functions ¶
func AgentsDir ¶ added in v0.4.0
AgentsDir returns ~/.agents/skills when that path exists and is a directory. It is a shared, read-only multi-agent skills root (Zero, Hermes, Claude Code, etc.) and is never the target of install/remove/lock. Missing, non-directory, or unresolvable home yields "" with no error and no directory creation.
Home resolution matches other packages: HOME, then USERPROFILE, then os.UserHomeDir(). ZERO_SKILLS_DIR is intentionally ignored — agents is a pure convention path, not a Zero-specific override.
func DefaultDir ¶
DefaultDir resolves the skills directory, mirroring sessions.DefaultRoot. An explicit ZERO_SKILLS_DIR override wins; otherwise it is $XDG_DATA_HOME/zero/skills or ~/.local/share/zero/skills. The directory is NOT created — a missing directory simply yields no skills.
DefaultDir is the primary write root for install/remove/lock. Runtime discovery also considers AgentsDir and plugin skill roots via DiscoveryRoots / LoadFromRoots.
func DiscoveryRoots ¶ added in v0.4.0
DiscoveryRoots returns ordered skill roots for runtime discovery: primary DefaultDir, optional AgentsDir when present, then pluginRoots. Empty strings are omitted. Earlier entries win on name clashes.
func GlobalRoots ¶ added in v0.4.0
GlobalRoots returns discovery roots for management CLI list/info: an explicit primary write/root dir (usually skillsDir / DefaultDir) plus AgentsDir when present. Plugin roots are excluded from management UX.
func ListFromRoots ¶ added in v0.4.0
func ListFromRoots(dirs []string) ([]Skill, []DuplicateName, error)
ListFromRoots is like LoadFromRoots but strips Content (like List).
func LoadFromRoots ¶ added in v0.4.0
func LoadFromRoots(dirs []string) ([]Skill, []DuplicateName, error)
LoadFromRoots loads and merges skills from the provided directories (earlier entries win on name clashes). Empty roots are skipped. Missing directories are treated as empty (same as Load). Intra-root and cross-root collisions are reported as DuplicateName.
The first non-empty root is the required primary: non-missing load failures (permission, I/O, not a directory, etc.) are returned so callers do not confuse a broken primary skills dir with "no skills". Later optional roots (e.g. ~/.agents/skills, plugin roots) fail open so one bad optional directory does not hide the rest.
Types ¶
type DuplicateName ¶
DuplicateName records two skills that resolved to the same frontmatter name. Winner is the SKILL.md path of the skill that was kept (the one in the lexicographically-first directory); Loser is the path that was dropped.
func Duplicates ¶
func Duplicates(dir string) ([]DuplicateName, error)
Duplicates returns the duplicate-name collisions Load resolved by the first-directory-wins rule, so a caller can warn the user that a shadowed skill was dropped. A missing directory yields no duplicates and no error.
type GitRunner ¶
GitRunner fetches the skill at source into destination. The default runner shallow-clones with the system git, which inherits the process environment (and therefore any proxy/egress settings). It is injectable so tests never hit the network. A runner must only fetch — it must never execute fetched content.
type InstallOptions ¶
type InstallOptions struct {
// Source is a git URL or a local filesystem path to a skill directory (one
// that contains a SKILL.md, or whose tree contains exactly one).
Source string
// Dir is the skills directory to install into (typically DefaultDir(env)).
Dir string
// Force allows overwriting a skill that was installed from a different source.
Force bool
// GitRunner overrides the fetch implementation (tests/proxy control). When
// nil, a git source is shallow-cloned with the system git.
GitRunner GitRunner
}
InstallOptions configures a single skill install.
type InstallResult ¶
type InstallResult struct {
Name string `json:"name"`
// Path is the absolute path to the installed SKILL.md.
Path string `json:"path"`
// Hash is the content hash recorded for the installed skill.
Hash string `json:"hash"`
// Source echoes the source the skill was installed from.
Source string `json:"source"`
// Updated is true when an existing install was replaced; PreviousHash then
// carries the prior recorded hash so a caller can show the change.
Updated bool `json:"updated"`
PreviousHash string `json:"previousHash,omitempty"`
}
InstallResult reports what an install did.
func Install ¶
func Install(ctx context.Context, options InstallOptions) (InstallResult, error)
Install fetches the skill at options.Source and copies its SKILL.md into options.Dir/<name>/, validating the frontmatter and recording a content hash in the lockfile. A git URL is fetched via the (injectable) GitRunner into a temp dir; a local path is read in place. Fetched content is never executed.
type Skill ¶
type Skill struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Content string `json:"content,omitempty"`
Path string `json:"path"`
}
Skill is a single discovered skill. Name and Description come from the SKILL.md frontmatter (Name falls back to the directory name); Content is the markdown body; Path is the absolute path to the SKILL.md file.
func List ¶
List loads the skills directory and returns each skill without its (possibly large) Content body — handy for `zero skills` listings.
func Load ¶
Load scans dir for */SKILL.md files and returns the parsed skills sorted by name. A missing directory yields an empty slice with no error; individual malformed skill files are skipped rather than failing the whole load.
When two skills declare the SAME frontmatter name, resolution is made DETERMINISTIC by a documented rule: the skill in the lexicographically-first directory name wins (os.ReadDir returns entries sorted by filename, so the first one encountered is kept and later same-name duplicates are dropped). This guarantees Load/List/Get always resolve a duplicated name to the same winner regardless of sort stability. Use Duplicates to surface a warning about any such collisions.
NOTE: Load scans one root. Runtime discovery uses LoadFromRoots / DiscoveryRoots (primary DefaultDir, optional ~/.agents/skills, then plugin skill roots). Prefer those multi-root helpers for agent/CLI discovery; keep Load for single-dir install/write call sites.
type SkillInfo ¶
type SkillInfo struct {
Skill Skill `json:"skill"`
Source string `json:"source,omitempty"`
Hash string `json:"hash,omitempty"`
}
SkillInfo bundles a discovered skill with its recorded source and hash, for `skill info`.
func Info ¶
Info returns the named skill plus its recorded source and hash, or ok=false if it is not discoverable in dir.
func InfoFromRoots ¶ added in v0.4.0
InfoFromRoots resolves the named skill across discovery roots (earlier roots win). Lock source/hash are attached only when the winning skill lives under primaryDir and that dir's lockfile has an entry — agents-only skills return frontmatter + path with empty Source/Hash. primaryDir is typically the Zero write root (DefaultDir / skillsDir).