You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A slightly more personalised version of this skill helped me migrate three
machines from Omarchy 3 to Omarchy 4 (Quattro), so I thought the generic version
might help someone else.
This is an agent skill, not a migration script. It gives an AI coding agent
a cautious workflow and practical heuristics for preserving personal
customisations across Quattro's major changes. It deliberately does not choose
your terminal, editor, coding agents, package manager, or preferred desktop
setup for you.
It covers the failure shapes that were easiest to miss:
Hyprland .conf → Lua, including bindings, rules, submaps and scripts;
Waybar/Walker/Mako → the Quickshell-based Omarchy shell;
the move from a user Omarchy checkout to package-owned /usr/share/omarchy;
GUI-session PATH versus interactive-shell PATH;
Herdr launchers containing old absolute paths;
mise wrappers shadowing an existing CLI and installing a second copy;
Other agents can use the same structure in their own skill directory. If your
agent only supports a single skill file, start with SKILL.md and provide the
three reference files as additional context when their topics arise.
Notes
Read the current Omarchy command metadata and packaged source: Quattro is
evolving, and live behaviour outranks examples.
Review the official upgrade route before starting; do not reuse an old beta
command from a post or chat log.
Keep upgrade backups until the replacement behaviours work.
Treat linked bug reports as useful diagnostic shapes, not proof that a bug is
still present.
Use and adapt freely. This is independent community guidance, not an official
Omarchy migration tool.
Hyprland 0.55 deprecated hyprlang configuration in favour of Lua. Omarchy 4
generates a Lua entry point and user modules. Do not translate text mechanically
without first reading the generated Quattro files and current packaged defaults.
Start with ~/.config/hypr/hyprland.lua. A current Omarchy-generated loader
bootstraps the packaged Lua path, loads Omarchy defaults, then requires user
modules. Exact helper names and ordering can change.
Do not reconstruct an old loader from snippets. Existing loaders have broken
after packaged modules began relying on helper globals that an older user file
did not initialize. Compare with the current generated default and
helpers.lua migration issue #5911
when errors mention a nil global such as o.
Add custom modules after defaults so user overrides win:
Lua types matter: booleans are true/false, strings are quoted, and table
entries use commas. Keys containing punctuation may need bracket syntax such
as ["col.active_border"] = "...".
Discover live output names and supported modes with hyprctl monitors all.
Do not copy another machine's output name or scale. Check both monitor scale and
toolkit variables such as GDK_SCALE; a correct live Hyprland scale can still
coexist with an oversized application environment.
Preserve intent from old variants rather than their spelling:
Old form
Lua option/behaviour
bind
plain hl.bind
bindr
{ release = true }
binde
{ repeating = true }
bindl
{ locked = true }
bindm
mouse binding with { drag = true } where appropriate
bindd
preserve the description with Omarchy's o.bind
Check the current Hyprland bind reference for combined flags. Unbind inherited
keys before replacing them; otherwise both the old default and custom action
may fire.
Autostart
Old:
exec-once = my-service --flag
Typical Omarchy Lua:
o.launch_on_start("my-service --flag")
Verify the helper in the current packaged defaults. Preserve ordering,
backgrounding, environment, and single-instance assumptions rather than
blindly wrapping every old line.
Window rules
Old rules should become structured Lua rules. Prefer current generated examples
because match and action schemas evolve:
Omarchy may also expose a concise o.window(...) helper. Use it only after
reading its current call sites. Give custom rules unique names and validate
regular expressions against actual hyprctl clients -j classes.
Submaps and press/release flows
Port both halves of push-to-talk, hold, drag, or modal workflows. A press bind
without its release bind is not equivalent.
Check current API names for no-op keys, dispatchers, and submap reset. Validate
the live submap after use so a failed script cannot strand the user in a mode.
Dispatches inside external scripts
Shell scripts may contain old dispatcher spelling even after Lua config is
clean. For current Hyprland, dispatch Lua expressions through hyprctl:
Translate the semantic action with the current dispatcher documentation. Do
not assume every old hyprctl dispatch <name> <arg> maps by string
substitution.
Files that may still be .conf
The Hyprland compositor's user configuration is Lua. Separate programs can
still own .conf files, including display portals, night-light services, or
other daemons in a given Omarchy release. Trace the process and packaged source
before deleting every .conf under ~/.config/hypr.
Quattro changes package ownership, executable locations, the GUI shell, and
mise-backed convenience wrappers. Many apparent application failures are
selection failures: the intended program still exists, but another path wins.
Treat PATH as several environments
Compare at least:
a login/interactive shell;
the systemd user-manager/GUI session;
the running parent process that launches the failing child;
a remote SSH command, if remote launchers are involved.
Examples:
zsh -lic 'print -r -- $PATH; type -a pi'# substitute the real login shell
systemctl --user show-environment | grep '^PATH='
pgrep -a herdr
tr '\0''\n'</proc/<pid>/environ | grep '^PATH='
readlink -f /proc/<pid>/cwd
A shell can work while a menu, keybinding, service, or Herdr server cannot.
Interactive shell initialization does not retroactively change an existing GUI
or service process.
Durable GUI environment sources commonly include ~/.config/uwsm/default and
~/.config/uwsm/env.d/. Read the current Omarchy/UWSM load path before editing.
A new login is the reliable activation boundary. systemctl --user set-environment can aid a current-session test but is not durable state.
Do not solve precedence bugs by repeatedly appending directories. Choose the
canonical executable and put its owning bin directory in the smallest correct
environment.
Package-root transition
Omarchy 3 commonly appeared as a user checkout under
~/.local/share/omarchy. Quattro's canonical package tree is normally
/usr/share/omarchy, and the old-looking home path may be a compatibility
symlink.
For every custom script or launcher containing an Omarchy path:
Use $OMARCHY_PATH inside Omarchy-aware code when current packaged examples do
so. Use command -v for external executables. Do not patch the package tree to
preserve an old absolute path.
Herdr path heuristic
An old launcher may run:
~/.local/bin/herdr
while the Quattro package provides:
/usr/bin/herdr
This is not universal. On every machine or SSH target involved, discover:
remote commands such as ssh -t host /old/path/herdr;
user services and desktop entries.
Update both the command and any process-matching expression. A correct launch
with a stale matcher can create duplicate Herdr windows. Preserve Herdr's
session file and live workspaces; path repair does not require deleting them.
Diagnose a shadowed command
For any important tool:
type -a <tool>
selected=$(command -v <tool>)printf'%s -> %s\n'"$selected""$(readlink -f "$selected")"
file "$selected"
mime=$(file -Lb --mime-type "$selected")
[[ $mime== text/* ]] && sed -n '1,8p'"$selected"
pacman -Qo "$selected"2>/dev/null ||true
mise current 2>/dev/null
mise which <tool>2>/dev/null ||true
mise where <tool>2>/dev/null ||true
Also inspect the actual package-manager locations the user chose: npm global,
Bun global, pipx, cargo, AUR/pacman, vendor installer, or a project-authored
installation. Version equality does not mean two installations have the same
runtime behaviour.
Mise lazy-wrapper trap
Quattro can place wrappers under ~/.local/bin using
omarchy-mise-install. A wrapper may call mise use -g on first run. That is
intentional for an uninstalled stock tool, but harmful when it shadows an
existing installation the user wants to keep.
Typical symptoms:
a tool unexpectedly announces that it is installing;
~/.config/mise/config.toml gains a new entry after first launch;
the selected binary changes from a JavaScript/Python/Rust install to a
standalone mise backend;
a wrapper recurses because it resolves its own command name through PATH;
an aqua-backed wrapper fails attestation even though a system binary exists.
Do not remove every wrapper by policy. For each tool, ask whether the user wants
the old authored/system install or Omarchy's mise-managed install.
When preserving the old install:
prove the old executable and package are intact;
ensure its bin directory is visible to both shell and GUI launchers;
remove only the shadowing wrapper;
remove the unwanted mise config/tool version if it was installed;
run mise reshim;
remove broken version-alias symlinks left by the uninstaller;
test resolution again in every relevant environment.
When adopting the mise install, make sure the wrapper resolves the installed
binary rather than itself and validate the backend/runtime before deleting the
old installation.
Pi-specific diagnostic, not a preferred install method
Pi can be installed as an npm/Bun global JavaScript package whose CLI shebang
selects Node, or as a mise-provided standalone executable built with Bun. A
user may legitimately prefer either.
If an upgraded system prints a Bun crash mentioning a NAPI module and
uv_async_init, check whether a new ~/.local/bin/pi wrapper selected a
standalone Bun build over an existing Node-run package. Compare:
type -a pi
file "$(command -v pi)"
pi_target=$(readlink -f "$(command -v pi)")
[[ $(file -Lb --mime-type "$pi_target")== text/* ]] && sed -n '1p'"$pi_target"
mise current pi 2>/dev/null ||true
find ~/.local/share/mise/installs ~/.cache/.bun/install/global \
-path '*/pi-coding-agent/package.json' -print 2>/dev/null
Preserve the user's chosen Pi installation. The lesson is ownership and
runtime selection, not “Node is always correct” or “mise is always correct.”
Terminals and editors are choices
Quattro may install or select a different default without removing the user's
old tool. Distinguish:
installed package;
Omarchy launcher default;
XDG terminal preference;
EDITOR and VISUAL;
MIME handlers;
terminal command embedded in a keybinding or desktop file.
Discover the current routed commands with omarchy commands --json; common
routes include:
Ask whether to keep the old preference, adopt Quattro's, or retain both. Only
then set defaults and remove packages/config directories.
Coding agents are choices too
For each agent, establish:
whether the user still wants it;
which installation is canonical;
whether it should be the Omarchy default;
whether shell/menu launchers can see it without installing another copy;
whether a stock wrapper is useful, redundant, or harmful.
omarchy default agent <name> may install the agent if Omarchy believes it is
missing. Inspect the current implementation before using it with a non-mise
installation. Do not assume the user's preferred roster, remove ordinary gh
when cleaning a Copilot agent, or erase agent data merely because a launcher is
retired.
Validation
Test both non-interactive metadata and real startup:
<tool> --version
<tool> --help
For TUIs with native modules, add an ephemeral PTY startup test when safe. A
version flag may exit before loading the failing module. Disable session saving
and use a temporary working directory when supported. Do not send prompts or
trigger external actions merely for a smoke test.
Quickshell, Custom Scripts, Hooks, Themes, and Network
Omarchy 4 replaces the old collection of bar, launcher, notification, and OSD
processes with one Quickshell-based Omarchy shell. Treat this as a product
surface change, not a directory rename.
shell.json is authoritative once present; it is not deep-merged with later
packaged defaults. Read the current packaged default and the live user file
before changing it. Preserve unknown entries and host-specific ordering.
The shell hot-reloads user JSON, JSONC, and plugins. Validate with:
Use omarchy restart shell only when hot reload fails or a changed component
requires a process restart. omarchy refresh shell is replacement/recovery,
not a routine reload.
Do not start a second Quickshell process for a custom widget. Extend the
running Omarchy shell through its plugin model.
Porting Waybar modules
Inventory each custom module as a behaviour bundle:
command or long-running process;
polling interval and signal handling;
click, scroll, and alternate-click actions;
JSON/text schema;
icons, fonts, CSS, and visibility conditions;
state files and secrets;
package and environment dependencies.
Then choose with the user:
map it to a built-in Quickshell widget;
create a user plugin with manifest.json and QML;
replace it with a menu action or simpler script;
retire it.
Do not assume everything under ~/.config/waybar was presentation-only. Users
often store module state there. Upgrade backups can contain small data files
that a script silently falls back from when missing.
Plugin validation may reject symlinked plugin folders. If so, keep canonical
source in the user's dotfiles repository but deploy a real copied plugin tree
under ~/.config/omarchy/plugins/; do not bypass validation or patch the
packaged shell.
Menus, launchers, and notifications
Old Walker menu scripts, Waybar menus, and Mako rules do not automatically
become Quickshell behaviour.
Put static menu additions and overrides in
~/.config/omarchy/extensions/omarchy-menu.jsonc.
Use providers only when the current menu API expects dynamic JSON rows.
Port hidden/default rows deliberately; an old script file existing does not
make it active.
Map notification intent to current Omarchy shell controls or hooks rather
than attempting to restart Mako alongside Quickshell.
Never test a suspend, reboot, shutdown, purchase, message, or other
consequential menu action merely to prove that it appears. Validate structure
and let the user exercise the live action.
GUI environment variables unavailable outside an interactive shell;
process matchers that still recognize only an old executable path.
Port the script and every caller together. A fixed command with an old
pgrep -f expression can create duplicates; a fixed script with an old
keybinding remains inert.
Remove generated __pycache__ when it was created only for migration
validation.
Hooks and themes
Current user hooks live under ~/.config/omarchy/hooks/. Generated theme state
commonly lives under ~/.local/state/omarchy/current/, while user theme sources
belong under ~/.config/omarchy/themes/.
Audit old hooks for:
calls into a user Git checkout that became /usr/share/omarchy;
writes into packaged/default trees;
references to old generated state under ~/.config/omarchy/current;
restarts for Waybar, Walker, Mako, or terminal components no longer used;
custom templates that still work and should remain user-owned.
Edit the source/template/hook, not generated current-theme output. Use current
routed Omarchy commands to reapply a theme or restart an affected component.
Network transition
Some Omarchy 3 installations used iwd directly while Quattro uses
NetworkManager. Community reports show iwd credentials may not be converted.
Before an upgrade—especially away from home—record how to reconnect and confirm
that remote access will survive.
Relevant paths/services can include:
/var/lib/iwd/
iwd.service
NetworkManager.service
Do not print or copy wireless secrets into chat or migration notes. Use a secure
local transfer if conversion is required.
If the upgrade aborts during the system transition, check that it did not leave
both network stacks enabled before rebooting. An incomplete-upgrade report is
documented in issue #6575;
use it as a failure-shape heuristic and follow the current official recovery
path rather than copying an old workaround.
Common symptom map
Symptom
First place to look
Bar/module disappeared
Waybar backup, shell.json, available shell plugins
Menu script exists but does nothing
Active JSONC extension and current menu API
Notification rules vanished
Mako backup versus Quickshell notification controls
Script says command not found
GUI/user-manager PATH, not only interactive shell
Script runs twice
old process matcher, duplicate autostart, second Quickshell instance
Layout script errors after Hyprland update
old dispatcher syntax and Lua API
Theme change no longer affects app
old generated-state path or packaged file edit
Wi-Fi gone after reboot
iwd credentials and NetworkManager state
Cleanup boundary
After the replacement works and the user approves cleanup:
remove inactive Waybar/Walker/Mako config and old autostart entries;
remove superseded scripts and their callers together;
remove old generated theme links, not current user theme sources;
remove upgrade backups only after every needed state file and behaviour has
been accounted for.
Omarchy 3 to 4 (Quattro) migration stewardship: preserve user intent across Hyprland Lua, Quickshell, package-path, GUI-environment, script, and CLI-wrapper changes.
Omarchy 3 → 4 Migration
Use this skill to assess, perform, or finish an Omarchy 3 to Omarchy 4
(Quattro) migration. It is a behaviour-preserving migration, not a recipe for
making every machine use the same terminal, editor, shell, or coding agent.
Current Omarchy command metadata, packaged source, and live runtime state
outrank examples here. Quattro changes quickly.
Non-negotiable boundaries
Use the current official upgrade route. Discover it from the installed
Omarchy command surface or current upstream documentation; do not replay an
old beta curl command from memory.
Do not delete Omarchy 3 configuration while Omarchy 3 is still the live
session. Upgrade, reach a successful completion, reboot, then port and clean.
Treat /usr/share/omarchy as package-owned and read-only. A
~/.local/share/omarchy compatibility path may resolve there.
Inventory user intent before changing defaults or removing packages.
Ask which terminal, editor, coding agents, package managers, and custom
services the user wants to keep. Never substitute a personal allowlist.
Do not restore an old config tree wholesale over Quattro. Port one behaviour
at a time into the current user-owned surface.
Confirm package removal, config refresh/reset, network-stack repair, logout,
reboot, and other disruptive actions unless already explicitly authorized.
Keep the upgrade-created backups until the migrated behaviours validate.
Their eventual deletion is a separate user decision.
Read selectively
references/hyprland-lua.md — .conf to Lua mapping and validation.
references/path-and-programs.md — package-root, PATH, Herdr, mise wrapper,
Pi, terminal/editor, and coding-agent diagnostics.
references/shell-and-scripts.md — Waybar/Walker/Mako to Quickshell,
custom scripts, hooks, themes, and network transition.
Migration workflow
1. Establish the phase
Identify one of these states before touching anything:
Pre-upgrade: Omarchy 3 is live.
Upgrade incomplete: the transition command failed or never reached its
final success/reboot boundary.
Quattro booted, personal behaviour missing: the normal porting phase.
Quattro works, cleanup remains: validate, then remove superseded state.
If the upgrade failed, preserve its first error and inspect the current
upgrade implementation and logs. Do not reboot a partially switched system
merely because package installation appeared to finish. Pay particular
attention to network services and whether both the old and new stacks are
enabled.
2. Build a behaviour inventory
Describe what the user expects, not just which files exist:
Shell behaviours: bar, menus, notifications, plugins, idle controls.
Themes, hooks, and peripheral scripts.
Package and file cleanup.
Validate after every tranche. A clean reload is evidence; copying files is not.
5. Review choices instead of imposing them
Before final cleanup, present independent findings with disposition choices.
Typical decisions are:
keep the old preferred terminal, adopt Quattro's default, or retain both;
keep the old editor and MIME ownership, adopt a new one, or split CLI/GUI;
preserve each coding agent's authored install, replace it with a mise-backed
install, or remove it;
port a custom Waybar module to a Quickshell plugin, drop it, or defer it;
port an old script/hook, retire it, or leave it inert until later.
The right result is the user's chosen canonical stack, not the smallest or most
stock stack.
6. Validate the final system
Match validation to the changed layer:
luac -p ~/.config/hypr/*.lua
hyprctl reload
hyprctl configerrors
hyprctl monitors
hyprctl getoption input:kb_layout
ghostty +validate-config # or the chosen terminal's validator
omarchy-shell shell ping
omarchy plugin validate ~/.config/omarchy/plugins/<id>
systemctl --user show-environment | grep '^PATH='
zsh -lic 'type -a <important-command>'# use the actual login shell
For a TUI that failed only during interactive startup, --version is not a
sufficient test. Use an ephemeral PTY smoke test with session saving disabled
when the program supports it, and remove any temporary output afterwards.
Do not launch social stacks, record audio, rearrange workspaces, or trigger
power actions merely to prove config structure. Ask before a disruptive visual
or behavioural test.
7. Clean only proved superseded state
Once the active replacement works:
remove inactive Hyprland .conf overrides, not unrelated daemon configs;
remove old Waybar/Walker/Mako files only when no current process loads them;
remove lazy wrappers only after the canonical executable is selected and
visible to both shell and GUI environments;
remove duplicate packages/config directories only after the user chooses a
canonical terminal/editor/agent;
run mise reshim after changing mise-managed tools and remove broken alias
symlinks left behind by uninstallers;
keep current user configuration and delete migration notes that no longer
affect behaviour only when the user wants the historical record gone.
Completion criteria
The preferred behaviours are represented in active Quattro user files.
Hyprland and Quickshell validate cleanly.
Interactive shells, GUI launchers, remote launchers, and service processes
resolve important programs to the intended installations.
No wrapper silently installs a second copy of an already-owned tool.
Custom scripts use current paths, commands, and dispatcher syntax.
Network and remote access survive a reboot.
Old packages and files remain only by deliberate user choice.