diff --git a/scripts/completion/ccs.zsh b/scripts/completion/ccs.zsh index 0ab058f4..225bf2b4 100644 --- a/scripts/completion/ccs.zsh +++ b/scripts/completion/ccs.zsh @@ -130,7 +130,7 @@ _ccs() { '--format[Output format]:format:(openai anthropic raw)' \ '--shell[Shell syntax]:shell:(bash fish powershell)' \ '(- *)'{-h,--help}'[Show help]' \ - '1:profile:($proxy_profiles)' + '1:profile:($proxy_profiles ${(k)settings_profiles_described})' ;; gemini|codex|agy|qwen) _arguments \ diff --git a/src/commands/env-command.ts b/src/commands/env-command.ts index d5e5e50b..7019e0e1 100644 --- a/src/commands/env-command.ts +++ b/src/commands/env-command.ts @@ -12,6 +12,7 @@ import { getEffectiveEnvVars } from '../cliproxy/config/env-builder'; import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager'; import { isUnifiedMode, loadUnifiedConfig } from '../config/unified-config-loader'; import { expandPath } from '../utils/helpers'; +import { getCcsDir } from '../utils/config-manager'; type ShellType = 'bash' | 'fish' | 'powershell'; type OutputFormat = 'openai' | 'anthropic' | 'raw'; @@ -46,7 +47,9 @@ export function formatExportLine(shell: ShellType, key: string, value: string): } } -/** Map Anthropic env vars to OpenAI-compatible format */ +/** Map Anthropic env vars to OpenAI-compatible format. + * ANTHROPIC_MODEL is intentionally omitted — the proxy endpoint handles + * model routing, so OPENAI_MODEL is unnecessary for OpenAI-compatible tools. */ export function transformToOpenAI(envVars: Record): Record { const baseUrl = envVars['ANTHROPIC_BASE_URL'] || ''; const apiKey = envVars['ANTHROPIC_AUTH_TOKEN'] || ''; @@ -58,7 +61,7 @@ export function transformToOpenAI(envVars: Record): Record a.startsWith(`--${flag}=`)); if (eqMatch) return eqMatch.split('=').slice(1).join('='); @@ -190,7 +193,7 @@ export async function handleEnvCommand(args: string[]): Promise { if (!resolved) { console.error(fail(`Profile '${profile}' not found.`)); console.error(dim(' Available CLIProxy profiles: ' + CLIPROXY_PROFILES.join(', '))); - console.error(dim(' Check ~/.ccs/config.yaml for custom profiles.')); + console.error(dim(` Check ${getCcsDir()}/config.yaml for custom profiles.`)); process.exit(1); } envVars = resolved; diff --git a/tests/unit/commands/env-command.test.ts b/tests/unit/commands/env-command.test.ts index efcae254..1bf01e5c 100644 --- a/tests/unit/commands/env-command.test.ts +++ b/tests/unit/commands/env-command.test.ts @@ -4,7 +4,12 @@ * Tests pure utility functions: detectShell, formatExportLine, transformToOpenAI */ import { describe, it, expect, afterEach } from 'bun:test'; -import { detectShell, formatExportLine, transformToOpenAI } from '../../../src/commands/env-command'; +import { + detectShell, + formatExportLine, + transformToOpenAI, + parseFlag, +} from '../../../src/commands/env-command'; describe('env-command', () => { describe('detectShell', () => { @@ -91,6 +96,16 @@ describe('env-command', () => { "export TOKEN='safe$(whoami)'" ); }); + + it('escapes single quotes in fish values', () => { + expect(formatExportLine('fish', 'VAL', "it's here")).toBe("set -gx VAL 'it\\'s here'"); + }); + + it('escapes single quotes in powershell values', () => { + expect(formatExportLine('powershell', 'VAL', "it's here")).toBe( + "$env:VAL = 'it''s here'" + ); + }); }); describe('transformToOpenAI', () => { @@ -131,4 +146,26 @@ describe('env-command', () => { expect(result['ANTHROPIC_MAX_TOKENS']).toBeUndefined(); }); }); + + describe('parseFlag', () => { + it('parses --flag=value style', () => { + expect(parseFlag(['--format=openai'], 'format')).toBe('openai'); + }); + + it('parses --flag value style', () => { + expect(parseFlag(['--format', 'openai'], 'format')).toBe('openai'); + }); + + it('handles values containing =', () => { + expect(parseFlag(['--format=key=val=ue'], 'format')).toBe('key=val=ue'); + }); + + it('returns undefined for missing flag', () => { + expect(parseFlag(['--shell', 'bash'], 'format')).toBeUndefined(); + }); + + it('does not consume next flag as value', () => { + expect(parseFlag(['--format', '--shell'], 'format')).toBeUndefined(); + }); + }); });