Cross-platform development environment setup using chezmoi + ansible.
- chezmoi: manages config files (dotfiles)
- ansible: installs system dependencies and tools
flowchart TB
subgraph Bootstrap["1. Bootstrap"]
BREW[Homebrew macOS]
UV[uv] --> ANS[ansible]
MISE[mise]
end
subgraph Chezmoi["2. chezmoi apply"]
DOT[Config files<br/>~/.gitconfig, ~/.config/]
ANSDIR[~/.ansible/]
BREWFILE[Brewfiles<br/>~/.config/homebrew/]
end
subgraph Ansible["3. Ansible Playbooks"]
BASE[base]
ZSH[zsh]
NVIM[neovim]
DEVTOOLS[devtools]
UVTOOLS[python_uv_tools]
CARGO[rust_cargo_tools]
LLMTOOLS[llm_tools]
end
subgraph BrewBundle["4. Brew Bundle opt-in"]
CASKS[GUI Apps]
MAS[App Store]
end
Bootstrap --> Chezmoi --> Ansible
Chezmoi -.-> BrewBundle
MISE -.->|Node.js, Rust| NVIM
MISE -.->|Rust| CARGO
UV -.->|Python tools| UVTOOLS
UV -.->|LiteLLM| LLMTOOLS
BREW -.->|casks| CASKS
BREW -.->|formulae / casks| LLMTOOLS
curl -fsSL https://raw.githubusercontent.com/daviddwlee84/dotfiles/main/bootstrap.sh | bashInstalls uv (if missing) and launches dotfiles-init — a Python wrapper that groups the ~19 chezmoi prompts into a clean multi-select UI, offers pre-set bundles (personal-mac / work-mac / server-linux / cloud-vm / minimal), checks SSH keys, then calls real chezmoi init with your answers pre-filled. Full docs: scripts/init/README.md.
On first-run or slow networks the uv resolver can sit silently for a few minutes — use the verbose form to see timestamped progress + uv --verbose resolver output + set -x:
curl -fsSL https://raw.githubusercontent.com/daviddwlee84/dotfiles/main/bootstrap.sh | DOTFILES_BOOTSTRAP_VERBOSE=1 bashPass extra args through to the wrapper via bash -s -- (e.g. run the schema-parity checker instead of the default init):
curl -fsSL https://raw.githubusercontent.com/daviddwlee84/dotfiles/main/bootstrap.sh | bash -s -- doctorBehind GFW? Set DOTFILES_RAW_URL / DOTFILES_REF to point at a mirror, or use the non-interactive path below.
# Installs chezmoi to ~/.local/bin and walks through the raw prompts one-by-one.
# Uses HTTPS to avoid SSH key setup on fresh machines.
export GITHUB_USERNAME=daviddwlee84
sh -c "$(curl -fsLS get.chezmoi.io)" -- -b "$HOME/.local/bin" init --apply "https://github.com/$GITHUB_USERNAME/dotfiles.git"
# After install, switch the chezmoi source repo to SSH (so future `chezmoi update` uses your SSH key):
# chezmoi cd
# git remote set-url origin git@github.com:$GITHUB_USERNAME/dotfiles.git
# exitThis automatically:
- Bootstraps Homebrew (macOS/Linux), uv, mise, ansible
- Deploys all config files
- Runs ansible playbooks (git, ripgrep, fd, neovim, etc.)
- Runs brew bundle (if
installBrewAppsis enabled, or on macOS ifinstallAiDesktopApps/installGamingAppsis enabled)
Some Linux environments — notably CentOS 7 + AD/LDAP user with high UID where pam_systemd never created /run/user/<UID>/ — leave the chezmoi-spawned bootstrap script unable to open /dev/tty, so the shared sudo helper can't prompt and Linuxbrew aborts with Insufficient permissions to install Homebrew to "/home/linuxbrew/.linuxbrew". The fix is to pre-stage the password via CHEZMOI_SUDO_PASSWORD_FILE (the same env-var fleet_apply uses over SSH).
The repo ships scripts/apply_with_sudo.sh to handle the prompt → 0600 tmpfile → export → run → shred dance:
# First time (chezmoi not installed yet):
sh -c "$(curl -fsLS get.chezmoi.io)" -- -b "$HOME/.local/bin" # install chezmoi only
bash <(curl -fsSL https://raw.githubusercontent.com/$GITHUB_USERNAME/dotfiles/main/scripts/apply_with_sudo.sh) \
--init "https://github.com/$GITHUB_USERNAME/dotfiles.git"
# Re-runs (after chezmoi source is cloned at ~/.local/share/chezmoi):
just apply-with-sudo # interactive prompt
just apply-with-sudo --pass-from-env # reads $SUDO_PASSWORD
SUDO_PASSWORD=xxx just apply-with-sudo --pass-from-env # one-linerThe wrapper validates the password against sudo -v before invoking chezmoi, then shreds the tmpfile on exit (any signal). Inside chezmoi's run-scripts, sudo_session_init adopts the file, moves it into the shared state dir, and spawns the watchdog that keeps the sudo timestamp warm.
Even simpler alternative if you have root and don't mind editing sudoers: add $USER ALL=(ALL) NOPASSWD: ALL to /etc/sudoers.d/99-bootstrap, run chezmoi apply, then remove the line. The shared helper short-circuits cleanly when sudo is truly passwordless.
CentOS 7 also blocks Linuxbrew install because system curl 7.29 is older than Homebrew's 7.41 minimum, and Homebrew bottles built on Ubuntu 22.04+ don't run on glibc 2.17 anyway. Linuxbrew is fully optional on Linux — every ansible task that uses it has a non-brew fallback (cargo / GitHub musl release / AppImage). On CentOS 7, drop a stub
~/.local/bin/brew(#!/bin/sh\nexit 0) before applying to short-circuit the bootstrap's brew step.
Full diagnosis: pitfalls/bootstrap-no-tty-sudo-prompt-skipped.md and pitfalls/centos7-noroot.md.
~/.local/binand the rest of the shared layer (mise/uv/cargo/bun PATH, plus Linux OpenCode's~/.opencode/bin) are exported via~/.config/shell/00_exports.sh, sourced by both the chezmoi-managed~/.bashrcand~/.zshrc.- On sudo-enabled machines, the ansible role for your
primaryShellchoice switches your login shell automatically (zshrole forprimaryShell=zsh,bashrole forprimaryShell=bash). Log out / back in to pick it up, or runexec zsh/exec bashnow. primaryShell=bashon macOS additionally installs Homebrew bash 5.x and adds it to/etc/shells(system bash 3.2 is too old for oh-my-bash plugins + ble.sh). zsh-primary mac users see no extra brew install.- On
noRoot=trueinstalls, login shell isn't changed — runexec zsh/exec bashper session, or ask your sysadmin:sudo chsh -s "$(command -v zsh)" $USER(orbash). - Open a new shell after install so PATH changes take effect. Both
~/.bashrcand~/.zshrcare deployed regardless ofprimaryShellchoice, so the other shell still works ad-hoc.
During chezmoi init, you'll be prompted for optional installs. The prompts
themselves are defined once in scripts/init/dotfiles_init.py
(PROMPTS) and generated into .chezmoi.toml.tmpl + Dockerfile; this table
is coverage-checked against that list by dotfiles_init.py gen --check.
| Option | Default | Description |
|---|---|---|
installCodingAgents |
true | Claude Code, Codex CLI, OpenCode, Cursor, Copilot, Gemini CLI, Antigravity CLI (agy), RTK, td, sidecar, specify-cli, etc. |
installBitwarden |
false | Bitwarden CLI (bw) + Desktop app (desktop profiles) with SSH agent auto-detection |
installPythonUvTools |
true | Python CLI tools via uv (mlflow, sqlit-tui, tmuxp, etc.) |
installJsCliTools |
true | Standalone JS/npm CLI utilities (readability-cli for terminal web reader, etc.) |
installLlmTools |
false | Local LLM tools: Ollama, LiteLLM, llmfit, models |
installAiDesktopApps |
false | macOS AI desktop apps via Homebrew Brewfile (Claude, ChatGPT, OpenCode Desktop, Antigravity, Codex Desktop on Apple Silicon; ollama-app also requires installLlmTools=true). macOS only. |
installBrewApps |
false | General GUI apps via Homebrew Brewfile (terminals, browsers, utilities, mas; excludes AI desktop apps). Desktop profiles only (macos/ubuntu_desktop). |
installGamingApps |
false | Gaming apps, currently Steam only. macOS uses Homebrew Cask; ubuntu_desktop uses Valve's official apt repo / steam-launcher. Desktop profiles only. |
installInputMethod |
false | Traditional Chinese input methods (McBopomofo, RIME/Squirrel on macOS; ibus-rime on Linux). Desktop profiles only (macos/ubuntu_desktop). |
discordChannel |
flatpak |
Discord install channel on ubuntu_desktop: flatpak (recommended), deb, or none. macOS uses the Brewfile cask; servers skip. |
installNiri |
false | niri scrollable-tiling Wayland compositor, built from source (ubuntu_desktop only). Installs the binary + GDM session/systemd files; NVIDIA gets a VRAM application-profile automatically. See docs/playbooks/niri.md. |
installNetworkingTools |
false | Networking CLI tools (nmap, mtr, httpie, gping, trippy, bandwhich, rustscan, etc.) |
installTunnelTools |
false | Tunnel tools (ngrok, cloudflared) — expose localhost / SSH reverse tunnels |
installIacTools |
false | Infrastructure-as-Code CLIs (Azure CLI, Terraform, OpenTofu) |
installMediaTools |
false | Media/AV CLI tools (ffmpeg, ImageMagick, exiftool, libvips). Also satisfies vhs's runtime ffmpeg dep. |
installMediaControl |
false | System media-control CLIs for the sys* shell helpers (media-control): nowplaying-cli + switchaudio-osx (macOS), playerctl (Linux). Unlocks full sysplay/sysnow; built-in sysvol/sysmute work without it. |
installDotnetTools |
false | .NET SDK via mise + dotnet global tools (azure-cost-cli for Azure cost analysis) |
installExtraRuntimes |
true | Extra mise runtimes (rust, bun, ruby; ~1.8GB) + their cargo/gem tool roles. Node is always installed regardless. Set false for lean cloud VMs / CI. |
installAuditd |
false | Linux audit framework (auditd) + baseline rules (identity / sudoers / sshd_config / privileged-exec). Linux only. See docs/sysadmin/auditd.md. |
installHomelabTools |
false | Homelab hardware-monitoring CLIs (lm-sensors, smartmontools, ipmitool, nvme-cli, storcli) + hw-* shell helpers, each gated on detected hardware. Linux only; no-op in VMs. See docs/sysadmin/hardware.md. |
useChineseMirror |
false | Switch Homebrew / pip / npm / cargo / etc. to China (GFW) mirrors |
gitleaksAllRepos |
false | Run gitleaks on ALL git repos, not just those with .pre-commit-config.yaml |
backupMode |
smart |
Back up existing dotfiles before apply: smart (only files chezmoi overwrites), full (fixed allowlist), off (skip) |
allowPartialFailure |
false | Continue installing other Ansible roles if one role fails |
noRoot |
false | Skip sudo-requiring tasks (for servers without root access). Linux only. |
motdStyle |
figlet |
SSH login banner style: figlet (~6 lines, default), fastfetch-slim (figlet + fastfetch w/o logo, ~10 lines), fastfetch-full (full distro logo + everything, ~22 lines). Runtime override: MOTD_STYLE=... in ~/.zshrc.adhoc. See docs/zsh/motd.md. |
primaryShell |
zsh |
Primary interactive shell: zsh (default, full-featured) or bash (oh-my-bash + ble.sh, close-to-zsh UX). Both ~/.zshrc and ~/.bashrc deploy on every host; this only governs which shell chsh switches to as the login shell. See docs/shells/bash.md for the bash side's known gaps and tradeoffs. |
enableVimMode |
true | Vim-style modal editing in shells (zsh-vi-mode, set -o vi, ble.sh) + tmux vim navigation. Does NOT affect Neovim. See docs/this_repo/vim-mode.md. |
To change options later, don't hand-edit ~/.config/chezmoi/chezmoi.toml — run the reconfigure tool, which seeds the same TUI from your current values and re-applies via chezmoi init --apply --prompt:
just reconfigure # interactive (or: czcfg)
just reconfigure -- --set installLlmTools=true --yes # non-interactive single keySee scripts/init/README.md → "Reconfigure".
~/.gitconfig- Git configuration; managed viamodify_sogh auth setup-git's[credential "..."]blocks (with the per-machine absolute path togh) survivechezmoi apply; default pull behavior is rebase + autostash (pull.rebase=true,rebase.autoStash=true) so LazyGit'sppull keeps local dirty work across the rebase (docs, LazyGit notes)~/.config/git/hooks/- Global Git hooks (via a globalcore.hooksPath): managedpre-commit(gitleaks) + Git LFS hooks, all hardened to skip gracefully — PATH-robust and non-fatal so a stripped-PATH clone likebrew tapcan't break (see pitfall)~/.config/gh-dash/config.yml- Global gh-dash config, withdiffnavas the diff pager~/.config/lazygit/config.yml- Global LazyGit config, withdeltaas the custom diff pager~/.config/bat/themes/tokyonight_night.tmTheme- Managed Tokyo Night theme for bat; apply rebuilds the bat cache and clears it if the cache is incompatible withdelta~/.config/nvim/- Neovim (LazyVim) configuration; system clipboard viaunnamedplus, with an SSH-conditional OSC 52 override so remote yanks reach the local clipboard (pairs with tmuxset-clipboard on, see docs)~/.config/uv/uv.toml- uv package manager config (PyPI: official / Aliyun+TUNA+USTC viauseChineseMirror)~/.cargo/config.toml- Cargo registry config (crates.io / TUNA sparse index viauseChineseMirror)~/.npmrc- npm registry config (official/npmmirror viauseChineseMirror)~/.config/.bunfig.toml- Bun global registry config (official/npmmirror viauseChineseMirror)~/.gemrc- RubyGems source + common options (TUNA mirror; only managed whenuseChineseMirror=true, gated via.chezmoiignore.tmpl)~/.condarc- Conda/Mamba channels with TUNA Anaconda mirror (only managed whenuseChineseMirror=true, preservesauto_activate_base/user channel prefs)~/.config/zsh/00_exports.zsh- Env-var mirror bundle (Homebrew / Rustup / mise / GOPROXY) whenuseChineseMirror=true; also exported by bootstrap + ansible runner scripts so first-run installs and ansible subprocesses inherit them (docs)~/.config/zsh/99_local_proxy.zsh- Create-only machine-local proxy override stub; uncommentLOCAL_PROXY_URL/LOCAL_PROXY_SOCKS_URLhere to pin Clash ports without future chezmoi diffs~/.docker/config.json- Docker clientproxies.defaultblock (auto-injected from$LOCAL_PROXY_URL/$LOCAL_PROXY_SOCKS_URL; preservesauths/credsStorevia chezmoi modify-script) (docs)~/.config/docker/daemon.json- Rootless Dockerregistry-mirrors(DaoCloud / USTC / NJU / ...; Linux +useChineseMirroronly) (docs)~/.config/alacritty/- Alacritty terminal config (CSI-u keybindings forCtrl+Numbertmux window switching,option_as_altfor Meta keys)~/Library/Application Support/{Code,Cursor,Antigravity}/User/(macOS) +~/.config/{Code,Cursor,Antigravity}/User/(Linux) - Editor settings overlay:modify_settings.jsondeep-merges a 6-key baseline (Hack Nerd Font Mono, relative line numbers, format on save, smart-accept suggestion, terminal font) into each editor's livesettings.jsonwithout overwriting other keys;create_keybindings.jsonseeds 5 universal keybindings on a fresh machine and never overwrites editor-added entries. Canonical templates live under.chezmoitemplates/editor/;.chezmoiignore.tmplgates each editor dir with astatpresence check so uninstalled editors never produce phantom directories.~/.config/ghostty/config- Ghostty/cmux terminal config (macos-option-as-altfor tmux Meta keybindings, disabled ligatures, explicitclipboard-write = allow/clipboard-read = askfor OSC 52) (docs)~/.config/starship.toml- Starship cross-shell prompt config~/.xonshrc+~/.config/xonsh/rc.xsh- Optional Xonsh (Python-superset shell) startup + extension sandbox. Installed viapython_uv_toolswith a curated set of xontribs (jedi/zoxide/pipeliner/fzf-widgets); not a login shell — drop in withxonshfor ad-hoc Python-in-shell work (docs)~/.config/direnv/direnvrc- direnv helper functions, including.venv-aware Python activation~/.config/yazi/- Yazi file manager config (v0.3.3+ syntax, docs)yazi.toml- Main config with open rules and openers (o/O)keymap.toml,theme.toml- Optional customization files (stubs provided)
~/.config/btop/btop.conf+~/.config/btop/themes/catppuccin_mocha.theme- btop system-monitor baseline (Catppuccin Mocha theme, process tree view,vim_keysmirrorsenableVimMode); seeded once viacreate_so btop's on-exit config rewrites never cause chezmoi drift; catppuccin theme vendored like bat's (docs)~/.claude/- Claude Code settings~/.cursor/cli-config.json,~/.config/opencode/opencode.json,~/.codex/config.toml- Coding-agent CLI overlays: eachmodify_*script deep-merges a small managed baseline (preferences, features, curated plugins) into the CLI's live config, preserving auth tokens, per-project trust, sessions, and other machine-local state. Codex specifically round-trips[projects]and[marketplaces.*]so per-project trust paths never get clobbered. OpenCode legacy~/.config/opencode/config.jsonis migrated to the modern filename by a one-shotrun_once_before_50_opencode_migrate.sh.tmpl. Full design: docs/tools/agent-overlays.md.~/.specstory/cli/config.toml- Global SpecStory defaults forspecstory runacross all projects~/.tmux.conf- Tmux configuration with TPM plugins (resurrect, continuum, tmux-floax floating pane, tmux-fzf for fuzzy keybinding/session/pane search rebound toprefix+?, tmux-fzf-url, tmux-open), vim-style copy mode with clipboard yank, capture-pane helpers (prefix+y/Y/C-y), a native popup menu (prefix+Spaceorprefix+e, script-driven with submenus and height-aware trimming) with layout/resize/session management, popup shell (prefix+\``) and lazygit popup (prefix+G), a glow-rendered cheatsheet (prefix+Space→?), vim-style pane nav,Ctrl+1..9quick window switching (CSI-u terminals), extended-keys for coding agents (includingCtrl+/in Neovim), sesh keybindings (prefix+gfzf picker,prefix+Ttelevision picker,prefix+Obuilt-in picker,prefix+Wwindow picker,prefix+Slast), responsive Catppuccin status bar (adapts modules to terminal width for mobile), and config reload onprefix+R` (docs)~/.dotfiles/bin/x- Cross-platform terminal wrapper forcopy/copy-file/paste/open;x copyhas an OSC 52 fallback for SSH text copy, whilex copy-filecopies desktop file objects for Finder / Files paste (docs)~/.dotfiles/bin/sms- Huawei router SMS reader (HiLink XML API; verification-code extraction, clipboard integration, TV channel) (docs)~/.config/sms/config.toml.example- Starter config for thesmsCLI (realconfig.tomlis created at runtime, never committed)~/.config/television/cable/sms.toml- Television channel for browsing router SMS inbox~/.dotfiles/bin/mi-router- Read-only inspector for Xiaomi / MiWiFi routers (LuCI JSON API;info/wifi/devices/mdns/rawsubcommands; sister tool tosms) (docs)~/.config/mi-router/config.toml.example- Starter config for themi-routerCLI (realconfig.tomlis created at runtime, never committed)~/.config/sesh/sesh.toml- Sesh session manager config (named sessions with windows, wildcards, defaults) (docs)~/.config/worktrunk/config.toml- Worktrunk (wt) git-worktree manager config — aliases (wt sw/ls/rm/cc/oc); hooks & LLM commit generation kept commented as opt-in (workflow playbook)~/.config/workmux/config.yaml- Workmux (wm) git-worktree + agent status orchestrator —nerdfont:false+status_format:false(we self-manage the tmux format intheme.catppuccin.conf); 🤖/💬/✅ icons fed by Claude/OpenCode hooks (playbook)~/.config/television/cable/sesh.toml- Television custom cable channel for sesh (overrides built-in with richer sources and actions)~/.config/television/cable/lan-devices.toml+~/.config/television/lan-scan.sh- Television channel for LAN device discovery with open ports, MAC/vendor, hostname, RTT; streams results incrementally via a cache file (docs)~/.config/television/cable/azure.toml+~/.config/television/azure-{source,preview,rotate-ip}.sh- Television channel for Azure resources (Resource Groups / VMs / Public IPs / NICs+NSGs / all) with VM actions (restart, start/stop, deallocate, rotate public IP, SSH, open in portal) and graceful login prompt (docs)~/.config/television/cable/clash.toml+~/.config/television/clash-switch.sh- YAML-backed Clash / mihomo Television channel; auto-resolves Clash's activeprofiles/<time>.ymlfrom~/.config/clash/profiles/list.yml, falls back to legacyconfig.{yaml,yml}, supports proxy-group switching, and turns empty proxies/groups/rules tabs into informative placeholder rows instead of blank panes (docs)~/.config/television/cable/clash-api.toml+~/.config/television/clash-switch.sh- Live Clash external-controller Television channel for/proxies,/rules,/configs, and/connections; supports remote controllers viaCLASH_CONTROLLER/CLASH_SECRETand proxy-group switching without adding an in-UI host switch (docs)~/.config/television/cable/logs.toml- Television channel for fuzzy-browsing log files with tailspin/bat previews,Enteropens inlnav,Alt+Tlive tails with tailspin (docs)~/.config/television/cable/services.toml- Cross-platform services channel for systemd (Linux) / launchd (macOS) — 5 source cycles (running/all/failed/user-scope/installed-on-disk), tailspin log preview, Alt-key lifecycle actions (restart/stop/start/reload/enable), sudo-aware (docs)~/.config/television/cable/skills.toml+~/.config/television/cable/skills-walk.toml- Two Television channels for browsing agent skills:skillsparsesnpx skills list(project + global, with Agents column),skills-walkwalks~/.agents/,~/.claude/,~/.codex/,~/.cursor/skills-cursor/directly without invoking npx; both preview SKILL.md with bat~/.config/television/cable/agent-sessions.toml+~/.config/television/agent-sessions.py- Unified Television channel for coding-agent sessions across OpenCode (SQLite), Claude Code (JSONL), Codex (JSONL), Cursor Agent CLI (store.db), and Cursor IDE composer chats (state.vscdb); 6 source cycles (all/per-agent), content-searchable via first-user-message snippet,Enterprintscd <dir> && <agent> --resume <id>,Alt+Tresumes in current shell (Cursor IDE: opens workspace folder viacursor <dir>since IDE has no chat-resume CLI flag)~/.config/zsh/tools/02_shell_integration.zsh+03_tmux_capture.zsh+04_ai_capture.zsh- aicapture layer: OSC 133 prompt markers +cpout/cpcmd/cpblock [N]scrollback helpers +aifix/aiexplainLLM wrappers (advisory-only; Haiku/mini by default) +aiblockPython TUI (multi-select + spawn agent window). Full guide: docs/tools/aicapture.md; design-space comparison against thefuck/Warp/wut/tmuxai/atuin in docs/this_repo/instant-llm-fix-prior-art.md~/.config/zsh/tools/05_aisuggest.zsh- aisuggest prompt-time NL→shell ghost text. Type a description, pressAlt+;, see the suggested command, Tab to accept. Routes through the same_aiagent_invokeautodetect (claude → opencode → codex → cursor-agent) — no API key needed. Inspired bykylesnowschwartz/zsh-ai-cmd; architecture in docs/tools/zsh-inline-ai.md~/.config/zsh/tools/22_sesh.zsh- Sesh keybinding (Alt+Sfor session picker),shere/srootsession helpers (supports bare command args, e.g.shere specstory run codex), and shell completion~/.config/zsh/tools/36_pueue.zsh- Pueue queue summary helper (pqsum)~/.config/zsh/tools/41_github.zsh- GitHub helper withghgetfor downloading a repo subdirectory from a tree URL~/.config/zsh/tools/50_networking.zsh- Networking aliases plus loopback proxy helpers (proxy-on,proxy-off,proxy-status,proxy-test,withproxy) that prefer$LOCAL_PROXY_URL, then Clash's active config, then loopback port probing~/.config/shell/37_lazygit.sh- LazyGit shell alias:lg(shared by zsh/bash)~/.config/zsh/tools/28_tldr.zsh-tldrfhelper withTLDR_LANGUAGESfallback order~/.config/zsh/tools/29_log_tools.zsh- Log viewer wrappers:catl(tspin colorful cat),lessl(ccze + less),logtail(tspin live follow) (docs)~/.config/shell/29_marimo.sh-marimoshell completion (shared by zsh/bash; per-shell_MARIMO_COMPLETEdispatch)~/.config/shell/26_eza.sh-eza-backedls/la/llaliases (shared by zsh/bash), pluslltfor tree view with git-aware subdirectory context~/.config/zsh/tools/32_try.zsh-try-clishell integration, with defaultTRY_PATHand graduate-friendlyTRY_PROJECTS~/.config/zsh/tools/94_ssh_agent.zsh- SSH agent with Bitwarden-first fallback to persistentssh-agent(auto-loads keys)~/.config/zsh/tools/95_bitwarden.zsh- Bitwarden CLI zsh completion~/.zlogin- SSH-gated MOTD banner with 3 styles selectable via themotdStylechezmoi prompt:figlet(default, ~6 lines),fastfetch-slim(~10 lines), orfastfetch-full(full distro logo). Silent for local terminals, scp/rsync/fleet-apply, and tmux panes. Runtime overrideMOTD_STYLE=...or full opt-outMOTD_DISABLE=1in~/.zshrc.adhoc(docs)~/.ssh/config- SSH main config skeleton (create-only:Include ~/.ssh/config.d/*+ conservativeHost *defaults)~/.ssh/config.d/00-defaults- SSH global defaults stub (commented examples only)~/.ssh/config.d/git- SSH host entries forgithub.comandgitlab.comwith commented multi-account/Bitwarden examples~/.config/tmuxp/claude-sidecar.yaml- tmuxp workspace for Claude + Sidecar~/.config/tmuxp/coding-agent.yaml- tmuxp workspace for coding agent (nvim 75% | specstory 25%, btop tab)~/.config/tmuxinator/coding-agent.yml- tmuxinator workspace for coding agent (alternative to tmuxp, native sesh integration)~/.config/tmuxinator/chezmoi.yml- tmuxinator workspace for chezmoi session (shell + lazygit + nvim overrides, native sesh integration)~/.config/zellij/config.kdl- Zellij config (locked default mode for coding agent compatibility, kitty keyboard protocol)~/.config/zellij/layouts/claude-sidecar.kdl- Zellij layout for Claude + Sidecar~/.config/herdr/config.toml- Herdr config (Rust terminal multiplexer + AI-agent orchestrator; trial tool that coexists with tmux, not a replacement). Catppuccin theme, tmux-like keybindings,tv/lazygit command-panes, herdr-plus Projects layouts, and native agent-state indicators. Managed via amodify_tomlkit overlay (enforces our tables every apply, preserves herdr's runtime writeback). Behavior model (two cwd fields, OSC7 tracking, nesting inside tmux) + feasibility matrix + documented gaps (seamless nvim nav, OSC133 copy-mode) in docs/tools/herdr.md~/.config/homebrew/- Brewfiles for GUI apps (macOS casks + mas) plus selected CLI formulas (e.g.,tailscale)
SSH files are managed as create-only templates: if ~/.ssh/config already exists, it is not overwritten. In that case, add Include ~/.ssh/config.d/* to your existing config manually to load the managed snippets.
Vendored upstream sources are declared in .chezmoiexternal.toml.tmpl and auto-refreshed weekly by chezmoi (refreshPeriod = "168h"). Force an immediate pull with chezmoi apply --refresh-externals.
~/.oh-my-zsh+ 4 custom plugins (zsh-autosuggestions,zsh-syntax-highlighting,zsh-completions,zsh-vi-mode*) — *zsh-vi-modeis conditional on theenableVimModechezmoi prompt (defaulttrue); see docs/this_repo/vim-mode.md.~/.tmux/plugins/tpm(TPM)~/.fzf(Linux only; apt version lacks--zsh)~/.local/share/toolkami/toolkami.rb
See docs/tools/chezmoi-prefixes.md for details and when to add entries here vs. ansible.
- Base: git, git-lfs, curl, ripgrep, fd, just, build tools
- Neovim: >= 0.11.2 with LazyVim dependencies
- LazyVim deps: fzf, lazygit, tree-sitter-cli, Node.js
- Git review stack: gh, glab, gh-dash (via
ghextension), diffnav, git-delta, lazygit - Markdown reader:
glowfor terminal Markdown rendering, plusreadurl <url>/readlocal/readnode/readrawto render web pages as markdown in the terminal with auto proxy fallback (see docs/tools/web-reader.md) - Shell testing:
bats(bats-core) — Bash test runner with TAP/JUnit output - Structured-data CLIs:
jq(JSON),yq(YAML/JSON; Mike Farah Go build),dasel(YAML/TOML/XML/JSON/CSV unified query & modify),jnv(interactivejqJSON viewer);taplofor TOML format/lint - DuckDB CLI:
duckdb(Homebrew on macOS, official downloads on Linux) - rclone: cloud storage sync CLI (Homebrew on macOS, official downloads on Linux)
- Ruby gem tools:
try-clifor ephemeral workspaces with graduate-to-project defaults,tmuxinatorfor declarative tmux session layouts (native sesh integration), plustoolkami - Coding Agents (optional): Claude Code,
claude-hudstatusline plugin, Codex CLI, CodexBar, OpenCode, Cursor CLI, Copilot CLI, Gemini CLI, RTK, SpecStory, OpenChamber, td, sidecar, specify-cli - Agent pane discovery:
tv agent-paneschannel + tmuxprefix + apopup — find which Claude/Codex/OpenCode/Cursor sessions are running in any tmux pane and jump straight to them; companionrecon(Claude-only fast popup) installed via cargo. See docs/tools/agent-panes-discovery.md - Bitwarden (optional): Bitwarden CLI (
bw) via npm, Desktop app (snap/deb on Linux, cask on macOS) on desktop profiles, with zsh completion and SSH agent auto-detection - LLM tools (optional): Ollama local runtime, LiteLLM proxy,
llmfithardware-fit recommender,modelsTUI/CLI for model discovery and benchmarks - Input Methods (optional): McBopomofo + RIME (Squirrel on macOS, ibus-rime on Linux)
- Networking tools (optional): nmap, arp-scan, mtr, iperf3, doggo, httpie, gping, trippy, bandwhich, speedtest, rustscan
- Linux audit framework (optional, Linux only,
installAuditd=true):auditd+ baseline rule set (identity / sudoers / sshd_config / privileged-exec watches), plusaudit-*shell helpers andtv sessions/tv sudo-history/tv audit-eventsTelevision channels for the routine "who did what on this server" queries. Full guide: docs/sysadmin/; ansible role: docs/playbooks/auditd.md - Homelab hardware monitoring (optional, Linux/physical only,
installHomelabTools=true): hardware-monitoring CLIs (lm-sensors, smartmontools, ipmitool, nvme-cli, storcli) installed only when the matching hardware is detected, plus thehw-*shell helper family (hw-fans,hw-temps,hw-raid,hw-smart,hw-status…) for the "is this server's hardware healthy?" sweep. Full guide: docs/sysadmin/hardware.md - Media / AV tools (optional,
installMediaTools=true): ffmpeg, ImageMagick, exiftool, libvips. Plus three zsh helpers (compress-video,extract-audio,to-wav16k) atdot_config/zsh/tools/29_media.zsh. Also satisfiesvhs's runtime ffmpeg dep. - System audio & media control (cross-platform):
sys*shell helpers —sysvol/sysmute(built-in, always available) andsysplay/sysnow(withinstallMediaControl=true, which addsnowplaying-cli/switchaudio-osxon macOS andplayerctlon Linux). Enables remote one-key mute viafleet exec --login -- sysmute on. See docs/tools/media-control.md. - .NET tools (optional): .NET SDK via mise +
azure-cost-cli(Azure cost analysis); see docs/tools/dotnet-tools.md - Docker: OrbStack (macOS) or Docker Engine (Linux)
- Cargo tools: pueue (process queue manager),
recon(Claude Code tmux dashboard — see agent pane discovery) - GUI Apps (macOS): general terminals, editors, browsers, network tools, and utilities via Brewfile when
installBrewApps=true, including developer apps likedbeaver-communityandsuperset(Apple Silicon only); AI desktop apps via Brewfile wheninstallAiDesktopApps=true(claude,chatgpt,opencode-desktop,antigravity,codex-appon Apple Silicon only,codeislandnotch HUD for coding-agent activity, andollama-apponly wheninstallLlmTools=true); Steam via Brewfile wheninstallGamingApps=true; Tailscale Desktop via Homebrew Cask; Tailscale CLI viabrew "tailscale"in shared Brewfile wheninstallBrewApps=true.
- Homebrew (macOS): Package manager for macOS
- uv: Python package manager for ansible
- mise: Runtime manager for Node.js and Rust (pins versions from
~/.config/mise/config.toml; upgrade withjust upgrade-mise, see Keeping tools up-to-date) - Dev tools: bat, bats, gh, glab, diffnav, git-delta, git-graph, eza, tldr, glow, gum, vhs, freeze, thefuck, zoxide, direnv, yazi, superfile, tmux+tpm, sesh, worktrunk (workflow playbook), workmux (playbook), zellij, btop, htop, taplo, television, pandoc, witr, figlet, toilet, lolcat, fastfetch (SSH login banner)
- Log viewers: tailspin (
tspin), lnav, grc, ccze (Linux only — no Homebrew formula) — pluscatl/lessl/logtailzsh wrappers and atv logsTelevision channel (docs) - GUI Apps on Linux (
gui_appstag onubuntu_desktop):gui_apps_linuxbundles Alacritty (cargo), AppImageLauncher (PPA with.debfallback, Lite variant for noRoot), VSCode (Microsoft apt repo), Cursor (.deb), Discord (Flatpak default or.deb), optional Steam via Valve apt repo wheninstallGamingApps=true, Zen Browser (AppImage +.desktopentry for launcher search), andlibfuse2for AppImage compatibility. macOS equivalents ship viaBrewfile.darwin.tmpl(Arc + Discord + optional Steam casks there). See docs/tools/appimage.md for AppImageLauncher install paths,ail-cliusage, and Ubuntu 24.04 AppArmor gotchas. - niri (optional,
installNiri=true,ubuntu_desktoponly): niri scrollable-tiling Wayland compositor built from source via theniriansible role (binary →/usr/local/bin, GDM session + systemd user units, NVIDIA VRAM application-profile when an NVIDIA GPU is present). Starter config atdot_config/niri/config.kdl. Full guide: docs/playbooks/niri.md. - AeroSpace (macOS, with
installBrewApps=true): AeroSpace tiling window manager (cask inBrewfile.darwin.tmpl). Config is chezmoi-managed at the XDG path~/.config/aerospace/aerospace.toml(legacy~/.aerospace.tomlis auto-removed via.chezmoiremoveto avoid AeroSpace's both-configs-present error). macOS counterpart to niri on Linux — keybinds (alt + hjkl/arrows,alt+enterterminal) are aligned. - Starship: Cross-shell prompt (replaces oh-my-zsh theme)
- Python tools (via uv): thefuck, apprise, sqlit-tui, dotenv, git-filter-repo, mlflow, tmuxp, trafilatura, marimo, jupyter (lab + classic notebook, with marimo-jupyter-extension)
- JS CLI tools (via npm) (optional): readability-cli (
readable— Mozilla Readability forreadnodeterminal web reader) - NerdFonts: Hack Nerd Font for terminal emulators
- Infrastructure & virtualization: docs/infra/ — Proxmox / ESXi / OrbStack / UTM / VirtualBox / libvirt comparison; CephFS / BeeGFS / NFS / Lustre shared storage; SLURM / Kubernetes / Nomad compute scheduling; FreeIPA + shared-home patterns. Documentation only; nothing is installed by chezmoi for these.
| Platform | Package Manager | Notes |
|---|---|---|
| macOS | Homebrew | Full support |
| Ubuntu Desktop | apt + snap (GUI apps only) | Full support |
| Ubuntu Server | apt + GitHub binaries | Full support; no snap (why) |
| Ubuntu Server (no root) | GitHub binaries + mise + cargo + uv | noRoot=true, tools installed to ~/.local/bin |
| Raspberry Pi 5 (64-bit OS) | apt + Linuxbrew | Full support (same as Ubuntu Server) |
| Raspberry Pi 4 (32-bit OS) | apt + GitHub binaries | Linuxbrew skipped; some tools without armhf builds are skipped |
chezmoi diff # Preview changes
chezmoi apply # Apply config files
chezmoi cd # Go to source directory
# Re-run ansible manually
cd ~/.ansible && ansible-playbook playbooks/macos.ymlchezmoi apply is deliberately install-only — ansible roles mostly use
state: present / creates: idempotency so re-applying never silently bumps
every tool on your machine. For explicit upgrades, use the dedicated entry
points (see docs/this_repo/upgrades.md for the full matrix):
just upgrade-all # externals + brew + mise + uv + npm + cargo + dotnet + gem + agents + plugins
just upgrade-dry-run # preview without executing
# Per category (compose as you like):
just upgrade-brew # formulas + casks (--greedy) + Brewfile (no --no-upgrade) + cleanup
just upgrade-mise # mise self-update + `mise upgrade`
just upgrade-uv # uv itself (auto-dispatches: brew vs self update) + `uv tool upgrade --all` (apprise, mlflow, sqlit-tui, ...)
just upgrade-npm # global npm packages (Bitwarden CLI, readability-cli, ...)
just upgrade-cargo # cargo install-update -a (bootstraps cargo-update)
just upgrade-dotnet # .NET global tools (azure-cost-cli, ...)
just upgrade-gem # Ruby gems (try-cli, tmuxinator, ...)
just upgrade-agents # re-runs install.sh for Claude Code / OpenCode / Cursor CLI / Ollama / llmfit / RTK
just upgrade-plugins # LazyVim :Lazy sync + TPM + claude-hud + pre-commit autoupdate + tldr + gh extensions
just upgrade-externals # chezmoi upgrade + chezmoi apply --refresh-externalsUnderlying script: scripts/upgrade_tools.sh —
best-effort, prints a SUCCESS / SKIPPED / FAILED summary at the end. Full
docs, rationale, run-order diagram, and extension guide live in
docs/this_repo/upgrades.md.
When installed, just upgrade-plugins also refreshes claude-hud to the
latest upstream release. Upstream claude-hud v0.0.12+ now follows Claude
Code's official stdin rate_limits only, so the old credential-derived Max
plan badge may disappear after upgrade.
Push the same chezmoi update --init to every host in
~/.config/fleet/machines.toml, in parallel, with sudo password sourced from
plaintext / interactive prompt / Bitwarden CLI:
just fleet-edit # edit ~/.config/fleet/machines.toml ($EDITOR)
just fleet-status # pre-flight readiness probe (read-only, ~1.5s/host)
just fleet-apply # parallel, all hosts (chezmoi update --init)
just fleet-apply-dry-run # `chezmoi diff` on each host (no changes)
just fleet-apply-one lab-box # single host, --serial mode (debug)Connection prefers ~/.ssh/config aliases (ProxyJump, IdentityAgent,
etc. all inherited) and falls back to explicit hostname/user/port/identity_file.
Per-host log: logs/fleet-apply/<UTC-timestamp>/<host>.log. Exit code = number
of failed hosts. Inventory file is seeded once by chezmoi as a
create_private_ template, so your edits are never overwritten.
fleet-status is the recommended pre-apply gate — it predicts what fleet-apply
would do per host (up-to-date / behind / drift / busy /
toml-mismatch / not-init / unreachable / ...) without changing
anything, and prints actionable hints for each non-green state. See
docs/this_repo/fleet-apply.md § Readiness probe
for the full state matrix.
Conflict-handling defaults match the "push canonical config" mental model:
--keep-going is on (skip drifted files non-destructively, continue
the rest), --force is off (don't auto-overwrite). Pass
--no-keep-going to fail-fast, or --force to silently overwrite local
drift on the remote — see docs/this_repo/fleet-apply.md
for the full flag reference and per-machine git override convention
(~/.gitconfig.local, same self-managed pattern as ~/.zshrc.adhoc).
Two layers, both opt-in — this is a personal dotfiles repo, tests only cover painful-regression zones:
just bats— fast unit tests (no Docker, no network). Covers proxy helpers (tests/unit/zsh_proxy.bats),ghgetURL parsing (tests/unit/ghget.bats), andlan-scan.shpure helpers (tests/unit/lan_scan.bats).just docker-test— smoke tests in a clean Ubuntu container: re-apply idempotency, zsh config parses,oh-my-zshplugins present, core CLI tools on PATH, nested unit tests pass under Ubuntu zsh (tests/smoke/docker_install.bats).just docker-test-centos7/just docker-test-rocky9— same smoke suite under thecentos_serverprofile. CentOS 7 covers the legacy glibc-2.17 noRoot corporate target (seepitfalls/centos7-noroot.md); Rocky 9 covers the modern dnf-native RHEL-family path.just docker-test-centos-allruns both.just check-all— everything: ansible syntax + pre-commit +bats+docker-test.
shellcheck and shfmt run via pre-commit on scripts/*.sh (see .pre-commit-config.yaml).
# Build and run the bats smoke suite
just docker-test
# Interactive devbox shell
just docker-run
# Build specific profiles
just docker-desktop # Ubuntu desktop profile
just docker-china # China mirror profile
# CentOS-family (centos_server profile) — covers RHEL-family install paths.
# Each base image has two flavors (sudo and noRoot) to match the actual
# corporate target (CentOS 7 + noRoot=true) and the modern dnf path.
just docker-run-centos7-noroot # CentOS 7 + noRoot=true (corporate scenario)
just docker-run-rocky9 # Rocky Linux 9, sudo path
just docker-test-centos7 # bats smoke suite on CentOS 7
just docker-test-rocky9 # bats smoke suite on Rocky 9
just docker-build-centos-all # build all four CentOS-family imagesThis project uses just as a command runner:
just # List all commands
just lint # Ansible syntax check
just check # Full lint + dry-run
just docker-build # Build Docker image
just info # Show system infoForward-looking work — long-term ideas, deferred items, things needing
evaluation — lives in TODO.md, prioritised P1 → P3 with effort
estimates (S/M/L/XL). Items with accompanying research, design notes, or paused
troubleshooting link to a corresponding backlog/<slug>.md doc.
Backward-looking knowledge — past traps and non-obvious debugging — lives in
pitfalls/, titled by symptom so future-you can grep the error
message and land on the root cause + workaround instead of re-debugging from
scratch.
See CLAUDE.md for the agent-facing repo contract, docs/this_repo/ansible_customization.md for ansible customization, docs/this_repo/testing.md for shell-script testing (bats, shellcheck, shfmt — and when to reach for ZUnit / ShellSpec instead), docs/input_methods/README.md for McBopomofo / Rime / Squirrel notes and backup strategy, docs/tools/tmux/README.md for tmux usage and managed keybindings, docs/tools/clipboard.md for how OSC 52 clipboard sync is wired across terminal / tmux / Neovim / x CLI (including SSH-remote yank), docs/tools/ghostty.md for Ghostty/cmux notes and the ghostty-ssh-terminfo helper, docs/tools/direnv.md for .venv-aware direnv usage, docs/tools/pre-commit.md for generic pre-commit reference (hook env model, uv-pinned bootstrap Python, debugging), docs/tools/git_diff_workflow.md for the managed Git diff stack, docs/tools/specstory.md for SpecStory configuration, docs/tools/td_sidecar.md for td/sidecar usage, docs/tools/specify_cli.md for Specify CLI, docs/tools/llm.md for local LLM tools, docs/tools/networking.md for networking tools, and docs/tools/chezmoi-prefixes.md for chezmoi source-state prefix semantics and when each is safe to chezmoi add.