How the Omarchy Plugin Registry Handles Discovery, Loading, and Hot Reload

The Omarchy plugin registry discovers plugins by scanning the shell/plugins/ directory for valid manifest.json files, dynamically loads them via QML loaders with injected configuration properties, and supports hot reloading through file system watchers that unload and re-instantiate components when source files change.

Omarchy is an open-source desktop shell built on Quickshell that uses a modular plugin architecture to manage panels, overlays, and system menus. The Omarchy plugin registry, implemented in shell/services/PluginRegistry.qml, serves as the central orchestrator that validates, instantiates, and manages the lifecycle of all shell extensions without requiring a full session restart.

Plugin Discovery and Validation

The registry begins its lifecycle by scanning the filesystem for valid plugin bundles.

Directory Scanning and Manifest Validation

According to the source code in shell/services/PluginRegistry.qml, the registry recursively scans the shell/plugins/ directory (and any additional paths defined in shell.json) for subdirectories containing a manifest.json file. For each discovered folder, the registry performs three validation steps:

  • Manifest parsing – Reads the JSON to extract the plugin name, type (panel, overlay, agent, or menu), and entry QML component path.
  • File existence check – Verifies that the entry QML file exists inside the plugin directory before attempting to load it.
  • Enablement verification – Checks the user’s ~/.config/omarchy/shell.json to determine if the plugin is marked as enabled. Disabled plugins are tracked but excluded from the active set.

Only plugins passing all three checks are passed to the loading phase.

Dynamic Loading Architecture

Once validated, plugins are instantiated through a dynamic component system that isolates each extension in its own runtime context.

Component Instantiation via QML Loaders

For each valid plugin, the registry creates a Loader element. The loader’s sourceComponent property is bound to the plugin’s entry QML file (for example, Panel.qml for panel-type plugins). This is exposed through the listPlugins property of the registry, which returns a JSON array of loaded plugin metadata sorted alphabetically.

When the loader instantiates, the QML component compiles and executes its Component.onCompleted hook, allowing the plugin to register its UI elements with the shell containers.

Configuration Injection

The registry injects per-plugin settings from ~/.config/omarchy/shell.json directly into each loader via property bindings. This enables runtime configuration of parameters such as position, shortcut, or custom enabled states without modifying the plugin source code. Each plugin receives its configuration subset as context properties before Component.onCompleted runs.

Hot Reload Implementation

Omarchy supports iterative development through a file-watching mechanism that refreshes plugins without restarting the shell session.

File System Watching

The registry utilizes Quickshell’s built-in FileWatcher QML type to monitor every plugin directory. When any file within a watched directory changes—whether the QML source, manifest.json, or auxiliary resources—the watcher emits a fileChanged signal captured by shell/services/PluginRegistry.qml.

Reload Sequence

Upon detecting a change, the registry executes a deterministic unload-reload sequence:

  1. Unload – Sets loader.sourceComponent = undefined to destroy the existing component instance and free associated resources.
  2. Re-validation – Re-reads the manifest.json to capture any metadata updates, renamed entry files, or type changes.
  3. Re-instantiate – Re-assigns loader.sourceComponent to the refreshed QML file, triggering recompilation and initialization.

Because each plugin is isolated within its own Loader, hot reloading affects only the targeted plugin. Transient UI state (such as open dropdowns or scroll positions) is discarded during reload, which aligns with the "refresh-and-reset" development workflow.

Manual Reload Trigger

Developers can force a full registry refresh by running the omarchy-reload-plugins command located in bin/. This utility signals the shell to re-evaluate all plugin directories immediately, bypassing the file watcher.

Creating a Compatible Plugin

To integrate with the registry, a plugin must follow the directory structure and metadata format expected by the discovery logic.

// shell/plugins/panels/example/manifest.json
{
  "name": "example",
  "type": "panel",
  "entry": "Panel.qml",
  "enabled": true
}
// shell/plugins/panels/example/Panel.qml
import QtQuick 2.15
import Quickshell 0.1

Item {
    id: root
    width: 200; height: 40
    Text { text: "Example Panel"; anchors.centerIn: parent }
}

After placing these files in shell/plugins/panels/example/, the registry discovers the plugin on the next shell start. If you edit Panel.qml while the shell is running, the FileWatcher triggers an immediate hot reload, refreshing the panel UI without disturbing other components.

Summary

  • Discovery – The registry scans shell/plugins/ for directories containing manifest.json, validates the entry QML exists, and checks enablement status in ~/.config/omarchy/shell.json.
  • Loading – Valid plugins are loaded via isolated Loader elements with configuration injected through property bindings, exposing a listPlugins property for shell consumption.
  • Hot Reload – FileWatcher monitors plugin directories; on change, the registry unloads (sourceComponent = undefined) and re-instantiates the component to reflect updates immediately.
  • Manual Control – The bin/omarchy-reload-plugins command forces a full registry refresh outside of the file-watching mechanism.

Frequently Asked Questions

How does the Omarchy plugin registry locate new plugins?

The registry scans the shell/plugins/ directory (and additional paths specified in shell.json) for subdirectories containing a manifest.json file. It validates that the manifest’s entry field points to an existing QML file and that the plugin is enabled in the user configuration file ~/.config/omarchy/shell.json.

What happens to UI state when a plugin hot reloads?

Transient UI state is discarded because the reload sequence sets loader.sourceComponent = undefined, which destroys the old component instance entirely. The new instance starts fresh with default property values and re-executes Component.onCompleted. This behavior is intentional for development workflows where a clean state is preferred.

Can plugins be disabled without removing their files from the system?

Yes. Users can set enabled: false for the specific plugin entry in ~/.config/omarchy/shell.json. The registry tracks disabled plugins in its internal state but excludes them from the listPlugins property, preventing instantiation without deleting the plugin directory from shell/plugins/.

How do I manually trigger a plugin reload if the file watcher fails?

Execute the omarchy-reload-plugins command found in the bin/ directory of the Omarchy repository. This CLI wrapper sends a signal to the running shell instance, forcing shell/services/PluginRegistry.qml to re-scan all plugin directories and reload enabled extensions regardless of file system events.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →