Documentation
¶
Overview ¶
Package skillslock owns the shared project-level skills-lock.json v1 format that gskill co-owns with compatible external tooling (e.g. `npx skills`).
The package provides a lossless model: fields it does not understand — unknown top-level keys, unknown skill-entry keys, and other tools' extension blocks — survive every rewrite with their values intact and their key order stable, so the file remains fully usable by the other tool. gskill's own metadata lives under the namespaced per-entry "gskill" field and never repurposes the shared core fields (source, sourceType, skillPath, computedHash).
It also hosts the in-memory working model (State/Record) commands operate on, and the bridge between records and shared-format entries.
Index ¶
- Constants
- Variables
- func Marshal(l *Lock) ([]byte, error)
- func Save(path string, l *Lock) error
- type Entry
- type Ext
- type ExtState
- type Installation
- type Lock
- func (l *Lock) CheckExts() error
- func (l *Lock) Entry(name string) (Entry, bool)
- func (l *Lock) Has(name string) bool
- func (l *Lock) Names() []string
- func (l *Lock) Remove(name string) bool
- func (l *Lock) ReplaceEntryCore(name string, e Entry)
- func (l *Lock) SetEntry(name string, e Entry)
- func (l *Lock) SetExt(name string, ext *Ext) error
- func (l *Lock) Validate() error
- func (l *Lock) Version() int
- type Metadata
- type OverrideDecl
- type Provenance
- type Record
- type Requested
- type Requires
- type Resolved
- type Source
- type State
Constants ¶
const FileName = "skills-lock.json"
FileName is the shared project-level lock file gskill co-owns with compatible external tooling.
const SchemaVersion = 1
SchemaVersion is the skills-lock.json schema this build understands.
const ScopeGlobal = "global"
ScopeGlobal is the user-global agent-target scope (mirrors installer.ScopeGlobal, duplicated here to keep skillslock dependency-free).
Variables ¶
var ( // ErrInvalid is returned for a structurally or semantically invalid lock. ErrInvalid = errors.New("invalid skills-lock.json") // ErrUnsupportedSchema is returned for a version other than SchemaVersion; // gskill refuses to guess at (or rewrite) a schema it does not understand. ErrUnsupportedSchema = errors.New("unsupported skills-lock.json schema version") )
Sentinel errors.
Functions ¶
Types ¶
type Entry ¶
type Entry struct {
Source string
Ref string // optional branch/tag used for installation (npx "ref")
SourceType string
SkillPath string
ComputedHash string
Ext *Ext
}
Entry is the typed view of one skill entry's core fields plus gskill's namespaced extension block (nil when the entry was never installed by gskill). Unknown entry fields stay in the Lock and survive rewrites.
func FromRecord ¶
FromRecord maps an in-memory record into a shared-format entry: core fields stay npx-skills-compatible, everything gskill-specific goes under the namespaced extension.
type Ext ¶
type Ext struct {
SourceURL string `json:"sourceUrl,omitempty"`
Ref string `json:"ref,omitempty"`
Commit string `json:"commit,omitempty"`
Version string `json:"version,omitempty"`
Agents []string `json:"agents,omitempty"`
InstallMode string `json:"installMode,omitempty"`
// BaseHash is the content hash of the upstream extract *before* any
// override is applied (spec 023). With no override declared it equals
// ContentHash and is omitted, so spec 022 entries stay byte-identical.
BaseHash string `json:"baseHash,omitempty"`
// ContentHash is the canonical full-content hash of the committed skill
// copy (spec 022): it covers symlinks that the shared core computedHash
// (npx-compat) skips, so restores verify against it. Since spec 023 it is
// the *post-override* result — the name and its role in verification are
// unchanged, which is what lets drift detection and restore keep working.
ContentHash string `json:"contentHash,omitempty"`
// OverrideDigest is the canonical digest over the resolved override
// declaration plus the content hash of every file it references (spec 023
// FR-007). Empty when no override is declared. Because the referenced
// files' bytes feed the digest, editing one is detected as drift without
// any file watching.
OverrideDigest string `json:"overrideDigest,omitempty"`
// Override records the resolved declaration itself, so the lockfile stays
// self-describing about what was applied: an audit or a --frozen-lockfile
// check can detect a changed declaration without consulting the manifest
// (spec 023 plan.md Deviation 1).
Override *OverrideDecl `json:"override,omitempty"`
// Scope records the agent-target scope and is written ONLY for
// user-global installs (`add --global`): that placement is not derivable
// from anything else in the entry, and dropping it would silently
// re-materialize the skill into the project on the next sync. The
// pre-022 store-location value "project" is read for migration but never
// written — entries drop it on their first rewrite (spec 022
// data-model §3), which is what marks them legacy.
Scope string `json:"scope,omitempty"`
// StoreHash is the pre-022 content-hash field: read for migration, never
// written — superseded by ContentHash.
StoreHash string `json:"storeHash,omitempty"`
SkillFileHash string `json:"skillFileHash,omitempty"`
InstalledAt string `json:"installedAt,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
// State nests the residual machine state existing commands still consume;
// see ExtState (bridge.go).
State *ExtState `json:"state,omitempty"`
}
Ext is the namespaced per-entry "gskill" extension block (FR-004): every gskill-owned fact lives here, never in the shared core fields, so the file stays fully usable by other tools. Timestamps are confined to this block and excluded from reproducible determinism, mirroring the legacy Provenance carve-out.
type ExtState ¶
type ExtState struct {
// SourceKind is gskill's resolved source type (e.g. a "local" entry whose
// path is a git repo resolves as "git"); the entry's core sourceType stays
// whatever the external tool wrote.
SourceKind string `json:"sourceKind,omitempty"`
SourceOriginal string `json:"sourceOriginal,omitempty"`
SourceOwner string `json:"sourceOwner,omitempty"`
SourceRepo string `json:"sourceRepo,omitempty"`
SourcePath string `json:"sourcePath,omitempty"`
RequestedVersion string `json:"requestedVersion,omitempty"`
RequestedRef string `json:"requestedRef,omitempty"`
RequestedCommit string `json:"requestedCommit,omitempty"`
DeclarationKind string `json:"declarationKind,omitempty"`
RefKind string `json:"refKind,omitempty"`
Tag string `json:"tag,omitempty"`
Branch string `json:"branch,omitempty"`
TreeHash string `json:"treeHash,omitempty"`
MutableRef bool `json:"mutableRef,omitempty"`
LocalPathHash string `json:"localPathHash,omitempty"`
MetaName string `json:"metaName,omitempty"`
MetaDescription string `json:"metaDescription,omitempty"`
MetaVersion string `json:"metaVersion,omitempty"`
MetaLicense string `json:"metaLicense,omitempty"`
RequiresSkills []string `json:"requiresSkills,omitempty"`
RequiresCommands []string `json:"requiresCommands,omitempty"`
RequiresEnvironment []string `json:"requiresEnvironment,omitempty"`
RequiresMCP []string `json:"requiresMcp,omitempty"`
ActivePath string `json:"activePath,omitempty"`
Targets map[string]string `json:"targets,omitempty"`
Modes map[string]string `json:"modes,omitempty"`
Trust string `json:"trust,omitempty"`
}
ExtState carries the residual gskill install state that has no place in the interop-visible Ext fields but is still consumed by existing commands (update skip logic, target removal, drift checks, display metadata). It lives nested under gskill.state so the top of the extension block stays the small, documented interop surface.
type Installation ¶
type Installation struct {
Scope string
Mode string
Agents []string
ActivePath string
Targets map[string]string
Modes map[string]string
}
Installation is the placement record (FR-019, FR-020, FR-027, FR-028). Mode is the representative install mode; Modes records the actual mode per agent for the case where they differ (e.g. a symlink falls back to a copy). ActivePath is the project-relative active-layer entry (.agents/skills/<name>) that every agent target derives from.
type Lock ¶
type Lock struct {
// contains filtered or unexported fields
}
Lock is the lossless in-memory form of skills-lock.json: typed access to the fields gskill understands, raw preservation for everything else.
func Unmarshal ¶
Unmarshal parses lock bytes, enforcing the structural schema: valid JSON, version == SchemaVersion, and a skills object whose values are objects. Entry-level field validation is Validate's job.
func (*Lock) CheckExts ¶
CheckExts fails when any entry carries a gskill block that does not parse. A corrupted block must fail closed, not silently degrade the entry to external-only (which would drop it from restore/remove/sync).
func (*Lock) ReplaceEntryCore ¶
ReplaceEntryCore rewrites an entry's core identity fields. This is the one sanctioned core rewrite: manifest-wins reconciliation (spec 012 FR-023), where the user explicitly chose the manifest's declaration over the lock. Stale verification facts — computedHash and the gskill block — are dropped so the next install re-resolves and re-records them; fields gskill does not understand still survive.
func (*Lock) SetEntry ¶
SetEntry creates or updates an entry, preserving any fields it does not understand. Core identity fields (source, ref, sourceType, skillPath) are owned by whichever tool wrote them first: gskill fills them only when absent and never rewrites them. computedHash is the shared verification fact and is updated in place; the gskill extension block is always gskill's to replace. New entries are inserted in sorted order after the original ones.
type OverrideDecl ¶ added in v0.7.0
type OverrideDecl struct {
Replace map[string]string `json:"replace,omitempty"`
Patch []string `json:"patch,omitempty"`
Prepend []string `json:"prepend,omitempty"`
Append []string `json:"append,omitempty"`
}
OverrideDecl is the resolved override declaration as recorded in the lock. Declared list order is significant — patches and layers apply in sequence — so it is preserved verbatim; the Replace map is serialized with sorted keys, since map iteration order must never reach output (Constitution I).
func (*OverrideDecl) Empty ¶ added in v0.7.0
func (o *OverrideDecl) Empty() bool
Empty reports whether the declaration transforms nothing.
type Provenance ¶
Provenance is best-effort trust info. Timestamps are excluded from reproducible determinism (FR-004).
type Record ¶
type Record struct {
Source Source
Requested Requested
Resolved Resolved
Metadata Metadata
Requires Requires
Installation Installation
Provenance Provenance
}
Record is the full reproduction record for one managed skill.
type Requested ¶
Requested echoes the human's intent (FR-010). Kind names the declaration shape the intent had when it was last resolved (spec 024 data-model.md §3); it is informational and empty on entries written before it existed.
type Resolved ¶
type Resolved struct {
Version string
RefKind string
Tag string
Branch string
Commit string
TreeHash string
ContentHash string
SkillFileHash string
// BaseHash, OverrideDigest, and Override carry the spec 023 override
// identity: the upstream hash before transformation, the canonical digest
// of the declaration plus its inputs, and the declaration itself. All
// three are empty for a skill with no override, so spec 022 records are
// unaffected.
BaseHash string
OverrideDigest string
Override *OverrideDecl
MutableRef bool
LocalPathHash string
// CompatHash is the npx-skills-compatible computedHash (spec 012). The
// shared skills-lock.json persists it as the core computedHash field.
CompatHash string
}
Resolved is the immutable identity gskill pinned to (FR-009, FR-010).