@vocoder/cli 0.12.2 → 0.13.0

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";
@@ -54,14 +54,6 @@ function writeVocoderConfig(options) {
54
54
  export default defineConfig({
55
55
  targetBranches: [${branchesStr}],
56
56
  include: [${includesStr}],
57
- exclude: [
58
- '**/node_modules/**',
59
- '**/.next/**',
60
- '**/dist/**',
61
- '**/build/**',
62
- '**/*.test.*',
63
- '**/*.spec.*',
64
- ],
65
57
  })
66
58
  `;
67
59
  try {
@@ -354,25 +346,35 @@ async function selectGitHubInstallation(installations, canInstallNew) {
354
346
 
355
347
  // src/utils/project-create.ts
356
348
  import * as p3 from "@clack/prompts";
357
- import chalk4 from "chalk";
349
+ import chalk3 from "chalk";
358
350
 
359
351
  // src/utils/branch-select.ts
360
352
  import { execSync } from "child_process";
361
353
  import { isCancel as isCancel2, Prompt } from "@clack/core";
354
+
355
+ // src/utils/theme.ts
362
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
363
372
  var S_BAR = "\u2502";
364
373
  var S_BAR_END = "\u2514";
365
374
  var S_ACTIVE = "\u25C6";
366
375
  var S_SUBMIT = "\u25C6";
367
376
  var S_CANCEL = "\u25A0";
368
377
  var S_ERROR = "\u25B2";
369
- var noColor = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
370
- var dim = (s) => noColor ? s : chalk2.gray(s);
371
- var cyan = (s) => noColor ? s : chalk2.cyan(s);
372
- var grn = (s) => noColor ? s : chalk2.green(s);
373
- var ylw = (s) => noColor ? s : chalk2.yellow(s);
374
- var red = (s) => noColor ? s : chalk2.red(s);
375
- var bld = (s) => noColor ? s : chalk2.bold(s);
376
378
  function symbol(state) {
377
379
  switch (state) {
378
380
  case "submit":
@@ -382,7 +384,7 @@ function symbol(state) {
382
384
  case "error":
383
385
  return ylw(S_ERROR);
384
386
  default:
385
- return cyan(S_ACTIVE);
387
+ return active(S_ACTIVE);
386
388
  }
387
389
  }
388
390
  function detectGitBranches(cwd) {
@@ -455,19 +457,19 @@ function buildList(filtered, cursor, scrollOffset, selected, filter, customPatte
455
457
  const item = filtered[i];
456
458
  const isCursor = i === cursor && !addCursor;
457
459
  const isChecked = selected.has(item.value);
458
- 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");
459
461
  let label = item.isCustom ? `${item.label} ${dim("(custom)")}` : item.label;
460
462
  if (isCursor) label = bld(label);
461
- lines.push(`${cyan(S_BAR)} ${icon} ${label}`);
463
+ lines.push(`${info(S_BAR)} ${icon} ${label}`);
462
464
  }
463
465
  const trimmed = filter.trim();
464
466
  const allItems = [...filtered];
465
467
  const isNewPattern = trimmed.length > 0 && !allItems.some((i) => i.value === trimmed) && !customPatterns.includes(trimmed);
466
468
  if (isNewPattern) {
467
469
  const err = validateBranchPattern(trimmed) ?? (excludedPatterns.has(trimmed) ? "Already used for automatic translation" : null);
468
- const icon = addCursor ? grn("\u25FB") : dim("\u25FB");
470
+ const icon = addCursor ? active("\u25FB") : dim("\u25FB");
469
471
  const label = err ? `${ylw("+")} ${dim(`"${trimmed}" \u2014 ${err}`)}` : `${grn("+")} Add "${trimmed}" as branch pattern`;
470
- lines.push(`${cyan(S_BAR)} ${icon} ${label}`);
472
+ lines.push(`${info(S_BAR)} ${icon} ${label}`);
471
473
  } else if (filtered.length === 0 && trimmed.length === 0) {
472
474
  lines.push(dim(`${S_BAR} No branches detected`));
473
475
  }
@@ -550,7 +552,7 @@ ${symbol(this.state)} ${message}
550
552
  default:
551
553
  return [
552
554
  hdr.trimEnd(),
553
- `${cyan(S_BAR)} ${dim("/")} ${hint}`,
555
+ `${info(S_BAR)} ${dim("/")} ${hint}`,
554
556
  buildList(
555
557
  filtered,
556
558
  cursor,
@@ -562,7 +564,7 @@ ${symbol(this.state)} ${message}
562
564
  optional,
563
565
  excludedSet
564
566
  ),
565
- `${cyan(S_BAR_END)}`,
567
+ `${info(S_BAR_END)}`,
566
568
  ""
567
569
  ].join("\n");
568
570
  }
@@ -635,30 +637,22 @@ ${symbol(this.state)} ${message}
635
637
  // src/utils/locale-search.ts
636
638
  import { isCancel as isCancel3, Prompt as Prompt2 } from "@clack/core";
637
639
  import * as p2 from "@clack/prompts";
638
- import chalk3 from "chalk";
639
640
  var S_BAR2 = "\u2502";
640
641
  var S_BAR_END2 = "\u2514";
641
642
  var S_ACTIVE2 = "\u25C6";
642
643
  var S_SUBMIT2 = "\u25C6";
643
644
  var S_CANCEL2 = "\u25A0";
644
645
  var S_ERROR2 = "\u25B2";
645
- var noColor2 = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
646
- var dim2 = (s) => noColor2 ? s : chalk3.gray(s);
647
- var cyan2 = (s) => noColor2 ? s : chalk3.cyan(s);
648
- var grn2 = (s) => noColor2 ? s : chalk3.green(s);
649
- var ylw2 = (s) => noColor2 ? s : chalk3.yellow(s);
650
- var red2 = (s) => noColor2 ? s : chalk3.red(s);
651
- var bld2 = (s) => noColor2 ? s : chalk3.bold(s);
652
646
  function symbol2(state) {
653
647
  switch (state) {
654
648
  case "submit":
655
- return grn2(S_SUBMIT2);
649
+ return grn(S_SUBMIT2);
656
650
  case "cancel":
657
- return red2(S_CANCEL2);
651
+ return red(S_CANCEL2);
658
652
  case "error":
659
- return ylw2(S_ERROR2);
653
+ return ylw(S_ERROR2);
660
654
  default:
661
- return cyan2(S_ACTIVE2);
655
+ return active(S_ACTIVE2);
662
656
  }
663
657
  }
664
658
  var MAX_VISIBLE2 = 12;
@@ -677,18 +671,18 @@ function buildList2(filtered, cursor, scrollOffset, selected) {
677
671
  const opt = filtered[i];
678
672
  const isCursor = i === cursor;
679
673
  const isChecked = isMulti && selected.has(opt.bcp47);
680
- 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");
681
675
  visibleLines.push(
682
- `${cyan2(S_BAR2)} ${icon} ${isCursor ? bld2(opt.label) : opt.label}`
676
+ `${info(S_BAR2)} ${icon} ${isCursor ? bld(opt.label) : opt.label}`
683
677
  );
684
678
  }
685
679
  const hidden = filtered.length - (end - scrollOffset);
686
680
  if (hidden > 0)
687
- visibleLines.push(dim2(`${S_BAR2} ${hidden} more \u2014 keep typing to narrow`));
688
- 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`));
689
683
  if (isMulti && selected.size > 0) {
690
684
  visibleLines.push(
691
- dim2(`${S_BAR2} ${selected.size} selected \u2014 Enter to confirm`)
685
+ dim(`${S_BAR2} ${selected.size} selected \u2014 Enter to confirm`)
692
686
  );
693
687
  }
694
688
  return visibleLines.join("\n");
@@ -724,43 +718,43 @@ async function runFilterablePrompt(opts) {
724
718
  render() {
725
719
  const filtered = getFiltered();
726
720
  clampCursor(filtered);
727
- const hdr = `${dim2(S_BAR2)}
721
+ const hdr = `${dim(S_BAR2)}
728
722
  ${symbol2(this.state)} ${message}
729
723
  `;
730
- const hint = filter.length > 0 ? filter : dim2(
724
+ const hint = filter.length > 0 ? filter : dim(
731
725
  `type to filter, \u2191\u2193 navigate${multi ? ", space select" : ""}`
732
726
  );
733
727
  switch (this.state) {
734
728
  case "submit": {
735
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 ?? "";
736
- return `${hdr}${dim2(S_BAR2)} ${bld2(val || dim2("none"))}`;
730
+ return `${hdr}${dim(S_BAR2)} ${bld(val || dim("none"))}`;
737
731
  }
738
732
  case "cancel":
739
- return `${hdr}${dim2(S_BAR2)}`;
733
+ return `${hdr}${dim(S_BAR2)}`;
740
734
  case "error":
741
735
  return [
742
736
  hdr.trimEnd(),
743
- `${ylw2(S_BAR2)} ${dim2("/")} ${hint}`,
737
+ `${ylw(S_BAR2)} ${dim("/")} ${hint}`,
744
738
  buildList2(
745
739
  filtered,
746
740
  cursor,
747
741
  scrollOffset,
748
742
  multi ? selected : null
749
743
  ),
750
- `${ylw2(S_BAR_END2)} ${ylw2(this.error)}`,
744
+ `${ylw(S_BAR_END2)} ${ylw(this.error)}`,
751
745
  ""
752
746
  ].join("\n");
753
747
  default:
754
748
  return [
755
749
  hdr.trimEnd(),
756
- `${cyan2(S_BAR2)} ${dim2("/")} ${hint}`,
750
+ `${info(S_BAR2)} ${dim("/")} ${hint}`,
757
751
  buildList2(
758
752
  filtered,
759
753
  cursor,
760
754
  scrollOffset,
761
755
  multi ? selected : null
762
756
  ),
763
- `${cyan2(S_BAR_END2)}`,
757
+ `${info(S_BAR_END2)}`,
764
758
  ""
765
759
  ].join("\n");
766
760
  }
@@ -869,7 +863,7 @@ function buildLanguageOptions(locales) {
869
863
  async function runProjectCreate(params) {
870
864
  const { api, userToken, organizationId, repoCanonical } = params;
871
865
  const projectName = (params.defaultName ?? "my-project").trim();
872
- p3.log.success(`Project: ${chalk4.bold(projectName)}`);
866
+ p3.log.success(`Project: ${chalk3.bold(projectName)}`);
873
867
  let sourceLocales;
874
868
  try {
875
869
  ({ sourceLocales } = await api.listLocales(userToken));
@@ -882,7 +876,7 @@ async function runProjectCreate(params) {
882
876
  const languageOptions = buildLanguageOptions(sourceLocales);
883
877
  const appDir = params.defaultAppDir ?? "";
884
878
  if (appDir) {
885
- p3.log.success(`App directory: ${chalk4.bold(appDir)}`);
879
+ p3.log.success(`App directory: ${chalk3.bold(appDir)}`);
886
880
  }
887
881
  const sourceLocale = await searchSelectLocale(
888
882
  languageOptions,
@@ -947,7 +941,7 @@ async function runProjectCreate(params) {
947
941
  appDirs: appDir ? [appDir] : [],
948
942
  repoCanonical
949
943
  });
950
- p3.log.success(`Project ${chalk4.bold(result.projectName)} created!`);
944
+ p3.log.success(`Project ${chalk3.bold(result.projectName)} created!`);
951
945
  return result;
952
946
  } catch (error) {
953
947
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -974,7 +968,7 @@ async function runAppCreate(params) {
974
968
  return null;
975
969
  }
976
970
  if (appDir) {
977
- p3.log.success(`App directory: ${chalk4.bold(appDir)}`);
971
+ p3.log.success(`App directory: ${chalk3.bold(appDir)}`);
978
972
  }
979
973
  const sourceLocale = await searchSelectLocale(
980
974
  languageOptions,
@@ -1026,7 +1020,7 @@ async function runAppCreate(params) {
1026
1020
  }
1027
1021
  const targetBranches = appPushBranches;
1028
1022
  try {
1029
- const result = await api.createApp(userToken, {
1023
+ const result = await api.createProject(userToken, {
1030
1024
  projectId,
1031
1025
  appDir,
1032
1026
  sourceLocale,
@@ -1035,7 +1029,7 @@ async function runAppCreate(params) {
1035
1029
  repoCanonical: repoCanonical ?? ""
1036
1030
  });
1037
1031
  p3.log.success(
1038
- `App ${chalk4.bold(appDir)} added to ${chalk4.bold(projectName)}!`
1032
+ `App ${chalk3.bold(appDir)} added to ${chalk3.bold(projectName)}!`
1039
1033
  );
1040
1034
  return {
1041
1035
  projectId: result.projectId,
@@ -1054,7 +1048,7 @@ async function runAppCreate(params) {
1054
1048
  }
1055
1049
 
1056
1050
  // src/commands/init.ts
1057
- import chalk6 from "chalk";
1051
+ import chalk5 from "chalk";
1058
1052
  import { join as join2 } from "path";
1059
1053
  import { config as loadEnv } from "dotenv";
1060
1054
 
@@ -1168,7 +1162,7 @@ function resolveGitContext() {
1168
1162
 
1169
1163
  // src/utils/workspace.ts
1170
1164
  import * as p4 from "@clack/prompts";
1171
- import chalk5 from "chalk";
1165
+ import chalk4 from "chalk";
1172
1166
  async function selectWorkspace(result) {
1173
1167
  const { workspaces, canCreateWorkspace } = result;
1174
1168
  if (workspaces.length === 0) {
@@ -1272,7 +1266,7 @@ function runScaffold(params) {
1272
1266
  if (detection.ecosystem) {
1273
1267
  const frameworkLabel = detection.framework ?? detection.ecosystem;
1274
1268
  const pmLabel = detection.packageManager;
1275
- p5.log.info(`Detected: ${chalk6.bold(frameworkLabel)} (${pmLabel})`);
1269
+ p5.log.info(`Detected: ${chalk5.bold(frameworkLabel)} (${pmLabel})`);
1276
1270
  }
1277
1271
  const { devPackages, runtimePackages } = getPackagesToInstall(detection);
1278
1272
  const allPackages = [...devPackages, ...runtimePackages];
@@ -1300,10 +1294,10 @@ function runScaffold(params) {
1300
1294
  devPackages.length > 0 ? buildInstallCommand(detection.packageManager, devPackages, true) : null,
1301
1295
  runtimePackages.length > 0 ? buildInstallCommand(detection.packageManager, runtimePackages, false) : null
1302
1296
  ].filter(Boolean).join(" && ");
1303
- p5.log.warn(`Run manually: ${chalk6.cyan(cmds)}`);
1297
+ p5.log.warn(`Run manually: ${highlight(cmds)}`);
1304
1298
  }
1305
1299
  } else if (detection.ecosystem) {
1306
- p5.log.info(`Packages: ${chalk6.green("already installed")}`);
1300
+ p5.log.info(`Packages: ${chalk5.green("already installed")}`);
1307
1301
  }
1308
1302
  const snippets = getSetupSnippets({
1309
1303
  framework: detection.framework,
@@ -1333,19 +1327,19 @@ function runScaffold(params) {
1333
1327
  code: snippets.wrapStep.code
1334
1328
  });
1335
1329
  p5.log.message("");
1336
- p5.log.message(chalk6.bold("Finish setup in your code"));
1330
+ p5.log.message(chalk5.bold("Finish setup in your code"));
1337
1331
  p5.log.message("");
1338
1332
  for (let i = 0; i < steps.length; i++) {
1339
1333
  const step = steps[i];
1340
1334
  p5.log.step(
1341
- `${chalk6.bold(step.label)} ${chalk6.dim(`\u2014 ${step.hint}`)}`
1335
+ `${chalk5.bold(step.label)} ${chalk5.dim(`\u2014 ${step.hint}`)}`
1342
1336
  );
1343
1337
  printCodeBlock(step.code);
1344
1338
  if (i < steps.length - 1) p5.log.message("");
1345
1339
  }
1346
1340
  const written = writeVocoderConfig({ targetBranches, useTypeScript });
1347
1341
  if (written) {
1348
- p5.log.success(`Created ${chalk6.cyan(written)}`);
1342
+ p5.log.success(`Created ${highlight(written)}`);
1349
1343
  } else if (!findExistingConfig(process.cwd())) {
1350
1344
  const ext = useTypeScript ? "ts" : "js";
1351
1345
  p5.log.warn(
@@ -1353,11 +1347,11 @@ function runScaffold(params) {
1353
1347
  );
1354
1348
  }
1355
1349
  p5.log.message("");
1356
- 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");
1357
1351
  p5.log.success(
1358
1352
  `Push to ${branchList} to trigger your first translation run.`
1359
1353
  );
1360
- p5.log.message(chalk6.gray(" Docs: https://vocoder.app/docs/getting-started"));
1354
+ p5.log.message(info(" Docs: https://vocoder.app/docs/getting-started"));
1361
1355
  }
1362
1356
  function writeApiKeyToEnv(apiKey) {
1363
1357
  const envPath = join2(process.cwd(), ".env");
@@ -1382,12 +1376,12 @@ function writeApiKeyToEnv(apiKey) {
1382
1376
  function printApiKey(apiKey) {
1383
1377
  const saved = writeApiKeyToEnv(apiKey);
1384
1378
  p5.log.message("");
1385
- p5.log.message(chalk6.bold("Your API Key"));
1379
+ p5.log.message(chalk5.bold("Your API Key"));
1386
1380
  printCodeBlock(`VOCODER_API_KEY=${apiKey}`);
1387
1381
  if (saved) {
1388
- p5.log.success(chalk6.dim("Saved to .env"));
1382
+ p5.log.success(chalk5.dim("Saved to .env"));
1389
1383
  } else {
1390
- 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"));
1391
1385
  }
1392
1386
  }
1393
1387
  function printCodeBlock(code) {
@@ -1396,20 +1390,20 @@ function printCodeBlock(code) {
1396
1390
  (max, line) => Math.max(max, line.length),
1397
1391
  0
1398
1392
  );
1399
- const bar = chalk6.gray("\u2502");
1393
+ const bar = chalk5.gray("\u2502");
1400
1394
  const pad = (s) => s + " ".repeat(maxLen - s.length);
1401
- process.stdout.write(`${chalk6.gray("\u2502")}
1395
+ process.stdout.write(`${chalk5.gray("\u2502")}
1402
1396
  `);
1403
1397
  process.stdout.write(
1404
- `${chalk6.gray("\u2502")} ${chalk6.gray(`\u250C${"\u2500".repeat(maxLen + 2)}\u2510`)}
1398
+ `${chalk5.gray("\u2502")} ${chalk5.gray(`\u250C${"\u2500".repeat(maxLen + 2)}\u2510`)}
1405
1399
  `
1406
1400
  );
1407
1401
  for (const line of lines) {
1408
- process.stdout.write(`${chalk6.gray("\u2502")} ${bar} ${pad(line)} ${bar}
1402
+ process.stdout.write(`${chalk5.gray("\u2502")} ${bar} ${pad(line)} ${bar}
1409
1403
  `);
1410
1404
  }
1411
1405
  process.stdout.write(
1412
- `${chalk6.gray("\u2502")} ${chalk6.gray(`\u2514${"\u2500".repeat(maxLen + 2)}\u2518`)}
1406
+ `${chalk5.gray("\u2502")} ${chalk5.gray(`\u2514${"\u2500".repeat(maxLen + 2)}\u2518`)}
1413
1407
  `
1414
1408
  );
1415
1409
  }
@@ -1548,7 +1542,7 @@ async function runAuthFlow(api, options, reauth = false, repoCanonical) {
1548
1542
  return null;
1549
1543
  }
1550
1544
  const userInfo = await api.getCliUserInfo(rawToken);
1551
- authSpinner.stop(`Authenticated as ${chalk6.bold(userInfo.email)}`);
1545
+ authSpinner.stop(`Authenticated as ${chalk5.bold(userInfo.email)}`);
1552
1546
  return {
1553
1547
  token: rawToken,
1554
1548
  ...userInfo,
@@ -1558,7 +1552,7 @@ async function runAuthFlow(api, options, reauth = false, repoCanonical) {
1558
1552
  }
1559
1553
  async function init(options = {}) {
1560
1554
  const apiUrl = options.apiUrl || process.env.VOCODER_API_URL || "https://vocoder.app";
1561
- p5.intro(chalk6.bold("Vocoder Setup"));
1555
+ p5.intro(active(chalk5.bold("Vocoder Setup")));
1562
1556
  try {
1563
1557
  const gitContext = resolveGitContext();
1564
1558
  const identity = gitContext.identity;
@@ -1572,15 +1566,15 @@ async function init(options = {}) {
1572
1566
  let repoProjectName = null;
1573
1567
  if (identity) {
1574
1568
  const anonApi = new VocoderAPI({ apiUrl, apiKey: "" });
1575
- const lookup = await anonApi.lookupProjectByRepo({
1569
+ const lookup = await anonApi.lookupAppByRepo({
1576
1570
  repoCanonical: identity.repoCanonical,
1577
1571
  appDir: identity.repoAppDir
1578
1572
  });
1579
1573
  if (lookup.exactMatch) {
1580
1574
  const { exactMatch } = lookup;
1581
- p5.log.success(`Project: ${chalk6.bold(exactMatch.projectName)}`);
1575
+ p5.log.success(`Project: ${chalk5.bold(exactMatch.projectName)}`);
1582
1576
  p5.log.info(
1583
- `Branches: ${chalk6.cyan((exactMatch.targetBranches ?? ["main"]).join(", "))}`
1577
+ `Branches: ${highlight((exactMatch.targetBranches ?? ["main"]).join(", "))}`
1584
1578
  );
1585
1579
  const needsKey = await p5.confirm({
1586
1580
  message: "Need to regenerate your API key?"
@@ -1613,18 +1607,18 @@ async function init(options = {}) {
1613
1607
  }
1614
1608
  const isTs = detectLocalEcosystem().isTypeScript;
1615
1609
  const written = writeVocoderConfig({ targetBranches: exactMatch.targetBranches ?? ["main"], useTypeScript: isTs });
1616
- if (written) p5.log.success(`Created ${chalk6.cyan(written)}`);
1617
- p5.outro("Vocoder is already set up for this repository.");
1610
+ if (written) p5.log.success(`Created ${highlight(written)}`);
1611
+ p5.outro(active("Vocoder is already set up for this repository."));
1618
1612
  return 0;
1619
1613
  }
1620
1614
  if (lookup.hasWholeRepoApp) {
1621
1615
  const wholeRepo = lookup.existingApps.find((a) => a.appDir === "");
1622
1616
  if (wholeRepo) {
1623
- p5.log.success(`Project: ${chalk6.bold(wholeRepo.projectName)}`);
1617
+ p5.log.success(`Project: ${chalk5.bold(wholeRepo.projectName)}`);
1624
1618
  const isTs = detectLocalEcosystem().isTypeScript;
1625
1619
  const written = writeVocoderConfig({ targetBranches: ["main"], useTypeScript: isTs });
1626
- if (written) p5.log.success(`Created ${chalk6.cyan(written)}`);
1627
- p5.outro("Vocoder is already set up for this repository.");
1620
+ if (written) p5.log.success(`Created ${highlight(written)}`);
1621
+ p5.outro(active("Vocoder is already set up for this repository."));
1628
1622
  return 0;
1629
1623
  }
1630
1624
  }
@@ -1641,7 +1635,7 @@ async function init(options = {}) {
1641
1635
  let authOrganizationId;
1642
1636
  const storedAuth = await verifyStoredAuth(api);
1643
1637
  if (storedAuth.status === "valid") {
1644
- p5.log.success(`Authenticated as ${chalk6.bold(storedAuth.email)}`);
1638
+ p5.log.success(`Authenticated as ${chalk5.bold(storedAuth.email)}`);
1645
1639
  userToken = storedAuth.token;
1646
1640
  userEmail = storedAuth.email;
1647
1641
  userName = storedAuth.name;
@@ -1681,7 +1675,7 @@ async function init(options = {}) {
1681
1675
  selectedWorkspaceId = authOrganizationId;
1682
1676
  selectedWorkspaceName = ws?.name ?? userEmail;
1683
1677
  p5.log.success(
1684
- `Connected as ${chalk6.bold(userEmail)} \u2014 workspace: ${chalk6.bold(selectedWorkspaceName)}`
1678
+ `Connected as ${chalk5.bold(userEmail)} \u2014 workspace: ${chalk5.bold(selectedWorkspaceName)}`
1685
1679
  );
1686
1680
  } else {
1687
1681
  const discoveryResult = await api.getCliGitHubDiscovery(userToken).catch(() => null);
@@ -1732,7 +1726,7 @@ async function init(options = {}) {
1732
1726
  });
1733
1727
  selectedWorkspaceId = claimResult.organizationId;
1734
1728
  selectedWorkspaceName = claimResult.organizationName;
1735
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1729
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1736
1730
  } else {
1737
1731
  const workspaceData = await api.listWorkspaces(userToken, {
1738
1732
  repo: identity?.repoCanonical
@@ -1746,13 +1740,13 @@ async function init(options = {}) {
1746
1740
  const ws = covering[0];
1747
1741
  selectedWorkspaceId = ws.id;
1748
1742
  selectedWorkspaceName = ws.name;
1749
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1743
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1750
1744
  } else if (repoCanonical && covering.length > 1) {
1751
1745
  const choice = await p5.select({
1752
1746
  message: "Select workspace for this repo",
1753
1747
  options: covering.map((w) => ({
1754
1748
  value: w.id,
1755
- 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" : ""})`)}`
1756
1750
  }))
1757
1751
  });
1758
1752
  if (p5.isCancel(choice)) {
@@ -1762,11 +1756,11 @@ async function init(options = {}) {
1762
1756
  const ws = covering.find((w) => w.id === choice);
1763
1757
  selectedWorkspaceId = ws.id;
1764
1758
  selectedWorkspaceName = ws.name;
1765
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1759
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1766
1760
  } else if (repoCanonical && covering.length === 0 && connected.length > 0) {
1767
1761
  const shortRepo = repoCanonical.split(":")[1] ?? repoCanonical;
1768
1762
  p5.log.warn(
1769
- `${chalk6.bold(shortRepo)} isn't accessible from your Vocoder installation.
1763
+ `${chalk5.bold(shortRepo)} isn't accessible from your Vocoder installation.
1770
1764
  Grant access to this repository or install on the account that owns it.`
1771
1765
  );
1772
1766
  const fixOptions = [];
@@ -1774,13 +1768,13 @@ async function init(options = {}) {
1774
1768
  if (ws.installationConfigureUrl) {
1775
1769
  fixOptions.push({
1776
1770
  value: `grant:${ws.id}`,
1777
- 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`
1778
1772
  });
1779
1773
  }
1780
1774
  }
1781
1775
  fixOptions.push({
1782
1776
  value: "install_new",
1783
- 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)")}`
1784
1778
  });
1785
1779
  fixOptions.push({ value: "cancel", label: "Cancel" });
1786
1780
  const fix = await p5.select({
@@ -1795,8 +1789,8 @@ async function init(options = {}) {
1795
1789
  const ws = connected.find((w) => `grant:${w.id}` === fix);
1796
1790
  await tryOpenBrowser2(ws.installationConfigureUrl);
1797
1791
  p5.cancel(
1798
- `Grant access to ${chalk6.bold(shortRepo)} in your browser,
1799
- 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")}.`
1800
1794
  );
1801
1795
  return 1;
1802
1796
  }
@@ -1813,13 +1807,13 @@ async function init(options = {}) {
1813
1807
  }
1814
1808
  selectedWorkspaceId = connectResult.organizationId;
1815
1809
  selectedWorkspaceName = connectResult.organizationName;
1816
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1810
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1817
1811
  } else {
1818
1812
  if (workspaceData.workspaces.length === 1 && !workspaceData.canCreateWorkspace) {
1819
1813
  const ws = workspaceData.workspaces[0];
1820
1814
  selectedWorkspaceId = ws.id;
1821
1815
  selectedWorkspaceName = ws.name;
1822
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1816
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1823
1817
  } else {
1824
1818
  const workspaceResult = await selectWorkspace(workspaceData);
1825
1819
  if (workspaceResult.action === "cancelled") {
@@ -1829,7 +1823,7 @@ async function init(options = {}) {
1829
1823
  if (workspaceResult.action === "use") {
1830
1824
  selectedWorkspaceId = workspaceResult.workspace.id;
1831
1825
  selectedWorkspaceName = workspaceResult.workspace.name;
1832
- p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
1826
+ p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
1833
1827
  } else {
1834
1828
  const connectChoice = await p5.select({
1835
1829
  message: "Connect your new workspace to GitHub",
@@ -1857,7 +1851,7 @@ async function init(options = {}) {
1857
1851
  selectedWorkspaceId = connectResult.organizationId;
1858
1852
  selectedWorkspaceName = connectResult.organizationName;
1859
1853
  p5.log.success(
1860
- `Workspace: ${chalk6.bold(selectedWorkspaceName)}`
1854
+ `Workspace: ${chalk5.bold(selectedWorkspaceName)}`
1861
1855
  );
1862
1856
  } else {
1863
1857
  const installations = await runGitHubDiscoveryFlow({
@@ -1919,7 +1913,7 @@ async function init(options = {}) {
1919
1913
  }
1920
1914
  }
1921
1915
  p5.log.success(
1922
- `Workspace: ${chalk6.bold(selectedWorkspaceName)}`
1916
+ `Workspace: ${chalk5.bold(selectedWorkspaceName)}`
1923
1917
  );
1924
1918
  }
1925
1919
  }
@@ -1929,8 +1923,8 @@ async function init(options = {}) {
1929
1923
  }
1930
1924
  if (repoProjectId && repoProjectName && existingAppsForRepo.length > 0) {
1931
1925
  p5.log.info(
1932
- `${chalk6.bold(repoProjectName)} is already set up for this repo.
1933
- 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(", ")}`
1934
1928
  );
1935
1929
  const appResult = await runAppCreate({
1936
1930
  api,
@@ -1951,7 +1945,7 @@ async function init(options = {}) {
1951
1945
  targetBranches: appResult.targetBranches,
1952
1946
  appDir: identity?.repoAppDir
1953
1947
  });
1954
- p5.outro("You're all set.");
1948
+ p5.outro(active("You're all set."));
1955
1949
  return 0;
1956
1950
  }
1957
1951
  try {
@@ -1959,14 +1953,13 @@ async function init(options = {}) {
1959
1953
  const ws = wsCheck.workspaces.find((w) => w.id === selectedWorkspaceId);
1960
1954
  if (ws && ws.maxProjects !== -1 && ws.projectCount >= ws.maxProjects) {
1961
1955
  p5.log.warn(
1962
- `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.`
1963
1957
  );
1964
- const hasRepoContext = !!identity?.repoCanonical;
1965
1958
  const options2 = [];
1966
- if (hasRepoContext) {
1959
+ if (repoProjectId) {
1967
1960
  options2.push({
1968
1961
  value: "connect",
1969
- label: "Connect this repo to an existing project"
1962
+ label: `Reconnect this repo to ${chalk5.bold(repoProjectName ?? "existing project")}`
1970
1963
  });
1971
1964
  }
1972
1965
  options2.push({ value: "upgrade", label: "Upgrade plan" });
@@ -1986,46 +1979,39 @@ async function init(options = {}) {
1986
1979
  );
1987
1980
  return 1;
1988
1981
  }
1989
- const existingProjects = await api.listProjects(
1982
+ const existingProjects = await api.listApps(
1990
1983
  userToken,
1991
1984
  selectedWorkspaceId
1992
1985
  );
1993
- if (existingProjects.length === 0) {
1994
- p5.log.error("No projects found in this workspace.");
1995
- return 1;
1996
- }
1997
- const chosenId = await p5.select({
1998
- message: "Which project should this repo be connected to?",
1999
- options: existingProjects.map((proj) => ({
2000
- value: proj.id,
2001
- label: proj.name
2002
- }))
2003
- });
2004
- if (p5.isCancel(chosenId)) {
2005
- p5.cancel("Setup cancelled.");
1986
+ const chosenProject = existingProjects.find(
1987
+ (proj) => proj.id === repoProjectId
1988
+ );
1989
+ if (!chosenProject) {
1990
+ p5.log.error("Could not find the project. Try again.");
2006
1991
  return 1;
2007
1992
  }
2008
- const chosen = existingProjects.find((proj) => proj.id === chosenId);
2009
- const appResult = await runAppCreate({
2010
- api,
2011
- userToken,
2012
- projectId: chosen.id,
2013
- projectName: chosen.name,
2014
- organizationName: selectedWorkspaceName,
2015
- repoCanonical: identity?.repoCanonical,
2016
- defaultAppDir: identity?.repoAppDir,
2017
- existingApps: []
2018
- });
2019
- if (!appResult) {
2020
- p5.log.error("Setup failed. Run `vocoder init` again.");
1993
+ try {
1994
+ const appResult = await api.createApp(userToken, {
1995
+ projectId: chosenProject.id,
1996
+ appDir: identity?.repoAppDir ?? "",
1997
+ sourceLocale: chosenProject.sourceLocale,
1998
+ targetLocales: chosenProject.targetLocales,
1999
+ targetBranches: chosenProject.targetBranches,
2000
+ repoCanonical: identity?.repoCanonical ?? ""
2001
+ });
2002
+ p5.log.success(`Connected to project: ${chalk5.bold(chosenProject.name)}`);
2003
+ printApiKey(appResult.apiKey);
2004
+ runScaffold({
2005
+ sourceLocale: chosenProject.sourceLocale,
2006
+ targetBranches: chosenProject.targetBranches,
2007
+ appDir: identity?.repoAppDir
2008
+ });
2009
+ } catch (err) {
2010
+ const msg = err instanceof Error ? err.message : String(err);
2011
+ p5.log.error(`Failed to create app binding: ${msg}`);
2021
2012
  return 1;
2022
2013
  }
2023
- runScaffold({
2024
- sourceLocale: appResult.sourceLocale,
2025
- targetBranches: appResult.targetBranches,
2026
- appDir: identity?.repoAppDir
2027
- });
2028
- p5.outro("You're all set.");
2014
+ p5.outro(active("You're all set."));
2029
2015
  return 0;
2030
2016
  }
2031
2017
  } catch {
@@ -2052,7 +2038,7 @@ Translations won't run automatically until you grant access.
2052
2038
  To fix: go to your GitHub App installation settings and add this
2053
2039
  repository to the allowed list, or switch to "All repositories".
2054
2040
  ` + (projectResult.configureUrl ? `
2055
- ${chalk6.dim(projectResult.configureUrl)}
2041
+ ${chalk5.dim(projectResult.configureUrl)}
2056
2042
  ` : "")
2057
2043
  );
2058
2044
  }
@@ -2062,7 +2048,7 @@ Translations won't run automatically until you grant access.
2062
2048
  appDir: identity?.repoAppDir
2063
2049
  });
2064
2050
  printApiKey(projectResult.apiKey);
2065
- p5.outro("You're all set.");
2051
+ p5.outro(active("You're all set."));
2066
2052
  return 0;
2067
2053
  } catch (error) {
2068
2054
  if (error instanceof Error) {
@@ -2080,7 +2066,7 @@ Translations won't run automatically until you grant access.
2080
2066
 
2081
2067
  // src/commands/locales.ts
2082
2068
  import * as p8 from "@clack/prompts";
2083
- import chalk9 from "chalk";
2069
+ import chalk7 from "chalk";
2084
2070
  import { config as loadEnv3 } from "dotenv";
2085
2071
 
2086
2072
  // src/commands/sync.ts
@@ -2088,7 +2074,7 @@ import { createHash, randomUUID } from "crypto";
2088
2074
  import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
2089
2075
  import { join as join3 } from "path";
2090
2076
  import * as p7 from "@clack/prompts";
2091
- import chalk8 from "chalk";
2077
+ import chalk6 from "chalk";
2092
2078
 
2093
2079
  // src/utils/branch.ts
2094
2080
  import { execSync as execSync4 } from "child_process";
@@ -2158,7 +2144,6 @@ function matchBranchPattern(branch, pattern) {
2158
2144
 
2159
2145
  // src/utils/config.ts
2160
2146
  import * as p6 from "@clack/prompts";
2161
- import chalk7 from "chalk";
2162
2147
  import { config as loadEnv2 } from "dotenv";
2163
2148
  loadEnv2();
2164
2149
  function validateLocalConfig(config) {
@@ -2217,7 +2202,7 @@ async function getMergedConfig(cliOptions, verbose = false, _startDir) {
2217
2202
  const fileConfig = loadVocoderConfig(process.cwd());
2218
2203
  if (!fileConfig) {
2219
2204
  p6.log.warn(
2220
- `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.`
2221
2206
  );
2222
2207
  }
2223
2208
  const envExtractionPattern = process.env.VOCODER_INCLUDE_PATTERN;
@@ -2296,13 +2281,13 @@ async function getMergedConfig(cliOptions, verbose = false, _startDir) {
2296
2281
  }
2297
2282
  if (verbose) {
2298
2283
  const lines = [
2299
- `Include patterns: ${chalk7.cyan(configSources.includePattern)}`,
2300
- ...excludePattern.length > 0 ? [`Exclude patterns: ${chalk7.cyan(configSources.excludePattern)}`] : [],
2301
- `API key: ${chalk7.cyan(configSources.apiKey)}`,
2302
- `API URL: ${chalk7.cyan(configSources.apiUrl)}`,
2303
- `Sync mode: ${chalk7.cyan(configSources.mode)}`,
2304
- ...maxWaitMs ? [`Max wait: ${chalk7.cyan(String(configSources.maxWaitMs))}`] : [],
2305
- `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))}`
2306
2291
  ];
2307
2292
  p6.note(lines.join("\n"), "Configuration sources");
2308
2293
  }
@@ -2573,7 +2558,7 @@ async function fetchApiSnapshot(api, params) {
2573
2558
  async function sync(options = {}) {
2574
2559
  const startTime = Date.now();
2575
2560
  const projectRoot = process.cwd();
2576
- p7.intro("Vocoder Sync");
2561
+ p7.intro(active("Vocoder Sync"));
2577
2562
  const mergedConfig = await getMergedConfig(options, options.verbose);
2578
2563
  if (!mergedConfig.apiKey) {
2579
2564
  p7.log.warn("No API key found. Run init to get started:");
@@ -2582,7 +2567,7 @@ async function sync(options = {}) {
2582
2567
  p7.log.info(
2583
2568
  " Or add your key to .env: VOCODER_API_KEY=vcp_..."
2584
2569
  );
2585
- p7.outro("Run `npx @vocoder/cli init` to set up your project.");
2570
+ p7.outro(active("Run `npx @vocoder/cli init` to set up your project."));
2586
2571
  return 1;
2587
2572
  }
2588
2573
  const spinner7 = p7.spinner();
@@ -2595,7 +2580,7 @@ async function sync(options = {}) {
2595
2580
  };
2596
2581
  validateLocalConfig(localConfig);
2597
2582
  const api = new VocoderAPI(localConfig);
2598
- const apiConfig = await api.getProjectConfig();
2583
+ const apiConfig = await api.getAppConfig();
2599
2584
  const requestedMode = mergedConfig.mode;
2600
2585
  const waitTimeoutMs = resolveWaitTimeoutMs({
2601
2586
  requestedMaxWaitMs: mergedConfig.maxWaitMs,
@@ -2612,10 +2597,10 @@ async function sync(options = {}) {
2612
2597
  ...fileConfig?.appIndustry ? { appIndustry: fileConfig.appIndustry } : {},
2613
2598
  ...fileConfig?.formality ? { formality: fileConfig.formality } : {}
2614
2599
  };
2615
- spinner7.stop(`Branch: ${chalk8.cyan(branch)}`);
2600
+ spinner7.stop(`Branch: ${highlight(branch)}`);
2616
2601
  if (!options.force && !isTargetBranch(branch, config.targetBranches)) {
2617
2602
  p7.log.warn(
2618
- `Skipping translations (${chalk8.cyan(branch)} is not a target branch)`
2603
+ `Skipping translations (${highlight(branch)} is not a target branch)`
2619
2604
  );
2620
2605
  p7.log.info(`Target branches: ${config.targetBranches.join(", ")}`);
2621
2606
  p7.log.info("Use --force to translate anyway");
@@ -2639,7 +2624,7 @@ async function sync(options = {}) {
2639
2624
  return 0;
2640
2625
  }
2641
2626
  spinner7.stop(
2642
- `Extracted ${chalk8.cyan(extractedStrings.length)} strings from ${chalk8.cyan(patternsDisplay)}`
2627
+ `Extracted ${highlight(extractedStrings.length)} strings from ${highlight(patternsDisplay)}`
2643
2628
  );
2644
2629
  if (options.verbose) {
2645
2630
  const sampleLines = extractedStrings.slice(0, 5).map((s) => ` "${s.text}" (${s.file}:${s.line})`);
@@ -2660,7 +2645,7 @@ async function sync(options = {}) {
2660
2645
  ].join("\n"),
2661
2646
  "Dry run - would translate"
2662
2647
  );
2663
- p7.outro("No API calls made.");
2648
+ p7.outro(active("No API calls made."));
2664
2649
  return 0;
2665
2650
  }
2666
2651
  const repoIdentity = resolveGitRepositoryIdentity();
@@ -2682,14 +2667,14 @@ async function sync(options = {}) {
2682
2667
  const cacheFile = getCacheFilePath(projectRoot, fingerprint);
2683
2668
  if (existsSync3(cacheFile)) {
2684
2669
  if (options.verbose) {
2685
- p7.log.info(`Cache hit: ${chalk8.dim(cacheFile)} (fingerprint ${chalk8.cyan(fingerprint)})`);
2670
+ p7.log.info(`Cache hit: ${chalk6.dim(cacheFile)} (fingerprint ${highlight(fingerprint)})`);
2686
2671
  }
2687
2672
  const duration2 = ((Date.now() - startTime) / 1e3).toFixed(1);
2688
- p7.outro(`Up to date (${duration2}s)`);
2673
+ p7.outro(active(`Up to date (${duration2}s)`));
2689
2674
  return 0;
2690
2675
  }
2691
2676
  if (options.verbose) {
2692
- 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`);
2693
2678
  }
2694
2679
  }
2695
2680
  spinner7.start("Submitting strings to Vocoder API");
@@ -2714,7 +2699,7 @@ async function sync(options = {}) {
2714
2699
  policy: config.syncPolicy
2715
2700
  });
2716
2701
  if (options.verbose) {
2717
- p7.log.info(`Batch: ${chalk8.dim(batchResponse.batchId)}`);
2702
+ p7.log.info(`Batch: ${chalk6.dim(batchResponse.batchId)}`);
2718
2703
  p7.log.info(`Requested mode: ${requestedMode}`);
2719
2704
  p7.log.info(`Effective mode: ${effectiveMode}`);
2720
2705
  p7.log.info(`Wait timeout: ${waitTimeoutMs}ms`);
@@ -2723,14 +2708,14 @@ async function sync(options = {}) {
2723
2708
  }
2724
2709
  }
2725
2710
  if (batchResponse.status === "UP_TO_DATE" && batchResponse.noChanges) {
2726
- 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`);
2727
2712
  } else if (batchResponse.newStrings === 0) {
2728
- const archivedNote = batchResponse.deletedStrings && batchResponse.deletedStrings > 0 ? `, ${chalk8.yellow(batchResponse.deletedStrings)} archived` : "";
2729
- 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`);
2730
2715
  } else {
2731
- const statParts = [`${chalk8.cyan(batchResponse.newStrings)} new, ${chalk8.cyan(batchResponse.totalStrings)} total`];
2716
+ const statParts = [`${highlight(batchResponse.newStrings)} new, ${highlight(batchResponse.totalStrings)} total`];
2732
2717
  if (batchResponse.deletedStrings && batchResponse.deletedStrings > 0) {
2733
- statParts.push(`${chalk8.yellow(batchResponse.deletedStrings)} archived`);
2718
+ statParts.push(`${chalk6.yellow(batchResponse.deletedStrings)} archived`);
2734
2719
  }
2735
2720
  const estTime = batchResponse.estimatedTime ? ` (~${batchResponse.estimatedTime}s)` : "";
2736
2721
  p7.log.info(`${statParts.join(", ")} \u2192 syncing to ${config.targetLocales.join(", ")}${estTime}`);
@@ -2851,7 +2836,7 @@ async function sync(options = {}) {
2851
2836
  );
2852
2837
  }
2853
2838
  const duration = ((Date.now() - startTime) / 1e3).toFixed(1);
2854
- p7.outro(`Sync complete! (${duration}s)`);
2839
+ p7.outro(active(`Sync complete! (${duration}s)`));
2855
2840
  return 0;
2856
2841
  } catch (error) {
2857
2842
  spinner7.stop();
@@ -2914,15 +2899,15 @@ async function listProjectLocales(options = {}) {
2914
2899
  if (!config) return 1;
2915
2900
  const api = new VocoderAPI(config);
2916
2901
  try {
2917
- const projectConfig2 = await api.getProjectConfig();
2902
+ const projectConfig = await api.getAppConfig();
2918
2903
  p8.log.info(
2919
- `Source locale: ${chalk9.cyan(projectConfig2.sourceLocale)}`
2904
+ `Source locale: ${highlight(projectConfig.sourceLocale)}`
2920
2905
  );
2921
- if (projectConfig2.targetLocales.length === 0) {
2906
+ if (projectConfig.targetLocales.length === 0) {
2922
2907
  p8.log.info("Target locales: (none configured)");
2923
2908
  } else {
2924
2909
  p8.log.info(
2925
- `Target locales: ${projectConfig2.targetLocales.map((l) => chalk9.cyan(l)).join(", ")}`
2910
+ `Target locales: ${projectConfig.targetLocales.map((l) => highlight(l)).join(", ")}`
2926
2911
  );
2927
2912
  }
2928
2913
  return 0;
@@ -2949,9 +2934,9 @@ async function addLocales(locales, options = {}) {
2949
2934
  try {
2950
2935
  const result = await api.addLocale(locale);
2951
2936
  lastTargetLocales = result.targetLocales;
2952
- spinner7.stop(`Added ${chalk9.cyan(locale)}`);
2937
+ spinner7.stop(`Added ${highlight(locale)}`);
2953
2938
  } catch (error) {
2954
- spinner7.stop(`Failed to add ${chalk9.red(locale)}`);
2939
+ spinner7.stop(`Failed to add ${chalk7.red(locale)}`);
2955
2940
  hadError = true;
2956
2941
  if (error instanceof VocoderAPIError && error.limitError) {
2957
2942
  const { limitError } = error;
@@ -2968,7 +2953,7 @@ async function addLocales(locales, options = {}) {
2968
2953
  }
2969
2954
  if (lastTargetLocales.length > 0) {
2970
2955
  p8.log.info(
2971
- `Target locales now: ${lastTargetLocales.map((l) => chalk9.cyan(l)).join(", ")}`
2956
+ `Target locales now: ${lastTargetLocales.map((l) => highlight(l)).join(", ")}`
2972
2957
  );
2973
2958
  }
2974
2959
  return hadError ? 1 : 0;
@@ -2989,9 +2974,9 @@ async function removeLocales(locales, options = {}) {
2989
2974
  try {
2990
2975
  const result = await api.removeLocale(locale);
2991
2976
  lastTargetLocales = result.targetLocales;
2992
- spinner7.stop(`Removed ${chalk9.cyan(locale)}`);
2977
+ spinner7.stop(`Removed ${highlight(locale)}`);
2993
2978
  } catch (error) {
2994
- spinner7.stop(`Failed to remove ${chalk9.red(locale)}`);
2979
+ spinner7.stop(`Failed to remove ${chalk7.red(locale)}`);
2995
2980
  hadError = true;
2996
2981
  p8.log.error(
2997
2982
  error instanceof Error ? error.message : "Unknown error"
@@ -3000,7 +2985,7 @@ async function removeLocales(locales, options = {}) {
3000
2985
  }
3001
2986
  if (lastTargetLocales.length > 0) {
3002
2987
  p8.log.info(
3003
- `Target locales now: ${lastTargetLocales.map((l) => chalk9.cyan(l)).join(", ")}`
2988
+ `Target locales now: ${lastTargetLocales.map((l) => highlight(l)).join(", ")}`
3004
2989
  );
3005
2990
  } else if (!hadError) {
3006
2991
  p8.log.info("Target locales now: (none configured)");
@@ -3013,10 +2998,10 @@ async function listSupportedLocales(options = {}) {
3013
2998
  const api = new VocoderAPI(config);
3014
2999
  try {
3015
3000
  const result = await api.listLocales(config.apiKey);
3016
- p8.log.info(chalk9.bold("Source locales:"));
3001
+ p8.log.info(chalk7.bold("Source locales:"));
3017
3002
  printLocaleTable(result.sourceLocales);
3018
3003
  p8.log.info("");
3019
- p8.log.info(chalk9.bold("Target locales:"));
3004
+ p8.log.info(chalk7.bold("Target locales:"));
3020
3005
  printLocaleTable(result.targetLocales);
3021
3006
  return 0;
3022
3007
  } catch (error) {
@@ -3029,7 +3014,7 @@ async function listSupportedLocales(options = {}) {
3029
3014
  function printLocaleTable(locales) {
3030
3015
  for (const locale of locales) {
3031
3016
  const native = locale.nativeName && locale.nativeName !== locale.name ? ` (${locale.nativeName})` : "";
3032
- p8.log.info(` ${chalk9.cyan(locale.code.padEnd(10))} ${locale.name}${native}`);
3017
+ p8.log.info(` ${highlight(locale.code.padEnd(10))} ${locale.name}${native}`);
3033
3018
  }
3034
3019
  }
3035
3020
 
@@ -3052,12 +3037,12 @@ async function logout(options = {}) {
3052
3037
  return 0;
3053
3038
  }
3054
3039
 
3055
- // src/commands/project-config.ts
3040
+ // src/commands/app-config.ts
3056
3041
  import * as p10 from "@clack/prompts";
3057
- import chalk10 from "chalk";
3042
+ import chalk8 from "chalk";
3058
3043
  import { config as loadEnv4 } from "dotenv";
3059
3044
  loadEnv4();
3060
- async function projectConfig(options = {}) {
3045
+ async function appConfig(options = {}) {
3061
3046
  const apiKey = process.env.VOCODER_API_KEY;
3062
3047
  if (!apiKey) {
3063
3048
  p10.log.error(
@@ -3068,21 +3053,21 @@ async function projectConfig(options = {}) {
3068
3053
  const apiUrl = options.apiUrl ?? process.env.VOCODER_API_URL ?? "https://vocoder.app";
3069
3054
  const api = new VocoderAPI({ apiKey, apiUrl });
3070
3055
  try {
3071
- const config = await api.getProjectConfig();
3056
+ const config = await api.getAppConfig();
3072
3057
  const lines = [
3073
- `Project: ${chalk10.bold(config.projectName)}`,
3058
+ `App: ${chalk8.bold(config.projectName)}`,
3074
3059
  `Organization: ${config.organizationName}`,
3075
- `Source locale: ${chalk10.cyan(config.sourceLocale)}`,
3076
- `Target locales: ${config.targetLocales.length > 0 ? config.targetLocales.map((l) => chalk10.cyan(l)).join(", ") : chalk10.dim("(none)")}`,
3077
- `Target branches: ${config.targetBranches.map((b) => chalk10.cyan(b)).join(", ")}`,
3078
- ...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)}`] : [],
3079
3064
  `Sync policy:`,
3080
- ` Blocking branches: ${config.syncPolicy.blockingBranches.map((b) => chalk10.cyan(b)).join(", ")}`,
3081
- ` Blocking mode: ${chalk10.cyan(config.syncPolicy.blockingMode)}`,
3082
- ` Non-blocking mode: ${chalk10.cyan(config.syncPolicy.nonBlockingMode)}`,
3083
- ` 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`
3084
3069
  ];
3085
- p10.note(lines.join("\n"), `${config.projectName} \u2014 project config`);
3070
+ p10.note(lines.join("\n"), `${config.projectName} \u2014 app config`);
3086
3071
  return 0;
3087
3072
  } catch (error) {
3088
3073
  p10.log.error(
@@ -3096,7 +3081,6 @@ async function projectConfig(options = {}) {
3096
3081
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
3097
3082
  import { join as join4 } from "path";
3098
3083
  import * as p11 from "@clack/prompts";
3099
- import chalk11 from "chalk";
3100
3084
  import { config as loadEnv5 } from "dotenv";
3101
3085
  loadEnv5();
3102
3086
  async function getTranslations(options = {}) {
@@ -3119,17 +3103,17 @@ async function getTranslations(options = {}) {
3119
3103
  return 1;
3120
3104
  }
3121
3105
  const spinner7 = p11.spinner();
3122
- spinner7.start(`Fetching translations for ${chalk11.cyan(branch)}\u2026`);
3106
+ spinner7.start(`Fetching translations for ${highlight(branch)}\u2026`);
3123
3107
  try {
3124
- const projectConfig2 = await api.getProjectConfig();
3125
- const targetLocales = options.locale ? [options.locale] : projectConfig2.targetLocales;
3108
+ const projectConfig = await api.getAppConfig();
3109
+ const targetLocales = options.locale ? [options.locale] : projectConfig.targetLocales;
3126
3110
  if (targetLocales.length === 0) {
3127
3111
  spinner7.stop("No target locales configured.");
3128
3112
  p11.log.info("Add target locales with `vocoder locales add <code>`.");
3129
3113
  return 1;
3130
3114
  }
3131
3115
  const snapshot = await api.getTranslationSnapshot({ branch, targetLocales });
3132
- spinner7.stop(`Fetched translations for ${chalk11.cyan(branch)}`);
3116
+ spinner7.stop(`Fetched translations for ${highlight(branch)}`);
3133
3117
  if (snapshot.status === "NOT_FOUND") {
3134
3118
  p11.log.warn(
3135
3119
  `No translation snapshot found for branch "${branch}". Run \`vocoder sync\` to generate one.`
@@ -3157,16 +3141,16 @@ function writeLocaleFiles(translations, outputDir) {
3157
3141
  for (const [locale, strings] of Object.entries(translations)) {
3158
3142
  const filePath = join4(outputDir, `${locale}.json`);
3159
3143
  writeFileSync4(filePath, JSON.stringify(strings, null, 2) + "\n", "utf-8");
3160
- p11.log.success(`Wrote ${chalk11.cyan(filePath)}`);
3144
+ p11.log.success(`Wrote ${highlight(filePath)}`);
3161
3145
  }
3162
3146
  }
3163
3147
 
3164
- // src/commands/create-project.ts
3148
+ // src/commands/create-app.ts
3165
3149
  import * as p12 from "@clack/prompts";
3166
- import chalk12 from "chalk";
3150
+ import chalk9 from "chalk";
3167
3151
  import { config as loadEnv6 } from "dotenv";
3168
3152
  loadEnv6();
3169
- async function createProject(options) {
3153
+ async function createApp(options) {
3170
3154
  const authData = readAuthData();
3171
3155
  if (!authData) {
3172
3156
  p12.log.error(
@@ -3196,7 +3180,7 @@ async function createProject(options) {
3196
3180
  const targetLocales = options.targetLocales ? options.targetLocales.split(",").map((l) => l.trim()).filter(Boolean) : [];
3197
3181
  const targetBranches = options.targetBranches ? options.targetBranches.split(",").map((b) => b.trim()).filter(Boolean) : ["main"];
3198
3182
  const spinner7 = p12.spinner();
3199
- spinner7.start(`Creating project "${options.name}"\u2026`);
3183
+ spinner7.start(`Creating app "${options.name}"\u2026`);
3200
3184
  try {
3201
3185
  const result = await api.createProject(authData.token, {
3202
3186
  organizationId: options.workspace,
@@ -3207,16 +3191,16 @@ async function createProject(options) {
3207
3191
  appDirs: [appDir],
3208
3192
  ...repoCanonical ? { repoCanonical } : {}
3209
3193
  });
3210
- spinner7.stop(`Created project ${chalk12.bold(result.projectName)}`);
3194
+ spinner7.stop(`Created app ${chalk9.bold(result.projectName)}`);
3211
3195
  const lines = [
3212
3196
  `Project ID: ${result.projectId}`,
3213
- `Source locale: ${chalk12.cyan(result.sourceLocale)}`,
3214
- `Target locales: ${result.targetLocales.length > 0 ? result.targetLocales.map((l) => chalk12.cyan(l)).join(", ") : chalk12.dim("(none)")}`,
3215
- `Branches: ${result.targetBranches.map((b) => chalk12.cyan(b)).join(", ")}`,
3216
- ...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})` : ""}`] : [],
3217
3201
  "",
3218
3202
  `Add this to your .env file:`,
3219
- ` ${chalk12.bold("VOCODER_API_KEY")}=${chalk12.cyan(result.apiKey)}`
3203
+ ` ${chalk9.bold("VOCODER_API_KEY")}=${highlight(result.apiKey)}`
3220
3204
  ];
3221
3205
  p12.note(lines.join("\n"), "Project created");
3222
3206
  if (!result.repositoryBound && repoCanonical) {
@@ -3244,7 +3228,7 @@ async function createProject(options) {
3244
3228
 
3245
3229
  // src/commands/whoami.ts
3246
3230
  import * as p13 from "@clack/prompts";
3247
- import chalk13 from "chalk";
3231
+ import chalk10 from "chalk";
3248
3232
  async function whoami(options = {}) {
3249
3233
  const stored = readAuthData();
3250
3234
  if (!stored) {
@@ -3254,10 +3238,10 @@ async function whoami(options = {}) {
3254
3238
  const apiUrl = options.apiUrl ?? stored.apiUrl ?? "https://vocoder.app";
3255
3239
  const api = new VocoderAPI({ apiUrl, apiKey: "" });
3256
3240
  try {
3257
- const info = await api.getCliUserInfo(stored.token);
3258
- p13.log.info(`Logged in as ${chalk13.bold(info.email)}`);
3259
- if (info.name) {
3260
- 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}`);
3261
3245
  }
3262
3246
  p13.log.info(`API: ${apiUrl}`);
3263
3247
  return 0;
@@ -3282,7 +3266,7 @@ program.name("vocoder").description("Vocoder CLI - Project setup and string extr
3282
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(
3283
3267
  "--ci",
3284
3268
  "Non-interactive mode: print auth URL to stdout, skip browser open"
3285
- ).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(
3286
3270
  "--target-locales <list>",
3287
3271
  "Comma-separated target locales (e.g. es,fr,de)"
3288
3272
  ).action((options) => runCommand(init, options));
@@ -3302,9 +3286,9 @@ localesCmd.command("remove <codes...>").description("Remove one or more target l
3302
3286
  (codes, options) => runCommand((opts) => removeLocales(codes, opts), options)
3303
3287
  );
3304
3288
  localesCmd.command("supported").description("List all locales supported by Vocoder").option("--api-url <url>", "Override Vocoder API URL").action((options) => runCommand(listSupportedLocales, options));
3305
- 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));
3306
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));
3307
- 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(
3308
3292
  "--target-locales <codes>",
3309
3293
  "Comma-separated target locale codes (e.g. fr,de,pt-BR)"
3310
3294
  ).option(
@@ -3325,7 +3309,7 @@ program.command("create-project").description("Create a new Vocoder project (req
3325
3309
  targetBranches: options.targetBranches,
3326
3310
  workspace: options.workspace
3327
3311
  };
3328
- return runCommand(createProject, translated);
3312
+ return runCommand(createApp, translated);
3329
3313
  });
3330
3314
  program.parse(process.argv);
3331
3315
  //# sourceMappingURL=bin.mjs.map