From 7faed1d84ba29ba02bf687bae5b3458617512e67 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Mon, 1 Dec 2025 17:54:41 -0500 Subject: [PATCH] feat(shell-completion): add --force flag and fix zsh profile coloring --- scripts/completion/ccs.bash | 32 ++++++++++++- scripts/completion/ccs.fish | 53 +++++++++++++++++---- scripts/completion/ccs.ps1 | 58 ++++++++++++++++++++++- scripts/completion/ccs.zsh | 58 ++++++++++++++++++++++- src/commands/shell-completion-command.ts | 8 +++- src/utils/shell-completion.ts | 59 ++++++++++++++++++------ 6 files changed, 239 insertions(+), 29 deletions(-) diff --git a/scripts/completion/ccs.bash b/scripts/completion/ccs.bash index 63f07fb1..2f134e6e 100644 --- a/scripts/completion/ccs.bash +++ b/scripts/completion/ccs.bash @@ -18,7 +18,7 @@ _ccs_completion() { # Top-level completion (first argument) if [[ ${COMP_CWORD} -eq 1 ]]; then - local commands="auth doctor sync update" + local commands="auth profile doctor sync update" local flags="--help --version --shell-completion -h -v -sc" local profiles="" @@ -45,6 +45,36 @@ _ccs_completion() { return 0 fi + # profile subcommands + if [[ ${prev} == "profile" ]]; then + local profile_commands="create list remove --help -h" + COMPREPLY=( $(compgen -W "${profile_commands}" -- ${cur}) ) + return 0 + fi + + # Completion for profile subcommands + if [[ ${COMP_WORDS[1]} == "profile" ]]; 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 diff --git a/scripts/completion/ccs.fish b/scripts/completion/ccs.fish index dd68ea41..483c61d5 100644 --- a/scripts/completion/ccs.fish +++ b/scripts/completion/ccs.fish @@ -78,22 +78,23 @@ 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 doctor sync update' -a 'auth' -d '[cmd] Manage multiple Claude accounts' -complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'doctor' -d '[cmd] Run health check and diagnostics' -complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'sync' -d '[cmd] Sync delegation commands and skills' -complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'update' -d '[cmd] Update CCS to latest version' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -a 'auth' -d '[cmd] Manage multiple Claude accounts' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -a 'profile' -d '[cmd] Manage API profiles (create/remove)' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -a 'doctor' -d '[cmd] Run health check and diagnostics' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -a 'sync' -d '[cmd] Sync delegation commands and skills' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -a 'update' -d '[cmd] Update CCS to latest version' # Model profiles - grouped with [model] prefix for visual distinction -complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'default' -d '[model] Default Claude Sonnet 4.5' -complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'glm' -d '[model] GLM-4.6 (cost-optimized)' -complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'glmt' -d '[model] GLM-4.6 with thinking mode' -complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'kimi' -d '[model] Kimi for Coding (long-context)' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -a 'default' -d '[model] Default Claude Sonnet 4.5' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -a 'glm' -d '[model] GLM-4.6 (cost-optimized)' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -a 'glmt' -d '[model] GLM-4.6 with thinking mode' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -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 doctor sync update' -a '(__fish_ccs_get_custom_settings_profiles)' -d '[model] Settings-based profile' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -a '(__fish_ccs_get_custom_settings_profiles)' -d '[model] Settings-based profile' # Account profiles - dynamic with [account] prefix -complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a '(__fish_ccs_get_account_profiles)' -d '[account] Account-based profile' +complete -c ccs -n 'not __fish_seen_subcommand_from auth profile doctor sync update' -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' @@ -132,3 +133,35 @@ complete -c ccs -n '__fish_ccs_using_auth_subcommand default' -a '(__fish_ccs_ge # doctor command flags complete -c ccs -n '__fish_seen_subcommand_from doctor' -s h -l help -d 'Show help for doctor command' + +# 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 + +# profile subcommands +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' + +# profile command flags +complete -c ccs -n '__fish_ccs_using_profile' -s h -l help -d 'Show help for profile commands' + +# profile create flags +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' + +# profile remove - profile names and flags +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' diff --git a/scripts/completion/ccs.ps1 b/scripts/completion/ccs.ps1 index 9c5fada5..5658610c 100644 --- a/scripts/completion/ccs.ps1 +++ b/scripts/completion/ccs.ps1 @@ -12,8 +12,10 @@ Register-ArgumentCompleter -CommandName ccs -ScriptBlock { param($commandName, $wordToComplete, $commandAst, $fakeBoundParameters) - $commands = @('auth', 'doctor', 'sync', 'update', '--help', '--version', '--shell-completion', '-h', '-v', '-sc') + $commands = @('auth', 'profile', 'doctor', 'sync', 'update', '--help', '--version', '--shell-completion', '-h', '-v', '-sc') $authCommands = @('create', 'list', 'show', 'remove', 'default', '--help', '-h') + $profileCommands = @('create', 'list', 'remove', '--help', '-h') + $profileCreateFlags = @('--base-url', '--api-key', '--model', '--force', '--yes', '-y') $shellCompletionFlags = @('--bash', '--zsh', '--fish', '--powershell') $listFlags = @('--verbose', '--json') $removeFlags = @('--yes', '-y') @@ -170,4 +172,58 @@ Register-ArgumentCompleter -CommandName ccs -ScriptBlock { } } } + + # profile subcommand completion + if ($words[1] -eq 'profile') { + if ($position -eq 3) { + # profile subcommands + $profileCommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new( + $_, + $_, + 'ParameterValue', + $_ + ) + } + } elseif ($position -eq 4) { + # Profile names or flags for profile subcommands + switch ($words[2]) { + 'remove' { + $options = (Get-CcsProfiles -Type settings) + $removeFlags + $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new( + $_, + $_, + 'ParameterValue', + $_ + ) + } + } + 'create' { + $profileCreateFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new( + $_, + $_, + 'ParameterValue', + $_ + ) + } + } + } + } elseif ($position -eq 5) { + # Flags after profile name + switch ($words[2]) { + 'remove' { + $removeFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new( + $_, + $_, + 'ParameterValue', + $_ + ) + } + } + } + } + } } diff --git a/scripts/completion/ccs.zsh b/scripts/completion/ccs.zsh index 501ee4c7..9b8f41bf 100644 --- a/scripts/completion/ccs.zsh +++ b/scripts/completion/ccs.zsh @@ -16,7 +16,7 @@ # Color codes: 0;34=blue, 0;32=green, 0;33=yellow, 2;37=dim white # Pattern format: =(#b)(group1)(group2)==color_for_group1=color_for_group2 # The leading '=' means no color for whole match, then each '=' assigns to each group -zstyle ':completion:*:*:ccs:*:commands' list-colors '=(#b)(auth|doctor|sync|update)([[:space:]]#--[[:space:]]#*)==0\;34=2\;37' +zstyle ':completion:*:*:ccs:*:commands' list-colors '=(#b)(auth|profile|doctor|sync|update)([[:space:]]#--[[:space:]]#*)==0\;34=2\;37' zstyle ':completion:*:*:ccs:*:model-profiles' list-colors '=(#b)(default|glm|glmt|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 '' @@ -33,6 +33,7 @@ _ccs() { # Define top-level commands (padded for alignment) commands=( 'auth:Manage multiple Claude accounts' + 'profile:Manage API profiles (create/remove)' 'doctor:Run health check and diagnostics' 'sync:Sync delegation commands and skills' 'update:Update CCS to latest version' @@ -90,6 +91,9 @@ _ccs() { auth) _ccs_auth ;; + profile) + _ccs_profile + ;; doctor) _arguments \ '(- *)'{-h,--help}'[Show help for doctor command]' @@ -110,6 +114,58 @@ _ccs() { esac } +_ccs_profile() { + local curcontext="$curcontext" state line + typeset -A opt_args + + local -a profile_commands settings_profiles + + # Define profile subcommands + profile_commands=( + 'create:Create new API profile (interactive)' + 'list:List all profiles' + 'remove:Remove a profile' + ) + + # Load settings profiles for remove command + if [[ -f ~/.ccs/config.json ]]; then + settings_profiles=(${(f)"$(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null)"}) + fi + + _arguments -C \ + '(- *)'{-h,--help}'[Show help for profile commands]' \ + '1: :->subcommand' \ + '*:: :->subargs' + + case $state in + subcommand) + _describe -t profile-commands 'profile commands' profile_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) + # No arguments + ;; + remove|delete|rm) + _arguments \ + '1:profile:($settings_profiles)' \ + {--yes,-y}'[Skip confirmation]' + ;; + esac + ;; + esac +} + _ccs_auth() { local curcontext="$curcontext" state line typeset -A opt_args diff --git a/src/commands/shell-completion-command.ts b/src/commands/shell-completion-command.ts index b8b8117a..804c7f7f 100644 --- a/src/commands/shell-completion-command.ts +++ b/src/commands/shell-completion-command.ts @@ -17,6 +17,7 @@ export async function handleShellCompletionCommand(args: string[]): Promise