Recently I set up a complete development toolchain on my Mac from scratch — from terminal emulator to editor, from dotfiles management to shell theming — and it took about a weekend of tinkering. Before this, I had been using everything piecemeal, and every time I switched devices I had to reconfigure everything from scratch. This time I unified management with chezmoi and tried out some new tools along the way.
This post records my current tool stack and configuration approach, mainly targeting Unix systems (Mac), with a possible future migration to Linux.
1. Dotfiles and Package Management
Dotfiles Manager — chezmoi
On Unix systems, configurations for all kinds of tools are almost always stored as hidden files in ~/.config or the home directory. Git, Zsh, Vim, tmux... all configurations are plain-text dotfiles. The question is: how do you sync these configurations across devices while protecting sensitive information like API keys?
A common approach is to create a Git repository and use symbolic links (ln -s) to map configuration files to the home directory, or manage them with tools like Stow/RCM. But the symlink approach has several pain points:
- Cross-device configuration differences are hard to handle (Mac and Linux servers may need different configurations)
- Secrets (API keys, tokens) can't go directly into a public repository
- After downloading, you need to manually adjust permissions and paths
chezmoi solves these problems in a way that's more like a "template engine." It doesn't blindly copy or link files; instead, it dynamically generates the final dotfiles based on system variables, template syntax, and secret managers.
Daily workflow:
1 | # 安装 |
One small detail: chezmoi edit modifies the copy in the chezmoi repository, and you must run chezmoi apply for it to be written to the actual path. This mechanism gives you a chance to compare and confirm before applying, so it's hard to mess up.
If the configuration file itself is a symbolic link, chezmoi add needs the -f flag to follow the symlink and read the actual file contents.
Package Manager — Homebrew
For development on macOS, Homebrew is basically a must-have. The official installation script often fails to download under domestic network conditions, so I used a mirror script from Gitee:
1 | /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" |
After installation, I added a few optimization options to .zshrc:
1 | # 关闭 Homebrew 自动更新(需要时手动 brew update) |
These switches make each brew install much faster, since you don't have to wait for it to auto-update.
2. Terminal and Shell Layer
This layer is where day-to-day interaction happens most frequently, covering everything from the terminal emulator, shell, plugins, to the prompt.
Terminal Emulator — Alacritty
The reason I chose Alacritty over terminal tools like Ghostty, Kitty, or iTerm is that I don't really want a bunch of features I'll never use getting downloaded, or having one feature implemented by two tools at the same time.
For example, terminal multiplexing: most terminals come with built-in multiplexing, but since I use tmux regularly myself, using a terminal emulator with built-in multiplexing would be redundant. There are a few similar reasons.
Alacritty is written in Rust, so it's faster and uses less memory.
Installation:
1 | brew install alacritty |
On macOS, the first time you open it you need to allow it in System Security settings. Also, the original icon really isn't great; you can refer to this Zhihu link to swap it out.
My configuration is as follows (~/.alacritty.toml), and more configuration options can be found in the official documentation:
1 | [window] |
The key bindings are configured with macOS-style shortcuts (Cmd+C/V copy/paste, Cmd+F find, Cmd+N new window, etc.), so I won't paste all of them here.
Shell Core — Zsh + Antidote
I don't use Oh My Zsh. Although it's really convenient, its startup speed isn't great, and it loads a ton of stuff you'll probably never use.
Final choice: vanilla Zsh + Antidote as the plugin manager.
Compared to Oh My Zsh and zinit, Antidote's advantages are simplicity, fast startup, and support for bridging ohmyzsh plugins. Installation:
1 | brew install antidote |
The initialization logic in .zshrc:
1 | # antidote 初始化 |
The caching mechanism is key: it only regenerates .zsh_plugins.zsh when .zsh_plugins.txt has updates, and daily startup loads the cached zsh file directly, so there's almost no perceptible latency.
Plugin list (~/.zsh_plugins.txt):
1 | # 语法高亮 |
Several commonly used ohmyzsh plugins are brought in through the bridge use-omz, which is more memory-efficient than loading the entire ohmyzsh library. zsh-z is for quickly jumping between directories, and extract extracts all kinds of archives with one command — both are used constantly.
.zshrc also contains some non-toolchain configuration:
1 | # 关闭 Homebrew 自动更新(前面提过) |
Prompt — Starship
Starship is a cross-shell prompt tool written in Rust. Its standout feature is that it's fast, and it comes with beautiful preset themes out of the box.
1 | brew install starship |
Initialize it in .zshrc:
1 | eval "$(starship init zsh)" |
I went straight with the official preset Pastel Powerline; you can check the official documentation for community preset configurations and specific setup instructions. This preset displays user info, the current directory, Git branch and status, various language versions, and the current time, with nicely layered color segments. With chezmoi managing it, everything stays consistent across all my devices.
Font — Nerd Font
Nerd Font is the foundation for displaying icons and special symbols in the terminal; both Alacritty and Starship depend on it.
1 | # 先搜一下有哪些字体可以使用 |
JetBrains Mono — clear glyphs, comfortable monospacing, and well optimized for programming scenarios.
3. Terminal Multiplexer — Tmux
tmux is basically a must-have for development: a single session can be split into multiple panes, and your working environment persists after disconnecting from SSH (server-training friendly).
Installation:
1 | brew install tmux |
My configuration (~/.tmux.conf):
1 | # 前缀键改为 Ctrl+a(比默认的 Ctrl+b 更顺手) |
The color scheme uses a Catppuccin Mocha-style dark palette (#1e1e2e background, #89b4fa blue highlight), with a status bar that concisely shows the session name and current time.
A few key choices:
- The prefix key is changed to
Ctrl+a, which is much easier to press than the defaultCtrl+b(you can pick one that suits your habits) - If you nest a local tmux inside a server tmux, it's best to use different prefixes on each side
- Pane indexing starts at 1, aligning with the numeric keypad
- Copy mode uses vi keybindings, keeping habits consistent with Neovim
4. Editor — Neovim
There are quite a few text editors to choose from. For simple use cases, you can go with nano; some people also use nvim or vim.
- People who use nano are generally doing simple text editing in the terminal and won't be doing complex code editing, so they don't need much in the way of text-editing optimization. Nano's shortcut hints are also fairly friendly and easy to use.
- If you need to do complex editing, Vscode is without a doubt the easiest to get started with and the most practical tool. Its community plugin coverage is comprehensive, it's trusted by users worldwide, and it just works.
- btw, if you want to tinker a bit or do some geek play, vim-style editors are a good choice. Both vim and nvim are solid options, and you can also go with an out-of-the-box editing config like Lazyvim. Once you get used to vim, it really does improve coding efficiency. But given the current prevalence of vibe coding, compared with efficiently typing out large blocks of code, what we seem to need more is a good terminal code browser (if you're using agent tools like claude code in the terminal and don't want to open another Vscode window). In that case, nvim is also a good choice.
Installation and Initialization
1 | brew install neovim |
kickstart.nvim is not a distribution; it's a teaching-oriented starting configuration. Its biggest advantage is that you can understand every line of the config — read it from top to bottom starting at init.lua, and you'll know what your editor is doing.
I first forked a copy to my own GitHub, then cloned it locally as the nvim config directory:
1 | git clone git@github.com:kzzzza/kickstart.nvim.git "${XDG_CONFIG_HOME:-$HOME/.config}"/nvim |
To manage everything uniformly with chezmoi, I removed the unneeded repository files (.git*, doc/, LICENSE, README), keeping only the core configuration:
1 | .config/nvim/ |
Plugin System
I use lazy.nvim as the plugin manager. Below are the selection rationale and core configuration for each plugin:
Basic editor experience:
| Plugin | Purpose | Selection rationale |
|---|---|---|
tokyonight.nvim |
Color scheme | Eye-friendly dark theme, disables italic comments, uses the tokyonight-night variant |
which-key.nvim |
Keybinding hints | A lifesaver when you can't remember shortcuts |
mini.nvim |
Statusline / text objects / surround editing | Lightweight replacement, one package solves three needs |
gitsigns.nvim |
Git change markers | Shows additions/deletions/modifications in the line-number gutter, supports hunk jumping |
todo-comments.nvim |
TODO comment highlighting | Highlights TODO, FIX markers in code |
Code intelligence:
| Plugin | Purpose |
|---|---|
nvim-lspconfig + mason.nvim |
LSP configuration and automatic installation |
blink.cmp |
Autocompletion (fuzzy matching implemented in Rust) |
conform.nvim |
Code formatting (runs automatically on save) |
nvim-treesitter |
Syntax highlighting and structural understanding |
Navigation and search:
| Plugin | Purpose |
|---|---|
telescope.nvim |
Fuzzy finding (files, symbols, grep, help docs) |
guess-indent.nvim |
Automatic indentation detection |
Snippet:
LuaSnipas the snippet engine, used together withblink.cmp
A Few Configuration Details
Some key settings in init.lua:
1 | -- leader 键设为空格 |
Keep 10 lines above and below the cursor (scrolloff = 10), enable mouse mode for convenient split-window dragging, and disable the arrow keys to force myself to use hjkl (although I still occasionally press the wrong keys).
Formatting uses conform.nvim, which runs automatically on save. Currently only Lua is configured (using stylua); I'll add the corresponding formatter when I add Python/Rust later.
5. Command-Line Tools
In addition to the major components above, there are also some small tools that are used frequently on a daily basis:
1 | brew install ripgrep # grep 的 Rust 替代,速度飞起 |
6. TODO
There are still a few additions I want to make to this setup. If they don't get done, it means I was focused on the project and didn't spend more time tinkering with the workflow (doge).
- Linux adaptation: The chezmoi configuration is currently macOS-oriented, with no differentiated setup for personal Linux PCs or servers yet. chezmoi itself supports generating different content based on system variables, so I'll need to use template syntax to separate the Mac and Linux branches later.
- One-click install script: Write a
install.shby hand so that running it once on a new machine installs all dependencies (Homebrew → chezmoi init → chezmoi apply → install fonts and toolchain). Ideally, switching computers would only requiregit clone+ one./install.sh. - Secret management: chezmoi can encrypt configuration files containing private information such as API keys. I'll explore this later — encrypting private data like model API endpoints or server addresses and managing them uniformly through chezmoi as well.
- Making the dotfiles repo public: The chezmoi repo is still private for now. Once sensitive information is completely stripped out (confirming no hardcoded API keys or tokens), I plan to make the full dotfiles public on GitHub so others can reference them too.