From e54d40165689be0f297342fa3d3994386fb8129b Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Fri, 28 Nov 2025 20:43:11 -0500 Subject: [PATCH] feat(cliproxy): refactor config loading and enhance base settings support - Introduce `BaseConfigLoader` for centralized configuration management - Add `base-agy.settings.json` configuration - Refactor `ConfigGenerator` to utilize new loading patterns - Update help command with improved documentation - Clean up lint violations file - Enhance postinstall script --- config/base-agy.settings.json | 10 +++ config/base-gemini.settings.json | 2 +- lint-violations.txt | 1 - scripts/postinstall.js | 13 +++- src/auth/profile-detector.ts | 4 +- src/ccs.ts | 2 +- src/cliproxy/auth-handler.ts | 16 ++-- src/cliproxy/base-config-loader.ts | 121 +++++++++++++++++++++++++++++ src/cliproxy/cliproxy-executor.ts | 2 +- src/cliproxy/config-generator.ts | 72 +++++++---------- src/cliproxy/index.ts | 9 ++- src/cliproxy/types.ts | 5 +- src/commands/help-command.ts | 101 +++++++++++++----------- src/management/doctor.ts | 2 +- src/types/utils.ts | 10 ++- src/utils/helpers.ts | 3 +- 16 files changed, 262 insertions(+), 111 deletions(-) create mode 100644 config/base-agy.settings.json delete mode 100644 lint-violations.txt create mode 100644 src/cliproxy/base-config-loader.ts diff --git a/config/base-agy.settings.json b/config/base-agy.settings.json new file mode 100644 index 00000000..b84cf134 --- /dev/null +++ b/config/base-agy.settings.json @@ -0,0 +1,10 @@ +{ + "env": { + "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/agy", + "ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed", + "ANTHROPIC_MODEL": "gemini-3-pro-preview", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "gemini-3-pro-preview", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "gemini-3-pro-preview", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gemini-2.5-flash" + } +} diff --git a/config/base-gemini.settings.json b/config/base-gemini.settings.json index d5a1000f..4881e8d3 100644 --- a/config/base-gemini.settings.json +++ b/config/base-gemini.settings.json @@ -3,7 +3,7 @@ "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/gemini", "ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed", "ANTHROPIC_MODEL": "gemini-2.5-pro", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "gemini-3-pro-preview", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "gemini-2.5-pro", "ANTHROPIC_DEFAULT_SONNET_MODEL": "gemini-2.5-pro", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gemini-2.5-flash" } diff --git a/lint-violations.txt b/lint-violations.txt deleted file mode 100644 index 2b7d6e05..00000000 --- a/lint-violations.txt +++ /dev/null @@ -1 +0,0 @@ -$ eslint src/ diff --git a/scripts/postinstall.js b/scripts/postinstall.js index e190c038..a2466ff9 100755 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -121,6 +121,8 @@ function createConfigFiles() { // This gives users control over when to modify their Claude configuration // Create config.json if missing + // NOTE: gemini/codex profiles NOT included - they are added on-demand when user + // runs `ccs gemini` or `ccs codex` for first time (requires OAuth auth first) const configPath = path.join(ccsDir, 'config.json'); if (!fs.existsSync(configPath)) { const config = { @@ -145,12 +147,17 @@ function createConfigFiles() { if (!config.profiles) { config.profiles = {}; } + let configUpdated = false; if (!config.profiles.glmt) { config.profiles.glmt = '~/.ccs/glmt.settings.json'; + configUpdated = true; + } + // NOTE: gemini/codex profiles added on-demand, not during migration + if (configUpdated) { const tmpPath = `${configPath}.tmp`; fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf8'); fs.renameSync(tmpPath, configPath); - console.log('[OK] Updated config with GLMT profile'); + console.log('[OK] Updated config with glmt profile'); } else { console.log('[OK] Config exists: ~/.ccs/config.json (preserved)'); } @@ -299,6 +306,10 @@ function createConfigFiles() { console.log('[OK] Kimi profile exists: ~/.ccs/kimi.settings.json (preserved)'); } + // NOTE: gemini.settings.json and codex.settings.json are NOT created during install + // They are created on-demand when user runs `ccs gemini` or `ccs codex` for the first time + // This prevents confusion - users need to run `--auth` first anyway + // Migrate existing Kimi configs to remove deprecated model fields (v4.1.2) // Kimi API changed - model fields now cause 401 errors if (fs.existsSync(kimiSettingsPath)) { diff --git a/src/auth/profile-detector.ts b/src/auth/profile-detector.ts index 100b94a7..dee4fff3 100644 --- a/src/auth/profile-detector.ts +++ b/src/auth/profile-detector.ts @@ -14,7 +14,7 @@ import { Config, Settings, ProfileMetadata } from '../types'; export type ProfileType = 'settings' | 'account' | 'cliproxy' | 'default'; /** CLIProxy profile names (OAuth-based, zero config) */ -export const CLIPROXY_PROFILES = ['gemini', 'codex', 'qwen'] as const; +export const CLIPROXY_PROFILES = ['gemini', 'codex', 'agy'] as const; export type CLIProxyProfileName = (typeof CLIPROXY_PROFILES)[number]; export interface ProfileDetectionResult { @@ -92,7 +92,7 @@ class ProfileDetector { return this.resolveDefaultProfile(); } - // Priority 0: Check CLIProxy profiles (gemini, chatgpt, qwen) - OAuth-based, zero config + // Priority 0: Check CLIProxy profiles (gemini, codex, agy) - OAuth-based, zero config if (CLIPROXY_PROFILES.includes(profileName as CLIProxyProfileName)) { return { type: 'cliproxy', diff --git a/src/ccs.ts b/src/ccs.ts index a92ac2f1..95b0f610 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -295,7 +295,7 @@ async function main(): Promise { const profileInfo = detector.detectProfileType(profile); if (profileInfo.type === 'cliproxy') { - // CLIPROXY FLOW: OAuth-based profiles (gemini, chatgpt, qwen) + // CLIPROXY FLOW: OAuth-based profiles (gemini, codex, agy) await execClaudeWithCLIProxy(claudeCli, profileInfo.name as CLIProxyProvider, remainingArgs); } else if (profileInfo.type === 'settings') { // Check if this is GLMT profile (requires proxy) diff --git a/src/cliproxy/auth-handler.ts b/src/cliproxy/auth-handler.ts index ba364599..9a13a18f 100644 --- a/src/cliproxy/auth-handler.ts +++ b/src/cliproxy/auth-handler.ts @@ -1,7 +1,7 @@ /** * Auth Handler for CLIProxyAPI * - * Manages OAuth authentication for CLIProxy providers (Gemini, Codex, Qwen). + * Manages OAuth authentication for CLIProxy providers (Gemini, Codex, Antigravity). * CLIProxyAPI handles OAuth internally - we just need to: * 1. Check if auth exists (token files in CCS auth directory) * 2. Trigger OAuth flow by spawning binary with auth flag @@ -93,12 +93,12 @@ const OAUTH_CONFIGS: Record = { scopes: ['openid', 'profile'], authFlag: '-codex-login', }, - qwen: { - provider: 'qwen', - displayName: 'Alibaba Qwen', - authUrl: 'https://auth.aliyun.com/oauth2/authorize', - scopes: ['dashscope'], - authFlag: '-qwen-login', + agy: { + provider: 'agy', + displayName: 'Antigravity', + authUrl: 'https://antigravity.ai/oauth/authorize', + scopes: ['api'], + authFlag: '-antigravity-login', }, }; @@ -181,7 +181,7 @@ export function getAuthStatus(provider: CLIProxyProvider): AuthStatus { * Get auth status for all providers */ export function getAllAuthStatus(): AuthStatus[] { - const providers: CLIProxyProvider[] = ['gemini', 'codex', 'qwen']; + const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy']; return providers.map(getAuthStatus); } diff --git a/src/cliproxy/base-config-loader.ts b/src/cliproxy/base-config-loader.ts new file mode 100644 index 00000000..09de1603 --- /dev/null +++ b/src/cliproxy/base-config-loader.ts @@ -0,0 +1,121 @@ +/** + * Base Config Loader for CLIProxy Providers + * + * Loads provider configurations from config/base-{provider}.settings.json files. + * This allows model mappings to be easily updated without digging into code. + * + * Config files are bundled with the npm package and read at runtime. + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import { CLIProxyProvider, ProviderModelMapping } from './types'; + +/** Base settings file structure */ +interface BaseSettings { + env: { + ANTHROPIC_BASE_URL: string; + ANTHROPIC_AUTH_TOKEN: string; + ANTHROPIC_MODEL: string; + ANTHROPIC_DEFAULT_OPUS_MODEL: string; + ANTHROPIC_DEFAULT_SONNET_MODEL: string; + ANTHROPIC_DEFAULT_HAIKU_MODEL: string; + }; +} + +/** Cached configs to avoid repeated file reads */ +const configCache: Map = new Map(); + +/** + * Get path to base config file for provider + * Config files are in the config/ directory relative to package root + */ +function getBaseConfigPath(provider: CLIProxyProvider): string { + // __dirname points to dist/cliproxy at runtime + // Config files are at package root: ../config/ + const configDir = path.join(__dirname, '..', '..', 'config'); + return path.join(configDir, `base-${provider}.settings.json`); +} + +/** + * Load base config for a provider + * Returns parsed settings from config/base-{provider}.settings.json + */ +export function loadBaseConfig(provider: CLIProxyProvider): BaseSettings { + // Check cache first + const cached = configCache.get(provider); + if (cached) { + return cached; + } + + const configPath = getBaseConfigPath(provider); + + if (!fs.existsSync(configPath)) { + throw new Error( + `Base config not found for provider '${provider}': ${configPath}\n` + + `Expected file: config/base-${provider}.settings.json` + ); + } + + try { + const content = fs.readFileSync(configPath, 'utf-8'); + const settings: BaseSettings = JSON.parse(content); + + // Validate required fields + if (!settings.env || typeof settings.env !== 'object') { + throw new Error('Missing or invalid "env" object'); + } + + const required = [ + 'ANTHROPIC_MODEL', + 'ANTHROPIC_DEFAULT_OPUS_MODEL', + 'ANTHROPIC_DEFAULT_SONNET_MODEL', + 'ANTHROPIC_DEFAULT_HAIKU_MODEL', + ]; + + for (const field of required) { + if (!settings.env[field as keyof BaseSettings['env']]) { + throw new Error(`Missing required field: env.${field}`); + } + } + + // Cache and return + configCache.set(provider, settings); + return settings; + } catch (error) { + const err = error as Error; + throw new Error(`Failed to load base config for '${provider}': ${err.message}`); + } +} + +/** + * Get model mapping from base config + * Extracts model names from env vars + */ +export function getModelMappingFromConfig(provider: CLIProxyProvider): ProviderModelMapping { + const config = loadBaseConfig(provider); + + return { + defaultModel: config.env.ANTHROPIC_MODEL, + claudeModel: config.env.ANTHROPIC_MODEL, + opusModel: config.env.ANTHROPIC_DEFAULT_OPUS_MODEL, + sonnetModel: config.env.ANTHROPIC_DEFAULT_SONNET_MODEL, + haikuModel: config.env.ANTHROPIC_DEFAULT_HAIKU_MODEL, + }; +} + +/** + * Get full env vars from base config + * Returns the complete env object for Claude CLI + */ +export function getEnvVarsFromConfig(provider: CLIProxyProvider): NodeJS.ProcessEnv { + const config = loadBaseConfig(provider); + return config.env as unknown as NodeJS.ProcessEnv; +} + +/** + * Clear config cache (useful for testing) + */ +export function clearConfigCache(): void { + configCache.clear(); +} diff --git a/src/cliproxy/cliproxy-executor.ts b/src/cliproxy/cliproxy-executor.ts index 2f77ba4d..986df860 100644 --- a/src/cliproxy/cliproxy-executor.ts +++ b/src/cliproxy/cliproxy-executor.ts @@ -80,7 +80,7 @@ async function waitForProxyReady( * Execute Claude CLI with CLIProxy (main entry point) * * @param claudeCli Path to Claude CLI executable - * @param provider CLIProxy provider (gemini, chatgpt, qwen) + * @param provider CLIProxy provider (gemini, codex, agy) * @param args Arguments to pass to Claude CLI * @param config Optional executor configuration */ diff --git a/src/cliproxy/config-generator.ts b/src/cliproxy/config-generator.ts index d7a31d27..6f39b34e 100644 --- a/src/cliproxy/config-generator.ts +++ b/src/cliproxy/config-generator.ts @@ -3,12 +3,16 @@ * * Generates config.yaml for CLIProxyAPI based on provider. * Handles OAuth token paths and provider-specific settings. + * + * Model mappings are loaded from config/base-{provider}.settings.json files + * to allow easy updates without code changes. */ import * as fs from 'fs'; import * as path from 'path'; import { getCcsDir } from '../utils/config-manager'; import { CLIProxyProvider, ProviderConfig, ProviderModelMapping } from './types'; +import { getModelMappingFromConfig } from './base-config-loader'; /** Settings file structure for user overrides */ interface ProviderSettings { @@ -21,61 +25,37 @@ export const CLIPROXY_DEFAULT_PORT = 8317; /** Internal API key for CCS-managed requests */ const CCS_INTERNAL_API_KEY = 'ccs-internal-managed'; -/** - * Provider configurations with model mappings - */ -export const PROVIDER_CONFIGS: Record = { - gemini: { - name: 'gemini', - displayName: 'Gemini', - models: { - defaultModel: 'gemini-2.5-pro', - claudeModel: 'gemini-2.5-pro', - opusModel: 'gemini-3-pro-preview', - sonnetModel: 'gemini-2.5-pro', - haikuModel: 'gemini-2.5-flash', - }, - requiresOAuth: true, - }, - codex: { - name: 'codex', - displayName: 'Codex', - models: { - defaultModel: 'gpt-5.1-codex-max', - claudeModel: 'gpt-5.1-codex-max', - opusModel: 'gpt-5.1-codex-max-high', - sonnetModel: 'gpt-5.1-codex-max', - haikuModel: 'gpt-5.1-codex-mini-high', - }, - requiresOAuth: true, - }, - qwen: { - name: 'qwen', - displayName: 'Qwen', - models: { - defaultModel: 'qwen-max', - claudeModel: 'qwen-max', - opusModel: 'qwen-max', - sonnetModel: 'qwen-plus', - haikuModel: 'qwen-turbo', - }, - requiresOAuth: true, - }, +/** Provider display names (static metadata) */ +const PROVIDER_DISPLAY_NAMES: Record = { + gemini: 'Gemini', + codex: 'Codex', + agy: 'Antigravity', }; /** * Get provider configuration + * Model mappings are loaded from config/base-{provider}.settings.json */ export function getProviderConfig(provider: CLIProxyProvider): ProviderConfig { - const config = PROVIDER_CONFIGS[provider]; - if (!config) { + const displayName = PROVIDER_DISPLAY_NAMES[provider]; + if (!displayName) { throw new Error(`Unknown provider: ${provider}`); } - return config; + + // Load models from base config file + const models = getModelMappingFromConfig(provider); + + return { + name: provider, + displayName, + models, + requiresOAuth: true, // All CLIProxy providers require OAuth + }; } /** * Get model mapping for provider + * Loads from config/base-{provider}.settings.json */ export function getModelMapping(provider: CLIProxyProvider): ProviderModelMapping { return getProviderConfig(provider).models; @@ -124,7 +104,7 @@ export function getBinDir(): string { /** * Generate UNIFIED config.yaml content for ALL providers - * This enables concurrent usage of gemini/codex/qwen without config conflicts. + * This enables concurrent usage of gemini/codex/agy without config conflicts. * CLIProxyAPI routes requests by model name to the appropriate provider. */ function generateUnifiedConfigContent(port: number = CLIPROXY_DEFAULT_PORT): string { @@ -132,7 +112,7 @@ function generateUnifiedConfigContent(port: number = CLIPROXY_DEFAULT_PORT): str // Unified config with all providers const config = `# CLIProxyAPI unified config generated by CCS -# Supports: gemini, codex, qwen (concurrent usage) +# Supports: gemini, codex, agy (concurrent usage) # Generated: ${new Date().toISOString()} port: ${port} @@ -144,7 +124,7 @@ usage-statistics-enabled: false api-keys: - "${CCS_INTERNAL_API_KEY}" -# OAuth tokens stored in subdirectories (gemini/, codex/, qwen/) +# OAuth tokens stored in auth/ directory # CLIProxyAPI auto-discovers auth files in subdirectories auth-dir: "${authDir}" diff --git a/src/cliproxy/index.ts b/src/cliproxy/index.ts index 339d3988..8673067d 100644 --- a/src/cliproxy/index.ts +++ b/src/cliproxy/index.ts @@ -58,10 +58,17 @@ export { getBinDir, configExists, deleteConfig, - PROVIDER_CONFIGS, CLIPROXY_DEFAULT_PORT, } from './config-generator'; +// Base config loader (for reading config/base-*.settings.json) +export { + loadBaseConfig, + getModelMappingFromConfig, + getEnvVarsFromConfig, + clearConfigCache, +} from './base-config-loader'; + // Executor export { execClaudeWithCLIProxy, isPortAvailable, findAvailablePort } from './cliproxy-executor'; diff --git a/src/cliproxy/types.ts b/src/cliproxy/types.ts index cf7ccc87..926ade98 100644 --- a/src/cliproxy/types.ts +++ b/src/cliproxy/types.ts @@ -107,8 +107,11 @@ export interface DownloadResult { /** * Supported CLIProxy providers + * - gemini: Google Gemini via OAuth + * - codex: OpenAI Codex via OAuth + * - agy: Antigravity via OAuth (short name for easy usage) */ -export type CLIProxyProvider = 'gemini' | 'codex' | 'qwen'; +export type CLIProxyProvider = 'gemini' | 'codex' | 'agy'; /** * CLIProxy config.yaml structure (minimal) diff --git a/src/commands/help-command.ts b/src/commands/help-command.ts index b514c032..ec6aa9f5 100644 --- a/src/commands/help-command.ts +++ b/src/commands/help-command.ts @@ -4,9 +4,7 @@ import { colored } from '../utils/helpers'; * Display comprehensive help information for CCS (Claude Code Switch) */ export function handleHelpCommand(): void { - console.log( - colored('CCS (Claude Code Switch) - Instant profile switching for Claude CLI', 'bold') - ); + console.log(colored('CCS (Claude Code Switch) - Profile switching for Claude CLI', 'bold')); console.log(''); console.log(colored('Usage:', 'cyan')); @@ -14,43 +12,61 @@ export function handleHelpCommand(): void { console.log(` ${colored('ccs', 'yellow')} [flags]`); console.log(''); - console.log(colored('Description:', 'cyan')); - console.log(' Switch between multiple Claude accounts and alternative models'); - console.log(' (GLM, Kimi) instantly. Run different Claude CLI sessions concurrently'); - console.log(' with auto-recovery. Zero downtime.'); + // ═══════════════════════════════════════════════════════════════════════════ + // SECTION 1: API KEY MODELS + // ═══════════════════════════════════════════════════════════════════════════ + console.log(colored('═══ API Key Models ═══', 'cyanBold')); + console.log(' Configure API keys in ~/.ccs/*.settings.json'); console.log(''); - - console.log(colored('Requirements:', 'cyan')); - console.log(' Node.js 14+ (detected automatically by bootstrap)'); - console.log(' npm 5.2+ (for npx, comes with Node.js 8.2+)'); - console.log(''); - - console.log(colored('Model Switching:', 'cyan')); console.log(` ${colored('ccs', 'yellow')} Use default Claude account`); - console.log( - ` ${colored('ccs codex', 'yellow')} Codex via OAuth (zero config)` - ); - console.log( - ` ${colored('ccs gemini', 'yellow')} Gemini via OAuth (zero config)` - ); console.log(` ${colored('ccs glm', 'yellow')} GLM 4.6 (API key required)`); console.log(` ${colored('ccs glmt', 'yellow')} GLM with thinking mode`); console.log(` ${colored('ccs kimi', 'yellow')} Kimi for Coding (API key)`); - console.log( - ` ${colored('ccs qwen', 'yellow')} Qwen via OAuth (zero config)` - ); - console.log(` ${colored('ccs gemini', 'yellow')} "explain this" Use Gemini with prompt`); console.log(''); - console.log(colored('Account Management:', 'cyan')); + // ═══════════════════════════════════════════════════════════════════════════ + // SECTION 2: ACCOUNT MANAGEMENT + // ═══════════════════════════════════════════════════════════════════════════ + console.log(colored('═══ Account Management ═══', 'cyanBold')); + console.log(' Run multiple Claude accounts concurrently'); + console.log(''); console.log( - ` ${colored('ccs auth --help', 'yellow')} Run multiple Claude accounts concurrently` + ` ${colored('ccs auth --help', 'yellow')} Show account management commands` ); + console.log(` ${colored('ccs auth create ', 'yellow')} Create new account profile`); + console.log(` ${colored('ccs auth list', 'yellow')} List all account profiles`); console.log(''); + // ═══════════════════════════════════════════════════════════════════════════ + // SECTION 3: CLI PROXY (OAUTH PROVIDERS) + // ═══════════════════════════════════════════════════════════════════════════ + console.log(colored('═══ CLI Proxy (OAuth Providers) ═══', 'cyanBold')); + console.log(' Zero-config OAuth authentication via CLIProxyAPI'); + console.log(' First run: Browser opens for authentication'); + console.log(' Settings: ~/.ccs/{provider}.settings.json (created after auth)'); + console.log(''); + console.log( + ` ${colored('ccs gemini', 'yellow')} Google Gemini (gemini-2.5-pro)` + ); + console.log( + ` ${colored('ccs codex', 'yellow')} OpenAI Codex (gpt-5.1-codex-max)` + ); + console.log( + ` ${colored('ccs agy', 'yellow')} Antigravity (gemini-3-pro-preview)` + ); + console.log(''); + console.log(` ${colored('ccs gemini --auth', 'yellow')} Authenticate only`); + console.log(` ${colored('ccs gemini --logout', 'yellow')} Clear authentication`); + console.log(` ${colored('ccs gemini --headless', 'yellow')} Headless auth (for SSH)`); + console.log(` ${colored('ccs gemini "explain code"', 'yellow')} Use with prompt`); + console.log(''); + + // ═══════════════════════════════════════════════════════════════════════════ + // DELEGATION + // ═══════════════════════════════════════════════════════════════════════════ console.log(colored('Delegation (inside Claude Code CLI):', 'cyan')); console.log( - ` ${colored('/ccs "task"', 'yellow')} Delegate task (auto-selects best profile)` + ` ${colored('/ccs "task"', 'yellow')} Delegate task (auto-selects profile)` ); console.log( ` ${colored('/ccs --glm "task"', 'yellow')} Force GLM-4.6 for simple tasks` @@ -59,7 +75,6 @@ export function handleHelpCommand(): void { console.log( ` ${colored('/ccs:continue "follow-up"', 'yellow')} Continue last delegation session` ); - console.log(' Save tokens by delegating simple tasks to cost-optimized models'); console.log(''); console.log(colored('Diagnostics:', 'cyan')); @@ -87,38 +102,34 @@ export function handleHelpCommand(): void { console.log(' Profiles: ~/.ccs/profiles.json'); console.log(' Instances: ~/.ccs/instances/'); console.log(' Settings: ~/.ccs/*.settings.json'); - console.log(' Environment: CCS_CONFIG (override config path)'); + console.log(''); + + console.log(colored('CLI Proxy:', 'cyan')); + console.log(' Binary: ~/.ccs/cliproxy/bin/cli-proxy-api'); + console.log(' Config: ~/.ccs/cliproxy/config.yaml'); + console.log(' Auth: ~/.ccs/cliproxy/auth/'); + console.log(' Port: 8317 (default)'); console.log(''); console.log(colored('Shared Data:', 'cyan')); console.log(' Commands: ~/.ccs/shared/commands/'); console.log(' Skills: ~/.ccs/shared/skills/'); console.log(' Agents: ~/.ccs/shared/agents/'); - console.log(' Plugins: ~/.ccs/shared/plugins/'); - console.log(' Note: Commands, skills, agents, and plugins are symlinked across all profiles'); + console.log(' Note: Symlinked across all profiles'); console.log(''); console.log(colored('Examples:', 'cyan')); console.log(` ${colored('$ ccs', 'yellow')} # Use default account`); - console.log(` ${colored('$ ccs gemini "explain code"', 'yellow')} # Zero-config OAuth`); - console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # Cost-optimized model`); - console.log(''); console.log( - ` For more: ${colored('https://github.com/kaitranntt/ccs/blob/main/README.md', 'cyan')}` + ` ${colored('$ ccs gemini', 'yellow')} # OAuth (browser opens first time)` ); + console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # API key model`); + console.log(''); + console.log(` Docs: ${colored('https://github.com/kaitranntt/ccs', 'cyan')}`); console.log(''); console.log(colored('Uninstall:', 'yellow')); - console.log(' npm (recommended): npm uninstall -g @kaitranntt/ccs'); - console.log(' Legacy (deprecated):'); - console.log(' macOS/Linux: curl -fsSL ccs.kaitran.ca/uninstall | bash'); - console.log(' Windows: irm ccs.kaitran.ca/uninstall | iex'); - console.log(''); - - console.log(colored('Documentation:', 'cyan')); - console.log(` GitHub: ${colored('https://github.com/kaitranntt/ccs', 'cyan')}`); - console.log(' Docs: https://github.com/kaitranntt/ccs/blob/main/README.md'); - console.log(' Issues: https://github.com/kaitranntt/ccs/issues'); + console.log(' npm uninstall -g @kaitranntt/ccs'); console.log(''); console.log(`${colored('License:', 'cyan')} MIT`); diff --git a/src/management/doctor.ts b/src/management/doctor.ts index 944f77fb..34d878b0 100644 --- a/src/management/doctor.ts +++ b/src/management/doctor.ts @@ -754,7 +754,7 @@ class Doctor { } /** - * Check 11: CLIProxy health (OAuth profiles: gemini, chatgpt, qwen) + * Check 11: CLIProxy health (OAuth profiles: gemini, codex, agy) */ private async checkCLIProxy(): Promise { // 1. Binary installed? diff --git a/src/types/utils.ts b/src/types/utils.ts index 8fdf68b4..8aa0f09f 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -18,7 +18,15 @@ export enum LogLevel { /** * Color codes (TTY-aware) */ -export type ColorName = 'red' | 'green' | 'yellow' | 'blue' | 'cyan' | 'bold' | 'reset'; +export type ColorName = + | 'red' + | 'green' + | 'yellow' + | 'blue' + | 'cyan' + | 'bold' + | 'cyanBold' + | 'reset'; /** * Terminal capabilities diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 6c4c6858..2a4179da 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -20,11 +20,12 @@ function getColors(): Record { green: '\x1b[0;32m', blue: '\x1b[0;34m', bold: '\x1b[1m', + cyanBold: '\x1b[1;36m', reset: '\x1b[0m', }; } - return { red: '', yellow: '', cyan: '', green: '', blue: '', bold: '', reset: '' }; + return { red: '', yellow: '', cyan: '', green: '', blue: '', bold: '', cyanBold: '', reset: '' }; } // Colors object (dynamic)