Native Omarchy shell plugin that shows your running
shopify theme dev and shopify app dev sessions in the bar, with a
keyboard-friendly panel to open previews, the theme editor, admin links,
copy URLs, and stop sessions.
Built as a first-class bar-widget plugin in the same style as the built-in
Tailscale and Dropbox panels.
- Bar icon that dims when idle and shows a dot when more than one session runs
(optional text label: store name, or
2 themes + 1 app) - Hover tooltip with one line per session
- Panel listing every session: store, theme name / ID / port or app name, project folder, and a live running time
- Per-session actions: open local preview, theme editor, share-preview link,
storefront, store admin, app in admin, GraphiQL and the local app URL for
app dev; open a Liquid console (shopify theme console) or the app log stream (shopify app logs) in a terminal; copy any URL, the store domain, theme ID / client ID, or the project path; stop the session (SIGINT, then SIGTERM if it ignores that — same as Ctrl+C in its terminal) - Shows the installed CLI version in the panel header, and theme dev modes
(
--theme-editor-sync,--live-reload) in the session line - Detects sessions by inspecting
/procand verifying the process executable is actually the CLI's runtime — nopgrep -f-style false positives from editors or shells that merely mention "shopify theme dev" - Store, theme ID, port and app details are read from the process's flags and
environment (
--store,-e,SHOPIFY_FLAG_STORE, …), the listening socket,shopify.theme.tomlandshopify.app.toml. Only the development theme's name / ID need the CLI (shopify theme info --development --json), which runs once per session in the background and is cached.
omarchy plugin add https://github.com/uwagz/omarchy-shopify-cli.git --enableThe widget lands in the right section of the bar; move it with
omarchy bar move uwagz.shopify-cli if you prefer another spot.
Requirements: Omarchy 4 (omarchy-shell), python3 (3.11+ for the
*.toml lookups), the Shopify CLI
on PATH, and wl-copy for the copy actions.
| Mouse | Action |
|---|---|
| Left click | Toggle the panel |
| Right click | Open the first session's primary link |
| Middle click | Refresh now |
| Click a row | Open its primary link (theme: local preview; app: app in admin) |
| on a row | Link menu |
| on a row | Copy menu |
| on a row | Stop that session |
Inside the panel (keys follow the hint line shopify theme dev prints):
| Key | Action |
|---|---|
j / k, arrows |
Move the cursor |
Enter / Space |
Open the selected session's primary link (header: refresh) |
l / → |
Open the link menu for the selected session |
h / ←, Esc |
Close an open menu |
t |
Open the local preview (http://127.0.0.1:9292) |
e |
Open the theme editor / customizer |
p |
Open the shareable preview link (?preview_theme_id=…) |
g |
Open GraphiQL for an app dev session (like the CLI's own g) |
o |
Open the primary link |
a |
Open the store admin |
s |
Open the storefront |
c |
Copy menu |
f |
Reveal the project folder |
x |
Stop the selected session |
r |
Refresh |
Esc |
Close the panel |
IPC for scripts and keybindings:
omarchy-shell uwagz.shopify-cli toggle # open/close the panel
omarchy-shell uwagz.shopify-cli status # "1 theme + 1 app" / "No dev sessions"
omarchy-shell uwagz.shopify-cli count # number of sessions
omarchy-shell uwagz.shopify-cli sessions # JSON array with every session
omarchy-shell uwagz.shopify-cli refreshsessions returns upstream strings (theme/app names, store, error text)
verbatim — consume it as data (parse the JSON), never eval it or render it
as markup.
omarchy plugin remove uwagz.shopify-cliThis deletes ~/.config/omarchy/plugins/uwagz.shopify-cli/ and takes the
widget out of the bar. The plugin's only other footprint is its cache at
~/.cache/omarchy-shopify-cli/, safe to delete at any time. It never edits
~/.config/omarchy/shell.json or any other user configuration itself —
enable/disable and placement go through omarchy plugin / omarchy bar.
Configure from the bar settings UI, or with omarchy bar set:
| Key | Default | Description |
|---|---|---|
refreshIntervalSec |
10 |
Rescan interval while the panel is closed (2–300 s); it polls every 2 s while open |
fetchDetails |
true |
Ask the Shopify CLI for the dev theme's name / ID / role |
showWhenIdle |
true |
Keep the icon in the bar when nothing is running |
showLabel |
false |
Show the store name (or session count) next to the icon |
omarchy bar set uwagz.shopify-cli showLabel true --json
omarchy bar set uwagz.shopify-cli fetchDetails false --jsonPanel.qml bar button + popup (cursor rows, link/copy menus, IPC)
Service.qml timers, runs status.py, owns every action (open/copy/stop/terminal)
Model.js pure helpers: URLs, labels, parsing (node-testable)
ShopifyIcon.qml Shopify mark rendered natively from its SVG path, with count badge
status.py 3-line entry point
shopify_cli_status.py /proc scan → sessions JSON; detached, cached CLI lookups
status.py walks /proc for processes owned by you that are a Shopify
CLI dev invocation: the shopify token must be argv[0] or argv[1] (a
binary, or an interpreter running the CLI script) and /proc/<pid>/exe must
be a JS runtime (node/bun/deno) or a shopify binary — so an unrelated
process that merely has shopify theme dev somewhere in its arguments is not
treated as a session. The CLI's own helper children (shopify notifications list) are ignored. It reads each session's cwd, flags, environment and
listening ports (for app dev, across the whole child process tree, which is
how GraphiQL on :3457 and a --use-localhost app URL are found), and prints
JSON. The CLI version comes from the package.json next to the resolved
shopify executable (resolving through a mise/asdf shim once, cached), never
from running shopify version. When fetchDetails is on and a theme
session's details are not cached yet, it spawns a detached copy of itself that
runs shopify theme info --development --json --path=<project> [--store=…]
with CI=1 (so it can never pop a login prompt) and writes the result to
~/.cache/omarchy-shopify-cli/<pid>-<start>.json; the next poll picks it up. Cache entries
for sessions that have exited are removed automatically.
The only Shopify CLI commands this plugin ever runs are shopify theme info
and shopify app info. It never pushes, pulls, publishes or deploys anything.
Cost: one idle poll is a ~20 ms python3 -S status.py (stdlib only, no
json/re on the hot path, bytecode cached under ~/.cache/omarchy-shopify-cli/pycache;
~6 ms of it is the actual /proc walk). At the default 10 s that is well
under half a percent of one core, in the same ballpark as the built-in
Tailscale and Dropbox panels. The session list is only republished to the UI
when something other than the clock changed, so rows are not rebuilt on every
poll; the duration ticker runs at 1 s only while a session is under a minute
old (30 s after), and the hero phrases only while the panel is open.
Like every Omarchy shell plugin, this runs as unsandboxed code inside
omarchy-shell. What it actually does:
- Only your own processes. The
/procscan is filtered to your UID, and a process is treated as a session only after the executable and argv checks above — not on a substring match. - No credentials. Only a whitelist of
SHOPIFY_FLAG_*display keys (store, port, theme id, environment, config, auth-alias nickname) is read from a session's environment.SHOPIFY_CLI_THEME_TOKEN, passwords and other secrets are never read, cached, or emitted.--auth-aliasis a profile nickname, not a secret; the Shopify CLI authenticates itself from its own store. - Read-only CLI use. The only commands ever run are
shopify theme infoandshopify app info, detached, withCI=1so they can't prompt or log in. Never push/pull/publish/deploy. Terminal actions (theme console,app logs) are launched only when you pick them, in a visible terminal. - Stopping is signal-safe. Stop opens a
pidfdfor the exact process instance and signals through it (SIGINT, thenSIGTERM), so a pid reused after the session exits can never be signalled. It only ever signals your own processes and never usesSIGKILL. - Untrusted repo data is inert. A dev command can be supplied by the repo
itself, so its flags and config are treated as untrusted: values from
shopify.app.toml/shopify.theme.tomlor CLI JSON are rendered as plain text (no QML rich-text/<img>injection); a Shopify link is only built from a validated*.myshopify.comhost with a numeric theme id or an opaque client id, so a link labelled "Store admin" cannot point elsewhere; other links open only forhttp(s)URLs; the preview host is restricted to loopback/private addresses; the project directory is verified to be a real directory before it reachesxdg-openor a terminal; and CLI flags are passed as--flag=value. - Writes stay in the cache. The only files written are under
~/.cache/omarchy-shopify-cli/. It never editsshell.jsonor any other configuration.
git clone https://github.com/uwagz/omarchy-shopify-cli.git
omarchy plugin validate ./omarchy-shopify-cli
cp -r omarchy-shopify-cli ~/.config/omarchy/plugins/uwagz.shopify-cli # or: omarchy plugin add <path>
omarchy plugin enable uwagz.shopify-cli
python3 status.py --no-details | jq # helper output without touching the CLI
node -e 'console.log(require("./Model.js").linkOptions({kind:"theme",store:"my-store",themeId:"1"}))'Saving files under ~/.config/omarchy/plugins/ hot-reloads the plugin. Shell
logs are in journalctl --user.
MIT — see LICENSE.
