skills

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: MIT Imports: 12 Imported by: 0

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

View Source
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

View Source
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

func AgentsDir(env map[string]string) string

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

func DefaultDir(env map[string]string) string

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

func DiscoveryRoots(env map[string]string, pluginRoots []string) []string

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

func GlobalRoots(primary string) []string

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.

func ReadLock

func ReadLock(dir string) (map[string]LockEntry, error)

ReadLock loads the lockfile from dir. A missing lockfile yields an empty map with no error so callers can treat "no lockfile" as "nothing installed".

func Remove

func Remove(dir string, name string) error

Remove deletes an installed skill directory and its lockfile entry. It errors if the named skill is not present in either the dir or the lockfile.

Types

type DuplicateName

type DuplicateName struct {
	Name   string
	Winner string
	Loser  string
}

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

type GitRunner func(ctx context.Context, destination string, source string) error

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 LockEntry

type LockEntry struct {
	Source string `json:"source"`
	Hash   string `json:"hash"`
}

LockEntry records the source and content hash for one installed skill.

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 Get

func Get(dir string, name string) (Skill, bool)

Get loads the named skill from dir, returning false if it is not found.

func List

func List(dir string) ([]Skill, error)

List loads the skills directory and returns each skill without its (possibly large) Content body — handy for `zero skills` listings.

func Load

func Load(dir string) ([]Skill, error)

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

func Info(dir string, name string) (SkillInfo, bool)

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

func InfoFromRoots(primaryDir string, roots []string, name string) (SkillInfo, bool)

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).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL