Documentation
¶
Overview ¶
Package skills owns spec-compliant Agent Skills: discovery, parsing, and the in-memory registry the agent loop reads. A Skill is a reusable capability playbook the model invokes via the `Skill` tool (description-matched) or the user invokes via `/<skill-name>` slash.
Disk layout per skill (mirrors agentskills.io/specification):
skills/<slug>/ ├── SKILL.md required — frontmatter + body ├── scripts/ optional — executable, loaded on demand ├── references/ optional — docs loaded on demand └── assets/ optional — templates, schemas
Progressive disclosure is the spec's load contract — only the metadata (name + description) is read into the system prompt at startup; the body loads when the skill is invoked; resources load when the model reads them via read_file or runs them via run_bash.
Index ¶
- Constants
- func BuiltinResource(slug, relPath string) ([]byte, error)
- func HashInstalledDir(dir string) (string, error)
- func NormalizeOfficialSource(src string) (string, bool, error)
- func OfficialSource(slug string) (string, error)
- func ProjectSkillsDir(cwd string) string
- func Uninstall(name string) (string, error)
- func UserSkillsDir() (string, error)
- type AlreadyInstalledError
- type CheckOptions
- type CheckResult
- type CheckStatus
- type InstallOptions
- type InstallResult
- type LoadResult
- type LockEntry
- type Lockfile
- type NewSkillOptions
- type NewSkillResult
- type OfficialCatalogCache
- type OfficialCatalogOptions
- type Scope
- type Skill
- type SourceType
- type UpdateOptions
- type UpdateResult
- type UpdateStatus
- type ValidateResult
Constants ¶
const ( OfficialSkillsOwner = "yottadynamics" OfficialSkillsRepo = "yottacode-skills" OfficialPrefix = "official/" )
OfficialSkillsOwner and OfficialSkillsRepo identify the public curated skills catalog maintained by YottaDynamics. Paid/private skill packs intentionally live in separate authenticated repositories so this public source stays simple and redistributable.
const CurrentLockfileVersion = 1
CurrentLockfileVersion is the schema version this binary writes. Loaders accept any version <= this; newer files are still parsed but a warning is logged so users know their newer client wrote it.
const LockfileName = ".lock.json"
LockfileName is the on-disk filename inside DestRoot. Dot-prefixed so registry.loadDir's existing hidden-entry skip already excludes it from skill enumeration.
const OfficialCatalogCacheName = "catalog.json"
OfficialCatalogCacheName is the metadata cache file under UserSkillsDir(). It stores only safe browse metadata; installing still fetches real skill bytes from the yottacode-skills repository.
const TrustUnverified = "unverified"
TrustUnverified is the v2 trust tag. Every installed entry receives it; the field is reserved for a future signing/trust subsystem (Ed25519 verification, trusted-publisher allowlists) that can upgrade installed entries without rewriting the lockfile schema.
Variables ¶
This section is empty.
Functions ¶
func BuiltinResource ¶
BuiltinResource reads a resource file (script, reference, asset) from inside a built-in skill's embedded directory. relPath is the resource's path relative to the skill's root (e.g. "references/playbook.md" or "scripts/check.sh"). Returns the bytes or an error if the resource isn't embedded. Disk-loaded skills do not flow through this path — their resources live on disk and are read by the model through read_file the normal way.
func HashInstalledDir ¶
HashInstalledDir produces a stable "sha256:HEXLOWER" identifier for the on-disk skill at dir. The lockfile is excluded from the manifest (the lockfile sits one level up from per-skill dirs, but we belt-and-suspenders this so a future colocation never breaks the hash) and so are any dot-prefixed staging dirs that Install may have crashed mid-flight and left behind.
func NormalizeOfficialSource ¶ added in v0.4.0
NormalizeOfficialSource expands the public official shortcut into the canonical GitHub shorthand. Non-official sources are returned unchanged so callers can pass every install source through this helper unconditionally.
func OfficialSource ¶ added in v0.4.0
OfficialSource returns the GitHub shorthand for a skill in the official public catalog. The returned string is accepted by the existing GitHub Contents installer, so official installs reuse the normal fetch/update path.
func ProjectSkillsDir ¶
ProjectSkillsDir returns the per-project skills dir: <cwd>/.yottacode/skills. Project-local definitions are checked in here so a team can ship a repo-specific skill alongside the codebase. Project wins on name collision with user and built-in.
func Uninstall ¶
Uninstall removes a user-scope skill by name. Returns the path removed (for logging). Intentionally scoped to UserSkillsDir() — a project-scope skill is committed source, the user removes that via git/rm directly; a built-in is embedded in the binary and can't be removed at runtime.
func UserSkillsDir ¶
UserSkillsDir returns the global skills dir: $YOTTACODE_HOME/skills (when the env var is set) or ~/.yottacode/skills otherwise — the shared ychome.Dir resolution, so all global state lives under the same root regardless of override.
Types ¶
type AlreadyInstalledError ¶ added in v0.4.0
AlreadyInstalledError reports that an install target already exists. CLI callers surface this as a hard error, while interactive Catalog installs can treat it as an idempotent no-op.
func IsAlreadyInstalled ¶ added in v0.4.0
func IsAlreadyInstalled(err error) (*AlreadyInstalledError, bool)
IsAlreadyInstalled reports whether err came from the install overwrite guard.
func (*AlreadyInstalledError) Error ¶ added in v0.4.0
func (e *AlreadyInstalledError) Error() string
type CheckOptions ¶
CheckOptions tunes Check. DestRoot defaults to UserSkillsDir; tests inject a tempdir. Name filters to a single skill when non-empty.
type CheckResult ¶
type CheckResult struct {
Name string
Status CheckStatus
Lock *LockEntry // nil when missing-lock
DiskHash string // empty when missing-lock or orphaned-lock
Dir string // absolute path under UserSkillsDir, even when missing
Error string // populated for hash-error
}
CheckResult is one line of the `skills check` report.
func Check ¶
func Check(opts CheckOptions) ([]CheckResult, error)
Check inspects every user-scope skill (or just the named one) and reports its drift status. The returned slice is name-sorted; an empty result means there are no user-installed skills at all (and no lockfile entries to compare against).
type CheckStatus ¶
type CheckStatus string
CheckStatus enumerates the outcomes Check can report for one skill. Stable strings so CLI scripting can grep on them.
const ( // CheckOK — disk hash matches the lockfile entry; nothing to do. CheckOK CheckStatus = "ok" // CheckModified — disk hash differs from the lockfile entry. // User edited the installed copy in place. CheckModified CheckStatus = "modified" // CheckMissingLock — the skill exists on disk but has no // lockfile entry (installed pre-Phase 2, or the lockfile was // deleted). Update can't refresh it; user must reinstall to // re-record provenance. CheckMissingLock CheckStatus = "missing-lock" // CheckOrphanedLock — the lockfile has an entry but the dir is // gone (someone `rm -rf`'d the install). Update can refetch from // the recorded source. CheckOrphanedLock CheckStatus = "orphaned-lock" // CheckHashError — couldn't compute the disk hash (permissions, // truncated file, etc.). Surfaced verbatim so the user can fix // the underlying problem. CheckHashError CheckStatus = "hash-error" )
type InstallOptions ¶
type InstallOptions struct {
// Source is the user-supplied locator. classifySource picks the
// SourceType based on shape and (for ambiguous bare names) a stat.
Source string
// Force overwrites an existing skill dir of the same slug. Without
// it, an existing install is a hard error — matches the notes'
// "Refuse overwrite unless --force".
Force bool
// DestRoot is the parent dir installs go into. Defaults to
// UserSkillsDir() when empty. Tests inject a tempdir.
DestRoot string
// HTTPClient overrides the default client. Tests inject an
// httptest server's client; production callers pass nil.
HTTPClient *http.Client
}
InstallOptions configures one install attempt.
type InstallResult ¶
type InstallResult struct {
Skill Skill
Dir string
SourceType SourceType
Lock LockEntry
Warnings []string
}
InstallResult describes a completed install. Returned so the surface (CLI / TUI) can render "installed remote-ops (github) at /home/.../skills/remote-ops" without re-parsing the SKILL.md.
Warnings carries non-fatal post-install issues (lockfile save failure is the main one in Phase 2) so the caller can surface them to the user without failing the whole install — the bytes are on disk and the skill works regardless.
func Install ¶
func Install(opts InstallOptions) (InstallResult, error)
Install resolves Source, validates its SKILL.md via ParseSkillFile, stages a copy in a tempdir, then atomic-renames into <DestRoot>/<slug>. Refuses to overwrite an existing slug unless Force is set.
type LoadResult ¶
LoadResult is what LoadAll returns: a deduplicated, name-sorted slice of skills plus a slice of human-readable warnings the caller can surface to the user. Warnings are non-fatal — a single malformed skill should not block startup.
func LoadAll ¶
func LoadAll(cwd string, reservedNames map[string]bool) (LoadResult, error)
LoadAll resolves skills from all three sources (built-in, user, project) and merges them with project > user > builtin precedence. reservedNames is the set of slash-command names a skill must not shadow — when a skill's name is in the set, the skill is dropped with a warning so the existing slash command keeps its meaning. Pass nil to skip the reserved-name guard (used by tests).
type LockEntry ¶
type LockEntry struct {
Name string `json:"name"`
SourceType SourceType `json:"source_type"`
Source string `json:"source"`
Hash string `json:"hash"`
InstalledAt time.Time `json:"installed_at"`
Trust string `json:"trust"`
}
LockEntry is one installed skill's provenance row. Fields mirror the bullet list in the Phase 2 design notes verbatim.
type Lockfile ¶
Lockfile is the top-level JSON document. Entries are keyed by skill name so lookups are O(1) and order on disk is stable.
func LoadLockfile ¶
LoadLockfile reads <destRoot>/.lock.json. Missing file is not an error — first-time install will Save() and create it. A malformed file is an error so we never silently overwrite half-broken state.
func (*Lockfile) AllSorted ¶
AllSorted returns entries in name order. Used by `skills check` / `skills update` (no-name forms) so output is deterministic.
func (*Lockfile) Get ¶
Get looks up an entry by name. The bool mirrors map-lookup convention so callers can distinguish "missing" from "zero value."
type NewSkillOptions ¶
type NewSkillOptions struct {
// Slug is the skill's canonical name. Must match the parent
// directory name (the parser enforces this), so we use the same
// string for both.
Slug string
// DestRoot is the parent dir the new skill folder is created
// under. Defaults to UserSkillsDir() when empty. Tests inject a
// tempdir.
DestRoot string
// Force overwrites an existing directory of the same slug.
// Mirrors the install --force semantics.
Force bool
}
NewSkillOptions configures a scaffold call.
type NewSkillResult ¶
NewSkillResult describes a completed scaffold. Returned so the CLI can render "wrote ~/.yottacode/skills/<slug>/SKILL.md" without recomputing the path.
func NewSkill ¶
func NewSkill(opts NewSkillOptions) (NewSkillResult, error)
NewSkill scaffolds a starter SKILL.md at <DestRoot>/<Slug>/. The body is a small template hinting at where to fill in the description, body, and resource dirs. Refuses to overwrite an existing dir unless Force is set.
type OfficialCatalogCache ¶ added in v0.4.0
type OfficialCatalogCache struct {
Version int `json:"version"`
UpdatedAt time.Time `json:"updated_at"`
Skills []Skill `json:"skills"`
}
OfficialCatalogCache is the on-disk metadata cache for the Official tab.
type OfficialCatalogOptions ¶ added in v0.4.0
OfficialCatalogOptions configures official catalog reads. HTTPClient is used only by RefreshOfficialCatalog; ListOfficialCatalog is intentionally offline.
type Scope ¶
type Scope string
Scope distinguishes where a skill came from. Mirrors subagents/usercmd.
type Skill ¶
type Skill struct {
Name string // required, [a-z0-9-]{1,64}, must match parent dir
Description string // required, 1-1024 chars
License string // optional
Compatibility string // optional, ≤500 chars, documentation-only
Metadata map[string]string // optional, free-form host-specific keys (e.g. slash, source-url)
AllowedTools []string // optional, experimental; parsed and stored but not enforced in v1
Body string // markdown body
Dir string // absolute directory of the skill, or "" for embedded built-ins
Source Scope // builtin | user | project
SourcePath string // absolute path to SKILL.md, or "embed:builtins/<slug>/SKILL.md"
}
Skill is one parsed SKILL.md plus the directory it lives in. Body is the post-frontmatter markdown content; Dir is the absolute path of the skill's directory (used to resolve scripts/, references/, assets/ relative paths the body may reference).
Field shapes follow the canonical SKILL.md spec verbatim: name, description, license, compatibility, metadata, allowed-tools. We do not invent required fields beyond the spec.
func Find ¶
Find returns the named skill or nil. Case-sensitive: skill names are lowercase-only by the slug pattern, so casing is part of identity.
func ListOfficialCatalog ¶ added in v0.4.0
func ListOfficialCatalog(opts OfficialCatalogOptions) ([]Skill, error)
ListOfficialCatalog returns the cached official metadata catalog, falling back to bundled metadata when no cache exists. It never touches the network, so opening /skills -> Catalog is instant and immune to GitHub rate limits.
func LoadBuiltins ¶
func LoadBuiltins() []Skill
LoadBuiltins parses every embedded SKILL.md under builtins/<slug>/ and returns the resulting Skill set. Source is always ScopeBuiltin so diagnostics can distinguish embedded vs disk-loaded definitions. Parse errors would be ship-blocking (the embedded files are part of the binary), so we panic — the test suite catches bad frontmatter before release.
SourcePath is set to "embed:builtins/<slug>/SKILL.md" so the path renders meaningfully in /help even though it does not exist on disk. Dir is left empty for built-ins; resources are read via the embedded FS through BuiltinResource, not the disk path.
func ParseSkillFile ¶
ParseSkillFile parses one SKILL.md into a Skill. expectedName is the parent directory name; the spec requires `name` to match the parent dir exactly, so we validate that here. Pass "" to skip the dir check (used by tests that hand in raw bytes).
Frontmatter format (YAML-ish, deliberately tolerant of hand-edits, matching the subagents/usercmd parsing style — no full YAML library dependency for what is functionally a flat map of scalars):
--- name: remote-ops description: Connect to and operate on remote hosts over SSH. license: MIT compatibility: linux, darwin metadata: slash: "true" source-url: https://github.com/example/remote-ops allowed-tools: Bash(ssh:*) Bash(scp:*) Read --- <markdown body>
func RefreshOfficialCatalog ¶ added in v0.4.0
func RefreshOfficialCatalog(opts OfficialCatalogOptions) ([]Skill, error)
RefreshOfficialCatalog fetches the public yottacode-skills catalog metadata from GitHub and writes ~/.yottacode/skills/catalog.json. It is an explicit user action; normal Catalog browsing remains offline.
func (Skill) SlashEnabled ¶
SlashEnabled reports whether this skill should be exposed as a `/<name>` slash command. Default is enabled; opt-out by setting `metadata.slash: "false"` in the frontmatter.
type SourceType ¶
type SourceType string
SourceType classifies an install source. Surfaced in InstallResult so the CLI/TUI can render "installed remote-ops from github" and so Phase 2's lockfile has the discriminator it needs.
const ( SourceLocal SourceType = "local" SourceURL SourceType = "url" SourceGitHub SourceType = "github" SourceOfficial SourceType = "official" )
type UpdateOptions ¶
UpdateOptions tunes Update. DestRoot defaults to UserSkillsDir; HTTPClient is forwarded to the installer for the URL/GitHub paths. Name filters to one skill (no-arg `skills update` updates every tracked entry). Force overrides the user-modified skip.
type UpdateResult ¶
type UpdateResult struct {
Name string
Status UpdateStatus
OldHash string
NewHash string
Message string
}
UpdateResult is one line of the `skills update` report.
func Update ¶
func Update(opts UpdateOptions) ([]UpdateResult, error)
Update re-runs Install for each lockfile entry, refreshing the installed bytes from the originally-recorded source. The dirty detection ensures we never clobber a hand-edit unless the user explicitly opts in via Force.
type UpdateStatus ¶
type UpdateStatus string
UpdateStatus enumerates the outcomes Update can report for one skill. Mirrors CheckStatus where applicable; adds the installer-specific outcomes (updated, unchanged, skipped-dirty).
const ( // UpdateUpdated — re-fetched and the new bytes differ from what // was on disk before. Lockfile entry refreshed. UpdateUpdated UpdateStatus = "updated" // UpdateUnchanged — re-fetched and the bytes are identical to // what was already installed. Lockfile InstalledAt is refreshed // but nothing else moves. UpdateUnchanged UpdateStatus = "unchanged" // UpdateSkippedDirty — on-disk copy differs from the recorded // hash and --force was not set. The user-modified bytes are // preserved; no fetch happens. UpdateSkippedDirty UpdateStatus = "skipped-user-modified" // UpdateSkippedNoLock — no lockfile entry, so Update can't know // where to refetch from. Surface the actionable hint // ("reinstall to record provenance"). UpdateSkippedNoLock UpdateStatus = "skipped-no-lockfile" // UpdateError — the re-fetch failed (network, parse, etc.). // Existing bytes are preserved when possible. UpdateError UpdateStatus = "error" )
type ValidateResult ¶
ValidateResult describes the outcome of a Validate call. Skill is populated on success (so callers can show the parsed name + description); Err carries the parser's error verbatim on failure so the user can see exactly which rule was violated.
func Validate ¶
func Validate(path string) ValidateResult
Validate parses the SKILL.md at path against the same rules the runtime loader uses, returning the parsed Skill or the parser's error. path may be a directory (resolves to <path>/SKILL.md) or a SKILL.md file directly. The directory-name match is enforced only when path is a directory — pointing at a bare SKILL.md is for "is this file syntactically valid" lints in CI.