skill

package
v0.17.4 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package skill loads named markdown procedures the model can invoke at runtime. Skills live in plain-text `.md` files with a small YAML-like frontmatter block; the loader assembles a Registry by reading every file from a layered search path.

Built-in skills

Hygge ships a small set of built-in skills embedded directly in the binary (see internal/skill/builtin/). They are loaded first and have the lowest priority: any user or project skill with the same name overrides the built-in. Built-in skills are always available, regardless of the user's ~/.agents or ~/.config/hygge paths.

The .agents convention

Hygge follows the vendor-neutral `.agents` directory convention as the gravitational center for shared agent assets. Skills are loaded from several layers (lowest priority first, later overrides earlier), including `.claude/skills`, `.agents/skills`, and Hygge-native `.hygge/skills` / `~/.config/hygge/skills` locations.

The hygge-native paths override `.agents` paths so users can shadow a shared skill with a hygge-specific tweak. Project paths override user paths so per-repo conventions win.

Walk-up

For project-level layers we walk parent directories from Pwd until we either find the directory in question or hit a project-root marker. The walk halts at the first `.git` directory at or above the current level — that's the conventional "this is the project root" signal. The walk also halts when it reaches $HOME so the user-level `.agents/` and `.hygge/` directories are not double-counted as project layers when Pwd lives below $HOME. Files above the .git (or $HOME) boundary are NOT loaded. Only the FIRST match in each project layer is used; we don't merge skills from multiple `.hygge/skills` directories up the tree.

System prompt integration

The Registry holds only the index (name + description + when-to-use) in memory; full skill bodies are loaded on demand via the `skill` tool. BuildSystemPromptAdditions renders the index as a markdown block appended to the base system prompt.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoFrontmatter = errors.New("skill: file has no frontmatter")

ErrNoFrontmatter is returned by ParseFile when the file does not start with the `---\n` opening delimiter. The loader treats this as a "this file is not a skill" signal and skips the file with a warning; it is not a fatal error.

Functions

func BuildSystemPromptAdditions

func BuildSystemPromptAdditions(r *Registry) string

BuildSystemPromptAdditions returns the text to APPEND to the base system prompt so the model knows what skills exist. Returns an empty string when the registry is nil or has no skills.

func FormatAvailable added in v0.3.4

func FormatAvailable(r *Registry) string

FormatAvailable renders the loaded skill index in the compact markdown shape used by tool descriptions and error messages.

func FormatAvailableVerbose added in v0.3.4

func FormatAvailableVerbose(r *Registry) string

FormatAvailableVerbose renders the loaded skill index for the system prompt. The XML-ish structure gives models clear fields to match a task against before using the skill tool.

Types

type LoadOptions

type LoadOptions struct {
	// HomeDir overrides $HOME for tests.
	HomeDir string
	// XDGConfigHome overrides $XDG_CONFIG_HOME for tests.  Empty means
	// fall back to $HOME/.config.
	XDGConfigHome string
	// Pwd is the starting directory for the project walk-up.  When
	// empty no project layers are consulted.
	Pwd string
}

LoadOptions configures Load. At least Pwd should be set; HomeDir falls back to os.UserHomeDir() if zero, and XDGConfigHome falls back to $HOME/.config.

type ParseError

type ParseError struct {
	Path   string
	Reason string
}

ParseError is the error type returned by ParseFile for malformed skill files: missing closing `---`, invalid name, missing required keys, or a filename-stem / frontmatter-name mismatch.

Path is the absolute path the parser was reading. Reason is a short human-readable explanation.

func (*ParseError) Error

func (e *ParseError) Error() string

Error implements the error interface.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry holds the loaded skills. Construct via Load; the zero value is a valid empty registry.

func Load

func Load(opts LoadOptions) (*Registry, error)

Load reads skills from all configured compatibility layers and returns a Registry. Skills with the same Name are deduped using the precedence order documented on the package: later layers override earlier ones. Returns an empty Registry if no skills exist anywhere.

A skill file with malformed frontmatter, an invalid name, or a filename-stem / frontmatter-name mismatch is logged via slog.Warn and skipped; other valid skills still appear. Files without frontmatter at all are silently skipped — they are not skills.

Missing layer directories are NOT an error — the common case is that most layers are empty.

func (*Registry) All

func (r *Registry) All() []Skill

All returns every loaded skill, sorted by Name ascending. The returned slice is a fresh copy.

func (*Registry) Get

func (r *Registry) Get(name string) (*Skill, bool)

Get returns a skill by name.

func (*Registry) Len

func (r *Registry) Len() int

Len returns the number of unique skills in the registry.

type Skill

type Skill struct {
	// Name is the unique identifier.  Must match the filename stem (or
	// the directory name for directory-style skills) and the regular
	// expression ^[a-z][a-z0-9-]{0,63}$.
	Name string

	// Description is the one-line summary shown in the system prompt.
	Description string

	// WhenToUse describes the situations in which the model should
	// invoke this skill.  Optional — `.agents`-standard skills fold
	// this guidance into Description.  Shown in the system prompt
	// under the description when non-empty.
	WhenToUse string

	// Body is the markdown body of the skill, with leading and
	// trailing blank lines stripped.  Loaded eagerly along with the
	// frontmatter; the tool returns it verbatim when invoked.
	Body string

	// Extras carries unknown frontmatter keys verbatim.  Empty when no
	// extra keys were present.
	Extras map[string]string

	// Path is the absolute path the skill file was loaded from.  For
	// directory-style skills this points at the SKILL.md inside the
	// skill's directory.  Built-in skills are embedded in the binary and
	// have no on-disk file, so SourceBuiltin skills leave Path empty.
	Path string

	// Dir is the absolute path to the skill's directory.  For flat
	// `<name>.md` skills this is the parent directory.  For
	// directory-style `<name>/SKILL.md` skills this is the skill's
	// own directory and is where auxiliary scripts / reference files
	// live.  Built-in skills use the virtual Dir token "builtin" for
	// diagnostics; callers must not treat it as a filesystem path.
	// The `skill` tool exposes this so the model can resolve relative
	// paths inside the skill body for filesystem-backed skills.
	Dir string

	// Source identifies which compatibility layer this skill came from.
	Source Source

	// LoadedAt is when the parser finished reading the file.
	LoadedAt time.Time
}

Skill is a single named procedure the agent can invoke.

func ParseFile

func ParseFile(path string) (Skill, error)

ParseFile reads path and parses it as a flat-layout skill. The filename stem must equal the frontmatter `name`. For directory- style skills (`<name>/SKILL.md`) use ParseSkillDir instead.

Returns a fully-populated Skill (Source and Dir are left zero — the loader fills them in) or an error. ErrNoFrontmatter is returned when the file does not begin with the `---\n` delimiter; the loader treats that as a non-skill file and skips it. Any other parse failure returns a *ParseError.

Validation enforced here:

  • The frontmatter must close with a `---` line.
  • `name` and `description` must be present and non-empty.
  • `when_to_use` is optional (`.agents`-standard skills fold this into the description).
  • `name` must match nameRegex.
  • The filename stem must equal the frontmatter `name`.

func ParseSkillDir

func ParseSkillDir(dir string) (Skill, error)

ParseSkillDir reads `<dir>/SKILL.md` and parses it as a directory- style skill. The directory's base name must equal the frontmatter `name`. Returns a fully-populated Skill with Path set to the SKILL.md location and Dir set to the directory itself. Source is left zero — the loader fills it in.

Validation is the same as ParseFile except the filename-stem check is replaced with a directory-name check.

type Source

type Source int

Source describes where a skill (or any .agents asset) was found.

const (
	// SourceBuiltin is the set of skills embedded in the Hygge binary.
	// Built-ins are loaded before any user or project layer and therefore
	// have the lowest priority: any skill with the same name in a later
	// layer overrides the built-in.  SourceBuiltin skills have no real
	// filesystem Path and use Dir == "builtin" only as a virtual source
	// label for diagnostics.
	SourceBuiltin Source = iota
	// SourceUserClaude is ~/.claude/skills/.
	SourceUserClaude
	// SourceUserAgents is ~/.agents/skills/.
	SourceUserAgents
	// SourceUserHygge is ~/.config/hygge/skills/.
	SourceUserHygge
	// SourceProjectClaude is <pwd>/.claude/skills/ (walk-up).
	SourceProjectClaude
	// SourceProjectAgents is <pwd>/.agents/skills/ (walk-up).
	SourceProjectAgents
	// SourceProjectHygge is <pwd>/.hygge/skills/ (walk-up).
	SourceProjectHygge
)

Source values, ordered by precedence (lower-priority first).

func (Source) String

func (s Source) String() string

String returns a short diagnostic token for the source.

Jump to

Keyboard shortcuts

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