I forked Mathias's dotfiles in 2017 and I've been heavily tuning and personalising mine since then.
My stack:
- Bash as shell
- Ghostty as terminal emulator
- Tmux for multiplexing and as the main terminal compatibility layer
- Neovim as primary editor, with a modular
lazy.nvimsetup originally based on Kickstart.nvim - Vim (with lean
.vimrc, no plugins) as fallback editor ranger(also trying outlf) as terminal file managers- Homebrew as package manager
- Karabiner-Elements for modifiers, navigation, and app launchers
- AeroSpace +
bordersfor tiling window management - Stats for lightweight menu bar system monitoring
With Git:
git clone --depth 5 git@github.com:gianlucatruda/dotfiles.git <your/dotfiles/path/>(I suggest ~/dotfiles for the path)
Or with curl:
curl -L -o dotfiles-master.zip https://github.com/gianlucatruda/dotfiles/archive/master.zip
unzip dotfiles-master.zip
cd dotfiles-masterFrom within the dotfiles directory:
source bootstrap.shCreate the ~/.config/.extra file:
echo "" >> ~/.config/.extraAdd the following details (and any other system-wide variables):
# Plain assignment vs export:
# - Plain assignment: only visible within the current shell process.
# Fine for variables consumed by sourced scripts (e.g. bootstrap.sh).
# - export: inherited by child processes (scripts, uv run, python, etc.).
# Required for anything read by external programs like API clients.
# Git identity (used by bootstrap.sh, which runs via `source` — plain assignment is sufficient)
GIT_USER_EMAIL="name@email.com"
GIT_SIGNING_KEY="<signing_key>"
# API keys (consumed by external processes — must be exported)
export OPENAI_API_KEY="sk-..."bootstrap.sh will apply git config --global core.editor, user.email, and user.signingkey if the variables above are set. Re-run bootstrap.sh after changing them.
Configure some macOS preferences:
./macos.shInstall packages with Homebrew:
./brew.shbrew.sh only bootstraps a tiny base (Homebrew Bash + completion) before handing off to brew bundle with .config/homebrew/Brewfile.
Update the dotfiles repo from the system:
cd <your-dotfiles-repo>
brew bundle dump --formula --tap --cask --mas --describe --force --file .config/homebrew/BrewfileNote: previously, I used brew bundle dump --all --describe --force --file .config/homebrew/Brewfile but this now includes language-specific tools like uv, npm, cargo, etc. that I don't want tracked. See man brew for details.
Update the system from the dotfiles repo:
cd <your-dotfiles-repo>
source bootstrap.sh
brew bundle install -v --cleanup --force --file ~/.config/homebrew/BrewfileNote: you can also install, cleanup, upgrade in steps:
brew bundle install --no-upgrade --file ~/.config/homebrew/Brewfile
brew bundle cleanup --force --file ~/.config/homebrew/Brewfile
brew bundle install --file ~/.config/homebrew/Brewfilellm plugins re-installed with update:
brew update && brew upgrade llm && llm install -U llm-anthropic llm-ollamaManually check the local path (even on macOS) and use your <profile_code>, as this path may change (which is why I just do it manually. It's more of a backup than a true config).
Sync browser to dotfiles (from within local dotfiles repo):
cp ~/Library/Application\ Support/zen/Profiles/<profile_code>.Default\ \(release\)/zen-keyboard-shortcuts.json .config/zen/zen-keyboard-shortcuts.jsonSync browser from dotfiles:
cp .config/zen/zen-keyboard-shortcuts.json ~/Library/Application\ Support/zen/Profiles/<profile_code>.Default\ \(release\)/zen-keyboard-shortcuts.json Note: Zen always re-formats the file, so it's a messy and manual backup more than a reliable config.
Keep keys encrypted but avoid repeated prompts. No macOS Keychain.
SSH key cache for 24h:
# (Optional) remove existing identity (clears prior TTL)
ssh-add -d ~/.ssh/id_ed25519
# re-add key with 24h cache
ssh-add -t 24h ~/.ssh/id_ed25519GPG signing cache for 24h:
Add these lines to ~/.gnupg/gpg-agent.conf
default-cache-ttl 86400 # cache passphrase for 24h
max-cache-ttl 86400 # cap max cache at 24hThen restart the GPG agent:
gpgconf --kill gpg-agent && gpgconf --launch gpg-agentThen load up by running reload, which is an alias for:
exec $SHELL -lNote: pyenv is initialized with --no-rehash for faster shell startup. Run pyenv rehash manually after installing new Python versions or global CLI tools.
Productivity essentials:
- Zen browser for primary minimalist browsing
- Obsidian for notes and knowledge management
- Shottr for screenshots (macOS only, one-time licence for all features)
- Helium browser for messaging/email web apps
Often helpful:
- spotify for tunes
- todoist for quick capture inbox and basic recurring tasks across devices
- brave-browser for distraction-free YouTube isolated from other browsing
- handbrake for video transcoding
- iina for longer video playback (nicer than VLC on Macs, more GUI than
mpv) - obs for screen captures
- anki for making / studying flashcards
- toothfairy for managing bluetooth audio devices
- Ghostty is the only terminal config in this repo now; Alacritty is fully deprecated.
- Ghostty uses its built-in
TokyoNight Moontheme and exportsDOTFILES_TERM=ghosttyas the outer terminal marker. - tmux provides the runtime contract:
tmux-256color, RGB enabled for modernxterm-256color-style clients, and a status line that mostly keeps terminal defaults. - Neovim reads that outer terminal marker; tmux refreshes
DOTFILES_TERMfrom the attaching client so terminal-specific behavior still works inside tmux. - Hack Nerd Font for terminal and editor use.
Current tracked structure:
.
├── .bash_profile
├── .bashrc
├── .config
│ ├── .aliases
│ ├── .bash_prompt
│ ├── .exports
│ ├── .functions
│ ├── .inputrc
│ ├── .path
│ ├── aerospace
│ │ └── aerospace.toml
│ ├── btop
│ │ └── btop.conf
│ ├── git
│ │ └── config
│ ├── ghostty
│ │ └── config
│ ├── homebrew
│ │ └── Brewfile
│ ├── htop
│ │ └── htoprc
│ ├── karabiner
│ │ ├── complex_modifications
│ │ │ └── 1584620783.json
│ │ └── karabiner.json
│ ├── lf
│ │ ├── colors
│ │ ├── icons
│ │ └── lfrc
│ ├── nvim
│ │ ├── SPEC.md
│ │ ├── init.lua
│ │ ├── lazy-lock.json
│ │ └── lua
│ │ ├── core
│ │ │ ├── keymaps.lua
│ │ │ ├── options.lua
│ │ │ ├── path.lua
│ │ │ ├── plugins.lua
│ │ │ └── terminal.lua
│ │ └── plugin_config
│ │ ├── blink.lua
│ │ ├── colourscheme.lua
│ │ ├── gitsigns.lua
│ │ ├── ibl.lua
│ │ ├── init.lua
│ │ ├── lazydev.lua
│ │ ├── lazygit.lua
│ │ ├── lsp.lua
│ │ ├── lualine.lua
│ │ ├── oil.lua
│ │ ├── telescope.lua
│ │ └── treesitter.lua
│ ├── ranger
│ │ └── rc.conf
│ ├── stats
│ │ ├── README.txt
│ │ └── Stats.plist
│ ├── tmux
│ │ └── tmux.conf
│ └── zen
│ └── zen-keyboard-shortcuts.json
├── .gitignore
├── .gitignore_global
├── .vimrc
├── AGENTS.md
├── bootstrap.sh
├── brew.sh
├── macos.sh
└── scripts
├── gt-btooth
├── gt-cheat
├── gt-perfprofile
├── gt-scan
├── gt-stt
├── gt-sync-homelab
├── gt-sync-obsidian
├── gt-synchdd
├── gt-todoist-export
└── gt-tts
v(): Opens the current directory or a specified directory in neovim if available, otherwise uses vi.sf(): Searches for text-readable, non-hidden files (or all files including hidden with-aflag, excluding.git) in the current directory usingrgandfzf, then opens the selected file in Vim.sd(): Searches directories usingfzfand changes to the selected directory, excluding paths containing.git.update_environment_from_tmux(): Refreshes tmux-managed environment variables when a shell is started or reloaded inside tmux.mkd(): Creates a new directory (and any necessary parent directories) then changes into it.fs(): Displays the size of a file or total size of a directory usingdu, presenting results in human-readable form.- Built-in Overridden
diff(): Uses Git’s colored diff functionality when Git is installed, otherwise falls back to standard behavior. o(): Opens the current directory or a specified file/directory with the default system application.tre(): Runs thetreecommand showing hidden files and colorizing the output (ignoring.git,node_modules, andbower_componentsdirectories) and pipes the results tolesswith options to keep colors and line numbers.
Neovim prepends Mason's bin to PATH so LSP/tools use Mason-managed binaries
inside Neovim without affecting your shell.
Markdown formatting uses Prettier (via Mason) when you run <leader>f or :Format.
Theme behavior stays intentionally simple: Neovim only enables Tokyo Night Moon in Ghostty; other terminals keep their own palette.
Neovim highlights:
- LSP UI toggles live under
<leader>tl(diagnostics, virtual text, inlay hints, Ty workspace) with<leader>tlafor all. - Completion auto-trigger toggle lives at
<leader>tc. - Winbar shows git-root-relative paths (fallback to CWD), and
<leader><tab>jumps to the most recent buffer.
See .config/nvim/SPEC.md for the full behavior-level spec.
.config/nvim/
├── SPEC.md
├── init.lua
├── lazy-lock.json
└── lua
├── core
│ ├── keymaps.lua
│ ├── options.lua
│ ├── path.lua
│ ├── plugins.lua
│ └── terminal.lua
└── plugin_config
├── blink.lua
├── colourscheme.lua
├── gitsigns.lua
├── ibl.lua
├── init.lua
├── lazydev.lua
├── lazygit.lua
├── lsp.lua
├── lualine.lua
├── oil.lua
├── telescope.lua
└── treesitter.lua