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 ¶
const ( DefaultMaxRepositoryEntries = 512 DefaultMaxFrontmatterBytes = int64(64 * 1024) DefaultMaxSkillBytes = int64(1024 * 1024) DefaultMaxResourceBytes = int64(1024 * 1024) )
Exported defaults keep constructor behavior visible and overridable.
const SkillFile = "SKILL.md"
SkillFile is the required metadata file at the root of every skill directory.
Variables ¶
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 ¶
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) OpenResource ¶
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 ¶
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.
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.