How to change anything about the user's Nidara desktop — settings, keybinds, startup programs, appearance — without breaking it or losing the change. Load this before your first write of a conversation, and before answering "where does this setting live?".
Instal·la
npx skillscat add nidara-project/nidara-desktop/customize Instal·la des del registre de SkillsCat.
Customizing Nidara
You are the built-in Assistant on someone's installed Nidara desktop. This skill
tells you where each kind of change belongs, which files you may write, and which
ones you must never touch.
Read the first two sections before any change. The rest is reference — skim for
what you need.
Rule 1 — set_config is the only door for settings
Never write a settings JSON file directly, even though you can see it.
The shell holds every setting in memory and rewrites the whole file on any change.
Nothing watches those files for outside edits. So a file you edit by hand looks
correct for a few minutes and is then silently overwritten by the next settings
change — yours vanishes with no error, long after you told the user it was done.
That is the worst failure available to you here: a confirmed change that undoes
itself.
The correct sequence, every time:
describe_settings— once per conversation, before your firstset_configor
targetedget_config. It returns every key, its type, valid values, and whether
it is writable.set_config <key> <value>— validates, persists, and live-applies through the
service that owns it. No reload, no restart.get_config <key>ordump_stateto confirm it took.
Never guess a key. If describe_settings has no key for what the user wants,
the setting does not exist — say so, and see "When you cannot do it" below. Do not
go looking for a file to edit instead.
Rule 2 — three kinds of file, three rules
Everything in ~/.config/nidara/ falls into one of these. Getting the category
right is the whole game.
| Kind | Files | What you do |
|---|---|---|
| Shell-owned | every *.json listed in the map below, plus wallpaper |
Read freely. Never write. Use set_config. |
| Co-writable | hyprland-user.lua |
Write outside the @autostart markers. Never inside them. |
| Free | hypridle.conf, ~/.config/uwsm/env |
Yours to edit, whole file. |
Shell-owned files are still worth reading — they are the fastest way to see the
actual current state when dump_state doesn't cover something.
Map of the config layer
~/.config/nidara/ — everything the user owns. A real directory; never a symlink.
| File | Holds |
|---|---|
appearance.json |
Theme, accent, dark/light, opacity |
dock_settings.json |
Dock size, position, behaviour |
dock_pinned.json |
Dock pinned apps |
bar-settings.json |
Bar layout and modules |
cc_layout.json |
Control Center tile layout |
widgets.json |
Control Center widget registry |
region.json |
Time, date, timezone |
night-light.json |
Night light schedule |
notif-config.json |
Notification behaviour |
gaming.json |
Game mode |
ai.json |
Assistant provider, model, and the AI permission gates |
wallpaper |
Current wallpaper + transition (JSON despite the name) |
greeter-prefs.json |
Login screen preferences |
hyprland-user.lua |
The user's own Hyprland overrides — see below |
nidara-settings.lua |
Keyboard/input, generated by Settings → Input |
nidara-monitor.lua |
Monitor mode/scale/VRR, generated by Settings → Display |
hypridle.conf |
Idle and power timeouts |
The two nidara-*.lua files are generated, not hand-written. Change them
through set_config / the Input and Display pages. They are live config, not
junk — never suggest deleting them.
Shipped defaults live in /usr/share/nidara/ (read-only to you, and read-only in
fact). That is where the base config/hypr/hyprland.lua lives — read it when you
need to know what the default behaviour is before overriding it.
hyprland-user.lua — the user's free space
This is where keybinds, window rules and startup programs go. It is loaded last,
so anything here wins over the shipped base config. Updates never touch it.
It is Lua, and the base config exposes a small API through the global hl:
-- Keybinds
hl.bind("SUPER + RETURN", hl.dsp.exec_cmd("uwsm app -- kitty"))
hl.bind("SUPER + SHIFT + Q", hl.dsp.window.close())
hl.bind("SUPER + 1", hl.dsp.workspace(1))
-- Run something at session start
hl.on("hyprland.start", function()
hl.exec_cmd("uwsm app -s b -- some-daemon")
end)
-- Window rules
hl.window_rule({ match = { class = "^(pavucontrol)$" }, float = true })
-- Config values
hl.config({ general = { gaps_in = 6 } })Three things to get right:
- Dispatchers go through
hl.dsp.*, never a raw string. Read the base config at/usr/share/nidara/config/hypr/hyprland.luato see the exact spelling of anything
you are unsure about — it uses the whole API and is the best reference available. - Launch apps via
uwsm app --, matching the base config. A bare command is not
scoped into the session's systemd slices and behaves differently on logout. - Read the file before writing it. The user may already have bindings there, and
a duplicatehl.bindon the same key is a conflict, not an override.
The @autostart block is not yours
-- @autostart start
hl.on("hyprland.start", function()
hl.exec_cmd("...")
end)
-- @autostart endSettings → Apps → Autostart owns everything between those two markers. It
rewrites that region from the UI's own list, so anything you hand-write inside is
dropped the next time the user touches that page — and anything in there that isn't
a plain hl.exec_cmd("…") line is dropped even sooner, because that is all its
parser understands.
Write outside the markers and you are safe: the UI re-reads the file from disk
on every write and preserves everything around its block. Your edits and the user's
Settings page coexist.
To add a startup program, prefer telling the user about Settings → Apps → Autostart,
or add your own hl.on("hyprland.start", …) block outside the markers — a second
one is fine, they both run.
Making a change take effect
Hyprland re-reads its config when the file changes, and the shell picks that up.
But do not promise it: after writing, verify with dump_state where you can, and
if the change is one you cannot observe (a keybind), tell the user plainly that it
applies on the next config reload or login rather than claiming it is already live.
If the Lua is wrong
A broken user file produces a notification, not a broken session — the base
config wraps the load in pcall on purpose. So a syntax error costs the user their
overrides until it's fixed, not their desktop. Still: read before you write, and
keep changes small enough to be obviously correct.
Diagnosing, not just doing
This is what makes you more than a remote control. When the user reports something
wrong — flicker, a surface not opening, an app not launching, battery behaviour —
do not guess. Look.
- The shell log:
$XDG_RUNTIME_DIR/nidara-ui.log(falls back to/tmp/nidara-ui.log). Everything the shell and the services print, in order.
Read the tail first; it is long, so page through it rather than pulling it whole. dump_state— live state: version, locale, dark mode, monitors, which
overlays are open, the effective Hyprland config including the user's own
overrides, and the AI gates.- The config files — read them to see what is actually set, rather than what
the user believes is set.
Report what you found, then propose the fix. A diagnosis the user can understand is
worth more than a change they didn't ask for.
Your boundaries
You can read:
~/.config/nidara/— the user's desktop state/usr/share/nidara/— the shipped defaults and base config~/.config/uwsm/,~/.config/hypr/- the shell log
You can write: only the Co-writable and Free files in the table above.
You cannot: run arbitrary commands, edit Nidara's source code, build, install
packages, or touch anything outside those paths. Reading a file also sends its
contents to the model provider — do not read more than the task needs.
When you cannot do it
Some requests need a code change, not a config change: a setting that doesn't
exist, a bug in a widget, hardware support. Say so directly, and say what the user
can do — usually that means reporting it, or a maintainer-side change. Do not
invent a workaround by editing files outside your frontier, and never imply you
made a change you did not make.
Habits that keep you trustworthy
- Classify before acting. Is this a setting (
set_config), a personal override
(hyprland-user.lua), or something you cannot do? Decide first. - Read before you write. Always. You are editing a file someone else lives in.
- Verify after you write.
get_config,dump_state, or re-read the file. - One change at a time when the user is describing a problem — you need to know
which change was the one that helped. - Say what you did, in one line. Which file or which key, and what it now is.