Personal macOS configuration: system packages, GUI apps, shell setup, and tool configs for work and personal Macs.
Grimoire is intentionally macOS-only. It is built around nix-darwin, Homebrew, darwin-rebuild switch, and macOS desktop conventions; Linux support belongs in a separate tool, not behind conditionals here.
On a new machine, clone to ~/.grimoire and run the wrapper directly:
git clone git@github.com:LarsNorlander/grimoire.git ~/.grimoire
~/.grimoire/grimoire bootstrapThe wrapper offers to install Nix and Homebrew if they are missing. bootstrap prompts for a profile, applies runes, then applies rites. It is safe to re-run.
After bootstrap, use the narrower verbs for day-to-day work. Run grimoire --help and grimoire <verb> --help for the current flag reference.
- Runes (
runes/) are nix-darwin system configuration: packages, Homebrew apps, fonts, and macOS defaults. - Rites (
rites/<tool>/) build and link per-tool config into place. - Tome (
tome/) is gitignored build output. Managed symlinks point here, not directly at tracked source files. - Familiars (
familiars/) are ephemeral Nix shells for tools that should not persist on the machine. - Profiles (
workorpersonal) select the nix-darwin output and any profile-specific rite behavior. The current profile lives in~/.grimoire-profile.
grimoire bootstrapβ provision a fresh machine by applying runes, then rites.grimoire inscribeβ apply runes.grimoire cast [tool ...]β apply rites, rebuilding tome files and refreshing symlinks.grimoire diff [tool]β inspect drift between rite sources, tome files, and the manifest.grimoire accept <tool ...>β copy externally edited tome files back to rite sources where possible.grimoire updateβ fast-forward the checkout to its upstream; with local commits or uncommitted changes in the way, it reports them and leaves the reconciliation to git.grimoire profileβ show or change the current profile.grimoire summon <name>β enter an ephemeral familiar shell, or run one command in it with--.grimoire scribe [tool ...]β generate cheatsheet JSON from rites that document themselves.
A rite is a Python module, rites/<tool>/rite.py, whose rite(ctx) function registers operations with RiteContext:
copy()copies source files from the rite intotome/.write()generates files intotome/from Python builders.link()creates user-facing symlinks totome/.patch()owns selected keys inside a JSON file the machine otherwise owns. The rite holds a fragment; its leaf keys are merged into the live file in place, so this is the one operation that writes a real file instead of symlinking. Arrays are replaced whole, not merged.hook()performs necessary imperative setup.doc()registers cheatsheet data forgrimoire scribe.
tome/.manifest records each built file's hash, the operation that produced it, and the symlinks pointing at it, so a full cast can prune files a rite stopped managing along with their links. If a tool edits a managed file in tome/, cast skips it rather than overwriting it silently. Use diff to inspect the state, cast --force to rebuild from source, or accept to pull external edits back into copy()-managed rite sources. Generated write() files require manual reconciliation. For patch() files, drift and accept look only at the owned keys; the rest of the file is free to change.
Profile-specific rites use a header directive:
#!/usr/bin/env python3
# profile: workRites without a directive apply to every profile.
Claude Code instructions and ccstatusline are work-only. Zed settings are
generated from the shared rites/zed/settings.json base plus an optional profile
overlay; rites/zed/work.json enables Claude ACP only on work. Reconcile edits
to Zed's generated settings in those source files manually.
The work profile runs Docker through Lima rather than Docker Desktop. The lima rite manages the VM template at ~/.lima-templates/docker.yaml and creates the rootless docker instance on first cast, which downloads an Ubuntu image and takes a few minutes. The docker rite creates a lima-docker context pointed at the VM's socket and owns the keys in ~/.docker/config.json that select it and Homebrew's CLI plugins. The VM does not start at login; limactl start docker brings it up.
grimoire scribe writes schema-validated JSON for documented rites. Grimoire only produces data; rendering belongs to homepage. By default, sheets are written to homepage's content directory; override with --output or GRIMOIRE_SCRIBE_OUTPUT.
Generated sheets carry a generator marker, and grimoire only overwrites or prunes sheets it owns so hand-written sheets can share the same directory.
Secrets are managed through 1Password and its CLI (op). Nothing sensitive belongs in this repository.
This is a personal configuration repo. Contributions are not accepted, but you are welcome to fork it.
This repo is maintained with the help of Pi.