Skip to main content

Plugin

Plugins extend a quickli application without modifying the core package. Each plugin registers its commands and resources against an Application instance through a well-defined contract.

Plugins exist at the same level as regular commands in the hierarchy: they attach new commands to an existing Application from the outside.

Application
├── Command (registered directly)
└── Plugin ← you are here
└── Command (registered by the plugin)

Plugin contract

Every plugin must subclass quickli.Plugin and implement three members:

MemberKindRequiredDescription
namestr propertyyesUnique, non-empty plugin identifier
descriptionstr propertyyesShort description of what the plugin provides
register(application)methodyesRegisters commands and resources against the app
import quickli


class VersionPlugin(quickli.Plugin):
@property
def name(self) -> str:
return "version-plugin"

@property
def description(self) -> str:
return "Adds a version command."

def register(self, application: quickli.Application) -> None:
@application.command(help_text="Prints the application version.")
def version() -> str:
return "1.0.0"

Loading a plugin

Call Application.load_plugin(plugin) to load a plugin into your application.

app = quickli.Application(name="demo")
app.load_plugin(VersionPlugin())
print(app.run(["version"])) # 1.0.0

load_plugin validates the plugin name, prevents duplicate loading, and calls plugin.register(application) to let the plugin register its commands.

Inspecting loaded plugins

Application.plugins returns a copy of the loaded plugin list.

for plugin in app.plugins:
print(f"{plugin.name}: {plugin.description}")

Error handling

quickli.PluginLoadError is raised when:

  • the plugin name is empty,
  • a plugin with the same name is already loaded,
  • or the plugin's register method raises an exception.
try:
app.load_plugin(VersionPlugin())
except quickli.PluginLoadError as error:
print(f"Failed to load plugin: {error}")

Tips

:::tip When to use a plugin Use a plugin when you want to package a reusable set of commands as a separate Python module or package. For example, a shared audit plugin can be loaded into any team's CLI without copying code. For small, app-specific commands, just use @app.command directly. :::

:::tip Plugins cannot override existing commands A plugin cannot replace a command that has already been registered — either by the application itself or by an earlier plugin. Design your plugins to add new commands rather than replace existing ones. :::

Current status

The plugin system is implemented in the Alpha release with explicit loading through Application.load_plugin(). Automatic plugin discovery via Python package metadata (importlib.metadata entry points) is planned for a future release.

Reference

See the plugin specification and ADR 0002 for the full design rationale.