Skip to content

glnds/dotfiles

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

657 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dotfiles

Personal dotfiles for a terminal-centric dev environment on macOS.

Installation

Prerequisites

Step 1: clone

cd ~
git clone https://github.com/glnds/dotfiles.git

Step 2: bootstrap

cd ~/dotfiles
brew install mise      # one-time, seeds the task runner
mise run bootstrap     # everything else

bootstrap chains: brew bundle (Brewfile) → symlinks into $HOMEmise install (all mise-managed tools) → hk install (per-repo git hooks from hk.pkl). Idempotent — safe to re-run.

Mise-managed tools also auto-install on first use (not_found_auto_install = true in .config/mise/config.toml). Available tasks: mise tasks.

Tip

If the shell ever reports mise: Unknown command, mise itself is gone — re-seed it with brew install mise. update can't recover this: it runs brew upgrade (installed formulae only), not brew bundle.

Step 3: change shell

Make fish your default shell:

sudo bash -c 'echo /opt/homebrew/bin/fish >> /etc/shells'
chsh -s /opt/homebrew/bin/fish

Periodically update fish completions:

fish_update_completions

Step 4: prepare nvim

Open nvim — LazyVim auto-installs plugins on first launch.

Tool Management

Two layers: brew bootstraps the system, mise handles everything else.

┌─ brew ────────────────────────────────────────────────────────────┐
│  Bootstrap only: git, mise, fish, neovim, tmux + GUI casks        │
└───────────────────────────┬───────────────────────────────────────┘
                            │ brew bundle (one time)
                            ▼
┌─ mise ────────────────────────────────────────────────────────────┐
│  Everything else, two scopes:                                     │
│                                                                   │
│   GLOBAL                          PER-REPO                        │
│   ~/.config/mise/conf.d/*.toml    <repo>/.mise.toml               │
│   ──────                          ──────                          │
│   Always available                Active only when cwd is         │
│   starship, atuin, rg, bat,       inside that repo                │
│   fd, eza, jq, uv, rumdl, ...       dotfiles → hk, pkl,           │
│                                                trufflehog         │
│                                     attracr  → python, node, uv, │
│                                                ruff, hk, sam, …   │
└───────────────────────────────────────────────────────────────────┘

Brew (system foundation)

Brewfile covers what must exist before mise runs, plus GUI casks and formulae with no good mise plugin:

  • Bootstrap: git, mise
  • Shell/editor: fish, neovim, luarocks, tmux, direnv
  • Utilities: trash, tree, btop (no aqua-registry darwin/arm64 build)
  • Casks: Alacritty, nerd fonts, Finch, MarkEdit, LuLu, BlockBlock, KnockKnock, Malwarebytes

Note

Editing the Brewfile does nothing on its own. Only brew bundle reconciles it — run mise run install (or mise run bootstrap) to apply new entries. update runs brew upgrade, which only upgrades already-installed formulae and never installs what you just added.

Pruning the other direction — after moving a tool from brew to mise — run brew bundle cleanup --force. It uninstalls formulae/casks not in the Brewfile (plus their orphaned deps), so the brew copy stops shadowing the mise shim. brew leaves should then equal the Brewfile.

Global mise tools

Declared in .config/mise/conf.d/*.toml, all pinned to latest. Available in every shell, no matter the cwd:

  • 20-shell.toml — starship, atuin, zoxide, fzf
  • 30-cli.toml — claude, gh, jq, ripgrep, bat, fd, eza, glow, dust, yazi, gitui, delta, rumdl, uv

Per-repo mise tools

Each repo ships its own .mise.toml declaring tools specific to that project. This repo's pins hk, pkl, and trufflehog — useless outside the dotfiles repo (git hook runner, its config language, secret scanner called from the pre-commit hook). Other repos pin what they need: a Python project pins python + uv + ruff; an AWS project pins aws-sam-cli + cfn-lint + cfn-guard. When you cd into the repo, mise puts those tools on PATH; when you leave, they're gone.

$ cd ~                  # no .mise.toml in scope
$ hk --version
mise ERROR No version is set for shim: hk

$ cd ~/dotfiles         # .mise.toml declares hk
$ hk --version
hk 1.46.0

Why this matters:

  • No global pollution. A project pinned to Python 3.11 doesn't fight another pinned to 3.13. Each has its own .mise.toml; both Just Work.
  • Reproducible across machines. .mise.toml is in git, so a fresh clone gets the exact same versions after mise install.
  • Fast onboarding. Clone, mise install, done — no chasing down which tools the project expects.

Why mise (over plain brew/asdf/pyenv/nvm/...)

  • One declarative tool for languages and CLIs — add a tool by editing one toml line, commit, done
  • Fast installs from precompiled binaries via the aqua registry
  • Auto-install on first use; no brew install round trips
  • Versions live in git, so machines stay in sync

Updating

Single command, runs brew + mise + uv in one go:

update           # fish wrapper
mise run update  # equivalent

Defined in .config/mise/conf.d/99-tasks.toml, the task chains:

  1. brew update && brew upgrade && brew cleanup
  2. mise upgrade — re-resolves latest pins and installs newer versions
  3. mise prune — removes the now-unused old versions
  4. uv tool upgrade --all — upgrades Python tools installed via uv tool

Tools

Shell and Terminal

  • fish — shell with autosuggestions and syntax highlighting
  • starship — fast, customizable cross-shell prompt
  • atuin — SQLite-backed shell history with fuzzy search
  • tmux — terminal multiplexer
  • Alacritty — GPU-accelerated terminal emulator (Cmd+Shift+M to toggle maximize)

Editor

CLI Replacements

  • batcat with syntax highlighting
  • eza — modern ls replacement
  • fd — fast find alternative
  • dust — visual du replacement
  • ripgrep — fast grep alternative
  • fzf — fuzzy finder
  • zoxide — smarter cd
  • trash — safe rm to trash can

Git

  • git — latest Homebrew-managed version
  • delta — syntax-highlighted git diffs with side-by-side view
  • gitui — lightweight terminal Git UI (aliased as tig)
  • gh — GitHub CLI
  • hk — per-repo git hook runner (config in hk.pkl, install with hk install). On Git 2.54+ hooks are config-based, not scripts in .git/hooks/ — so an empty .git/hooks/ is expected. Verify with git config --get-regexp '^hook\.', not ls .git/hooks/.

Containers

  • Finch — open-source container tool (Docker alternative)

AWS tooling (aws-cli, aws-sam-cli, cfn-lint, cfn-guard) lives in each AWS project's .mise.toml, not globally.

Utilities

  • jq — JSON processor
  • btop — system monitor with CPU, memory, disk, network, and GPU stats
  • yazi — fast terminal file manager
  • glow — terminal Markdown renderer
  • tree — directory listing
  • direnv — per-directory environment variables
  • rumdl — fast Markdown linter (CLI + LSP for nvim)
  • uv — fast Python package manager

Security

  • LuLu — open-source firewall, blocks unknown outgoing connections
  • BlockBlock — monitors persistence locations (launch daemons, login items)
  • KnockKnock — scans for persistently installed software
  • Malwarebytes — on-demand malware scanner

TruffleHog (secret scanner) lives in each hk-enabled repo's .mise.toml, called from the pre-commit hook — see this repo's hk.pkl for an example.

Custom Shortcuts

Yazi folder favorites

Keys Directory
gs ~/source
gk ~/Desktop
gd ~/Downloads
go ~/Documents

tmux Notifications for Claude Code

tmux monitor-activity is too noisy for Claude Code (fires on every output line). Instead, bell-based notifications trigger only when Claude needs input.

How it works: Claude Code hooks send a terminal bell (\a) on Stop and permission_prompt events → tmux monitor-bell highlights the window name in the status bar (bold red).

The hooks are configured in ~/.claude/settings.json (not tracked in this repo).

GitHub Multi-Account

The gh CLI supports multiple accounts natively (v2.40+). Combined with direnv, account switching is automatic per directory.

Step 1: authenticate both accounts

gh auth login  # work account
gh auth login  # private account (stacks)

Verify with gh auth status.

Step 2: automatic per-directory switching with direnv

Create .envrc in each repos root:

Work repos (~/work/.envrc):

export GH_TOKEN=$(gh auth token --user your-work-username 2>/dev/null)

Private repos (~/personal/.envrc):

export GH_TOKEN=$(gh auth token --user your-private-username 2>/dev/null)

Then direnv allow inside each folder.

GH_TOKEN overrides gh auth switch, so gh automatically uses the right account based on working directory — mirroring how SSH config works. The 2>/dev/null variant dynamically pulls the current stored token rather than a hardcoded value.

Caveat: if you re-auth an account, the token updates automatically since the .envrc evaluates gh auth token on each shell entry.

About

💙.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors