Maintenance — Brewfile, gitconfig, update, clean-node-modules, bigfiles
Declarative package lists, a global gitconfig with delta diffs, a one-command updater, a node_modules sweeper, a line-count refactor radar, and the modular installer.
Declarative package lists, a global gitconfig with delta diffs, a one-command updater, a worktree-friendly node_modules sweeper, a line-count ranker that exposes refactor candidates, and a background LaunchAgent that quietly sweeps ~/Downloads onto the NAS. Keeps the system reproducible, current, and tidy without thought.
Jump to:
- Brewfile — declarative package list
~/.gitconfig— delta diffs · aliasesbin/update— one-command updaterbin/clean-node-modules— sweepnode_modulesbin/bigfiles— largest files · refactor radarbin/sort-downloads— lives on NAS Workflowinstall.shmodules — modular installer
Brewfile
Three declarative package lists at the repo root. install.sh runs brew bundle against the right one based on your WM choice. Adding a new tool is one line in a file instead of editing a shell script.
| File | When applied | Contents |
|---|---|---|
Brewfile | Always | fish, starship, fastfetch, bat, eza, lsd, fzf, zoxide, thefuck, jq, git-delta, rbenv, nvm, fnm, openjdk@17, scrcpy, JetBrains Mono Nerd Font |
Brewfile.omniwm | If WM = OmniWM | omniwm, sketchybar, sf-symbols, codexbar |
Brewfile.aerospace | If WM = AeroSpace | aerospace, sketchybar, sf-symbols, codexbar |
cd ~/dotfiles
brew bundle --file=Brewfile
brew bundle --file=Brewfile.aerospace # or Brewfile.omniwm Optional GUI apps (Ghostty, Cursor, Claude, Spotify) remain as prompts in install.sh — they’re not in any Brewfile so brew bundle doesn’t force every app on you.
~/.gitconfig
Symlinked from git/.gitconfig. Delta diffs in Tokyo Night, sensible defaults, and a tight alias set.
Defaults
init.defaultBranch = mainpush.autoSetupRemote— no more--set-upstreamrebase.autoStash+autoSquashfetch.prune+pruneTagsmerge.conflictstyle = zdiff3rerere.enabled— replay conflict resolutionsbranch.sort = -committerdate— recent first
Delta integration
Syntax-highlighted, hyperlinked, line-numbered diffs in git diff / log / show.
- Tokyo Night-tinted plus/minus backgrounds
- n / N navigates between files
- File paths are ⌘-click hyperlinks
Aliases
| Alias | Expands to |
|---|---|
git s | git status -sb |
git lg | graph log with author, date, refs |
git last | git log -1 HEAD --stat |
git who | git shortlog -sne — author leaderboard |
git swc <name> | git switch -c — new branch |
git cm "msg" | git commit -m |
git amend | git commit --amend --no-edit |
git undo | git reset --soft HEAD~1 |
git unstage | git reset HEAD -- |
git pushf | git push --force-with-lease (safe force) |
git cleanup | Delete branches already merged into main |
git aliases | List every alias in this file |
Machine-specific overrides (work email, GPG key) go in ~/.gitconfig.local — it’s silently included if it exists.
bin/update
A single command that keeps the whole stack current. Symlinked to ~/.local/bin/update so it’s on your $PATH from anywhere.
update | Step | What it does |
|---|---|
| Dotfiles | git pull --ff-only in ~/dotfiles (skips if dirty) |
| Homebrew | brew update + upgrade + upgrade --cask + bundle + cleanup |
| Bun | bun upgrade |
| npm globals | npm update -g |
| Claude Code | Best-effort claude update (Claude self-updates on launch anyway) |
Safe to re-run. Each step is gated behind command -v so missing tools are quietly skipped instead of erroring.
bin/clean-node-modules
Reclaim disk space across your repo and its git worktrees. Scans $PWD recursively, lists every top-level node_modules with its size, then deletes them after a single confirmation. Nested node_modules inside another node_modules are pruned from the walk so the scan stays fast. Symlinked to ~/.local/bin/clean-node-modules so it’s on your $PATH in zsh and fish.
clean-node-modules | Flag | What it does |
|---|---|
-n / --dry-run | List matches and total size, delete nothing |
-y / --yes | Skip the confirmation prompt |
-h / --help | Show usage |
cd ~/code/some-monorepo
clean-node-modules -n cd ~/code/worktrees
clean-node-modules -y Run from the directory you want to scan — the tool always uses $PWD as the root, never your home directory. npm install / bun install rebuilds node_modules from the existing lockfile, so deleting is safe on any committed project.
bin/bigfiles
Refactor radar. Ranks every source file under $PWD by line count, skipping the noise — node_modules, .git, build, dist, out, target, .next, .nuxt, .turbo, .expo, .yarn, .venv, __pycache__, .gradle, Pods, DerivedData, .dart_tool, vendor, lock files, minified bundles, source maps — so the ranking reflects code you actually wrote. Useful when an AI-assisted codebase grows quietly and a single file drifts past four-figure line counts. Symlinked to ~/.local/bin/bigfiles so it’s on $PATH in zsh and bash; a thin fish wrapper at ~/.config/fish/functions/bigfiles.fish exposes it as a fish function too.
bigfiles | Flag | What it does |
|---|---|
-n <N> / --top <N> | Show the top N files (default 20) |
-t <N> / --threshold <N> | Only files with at least N lines |
-e <csv> / --ext <csv> | Restrict to extensions, e.g. ts,tsx,js |
-a / --all | Don’t filter by extension — count every file |
--cloc | Delegate to cloc with the same excludes — full code/comment/blank breakdown |
-h / --help | Show usage |
| Color | Line count |
|---|---|
| red | ≥ 2,000 — split it |
| yellow | ≥ 1,000 — review |
| cyan | ≥ 500 — keep an eye on it |
| green | < 500 — fine |
cd ~/code/my-app
bigfiles -t 500 -n 100 bigfiles -e ts,tsx -n 30 bigfiles --cloc Counts physical lines (wc -l) — fast and good enough for “what should I split next?” The --cloc mode hands the same exclude list to cloc for a comments-vs-code breakdown when you need the nuance (brew install cloc).
bin/sort-downloads
Auto-sort for ~/Downloads — classify each file by extension, move to a matching folder on the NAS. Full documentation (LaunchAgent setup, category routing, screenshot caveat, atomic copy pattern) lives on the dedicated NAS Workflow page.
Installed only when ENABLE_SORT_DOWNLOADS=true in ~/.config/dotfiles/local.env. The wizard asks during ./install.sh setup.
install.sh — modular installer
install.sh at the repo root is a thin orchestrator (~190 lines). Each part of the install lives in its own file under install.d/, exposing one function: module_<name>(). The orchestrator parses flags, decides which modules to run, then sources and calls them in order. Different machines pick different combos — no more “all or nothing”.
The modules
| Module | Role |
|---|---|
00-lib.sh | Shared helpers — colors, logging, link, brew_install, npm_install, curl_install, prompt. Sourced by every module. |
01-menu.sh | Module catalog + pure-bash interactive picker + --only/--skip/--yes flag parsing. Defines should_run <name>. |
10-prereqs | Always runs. macOS check, Xcode CLT install, banner. Not in the picker — it’s a hard requirement. |
20-homebrew | Homebrew install + brew bundle of Brewfile + Node LTS (nvm) + Bun + WM-specific Brewfile + sketchybar font + fish in /etc/shells |
30-wm | Window manager picker — OmniWM / AeroSpace / none. Sets $WM_CHOICE which other modules read. |
40-shells | fzf-tab git clone (no brew formula for it) |
50-apps | Optional GUI apps — per-app Y/N prompts (Ghostty, Cursor, Claude, Codex CLI, Spotify, …) |
60-symlinks | Every link <src> <dst> call — the actual dotfiles linking |
70-launchd | Time Machine monthly LaunchDaemon + Downloads-sort LaunchAgent + NAS auto-mount retry LaunchAgent (sed-templated for $HOME) |
80-macos | macOS preferences — Dock, Finder, screenshots, Office telemetry, etc. |
90-sketchybar | Restart SketchyBar after packages + symlinks are in place |
Picking what to install
Four ways to choose, in priority order:
./install.sh
# Shows a numbered menu. Type "1 3 5" to toggle items, 'a' for all,
# 'n' for none, Enter to confirm. Symlinks is pre-checked. ./install.sh --only=symlinks,macos
# Skips the menu entirely. Useful for scripted runs and CI. ./install.sh --skip=wm,homebrew
# Runs everything except these. Useful on machines that
# already have Homebrew and don't want a WM. ./install.sh --yes
# Runs all modules. Inside each module, Y/N prompts auto-answer Y. INSTALL_MODULES=symlinks,macos ./install.sh | Flag | What it does |
|---|---|
--dry-run | Print every action without making changes (combines with any other flag) |
--yes / -y | Run all modules; inside each, auto-answer Y to sub-prompts |
--only=<csv> | Run only the listed modules, skip the picker |
--skip=<csv> | Run everything except the listed modules |
--modules | Print the module list and exit |
--man | Open the full man page (with a framed ASCII logo) |
--help / -h | Show short usage summary |
Interactive picker keys
When no selection flag is given, the picker opens. Single-keystroke, no Enter needed for each key. The frame redraws atomically (one printf call building the whole frame as a string) so movement is smooth with no flicker.
| Key | Action |
|---|---|
↑ / k | Move cursor up |
↓ / j | Move cursor down |
| Space | Toggle the highlighted module |
| a | Select all |
| n | Clear all |
| r | Reset to defaults (symlinks only) |
| Enter | Confirm and run |
q / Esc | Quit (clears selection, bails) |
Footer shows a live Selected: X/8 counter so you can see what you’ve picked at a glance.
Man page
A real groff man page lives at man/install.1 — opens in your pager via ./install.sh --man. Includes a framed ASCII logo and full reference for every flag, every module, every interactive key, environment variables, exit codes, and example invocations.
./install.sh --man Design notes
- Pure-bash picker. No
whiptail/dialogdependency — the menu works on a bare macOS install where nothing extra has been brewed. - Flicker-free redraw. The whole frame is built as a single bash string and emitted with one
printfcall. Each line ends with\033[K(clear-to-end-of-line) so leftover characters from the previous frame are wiped without a screen-clear flash. Color variables use ANSI-C quoting ($'\e[…m') so bothecho -eandprintf '%s'render correctly. - bash 3.2 compatible. Uses parallel indexed arrays instead of
declare -A, so the script runs on macOS’s stock/bin/bashon a fresh machine before Homebrew has installed anything. - Mutually exclusive flags.
--onlyand--skipcan’t be combined — the orchestrator errors out cleanly if both are given. - Atomic curl bootstrap. All
install.d/files land in a single commit socurl … | bashusers never see a half-modular state mid-clone. - Adding a new module. Drop
install.d/NN-name.shdefiningmodule_name(), appendname|descriptionto the catalog in01-menu.sh, and add ashould_run name && … && module_nameblock ininstall.sh. Three edits, one new file.
The public bootstrap at docs/install.sh (served from dotfiles.rafay99.com/install.sh) is unchanged — it still git-clones the repo and runs bash $TARGET/install.sh. The orchestrator handles everything from there.