cc-hub-cli 1.1.6 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +199 -36
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -254,6 +254,14 @@ var NoOpDesktopApp = class {
254
254
  import fs3 from "fs";
255
255
  import path3 from "path";
256
256
  import { randomUUID } from "crypto";
257
+ var ANTHROPIC_ALIASES = ["claude-sonnet-4-5", "claude-opus-4-7", "claude-haiku-4-5-20251001"];
258
+ function isAnthropicModel(model) {
259
+ const anthropicAliases = ["opus", "sonnet", "haiku", "best", "default", "opusplan", "opus[1m]", "sonnet[1m]"];
260
+ const lower = model.toLowerCase();
261
+ if (anthropicAliases.includes(lower)) return true;
262
+ if (lower.startsWith("claude-")) return true;
263
+ return false;
264
+ }
257
265
  function toDesktopProfile(p) {
258
266
  const models = p.models || (p.model ? [p.model] : []);
259
267
  const isAnthropic = p.provider === "anthropic" || !p.provider && !p.url;
@@ -263,13 +271,24 @@ function toDesktopProfile(p) {
263
271
  inferenceModels: models.map((m) => ({ name: m, supports1m: true }))
264
272
  };
265
273
  }
266
- return {
274
+ const mappings = [];
275
+ const mappedModels = models.map((m, index) => {
276
+ if (isAnthropicModel(m)) return m;
277
+ const alias = ANTHROPIC_ALIASES[Math.min(index, ANTHROPIC_ALIASES.length - 1)];
278
+ mappings.push({ alias, actual: m });
279
+ return alias;
280
+ });
281
+ const result = {
267
282
  inferenceProvider: "gateway",
268
283
  inferenceGatewayBaseUrl: p.url || void 0,
269
284
  inferenceGatewayApiKey: p.token || void 0,
270
285
  inferenceGatewayAuthScheme: "bearer",
271
- inferenceModels: models.map((m) => ({ name: m, supports1m: true }))
286
+ inferenceModels: mappedModels.map((m) => ({ name: m, supports1m: true }))
272
287
  };
288
+ if (mappings.length > 0) {
289
+ result.inferenceModelMappings = mappings;
290
+ }
291
+ return result;
273
292
  }
274
293
  var DesktopProfileSyncer = class {
275
294
  constructor(app) {
@@ -471,6 +490,7 @@ function fixJsonFile(filePath, fallback = {}) {
471
490
  const raw = fs4.readFileSync(filePath, "utf-8");
472
491
  try {
473
492
  JSON.parse(raw);
493
+ fs4.mkdirSync(CLAUDE_DIR, { recursive: true });
474
494
  fs4.copyFileSync(filePath, backupPath);
475
495
  return;
476
496
  } catch {
@@ -500,14 +520,24 @@ function fixJsonFile(filePath, fallback = {}) {
500
520
  warn(`Fixed invalid JSON in ${path4.basename(filePath)}.`);
501
521
  console.error(`Fixed invalid JSON in ${path4.basename(filePath)}.`);
502
522
  } catch {
523
+ let restored = false;
503
524
  if (fs4.existsSync(backupPath)) {
504
- fs4.copyFileSync(backupPath, filePath);
505
- warn(`Restored ${path4.basename(filePath)} from backup.`);
506
- console.error(`Restored ${path4.basename(filePath)} from backup.`);
507
- } else {
525
+ try {
526
+ const backupRaw = fs4.readFileSync(backupPath, "utf-8");
527
+ JSON.parse(backupRaw);
528
+ fs4.copyFileSync(backupPath, filePath);
529
+ restored = true;
530
+ warn(`Restored ${path4.basename(filePath)} from backup.`);
531
+ console.error(`Restored ${path4.basename(filePath)} from backup.`);
532
+ } catch {
533
+ error(`Backup ${path4.basename(backupPath)} is also corrupt; using fallback.`);
534
+ console.error(`Backup ${path4.basename(backupPath)} is also corrupt; using fallback.`);
535
+ }
536
+ }
537
+ if (!restored) {
508
538
  writeJson(filePath, fallback);
509
- error(`Could not fix ${path4.basename(filePath)}, no backup found, reset to default.`);
510
- console.error(`Could not fix ${path4.basename(filePath)}, no backup found, reset to default.`);
539
+ error(`Could not fix ${path4.basename(filePath)}, no valid backup found, reset to default.`);
540
+ console.error(`Could not fix ${path4.basename(filePath)}, no valid backup found, reset to default.`);
511
541
  }
512
542
  }
513
543
  }
@@ -518,6 +548,9 @@ import { spawnSync as spawnSync2, spawn } from "child_process";
518
548
  // src/provider/index.ts
519
549
  import { Command } from "commander";
520
550
 
551
+ // src/provider/server.ts
552
+ import http from "http";
553
+
521
554
  // src/provider/transform.ts
522
555
  function sanitizeToolId(id) {
523
556
  let sanitized = id.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -675,7 +708,7 @@ function transformOpenAIResponseToAnthropic(openaiResponse, originalModel) {
675
708
  id: openaiResponse.id ?? `msg_${Date.now()}`,
676
709
  type: "message",
677
710
  role: "assistant",
678
- model: openaiResponse.model ?? originalModel,
711
+ model: originalModel,
679
712
  content,
680
713
  stop_reason: finishMap[choice.finish_reason] ?? "end_turn",
681
714
  stop_sequence: null,
@@ -743,8 +776,7 @@ data: ${JSON.stringify(data)}
743
776
  }
744
777
 
745
778
  // src/provider/server.ts
746
- import http from "http";
747
- async function startOpenAIProxy(targetUrl, apiKey, model, models = []) {
779
+ async function startOpenAIProxy(targetUrl, apiKey, model, models = [], modelMappings = {}) {
748
780
  const base = targetUrl.replace(/\/+$/, "");
749
781
  const server = http.createServer(async (req, res) => {
750
782
  debug(`Proxy request: ${req.method} ${req.url}`);
@@ -770,7 +802,9 @@ async function startOpenAIProxy(targetUrl, apiKey, model, models = []) {
770
802
  return;
771
803
  }
772
804
  const isStream = !!parsed.stream;
773
- const openaiBody = transformAnthropicToOpenAI({ ...parsed, stream: false });
805
+ const requestModel = parsed.model ?? model;
806
+ const actualModel = modelMappings[requestModel] || requestModel;
807
+ const openaiBody = transformAnthropicToOpenAI({ ...parsed, model: actualModel, stream: false });
774
808
  if (isStream) {
775
809
  res.writeHead(200, {
776
810
  "Content-Type": "text/event-stream",
@@ -875,6 +909,17 @@ var PROVIDERS = [
875
909
  description: "Embedded proxy \u2014 translates Anthropic requests to OpenAI Chat Completions format"
876
910
  }
877
911
  ];
912
+ var ANTHROPIC_ALIASES2 = ["claude-sonnet-4-5", "claude-opus-4-7", "claude-haiku-4-5-20251001"];
913
+ function isAnthropicModel2(model) {
914
+ const anthropicAliases = ["opus", "sonnet", "haiku", "best", "default", "opusplan", "opus[1m]", "sonnet[1m]"];
915
+ const lower = model.toLowerCase();
916
+ if (anthropicAliases.includes(lower)) return true;
917
+ if (lower.startsWith("claude-")) return true;
918
+ return false;
919
+ }
920
+ function collect(value, previous) {
921
+ return previous.concat([value]);
922
+ }
878
923
  function providerCommand() {
879
924
  const cmd = new Command("provider").description("Manage provider types");
880
925
  cmd.command("list").description("List available provider types").action(safeAction(() => {
@@ -887,8 +932,51 @@ function providerCommand() {
887
932
  }));
888
933
  return cmd;
889
934
  }
935
+ function proxyCommand() {
936
+ return new Command("proxy").description("Start a standalone OpenAI proxy for the desktop app").option("--profile <name>", "Use configuration from a saved profile").option("-u, --url <url>", "Upstream base URL (e.g., https://api.openai.com)").option("-k, --api-key <key>", "Upstream API key").option("-m, --model <model>", "Default model", "gpt-4o").option("--mapping <mapping>", "Model alias mapping (format: alias:actual, can be used multiple times)", collect, []).action(safeAction(async (opts) => {
937
+ let targetUrl = opts.url || "https://api.openai.com";
938
+ let apiKey = opts.apiKey || "";
939
+ let defaultModel = opts.model || "gpt-4o";
940
+ let models = [];
941
+ const modelMappings = {};
942
+ if (opts.profile) {
943
+ ensureProfilesFile();
944
+ const data = readJson(PROFILES_FILE);
945
+ const p = data.profiles[opts.profile];
946
+ if (!p) {
947
+ throw new Error(`Profile '${opts.profile}' not found.`);
948
+ }
949
+ targetUrl = p.url || targetUrl;
950
+ apiKey = p.token || apiKey;
951
+ models = p.models || (p.model ? [p.model] : []);
952
+ defaultModel = models[0] || defaultModel;
953
+ models.forEach((m, i) => {
954
+ if (!isAnthropicModel2(m)) {
955
+ const alias = ANTHROPIC_ALIASES2[Math.min(i, ANTHROPIC_ALIASES2.length - 1)];
956
+ modelMappings[alias] = m;
957
+ }
958
+ });
959
+ } else {
960
+ for (const m of opts.mapping) {
961
+ const [alias, actual] = m.split(":");
962
+ if (alias && actual) {
963
+ modelMappings[alias] = actual;
964
+ }
965
+ }
966
+ models = [defaultModel];
967
+ }
968
+ const { baseUrl, stop } = await startOpenAIProxy(targetUrl, apiKey, defaultModel, models, modelMappings);
969
+ console.log(`Proxy running at ${baseUrl}`);
970
+ console.log("Press Ctrl+C to stop");
971
+ process.on("SIGINT", () => {
972
+ stop();
973
+ process.exit(0);
974
+ });
975
+ }));
976
+ }
890
977
 
891
978
  // src/profiles/runner.ts
979
+ var BUILT_IN_DEFAULT = "__builtin__";
892
980
  function resolveClaudeBinary() {
893
981
  return createBinaryResolver().resolve();
894
982
  }
@@ -909,7 +997,8 @@ function updateSettingsForProfile(p) {
909
997
  "ANTHROPIC_DEFAULT_HAIKU_MODEL",
910
998
  "ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME",
911
999
  "ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION",
912
- "ANTHROPIC_CUSTOM_MODEL_OPTION"
1000
+ "ANTHROPIC_CUSTOM_MODEL_OPTION",
1001
+ "ANTHROPIC_API_KEY"
913
1002
  ];
914
1003
  if (settings2.env) {
915
1004
  const env = settings2.env;
@@ -962,7 +1051,8 @@ function execClaude(profileName, p, extraArgs) {
962
1051
  p.url || "https://api.openai.com",
963
1052
  p.token || "",
964
1053
  firstModel || "gpt-4o",
965
- allModels
1054
+ allModels,
1055
+ {}
966
1056
  ).then(({ baseUrl, stop }) => {
967
1057
  env.ANTHROPIC_BASE_URL = baseUrl;
968
1058
  debug(`execClaude: proxy running at ${baseUrl}`);
@@ -985,6 +1075,22 @@ function execClaude(profileName, p, extraArgs) {
985
1075
  process.exit(result.status ?? 1);
986
1076
  }
987
1077
  }
1078
+ function execClaudeBuiltIn(extraArgs) {
1079
+ const binary = resolveClaudeBinary();
1080
+ const cmd = [binary, ...extraArgs];
1081
+ const env = {
1082
+ ...process.env,
1083
+ CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1",
1084
+ CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1"
1085
+ };
1086
+ info(`Launching Claude with built-in official models: binary=${binary}`);
1087
+ const result = spawnSync2(cmd[0], cmd.slice(1), {
1088
+ stdio: "inherit",
1089
+ env,
1090
+ shell: process.platform === "win32"
1091
+ });
1092
+ process.exit(result.status ?? 1);
1093
+ }
988
1094
 
989
1095
  // src/profiles/commands.ts
990
1096
  function maskToken(token) {
@@ -994,10 +1100,10 @@ function maskToken(token) {
994
1100
  }
995
1101
  function formatModels(p) {
996
1102
  if (p.models && p.models.length > 0) {
997
- const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel(m));
1103
+ const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel3(m));
998
1104
  const parts = [];
999
1105
  p.models.forEach((m, i) => {
1000
- if (!isAnthropicModel(m)) {
1106
+ if (!isAnthropicModel3(m)) {
1001
1107
  const aliasIndex = nonAnthropicModels.indexOf(m);
1002
1108
  if (aliasIndex === 0) parts.push(`${m} (sonnet)`);
1003
1109
  else if (aliasIndex === 1) parts.push(`${m} (opus)`);
@@ -1015,20 +1121,20 @@ function formatModels(p) {
1015
1121
  }
1016
1122
  return p.model || "(unset)";
1017
1123
  }
1018
- function isAnthropicModel(model) {
1124
+ function isAnthropicModel3(model) {
1019
1125
  const anthropicAliases = ["opus", "sonnet", "haiku", "best", "default", "opusplan", "opus[1m]", "sonnet[1m]"];
1020
1126
  const lower = model.toLowerCase();
1021
1127
  if (anthropicAliases.includes(lower)) return true;
1022
1128
  if (lower.startsWith("claude-")) return true;
1023
1129
  return false;
1024
1130
  }
1025
- function collect(value, previous) {
1131
+ function collect2(value, previous) {
1026
1132
  return previous.concat([value]);
1027
1133
  }
1028
1134
  function profileCommand() {
1029
1135
  const profile = new Command2("profile").description("Manage Claude CLI profiles");
1030
1136
  const syncer = createProfileSyncer();
1031
- profile.command("add").description("Add or update a profile").argument("<name>", "Profile name").option("-m, --model <model>", "Model ID - can be used multiple times (max 3)", collect, []).option("-t, --token <token>", "API key / token").option("-u, --url <url>", "Base URL").option("-p, --provider <provider>", "Provider type: anthropic (default) or openai").action(safeAction((name, opts) => {
1137
+ profile.command("add").description("Add or update a profile").argument("<name>", "Profile name").option("-m, --model <model>", "Model ID - can be used multiple times (max 3)", collect2, []).option("-t, --token <token>", "API key / token").option("-u, --url <url>", "Base URL").option("-p, --provider <provider>", "Provider type: anthropic (default) or openai").action(safeAction((name, opts) => {
1032
1138
  const models = opts.model && opts.model.length > 0 ? opts.model : void 0;
1033
1139
  if (models && models.length > 3) {
1034
1140
  throw new Error("Error: A profile can have at most 3 models.");
@@ -1050,7 +1156,7 @@ function profileCommand() {
1050
1156
  debug(`profile add: wrote ${PROFILES_FILE}`);
1051
1157
  console.log(`Profile '${name}' saved.`);
1052
1158
  }));
1053
- profile.command("update").description("Update fields of an existing profile").argument("<name>", "Profile name (must already exist)").option("-m, --model <model>", "Model ID - can be used multiple times", collect, []).option("-d, --delete-model <model>", "Remove model ID - can be used multiple times", collect, []).option("-t, --token <token>", "API key / token").option("-u, --url <url>", "Base URL").option("-p, --provider <provider>", "Provider type").action(safeAction((name, opts) => {
1159
+ profile.command("update").description("Update fields of an existing profile").argument("<name>", "Profile name (must already exist)").option("-m, --model <model>", "Model ID - can be used multiple times", collect2, []).option("-d, --delete-model <model>", "Remove model ID - can be used multiple times", collect2, []).option("-t, --token <token>", "API key / token").option("-u, --url <url>", "Base URL").option("-p, --provider <provider>", "Provider type").action(safeAction((name, opts) => {
1054
1160
  ensureProfilesFile();
1055
1161
  const data = readJson(PROFILES_FILE);
1056
1162
  if (!data.profiles[name]) {
@@ -1142,6 +1248,9 @@ function profileCommand() {
1142
1248
  profile.command("view").description("View full details of a profile (token unmasked)").argument("<name>", "Profile name").option("-j, --json", "Output as JSON").action(safeAction((name, opts) => {
1143
1249
  ensureProfilesFile();
1144
1250
  const data = readJson(PROFILES_FILE);
1251
+ if (name === BUILT_IN_DEFAULT) {
1252
+ throw new Error(`'${BUILT_IN_DEFAULT}' is not a stored profile. Use 'cc-hub run --built-in' or 'cc-hub use --built-in' for official Anthropic models.`);
1253
+ }
1145
1254
  const p = data.profiles[name];
1146
1255
  if (!p) {
1147
1256
  throw new Error(`Profile '${name}' not found.`);
@@ -1153,10 +1262,10 @@ function profileCommand() {
1153
1262
  console.log(`Name: ${name}`);
1154
1263
  console.log(`Model: ${p.model || "(unset)"}`);
1155
1264
  if (p.models && p.models.length > 0) {
1156
- const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel(m));
1265
+ const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel3(m));
1157
1266
  console.log(`Models:`);
1158
1267
  for (const m of p.models) {
1159
- if (!isAnthropicModel(m)) {
1268
+ if (!isAnthropicModel3(m)) {
1160
1269
  const aliasIndex = nonAnthropicModels.indexOf(m);
1161
1270
  let alias = "";
1162
1271
  if (aliasIndex === 0) alias = " (sonnet)";
@@ -1203,9 +1312,19 @@ function profileCommand() {
1203
1312
  writeJson(PROFILES_FILE, data);
1204
1313
  console.log(`Profile '${oldName}' renamed to '${newName}'.`);
1205
1314
  }));
1206
- profile.command("default").description("Set the default profile").argument("<name>", "Profile name to set as default").action(safeAction((name) => {
1315
+ profile.command("default").description("Set the default profile").option("--built-in", "Use official Anthropic models as default").argument("[name]", "Profile name to set as default (required unless --built-in)").action(safeAction((name, opts) => {
1207
1316
  ensureProfilesFile();
1208
1317
  const data = readJson(PROFILES_FILE);
1318
+ if (opts.builtIn) {
1319
+ data.default = BUILT_IN_DEFAULT;
1320
+ writeJson(PROFILES_FILE, data);
1321
+ debug(`profile default: wrote ${PROFILES_FILE}`);
1322
+ console.log("Default set to built-in official Anthropic models.");
1323
+ return;
1324
+ }
1325
+ if (!name) {
1326
+ throw new Error("Profile name is required. Use --built-in for official Anthropic models.");
1327
+ }
1209
1328
  if (!data.profiles[name]) {
1210
1329
  throw new Error(`Profile '${name}' not found.`);
1211
1330
  }
@@ -1228,6 +1347,7 @@ function profileCommand() {
1228
1347
  return;
1229
1348
  }
1230
1349
  for (const name of names) {
1350
+ if (name === BUILT_IN_DEFAULT) continue;
1231
1351
  const p = data.profiles[name];
1232
1352
  debug(`profile sync: syncing '${name}' to desktop`);
1233
1353
  syncer.sync(name, p);
@@ -1240,9 +1360,19 @@ function profileCommand() {
1240
1360
  }
1241
1361
  function useCommand() {
1242
1362
  const syncer = createProfileSyncer();
1243
- return new Command2("use").description("Set a profile as the default").argument("<name>", "Profile name").action(safeAction((name) => {
1363
+ return new Command2("use").description("Set a profile as the default").option("--built-in", "Use official Anthropic models as default").argument("[name]", "Profile name (required unless --built-in)").action(safeAction((name, opts) => {
1244
1364
  ensureProfilesFile();
1245
1365
  const data = readJson(PROFILES_FILE);
1366
+ if (opts.builtIn) {
1367
+ data.default = BUILT_IN_DEFAULT;
1368
+ writeJson(PROFILES_FILE, data);
1369
+ debug(`use: wrote ${PROFILES_FILE}`);
1370
+ console.log("Default set to built-in official Anthropic models.");
1371
+ return;
1372
+ }
1373
+ if (!name) {
1374
+ throw new Error("Profile name is required. Use --built-in for official Anthropic models.");
1375
+ }
1246
1376
  if (!data.profiles[name]) {
1247
1377
  throw new Error(`Profile '${name}' not found.`);
1248
1378
  }
@@ -1255,10 +1385,14 @@ function useCommand() {
1255
1385
  }));
1256
1386
  }
1257
1387
  function runCommand() {
1258
- return new Command2("run").description("Launch Claude Code using the default or a specified profile").allowUnknownOption().argument("[args...]", "Optional profile name followed by extra arguments").action(safeAction((args) => {
1388
+ return new Command2("run").description("Launch Claude Code using the default or a specified profile").option("--built-in", "Use official Anthropic models (no custom profile)").allowUnknownOption().argument("[args...]", "Optional profile name followed by extra arguments").action(safeAction((args, opts) => {
1259
1389
  fixJsonFile(CLAUDE_JSON);
1260
1390
  ensureProfilesFile();
1261
1391
  const data = readJson(PROFILES_FILE);
1392
+ if (opts.builtIn) {
1393
+ execClaudeBuiltIn(args);
1394
+ return;
1395
+ }
1262
1396
  let profileName = "";
1263
1397
  let claudeArgs;
1264
1398
  if (args.length > 0 && data.profiles[args[0]]) {
@@ -1268,8 +1402,12 @@ function runCommand() {
1268
1402
  profileName = data.default || "";
1269
1403
  claudeArgs = args;
1270
1404
  }
1405
+ if (profileName === BUILT_IN_DEFAULT) {
1406
+ execClaudeBuiltIn(claudeArgs);
1407
+ return;
1408
+ }
1271
1409
  if (!profileName) {
1272
- throw new Error("No default profile set. Use 'cc-hub use <name>' first.");
1410
+ throw new Error("No default profile set. Use 'cc-hub use <name>' or 'cc-hub use --built-in' first.");
1273
1411
  }
1274
1412
  const p = data.profiles[profileName];
1275
1413
  debug(`run: launching claude with profile '${profileName}', args=[${claudeArgs.join(", ")}]`);
@@ -2025,7 +2163,7 @@ _cc-hub() {
2025
2163
  local -a commands
2026
2164
  commands=(
2027
2165
  'profile:Manage Claude CLI profiles'
2028
- 'use:Launch Claude Code with a saved profile'
2166
+ 'use:Set a profile as the default'
2029
2167
  'run:Launch Claude Code using the default or a specified profile'
2030
2168
  'hook:Manage Claude Code hooks in settings.json'
2031
2169
  'session:Manage Claude Code sessions'
@@ -2104,8 +2242,10 @@ _cc-hub() {
2104
2242
  profile)
2105
2243
  if (( CURRENT == 2 )); then
2106
2244
  _describe -t profile-subcmds 'profile subcommand' profile_subcmds
2107
- elif [[ $words[2] == "view" || $words[2] == "remove" || $words[2] == "default" ]]; then
2245
+ elif [[ $words[2] == "view" || $words[2] == "remove" ]]; then
2108
2246
  _cc_hub_profiles
2247
+ elif [[ $words[2] == "default" ]]; then
2248
+ _arguments -C -S '--built-in[Use official Anthropic models as default]' '*:profile:_cc_hub_profiles'
2109
2249
  elif [[ $words[2] == "rename" ]]; then
2110
2250
  if (( CURRENT == 3 )); then
2111
2251
  _cc_hub_profiles
@@ -2137,7 +2277,7 @@ _cc-hub() {
2137
2277
  fi
2138
2278
  ;;
2139
2279
  use|run)
2140
- _cc_hub_profiles
2280
+ _arguments -C -S '--built-in[Use official Anthropic models (no custom profile)]' '*:profile:_cc_hub_profiles'
2141
2281
  ;;
2142
2282
  hook)
2143
2283
  if (( CURRENT == 2 )); then
@@ -2160,20 +2300,22 @@ compdef _cc-hub cc-hub
2160
2300
  `;
2161
2301
 
2162
2302
  // src/complete/bash.ts
2163
- var BASH_COMPLETION = `_cc-hub_profiles() {
2303
+ var BASH_COMPLETION = `_cc-hub_profile_names() {
2164
2304
  local profiles_file="\${CLAUDE_PROFILES_FILE:-$HOME/.claude/profiles.json}"
2165
2305
  if [[ -f "$profiles_file" ]]; then
2166
- local names
2167
- names=$(command python3 -c "
2306
+ command python3 -c "
2168
2307
  import json
2169
2308
  data = json.load(open('$profiles_file'))
2170
2309
  for name in data.get('profiles', {}):
2171
2310
  print(name)
2172
- " 2>/dev/null)
2173
- COMPREPLY=($(compgen -W "$names" -- "\${cur}"))
2311
+ " 2>/dev/null
2174
2312
  fi
2175
2313
  }
2176
2314
 
2315
+ _cc-hub_profiles() {
2316
+ COMPREPLY=($(compgen -W "$(_cc-hub_profile_names)" -- "\${cur}"))
2317
+ }
2318
+
2177
2319
  _cc-hub_models_for_profile() {
2178
2320
  local profile_name="$1"
2179
2321
  local profiles_file="\${CLAUDE_PROFILES_FILE:-$HOME/.claude/profiles.json}"
@@ -2222,8 +2364,10 @@ _cc-hub() {
2222
2364
  profile)
2223
2365
  if [[ \${COMP_CWORD} -eq 2 ]]; then
2224
2366
  COMPREPLY=($(compgen -W "$profile_subcmds" -- "$cur"))
2225
- elif [[ "$prev" == "view" || "$prev" == "remove" || "$prev" == "default" ]]; then
2367
+ elif [[ "$prev" == "view" || "$prev" == "remove" ]]; then
2226
2368
  _cc-hub_profiles
2369
+ elif [[ "$prev" == "default" ]]; then
2370
+ COMPREPLY=($(compgen -W "--built-in $(_cc-hub_profile_names)" -- "$cur"))
2227
2371
  elif [[ "$prev" == "rename" ]]; then
2228
2372
  _cc-hub_profiles
2229
2373
  elif [[ "$prev" == "profile" ]]; then
@@ -2249,7 +2393,11 @@ _cc-hub() {
2249
2393
  fi
2250
2394
  ;;
2251
2395
  use|run)
2252
- _cc-hub_profiles
2396
+ if [[ "$prev" == "--built-in" ]]; then
2397
+ :
2398
+ else
2399
+ COMPREPLY=($(compgen -W "--built-in $(_cc-hub_profile_names)" -- "$cur"))
2400
+ fi
2253
2401
  ;;
2254
2402
  hook)
2255
2403
  if [[ \${COMP_CWORD} -eq 2 ]]; then
@@ -2311,6 +2459,10 @@ var POWERSHELL_COMPLETION = `Register-ArgumentCompleter -Native -CommandName cc-
2311
2459
  $profileSubcmds | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
2312
2460
  return
2313
2461
  }
2462
+ if ($tokens[2] -eq 'default' -and $tokens.Count -ge 3) {
2463
+ $opts = @('--built-in')
2464
+ $opts | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
2465
+ }
2314
2466
  }
2315
2467
  'hook' {
2316
2468
  if ($tokens.Count -eq 2 -or ($tokens.Count -eq 3 -and $wordToComplete -ne '')) {
@@ -2330,6 +2482,16 @@ var POWERSHELL_COMPLETION = `Register-ArgumentCompleter -Native -CommandName cc-
2330
2482
  return
2331
2483
  }
2332
2484
  }
2485
+ 'use' {
2486
+ $opts = @('--built-in')
2487
+ $opts | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
2488
+ return
2489
+ }
2490
+ 'run' {
2491
+ $opts = @('--built-in')
2492
+ $opts | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
2493
+ return
2494
+ }
2333
2495
  }
2334
2496
  }`;
2335
2497
 
@@ -2369,6 +2531,7 @@ program.addCommand(hooksCommand());
2369
2531
  program.addCommand(sessionCommand());
2370
2532
  program.addCommand(completionCommand());
2371
2533
  program.addCommand(providerCommand());
2534
+ program.addCommand(proxyCommand());
2372
2535
  try {
2373
2536
  program.parse();
2374
2537
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-hub-cli",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "description": "Manage Claude CLI profiles, hooks, and sessions",
5
5
  "type": "module",
6
6
  "bin": {