Upgrading dots-hyprland Without Losing My Customizations
I run end-4/dots-hyprland on top of CachyOS. It's gorgeous and it moves fast — too fast for me to keep up with manually. The last time I upgraded I lost a chunk of my keybinds and spent the rest of the afternoon rebuilding them from memory and git history. After that, I made ~/.config a git repo and tracked hypr/ so I could at least see what an upgrade actually changes.
Today I was 215 commits behind.
The plan
The dots-hyprland installer isn't git-aware. It uses rsync --delete against several config dirs under ~/.config/, so on every upgrade it'll happily wipe customizations in kitty, quickshell, fish, kdeglobals, and a long list of others. Anything inside the official hypr/custom/ extension point is safe — the installer uses install_dir__skip_existed for that one — but everything else is fair game.
My git wrapper only tracks hypr/, which means I'm protected exactly there and nowhere else. I haven't customized the rest, so that's fine. But the upgrade still needs to be reviewable, and I want a clean merge — not a 215-commit landslide that I have to grep through afterwards.
So: branch off master in the ~/.config repo, run the installer in a real terminal (sudo needs a TTY, Claude's bash subprocess doesn't have one), let Claude curate the diff into a single migration commit, then fast-forward master. If anything breaks I roll back the curation commit and keep the raw upstream snapshot to fix by hand.
The receipt
Without the diff and curation step, my overrides would have been gone — same as last time. Here's what it did:
- Upstream restructured
hyprland.conf— new variable system ($terminal,$browser, etc.) replacing inline launch scripts. Keybinds migrated accordingly. - My customizations preserved —
vrr=1re-enabled (upstream flipped it to 0), browser override migrated to the newcustom/variables.conf, stale keybind workarounds cleaned up, monitor/workspace configs untouched. - Bug found and fixed — the installer pulled in
fish-pure-promptwhich brokefish_titlefor starship users. Patched with a local override. - Commit graph — raw upstream snapshot first, then a single curation commit on top. One commit to revert if anything breaks.
The skill
I packaged the workflow as a Claude Code skill so I can run it again next time. Drop it at .claude/skills/dots-hyprland-upgrade/SKILL.md and /dots-hyprland-upgrade runs the playbook end-to-end.
The load-bearing safety check is at the top: if ~/.config (or at least ~/.config/hypr) isn't a git repo, the skill stops and explains exactly what the installer is going to overwrite, then offers to git init for you. If git tracking is present, it states clearly which subtree is reversible — because git only protects what it tracks, and people (me included) routinely overestimate that scope.
---
name: dots-hyprland-upgrade
version: 0.1.0
description: |
Safely upgrade end-4/dots-hyprland while preserving local customizations.
Creates a branch in ~/.config (if it's a git repo), runs ./setup install,
curates the diff into a clean commit, and merges back to main. Detects
and surfaces post-install regressions in fish, hyprlock, and shell theming.
allowed-tools: [Bash, Read, Write, Edit, Grep, Glob, AskUserQuestion]
---
# Step 1 — Pre-flight: detect git tracking on ~/.config
# This is the load-bearing safety check.
# Do not proceed past this step if the user has no git tracking
# and chooses not to add it.
CONFIG_GIT=""
if [ -d "$HOME/.config/.git" ]; then
CONFIG_GIT="$HOME/.config"
elif [ -d "$HOME/.config/hypr/.git" ]; then
CONFIG_GIT="$HOME/.config/hypr"
fi
Full skill on GitHub Gist.
What this is, and isn't
Nothing here is beyond git diff, grep, and a long afternoon. The point is I didn't have to spend that afternoon. The skill file means next time I don't have to re-derive any of it either — one slash command and it runs the same playbook again.