@vocoder/cli 0.12.3 → 0.13.1

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.
package/dist/bin.mjs CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  readAuthData,
14
14
  verifyStoredAuth,
15
15
  writeAuthData
16
- } from "./chunk-2Q3JPWWV.mjs";
16
+ } from "./chunk-ZHRKJ2KZ.mjs";
17
17
 
18
18
  // src/bin.ts
19
19
  import { Command } from "commander";
@@ -346,25 +346,35 @@ async function selectGitHubInstallation(installations, canInstallNew) {
346
346
 
347
347
  // src/utils/project-create.ts
348
348
  import * as p3 from "@clack/prompts";
349
- import chalk4 from "chalk";
349
+ import chalk3 from "chalk";
350
350
 
351
351
  // src/utils/branch-select.ts
352
352
  import { execSync } from "child_process";
353
353
  import { isCancel as isCancel2, Prompt } from "@clack/core";
354
+
355
+ // src/utils/theme.ts
354
356
  import chalk2 from "chalk";
357
+ var ORANGE = "#FC5206";
358
+ var PINK = "#D51977";
359
+ var BLUE = "#2450A9";
360
+ var noColor = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
361
+ var hex = (color) => (s) => noColor ? s : chalk2.hex(color)(s);
362
+ var dim = (s) => noColor ? s : chalk2.dim(s);
363
+ var bld = (s) => noColor ? s : chalk2.bold(s);
364
+ var grn = (s) => noColor ? s : chalk2.green(s);
365
+ var ylw = (s) => noColor ? s : chalk2.yellow(s);
366
+ var red = (s) => noColor ? s : chalk2.red(s);
367
+ var highlight = hex(PINK);
368
+ var info = hex(BLUE);
369
+ var active = hex(ORANGE);
370
+
371
+ // src/utils/branch-select.ts
355
372
  var S_BAR = "\u2502";
356
373
  var S_BAR_END = "\u2514";
357
374
  var S_ACTIVE = "\u25C6";
358
375
  var S_SUBMIT = "\u25C6";
359
376
  var S_CANCEL = "\u25A0";
360
377
  var S_ERROR = "\u25B2";
361
- var noColor = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
362
- var dim = (s) => noColor ? s : chalk2.gray(s);
363
- var cyan = (s) => noColor ? s : chalk2.cyan(s);
364
- var grn = (s) => noColor ? s : chalk2.green(s);
365
- var ylw = (s) => noColor ? s : chalk2.yellow(s);
366
- var red = (s) => noColor ? s : chalk2.red(s);
367
- var bld = (s) => noColor ? s : chalk2.bold(s);
368
378
  function symbol(state) {
369
379
  switch (state) {
370
380
  case "submit":
@@ -374,7 +384,7 @@ function symbol(state) {
374
384
  case "error":
375
385
  return ylw(S_ERROR);
376
386
  default:
377
- return cyan(S_ACTIVE);
387
+ return active(S_ACTIVE);
378
388
  }
379
389
  }
380
390
  function detectGitBranches(cwd) {
@@ -447,19 +457,19 @@ function buildList(filtered, cursor, scrollOffset, selected, filter, customPatte
447
457
  const item = filtered[i];
448
458
  const isCursor = i === cursor && !addCursor;
449
459
  const isChecked = selected.has(item.value);
450
- const icon = isChecked ? isCursor ? grn("\u25FC") : "\u25FC" : isCursor ? grn("\u25FB") : dim("\u25FB");
460
+ const icon = isChecked ? isCursor ? info("\u25FC") : info("\u25FC") : isCursor ? active("\u25FB") : dim("\u25FB");
451
461
  let label = item.isCustom ? `${item.label} ${dim("(custom)")}` : item.label;
452
462
  if (isCursor) label = bld(label);
453
- lines.push(`${cyan(S_BAR)} ${icon} ${label}`);
463
+ lines.push(`${info(S_BAR)} ${icon} ${label}`);
454
464
  }
455
465
  const trimmed = filter.trim();
456
466
  const allItems = [...filtered];
457
467
  const isNewPattern = trimmed.length > 0 && !allItems.some((i) => i.value === trimmed) && !customPatterns.includes(trimmed);
458
468
  if (isNewPattern) {
459
469
  const err = validateBranchPattern(trimmed) ?? (excludedPatterns.has(trimmed) ? "Already used for automatic translation" : null);
460
- const icon = addCursor ? grn("\u25FB") : dim("\u25FB");
470
+ const icon = addCursor ? active("\u25FB") : dim("\u25FB");
461
471
  const label = err ? `${ylw("+")} ${dim(`"${trimmed}" \u2014 ${err}`)}` : `${grn("+")} Add "${trimmed}" as branch pattern`;
462
- lines.push(`${cyan(S_BAR)} ${icon} ${label}`);
472
+ lines.push(`${info(S_BAR)} ${icon} ${label}`);
463
473
  } else if (filtered.length === 0 && trimmed.length === 0) {
464
474
  lines.push(dim(`${S_BAR} No branches detected`));
465
475
  }
@@ -542,7 +552,7 @@ ${symbol(this.state)} ${message}
542
552
  default:
543
553
  return [
544
554
  hdr.trimEnd(),
545
- `${cyan(S_BAR)} ${dim("/")} ${hint}`,
555
+ `${info(S_BAR)} ${dim("/")} ${hint}`,
546
556
  buildList(
547
557
  filtered,
548
558
  cursor,
@@ -554,7 +564,7 @@ ${symbol(this.state)} ${message}
554
564
  optional,
555
565
  excludedSet
556
566
  ),
557
- `${cyan(S_BAR_END)}`,
567
+ `${info(S_BAR_END)}`,
558
568
  ""
559
569
  ].join("\n");
560
570
  }
@@ -627,30 +637,22 @@ ${symbol(this.state)} ${message}
627
637
  // src/utils/locale-search.ts
628
638
  import { isCancel as isCancel3, Prompt as Prompt2 } from "@clack/core";
629
639
  import * as p2 from "@clack/prompts";
630
- import chalk3 from "chalk";
631
640
  var S_BAR2 = "\u2502";
632
641
  var S_BAR_END2 = "\u2514";
633
642
  var S_ACTIVE2 = "\u25C6";
634
643
  var S_SUBMIT2 = "\u25C6";
635
644
  var S_CANCEL2 = "\u25A0";
636
645
  var S_ERROR2 = "\u25B2";
637
- var noColor2 = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
638
- var dim2 = (s) => noColor2 ? s : chalk3.gray(s);
639
- var cyan2 = (s) => noColor2 ? s : chalk3.cyan(s);
640
- var grn2 = (s) => noColor2 ? s : chalk3.green(s);
641
- var ylw2 = (s) => noColor2 ? s : chalk3.yellow(s);
642
- var red2 = (s) => noColor2 ? s : chalk3.red(s);
643
- var bld2 = (s) => noColor2 ? s : chalk3.bold(s);
644
646
  function symbol2(state) {
645
647
  switch (state) {
646
648
  case "submit":
647
- return grn2(S_SUBMIT2);
649
+ return grn(S_SUBMIT2);
648
650
  case "cancel":
649
- return red2(S_CANCEL2);
651
+ return red(S_CANCEL2);
650
652
  case "error":
651
- return ylw2(S_ERROR2);
653
+ return ylw(S_ERROR2);
652
654
  default:
653
- return cyan2(S_ACTIVE2);
655
+ return active(S_ACTIVE2);
654
656
  }
655
657
  }
656
658
  var MAX_VISIBLE2 = 12;
@@ -669,18 +671,18 @@ function buildList2(filtered, cursor, scrollOffset, selected) {
669
671
  const opt = filtered[i];
670
672
  const isCursor = i === cursor;
671
673
  const isChecked = isMulti && selected.has(opt.bcp47);
672
- const icon = isMulti ? isChecked ? isCursor ? grn2("\u25FC") : "\u25FC" : isCursor ? grn2("\u25FB") : dim2("\u25FB") : isCursor ? grn2("\u25CF") : dim2("\u25CB");
674
+ const icon = isMulti ? isChecked ? isCursor ? info("\u25FC") : info("\u25FC") : isCursor ? active("\u25FB") : dim("\u25FB") : isCursor ? active("\u25CF") : dim("\u25CB");
673
675
  visibleLines.push(
674
- `${cyan2(S_BAR2)} ${icon} ${isCursor ? bld2(opt.label) : opt.label}`
676
+ `${info(S_BAR2)} ${icon} ${isCursor ? bld(opt.label) : opt.label}`
675
677
  );
676
678
  }
677
679
  const hidden = filtered.length - (end - scrollOffset);
678
680
  if (hidden > 0)
679
- visibleLines.push(dim2(`${S_BAR2} ${hidden} more \u2014 keep typing to narrow`));
680
- if (filtered.length === 0) visibleLines.push(dim2(`${S_BAR2} No matches`));
681
+ visibleLines.push(dim(`${S_BAR2} ${hidden} more \u2014 keep typing to narrow`));
682
+ if (filtered.length === 0) visibleLines.push(dim(`${S_BAR2} No matches`));
681
683
  if (isMulti && selected.size > 0) {
682
684
  visibleLines.push(
683
- dim2(`${S_BAR2} ${selected.size} selected \u2014 Enter to confirm`)
685
+ dim(`${S_BAR2} ${selected.size} selected \u2014 Enter to confirm`)
684
686
  );
685
687
  }
686
688
  return visibleLines.join("\n");
@@ -716,43 +718,43 @@ async function runFilterablePrompt(opts) {
716
718
  render() {
717
719
  const filtered = getFiltered();
718
720
  clampCursor(filtered);
719
- const hdr = `${dim2(S_BAR2)}
721
+ const hdr = `${dim(S_BAR2)}
720
722
  ${symbol2(this.state)} ${message}
721
723
  `;
722
- const hint = filter.length > 0 ? filter : dim2(
724
+ const hint = filter.length > 0 ? filter : dim(
723
725
  `type to filter, \u2191\u2193 navigate${multi ? ", space select" : ""}`
724
726
  );
725
727
  switch (this.state) {
726
728
  case "submit": {
727
729
  const val = multi ? Array.from(selected).map((id) => options.find((o) => o.bcp47 === id)?.label ?? id).join(", ") : options.find((o) => o.bcp47 === this.value)?.label ?? "";
728
- return `${hdr}${dim2(S_BAR2)} ${bld2(val || dim2("none"))}`;
730
+ return `${hdr}${dim(S_BAR2)} ${bld(val || dim("none"))}`;
729
731
  }
730
732
  case "cancel":
731
- return `${hdr}${dim2(S_BAR2)}`;
733
+ return `${hdr}${dim(S_BAR2)}`;
732
734
  case "error":
733
735
  return [
734
736
  hdr.trimEnd(),
735
- `${ylw2(S_BAR2)} ${dim2("/")} ${hint}`,
737
+ `${ylw(S_BAR2)} ${dim("/")} ${hint}`,
736
738
  buildList2(
737
739
  filtered,
738
740
  cursor,
739
741
  scrollOffset,
740
742
  multi ? selected : null
741
743
  ),
742
- `${ylw2(S_BAR_END2)} ${ylw2(this.error)}`,
744
+ `${ylw(S_BAR_END2)} ${ylw(this.error)}`,
743
745
  ""
744
746
  ].join("\n");
745
747
  default:
746
748
  return [
747
749
  hdr.trimEnd(),
748
- `${cyan2(S_BAR2)} ${dim2("/")} ${hint}`,
750
+ `${info(S_BAR2)} ${dim("/")} ${hint}`,
749
751
  buildList2(
750
752
  filtered,
751
753
  cursor,
752
754
  scrollOffset,
753
755
  multi ? selected : null
754
756
  ),
755
- `${cyan2(S_BAR_END2)}`,
757
+ `${info(S_BAR_END2)}`,
756
758
  ""
757
759
  ].join("\n");
758
760
  }
@@ -861,7 +863,7 @@ function buildLanguageOptions(locales) {
861
863
  async function runProjectCreate(params) {
862
864
  const { api, userToken, organizationId, repoCanonical } = params;
863
865
  const projectName = (params.defaultName ?? "my-project").trim();
864
- p3.log.success(`Project: ${chalk4.bold(projectName)}`);
866
+ p3.log.success(`Project: ${chalk3.bold(projectName)}`);
865
867
  let sourceLocales;
866
868
  try {
867
869
  ({ sourceLocales } = await api.listLocales(userToken));
@@ -874,7 +876,7 @@ async function runProjectCreate(params) {
874
876
  const languageOptions = buildLanguageOptions(sourceLocales);
875
877
  const appDir = params.defaultAppDir ?? "";
876
878
  if (appDir) {
877
- p3.log.success(`App directory: ${chalk4.bold(appDir)}`);
879
+ p3.log.success(`App directory: ${chalk3.bold(appDir)}`);
878
880
  }
879
881
  const sourceLocale = await searchSelectLocale(
880
882
  languageOptions,
@@ -939,7 +941,7 @@ async function runProjectCreate(params) {
939
941
  appDirs: appDir ? [appDir] : [],
940
942
  repoCanonical
941
943
  });
942
- p3.log.success(`Project ${chalk4.bold(result.projectName)} created!`);
944
+ p3.log.success(`Project ${chalk3.bold(result.projectName)} created!`);
943
945
  return result;
944
946
  } catch (error) {
945
947
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -966,7 +968,7 @@ async function runAppCreate(params) {
966
968
  return null;
967
969
  }
968
970
  if (appDir) {
969
- p3.log.success(`App directory: ${chalk4.bold(appDir)}`);
971
+ p3.log.success(`App directory: ${chalk3.bold(appDir)}`);
970
972
  }
971
973
  const sourceLocale = await searchSelectLocale(
972
974
  languageOptions,
@@ -1018,7 +1020,7 @@ async function runAppCreate(params) {
1018
1020
  }
1019
1021
  const targetBranches = appPushBranches;
1020
1022
  try {
1021
- const result = await api.createApp(userToken, {
1023
+ const result = await api.createProject(userToken, {
1022
1024
  projectId,
1023
1025
  appDir,
1024
1026
  sourceLocale,
@@ -1027,7 +1029,7 @@ async function runAppCreate(params) {
1027
1029
  repoCanonical: repoCanonical ?? ""
1028
1030
  });
1029
1031
  p3.log.success(
1030
- `App ${chalk4.bold(appDir)} added to ${chalk4.bold(projectName)}!`
1032
+ `App ${chalk3.bold(appDir)} added to ${chalk3.bold(projectName)}!`
1031
1033
  );
1032
1034
  return {
1033
1035
  projectId: result.projectId,
@@ -1046,7 +1048,7 @@ async function runAppCreate(params) {
1046
1048
  }
1047
1049
 
1048
1050
  // src/commands/init.ts
1049
- import chalk6 from "chalk";
1051
+ import chalk5 from "chalk";
1050
1052
  import { join as join2 } from "path";
1051
1053
  import { config as loadEnv } from "dotenv";
1052
1054
 
@@ -1160,7 +1162,7 @@ function resolveGitContext() {
1160
1162
 
1161
1163
  // src/utils/workspace.ts
1162
1164
  import * as p4 from "@clack/prompts";
1163
- import chalk5 from "chalk";
1165
+ import chalk4 from "chalk";
1164
1166
  async function selectWorkspace(result) {
1165
1167
  const { workspaces, canCreateWorkspace } = result;
1166
1168
  if (workspaces.length === 0) {
@@ -1264,7 +1266,7 @@ function runScaffold(params) {
1264
1266
  if (detection.ecosystem) {
1265
1267
  const frameworkLabel = detection.framework ?? detection.ecosystem;
1266
1268
  const pmLabel = detection.packageManager;
1267
- p5.log.info(`Detected: ${chalk6.bold(frameworkLabel)} (${pmLabel})`);
1269
+ p5.log.info(`Detected: ${chalk5.bold(frameworkLabel)} (${pmLabel})`);
1268
1270
  }
1269
1271
  const { devPackages, runtimePackages } = getPackagesToInstall(detection);
1270
1272
  const allPackages = [...devPackages, ...runtimePackages];
@@ -1292,10 +1294,10 @@ function runScaffold(params) {
1292
1294
  devPackages.length > 0 ? buildInstallCommand(detection.packageManager, devPackages, true) : null,
1293
1295
  runtimePackages.length > 0 ? buildInstallCommand(detection.packageManager, runtimePackages, false) : null
1294
1296
  ].filter(Boolean).join(" && ");
1295
- p5.log.warn(`Run manually: ${chalk6.cyan(cmds)}`);
1297
+ p5.log.warn(`Run manually: ${highlight(cmds)}`);
1296
1298
  }
1297
1299
  } else if (detection.ecosystem) {
1298
- p5.log.info(`Packages: ${chalk6.green("already installed")}`);
1300
+ p5.log.info(`Packages: ${chalk5.green("already installed")}`);
1299
1301
  }
1300
1302
  const snippets = getSetupSnippets({
1301
1303
  framework: detection.framework,
@@ -1325,19 +1327,19 @@ function runScaffold(params) {
1325
1327
  code: snippets.wrapStep.code
1326
1328
  });
1327
1329
  p5.log.message("");
1328
- p5.log.message(chalk6.bold("Finish setup in your code"));
1330
+ p5.log.message(chalk5.bold("Finish setup in your code"));
1329
1331
  p5.log.message("");
1330
1332
  for (let i = 0; i < steps.length; i++) {
1331
1333
  const step = steps[i];
1332
1334
  p5.log.step(
1333
- `${chalk6.bold(step.label)} ${chalk6.dim(`\u2014 ${step.hint}`)}`
1335
+ `${chalk5.bold(step.label)} ${chalk5.dim(`\u2014 ${step.hint}`)}`
1334
1336
  );
1335
1337
  printCodeBlock(step.code);
1336
1338
  if (i < steps.length - 1) p5.log.message("");
1337
1339
  }
1338
1340
  const written = writeVocoderConfig({ targetBranches, useTypeScript });
1339
1341
  if (written) {
1340
- p5.log.success(`Created ${chalk6.cyan(written)}`);
1342
+ p5.log.success(`Created ${highlight(written)}`);
1341
1343
  } else if (!findExistingConfig(process.cwd())) {
1342
1344
  const ext = useTypeScript ? "ts" : "js";
1343
1345
  p5.log.warn(
@@ -1345,11 +1347,11 @@ function runScaffold(params) {
1345
1347
  );
1346
1348
  }
1347
1349
  p5.log.message("");
1348
- const branchList = targetBranches.length > 0 ? targetBranches.map((b) => chalk6.cyan(b)).join(" or ") : chalk6.cyan("your target branch");
1350
+ const branchList = targetBranches.length > 0 ? targetBranches.map((b) => highlight(b)).join(" or ") : highlight("your target branch");
1349
1351
  p5.log.success(
1350
1352
  `Push to ${branchList} to trigger your first translation run.`
1351
1353
  );
1352
- p5.log.message(chalk6.gray(" Docs: https://vocoder.app/docs/getting-started"));
1354
+ p5.log.message(info(" Docs: https://vocoder.app/docs/getting-started"));
1353
1355
  }
1354
1356
  function writeApiKeyToEnv(apiKey) {
1355
1357
  const envPath = join2(process.cwd(), ".env");
@@ -1374,12 +1376,12 @@ function writeApiKeyToEnv(apiKey) {
1374
1376
  function printApiKey(apiKey) {
1375
1377
  const saved = writeApiKeyToEnv(apiKey);
1376
1378
  p5.log.message("");
1377
- p5.log.message(chalk6.bold("Your API Key"));
1379
+ p5.log.message(chalk5.bold("Your API Key"));
1378
1380
  printCodeBlock(`VOCODER_API_KEY=${apiKey}`);
1379
1381
  if (saved) {
1380
- p5.log.success(chalk6.dim("Saved to .env"));
1382
+ p5.log.success(chalk5.dim("Saved to .env"));
1381
1383
  } else {
1382
- p5.log.message(chalk6.dim(" Add the above to your .env file"));
1384
+ p5.log.message(chalk5.dim(" Add the above to your .env file"));
1383
1385
  }
1384
1386
  }
1385
1387
  function printCodeBlock(code) {
@@ -1388,20 +1390,20 @@ function printCodeBlock(code) {
1388
1390
  (max, line) => Math.max(max, line.length),
1389
1391
  0
1390
1392
  );
1391
- const bar = chalk6.gray("\u2502");
1393
+ const bar = chalk5.gray("\u2502");
1392
1394
  const pad = (s) => s + " ".repeat(maxLen - s.length);
1393
- process.stdout.write(`${chalk6.gray("\u2502")}
1395
+ process.stdout.write(`${chalk5.gray("\u2502")}
1394
1396
  `);
1395
1397
  process.stdout.write(
1396
- `${chalk6.gray("\u2502")} ${chalk6.gray(`\u250C${"\u2500".repeat(maxLen + 2)}\u2510`)}
1398
+ `${chalk5.gray("\u2502")} ${chalk5.gray(`\u250C${"\u2500".repeat(maxLen + 2)}\u2510`)}
1397
1399
  `
1398
1400
  );
1399
1401
  for (const line of lines) {
1400
- process.stdout.write(`${chalk6.gray("\u2502")} ${bar} ${pad(line)} ${bar}
1402
+ process.stdout.write(`${chalk5.gray("\u2502")} ${bar} ${pad(line)} ${bar}
1401
1403
  `);
1402
1404
  }
1403
1405
  process.stdout.write(
1404
- `${chalk6.gray("\u2502")} ${chalk6.gray(`\u2514${"\u2500".repeat(maxLen + 2)}\u2518`)}
1406
+ `${chalk5.gray("\u2502")} ${chalk5.gray(`\u2514${"\u2500".repeat(maxLen + 2)}\u2518`)}
1405
1407
  `
1406
1408
  );
1407
1409
  }
@@ -1540,7 +1542,7 @@ async function runAuthFlow(api, options, reauth = false, repoCanonical) {
1540
1542
  return null;
1541
1543
  }
1542
1544
  const userInfo = await api.getCliUserInfo(rawToken);
1543
- authSpinner.stop(`Authenticated as ${chalk6.bold(userInfo.email)}`);
1545
+ authSpinner.stop(`Authenticated as ${chalk5.bold(userInfo.email)}`);
1544
1546
  return {
1545
1547
  token: rawToken,
1546
1548
  ...userInfo,
@@ -1550,7 +1552,7 @@ async function runAuthFlow(api, options, reauth = false, repoCanonical) {
1550
1552
  }
1551
1553
  async function init(options = {}) {
1552
1554
  const apiUrl = options.apiUrl || process.env.VOCODER_API_URL || "https://vocoder.app";
1553
- p5.intro(chalk6.bold("Vocoder Setup"));
1555
+ p5.intro(chalk5.bold("Vocoder Setup"));
1554
1556
  try {
1555
1557
  const gitContext = resolveGitContext();
1556
1558
  const identity = gitContext.identity;
@@ -1564,15 +1566,15 @@ async function init(options = {}) {
1564
1566
  let repoProjectName = null;
1565
1567
  if (identity) {
1566
1568
  const anonApi = new VocoderAPI({ apiUrl, apiKey: "" });
1567
- const lookup = await anonApi.lookupProjectByRepo({
1569
+ const lookup = await anonApi.lookupAppByRepo({
1568
1570
  repoCanonical: identity.repoCanonical,
1569
1571
  appDir: identity.repoAppDir
1570
1572
  });
1571
1573
  if (lookup.exactMatch) {
1572
1574
  const { exactMatch } = lookup;
1573
- p5.log.success(`Project: ${chalk6.bold(exactMatch.projectName)}`);
1575
+ p5.log.success(`Project: ${chalk5.bold(exactMatch.projectName)}`);
1574
1576
  p5.log.info(
1575
- `Branches: ${chalk6.cyan((exactMatch.targetBranches ?? ["main"]).join(", "))}`
1577
+ `Branches: ${highlight((exactMatch.targetBranches ?? ["main"]).join(", "))}`
1576
1578
  );
1577
1579
  const needsKey = await p5.confirm({
1578
1580
  message: "Need to regenerate your API key?"
@@ -1605,17 +1607,17 @@ async function init(options = {}) {
1605
1607
  }
1606
1608
  const isTs = detectLocalEcosystem().isTypeScript;
1607
1609
  const written = writeVocoderConfig({ targetBranches: exactMatch.targetBranches ?? ["main"], useTypeScript: isTs });
1608
- if (written) p5.log.success(`Created ${chalk6.cyan(written)}`);
1610
+ if (written) p5.log.success(`Created ${highlight(written)}`);
1609
1611
  p5.outro("Vocoder is already set up for this repository.");
1610
1612
  return 0;
1611
1613
  }
1612
1614
  if (lookup.hasWholeRepoApp) {
1613
1615
  const wholeRepo = lookup.existingApps.find((a) => a.appDir === "");
1614
1616
  if (wholeRepo) {
1615
- p5.log.success(`Project: ${chalk6.bold(wholeRepo.projectName)}`);
1617
+ p5.log.success(`Project: ${chalk5.bold(wholeRepo.projectName)}`);
1616
1618
  const isTs = detectLocalEcosystem().isTypeScript;
1617
1619
  const written = writeVocoderConfig({ targetBranches: ["main"], useTypeScript: isTs });
1618
- if (written) p5.log.success(`Created ${chalk6.cyan(written)}`);
1620
+ if (written) p5.log.success(`Created ${highlight(written)}`);
1619
1621
  p5.outro("Vocoder is already set up for this repository.");
1620
1622
  return 0;
1621
1623
  }
@@ -1633,7 +1635,7 @@ async function init(options = {}) {
1633
1635
  let authOrganizationId;
1634
1636
  const storedAuth = await verifyStoredAuth(api);
1635
1637
  if (storedAuth.status === "valid") {
1636
- p5.log.success(`Authenticated as ${chalk6.bold(storedAuth.email)}`);
1638
+ p5.log.success(`Authenticated as ${chalk5.bold(storedAuth.email)}`);
1637
1639
  userToken = storedAuth.token;
1638
1640
  userEmail = storedAuth.email;
1639
1641
  userName = storedAuth.name;
@@ -1673,7 +1675,7 @@ async function init(options = {}) {
1673
1675
  selectedWorkspaceId = authOrganizationId;
1674
1676
  selectedWorkspaceName = ws?.name ?? userEmail;
1675
1677
  p5.log.success(
1676
- `Connected as ${chalk6.bold(userEmail)} \u2014 workspace: ${chalk6.bold(selectedWorkspaceName)}`
1678
+ `Connected as ${chalk5.bold(userEmail)} \u2014 workspace: ${chalk5.bold(selectedWorkspaceName)}`
1677
1679
  );
1678
1680
  } else {
1679
1681
  const discoveryResult = await api.getCliGitHubDiscovery(userToken).catch(() => null);
@@ -1724,7 +1726,7 @@ async function init(options = {}) {
1724
1726
  });
1725
1727
  selectedWorkspaceId = claimResult.organizationId;
1726
1728
  selectedWorkspaceName = claimResult.organizationName;
1727
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1729
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1728
1730
  } else {
1729
1731
  const workspaceData = await api.listWorkspaces(userToken, {
1730
1732
  repo: identity?.repoCanonical
@@ -1738,13 +1740,13 @@ async function init(options = {}) {
1738
1740
  const ws = covering[0];
1739
1741
  selectedWorkspaceId = ws.id;
1740
1742
  selectedWorkspaceName = ws.name;
1741
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1743
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1742
1744
  } else if (repoCanonical && covering.length > 1) {
1743
1745
  const choice = await p5.select({
1744
1746
  message: "Select workspace for this repo",
1745
1747
  options: covering.map((w) => ({
1746
1748
  value: w.id,
1747
- label: `${w.name} ${chalk6.dim(`(${w.projectCount} project${w.projectCount !== 1 ? "s" : ""})`)}`
1749
+ label: `${w.name} ${chalk5.dim(`(${w.projectCount} project${w.projectCount !== 1 ? "s" : ""})`)}`
1748
1750
  }))
1749
1751
  });
1750
1752
  if (p5.isCancel(choice)) {
@@ -1754,11 +1756,11 @@ async function init(options = {}) {
1754
1756
  const ws = covering.find((w) => w.id === choice);
1755
1757
  selectedWorkspaceId = ws.id;
1756
1758
  selectedWorkspaceName = ws.name;
1757
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1759
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1758
1760
  } else if (repoCanonical && covering.length === 0 && connected.length > 0) {
1759
1761
  const shortRepo = repoCanonical.split(":")[1] ?? repoCanonical;
1760
1762
  p5.log.warn(
1761
- `${chalk6.bold(shortRepo)} isn't accessible from your Vocoder installation.
1763
+ `${chalk5.bold(shortRepo)} isn't accessible from your Vocoder installation.
1762
1764
  Grant access to this repository or install on the account that owns it.`
1763
1765
  );
1764
1766
  const fixOptions = [];
@@ -1766,13 +1768,13 @@ async function init(options = {}) {
1766
1768
  if (ws.installationConfigureUrl) {
1767
1769
  fixOptions.push({
1768
1770
  value: `grant:${ws.id}`,
1769
- label: `Configure ${chalk6.bold(ws.connectionLabel ?? ws.name)}'s GitHub App installation`
1771
+ label: `Configure ${chalk5.bold(ws.connectionLabel ?? ws.name)}'s GitHub App installation`
1770
1772
  });
1771
1773
  }
1772
1774
  }
1773
1775
  fixOptions.push({
1774
1776
  value: "install_new",
1775
- label: `Install on a different GitHub account ${chalk6.dim("(creates a new personal workspace)")}`
1777
+ label: `Install on a different GitHub account ${chalk5.dim("(creates a new personal workspace)")}`
1776
1778
  });
1777
1779
  fixOptions.push({ value: "cancel", label: "Cancel" });
1778
1780
  const fix = await p5.select({
@@ -1787,8 +1789,8 @@ async function init(options = {}) {
1787
1789
  const ws = connected.find((w) => `grant:${w.id}` === fix);
1788
1790
  await tryOpenBrowser2(ws.installationConfigureUrl);
1789
1791
  p5.cancel(
1790
- `Grant access to ${chalk6.bold(shortRepo)} in your browser,
1791
- then re-run ${chalk6.bold("vocoder init")}.`
1792
+ `Grant access to ${chalk5.bold(shortRepo)} in your browser,
1793
+ then re-run ${chalk5.bold("vocoder init")}.`
1792
1794
  );
1793
1795
  return 1;
1794
1796
  }
@@ -1805,13 +1807,13 @@ async function init(options = {}) {
1805
1807
  }
1806
1808
  selectedWorkspaceId = connectResult.organizationId;
1807
1809
  selectedWorkspaceName = connectResult.organizationName;
1808
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1810
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1809
1811
  } else {
1810
1812
  if (workspaceData.workspaces.length === 1 && !workspaceData.canCreateWorkspace) {
1811
1813
  const ws = workspaceData.workspaces[0];
1812
1814
  selectedWorkspaceId = ws.id;
1813
1815
  selectedWorkspaceName = ws.name;
1814
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1816
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1815
1817
  } else {
1816
1818
  const workspaceResult = await selectWorkspace(workspaceData);
1817
1819
  if (workspaceResult.action === "cancelled") {
@@ -1821,7 +1823,7 @@ async function init(options = {}) {
1821
1823
  if (workspaceResult.action === "use") {
1822
1824
  selectedWorkspaceId = workspaceResult.workspace.id;
1823
1825
  selectedWorkspaceName = workspaceResult.workspace.name;
1824
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1826
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1825
1827
  } else {
1826
1828
  const connectChoice = await p5.select({
1827
1829
  message: "Connect your new workspace to GitHub",
@@ -1849,7 +1851,7 @@ async function init(options = {}) {
1849
1851
  selectedWorkspaceId = connectResult.organizationId;
1850
1852
  selectedWorkspaceName = connectResult.organizationName;
1851
1853
  p5.log.success(
1852
- `Workspace: ${chalk6.bold(selectedWorkspaceName)}`
1854
+ `Workspace: ${chalk5.bold(selectedWorkspaceName)}`
1853
1855
  );
1854
1856
  } else {
1855
1857
  const installations = await runGitHubDiscoveryFlow({
@@ -1911,7 +1913,7 @@ async function init(options = {}) {
1911
1913
  }
1912
1914
  }
1913
1915
  p5.log.success(
1914
- `Workspace: ${chalk6.bold(selectedWorkspaceName)}`
1916
+ `Workspace: ${chalk5.bold(selectedWorkspaceName)}`
1915
1917
  );
1916
1918
  }
1917
1919
  }
@@ -1921,8 +1923,8 @@ async function init(options = {}) {
1921
1923
  }
1922
1924
  if (repoProjectId && repoProjectName && existingAppsForRepo.length > 0) {
1923
1925
  p5.log.info(
1924
- `${chalk6.bold(repoProjectName)} is already set up for this repo.
1925
- Configured apps: ${existingAppsForRepo.map((a) => chalk6.cyan(a.appDir || "(entire repo)")).join(", ")}`
1926
+ `${chalk5.bold(repoProjectName)} is already set up for this repo.
1927
+ Configured apps: ${existingAppsForRepo.map((a) => highlight(a.appDir || "(entire repo)")).join(", ")}`
1926
1928
  );
1927
1929
  const appResult = await runAppCreate({
1928
1930
  api,
@@ -1951,13 +1953,13 @@ async function init(options = {}) {
1951
1953
  const ws = wsCheck.workspaces.find((w) => w.id === selectedWorkspaceId);
1952
1954
  if (ws && ws.maxProjects !== -1 && ws.projectCount >= ws.maxProjects) {
1953
1955
  p5.log.warn(
1954
- `Project limit reached \u2014 ${ws.projectCount}/${ws.maxProjects} on your ${chalk6.bold(ws.planId)} plan.`
1956
+ `Project limit reached \u2014 ${ws.projectCount}/${ws.maxProjects} on your ${chalk5.bold(ws.planId)} plan.`
1955
1957
  );
1956
1958
  const options2 = [];
1957
1959
  if (repoProjectId) {
1958
1960
  options2.push({
1959
1961
  value: "connect",
1960
- label: `Reconnect this repo to ${chalk6.bold(repoProjectName ?? "existing project")}`
1962
+ label: `Reconnect this repo to ${chalk5.bold(repoProjectName ?? "existing project")}`
1961
1963
  });
1962
1964
  }
1963
1965
  options2.push({ value: "upgrade", label: "Upgrade plan" });
@@ -1977,7 +1979,7 @@ async function init(options = {}) {
1977
1979
  );
1978
1980
  return 1;
1979
1981
  }
1980
- const existingProjects = await api.listProjects(
1982
+ const existingProjects = await api.listApps(
1981
1983
  userToken,
1982
1984
  selectedWorkspaceId
1983
1985
  );
@@ -1997,7 +1999,7 @@ async function init(options = {}) {
1997
1999
  targetBranches: chosenProject.targetBranches,
1998
2000
  repoCanonical: identity?.repoCanonical ?? ""
1999
2001
  });
2000
- p5.log.success(`Connected to project: ${chalk6.bold(chosenProject.name)}`);
2002
+ p5.log.success(`Connected to project: ${chalk5.bold(chosenProject.name)}`);
2001
2003
  printApiKey(appResult.apiKey);
2002
2004
  runScaffold({
2003
2005
  sourceLocale: chosenProject.sourceLocale,
@@ -2036,7 +2038,7 @@ Translations won't run automatically until you grant access.
2036
2038
  To fix: go to your GitHub App installation settings and add this
2037
2039
  repository to the allowed list, or switch to "All repositories".
2038
2040
  ` + (projectResult.configureUrl ? `
2039
- ${chalk6.dim(projectResult.configureUrl)}
2041
+ ${chalk5.dim(projectResult.configureUrl)}
2040
2042
  ` : "")
2041
2043
  );
2042
2044
  }
@@ -2064,7 +2066,7 @@ Translations won't run automatically until you grant access.
2064
2066
 
2065
2067
  // src/commands/locales.ts
2066
2068
  import * as p8 from "@clack/prompts";
2067
- import chalk9 from "chalk";
2069
+ import chalk7 from "chalk";
2068
2070
  import { config as loadEnv3 } from "dotenv";
2069
2071
 
2070
2072
  // src/commands/sync.ts
@@ -2072,7 +2074,7 @@ import { createHash, randomUUID } from "crypto";
2072
2074
  import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
2073
2075
  import { join as join3 } from "path";
2074
2076
  import * as p7 from "@clack/prompts";
2075
- import chalk8 from "chalk";
2077
+ import chalk6 from "chalk";
2076
2078
 
2077
2079
  // src/utils/branch.ts
2078
2080
  import { execSync as execSync4 } from "child_process";
@@ -2142,7 +2144,6 @@ function matchBranchPattern(branch, pattern) {
2142
2144
 
2143
2145
  // src/utils/config.ts
2144
2146
  import * as p6 from "@clack/prompts";
2145
- import chalk7 from "chalk";
2146
2147
  import { config as loadEnv2 } from "dotenv";
2147
2148
  loadEnv2();
2148
2149
  function validateLocalConfig(config) {
@@ -2201,7 +2202,7 @@ async function getMergedConfig(cliOptions, verbose = false, _startDir) {
2201
2202
  const fileConfig = loadVocoderConfig(process.cwd());
2202
2203
  if (!fileConfig) {
2203
2204
  p6.log.warn(
2204
- `No ${chalk7.cyan("vocoder.config.ts")} found \u2014 run ${chalk7.cyan("npx @vocoder/cli init")} to generate one.`
2205
+ `No ${highlight("vocoder.config.ts")} found \u2014 run ${highlight("npx @vocoder/cli init")} to generate one.`
2205
2206
  );
2206
2207
  }
2207
2208
  const envExtractionPattern = process.env.VOCODER_INCLUDE_PATTERN;
@@ -2280,13 +2281,13 @@ async function getMergedConfig(cliOptions, verbose = false, _startDir) {
2280
2281
  }
2281
2282
  if (verbose) {
2282
2283
  const lines = [
2283
- `Include patterns: ${chalk7.cyan(configSources.includePattern)}`,
2284
- ...excludePattern.length > 0 ? [`Exclude patterns: ${chalk7.cyan(configSources.excludePattern)}`] : [],
2285
- `API key: ${chalk7.cyan(configSources.apiKey)}`,
2286
- `API URL: ${chalk7.cyan(configSources.apiUrl)}`,
2287
- `Sync mode: ${chalk7.cyan(configSources.mode)}`,
2288
- ...maxWaitMs ? [`Max wait: ${chalk7.cyan(String(configSources.maxWaitMs))}`] : [],
2289
- `No fallback: ${chalk7.cyan(String(configSources.noFallback))}`
2284
+ `Include patterns: ${highlight(configSources.includePattern)}`,
2285
+ ...excludePattern.length > 0 ? [`Exclude patterns: ${highlight(configSources.excludePattern)}`] : [],
2286
+ `API key: ${highlight(configSources.apiKey)}`,
2287
+ `API URL: ${highlight(configSources.apiUrl)}`,
2288
+ `Sync mode: ${highlight(configSources.mode)}`,
2289
+ ...maxWaitMs ? [`Max wait: ${highlight(String(configSources.maxWaitMs))}`] : [],
2290
+ `No fallback: ${highlight(String(configSources.noFallback))}`
2290
2291
  ];
2291
2292
  p6.note(lines.join("\n"), "Configuration sources");
2292
2293
  }
@@ -2579,7 +2580,7 @@ async function sync(options = {}) {
2579
2580
  };
2580
2581
  validateLocalConfig(localConfig);
2581
2582
  const api = new VocoderAPI(localConfig);
2582
- const apiConfig = await api.getProjectConfig();
2583
+ const apiConfig = await api.getAppConfig();
2583
2584
  const requestedMode = mergedConfig.mode;
2584
2585
  const waitTimeoutMs = resolveWaitTimeoutMs({
2585
2586
  requestedMaxWaitMs: mergedConfig.maxWaitMs,
@@ -2596,10 +2597,10 @@ async function sync(options = {}) {
2596
2597
  ...fileConfig?.appIndustry ? { appIndustry: fileConfig.appIndustry } : {},
2597
2598
  ...fileConfig?.formality ? { formality: fileConfig.formality } : {}
2598
2599
  };
2599
- spinner7.stop(`Branch: ${chalk8.cyan(branch)}`);
2600
+ spinner7.stop(`Branch: ${highlight(branch)}`);
2600
2601
  if (!options.force && !isTargetBranch(branch, config.targetBranches)) {
2601
2602
  p7.log.warn(
2602
- `Skipping translations (${chalk8.cyan(branch)} is not a target branch)`
2603
+ `Skipping translations (${highlight(branch)} is not a target branch)`
2603
2604
  );
2604
2605
  p7.log.info(`Target branches: ${config.targetBranches.join(", ")}`);
2605
2606
  p7.log.info("Use --force to translate anyway");
@@ -2623,7 +2624,7 @@ async function sync(options = {}) {
2623
2624
  return 0;
2624
2625
  }
2625
2626
  spinner7.stop(
2626
- `Extracted ${chalk8.cyan(extractedStrings.length)} strings from ${chalk8.cyan(patternsDisplay)}`
2627
+ `Extracted ${highlight(extractedStrings.length)} strings from ${highlight(patternsDisplay)}`
2627
2628
  );
2628
2629
  if (options.verbose) {
2629
2630
  const sampleLines = extractedStrings.slice(0, 5).map((s) => ` "${s.text}" (${s.file}:${s.line})`);
@@ -2666,14 +2667,14 @@ async function sync(options = {}) {
2666
2667
  const cacheFile = getCacheFilePath(projectRoot, fingerprint);
2667
2668
  if (existsSync3(cacheFile)) {
2668
2669
  if (options.verbose) {
2669
- p7.log.info(`Cache hit: ${chalk8.dim(cacheFile)} (fingerprint ${chalk8.cyan(fingerprint)})`);
2670
+ p7.log.info(`Cache hit: ${chalk6.dim(cacheFile)} (fingerprint ${highlight(fingerprint)})`);
2670
2671
  }
2671
2672
  const duration2 = ((Date.now() - startTime) / 1e3).toFixed(1);
2672
2673
  p7.outro(`Up to date (${duration2}s)`);
2673
2674
  return 0;
2674
2675
  }
2675
2676
  if (options.verbose) {
2676
- p7.log.info(`No cache for fingerprint ${chalk8.cyan(fingerprint)} \u2014 will submit to API`);
2677
+ p7.log.info(`No cache for fingerprint ${highlight(fingerprint)} \u2014 will submit to API`);
2677
2678
  }
2678
2679
  }
2679
2680
  spinner7.start("Submitting strings to Vocoder API");
@@ -2698,7 +2699,7 @@ async function sync(options = {}) {
2698
2699
  policy: config.syncPolicy
2699
2700
  });
2700
2701
  if (options.verbose) {
2701
- p7.log.info(`Batch: ${chalk8.dim(batchResponse.batchId)}`);
2702
+ p7.log.info(`Batch: ${chalk6.dim(batchResponse.batchId)}`);
2702
2703
  p7.log.info(`Requested mode: ${requestedMode}`);
2703
2704
  p7.log.info(`Effective mode: ${effectiveMode}`);
2704
2705
  p7.log.info(`Wait timeout: ${waitTimeoutMs}ms`);
@@ -2707,14 +2708,14 @@ async function sync(options = {}) {
2707
2708
  }
2708
2709
  }
2709
2710
  if (batchResponse.status === "UP_TO_DATE" && batchResponse.noChanges) {
2710
- p7.log.success(`Up to date \u2014 ${chalk8.cyan(batchResponse.totalStrings)} strings, no changes`);
2711
+ p7.log.success(`Up to date \u2014 ${highlight(batchResponse.totalStrings)} strings, no changes`);
2711
2712
  } else if (batchResponse.newStrings === 0) {
2712
- const archivedNote = batchResponse.deletedStrings && batchResponse.deletedStrings > 0 ? `, ${chalk8.yellow(batchResponse.deletedStrings)} archived` : "";
2713
- p7.log.success(`No new strings \u2014 ${chalk8.cyan(batchResponse.totalStrings)} total${archivedNote}, using existing translations`);
2713
+ const archivedNote = batchResponse.deletedStrings && batchResponse.deletedStrings > 0 ? `, ${chalk6.yellow(batchResponse.deletedStrings)} archived` : "";
2714
+ p7.log.success(`No new strings \u2014 ${highlight(batchResponse.totalStrings)} total${archivedNote}, using existing translations`);
2714
2715
  } else {
2715
- const statParts = [`${chalk8.cyan(batchResponse.newStrings)} new, ${chalk8.cyan(batchResponse.totalStrings)} total`];
2716
+ const statParts = [`${highlight(batchResponse.newStrings)} new, ${highlight(batchResponse.totalStrings)} total`];
2716
2717
  if (batchResponse.deletedStrings && batchResponse.deletedStrings > 0) {
2717
- statParts.push(`${chalk8.yellow(batchResponse.deletedStrings)} archived`);
2718
+ statParts.push(`${chalk6.yellow(batchResponse.deletedStrings)} archived`);
2718
2719
  }
2719
2720
  const estTime = batchResponse.estimatedTime ? ` (~${batchResponse.estimatedTime}s)` : "";
2720
2721
  p7.log.info(`${statParts.join(", ")} \u2192 syncing to ${config.targetLocales.join(", ")}${estTime}`);
@@ -2898,15 +2899,15 @@ async function listProjectLocales(options = {}) {
2898
2899
  if (!config) return 1;
2899
2900
  const api = new VocoderAPI(config);
2900
2901
  try {
2901
- const projectConfig2 = await api.getProjectConfig();
2902
+ const projectConfig = await api.getAppConfig();
2902
2903
  p8.log.info(
2903
- `Source locale: ${chalk9.cyan(projectConfig2.sourceLocale)}`
2904
+ `Source locale: ${highlight(projectConfig.sourceLocale)}`
2904
2905
  );
2905
- if (projectConfig2.targetLocales.length === 0) {
2906
+ if (projectConfig.targetLocales.length === 0) {
2906
2907
  p8.log.info("Target locales: (none configured)");
2907
2908
  } else {
2908
2909
  p8.log.info(
2909
- `Target locales: ${projectConfig2.targetLocales.map((l) => chalk9.cyan(l)).join(", ")}`
2910
+ `Target locales: ${projectConfig.targetLocales.map((l) => highlight(l)).join(", ")}`
2910
2911
  );
2911
2912
  }
2912
2913
  return 0;
@@ -2933,9 +2934,9 @@ async function addLocales(locales, options = {}) {
2933
2934
  try {
2934
2935
  const result = await api.addLocale(locale);
2935
2936
  lastTargetLocales = result.targetLocales;
2936
- spinner7.stop(`Added ${chalk9.cyan(locale)}`);
2937
+ spinner7.stop(`Added ${highlight(locale)}`);
2937
2938
  } catch (error) {
2938
- spinner7.stop(`Failed to add ${chalk9.red(locale)}`);
2939
+ spinner7.stop(`Failed to add ${chalk7.red(locale)}`);
2939
2940
  hadError = true;
2940
2941
  if (error instanceof VocoderAPIError && error.limitError) {
2941
2942
  const { limitError } = error;
@@ -2952,7 +2953,7 @@ async function addLocales(locales, options = {}) {
2952
2953
  }
2953
2954
  if (lastTargetLocales.length > 0) {
2954
2955
  p8.log.info(
2955
- `Target locales now: ${lastTargetLocales.map((l) => chalk9.cyan(l)).join(", ")}`
2956
+ `Target locales now: ${lastTargetLocales.map((l) => highlight(l)).join(", ")}`
2956
2957
  );
2957
2958
  }
2958
2959
  return hadError ? 1 : 0;
@@ -2973,9 +2974,9 @@ async function removeLocales(locales, options = {}) {
2973
2974
  try {
2974
2975
  const result = await api.removeLocale(locale);
2975
2976
  lastTargetLocales = result.targetLocales;
2976
- spinner7.stop(`Removed ${chalk9.cyan(locale)}`);
2977
+ spinner7.stop(`Removed ${highlight(locale)}`);
2977
2978
  } catch (error) {
2978
- spinner7.stop(`Failed to remove ${chalk9.red(locale)}`);
2979
+ spinner7.stop(`Failed to remove ${chalk7.red(locale)}`);
2979
2980
  hadError = true;
2980
2981
  p8.log.error(
2981
2982
  error instanceof Error ? error.message : "Unknown error"
@@ -2984,7 +2985,7 @@ async function removeLocales(locales, options = {}) {
2984
2985
  }
2985
2986
  if (lastTargetLocales.length > 0) {
2986
2987
  p8.log.info(
2987
- `Target locales now: ${lastTargetLocales.map((l) => chalk9.cyan(l)).join(", ")}`
2988
+ `Target locales now: ${lastTargetLocales.map((l) => highlight(l)).join(", ")}`
2988
2989
  );
2989
2990
  } else if (!hadError) {
2990
2991
  p8.log.info("Target locales now: (none configured)");
@@ -2997,10 +2998,10 @@ async function listSupportedLocales(options = {}) {
2997
2998
  const api = new VocoderAPI(config);
2998
2999
  try {
2999
3000
  const result = await api.listLocales(config.apiKey);
3000
- p8.log.info(chalk9.bold("Source locales:"));
3001
+ p8.log.info(chalk7.bold("Source locales:"));
3001
3002
  printLocaleTable(result.sourceLocales);
3002
3003
  p8.log.info("");
3003
- p8.log.info(chalk9.bold("Target locales:"));
3004
+ p8.log.info(chalk7.bold("Target locales:"));
3004
3005
  printLocaleTable(result.targetLocales);
3005
3006
  return 0;
3006
3007
  } catch (error) {
@@ -3013,7 +3014,7 @@ async function listSupportedLocales(options = {}) {
3013
3014
  function printLocaleTable(locales) {
3014
3015
  for (const locale of locales) {
3015
3016
  const native = locale.nativeName && locale.nativeName !== locale.name ? ` (${locale.nativeName})` : "";
3016
- p8.log.info(` ${chalk9.cyan(locale.code.padEnd(10))} ${locale.name}${native}`);
3017
+ p8.log.info(` ${highlight(locale.code.padEnd(10))} ${locale.name}${native}`);
3017
3018
  }
3018
3019
  }
3019
3020
 
@@ -3036,12 +3037,12 @@ async function logout(options = {}) {
3036
3037
  return 0;
3037
3038
  }
3038
3039
 
3039
- // src/commands/project-config.ts
3040
+ // src/commands/app-config.ts
3040
3041
  import * as p10 from "@clack/prompts";
3041
- import chalk10 from "chalk";
3042
+ import chalk8 from "chalk";
3042
3043
  import { config as loadEnv4 } from "dotenv";
3043
3044
  loadEnv4();
3044
- async function projectConfig(options = {}) {
3045
+ async function appConfig(options = {}) {
3045
3046
  const apiKey = process.env.VOCODER_API_KEY;
3046
3047
  if (!apiKey) {
3047
3048
  p10.log.error(
@@ -3052,21 +3053,21 @@ async function projectConfig(options = {}) {
3052
3053
  const apiUrl = options.apiUrl ?? process.env.VOCODER_API_URL ?? "https://vocoder.app";
3053
3054
  const api = new VocoderAPI({ apiKey, apiUrl });
3054
3055
  try {
3055
- const config = await api.getProjectConfig();
3056
+ const config = await api.getAppConfig();
3056
3057
  const lines = [
3057
- `Project: ${chalk10.bold(config.projectName)}`,
3058
+ `App: ${chalk8.bold(config.projectName)}`,
3058
3059
  `Organization: ${config.organizationName}`,
3059
- `Source locale: ${chalk10.cyan(config.sourceLocale)}`,
3060
- `Target locales: ${config.targetLocales.length > 0 ? config.targetLocales.map((l) => chalk10.cyan(l)).join(", ") : chalk10.dim("(none)")}`,
3061
- `Target branches: ${config.targetBranches.map((b) => chalk10.cyan(b)).join(", ")}`,
3062
- ...config.primaryBranch ? [`Primary branch: ${chalk10.cyan(config.primaryBranch)}`] : [],
3060
+ `Source locale: ${highlight(config.sourceLocale)}`,
3061
+ `Target locales: ${config.targetLocales.length > 0 ? config.targetLocales.map((l) => highlight(l)).join(", ") : chalk8.dim("(none)")}`,
3062
+ `Target branches: ${config.targetBranches.map((b) => highlight(b)).join(", ")}`,
3063
+ ...config.primaryBranch ? [`Primary branch: ${highlight(config.primaryBranch)}`] : [],
3063
3064
  `Sync policy:`,
3064
- ` Blocking branches: ${config.syncPolicy.blockingBranches.map((b) => chalk10.cyan(b)).join(", ")}`,
3065
- ` Blocking mode: ${chalk10.cyan(config.syncPolicy.blockingMode)}`,
3066
- ` Non-blocking mode: ${chalk10.cyan(config.syncPolicy.nonBlockingMode)}`,
3067
- ` Max wait: ${chalk10.cyan(String(config.syncPolicy.defaultMaxWaitMs))} ms`
3065
+ ` Blocking branches: ${config.syncPolicy.blockingBranches.map((b) => highlight(b)).join(", ")}`,
3066
+ ` Blocking mode: ${highlight(config.syncPolicy.blockingMode)}`,
3067
+ ` Non-blocking mode: ${highlight(config.syncPolicy.nonBlockingMode)}`,
3068
+ ` Max wait: ${highlight(String(config.syncPolicy.defaultMaxWaitMs))} ms`
3068
3069
  ];
3069
- p10.note(lines.join("\n"), `${config.projectName} \u2014 project config`);
3070
+ p10.note(lines.join("\n"), `${config.projectName} \u2014 app config`);
3070
3071
  return 0;
3071
3072
  } catch (error) {
3072
3073
  p10.log.error(
@@ -3080,7 +3081,6 @@ async function projectConfig(options = {}) {
3080
3081
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
3081
3082
  import { join as join4 } from "path";
3082
3083
  import * as p11 from "@clack/prompts";
3083
- import chalk11 from "chalk";
3084
3084
  import { config as loadEnv5 } from "dotenv";
3085
3085
  loadEnv5();
3086
3086
  async function getTranslations(options = {}) {
@@ -3103,17 +3103,17 @@ async function getTranslations(options = {}) {
3103
3103
  return 1;
3104
3104
  }
3105
3105
  const spinner7 = p11.spinner();
3106
- spinner7.start(`Fetching translations for ${chalk11.cyan(branch)}\u2026`);
3106
+ spinner7.start(`Fetching translations for ${highlight(branch)}\u2026`);
3107
3107
  try {
3108
- const projectConfig2 = await api.getProjectConfig();
3109
- const targetLocales = options.locale ? [options.locale] : projectConfig2.targetLocales;
3108
+ const projectConfig = await api.getAppConfig();
3109
+ const targetLocales = options.locale ? [options.locale] : projectConfig.targetLocales;
3110
3110
  if (targetLocales.length === 0) {
3111
3111
  spinner7.stop("No target locales configured.");
3112
3112
  p11.log.info("Add target locales with `vocoder locales add <code>`.");
3113
3113
  return 1;
3114
3114
  }
3115
3115
  const snapshot = await api.getTranslationSnapshot({ branch, targetLocales });
3116
- spinner7.stop(`Fetched translations for ${chalk11.cyan(branch)}`);
3116
+ spinner7.stop(`Fetched translations for ${highlight(branch)}`);
3117
3117
  if (snapshot.status === "NOT_FOUND") {
3118
3118
  p11.log.warn(
3119
3119
  `No translation snapshot found for branch "${branch}". Run \`vocoder sync\` to generate one.`
@@ -3141,16 +3141,16 @@ function writeLocaleFiles(translations, outputDir) {
3141
3141
  for (const [locale, strings] of Object.entries(translations)) {
3142
3142
  const filePath = join4(outputDir, `${locale}.json`);
3143
3143
  writeFileSync4(filePath, JSON.stringify(strings, null, 2) + "\n", "utf-8");
3144
- p11.log.success(`Wrote ${chalk11.cyan(filePath)}`);
3144
+ p11.log.success(`Wrote ${highlight(filePath)}`);
3145
3145
  }
3146
3146
  }
3147
3147
 
3148
- // src/commands/create-project.ts
3148
+ // src/commands/create-app.ts
3149
3149
  import * as p12 from "@clack/prompts";
3150
- import chalk12 from "chalk";
3150
+ import chalk9 from "chalk";
3151
3151
  import { config as loadEnv6 } from "dotenv";
3152
3152
  loadEnv6();
3153
- async function createProject(options) {
3153
+ async function createApp(options) {
3154
3154
  const authData = readAuthData();
3155
3155
  if (!authData) {
3156
3156
  p12.log.error(
@@ -3180,7 +3180,7 @@ async function createProject(options) {
3180
3180
  const targetLocales = options.targetLocales ? options.targetLocales.split(",").map((l) => l.trim()).filter(Boolean) : [];
3181
3181
  const targetBranches = options.targetBranches ? options.targetBranches.split(",").map((b) => b.trim()).filter(Boolean) : ["main"];
3182
3182
  const spinner7 = p12.spinner();
3183
- spinner7.start(`Creating project "${options.name}"\u2026`);
3183
+ spinner7.start(`Creating app "${options.name}"\u2026`);
3184
3184
  try {
3185
3185
  const result = await api.createProject(authData.token, {
3186
3186
  organizationId: options.workspace,
@@ -3191,16 +3191,16 @@ async function createProject(options) {
3191
3191
  appDirs: [appDir],
3192
3192
  ...repoCanonical ? { repoCanonical } : {}
3193
3193
  });
3194
- spinner7.stop(`Created project ${chalk12.bold(result.projectName)}`);
3194
+ spinner7.stop(`Created app ${chalk9.bold(result.projectName)}`);
3195
3195
  const lines = [
3196
3196
  `Project ID: ${result.projectId}`,
3197
- `Source locale: ${chalk12.cyan(result.sourceLocale)}`,
3198
- `Target locales: ${result.targetLocales.length > 0 ? result.targetLocales.map((l) => chalk12.cyan(l)).join(", ") : chalk12.dim("(none)")}`,
3199
- `Branches: ${result.targetBranches.map((b) => chalk12.cyan(b)).join(", ")}`,
3200
- ...repoCanonical ? [`Repository: ${chalk12.cyan(repoCanonical)}${appDir !== "." ? ` (${appDir})` : ""}`] : [],
3197
+ `Source locale: ${highlight(result.sourceLocale)}`,
3198
+ `Target locales: ${result.targetLocales.length > 0 ? result.targetLocales.map((l) => highlight(l)).join(", ") : chalk9.dim("(none)")}`,
3199
+ `Branches: ${result.targetBranches.map((b) => highlight(b)).join(", ")}`,
3200
+ ...repoCanonical ? [`Repository: ${highlight(repoCanonical)}${appDir !== "." ? ` (${appDir})` : ""}`] : [],
3201
3201
  "",
3202
3202
  `Add this to your .env file:`,
3203
- ` ${chalk12.bold("VOCODER_API_KEY")}=${chalk12.cyan(result.apiKey)}`
3203
+ ` ${chalk9.bold("VOCODER_API_KEY")}=${highlight(result.apiKey)}`
3204
3204
  ];
3205
3205
  p12.note(lines.join("\n"), "Project created");
3206
3206
  if (!result.repositoryBound && repoCanonical) {
@@ -3228,7 +3228,7 @@ async function createProject(options) {
3228
3228
 
3229
3229
  // src/commands/whoami.ts
3230
3230
  import * as p13 from "@clack/prompts";
3231
- import chalk13 from "chalk";
3231
+ import chalk10 from "chalk";
3232
3232
  async function whoami(options = {}) {
3233
3233
  const stored = readAuthData();
3234
3234
  if (!stored) {
@@ -3238,10 +3238,10 @@ async function whoami(options = {}) {
3238
3238
  const apiUrl = options.apiUrl ?? stored.apiUrl ?? "https://vocoder.app";
3239
3239
  const api = new VocoderAPI({ apiUrl, apiKey: "" });
3240
3240
  try {
3241
- const info = await api.getCliUserInfo(stored.token);
3242
- p13.log.info(`Logged in as ${chalk13.bold(info.email)}`);
3243
- if (info.name) {
3244
- p13.log.info(`Name: ${info.name}`);
3241
+ const info2 = await api.getCliUserInfo(stored.token);
3242
+ p13.log.info(`Logged in as ${chalk10.bold(info2.email)}`);
3243
+ if (info2.name) {
3244
+ p13.log.info(`Name: ${info2.name}`);
3245
3245
  }
3246
3246
  p13.log.info(`API: ${apiUrl}`);
3247
3247
  return 0;
@@ -3266,7 +3266,7 @@ program.name("vocoder").description("Vocoder CLI - Project setup and string extr
3266
3266
  program.command("init").description("Authenticate and provision Vocoder for this project").option("--api-url <url>", "Override Vocoder API URL").option("--yes", "Allow overwriting existing local config values").option(
3267
3267
  "--ci",
3268
3268
  "Non-interactive mode: print auth URL to stdout, skip browser open"
3269
- ).option("--project-name <name>", "Starter project name to create").option("--source-locale <locale>", "Source locale for the starter project").option(
3269
+ ).option("--app-name <name>", "Starter app name to create").option("--source-locale <locale>", "Source locale for the starter project").option(
3270
3270
  "--target-locales <list>",
3271
3271
  "Comma-separated target locales (e.g. es,fr,de)"
3272
3272
  ).action((options) => runCommand(init, options));
@@ -3286,9 +3286,9 @@ localesCmd.command("remove <codes...>").description("Remove one or more target l
3286
3286
  (codes, options) => runCommand((opts) => removeLocales(codes, opts), options)
3287
3287
  );
3288
3288
  localesCmd.command("supported").description("List all locales supported by Vocoder").option("--api-url <url>", "Override Vocoder API URL").action((options) => runCommand(listSupportedLocales, options));
3289
- program.command("project").description("Show current project configuration").option("--api-url <url>", "Override Vocoder API URL").action((options) => runCommand(projectConfig, options));
3289
+ program.command("project").description("Show current app configuration").option("--api-url <url>", "Override Vocoder API URL").action((options) => runCommand(appConfig, options));
3290
3290
  program.command("translations").description("Download the current translation snapshot").option("--branch <branch>", "Git branch (auto-detected if omitted)").option("--locale <locale>", "Fetch a specific locale only").option("--output <dir>", "Write locale JSON files to this directory").option("--api-url <url>", "Override Vocoder API URL").action((options) => runCommand(getTranslations, options));
3291
- program.command("create-project").description("Create a new Vocoder project (requires prior `vocoder init`)").requiredOption("--name <name>", "Project display name").requiredOption("--source-locale <code>", "Source language BCP 47 code (e.g. en)").requiredOption("--workspace <org-id>", "Workspace organization ID").option(
3291
+ program.command("create-app").description("Create a new Vocoder app (requires prior `vocoder init`)").requiredOption("--name <name>", "App display name").requiredOption("--source-locale <code>", "Source language BCP 47 code (e.g. en)").requiredOption("--workspace <org-id>", "Workspace organization ID").option(
3292
3292
  "--target-locales <codes>",
3293
3293
  "Comma-separated target locale codes (e.g. fr,de,pt-BR)"
3294
3294
  ).option(
@@ -3309,7 +3309,7 @@ program.command("create-project").description("Create a new Vocoder project (req
3309
3309
  targetBranches: options.targetBranches,
3310
3310
  workspace: options.workspace
3311
3311
  };
3312
- return runCommand(createProject, translated);
3312
+ return runCommand(createApp, translated);
3313
3313
  });
3314
3314
  program.parse(process.argv);
3315
3315
  //# sourceMappingURL=bin.mjs.map