---
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

- .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

| 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`).
