plugins

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Index

Constants

View Source
const LockFileName = "plugins.lock"

LockFileName maps an installed plugin id to its source and content hash.

Variables

View Source
var ErrNameClash = errors.New("a different plugin with that id is already installed")

ErrNameClash is returned when an install would overwrite a plugin already installed from a DIFFERENT source, unless InstallOptions.Force is set.

Functions

func FormatList

func FormatList(plugins []LoadedPlugin, diagnostics []Diagnostic) string

func MergedSkills

func MergedSkills(defaultDir string, pluginRoots []string) ([]skills.Skill, []skills.DuplicateName)

MergedSkills loads the default skills directory plus the supplied plugin skill roots and returns one merged, name-deduplicated list (Content stripped, like skills.List) alongside the duplicate-name collisions across all roots. Earlier roots win a name clash, matching skills.Load's first-wins rule; the default dir is always considered first so a user skill shadows a same-named plugin skill. A bad root simply yields no skills rather than failing the merge.

func MergedSkillsLoaded

func MergedSkillsLoaded(defaultDir string, pluginRoots []string) ([]skills.Skill, []skills.DuplicateName)

MergedSkillsLoaded is MergedSkills but keeps each skill's Content (so the skill tool can return a body). MergedSkills strips Content for listing callers.

func NewSkillTool

func NewSkillTool(defaultDir string, pluginRoots []string) tools.Tool

NewSkillTool builds the plugin-aware skill tool. defaultDir is the standard skills directory (skills.DefaultDir); pluginRoots are the plugin skill search roots from an ActivationResult. The returned tool merges both, deterministically deduplicating by name (default dir wins a clash) and listing all available skills when an unknown name is requested.

func ReadLock

func ReadLock(dir string) (map[string]LockEntry, error)

ReadLock loads the plugins lockfile from dir. A missing lockfile yields an empty map with no error.

func Remove

func Remove(dir string, id string) error

Remove deletes an installed plugin directory and its lockfile entry. It errors if the named plugin is not present in either the dir or the lockfile.

func ResolvePluginPath

func ResolvePluginPath(pluginDir string, value string, fieldPath string) (string, error)

Types

type ActivateOptions

type ActivateOptions struct {
	// Cwd is the working directory plugin tool commands run in. Empty falls back to
	// the plugin's own directory.
	Cwd string
	// Timeout bounds each plugin tool command. <= 0 uses defaultPluginToolTimeout.
	Timeout time.Duration
	// Env supplies the base environment for plugin tool commands. nil uses the
	// current process environment (os.Environ()).
	Env []string
	// contains filtered or unexported fields
}

ActivateOptions configures activation. runTool is injectable for tests; when nil it defaults to executing the plugin command as a subprocess.

type ActivationResult

type ActivationResult struct {
	// Hooks are the live hook definitions built from plugin manifests, ready to be
	// merged into the dispatcher's hook set. Order is deterministic.
	Hooks []hooks.Definition
	// SkillRoots are directories internal/skills.Load can scan to discover plugin
	// skills, deduplicated and deterministically ordered.
	SkillRoots []string
	// Tools/HookProv/Skills carry provenance so a listing can attribute each live
	// extension to its originating plugin.
	Tools    []ToolProvenance
	HookProv []HookProvenance
	Skills   []SkillProvenance
	// Warnings collects per-plugin/per-extension issues that caused an extension to
	// be skipped. A single malformed plugin never aborts activation.
	Warnings []string
}

ActivationResult reports what a single Activate call wired up. Tools are registered into the supplied registry directly; the remaining fields are returned for the caller (the agent bootstrap) to merge into the hook dispatcher and skills loader, and to surface provenance/warnings.

func Activate

func Activate(registry *tools.Registry, loaded []LoadedPlugin, options ActivateOptions) ActivationResult

Activate turns the resolved plugin extensions into live registrations: it registers each plugin tool into registry, and returns the hook definitions and skill roots for the caller to wire into the dispatcher and skills loader.

Activation is isolation-first: a malformed plugin or extension is skipped with a recorded warning rather than aborting the whole activation, mirroring how skills.Load skips a bad skill. Plugins are processed in a deterministic order (sorted by ID) so the resulting registrations are stable across runs.

type Diagnostic

type Diagnostic struct {
	Kind         DiagnosticKind `json:"kind"`
	Message      string         `json:"message"`
	Source       Source         `json:"source,omitempty"`
	Root         string         `json:"root,omitempty"`
	PluginPath   string         `json:"pluginPath,omitempty"`
	ManifestPath string         `json:"manifestPath,omitempty"`
	FieldPath    string         `json:"fieldPath,omitempty"`
	PluginID     string         `json:"pluginId,omitempty"`
}

type DiagnosticKind

type DiagnosticKind string
const (
	DiagnosticIO        DiagnosticKind = "io"
	DiagnosticJSON      DiagnosticKind = "json"
	DiagnosticSchema    DiagnosticKind = "schema"
	DiagnosticDuplicate DiagnosticKind = "duplicate"
)

type GitRunner

type GitRunner func(ctx context.Context, destination string, source string) error

GitRunner fetches the plugin at source into destination. The default runner shallow-clones with the system git (inheriting the process environment, so proxy/egress settings are honored). It is injectable so tests never hit the network. A runner must only fetch — it must never execute fetched content.

type HookEvent

type HookEvent string
const (
	HookBeforeTool   HookEvent = "beforeTool"
	HookAfterTool    HookEvent = "afterTool"
	HookSessionStart HookEvent = "sessionStart"
	HookSessionEnd   HookEvent = "sessionEnd"
)

type HookExtension

type HookExtension struct {
	Name        string    `json:"name"`
	Description string    `json:"description,omitempty"`
	Event       HookEvent `json:"event"`
	Command     string    `json:"command"`
	Args        []string  `json:"args"`
}

type HookProvenance

type HookProvenance struct {
	HookID   string `json:"hookId"`
	PluginID string `json:"pluginId"`
}

HookProvenance records which plugin an activated hook originated from.

type InstallOptions

type InstallOptions struct {
	// Source is a git URL or a local filesystem path to a plugin directory (one
	// that contains a plugin.json, or whose tree contains exactly one).
	Source string
	// Dir is the plugins directory to install into (typically the user plugins
	// root from ResolveRoots).
	Dir string
	// Force allows overwriting a plugin installed from a different source.
	Force bool
	// GitRunner overrides the fetch implementation. When nil, a git source is
	// shallow-cloned with the system git.
	GitRunner GitRunner
}

InstallOptions configures a single plugin install.

type InstallResult

type InstallResult struct {
	ID           string `json:"id"`
	Name         string `json:"name"`
	Version      string `json:"version"`
	ManifestPath string `json:"manifestPath"`
	Hash         string `json:"hash"`
	Source       string `json:"source"`
	Updated      bool   `json:"updated"`
	PreviousHash string `json:"previousHash,omitempty"`
}

InstallResult reports what an install did.

func Install

func Install(ctx context.Context, options InstallOptions) (InstallResult, error)

Install fetches the plugin at options.Source, validates its manifest, copies the plugin tree into options.Dir/<id>/, and records a content hash (over the manifest bytes) in the lockfile. Fetched content is never executed.

type LoadOptions

type LoadOptions struct {
	Roots                         []Root
	Cwd                           string
	Env                           map[string]string
	AllowManifestToolAutoApproval bool
}

type LoadResult

type LoadResult struct {
	Roots       []Root         `json:"roots"`
	Plugins     []LoadedPlugin `json:"plugins"`
	Diagnostics []Diagnostic   `json:"diagnostics"`
}

func Load

func Load(options LoadOptions) (LoadResult, error)

type LoadedPlugin

type LoadedPlugin struct {
	SchemaVersion int             `json:"schemaVersion"`
	ID            string          `json:"id"`
	Name          string          `json:"name"`
	Version       string          `json:"version"`
	Description   string          `json:"description,omitempty"`
	Enabled       bool            `json:"enabled"`
	Source        Source          `json:"source"`
	Root          string          `json:"root"`
	PluginDir     string          `json:"pluginDir"`
	ManifestPath  string          `json:"manifestPath"`
	Tools         []ToolExtension `json:"tools"`
	Prompts       []PathExtension `json:"prompts"`
	Skills        []PathExtension `json:"skills"`
	Hooks         []HookExtension `json:"hooks"`
	// Optional, additive metadata (omitempty so existing plugins are unchanged).
	// Author/Interface are pointers: a non-pointer struct is never "empty" to
	// encoding/json, so omitempty would still emit `author:{}` / `interface:{}`
	// and change the serialized form of plugins that don't set them.
	Author    *PluginAuthor    `json:"author,omitempty"`
	License   string           `json:"license,omitempty"`
	Keywords  []string         `json:"keywords,omitempty"`
	Homepage  string           `json:"homepage,omitempty"`
	Interface *PluginInterface `json:"interface,omitempty"`
}

func ParseManifest

func ParseManifest(raw any, options ParseManifestOptions) (LoadedPlugin, error)

type LockEntry

type LockEntry struct {
	Source string `json:"source"`
	Hash   string `json:"hash"`
}

LockEntry records the source and content hash for one installed plugin.

type ManifestError

type ManifestError struct {
	FieldPath string
	Message   string
}

func (ManifestError) Error

func (err ManifestError) Error() string

type ParseManifestOptions

type ParseManifestOptions struct {
	Source                        Source
	Root                          string
	PluginDir                     string
	ManifestPath                  string
	AllowManifestToolAutoApproval bool
}

type PathExtension

type PathExtension struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Path        string `json:"path"`
}

type PluginAuthor

type PluginAuthor struct {
	Name  string `json:"name,omitempty"`
	Email string `json:"email,omitempty"`
	URL   string `json:"url,omitempty"`
}

PluginAuthor is optional manifest authorship metadata. All fields are best-effort; missing values stay empty.

type PluginInterface

type PluginInterface struct {
	DisplayName    string   `json:"displayName,omitempty"`
	Category       string   `json:"category,omitempty"`
	BrandColor     string   `json:"brandColor,omitempty"`
	DefaultPrompts []string `json:"defaultPrompts,omitempty"`
}

PluginInterface is optional presentation metadata describing how a plugin surfaces in a UI. All fields are optional and default to empty.

type ResolveRootOptions

type ResolveRootOptions struct {
	Cwd string
	Env map[string]string
}

type Root

type Root struct {
	Source Source `json:"source"`
	Path   string `json:"path"`
}

func ResolveRoots

func ResolveRoots(options ResolveRootOptions) ([]Root, error)

type SkillProvenance

type SkillProvenance struct {
	SkillName string `json:"skillName"`
	Root      string `json:"root"`
	PluginID  string `json:"pluginId"`
}

SkillProvenance records which plugin contributed a skill search root.

type Source

type Source string
const (
	SourceUser    Source = "user"
	SourceProject Source = "project"
	SourceCustom  Source = "custom"
)

type ToolExtension

type ToolExtension struct {
	Name        string         `json:"name"`
	Description string         `json:"description,omitempty"`
	Command     string         `json:"command"`
	Args        []string       `json:"args"`
	InputSchema map[string]any `json:"inputSchema"`
	Permission  ToolPermission `json:"permission"`
}

type ToolPermission

type ToolPermission string
const (
	PermissionAllow  ToolPermission = "allow"
	PermissionPrompt ToolPermission = "prompt"
	PermissionDeny   ToolPermission = "deny"
)

type ToolProvenance

type ToolProvenance struct {
	ToolName string `json:"toolName"`
	PluginID string `json:"pluginId"`
}

ToolProvenance records which plugin a registered tool originated from, for `zero plugin list` and debugging.

Jump to

Keyboard shortcuts

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