Skip to content

Instantly share code, notes, and snippets.

@briandconnelly
Created February 6, 2026 21:39
Show Gist options
  • Select an option

  • Save briandconnelly/d002477c18286b9a78aedc06c998d0ba to your computer and use it in GitHub Desktop.

Select an option

Save briandconnelly/d002477c18286b9a78aedc06c998d0ba to your computer and use it in GitHub Desktop.
zsh completion for Claude Code 2.1.34
#compdef claude
# -----------------------------------------------------------------------------
#
# Claude Code CLI completion (v2.1.34)
# https://code.claude.com/docs/en/cli-reference
#
# -----------------------------------------------------------------------------
# Cache directory for expensive completions
_claude_cache_dir="${TMPDIR:-/tmp}/zsh-claude-completion-${UID}"
# Helper: read from cache if fresh, otherwise regenerate
# Usage: _claude_cached <cache_name> <ttl_seconds> <generator_command>
_claude_cached() {
local cache_name=$1 ttl=$2; shift 2
local cache_file="${_claude_cache_dir}/${cache_name}"
mkdir -p "$_claude_cache_dir" 2>/dev/null
if [[ -f $cache_file ]]; then
local now=$(date +%s)
local mtime=$(stat -f %m "$cache_file" 2>/dev/null || stat -c %Y "$cache_file" 2>/dev/null)
if (( now - mtime < ttl )); then
cat "$cache_file"
return
fi
fi
eval "$@" | tee "$cache_file"
}
# Dynamic completion: MCP server names (cached 60s due to slow health checks)
_claude_mcp_servers() {
local -a servers
servers=(${(f)"$(_claude_cached mcp_servers 60 "claude mcp list 2>/dev/null | sed -n '/: /{s/: .*//;p;}'")"})
(( ${#servers} )) && _describe 'mcp server' servers
}
# Dynamic completion: installed plugin names
_claude_plugins() {
local -a plugins
plugins=(${(f)"$(_claude_cached plugins 30 "claude plugin list 2>/dev/null | sed -n 's/^[[:space:]]*❯ //p'")"})
(( ${#plugins} )) && _describe 'plugin' plugins
}
# Dynamic completion: model names extracted from the claude binary (cached until binary changes)
_claude_models() {
local claude_bin=${commands[claude]:-claude}
local -a models
models=(sonnet opus haiku)
if [[ -x $claude_bin ]]; then
local cache_file="${_claude_cache_dir}/models"
local bin_mtime=$(stat -f %m "$claude_bin" 2>/dev/null || stat -c %Y "$claude_bin" 2>/dev/null)
local cache_valid=0
mkdir -p "$_claude_cache_dir" 2>/dev/null
if [[ -f $cache_file ]] && [[ -f "${cache_file}.mtime" ]]; then
local cached_mtime=$(<"${cache_file}.mtime")
[[ $cached_mtime == $bin_mtime ]] && cache_valid=1
fi
local -a full_models
if (( cache_valid )); then
full_models=(${(f)"$(<$cache_file)"})
else
full_models=(${(f)"$(strings "$claude_bin" 2>/dev/null | grep -E '^claude-(sonnet|opus|haiku)-[0-9]' | grep -v '\[' | grep -v '@' | sort -u)"})
if (( ${#full_models} )); then
printf '%s\n' "${full_models[@]}" > "$cache_file"
echo "$bin_mtime" > "${cache_file}.mtime"
fi
fi
(( ${#full_models} )) && models+=($full_models)
fi
_describe 'model' models
}
_claude() {
local context curcontext=$curcontext state line ret=1
declare -A opt_args
local -a commands=(
'doctor:Check the health of your Claude Code auto-updater'
'install:Install Claude Code native build'
'mcp:Configure and manage MCP servers'
'plugin:Manage Claude Code plugins'
'setup-token:Set up a long-lived authentication token'
'update:Check for updates and install if available'
'upgrade:Check for updates and install if available'
)
local -a _global_flags=(
'(- 1 *)'{-h,--help}'[Show help information]'
'(- 1 *)'{-v,--version}'[Output version number]'
'(-c --continue)'{-c,--continue}'[Load the most recent conversation in the current directory]'
'(-p --print)'{-p,--print}'[Print response without interactive mode]'
'(-r --resume)'{-r,--resume}'[Resume a specific session by ID or open interactive picker]::session-id:'
'(-d --debug)'{-d,--debug}'[Enable debug mode with optional category filtering]::categories:'
'*--add-dir[Add additional working directories for Claude to access]:directory:_directories'
'--agent[Specify an agent for the current session]:agent-name:'
'--agents[Define custom subagents dynamically via JSON]:json-object:'
'(--dangerously-skip-permissions)--allow-dangerously-skip-permissions[Enable bypassing all permission checks as an option]'
'(--allowed-tools)--allowedTools[Tools that should be allowed without prompting]:tools:'
'(--allowedTools)--allowed-tools[Tools that should be allowed without prompting]:tools:'
'(--system-prompt)--append-system-prompt[Append custom text to the end of the default system prompt]:prompt-text:'
'*--betas[Beta headers to include in API requests (API key users only)]:beta-features:'
'(--no-chrome)--chrome[Enable Claude in Chrome integration]'
'(--allow-dangerously-skip-permissions)--dangerously-skip-permissions[Skip permission prompts (use with caution)]'
'--debug-file[Write debug logs to a specific file path]:path:_files'
'--disable-slash-commands[Disable all skills]'
'(--disallowed-tools)--disallowedTools[Tools that should be disallowed without prompting]:tools:'
'(--disallowedTools)--disallowed-tools[Tools that should be disallowed without prompting]:tools:'
'--fallback-model[Enable automatic fallback model when default is overloaded]:model-name:_claude_models'
'*--file[File resources to download at startup (file_id\:relative_path)]:specs:'
'--fork-session[Create a new session ID when resuming instead of reusing original]'
'--from-pr[Resume a session linked to a PR by number/URL or open interactive picker]::pr-number-or-url:'
'--ide[Automatically connect to IDE on startup if exactly one valid IDE is available]'
'--include-partial-messages[Include partial streaming events in output (requires --print and --output-format=stream-json)]'
'--input-format[Specify input format for print mode]:format:(text stream-json)'
'--json-schema[Get validated JSON output matching a JSON Schema]:json-schema:'
'--max-budget-usd[Maximum dollar amount to spend on API calls (only works with --print)]:amount:'
'--max-turns[Limit the number of agentic turns in non-interactive mode]:number:'
'*--mcp-config[Load MCP servers from JSON files or strings]:config-file:_files -g "*.json"'
'--mcp-debug[Enable MCP debug mode (deprecated, use --debug instead)]'
'--model[Set the model for the current session]:model:_claude_models'
'(--chrome)--no-chrome[Disable Claude in Chrome integration]'
'--no-session-persistence[Disable session persistence (only works with --print)]'
'--output-format[Specify output format for print mode]:format:(text json stream-json)'
'--permission-mode[Begin in a specified permission mode]:mode:(acceptEdits bypassPermissions default delegate dontAsk plan)'
'*--plugin-dir[Load plugins from directories for this session only]:directory:_directories'
'--replay-user-messages[Re-emit user messages from stdin back on stdout for acknowledgment]'
'--session-id[Use a specific session ID for the conversation]:uuid:'
'--setting-sources[Comma-separated list of setting sources to load]:sources:'
'--settings[Path to a settings JSON file or a JSON string]:settings:_files -g "*.json"'
'--strict-mcp-config[Only use MCP servers from --mcp-config, ignoring all other MCP configurations]'
'(--append-system-prompt)--system-prompt[Replace the entire system prompt with custom text]:prompt-text:'
'*--tools[Specify the list of available tools from the built-in set]:tools:'
'--verbose[Enable verbose logging, shows full turn-by-turn output]'
)
_arguments -C \
$_global_flags \
'1: :->cmds' \
'*:: :->args' && ret=0
case $state in
cmds)
_describe -t commands 'claude commands' commands && ret=0
_message 'query or prompt string' && ret=0
;;
args)
case $words[1] in
doctor|setup-token)
_arguments '(- 1 *)'{-h,--help}'[Show help information]' && ret=0
;;
install)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'--force[Force installation even if already installed]' \
'1::target:(stable latest)' && ret=0
;;
update|upgrade)
_arguments '(- 1 *)'{-h,--help}'[Show help information]' && ret=0
;;
mcp)
local -a mcp_commands=(
'add:Add an MCP server'
'add-from-claude-desktop:Import MCP servers from Claude Desktop'
'add-json:Add an MCP server with a JSON string'
'get:Get details about an MCP server'
'list:List configured MCP servers'
'remove:Remove an MCP server'
'reset-project-choices:Reset all approved and rejected project-scoped servers'
'serve:Start the Claude Code MCP server'
)
_arguments -C \
'(- 1 *)'{-h,--help}'[Show help information]' \
'1: :->mcp_cmds' \
'*:: :->mcp_args' && ret=0
case $state in
mcp_cmds)
_describe -t mcp-commands 'mcp subcommands' mcp_commands && ret=0
;;
mcp_args)
case $words[1] in
add)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'--callback-port[Fixed port for OAuth callback]:port:' \
'--client-id[OAuth client ID for HTTP/SSE servers]:clientId:' \
'--client-secret[Prompt for OAuth client secret]' \
'*'{-e,--env}'[Set environment variables]:env-var:' \
'*'{-H,--header}'[Set headers]:header:' \
'(-s --scope)'{-s,--scope}'[Configuration scope]:scope:(local user project)' \
'(-t --transport)'{-t,--transport}'[Transport type]:transport:(stdio sse http)' \
'1:name:' \
'2:command-or-url:_files' \
'*:args:' && ret=0
;;
add-from-claude-desktop)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'(-s --scope)'{-s,--scope}'[Configuration scope]:scope:(local user project)' && ret=0
;;
add-json)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'--client-secret[Prompt for OAuth client secret]' \
'(-s --scope)'{-s,--scope}'[Configuration scope]:scope:(local user project)' \
'1:name:' \
'2:json:' && ret=0
;;
get)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'1:name:_claude_mcp_servers' && ret=0
;;
list)
_arguments '(- 1 *)'{-h,--help}'[Show help information]' && ret=0
;;
remove)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'(-s --scope)'{-s,--scope}'[Configuration scope]:scope:(local user project)' \
'1:name:_claude_mcp_servers' && ret=0
;;
reset-project-choices)
_arguments '(- 1 *)'{-h,--help}'[Show help information]' && ret=0
;;
serve)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'(-d --debug)'{-d,--debug}'[Enable debug mode]' \
'--verbose[Override verbose mode setting from config]' && ret=0
;;
esac
;;
esac
;;
plugin)
local -a plugin_commands=(
'install:Install a plugin from available marketplaces'
'uninstall:Uninstall an installed plugin'
'remove:Uninstall an installed plugin'
'list:List installed plugins'
'enable:Enable a disabled plugin'
'disable:Disable an enabled plugin'
'update:Update a plugin to the latest version'
'validate:Validate a plugin or marketplace manifest'
'marketplace:Manage Claude Code marketplaces'
)
_arguments -C \
'(- 1 *)'{-h,--help}'[Show help information]' \
'1: :->plugin_cmds' \
'*:: :->plugin_args' && ret=0
case $state in
plugin_cmds)
_describe -t plugin-commands 'plugin subcommands' plugin_commands && ret=0
;;
plugin_args)
case $words[1] in
install|i)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'(-s --scope)'{-s,--scope}'[Installation scope]:scope:(user project local)' \
'1:plugin:' && ret=0
;;
uninstall|remove)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'(-s --scope)'{-s,--scope}'[Uninstall from scope]:scope:(user project local)' \
'1:plugin:_claude_plugins' && ret=0
;;
list)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'--available[Include available plugins from marketplaces (requires --json)]' \
'--json[Output as JSON]' && ret=0
;;
enable)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'(-s --scope)'{-s,--scope}'[Installation scope]:scope:(user project local)' \
'1:plugin:_claude_plugins' && ret=0
;;
disable)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'(-a --all)'{-a,--all}'[Disable all enabled plugins]' \
'(-s --scope)'{-s,--scope}'[Installation scope]:scope:(user project local)' \
'1::plugin:_claude_plugins' && ret=0
;;
update)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'(-s --scope)'{-s,--scope}'[Installation scope]:scope:(user project local managed)' \
'1:plugin:_claude_plugins' && ret=0
;;
validate)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'1:path:_files' && ret=0
;;
marketplace)
local -a marketplace_commands=(
'add:Add a marketplace from a URL, path, or GitHub repo'
'list:List all configured marketplaces'
'remove:Remove a configured marketplace'
'update:Update marketplace(s) from their source'
)
_arguments -C \
'(- 1 *)'{-h,--help}'[Show help information]' \
'1: :->mp_cmds' \
'*:: :->mp_args' && ret=0
case $state in
mp_cmds)
_describe -t marketplace-commands 'marketplace subcommands' marketplace_commands && ret=0
;;
mp_args)
case $words[1] in
add)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'1:source:' && ret=0
;;
list)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'--json[Output as JSON]' && ret=0
;;
remove|rm)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'1:name:' && ret=0
;;
update)
_arguments \
'(- 1 *)'{-h,--help}'[Show help information]' \
'1::name:' && ret=0
;;
esac
;;
esac
;;
esac
;;
esac
;;
esac
;;
esac
return ret
}
_claude "$@"
# Local Variables:
# mode: sh
# End:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment