skills

package module
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package skills is a read-only repository over Agent Skills (https://agentskills.io) — directories that each hold a SKILL.md (YAML frontmatter + Markdown instructions) plus optional bundled resources under references/, assets/, and scripts/.

It exposes Source for List/Load and ResourceSource for bundled files read on demand. NewRepository wraps any fs.FS; NewDirectoryRepository confines a real directory.

The package is deliberately minimal: it parses, validates, and serves skill content. It does NOT execute scripts — an agent runs those with its own shell/file tools — and it does NOT know about chat models or tools. The LLM-callable wrapper lives in tools/skills, a thin adapter over ResourceSource.

Index

Examples

Constants

View Source
const (
	DefaultMaxRepositoryEntries = 512
	DefaultMaxFrontmatterBytes  = int64(64 * 1024)
	DefaultMaxSkillBytes        = int64(1024 * 1024)
	DefaultMaxResourceBytes     = int64(1024 * 1024)
)

Exported defaults keep constructor behavior visible and overridable.

View Source
const SkillFile = "SKILL.md"

SkillFile is the required metadata file at the root of every skill directory.

Variables

View Source
var (
	ErrInvalidSkill       = errors.New("skills: invalid skill")
	ErrNilSkill           = errors.New("skills: skill must not be nil")
	ErrNilFilesystem      = errors.New("skills: filesystem must not be nil")
	ErrNilSource          = errors.New("skills: source must not be nil")
	ErrNilResourceFile    = errors.New("skills: resource source returned a nil file without an error")
	ErrResourceNotRegular = errors.New("skills: resource must be a regular file")
	ErrInvalidLimit       = errors.New("skills: invalid limit")
	ErrContentTooLarge    = errors.New("skills: content exceeds configured limit")
	ErrRepositoryLarge    = errors.New("skills: repository exceeds configured entry limit")

	ErrNoFrontmatter = errors.New("skills: SKILL.md must open with a YAML frontmatter block delimited by ---")

	ErrNameEmpty    = errors.New("skills: name must not be empty")
	ErrNameTooLong  = errors.New("skills: name exceeds 64 characters")
	ErrNameInvalid  = errors.New("skills: name must be lowercase alphanumerics joined by single hyphens (no leading, trailing, or consecutive hyphens)")
	ErrNameMismatch = errors.New("skills: frontmatter name must match the skill directory name")

	ErrDescriptionEmpty   = errors.New("skills: description must not be empty")
	ErrDescriptionTooLong = errors.New("skills: description exceeds 1024 characters")

	ErrCompatibilityTooLong = errors.New("skills: compatibility exceeds 500 characters")

	ErrResourcePath = errors.New("skills: resource path escapes the skill directory")
)

Functions

func ReadResource

func ReadResource(
	ctx context.Context,
	src ResourceSource,
	name string,
	resource string,
	maxBytes int64,
) ([]byte, bool, error)

ReadResource reads at most maxBytes from a bundled skill resource. The truncated result is valid content but must not be treated as the complete resource. maxBytes must be positive.

func ValidateName

func ValidateName(name string) error

ValidateName reports whether name satisfies the Agent Skills specification. It is useful at boundaries that only carry a skill identifier and should not need to fabricate a Frontmatter value to validate it.

Types

type Frontmatter

type Frontmatter struct {
	// Name is the unique skill identifier; it must match the skill's parent
	// directory name. Required.
	Name string `yaml:"name"`
	// Description states what the skill does and when to use it — the text an
	// agent reads to decide relevance. Required.
	Description string `yaml:"description"`
	// License names the license, or a bundled license file. Optional.
	License string `yaml:"license,omitempty"`
	// Compatibility states environment requirements (target product, system
	// packages, network access, ...). Optional.
	Compatibility string `yaml:"compatibility,omitempty"`
	// Metadata is an arbitrary string map for client-defined properties.
	Metadata map[string]string `yaml:"metadata,omitempty"`
	// AllowedTools is a space-separated list of pre-approved tools. Optional
	// and experimental; this package parses but does not enforce it.
	AllowedTools string `yaml:"allowed-tools,omitempty"`
}

Frontmatter is the YAML metadata block at the head of a SKILL.md file, as defined by the Agent Skills specification.

func (Frontmatter) AllowedToolList

func (f Frontmatter) AllowedToolList() []string

AllowedToolList splits the space-separated allowed-tools field into its entries. The field is experimental and advisory — this package neither interprets nor enforces it; the splitter is offered for callers that do.

func (Frontmatter) Validate

func (f Frontmatter) Validate() error

type Repository

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

Repository is a read-only Agent Skills repository backed by an fs.FS. Reads are lazy and per-call, so changes to the backing filesystem are visible without a refresh operation.

func NewDirectoryRepository

func NewDirectoryRepository(root string, config RepositoryConfig) (*Repository, error)

NewDirectoryRepository roots the filesystem at a directory so a skill cannot escape it through a relative path, which matters because skill names reach this layer from untrusted bundles.

func NewRepository

func NewRepository(fsys fs.FS, config RepositoryConfig) (*Repository, error)

NewRepository takes an fs.FS rather than a path so a skill set can come from an embedded bundle, an archive, or a test fixture without a temporary directory. Limits are resolved here because an unbounded repository would let a malformed skill exhaust memory during discovery, before any skill runs.

Example
package main

import (
	"context"
	"fmt"
	"testing/fstest"

	"github.com/Tangerg/scope/skills"
)

func main() {
	repository, err := skills.NewRepository(fstest.MapFS{
		"review/SKILL.md": {Data: []byte("---\nname: review\ndescription: Review code.\n---\nRead the code before suggesting changes.")},
	}, skills.RepositoryConfig{})
	if err != nil {
		panic(err)
	}
	summaries, err := repository.List(context.Background())
	if err != nil {
		panic(err)
	}
	skill, err := repository.Load(context.Background(), summaries[0].Name)
	if err != nil {
		panic(err)
	}

	fmt.Println(skill.Name, skill.Instructions)
}
Output:
review Read the code before suggesting changes.

func (*Repository) List

func (r *Repository) List(ctx context.Context) (summaries []Summary, err error)

List returns a summary for every valid skill directory, sorted by name. Invalid skill entries are skipped. Repository access failures are returned. A missing root directory is treated as an empty repository.

func (*Repository) Load

func (r *Repository) Load(ctx context.Context, name string) (*Skill, error)

Load reads, parses, and validates one skill by directory name.

func (*Repository) OpenResource

func (r *Repository) OpenResource(ctx context.Context, name, resource string) (fs.File, error)

OpenResource opens a file bundled under a skill. The resource path is resolved relative to the skill directory. Lexical traversal is rejected; repositories returned by NewDirectoryRepository also reject symlink escapes.

type RepositoryConfig

type RepositoryConfig struct {
	MaxEntries          int
	MaxFrontmatterBytes int64
	MaxSkillBytes       int64
}

RepositoryConfig bounds repository discovery and skill-document reads. Zero fields select the package defaults.

type ResourceSource

type ResourceSource interface {
	Source
	// OpenResource opens one bundled resource beneath the exact skill root. It
	// must reject absolute paths, traversal, and symlink escape according to the
	// source's trust boundary; the caller owns and closes the returned file.
	OpenResource(ctx context.Context, name, resource string) (fs.File, error)
}

ResourceSource extends Source with progressive-disclosure level 3: opening a resource bundled under a skill directory.

func Merge

func Merge(sources ...ResourceSource) ResourceSource

Merge layers several resource sources into one. Earlier sources take precedence: on a name collision the first source that has the skill wins, so callers express precedence by order (e.g. a project source before a global one). The winning source owns the complete skill bundle; missing resources do not fall through to a lower-precedence copy with the same name.

Nil and typed-nil sources are dropped. Merge of none yields an empty source (List returns nothing, Load reports not found).

type Skill

type Skill struct {
	Frontmatter
	Instructions string
}

Skill is a fully loaded skill: its frontmatter metadata plus the Markdown instruction body. Bundled resource files (references/, assets/, scripts/) are not loaded here — they are opened on demand via ResourceSource, the third level of progressive disclosure.

func Parse

func Parse(content []byte) (*Skill, error)

Parse requires exact YAML fences so arbitrary Markdown cannot silently enter the skill discovery path as a partially populated manifest. The body after the closing fence remains untouched Markdown instruction content.

func (Skill) Summary

func (s Skill) Summary() Summary

func (*Skill) Validate

func (s *Skill) Validate() error

type Source

type Source interface {
	// List returns detached, valid summaries in the implementation's stable
	// discovery order. Invalid skill bundles may be skipped, but repository I/O,
	// permission, and context failures must be returned rather than disguised as
	// an empty source.
	List(ctx context.Context) ([]Summary, error)
	// Load validates and returns one complete skill by exact name. The caller owns
	// the returned value; missing skills and malformed bundles are distinct from
	// context cancellation, which remains identifiable through errors.Is.
	Load(ctx context.Context, name string) (*Skill, error)
}

Source is the read-only repository that lists and loads skills. Its two operations mirror the first progressive-disclosure levels, so a consumer pulls in only as much as a task needs:

  • List — name + description for every skill (level 1)
  • Load — one skill's full instructions (level 2)

Implementations must return valid Summary and Skill models, honor ctx cancellation, and return an error matching context.Canceled or context.DeadlineExceeded.

type Summary

type Summary struct {
	Name        string
	Description string
}

Summary is the metadata view — just enough for an agent to decide whether a skill is relevant without loading its instructions (progressive-disclosure level 1).

func (Summary) Validate

func (s Summary) Validate() error

Jump to

Keyboard shortcuts

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