@tolkbot/cli 0.4.0 → 0.5.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.
Files changed (2) hide show
  1. package/dist/index.js +348 -108
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -559,7 +559,7 @@ var require_to_regex_range = __commonJS({
559
559
  let zipped = zip(start, stop);
560
560
  let digits = zipped.length;
561
561
  let pattern = "";
562
- let count = 0;
562
+ let count2 = 0;
563
563
  for (let i = 0; i < digits; i++) {
564
564
  let [startDigit, stopDigit] = zipped[i];
565
565
  if (startDigit === stopDigit) {
@@ -567,13 +567,13 @@ var require_to_regex_range = __commonJS({
567
567
  } else if (startDigit !== "0" || stopDigit !== "9") {
568
568
  pattern += toCharacterClass(startDigit, stopDigit, options);
569
569
  } else {
570
- count++;
570
+ count2++;
571
571
  }
572
572
  }
573
- if (count) {
573
+ if (count2) {
574
574
  pattern += options.shorthand === true ? "\\d" : "[0-9]";
575
575
  }
576
- return { pattern, count: [count], digits };
576
+ return { pattern, count: [count2], digits };
577
577
  }
578
578
  function splitToPatterns(min, max, tok, options) {
579
579
  let ranges = splitToRanges(min, max);
@@ -2320,13 +2320,13 @@ var require_parse2 = __commonJS({
2320
2320
  consume(token.value);
2321
2321
  };
2322
2322
  const negate = () => {
2323
- let count = 1;
2323
+ let count2 = 1;
2324
2324
  while (peek() === "!" && (peek(2) !== "(" || peek(3) === "?")) {
2325
2325
  advance();
2326
2326
  state.start++;
2327
- count++;
2327
+ count2++;
2328
2328
  }
2329
- if (count % 2 === 0) {
2329
+ if (count2 % 2 === 0) {
2330
2330
  return false;
2331
2331
  }
2332
2332
  state.negated = true;
@@ -5784,10 +5784,10 @@ var require_out4 = __commonJS({
5784
5784
  });
5785
5785
 
5786
5786
  // src/main.ts
5787
- import { Command as Command7, CommanderError, Option } from "commander";
5787
+ import { Command as Command8, CommanderError, Option } from "commander";
5788
5788
 
5789
5789
  // package.json
5790
- var version = "0.4.0";
5790
+ var version = "0.5.0";
5791
5791
 
5792
5792
  // src/commands/auth/index.ts
5793
5793
  import { Command } from "commander";
@@ -8068,14 +8068,14 @@ function codePointLength(str) {
8068
8068
  const units = str.length;
8069
8069
  if (!highSurrogate.test(str))
8070
8070
  return units;
8071
- let count = units;
8071
+ let count2 = units;
8072
8072
  for (let i = 0; i < units - 1; i++) {
8073
8073
  if ((str.charCodeAt(i) & 64512) === 55296 && (str.charCodeAt(i + 1) & 64512) === 56320) {
8074
- count--;
8074
+ count2--;
8075
8075
  i++;
8076
8076
  }
8077
8077
  }
8078
- return count;
8078
+ return count2;
8079
8079
  }
8080
8080
  function getLengthableOrigin(input2) {
8081
8081
  if (Array.isArray(input2))
@@ -12432,8 +12432,8 @@ function az_default() {
12432
12432
  localeError: error2()
12433
12433
  };
12434
12434
  }
12435
- function getBelarusianPlural(count, one, few, many) {
12436
- const absCount = Math.abs(count);
12435
+ function getBelarusianPlural(count2, one, few, many) {
12436
+ const absCount = Math.abs(count2);
12437
12437
  const lastDigit = absCount % 10;
12438
12438
  const lastTwoDigits = absCount % 100;
12439
12439
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -15038,8 +15038,8 @@ function hu_default() {
15038
15038
  localeError: error23()
15039
15039
  };
15040
15040
  }
15041
- function getArmenianPlural(count, one, many) {
15042
- return Math.abs(count) === 1 ? one : many;
15041
+ function getArmenianPlural(count2, one, many) {
15042
+ return Math.abs(count2) === 1 ? one : many;
15043
15043
  }
15044
15044
  function withDefiniteArticle(word) {
15045
15045
  if (!word)
@@ -17731,8 +17731,8 @@ function ro_default() {
17731
17731
  localeError: error45()
17732
17732
  };
17733
17733
  }
17734
- function getRussianPlural(count, one, few, many) {
17735
- const absCount = Math.abs(count);
17734
+ function getRussianPlural(count2, one, few, many) {
17735
+ const absCount = Math.abs(count2);
17736
17736
  const lastDigit = absCount % 10;
17737
17737
  const lastTwoDigits = absCount % 100;
17738
17738
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -27354,6 +27354,7 @@ var analyticsEventNames = [
27354
27354
  "glossary_imported",
27355
27355
  "translation_job_viewed",
27356
27356
  "translation_logs_viewed",
27357
+ "activity_timeline_viewed",
27357
27358
  "cli_login_completed",
27358
27359
  "cli_command_completed",
27359
27360
  "cli_init_completed",
@@ -28316,6 +28317,16 @@ async function loadPlanContext(cwd) {
28316
28317
  framework: config3.config.framework
28317
28318
  };
28318
28319
  }
28320
+ async function readCreditBalance(ctx) {
28321
+ try {
28322
+ const balance = await ctx.client.query(api.usage.getCreditBalance, {
28323
+ organizationId: ctx.project.organizationId
28324
+ });
28325
+ return balance?.total ?? null;
28326
+ } catch {
28327
+ return null;
28328
+ }
28329
+ }
28319
28330
  function describePlan(plan) {
28320
28331
  const { summary } = plan;
28321
28332
  const lines = [
@@ -28331,9 +28342,9 @@ function describeLeftOut(plan) {
28331
28342
  const lines = [];
28332
28343
  const protectedCount = plan.protectedKeys.length;
28333
28344
  if (protectedCount > 0) {
28334
- const reasons = Object.entries(summary.protected).map(([reason, n]) => `${n} ${reason}`).join(", ");
28345
+ const reasons2 = Object.entries(summary.protected).map(([reason, n]) => `${n} ${reason}`).join(", ");
28335
28346
  lines.push(
28336
- `${protectedCount} changed but kept for a person (${reasons})`
28347
+ `${protectedCount} changed but kept for a person (${reasons2})`
28337
28348
  );
28338
28349
  }
28339
28350
  if (summary.held > 0)
@@ -28551,11 +28562,11 @@ function flattenValues(data) {
28551
28562
  const values = /* @__PURE__ */ new Map();
28552
28563
  function walk(obj, prefix) {
28553
28564
  for (const [key, value] of Object.entries(obj)) {
28554
- const fullKey5 = prefix ? `${prefix}.${key}` : key;
28565
+ const fullKey6 = prefix ? `${prefix}.${key}` : key;
28555
28566
  if (typeof value === "string") {
28556
- values.set(fullKey5, value);
28567
+ values.set(fullKey6, value);
28557
28568
  } else if (typeof value === "object" && value !== null) {
28558
- walk(value, fullKey5);
28569
+ walk(value, fullKey6);
28559
28570
  }
28560
28571
  }
28561
28572
  }
@@ -31982,9 +31993,246 @@ learningsCommand.command("remove").description("Permanently remove a learning").
31982
31993
  await runLearningsRemove(parsed);
31983
31994
  });
31984
31995
 
31985
- // src/commands/telemetry.ts
31996
+ // src/commands/status/index.ts
31986
31997
  import { Command as Command5 } from "commander";
31987
31998
  import { z as z7 } from "zod/v4";
31999
+
32000
+ // src/commands/status/status.tsx
32001
+ import { Spinner as Spinner13 } from "@inkjs/ui";
32002
+ import { Box as Box16, Text as Text16 } from "ink";
32003
+
32004
+ // src/commands/status/status-core.ts
32005
+ function fullKey4(namespace, key) {
32006
+ return namespace ? `${namespace}.${key}` : key;
32007
+ }
32008
+ function isPersonHold(reason) {
32009
+ return reason === "locked" || reason === "pending-draft";
32010
+ }
32011
+ function count(reasons2) {
32012
+ const byReason = {};
32013
+ for (const reason of reasons2)
32014
+ byReason[reason] = (byReason[reason] ?? 0) + 1;
32015
+ return { total: reasons2.length, byReason };
32016
+ }
32017
+ function openPullRequest(rows, locale) {
32018
+ const row = rows.find(
32019
+ (r) => r.prState === "open" && r.prUrl !== void 0 && sameLocale(r.targetLanguage, locale)
32020
+ );
32021
+ if (!row?.prUrl) return null;
32022
+ return { number: row.prNumber ?? null, url: row.prUrl };
32023
+ }
32024
+ function languageStatus(locale, plan, pullRequests, opts = {}) {
32025
+ const { summary } = plan;
32026
+ const held = [
32027
+ ...plan.heldKeys,
32028
+ ...plan.protectedKeys.flatMap(
32029
+ ({ key, reason }) => isPersonHold(reason) ? [{ key, reason }] : []
32030
+ )
32031
+ ];
32032
+ const kept = plan.protectedKeys.flatMap(
32033
+ ({ key, reason }) => isPersonHold(reason) ? [] : [{ key, reason }]
32034
+ );
32035
+ const tasks = (reason) => plan.tasks.filter((t) => t.reason === reason).map((t) => fullKey4(t.namespace, t.key));
32036
+ return {
32037
+ locale,
32038
+ expected: summary.totalKeys,
32039
+ upToDate: summary.upToDate,
32040
+ missing: summary.missing,
32041
+ changed: summary.changed,
32042
+ heldByPerson: count(held.map((h) => h.reason)),
32043
+ keptForPerson: count(kept.map((k) => k.reason)),
32044
+ untracked: summary.untracked,
32045
+ pullRequest: openPullRequest(pullRequests, locale),
32046
+ ...opts.keys ? {
32047
+ keys: {
32048
+ missing: tasks("missing"),
32049
+ changed: tasks("changed"),
32050
+ heldByPerson: held,
32051
+ keptForPerson: kept,
32052
+ untracked: plan.untrackedKeys.map((k) => k.key)
32053
+ }
32054
+ } : {}
32055
+ };
32056
+ }
32057
+ function needsTranslation(language) {
32058
+ return language.missing > 0 || language.changed > 0;
32059
+ }
32060
+ var REASON_WORDS = {
32061
+ locked: "locked",
32062
+ "pending-draft": "pending draft",
32063
+ manual: "written by a person",
32064
+ "target-edited": "edited since TolkBot wrote it",
32065
+ unknown: "no record of who wrote it"
32066
+ };
32067
+ function reasons(counts) {
32068
+ return Object.entries(counts.byReason).map(
32069
+ ([reason, n]) => `${n} ${REASON_WORDS[reason]}`
32070
+ ).join(", ");
32071
+ }
32072
+ function describeLanguage(language) {
32073
+ const counts = [
32074
+ `${language.expected} expected`,
32075
+ `${language.upToDate} up to date`,
32076
+ `${language.missing} missing`,
32077
+ `${language.changed} changed since translated`
32078
+ ].join(", ");
32079
+ const mark = needsTranslation(language) ? "\u2022" : "\u2713";
32080
+ const lines = [`${mark} ${language.locale}: ${counts}`];
32081
+ if (language.heldByPerson.total > 0) {
32082
+ lines.push(
32083
+ ` ${language.heldByPerson.total} held by a person (${reasons(language.heldByPerson)})`
32084
+ );
32085
+ }
32086
+ if (language.keptForPerson.total > 0) {
32087
+ lines.push(
32088
+ ` ${language.keptForPerson.total} changed but kept for a person (${reasons(language.keptForPerson)})`
32089
+ );
32090
+ }
32091
+ if (language.untracked > 0) {
32092
+ lines.push(
32093
+ ` ${language.untracked} untracked: TolkBot has no record of them`
32094
+ );
32095
+ }
32096
+ if (language.pullRequest) {
32097
+ lines.push(` Pull request: ${language.pullRequest.url}`);
32098
+ }
32099
+ const { keys } = language;
32100
+ if (keys) {
32101
+ const list = (label, names) => {
32102
+ if (names.length === 0) return;
32103
+ lines.push(` ${label}:`);
32104
+ for (const name of names) lines.push(` ${name}`);
32105
+ };
32106
+ list("Missing", keys.missing);
32107
+ list("Changed", keys.changed);
32108
+ list(
32109
+ "Held by a person",
32110
+ keys.heldByPerson.map(
32111
+ (k) => `${k.key} (${REASON_WORDS[k.reason]})`
32112
+ )
32113
+ );
32114
+ list(
32115
+ "Kept for a person",
32116
+ keys.keptForPerson.map(
32117
+ (k) => `${k.key} (${REASON_WORDS[k.reason]})`
32118
+ )
32119
+ );
32120
+ list("Untracked", keys.untracked);
32121
+ }
32122
+ return lines;
32123
+ }
32124
+
32125
+ // src/commands/status/status.tsx
32126
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
32127
+ var OFFLINE_HINT = "Without a login, 'tolkbot validate' checks the files on this machine.";
32128
+ async function runStatus(opts) {
32129
+ let ctx;
32130
+ try {
32131
+ ctx = await loadPlanContext(process.cwd());
32132
+ } catch (error62) {
32133
+ if (error62 instanceof AuthError) {
32134
+ throw new AuthError(
32135
+ error62.baseMessage,
32136
+ [error62.hint, OFFLINE_HINT].filter(Boolean).join(" "),
32137
+ error62.code
32138
+ );
32139
+ }
32140
+ throw error62;
32141
+ }
32142
+ const { project } = ctx;
32143
+ const srcLocale = project.baseLanguage;
32144
+ const locales = project.targetLanguages;
32145
+ const progress = renderTransient(
32146
+ /* @__PURE__ */ jsxs16(Box16, { children: [
32147
+ /* @__PURE__ */ jsx16(Spinner13, {}),
32148
+ /* @__PURE__ */ jsxs16(Text16, { children: [
32149
+ " Reading the status of ",
32150
+ project.name,
32151
+ "\u2026"
32152
+ ] })
32153
+ ] })
32154
+ );
32155
+ const languages = [];
32156
+ let credits;
32157
+ try {
32158
+ const pullRequests = locales.length === 0 ? [] : await ctx.client.query(api.prJobs.listByProject, {
32159
+ organizationId: project.organizationId,
32160
+ projectId: project._id
32161
+ });
32162
+ for (const locale of locales) {
32163
+ const plan = await planForLocale(ctx, srcLocale, locale);
32164
+ languages.push(
32165
+ languageStatus(locale, plan, pullRequests, {
32166
+ keys: opts.keys
32167
+ })
32168
+ );
32169
+ }
32170
+ credits = await readCreditBalance(ctx);
32171
+ } finally {
32172
+ progress.unmount();
32173
+ }
32174
+ const payload = {
32175
+ project: {
32176
+ name: project.name,
32177
+ slug: project.slug,
32178
+ url: `${env.TOLKBOT_WEB_URL}/projects/${project.slug}`,
32179
+ baseLanguage: srcLocale,
32180
+ pattern: fullPattern(scopeOf(project)),
32181
+ autoCreatePR: project.workflow?.autoCreatePR ?? DEFAULT_BEHAVIORAL_SETTINGS.workflow.autoCreatePR
32182
+ },
32183
+ credits: { balance: credits },
32184
+ languages
32185
+ };
32186
+ const outOfDate = languages.some(needsTranslation);
32187
+ const exitCode = opts.exitCode && outOfDate ? 1 : 0;
32188
+ if (opts.json) {
32189
+ if (exitCode === 0) jsonSuccess(payload);
32190
+ jsonResult(payload, exitCode);
32191
+ return;
32192
+ }
32193
+ const { project: p } = payload;
32194
+ console.log(`${p.name} (${p.slug}) ${p.url}`);
32195
+ console.log(
32196
+ `Base language ${p.baseLanguage}, files ${p.pattern}, auto-create pull requests ${p.autoCreatePR ? "on" : "off"}`
32197
+ );
32198
+ console.log(
32199
+ credits === null ? "Credits left: could not be read" : `Credits left: ${credits}`
32200
+ );
32201
+ console.log("");
32202
+ if (languages.length === 0) {
32203
+ console.log(
32204
+ `No target languages configured. Add them in the project settings at ${p.url}/settings.`
32205
+ );
32206
+ }
32207
+ for (const language of languages) {
32208
+ for (const line of describeLanguage(language)) console.log(line);
32209
+ }
32210
+ if (outOfDate) {
32211
+ console.log(
32212
+ "\nRun 'tolkbot translate --all' to translate what is missing or changed."
32213
+ );
32214
+ }
32215
+ process.exitCode = exitCode;
32216
+ }
32217
+
32218
+ // src/commands/status/index.ts
32219
+ var statusCommand = new Command5("status").description(
32220
+ "Show what each language is missing, what waits for a person, the open pull requests and the credits left"
32221
+ ).option("--keys", "List the keys by name under each language").option(
32222
+ "--exit-code",
32223
+ "Exit 1 when any language has missing or changed keys"
32224
+ ).option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
32225
+ const schema = z7.object({
32226
+ keys: z7.boolean().optional(),
32227
+ exitCode: z7.boolean().optional(),
32228
+ json: z7.boolean().optional()
32229
+ });
32230
+ await runStatus(validateOrExit(schema, opts));
32231
+ });
32232
+
32233
+ // src/commands/telemetry.ts
32234
+ import { Command as Command6 } from "commander";
32235
+ import { z as z8 } from "zod/v4";
31988
32236
  var REASONS = {
31989
32237
  env: `disabled by $${TELEMETRY_ENV_VAR}`,
31990
32238
  "do-not-track": `disabled by $${DO_NOT_TRACK_ENV_VAR}`,
@@ -32005,12 +32253,12 @@ async function printStatus(json2) {
32005
32253
  `Turn it off with \`tolkbot telemetry off\`, $${TELEMETRY_ENV_VAR}=0 or $${DO_NOT_TRACK_ENV_VAR}=1.`
32006
32254
  );
32007
32255
  }
32008
- var telemetryCommand = new Command5("telemetry").description(
32256
+ var telemetryCommand = new Command6("telemetry").description(
32009
32257
  "Control anonymous usage reporting"
32010
32258
  );
32011
32259
  telemetryCommand.command("status", { isDefault: true }).description("Show whether telemetry is on and why").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
32012
32260
  const parsed = validateOrExit(
32013
- z7.object({ json: z7.boolean().optional() }),
32261
+ z8.object({ json: z8.boolean().optional() }),
32014
32262
  opts
32015
32263
  );
32016
32264
  await printStatus(parsed.json);
@@ -32027,8 +32275,8 @@ telemetryCommand.command("on").description("Resume reporting usage from this mac
32027
32275
  });
32028
32276
 
32029
32277
  // src/commands/translate/index.ts
32030
- import { Command as Command6 } from "commander";
32031
- import { z as z8 } from "zod/v4";
32278
+ import { Command as Command7 } from "commander";
32279
+ import { z as z9 } from "zod/v4";
32032
32280
 
32033
32281
  // src/commands/translate/adopt.tsx
32034
32282
  var STAMP_CHUNK2 = 200;
@@ -32109,8 +32357,8 @@ async function runAdopt(opts) {
32109
32357
 
32110
32358
  // src/commands/translate/edit.tsx
32111
32359
  import path10 from "path";
32112
- import { Spinner as Spinner13 } from "@inkjs/ui";
32113
- import { Box as Box16, render as render16, Text as Text16 } from "ink";
32360
+ import { Spinner as Spinner14 } from "@inkjs/ui";
32361
+ import { Box as Box17, render as render16, Text as Text17 } from "ink";
32114
32362
 
32115
32363
  // src/utils/locale-files.ts
32116
32364
  import path9 from "path";
@@ -32148,25 +32396,25 @@ function resolveKeyInFile(key, targetFile, fileCount, namespace) {
32148
32396
  }
32149
32397
 
32150
32398
  // src/commands/translate/edit.tsx
32151
- import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
32399
+ import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
32152
32400
  function EditUI({ state }) {
32153
32401
  if (state.type === "editing") {
32154
- return /* @__PURE__ */ jsxs16(Box16, { children: [
32155
- /* @__PURE__ */ jsx16(Spinner13, {}),
32156
- /* @__PURE__ */ jsx16(Text16, { children: " Editing translation\u2026" })
32402
+ return /* @__PURE__ */ jsxs17(Box17, { children: [
32403
+ /* @__PURE__ */ jsx17(Spinner14, {}),
32404
+ /* @__PURE__ */ jsx17(Text17, { children: " Editing translation\u2026" })
32157
32405
  ] });
32158
32406
  }
32159
- return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
32160
- /* @__PURE__ */ jsxs16(Box16, { children: [
32161
- /* @__PURE__ */ jsx16(Text16, { color: "green", children: "\u2713" }),
32162
- /* @__PURE__ */ jsxs16(Text16, { children: [
32407
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
32408
+ /* @__PURE__ */ jsxs17(Box17, { children: [
32409
+ /* @__PURE__ */ jsx17(Text17, { color: "green", children: "\u2713" }),
32410
+ /* @__PURE__ */ jsxs17(Text17, { children: [
32163
32411
  " ",
32164
32412
  "Updated ",
32165
- /* @__PURE__ */ jsx16(Text16, { bold: true, children: state.key })
32413
+ /* @__PURE__ */ jsx17(Text17, { bold: true, children: state.key })
32166
32414
  ] })
32167
32415
  ] }),
32168
- /* @__PURE__ */ jsx16(Box16, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: state.value }) }),
32169
- /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
32416
+ /* @__PURE__ */ jsx17(Box17, { paddingLeft: 2, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: state.value }) }),
32417
+ /* @__PURE__ */ jsx17(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
32170
32418
  "File: ",
32171
32419
  state.filePath
32172
32420
  ] }) })
@@ -32223,20 +32471,20 @@ async function runEdit(opts) {
32223
32471
  value: result2.value
32224
32472
  });
32225
32473
  }
32226
- const { unmount } = renderTransient(/* @__PURE__ */ jsx16(EditUI, { state: { type: "editing" } }));
32474
+ const { unmount } = renderTransient(/* @__PURE__ */ jsx17(EditUI, { state: { type: "editing" } }));
32227
32475
  let result;
32228
32476
  try {
32229
32477
  result = await performEdit(opts);
32230
32478
  } finally {
32231
32479
  unmount();
32232
32480
  }
32233
- render16(/* @__PURE__ */ jsx16(EditUI, { state: { type: "success", ...result } })).unmount();
32481
+ render16(/* @__PURE__ */ jsx17(EditUI, { state: { type: "success", ...result } })).unmount();
32234
32482
  }
32235
32483
 
32236
32484
  // src/commands/translate/translate.tsx
32237
- import { Spinner as Spinner14 } from "@inkjs/ui";
32238
- import { Box as Box17, Text as Text17 } from "ink";
32239
- import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
32485
+ import { Spinner as Spinner15 } from "@inkjs/ui";
32486
+ import { Box as Box18, Text as Text18 } from "ink";
32487
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
32240
32488
  function planOutput(plan) {
32241
32489
  return {
32242
32490
  ...plan.summary,
@@ -32245,7 +32493,7 @@ function planOutput(plan) {
32245
32493
  untrackedKeys: plan.untrackedKeys.map((k) => k.key)
32246
32494
  };
32247
32495
  }
32248
- function fullKey4(namespace, key) {
32496
+ function fullKey5(namespace, key) {
32249
32497
  return namespace ? `${namespace}.${key}` : key;
32250
32498
  }
32251
32499
  function characters(text) {
@@ -32273,21 +32521,11 @@ async function runTranslate(opts) {
32273
32521
  }
32274
32522
  await runLanguages(ctx, srcLocale, dstLocales, opts);
32275
32523
  }
32276
- async function creditBalance(ctx) {
32277
- try {
32278
- const balance = await ctx.client.query(api.usage.getCreditBalance, {
32279
- organizationId: ctx.project.organizationId
32280
- });
32281
- return balance?.total ?? null;
32282
- } catch {
32283
- return null;
32284
- }
32285
- }
32286
32524
  var NO_ESTIMATE = "No credit estimate exists for this project yet. Keys and characters are what a run would send.";
32287
32525
  async function runDryRun(ctx, srcLocale, dstLocales, opts) {
32288
32526
  const languages = [];
32289
32527
  const progress = renderTransient(
32290
- /* @__PURE__ */ jsx17(Working, { text: `Planning ${dstLocales.join(", ")}\u2026` })
32528
+ /* @__PURE__ */ jsx18(Working, { text: `Planning ${dstLocales.join(", ")}\u2026` })
32291
32529
  );
32292
32530
  try {
32293
32531
  for (const dstLocale of dstLocales) {
@@ -32302,7 +32540,7 @@ async function runDryRun(ctx, srcLocale, dstLocales, opts) {
32302
32540
  0
32303
32541
  ),
32304
32542
  tasks: plan.tasks.map((task) => ({
32305
- key: fullKey4(task.namespace, task.key),
32543
+ key: fullKey5(task.namespace, task.key),
32306
32544
  path: task.path,
32307
32545
  reason: task.reason,
32308
32546
  ...opts.verbose ? { src: task.src } : {}
@@ -32317,7 +32555,7 @@ async function runDryRun(ctx, srcLocale, dstLocales, opts) {
32317
32555
  keys: languages.reduce((n, l) => n + l.keys, 0),
32318
32556
  characters: languages.reduce((n, l) => n + l.characters, 0)
32319
32557
  };
32320
- const balance = await creditBalance(ctx);
32558
+ const balance = await readCreditBalance(ctx);
32321
32559
  track("cli_translate_completed", {
32322
32560
  mode: "plan",
32323
32561
  key_count: total.keys,
@@ -32365,9 +32603,9 @@ In all: ${total.keys} key(s), ${total.characters} characters.` + (balance === nu
32365
32603
  console.log("Nothing was started. Run without --dry-run to translate.");
32366
32604
  }
32367
32605
  function Working({ text }) {
32368
- return /* @__PURE__ */ jsxs17(Box17, { children: [
32369
- /* @__PURE__ */ jsx17(Spinner14, {}),
32370
- /* @__PURE__ */ jsxs17(Text17, { children: [
32606
+ return /* @__PURE__ */ jsxs18(Box18, { children: [
32607
+ /* @__PURE__ */ jsx18(Spinner15, {}),
32608
+ /* @__PURE__ */ jsxs18(Text18, { children: [
32371
32609
  " ",
32372
32610
  text
32373
32611
  ] })
@@ -32420,7 +32658,7 @@ async function runLanguages(ctx, srcLocale, dstLocales, opts) {
32420
32658
  };
32421
32659
  for (const dstLocale of dstLocales) {
32422
32660
  const progress = renderTransient(
32423
- /* @__PURE__ */ jsx17(Working, { text: `${dstLocale}: planning\u2026` })
32661
+ /* @__PURE__ */ jsx18(Working, { text: `${dstLocale}: planning\u2026` })
32424
32662
  );
32425
32663
  try {
32426
32664
  const { tasks, plan } = await generateTasks(
@@ -32453,7 +32691,7 @@ async function runLanguages(ctx, srcLocale, dstLocales, opts) {
32453
32691
  printNotes(json2, leftOut);
32454
32692
  continue;
32455
32693
  }
32456
- progress.rerender(/* @__PURE__ */ jsx17(Working, { text: progressText(dstLocale, null) }));
32694
+ progress.rerender(/* @__PURE__ */ jsx18(Working, { text: progressText(dstLocale, null) }));
32457
32695
  let jobId;
32458
32696
  try {
32459
32697
  jobId = await startTranslationJob({
@@ -32490,7 +32728,7 @@ async function runLanguages(ctx, srcLocale, dstLocales, opts) {
32490
32728
  project.organizationId,
32491
32729
  jobId,
32492
32730
  (latest) => progress.rerender(
32493
- /* @__PURE__ */ jsx17(Working, { text: progressText(dstLocale, latest) })
32731
+ /* @__PURE__ */ jsx18(Working, { text: progressText(dstLocale, latest) })
32494
32732
  )
32495
32733
  );
32496
32734
  } catch (error62) {
@@ -32515,7 +32753,7 @@ async function runLanguages(ctx, srcLocale, dstLocales, opts) {
32515
32753
  };
32516
32754
  if (summary.status !== "cancelled") {
32517
32755
  progress.rerender(
32518
- /* @__PURE__ */ jsx17(Working, { text: `${dstLocale}: writing the files\u2026` })
32756
+ /* @__PURE__ */ jsx18(Working, { text: `${dstLocale}: writing the files\u2026` })
32519
32757
  );
32520
32758
  const applied = await applyJob(ctx, jobId);
32521
32759
  finished.written = { files: applied.files, keys: applied.keys };
@@ -32579,7 +32817,7 @@ function checkTranslateOptions(opts) {
32579
32817
 
32580
32818
  // src/commands/translate/index.ts
32581
32819
  var RETRANSLATE_UNTRACKED_HELP = "Also retranslate translations TolkBot has no record of (kept by default)";
32582
- var translateCommand = new Command6("translate").description(
32820
+ var translateCommand = new Command7("translate").description(
32583
32821
  "Translate what is missing or changed, and write the results into your locale files"
32584
32822
  ).option(
32585
32823
  "--src <locale>",
@@ -32595,15 +32833,15 @@ var translateCommand = new Command6("translate").description(
32595
32833
  "Run 'tolkbot translate --help'. Jobs are under 'tolkbot jobs'."
32596
32834
  );
32597
32835
  }
32598
- const schema = z8.object({
32599
- src: z8.string().min(1).optional(),
32600
- dst: z8.string().min(1).optional(),
32601
- all: z8.boolean().optional(),
32602
- dryRun: z8.boolean().optional(),
32603
- wait: z8.boolean(),
32604
- retranslateUntracked: z8.boolean().optional(),
32605
- verbose: z8.boolean().optional(),
32606
- json: z8.boolean().optional()
32836
+ const schema = z9.object({
32837
+ src: z9.string().min(1).optional(),
32838
+ dst: z9.string().min(1).optional(),
32839
+ all: z9.boolean().optional(),
32840
+ dryRun: z9.boolean().optional(),
32841
+ wait: z9.boolean(),
32842
+ retranslateUntracked: z9.boolean().optional(),
32843
+ verbose: z9.boolean().optional(),
32844
+ json: z9.boolean().optional()
32607
32845
  });
32608
32846
  const parsed = validateOrExit(schema, opts);
32609
32847
  checkTranslateOptions(parsed);
@@ -32615,11 +32853,11 @@ translateCommand.command("adopt").description(
32615
32853
  "--src <locale>",
32616
32854
  "Source locale (defaults to the project's base language)"
32617
32855
  ).option("--dst <locale>", "Destination locale").option("--all", "Every target language").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
32618
- const schema = z8.object({
32619
- src: z8.string().min(1).optional(),
32620
- dst: z8.string().min(1).optional(),
32621
- all: z8.boolean().optional(),
32622
- json: z8.boolean().optional()
32856
+ const schema = z9.object({
32857
+ src: z9.string().min(1).optional(),
32858
+ dst: z9.string().min(1).optional(),
32859
+ all: z9.boolean().optional(),
32860
+ json: z9.boolean().optional()
32623
32861
  });
32624
32862
  const parsed = validateOrExit(schema, opts);
32625
32863
  if (parsed.dst && parsed.all) {
@@ -32628,19 +32866,19 @@ translateCommand.command("adopt").description(
32628
32866
  await runAdopt(parsed);
32629
32867
  });
32630
32868
  translateCommand.command("edit").description("Edit a translation value in a locale file").requiredOption("--key <key>", "Translation key (e.g. common.hello)").requiredOption("--locale <locale>", "Target locale (e.g. fr, de)").requiredOption("--value <text>", "New translation value").option("--namespace <ns>", "Namespace to resolve file (if ambiguous)").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
32631
- const schema = z8.object({
32632
- key: z8.string().min(1),
32633
- locale: z8.string().min(1),
32634
- value: z8.string().min(1),
32635
- namespace: z8.string().optional(),
32636
- json: z8.boolean().optional()
32869
+ const schema = z9.object({
32870
+ key: z9.string().min(1),
32871
+ locale: z9.string().min(1),
32872
+ value: z9.string().min(1),
32873
+ namespace: z9.string().optional(),
32874
+ json: z9.boolean().optional()
32637
32875
  });
32638
32876
  const parsed = validateOrExit(schema, opts);
32639
32877
  await runEdit(parsed);
32640
32878
  });
32641
32879
 
32642
32880
  // src/commands/validate.tsx
32643
- import { Box as Box18, render as render17, Text as Text18 } from "ink";
32881
+ import { Box as Box19, render as render17, Text as Text19 } from "ink";
32644
32882
 
32645
32883
  // ../ai/src/validators/placeholders.ts
32646
32884
  var IDENTIFIER = /^[A-Za-z0-9_$][A-Za-z0-9_$.[\]-]*$/;
@@ -33733,7 +33971,7 @@ function finalize2(issues, stats, strict) {
33733
33971
  }
33734
33972
 
33735
33973
  // src/commands/validate.tsx
33736
- import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
33974
+ import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
33737
33975
  var LEVEL_COLOR = {
33738
33976
  error: "red",
33739
33977
  warning: "yellow",
@@ -33742,12 +33980,12 @@ var LEVEL_COLOR = {
33742
33980
  var GUTTER = 9;
33743
33981
  function IssueLine({ issue: issue2 }) {
33744
33982
  const location = [issue2.file, issue2.key].filter(Boolean).join(" \xB7 ");
33745
- return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
33746
- /* @__PURE__ */ jsxs18(Box18, { children: [
33747
- /* @__PURE__ */ jsx18(Box18, { width: GUTTER, flexShrink: 0, children: /* @__PURE__ */ jsx18(Text18, { color: LEVEL_COLOR[issue2.level], children: issue2.level }) }),
33748
- /* @__PURE__ */ jsx18(Box18, { flexGrow: 1, children: /* @__PURE__ */ jsx18(Text18, { children: issue2.message }) })
33983
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
33984
+ /* @__PURE__ */ jsxs19(Box19, { children: [
33985
+ /* @__PURE__ */ jsx19(Box19, { width: GUTTER, flexShrink: 0, children: /* @__PURE__ */ jsx19(Text19, { color: LEVEL_COLOR[issue2.level], children: issue2.level }) }),
33986
+ /* @__PURE__ */ jsx19(Box19, { flexGrow: 1, children: /* @__PURE__ */ jsx19(Text19, { children: issue2.message }) })
33749
33987
  ] }),
33750
- /* @__PURE__ */ jsx18(Box18, { marginLeft: GUTTER, children: /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
33988
+ /* @__PURE__ */ jsx19(Box19, { marginLeft: GUTTER, children: /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
33751
33989
  issue2.rule,
33752
33990
  location ? ` \u2014 ${location}` : ""
33753
33991
  ] }) })
@@ -33756,20 +33994,20 @@ function IssueLine({ issue: issue2 }) {
33756
33994
  function ReportUI({ report }) {
33757
33995
  const { counts, stats } = report;
33758
33996
  const summary = ["error", "warning", "info"].filter((level) => counts[level] > 0).map((level) => `${counts[level]} ${level}(s)`).join(", ");
33759
- return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
33760
- /* @__PURE__ */ jsxs18(Box18, { children: [
33761
- /* @__PURE__ */ jsx18(Text18, { color: report.valid ? "green" : "red", children: report.valid ? "\u2713" : "\u2717" }),
33762
- /* @__PURE__ */ jsxs18(Text18, { children: [
33997
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
33998
+ /* @__PURE__ */ jsxs19(Box19, { children: [
33999
+ /* @__PURE__ */ jsx19(Text19, { color: report.valid ? "green" : "red", children: report.valid ? "\u2713" : "\u2717" }),
34000
+ /* @__PURE__ */ jsxs19(Text19, { children: [
33763
34001
  " Validation ",
33764
34002
  report.valid ? "passed" : "failed"
33765
34003
  ] }),
33766
- summary ? /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
34004
+ summary ? /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
33767
34005
  " \u2014 ",
33768
34006
  summary
33769
34007
  ] }) : null
33770
34008
  ] }),
33771
- stats.locales.length > 0 && /* @__PURE__ */ jsxs18(Box18, { marginTop: 1, flexDirection: "column", children: [
33772
- /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
34009
+ stats.locales.length > 0 && /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, flexDirection: "column", children: [
34010
+ /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
33773
34011
  stats.filesChecked,
33774
34012
  " ",
33775
34013
  stats.format,
@@ -33778,18 +34016,18 @@ function ReportUI({ report }) {
33778
34016
  stats.totalKeys,
33779
34017
  " keys"
33780
34018
  ] }),
33781
- /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
34019
+ /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
33782
34020
  "Locales: ",
33783
34021
  stats.locales.join(", "),
33784
34022
  stats.baseLocale ? ` (base: ${stats.baseLocale})` : ""
33785
34023
  ] }),
33786
- stats.pattern && /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
34024
+ stats.pattern && /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
33787
34025
  "Pattern: ",
33788
34026
  stats.pattern
33789
34027
  ] })
33790
34028
  ] }),
33791
- report.issues.length > 0 && /* @__PURE__ */ jsx18(Box18, { marginTop: 1, flexDirection: "column", gap: 1, children: report.issues.map((issue2, idx) => /* @__PURE__ */ jsx18(IssueLine, { issue: issue2 }, idx)) }),
33792
- !report.strict && counts.warning > 0 && report.valid && /* @__PURE__ */ jsx18(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Warnings did not fail this run. Use --strict to treat them as errors." }) })
34029
+ report.issues.length > 0 && /* @__PURE__ */ jsx19(Box19, { marginTop: 1, flexDirection: "column", gap: 1, children: report.issues.map((issue2, idx) => /* @__PURE__ */ jsx19(IssueLine, { issue: issue2 }, idx)) }),
34030
+ !report.strict && counts.warning > 0 && report.valid && /* @__PURE__ */ jsx19(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: "Warnings did not fail this run. Use --strict to treat them as errors." }) })
33793
34031
  ] });
33794
34032
  }
33795
34033
  function emit(report, json2) {
@@ -33809,7 +34047,7 @@ function emit(report, json2) {
33809
34047
  if (!report.valid) fail(validationFailure(report));
33810
34048
  return;
33811
34049
  }
33812
- render17(/* @__PURE__ */ jsx18(ReportUI, { report })).unmount();
34050
+ render17(/* @__PURE__ */ jsx19(ReportUI, { report })).unmount();
33813
34051
  process.exitCode = exitCode;
33814
34052
  }
33815
34053
  function validationFailure(report) {
@@ -33868,7 +34106,7 @@ function commandPath(command) {
33868
34106
  return names.join(" ");
33869
34107
  }
33870
34108
  function buildProgram() {
33871
- const program = new Command7().name("tolkbot").description("Localization workflow for your repo").version(version, "-v, --version", "Output the version number").helpOption("-h, --help", "Show help").helpCommand("help [command]", "Show help for a command").option("--token <token>", TOKEN_HELP).enablePositionalOptions();
34109
+ const program = new Command8().name("tolkbot").description("Localization workflow for your repo").version(version, "-v, --version", "Output the version number").helpOption("-h, --help", "Show help").helpCommand("help [command]", "Show help for a command").option("--token <token>", TOKEN_HELP).enablePositionalOptions();
33872
34110
  program.hook("preAction", (_thisCommand, actionCommand) => {
33873
34111
  const { token } = actionCommand.optsWithGlobals();
33874
34112
  setTokenOverride(token);
@@ -33898,6 +34136,7 @@ function buildProgram() {
33898
34136
  program.addCommand(glossaryCommand);
33899
34137
  program.addCommand(jobsCommand);
33900
34138
  program.addCommand(learningsCommand);
34139
+ program.addCommand(statusCommand);
33901
34140
  program.addCommand(telemetryCommand);
33902
34141
  program.addCommand(translateCommand);
33903
34142
  program.addHelpText("after", PROGRAM_HELP_FOOTER);
@@ -33955,6 +34194,7 @@ var PROGRAM_HELP_FOOTER = `
33955
34194
  Examples:
33956
34195
  $ tolkbot auth login
33957
34196
  $ tolkbot init
34197
+ $ tolkbot status
33958
34198
  $ tolkbot translate --all
33959
34199
  $ tolkbot translate --all --dry-run
33960
34200
  $ tolkbot jobs list
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tolkbot/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Command-line interface for TolkBot: AI translation for the i18n files in a GitHub repository.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://tolkbot.ai",