mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-02 11:32:42 +00:00
docs: add comprehensive documentation for AI agents, chezmoi workflow, shell setup, desktop configuration, terminal multiplexers, and developer tools
This commit is contained in:
1 parent
41437d4abd
commit
1da988c876
47 files changed
+5485
No files matched your search
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Config and roles
|
||||
description: The .chezmoi.toml.tmpl config template, its three prompts, symlink mode, age encryption, and where the resulting data is used.
|
||||
---
|
||||
|
||||
`.chezmoi.toml.tmpl` is rendered once by `chezmoi init` into `~/.config/chezmoi/chezmoi.toml`. It asks three questions, turns on symlink mode and age encryption, and stores the answers as template data for every other `.tmpl` file.
|
||||
|
||||
## Source
|
||||
|
||||
```toml .chezmoi.toml.tmpl
|
||||
{{- $name := promptStringOnce . "name" "Full name" "Prad Nukala" -}}
|
||||
{{- $email := promptStringOnce . "email" "Email" "prad@sonr.io" -}}
|
||||
{{- $roleDefault := "desktop" -}}
|
||||
{{- if eq .chezmoi.os "darwin" -}}{{- $roleDefault = "mac" -}}{{- end -}}
|
||||
{{- $role := promptChoiceOnce . "role" "Machine role" (list "desktop" "laptop" "mac" "server") $roleDefault -}}
|
||||
|
||||
mode = "symlink"
|
||||
encryption = "age"
|
||||
|
||||
[age]
|
||||
identity = "~/.config/chezmoi/key.txt"
|
||||
recipient = "age19tdqgavaekhvgqsd75n8a09vvshen2ykzy8kw6tenczz73449anqc3l7a6"
|
||||
useBuiltin = true
|
||||
|
||||
[data]
|
||||
name = {{ $name | quote }}
|
||||
email = {{ $email | quote }}
|
||||
role = {{ $role | quote }}
|
||||
```
|
||||
|
||||
## Prompts
|
||||
|
||||
| Key | Prompt | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| `name` | `Full name` | `promptStringOnce` | `Prad Nukala` |
|
||||
| `email` | `Email` | `promptStringOnce` | `prad@sonr.io` |
|
||||
| `role` | `Machine role` | `promptChoiceOnce` from `desktop`, `laptop`, `mac`, `server` | `mac` when `.chezmoi.os` is `darwin`, otherwise `desktop` |
|
||||
|
||||
The `*Once` functions only prompt when the key is missing from the existing config, so re-running `chezmoi init` keeps earlier answers. `chezmoi init --prompt` forces all three prompts again. Changing the role changes the rendered ignore file and package script, so the next `chezmoi apply` adds or drops the desktop layer and re-runs the package install.
|
||||
|
||||
### Machine roles
|
||||
|
||||
| Role | Intended platform | Linux desktop layer | Desktop packages (`niri`, `xdg-user-dirs`, `noctalia-shell`) |
|
||||
| --- | --- | --- | --- |
|
||||
| `desktop` | Arch Linux | Applied | Installed |
|
||||
| `laptop` | Arch Linux | Applied | Installed |
|
||||
| `mac` | macOS | Skipped | n/a (Homebrew list only) |
|
||||
| `server` | Arch Linux, headless | Skipped | Skipped |
|
||||
|
||||
`desktop` and `laptop` behave identically in the current templates; nothing branches on one without the other. The desktop layer is also skipped whenever the OS is not Linux, whatever the role. See [the ignore rules](/chezmoi/externals-and-ignore#chezmoiignore) for the exact list of gated paths.
|
||||
|
||||
## Mode
|
||||
|
||||
`mode = "symlink"` makes `chezmoi apply` link plain files in `$HOME` back to the source tree. Templates, `private_`, `executable_`, and encrypted files are still written as real files. The [overview](/chezmoi#symlink-mode) covers the editing consequences.
|
||||
|
||||
## Age encryption
|
||||
|
||||
| Setting | Value | Effect |
|
||||
| --- | --- | --- |
|
||||
| `encryption` | `"age"` | `chezmoi add --encrypt` and `encrypted_` source files use age. |
|
||||
| `age.identity` | `~/.config/chezmoi/key.txt` | Private key used to decrypt. Distributed by hand between machines, never committed. |
|
||||
| `age.recipient` | `age19tdqgavaekhvgqsd75n8a09vvshen2ykzy8kw6tenczz73449anqc3l7a6` | Public key files are encrypted to. Safe to publish. |
|
||||
| `age.useBuiltin` | `true` | Uses chezmoi's built-in age implementation; the `age` binary is not required. |
|
||||
|
||||
No source file currently carries the `encrypted_` attribute, so the key is only exercised when a secret is added with `chezmoi add --encrypt`. The README still asks for the key to be copied into place before the first apply:
|
||||
|
||||
```sh
|
||||
mkdir -p ~/.config/chezmoi
|
||||
scp <existing-machine>:.config/chezmoi/key.txt ~/.config/chezmoi/key.txt
|
||||
chmod 600 ~/.config/chezmoi/key.txt
|
||||
```
|
||||
|
||||
## Template data
|
||||
|
||||
The `[data]` table exposes `.name`, `.email`, and `.role` to every template. chezmoi also merges `.chezmoidata/*.yaml` into the same namespace (`.packages`, `.aliases`; see [Packages and aliases](/chezmoi/data)) and provides built-ins such as `.chezmoi.os`.
|
||||
|
||||
| Variable | Consumed by | Use |
|
||||
| --- | --- | --- |
|
||||
| `.name` | `dot_gitconfig.tmpl` | `[user] name` in `~/.gitconfig` |
|
||||
| `.email` | `dot_gitconfig.tmpl` | `[user] email` in `~/.gitconfig` |
|
||||
| `.role` | `.chezmoiignore.tmpl` | Skips the Linux desktop layer unless role is `desktop` or `laptop` |
|
||||
| `.role` | `.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl` | Adds the `arch.desktop` pacman and AUR packages for `desktop`/`laptop` |
|
||||
| `.chezmoi.os` | `.chezmoi.toml.tmpl` | Role default (`mac` on darwin) |
|
||||
| `.chezmoi.os` | `.chezmoiignore.tmpl` | Desktop layer requires `linux` |
|
||||
| `.chezmoi.os` | `run_onchange_before_10-packages.sh.tmpl` | pacman/paru on `linux`, Homebrew on `darwin` |
|
||||
| `.chezmoi.os` | `run_once_after_30-user-services.sh.tmpl` | systemd user units on `linux` only |
|
||||
| `.chezmoi.os` | `run_onchange_after_40-post.sh.tmpl` | `fc-cache` on `linux` only |
|
||||
| `.chezmoi.os` | `dot_config/yazi/yazi.toml.tmpl` | Yazi's `open` opener runs `open` on darwin, `xdg-open` elsewhere |
|
||||
|
||||
```ini dot_gitconfig.tmpl
|
||||
[user]
|
||||
email = {{ .email }}
|
||||
name = {{ .name }}
|
||||
```
|
||||
|
||||
The rest of `~/.gitconfig` is covered under [Tools](/tools).
|
||||
@@ -0,0 +1,189 @@
|
||||
---
|
||||
title: Packages and aliases
|
||||
description: The .chezmoidata YAML files that declare every installed package and every shared shell alias, and how they render.
|
||||
---
|
||||
|
||||
Files in `.chezmoidata/` are merged into chezmoi's template data on every run. Nothing in the directory is written to `$HOME`; the data only reaches the machine through templates and scripts.
|
||||
|
||||
| File | Data key | Consumed by |
|
||||
| --- | --- | --- |
|
||||
| `.chezmoidata/packages.yaml` | `.packages` | [`run_onchange_before_10-packages.sh.tmpl`](/chezmoi/scripts#10-packages), [`run_onchange_after_20-gh-extensions.sh.tmpl`](/chezmoi/scripts#20-gh-extensions) |
|
||||
| `.chezmoidata/aliases.yaml` | `.aliases` | `dot_config/shell/aliases.sh.tmpl` (bash and zsh) |
|
||||
|
||||
## packages.yaml [#packagesyaml]
|
||||
|
||||
One manifest per platform, plus a `shared` block. Any edit to the file changes the hash embedded in the package script, so the next `chezmoi apply` re-runs the install. Installs are idempotent (`pacman --needed`, `paru --needed`, `brew install` on already-installed formulae).
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Arch">
|
||||
|
||||
`packages.arch.pacman`, installed on every Linux machine with `pacman -S --needed --noconfirm`:
|
||||
|
||||
| Group | Packages |
|
||||
| --- | --- |
|
||||
| Shell and editors | `fish`, `neovim`, `tmux`, `micro` |
|
||||
| Terminal | `kitty` |
|
||||
| TUI tools | `yazi`, `lazygit`, `lazydocker`, `btop`, `fastfetch` |
|
||||
| VCS and forge | `git`, `github-cli`, `git-delta`, `diffnav` |
|
||||
| Search and files | `fzf`, `fd`, `ripgrep`, `zoxide`, `eza`, `bat`, `jq`, `duf` |
|
||||
| Media and preview (yazi deps) | `ffmpeg`, `ffmpegthumbnailer`, `poppler`, `resvg`, `imagemagick`, `7zip`, `unzip` |
|
||||
| Misc CLI | `gum`, `glow`, `atuin`, `wget`, `starship`, `zsh-syntax-highlighting` |
|
||||
| Fonts | `ttf-victor-mono-nerd` |
|
||||
|
||||
`packages.arch.aur`, installed with `paru -S --needed --noconfirm` on every Linux machine: `topgrade`.
|
||||
|
||||
`packages.arch.desktop`, added only for roles `desktop` and `laptop`:
|
||||
|
||||
| Source | Packages |
|
||||
| --- | --- |
|
||||
| pacman | `niri`, `xdg-user-dirs` |
|
||||
| AUR | `noctalia-shell` |
|
||||
|
||||
</Tab>
|
||||
<Tab title="macOS">
|
||||
|
||||
`packages.darwin.brew`, installed with `brew install`:
|
||||
|
||||
`fish`, `neovim`, `tmux`, `yazi`, `lazygit`, `lazydocker`, `btop`, `fastfetch`, `git`, `gh`, `git-delta`, `fzf`, `fd`, `ripgrep`, `zoxide`, `eza`, `bat`, `jq`, `duf`, `ffmpeg`, `ffmpegthumbnailer`, `poppler`, `resvg`, `imagemagick`, `sevenzip`, `gum`, `glow`, `atuin`, `starship`, `zsh-syntax-highlighting`, `topgrade`
|
||||
|
||||
`packages.darwin.cask`, installed with `brew install --cask`: `kitty`, `font-victor-mono-nerd-font`.
|
||||
|
||||
Compared with Arch, macOS has no `micro`, `diffnav`, `unzip`, or `wget`, and gets `topgrade` from Homebrew instead of the AUR. The role does not change the macOS list.
|
||||
|
||||
</Tab>
|
||||
<Tab title="Shared">
|
||||
|
||||
`packages.shared.gh_extensions`, installed by the gh-extensions script on every OS:
|
||||
|
||||
| Extension | Purpose |
|
||||
| --- | --- |
|
||||
| `davidraviv/gh-clean-branches` | `gh clean-branches`: delete local branches whose upstream is gone |
|
||||
| `prdlk/gh-commit` | `gh commit` |
|
||||
| `dlvhdr/gh-dash` | `gh dash`: PR and issue dashboard |
|
||||
| `meiji163/gh-notify` | `gh notify`: notifications in the terminal |
|
||||
|
||||
`packages.shared.go` is an empty list. `packages.shared.cargo` lists `suvadu`. No script in the repo reads either key; they document intent only, and cargo installs are kept current by topgrade's `[cargo]` step.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
:::note
|
||||
AUR packages need `paru`. Without it the package script prints a warning listing the skipped AUR packages and continues.
|
||||
:::
|
||||
|
||||
## aliases.yaml [#aliasesyaml]
|
||||
|
||||
The single source for aliases shared across shells. Each entry has a `section` and a list of `{ name, cmd }` items.
|
||||
|
||||
### Editors / file managers
|
||||
|
||||
| Alias | Command |
|
||||
| --- | --- |
|
||||
| `e` | `nvim` |
|
||||
| `e.d` | `cd ~/.config/dotstate/storage/Rogz/.config && yazi` |
|
||||
| `e.f` | `cd ~/.config/fish && yazi` |
|
||||
| `e.k` | `yazi ~/.config/kitty` |
|
||||
| `e.n` | `yazi ~/.config/niri` |
|
||||
| `e.v` | `yazi ~/.config/nvim/lua` |
|
||||
| `e.y` | `yazi ~/.config/yazi` |
|
||||
| `e.t` | `nvim ~/.config/tmux/tmux.conf` |
|
||||
| `hx` | `helix` |
|
||||
|
||||
### Quick directory jumps
|
||||
|
||||
| Alias | Command |
|
||||
| --- | --- |
|
||||
| `cdc` | `cd ~/.config && yazi` |
|
||||
| `cdd` | `cd ~/Developer && yazi` |
|
||||
| `cdk` | `cd ~/Documents/Knowledge && obsidian` |
|
||||
| `cdl` | `cd ~/.local && yazi` |
|
||||
| `cds` | `cd ~/.local/share/services && ls` |
|
||||
| `cdw` | `cd ~/Downloads && yazi` |
|
||||
|
||||
### Tools
|
||||
|
||||
| Alias | Command |
|
||||
| --- | --- |
|
||||
| `dd` | `lazydocker` |
|
||||
| `gg` | `lazygit` |
|
||||
| `ff` | `exec fish` |
|
||||
| `npx` | `bunx` |
|
||||
| `sm` | `sudo micro` |
|
||||
| `sd` | `ssh prad@<lan-host>` |
|
||||
| `oc` | `opencode` |
|
||||
| `dss` | `dotstate sync` |
|
||||
| `fabric` | `fabric-ai` |
|
||||
| `bk` | `bead-kanban` |
|
||||
| `sua` | `shelly upgrade-all -n` |
|
||||
| `lc` | `leetcode` |
|
||||
| `tk` | `task` |
|
||||
|
||||
`dd` shadows coreutils `dd` in interactive shells.
|
||||
|
||||
## How aliases render
|
||||
|
||||
### bash and zsh
|
||||
|
||||
`dot_config/shell/aliases.sh.tmpl` renders to `~/.config/shell/aliases.sh`. Because it is a template it is written as a real file, not a symlink; edit the YAML and run `chezmoi apply` to regenerate it.
|
||||
|
||||
```sh dot_config/shell/aliases.sh.tmpl
|
||||
{{- range .aliases }}
|
||||
|
||||
# {{ .section }}
|
||||
{{- range .items }}
|
||||
alias {{ .name }}='{{ .cmd }}'
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
```
|
||||
|
||||
After the generated block the file adds shell-specific pieces that are not in the YAML:
|
||||
|
||||
| Name | Definition |
|
||||
| --- | --- |
|
||||
| `cpy` / `pst` | `wl-copy` / `wl-paste` when `wl-copy` exists, else `pbcopy` / `pbpaste` when `pbcopy` exists |
|
||||
| `q` | Function: if `$YAZI_ID` is set and `ya` exists, runs `ya emit quit` so the parent yazi also exits, then `exit` |
|
||||
| `celar`, `claer` | Typo guards for `clear` |
|
||||
| `ls` | `eza --icons`, when `eza` is installed |
|
||||
|
||||
`~/.bashrc` and `~/.zshrc` source it for interactive shells:
|
||||
|
||||
```sh dot_bashrc
|
||||
[ -f "$HOME/.config/shell/aliases.sh" ] && . "$HOME/.config/shell/aliases.sh"
|
||||
```
|
||||
|
||||
See [bash and zsh](/shell) for the rest of those files.
|
||||
|
||||
### fish
|
||||
|
||||
`dot_config/fish/conf.d/20-aliases.fish` is not a template. It is a hand-maintained plain file (symlinked into `~/.config/fish/conf.d/`) whose header says it is generated from the YAML; the YAML header likewise names a `20-aliases.fish.tmpl` that does not exist. The fish file has drifted from `aliases.yaml`:
|
||||
|
||||
| Alias | aliases.yaml (bash/zsh) | fish |
|
||||
| --- | --- | --- |
|
||||
| `e.d` | `cd ~/.config/dotstate/storage/Rogz/.config && yazi` | not defined |
|
||||
| `e.f` | `cd ~/.config/fish && yazi` | `cd ~/.local/share/chezmoi/dot_config/fish && yazi` |
|
||||
| `e.h` | not defined | `cd ~/.local/share/chezmoi/dot_config/hypr && yazi` |
|
||||
| `e.k` | `yazi ~/.config/kitty` | `yazi ~/.local/share/chezmoi/dot_config/kitty` |
|
||||
| `e.n` | `yazi ~/.config/niri` | `yazi ~/.local/share/chezmoi/dot_config/niri` |
|
||||
| `e.y` | `yazi ~/.config/yazi` | `yazi ~/.local/share/chezmoi/dot_config/yazi` |
|
||||
| `e.t` | `nvim ~/.config/tmux/tmux.conf` | `nvim ~/.local/share/chezmoi/dot_config/tmux/tmux.conf` |
|
||||
| `cdd` | `cd ~/Developer && yazi` | not defined |
|
||||
| `,,` | not defined | `cd ~/.local/share/chezmoi && yazi` |
|
||||
| `..` | not defined | `cd ..` |
|
||||
|
||||
fish-only Bun aliases:
|
||||
|
||||
| Alias | Command |
|
||||
| --- | --- |
|
||||
| `br` | `bun run` |
|
||||
| `br.d` | `bun run dev` |
|
||||
| `br.b` | `bun run build` |
|
||||
| `br.t` | `bun run test` |
|
||||
| `bi` | `bun install` |
|
||||
| `bg` | `bun install -g` (shadows fish's `bg` job-control builtin) |
|
||||
| `bx` | `bunx` |
|
||||
|
||||
Every other YAML alias (`e`, `e.v`, `hx`, `cdc`, `cdk`, `cdl`, `cds`, `cdw`, and the whole Tools section) is defined identically in fish. The file ends with the same extras as the sh version: `cpy`/`pst` clipboard detection, the `q` function, and, in interactive sessions only, `celar`/`claer` and `ls` → `eza --icons`. `clear` itself is an autoloaded function (`functions/clear.fish`), which is why it is not aliased. See [fish](/shell/fish).
|
||||
|
||||
:::tip
|
||||
To add an alias for every shell, add it to `aliases.yaml` for bash/zsh and to `20-aliases.fish` by hand for fish.
|
||||
:::
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
title: Externals and ignore rules
|
||||
description: The git repos chezmoi clones into $HOME and every rule in .chezmoiignore.tmpl, including the role gate for the Linux desktop layer.
|
||||
---
|
||||
|
||||
## .chezmoiexternal.toml [#externals]
|
||||
|
||||
Externals are content chezmoi fetches instead of storing in the repo. Both entries are `git-repo` externals: chezmoi runs `git clone` when the target directory is missing. Because they are real git checkouts, they are never symlinked into the source tree.
|
||||
|
||||
```toml .chezmoiexternal.toml
|
||||
[".config/nvim"]
|
||||
type = "git-repo"
|
||||
url = "https://github.com/prdlk/nvim.git"
|
||||
[".config/nvim".pull]
|
||||
args = ["--ff-only"]
|
||||
|
||||
[".config/bat/syntaxes/sublime-purescript-syntax"]
|
||||
type = "git-repo"
|
||||
url = "https://github.com/tellnobody1/sublime-purescript-syntax.git"
|
||||
[".config/bat/syntaxes/sublime-purescript-syntax".pull]
|
||||
args = ["--ff-only"]
|
||||
```
|
||||
|
||||
| Target | Repo | Used by |
|
||||
| --- | --- | --- |
|
||||
| `~/.config/nvim` | [prdlk/nvim](https://github.com/prdlk/nvim) | Neovim config, kept in its own repo |
|
||||
| `~/.config/bat/syntaxes/sublime-purescript-syntax` | [tellnobody1/sublime-purescript-syntax](https://github.com/tellnobody1/sublime-purescript-syntax) | PureScript syntax for bat, compiled into bat's cache by the [bat cache scripts](/chezmoi/scripts#build-bat-cache) |
|
||||
|
||||
### No refreshPeriod
|
||||
|
||||
Neither entry sets `refreshPeriod`, so it stays at chezmoi's default of never. chezmoi clones each repo on a fresh machine and otherwise leaves it alone: `chezmoi apply` and `chezmoi update` do not pull. The local nvim checkout always wins over the remote, so in-progress Neovim edits and unpushed commits are never disturbed by an apply.
|
||||
|
||||
To update explicitly, run `chezmoi apply --refresh-externals` (`-R`), which pulls with `git pull --ff-only`. `--ff-only` makes the pull fail instead of creating a merge commit when the local checkout has diverged.
|
||||
|
||||
## .chezmoiignore.tmpl [#chezmoiignore]
|
||||
|
||||
Patterns match target paths (relative to `$HOME`). Matching entries are neither written nor removed. The file is a template, so the desktop block appears only on machines where it applies. Source entries that start with `.` (`.github/`, `.claude/`, `.crush/`, `.gitignore`) are ignored by chezmoi without needing a rule.
|
||||
|
||||
### Repo docs
|
||||
|
||||
```txt
|
||||
MIGRATION-NOTES.md
|
||||
README.md
|
||||
CLAUDE.md
|
||||
AGENTS.md
|
||||
docs
|
||||
.blume
|
||||
node_modules
|
||||
blume.config.ts
|
||||
package.json
|
||||
bun.lock
|
||||
public
|
||||
dist
|
||||
```
|
||||
|
||||
Files at the source root that would otherwise land in `$HOME` as `~/README.md`, `~/docs`, `~/package.json`, and so on. This covers the repo README and agent instruction files, plus this Blume docs site and its build output.
|
||||
|
||||
### Runtime and plugin state inside managed trees
|
||||
|
||||
| Pattern | Why |
|
||||
| --- | --- |
|
||||
| `.config/lazygit/.omc/**` | Tool state written inside the lazygit config dir |
|
||||
| `.config/yazi/plugins/**` | Installed by `ya pkg install` from `package.toml` ([script](/chezmoi/scripts#install-yazi-plugins)) |
|
||||
| `.config/tmux/plugins/**` | tpm and the plugins it installs ([40-post](/chezmoi/scripts#40-post)) |
|
||||
| `.config/fish/fish_variables` | fish universal variables, rewritten by fish at runtime |
|
||||
| `.config/nvim/lazy-lock.json` | lazy.nvim lockfile inside the nvim external |
|
||||
| `.config/nvim/.git`, `.config/nvim/.git/**` | The external's git metadata |
|
||||
| `.config/nvim/.omc/**`, `.config/nvim/.crush/**`, `.config/nvim/.understand-anything/**` | Agent tool state inside the nvim checkout |
|
||||
|
||||
`.gitignore` covers the source side of the same problem: `dot_config/lazygit/dot_omc`, `dot_config/tmux/plugins`, and `__pycache__/` never get committed even though symlink mode lets tools write into the source tree.
|
||||
|
||||
### mise
|
||||
|
||||
```txt
|
||||
.config/mise
|
||||
.config/mise/**
|
||||
```
|
||||
|
||||
mise tool versions stay local to each machine and are not managed.
|
||||
|
||||
### Linux desktop layer (role-gated)
|
||||
|
||||
```txt .chezmoiignore.tmpl
|
||||
{{ if or (ne .chezmoi.os "linux") (and (ne .role "desktop") (ne .role "laptop")) }}
|
||||
.config/niri
|
||||
.config/niri/**
|
||||
.config/noctalia
|
||||
.config/noctalia/**
|
||||
.config/niri-dynamic-workspaces
|
||||
.config/niri-dynamic-workspaces/**
|
||||
.config/fontconfig
|
||||
.config/fontconfig/**
|
||||
.config/mimeapps.list
|
||||
.config/systemd
|
||||
.config/systemd/**
|
||||
.local/bin/niri-launch
|
||||
.config/fish/auto-Niri.fish
|
||||
.local/bin/cp-appid
|
||||
.config/tmux/niri-title.sh
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
The block is emitted, and these paths skipped, unless the OS is `linux` and the role is `desktop` or `laptop`. `mac` and `server` machines, and any non-Linux machine regardless of role, never receive the niri compositor config (`dot_config/niri/`), fontconfig (`dot_config/fontconfig/fonts.conf`), default-app associations (`dot_config/mimeapps.list`), the fish auto-start hook (`dot_config/fish/auto-Niri.fish`), the tmux window-title helper, or the `niri-launch` and `cp-appid` scripts. The `noctalia`, `niri-dynamic-workspaces`, and `systemd` rules match nothing in the current source tree; they only take effect if those configs are added later.
|
||||
|
||||
| Machine | Desktop layer |
|
||||
| --- | --- |
|
||||
| Linux, `desktop` or `laptop` | Applied |
|
||||
| Linux, `server` or `mac` | Ignored |
|
||||
| macOS or other OS, any role | Ignored |
|
||||
|
||||
See [Desktop](/desktop/niri) for what the layer contains. Gaps in the gate: the block lists `niri-launch` but not its siblings (`niri-launch-ai`, `niri-launch-browser`, `niri-launch-terminal`, `niri-launch-webapp`), and the Hyprland and waybar configs (`dot_config/hypr/`, `dot_config/waybar/`) are not gated at all, so those apply on every role. `dot_config/niri/scripts/` is covered by `.config/niri/**`.
|
||||
|
||||
### Secrets in runtime state
|
||||
|
||||
```txt
|
||||
.config/noctalia/plugins
|
||||
.config/noctalia/plugins/**
|
||||
```
|
||||
|
||||
noctalia writes plugin state containing embedded tokens here. These paths are ignored on every machine so tokens are never pulled into the repo.
|
||||
|
||||
### Python byte-cache
|
||||
|
||||
```txt
|
||||
.config/kitty/__pycache__
|
||||
.config/kitty/__pycache__/**
|
||||
```
|
||||
|
||||
kitty imports `tab_bar.py` and the other Python helpers in `~/.config/kitty`, and Python writes `__pycache__` next to them. In symlink mode that directory would otherwise appear in the source tree.
|
||||
@@ -0,0 +1,151 @@
|
||||
---
|
||||
title: How it works
|
||||
description: Source layout, naming conventions, symlink mode, and the day-to-day chezmoi workflow for this repo.
|
||||
sidebar:
|
||||
label: Overview
|
||||
icon: settings
|
||||
---
|
||||
|
||||
The repo is a [chezmoi](https://chezmoi.io) source directory. It lives at `~/.local/share/chezmoi` and its remote is [prdlk/dotfiles](https://github.com/prdlk/dotfiles). `chezmoi apply` renders it into `$HOME`. The [Installation](/installation) page covers first-time setup.
|
||||
|
||||
## Pages in this section
|
||||
|
||||
| Page | Covers |
|
||||
| --- | --- |
|
||||
| [Config and roles](/chezmoi/config) | `.chezmoi.toml.tmpl`: prompts, machine role, symlink mode, age encryption, template data |
|
||||
| [Packages and aliases](/chezmoi/data) | `.chezmoidata/packages.yaml` and `.chezmoidata/aliases.yaml`, and how they render |
|
||||
| [Apply scripts](/chezmoi/scripts) | Every `run_*` script: when it runs, what triggers it, what it does per OS and role |
|
||||
| [Externals and ignore rules](/chezmoi/externals-and-ignore) | `.chezmoiexternal.toml` git clones and every `.chezmoiignore.tmpl` rule |
|
||||
|
||||
## Source layout
|
||||
|
||||
<FileTree>
|
||||
|
||||
- .chezmoi.toml.tmpl
|
||||
- .chezmoidata/
|
||||
- aliases.yaml
|
||||
- packages.yaml
|
||||
- .chezmoiexternal.toml
|
||||
- .chezmoiignore.tmpl
|
||||
- .chezmoiscripts/
|
||||
- run_onchange_before_10-packages.sh.tmpl
|
||||
- run_onchange_after_20-gh-extensions.sh.tmpl
|
||||
- run_once_after_30-user-services.sh.tmpl
|
||||
- run_onchange_after_40-post.sh.tmpl
|
||||
- run_onchange_after_build-bat-cache.sh.tmpl
|
||||
- run_onchange_after_install-yazi-plugins.sh.tmpl
|
||||
- dot_bashrc
|
||||
- dot_zshrc
|
||||
- dot_gitconfig.tmpl
|
||||
- dot_config/
|
||||
- atuin/
|
||||
- bash/
|
||||
- bat/
|
||||
- fish/
|
||||
- fontconfig/
|
||||
- gh/
|
||||
- gh-dash/
|
||||
- ghostty/
|
||||
- herdr/
|
||||
- herdr-nvim/
|
||||
- hunk/
|
||||
- hypr/
|
||||
- kitty/
|
||||
- lazydocker/
|
||||
- lazygit/
|
||||
- mimeapps.list
|
||||
- niri/
|
||||
- shell/
|
||||
- starship.toml
|
||||
- tmux/
|
||||
- topgrade.toml
|
||||
- waybar/
|
||||
- yazi/
|
||||
- zsh/
|
||||
- dot_local/
|
||||
- bin/
|
||||
- dot_claude/
|
||||
- commands/
|
||||
- hooks/
|
||||
- skills/
|
||||
- settings.json
|
||||
- empty_CLAUDE.md
|
||||
- dot_agents/
|
||||
- skills/
|
||||
- dot_skill-lock.json
|
||||
- dot_omp/
|
||||
- loops/
|
||||
- private_agent/
|
||||
- dot_leetcode/
|
||||
- docs/
|
||||
- public/
|
||||
- blume.config.ts
|
||||
- package.json
|
||||
- bun.lock
|
||||
- README.md
|
||||
|
||||
</FileTree>
|
||||
|
||||
| Path | Target | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `.chezmoi.toml.tmpl` | `~/.config/chezmoi/chezmoi.toml` | Config template rendered by `chezmoi init`. See [Config and roles](/chezmoi/config). |
|
||||
| `.chezmoidata/` | — | YAML merged into template data (`.packages`, `.aliases`). See [Packages and aliases](/chezmoi/data). |
|
||||
| `.chezmoiexternal.toml` | — | Git repos cloned into `$HOME` (nvim config, a bat syntax). |
|
||||
| `.chezmoiignore.tmpl` | — | Target paths chezmoi skips; gates the Linux desktop layer by role. |
|
||||
| `.chezmoiscripts/`, root `run_*` | — | Install and housekeeping scripts. See [Apply scripts](/chezmoi/scripts). |
|
||||
| `dot_config/` | `~/.config/` | Per-tool config. Documented under [Shell](/shell), [Terminal](/terminal), [Tools](/tools), [Desktop](/desktop). |
|
||||
| `dot_local/bin/` | `~/.local/bin/` | 33 standalone scripts shared by every shell. |
|
||||
| `dot_claude/` | `~/.claude/` | Claude Code settings, commands, hooks, and skill symlinks. See [AI agents](/ai/claude-code). |
|
||||
| `dot_agents/` | `~/.agents/` | The shared agent skill library the `~/.claude/skills` symlinks resolve against. |
|
||||
| `dot_omp/` | `~/.omp/` | omp agent config and loops. |
|
||||
| `dot_leetcode/` | `~/.leetcode/` | leetcode CLI workspace state. |
|
||||
| `docs/`, `public/`, `blume.config.ts`, `package.json`, `bun.lock`, `README.md` | — | This docs site and the repo README. Listed in `.chezmoiignore.tmpl`, never applied. |
|
||||
|
||||
Entries whose source name starts with `.` (`.github/`, `.claude/`, `.crush/`, `.gitignore`) are ignored by chezmoi automatically and stay repo-only. `.github/workflows/docs.yml` builds this site.
|
||||
|
||||
## Naming conventions
|
||||
|
||||
chezmoi encodes target names and attributes in source file names. These are the prefixes and suffixes the repo uses:
|
||||
|
||||
| Source form | Meaning | Examples here |
|
||||
| --- | --- | --- |
|
||||
| `dot_` | Target name starts with `.` | `dot_config` → `~/.config`, `dot_zshrc` → `~/.zshrc`, `dot_skill-lock.json` → `.skill-lock.json` |
|
||||
| `private_` | Target has group/world permissions cleared (`0600`/`0700`) | `dot_config/atuin/private_config.toml`, `dot_config/gh/private_config.yml`, `dot_config/hunk/private_state.json`, `dot_omp/private_agent/`, `dot_omp/loops/*/private_loop.md` |
|
||||
| `executable_` | Target gets the executable bit | 49 files: every script in `dot_local/bin/`, `dot_config/niri/scripts/`, `dot_config/tmux/executable_niri-title.sh`, Claude hooks, skill helper scripts |
|
||||
| `exact_` | Directory is exact: entries in the target that are not in the source are deleted | `dot_config/niri/exact_config.d/` (numbered `10-…kdl` through `95-…kdl` fragments) |
|
||||
| `empty_` | File is kept even when empty | `dot_claude/empty_CLAUDE.md`, `dot_config/herdr/empty_dot_plugins.lock` |
|
||||
| `symlink_` | Target is a symlink; file content is the link destination | 67 entries in `dot_claude/skills/` (each → `../../.agents/skills/<name>`), `dot_config/kitty/symlink_current-theme.conf` (→ `themes/noctalia.conf`) |
|
||||
| `.tmpl` | Rendered with Go templates and chezmoi data | `dot_gitconfig.tmpl`, `dot_config/shell/aliases.sh.tmpl`, `dot_config/yazi/yazi.toml.tmpl`, all scripts, the config and ignore files |
|
||||
| `run_once_` / `run_onchange_` + `before_` / `after_` | Script, with run frequency and phase | See [Apply scripts](/chezmoi/scripts) |
|
||||
|
||||
Prefixes stack in chezmoi's fixed order, e.g. `private_dot_foo`, `executable_dot_bar`. No source file currently uses `encrypted_`, even though age encryption is configured.
|
||||
|
||||
## Symlink mode
|
||||
|
||||
`.chezmoi.toml.tmpl` sets `mode = "symlink"`. `chezmoi apply` then makes each managed dotfile a symlink into the source tree instead of a copy, as long as the file is a plain regular file. Files that are templates, `private_`, `executable_`, or encrypted are still written as real files.
|
||||
|
||||
What that means in practice:
|
||||
|
||||
- Editing a plain file in `$HOME` (for example `~/.config/kitty/kitty.conf`) edits `~/.local/share/chezmoi/dot_config/kitty/kitty.conf` directly. The change is live and shows up in `git status` of the repo with no `chezmoi add` step.
|
||||
- Templates are rendered copies. Editing `~/.gitconfig` or `~/.config/shell/aliases.sh` changes only the copy; the next `chezmoi apply` overwrites it. Edit the `.tmpl` source (`chezmoi edit ~/.gitconfig`) instead.
|
||||
- `private_` and `executable_` targets are real files too. After editing one in place, run `chezmoi re-add <path>` to copy it back into the source.
|
||||
- Tools that rewrite their own config write through the symlink into the repo. The runtime state that would otherwise pollute the source (plugin dirs, lock files, `fish_variables`, `__pycache__`) is excluded in `.chezmoiignore.tmpl` and `.gitignore`; see [Externals and ignore rules](/chezmoi/externals-and-ignore).
|
||||
|
||||
:::note
|
||||
Several fish aliases (`e.f`, `e.h`, `e.k`, `e.n`, `e.y`, `e.t`, `,,`) open the chezmoi source paths directly. Symlink mode makes that equivalent to editing the live config.
|
||||
:::
|
||||
|
||||
## Daily workflow
|
||||
|
||||
| Command | What it does here |
|
||||
| --- | --- |
|
||||
| `chezmoi apply` | Runs `before_` scripts (package install), writes symlinks/files/templates into `$HOME`, clones missing externals, then runs `after_` scripts. |
|
||||
| `chezmoi update` | `git pull` in the source directory, then `apply`. |
|
||||
| `chezmoi diff` | Shows what `apply` would change, including rendered templates and scripts that would run. |
|
||||
| `chezmoi add <path>` | Moves a new file into the source tree under the right name. In symlink mode the target becomes a symlink on the next apply. Use `--template` or `--encrypt` for templated or secret files. |
|
||||
| `chezmoi re-add <path>` | Copies edits made to non-symlinked targets (private, executable) back into the source. |
|
||||
| `chezmoi edit <path>` | Opens the source file for a target, the right way to change templates. |
|
||||
| `chezmoi cd` | Opens a shell in `~/.local/share/chezmoi` for git work. The fish alias `,,` opens yazi there instead. |
|
||||
| `chezmoi init --prompt` | Re-asks name, email, and role and regenerates `~/.config/chezmoi/chezmoi.toml`. |
|
||||
|
||||
Commit and push from the source directory with plain git; the remote pushes over SSH (`git@github.com:prdlk/dotfiles.git`).
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineMeta } from "blume";
|
||||
|
||||
export default defineMeta({
|
||||
title: "How it works",
|
||||
icon: "settings",
|
||||
pages: ["index", "config", "data", "scripts", "externals-and-ignore"],
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: Apply scripts
|
||||
description: Every run_ script chezmoi executes during apply, when each one fires, and what it does on each OS and role.
|
||||
---
|
||||
|
||||
Six scripts run during `chezmoi apply`. Four live in `.chezmoiscripts/`, a directory whose scripts run as if they sat at the source root without creating a matching target directory. Two sit at the repo root. All six are templates and all use `#!/bin/sh` with `set -eu`.
|
||||
|
||||
## Execution order
|
||||
|
||||
`before_` scripts run before any file is written; `after_` scripts run after every file, symlink, and external is in place. Within a phase chezmoi runs scripts in ASCII order of their target names, so the numeric prefixes come first.
|
||||
|
||||
<Steps>
|
||||
<Step title="10-packages (before, onchange)">Install system packages from `packages.yaml`.</Step>
|
||||
<Step title="Files, symlinks, externals">chezmoi writes the target state.</Step>
|
||||
<Step title="20-gh-extensions (after, onchange)">Install gh extensions.</Step>
|
||||
<Step title="30-user-services (after, once)">Enable systemd user units on Linux.</Step>
|
||||
<Step title="40-post (after, onchange)">tpm clone, bat cache, font cache.</Step>
|
||||
<Step title="build-bat-cache (after, onchange)">Rebuild bat cache when its theme or config changes.</Step>
|
||||
<Step title="install-yazi-plugins (after, onchange)">Reinstall yazi plugins when the manifest changes.</Step>
|
||||
</Steps>
|
||||
|
||||
| Script | Phase | Frequency | Re-run trigger |
|
||||
| --- | --- | --- | --- |
|
||||
| `.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl` | before | onchange | sha256 of `.chezmoidata/packages.yaml`; also the role and OS, which change the rendered package list |
|
||||
| `.chezmoiscripts/run_onchange_after_20-gh-extensions.sh.tmpl` | after | onchange | sha256 of `packages.shared.gh_extensions` as JSON |
|
||||
| `.chezmoiscripts/run_once_after_30-user-services.sh.tmpl` | after | once | Runs once per distinct rendered content |
|
||||
| `.chezmoiscripts/run_onchange_after_40-post.sh.tmpl` | after | onchange | Only edits to the script itself (or an OS change) |
|
||||
| `run_onchange_after_build-bat-cache.sh.tmpl` | after | onchange | sha256 of `dot_config/bat/themes/cyberdream.tmTheme` and `dot_config/bat/config` |
|
||||
| `run_onchange_after_install-yazi-plugins.sh.tmpl` | after | onchange | sha256 of `dot_config/yazi/package.toml` |
|
||||
|
||||
`run_onchange_` scripts re-run whenever their rendered content differs from the last successful run. The hash triggers work by writing a checksum of another file into a comment, so editing that file changes the script's content:
|
||||
|
||||
```sh
|
||||
# packages.yaml hash: {{ include ".chezmoidata/packages.yaml" | sha256sum }}
|
||||
```
|
||||
|
||||
`run_once_` scripts run once per unique rendered content and again only if that content changes or the previous run failed.
|
||||
|
||||
## 10-packages [#10-packages]
|
||||
|
||||
`.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl` installs everything in [`packages.yaml`](/chezmoi/data#packagesyaml). It runs before files are applied so the tools exist when configs land.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux">
|
||||
|
||||
1. Uses `pacman` directly when running as root, otherwise `sudo pacman`.
|
||||
2. `pacman -S --needed --noconfirm` with every entry in `packages.arch.pacman`.
|
||||
3. For roles `desktop` and `laptop`, a second `pacman -S --needed --noconfirm` with `packages.arch.desktop.pacman`.
|
||||
4. Builds the AUR list: `packages.arch.desktop.aur` plus `packages.arch.aur` for `desktop`/`laptop`, only `packages.arch.aur` for other roles.
|
||||
5. If the list is non-empty and `paru` exists, runs `paru -S --needed --noconfirm` on it. Without `paru` it prints `warning: paru not found — skipping AUR packages: …` and continues.
|
||||
|
||||
</Tab>
|
||||
<Tab title="macOS">
|
||||
|
||||
1. Exits with an error pointing at https://brew.sh if `brew` is missing.
|
||||
2. `brew install` with every entry in `packages.darwin.brew`.
|
||||
3. `brew install --cask` with every entry in `packages.darwin.cask`.
|
||||
|
||||
The role is not consulted on macOS.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
On any other OS the script renders to just the shebang and comments and does nothing.
|
||||
|
||||
## 20-gh-extensions [#20-gh-extensions]
|
||||
|
||||
`.chezmoiscripts/run_onchange_after_20-gh-extensions.sh.tmpl` installs each `packages.shared.gh_extensions` entry on every OS.
|
||||
|
||||
- Exits 0 with `warning: gh not installed — skipping extensions` if `gh` is missing.
|
||||
- Exits 0 with `warning: gh not authenticated — run 'gh auth login', then 'chezmoi apply' again` if `gh auth status` fails.
|
||||
- Reads `gh extension list`, then runs `gh extension install <repo>` only for extensions not already present.
|
||||
|
||||
:::warning
|
||||
A skipped run still exits 0, so chezmoi records this content as done and a plain re-apply does not retry it, despite what the warning text says. After `gh auth login`, either run `gh extension install` for each entry by hand, or clear chezmoi's onchange state with `chezmoi state delete-bucket --bucket=entryState` and apply again (this re-runs every `run_onchange_` script).
|
||||
:::
|
||||
|
||||
## 30-user-services [#30-user-services]
|
||||
|
||||
`.chezmoiscripts/run_once_after_30-user-services.sh.tmpl` enables session-critical systemd user units on Linux.
|
||||
|
||||
- If `~/.config/systemd/user/nirinit.service` exists and `systemctl` is available, runs `systemctl --user daemon-reload` and `systemctl --user enable nirinit.service`. Failures are ignored.
|
||||
- On non-Linux systems it renders to `#!/bin/sh` / `exit 0`.
|
||||
|
||||
Other personal automation units (brain-lens, omp-loop, rclone, hermes, voxtype) are deliberately not enabled; the script comment says to enable them by hand per machine. The repo does not currently contain a `dot_config/systemd/` directory, so the unit file has to exist on the machine already for this script to do anything.
|
||||
|
||||
## 40-post [#40-post]
|
||||
|
||||
`.chezmoiscripts/run_onchange_after_40-post.sh.tmpl` is post-apply housekeeping. It has no hash comment, so it only re-runs when the script itself is edited.
|
||||
|
||||
| Step | Condition | Action |
|
||||
| --- | --- | --- |
|
||||
| tmux plugin manager | `tmux` installed and `~/.config/tmux/plugins/tpm` missing | `git clone --depth=1 https://github.com/tmux-plugins/tpm ~/.config/tmux/plugins/tpm` |
|
||||
| bat cache | `bat` installed | `bat cache --build` |
|
||||
| Font cache | Linux, `fc-cache` installed | `fc-cache -f` |
|
||||
|
||||
Each step ignores failures (`|| true`). `tmux.conf` expects tpm at that path; see [tmux](/terminal/tmux).
|
||||
|
||||
## build-bat-cache [#build-bat-cache]
|
||||
|
||||
`run_onchange_after_build-bat-cache.sh.tmpl` rebuilds bat's syntax and theme cache when the managed inputs change. It hashes `dot_config/bat/themes/cyberdream.tmTheme` and `dot_config/bat/config`, exits quietly if `bat` is missing, and otherwise runs `bat cache --build`. This registers the cyberdream theme and the PureScript syntax cloned by [`.chezmoiexternal.toml`](/chezmoi/externals-and-ignore#externals).
|
||||
|
||||
## install-yazi-plugins [#install-yazi-plugins]
|
||||
|
||||
`run_onchange_after_install-yazi-plugins.sh.tmpl` reinstalls yazi plugins whenever `dot_config/yazi/package.toml` changes. It exits quietly if `ya` is missing or `${XDG_CONFIG_HOME:-$HOME/.config}/yazi/package.toml` does not exist, and otherwise runs `ya pkg install`. The installed plugins land in `~/.config/yazi/plugins/`, which `.chezmoiignore.tmpl` excludes. The plugin list is on the [yazi page](/tools/yazi).
|
||||
Reference in new issue
Block a user