feat(cli): refresh help and shell completion UX

- add a shared command catalog for root help and completion routing

- replace shell-local completion logic with a hidden __complete backend

- add topic help and parity tests for router, help, and completion coverage
This commit is contained in:
Tam Nhu Tran committed 2026-04-03 00:53:10 -04:00
1 parent 3eb55d0e33
commit 24ba2abe10
15 files changed
+1227 -2118

No files matched your search

+56 -132
View File
@@ -2,14 +2,16 @@
Tab completion for CCS commands, subcommands, profiles, and flags.
The completion scripts are thin adapters over the hidden `ccs __complete` backend so
all supported shells stay aligned with the same command graph.
**Supported Shells:** Bash, Zsh, Fish, PowerShell
## Features
- Complete profile names (both settings-based and account-based)
- Complete `ccs auth` subcommands (create, list, show, remove, default)
- Complete flags (`--help`, `--version`, `--json`, `--verbose`, `--yes`)
- Complete profile names for auth subcommands
- Complete root commands, help topics, provider shortcuts, and command flags
- Complete `ccs auth` and `ccs api` lifecycle subcommands
- Context-aware: suggests relevant options based on current command
## Quick Install (Recommended)
@@ -32,6 +34,12 @@ ccs --shell-completion --fish # Force fish
ccs --shell-completion --powershell # Force PowerShell
```
**Help and verification:**
```bash
ccs help completion
ccs --shell-completion --force
```
## Manual Installation
Completion files are installed to `~/.ccs/completions/` during `npm install`.
@@ -116,188 +124,104 @@ sudo cp scripts/completion/ccs.fish /usr/share/fish/vendor_completions.d/
```bash
$ ccs <TAB>
auth doctor glm kimi work personal --help --version
auth api cliproxy config doctor docker help
$ ccs auth <TAB>
create list show remove default --help
$ ccs help <TAB>
profiles providers completion targets
```
### Profile Completion
### Context Completion
```bash
$ ccs auth show <TAB>
work personal team --json
$ ccs auth remove <TAB>
work personal team --yes -y
$ ccs api <TAB>
create discover copy export import remove
```
### Flag Completion
### Backend Contract
```bash
$ ccs auth list <TAB>
--verbose --json
$ ccs auth show work <TAB>
--json
$ ccs __complete --shell bash --current do
doctor
docker
```
## Completion Behavior
### Top-level (after `ccs`)
- Built-in commands: `auth`, `doctor`
- Flags: `--help`, `--version`, `-h`, `-v`
- Settings-based profiles: from `~/.ccs/config.json`
- Account-based profiles: from `~/.ccs/profiles.json`
### After `ccs auth`
- Subcommands: `create`, `list`, `show`, `remove`, `default`
- Flags: `--help`, `-h`
### After `ccs auth <subcommand>`
- **create**: No completion (user enters new profile name)
- Flags: `--force`
- **list**: No profile completion
- Flags: `--verbose`, `--json`
- **show**: Account profiles only
- Flags: `--json`
- **remove**: Account profiles only
- Flags: `--yes`, `-y`
- **default**: Account profiles only
### After `ccs <profile>`
- No completion (Claude CLI arguments are free-form)
Shell adapters now call the shared CCS completion backend instead of maintaining their own
copy of the command graph. That means:
- top-level commands, help topics, and provider shortcuts come from CCS itself
- dynamic profiles and CLIProxy variants resolve through the real config loaders
- bash, zsh, fish, and PowerShell stay aligned with the same completion logic
## Troubleshooting
### Bash: Completion not working
### Bash
1. Check if bash-completion is installed:
```bash
# macOS
brew install bash-completion
# Ubuntu/Debian
sudo apt install bash-completion
```
2. Verify jq is installed (required for profile completion):
```bash
command -v jq
```
3. Check if completion is loaded:
1. Check if completion is loaded:
```bash
complete -p ccs
```
Should output:
```
complete -F _ccs_completion ccs
2. Verify the backend directly:
```bash
ccs __complete --shell bash --current "" -- help
```
### Zsh: Completion not working
### Zsh
1. Verify completion system is enabled in `~/.zshrc`:
1. Verify completion system is enabled:
```zsh
autoload -Uz compinit && compinit
```
2. Check if completion is loaded:
```zsh
which _ccs
```
3. Rebuild completion cache:
2. Rebuild the cache if needed:
```zsh
rm ~/.zcompdump && compinit
```
### PowerShell: Completion not working
1. Check PowerShell version (5.1+ required):
```powershell
$PSVersionTable.PSVersion
3. Verify the backend directly:
```zsh
ccs __complete --shell zsh --current "" -- help
```
2. Verify profile is loaded:
### PowerShell
1. Check that the profile exists:
```powershell
Test-Path $PROFILE
```
3. Check if completion is registered:
2. Verify the backend directly:
```powershell
(Get-ArgumentCompleter).CommandName | Select-String ccs
ccs __complete --shell powershell --current "" -- help
```
### Fish: Completion not working
### Fish
1. Check Fish version (3.0+ required):
```fish
fish --version
```
2. Verify completion file is in the right location:
1. Verify completion file location:
```fish
ls ~/.config/fish/completions/ccs.fish
```
3. Verify jq is installed (required for profile completion):
```fish
which jq
```
4. Test completion manually:
2. Test completion manually:
```fish
complete -C'ccs '
```
5. If needed, rebuild completions:
3. Verify the backend directly:
```fish
fish_update_completions
ccs __complete --shell fish --current "" -- help
```
## Technical Details
### Bash Implementation
- Uses `complete -F` for programmable completion
- Compatible with bash 3.2+ (macOS default)
- Reads profiles dynamically using `jq`
- Context-aware based on `COMP_CWORD` and `COMP_WORDS`
### Zsh Implementation
- Uses `_arguments` and `_describe` for rich completion
- Compatible with zsh 5.0+
- Supports completion descriptions
- Context-aware using `$state` and `$words`
### PowerShell Implementation
- Uses `Register-ArgumentCompleter`
- Compatible with PowerShell 5.1+
- Reads profiles dynamically using `ConvertFrom-Json`
- Provides `CompletionResult` objects
### Fish Implementation
- Uses declarative `complete` command
- Compatible with Fish 3.0+
- Automatic loading from `~/.config/fish/completions/`
- Helper functions for dynamic profile loading
- Context-aware using `__fish_seen_subcommand_from`
- No manual sourcing required
## Dependencies
- **jq**: Required for reading profiles from JSON files
- Install: `brew install jq` (macOS) or `apt install jq` (Ubuntu)
- Already required by CCS core functionality
- Bash uses `complete -F`
- Zsh uses a custom `_ccs` completion function
- Fish uses `complete -a` with backend command substitution
- PowerShell uses `Register-ArgumentCompleter`
- All four shells now delegate suggestion logic to `ccs __complete`
## Contributing
When adding new commands or flags:
1. Update all four completion scripts (bash, zsh, fish, PowerShell)
2. Test on each shell
3. Update this README with new completion examples
4. Maintain cross-shell parity
When adding or changing command surfaces:
1. Update the shared TypeScript command/completion catalog
2. Run `bun run validate`
3. Smoke-check at least one installed shell adapter plus the backend directly
## See Also
+28 -196
View File
@@ -1,210 +1,42 @@
# Bash completion for CCS (Claude Code Switch)
# Compatible with bash 3.2+
#
# Installation:
# Add to ~/.bashrc or ~/.bash_profile:
# source /path/to/ccs/scripts/completion/ccs.bash
#
# Or install system-wide (requires sudo):
# sudo cp scripts/completion/ccs.bash /etc/bash_completion.d/ccs
_ccs_completion() {
local cur prev words cword
local cur
COMPREPLY=()
# Get current word and previous word
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Top-level completion (first argument)
if [[ ${COMP_CWORD} -eq 1 ]]; then
local commands="auth api cliproxy config doctor env persist sync update"
local flags="--help --version --shell-completion -h -v -sc"
local cliproxy_profiles="gemini codex agy qwen"
local profiles=""
# Add profiles from config.json (settings-based profiles)
if [[ -f ~/.ccs/config.json ]]; then
profiles="$profiles $(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null || true)"
fi
# Add profiles from profiles.json (account-based profiles)
if [[ -f ~/.ccs/profiles.json ]]; then
profiles="$profiles $(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null || true)"
fi
# Add cliproxy variants from config.json
if [[ -f ~/.ccs/config.json ]]; then
profiles="$profiles $(jq -r '.cliproxy | keys[]' ~/.ccs/config.json 2>/dev/null || true)"
fi
# Combine all options
local opts="$commands $flags $cliproxy_profiles $profiles"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
local tokens_before_current=()
if [[ ${COMP_CWORD} -gt 1 ]]; then
tokens_before_current=("${COMP_WORDS[@]:1:COMP_CWORD-1}")
fi
# CLIProxy provider flags (gemini, codex, agy, qwen)
if [[ ${COMP_WORDS[1]} =~ ^(gemini|codex|agy|qwen)$ ]]; then
local provider_flags="--auth --config --logout --headless --help -h"
COMPREPLY=( $(compgen -W "${provider_flags}" -- ${cur}) )
return 0
fi
# auth subcommands
if [[ ${prev} == "auth" ]]; then
local auth_commands="create list show remove default --help -h"
COMPREPLY=( $(compgen -W "${auth_commands}" -- ${cur}) )
return 0
fi
# api subcommands
if [[ ${prev} == "api" ]]; then
local api_commands="create list remove --help -h"
COMPREPLY=( $(compgen -W "${api_commands}" -- ${cur}) )
return 0
fi
# cliproxy subcommands
if [[ ${prev} == "cliproxy" ]]; then
local cliproxy_commands="create list remove --install --latest --help -h"
COMPREPLY=( $(compgen -W "${cliproxy_commands}" -- ${cur}) )
return 0
fi
# Completion for cliproxy subcommands
if [[ ${COMP_WORDS[1]} == "cliproxy" ]]; then
case "${prev}" in
remove|delete|rm)
# Complete with cliproxy variant names
if [[ -f ~/.ccs/config.json ]]; then
local variants=$(jq -r '.cliproxy | keys[]' ~/.ccs/config.json 2>/dev/null || true)
COMPREPLY=( $(compgen -W "${variants}" -- ${cur}) )
fi
return 0
;;
create)
# Complete with create flags
COMPREPLY=( $(compgen -W "--provider --model --force --yes -y" -- ${cur}) )
return 0
;;
--provider)
# Complete with provider names
COMPREPLY=( $(compgen -W "gemini codex agy qwen" -- ${cur}) )
return 0
;;
list|ls)
# No flags for list
return 0
;;
--install)
# User enters version number
return 0
;;
esac
fi
# Completion for api subcommands
if [[ ${COMP_WORDS[1]} == "api" ]]; then
case "${prev}" in
remove|delete|rm)
# Complete with settings profile names
if [[ -f ~/.ccs/config.json ]]; then
local profiles=$(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null || true)
COMPREPLY=( $(compgen -W "${profiles}" -- ${cur}) )
fi
return 0
;;
create)
# Complete with create flags
COMPREPLY=( $(compgen -W "--base-url --api-key --model --force --yes -y" -- ${cur}) )
return 0
;;
list)
# No flags for list
return 0
;;
esac
fi
# Completion for auth subcommands that need profile names
if [[ ${COMP_WORDS[1]} == "auth" ]]; then
case "${prev}" in
show|remove|default)
# Complete with account profile names only
if [[ -f ~/.ccs/profiles.json ]]; then
local profiles=$(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null || true)
COMPREPLY=( $(compgen -W "${profiles}" -- ${cur}) )
fi
return 0
;;
create)
# Complete with create flags
COMPREPLY=( $(compgen -W "--force" -- ${cur}) )
return 0
;;
list)
# Complete with list flags
COMPREPLY=( $(compgen -W "--verbose --json" -- ${cur}) )
return 0
;;
esac
fi
# env subcommands
if [[ ${COMP_WORDS[1]} == "env" ]]; then
case "${prev}" in
env)
# Complete with profile names and flags (inline profiles since $cliproxy_profiles is out of scope)
local env_opts="--format --shell --ide --help -h gemini codex agy qwen iflow kiro ghcp claude default"
if [[ -f ~/.ccs/config.json ]]; then
env_opts="$env_opts $(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null || true)"
fi
if [[ -f ~/.ccs/profiles.json ]]; then
env_opts="$env_opts $(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null || true)"
fi
COMPREPLY=( $(compgen -W "${env_opts}" -- ${cur}) )
return 0
;;
--format)
COMPREPLY=( $(compgen -W "openai anthropic raw claude-extension" -- ${cur}) )
return 0
;;
--shell)
COMPREPLY=( $(compgen -W "auto bash zsh fish powershell" -- ${cur}) )
return 0
;;
--ide)
COMPREPLY=( $(compgen -W "vscode cursor windsurf" -- ${cur}) )
return 0
;;
*)
COMPREPLY=( $(compgen -W "--format --shell --ide --help -h" -- ${cur}) )
return 0
;;
esac
fi
# Flags for doctor command
if [[ ${COMP_WORDS[1]} == "doctor" ]]; then
COMPREPLY=( $(compgen -W "--help -h" -- ${cur}) )
return 0
fi
# Flags for update command
if [[ ${COMP_WORDS[1]} == "update" ]]; then
COMPREPLY=( $(compgen -W "--force --beta --dev --help -h" -- ${cur}) )
return 0
fi
# Flags for shell-completion command
if [[ ${prev} == "--shell-completion" || ${prev} == "-sc" ]]; then
COMPREPLY=( $(compgen -W "--bash --zsh --fish --powershell" -- ${cur}) )
return 0
fi
while IFS= read -r line; do
[[ -n "${line}" ]] && COMPREPLY+=("${line}")
done < <(__ccs_completion_run "${cur}" "${tokens_before_current[@]}")
return 0
}
# Register completion function
__ccs_completion_run() {
local current="$1"
shift || true
local script_dir repo_root repo_cli
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "${script_dir}/../.." && pwd)"
repo_cli="${repo_root}/dist/ccs.js"
if [[ ! -f "${repo_cli}" ]]; then
repo_cli="${repo_root}/bin/ccs.js"
fi
if [[ -f "${repo_cli}" ]]; then
node "${repo_cli}" __complete --shell bash --current "${current}" -- "$@" 2>/dev/null
return 0
fi
if command -v ccs >/dev/null 2>&1; then
ccs __complete --shell bash --current "${current}" -- "$@" 2>/dev/null
fi
}
complete -F _ccs_completion ccs
+21 -250
View File
@@ -1,256 +1,27 @@
# Fish completion for CCS (Claude Code Switch)
# Compatible with fish 3.0+
#
# Features:
# - Categorized completions with [cmd], [proxy], [model], and [account] prefixes
# - Dynamic profile loading from config.json and profiles.json
# - Context-aware subcommand completion
#
# Installation:
# Copy to ~/.config/fish/completions/:
# mkdir -p ~/.config/fish/completions
# cp scripts/completion/ccs.fish ~/.config/fish/completions/
#
# Fish will automatically load completions from this directory.
# No need to source or reload - completions are loaded on demand.
# Helper function to get settings profiles
function __fish_ccs_get_settings_profiles
set -l config_path ~/.ccs/config.json
function __fish_ccs_complete
set -l tokens_before_current (commandline -opc)
if test (count $tokens_before_current) -gt 0
set -e tokens_before_current[1]
end
if test -f $config_path
jq -r '.profiles | keys[]' $config_path 2>/dev/null
set -l current (commandline -ct)
set -l script_file (status filename)
set -l repo_root (realpath (dirname $script_file)/../.. 2>/dev/null)
set -l repo_cli "$repo_root/dist/ccs.js"
if not test -f "$repo_cli"
set repo_cli "$repo_root/bin/ccs.js"
end
if test -f "$repo_cli"
node "$repo_cli" __complete --shell fish --current "$current" -- $tokens_before_current 2>/dev/null
return
end
if command -sq ccs
ccs __complete --shell fish --current "$current" -- $tokens_before_current 2>/dev/null
end
end
# Helper function to get custom/unknown settings profiles
function __fish_ccs_get_custom_settings_profiles
set -l config_path ~/.ccs/config.json
set -l known_profiles default glm kimi
if test -f $config_path
set -l all_profiles (jq -r '.profiles | keys[]' $config_path 2>/dev/null)
for profile in $all_profiles
if not contains $profile $known_profiles
echo $profile
end
end
end
end
# Helper function to get cliproxy variants
function __fish_ccs_get_cliproxy_variants
set -l config_path ~/.ccs/config.json
if test -f $config_path
jq -r '.cliproxy | keys[]' $config_path 2>/dev/null
end
end
# Helper function to get profiles with all types
function __fish_ccs_get_profiles
__fish_ccs_get_settings_profiles
__fish_ccs_get_account_profiles
end
# Helper function to get account profiles only
function __fish_ccs_get_account_profiles
set -l profiles_path ~/.ccs/profiles.json
if test -f $profiles_path
jq -r '.profiles | keys[]' $profiles_path 2>/dev/null
end
end
# Helper function to check if we're in auth context
function __fish_ccs_using_auth
__fish_seen_subcommand_from auth
end
# Helper function to check specific auth subcommand
function __fish_ccs_using_auth_subcommand
set -l subcommand $argv[1]
__fish_ccs_using_auth; and __fish_seen_subcommand_from $subcommand
end
# Helper function to check if we're in api context
function __fish_ccs_using_api
__fish_seen_subcommand_from api
end
# Helper function to check specific api subcommand
function __fish_ccs_using_api_subcommand
set -l subcommand $argv[1]
__fish_ccs_using_api; and __fish_seen_subcommand_from $subcommand
end
# Helper function to check if we're in cliproxy context
function __fish_ccs_using_cliproxy
__fish_seen_subcommand_from cliproxy
end
# Helper function to check specific cliproxy subcommand
function __fish_ccs_using_cliproxy_subcommand
set -l subcommand $argv[1]
__fish_ccs_using_cliproxy; and __fish_seen_subcommand_from $subcommand
end
# Helper function to check if we're in profile context
function __fish_ccs_using_profile
__fish_seen_subcommand_from profile
end
# Helper function to check specific profile subcommand
function __fish_ccs_using_profile_subcommand
set -l subcommand $argv[1]
__fish_ccs_using_profile; and __fish_seen_subcommand_from $subcommand
end
# Helper function to check if we're using a CLIProxy provider
function __fish_ccs_using_provider
__fish_seen_subcommand_from gemini codex agy qwen
end
# Disable file completion for ccs
complete -c ccs -f
# Top-level flags
complete -c ccs -s h -l help -d 'Show help message'
complete -c ccs -s v -l version -d 'Show version information'
complete -c ccs -s sc -l shell-completion -d 'Install shell completion'
# Commands - grouped with [cmd] prefix for visual distinction
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'auth' -d '[cmd] Manage multiple Claude accounts'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'api' -d '[cmd] Manage API profiles (create/remove)'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'cliproxy' -d '[cmd] Manage CLIProxy variants and binary'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'doctor' -d '[cmd] Run health check and diagnostics'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'env' -d '[cmd] Export env vars for third-party tools'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'sync' -d '[cmd] Sync delegation commands and skills'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'update' -d '[cmd] Update CCS to latest version'
# CLIProxy profiles - grouped with [proxy] prefix for OAuth providers
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'gemini' -d '[proxy] Google Gemini (OAuth)'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'codex' -d '[proxy] OpenAI Codex (OAuth)'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'agy' -d '[proxy] Antigravity (OAuth)'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'qwen' -d '[proxy] Qwen Code (OAuth)'
# Model profiles - grouped with [model] prefix for visual distinction
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'default' -d '[model] Default Claude Sonnet 4.5'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'glm' -d '[model] GLM-4.6 (cost-optimized)'
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'kimi' -d '[model] Kimi for Coding (long-context)'
# Custom model profiles - dynamic with [model] prefix
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a '(__fish_ccs_get_custom_settings_profiles)' -d '[model] Settings-based profile'
# CLIProxy variants - dynamic with [variant] prefix
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a '(__fish_ccs_get_cliproxy_variants)' -d '[variant] CLIProxy variant'
# Account profiles - dynamic with [account] prefix
complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a '(__fish_ccs_get_account_profiles)' -d '[account] Account-based profile'
# shell-completion subflags
complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l bash -d 'Install for bash'
complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l zsh -d 'Install for zsh'
complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l fish -d 'Install for fish'
complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l powershell -d 'Install for PowerShell'
# CLIProxy provider flags (gemini, codex, agy, qwen)
complete -c ccs -n '__fish_ccs_using_provider' -l auth -d 'Authenticate only'
complete -c ccs -n '__fish_ccs_using_provider' -l config -d 'Change model configuration'
complete -c ccs -n '__fish_ccs_using_provider' -l logout -d 'Clear authentication'
complete -c ccs -n '__fish_ccs_using_provider' -l headless -d 'Headless auth (for SSH)'
complete -c ccs -n '__fish_ccs_using_provider' -s h -l help -d 'Show help'
# update command flags
complete -c ccs -n '__fish_seen_subcommand_from update' -l force -d 'Force reinstall current version'
complete -c ccs -n '__fish_seen_subcommand_from update' -l beta -d 'Install from dev channel'
complete -c ccs -n '__fish_seen_subcommand_from update' -l dev -d 'Install from dev channel'
complete -c ccs -n '__fish_seen_subcommand_from update' -s h -l help -d 'Show help'
# doctor command flags
complete -c ccs -n '__fish_seen_subcommand_from doctor' -s h -l help -d 'Show help for doctor command'
# env command completions
complete -c ccs -n '__fish_seen_subcommand_from env; and not __fish_seen_argument -l format -l shell' -a 'gemini codex agy qwen iflow kiro ghcp claude' -d '[proxy] CLIProxy profile'
complete -c ccs -n '__fish_seen_subcommand_from env' -l format -d 'Output format'
complete -c ccs -n '__fish_seen_subcommand_from env; and __fish_seen_argument -l format' -a 'openai anthropic raw' -d 'Format'
complete -c ccs -n '__fish_seen_subcommand_from env' -l shell -d 'Shell syntax'
complete -c ccs -n '__fish_seen_subcommand_from env; and __fish_seen_argument -l shell' -a 'auto bash zsh fish powershell' -d 'Shell'
complete -c ccs -n '__fish_seen_subcommand_from env' -s h -l help -d 'Show help for env command'
# ============================================================================
# auth subcommands
# ============================================================================
complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'create' -d 'Create new profile and login'
complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'list' -d 'List all saved profiles'
complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'show' -d 'Show profile details'
complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'remove' -d 'Remove saved profile'
complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'default' -d 'Set default profile'
complete -c ccs -n '__fish_ccs_using_auth' -s h -l help -d 'Show help for auth commands'
complete -c ccs -n '__fish_ccs_using_auth_subcommand create' -l force -d 'Allow overwriting existing profile'
complete -c ccs -n '__fish_ccs_using_auth_subcommand list' -l verbose -d 'Show additional details'
complete -c ccs -n '__fish_ccs_using_auth_subcommand list' -l json -d 'Output in JSON format'
complete -c ccs -n '__fish_ccs_using_auth_subcommand show' -a '(__fish_ccs_get_account_profiles)' -d 'Account profile'
complete -c ccs -n '__fish_ccs_using_auth_subcommand show' -l json -d 'Output in JSON format'
complete -c ccs -n '__fish_ccs_using_auth_subcommand remove' -a '(__fish_ccs_get_account_profiles)' -d 'Account profile'
complete -c ccs -n '__fish_ccs_using_auth_subcommand remove' -l yes -d 'Skip confirmation prompts'
complete -c ccs -n '__fish_ccs_using_auth_subcommand remove' -s y -d 'Skip confirmation prompts'
complete -c ccs -n '__fish_ccs_using_auth_subcommand default' -a '(__fish_ccs_get_account_profiles)' -d 'Account profile'
# ============================================================================
# api subcommands
# ============================================================================
complete -c ccs -n '__fish_ccs_using_api; and not __fish_seen_subcommand_from create list remove' -a 'create' -d 'Create new API profile'
complete -c ccs -n '__fish_ccs_using_api; and not __fish_seen_subcommand_from create list remove' -a 'list' -d 'List all API profiles'
complete -c ccs -n '__fish_ccs_using_api; and not __fish_seen_subcommand_from create list remove' -a 'remove' -d 'Remove an API profile'
complete -c ccs -n '__fish_ccs_using_api' -s h -l help -d 'Show help for api commands'
complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l base-url -d 'API base URL'
complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l api-key -d 'API key'
complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l model -d 'Default model'
complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l force -d 'Overwrite existing profile'
complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l yes -d 'Skip prompts'
complete -c ccs -n '__fish_ccs_using_api_subcommand create' -s y -d 'Skip prompts'
complete -c ccs -n '__fish_ccs_using_api_subcommand remove' -a '(__fish_ccs_get_settings_profiles)' -d 'Settings profile'
complete -c ccs -n '__fish_ccs_using_api_subcommand remove' -l yes -d 'Skip confirmation'
complete -c ccs -n '__fish_ccs_using_api_subcommand remove' -s y -d 'Skip confirmation'
# ============================================================================
# cliproxy subcommands
# ============================================================================
complete -c ccs -n '__fish_ccs_using_cliproxy; and not __fish_seen_subcommand_from create list remove' -a 'create' -d 'Create new CLIProxy variant'
complete -c ccs -n '__fish_ccs_using_cliproxy; and not __fish_seen_subcommand_from create list remove' -a 'list' -d 'List all CLIProxy variants'
complete -c ccs -n '__fish_ccs_using_cliproxy; and not __fish_seen_subcommand_from create list remove' -a 'remove' -d 'Remove a CLIProxy variant'
complete -c ccs -n '__fish_ccs_using_cliproxy' -s h -l help -d 'Show help for cliproxy commands'
complete -c ccs -n '__fish_ccs_using_cliproxy' -l install -d 'Install specific version'
complete -c ccs -n '__fish_ccs_using_cliproxy' -l latest -d 'Install latest version'
complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -l provider -d 'Provider name'
complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create; and __fish_seen_argument -l provider' -a 'gemini codex agy qwen' -d 'Provider'
complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -l model -d 'Model name'
complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -l force -d 'Overwrite existing variant'
complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -l yes -d 'Skip prompts'
complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -s y -d 'Skip prompts'
complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand remove' -a '(__fish_ccs_get_cliproxy_variants)' -d 'CLIProxy variant'
complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand remove' -l yes -d 'Skip confirmation'
complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand remove' -s y -d 'Skip confirmation'
# ============================================================================
# profile subcommands (legacy - redirects to api)
# ============================================================================
complete -c ccs -n '__fish_ccs_using_profile; and not __fish_seen_subcommand_from create list remove' -a 'create' -d 'Create new API profile'
complete -c ccs -n '__fish_ccs_using_profile; and not __fish_seen_subcommand_from create list remove' -a 'list' -d 'List all profiles'
complete -c ccs -n '__fish_ccs_using_profile; and not __fish_seen_subcommand_from create list remove' -a 'remove' -d 'Remove a profile'
complete -c ccs -n '__fish_ccs_using_profile' -s h -l help -d 'Show help for profile commands'
complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l base-url -d 'API base URL'
complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l api-key -d 'API key'
complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l model -d 'Default model'
complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l force -d 'Overwrite existing profile'
complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l yes -d 'Skip prompts'
complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -s y -d 'Skip prompts'
complete -c ccs -n '__fish_ccs_using_profile_subcommand remove' -a '(__fish_ccs_get_settings_profiles)' -d 'Settings profile'
complete -c ccs -n '__fish_ccs_using_profile_subcommand remove' -l yes -d 'Skip confirmation'
complete -c ccs -n '__fish_ccs_using_profile_subcommand remove' -s y -d 'Skip confirmation'
complete -c ccs -f -a "(__fish_ccs_complete)"
+42 -432
View File
@@ -1,446 +1,56 @@
# PowerShell completion for CCS (Claude Code Switch)
# Compatible with PowerShell 5.1+
#
# Installation:
# Add to your PowerShell profile ($PROFILE):
# . /path/to/ccs/scripts/completion/ccs.ps1
#
# Or install for current user:
# Copy-Item scripts/completion/ccs.ps1 ~\Documents\PowerShell\Scripts\
# Add to profile: . ~\Documents\PowerShell\Scripts\ccs.ps1
function Invoke-CcsCompletionBackend {
param(
[string]$CurrentWord,
[string[]]$TokensBeforeCurrent
)
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
$repoCli = Join-Path $repoRoot 'dist\ccs.js'
if (-not (Test-Path $repoCli)) {
$repoCli = Join-Path $repoRoot 'bin\ccs.js'
}
if (Test-Path $repoCli) {
& node $repoCli __complete --shell powershell --current $CurrentWord -- @TokensBeforeCurrent 2>$null
return
}
if (Get-Command ccs -ErrorAction SilentlyContinue) {
& ccs __complete --shell powershell --current $CurrentWord -- @TokensBeforeCurrent 2>$null
}
}
Register-ArgumentCompleter -CommandName ccs -ScriptBlock {
param($commandName, $wordToComplete, $commandAst, $fakeBoundParameters)
$commands = @('auth', 'api', 'cliproxy', 'doctor', 'env', 'sync', 'update', '--help', '--version', '--shell-completion', '-h', '-v', '-sc')
$cliproxyProfiles = @('gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp', 'claude')
$authCommands = @('create', 'list', 'show', 'remove', 'default', '--help', '-h')
$apiCommands = @('create', 'list', 'remove', '--help', '-h')
$cliproxyCommands = @('create', 'list', 'remove', '--install', '--latest', '--help', '-h')
$apiCreateFlags = @('--base-url', '--api-key', '--model', '--force', '--yes', '-y')
$cliproxyCreateFlags = @('--provider', '--model', '--force', '--yes', '-y')
$providerFlags = @('--auth', '--config', '--logout', '--headless', '--help', '-h')
$updateFlags = @('--force', '--beta', '--dev', '--help', '-h')
$envFlags = @('--format', '--shell', '--help', '-h')
$envFormats = @('openai', 'anthropic', 'raw')
$envShells = @('auto', 'bash', 'zsh', 'fish', 'powershell')
$shellCompletionFlags = @('--bash', '--zsh', '--fish', '--powershell')
$listFlags = @('--verbose', '--json')
$removeFlags = @('--yes', '-y')
$showFlags = @('--json')
$providers = @('gemini', 'codex', 'agy', 'qwen')
# Get current position in command
$words = $commandAst.ToString() -split '\s+' | Where-Object { $_ -ne '' }
$position = $words.Count
# Helper function to get profiles
function Get-CcsProfiles {
param([string]$Type = 'all')
$profiles = @()
# Settings-based profiles
if ($Type -in @('all', 'settings')) {
$configPath = "$env:USERPROFILE\.ccs\config.json"
if (Test-Path $configPath) {
try {
$config = Get-Content $configPath -Raw | ConvertFrom-Json
$profiles += $config.profiles.PSObject.Properties.Name
} catch {}
$commandElements = @($commandAst.CommandElements | ForEach-Object { $_.Extent.Text })
$tokensBeforeCurrent = @()
if ($commandElements.Count -gt 1) {
$tokensBeforeCurrent = $commandElements[1..($commandElements.Count - 1)]
if ($tokensBeforeCurrent.Count -gt 0 -and $tokensBeforeCurrent[-1] -eq $wordToComplete) {
$tokensBeforeCurrent = if ($tokensBeforeCurrent.Count -gt 1) {
$tokensBeforeCurrent[0..($tokensBeforeCurrent.Count - 2)]
} else {
@()
}
}
# Account-based profiles
if ($Type -in @('all', 'account')) {
$profilesPath = "$env:USERPROFILE\.ccs\profiles.json"
if (Test-Path $profilesPath) {
try {
$data = Get-Content $profilesPath -Raw | ConvertFrom-Json
$profiles += $data.profiles.PSObject.Properties.Name
} catch {}
}
}
# CLIProxy variants
if ($Type -in @('all', 'cliproxy')) {
$configPath = "$env:USERPROFILE\.ccs\config.json"
if (Test-Path $configPath) {
try {
$config = Get-Content $configPath -Raw | ConvertFrom-Json
if ($config.cliproxy) {
$profiles += $config.cliproxy.PSObject.Properties.Name
}
} catch {}
}
}
return $profiles | Sort-Object -Unique
}
# Top-level completion
if ($position -eq 2) {
$allOptions = $commands + $cliproxyProfiles + (Get-CcsProfiles)
$allOptions | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
foreach ($line in Invoke-CcsCompletionBackend -CurrentWord $wordToComplete -TokensBeforeCurrent $tokensBeforeCurrent) {
if ([string]::IsNullOrWhiteSpace($line)) {
continue
}
return
}
# shell-completion flag completion
if ($words[1] -eq '--shell-completion' -or $words[1] -eq '-sc') {
if ($position -eq 3) {
$shellCompletionFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
return
}
$parts = $line -split "`t", 2
$value = $parts[0]
$description = if ($parts.Count -gt 1) { $parts[1] } else { $parts[0] }
# CLIProxy provider flags (gemini, codex, agy, qwen)
if ($words[1] -in $cliproxyProfiles) {
$providerFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
return
}
# update command completion
if ($words[1] -eq 'update') {
$updateFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
return
}
# env command completion
if ($words[1] -eq 'env') {
if ($position -eq 3) {
$options = $cliproxyProfiles + (Get-CcsProfiles -Type settings) + $envFlags
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
} elseif ($position -ge 4) {
switch ($words[$position - 2]) {
'--format' {
$envFormats | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'--shell' {
$envShells | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
default {
$envFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
}
}
return
}
# auth subcommand completion
if ($words[1] -eq 'auth') {
if ($position -eq 3) {
$authCommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
} elseif ($position -eq 4) {
switch ($words[2]) {
'show' {
$options = (Get-CcsProfiles -Type account) + $showFlags
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'remove' {
$options = (Get-CcsProfiles -Type account) + $removeFlags
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'default' {
Get-CcsProfiles -Type account | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'list' {
$listFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'create' {
@('--force') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
}
} elseif ($position -eq 5) {
switch ($words[2]) {
'show' {
$showFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'remove' {
$removeFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
}
}
return
}
# api subcommand completion
if ($words[1] -eq 'api') {
if ($position -eq 3) {
$apiCommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
} elseif ($position -eq 4) {
switch ($words[2]) {
'remove' {
$options = (Get-CcsProfiles -Type settings) + $removeFlags
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'create' {
$apiCreateFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
}
} elseif ($position -eq 5) {
switch ($words[2]) {
'remove' {
$removeFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
}
}
return
}
# cliproxy subcommand completion
if ($words[1] -eq 'cliproxy') {
if ($position -eq 3) {
$cliproxyCommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
} elseif ($position -eq 4) {
switch ($words[2]) {
'remove' {
$options = (Get-CcsProfiles -Type cliproxy) + $removeFlags
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'create' {
$cliproxyCreateFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
}
} elseif ($position -eq 5) {
switch ($words[2]) {
'remove' {
$removeFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'create' {
# After --provider, complete with provider names
if ($words[3] -eq '--provider') {
$providers | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
}
}
}
return
}
# profile subcommand completion (legacy)
if ($words[1] -eq 'profile') {
if ($position -eq 3) {
@('create', 'list', 'remove', '--help', '-h') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
} elseif ($position -eq 4) {
switch ($words[2]) {
'remove' {
$options = (Get-CcsProfiles -Type settings) + $removeFlags
$options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
'create' {
$apiCreateFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
}
} elseif ($position -eq 5) {
switch ($words[2]) {
'remove' {
$removeFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_,
$_,
'ParameterValue',
$_
)
}
}
}
}
return
[System.Management.Automation.CompletionResult]::new(
$value,
$value,
'ParameterValue',
$description
)
}
}
+26 -304
View File
@@ -1,319 +1,41 @@
#compdef ccs
# Zsh completion for CCS (Claude Code Switch)
# Compatible with zsh 5.0+
#
# Installation:
# Add to ~/.zshrc:
# fpath=(~/.zsh/completion $fpath)
# autoload -Uz compinit && compinit
# source /path/to/ccs/scripts/completion/ccs.zsh
#
# Or install system-wide:
# sudo cp scripts/completion/ccs.zsh /usr/local/share/zsh/site-functions/_ccs
# Set up completion styles for better formatting and colors
zstyle ':completion:*:*:ccs:*:commands' list-colors '=(#b)(auth|api|cliproxy|doctor|env|sync|update)([[:space:]]#--[[:space:]]#*)==0\;34=2\;37'
zstyle ':completion:*:*:ccs:*:proxy-profiles' list-colors '=(#b)(gemini|codex|agy|qwen)([[:space:]]#--[[:space:]]#*)==0\;35=2\;37'
zstyle ':completion:*:*:ccs:*:model-profiles' list-colors '=(#b)(default|glm|kimi|[^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;32=2\;37'
zstyle ':completion:*:*:ccs:*:account-profiles' list-colors '=(#b)([^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;33=2\;37'
zstyle ':completion:*:*:ccs:*' group-name ''
zstyle ':completion:*:*:ccs:*:descriptions' format $'\n%B%F{yellow}── %d ──%f%b'
zstyle ':completion:*:*:ccs:*' list-separator ' -- '
zstyle ':completion:*:*:ccs:*' list-rows-first true
zstyle ':completion:*:*:ccs:*' menu select
_ccs() {
local -a commands proxy_profiles settings_profiles_described account_profiles_described cliproxy_variants_described
local curcontext="$curcontext" state line
typeset -A opt_args
local current
current="${words[CURRENT]}"
# Define top-level commands
commands=(
'auth:Manage multiple Claude accounts'
'api:Manage API profiles (create/remove)'
'cliproxy:Manage CLIProxy variants and binary'
'doctor:Run health check and diagnostics'
'env:Export env vars for third-party tools'
'sync:Sync delegation commands and skills'
'update:Update CCS to latest version'
)
# Define CLIProxy hardcoded profiles (OAuth providers)
proxy_profiles=(
'gemini:Google Gemini (OAuth)'
'codex:OpenAI Codex (OAuth)'
'agy:Antigravity (OAuth)'
'qwen:Qwen Code (OAuth)'
'iflow:iFlow (OAuth)'
'kiro:Kiro (OAuth)'
'ghcp:GitHub Copilot (OAuth)'
'claude:Claude Direct (OAuth)'
)
# Define known settings profiles with descriptions
local -A profile_descriptions
profile_descriptions=(
'default' 'Default Claude Sonnet 4.5'
'glm' 'GLM-4.6 (cost-optimized)'
'kimi' 'Kimi for Coding (long-context)'
)
# Load settings-based profiles from config.json
if [[ -f ~/.ccs/config.json ]]; then
local -a raw_settings_profiles
raw_settings_profiles=(${(f)"$(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null)"})
for profile in $raw_settings_profiles; do
local desc="${profile_descriptions[$profile]:-Settings-based profile}"
settings_profiles_described+=("${profile}:${desc}")
done
local -a tokens_before_current
if (( CURRENT > 2 )); then
tokens_before_current=("${words[@]:2:$((CURRENT-2))}")
else
tokens_before_current=()
fi
# Load account-based profiles from profiles.json
if [[ -f ~/.ccs/profiles.json ]]; then
local -a raw_account_profiles
raw_account_profiles=(${(f)"$(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null)"})
for profile in $raw_account_profiles; do
account_profiles_described+=("${profile}:Account-based profile")
done
fi
# Load cliproxy variants from config.json
if [[ -f ~/.ccs/config.json ]]; then
local -a raw_cliproxy_variants
raw_cliproxy_variants=(${(f)"$(jq -r '.cliproxy | keys[]' ~/.ccs/config.json 2>/dev/null)"})
for variant in $raw_cliproxy_variants; do
cliproxy_variants_described+=("${variant}:CLIProxy variant")
done
fi
_arguments -C \
'(- *)'{-h,--help}'[Show help message]' \
'(- *)'{-v,--version}'[Show version information]' \
'(- *)'{-sc,--shell-completion}'[Install shell completion]' \
'1: :->command' \
'*:: :->args'
case $state in
command)
_describe -t commands 'commands' commands
_describe -t proxy-profiles 'CLIProxy profiles' proxy_profiles
_describe -t model-profiles 'model profiles' settings_profiles_described
_describe -t account-profiles 'account profiles' account_profiles_described
_describe -t cliproxy-variants 'CLIProxy variants' cliproxy_variants_described
;;
args)
case $words[1] in
auth)
_ccs_auth
;;
api)
_ccs_api
;;
cliproxy)
_ccs_cliproxy
;;
update)
_arguments \
'--force[Force reinstall current version]' \
'--beta[Install from dev channel]' \
'--dev[Install from dev channel]' \
'(- *)'{-h,--help}'[Show help]'
;;
doctor)
_arguments \
'(- *)'{-h,--help}'[Show help for doctor command]'
;;
env)
_arguments \
'--format[Output format]:format:(openai anthropic raw)' \
'--shell[Shell syntax]:shell:(auto bash zsh fish powershell)' \
'(- *)'{-h,--help}'[Show help]' \
'1:profile:($proxy_profiles ${(k)settings_profiles_described})'
;;
gemini|codex|agy|qwen)
_arguments \
'--auth[Authenticate only]' \
'--config[Change model configuration]' \
'--logout[Clear authentication]' \
'--headless[Headless auth (for SSH)]' \
'(- *)'{-h,--help}'[Show help]'
;;
--shell-completion|-sc)
_arguments \
'--bash[Install for bash]' \
'--zsh[Install for zsh]' \
'--fish[Install for fish]' \
'--powershell[Install for PowerShell]'
;;
*)
_message 'Claude CLI arguments'
;;
esac
;;
esac
local -a suggestions
suggestions=("${(@f)$(__ccs_completion_run "${current}" "${tokens_before_current[@]}")}")
compadd -- "${suggestions[@]}"
}
_ccs_api() {
local curcontext="$curcontext" state line
typeset -A opt_args
__ccs_completion_run() {
local current="$1"
shift || true
local -a api_commands settings_profiles
api_commands=(
'create:Create new API profile (interactive)'
'list:List all API profiles'
'remove:Remove an API profile'
)
if [[ -f ~/.ccs/config.json ]]; then
settings_profiles=(${(f)"$(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null)"})
local script_path script_dir repo_root repo_cli
script_path="${(%):-%N}"
script_dir="${script_path:A:h}"
repo_root="${script_dir:h:h}"
repo_cli="${repo_root}/dist/ccs.js"
if [[ ! -f "${repo_cli}" ]]; then
repo_cli="${repo_root}/bin/ccs.js"
fi
if [[ -f "${repo_cli}" ]]; then
node "${repo_cli}" __complete --shell zsh --current "${current}" -- "$@" 2>/dev/null
return 0
fi
_arguments -C \
'(- *)'{-h,--help}'[Show help for api commands]' \
'1: :->subcommand' \
'*:: :->subargs'
case $state in
subcommand)
_describe -t api-commands 'api commands' api_commands
;;
subargs)
case $words[1] in
create)
_arguments \
'1:profile name:' \
'--base-url[API base URL]:url:' \
'--api-key[API key]:key:' \
'--model[Default model]:model:' \
'--force[Overwrite existing profile]' \
{--yes,-y}'[Skip prompts]'
;;
list)
;;
remove|delete|rm)
_arguments \
'1:profile:($settings_profiles)' \
{--yes,-y}'[Skip confirmation]'
;;
esac
;;
esac
}
_ccs_cliproxy() {
local curcontext="$curcontext" state line
typeset -A opt_args
local -a cliproxy_commands cliproxy_variants providers
cliproxy_commands=(
'create:Create new CLIProxy variant profile'
'list:List all CLIProxy variant profiles'
'remove:Remove a CLIProxy variant profile'
)
providers=(gemini codex agy qwen)
if [[ -f ~/.ccs/config.json ]]; then
cliproxy_variants=(${(f)"$(jq -r '.cliproxy | keys[]' ~/.ccs/config.json 2>/dev/null)"})
if (( $+commands[ccs] )); then
ccs __complete --shell zsh --current "${current}" -- "$@" 2>/dev/null
fi
_arguments -C \
'(- *)'{-h,--help}'[Show help for cliproxy commands]' \
'--install[Install specific version]:version:' \
'--latest[Install latest version]' \
'1: :->subcommand' \
'*:: :->subargs'
case $state in
subcommand)
_describe -t cliproxy-commands 'cliproxy commands' cliproxy_commands
;;
subargs)
case $words[1] in
create)
_arguments \
'1:variant name:' \
'--provider[Provider name]:provider:($providers)' \
'--model[Model name]:model:' \
'--force[Overwrite existing variant]' \
{--yes,-y}'[Skip prompts]'
;;
list|ls)
;;
remove|delete|rm)
_arguments \
'1:variant:($cliproxy_variants)' \
{--yes,-y}'[Skip confirmation]'
;;
esac
;;
esac
}
_ccs_auth() {
local curcontext="$curcontext" state line
typeset -A opt_args
local -a auth_commands account_profiles
auth_commands=(
'create:Create new profile and login'
'list:List all saved profiles'
'show:Show profile details'
'remove:Remove saved profile'
'default:Set default profile'
)
if [[ -f ~/.ccs/profiles.json ]]; then
account_profiles=(${(f)"$(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null)"})
fi
_arguments -C \
'(- *)'{-h,--help}'[Show help for auth commands]' \
'1: :->subcommand' \
'*:: :->subargs'
case $state in
subcommand)
_describe -t auth-commands 'auth commands' auth_commands
;;
subargs)
case $words[1] in
create)
_message 'new profile name'
_arguments '--force[Allow overwriting existing profile]'
;;
list)
_arguments \
'--verbose[Show additional details]' \
'--json[Output in JSON format]'
;;
show)
_arguments \
'1:profile:($account_profiles)' \
'--json[Output in JSON format]'
;;
remove)
_arguments \
'1:profile:($account_profiles)' \
{--yes,-y}'[Skip confirmation prompts]'
;;
default)
_arguments '1:profile:($account_profiles)'
;;
esac
;;
esac
}
_ccs "$@"