-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdot_zshrc.tmpl
More file actions
300 lines (247 loc) · 10.8 KB
/
Copy pathdot_zshrc.tmpl
File metadata and controls
300 lines (247 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# ============================================================================
# Mise activation — SHIM MODE (deliberately NOT hook-env)
# ============================================================================
# mise runs in SHIM mode, not via `mise activate zsh` (hook-env). Shim mode keeps
# `which <tool>` pointing at ~/.local/share/mise/shims/<tool> (hook-env points
# tools at the install path instead), which keeps tool resolution predictable for
# external tooling and works in non-interactive/agent subprocesses. The shims dir
# is prepended in .zshenv (non-login shells) and re-asserted in .zprofile (login
# shells, after macOS path_helper reorders PATH). Nothing to do here.
# ============================================================================
# Agent/CI detection via envsense
# See: docs/adrs/002-agent-optimized-shell-with-envsense.md
# ============================================================================
_is_automated=0
if command -v envsense >/dev/null && envsense check -q --any agent ci; then
_is_automated=1
fi
# ============================================================================
# Interactive-only: plugins, prompt, and tool-replacement aliases
# Skipped for agents/CI to preserve standard command output and reduce startup
# ============================================================================
if (( ! _is_automated )); then
# Oh My Zsh framework
export ZSH="$HOME/.oh-my-zsh"
# shellcheck disable=SC2034
plugins=(
git
starship
)
source "$ZSH"/oh-my-zsh.sh
# Initialize atuin for magical shell history
{{- if lookPath "atuin" }}
eval "$(atuin init zsh)"
{{- end }}
# Initialize zoxide for fast directory jumping
{{- if lookPath "zoxide" }}
eval "$(zoxide init zsh)"
{{- end }}
{{- $zshAutoSuggestPath := joinPath .chezmoi.homeDir ".config/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh" }}
{{- if stat $zshAutoSuggestPath }}
# Speed-optimized zsh-autosuggestions loading
# All checks done at template generation time by chezmoi
# Pre-configure before loading for maximum speed
export ZSH_AUTOSUGGEST_USE_ASYNC=1
export ZSH_AUTOSUGGEST_MANUAL_REBIND=1 # We'll handle rebinding for vim mode
export ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20
export ZSH_AUTOSUGGEST_HISTORY_IGNORE="?(#c50,)" # Ignore commands > 50 chars
export ZSH_AUTOSUGGEST_COMPLETION_IGNORE="* --help" # Skip help completions
# Catpuccin theme support - optimized for Ghostty + tmux
if [[ "${TERM}" == "xterm-ghostty" ]] || [[ -n "${TMUX}" ]]; then
# Ghostty and tmux support proper color sequences
export ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' # ANSI bright black adapts to theme
else
# Fallback to neutral gray for other terminals
export ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=244'
fi
# Load plugin
source {{ $zshAutoSuggestPath }}
# Custom vim mode integration
_zsh_autosuggest_bind_vim_mode() {
# Clear default bindings
bindkey -M viins '^[[C' forward-char # Right arrow baseline
bindkey -M viins '^[[D' backward-char # Left arrow baseline
# Insert mode bindings
bindkey -M viins '^F' vi-forward-word # Ctrl+F: accept word
bindkey -M viins '^E' vi-end-of-line # Ctrl+E: accept to end
bindkey -M viins '^]' autosuggest-accept # Ctrl+]: accept full
# Visual mode support
bindkey -M vicmd '^]' autosuggest-accept
bindkey -M visual '^]' autosuggest-accept
# Quick accepts in normal mode
bindkey -M vicmd 'L' vi-end-of-line
bindkey -M vicmd 'A' vi-add-eol # A should accept and go to end
}
# Apply vim bindings after plugin loads
_zsh_autosuggest_bind_vim_mode
# Speed hack: only fetch suggestions for commands > 2 chars
_zsh_autosuggest_should_fetch() {
(( ${#BUFFER} > 2 ))
}
{{- end }}
# Enable Vi mode for command line editing
bindkey -v
# Ctrl+G opens command line in $EDITOR
autoload -Uz edit-command-line
zle -N edit-command-line
bindkey '^G' edit-command-line
# Tool-replacement aliases (change output format — breaks agent expectations)
{{- range .aliases.replacements }}
{{- if lookPath .tool }}
alias {{ .replaces }}='{{ .tool }}'
{{- end }}
{{- end }}
{{- range .aliases.custom }}
{{- if lookPath .tool }}
alias {{ .replaces }}='{{ .command }}'
{{- end }}
{{- end }}
{{- range .aliases.fallbacks }}
{{- $replaces := .replaces }}
{{- $matched := false }}
{{- range .chain }}
{{- if and (not $matched) (lookPath .tool) }}
{{- $matched = true }}
alias {{ $replaces }}='{{ .command }}'
{{- end }}
{{- end }}
{{- end }}
# grep with extra flags (filename, line numbers)
alias grep='grep -Hn --color=auto'
# Suffix aliases - automatically open files by extension
alias -s md="glow --pager"
{{- $zshSyntaxHighlightPath := joinPath .chezmoi.homeDir ".config/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" }}
{{- if stat $zshSyntaxHighlightPath }}
# ============================================================================
# Zsh Syntax Highlighting - MUST BE LOADED LAST (in interactive block)
# ============================================================================
# Speed-optimized configuration with compile-time checks
# Configure highlighters BEFORE loading the plugin (must be a regular array)
ZSH_HIGHLIGHT_HIGHLIGHTERS=(main brackets pattern)
# Performance optimization for large buffers
export ZSH_HIGHLIGHT_MAXLENGTH=20000
# Configure highlighting styles for catppuccin compatibility
typeset -gA ZSH_HIGHLIGHT_STYLES=(
# Commands and builtins
'builtin' 'fg=blue'
'command' 'fg=green'
'precommand' 'fg=green,underline'
'alias' 'fg=cyan'
'function' 'fg=cyan'
# Paths
'path' 'fg=white,underline'
'path_prefix' 'fg=white'
'path_approx' 'fg=yellow'
# Options and arguments
'single-hyphen-option' 'fg=magenta'
'double-hyphen-option' 'fg=magenta'
# Quotes and globbing
'single-quoted-argument' 'fg=yellow'
'double-quoted-argument' 'fg=yellow'
'dollar-quoted-argument' 'fg=yellow'
'back-quoted-argument' 'fg=magenta'
'globbing' 'fg=cyan,bold'
# Variables
'assign' 'fg=white'
'parameter' 'fg=yellow'
# Errors
'unknown-token' 'fg=red,bold'
'reserved-word' 'fg=magenta'
'commandseparator' 'fg=white'
# Brackets
'bracket-level-1' 'fg=blue,bold'
'bracket-level-2' 'fg=green,bold'
'bracket-level-3' 'fg=magenta,bold'
'bracket-level-4' 'fg=yellow,bold'
'bracket-error' 'fg=red,bold'
)
# Custom patterns for common commands
typeset -gA ZSH_HIGHLIGHT_PATTERNS=(
'rm -rf *' 'fg=white,bold,bg=red'
'sudo *' 'fg=white,bold,bg=red'
)
# Load the plugin - MUST BE LAST
source {{ $zshSyntaxHighlightPath }}
# On zsh 5.9+, syntax highlighting uses zle-line-pre-redraw hook (no widget
# wrapping needed). Starship already handles prompt refresh on keymap change
# via its own zle-keymap-select widget — no custom override required.
{{- end }}
fi # end (( ! _is_automated ))
# ============================================================================
# Always-available: PATH, functions, credential aliases
# These are needed by both humans and agents
# ============================================================================
# Add custom functions and completions directories to fpath and enable autoloading
fpath=(
"$XDG_CONFIG_HOME/zsh/functions"
"$XDG_CONFIG_HOME/zsh/completions"
"${fpath[@]}"
)
autoload -Uz c
# Credential-injecting aliases: fetch secret from 1Password on each invocation
{{- range .aliases.credentials }}
{{- if and (lookPath "op") (lookPath .tool) }}
alias {{ if hasKey . "alias" }}{{ .alias }}{{ else }}{{ .tool }}{{ end }}='{{ .env }}=$(op read "{{ index $.credentials .op_ref_key }}") command {{ .tool }}'
{{- end }}
{{- end }}
{{- if and (lookPath "gh") (lookPath "mise") }}
# Wrap mise to use GitHub token from gh CLI
# Fixes "API rate limit exceeded" errors when mise downloads tools from GitHub releases
alias mise='GITHUB_TOKEN="$(gh auth token)" command mise'
{{- end }}
{{- if lookPath "claude" }}
# Give Claude Code access to source checkouts for interactive/prompt sessions,
# but leave subcommands (mcp, update, doctor, …) untouched — they don't take
# --add-dir and shouldn't have it injected. Can't be an alias: a bare prompt
# (`claude "fix x"`) is a non-flag positional just like a subcommand, so the
# only reliable way to tell them apart is to match the known subcommand names.
claude() {
case "$1" in
agents|auth|auto-mode|doctor|install|mcp|plugin|plugins|project|setup-token|ultrareview|update|upgrade)
command claude "$@" ;;
*)
command claude --add-dir ~/src "$@" ;;
esac
}
{{- end }}
# Reload shell function - re-execs the exact same zsh binary
function reload! {
# Find the actual zsh binary path using multiple fallback methods
local zsh_path
# Method 1: Linux - /proc/$$/exe symlinks to the actual executable
if [[ -r /proc/$$/exe ]]; then
zsh_path=$(readlink /proc/$$/exe)
# Method 2: macOS/BSD - Use lsof to find the binary from open file descriptors
# This works because the shell binary stays open as the executing process
elif command -v lsof >/dev/null && zsh_path=$(lsof -p $$ -Fn | awk '/^n.*zsh$/{print substr($0,2); exit}'); then
: # zsh_path already set by command substitution
# Method 3: Non-login shells - $0 contains the actual path when not a login shell
# Login shells have $0 as "-zsh" but regular shells show the full path
elif [[ $0 != -* ]]; then
zsh_path=$0
# Method 4: Login shell detection - If $0 starts with '-', we're in a login shell
# Use getent/dscl to find the user's actual shell from the system database
elif [[ $0 == -* ]]; then
if command -v getent >/dev/null; then
# Linux/Unix systems - query passwd database
zsh_path=$(getent passwd "$USER" | cut -d: -f7)
elif command -v dscl >/dev/null; then
# macOS - query Directory Services
zsh_path=$(dscl . -read "/Users/$USER" UserShell | awk '{print $2}')
else
# Fallback to $SHELL if system tools unavailable
zsh_path=$SHELL
fi
# Method 5: Final fallback - Use $SHELL environment variable
# This may not be the currently running shell but is the user's preferred shell
else
zsh_path=$SHELL
fi
exec "$zsh_path" "$@"
}
# Git-safe PATH modification - MUST RUN LAST
# Prepends .git/safe/../../bin to PATH for per-repository trusted binaries
# This allows each git repository to provide its own trusted executables
# Inspired by: https://thoughtbot.com/blog/git-safe
# Note: Placed at end to ensure it takes precedence over all other PATH modifications
export PATH=".git/safe/../../bin:$PATH"