diff --git a/scripts/completion/ccs.bash b/scripts/completion/ccs.bash index d0d13d33..a9b1d392 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 api cliproxy doctor sync update" + local commands="auth api cliproxy doctor env sync update" local flags="--help --version --shell-completion -h -v -sc" local cliproxy_profiles="gemini codex agy qwen" local profiles="" @@ -151,6 +151,33 @@ _ccs_completion() { esac fi + # env subcommands + if [[ ${COMP_WORDS[1]} == "env" ]]; then + case "${prev}" in + env) + # Complete with profile names and flags + local env_opts="--format --shell --help -h $cliproxy_profiles" + if [[ -f ~/.ccs/config.json ]]; then + env_opts="$env_opts $(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null || true)" + fi + COMPREPLY=( $(compgen -W "${env_opts}" -- ${cur}) ) + return 0 + ;; + --format) + COMPREPLY=( $(compgen -W "openai anthropic raw" -- ${cur}) ) + return 0 + ;; + --shell) + COMPREPLY=( $(compgen -W "bash fish powershell" -- ${cur}) ) + return 0 + ;; + *) + COMPREPLY=( $(compgen -W "--format --shell --help -h" -- ${cur}) ) + return 0 + ;; + esac + fi + # Flags for doctor command if [[ ${COMP_WORDS[1]} == "doctor" ]]; then COMPREPLY=( $(compgen -W "--help -h" -- ${cur}) ) diff --git a/scripts/completion/ccs.fish b/scripts/completion/ccs.fish index cb058cc8..9593e736 100644 --- a/scripts/completion/ccs.fish +++ b/scripts/completion/ccs.fish @@ -121,33 +121,34 @@ 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 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 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 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 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 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 sync update gemini codex agy qwen' -a 'update' -d '[cmd] Update CCS to latest version' +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 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 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 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 sync update gemini codex agy qwen' -a 'qwen' -d '[proxy] Qwen Code (OAuth)' +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 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 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 sync update gemini codex agy qwen' -a 'glmt' -d '[model] GLM-4.6 with thinking mode' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor sync update gemini codex agy qwen' -a 'kimi' -d '[model] Kimi for Coding (long-context)' +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 'glmt' -d '[model] GLM-4.6 with thinking mode' +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 sync update gemini codex agy qwen' -a '(__fish_ccs_get_custom_settings_profiles)' -d '[model] Settings-based profile' +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 sync update gemini codex agy qwen' -a '(__fish_ccs_get_cliproxy_variants)' -d '[variant] CLIProxy variant' +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 sync update gemini codex agy qwen' -a '(__fish_ccs_get_account_profiles)' -d '[account] Account-based profile' +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' @@ -171,6 +172,13 @@ complete -c ccs -n '__fish_seen_subcommand_from update' -s h -l help -d 'Show he # 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' -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 'bash 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 # ============================================================================ diff --git a/scripts/completion/ccs.ps1 b/scripts/completion/ccs.ps1 index eb640cbf..9f5ac508 100644 --- a/scripts/completion/ccs.ps1 +++ b/scripts/completion/ccs.ps1 @@ -12,7 +12,7 @@ Register-ArgumentCompleter -CommandName ccs -ScriptBlock { param($commandName, $wordToComplete, $commandAst, $fakeBoundParameters) - $commands = @('auth', 'api', 'cliproxy', 'doctor', 'sync', 'update', '--help', '--version', '--shell-completion', '-h', '-v', '-sc') + $commands = @('auth', 'api', 'cliproxy', 'doctor', 'env', 'sync', 'update', '--help', '--version', '--shell-completion', '-h', '-v', '-sc') $cliproxyProfiles = @('gemini', 'codex', 'agy', 'qwen') $authCommands = @('create', 'list', 'show', 'remove', 'default', '--help', '-h') $apiCommands = @('create', 'list', 'remove', '--help', '-h') @@ -21,6 +21,9 @@ Register-ArgumentCompleter -CommandName ccs -ScriptBlock { $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 = @('bash', 'fish', 'powershell') $shellCompletionFlags = @('--bash', '--zsh', '--fish', '--powershell') $listFlags = @('--verbose', '--json') $removeFlags = @('--yes', '-y') @@ -130,6 +133,55 @@ Register-ArgumentCompleter -CommandName ccs -ScriptBlock { 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) { diff --git a/scripts/completion/ccs.zsh b/scripts/completion/ccs.zsh index cb0ef587..0ab058f4 100644 --- a/scripts/completion/ccs.zsh +++ b/scripts/completion/ccs.zsh @@ -13,7 +13,7 @@ # 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|sync|update)([[:space:]]#--[[:space:]]#*)==0\;34=2\;37' +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|glmt|kimi|[^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;32=2\;37' zstyle ':completion:*:*:ccs:*:account-profiles' list-colors '=(#b)([^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;33=2\;37' @@ -34,6 +34,7 @@ _ccs() { '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' ) @@ -124,6 +125,13 @@ _ccs() { _arguments \ '(- *)'{-h,--help}'[Show help for doctor command]' ;; + env) + _arguments \ + '--format[Output format]:format:(openai anthropic raw)' \ + '--shell[Shell syntax]:shell:(bash fish powershell)' \ + '(- *)'{-h,--help}'[Show help]' \ + '1:profile:($proxy_profiles)' + ;; gemini|codex|agy|qwen) _arguments \ '--auth[Authenticate only]' \ diff --git a/src/commands/env-command.ts b/src/commands/env-command.ts index a29754fa..d5e5e50b 100644 --- a/src/commands/env-command.ts +++ b/src/commands/env-command.ts @@ -18,6 +18,7 @@ type OutputFormat = 'openai' | 'anthropic' | 'raw'; const VALID_FORMATS: OutputFormat[] = ['openai', 'anthropic', 'raw']; const VALID_SHELLS: ShellType[] = ['bash', 'fish', 'powershell']; +const VALID_ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/; /** Auto-detect shell from environment */ export function detectShell(flag?: string): ShellType { @@ -231,7 +232,7 @@ export async function handleEnvCommand(args: string[]): Promise { // Output shell-formatted exports to stdout for (const [key, value] of Object.entries(output)) { - if (value) { + if (value && VALID_ENV_KEY.test(key)) { console.log(formatExportLine(shell, key, value)); } }