现代终端工作流配置方案

目标

这套配置面向 infra / platform engineer 的日常工作流:

  • Terminal: Ghostty
  • Shell: zsh
  • Prompt: starship
  • Multiplexer: tmux
  • CLI 美化: eza / bat / fd / ripgrep
  • K8s: k9s / kubectx / kubens / stern / helm
  • Git TUI: lazygit
  • 辅助工具: fzf / zoxide / git-delta / jq / yq / bottom / dust

启动链路:

Ghostty -> zsh vi mode -> starship prompt -> tmux session -> lazygit / k9s / kubectl / eza / bat / rg / fd

当前取舍:

  • zsh 使用 vi mode。
  • tmux prefix 使用 Ctrl-a
  • zsh insert mode 支持 EscCtrl-[jj 进入 normal mode。
  • Ghostty 显式把 Esc / Ctrl-[ 发送为 legacy ESC,避免 CSI-u 1;5u 影响 zsh vi mode。
  • starship 初始化做幂等保护,避免反复 source ~/.zshrczle-keymap-select wrapper 递归。

安装命令

brew install ghostty zsh starship tmux eza bat fd ripgrep k9s lazygit
brew install jq yq fzf zoxide git-delta kubectx helm stern bottom dust
brew install --cask font-jetbrains-mono-nerd-font

配置文件位置

~/.zshrc
~/.config/starship.toml
~/.tmux.conf
~/.config/ghostty/config
~/.config/bat/config

zsh 配置

文件:~/.zshrc

eval "$(/opt/homebrew/bin/brew shellenv)"
 
export PATH="/opt/homebrew/opt/openjdk@21/bin:$PATH"
export PATH="/Users/justinx/Library/Python/3.9/bin:$PATH"
export PATH="$HOME/bin:$PATH"
 
# >>> Codex modern CLI workflow >>>
export EDITOR="${EDITOR:-vim}"
export PAGER="${PAGER:-less}"
export LESS="-R"
 
if [[ -o interactive ]]; then
  autoload -Uz compinit
  compinit
 
  bindkey -v
  bindkey -M viins '^[' vi-cmd-mode
  bindkey -M viins '\e' vi-cmd-mode
 
  if [[ "${TERM:-}" != "dumb" ]]; then
    if [[ "${widgets[zle-keymap-select]-}" == "user:starship_zle-keymap-select-wrapped" ]] && (( ${+functions[starship_zle-keymap-select]} )); then
      zle -N zle-keymap-select starship_zle-keymap-select
    elif [[ "${widgets[zle-keymap-select]-}" != user:starship_zle-keymap-select* ]]; then
      eval "$(starship init zsh)"
    fi
  fi
 
  if [[ "${TERM:-}" != "dumb" ]] && [[ -t 0 && -t 1 ]] && command -v fzf >/dev/null 2>&1; then
    source <(fzf --zsh)
  fi
fi
 
if command -v zoxide >/dev/null 2>&1; then
  eval "$(zoxide init zsh)"
fi
 
if [[ -o interactive ]]; then
  bindkey -M viins 'jj' vi-cmd-mode
  export KEYTIMEOUT=20
 
  for key in '^A' '^B' '^E' '^F' '^K' '^N' '^O' '^P' '^S' '^T' '^Y' '^Z'; do
    if bindkey -M viins "$key" 2>/dev/null | command grep -q 'self-insert$'; then
      bindkey -M viins "$key" undefined-key
    fi
  done
fi
 
alias ls="eza --icons --group-directories-first"
alias ll="eza -lah --icons --group-directories-first"
alias la="eza -a --icons --group-directories-first"
alias tree="eza --tree --icons"
 
alias cat="bat"
alias grep="rg"
alias find="fd"
 
alias lg="lazygit"
alias k="kubectl"
alias kctx="kubectl config current-context"
alias kx="kubectx"
alias kn="kubens"
 
alias ta="tmux attach -t"
alias tls="tmux ls"
alias tn="tmux new -s"
 
alias du="dust"
alias top="btm"
alias diff="delta"
# <<< Codex modern CLI workflow <<<

说明:

  • bindkey -v 明确启用 zsh vi mode。
  • Esc / Ctrl-[ 绑定到 vi-cmd-mode
  • jj 也绑定到 vi-cmd-mode
  • KEYTIMEOUT=20 表示多键序列等待约 200ms;如果 jj 人工输入不稳定,可调到 50100
  • 对仍是 self-insert 的部分 Ctrl+字母 改成 undefined-key,避免命令行出现 ^A^E^K 这类控制字符。
  • starship 初始化不再依赖 STARSHIP_SHELL 环境变量判断,因为 tmux server 可能继承旧变量;改为检查当前 shell 的 zle widget。
  • 反复 source ~/.zshrc 时,如果 starship wrapper 已经递归套娃,会复位到 starship_zle-keymap-select

Starship 配置

文件:~/.config/starship.toml

format = """
$username$hostname$directory$git_branch$git_status$kubernetes$docker_context$terraform$nodejs$python$golang$java$cmd_duration
$character
"""
 
add_newline = true
 
[character]
success_symbol = "[❯](bold green)"
error_symbol = "[❯](bold red)"
 
[directory]
style = "bold cyan"
truncation_length = 3
truncate_to_repo = true
 
[git_branch]
symbol = "git:"
style = "bold purple"
 
[git_status]
style = "bold red"
 
[kubernetes]
disabled = false
symbol = "k8s:"
format = '[$symbol$context( \($namespace\))]($style) '
style = "bold blue"
 
[docker_context]
symbol = "docker:"
style = "blue"
 
[terraform]
symbol = "tf:"
style = "bold 105"
 
[nodejs]
symbol = "node:"
format = "[$symbol($version)]($style) "
 
[python]
symbol = "py:"
format = "[$symbol($version)]($style) "
 
[golang]
symbol = "go:"
format = "[$symbol($version)]($style) "
 
[java]
symbol = "java:"
format = "[$symbol($version)]($style) "
 
[cmd_duration]
min_time = 1000
format = "took [$duration](bold yellow) "

tmux 配置

文件:~/.tmux.conf

set -g mouse on
setw -g mode-keys vi
set -sg escape-time 0
set -g history-limit 100000
 
set -g base-index 1
setw -g pane-base-index 1
set -g renumber-windows on
 
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",xterm-256color:Tc"
 
unbind C-b
set -g prefix C-a
bind C-a send-prefix
 
bind r source-file ~/.tmux.conf \; display-message "tmux config reloaded"
 
bind | split-window -h
bind - split-window -v
 
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
 
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5
 
bind m resize-pane -Z
 
bind -n M-h previous-window
bind -n M-l next-window
 
bind-key -T copy-mode-vi v send-keys -X begin-selection
bind-key -T copy-mode-vi y send-keys -X copy-selection-and-cancel
 
set -g status-bg black
set -g status-fg white
set -g status-left "#[fg=green]#S "
set -g status-right "#[fg=yellow]%Y-%m-%d %H:%M "

说明:

  • tmux prefix 使用 Ctrl-a
  • Ctrl-a | 左右分屏,Ctrl-a - 上下分屏。
  • Ctrl-a h/j/k/l 在 pane 间移动。
  • Ctrl-a Shift-h/j/k/l 调整 pane 大小。
  • Ctrl-a m 最大化/恢复 pane。
  • Alt-h / Alt-l 切换 window。
  • Ctrl-a r 重新加载 tmux 配置。
  • default-terminal 使用 tmux-256color,不使用较旧的 screen-256color

Ghostty 配置

文件:~/.config/ghostty/config

term = xterm-256color
 
font-family = JetBrainsMono Nerd Font
font-size = 14
 
theme = Catppuccin Mocha
 
window-padding-x = 10
window-padding-y = 8
 
cursor-style = block
cursor-style-blink = false
 
macos-option-as-alt = true
clipboard-read = allow
clipboard-write = allow
 
# Send a legacy ESC for zsh vi mode instead of CSI-u "1;5u".
keybind = escape=text:\x1b
keybind = ctrl+[=text:\x1b
keybind = ctrl+left_bracket=text:\x1b

说明:

  • Ghostty 默认/增强键盘协议下,Ctrl+[ 可能发成 CSI-u 1;5u
  • zsh vi mode 需要 legacy ESC,也就是 /bin/cat -v 中看到的 ^[
  • 因此显式把 EscCtrl+[Ctrl+left_bracket 映射成 text:\x1b
  • clipboard-read/write = allow 允许终端和 tmux 更顺畅地使用系统剪贴板。

bat 配置

文件:~/.config/bat/config

--theme="TwoDark"
--style="numbers,changes,header"
--paging=auto

验证命令

ghostty +validate-config --config-file=$HOME/.config/ghostty/config
starship explain >/tmp/starship-explain.txt && echo STARSHIP_OK
tmux -f ~/.tmux.conf start-server \; source-file ~/.tmux.conf \; show-options -g prefix
TERM=xterm-256color zsh -ic 'echo KEYTIMEOUT=$KEYTIMEOUT; bindkey -M viins jj; bindkey -M viins "^["'

检查 Ghostty 是否发送 legacy ESC:

/bin/cat -v

EscCtrl+[,应该看到:

^[

检查 starship wrapper 是否正常:

TERM=xterm-256color zsh -ic 'source ~/.zshrc; source ~/.zshrc; echo ${widgets[zle-keymap-select]-unset}'

期望:

user:starship_zle-keymap-select

主题切换

Ghostty 主题:

ghostty +list-themes

修改:

theme = GitHub Dark Dimmed

验证:

ghostty +validate-config --config-file=$HOME/.config/ghostty/config

bat 主题:

bat --list-themes

修改:

--theme="Catppuccin Mocha"

注意事项

  • tmux 内已有 pane 不会自动加载新的 .zshrc,需要执行 source ~/.zshrc 或新开 pane/window。
  • tmux server 会继承启动时环境变量,例如 STARSHIP_SHELLTERM,所以 starship 初始化判断不能只看环境变量。
  • 如果 jj 不稳定,优先调大 KEYTIMEOUT
  • 如果命令行出现 ^A^E^K 等,说明某些 Ctrl 组合仍是 self-insert,需要用 bindkey -M viins 检查。