Documentation
¶
Overview ¶
Package plugins provides types and interfaces for managing ToolHive plugins (Claude Plugin manifest format, .claude-plugin/plugin.json).
A plugin is an OCI artifact containing a .claude-plugin/plugin.json manifest and its component directories (commands, agents, skills, hooks). The package mirrors pkg/skills: the scoping model (user vs. project) and install-status lifecycle are identical, so Scope and InstallStatus are re-exported as type aliases from pkg/skills to avoid conversion churn at storage boundaries.
Index ¶
- Constants
- Variables
- func ValidatePluginName(name string) error
- type Author
- type BuildOptions
- type BuildResult
- type ComponentInventory
- type ContentOptions
- type Dependency
- type InfoOptions
- type InstallOptions
- type InstallResult
- type InstallStatus
- type InstalledPlugin
- type ListOptions
- type LocalBuild
- type PluginContent
- type PluginFileEntry
- type PluginInfo
- type PluginManifest
- type PluginMetadata
- type PluginService
- type PushOptions
- type Scope
- type UninstallOptions
- type ValidationResult
Constants ¶
const ( // ScopeUser indicates a plugin installed user-wide. ScopeUser = skills.ScopeUser // ScopeProject indicates a plugin installed for a specific project. ScopeProject = skills.ScopeProject )
const ( InstallStatusInstalled = skills.InstallStatusInstalled InstallStatusPending = skills.InstallStatusPending InstallStatusFailed = skills.InstallStatusFailed )
Install lifecycle statuses. Aliased from skills.InstallStatus because the lifecycle model is identical.
const ManifestPath = ".claude-plugin/plugin.json"
ManifestPath is the required manifest file path for a plugin directory, matching the Claude Plugin manifest format.
const MaxComponentsPerGroup = 100
MaxComponentsPerGroup caps the number of entries allowed in a single component array (commands, agents, skills, hooks). Mirrors the skills parser's MaxDependencies bound and bounds the cost of ValidatePluginDir, which walks each bundled skill path. A 64KB manifest can otherwise pack thousands of "./x" entries cheaply.
const MaxManifestSize = 64 * 1024
MaxManifestSize limits the plugin.json size to prevent JSON parsing attacks (e.g. billion laughs). Mirrors the packager's maxManifestSize.
Variables ¶
var CheckFilesystem = skills.CheckFilesystem
CheckFilesystem walks a plugin directory checking for symlinks and path traversal. Re-exported from skills (the filesystem safety check is generic).
var ErrInvalidManifest = errors.New("invalid plugin manifest")
ErrInvalidManifest indicates that the plugin manifest is malformed, missing, or fails toolhive-side strictness checks.
var NormalizeScopeAndProjectRoot = skills.NormalizeScopeAndProjectRoot
NormalizeScopeAndProjectRoot validates scope and project_root and returns normalized values. Re-exported from skills (the scope/project_root normalization rule is identical for plugins).
var ValidateProjectRoot = skills.ValidateProjectRoot
ValidateProjectRoot validates a project root path and returns its cleaned form. Re-exported from skills: project-root validation (absolute, no traversal, no symlinks, must be a git repo) is identical for plugins.
var ValidateScope = skills.ValidateScope
ValidateScope validates a plugin scope. Re-exported from skills because the rule (empty | "user" | "project") is identical.
Functions ¶
func ValidatePluginName ¶
ValidatePluginName checks that a plugin name conforms to the kebab-case rule shared with skills (2-64 lowercase alphanumeric/hyphens, no consecutive hyphens). Re-exported from skills because the rule is identical.
Types ¶
type Author ¶
type Author struct {
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
URL string `json:"url,omitempty"`
}
Author represents the author field of a plugin manifest.
type BuildOptions ¶
type BuildOptions = skills.BuildOptions
BuildOptions configures the behavior of the Build operation. Alias for skills.BuildOptions (Path, Tag).
type BuildResult ¶
type BuildResult = skills.BuildResult
BuildResult contains the outcome of a Build operation. Alias for skills.BuildResult (Reference).
type ComponentInventory ¶
type ComponentInventory = ociplugins.ComponentInventory
ComponentInventory summarizes the component types declared by a plugin (map of component-type name to count). Alias for the toolhive-core type.
type ContentOptions ¶
type ContentOptions = skills.ContentOptions
ContentOptions configures the behavior of the GetContent operation. Alias for skills.ContentOptions.
type Dependency ¶
type Dependency = skills.Dependency
Dependency is an external plugin dependency (OCI reference). Alias for skills.Dependency; the {Name, Reference, Digest} shape is identical.
type InfoOptions ¶
type InfoOptions = skills.InfoOptions
InfoOptions configures the behavior of the Info operation. Alias for skills.InfoOptions.
type InstallOptions ¶
type InstallOptions struct {
// Name is the plugin name or OCI reference to install.
Name string `json:"name"`
// Version is the specific version to install. Empty means latest.
Version string `json:"version,omitempty"`
// Scope is the installation scope.
Scope Scope `json:"scope,omitempty"`
// Clients lists target clients (e.g., "claude-code").
Clients []string `json:"clients,omitempty"`
// Force allows overwriting unmanaged plugin directories.
Force bool `json:"force,omitempty"`
// ProjectRoot is the project root path for project-scoped installs.
ProjectRoot string `json:"project_root,omitempty"`
// Group is the group name to add the plugin to after installation.
Group string `json:"group,omitempty"`
// LayerData is the tar.gz content from an OCI layer. Internal use only — NOT exposed via HTTP API.
LayerData []byte `json:"-"`
// Reference is the full OCI reference (e.g. ghcr.io/org/plugin:v1).
Reference string `json:"-"`
// Digest is the OCI digest for upgrade detection.
Digest string `json:"-"`
}
InstallOptions configures the behavior of the Install operation. Mirrors skills.InstallOptions with the plugin-specific addition of Reference/Digest passthrough fields used by install-from-OCI flows.
type InstallResult ¶
type InstallResult struct {
// Plugin is the installed plugin.
Plugin InstalledPlugin `json:"plugin"`
}
InstallResult contains the outcome of an Install operation.
type InstallStatus ¶
type InstallStatus = skills.InstallStatus
InstallStatus is the lifecycle status of an installed plugin. Alias for skills.InstallStatus (installed | pending | failed).
type InstalledPlugin ¶
type InstalledPlugin struct {
// Metadata contains the plugin's metadata.
Metadata PluginMetadata `json:"metadata"`
// Scope is the installation scope (user or project).
Scope Scope `json:"scope"`
// ProjectRoot is the project root path for project-scoped plugins. Empty for user-scoped.
ProjectRoot string `json:"project_root,omitempty"`
// Reference is the full OCI reference (e.g. ghcr.io/org/plugin:v1).
Reference string `json:"reference,omitempty"`
// Tag is the OCI tag (e.g. v1.0.0).
Tag string `json:"tag,omitempty"`
// Digest is the OCI digest (sha256:...) for upgrade detection.
Digest string `json:"digest,omitempty"`
// Status is the current installation status.
Status InstallStatus `json:"status"`
// InstalledAt is the timestamp when the plugin was installed.
InstalledAt time.Time `json:"installed_at"`
// Clients is the list of client identifiers the plugin is installed for.
Clients []string `json:"clients,omitempty"`
// Components is the inventory of component types declared by the plugin
// (e.g. {"commands": 3, "skills": 2}). Extracted from the OCI artifact.
Components ComponentInventory `json:"components,omitempty"`
// Signature is the optional signing signature for the plugin artifact.
Signature string `json:"signature,omitempty"`
// Dependencies is the list of external plugin dependencies.
Dependencies []Dependency `json:"dependencies,omitempty"`
}
InstalledPlugin represents a plugin that has been installed locally.
type ListOptions ¶
type ListOptions = skills.ListOptions
ListOptions configures the behavior of the List operation. Alias for skills.ListOptions (identical shape: Scope, ClientApp, ProjectRoot, Group).
type LocalBuild ¶
type LocalBuild = skills.LocalBuild
LocalBuild represents a locally-built OCI plugin artifact in the local store. Alias for skills.LocalBuild (identical shape: Tag, Digest, Name, Description, Version).
type PluginContent ¶
type PluginContent struct {
// Name is the plugin name from the OCI config labels.
Name string `json:"name"`
// Description is the plugin description from the OCI config labels.
Description string `json:"description,omitempty"`
// Version is the plugin version from the OCI config labels.
Version string `json:"version,omitempty"`
// License is the SPDX license identifier from the OCI config labels.
License string `json:"license,omitempty"`
// Manifest is the raw .claude-plugin/plugin.json body.
Manifest string `json:"manifest"`
// Files is the list of all files in the artifact with their sizes.
Files []PluginFileEntry `json:"files"`
}
PluginContent contains the manifest body and file listing extracted from an OCI plugin artifact without installing it.
type PluginFileEntry ¶
type PluginFileEntry = skills.SkillFileEntry
PluginFileEntry represents a single file within a plugin artifact. Alias for skills.SkillFileEntry (same {Path, Size} shape).
type PluginInfo ¶
type PluginInfo struct {
// Metadata contains the plugin's metadata.
Metadata PluginMetadata `json:"metadata"`
// InstalledPlugin contains the full installation record.
InstalledPlugin *InstalledPlugin `json:"installed_plugin,omitempty"`
}
PluginInfo contains detailed information about an installed plugin.
type PluginManifest ¶
type PluginManifest struct {
Name string `json:"name"`
Version string `json:"version,omitempty"`
Description string `json:"description,omitempty"`
Author Author `json:"author,omitempty"`
Homepage string `json:"homepage,omitempty"`
Repository string `json:"repository,omitempty"`
License string `json:"license,omitempty"`
// Keywords MUST be a JSON array (a string is a hard error — see strictStringSlice).
Keywords strictStringSlice `json:"keywords"`
// Component directories: each entry must be a relative path starting with "./".
Commands []string `json:"commands,omitempty"`
Agents []string `json:"agents,omitempty"`
Skills []string `json:"skills,omitempty"`
Hooks []string `json:"hooks,omitempty"`
McpServers json.RawMessage `json:"mcpServers,omitempty"`
LspServers json.RawMessage `json:"lspServers,omitempty"`
// Raw is the full original document, preserved for round-tripping unknown fields.
Raw json.RawMessage `json:"-"`
}
PluginManifest represents the toolhive-readable fields of .claude-plugin/plugin.json. Unknown fields are preserved in Raw so the manifest can be round-tripped without loss.
func ParsePluginManifest ¶
func ParsePluginManifest(pluginDir string) (*PluginManifest, error)
ParsePluginManifest reads and parses .claude-plugin/plugin.json from pluginDir. It performs toolhive-specific pre-build strictness checks only; the packager re-walks the directory and re-validates at build time.
Strictness (exit gate):
- keywords MUST be a JSON array; a string is a hard error.
- component paths (commands/agents/skills/hooks) must be relative, start with "./", and contain no ".." traversal segments.
The full document is preserved in .Raw (unknown fields preserved).
type PluginMetadata ¶
type PluginMetadata struct {
// Name is the unique name of the plugin (kebab-case).
Name string `json:"name"`
// Version is the semantic version of the plugin.
Version string `json:"version,omitempty"`
// Description is a human-readable description of the plugin.
Description string `json:"description,omitempty"`
// Author is the plugin author or maintainer.
Author string `json:"author,omitempty"`
// License is the SPDX license identifier for the plugin.
License string `json:"license,omitempty"`
// Keywords is a list of keywords for categorization/search.
Keywords []string `json:"keywords,omitempty"`
}
PluginMetadata contains metadata about a plugin, drawn from the .claude-plugin/plugin.json manifest.
type PluginService ¶
type PluginService interface {
// Validate checks whether a plugin definition is valid.
Validate(ctx context.Context, path string) (*ValidationResult, error)
// Build builds a plugin from a local directory into an OCI artifact.
Build(ctx context.Context, opts BuildOptions) (*BuildResult, error)
// Push pushes a built plugin artifact to a remote registry.
Push(ctx context.Context, opts PushOptions) error
// ListBuilds returns all locally-built OCI plugin artifacts in the local store.
ListBuilds(ctx context.Context) ([]LocalBuild, error)
// DeleteBuild removes a locally-built OCI plugin artifact from the local store.
DeleteBuild(ctx context.Context, tag string) error
// GetContent retrieves the plugin.json body and file listing from an OCI
// artifact without installing it. Works for both remote registry references
// and local build tags.
GetContent(ctx context.Context, opts ContentOptions) (*PluginContent, error)
}
PluginService declares the plugin lifecycle surface (mirrors skills.SkillService).
Phase 2 implements ONLY: Validate, Build, Push, ListBuilds, DeleteBuild, and GetContent — the build/validate/push/content surface. The install/uninstall/ list/info methods are NOT declared on the interface yet: they land in Phase 3 (#5527), which will widen this interface (the only change is the addition; existing method signatures stay stable). The Phase-3 option/result types they will use (InstallOptions, InstallResult, UninstallOptions, InfoOptions, PluginInfo, ListOptions) are already declared in options.go as forward declarations of the Phase-3 contract.
type PushOptions ¶
type PushOptions = skills.PushOptions
PushOptions configures the behavior of the Push operation. Alias for skills.PushOptions (Reference).
type Scope ¶
Scope is the installation scope for a plugin. It is an alias for skills.Scope because the scoping model is identical: a plugin is installed either user-wide or project-local, and the storage layer keys both on the same (scope, project_root) pair.
type UninstallOptions ¶
type UninstallOptions = skills.UninstallOptions
UninstallOptions configures the behavior of the Uninstall operation. Alias for skills.UninstallOptions (identical shape).
type ValidationResult ¶
type ValidationResult = skills.ValidationResult
ValidationResult contains the outcome of a Validate operation. Alias for skills.ValidationResult.
func ValidatePluginDir ¶
func ValidatePluginDir(path string) (*ValidationResult, error)
ValidatePluginDir validates a plugin directory at the given path. It is the plugin analogue of skills.ValidateSkillDir. Steps:
- Clean + absolute path check.
- CheckFilesystem (symlink + traversal walk).
- ParsePluginManifest (strict keywords + component-path checks).
- Validate name (ValidatePluginName) and assert it matches the dir basename.
- For each manifest.Skills entry, reuse skills.ValidateSkillDir on the bundled skill at <pluginDir>/<path-without-leading-./> — this reuses the pkg/skills validator for bundled skills rather than duplicating it.
I/O errors are returned as error; validation issues are returned in ValidationResult.