skillsync

package
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Sep 17, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package skillsync installs immutable, CLI-pinned Agent Skills bundles into supported harness directories. It owns no command framework or product policy: callers supply their identity, embedded bundle snapshots, targets, and any host-specific error mapping.

Index

Constants

View Source
const StateFileName = ".cli-helpers-skills-sync.json"

StateFileName is deliberately provider-neutral and stores only verified ownership/provenance, never mutable source content.

Variables

View Source
var (
	ErrInvalidConfig     = errors.New("invalid skills sync configuration")
	ErrDigestMismatch    = errors.New("bundle digest mismatch")
	ErrStateCorrupt      = errors.New("skills sync state is corrupt")
	ErrRecoveryPending   = errors.New("skills sync recovery is pending")
	ErrNoNewerCompatible = errors.New("no newer compatible bundle")
	ErrSearchIncomplete  = errors.New("newer-compatible release search incomplete")
)

Functions

func CompareVersions

func CompareVersions(a, b string) (int, error)

CompareVersions compares two validated semantic versions.

func Compatible

func Compatible(current string, c Compatibility) bool

Compatible reports whether a semantic CLI version satisfies compatibility.

func Digest

func Digest(source fs.FS) (string, error)

Digest returns a deterministic SHA-256 of source bytes and executable mode. For embed.FS callers, use DigestWithExecutables with the source descriptor's explicit executable paths.

func DigestWithExecutables

func DigestWithExecutables(source fs.FS, executablePaths []string) (string, error)

func NormalizeExecutablePaths

func NormalizeExecutablePaths(source fs.FS, paths []string) ([]string, error)

NormalizeExecutablePaths returns the complete sorted executable manifest. It combines declared paths with executable modes carried by a local source filesystem, which embedding otherwise loses.

func ValidateDescriptor

func ValidateDescriptor(d BundleDescriptor) error

ValidateDescriptor verifies provenance and descriptor-only fields before a remote adapter uses them for release selection. Content and executable-file existence are verified later by EmbeddedBundle.

func ValidateTarget

func ValidateTarget(dir string) (string, error)

ValidateTarget normalizes a target path and rejects symlinked or non- directory existing ancestors. It permits only the verified macOS /tmp and /var system aliases, returning their canonical path for callers that must deduplicate targets before writing them.

Types

type Action

type Action string
const (
	Added     Action = "added"
	Updated   Action = "updated"
	Unchanged Action = "unchanged"
	Removed   Action = "removed"
	Conflict  Action = "conflict"
)

type Bundle

type Bundle struct {
	Plugin          PluginIdentity
	Source          Source
	FS              fs.FS
	ExecutablePaths []string
}

Bundle is an immutable source snapshot. Source is its sole provenance and version authority. ExecutablePaths preserves the executable bits that embed.FS cannot represent.

func EmbeddedBundle

func EmbeddedBundle(d BundleDescriptor, content fs.FS) (Bundle, error)

EmbeddedBundle binds one canonical embedded tree to its immutable metadata.

type BundleDescriptor

type BundleDescriptor struct {
	Plugin          PluginIdentity `json:"plugin"`
	Source          Source         `json:"source"`
	ExecutablePaths []string       `json:"executable_paths,omitempty"`
}

BundleDescriptor is serializable build metadata. EmbeddedBundle is the common loader for go:embed snapshots, so hosts need not duplicate fs.Sub, source-provenance validation, or digest checks.

type Change

type Change struct {
	Plugin  PluginIdentity `json:"plugin"`
	Name    string         `json:"name"`
	Action  Action         `json:"action"`
	Outcome Outcome        `json:"outcome,omitempty"`
	Reason  string         `json:"reason,omitempty"`
}

type Compatibility

type Compatibility struct {
	MinCLI string `json:"min_cli,omitempty"`
	MaxCLI string `json:"max_cli,omitempty"`
}

Compatibility limits the CLI versions a bundle can be installed by. Empty bounds are open; values use numeric dot-separated versions, optionally with a leading "v". Pre-release selection is intentionally host policy.

type Config

type Config struct {
	CLI            Identity
	CurrentVersion string
	Bundles        []Bundle
}

Config declares the installed CLI and its offline matched snapshots. A development build may use an undetermined CurrentVersion only when its embedded matched bundles declare no compatibility bounds; it never selects a newer release by default.

type Identity

type Identity struct {
	Publisher string `json:"publisher"`
	Name      string `json:"name"`
}

Identity identifies the CLI that supplied a bundle. It is recorded as provenance only; plugin ownership always uses PluginIdentity.

func (Identity) String

func (i Identity) String() string

type LegacyImport

type LegacyImport struct {
	MarkerFile string
	Plugin     PluginIdentity
}

LegacyImport enables only a host's explicit one-time marker migration. MarkerFile is relative to the target directory and Plugin is the identity that will own matching, verified legacy skills.

type Options

type Options struct {
	Dir                   string
	DryRun                bool
	PreferNewerCompatible bool
	Resolver              Resolver
	LockTimeout           time.Duration
	Legacy                LegacyImport
}

Options supplies a target and controls whether Sync changes it. Resolver is reserved for explicit newer-compatible bundle selection; normal sync never calls it and therefore never requires network access.

type Outcome

type Outcome string

Outcome tells callers whether a planned mutation reached durable ownership. Conflict and unchanged entries deliberately have no mutation outcome.

const (
	Planned    Outcome = "planned"
	Applied    Outcome = "applied"
	Restored   Outcome = "restored"
	Incomplete Outcome = "incomplete"
)

type PluginIdentity

type PluginIdentity struct {
	Publisher string `json:"publisher"`
	Name      string `json:"name"`
}

PluginIdentity is globally stable and deliberately separate from a skill directory name, which avoids flat-directory collisions between products.

A plugin identity is also Sync's independent-install and removal boundary: one Sync call computes desired state, including removals, only within the plugin keys present in that call's Config.Bundles, and only from skills that plugin key already owns. Two skills sharing one PluginIdentity are therefore installed and removed together — Sync-ing one of them alone drops the other, because it is no longer "desired" under that shared key. A caller that wants each skill installable and removable on its own (for example a CLI offering per-skill consent, one `install <skill>` call per bundle) MUST give each such skill its own PluginIdentity. Skills owned by a different plugin key, or never registered with skillsync at all, are never touched by a Sync call that does not name their key.

func (PluginIdentity) String

func (p PluginIdentity) String() string

type Prepared

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

Prepared is one validated, immutable source selection. A host that syncs multiple harnesses prepares it once, then uses Sync for every target so an explicit newer-compatible resolver cannot select different releases midway through a command.

func Prepare

func Prepare(ctx context.Context, cfg Config, opts Options) (Prepared, error)

Prepare validates the host configuration and resolves any explicitly requested newer-compatible bundles once. It performs no target I/O.

func (Prepared) Sync

func (p Prepared) Sync(ctx context.Context, opts Options) (Report, error)

Sync applies this already-prepared source set to one target. Bundle content is revalidated before target classification and mutation, so preparation is a consistency boundary rather than a trust bypass.

type ReleaseResolver

type ReleaseResolver struct {
	Source         ReleaseSource
	CurrentVersion string
}

ReleaseResolver is the standard explicit newer-compatible adapter. Put it in Options.Resolver at CLI wiring time; ordinary sync does not invoke it.

func (ReleaseResolver) Resolve

func (r ReleaseResolver) Resolve(ctx context.Context, matched Bundle) (Bundle, error)

type ReleaseSource

type ReleaseSource interface {
	NewerCompatible(context.Context, Source, string) (BundleDescriptor, fs.FS, error)
}

ReleaseSource retrieves one newer source snapshot chosen against a CLI version. Implementations may read a signed release archive or a local cache; the shared resolver still validates its immutable descriptor and content digest before it reaches Sync.

type Report

type Report struct {
	Dir        string           `json:"dir"`
	CLI        Identity         `json:"cli"`
	CLIVersion string           `json:"cli_version"`
	DryRun     bool             `json:"dry_run"`
	Bundles    []ResolvedBundle `json:"bundles"`
	Changes    []Change         `json:"changes"`
}

func Sync

func Sync(ctx context.Context, cfg Config, opts Options) (Report, error)

Sync validates all bundles before touching a target, then applies a plugin-scoped plan under a target lock. It defaults to the embedded bundle snapshots supplied in Config and never contacts a Resolver unless the caller explicitly selected PreferNewerCompatible.

func (Report) Changed

func (r Report) Changed() bool

func (Report) ChangesFor

func (r Report) ChangesFor(a Action, outcomes ...Outcome) []Change

ChangesFor returns action-matched changes, optionally limited to exact mutation outcomes. It keeps renderers from mistaking restored work for a completed update.

func (Report) Names

func (r Report) Names(a Action) []string

func (Report) NamesFor

func (r Report) NamesFor(a Action, outcomes ...Outcome) []string

NamesFor returns names from ChangesFor in deterministic order.

type ResolvedBundle

type ResolvedBundle struct {
	Plugin          PluginIdentity `json:"plugin"`
	Source          Source         `json:"source"`
	PriorCLIVersion string         `json:"prior_cli_version,omitempty"`
}

type Resolver

type Resolver interface {
	Resolve(context.Context, Bundle) (Bundle, error)
}

Resolver resolves an explicitly requested newer compatible bundle. It must return a complete, digest-pinned Bundle; Sync verifies it before planning.

type Skill

type Skill struct{ Name, Digest string }

Skill is one valid skill directory in a bundle.

func Discover

func Discover(source fs.FS) ([]Skill, error)

Discover lists valid skill directories and their deterministic digests.

type Source

type Source struct {
	Repository    string        `json:"repository"`
	Path          string        `json:"path"`
	Revision      string        `json:"revision"`
	Version       string        `json:"version"`
	Digest        string        `json:"digest"`
	Compatibility Compatibility `json:"compatibility,omitempty"`
}

Source is reproducible bundle provenance. Repository, Path, Revision, and Digest identify exact bytes; Version names the plugin release for people and plugin hosts. It is persisted with ownership state for diagnostics.

type Status

type Status struct {
	Installed           bool                         `json:"installed"`
	Plugins             map[string]Source            `json:"plugins"`
	SupplierCLIVersions map[string]map[string]string `json:"supplier_cli_versions,omitempty"`
}

Status is the marker-only query hosts use for drift banners; it never walks installed skill trees or contacts a release source.

func ReadStatus

func ReadStatus(dir string) (Status, error)

ReadStatus reads only the small ownership marker. A missing marker is the normal not-yet-synced state, while corrupt state remains a safe error.

Directories

Path Synopsis
Package cliui renders skillsync reports for any command framework.
Package cliui renders skillsync reports for any command framework.
Package cobracmd exposes optional Cobra wiring for skillsync.
Package cobracmd exposes optional Cobra wiring for skillsync.
Package githubrelease resolves explicitly requested newer-compatible bundles from published GitHub Release assets.
Package githubrelease resolves explicitly requested newer-compatible bundles from published GitHub Release assets.
Package producer builds one immutable skillsync release snapshot from an already-checked-out local Git repository.
Package producer builds one immutable skillsync release snapshot from an already-checked-out local Git repository.
Package reexec runs the newly installed CLI for a post-update skills sync.
Package reexec runs the newly installed CLI for a post-update skills sync.
Package selfupdate connects the reusable skills refresh runner to the optional typed callback exposed by cli-helpers/selfupdate.
Package selfupdate connects the reusable skills refresh runner to the optional typed callback exposed by cli-helpers/selfupdate.
Package snapshot encodes one verified skillsync bundle as a reproducible tar artifact and exposes the same descriptor/content pair for embedding.
Package snapshot encodes one verified skillsync bundle as a reproducible tar artifact and exposes the same descriptor/content pair for embedding.

Jump to

Keyboard shortcuts

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