Documentation
¶
Overview ¶
Package plugin implements the Claude Code plugin marketplace format — marketplace.json catalogs, plugin.json manifests, the on-disk install state — plus the ask-side operations over it: add/refresh marketplaces, install/enable plugins at user or project scope, publish local skills, agents, and workflows into a marketplace, and import the state Claude Code already holds under ~/.claude/plugins.
The file shapes are byte-compatible with Claude Code so a marketplace built for one tool installs in the other. ask-only content (workflows, the `provider:` key on agents) rides in the same plugin directory; Claude Code ignores what it does not know. Plugins may also ship MCP servers as a plugin-root `.mcp.json` or an `mcps/` directory of `.mcp.json`-format files; ask attaches them to sessions, and Claude Code reads the root `.mcp.json` too.
Index ¶
- Constants
- Variables
- func BumpPatch(v string) string
- func CacheDir() string
- func EntryLocalDir(m Marketplace, entry Entry) (string, bool)
- func ForgetPublication(cwd, kind, name, scope string) error
- func HashBytes(data []byte) string
- func HashPath(p string) string
- func InitMarketplace(ctx context.Context, dir, name, owner string) error
- func MarketplacesDir() string
- func ProjectFilePath(cwd string) string
- func PublishedCopyPath(m Marketplace, pub Publication) string
- func RecordPublication(cwd string, pub Publication) error
- func RefreshAll(ctx context.Context, cwd string) []error
- func RefreshMarketplace(ctx context.Context, cwd string, m Marketplace) error
- func RemoveMarketplace(cwd, name string, scope Scope) error
- func RootDir() string
- func SetEnabled(ref Ref, enabled bool) error
- func UninstallPlugin(cwd string, ref Ref, scope Scope) error
- func ValidateName(name string) error
- type Author
- type ClaudeState
- type Contents
- type Entry
- type ImportReport
- type Installed
- type InstalledRecord
- type KnownMarketplace
- type MCPServersField
- type Marketplace
- type MarketplaceManifest
- type MarketplaceMetadata
- type MarketplaceSource
- type Owner
- type PathList
- type PluginManifest
- type ProjectFile
- type Publication
- type PublishRequest
- type PublishResult
- type Ref
- type Scope
- type Source
- type SyncReport
- type SyncState
Constants ¶
const ( MarketplaceManifestRel = ".claude-plugin/marketplace.json" PluginManifestRel = ".claude-plugin/plugin.json" )
const ( SourcePath = "path" SourceGitHub = "github" SourceURL = "url" SourceGitSubdir = "git-subdir" SourceGit = "git" )
Source kinds for a plugin entry.
const ( MarketplaceSourceGitHub = "github" MarketplaceSourceGit = "git" MarketplaceSourceDirectory = "directory" MarketplaceSourceURL = "url" )
Marketplace source kinds — the `source` field of known_marketplaces.json.
Variables ¶
var ClaudeHome = func() string { home, err := os.UserHomeDir() if err != nil || home == "" { return "" } return filepath.Join(home, ".claude") }
ClaudeHome locates Claude Code's state; swappable for tests.
var HTTPClient = http.DefaultClient
HTTPClient fetches marketplace.json URLs; swappable for tests.
var Now = time.Now
Now is the clock for timestamps; swappable for tests.
var RunGit = func(ctx context.Context, dir string, args ...string) (string, error) { cmd := exec.CommandContext(ctx, "git", args...) if dir != "" { cmd.Dir = dir } cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0") out, err := cmd.CombinedOutput() if err != nil { return string(out), fmt.Errorf("git %s: %w: %s", strings.Join(args, " "), err, strings.TrimSpace(string(out))) } return string(out), nil }
RunGit executes git with args in dir and returns its combined output. Swappable so tests never spawn a subprocess.
Functions ¶
func BumpPatch ¶
BumpPatch increments the patch number of a semver string; anything that is not major.minor.patch is returned unchanged.
func EntryLocalDir ¶
func EntryLocalDir(m Marketplace, entry Entry) (string, bool)
EntryLocalDir returns the plugin directory for a path-source entry inside the marketplace's local clone, so its contents can be listed before installing. Remote entries have no local directory.
func ForgetPublication ¶
ForgetPublication drops the link (the marketplace copy is untouched).
func HashPath ¶
HashPath hashes a file, or a directory's relative paths and contents (sorted, .git skipped), so two copies compare by content alone.
func InitMarketplace ¶
InitMarketplace turns dir into an empty marketplace (git-initialised) so the user can publish into it and push it wherever they like.
func MarketplacesDir ¶
func MarketplacesDir() string
func ProjectFilePath ¶
ProjectFilePath is <project root>/.ask/plugins.json.
func PublishedCopyPath ¶
func PublishedCopyPath(m Marketplace, pub Publication) string
PublishedCopyPath is where the marketplace holds the published item.
func RecordPublication ¶
func RecordPublication(cwd string, pub Publication) error
RecordPublication stores pub — in the project file for project-scope items (committed, so the team sees where the item lives), in the user file otherwise.
func RefreshAll ¶
RefreshAll refreshes every registered marketplace, collecting failures.
func RefreshMarketplace ¶
func RefreshMarketplace(ctx context.Context, cwd string, m Marketplace) error
RefreshMarketplace pulls the latest catalog for m.
func RemoveMarketplace ¶
RemoveMarketplace drops the registration in scope. The clone is deleted once no scope references it; directory sources are never touched.
func SetEnabled ¶
SetEnabled flips a user-scope install on or off without removing it.
func UninstallPlugin ¶
UninstallPlugin disables ref in scope; the cached copy is removed once no scope references it.
func ValidateName ¶
ValidateName enforces the kebab-case rule shared by marketplace, plugin, skill, and agent names.
Types ¶
type Author ¶
type Author struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
URL string `json:"url,omitempty"`
}
Author accepts both the object form and a bare string.
func (*Author) UnmarshalJSON ¶
type ClaudeState ¶
type ClaudeState struct {
Marketplaces map[string]KnownMarketplace
UserEnabled map[string]bool
ProjectEnabled map[string]bool
}
ClaudeState is what Claude Code holds that ask can import: registered marketplaces and the plugins enabled at user and project level.
func ReadClaudeState ¶
func ReadClaudeState(cwd string) ClaudeState
ReadClaudeState reads ~/.claude/plugins/known_marketplaces.json, ~/.claude/settings.json, and <root>/.claude/settings.json. Missing files are simply absent from the result.
func (ClaudeState) Empty ¶
func (s ClaudeState) Empty() bool
Empty reports whether there is nothing to import.
func (ClaudeState) EnabledRefs ¶
func (s ClaudeState) EnabledRefs() []string
EnabledRefs is the sorted union of user- and project-enabled plugins.
type Contents ¶
type Contents struct {
SkillDirs []string
CommandFiles []string
AgentFiles []string
WorkflowFiles []string
// MCPFiles are .mcp.json-format files the plugin ships: the plugin-root
// .mcp.json, every *.json under mcps/, plus any manifest/entry
// "mcpServers" paths.
MCPFiles []string
// InlineMCP holds inline `mcpServers` objects declared directly in the
// plugin.json manifest or the marketplace entry (Claude Code's inline
// form), each a raw `{"name": {…}}` map. tools.PluginMCPServers decodes
// them alongside the file-based servers.
InlineMCP []json.RawMessage
}
Contents is what a plugin directory contributes, resolved to absolute paths: skill packages (directories holding SKILL.md), single-file commands (Claude Code's commands/*.md, loaded as skills), agent definitions, and ask workflows.
func ResolveContents ¶
func ResolveContents(dir string, entry *Entry, manifest *PluginManifest) Contents
ResolveContents applies the plugin.json / marketplace-entry component paths (strict: both merge; strict:false: the entry alone) with the default directories, and expands them to concrete files.
type Entry ¶
type Entry struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Version string `json:"version,omitempty"`
Category string `json:"category,omitempty"`
Homepage string `json:"homepage,omitempty"`
Author *Author `json:"author,omitempty"`
Source Source `json:"source"`
Strict *bool `json:"strict,omitempty"`
Skills PathList `json:"skills,omitempty"`
Agents PathList `json:"agents,omitempty"`
Commands PathList `json:"commands,omitempty"`
Workflows PathList `json:"workflows,omitempty"`
MCPServers MCPServersField `json:"mcpServers,omitempty"`
Tags []string `json:"tags,omitempty"`
Keywords []string `json:"keywords,omitempty"`
}
Entry is one plugin listed by a marketplace.
type ImportReport ¶
type ImportReport struct {
Marketplaces []string
Plugins []string
Skipped []string
Errors []string
}
ImportReport is what ImportFromClaude did.
func ImportFromClaude ¶
func ImportFromClaude(ctx context.Context, cwd string, st ClaudeState, scope Scope) ImportReport
ImportFromClaude registers Claude Code's marketplaces and installs its enabled plugins into ask's own store at scope. Nothing under ~/.claude is modified.
func (ImportReport) Summary ¶
func (r ImportReport) Summary() string
Summary is the one-line toast form.
type Installed ¶
type Installed struct {
Ref Ref
Scope Scope
Scopes []Scope
// Dir is the installed copy; "" (and Missing) when the project file
// enables a plugin this machine has not fetched yet.
Dir string
Version string
Missing bool
Enabled bool
Entry *Entry
Manifest *PluginManifest
}
Installed is a plugin enabled for the current project/user.
func EnabledPlugins ¶
EnabledPlugins resolves every plugin enabled for cwd — user installs plus the project file — in ref order. Entries the project enables but this machine has not fetched come back Missing.
func FindInstalled ¶
FindInstalled returns the enabled install for ref, if any.
func InstallPlugin ¶
InstallPlugin fetches ref's plugin into the cache and enables it in scope.
func InstalledPlugins ¶
InstalledPlugins lists every install record on this machine (enabled or not) plus the project's enabled set, for the browser's installed lens.
func (Installed) Description ¶
Description prefers the plugin's own manifest over the catalog entry.
type InstalledRecord ¶
type InstalledRecord struct {
Scope Scope `json:"scope"`
ProjectRoot string `json:"projectRoot,omitempty"`
InstallPath string `json:"installPath"`
Version string `json:"version,omitempty"`
InstalledAt string `json:"installedAt,omitempty"`
LastUpdated string `json:"lastUpdated,omitempty"`
GitCommitSha string `json:"gitCommitSha,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
Entry *Entry `json:"entry,omitempty"`
}
InstalledRecord is one install of a plugin, keyed by "name@marketplace" in installed_plugins.json.
type KnownMarketplace ¶
type KnownMarketplace struct {
Source MarketplaceSource `json:"source"`
InstallLocation string `json:"installLocation,omitempty"`
LastUpdated string `json:"lastUpdated,omitempty"`
AutoUpdate *bool `json:"autoUpdate,omitempty"`
}
KnownMarketplace is one registration — the value shape of Claude Code's known_marketplaces.json.
type MCPServersField ¶
type MCPServersField struct {
Paths PathList
// Inline is the raw inline `mcpServers` object (`{"name": {…}}`) when the
// inline-object form was used, or nil. It is the value of the mcpServers
// key, not a wrapped `{"mcpServers": …}` document, so it decodes straight
// into a map[string]MCPServerConfig.
Inline json.RawMessage
}
MCPServersField is the manifest/marketplace-entry "mcpServers" component field. Claude Code allows either a path (or list of paths) to .mcp.json-format files, or an inline object of servers. We honor both: the path forms add files that ResolveContents picks up, and the inline object (the raw `{"name": {…}}` map) is preserved in Inline so ResolveContents can surface it and tools.PluginMCPServers can decode it directly.
func (MCPServersField) MarshalJSON ¶
func (f MCPServersField) MarshalJSON() ([]byte, error)
MarshalJSON round-trips the field back to Claude Code's real "mcpServers" shape — the inline object, a path array, or null — rather than the Go struct's default `{"Paths":…,"Inline":…}`. Without this, persisting an Entry (e.g. into installed_plugins.json) and re-reading it would decode the struct blob as a spurious inline object.
func (*MCPServersField) UnmarshalJSON ¶
func (f *MCPServersField) UnmarshalJSON(b []byte) error
type Marketplace ¶
type Marketplace struct {
Name string
Scope Scope
Source MarketplaceSource
// Dir is the local clone (or the directory itself for directory
// sources). It may not exist yet on this machine for a marketplace a
// teammate registered in .ask/plugins.json.
Dir string
Manifest *MarketplaceManifest
// Err explains why Manifest is nil (not fetched, malformed).
Err string
}
Marketplace is one registered marketplace plus its fetched catalog.
func AddMarketplace ¶
AddMarketplace registers the marketplace at raw (owner/repo, git URL, directory, or marketplace.json URL) in scope, fetching its catalog. Re-adding a source that is already registered is idempotent.
func FindMarketplace ¶
func FindMarketplace(cwd, name string) (Marketplace, bool)
FindMarketplace looks a registered marketplace up by name.
func ListMarketplaces ¶
func ListMarketplaces(cwd string) []Marketplace
ListMarketplaces merges the user registrations with the project file. A name registered in both scopes is listed once, as the user's.
func (Marketplace) Entry ¶
func (m Marketplace) Entry(name string) (Entry, bool)
Entry returns the catalog entry for plugin name.
func (Marketplace) Fetched ¶
func (m Marketplace) Fetched() bool
Fetched reports whether the catalog is available locally.
func (Marketplace) Writable ¶
func (m Marketplace) Writable() bool
Writable reports whether Publish can land files in this marketplace: a directory or git clone we hold locally. A marketplace fetched as a bare marketplace.json URL has nowhere to put a plugin.
type MarketplaceManifest ¶
type MarketplaceManifest struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Owner Owner `json:"owner"`
Metadata *MarketplaceMetadata `json:"metadata,omitempty"`
Plugins []Entry `json:"plugins"`
}
MarketplaceManifest is .claude-plugin/marketplace.json.
func ReadMarketplaceManifest ¶
func ReadMarketplaceManifest(dir string) (*MarketplaceManifest, error)
ReadMarketplaceManifest parses dir/.claude-plugin/marketplace.json.
type MarketplaceMetadata ¶
type MarketplaceSource ¶
type MarketplaceSource struct {
Kind string `json:"source"`
Repo string `json:"repo,omitempty"`
URL string `json:"url,omitempty"`
Path string `json:"path,omitempty"`
}
MarketplaceSource says where a marketplace comes from.
func ParseMarketplaceSource ¶
func ParseMarketplaceSource(cwd, raw string) (MarketplaceSource, error)
ParseMarketplaceSource classifies what the user typed after `/skills add marketplace`: a GitHub `owner/repo`, a git URL, a local directory, or a direct URL to a marketplace.json. Relative directories resolve against cwd.
func (MarketplaceSource) Display ¶
func (s MarketplaceSource) Display() string
Display is the one-line form shown in the browser.
func (MarketplaceSource) Equal ¶
func (s MarketplaceSource) Equal(o MarketplaceSource) bool
func (MarketplaceSource) GitURL ¶
func (s MarketplaceSource) GitURL() string
GitURL is the clone URL for git-backed sources, "" otherwise.
func (MarketplaceSource) Raw ¶
func (s MarketplaceSource) Raw() string
Raw is the string form AddMarketplace accepts back — what a user would have typed to register this source.
type Owner ¶
type Owner struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
URL string `json:"url,omitempty"`
}
Owner is the marketplace owner block.
type PathList ¶
type PathList []string
PathList is a manifest field that may be one path or a list of paths.
func (*PathList) UnmarshalJSON ¶
type PluginManifest ¶
type PluginManifest struct {
Name string `json:"name"`
DisplayName string `json:"displayName,omitempty"`
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 []string `json:"keywords,omitempty"`
Skills PathList `json:"skills,omitempty"`
Agents PathList `json:"agents,omitempty"`
Commands PathList `json:"commands,omitempty"`
Workflows PathList `json:"workflows,omitempty"`
MCPServers MCPServersField `json:"mcpServers,omitempty"`
}
PluginManifest is .claude-plugin/plugin.json.
func ReadPluginManifest ¶
func ReadPluginManifest(dir string) (*PluginManifest, error)
ReadPluginManifest parses dir/.claude-plugin/plugin.json; a missing file is (nil, nil) — the marketplace entry then describes the plugin.
type ProjectFile ¶
type ProjectFile struct {
Marketplaces map[string]KnownMarketplace `json:"marketplaces,omitempty"`
Enabled map[string]bool `json:"enabled,omitempty"`
Published map[string]Publication `json:"published,omitempty"`
}
ProjectFile is <root>/.ask/plugins.json.
func ReadProjectFile ¶
func ReadProjectFile(cwd string) ProjectFile
ReadProjectFile loads the project's plugin file (zero value when absent).
type Publication ¶
type Publication struct {
Kind string `json:"kind"`
Name string `json:"name"`
Scope string `json:"scope"`
Marketplace string `json:"marketplace"`
Plugin string `json:"plugin"`
// File is the item's name inside the plugin: the skill directory,
// the agent file, or the workflow file.
File string `json:"file"`
Version string `json:"version,omitempty"`
Hash string `json:"hash"`
PublishedAt string `json:"publishedAt,omitempty"`
Commit string `json:"commit,omitempty"`
}
A Publication links a local skill, agent, or workflow to the plugin it was published as. The local copy stays the source of truth; the link records what was last published (Hash) so the two can be compared.
func FindPublication ¶
func FindPublication(cwd, kind, name, scope string) (Publication, bool)
FindPublication returns the link for a local item.
func PublicationForRef ¶
func PublicationForRef(cwd string, ref Ref) (Publication, bool)
PublicationForRef says whether a marketplace plugin is one the user published from a local copy here.
func Publications ¶
func Publications(cwd string) []Publication
Publications lists every link visible from cwd.
func Pull ¶
func Pull(cwd string, pub Publication, localPath string) (Publication, error)
Pull replaces the local copy with the marketplace copy and re-bases the publication on it.
func (Publication) Ref ¶
func (p Publication) Ref() Ref
Ref is the plugin reference the publication points at.
type PublishRequest ¶
type PublishRequest struct {
PluginName string
Description string
Version string
// SkillDirs are skill packages (directories holding SKILL.md).
SkillDirs []string
// AgentFiles are agent definitions (*.md).
AgentFiles []string
// WorkflowFiles are ask workflow definitions (*.json).
WorkflowFiles []string
Message string
// NoPush keeps the commit local even when the marketplace has a
// remote. By default a git-backed marketplace is pulled (ff-only)
// before the commit and pushed after it.
NoPush bool
}
PublishRequest describes what to land in a marketplace as one plugin.
type PublishResult ¶
type PublishResult struct {
PluginDir string
Version string
Committed bool
Pushed bool
Commit string
Note string
}
PublishResult reports where the plugin landed and how far git got.
func Publish ¶
func Publish(ctx context.Context, m Marketplace, req PublishRequest) (PublishResult, error)
Publish copies the request's items into <marketplace>/plugins/<name>/, writes the plugin manifest, upserts the catalog entry, and commits (and pushes, when asked and a remote exists).
type Scope ¶
type Scope string
Scope says where a marketplace registration or plugin enablement lives.
func NormalizeScope ¶
NormalizeScope maps "" to user and validates the rest.
type Source ¶
Source locates a plugin: a path relative to the marketplace, or a git repository (optionally pinned to a ref/sha and narrowed to a subdir).
func ParseSourceString ¶
ParseSourceString classifies the string form of a plugin source.
func (Source) MarshalJSON ¶
func (*Source) UnmarshalJSON ¶
type SyncReport ¶
SyncReport is what SyncProject did.
func SyncProject ¶
func SyncProject(ctx context.Context, cwd string) SyncReport
SyncProject fetches every plugin the project file enables but this machine lacks, registering the project's marketplaces as needed.
type SyncState ¶
type SyncState int
SyncState compares the local copy and the marketplace copy against what was last published.