Personal zsh setup with:
- a single entrypoint (
init.zsh) - local secret loading from
.env - Spaceship prompt customization
- optional productivity plugins and CLI integrations
- Overview
- Prerequisites
- Setup (Fresh Machine)
- Update (Existing Install)
- Configuration Load Order
- Repository Layout
- Functions Reference
- Secrets Management
- DevX Bootstrap
- Spaceship Prompt Notes
- Customization Examples
- Verification Commands
- Troubleshooting
This repo manages shell behavior through one entrypoint:
source ~/.zsh/init.zshinit.zsh then loads settings, keys, prompt, functions, and plugins in a predictable order.
- macOS or Linux with
zsh git- Homebrew (recommended on macOS for tool installation)
- Clone repo:
git clone git@github.com:ivankristianto/zsh.git ~/.zsh
cd ~/.zsh- Initialize submodules (required for Spaceship prompt):
git submodule update --init --recursive- Create your local secrets file:
cp .env.example .env- Add entrypoint to
~/.zshrc:
source ~/.zsh/init.zsh- Reload shell:
exec zshFrom ~/.zsh:
git pull
git submodule update --init --recursive --remote
exec zshinit.zsh loads files in this order:
settings.zshkeys.zshthemes/.zsh_themefunctions/ai_functions.zshfunctions/functions.zshplugins/plugins.zsh
This order is intentional:
- secrets are available before function/theme usage
- prompt is configured before interactive usage
- syntax-highlighting plugin is loaded near the end
init.zsh: single entrypointsettings.zsh: shell history and zsh optionskeys.zsh: loads variables from.envand exports compatibility vars.env.example: tracked template for required env variable namesfunctions/: custom shell functionsthemes/.zsh_theme: Spaceship prompt configthemes/spaceship-prompt/: Spaceship git submoduleplugins/plugins.zsh: optional plugin/tool loader (safe guards if missing)
Both files are auto-loaded from init.zsh:
functions/ai_functions.zshfunctions/functions.zsh
Primary command:
ai [command] [args...]Implementation layout:
- Entry loader:
functions/ai_functions.zsh - Modular files:
functions/ai/*.zsh - Provider domains:
functions/ai/providers/{claude,ollama,llamacpp,codex,gemini,copilot,opencode}.zsh
| Command | Alias | Purpose | Example |
|---|---|---|---|
ai |
- | Open interactive fzf provider picker |
ai |
sonnet |
s |
Claude Code with Claude Sonnet | ai sonnet "review this diff" |
haiku |
h |
Claude Code with Claude Haiku | ai haiku "summarize this log" |
opus |
o |
Claude Code with Claude Opus | ai opus "deep architecture analysis" |
glm |
g |
Claude Code via GLM-5.1 (Z.ai) | ai glm "plan rollout steps" |
kimi |
k |
Claude Code via Kimi K2.5 (Moonshot) | ai k "draft migration plan" |
mini |
m |
Claude Code via MiniMax M2.1 | ai mini "quick fix proposal" |
openrouter |
or |
Claude Code via OpenRouter (--model supported) |
ai openrouter --model anthropic/claude-opus-4 "check this service design" |
ollama |
ol |
Claude Code via local Ollama (--model supported) |
ai ollama --model qwen2.5-coder:14b "write unit tests" |
llama.cpp |
ll |
Claude Code via local llama.cpp (--model supported) |
ai llama.cpp --model qwen2.5-coder:14b "write unit tests" |
custom |
cu |
Claude Code via custom Anthropic-compatible endpoint | ai custom --model gpt-4o --endpoint https://... --apikey ... "debug error" |
codex |
c |
OpenAI Codex CLI | ai codex "refactor this function" |
gemini |
ge |
Gemini CLI in yolo mode | ai gemini "summarize changes" |
copilot |
cp |
GitHub Copilot CLI | ai copilot "generate release notes" |
opencode |
oc |
OpenCode build agent (--model, --review) |
ai opencode --review |
install |
i |
Install a supported coding agent CLI via npm global | ai install codex |
bench |
b |
Run one prompt across Claude-backed providers | ai bench "review this diff" glm kimi or |
context |
ctx |
Dump project context as markdown | ai context --copy |
last |
l |
Re-run last selected provider | ai last |
help |
-h |
Show built-in help and status (shows only ready cmds) | ai help |
<cmd> ship |
- | Interactive git assistant (commit/push/PR) | ai g ship |
Required tooling/env depends on command:
- Claude Code backends (
sonnet/haiku/opus/glm/kimi/mini/openrouter/ollama/llama.cpp/custom) requireclaude ai glmalso requiresGLM_API_KEYai k(ai kimi) also requiresKIMI_API_KEYai minialso requiresMINIMAX_API_KEYai openrouter(ai or) also requiresOPENROUTER_API_KEYai llama.cpp(ai ll) uses hardcoded local Anthropic-compatible values (http://localhost:8001,sk-no-key-required)ai codexrequirescodex+OPENAI_API_KEYai geminirequiresgemini+GEMINI_API_KEYai copilotrequirescopilotai opencode(ai oc) requiresopencode- Interactive picker (
ai) requiresfzf
Examples (long + shorthand):
# Claude Code backends
ai sonnet "review this diff for bugs"
ai s "review this diff for bugs"
ai openrouter --model anthropic/claude-opus-4 "analyze this architecture"
ai or --model anthropic/claude-opus-4 "analyze this architecture"
ai llama.cpp --model qwen2.5-coder:14b "write unit tests"
ai ll --model qwen2.5-coder:14b "write unit tests"
ai custom --model gpt-4o --endpoint https://api.example.com --apikey sk-... "explain this stack trace"
ai cu --model gpt-4o --endpoint https://api.example.com --apikey sk-... "explain this stack trace"
# Standalone CLIs
ai codex "refactor this function safely"
ai c "refactor this function safely"
ai copilot "create release notes from commits"
ai cp "create release notes from commits"
ai opencode --review
ai oc --review
# Utility
ai last
ai l
ai install claude
ai install codex --dry-run
ai bench "compare rollback plans" glm kimi openrouter
ai context
ai context --copy- Repo helper link: https://github.com/ivankristianto/zsh
- In-shell installer supports:
ai install claudeai install codexai install geminiai install ollamaai install copilotai install opencode
Launch interactive git assistant with any provider:
# Default (Haiku)
ai ship
# With specific providers
ai s ship # Claude Sonnet
ai g ship # GLM
ai k ship # Kimi
ai or ship # OpenRouterThe AI will:
- Review your changes
- Help craft commit messages
- Commit and push
- Create a PR via
gh
| Function | Purpose | Example |
|---|---|---|
tt |
Send Telegram message using TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID |
tt -m "deploy done" |
k2-dev |
Shortcut SSH/orb connect helper for k2-dev |
k2-dev |
switchphp |
Switch linked Homebrew PHP version | switchphp 8.3 |
up |
Run daily software refresh (~/.zsh, current repo, Homebrew, npm globals, Node LTS, skills) |
up |
zload |
Profile interactive zsh startup with zprof |
zload 10 |
tmexclude |
Recursively exclude folders from Time Machine backups | tmexclude ~/projects node_modules |
Daily update command details:
upup executes:
git -C ~/.zsh pull --ff-onlygit pull --ff-only(when inside a git repo and tracking branch exists)brew upgradebrew cleanupnpm install -g <all currently installed global npm packages except npm>nvm install --lts(whennvmis available)npx skills update
Startup profiling:
zload 10zload launches an interactive shell with zprof preloaded and prints the top startup contributors.
Time Machine exclusions:
tmexclude ~/projects node_modules # Exclude all node_modules folders
tmexclude -d ~ "Caches" # Dry-run: preview what would be excludedRules:
- store real tokens only in
.env - never commit
.env - commit only
.env.example
.env is ignored by git via .gitignore.
Minimal example:
OPENAI_API_KEY="your-openai-api-key"
OPENROUTER_API_KEY="your-openrouter-api-key"
KIMI_API_KEY="your-kimi-api-key"Install core tools:
brew install fzf zoxide eza bat fd ripgrep
$(brew --prefix)/opt/fzf/install --key-bindings --completion --no-update-rc| Tool | Purpose | Repo | Install |
|---|---|---|---|
fzf |
Fuzzy finder for files/history/processes | https://github.com/junegunn/fzf | brew install fzf |
zoxide |
Smarter cd that learns frequent paths |
https://github.com/ajeetdsouza/zoxide | brew install zoxide |
eza |
Modern ls replacement with icons and git metadata |
https://github.com/eza-community/eza | brew install eza |
bat |
Syntax-highlighted cat with pager integration |
https://github.com/sharkdp/bat | brew install bat |
fd |
Fast and simple alternative to find |
https://github.com/sharkdp/fd | brew install fd |
ripgrep (rg) |
Fast recursive text search | https://github.com/BurntSushi/ripgrep | brew install ripgrep |
Usage examples:
# fzf: fuzzy-find files under current directory
fzf
# zoxide: jump to a frequently visited directory (configured as `cd`)
cd .zsh
# eza: long list with icons and git status
eza -la --icons --git
# bat: preview file with syntax highlighting
bat ~/.zsh/README.md
# fd: find zsh files quickly
fd '\.zsh$' ~/.zsh
# ripgrep: search text recursively
rg "SPACESHIP_HOST_SHOW" ~/.zshNote: this config initializes zoxide with --cmd cd in plugins/plugins.zsh, so use cd (not z).
Install zsh plugins used by this repo:
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions.git ~/.zsh/plugins/zsh-autosuggestions
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting.git ~/.zsh/plugins/zsh-syntax-highlighting
git clone --depth=1 https://github.com/zsh-users/zsh-completions.git ~/.zsh/plugins/zsh-completions| Plugin | Purpose | Repo | Install Path |
|---|---|---|---|
zsh-autosuggestions |
Suggests commands based on shell history | https://github.com/zsh-users/zsh-autosuggestions | ~/.zsh/plugins/zsh-autosuggestions |
zsh-syntax-highlighting |
Highlights valid/invalid commands as you type | https://github.com/zsh-users/zsh-syntax-highlighting | ~/.zsh/plugins/zsh-syntax-highlighting |
zsh-completions |
Adds many extra command completions | https://github.com/zsh-users/zsh-completions | ~/.zsh/plugins/zsh-completions |
Usage examples:
zsh-autosuggestions: typegit st(aftergit statusexists in history), then press Right Arrow to accept suggestion.zsh-syntax-highlighting: compare typingls(valid) vslss(invalid) to see different highlighting.zsh-completions: typegit chthen press Tab to expand completion candidates.
Optional aliases:
alias ls='eza --group-directories-first'
alias ll='eza -la --icons --git --group-directories-first'
alias cat='bat'Configured in themes/.zsh_theme.
Current default prompt order:
dirgitnodephpexec_timetimeline_sepjobsexit_codechar
Important behavior:
- host section is disabled (
SPACESHIP_HOST_SHOW=false) - time uses
SPACESHIP_TIME_FORMAT="%D{%H:%M}"
This avoids%Mhostname expansion side effects.
Hide Node and PHP sections:
SPACESHIP_NODE_SHOW=false
SPACESHIP_PHP_SHOW=falseRemove at before time:
SPACESHIP_TIME_PREFIX=""Disable time completely:
SPACESHIP_TIME_SHOW=falseShow username always:
SPACESHIP_USER_SHOW=alwaysValidate zsh syntax:
zsh -n ~/.zsh/init.zsh ~/.zsh/settings.zsh ~/.zsh/keys.zsh ~/.zsh/themes/.zsh_theme ~/.zsh/plugins/plugins.zsh ~/.zsh/functions/ai_functions.zsh ~/.zsh/functions/ai/*.zsh ~/.zsh/functions/ai/providers/*.zshVerify secrets load:
zsh -lc 'source ~/.zsh/keys.zsh; [[ -n "$OPENAI_API_KEY" ]] && echo OK || echo MISSING'Check current Spaceship values:
zsh -lic 'print -r -- "HOST_SHOW=$SPACESHIP_HOST_SHOW TIME_FORMAT=$SPACESHIP_TIME_FORMAT"'Run AI function tests:
zsh ~/.zsh/tests/ai_functions_test.zshPrompt still shows hostname-like output at the end:
- check
SPACESHIP_TIME_FORMATfirst;%Minside prompt context can render hostname - recommended value:
SPACESHIP_TIME_FORMAT="%D{%H:%M}" - reload shell:
exec zsh
Plugins not loading:
- ensure plugin dirs exist under
~/.zsh/plugins - plugin loader is guarded; missing plugins do not crash startup
Submodule missing after clone:
- run
git submodule update --init --recursive
Secrets not available in commands:
- confirm
.envexists and contains validKEY="value"lines - run
source ~/.zsh/keys.zsh - verify with
printenv | rg 'OPENAI_API_KEY|OPENROUTER_API_KEY|KIMI_API_KEY'