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.
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 ¶
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 ¶
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
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
FormatAvailableVerbose renders the loaded skill index for the system prompt. The XML-ish structure mirrors OpenCode and 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 ¶
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 ¶
All returns every loaded skill, sorted by Name ascending. The returned slice is a fresh copy.
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.
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. The `skill` tool exposes this so the model can resolve
// relative paths inside the skill body.
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 ¶
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 ¶
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 ( // SourceUserClaude is ~/.claude/skills/. SourceUserClaude Source = iota // 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).