Documentation
¶
Overview ¶
Package plugins implements the plugin loader, registry, settings store, and state store for the bud2 extensibility framework (WS2 + WS3).
Index ¶
- func CronMatches(c *ParsedCron, t time.Time) bool
- type ActionProxy
- type Behavior
- type Capability
- type CapabilityMeta
- type Dispatcher
- func (d *Dispatcher) EmitCapabilityEvent(phase, extName, capName string, payload map[string]any)
- func (d *Dispatcher) FirePercept(source, typ, content string, extra map[string]any)
- func (d *Dispatcher) FireSlashCommand(name string, data map[string]any)
- func (d *Dispatcher) HandleOnResult(ctx context.Context, configs []OnResultConfig, result any, vars map[string]any)
- func (d *Dispatcher) ListSlashCommands() []DispatcherSlashCommandInfo
- func (d *Dispatcher) RegisterAll(ctx context.Context)
- func (d *Dispatcher) RegisterPlugin(ctx context.Context, ext *Plugin) error
- func (d *Dispatcher) SetSaveThought(s SaveThought)
- func (d *Dispatcher) SetTalkToUser(t TalkToUser)
- func (d *Dispatcher) Stop()
- func (d *Dispatcher) UnregisterPlugin(name string)
- type DispatcherSlashCommandInfo
- type Event
- type EventBus
- type EventHandler
- type MCPServerDef
- type Manifest
- type OnResultConfig
- type ParamDef
- type ParsedCron
- type PatternMatcher
- type Plugin
- type Registry
- func (r *Registry) All() []*Plugin
- func (r *Registry) Capabilities() []string
- func (r *Registry) CapabilitiesOfType(capType string) []struct{ ... }
- func (r *Registry) FindCapabilityByName(name string) (*Capability, *Plugin, bool)
- func (r *Registry) Get(name string) *Plugin
- func (r *Registry) GetCapabilityByFullName(fullName string) (*Capability, *Plugin, bool)
- func (r *Registry) Len() int
- type Requirements
- type SaveThought
- type SchemaNode
- type TalkToUser
- type WorkflowRunner
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CronMatches ¶
func CronMatches(c *ParsedCron, t time.Time) bool
CronMatches returns true if all fields of the parsed cron expression match t. Exported for use in tests.
Types ¶
type ActionProxy ¶
type ActionProxy struct {
// contains filtered or unexported fields
}
ActionProxy exposes plugin-declared shell actions to the MCP server and to workflow type:direct steps. It implements mcp.ToolCaller so it can be set as the reflex engine's action caller.
func NewActionProxy ¶
func NewActionProxy(registry *Registry) *ActionProxy
NewActionProxy builds an ActionProxy from the loaded registry. All plugin capabilities with type=="action" and a non-empty run: field are indexed. Call RegisterMCPTools to wire callable_from:both|model actions onto an MCP server.
func (*ActionProxy) Call ¶
Call invokes a shell action by its fully-qualified "<ext>:<cap>" name. This is the direct-invocation path used by workflow type:direct steps. Both callable_from:both|model and callable_from:direct actions are reachable here.
func (*ActionProxy) HasAction ¶
func (p *ActionProxy) HasAction(toolName string) bool
HasAction reports whether the proxy knows about a tool with the given name.
func (*ActionProxy) RegisterMCPTools ¶
func (p *ActionProxy) RegisterMCPTools(server *mcp.Server)
RegisterMCPTools registers shell actions with callable_from=both|model as MCP tools on the given server. Each tool is named "<ext>:<cap>" and its input schema is derived from the capability's params: block.
Actions with callable_from=direct are silently skipped — they are reachable only from workflow type:direct steps via the Call method.
type Behavior ¶
type Behavior struct {
Name string `yaml:"name"`
Trigger map[string]any `yaml:"trigger"`
Workflow string `yaml:"workflow,omitempty"`
}
Behavior is a trigger-to-workflow binding declared in plugin.yaml. The trigger field is kept as a generic map so it can hold any trigger type (schedule, slash_command, pattern_match, event, condition, manual) without requiring schema changes as new trigger types are added in later workstreams.
type Capability ¶
type Capability struct {
Name string
Description string
Type string // "skill", "agent", "workflow", "action"
CallableFrom string // "model", "direct", "both"
Body string // raw markdown body (content after frontmatter); empty for YAML capabilities
Model string // optional model override for agent capabilities ("sonnet", "opus", "haiku")
Tools []string // per-capability tool allow-list (agent capabilities only)
// Action-specific fields (populated for type: action from .yaml capability files)
Run string // path to shell script relative to extension dir
Params map[string]ParamDef // parameter schema for input validation and MCP tool generation
}
Capability is a loaded capability from a capabilities/*.md or capabilities/*.yaml file.
type CapabilityMeta ¶
type CapabilityMeta struct {
CallableFrom string `yaml:"callable_from"`
}
CapabilityMeta is the per-capability entry in plugin.yaml capabilities map.
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher registers and fires behaviors from all loaded plugins. It owns the EventBus and is the authority on trigger lifecycle.
func NewDispatcher ¶
func NewDispatcher(registry *Registry, bus *EventBus, runner WorkflowRunner) *Dispatcher
NewDispatcher creates a Dispatcher. Call RegisterAll or RegisterPlugin to wire behaviors, then Start to begin condition-polling loops.
func (*Dispatcher) EmitCapabilityEvent ¶
func (d *Dispatcher) EmitCapabilityEvent(phase, extName, capName string, payload map[string]any)
EmitCapabilityEvent fires before/after events for a capability invocation. The before event is fire-and-forget (goroutine); the after event is synchronous so on_result handlers complete before the caller proceeds.
func (*Dispatcher) FirePercept ¶
func (d *Dispatcher) FirePercept(source, typ, content string, extra map[string]any)
FirePercept routes an incoming message to pattern_match triggers.
func (*Dispatcher) FireSlashCommand ¶
func (d *Dispatcher) FireSlashCommand(name string, data map[string]any)
FireSlashCommand manually fires a slash_command event (called by the runtime when a Discord slash command arrives that should route through the plugin system).
func (*Dispatcher) HandleOnResult ¶
func (d *Dispatcher) HandleOnResult(ctx context.Context, configs []OnResultConfig, result any, vars map[string]any)
HandleOnResult applies a list of on_result handlers to the output of a workflow run. result is the raw output (may be string, map, or nil). vars provides template resolution context.
func (*Dispatcher) ListSlashCommands ¶
func (d *Dispatcher) ListSlashCommands() []DispatcherSlashCommandInfo
ListSlashCommands returns the slash command names registered across all enabled plugins.
func (*Dispatcher) RegisterAll ¶
func (d *Dispatcher) RegisterAll(ctx context.Context)
RegisterAll registers behaviors for every plugin currently in the registry. Call this once after initial load.
func (*Dispatcher) RegisterPlugin ¶
func (d *Dispatcher) RegisterPlugin(ctx context.Context, ext *Plugin) error
RegisterPlugin registers all behaviors declared in ext.Manifest.Behaviors. Idempotent — existing registrations for the plugin are removed first.
func (*Dispatcher) SetSaveThought ¶
func (d *Dispatcher) SetSaveThought(s SaveThought)
SetSaveThought wires the on_result:action=log callback.
func (*Dispatcher) SetTalkToUser ¶
func (d *Dispatcher) SetTalkToUser(t TalkToUser)
SetTalkToUser wires the on_result:action=notify callback.
func (*Dispatcher) Stop ¶
func (d *Dispatcher) Stop()
Stop cancels all trigger registrations and waits for background goroutines to exit.
func (*Dispatcher) UnregisterPlugin ¶
func (d *Dispatcher) UnregisterPlugin(name string)
UnregisterPlugin removes all trigger registrations for the named plugin.
type DispatcherSlashCommandInfo ¶
DispatcherSlashCommandInfo describes a slash command behavior from a plugin.
type Event ¶
type Event struct {
// Topic is the canonical event name: "<ext>:<cap>:<phase>" (e.g. "bud-core:fetch:after").
Topic string
Payload map[string]any
}
Event is an in-process pub/sub message emitted before or after a capability invocation.
type EventBus ¶
type EventBus struct {
// contains filtered or unexported fields
}
EventBus is an in-process publish/subscribe bus for capability lifecycle events.
Topic format: <ext>:<cap>:<phase> e.g. "bud-core:fetch:after" Wildcard rules: any segment may be replaced with "*", e.g. "bud-core:*:after" or "*:*:after".
Indexing strategy:
- Subscriptions are indexed by a normalised key derived by replacing non-wildcard segments with "_" — this gives O(1) bucket lookup for each incoming topic.
- On Publish, we compute all 8 possible bucket keys for a 3-segment topic and iterate only the matching buckets.
func (*EventBus) Publish ¶
Publish delivers event to all matching subscribers. Matching is determined by segment-wise equality or wildcard "*" in the subscription pattern. Publish is synchronous — all handlers run on the caller's goroutine. Callers that want fire-and-forget semantics should wrap the call in a goroutine.
func (*EventBus) Subscribe ¶
func (b *EventBus) Subscribe(pattern string, handler EventHandler) func()
Subscribe registers handler for events matching pattern. Pattern must be a 3-segment colon-separated string; any segment may be "*". Returns an unsubscribe function that removes this handler.
type EventHandler ¶
type EventHandler func(e Event)
EventHandler is a function invoked when a matching event is published.
type MCPServerDef ¶
type MCPServerDef struct {
Command string `yaml:"command"`
Args []string `yaml:"args,omitempty"`
Env map[string]string `yaml:"env,omitempty"`
}
MCPServerDef describes an MCP subprocess that the extension can start on demand.
type Manifest ¶
type Manifest struct {
Name string `yaml:"name"`
Version string `yaml:"version,omitempty"`
Description string `yaml:"description"`
Author string `yaml:"author,omitempty"`
Capabilities map[string]CapabilityMeta `yaml:"capabilities,omitempty"`
Behaviors []Behavior `yaml:"behaviors,omitempty"`
Lifecycle map[string]string `yaml:"lifecycle,omitempty"`
Requires Requirements `yaml:"requires,omitempty"`
MCPServers map[string]MCPServerDef `yaml:"mcp_servers,omitempty"`
// Settings is a flat map from setting key to its JSON Schema subset node.
// Treat this as the "properties" of an implicit root object schema.
Settings map[string]SchemaNode `yaml:"settings,omitempty"`
// SettingsRequired lists which top-level settings keys are required.
// Missing required settings emit a warning on load, but never hard-fail.
SettingsRequired []string `yaml:"settings_required,omitempty"`
}
Manifest is the parsed content of plugin.yaml.
type OnResultConfig ¶
type OnResultConfig struct {
// Parse pre-processes the workflow result before evaluation.
// Supported: "json" (extract JSON from the result string).
Parse string `yaml:"parse,omitempty"`
// Condition is an expr-lang expression evaluated against the (possibly parsed) result.
// The handler fires only when the condition is true or when Condition is empty.
Condition string `yaml:"condition,omitempty"`
// Action determines what to do when the handler fires.
// "notify" → talk_to_user, "log" → save_thought, "invoke" → run a workflow.
Action string `yaml:"action"`
// Message is the notification message for action=notify. Supports {{var}} templates.
Message string `yaml:"message,omitempty"`
// Workflow is the workflow name for action=invoke. Supports {{var}} templates.
Workflow string `yaml:"workflow,omitempty"`
// Params are passed to the invoked workflow. Values support {{var}} templates.
Params map[string]any `yaml:"params,omitempty"`
}
OnResultConfig is the parsed on_result block from a behavior or capability definition.
type ParamDef ¶
type ParamDef struct {
Type string `yaml:"type,omitempty"` // "string", "integer", "boolean", "number"
Description string `yaml:"description,omitempty"` // human-readable description
Required bool `yaml:"required,omitempty"` // whether the parameter must be supplied
Default any `yaml:"default,omitempty"` // value applied when param is absent
Enum []any `yaml:"enum,omitempty"` // allowed values
}
ParamDef describes a single parameter for an action capability. Unlike SchemaNode (which uses Required as a []string for object schemas), ParamDef uses Required as a bool to indicate whether the parameter must be provided.
type ParsedCron ¶
type ParsedCron struct {
// contains filtered or unexported fields
}
ParsedCron holds the 5 fields: minute, hour, dom, month, dow. Exported for use in tests.
func ParseCron ¶
func ParseCron(expr string) (*ParsedCron, error)
ParseCron parses a 5-field cron expression. Exported for use in tests.
type PatternMatcher ¶
type PatternMatcher interface {
// MatchAndRun attempts to match content against the named reflex (by name) and execute it.
// Returns (matched, error).
MatchAndRun(ctx context.Context, name string, source, typ, content string, data map[string]any) (bool, error)
}
PatternMatcher checks whether content from a given source/type matches a reflex trigger. The Dispatcher delegates pattern_match triggers to this.
type Plugin ¶
type Plugin struct {
Manifest Manifest
Dir string
Capabilities map[string]*Capability
Settings map[string]any // current settings (loaded + schema defaults applied)
State map[string]any // current state
// contains filtered or unexported fields
}
Plugin is a fully-loaded plugin ready for use by the runtime.
func LoadPlugin ¶
LoadPlugin reads one plugin from dir, validates the manifest, loads all capabilities, applies settings schema defaults, and loads any existing state. Soft failures (unknown schema keywords, missing-but-defaultable settings, etc.) emit log warnings rather than returning errors.
func (*Plugin) Enabled ¶
Enabled reports whether the plugin is currently enabled. A plugin is considered enabled if _enabled is absent (default on) or explicitly set to true.
func (*Plugin) SettingsGet ¶
SettingsGet returns the current value for the given settings key. Returns nil (no error) if the key does not exist.
func (*Plugin) SettingsSet ¶
SettingsSet writes a new value for the given settings key. If the plugin manifest declares a schema for this key, the value is validated against it; a type mismatch returns an error and nothing is written. On success the in-memory settings map is updated and settings.json is persisted.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds all successfully loaded plugins, ordered by dependency.
func LoadAll ¶
LoadAll loads plugins from each of the given dirs in order. Later dirs override earlier ones when the same plugin name appears in multiple dirs.
Plugins are ordered by their declared requires.plugins dependency graph. If a cycle is detected, the cycled plugins are excluded from the Registry.
Empty strings in dirs are silently skipped.
func (*Registry) Capabilities ¶
Capabilities returns the names of all capabilities across all loaded plugins. Names are in the form "<plugin-name>:<capability-name>".
func (*Registry) CapabilitiesOfType ¶
func (r *Registry) CapabilitiesOfType(capType string) []struct { FullName string Cap *Capability Ext *Plugin }
CapabilitiesOfType returns all capabilities across all plugins with the given type and a callable_from value that includes model invocation ("model" or "both"). Each entry is a (fullName, capability, plugin) triple.
func (*Registry) FindCapabilityByName ¶
func (r *Registry) FindCapabilityByName(name string) (*Capability, *Plugin, bool)
FindCapabilityByName searches all plugins for a capability with the given short name. If multiple plugins define a capability with the same name, returns the first match. Returns (nil, nil, false) if not found.
func (*Registry) GetCapabilityByFullName ¶
func (r *Registry) GetCapabilityByFullName(fullName string) (*Capability, *Plugin, bool)
GetCapabilityByFullName looks up a capability by its "pluginname:capname" full name. Returns (capability, plugin, true) on success, or (nil, nil, false) if not found.
type Requirements ¶
type Requirements struct {
// Plugins lists required plugin names. LoadAll topologically sorts
// based on this field and rejects cycles.
Plugins []string `yaml:"plugins,omitempty"`
Tools []string `yaml:"tools,omitempty"`
MCPServers []string `yaml:"mcp_servers,omitempty"`
}
Requirements declares what a plugin depends on from the runtime.
type SaveThought ¶
SaveThought persists a log entry (used by on_result:action=log).
type SchemaNode ¶
type SchemaNode struct {
Type string `yaml:"type,omitempty"`
Description string `yaml:"description,omitempty"`
Default any `yaml:"default,omitempty"`
// Required lists required property names within an object-type SchemaNode.
Required []string `yaml:"required,omitempty"`
Enum []any `yaml:"enum,omitempty"`
Minimum *float64 `yaml:"minimum,omitempty"`
Maximum *float64 `yaml:"maximum,omitempty"`
MinLength *int `yaml:"minLength,omitempty"`
MaxLength *int `yaml:"maxLength,omitempty"`
Pattern string `yaml:"pattern,omitempty"`
Items *SchemaNode `yaml:"items,omitempty"`
Properties map[string]SchemaNode `yaml:"properties,omitempty"`
// Unknown captures any YAML keys not listed above for warning purposes.
Unknown map[string]any `yaml:",inline"`
}
SchemaNode is a subset of JSON Schema used to describe and validate settings values.
Supported keywords: type, description, default, required (array, for object types), enum, minimum, maximum, minLength, maxLength, pattern, items, properties.
Unknown YAML keys are captured in Unknown and produce a warning on load. They are never treated as errors — unsupported keywords are warn-and-ignored.
type TalkToUser ¶
TalkToUser sends a notification message to the user (used by on_result:action=notify).