Files
DotFiles/CLAUDE.md
T
haylan 2227a23630 Document the confd/hyprpaper config rewrite traps and pin known versions
Both bugs fixed in the preceding commits came from assuming an older
config syntax was still valid. Recorded the gotcha directly on the
relevant Architecture bullets, plus a version table for every
dotfiles-managed package so future edits check the installed version
against upstream's current schema instead of repeating the mistake.
2026-07-07 23:27:34 +02:00

62 lines
7.4 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this repo is
Master dotfiles repo for a Hyprland desktop on Arch Linux. It has two halves:
- `src/` — a small interactive TypeScript CLI (install/update/remove) that manages packages and dotfiles.
- `packages/` — the actual dotfiles, laid out as [GNU Stow](https://www.gnu.org/software/stow/) packages (one top-level dir per app, mirroring `$HOME`, e.g. `packages/waybar/.config/waybar/config`).
The CLI is the only orchestration layer; it shells out to the real `stow`, `pacman`, and `yay` binaries rather than reimplementing symlinking or package management.
## Commands
```
npm install
npm start install # interactive: choose packages, installs them, backs up conflicting files, symlinks into $HOME
npm start update # pacman -Syu + yay -Syu, then re-links (stow --restow) any new files
npm start remove # removes symlinks (stow -D), restores the most recent backup (does NOT uninstall packages)
npx tsc --noEmit # typecheck
npm test # runs test/ via Node's built-in test runner (node:test) through tsx
./docker/test.sh "npm test" # run the full suite, including stow-dependent tests skipped on a bare host
```
There is no build step — `tsx` runs the TypeScript directly.
## Testing
`test/*.test.ts` uses Node's built-in `node:test` + `node:assert` (via `tsx --test`) — no test framework dependency was added. `test/gpu.test.ts` and `test/packages.test.ts` are pure unit tests. `test/stow.test.ts` exercises the real `stow` binary and real filesystem calls against a throwaway `$HOME` (a `mkdtempSync` dir, swapped in via `process.env.HOME` before `stow.ts` is dynamically imported, since `os.homedir()` reads `$HOME` and the module's path constants are computed at import time); each case is skipped when `stow` isn't installed, which is the case on a bare host — run `./docker/test.sh "npm test"` to execute those for real inside a container that has `stow`.
`docker/test.sh` (see `docker/Dockerfile`) spins up a disposable Arch container with `stow`/`nodejs`/`npm`/`git`/`sudo` preinstalled and the repo copied to a writable path inside it, so `npm install`, Stow symlinks, and backups never touch the host. Run it with no args for an interactive shell, or pass a command string to run non-interactively.
## Architecture
- `src/packages.ts` — the package model. Each entry has a `tier` (`required` installs with no prompt; `recommended` is offered via checkbox), an optional `group` for mutually-exclusive alternatives (e.g. `waybar` vs an alternative both having `group: "bar"`, only one gets picked via a select prompt — `bar` and `launcher` currently each have just one member but are kept as groups so alternatives can be added the same way), and an optional `dir` for when a package's Stow directory name needs to differ from the package name. `toDirs()` converts chosen package names to their Stow directory names.
- `src/gpu.ts``detectGpuVendors()` shells out to `lspci` and matches VGA/3D controller lines against NVIDIA/AMD/Intel keywords (word-boundary regexes — a naive `/amd|ati/i` used to false-positive on the "ati" inside "Corporation"); `driverPackagesFor()` maps detected vendors to their Arch driver packages. `install.ts` shows every detected vendor's packages in a pre-checked confirm prompt (installing all of them on hybrid/Optimus laptops), and if NVIDIA is kept, `stow.ts`'s `writeNvidiaEnvConfig()` writes `~/.config/hypr/confd/nvidia.lua` (machine-generated, not stowed) with the Wayland env vars Hyprland's NVIDIA docs recommend.
- `src/stow.ts` — all filesystem/process side effects live here: bootstrapping `yay` and oh-my-zsh/zinit if missing, installing packages via `yay`, enabling the `sddm` systemd service (`ensureSddmEnabled`), backing up real (non-symlink) files that would collide with a Stow target into `~/.dotfiles-backup/<timestamp>/`, and wrapping `stow`/`--restow`/`-D` (always invoked with an explicit `-d <PACKAGES_DIR>` so behavior doesn't depend on cwd). Also reads/writes `~/.config/dotfiles/state.json`, which records which package dirs were chosen at install time — `update` and `remove` act on this list rather than re-prompting.
- `src/install.ts`, `src/update.ts`, `src/remove.ts` — the three subcommands, each a thin sequence of calls into `stow.ts`, using `@clack/prompts` for interactive selection in `install.ts`.
- `src/cli.ts` — dispatches `install|update|remove` via Node's built-in `node:util.parseArgs`.
- `packages/hyprland/.config/hypr/hyprland.lua` — Hyprland's config, in its **current Lua format** (`hl.config`/`hl.bind`/`hl.monitor`/`hl.on`/`hl.env`), not the old `hyprland.conf` ini/hyprlang syntax deprecated since Hyprland 0.55. Easy detail to get wrong when editing — check `wiki.hypr.land` (or the raw `hyprland-wiki` GitHub markdown) for current syntax rather than assuming the old ini style. `require()` inside it resolves dots in the module string as directory separators (standard Lua `package.path` behavior) — that's why the machine-generated config dir is named `confd`, not `conf.d`: a literal dot in the directory name would make `require("conf.d.foo")` look for `conf/d/foo.lua` instead and silently fail (wrapped in `pcall`, so it fails with no visible error).
- `packages/hyprpaper/.config/hypr/hyprpaper.conf` — hyprpaper's config, in its **current hyprlang block format** (`wallpaper { monitor = ... path = ... fit_mode = ... }`), not the old `preload =` / `wallpaper = monitor,path` key=value syntax removed in hyprpaper 0.8.0. `preload` doesn't exist anymore at all. Verify against `src/config/ConfigManager.cpp` in the `hyprwm/hyprpaper` repo (`m_config.addSpecialConfigValue(...)` calls list every valid key) rather than assuming the old ini style — same trap as `hyprland.lua` above.
### Pinned reference versions (Arch, checked 2026-07-07)
These packages have had **breaking, from-scratch config rewrites** in living memory — don't assume a config snippet found online (or from an older revision of this repo) is still valid without checking the installed version against the package's current source/changelog:
| package | version | note |
|---|---|---|
| `hyprland` | 0.55.4 | Lua config (`hyprland.lua`) since 0.55; older configs use `hyprland.conf` hyprlang/ini syntax. |
| `hyprpaper` | 0.8.4 | Rewritten onto `hyprtoolkit` in 0.8.0 ("configs are broken and much simplified" — upstream's own words); new block-based `wallpaper { }` syntax, `preload` removed. |
Other managed packages (`waybar` 0.15.0, `mako` 1.11.0, `hyprlauncher` 0.1.6, `kitty` 0.47.4, `foot` 1.27.0, `zsh` 5.9.1, `xdg-desktop-portal-hyprland` 1.3.12, `sddm` 0.21.0, `stow` 2.4.1) are recorded here for reference, but haven't been confirmed to carry a similar breaking-rewrite risk — re-check versions with `pacman -Qi <pkg>` before trusting this table, since this is a rolling-release Arch machine and it will drift.
### Adding a new app
Add a directory under `packages/` mirroring its `$HOME` layout (e.g. `packages/foo/.config/foo/config`), then add a matching entry to `src/packages.ts` (`dir` if the Stow directory name differs from the package name, `group` if it's a mutually-exclusive alternative to another package).
### Backup/restore
Backups are stored under `~/.dotfiles-backup/<timestamp>/`, mirroring the relative path of whatever real file was moved aside before stowing. `remove` restores only the most recent backup automatically; restoring an older one is a manual `cp -a ~/.dotfiles-backup/<timestamp>/. ~/`.