skills

package
v1.4.5 Latest Latest
Warning

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

Go to latest
Published: Apr 15, 2026 License: Apache-2.0 Imports: 16 Imported by: 5

Documentation

Overview

Package skills provides types and utilities for loading, parsing, and managing PromptKit skills defined via the AgentSkills.io SKILL.md format.

Index

Constants

View Source
const (
	SkillActivateTool     = "skill__activate"
	SkillDeactivateTool   = "skill__deactivate"
	SkillReadResourceTool = "skill__read_resource"
	SkillNamespace        = "skill"
	SkillExecutorName     = "skill"
)

Tool name constants for the skill__ namespace.

Variables

View Source
var ErrPathTraversal = errors.New("resource path escapes skill directory")

ErrPathTraversal is returned when a resource path attempts to escape the skill directory.

Functions

func BuildSkillActivateDescriptor

func BuildSkillActivateDescriptor() *tools.ToolDescriptor

BuildSkillActivateDescriptor returns the tool descriptor for skill__activate.

func BuildSkillActivateDescriptorWithIndex

func BuildSkillActivateDescriptorWithIndex(index string) *tools.ToolDescriptor

BuildSkillActivateDescriptorWithIndex returns a skill__activate descriptor whose Description includes the available-skills index so the LLM can discover which skills are available.

func BuildSkillDeactivateDescriptor

func BuildSkillDeactivateDescriptor() *tools.ToolDescriptor

BuildSkillDeactivateDescriptor returns the tool descriptor for skill__deactivate.

func BuildSkillReadResourceDescriptor

func BuildSkillReadResourceDescriptor() *tools.ToolDescriptor

BuildSkillReadResourceDescriptor returns the tool descriptor for skill__read_resource.

func DefaultSkillsProjectDir

func DefaultSkillsProjectDir() (string, error)

DefaultSkillsProjectDir returns the project-level skills directory.

func DefaultSkillsUserDir

func DefaultSkillsUserDir() (string, error)

DefaultSkillsUserDir returns the XDG-compliant user-level skills directory.

func IsLocalPath

func IsLocalPath(arg string) bool

IsLocalPath returns true if the argument looks like a local filesystem path rather than a skill reference (starts with "./", "../", or "/").

func SkillFilterFromContext added in v1.4.5

func SkillFilterFromContext(ctx context.Context) string

SkillFilterFromContext returns the skill filter from context, or "" if not set.

func WithSkillFilter added in v1.4.5

func WithSkillFilter(ctx context.Context, filter string) context.Context

WithSkillFilter returns a context with the given skill filter glob pattern. The ToolExecutor reads this to apply per-run filtering in concurrent scenarios.

Types

type Executor

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

Executor manages the skill activation lifecycle and provides functions that implement the skill__ namespaced tools.

func NewExecutor

func NewExecutor(cfg ExecutorConfig) *Executor

NewExecutor creates a new Executor from the given configuration. If Selector is nil, a ModelDrivenSelector is used.

func (*Executor) Activate

func (e *Executor) Activate(name string) (instructions string, addedTools []string, retErr error)

Activate loads a skill's instructions and returns them. It extends the active tool set with the skill's allowed-tools (capped by pack tools). If the skill is already active, it returns the instructions again (idempotent). Returns error if skill not found or at max active limit.

func (*Executor) ActivateWithFilter added in v1.4.5

func (e *Executor) ActivateWithFilter(name, filter string) (instructions string, addedTools []string, retErr error)

ActivateWithFilter is like Activate but applies the given filter instead of the executor's default filter. This supports per-run filtering in concurrent scenarios.

func (*Executor) ActiveSkills

func (e *Executor) ActiveSkills() []string

ActiveSkills returns the names of currently active skills, sorted.

func (*Executor) ActiveTools

func (e *Executor) ActiveTools() []string

ActiveTools returns the aggregate set of tools added by all active skills (each capped by pack tools). The result is deduplicated and sorted.

func (*Executor) Deactivate

func (e *Executor) Deactivate(name string) (removedTools []string, retErr error)

Deactivate removes a skill from the active set. Returns the tools that should be removed from the active tool set. A tool is only removed if no other active skill also needs it.

func (*Executor) ReadResource

func (e *Executor) ReadResource(skillName, path string) ([]byte, error)

ReadResource reads a file from within a skill's directory. Delegates to the underlying registry.

func (*Executor) SetFilter added in v1.4.5

func (e *Executor) SetFilter(glob string) []string

SetFilter sets a glob pattern that restricts which skills can be activated. Empty string means all skills are available. "none" (case-insensitive) disables all. Returns names of skills that were deactivated because they no longer match.

func (*Executor) SetNewSelector added in v1.4.5

func (e *Executor) SetNewSelector(sel selection.Selector)

SetNewSelector replaces the external selector after construction. It is safe to call between Send()s; callers typically wire this at capability-registration time based on RuntimeConfig.

func (*Executor) SkillIndex

func (e *Executor) SkillIndex(skillsDir string) string

SkillIndex returns the Phase 1 skill index string for inclusion in the skill__activate tool description. It is a thin wrapper over SkillIndexFiltered with no query, so it preserves historical behavior when no external selector is configured.

func (*Executor) SkillIndexFiltered added in v1.4.5

func (e *Executor) SkillIndexFiltered(ctx context.Context, query, skillsDir string) string

SkillIndexFiltered returns the skill index string, optionally narrowed by the configured external Selector. When no selector is configured, or the selector returns an error or an empty result, the full eligible set is returned — PromptKit never crashes a conversation because selection failed.

query is the current-turn context the selector may rank against; when empty, the selector is skipped entirely (there's nothing to rank against, so all eligible skills surface).

type ExecutorConfig

type ExecutorConfig struct {
	Registry    *Registry
	Selector    SkillSelector      // nil = ModelDrivenSelector
	NewSelector selection.Selector // optional external selector for narrowing the skill index
	PackTools   []string           // all tools declared in the pack
	MaxActive   int                // 0 = unlimited
	ConfigDir   string             // base directory for computing relative skill paths in filters
}

ExecutorConfig configures the skill executor.

type InstalledSkill

type InstalledSkill struct {
	Org      string // e.g., "anthropic"
	Name     string // e.g., "pdf-processing"
	Location string // "user" or "project"
	Path     string // filesystem path to the skill directory
}

InstalledSkill describes a skill found in user-level or project-level directories.

type Installer

type Installer struct {
	UserDir      string                       // ~/.config/promptkit/skills/
	ProjectDir   string                       // .promptkit/skills/
	GitCloneFunc func(url, dest string) error // injectable for testing
	GitCheckout  func(dir, ref string) error  // injectable for testing
	CopyDirFunc  func(src, dest string) error // injectable for testing
}

Installer manages installing, removing, listing, and resolving shared skills.

func NewInstaller

func NewInstaller() (*Installer, error)

NewInstaller creates an Installer with default XDG-compliant paths.

func (*Installer) Install

func (inst *Installer) Install(ref SkillRef, projectLevel bool) (string, error)

Install clones a skill from its Git repository into the appropriate directory. If projectLevel is true, installs to .promptkit/skills/; otherwise to user-level. Returns the installation path.

func (*Installer) InstallInto

func (inst *Installer) InstallInto(ref SkillRef, targetDir string) (string, error)

InstallInto clones a skill from its Git repository directly into a target directory (e.g., a workflow stage directory like ./skills/billing/). The skill is placed at <targetDir>/<name>/. Returns the installation path.

func (*Installer) InstallLocal

func (inst *Installer) InstallLocal(srcPath string, projectLevel bool) (string, error)

InstallLocal copies a skill from a local path into the appropriate directory. Returns the installation path.

func (*Installer) InstallLocalInto

func (inst *Installer) InstallLocalInto(srcPath, targetDir string) (string, error)

InstallLocalInto copies a skill from a local path directly into a target directory. Returns the installation path.

func (*Installer) List

func (inst *Installer) List() ([]InstalledSkill, error)

List returns all installed skills from both user and project directories.

func (*Installer) Remove

func (inst *Installer) Remove(ref SkillRef) error

Remove removes an installed skill. It checks project-level first, then user-level.

func (*Installer) Resolve

func (inst *Installer) Resolve(ref SkillRef) (string, error)

Resolve finds the installation path for a skill reference. Checks project-level first, then user-level.

type ModelDrivenSelector

type ModelDrivenSelector struct{}

ModelDrivenSelector is the default selector — it returns all available skills, letting the model decide which to activate via skill__activate. Effective for up to ~50 skills. Safe for concurrent use.

func NewModelDrivenSelector

func NewModelDrivenSelector() *ModelDrivenSelector

NewModelDrivenSelector creates a new ModelDrivenSelector.

func (*ModelDrivenSelector) Select

func (s *ModelDrivenSelector) Select(
	_ context.Context,
	_ string,
	available []SkillMetadata,
) ([]string, error)

Select returns all skill names from available.

type Registry

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

Registry holds discovered skills and provides access by name and directory.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates a new empty skill registry.

func (*Registry) Discover

func (r *Registry) Discover(sources []SkillSource) error

Discover scans skill sources and registers all found skills. Sources are processed in order: the first source to register a skill name wins for path/metadata, but a later source may upgrade the preload flag from false to true. This supports the common pattern of a broad `path: skills/` entry followed by narrower per-directory entries that mark specific skills as preload.

func (*Registry) Has

func (r *Registry) Has(name string) bool

Has returns true if a skill with the given name is registered.

func (*Registry) List

func (r *Registry) List() []SkillMetadata

List returns metadata for all registered skills, sorted by name.

func (*Registry) ListForDir

func (r *Registry) ListForDir(dir string) []SkillMetadata

ListForDir returns metadata for skills whose virtual path is under the given directory. If dir is empty, returns all skills. Results are sorted by name.

Matching uses the virtual path (i.e. honoring MountAs), which is what the workflow filter and consumers see. Inline skills are skipped.

func (*Registry) Load

func (r *Registry) Load(name string) (*Skill, error)

Load returns the full skill by name, reading instructions from disk on demand.

func (*Registry) PreloadedSkills

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

PreloadedSkills returns fully loaded skills marked with preload: true.

func (*Registry) ReadResource

func (r *Registry) ReadResource(name, resourcePath string) ([]byte, error)

ReadResource reads a file from within a skill's directory. Returns error if the path escapes the skill directory (path traversal prevention).

func (*Registry) ResolveRef

func (r *Registry) ResolveRef(ref SkillRef) (string, error)

ResolveRef resolves an @org/name skill reference to its installed filesystem path. It delegates to the Installer's Resolve method which checks project-level first, then user-level directories.

type Skill

type Skill struct {
	SkillMetadata
	Instructions string `json:"instructions"` // Markdown body from SKILL.md
	// Path is the consumer-facing skill directory. For sources without
	// MountAs this is the real on-disk path. For sources with MountAs it
	// is the *virtual* path (MountAs prefix + relative subpath under the
	// source dir) — workflow glob filters and ListForDir match against
	// this. To read files from the skill, always go through
	// Registry.ReadResource; do not treat Path as a filesystem path.
	Path string `json:"path"`
}

Skill holds a fully loaded skill — metadata + instructions + path. This is the Phase 2 data — loaded on activation.

func ParseSkillFile

func ParseSkillFile(path string) (*Skill, error)

ParseSkillFile parses a SKILL.md file at the given path into a Skill. It extracts YAML frontmatter and markdown body.

type SkillMetadata

type SkillMetadata struct {
	Name          string            `yaml:"name" json:"name"`
	Description   string            `yaml:"description" json:"description"`
	License       string            `yaml:"license,omitempty" json:"license,omitempty"`
	Compatibility string            `yaml:"compatibility,omitempty" json:"compatibility,omitempty"`
	Metadata      map[string]string `yaml:"metadata,omitempty" json:"metadata,omitempty"`
	AllowedTools  []string          `yaml:"allowed-tools,omitempty" json:"allowed_tools,omitempty"`
}

SkillMetadata holds the YAML frontmatter from a SKILL.md file. This is the Phase 1 data — loaded at discovery time (~50 tokens per skill).

func ParseSkillContent

func ParseSkillContent(content []byte) (*SkillMetadata, string, error)

ParseSkillContent parses SKILL.md content from bytes. It returns the parsed metadata, the markdown body, and any error.

func ParseSkillMetadata

func ParseSkillMetadata(path string) (*SkillMetadata, error)

ParseSkillMetadata parses only the YAML frontmatter from a SKILL.md file. Used for Phase 1 discovery — avoids loading the full body.

type SkillRef

type SkillRef struct {
	Org     string // e.g., "anthropic"
	Name    string // e.g., "pdf-processing"
	Version string // e.g., "v1.0.0" (empty = latest)
}

SkillRef represents a parsed skill reference like @org/skill-name@v1.0.0.

func ParseSkillRef

func ParseSkillRef(s string) (SkillRef, error)

ParseSkillRef parses "@org/name[@version]" into a SkillRef.

func (SkillRef) FullName

func (r SkillRef) FullName() string

FullName returns "org/skill-name".

func (SkillRef) GitURL

func (r SkillRef) GitURL() string

GitURL returns the GitHub clone URL.

type SkillSelector

type SkillSelector interface {
	// Select returns the names of skills that should appear in the Phase 1 index.
	// The query is typically the latest user message or conversation context.
	// Available contains all skills that passed directory filtering for the current state.
	Select(ctx context.Context, query string, available []SkillMetadata) ([]string, error)
}

SkillSelector determines which skills from the available set should be presented to the model in the Phase 1 index. Implementations can filter, rank, or transform the available skill list.

type SkillSource

type SkillSource struct {
	// For directory-based skills.
	// Dir is the real on-disk directory to walk for SKILL.md files. Path is a
	// YAML/JSON alias accepted for legacy schemas; if both are set Dir wins.
	Dir  string `yaml:"dir,omitempty" json:"dir,omitempty"`
	Path string `yaml:"path,omitempty" json:"path,omitempty"`

	// For inline skills — Name is required and uniquely identifies the
	// skill in the registry. Description and Instructions populate the
	// in-memory Skill directly; no SKILL.md is read.
	Name         string `yaml:"name,omitempty" json:"name,omitempty"`
	Description  string `yaml:"description,omitempty" json:"description,omitempty"`
	Instructions string `yaml:"instructions,omitempty" json:"instructions,omitempty"`

	// MountAs, when set on a directory-based source, presents discovered
	// skills to the registry, workflow filters, and Skill.Path consumers
	// as if they lived under this virtual directory prefix instead of
	// their real on-disk location. ReadResource always uses the real
	// path — MountAs only affects display and glob matching, never IO
	// or path-containment checks.
	//
	// Subdirectory structure under Dir is preserved: a skill at
	// /foo/agentskills/billing/payments/SKILL.md discovered with
	// Dir: "/foo/agentskills" and MountAs: "skills" is exposed as
	// virtual path "skills/billing/payments", which means a workflow
	// state with `skills: "skills/billing/*"` will match it.
	//
	// MountAs must be a literal directory prefix — glob metacharacters
	// (* ? [) and ".." segments are rejected at Discover time. Setting
	// MountAs on an inline source (Name set, Dir empty) is also rejected.
	MountAs string `yaml:"mount_as,omitempty" json:"mount_as,omitempty"`

	// Preload, when true, causes the skill's instructions to be loaded
	// into the system prompt at session start instead of being activated
	// on demand via skill__activate. Use sparingly — preloaded skills
	// consume context budget every turn. Across duplicate sources Preload
	// is OR-combined: a later source can upgrade preload from false to
	// true, but never the reverse.
	Preload bool `yaml:"preload,omitempty" json:"preload,omitempty"`
}

SkillSource describes one source of skills to register with a Registry.

A source is either:

  • directory-based — set Dir (or Path as an alias). The registry walks the directory for SKILL.md files and registers each one found. MountAs and Preload apply.
  • inline — set Name, Description, and Instructions. The skill is registered as if it had been read from disk. MountAs is rejected here.

Multiple SkillSources can be passed to Registry.Discover. They are processed in order; the first source to register a given skill name wins for path and metadata. Preload is OR-combined: a later source can upgrade preload from false to true but never the reverse.

func (*SkillSource) EffectiveDir

func (s *SkillSource) EffectiveDir() string

EffectiveDir returns the real on-disk source directory, preferring Dir over the legacy Path alias. Returns the empty string for inline sources. This is always the *real* path; MountAs has no effect here.

type TagSelector

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

TagSelector pre-filters skills based on metadata tags. Skills whose Metadata map contains a "tags" key with a comma-separated list are matched against the selector's required tags. A skill is included if it has at least one tag in common with the selector's required tags. If a skill has no "tags" metadata key, it is excluded. Safe for concurrent use.

func NewTagSelector

func NewTagSelector(tags []string) *TagSelector

NewTagSelector creates a TagSelector that matches skills having at least one of the specified tags. Duplicate tags are deduplicated automatically.

func (*TagSelector) Select

func (s *TagSelector) Select(
	_ context.Context,
	_ string,
	available []SkillMetadata,
) ([]string, error)

Select returns the names of skills whose metadata["tags"] contains at least one tag matching the selector's required tags.

type ToolExecutor

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

ToolExecutor handles execution of skill__ tools by delegating to the Executor.

func NewToolExecutor

func NewToolExecutor(exec *Executor) *ToolExecutor

NewToolExecutor creates a new ToolExecutor wrapping the given skills Executor.

func (*ToolExecutor) Execute

func (e *ToolExecutor) Execute(
	ctx context.Context, tool *tools.ToolDescriptor, args json.RawMessage,
) (json.RawMessage, error)

Execute dispatches a skill tool call to the appropriate executor method.

func (*ToolExecutor) Name

func (e *ToolExecutor) Name() string

Name returns the executor name used for mode matching.

Jump to

Keyboard shortcuts

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