skills

package
v0.1.0-rc.1 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const ImportLockVersion = 2

ImportLockVersion is the highest skills.lock.yaml schema version this gridctl reads. Version 1 added the version field itself and per-source agents; version 2 added per-source pack records. Files are written at the lowest version that can represent them (see WriteLockFile), so users without packs keep downgrade freedom.

Variables

View Source
var ErrImportLockBusy = errors.New("timeout acquiring import lock")

ErrImportLockBusy signals contention on the cross-process import lock: the operation should be retried, nothing is corrupted.

View Source
var ErrNewerImportLockVersion = errors.New("import lockfile was written by a newer gridctl version")

ErrNewerImportLockVersion signals a skills.lock.yaml written by a newer gridctl. Callers must never paper over it: acting on state a newer version wrote risks silent data loss (the pkg/project lesson).

Functions

func AgentDir

func AgentDir(registryDir, name string) string

AgentDir returns the canonical directory for one imported agent; the definition itself lives at AgentDir(...)/AGENT.md.

func AgentsRoot

func AgentsRoot(registryDir string) string

AgentsRoot returns the canonical agent store directory under a registry directory.

func BackupSkillFileInDir

func BackupSkillFileInDir(skillDir, shortSHA string) (string, error)

BackupSkillFileInDir is BackupSkillFile for callers that already hold the skill directory (skill projection adopt shares the convention, so there is exactly one hand-edit backup vocabulary).

func BehavioralChanges

func BehavioralChanges(old, new *Fingerprint) []string

BehavioralChanges compares two fingerprints and returns human-readable changes.

func BuildAuther

func BuildAuther(cfg AuthConfig) (gitpkg.Auther, error)

BuildAuther constructs a git.Auther matching the AuthConfig's Method. Returns an error for unknown methods. Individual Auther implementations also validate their own inputs (e.g. HTTPSTokenAuth rejects empty tokens).

func CheckUpdatesBackground

func CheckUpdatesBackground(registryDir string, logger *slog.Logger)

CheckUpdatesBackground runs update checks in a background goroutine. Results are written to the cache file for display on next CLI command.

func ContentHashFile

func ContentHashFile(path string) (string, error)

ContentHashFile computes a SHA-256 hash of a file.

func DeleteAgent

func DeleteAgent(registryDir, name string) error

DeleteAgent removes one agent from the canonical store.

func DeleteOrigin

func DeleteOrigin(skillDir string) error

DeleteOrigin removes the .origin.json file from a skill directory.

func DetectAgentDrift

func DetectAgentDrift(ctx context.Context, registryDir string) ([]string, error)

DetectAgentDrift returns the names of imported agents whose on-disk AGENT.md has been edited since the last import. Same fail-open policy as DetectDrift: a missing origin or InstalledHash is not drift.

func DetectDrift

func DetectDrift(ctx context.Context, store *registry.Store, lockPath, sourceName string) ([]string, error)

DetectDrift returns the names of imported skills in sourceName whose on-disk SKILL.md has been edited since the last import or sync. Drift is detected by comparing the current file hash against the InstalledHash snapshot written when the skill was last installed.

Skills imported before InstalledHash was tracked (empty value) are treated as not drifted — DetectDrift fails open rather than reporting noise. Skills with no Origin (purely local) are not considered.

Pass an empty sourceName to scan every imported skill in the registry.

func FetchAndCompare

func FetchAndCompare(repo, ref, currentSHA string, auth AuthConfig, logger *slog.Logger) (string, bool, error)

FetchAndCompare fetches the latest from a remote and compares with current.

func FormatFindings

func FormatFindings(findings []SecurityFinding) string

FormatFindings returns a human-readable summary of security findings.

func FormatUpdateNotice

func FormatUpdateNotice() string

FormatUpdateNotice returns a user-friendly message about available updates.

func HasOrigin

func HasOrigin(skillDir string) bool

HasOrigin checks if a skill directory has an .origin.json file.

func IsPinnedRef

func IsPinnedRef(ref string) bool

IsPinnedRef returns true when ref looks like an immutable pin (a specific version tag containing a ".", or a full 40-character commit SHA). Bare branch names and empty refs return false so they are treated as floating.

This is a heuristic, not a guarantee: a tag like "release-2026" with no dot will read as unpinned, and a branch named "feature.x" will read as pinned. Used by aggregate sync to skip pins by default so a bulk operation does not silently bump a user's intentionally-fixed version.

func IsSemVerConstraint

func IsSemVerConstraint(ref string) bool

IsSemVerConstraint returns true if the ref looks like a semver constraint.

func ListRemoteTags

func ListRemoteTags(repoPath string) ([]string, error)

ListRemoteTags returns all tags from a cached repository.

func LockFilePath

func LockFilePath() string

LockFilePath returns the default path to skills.lock.yaml.

func MutateLockFile

func MutateLockFile(ctx context.Context, path string, fn func(*LockFile) (bool, error)) error

MutateLockFile runs one read-modify-write cycle over skills.lock.yaml while holding the cross-process import lock, so concurrent operations serialize instead of losing each other's updates. fn returns whether its changes should be written; false skips the write and succeeds.

func RepoToName

func RepoToName(repo string) string

RepoToName extracts a short name from a repo URL (the last path segment with any ".git" suffix stripped). Exported so callers like the CLI can match the source names this package uses without duplicating the logic.

func ResolveSemVerConstraint

func ResolveSemVerConstraint(constraintStr string, tags []string) (string, error)

ResolveSemVerConstraint finds the best matching tag for a constraint.

func SafeRepoPath

func SafeRepoPath(path string) error

SafeRepoPath validates a path component to prevent directory traversal.

func ShortSHA

func ShortSHA(sha string) string

ShortSHA returns the first 8 characters of a commit SHA, or the whole string when it is shorter (including the empty string from an uncached fetch). It keeps SHA formatting panic-free for logs, backup names, and messages.

func ShouldCheckUpdates

func ShouldCheckUpdates() bool

ShouldCheckUpdates returns false if update checks are disabled. GRIDCTL_NO_SKILL_UPDATE_CHECK accepts the shared boolean vocabulary (env.Bool); a malformed value is ignored and checks proceed.

func SkillsConfigPath

func SkillsConfigPath() string

SkillsConfigPath returns the default path to skills.yaml.

func UpdateCachePath

func UpdateCachePath() string

UpdateCachePath returns the path to the cached update status file.

func ValidateAgentName

func ValidateAgentName(name string) error

ValidateAgentName validates an agent name: non-empty, lowercase letters, digits, and hyphens only.

func WriteLockFile

func WriteLockFile(path string, lf *LockFile) error

WriteLockFile writes skills.lock.yaml atomically. Keys are sorted for minimal merge conflicts.

func WriteOrigin

func WriteOrigin(skillDir string, origin *Origin) error

WriteOrigin writes the .origin.json file to a skill directory.

func WriteUpdateCache

func WriteUpdateCache(status *UpdateStatus) error

WriteUpdateCache writes the update status to the default cache path.

func WriteUpdateCacheAt

func WriteUpdateCacheAt(path string, status *UpdateStatus) error

WriteUpdateCacheAt writes the update status to an explicit path.

Types

type AgentDefinition

type AgentDefinition struct {
	Name        string
	Description string
	// Extra holds every frontmatter key other than name and description,
	// in document order, as raw YAML nodes.
	Extra []AgentExtraField
	// Body is the markdown after the frontmatter block.
	Body string
	// Raw is the file exactly as read; imports write it verbatim.
	Raw []byte
}

AgentDefinition is a parsed Claude Code subagent definition (AGENT.md / agents/<name>.md). Name and Description are the only typed fields; every other frontmatter key (tools, model, hooks, mcpServers, permissionMode, vendor extensions) rides in Extra as raw YAML nodes in document order.

AgentDefinition deliberately does not reuse AgentSkill/ParseSkillMD: that parser types skill-specific keys and would silently strand tools and model in an untyped map, and its render path normalizes frontmatter. Agents are stored and projected verbatim (identity render), so Raw is the source of truth; the parsed form exists for validation, scanning, and listing.

func ParseAgentMD

func ParseAgentMD(data []byte) (*AgentDefinition, error)

ParseAgentMD parses an agent definition file. Frontmatter with a non-empty description is required: a markdown file without it (a README dropped into an agents/ directory, say) is not an agent definition. The frontmatter mapping is walked by hand so unknown keys are preserved in order rather than silently dropped, and a duplicate key cannot discard the valid ones.

func SaveAgent

func SaveAgent(registryDir, name string, raw []byte) (*AgentDefinition, error)

SaveAgent validates raw as an agent definition named name and writes it into the canonical store byte-for-byte. No normalization happens here on purpose: identity projections copy the canonical bytes verbatim, so any rewrite (key reordering, trailing-newline fixes) would surface as drift on every synced client after an edit. Returns the parsed definition so callers can inspect what was written.

func (*AgentDefinition) DeclaredModel

func (d *AgentDefinition) DeclaredModel() (string, bool)

DeclaredModel reads the definition's top-level `model:` value. ok is false when the declaration exists but is not a scalar (a structured value cannot be honored or safely rewritten); an absent key reports ("", true). Every surface that answers "what model does this agent declare" (sync, CLI, REST) goes through this one helper so they can never disagree on non-scalar handling.

func (*AgentDefinition) ExtraByKey

func (d *AgentDefinition) ExtraByKey(key string) (*yaml.Node, bool)

ExtraByKey returns the raw frontmatter node for one passthrough key. Renderers use it for key-level access without scanning Extra at every call site.

type AgentExtraField

type AgentExtraField struct {
	Key   string
	Value *yaml.Node
}

AgentExtraField is one passthrough frontmatter key.

type AuthConfig

type AuthConfig struct {
	Method         string // "", "none", "token", "ssh-agent", "ssh-key"
	Token          string // resolved plaintext — transient, never persisted
	CredentialRef  string // e.g. "${vault:GIT_TOKEN}" — persisted
	SSHUser        string // defaults to "git" when empty
	SSHKeyPath     string // required for method "ssh-key"
	SSHPassphrase  string // transient
	KnownHostsPath string // reserved for future host-key policy work
}

AuthConfig carries authentication configuration for a git operation. The Token and SSHPassphrase fields are transient — they must never be persisted to disk. CredentialRef is the opaque reference string (e.g. "${vault:GIT_TOKEN}") that gets stored in Origin/LockFile so that Update can re-resolve it later.

type CloneResult

type CloneResult struct {
	RepoPath  string
	CommitSHA string
	Skills    []DiscoveredSkill
	Malformed []MalformedSkill
	// Agents are agent definitions discovered under the agents/*.md
	// convention; MalformedAgents records files in agents/ directories
	// that are not parseable agent definitions.
	Agents          []DiscoveredAgent
	MalformedAgents []MalformedAgent
}

CloneResult contains the result of a clone + discovery operation.

func CloneAndDiscover

func CloneAndDiscover(repo, ref, subPath string, auth AuthConfig, logger *slog.Logger) (*CloneResult, error)

CloneAndDiscover clones a repo and discovers all SKILL.md files plus any agents/*.md definitions it ships.

type CredentialResolver

type CredentialResolver func(ref string) (string, error)

CredentialResolver resolves an opaque reference like "${vault:GIT_TOKEN}" to its raw value. Callers (CLI, HTTP API) register one via Importer.SetCredentialResolver so that Update can re-resolve credentials recorded in Origin/LockFile without the importer needing to know where the values live.

type DiffResult

type DiffResult struct {
	Skill    string `json:"skill"`
	Local    string `json:"local"`    // current on-disk full SKILL.md text
	Upstream string `json:"upstream"` // content an update would install
	Drifted  bool   `json:"drifted"`  // on-disk file diverges from InstalledHash
}

DiffResult holds a skill's current on-disk SKILL.md alongside the content an update would install, for on-demand comparison. Producing it changes no registry state, SHAs, or InstalledHashes.

type DiscoveredAgent

type DiscoveredAgent struct {
	Name        string
	Path        string // Relative path from repo root to the .md file
	Definition  *AgentDefinition
	ContentHash string
}

DiscoveredAgent represents an agent definition found in a cloned repo under an agents/ directory.

type DiscoveredSkill

type DiscoveredSkill struct {
	Name        string
	Path        string // Relative path from repo root to SKILL.md directory
	Skill       *registry.AgentSkill
	ContentHash string
}

DiscoveredSkill represents a SKILL.md found in a cloned repo.

type Fingerprint

type Fingerprint struct {
	ContentHash string   `json:"contentHash" yaml:"content_hash"`
	ToolsHash   string   `json:"toolsHash" yaml:"tools_hash"`
	Tools       []string `json:"tools,omitempty" yaml:"tools,omitempty"`
}

Fingerprint captures the behavioral identity of a skill.

func ComputeFingerprint

func ComputeFingerprint(skill *registry.AgentSkill) *Fingerprint

ComputeFingerprint generates a behavioral fingerprint for a skill.

type ImportOptions

type ImportOptions struct {
	Repo       string
	Ref        string
	Path       string
	Trust      bool     // Skip security scan confirmation
	NoActivate bool     // Import as draft instead of active
	Force      bool     // Overwrite existing skills
	Rename     string   // Rename the skill on import
	Selected   []string // Only import skills with these names (empty = import all)
	// SelectedAgents imports exactly these agent names. When empty, the
	// legacy behavior holds: all agents when Selected is also empty, no
	// agents when a skill selection is present (the web UI picker's
	// contract). Pack imports always pass fully resolved lists.
	SelectedAgents []string
	// Discovered supplies a pre-cloned discovery result so callers that
	// already ran CloneAndDiscover (pack add reads the manifest first)
	// do not clone twice. Nil means Import clones itself.
	Discovered *CloneResult
	Auth       AuthConfig
	// PreserveState carries over the existing skill's State (draft/active/
	// disabled) instead of resetting it. Used by Update so that re-syncing
	// a source does not silently re-activate skills the user disabled.
	PreserveState bool
}

ImportOptions controls the import behavior.

type ImportResult

type ImportResult struct {
	Imported []ImportedSkill `json:"imported"`
	Skipped  []SkippedSkill  `json:"skipped"`
	Warnings []string        `json:"warnings"`
	// ImportedAgents and SkippedAgents record agent definitions the same
	// import discovered under the agents/*.md convention.
	ImportedAgents []ImportedAgent `json:"importedAgents,omitempty"`
	SkippedAgents  []SkippedAgent  `json:"skippedAgents,omitempty"`
}

ImportResult contains the results of an import operation.

type ImportedAgent

type ImportedAgent struct {
	Name     string            `json:"name"`
	Path     string            `json:"path"`
	Origin   *Origin           `json:"origin,omitempty"`
	Findings []SecurityFinding `json:"findings,omitempty"`
}

ImportedAgent records a successfully imported agent definition.

type ImportedSkill

type ImportedSkill struct {
	Name   string  `json:"name"`
	Path   string  `json:"path"`
	Origin *Origin `json:"origin,omitempty"`
	// FilesCopied counts supporting files installed alongside SKILL.md
	// (scripts/, references/, assets/, and package metadata).
	FilesCopied int               `json:"filesCopied"`
	Findings    []SecurityFinding `json:"findings,omitempty"`
}

ImportedSkill records a successfully imported skill.

type Importer

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

Importer orchestrates the skill import process.

func NewImporter

func NewImporter(store *registry.Store, registryDir, lockPath string, logger *slog.Logger) *Importer

NewImporter creates a new skill importer.

func (*Importer) AdvanceTracking

func (imp *Importer) AdvanceTracking(ctx context.Context, skillName, newSHA string) error

AdvanceTracking records that a skill has been reconciled against upstream commit newSHA without changing its on-disk content. It advances only the version-tracking metadata — the skill's origin CommitSHA and the lock-file source's CommitSHA/ContentHash/FetchedAt — leaving the SKILL.md file and its InstalledHash untouched.

Used when a sync skips a locally-edited (drifted) skill: the reviewed upstream version is recorded so it no longer surfaces as an available update, while the user's local edits (and the drift signal that DetectDrift derives from InstalledHash) are preserved.

func (*Importer) AgentInfo

func (imp *Importer) AgentInfo(agentName string) (*SkillInfo, error)

AgentInfo returns details about an imported agent's origin.

func (*Importer) BackupSkillFile

func (imp *Importer) BackupSkillFile(ctx context.Context, skillName, shortSHA string) (string, error)

BackupSkillFile copies a skill's current SKILL.md to SKILL.md.pre-<shortSHA> next to it before an overwrite, so a forced update of a locally-edited skill stays recoverable. It returns the backup file name (relative to the skill directory). A missing SKILL.md is a no-op that returns an empty name.

func (*Importer) Detach

func (imp *Importer) Detach(ctx context.Context, skillName string) error

Detach makes an imported skill local-only by removing its origin sidecar and its lock-file entry. The SKILL.md and the skill itself remain; it simply no longer tracks an upstream source and will not be touched by sync.

func (*Importer) Diff

func (imp *Importer) Diff(ctx context.Context, skillName string) (*DiffResult, error)

Diff fetches the latest upstream SKILL.md for an imported skill and returns both the current on-disk content and the content an update would install, without writing anything to the registry or changing any SHAs/InstalledHash. It is on-demand only — the caller pays for one git fetch.

func (*Importer) Import

func (imp *Importer) Import(opts ImportOptions) (*ImportResult, error)

Import clones a repo, discovers skills and agents, validates, scans, and imports.

func (*Importer) Info

func (imp *Importer) Info(skillName string) (*SkillInfo, error)

Info returns details about a skill's origin and update status.

func (*Importer) Pin

func (imp *Importer) Pin(skillName, ref string) error

Pin updates a skill's ref and disables auto-update.

func (*Importer) Remove

func (imp *Importer) Remove(skillName string) error

Remove removes an imported skill and cleans up origin and lock entries.

func (*Importer) RemoveAgent

func (imp *Importer) RemoveAgent(agentName string) error

RemoveAgent removes an imported agent and cleans up origin and lock entries.

func (*Importer) SetCredentialResolver

func (imp *Importer) SetCredentialResolver(r CredentialResolver)

SetCredentialResolver registers a resolver used to expand CredentialRef values stored in Origin/LockFile when Update fetches the latest state. Without a resolver, Update can still run for sources that have no stored reference (ambient GITHUB_TOKEN / public repos), but will fail fast for sources that do.

func (*Importer) Update

func (imp *Importer) Update(skillName string, dryRun, force, trust bool) (*ImportResult, error)

Update fetches latest for a skill and applies changes.

trust forwards to ImportOptions.Trust. It defaults to false at every caller: a sync that surfaces new security findings is skipped with the finding text rather than applied silently. Previously this was hardcoded true, which meant every sync refreshed upstream content with the scan gate disabled, harmless while only the SKILL.md body was scanned, but not once supporting files are installed too.

type InstalledAgent

type InstalledAgent struct {
	Name       string
	Definition *AgentDefinition
	// Dir is the agent's canonical directory (holds AGENT.md and its
	// .origin.json sidecar).
	Dir string
}

InstalledAgent is one agent present in the canonical store.

func GetAgent

func GetAgent(registryDir, name string) (*InstalledAgent, error)

GetAgent returns one agent from the canonical store.

func ListAgents

func ListAgents(registryDir string) ([]InstalledAgent, error)

ListAgents returns the agents in the canonical store, sorted by name. A missing store directory is the normal no-agents state. Entries whose AGENT.md is missing or unparseable are skipped: the store lists what it can serve.

type LockFile

type LockFile struct {
	Version int                     `yaml:"version"`
	Sources map[string]LockedSource `yaml:"sources"`
}

LockFile represents skills.lock.yaml — pins exact versions of imported skills.

func ReadLockFile

func ReadLockFile(path string) (*LockFile, error)

ReadLockFile reads and parses skills.lock.yaml. Version-less files (written before the schema carried a version) migrate to the current version on read; files from a newer gridctl are rejected with ErrNewerImportLockVersion.

func (*LockFile) FindAgentSource

func (lf *LockFile) FindAgentSource(agentName string) (string, *LockedSource, bool)

FindAgentSource finds the source name for a given agent.

func (*LockFile) FindPackSource

func (lf *LockFile) FindPackSource(packName string) (string, *LockedSource, bool)

FindPackSource finds the source carrying a pack by pack name.

func (*LockFile) FindSkillSource

func (lf *LockFile) FindSkillSource(skillName string) (string, *LockedSource, bool)

FindSkillSource finds the source name for a given skill.

func (*LockFile) RemoveAgent

func (lf *LockFile) RemoveAgent(agentName string)

RemoveAgent removes a single agent from the lock file, cleaning up the source when neither skills nor agents remain under it.

func (*LockFile) RemoveSkill

func (lf *LockFile) RemoveSkill(skillName string)

RemoveSkill removes a single skill from the lock file, cleaning up the source when neither skills nor agents remain under it.

func (*LockFile) RemoveSource

func (lf *LockFile) RemoveSource(name string)

RemoveSource removes a source from the lock file.

func (*LockFile) SetSource

func (lf *LockFile) SetSource(name string, src LockedSource)

SetSource updates or adds a source in the lock file.

type LockedAgent

type LockedAgent struct {
	Path        string `yaml:"path"`
	ContentHash string `yaml:"content_hash"`
}

LockedAgent records per-agent metadata within a source.

type LockedPack

type LockedPack struct {
	Name    string `yaml:"name"`
	Version string `yaml:"version,omitempty"`
	// Description and Author persist the manifest metadata a list view
	// needs, so no consumer ever has to re-clone the repo to show it.
	// Additive fields: files without them keep loading, and version 2
	// (which every pack record already stamps) covers them.
	Description string   `yaml:"description,omitempty"`
	Author      string   `yaml:"author,omitempty"`
	Wiring      bool     `yaml:"wiring,omitempty"`
	Clients     []string `yaml:"clients,omitempty"`
	Skills      []string `yaml:"skills,omitempty"`
	Agents      []string `yaml:"agents,omitempty"`
	// Rules lists context rule fragments imported from the pack repo.
	// Superseded by RuleFiles, which adds per-rule provenance; retained so
	// lockfiles written before that keep loading, and kept in sync on write
	// so a downgrade still sees the selection.
	Rules []string `yaml:"rules,omitempty"`
	// RuleFiles records per-rule provenance keyed by fragment name. An entry
	// with an empty ContentHash means provenance is unknown (migrated from a
	// Rules-only lockfile), and callers must fall back to byte comparison
	// rather than treating the empty hash as a match.
	RuleFiles map[string]LockedRule `yaml:"rule_files,omitempty"`
	// Unresolved lists manifest-selected names discovery could not find,
	// so status can keep reporting them until the upstream repo (or the
	// manifest) is fixed.
	Unresolved []string `yaml:"unresolved,omitempty"`
}

LockedPack is the recorded state of an imported pack: the manifest identity plus the selection as resolved against discovery at import time (never the empty-means-all shorthand).

type LockedRule

type LockedRule struct {
	Path        string `yaml:"path"`
	ContentHash string `yaml:"content_hash"`
}

LockedRule records per-rule-fragment metadata within a pack source. The content hash is what lets a later install tell an upstream change apart from a local edit; without it the only available comparison is raw bytes against disk, which conflates the two.

type LockedSkill

type LockedSkill struct {
	Path        string       `yaml:"path"`
	ContentHash string       `yaml:"content_hash"`
	Fingerprint *Fingerprint `yaml:"fingerprint,omitempty"`
}

LockedSkill records per-skill metadata within a source.

type LockedSource

type LockedSource struct {
	Repo        string                 `yaml:"repo"`
	Ref         string                 `yaml:"ref"`
	ResolvedRef string                 `yaml:"resolved_ref,omitempty"`
	CommitSHA   string                 `yaml:"commit_sha"`
	FetchedAt   time.Time              `yaml:"fetched_at"`
	ContentHash string                 `yaml:"content_hash"`
	Skills      map[string]LockedSkill `yaml:"skills"`
	// Agents records agent definitions imported from this source.
	Agents map[string]LockedAgent `yaml:"agents,omitempty"`
	// CredentialRef is an opaque reference like "${vault:GIT_TOKEN}" used to
	// re-resolve credentials on source update. Raw tokens are never stored.
	CredentialRef string `yaml:"credential_ref,omitempty"`
	// Pack records the pack manifest this source was imported through,
	// with its resolved selection. Nil for plain skill/agent sources.
	Pack *LockedPack `yaml:"pack,omitempty"`
}

LockedSource records the resolved state of a skill source.

type MalformedAgent

type MalformedAgent = MalformedSkill

MalformedAgent aliases MalformedSkill so agent call sites read as what they are; the shape and JSON encoding are identical.

type MalformedSkill

type MalformedSkill struct {
	Path string `json:"path"` // Relative path from repo root
	Err  string `json:"error"`
}

MalformedSkill records a SKILL.md that could not be read or parsed (or a directory that could not be walked), so callers can surface the failure instead of silently dropping it.

type Origin

type Origin struct {
	Repo        string    `json:"repo"`
	Ref         string    `json:"ref"`
	Path        string    `json:"path,omitempty"`
	CommitSHA   string    `json:"commitSha"`
	ImportedAt  time.Time `json:"importedAt"`
	ContentHash string    `json:"contentHash"`
	// InstalledHash is the SHA-256 of the SKILL.md as written to disk
	// immediately after the last import. DetectDrift compares the current
	// on-disk hash against this to surface local user edits. Distinct from
	// ContentHash, which records the upstream file as fetched (and which
	// diverges from the installed file because of frontmatter render
	// normalization and state injection).
	InstalledHash string       `json:"installedHash,omitempty"`
	Fingerprint   *Fingerprint `json:"fingerprint,omitempty"`
	// CredentialRef is an opaque reference like "${vault:GIT_TOKEN}" used to
	// re-resolve credentials on skill update. Raw token values are never
	// persisted — only the reference string.
	CredentialRef string `json:"credentialRef,omitempty"`
}

Origin tracks the remote source of an imported skill. Stored as .origin.json alongside the SKILL.md file.

func ReadOrigin

func ReadOrigin(skillDir string) (*Origin, error)

ReadOrigin reads the .origin.json file from a skill directory.

type ScanResult

type ScanResult struct {
	SkillName string            `json:"skillName"`
	Findings  []SecurityFinding `json:"findings"`
	Safe      bool              `json:"safe"`
}

ScanResult contains the security scan results for a skill.

func ScanAgent

func ScanAgent(def *AgentDefinition) *ScanResult

ScanAgent checks an agent definition for dangerous patterns in its body and frontmatter values. Like skill bodies (and unlike supporting files), any finding blocks: an agent definition is instructions the client executes with tool access.

func ScanFragment

func ScanFragment(name string, content []byte) *ScanResult

ScanFragment checks a context rule fragment for dangerous patterns. Rule fragments are instructions clients inject into every session, so they get the same blocking scan agents do: the whole file (frontmatter and body) is scanned, and any finding blocks without --trust.

func ScanSkill

func ScanSkill(sk *registry.AgentSkill) *ScanResult

ScanSkill checks a skill for dangerous patterns in its body.

type SecurityFinding

type SecurityFinding struct {
	StepID      string `json:"stepId"`
	Pattern     string `json:"pattern"`
	Description string `json:"description"`
	Severity    string `json:"severity"` // "warning" or "danger"
}

SecurityFinding represents a potentially dangerous pattern found in a skill.

func ScanSkillTree

func ScanSkillTree(sk *registry.AgentSkill, srcDir string) (findings []SecurityFinding, blocking bool)

ScanSkillTree runs the same security gate an import applies to one discovered skill: the SKILL.md body (any finding blocks) plus the supporting-file tree (danger-severity findings block; lower severities surface without blocking). srcDir is the skill's directory in the clone. Callers that refuse before importing (the pack REST surface) use this so their refusal covers exactly what the importer would skip.

type SkillDefaults

type SkillDefaults struct {
	AutoUpdate     bool   `yaml:"auto_update" json:"autoUpdate"`
	UpdateInterval string `yaml:"update_interval" json:"updateInterval"`
}

SkillDefaults defines global defaults for skill sources.

type SkillInfo

type SkillInfo struct {
	Name        string    `json:"name"`
	Origin      *Origin   `json:"origin,omitempty"`
	IsRemote    bool      `json:"isRemote"`
	UpdateAvail bool      `json:"updateAvailable"`
	LatestSHA   string    `json:"latestSha,omitempty"`
	LastChecked time.Time `json:"lastChecked,omitempty"`
}

SkillInfo returns details about an imported skill.

type SkillSource

type SkillSource struct {
	Name           string      `yaml:"name" json:"name"`
	Repo           string      `yaml:"repo" json:"repo"`
	Ref            string      `yaml:"ref,omitempty" json:"ref,omitempty"`
	Path           string      `yaml:"path,omitempty" json:"path,omitempty"`
	AutoUpdate     *bool       `yaml:"auto_update,omitempty" json:"autoUpdate,omitempty"`
	UpdateInterval string      `yaml:"update_interval,omitempty" json:"updateInterval,omitempty"`
	Auth           *SourceAuth `yaml:"auth,omitempty" json:"auth,omitempty"`
}

SkillSource defines a remote skill source in skills.yaml.

type SkillUpdate

type SkillUpdate struct {
	CurrentSHA string `yaml:"current_sha"`
	LatestSHA  string `yaml:"latest_sha"`
	Repo       string `yaml:"repo"`
	Ref        string `yaml:"ref"`
}

SkillUpdate describes an available update for a skill.

type SkillsConfig

type SkillsConfig struct {
	Defaults SkillDefaults `yaml:"defaults,omitempty" json:"defaults,omitempty"`
	Sources  []SkillSource `yaml:"sources" json:"sources"`
}

SkillsConfig represents the skills.yaml file.

func DefaultSkillsConfig

func DefaultSkillsConfig() *SkillsConfig

DefaultSkillsConfig returns a config with sensible defaults.

func LoadSkillsConfig

func LoadSkillsConfig(path string) (*SkillsConfig, error)

LoadSkillsConfig reads and parses a skills.yaml file.

func (*SkillsConfig) EffectiveAutoUpdate

func (c *SkillsConfig) EffectiveAutoUpdate(src *SkillSource) bool

EffectiveAutoUpdate returns the auto_update setting for a source, falling back to the global default.

func (*SkillsConfig) EffectiveUpdateInterval

func (c *SkillsConfig) EffectiveUpdateInterval(src *SkillSource) time.Duration

EffectiveUpdateInterval returns the update_interval for a source, falling back to the global default.

type SkippedAgent

type SkippedAgent = SkippedSkill

SkippedAgent aliases SkippedSkill so agent call sites read as what they are; the shape and JSON encoding are identical.

type SkippedSkill

type SkippedSkill struct {
	Name   string `json:"name"`
	Reason string `json:"reason"`
}

SkippedSkill records a skill (or agent) that was skipped during import.

type SourceAuth

type SourceAuth struct {
	Method        string `yaml:"method,omitempty" json:"method,omitempty"`
	CredentialRef string `yaml:"credential_ref,omitempty" json:"credentialRef,omitempty"`
	SSHUser       string `yaml:"ssh_user,omitempty" json:"sshUser,omitempty"`
	SSHKeyPath    string `yaml:"ssh_key_path,omitempty" json:"sshKeyPath,omitempty"`
}

SourceAuth is the declarative auth block on a skills.yaml source. Raw tokens must NOT appear here — use CredentialRef (e.g. "${vault:GIT_TOKEN}") which is resolved against the live vault at clone/fetch time.

func (*SourceAuth) ToAuthConfig

func (a *SourceAuth) ToAuthConfig() AuthConfig

ToAuthConfig converts the declarative block into a runtime AuthConfig. CredentialRef is copied through unchanged; callers are responsible for resolving it to a raw Token before invoking the importer.

type UpdateStatus

type UpdateStatus struct {
	CheckedAt time.Time              `yaml:"checked_at"`
	Updates   map[string]SkillUpdate `yaml:"updates,omitempty"`
	Errors    []string               `yaml:"errors,omitempty"`
}

UpdateStatus records the result of a background update check.

func ReadUpdateCache

func ReadUpdateCache() (*UpdateStatus, error)

ReadUpdateCache reads the cached update status from the default path.

func ReadUpdateCacheAt

func ReadUpdateCacheAt(path string) (*UpdateStatus, error)

ReadUpdateCacheAt reads the cached update status from an explicit path. Returns (nil, nil) when the file does not exist so callers can fail open.

Jump to

Keyboard shortcuts

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