cc-hub-cli 1.1.13 → 1.1.15

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 +74 -28
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -923,6 +923,9 @@ function fixJsonFile(filePath, fallback = {}) {
923
923
  }
924
924
  }
925
925
 
926
+ // src/profiles/commands.ts
927
+ import path5 from "path";
928
+
926
929
  // src/profiles/runner.ts
927
930
  import { spawnSync as spawnSync2, spawn } from "child_process";
928
931
  var BUILT_IN_DEFAULT = "__builtin__";
@@ -1298,6 +1301,48 @@ function profileCommand() {
1298
1301
  debug(`profile sync: wrote ${PROFILES_FILE}`);
1299
1302
  console.log(`Synced ${names.length} profile(s) to the desktop app.`);
1300
1303
  }));
1304
+ profile.command("export").description("Export a profile to a settings file").argument("<name>", "Profile name").action(safeAction((name) => {
1305
+ ensureProfilesFile();
1306
+ const data = readJson(PROFILES_FILE);
1307
+ const p = data.profiles[name];
1308
+ if (!p) {
1309
+ throw new Error(`Profile '${name}' not found.`);
1310
+ }
1311
+ ensureSettingsFile();
1312
+ const settings2 = readJson(SETTINGS_FILE);
1313
+ const exported = Object.fromEntries(
1314
+ Object.entries(settings2).filter(([key]) => !key.startsWith("_") && key != "env")
1315
+ );
1316
+ const env = {
1317
+ ...typeof exported.env === "object" && exported.env !== null ? exported.env : {}
1318
+ };
1319
+ if (p.token) env.ANTHROPIC_AUTH_TOKEN = p.token;
1320
+ if (p.url) env.ANTHROPIC_BASE_URL = p.url;
1321
+ const models = p.models || (p.model ? [p.model] : []);
1322
+ if (models.length > 0) {
1323
+ if (models[0]) {
1324
+ env.ANTHROPIC_DEFAULT_SONNET_MODEL = models[0];
1325
+ env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME = models[0];
1326
+ env.ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION = `Custom: ${models[0]}`;
1327
+ }
1328
+ if (models[1]) {
1329
+ env.ANTHROPIC_DEFAULT_OPUS_MODEL = models[1];
1330
+ env.ANTHROPIC_DEFAULT_OPUS_MODEL_NAME = models[1];
1331
+ env.ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION = `Custom: ${models[1]}`;
1332
+ }
1333
+ if (models[2]) {
1334
+ env.ANTHROPIC_DEFAULT_HAIKU_MODEL = models[2];
1335
+ env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME = models[2];
1336
+ env.ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION = `Custom: ${models[2]}`;
1337
+ }
1338
+ }
1339
+ env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "1";
1340
+ env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
1341
+ exported.env = env;
1342
+ const exportPath = path5.join(CLAUDE_DIR, `settings.${name}.json`);
1343
+ writeJson(exportPath, exported);
1344
+ console.log(`Profile '${name}' exported to ${exportPath}`);
1345
+ }));
1301
1346
  return profile;
1302
1347
  }
1303
1348
  function useCommand() {
@@ -1577,12 +1622,12 @@ function decodePath2(encoded) {
1577
1622
 
1578
1623
  // src/sessions/stats.ts
1579
1624
  import fs5 from "fs";
1580
- import path5 from "path";
1625
+ import path6 from "path";
1581
1626
  function getDirSize(dir) {
1582
1627
  let total = 0;
1583
1628
  try {
1584
1629
  for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
1585
- const fullPath = path5.join(dir, entry.name);
1630
+ const fullPath = path6.join(dir, entry.name);
1586
1631
  if (entry.isDirectory()) {
1587
1632
  total += getDirSize(fullPath);
1588
1633
  } else {
@@ -1606,7 +1651,7 @@ function formatSize(bytes) {
1606
1651
 
1607
1652
  // src/sessions/utils.ts
1608
1653
  import fs6 from "fs";
1609
- import path6 from "path";
1654
+ import path7 from "path";
1610
1655
  function formatTimestamp(ms) {
1611
1656
  const d = new Date(ms);
1612
1657
  const pad = (n) => String(n).padStart(2, "0");
@@ -1615,7 +1660,7 @@ function formatTimestamp(ms) {
1615
1660
  function findProjectDir(query) {
1616
1661
  debug(`sessions: findProjectDir query="${query}"`);
1617
1662
  const encoded = encodePath(query);
1618
- if (fs6.existsSync(path6.join(PROJECTS_DIR, encoded))) {
1663
+ if (fs6.existsSync(path7.join(PROJECTS_DIR, encoded))) {
1619
1664
  debug(`sessions: findProjectDir exact match ${encoded}`);
1620
1665
  return encoded;
1621
1666
  }
@@ -1693,10 +1738,10 @@ function findSessionFile(sessionQuery, projectQuery) {
1693
1738
  if (!projDir) {
1694
1739
  throw new Error(`No project matched: ${projectQuery}`);
1695
1740
  }
1696
- searchDirs.push(path6.join(PROJECTS_DIR, projDir));
1741
+ searchDirs.push(path7.join(PROJECTS_DIR, projDir));
1697
1742
  } else {
1698
1743
  try {
1699
- searchDirs = fs6.readdirSync(PROJECTS_DIR).map((d) => path6.join(PROJECTS_DIR, d));
1744
+ searchDirs = fs6.readdirSync(PROJECTS_DIR).map((d) => path7.join(PROJECTS_DIR, d));
1700
1745
  } catch {
1701
1746
  throw new Error(`No projects directory found at ${PROJECTS_DIR}`);
1702
1747
  }
@@ -1712,7 +1757,7 @@ function findSessionFile(sessionQuery, projectQuery) {
1712
1757
  for (const file of files) {
1713
1758
  const sessionId = file.replace(/\.jsonl$/, "");
1714
1759
  if (sessionId.toLowerCase().includes(sessionQuery.toLowerCase())) {
1715
- matches.push({ filePath: path6.join(dir, file), project: path6.basename(dir) });
1760
+ matches.push({ filePath: path7.join(dir, file), project: path7.basename(dir) });
1716
1761
  }
1717
1762
  }
1718
1763
  }
@@ -1720,7 +1765,7 @@ function findSessionFile(sessionQuery, projectQuery) {
1720
1765
  return null;
1721
1766
  }
1722
1767
  if (matches.length > 1) {
1723
- const lines = matches.map((m) => ` ${path6.basename(m.filePath)} in ${decodePath(m.project)}`).join("\n");
1768
+ const lines = matches.map((m) => ` ${path7.basename(m.filePath)} in ${decodePath(m.project)}`).join("\n");
1724
1769
  throw new Error(`Multiple sessions matched '${sessionQuery}':
1725
1770
  ${lines}
1726
1771
  Use --project to disambiguate.`);
@@ -1731,7 +1776,7 @@ Use --project to disambiguate.`);
1731
1776
  // src/sessions/commands.ts
1732
1777
  import { Command as Command4 } from "commander";
1733
1778
  import fs7 from "fs";
1734
- import path7 from "path";
1779
+ import path8 from "path";
1735
1780
  import { spawnSync as spawnSync3 } from "child_process";
1736
1781
  function sessionCommand() {
1737
1782
  const session = new Command4("session").description("Manage Claude Code sessions");
@@ -1748,14 +1793,14 @@ function sessionCommand() {
1748
1793
  return;
1749
1794
  }
1750
1795
  dirs.sort((a, b) => {
1751
- const statA = fs7.statSync(path7.join(PROJECTS_DIR, a));
1752
- const statB = fs7.statSync(path7.join(PROJECTS_DIR, b));
1796
+ const statA = fs7.statSync(path8.join(PROJECTS_DIR, a));
1797
+ const statB = fs7.statSync(path8.join(PROJECTS_DIR, b));
1753
1798
  return statB.mtimeMs - statA.mtimeMs;
1754
1799
  });
1755
1800
  let count = 0;
1756
1801
  for (const projDir of dirs) {
1757
1802
  if (count >= limit) break;
1758
- const fullPath = path7.join(PROJECTS_DIR, projDir);
1803
+ const fullPath = path8.join(PROJECTS_DIR, projDir);
1759
1804
  let nSessions = 0;
1760
1805
  try {
1761
1806
  nSessions = fs7.readdirSync(fullPath).filter((f) => f.endsWith(".jsonl")).length;
@@ -1779,7 +1824,7 @@ function sessionCommand() {
1779
1824
  if (!projDir) {
1780
1825
  throw new Error(`No project matched: ${project}`);
1781
1826
  }
1782
- const fullPath = path7.join(PROJECTS_DIR, projDir);
1827
+ const fullPath = path8.join(PROJECTS_DIR, projDir);
1783
1828
  console.log(`Project: ${decodePath2(projDir)}`);
1784
1829
  console.log(`Dir: ${fullPath}`);
1785
1830
  console.log("");
@@ -1793,7 +1838,7 @@ function sessionCommand() {
1793
1838
  return;
1794
1839
  }
1795
1840
  for (const file of files) {
1796
- const filePath = path7.join(fullPath, file);
1841
+ const filePath = path8.join(fullPath, file);
1797
1842
  const sessionId = file.replace(/\.jsonl$/, "");
1798
1843
  let msgCount = 0;
1799
1844
  try {
@@ -1843,7 +1888,7 @@ function sessionCommand() {
1843
1888
  if (!projDir) {
1844
1889
  throw new Error(`No project matched: ${opts.project}`);
1845
1890
  }
1846
- searchRoots = [{ root: path7.join(PROJECTS_DIR, projDir), label: "" }];
1891
+ searchRoots = [{ root: path8.join(PROJECTS_DIR, projDir), label: "" }];
1847
1892
  }
1848
1893
  const limit = parseInt(opts.limit, 10);
1849
1894
  let count = 0;
@@ -1857,7 +1902,7 @@ function sessionCommand() {
1857
1902
  }
1858
1903
  for (const entry of entries) {
1859
1904
  if (count >= limit) break;
1860
- const fullPath = path7.join(dir, entry.name);
1905
+ const fullPath = path8.join(dir, entry.name);
1861
1906
  if (entry.isDirectory()) {
1862
1907
  searchDir(fullPath, label, baseDir);
1863
1908
  } else if (entry.name.endsWith(".jsonl")) {
@@ -1871,9 +1916,9 @@ function sessionCommand() {
1871
1916
  const match = opts.ignoreCase ? line.toLowerCase().includes(query.toLowerCase()) : line.includes(query);
1872
1917
  if (match) {
1873
1918
  if (!found) {
1874
- const relPath = path7.relative(baseDir, fullPath);
1875
- const projEnc = relPath.split(path7.sep)[0];
1876
- const sessionId = path7.basename(fullPath, ".jsonl");
1919
+ const relPath = path8.relative(baseDir, fullPath);
1920
+ const projEnc = relPath.split(path8.sep)[0];
1921
+ const sessionId = path8.basename(fullPath, ".jsonl");
1877
1922
  const projName = label ? projEnc : decodePath2(projEnc);
1878
1923
  console.log(`${label}[${projName} \u2192 ${sessionId}]`);
1879
1924
  found = true;
@@ -1925,7 +1970,7 @@ function sessionCommand() {
1925
1970
  console.log(fmt("---", "----------", "-------", "---", ""));
1926
1971
  for (const file of files) {
1927
1972
  try {
1928
- const data = JSON.parse(fs7.readFileSync(path7.join(SESSIONS_DIR, file), "utf-8"));
1973
+ const data = JSON.parse(fs7.readFileSync(path8.join(SESSIONS_DIR, file), "utf-8"));
1929
1974
  const pid = String(data.pid || "?");
1930
1975
  const sessionId = data.sessionId || "?";
1931
1976
  const cwd = data.cwd || "?";
@@ -1953,7 +1998,7 @@ function sessionCommand() {
1953
1998
  const results = [];
1954
1999
  try {
1955
2000
  for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
1956
- const fullPath = path7.join(dir, entry.name);
2001
+ const fullPath = path8.join(dir, entry.name);
1957
2002
  if (entry.isDirectory()) results.push(...walk(fullPath));
1958
2003
  else if (entry.name.endsWith(".jsonl")) results.push(fullPath);
1959
2004
  }
@@ -2030,7 +2075,7 @@ function sessionCommand() {
2030
2075
  return;
2031
2076
  }
2032
2077
  for (const entry of entries) {
2033
- const fullPath = path7.join(dir, entry.name);
2078
+ const fullPath = path8.join(dir, entry.name);
2034
2079
  if (entry.isDirectory()) {
2035
2080
  walk(fullPath);
2036
2081
  } else if (entry.name.endsWith(".jsonl")) {
@@ -2136,6 +2181,7 @@ _cc-hub() {
2136
2181
  'rename:Rename a profile'
2137
2182
  'default:Set the default profile'
2138
2183
  'sync:Synchronize all CLI profiles to the desktop app'
2184
+ 'export:Export a profile to a settings file'
2139
2185
  )
2140
2186
 
2141
2187
  local -a hooks_subcmds
@@ -2190,7 +2236,7 @@ _cc-hub() {
2190
2236
  profile)
2191
2237
  if (( CURRENT == 2 )); then
2192
2238
  _describe -t profile-subcmds 'profile subcommand' profile_subcmds
2193
- elif [[ $words[2] == "view" || $words[2] == "remove" ]]; then
2239
+ elif [[ $words[2] == "view" || $words[2] == "remove" || $words[2] == "export" ]]; then
2194
2240
  _cc_hub_profiles
2195
2241
  elif [[ $words[2] == "default" ]]; then
2196
2242
  _arguments -C -S '--built-in[Use official Anthropic models as default]' '*:profile:_cc_hub_profiles'
@@ -2299,7 +2345,7 @@ _cc-hub() {
2299
2345
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
2300
2346
  commands="profile use run hook session provider cache completion help"
2301
2347
 
2302
- local profile_subcmds="add update list view remove rename default sync"
2348
+ local profile_subcmds="add update list view remove rename default sync export"
2303
2349
  local provider_subcmds="list"
2304
2350
  local provider_types="anthropic openai"
2305
2351
  local hooks_subcmds="list add remove enable disable"
@@ -2318,7 +2364,7 @@ _cc-hub() {
2318
2364
  profile)
2319
2365
  if [[ \${COMP_CWORD} -eq 2 ]]; then
2320
2366
  COMPREPLY=($(compgen -W "$profile_subcmds" -- "$cur"))
2321
- elif [[ "$prev" == "view" || "$prev" == "remove" ]]; then
2367
+ elif [[ "$prev" == "view" || "$prev" == "remove" || "$prev" == "export" ]]; then
2322
2368
  _cc-hub_profiles
2323
2369
  elif [[ "$prev" == "default" ]]; then
2324
2370
  COMPREPLY=($(compgen -W "--built-in $(_cc-hub_profile_names)" -- "$cur"))
@@ -2399,7 +2445,7 @@ var POWERSHELL_COMPLETION = `Register-ArgumentCompleter -Native -CommandName cc-
2399
2445
  'help:Display help for a command'
2400
2446
  )
2401
2447
 
2402
- $profileSubcmds = @('add', 'update', 'list', 'view', 'remove', 'rename', 'default', 'sync')
2448
+ $profileSubcmds = @('add', 'update', 'list', 'view', 'remove', 'rename', 'default', 'sync', 'export')
2403
2449
  $hookSubcmds = @('list', 'add', 'remove', 'enable', 'disable')
2404
2450
  $sessionSubcmds = @('list', 'show', 'search', 'ps', 'stats', 'clean', 'troubleshoot')
2405
2451
  $cacheSubcmds = @('restore')
@@ -2533,7 +2579,7 @@ function proxyCommand() {
2533
2579
  // src/cache/commands.ts
2534
2580
  import { Command as Command7 } from "commander";
2535
2581
  import fs8 from "fs";
2536
- import path8 from "path";
2582
+ import path9 from "path";
2537
2583
  import { spawnSync as spawnSync4 } from "child_process";
2538
2584
  import { createInterface } from "readline/promises";
2539
2585
  async function confirmPrompt(message) {
@@ -2608,7 +2654,7 @@ function killProcesses(pids) {
2608
2654
  function cacheCommand() {
2609
2655
  const cache = new Command7("cache").description("Manage Claude Code cache and backup files");
2610
2656
  cache.command("restore").description("Restore ~/.claude/.claude.json.backup to ~/.claude.json").action(safeAction(async () => {
2611
- const backupPath = path8.join(CLAUDE_DIR, ".claude.json.backup");
2657
+ const backupPath = path9.join(CLAUDE_DIR, ".claude.json.backup");
2612
2658
  const targetPath = CLAUDE_JSON;
2613
2659
  if (!fs8.existsSync(backupPath)) {
2614
2660
  throw new Error(`Backup not found: ${backupPath}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-hub-cli",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
4
4
  "description": "Manage Claude CLI profiles, hooks, and sessions",
5
5
  "type": "module",
6
6
  "bin": {