1
0
Fork
You've already forked dotfiles
0
files, that start with a dot.
  • Shell 50.5%
  • Lua 27.5%
  • HTML 17%
  • Python 5%
Martijn van Exel 41b49519da
readme: document re-stowing after pulling newly-added files
Editing an existing linked file needs nothing, but a pulled commit that
adds a new file/dir (e.g. ~/.config/zsh/claude.zsh) has no symlink until
you re-stow. Note stow -R and the conflict caveat.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026年07月13日 13:53:22 -06:00
.git-crypt
.githooks
aerc/.config/aerc aerc: mail client config; accounts.conf git-crypt encrypted 2026年07月12日 17:32:09 -06:00
alacritty/.config/alacritty
bat/.config/bat git: add common aliases, delta as diff pager (gruvbox theme) 2026年07月11日 17:02:32 -06:00
claude/.claude Merge branch 'main' of ssh://codeberg.org/mvexel/dotfiles 2026年07月12日 21:41:40 -06:00
codex/.codex Merge branch 'main' of ssh://codeberg.org/mvexel/dotfiles 2026年07月12日 21:41:40 -06:00
docs Merge branch 'main' of ssh://codeberg.org/mvexel/dotfiles 2026年07月12日 21:41:40 -06:00
eza/.config/eza
ghostty ghostty: window shape, quit-after-close, clipboard, quick-terminal keybinds 2026年07月12日 17:27:06 -06:00
git claude: keep runtime model selection out of the repo via clean filter 2026年07月12日 21:29:21 -06:00
gnupg git, gnupg: add commit signing config 2026年07月11日 16:00:20 -06:00
josm josm: clean filter strips OAuth token from staged content 2026年07月12日 21:27:30 -06:00
mise/.config/mise mise: add lazydocker, lazygit, zig, ruby, hcloud, cloudflared, restic; hcloud zsh completions 2026年07月12日 17:27:13 -06:00
nvim Merge branch 'main' of ssh://codeberg.org/mvexel/dotfiles 2026年07月12日 21:41:40 -06:00
soma @299b9d08d9 claude: reflow CLAUDE/PERSONA to semantic line breaks; bump soma 2026年07月04日 16:42:43 -06:00
ssh/.ssh ssh: stow config (git-crypt), keys stay out of the repo 2026年07月12日 17:32:08 -06:00
tmux tmux: session picker popup on prefix+S (local + ssh-config remotes) 2026年07月12日 17:27:05 -06:00
zed/.config/zed zed: switch to plain Iosevka with fallbacks, set terminal font 2026年07月12日 17:27:13 -06:00
zsh Merge branch 'main' of ssh://codeberg.org/mvexel/dotfiles 2026年07月12日 21:41:40 -06:00
.gitattributes claude: keep runtime model selection out of the repo via clean filter 2026年07月12日 21:29:21 -06:00
.gitignore gitignore: narrow codex/.codex un-ignore too 2026年07月11日 16:08:09 -06:00
.gitmodules
.rumdl.toml
.stylua.toml feat: add Neovim IDE and Linux desktop integration 2026年07月11日 18:35:14 -06:00
README.md readme: document re-stowing after pulling newly-added files 2026年07月13日 13:53:22 -06:00

dotfiles

Welcome to the jungle that is my collection of dotfiles.

GNU Stow-managed like the grownups do.

Packages

  • bat -> ~/.config/bat/config
  • claude -> ~/.claude (only the actual config is tracked — see Per-package Notes)
  • codex -> ~/.codex/AGENTS.md (loads the canonical instructions and persona from the claude package)
  • eza -> ~/.config/eza/theme.yml
  • ghostty -> ~/.config/ghostty/config and ~/.local/bin/ghostty-tmux
  • git -> ~/.gitconfig
  • gnupg -> ~/.gnupg/gpg-agent.conf and ~/.local/bin/pinentry-resolve
  • josm -> ~/.config/JOSM/preferences.xml and ~/.local/share/applications/org.openstreetmap.josm.desktop
  • mise -> ~/.config/mise/config.toml
  • nvim -> ~/.config/nvim (Neovim 0.11 IDE configuration)
  • tmux -> ~/.tmux.conf and ~/.config/tmux
  • zsh -> ~/.zshrc and ~/.config/zsh/claude.zsh
  • zed -> ~/.config/zed/settings.json and ~/.config/zed/keymap.json
  • alacritty -> ~/.config/alacritty (not using anymore, keeping around in case I change my mind.)

Install

Clone with submodules (soma, the SomaFM zsh plugin sourced by zsh/.zshrc):

git clone --recurse-submodules <repo-url> ~/dotfiles

Then from the repository:

stow --target="$HOME" alacritty bat claude codex eza ghostty git gnupg josm mise nvim tmux zsh zed

Then mise install to pull the pinned tool versions (see mise).

On macOS, JOSM normally reads preferences from ~/Library/Preferences/JOSM. To use the stowed XDG-style config, either point JOSM at it when launching:

java -Djosm.pref="$HOME/.config/JOSM" -jar josm.jar

or replace the macOS preference directory with a symlink after checking that it does not contain local-only settings you want to keep:

mv "$HOME/Library/Preferences/JOSM" "$HOME/Library/Preferences/JOSM.bak"
ln -s "$HOME/.config/JOSM" "$HOME/Library/Preferences/JOSM"

Updating on an already-set-up machine

git pull only updates the files inside the repo — it does not touch the symlinks in $HOME. Stow creates those, and it only relinks when you run it. Editing an existing already-linked file needs nothing (the symlink points at it). But when a pulled commit adds a new file or directory to a package, that new path has no symlink yet, so anything referencing it breaks — e.g. .zshrc sourcing a freshly-added ~/.config/zsh/claude.zsh fails with no such file or directory until you relink.

Re-stow the affected package to create the missing links:

stow -R zsh # -R = restow: remove and recreate this package's links

Stow folds correctly into existing directories, so this is safe. If it prints a would cause conflicts message, that's stow refusing to clobber a real (non-symlink) file — move that file aside or use --adopt after checking the diff, don't force past it.

Dependencies

Common:

  • GNU Stow
  • Bash
  • tmux
  • Ghostty
  • IosevkaTerm Nerd Font
  • mpg123, curl, jq for the soma plugin (SomaFM player)
  • GnuPG (gpg, gpg-agent) plus a pinentry backend — pinentry-mac on macOS, pinentry-gnome3 or pinentry-curses on Linux — for commit signing (see git/gnupg)
  • mise itself — installs the tools it pins, but isn't in its own registry, so install it via its own instructions first

Linux clipboard support uses wl-copy, xclip, or xsel, in that order. macOS clipboard support uses pbcopy.

Linux / GNOME / Wayland Notes

These are machine setup notes, not Stow-managed configuration. GNOME stores settings such as custom keyboard shortcuts in its dconf database, while APT repository configuration lives under /etc; neither should be symlinked into the home directory.

1Password Quick Access

1Password's in-app global shortcut does not work directly under GNOME Wayland. Create a GNOME custom keyboard shortcut instead:

  • Name: 1Password Quick Access
  • Command: /usr/bin/1password --quick-access
  • Shortcut: Ctrl+Shift+Space

This can be configured in Settings → Keyboard → Keyboard Shortcuts → View and Customize Shortcuts → Custom Shortcuts. It follows 1Password's official Linux shortcut guidance.

Firefox on Ubuntu

Ubuntu's firefox APT package may be a transition package for the Snap build. This machine instead uses Mozilla's official .deb repository, following Mozilla's installation instructions:

  • repository: https://packages.mozilla.org/apt
  • signing-key fingerprint: 35BAA0B33E9EB396F59CA838C0BA5CE6DC6315A3
  • APT pin priority: 1000 for origin packages.mozilla.org
  • default browser desktop entry: firefox.desktop

The repository key, source, and pin live in /etc/apt/keyrings/, /etc/apt/sources.list.d/, and /etc/apt/preferences.d/; they are deliberately not tracked here.

Graphical programs launched from tmux

A long-running tmux server can retain stale desktop-session state. GUI launchers under Wayland need DBUS_SESSION_BUS_ADDRESS, WAYLAND_DISPLAY, XDG_CURRENT_DESKTOP, and XDG_RUNTIME_DIR. The tmux package includes these in update-environment, so attaching a client from the current desktop session refreshes them. If a graphical command still silently fails after logging into a new desktop session, detach and reattach the tmux client before debugging the application itself.

Per-package Notes

claude

~/.claude mixes real config with Claude Code's own runtime state (OAuth credentials, session transcripts, caches, sqlite dbs). .gitignore ignores claude/.claude/* by default and only allows the actual config through (CLAUDE.md, PERSONA.md, settings.json, statusline-command.sh, skills/) — don't widen that allowlist to the whole tree again, it's what keeps things like .credentials.json from being stageable at all.

settings.json's statusLine.command uses $HOME, not a hardcoded path — keep it that way or the statusline silently stops rendering on whichever machine doesn't match the hardcoded one.

git, gnupg

Commit signing config. git/.gitconfig sets user.signingkey and commit.gpgsign/tag.gpgsign. gnupg/.gnupg/gpg-agent.conf sets cache TTLs and points pinentry-program at gnupg/.local/bin/pinentry-resolve, a small wrapper that picks pinentry-mac on macOS or pinentry-gnome3/pinentry-curses on Linux, so the same conf file works unmodified on both.

Only config is versioned — the actual private key stays local under ~/.gnupg/private-keys-v1.d, untouched by stow or git. On a new machine, import your own key (gpg --import) and trust it (gpg --edit-key <id> -> trust -> 5) before signing will work; this repo has no key material to bootstrap from.

josm

The josm package ships a sanitized preferences.xml with the OSM OAuth access token stripped, so JOSM will prompt you to log in once per machine. Do not commit your live token back into the repo.

It also ships a local desktop entry (~/.local/share/applications/org.openstreetmap.josm.desktop) that overrides the system one to set JAVA_OPTS=-Dsun.java2d.uiScale=1.5 for hi-dpi screens on Linux — adjust the scale factor per machine, or delete the package's copy if you don't need it.

mise

Pins jq and fzf (both referenced elsewhere in this repo — the soma plugin and statusline script use jq, .zshrc and the tmux open-url.sh script use fzf) plus node, gh, and uv. Run mise install after stowing to actually pull the pinned versions — stowing only puts the config in place.

The manifest also installs the language servers and formatters shared by Zed and Neovim: stylua, lua-language-server, shellcheck, shfmt, ruff, ty, basedpyright, prettier, taplo, marksman, and the Bash, YAML, and VS Code HTML/JSON language servers.

curl, mpg123, stow, GnuPG, Ghostty, and Docker aren't in mise's registry (or, for Docker, need a real daemon rather than a version-managed binary) and stay OS-package-manager territory; see Dependencies.

zsh completions

Tab completion only works for a command if a _toolname completion function is sitting in $fpath by the time compinit runs — that happens for free for tools packaged by the OS or Homebrew (git, npm, etc.), but not for tools that generate their own completion script on demand (mise, gh, uv, docker). .zshrc evals each of those explicitly, right after compinit/mise activate, guarded by (( $+commands[...] )) so a missing tool doesn't break shell startup. Add new tools to that block the same way, not to fpathcompdef, which these generated scripts call, only exists once compinit has already run.

alacritty

The config contains a toggle script to switch between Gruvbox light and dark. This happens by copying one of the theme files to active.toml which is what the alacritty config imports after the dark default. The active.toml file is gitignored here.

tmux

The statusline uses ANSI indices (colour0-15) so it follows the terminal theme (Ghostty's Gruvbox Material Dark). Pane dimming and the statusline bg use fixed 256-cube greys (colour234/colour237), tuned against that dark theme — revisit if switching to a light theme.

default-shell is pinned explicitly per OS (/bin/zsh on macOS, /usr/bin/zsh on Linux) rather than inherited from $SHELL — some launch paths (e.g. Ghostty's wrapper) don't reliably set $SHELL to zsh, and a pane silently running bash never sources .zshrc.

eza

eza has no config file for default flags; theme.yml only controls colors (loaded via EZA_CONFIG_DIR, exported in the zsh package). The git-status column comes from the --git flag on the long-format aliases in zsh/.zshrc (ll, la). Grid ls doesn't show git — the column only renders in long mode.

zsh

Framework-free (no oh-my-zsh); everything lives in .zshrc, plus the sourced .config/zsh/claude.zsh and the soma submodule. brew shellenv is guarded ([ -x /opt/homebrew/bin/brew ]) so it no-ops on Linux instead of erroring. Postgres.app and the Zed application alias are likewise enabled only on macOS when their binaries exist. PNPM_HOME uses ~/Library/pnpm on macOS and the XDG data directory (~/.local/share/pnpm by default) on Linux.

claude.zsh (sourced from .zshrc via ~/.config/zsh/claude.zsh) provides claude-personal and claude-work aliases for switching between two Claude.ai subscription accounts on macOS. Tokens are stored in macOS Keychain — not in this repo. The aliases and the Keychain-backed Codeberg token are not configured on Linux. After adopting the dotfiles on a new machine, generate a fresh token per account and store it:

# While logged into the account (claude /login first if needed):
claude setup-token
security add-generic-password -a claude-personal -s claude-tokens -w "paste-token-here"
# Repeat for the work account with -a claude-work

Do not transfer tokens between machines — regenerate them. Each token lasts one year.

Git hooks

A tracked pre-commit hook in .githooks/ refuses commits that would leak an OSM OAuth access token from a JOSM preferences.xml (JOSM rewrites that file with your live token on exit). core.hooksPath is local git config and is not set by git clone, so enable it once per clone:

git config core.hooksPath .githooks

If these files already exist outside Stow, move them aside first or use stow --adopt carefully after checking the diff.