Managed with chezmoi. This repository is the chezmoi
source tree, so files here carry chezmoi's naming: dot_zshrc.tmpl becomes
~/.zshrc, private_dot_config/ becomes ~/.config with mode 0700,
executable_ sets the executable bit, and encrypted_ is age ciphertext.
Caution
This replaces your dotfiles. Back up first.
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply xiaosq2000Needs curl, git and unzip, and Python 3 on a machine whose bundles select
downloads. The desktop bundle installs the terminal font; a remote machine
uses the font of the terminal you connect from. zsh is not a prerequisite;
without root, pixi global install zsh puts one in your home directory — see
CAVEATS.md for that and the other shell-setup
constraints.
init asks two questions and records the answers in
~/.config/chezmoi/chezmoi.toml, so they are never asked again:
| prompt | value |
|---|---|
machine |
an entry of .chezmoidata/machines.toml |
theme |
rose-pine, rose-pine-moon, rose-pine-dawn, catppuccin-latte, catppuccin-frappe, catppuccin-macchiato, catppuccin-mocha |
machine defaults to whichever entry's hostnamePattern matches, so on a known
host press enter. To answer up front, for a container, a script, or a host with
no pattern:
chezmoi init --apply \
--promptString machine=workstation \
--promptString theme=rose-pine \
xiaosq2000A machine not in the table yet can start as generic: a working shell, nothing
else assumed.
chezmoi diff # what would change
chezmoi apply # make ~ match this repo
chezmoi update # pull, then apply
chezmoi edit ~/.zshrc # edit the source of a file, not the copy in ~
chezmoi cd # open a shell in the source treeEditing a file in ~ does not update the repository. Use chezmoi edit, or
edit in place and run chezmoi re-add.
chezmoi edit takes a target path, so it cannot reach the files that have no
target: .chezmoidata/*.toml, .chezmoiignore and .chezmoiexternal.toml are
read by chezmoi and never deployed. Reach them through the source tree:
chezmoi cd # then edit normally
$EDITOR "$(chezmoi source-path)/.chezmoidata/machines.toml"
chezmoi edit-config-template # the config templateThe Git setup script sets diff.external = difft when Difftastic is available.
It runs after pixi setup and repeats when tool availability changes.
If difft is absent, the script removes that setting but preserves any other diff tool.
Difftastic renders diffs, not pager input, so Git's pager stays unchanged.
git diff # Difftastic when configured
git show --ext-diff # use Difftastic for a commit
git log -p --ext-diff # use Difftastic in patch history
git diff --no-ext-diff # standard patches and text-conversion driversUse --no-ext-diff for patches that tools must parse or apply.
It also enables this repository's .age text-conversion driver, which Difftastic bypasses.
theme # pick a variant with fzf (--help for the plain list)
theme rose-pine-dawn # switch everywhere and apply
theme --dry-run catppuccin-mochaOne switch covers btop, kitty, alacritty, Neovim, zathura, fzf, starship and Herdr. Variants use the slug upstream uses.
Zathura starts with recolouring off on every variant, so documents keep their
own colours; Ctrl+R turns it on in the open window.
Each colour scheme is one file — themes-rose-pine.toml, themes-catppuccin.toml — and chezmoi merges them, so adding a scheme means adding a file and editing nothing. Each holds:
[schemes.<name>]— where theme files come from, and which Neovim plugin provides them.[themes.<slug>]— the token each tool uses for that variant, itsappearance(lightordark), and its fzf and starship palettes.
Tokens are per tool because upstreams disagree: Catppuccin Mocha is
catppuccin_mocha to btop, mocha to kitty and catppuccin-mocha to
alacritty.
Theme files are fetched to a fixed name —
~/.config/btop/themes/current.theme,
~/.config/alacritty/current-theme.toml,
~/.config/kitty/current-theme.conf — so nothing is vendored per variant.
Starship is the exception: it has no include mechanism, so
~/.config/starship.toml is rendered from
a template with the palette inlined
from [themes.<slug>.starship].
appearance is the fallback for a tool with no port for a scheme, and the one
field every variant has.
To restyle another tool, add a key to each scheme file and reference it from that tool's template. Never hard-code a variant name in a config file.
/i-have-adhd # Claude Code, for this session
$i-have-adhd # Codex, for this session
stop adhd mode # end iti-have-adhd reshapes agent output to be
acted on: next action first, numbered steps, no preamble.
agent-plugins.sh installs it wherever
claude or codex is on PATH, and does nothing where neither is, so it is not
gated on the machine's bundles.
Installing changes nothing by itself, which is the point. The skill is opt-in on
both agents, so neither model can reach for it unasked and chezmoi update does
not quietly change how a machine's agents talk.
There is deliberately no always-on switch. On every machine it would need a flag
file for Claude Code's SessionStart hook and, for Codex, a block written into
~/.codex/AGENTS.md, which is the file every agent shares, only to save typing
one command. If you want it always on in Claude Code anyway, upstream's own flag
file still works by hand: touch ~/.claude/.i-have-adhd-always.
dot_agents/AGENTS.md holds the user's preferences for
every project, such as where knowledge is written down, keeping downloads off
the metered proxy, and how documents state facts. It deploys to
~/.agents/AGENTS.md and is linked as ~/.claude/CLAUDE.md,
~/.codex/AGENTS.md, ~/.config/opencode/AGENTS.md and ~/.gemini/AGENTS.md,
so Claude Code, Codex, OpenCode and Antigravity CLI all load the same file.
The agent-tools bundle also links it as ~/.pi/agent/AGENTS.md for Pi.
Pi reads ~/.agents/skills directly.
Antigravity CLI (agy) also reads the shared skills through symlinks in
~/.gemini/config/skills/. See
Shared agent skills for discovery and setup.
AGENTS.md, which CLAUDE.md imports, is for an agent changing this
repository. It covers the source-versus-target model, the naming attributes, and
the rules that keep secrets out of a public repository.
The machines skill is for an agent working anywhere else. It carries a page per
machine (what it is for, how to reach it, accounts, hardware, storage, rules and
quirks) plus a page on the network between them. The pages are age-encrypted in
dot_agents/skills/private_machines and
decrypted to ~/.agents/skills/machines on apply, so they exist only where the
key is. machine prints the path of the current machine's page.
Workstation, laptop, imrl and sicc select agent-tools. The vps, container and
generic profiles do not. The bundle installs Pi and RTK from upstream release
archives. Pi includes its runtime. The bundle also installs Node/npm for Pi
extensions.
Checked on 2026-10-20. Pi is pinned to v1.3.0, and RTK to v1.0.0, in
downloads.toml. To upgrade or roll back, change
those tag values and run chezmoi apply on each target.
dotfiles-fetch update pi rtk respects the pins. Do not use pi update to
update the dotfiles-managed executable.
command -v pi rtk # ~/.local/opt/dotfiles/bin on a new shell
pi --version
rtk --version
dotfiles-fetch statusPi loads ~/.pi/agent/AGENTS.md and discovers ~/.agents/skills natively.
pi.toml defines the shared startup settings and pins
pi-openai-toolkit and pi-ask-user. A modify script merges those values into
~/.pi/agent/settings.json. It preserves other settings and unrelated packages.
The selected package declarations, including their resource filters, belong to
the shared baseline. Changing them locally does not persist across an apply.
Each apply checks installed package versions after the Pi download. Matching
versions need no network request. Missing or mismatched packages install through
the managed Pi. Failed installations warn and retry on the next apply. Change
shared package versions in pi.toml, not through pi update --extensions.
Package installation and startup repairs clear the proxy variables.
New sessions default to openai-codex/gpt-6.1-sol with xhigh thinking.
Toolkit configuration
enables remote compaction for the openai-codex-responses API, including new
Codex model IDs. Other APIs use Pi's native compaction. Remote failure can fall
back to a text summary, so a completed /compact is not proof of a checkpoint.
Search remains unmanaged. Image generation and Toolkit WebSocket transport stay
off.
Authenticate separately on each machine with /login openai-codex. Credentials,
sessions, device IDs, caches and unrelated extensions remain local. Existing
sessions keep their model selection. To check a new session, run
/toolkit-config validate, then test /compact after a few turns.
RTK is available for explicit commands, such as rtk git status. This bundle
does not install RTK hooks, change telemetry consent, or enable automatic
filtering. Existing RTK integrations stay unchanged.
Manual installations remain in place as fallbacks. New bash and zsh shells put the managed downloads before those copies. Downloads bypass the metered AI proxy. If a target cannot reach an upstream directly, transfer the verified archives or resolve network access before deployment. Do not use the proxy without explicit approval.
The workstation, laptop, imrl and sicc select the herdr bundle for coding agents.
Start herdr in a project directory. The shared herdr skill teaches agents to
control panes when asked.
theme selects a supported Herdr palette, preserving light or dark appearance,
and reloads the running session when available. It owns the [theme] section of
Herdr's config; other settings are preserved.
| Dotfiles variants | Herdr palette |
|---|---|
rose-pine, rose-pine-moon |
rose-pine |
rose-pine-dawn |
rose-pine-dawn |
catppuccin-frappe, catppuccin-macchiato, catppuccin-mocha |
catppuccin |
catppuccin-latte |
catppuccin-latte |
Herdr may add more built-in variants. When it does, update each variant's
herdr token in .chezmoidata/themes-*.toml.
Checked on 2026-10-04 with Herdr 0.9.3 and Codex 0.160.0. Codex commands can lose
the pane's inherited HERDR_* variables, preventing agents from using the skill.
The cause is unresolved.
A machine's bundles in machines.toml decide
everything it gets. Each bundle in tools.toml can
name three kinds of thing: pixi packages, downloads from
downloads.toml for software conda-forge does not
package, and config paths that only a machine selecting it receives.
| bundle | contents |
|---|---|
core |
shell, editor, navigation — every machine, including a login node |
dev-c dev-py dev-web dev-tex |
per-language build and format tooling |
lsp |
language servers and the binaries Neovim's plugins call |
media |
audio, video and image conversion |
ml |
model and dataset transfer |
secrets |
age, sops, Bitwarden client |
github |
the GitHub CLI; every machine except the vps |
extras |
convenience and diagnostics, including tre; nothing load-bearing |
desktop |
kitty, the Maple Mono NF CN terminal font, and the GUI programs' config |
fonts |
eighteen more document, slide, design, math, CJK and emoji families |
research |
Zotero |
agent-tools |
pinned Pi and RTK binaries, Node/npm, shared Pi settings and extensions, on every computing machine except the vps |
herdr |
terminal workspaces for coding agents on workstation, laptop, imrl and sicc |
vpn |
um-vpn, the university VPN client, which pixi builds from its repository |
laptop |
a user service that drops the panel to 60 Hz on battery |
To add software, add it to a bundle and run chezmoi apply.
Packages. ~/.pixi/manifests/pixi-global.toml is generated, and
pixi global sync makes the machine match it exactly, so a pixi global install by hand is undone by the next apply and dropping a bundle uninstalls
its tools. Each package gets its own environment, and exposed is listed
explicitly, so one dependency conflict cannot break a whole bundle and
conda-forge runtimes (node, openssl, python3.14, tclsh, wish) stay off
PATH.
Downloads. chezmoi renders the selection into
~/.config/dotfiles/downloads.json and runs dotfiles-fetch sync whenever it
changes. chezmoi never downloads these itself, so chezmoi diff and cat stay
fast and work offline. dotfiles-fetch installs only under names of its own:
| what | where |
|---|---|
| applications, one directory per version | ~/.local/opt/dotfiles/<id>/ |
their commands, on PATH after pixi's |
~/.local/opt/dotfiles/bin/ |
| fonts | ~/.local/share/fonts/dotfiles/<id>/ |
| launchers | ~/.local/share/applications/dotfiles-*.desktop |
A kitty, Zotero or font installed some other way is never touched, so there is
nothing to adopt or back up. The one shared path is ~/.terminfo, where kitty's
entry is written only if missing, the same files kitten ssh writes on a
remote machine. Removing a download from a bundle removes what it installed.
Unpinned downloads keep their installed version until you request an update.
A GitHub download with a catalog tag follows that pin on every sync, including
upgrades and rollbacks. Updating is a decision, as with sheldon lock --update:
dotfiles-fetch status # what is selected and installed; offline
dotfiles-fetch outdated # GitHub releases newer than what is installed
dotfiles-fetch update [id...] # latest release, or the catalog tag when pinnedGitHub releases are verified against the digest GitHub publishes. Zotero's
built-in updater is disabled, since dotfiles-fetch update replaces the
application. A download with no build for the machine's platform is skipped with
a warning; as of 2026-09-23 that is tre on linux-arm64.
Kitty, Zotero and the fonts that setup.d/typefaces.sh installed before this
lived at shared paths. dotfiles-fetch migrate lists the font directories that
script wrote, and --apply deletes them; it only reports the rest.
Config. The config paths of every bundle a machine does not select go into
.chezmoiignore. Alacritty, Zathura, Fcitx5 and kmonad come from the
distribution; desktop supplies only their configuration. A machine that drops
a bundle keeps the files it already has, the way an application keeps its
profile.
Checked on 2026-10-18. CI has two jobs:
| Job | Scope |
|---|---|
checks |
Pre-commit hooks, except the local Gitleaks scan, and offline downloader tests |
smoke |
Render every machine, check shell syntax, then apply and verify in a clean no-key container without software bundles |
CI skips external theme downloads. It does not enforce specific package versions, key bindings, colours, or other configuration choices.
Run the portable checks locally:
pre-commit run --all-files
python3 .github/scripts/check-render.py
python3 .github/scripts/check-fetch.py
python3 .github/scripts/check-pi.pyThe render check requires chezmoi, Python 3.11+, bash, zsh, and ShellCheck.
It uses a temporary home and does not install software.
Other behavioral tests in .github/scripts/ remain available for targeted local checks.
check-fetch.py --live installs the real catalog in a temporary home.
It downloads a few gigabytes. Run it manually without the proxy when testing
upstream downloads. CI uses local fixtures only.
Files outside ~, such as scripts in /usr/local/sbin, systemd units and udev
rules, live in system/<machine>/ at their paths under /.
.chezmoiscripts/run_after_80-system-files.sh.tmpl installs the ones that
differ, as root, and never asks for a password: without cached sudo it lists
them and the next apply tries again. To install:
sudo -v && chezmoi applyIt reloads systemd and udev and enables installed units, but restarts nothing.
Deleting a file from system/ leaves the installed copy in place.
This repository is public, so anything secret in it is age ciphertext:
encrypted_* is decrypted on apply and the key is never committed.
Encryption is on. .chezmoidata/secrets.toml holds the recipient; the
identity lives at ~/.config/chezmoi/key.txt, with a copy in Bitwarden so a new
machine can fetch it once. docs/secrets.md has the design and
the commands.
Two things are encrypted: ~/.ssh/config, and the machine pages of the
machines agent skill. A pre-commit hook refuses a commit that would put
plaintext where either lives, and on a machine with the key git diff shows
both decrypted.
Claude Code, Codex and opencode are kept from reading ssh private keys and the age identity, and pre-commit hooks refuse a commit that contains either. docs/secrets.md lists what each agent gets and what it does not stop.
.chezmoidata/machines.toml is deliberately in the clear: bundles, roles and filesystem layout are not secrets. Addresses, account names and tokens are.