Plugin Types¶
Base classes for extending Lightfall. Every plugin implements one of the
plugin-type contracts below; plugins are declared in a manifest and loaded
by PluginLoader at startup.
Import paths¶
The most common classes are re-exported from lightfall.plugins:
from lightfall.plugins import (
PluginType, PluginManifest, PluginEntry,
PluginInfo, PluginRegistry, PluginLoader,
AgentPlugin, ControllerPlugin, PanelPlugin, PlanPlugin, SettingsPlugin,
PluginStatus, PluginError,
)
EnginePlugin, StatusBarPlugin, and ThemePlugin are not re-exported;
import them from their modules:
from lightfall.plugins.engine_plugin import EnginePlugin
from lightfall.plugins.statusbar_plugin import StatusBarPlugin, StatusBarPluginMetadata
from lightfall.plugins.theme_plugin import ThemePlugin, ThemeDefinition
Registration¶
Manifests and entry points¶
Plugins are declared in a PluginManifest — a module-level object in your
package that lists PluginEntry items. An entry point in the
lightfall.plugins group points at the manifest:
# my_beamline/manifest.py
from lightfall.plugins import PluginManifest, PluginEntry
manifest = PluginManifest(
name="my-beamline-plugins",
version="1.0.0",
plugins=[
PluginEntry("panel", "my_panel", "my_beamline.plugins:MyPanelPlugin",
preload=True),
PluginEntry("agent", "my_tools", "my_beamline.plugins:MyAgentPlugin"),
],
)
# pyproject.toml
[project.entry-points."lightfall.plugins"]
my_beamline = "my_beamline.manifest:manifest"
Because the entry point references a manifest module, plugins can be added, removed, or re-pointed by editing the manifest and restarting — no package reinstall required.
PluginEntry fields:
Field |
Type |
Description |
|---|---|---|
|
|
Plugin type (see table below). |
|
|
Unique name within the type. |
|
|
|
|
|
Optional plugin-specific metadata. |
|
|
Load synchronously before the main window is created. Use for plugins that must apply before any UI appears (themes, appearance settings) and for panel plugins. |
Plugin types loaded at startup¶
The application registers these type names with the loader
(lightfall.main._setup_plugins); manifest entries with any other
type_name are skipped with a warning:
|
Base class |
Provides |
|---|---|---|
|
|
Color scheme + optional CSS overrides |
|
|
A page in the Preferences dialog |
|
|
An execution engine (RunEngine wrapper) |
|
|
Skill prompt and/or MCP tools for the embedded Claude agent |
|
|
An indicator widget in the status bar |
|
|
A device-specific control widget |
|
|
A dockable |
PlanPlugin exists and is exported from lightfall.plugins, but the
"plan" type is not currently registered in the default startup sequence,
so manifest entries of type "plan" are skipped. The supported route for
adding plans today is user plan files in ~/lightfall/plans/ — see
Plans.
User plugin files¶
Python files dropped into ~/lightfall/plugins/ are executed by
UserPluginService with hot-reload. Any non-abstract PluginType subclass
defined in such a file auto-registers via PluginType.__init_subclass__ —
no manifest needed. The auto-registration path currently supports the
agent and panel types; other types log a warning. BasePanel
subclasses that self-register with PanelRegistry at module scope are also
tracked for unload. Each change to a user plugin file is committed to a
local git repository automatically.
PluginType (base class)¶
All plugin types inherit from lightfall.plugins.types.PluginType.
Member |
Kind |
Description |
|---|---|---|
|
ClassVar |
Unique type identifier (e.g. |
|
ClassVar |
Whether one instance exists per plugin. All current plugin types set |
|
abstract property |
Unique plugin name within the type. Required. |
|
property |
Human-readable description. Optional override. |
|
classmethod |
Type-specific validation; default is an |
|
method |
Dict consumed by the agent’s MCP tools. Each plugin type extends it. |
AgentPlugin¶
lightfall.plugins.agent_plugin.AgentPlugin — extends the embedded Claude
agent with a skill prompt (materialized as a SKILL.md) and/or an
in-process MCP server. One settings toggle controls both contributions.
Member |
Kind |
Description |
|---|---|---|
|
abstract property |
Required. ≤64 chars, lowercase with hyphens/underscores. Becomes the SKILL.md name and the MCP server namespace ( |
|
abstract property |
Required. One line; shown in settings UI and SKILL.md frontmatter (truncated to 1024 chars). |
|
method |
SKILL.md body. Empty string (default) = no skill contribution. |
|
method |
List of |
|
method |
Optional package directory of supplementary docs, copied to the skill’s |
|
property |
Settings-UI label. Defaults to title-cased |
|
property |
Settings-UI grouping ( |
|
property |
Default |
|
property |
Settings-UI sort order (lower = first). Default |
A useful AgentPlugin overrides at least one of get_system_prompt() or
create_tools().
- class lightfall.plugins.agent_plugin.AgentPlugin[source]¶
Bases:
PluginTypeExtends the embedded Claude agent with an optional skill prompt and/or a bag of MCP tools.
When enabled, contributes:
a SKILL.md (if get_system_prompt() returns non-empty text), materialized into the per-session SDK plugin dir at agent construction time;
an in-process MCP server (if create_tools() returns tools), registered as mcp_servers[plugin.name] with namespace mcp__<plugin.name>__*.
See docs/superpowers/specs/2026-04-25-lightfall-sdk-native-plugins-design.md.
- abstract property name : str¶
Unique plugin identifier.
≤64 chars. Lowercase + hyphens/underscores. Used as:
the manifest entry name,
the SKILL.md frontmatter name field (with underscores → hyphens conversion at materialization, per spec Open question),
the MCP server name (
mcp__<name>__*),the settings UI preference identifier.
- abstract property description : str¶
One-line description shown in settings UI and in SKILL.md frontmatter.
Truncated to 1024 chars at SKILL.md materialization time (SDK limit).
- property category : str¶
general, devices, acquisition, operations, development.
- Type:¶
Settings-UI grouping. Common values
- create_tools() list[Any][source]¶
Return @tool-decorated callables. Empty = no MCP server contribution.
- create_external_servers() dict[str, dict[str, Any]][source]¶
Return external (stdio/http) MCP server specs to register in the session.
Keys are server names (become the
mcp__<name>__*tool namespace). Values are SDK-compatible external-server dicts, e.g.{"type": "stdio", "command": ..., "args": [...], "env": {...}}. Empty dict (default) = no external server contribution.
- has_external_servers() bool[source]¶
Cheap, side-effect-free signal for introspection: does this plugin contribute external MCP servers?
Override with a lightweight check. Do NOT build the specs here –
create_external_servers()may have side effects (writing config, spawning probes) that belong to session-assembly time, not to a metadata read.
ControllerPlugin¶
lightfall.plugins.controller_plugin.ControllerPlugin — provides a
device-specific control widget. Controllers inspect the selected device
items and return a priority; the highest-priority match supplies the widget.
Member |
Kind |
Description |
|---|---|---|
|
abstract property |
Required. Unique controller name. |
|
abstract method |
Required. Return an |
|
abstract method |
Required. Return a new widget; it should accept devices via a |
|
property |
Widget-selector label. Defaults to title-cased |
EnginePlugin¶
lightfall.plugins.engine_plugin.EnginePlugin — provides an execution
engine selectable through user preferences.
Member |
Kind |
Description |
|---|---|---|
|
abstract property |
Required. Engine identifier for registration and preferences. |
|
abstract method |
Required. Return a fully initialized |
|
property |
UI label. Defaults to title-cased |
|
property |
Longer description for the UI. Default empty. |
PanelPlugin¶
lightfall.plugins.panel_plugin.PanelPlugin — wraps a BasePanel subclass
(see Panels) and registers it with PanelRegistry on load.
Use preload=True in the manifest entry so the panel class is available
when the main window builds its sidebar.
Member |
Kind |
Description |
|---|---|---|
|
abstract property |
Required. Unique plugin name. |
|
abstract method |
Required. Return the |
|
property |
Convenience: the wrapped panel’s |
class MyPanelPlugin(PanelPlugin):
@property
def name(self) -> str:
return "my_panel"
def get_panel_class(self):
from my_package.panels import MyPanel
return MyPanel
PlanPlugin¶
lightfall.plugins.plan_plugin.PlanPlugin — wraps a Bluesky plan generator
function with metadata for UI generation. As noted in the startup-types
table above, the "plan" manifest type is not registered in the current
startup sequence; prefer user plan files (see Plans) for adding
plans today.
Member |
Kind |
Description |
|---|---|---|
|
abstract property |
Required. Plan name for registry and UI. |
|
abstract method |
Required. Return the plan generator function (yields Bluesky messages). |
|
property |
Grouping in the UI (e.g. |
|
property |
Defaults to the plan function’s docstring. |
|
method |
Builds a |
SettingsPlugin¶
lightfall.plugins.settings_plugin.SettingsPlugin — provides a page in the
Preferences dialog.
Lifecycle: instantiate → on_loaded() (before the main window for
preload=True entries) → on dialog open: create_widget() (once, cached)
then load_settings() → on user edits: apply_preview() → on OK/Apply:
validate() then save_settings() → on Cancel: revert_preview().
Member |
Kind |
Description |
|---|---|---|
|
abstract property |
Required. Unique settings-page identifier. |
|
abstract method |
Required. Build the settings widget (called once, cached). |
|
abstract method |
Required. Populate the widget from |
|
abstract method |
Required. Persist widget values to |
|
property |
Sidebar label. Defaults to title-cased |
|
property |
Optional |
|
property |
Sidebar grouping ( |
|
property |
Sort order within category (lower = first). Default |
|
method |
Return a list of error messages; non-empty blocks save. |
|
methods |
Optional live preview (e.g. theme changes) and its undo. |
|
method |
Apply settings at startup (preload plugins run before the main window). |
StatusBarPlugin¶
lightfall.plugins.statusbar_plugin.StatusBarPlugin — an indicator in the
main window’s status bar. The default widget is a flat QToolButton;
subclasses typically only implement update(), connect_signals(),
disconnect_signals(), and optionally on_clicked(), using the display
helpers to drive the button.
Subclasses must define a class-level metadata: StatusBarPluginMetadata
and, if they override __init__, call super().__init__().
Member |
Kind |
Description |
|---|---|---|
|
ClassVar |
Required. |
|
abstract property |
Required. Unique plugin name. |
|
abstract method |
Required. Refresh the display from current state. |
|
abstract methods |
Required. Wire/unwire the service signals that trigger |
|
method |
React to a click on the default button. Default no-op. |
|
method |
Override for a custom widget; you must assign |
|
helpers |
Drive the default button. |
|
visibility |
Hidden widgets are removed from the layout flow. |
ThemePlugin¶
lightfall.plugins.theme_plugin.ThemePlugin — provides a color scheme.
Themes register with ThemeRegistry and appear in the Appearance
preferences.
Member |
Kind |
Description |
|---|---|---|
|
abstract property |
Required. Lowercase identifier (e.g. |
|
abstract property |
Required. Label in the theme selector. |
|
abstract property |
Required. Used for OS-level “System” theme selection. |
|
abstract method |
Required. Return a |
ThemeDefinition is a dataclass of color strings — primary, secondary,
success, warning, error, info, background, surface, text,
text_secondary, border, plus connected/disconnected (default to
success/error), sea (Islands-layout gap color, falls back to
background), and css_overrides (CSS appended after the base stylesheet).
Infrastructure¶
These classes implement plugin discovery and loading. Plugin authors rarely
interact with them directly beyond writing a manifest, but they define the
loading lifecycle: DISCOVERED → QUEUED_LOAD → LOADING → QUEUED_INIT → INITIALIZING → READY (or FAILED_LOAD / FAILED_INIT / DISABLED).