Skip to content

Commit 6427ecf

Browse files
committed
feat(cliproxy): add crud commands for variant profiles
- Add `ccs cliproxy create/list/remove` commands for variant management - Interactive wizard with provider/model selection from catalog - Settings file auto-generated with all 6 ANTHROPIC_* env fields - Fix `ccs api create` to include all 4 model fields (was missing 3) - Fix `--config` flag to save to correct variant settings file - Remove paid tier badge from AGY models (all free via Antigravity) - Add settings file format example to README for CLIProxy variants - Add 22 unit tests for cliproxy command validation and config handling
1 parent e103f21 commit 6427ecf

File tree

8 files changed

+926
-27
lines changed

8 files changed

+926
-27
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,24 @@ CCS resolves profiles in priority order:
411411

412412
Usage: `ccs flash "quick task"` or `ccs pro "complex analysis"`
413413

414+
Settings file format (`~/.ccs/gemini-flash.settings.json`):
415+
416+
```json
417+
{
418+
"env": {
419+
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/gemini",
420+
"ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed",
421+
"ANTHROPIC_MODEL": "gemini-2.5-flash",
422+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "gemini-2.5-flash",
423+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "gemini-2.5-flash",
424+
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "gemini-2.5-flash"
425+
}
426+
}
427+
```
428+
429+
> [!TIP]
430+
> Copy from `~/.ccs/gemini.settings.json` (auto-generated on first `ccs gemini` run) and modify `ANTHROPIC_MODEL` to your desired model.
431+
414432
**Settings-based**: GLM, GLMT, Kimi, default
415433
- Uses `--settings` flag pointing to config files
416434
- GLMT: Embedded proxy for thinking mode support

src/cliproxy/cliproxy-executor.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,9 @@ export async function execClaudeWithCLIProxy(
125125
const forceConfig = args.includes('--config');
126126

127127
// Handle --config: configure model selection and exit
128+
// Pass customSettingsPath for CLIProxy variants to save to correct file
128129
if (forceConfig && supportsModelConfig(provider)) {
129-
await configureProviderModel(provider, true);
130+
await configureProviderModel(provider, true, cfg.customSettingsPath);
130131
process.exit(0);
131132
}
132133

@@ -166,12 +167,13 @@ export async function execClaudeWithCLIProxy(
166167

167168
// 4. First-run model configuration (interactive)
168169
// For supported providers, prompt user to select model on first run
170+
// Pass customSettingsPath for CLIProxy variants
169171
if (supportsModelConfig(provider)) {
170-
await configureProviderModel(provider, false); // false = only if not configured
172+
await configureProviderModel(provider, false, cfg.customSettingsPath); // false = only if not configured
171173
}
172174

173175
// 5. Check for known broken models and warn user
174-
const currentModel = getCurrentModel(provider);
176+
const currentModel = getCurrentModel(provider, cfg.customSettingsPath);
175177
if (currentModel && isModelBroken(provider, currentModel)) {
176178
const modelEntry = findModel(provider, currentModel);
177179
const issueUrl = getModelIssueUrl(provider, currentModel);

src/cliproxy/model-catalog.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ export const MODEL_CATALOG: Partial<Record<CLIProxyProvider, ProviderCatalog>> =
6464
{
6565
id: 'gemini-3-pro-preview',
6666
name: 'Gemini 3 Pro',
67-
tier: 'paid',
68-
description: 'Google latest, requires paid Google account',
67+
description: 'Google latest model via Antigravity',
6968
},
7069
],
7170
},

src/cliproxy/model-config.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,16 @@ export function hasUserSettings(provider: CLIProxyProvider): boolean {
4040

4141
/**
4242
* Get current model from user settings
43+
* @param provider CLIProxy provider
44+
* @param customSettingsPath Optional custom settings path for CLIProxy variants
4345
*/
44-
export function getCurrentModel(provider: CLIProxyProvider): string | undefined {
45-
const settingsPath = getProviderSettingsPath(provider);
46+
export function getCurrentModel(
47+
provider: CLIProxyProvider,
48+
customSettingsPath?: string
49+
): string | undefined {
50+
const settingsPath = customSettingsPath
51+
? customSettingsPath.replace(/^~/, process.env.HOME || process.env.USERPROFILE || '')
52+
: getProviderSettingsPath(provider);
4653
if (!fs.existsSync(settingsPath)) return undefined;
4754

4855
try {
@@ -80,11 +87,13 @@ function formatModelDetailed(model: ModelEntry, isCurrent: boolean): string {
8087
*
8188
* @param provider CLIProxy provider (agy, gemini)
8289
* @param force Force reconfiguration even if settings exist
90+
* @param customSettingsPath Optional custom settings path for CLIProxy variants
8391
* @returns true if configuration was performed, false if skipped
8492
*/
8593
export async function configureProviderModel(
8694
provider: CLIProxyProvider,
87-
force: boolean = false
95+
force: boolean = false,
96+
customSettingsPath?: string
8897
): Promise<boolean> {
8998
// Check if provider supports model configuration
9099
if (!supportsModelConfig(provider)) {
@@ -94,7 +103,10 @@ export async function configureProviderModel(
94103
const catalog = getProviderCatalog(provider);
95104
if (!catalog) return false;
96105

97-
const settingsPath = getProviderSettingsPath(provider);
106+
// Use custom settings path for CLIProxy variants, otherwise use default provider path
107+
const settingsPath = customSettingsPath
108+
? customSettingsPath.replace(/^~/, process.env.HOME || process.env.USERPROFILE || '')
109+
: getProviderSettingsPath(provider);
98110

99111
// Skip if already configured (unless --config flag)
100112
if (!force && fs.existsSync(settingsPath)) {
@@ -111,7 +123,7 @@ export async function configureProviderModel(
111123
}));
112124

113125
// Find default index - use current model if configured, otherwise catalog default
114-
const currentModel = getCurrentModel(provider);
126+
const currentModel = getCurrentModel(provider, customSettingsPath);
115127
const targetModel = currentModel || catalog.defaultModel;
116128
const defaultIdx = catalog.models.findIndex((m) => m.id === targetModel);
117129
const safeDefaultIdx = defaultIdx >= 0 ? defaultIdx : 0;

src/commands/api-command.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ function apiExists(name: string): boolean {
109109

110110
/**
111111
* Create settings.json file for API profile
112+
* Includes all 4 model fields for proper Claude CLI integration
112113
*/
113114
function createSettingsFile(name: string, baseUrl: string, apiKey: string, model: string): string {
114115
const ccsDir = getCcsDir();
@@ -119,6 +120,9 @@ function createSettingsFile(name: string, baseUrl: string, apiKey: string, model
119120
ANTHROPIC_BASE_URL: baseUrl,
120121
ANTHROPIC_AUTH_TOKEN: apiKey,
121122
ANTHROPIC_MODEL: model,
123+
ANTHROPIC_DEFAULT_OPUS_MODEL: model,
124+
ANTHROPIC_DEFAULT_SONNET_MODEL: model,
125+
ANTHROPIC_DEFAULT_HAIKU_MODEL: model,
122126
},
123127
};
124128

0 commit comments

Comments
 (0)