Migrate legacy Hyprland hyprlang config files (.conf, Hyprland ≤0.54) to the
new Lua config format (.lua, Hyprland ≥0.55).
Implements a three-phase semantic pipeline — Decompose → Sort → Reconstruct —
not line-by-line substitution. Every emitted hl.* call matches documented APIs
on the Hyprland wiki.
- Python 3.10+
git clone https://github.com/your-org/hypr-migrate.git
cd hypr-migrate
./hypr_migrate.py ~/.config/hypr/hyprland.confpip install hypr-migrate # once published
hypr-migrate ~/.config/hypr/hyprland.confNo special package is needed — just run python hypr_migrate.py directly.
Python 3.10+ ships with both distributions. For convenience, symlink it into
your PATH:
ln -s "$(pwd)/hypr_migrate.py" ~/.local/bin/hypr-migrate# Write to stdout (default)
hypr-migrate ~/.config/hypr/hyprland.conf
# Write to a specific file
hypr-migrate ~/.config/hypr/hyprland.conf --out ~/.config/hypr/hyprland.lua
# In-place: overwrite input path as .lua, backup original as .conf.bak
hypr-migrate ~/.config/hypr/hyprland.conf --in-place
# Show diff against existing .lua
hypr-migrate ~/.config/hypr/hyprland.conf --diff
# Dry-run: parse and report warnings but don't write anything
hypr-migrate ~/.config/hypr/hyprland.conf --dry-run
# Strict mode: exit non-zero if any manual review note is generated
hypr-migrate ~/.config/hypr/hyprland.conf --strict --out converted.lua
# Verbose: print phase-by-phase progress
hypr-migrate ~/.config/hypr/hyprland.conf --verbose| Code | Meaning |
|---|---|
0 |
Success, no warnings |
1 |
Success with MIGRATION_WARNINGs (check output) |
2 |
Success with MIGRATION_NOTEs requiring manual review |
3 |
Parse error in input file |
# Monitor setup
monitor = DP-1, 2560x1440@144, 0x0, 1
monitor = HDMI-A-1, 1920x1080@60, 2560x0, 1
# Variables
$terminal = kitty
$mainMod = SUPER
# Keybinds
bind = $mainMod, Return, exec, $terminal
bind = $mainMod, Q, killactive
bind = $mainMod, 1, workspace, 1
bind = $mainMod, 2, workspace, 2
bind = $mainMod, 3, workspace, 3
bind = $mainMod, 4, workspace, 4
bind = $mainMod, 5, workspace, 5
bind = $mainMod, 6, workspace, 6
bind = $mainMod, 7, workspace, 7
bind = $mainMod, 8, workspace, 8
bind = $mainMod, 9, workspace, 9
bind = $mainMod, 0, workspace, 10
# Window rules
windowrule = float, class:^(pavucontrol)$
windowrulev2 = opacity 0.9 override 0.5 override, class:^(kitty)$, title:^(dialog)$
# Workspace rules
workspace = 1, monitor:DP-1, default:true
workspace = 2, monitor:DP-1
# Startup
exec-once = waybar &
# Environment
env = XCURSOR_SIZE,24
# Colors
general {
col.active_border = 0xffcba6f7
col.inactive_border = 0xff45475a
}
decoration {
rounding = 10
drop_shadow = true
}-- hyprland.lua
-- Generated by hypr-migrate from: hyprland.conf
-- Hyprland >= 0.55 required — https://wiki.hypr.land/
--
-- MIGRATION SUMMARY:
-- Variables: 2 resolved
-- Config keys: 4 emitted
-- Monitors: 2
-- Keybinds: 13 total, 9 collapsed into loops
-- Window rules: 2 total, 0 merged
-- Workspace rules: 2
-- Exec commands: 1
-- Warnings: 0 ← search MIGRATION_WARNING
-- Review needed: 0 ← search MIGRATION_NOTE
--
-- IMPORTANT: Verify output against https://wiki.hypr.land/ before use
-- The wiki is the only authoritative reference for Lua syntax.
local terminal = "kitty"
local mainMod = "SUPER"
hl.env("XCURSOR_SIZE", "24")
hl.config({
general = {
col.active_border = "rgba(cba6f7ff)",
col.inactive_border = "rgba(45475aff)",
},
decoration = {
rounding = 10,
drop_shadow = true,
},
})
hl.monitor({ output = "DP-1", mode = "2560x1440", refresh = 144, position = "0x0" })
hl.monitor({ output = "HDMI-A-1", mode = "1920x1080", refresh = 60, position = "2560x0" })
hl.workspace_rule({ workspace = "1", monitor = "DP-1", default = true })
hl.workspace_rule({ workspace = "2", monitor = "DP-1" })
hl.window_rule({ match = { class = "^(pavucontrol)$" }, effect = "float" })
hl.window_rule({ match = { class = "^(kitty)$", title = "^(dialog)$" },
effect = "opacity 0.9 override 0.5 override" })
-- PATTERN: 10 sequential binds collapsed to loop
for i = 1, 10 do
hl.bind("SUPER, " .. (i % 10), hl.dsp.workspace({ i }))
end
hl.bind("SUPER, Return", function()
hl.exec_cmd("kitty")
end)
hl.bind("SUPER, Q", hl.dsp.killactive())
hl.on("hyprland.start", function()
hl.exec_cmd("waybar &")
end)-
Verify against the wiki. This tool is a best-effort migration assistant. The Hyprland wiki at https://wiki.hypr.land/ is the only authoritative reference for Lua config syntax. Always test your migrated configs before deploying.
-
Backups. Use
--in-placeto automatically create a.conf.bakbackup. The--outflag writes to a separate file and leaves the original unchanged. -
hl.dsp.exit()is not available in the Lua API. Binds using theexitdispatcher are annotated with a MIGRATION_NOTE directing you to useuwsm stopinstead. -
source = pathdirectives are converted to Luarequire()calls. Verify that the module path resolves correctly in your Lua setup. -
hl.layer_rule()is emitted forlayerruledirectives, mapping rules directly to effects.hl.animation()is emitted foranimationdirectives in the oldNAME, ENABLED, SPEED, CURVE, STYLEformat. -
wsbind has no direct Lua equivalent and is emitted as a commented MIGRATION_NOTE entry.
-
Sequential workspace binds (e.g.,
SUPER+1throughSUPER+0) are automatically collapsed intoforloops.
python -m unittest tests.test_hypr_migrate -vMIT