Skip to content
Operations
Agent

plugins

Read this before changing the status bar, notifications, shell plugins, widgets, or idle/lock behavior.

From plugin
omarchy
47k16 skills16 agents

Claude Code

$ npx -y skills add omacom/omarchy --agent claude-code

Agent definition

plugins.md

Omarchy Shell: Bar, Plugins, and Idle

Read this before changing the status bar, notifications, shell plugins, widgets, or idle/lock behavior.

The bar, notification daemon, settings panel, and assorted overlays all run inside a single long-running Quickshell process (`omarchy-shell`).

~/.config/omarchy/shell.json             # User overrides: bar, plugins, idle
~/.config/omarchy/plugins/<plugin-id>/   # User-owned shell plugins
$OMARCHY_PATH/config/omarchy/shell.json  # Canonical defaults

The shell hot-reloads `shell.json` on save — no restart needed for layout changes. `idle.screensaver` and `idle.lock` are seconds since user idle began.

**Commands:** `omarchy restart shell`, `omarchy refresh shell`

Plugin UI Safety

Plugins run inside Omarchy's long-lived, unsandboxed shell. Treat text from network responses, files, IPC, settings, environment variables, and command output as untrusted.

  • QML `Text` defaults to `Text.AutoText`. Do not bind untrusted data to an AutoText sink: markup-shaped input such as `<img src="https://example.invalid/tracker">` can be interpreted as rich text and load a resource.
  • For display text owned by the plugin, set `textFormat: Text.PlainText`. Prefer a local `PlainText` component when the plugin has multiple text elements.
Text {
  textFormat: Text.PlainText
  text: modelData.name
}
  • When passing dynamic text to an Omarchy-owned component whose internal `Text` format the plugin cannot set, neutralize markup in the plugin first with a helper of its own, and add a regression test; Omarchy ships no such helper. Never edit the packaged component to add a plain-text option: that belongs in a pull request to Omarchy.
  • Bound and validate downloaded data before displaying or saving it: limit response and collection sizes, string lengths, identifiers, and timestamps.
  • Keep network access explicit. Use fixed HTTPS sources, timeouts, response-size limits, and validation. For periodic background refreshes, disclose the behavior and let users enable or disable it. Never execute downloaded content or interpolate untrusted values into shell commands.
  • Add regression coverage for markup-shaped input and every shared tooltip or notification sink that receives dynamic text.

Plugin Motion

Motion in a plugin should feel like the rest of the shell. Most of it comes free from Omarchy's kit in `qs.Ui`: a `KeyboardPanel` grows out of its bar icon and fades, morphs into a neighbouring panel in the same bar section, and grows to fit content that arrives late; a `PopupCard` grows and fades; a `Button` gives a little when pressed. Build on those rather than animating your own open and close.

For anything the plugin animates itself, `import qs.Commons` and:

  • Give every duration as `Style.duration(ms)`, never a bare number. It returns 0 when the user has turned animations off with `omarchy toggle animations`.
  • Ease movement and entrances with `Style.easeOut` (`easing.type: Easing.Bezier; easing.bezierCurve: Style.easeOut`) or `Easing.OutCubic`. Never an ease-in curve, no bounce, and linear only for constant motion such as a spinner.
  • Keep it under 300ms, with exits shorter than entrances: about 180ms in, 120ms out.
  • Never scale from 0; start at 0.95–0.97 with opacity 0.
  • Don't animate what a single keystroke opens or dismisses many times a day; it should just appear or go.
  • Animate only for a reason: showing where something came from or went, confirming an input, or keeping a change from jumping.
import QtQuick
import qs.Commons

Item {
  property bool shown: false
  opacity: shown ? 1 : 0
  scale: shown ? 1 : 0.97
  Behavior on opacity { NumberAnimation { duration: Style.duration(180); easing.type: Easing.OutCubic } }
  Behavior on scale {
    NumberAnimation { duration: Style.duration(180); easing.type: Easing.Bezier; easing.bezierCurve: Style.easeOut }
  }
}

Translations

Omarchy runs in 50 languages, and a plugin should follow the one picked under Setup > Language the way the built-in plugins do: live, without a shell restart. Ship the plugin's strings as flat JSON catalogs in an `i18n/` directory beside its QML, one file per Omarchy language code (`en.json`, `da.json`, `pt-BR.json`; `$OMARCHY_PATH/default/i18n/locales.json` lists all 50), and read them through `Commons.Translations`:

import qs.Commons as Commons

Item {
  Commons.Translations { id: strings; directory: Qt.resolvedUrl("i18n") }

  Text {
    textFormat: Text.PlainText
    text: strings.tn("panel.items", count).arg(Commons.I18n.formatNumber(count))
  }
}
{ "panel.items": { "one": "%1 item", "other": "%1 items" } }
  • A string missing from the current language falls back to its base language (`pt-BR` to `pt`), then to `en.json`, then to the key, so a plugin can ship English alone and add languages one file at a time.
  • Keep each string whole with `%1` placeholders filled by `.arg()`; never glue translated fragments into a sentence, since other languages reorder words.
  • A string that counts something is a plural entry, an object keyed by CLDR category, read with `strings.tn(key, count)`. English needs `one` and `other`; each translation supplies the categories its language uses (Russian `one`, `few`, `many`, `other`; Arabic six; Japanese only `other`; the rules are in `$OMARCHY_PATH/shell/Commons/Plural.js`).
  • Write displayed numbers with `Commons.I18n.formatNumber(value, decimals)` so they take the language's decimal separator and digits (`13,1` in Danish).
  • Arabic, Hebrew, Persian, and Urdu mirror the layout. A plugin built on `KeyboardPanel` or `PopupCard` is mirrored for free; anything it positions by explicit `x` should follow `LayoutMirroring.enabled`, glyphs that mean back or forward should swap with `Commons.I18n.rightToLeft`, and Left/Right keys should go through `Util.readingKey(event)`.
  • Keep calls to `strings.t(...)` inside bindings so a language switch re-renders them. Text built once when something happens keeps th
Read more
@basecamp
Ships withomarchy

Omarchy is a beautiful, fun & agentic Linux distribution by DHH. Read more at omarchy.org.

47k3 skills16 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
46.7k
Star history of basecamp/omarchy on GitHub, a chart by star-history.com

Other agents on omarchy.

acceptance-tests

acceptance-tests

Agent

Read this before writing or running the graphical acceptance suite under `test/acceptance.d/`.

@basecamp@basecampInstall Agent
icon-font

icon-font

Agent

Read this before adding a branded glyph to `default/fonts/omarchy/omarchy.ttf`.

@basecamp@basecampInstall Agent
mise-tools

mise-tools

Agent

Read this before adding or changing a mise-managed tool. Default tools belong in `etc/mise/conf.d/omarchy-tools.toml`, which the…

@basecamp@basecampInstall Agent

More agents from other plugins.

api-reference

api-reference

Agent

**⚠️ DEVELOPER REFERENCE ONLY** — This file documents the raw GraphQL API schema for development and maintenance purposes (adding…

unraid2w150
@dinglebear-ai@dinglebear-aiInstall Agent
agent

agent

Agent

The generic template and base persona for PAI. All role-specific agents under [/agents/](../agents/) inherit from this file,…

pai11d115
@nirholas@nirholasInstall Agent

Questions about plugins.

Which coding agents can use plugins?
plugins installs in Claude Code and Cursor: each loads subagents from .claude/agents, so copy the file there. Flowy's auto-invocation runs in Claude Code only.