Skip to content

Latest commit

 

History

History
186 lines (128 loc) · 4.13 KB

File metadata and controls

186 lines (128 loc) · 4.13 KB

Plugin System

preonic-cli includes a lightweight plugin system for extending functionality.

Overview

Plugins are Python files or packages placed in the system plugin directory. They are discovered automatically and can be managed via the plugins command group.

Plugin Directory

Platform Path
Linux ~/.local/share/preonic/plugins/
macOS ~/Library/Application Support/preonic/plugins/
Windows %LOCALAPPDATA%\preonic\plugins\

Managing Plugins

List Installed Plugins

preonic plugins list

Get Plugin Info

preonic plugins info <name>

Install a Plugin

From source code (inline):

preonic plugins install hello --source "print('hello')"

From a file:

preonic plugins install myplugin --source ./my_plugin.py

Remove a Plugin

preonic plugins remove <name>

Creating a Plugin

A plugin is a Python file that follows the naming convention <name>.py and is placed in the plugin directory.

Plugin Structure

"""my_plugin.py — A simple preonic plugin."""

from __future__ import annotations


def register(app) -> None:
    """Register commands with the Typer app.

    This function is called by the plugin loader when the plugin is discovered.
    """
    import typer

    @app.command()
    def hello(name: str = "World") -> None:
        """Say hello from my plugin."""
        print(f"Hello, {name}!")

Plugin API

The register(app) function receives the root Typer application instance. Inside this function, you can:

  • Add commands with @app.command()
  • Add command groups with app.add_typer()
  • Access the Typer app's full API
  • Import and use any preonic-cli services

Using preonic-cli Services

"""my_plugin.py — Plugin using preonic services."""

from __future__ import annotations


def register(app) -> None:
    import typer
    from rich.console import Console

    console = Console()

    @app.command()
    def hash_text(text: str, algorithm: str = "sha256") -> None:
        """Hash text using preonic's developer service."""
        from preonic.services.developer import DeveloperService

        result = DeveloperService.hash_data(text, algorithm=algorithm)
        console.print(f"[bold cyan]{algorithm}:[/] {result}")

Package Plugins

For more complex plugins, create a directory with __init__.py:

~/.local/share/preonic/plugins/
└── my_plugin/
    ├── __init__.py
    ├── commands.py
    └── utils.py

The __init__.py must expose a register(app) function.

Plugin Discovery

The PluginService scans the plugin directory on startup:

  1. Looks for .py files (single-file plugins)
  2. Looks for directories with __init__.py (package plugins)
  3. Ignores files/directories starting with _
  4. Calls register(app) for each discovered plugin

Security Considerations

  • Plugins execute arbitrary Python code
  • Only install plugins from trusted sources
  • Review plugin code before installation
  • Plugins run in the same process as preonic-cli

API Reference

PluginService

class PluginService:
    @staticmethod
    def list_plugins() -> list[PluginInfo]
    """Return all discovered plugins."""

    @staticmethod
    def plugin_info(name: str) -> PluginInfo
    """Get metadata for a specific plugin."""

    @staticmethod
    def install_plugin(name: str, source: str | Path) -> Path
    """Install a plugin from source code or file path."""

    @staticmethod
    def remove_plugin(name: str) -> None
    """Remove an installed plugin."""

    @staticmethod
    def get_plugin_dir() -> Path
    """Return the plugin directory path."""

    @staticmethod
    def set_plugin_dir(path: Path) -> None
    """Override the plugin directory (useful for testing)."""

PluginInfo

class PluginInfo:
    name: str          # Plugin name
    path: Path         # File or directory path
    enabled: bool      # Whether the plugin is enabled

    @property
    def metadata(self) -> dict[str, Any]
    """Plugin metadata from __init__.py."""

    def to_dict(self) -> dict[str, Any]
    """Serialize to dictionary."""