No description
  • Shell 38.9%
  • Zig 24.1%
  • Python 20.5%
  • Lua 7.6%
  • Makefile 3.7%
  • Other 5.2%
Find a file
2026-10-05 11:50:59 -05:00
a485/etc/ghostty stow: Flatten overlay package layouts 2026-08-06 21:06:34 -05:00
alacritty alacritty: Move config generation into build step 2026-03-23 15:45:28 -05:00
bin Makefile: Split user and system tools 2026-08-25 10:41:01 -05:00
darwin stow: Flatten overlay package layouts 2026-08-06 21:06:34 -05:00
docs/superpowers tools/python: Add agent-conf compiler 2026-06-23 09:48:03 -05:00
home shenv: Compute SUDO_EDITOR 2026-10-05 11:50:59 -05:00
linux stow: Flatten Linux package layout 2026-08-06 20:58:52 -05:00
system Makefile: Split user and system tools 2026-08-25 10:41:01 -05:00
tools ncmp: Keep errors visible in silent mode 2026-08-25 10:41:01 -05:00
.gitignore gitignore: Update zig patterns 2026-05-27 12:30:43 -05:00
.gitmodules Split dotfiles from tool projects 2026-03-18 23:29:53 -05:00
AGENTS.md Makefile: Stow system bin for users 2026-08-25 10:41:02 -05:00
CLAUDE.md CLAUDE.md: Add (delegates to AGENTS.md) 2026-03-03 10:02:08 -06:00
Makefile Makefile: Add component build targets 2026-09-24 11:49:03 -05:00
opencode.jsonc opencode: Add Superpowers skills to repo 2026-07-13 09:47:40 -05:00
README.md Makefile: Add component build targets 2026-09-24 11:49:03 -05:00

dotfiles

This repo contains my GNU Stow-managed dotfiles, app configs, and a handful of CLI utilities written in various languages.

This is a living home-directory repo, not a generic starter kit. It is useful as:

  • a directly installable Stow setup if you want these exact defaults,
  • a reference for stealing individual configs or scripts,
  • a source tree for the bundled Zig and Python command-line tools.

Starting points

According to my shell history, here is what I use the most:

  • bin/xc - Read or write from system clipboard. I use this dozens of times daily to chop up shell pipelines and feed external data through CLI helpers.
  • system/bin/sshq - SSH but with arguments escaped so shell parsing isn't done twice
  • home/.shenv, home/etc/shrc.d/30-aliases.sh, and home/etc/zsh/.zshrc.d/git.plugin.zsh - Aliases and helpers I use frequently, especially alias for printing (p, pl), directory listing (l, ll), and git shortcuts (g, gs, ga, gd, gc)
  • tools/zig/src/uniqp.zig, tools/zig/src/xcut.zig, tools/zig/src/trim.zig, tools/zig/src/ncmp.zig, system/bin/hsize, and system/bin/urlparam - General purpose, Unix-like command line utilities
  • tools/python/qrbr.py - If you have a nice braille font, this is nice for generating more compact QR codes than qrencode
  • home/etc/nvim/, home/etc/mpv/, - Editor and media player configs

Quick paths:

What Lives Here

  • home/ - The main shared Stow package; top-level dotfiles plus etc/, share/, and var/
  • bin/ - User-specific scripts and wrappers, stowed into $PREFIX/bin
  • system/bin/ - General-purpose tools installed as root-owned copies in /usr/local/bin
  • system/sbin/ - Administrative tools installed as root-owned copies in /usr/local/sbin
  • alacritty/ - Build-only Alacritty source config that compiles into generated TOML
  • darwin/ - macOS-only overlay; directly stowed files plus copied plist snapshots under darwin/Library/Preferences/
  • a485/ - Hostname-specific overlay stowed when the hostname is a485
  • tools/zig/ - Standalone Zig CLI project with uniqp, xcut, trim, jshuf, and ncmp
  • tools/python/ - Standalone Python packages and helper CLIs, including qrbr and alacritty-preprocess

Before You Install

  • make generates the Alacritty config and Zig tool binaries
  • make install installs the user environment into $PREFIX (default ~), and on macOS also copies tracked plist snapshots; review with make dryrun first
  • make stage-system prepares root-independent system tool artifacts; run sudo make install-system afterward to install /usr/local/bin and /usr/local/sbin copies
  • Several configs are intentionally personal and need editing before reuse, especially home/.gitconfig, home/etc/newsboat/config, and qutebrowser secret/search-engine files
  • Some app configs assume companion tools are already installed (mpv, qutebrowser, ghostty, alacritty, i3, polybar, dunst, Hammerspoon, and others)
  • mpv extras live in git submodules under home/etc/mpv/submodules/; initialize them after cloning
  • At least one submodule URL uses GitHub SSH, so git submodule update --init --recursive may require SSH access

Prerequisites

Common:

  • git
  • make
  • GNU stow

macOS-specific:

  • plutil for importing app-managed plist state back into the repo

Optional toolchain dependencies:

  • Zig >= 0.16.0 if you want make build, make test, and make install to include tools/zig/
  • Python >= 3.12 for tools/python/
  • uv if you want make build to sync tools/python/.venv and build Alacritty, or make install to install the Python tools
  • kcov if you want Zig coverage runs from zig build -Dcoverage=true test

Build generated files

make

Install dotfiles

Clone and initialize submodules:

git clone --recurse-submodules <your-fork-or-this-repo-url>
cd dotfiles

or,

git clone <your-fork-or-this-repo-url>
cd dotfiles
git submodule update --init --recursive

Preview the filesystem changes:

make dryrun

If you are upgrading from an old commit, run this one-time migration in case any breaking changes have been made:

make migrate

Install into your home directory:

make install

To install into a different prefix instead of ~:

make install PREFIX=/path/to/prefix

What make install does:

  • Always Stows home/ into $PREFIX (default ~)
  • Always Stows the user-only bin/ package and the source tools from system/bin/ into $PREFIX/bin
  • Also builds alacritty/alacritty.yml into build/etc/alacritty/alacritty.toml and Stows that generated file into $PREFIX
  • Also builds the Zig tools into tools/zig/zig-out/bin
  • Also installs the Python tools into $PREFIX/bin with uv tool install
  • Does not install the Zig tools into $PREFIX/bin; make stage-system and make install-system install them into /usr/local/bin
  • On macOS, also Stows darwin/ into $PREFIX, except darwin/Library/Preferences/*.plist
  • On macOS, also copies darwin/Library/Preferences/*.plist into $PREFIX/Library/Preferences/*.plist
  • When a matching hostname directory exists, also Stows its package into $PREFIX
  • macOS preference plists are stored in the repo as XML-format .plist files that mirror their paths under ~; they are copied, not symlinked, because apps rewrite them, and make import syncs the current installed state back into darwin/Library/Preferences/
  • Does not execute darwin/.macsettings; that script is stowed, but you run it manually if you want those defaults write tweaks

Useful maintenance commands:

make build-python    # sync tools/python/.venv
make build-alacritty # generate build/etc/alacritty/alacritty.toml
make build-zig       # build tools/zig/zig-out/bin
make build           # run all build steps above
make test            # run tools/python pytest tests and tools/zig unit tests
make import     # import current macOS plist state into darwin/Library/Preferences/
make migrate                 # migrate old user installs to their current form
make stage-system             # stage system/bin and system/sbin plus built tools
sudo make install-system     # install root-owned copies under /usr/local
sudo make migrate-system     # migrate old /usr/local symlinks if needed
make clean                   # remove build/ and tools/zig/zig-out, but keep tools/zig/.zig-cache
make uninstall               # remove stowed user files and user-installed Python tools
sudo make uninstall-system  # remove the manifest-tracked system tools

Use Just The Tools

If you do not want to Stow the whole repo, you can still use the tool projects directly.

Zig tools

In tools/zig, tools can be built with:

zig build
# Run individual tools
zig build run -- uniqp --help
  • Built binaries land in zig-out/bin/
  • make stage-system stages them for /usr/local/bin; sudo make install-system installs root-owned copies
  • Tests are run by zig build test
  • Coverage-enabled test runs use zig build -Dcoverage=true test and write reports under kcov-output/

Python tools

In tools/python, tools can be installed with your preferred package manager:

With pip:

python -m pip install -e .

With pipx:

pipx install .

With uv:

uv tool install .

Usage:

qrbr 'https://example.com'
alacritty-preprocess ../../alacritty/alacritty.yml --xresources ../../home/.Xresources

Repository Layout

.
|- bin/                   User-specific scripts stowed into ~/bin
|- system/bin/            General-purpose tools copied into /usr/local/bin
|- system/sbin/           Administrative tools copied into /usr/local/sbin
|- home/                  Shared Stow package
|  |- etc/                XDG-style app configs
|  |- share/              Shared data such as zsh site-functions
|  `- .*                  Top-level dotfiles (~/.gitconfig, ~/.tmux.conf, ...)
|- alacritty/             Build-only Alacritty config source
|- build/                 Generated Stow package output
|- darwin/                macOS-only Stow package and copied plist snapshots
|- tools/zig/             Zig CLI project
`- tools/python/          Python CLI project

Everyday Tools

These are the user-facing scripts in the top-level bin/ Stow package that I use the most often. General-purpose pipeline tools live under system/bin/; the alias-heavy side of the same workflow lives in the shell and Git sections under Config Guide.

bin/xc - Clipboard abstraction across platforms
  • Wraps X11, Wayland, macOS, WSL, and Termux clipboard commands behind one interface.
  • Paste mode (default): xc (can be explicitly invoked with xc -o).
  • Copy mode: xc -i file.txt, printf 'hello' | xc -i, xc -i -e 'literal text'.
  • Supports primary selection (-p), target types (-t), whitespace trimming (-T), and explicit trailing-newline policies (-n).

Examples:

xc
printf 'hello\n' | xc -i
xc -i -e 'plain text'
system/bin/ydiff - Diff JSON after normalizing to YAML
  • Runs diff on two JSON/JSONL/NDJSON files after normalizing both through yq to YAML.
  • Supports an optional -f filter so you can preprocess a document before diffing.
  • Useful for review of generated config files when raw JSON formatting would create too much noise.

Examples:

ydiff config-a.json config-b.json
ydiff -f '.items[] | {name, value}' before.jsonl after.jsonl
system/bin/hsize - Make byte counts human-readable
  • Formats numeric size columns as human-readable values.
  • Works well in pipelines from tools like du, find, or custom tabular output.
  • Supports selecting a column with -c, changing the input block size with -B, and choosing IEC vs SI units.

Examples:

printf '123456\n' | hsize
du -s . | hsize -B512
system/bin/urlparam - Inspect or extract URL query parameters
  • Reads URLs from stdin.
  • With no extra arguments, prints every query parameter pair it finds without unescaping.
  • With parameter names, filters to those parameters; with exactly one parameter name, unescapes the value and prints it.
  • Useful when processing redirect links, copied browser URLs, or API callbacks.

Examples:

printf '%s\n' 'https://example.com/?a=1&b=2' | urlparam
printf '%s\n' 'https://example.com/?a=1&b=2' | urlparam b
system/bin/sshq - Quote remote ssh commands safely
  • Takes a remote command and shell-quotes it before passing it to ssh.
  • Useful when you want ssh to behave more like exec, env, or nohup without fighting nested quoting.
  • Leaves plain ssh host behavior alone when no remote command is supplied.

Examples:

sshq host sh -c 'printf "%s\n" "$@"' sh 'foo $bar \baz' 'Hello, world!'
sshq host env FOO='a b' sh -lc 'printf "%s\n" "$FOO"'
bin/xbrs - Search Void packages with better filtering
  • Wraps xbps-query -Rs with pattern filtering and cleaner aligned output.
  • Can hide -devel, -dbg, and 32bit packages, or limit to installed / not-installed packages.
  • Very specific to my Void Linux workflow.

Examples:

xbrs ripgrep
xbrs -i zsh
xbrs -a llvm
bin/mullvad-exclude - Run a command outside Mullvad split tunnel
  • Adds the current process to Mullvad split tunneling, then execs the requested command.
  • Extremely small, but a frequent quality-of-life wrapper when Mullvad is enabled.
  • Only useful if you use Mullvad and its split-tunnel CLI.

Examples:

mullvad-exclude firefox
mullvad-exclude curl https://ifconfig.me

System Tools

The system packages have separate deployment paths:

  • system/bin/ contains general-purpose tools such as sleep_until, pextrace, defrag, and the Zig utilities. Its source tools are also stowed into the user's $PREFIX/bin by make install.
  • system/sbin/ contains administrative tools; currently this is only make_swap_file, and it remains system-only.
  • make stage-system builds a root-independent staging tree under build/system/.
  • sudo make install-system copies the staged files as root:root into /usr/local/bin and /usr/local/sbin.
  • Installation does not migrate existing symlinks; run sudo make migrate-system separately when upgrading an old system install.
  • make migrate handles old user-bin symlinks; sudo make migrate-system handles old system-bin symlinks.

System installation deliberately does not use symlinks into the checkout. Existing unexpected files or symlinks are left untouched and cause installation to fail rather than being overwritten.

Standalone Tool Projects

These are the self-contained tools I use most from the standalone Zig/Python projects. You can build or install them without adopting the whole dotfiles setup.

tools/zig/src/uniqp.zig - Unique-record filter and counter
  • Reads newline-delimited input from files or stdin.
  • uniqp prints only the first occurrence of each unique line.
  • uniqp -c counts occurrences of each unique line.
  • uniqp --skip-empty ignores empty input lines before filtering or counting.
  • uniqp -l prints the number of unique lines across all input.
  • uniqp -L prints per-file unique counts

The tool also supports combining -l and -L.

Examples:

printf "%s\n" foo foo bar | uniqp
printf "%s\n" foo foo bar | uniqp -c
printf '\nfoo\n\nbar\n' | uniqp --skip-empty
uniqp -l -L file1.txt file2.txt
tools/zig/src/xcut.zig - Delimiter-aware field cutter
  • Similar in spirit to cut, but built for richer field selection.
  • Supports custom field delimiters, custom output delimiters, record delimiters, escaped delimiter strings, range syntax, and NUL-terminated mode.
  • Requires -f/--fields.

Examples:

printf "%s\n" "a:b:c" | xcut -d : -f 1,3
printf "%s\n" "a:b:c:d" | xcut -d : -f 2..4
printf "%s\0" a b | xcut -0 -f 1
tools/zig/src/trim.zig - Stream and line trimmer
  • Trims leading and trailing characters from whole streams by default.
  • -l switches to line-by-line trimming and only prints non-empty lines.
  • -t chooses custom trim characters.
  • -n strips the final newline; -N preserves it.

Examples:

printf '   hello   \n' | trim
printf '  a  \n\n  b  \n' | trim -l
printf '..hello..\n' | trim -t '.'
tools/zig/src/ncmp.zig - Compare multiple files byte by byte
  • Compares two or more files and exits after their first differing byte.
  • Prints the one-based byte position by default.
  • -q/--quiet and -s/--silent suppress comparison output, but not invocation or I/O errors.
  • Exits 0 for equal inputs, 1 for different inputs, and 2 for errors.
  • Accepts one - operand to compare standard input with files.

Examples:

ncmp first.bin second.bin third.bin
ncmp --silent first.bin second.bin
printf 'payload' | ncmp - saved-payload.bin
tools/python/qrbr.py - Print QR codes as braille
  • Turns text from an argument or stdin into a terminal-friendly Unicode braille QR code.
  • Depends on numpy and qrcode[pil].
  • Good for sharing links in a terminal without opening an image viewer.

Examples:

qrbr 'https://example.com'
printf 'wifi:ssid=example' | qrbr -e H
tools/python/alacritty_preprocess.py - Build Alacritty TOML from source YAML
  • Resolves the custom !xresources YAML tags from home/.Xresources after preprocessing it with pcpp.
  • Emits TOML for make build, replacing the old live watcher-based preprocessing flow.
  • Installs as alacritty-preprocess through tools/python/pyproject.toml, and make build first runs uv sync --project tools/python --locked before invoking it.

Example:

alacritty-preprocess alacritty/alacritty.yml --xresources home/.Xresources

Config Guide

This section starts with the configs that show up most in my day-to-day workflow: shell, Git, editor, browser, and media.

home/.shenv, home/etc/shrc, home/etc/shrc.d/, home/etc/zsh/.zshrc - shell bootstrap stack
  • Establishes XDG-style paths such as XDG_CONFIG_HOME=~/etc and XDG_DATA_HOME=~/var.
  • Builds PATH, MANPATH, editor/pager defaults, shell helpers, aliases, prompt setup, completion, and host-specific extras.
  • Defines many of the high-use helpers and aliases in the history analysis, including p, pl, l, ll, and vim -> nvim.
  • Bash and POSIX shells flow through ~/.profile and home/etc/shrc; zsh uses ~/.zshenv and home/etc/zsh/.zshrc.
  • Also exports many personal environment defaults for Homebrew, pipx, pyenv, Bun, Perl local::lib, cargo, and more.

Notes:

  • Very opinionated
  • Assumes ~/etc, ~/var, ~/bin, and related paths are acceptable in your environment
home/.gitconfig - Git aliases, paging, and signing defaults
  • Adds short aliases like git s, git go, git gl, and commit/push/pull wrappers that refresh GPG agent TTY state. I don't really use these anymore, instead preferring the zsh Git aliases.
  • Pairs with the zsh Git aliases used most in practice: g, gs, gc, ga, and gd.
  • Uses delta or diff-highlight when available.
  • Enables signing by default, rebasing on pull, rerere, and some custom diff generators.

Important:

  • The checked-in config contains a real user name, email, and signing key
  • Change those values before using this file on another machine
home/etc/nvim/ - Lua-based Neovim config
  • Bootstraps lazy.nvim automatically on first run.
  • Uses plugin specs under home/etc/nvim/lua/plugins/ for Treesitter, Oil, Mason/LSP, and lightweight editing plugins.
  • Mason installs LSP servers lazily when matching filetypes are opened.
  • Includes many custom mappings, templates, autocmd helpers, and traditional Vim-like defaults.

Notes:

  • First launch may clone plugins and install language servers
  • If you want a minimal config, copy selectively rather than using this whole tree unchanged
home/etc/qutebrowser/ - Locked-down qutebrowser setup with userscripts
  • Disables autoconfig and sets qutebrowser directly from config.py.
  • Turns JavaScript off by default, then selectively re-enables it through secret-managed allowlists.
  • Loads search engines from ../searchengines.yml and extra settings from secrets.yml.
  • Adds userscript bindings for URL rewriting, alternate download mode, and mpv/umpv launching.

I haven't used qutebrowser for years, so this may be outdated.

Included userscripts:

  • urlsub rewrites YouTube, Reddit, Twitter, Instagram, and some Quora URLs toward alternative frontends or privacy-friendlier forms
  • download-alternate temporarily flips download prompt/remember settings for hint-driven downloads

Notes:

  • Requires PyYAML
  • Expects local files that are not in the repo: searchengines.yml, secrets.yml
  • Some URL rewrite targets depend on third-party public instances
home/etc/mpv/ - Mpv profiles, keybindings, and Lua scripts
  • mpv.conf tunes streaming, caching, subtitles, screenshots, audio, and a set of named profiles.
  • input.conf remaps transport, subtitle, crop, scaling, ontop, and screenshot controls.
  • scripts/ mixes custom Lua helpers with third-party submodule scripts.

Notable custom scripts:

  • chapter_pause.lua pauses automatically at chapter boundaries or end-of-file when toggled
  • scale_window.lua resizes the window relative to the monitor or current window size
  • smart_ontop.lua keeps the window on top only while actively playing

Notes:

  • Initialize submodules for the bundled third-party scripts
  • Assumes a fairly advanced mpv setup with extra scripts and external tools already available
home/etc/ghostty/ and platform packages - Ghostty config
  • The shared config sets theme, cursor behavior, font features, working directory, and general window behavior.
  • Platform-specific files live in darwin/etc/ghostty/config.d/darwin and linux/etc/ghostty/config.d/linux; hostname-specific overrides live in matching host packages such as a485/etc/ghostty/a485.
  • The shared config optionally includes the platform files and the hostname override; matching packages provide the files that exist.

Notes:

  • No manual platform symlink is needed after installing with make install
alacritty/ - Build-only Alacritty config source
  • alacritty/alacritty.yml is the source file.
  • tools/python/alacritty_preprocess.py preprocesses home/.Xresources with pcpp, resolves the custom YAML tags, and writes TOML into build/etc/alacritty/alacritty.toml.
  • make clean removes the generated build/ tree and tools/zig/zig-out build output, but leaves tools/zig/.zig-cache in place.
  • make build regenerates the installed Alacritty config; there is no longer a background watcher.
  • make migrate performs migrations from old installs.

Notes:

  • Depends on uv plus the locked tools/python dependencies
  • Useful if you want terminal colors to track the repo's Xresources palette without runtime preprocessing
home/etc/i3/, home/etc/polybar/, home/etc/dunst/ - Linux desktop stack
  • i3 config provides Vim-like navigation, workspace bindings, scratchpad use, and launches Polybar at startup.
  • Polybar reads colors from Xresources and exposes modules for i3 workspaces, mpd, filesystem, VPN, wifi, ethernet, memory, CPU, battery, temperature, and date.
  • bin/polybar-launch reloads Polybar after exporting XRDB-derived colors.
  • Dunst is configured as the matching lightweight notification daemon.

Notes:

  • Strongly tied to X11/Linux
  • Network interfaces, Mullvad integration, fonts, and battery names are machine-specific
home/.hammerspoon/init.lua - macOS automation
  • Adds hotkeys for window movement and maximizing all windows of the current app.
  • Watches Wi-Fi SSIDs and mutes/reduces speaker output on specific networks.
  • Launches mpv or umpv against URLs in the clipboard.

Notes:

  • Hammerspoon only
  • Relies on local shell environment and installed media tools
darwin/Library/Preferences/, darwin/.macsettings, and darwin/.editrc - macOS overlay
  • darwin/Library/Preferences/com.googlecode.iterm2.plist stores the canonical XML snapshot of the iTerm2 plist at the same path it installs under ~.
  • .macsettings is a manual script of defaults write tweaks such as screenshot location, key-repeat behavior, and menu bar spacing.
  • .editrc adds macOS-specific line editing defaults.

Notes:

  • make install stows darwin/ into $PREFIX but copies darwin/Library/Preferences/*.plist instead of symlinking them
  • make import converts installed plist files back into XML in darwin/Library/Preferences/
  • make uninstall removes stowed macOS files but leaves copied preference plists in place

Other Notable Config Trees

The sections above cover the main pieces. There are also many smaller app configs under home/etc/, including:

  • ranger/, zathura/, streamlink/, fontconfig/, pulse/, xkb/
  • msmtp/, ncmpcpp/, ncdu/
  • transmission-daemon/, tremc/, yt-dlp/, youtube-dl/
  • sxhkd/, sv/, acpid/, crontab.d/

And many classic top-level dotfiles live directly in home/, such as:

  • home/.tmux.conf, home/.screenrc, home/.inputrc, home/.Xresources
  • home/.emacs, home/.vim/, home/.mutt/, home/.gnupg/
  • home/.bashrc, home/.profile, home/.zshenv

Generated Files And Safe Editing

  • Edit source files in bin/, home/, alacritty/, darwin/, and tools/; avoid hand-editing build/
  • build/, tools/zig/zig-out/, tools/zig/kcov-output*/, and tools/python/build/ are generated artifacts

Validation And Maintenance

Useful commands after changes:

make dryrun
make build
make test

When changing uniqp behavior and expected fixture output:

cd tools/zig
./generate-test-output.sh ./zig-out/bin/uniqp

When changing the generated config source under alacritty/:

make build

When changing Darwin plist XML snapshots:

make install

Practical Reuse Advice

  • If you are adopting this repo on your own machine, copy selective configs before running make install
  • If you do want the full setup, update the personal identity, secrets, and service endpoints first
  • The cleanest reuse path is usually: keep the CLI tools, borrow the configs selectively