Pain Point: Starting Over with Every Command
When using Claude Code for tasks, there have always been two minor frictions that I hadn't seriously addressed until now.
The first is that the shell itself is too bare-bones: there's nothing in ~/.zshrc except for two lines of PATH. No auto-completion, no command history. Navigating history requires pressing the up arrow repeatedly, and even then, similar commands must be manually edited. The second is when collaborating with Claude Code, some commands that are clearly read-only and have no side effects (listing files, checking versions, curling a README) require a manual "allow" click every time. This back-and-forth breaks the flow.
This post records the process of tackling both issues at once: how tools were chosen, what pitfalls were encountered, and how it was eventually integrated with Claude Code's permission system.
Solution 1: Let Zsh Remember What You've Typed
I haven't installed oh-my-zsh and didn't want to carry a whole framework for just two features, so I picked the two smallest, sufficient packages:
-
zsh-autosuggestions: Suggests similar commands in gray text while typing; press
Ctrl+Spaceor→to accept. - zsh-completions: Enhances the coverage of tab auto-completion.
brew install zsh-autosuggestions zsh-completions
Combined with history settings, the up/down keys can filter history based on current input instead of just scrolling through everything:
# --- Command History Settings ---
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt SHARE_HISTORY # Share history across multiple terminal windows
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_FIND_NO_DUPS
setopt INC_APPEND_HISTORY # Write to history file immediately upon command entry
autoload -Uz up-line-or-beginning-search down-line-or-beginning-search
zle -N up-line-or-beginning-search
zle -N down-line-or-beginning-search
bindkey "^[[A" up-line-or-beginning-search
bindkey "^[[B" down-line-or-beginning-search
After installing the packages and adding the settings, theoretically, restarting the terminal should work—but it wasn't that smooth.
Solution 2: Add Color to Read the Terminal Faster
Once auto-completion was set up, I added colors as well:
- zsh-syntax-highlighting: Real-time coloring while typing; green for valid commands, red for invalid ones.
-
ls -G: Different colors for folders, executables, and links. -
grep --color=auto: Highlights matched keywords in red.
export CLICOLOR=1
export LSCOLORS=GxFxCxDxBxegedabagaced
alias grep='grep --color=auto'
source /opt/homebrew/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
Handled fonts too: Ghostty's config file (~/Library/Application Support/com.mitchellh.ghostty/config.ghostty) didn't specify a font size and defaulted to the system's 13. Adding font-size = 16 solved it.
Solution 3: Replace ls, cat, and cd with Smarter Versions
Color and auto-completion are infrastructure; next, I added modern alternatives for three common commands. The selection criterion was simple: single executable, no background daemon, and no impact on startup speed.
| Command | Alternative | Benefit |
|---|---|---|
ls |
eza --icons |
Color, icons, tree structure (lt) |
cat |
bat --paging=never |
Syntax highlighting, line numbers |
cd (helper) |
zoxide |
Remembers frequent folders; z proj jumps directly |
I evaluated fzf but didn't install it this time—my current habits don't require fuzzy searching history/files yet. I'll add it if I feel a bottleneck later.
Solution 4: Make File Searching and Comparison Faster for Claude Code
The previous items were for "user experience"; this one is for "Claude Code speed." Claude Code's built-in search tool already uses ripgrep (rg), and jq is already installed, so these three were missing:
-
fd: Replaces
find; simple syntax, fast, especially noticeable when listing files. - ast-grep: Structural code search. Not just text matching, but looking at the Abstract Syntax Tree (AST). It can search for "all calls to a function where the first argument is a string," which is much more accurate than regex for large-scale refactoring or precise searches.
-
difftastic (
difft): Syntax-aware diff. It recognizes when a function has been moved rather than deleted and rewritten.
brew install fd ast-grep difftastic git-delta
git-delta is mainly for human-readable git diff. It's unrelated to Claude Code, but I installed it anyway.
Solution 5: Add Common Read-Only Commands to Claude Code's Permission Whitelist
A new problem emerged after installing the tools: the first time Claude Code calls these new commands, it still asks for permission. Using the fewer-permission-prompts skill to scan recent session transcripts, I identified frequently run, truly read-only commands and compiled a whitelist:
{
"permissions": {
"allow": [
"Bash(curl -s https://raw.githubusercontent.com/*)",
"Bash(curl -s \"https://api.github.com/*)",
"Bash(brew list*)",
"Bash(xcodes list*)",
"Bash(xcodebuild -version)",
"Bash(curl -sI *)",
"Bash(difft *)",
"Bash(delta *)"
]
}
}
fd, rg, and jq are not on the list, not because they were missed, but because Claude Code already includes them as built-in, auto-allowed read-only commands.
Three Easily Overlooked Pitfalls
Pitfall 1: compinit complaining about "insecure directories"
After installing packages and restarting the terminal, the first launch showed:
zsh compinit: insecure directories, run compaudit for list.
compaudit revealed the issue was that the /opt/homebrew/share directory permissions were too open (group write access). compinit checks permissions before loading completion scripts; if any directory is "writable by others," it refuses to load to prevent malicious scripts from being injected into the completion path. The fix is the official Homebrew recommendation:
chmod go-w /opt/homebrew/share
chmod -R go-w /opt/homebrew/share/zsh
rm -f ~/.zcompdump*
Clearing the cache to let it rebuild solved the problem.
Pitfall 2: zsh-syntax-highlighting must be the last line
The official documentation is clear: this line must be the last thing executed in .zshrc. If placed before zsh-autosuggestions or other bindkey settings, syntax highlighting and auto-suggestions may interfere, and key bindings might fail. I specifically moved it to the very end of the file.
Pitfall 3: ast-grep was intentionally left out of the whitelist
ast-grep is missing from the whitelist on purpose. By default, it's a read-only search, but adding the -U / --update-all flag allows it to rewrite files. Permission rules use prefix matching, so I can't allow only the "no -U" usage. Opening a broad rule like Bash(ast-grep *) theoretically allows file-modifying usage as well.
This aligns with the original logic for sed: only "read-only expressions" are auto-allowed; any usage with in-place editing still prompts for permission. Rather than re-evaluating the risk, I followed the same standard.
Summary and Benefits
This terminal environment overhaul was essentially about optimizing "human typing" and "Claude Code execution" separately:
-
Less typing:
zsh-autosuggestions+ history filtering means almost no re-typing repetitive commands. -
Faster reading: Syntax highlighting,
eza, andbatmake outputs instantly readable. -
Faster navigation:
zoxidereplaces memorizing paths, andfdreplaces the slowfind. -
More accurate Claude Code searches:
ast-grepadds structural search that plain text matching can't do, anddifftasticmakes diff results closer to actual changes. -
Fewer "allow" clicks: Whitelisting truly read-only, risk-controlled commands while still prompting for things that should be asked (like
ast-grep -U).
The core principle for tool selection remained the same: single executable, no background daemons, and avoiding frameworks where possible. The real time-consumer was deciding "whether to whitelist this"—speed is secondary; the priority is ensuring a command that can modify files isn't accidentally wrapped in a seemingly safe, generic rule.
Top comments (0)