skills

package
v0.182.1 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package skills implements discovery of Agent Skills. A skill is a folder containing a SKILL.md whose YAML frontmatter (name, description) is parsed at startup; the body is read lazily by the model via the Read tool.

The on-disk format intentionally matches the contract shared by the standard, so a folder authored for any of them drops in unchanged.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CleanupDynamic

func CleanupDynamic() error

CleanupDynamic removes all dynamically downloaded skills from the temporary skills directory. Called after the session ends.

func ExpandShorthand

func ExpandShorthand(input, repository string) string

ExpandShorthand turns shorthand install targets into full GitHub tree URLs, resolving them against the configured "<owner>/<repo>" skills repository (config.DefaultSkillsRepository when empty). For the default repository:

The two-segment form keeps the repository *name* from the configured value and swaps only the owner, so a renamed fork stays reachable as "<org>/<skill>".

Anything else (3+ slash-separated segments, empty segments, etc.) is returned unchanged so ParseGitHubTreeURL produces its existing error.

func LoadSkillMetadata

func LoadSkillMetadata(skillDir, dirName string, scope agentdomain.SkillScope, pluginName string) (*agentdomain.Skill, *agentdomain.SkillLoadError)

LoadSkillMetadata reads <skillDir>/SKILL.md, parses frontmatter, validates the fields, and returns the populated agentdomain.Skill. Returns (nil, nil) when the directory has no SKILL.md, (nil, err) when SKILL.md is invalid. pluginName is set only for plugin-scoped skills.

func SeedBuiltins

func SeedBuiltins(destDir string, overwrite bool) error

SeedBuiltins writes each embedded built-in skill under destDir, mirroring the embedded tree (destDir/<name>/SKILL.md, plus any helper files). A file is written only when it is absent, unless overwrite is set - so a user's edits to a seeded skill survive a re-run of `infer init` (seed-if-absent). destDir is the user-scope skills directory (~/.infer/skills), the same directory the loader reads user skills from.

func SkillTreeURL

func SkillTreeURL(repository, skill string) string

SkillTreeURL returns the GitHub tree URL of skill inside the given "<owner>/<repo>" skills repository. It is the single place the skills-repo layout (<ref>/<subdir>/<skill>) is encoded.

func Uninstall

func Uninstall(name, destBase string) (string, error)

Uninstall removes the skill folder named `name` from `destBase`. The name must match the on-disk skill-name regex (lowercase letters, digits and hyphens) so callers can't smuggle in path-traversal payloads like `../`. Returns an error if the folder doesn't exist or isn't a directory.

Types

type CatalogClient

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

CatalogClient fetches skill metadata from the centralized skills registry. It implements progressive discovery: only the index (name + description) is fetched up front; the full skill body is downloaded only on activation.

func NewCatalogClient

func NewCatalogClient(cfg *config.Config) *CatalogClient

NewCatalogClient returns a CatalogClient for the configured skills repository. Both the index and the skill bodies come from that one repo, so agent.skills.repository is the only knob needed to point the CLI at a fork.

func (*CatalogClient) DownloadSkill

func (c *CatalogClient) DownloadSkill(ctx context.Context, name string) (string, error)

DownloadSkill fetches the full skill folder from the catalog and writes it to the dynamic skills directory. Returns the path to the downloaded SKILL.md.

func (*CatalogClient) Index

func (c *CatalogClient) Index(ctx context.Context) []catalogEntry

Index fetches the catalog index (name + description for every published skill) and caches it for the process lifetime. Returns nil on any failure - the catalog is best-effort, never fatal. No body download.

func (*CatalogClient) Lookup

func (c *CatalogClient) Lookup(ctx context.Context, name string) (*catalogEntry, bool)

Lookup queries the catalog index for a skill with the given name. Returns the entry metadata and true on success, or false when the skill is not found.

func (*CatalogClient) Release

func (c *CatalogClient) Release() (release, updated string)

Release returns the catalog's published release version and update timestamp as of the last Index fetch. Both are empty when the catalog has not been fetched or does not publish them. Individual skills carry no version of their own - the catalog is versioned as a whole.

func (*CatalogClient) ResolveInstallURL

func (c *CatalogClient) ResolveInstallURL(ctx context.Context, input string) (string, bool)

ResolveInstallURL maps a bare catalog skill name to the GitHub tree URL in its catalog `source`, so `skills install <name>` fetches the body from wherever the skill actually lives instead of the hardcoded <repo>/skills/<name> convention. ok is false for inputs that already carry their own location - a full URL or an "<org>/<skill>" shorthand, both of which contain "/" or ":" - and for names the catalog does not list, leaving the caller's shorthand expansion in charge.

func (*CatalogClient) Search

func (c *CatalogClient) Search(ctx context.Context, query string, limit int) []agentdomain.Skill

Search matches query against the catalog, best match first, capped at limit. An empty query returns the head of the index so the command doubles as a browse.

Ranking, in order: names fuzzy-matched by score, then entries whose description literally contains the query. Fuzzy runs over names ONLY - a subsequence match against a paragraph of prose is meaningless ("rust" matches r..u..s..t in most English sentences), which makes description matching substring-only.

Matching is local over the cached index because catalog.json is a single static file with no server-side query support - see NewCatalogClient.

type GitHubLocation

type GitHubLocation struct {
	Owner string
	Repo  string
	Ref   string
	Path  string
}

GitHubLocation identifies a directory inside a public GitHub repository, parsed out of a /tree/<ref>/<path> URL.

func ParseGitHubTreeURL

func ParseGitHubTreeURL(rawURL string) (*GitHubLocation, error)

ParseGitHubTreeURL accepts URLs of the form

https://github.com/<owner>/<repo>/tree/<ref>/<path-to-skill>

Refs containing a literal "/" (e.g. "feature/foo" branches) are not supported; pass the URL of a tag or single-segment branch instead.

type Installer

type Installer struct {
	Client  *http.Client
	APIBase string
	RawBase string
	Token   string
	// Repository is the "<owner>/<repo>" skills repository shorthand install
	// targets resolve against. Empty means config.DefaultSkillsRepository.
	Repository string
}

Installer downloads a skill folder from a GitHub repo into a local destination directory. When Token is non-empty its requests are authenticated, which raises the GitHub API rate limit from 60 to 5,000 requests/hour and allows access to private repositories the token can see.

func NewInstaller

func NewInstaller(repository string) *Installer

NewInstaller returns an Installer pointed at github.com with a 30s HTTP timeout, resolving shorthand targets against the given "<owner>/<repo>" skills repository (empty = config.DefaultSkillsRepository). The GitHub token is read from the environment (GITHUB_TOKEN, then GH_TOKEN); when neither is set requests are made unauthenticated. Tests substitute APIBase / RawBase to point at httptest.Server.

func (*Installer) InstallFromGitHub

func (i *Installer) InstallFromGitHub(ctx context.Context, rawURL, destBase string, overwrite bool) (string, error)

InstallFromGitHub downloads the skill folder at rawURL into <destBase>/<dirname>/, where dirname is the last path segment of the repo URL. Existing folders are rejected unless overwrite is true.

The downloaded folder is post-validated with LoadSkillMetadata - if frontmatter fails the spec checks, the folder is removed and the validation error is returned. There is never a half-installed state.

Returns the absolute path of the installed skill on success.

type Service

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

Service discovers SKILL.md files and exposes the parsed metadata. Loading is gated by config.AgentSkillsConfig - when disabled the scan is skipped entirely so there is zero token / IO cost.

func New

func New(cfg *config.Config) *Service

New returns a Service bound to cfg. Call Load to populate the skill list.

func (*Service) CleanupDynamic

func (s *Service) CleanupDynamic(_ context.Context) error

CleanupDynamic removes dynamically downloaded skills from disk. When cleanup is enabled in config, this removes all skills that were fetched from the catalog during this session. No-op when discovery is disabled or cleanup is explicitly turned off.

func (*Service) Discover

func (s *Service) Discover(ctx context.Context, name string) (agentdomain.Skill, bool)

Discover resolves a skill by name, downloading its body from the centralized catalog when the loaded entry is a metadata-only catalog placeholder (empty Path, seeded by Load). Any skill already backed by a file on disk - local or previously downloaded - is returned as-is, so this is the safe replacement for Get on the activation path. Returns false when the name is unknown, or when it is only resolvable via the catalog and discovery is disabled.

func (*Service) Errors

func (s *Service) Errors() []agentdomain.SkillLoadError

Errors returns a defensive copy of validation failures from the last Load.

func (*Service) Get

func (s *Service) Get(name string) (agentdomain.Skill, bool)

Get returns the loaded skill with the given name. Lookup is exact (names are validated to the lowercase `[a-z0-9-]+` charset at load time).

func (*Service) List

func (s *Service) List() []agentdomain.Skill

List returns a defensive copy of the loaded skills.

func (*Service) Load

func (s *Service) Load(ctx context.Context) error

Load scans both project and user-global skill directories, parses frontmatter, validates each skill, and populates the in-memory list. When skills are disabled in config the call is a no-op and returns nil.

Jump to

Keyboard shortcuts

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