@sonenta/cli 0.3.1 → 0.4.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/README.md CHANGED
@@ -72,10 +72,36 @@ scope (see [Auth](#auth)).
72
72
  | `releases publish` | Trigger a CDN release (publish bundles) |
73
73
  | `snapshot` | Emit a build-time `initialBundles` module for `@sonenta/react-i18next` |
74
74
  | `missing` | List runtime-detected missing keys |
75
+ | `agents list` | List the bundled Claude agents available to install |
76
+ | `agents add <name>`| Write a bundled agent into `.claude/agents/<name>.md` |
75
77
 
76
78
  Mutating commands accept `--dry-run` and print a `created / updated / unchanged`
77
79
  summary.
78
80
 
81
+ ### Installable agents
82
+
83
+ `agents add` drops a ready-made Claude agent into the project's
84
+ `.claude/agents/` directory — usable interactively in Claude Code or headless in
85
+ CI. The flagship agent is **`sonenta-a11y`**, an accessibility auditor that
86
+ drives the Sonenta a11y MCP tools (`a11y_report`, `list_a11y_gaps`,
87
+ `a11y_estimate`, `generate_a11y_variant`, `translate_a11y_variants`,
88
+ `set_a11y_variant`) to find WCAG gaps — missing aria-labels, images without alt
89
+ text, hard-to-read copy, missing or untranslated a11y variants — and propose or
90
+ apply fixes.
91
+
92
+ ```bash
93
+ # see what's available (and what's already installed)
94
+ sonenta agents list
95
+
96
+ # install the accessibility agent into .claude/agents/
97
+ sonenta agents add sonenta-a11y
98
+ ```
99
+
100
+ The agent reaches the a11y tools through the
101
+ [`@sonenta/mcp`](https://www.npmjs.com/package/@sonenta/mcp) server, so configure
102
+ that server with an `mcp:*` key first. `--dir <path>` targets another project
103
+ directory; `--force` overwrites an existing definition.
104
+
79
105
  ### Sync workflow
80
106
 
81
107
  ```bash
package/dist/index.js CHANGED
@@ -1,26 +1,182 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command15 } from "commander";
4
+ import { Command as Command16 } from "commander";
5
+
6
+ // src/commands/agents.ts
7
+ import { Command } from "commander";
8
+
9
+ // src/agents.ts
10
+ import { promises as fs } from "fs";
11
+ import { resolve } from "path";
12
+ var AGENTS_DIR = ".claude/agents";
13
+ var SONENTA_A11Y = `---
14
+ name: sonenta-a11y
15
+ description: Accessibility (a11y) auditor and fixer for Sonenta-managed i18n projects. Scans translation keys for WCAG gaps (missing aria-labels, images without alt text, hard-to-read copy, missing or untranslated a11y variants) and proposes or applies fixes through the Sonenta a11y MCP tools. Use interactively in Claude Code or headless in CI.
16
+ ---
17
+
18
+ You are **sonenta-a11y**, an accessibility specialist for internationalized
19
+ projects managed with Sonenta. You turn an accessibility audit into concrete,
20
+ reviewable fixes, operating entirely through the Sonenta MCP server's a11y tools.
21
+
22
+ ## Requirements
23
+ - The Sonenta MCP server (\`@sonenta/mcp\`) must be configured with an \`mcp:*\`
24
+ API key. Every a11y operation goes through its tools \u2014 never call the HTTP API
25
+ directly. If the a11y tools are missing, tell the user to add the server
26
+ (\`npx -y @sonenta/mcp\`) and set \`SONENTA_API_KEY\`.
27
+ - a11y MCP tools and what they do:
28
+ - \`a11y_report\` \u2014 full WCAG gap report for the project (rollups + per-item gaps).
29
+ - \`list_a11y_gaps\` \u2014 the actionable gap list, filterable by gap / surface / locale.
30
+ - \`a11y_estimate\` \u2014 preview the CREDIT cost of a generate/translate run.
31
+ - \`generate_a11y_variant\` \u2014 AI-generate source-language a11y variants.
32
+ - \`translate_a11y_variants\` \u2014 translate existing a11y variants into locales.
33
+ - \`set_a11y_variant\` / \`delete_a11y_variant\` \u2014 set/clear one variant by hand.
34
+
35
+ ## The four a11y surfaces
36
+ - \`aria_label\` \u2014 accessible name for an interactive element (button, icon, link).
37
+ - \`alt_text\` \u2014 alternative text for an image.
38
+ - \`screen_reader\` \u2014 verbose screen-reader-only text.
39
+ - \`plain_language\` \u2014 simplified / clear-language (FALC) rendering.
40
+
41
+ a11y values are SEMANTIC (the accessible name / alt / simplified wording for
42
+ assistive tech) \u2014 not the visible UI string. Keep them concise and meaningful.
43
+
44
+ ## Gap types and how you resolve each
45
+ - \`a11y_untranslated\` \u2014 a source a11y variant exists but a target locale lacks it
46
+ \u2192 \`translate_a11y_variants\` for that \`language_code\`.
47
+ - \`alt_missing\` \u2014 an image key has no source \`alt_text\` \u2192 \`generate_a11y_variant\`
48
+ (or \`set_a11y_variant\`) for surface \`alt_text\`.
49
+ - \`reading_level_high\` \u2014 source copy is hard to read with no \`plain_language\`
50
+ \u2192 generate a \`plain_language\` variant.
51
+ - \`a11y_variant_absent\` \u2014 a required surface is missing in the source
52
+ \u2192 generate or set that surface.
53
+
54
+ ## Workflow
55
+ 1. **Scan.** Call \`a11y_report\`, passing \`require_surface\` for the surfaces this
56
+ project needs (typically \`aria_label\` and \`alt_text\`). Summarize
57
+ \`total_gaps\`, \`by_gap\`, \`by_severity\`, \`by_surface\`. Use \`list_a11y_gaps\`
58
+ to drill into a specific gap type, surface, or locale.
59
+ 2. **Triage.** Group gaps by type and severity \u2014 warnings first
60
+ (\`a11y_untranslated\`, \`alt_missing\`), then info (\`reading_level_high\`,
61
+ \`a11y_variant_absent\`).
62
+ 3. **Estimate before spending.** generate and translate BILL CREDITS. ALWAYS call
63
+ \`a11y_estimate\` first (mode \`generate\` or \`translate\`), report
64
+ \`credits_required\` vs \`balance\`, and STOP if \`sufficient\` is false.
65
+ 4. **Fix source gaps.** Prefer \`generate_a11y_variant\`, scoped with
66
+ \`key_uuids\` / \`namespace_uuid\` / \`surfaces\`. For a precise known value use
67
+ \`set_a11y_variant\`, addressing by \`key_uuid\` + locale \`code\` + \`surface\` \u2014
68
+ exactly the fields each gap already carries (no UUID resolution).
69
+ 5. **Propagate to locales.** For \`a11y_untranslated\`, run
70
+ \`translate_a11y_variants\` with the target \`language_codes\`.
71
+ 6. **Report.** Generated/translated values are **drafts** (bot-authored) awaiting
72
+ human review \u2014 never present them as final. Summarize what changed (counts,
73
+ \`credits_charged\` / \`credits_refunded\`) and what still needs review.
74
+
75
+ ## Modes
76
+ - **Interactive (Claude Code):** present the fix plan and the credit estimate,
77
+ and confirm before running any credit-billed operation unless the user already
78
+ said to proceed.
79
+ - **CI / headless:** run \`a11y_report\` and exit non-zero when \`total_gaps\` (or a
80
+ chosen severity) exceeds the project's threshold. Only auto-generate or
81
+ translate when the run is explicitly authorized to spend credits.
82
+
83
+ ## Guardrails
84
+ - Never spend credits without an estimate first.
85
+ - generate only FILLS gaps \u2014 never overwrite a human-reviewed variant blindly.
86
+ - Stay within the configured project; confirm it before any bulk operation.
87
+ `;
88
+ var AGENTS = {
89
+ "sonenta-a11y": {
90
+ name: "sonenta-a11y",
91
+ summary: "Accessibility (a11y) auditor + fixer: scans WCAG gaps and fixes them via the Sonenta a11y MCP tools.",
92
+ content: SONENTA_A11Y
93
+ }
94
+ };
95
+ function listAgents() {
96
+ return Object.values(AGENTS);
97
+ }
98
+ function getAgent(name) {
99
+ return AGENTS[name];
100
+ }
101
+ function agentInstallPath(name, baseDir = process.cwd()) {
102
+ return resolve(baseDir, AGENTS_DIR, `${name}.md`);
103
+ }
104
+ async function isInstalled(name, baseDir = process.cwd()) {
105
+ try {
106
+ await fs.access(agentInstallPath(name, baseDir));
107
+ return true;
108
+ } catch {
109
+ return false;
110
+ }
111
+ }
112
+ async function writeAgent(name, opts = {}) {
113
+ const agent = getAgent(name);
114
+ if (!agent) {
115
+ const known = Object.keys(AGENTS).join(", ");
116
+ throw new Error(`Unknown agent "${name}". Available: ${known}`);
117
+ }
118
+ const baseDir = opts.baseDir ?? process.cwd();
119
+ const path = agentInstallPath(name, baseDir);
120
+ if (!opts.force) {
121
+ try {
122
+ await fs.access(path);
123
+ throw new Error(
124
+ `${path} already exists. Pass --force to overwrite.`
125
+ );
126
+ } catch (err) {
127
+ if (err instanceof Error && err.message.includes("already exists")) throw err;
128
+ }
129
+ }
130
+ await fs.mkdir(resolve(baseDir, AGENTS_DIR), { recursive: true });
131
+ await fs.writeFile(path, agent.content, "utf8");
132
+ return path;
133
+ }
134
+
135
+ // src/commands/agents.ts
136
+ var agentsCommand = new Command("agents").description("Install bundled Claude agents (e.g. sonenta-a11y) into .claude/agents/.").addCommand(
137
+ new Command("list").description("List the bundled agents available to install.").option("--dir <path>", "Project directory (default: current directory)").action(async (opts) => {
138
+ const baseDir = opts.dir;
139
+ const agents = listAgents();
140
+ console.log(`Available agents (${agents.length}):`);
141
+ for (const a of agents) {
142
+ const installed = await isInstalled(a.name, baseDir);
143
+ const mark = installed ? "\u2713 installed" : " not installed";
144
+ console.log(` ${a.name.padEnd(16)} ${mark}`);
145
+ console.log(` ${a.summary}`);
146
+ }
147
+ console.log(`
148
+ Install with: sonenta agents add <name>`);
149
+ })
150
+ ).addCommand(
151
+ new Command("add").description("Write a bundled agent definition into <dir>/.claude/agents/<name>.md.").argument("<name>", "Agent name (e.g. sonenta-a11y)").option("--dir <path>", "Project directory (default: current directory)").option("--force", "Overwrite an existing agent definition", false).action(async (name, opts) => {
152
+ const path = await writeAgent(name, { baseDir: opts.dir, force: opts.force });
153
+ console.log(`Wrote ${path}`);
154
+ console.log(
155
+ `
156
+ The ${name} agent drives the Sonenta a11y MCP tools. Make sure the Sonenta MCP server is configured (npx -y @sonenta/mcp) with an mcp:* SONENTA_API_KEY, then use the agent in Claude Code or CI.`
157
+ );
158
+ console.log(`Agent dir: ${AGENTS_DIR}/`);
159
+ })
160
+ );
5
161
 
6
162
  // src/commands/export.ts
7
- import { promises as fs3 } from "fs";
163
+ import { promises as fs4 } from "fs";
8
164
  import { join as join2 } from "path";
9
- import { Command } from "commander";
165
+ import { Command as Command2 } from "commander";
10
166
 
11
167
  // src/config.ts
12
- import { promises as fs } from "fs";
13
- import { dirname, resolve } from "path";
168
+ import { promises as fs2 } from "fs";
169
+ import { dirname, resolve as resolve2 } from "path";
14
170
  var CONFIG_FILENAME = "sonenta.config.json";
15
171
  var LEGACY_CONFIG_FILENAME = "verbumia.config.json";
16
172
  var warnedLegacy = false;
17
173
  async function findConfigPath(startDir) {
18
- let dir = resolve(startDir);
174
+ let dir = resolve2(startDir);
19
175
  while (true) {
20
176
  for (const name of [CONFIG_FILENAME, LEGACY_CONFIG_FILENAME]) {
21
- const candidate = resolve(dir, name);
177
+ const candidate = resolve2(dir, name);
22
178
  try {
23
- await fs.access(candidate);
179
+ await fs2.access(candidate);
24
180
  if (name === LEGACY_CONFIG_FILENAME && !warnedLegacy) {
25
181
  warnedLegacy = true;
26
182
  process.emitWarning(
@@ -40,18 +196,18 @@ async function findConfigPath(startDir) {
40
196
  async function readConfig(startDir = process.cwd()) {
41
197
  const path = await findConfigPath(startDir);
42
198
  if (!path) return { path: null, config: {} };
43
- const raw = await fs.readFile(path, "utf8");
199
+ const raw = await fs2.readFile(path, "utf8");
44
200
  const parsed = JSON.parse(raw);
45
201
  return { path, config: parsed };
46
202
  }
47
203
  async function writeConfig(config, targetDir = process.cwd()) {
48
- const path = resolve(targetDir, CONFIG_FILENAME);
49
- await fs.writeFile(path, JSON.stringify(config, null, 2) + "\n", "utf8");
204
+ const path = resolve2(targetDir, CONFIG_FILENAME);
205
+ await fs2.writeFile(path, JSON.stringify(config, null, 2) + "\n", "utf8");
50
206
  return path;
51
207
  }
52
208
 
53
209
  // src/credentials.ts
54
- import { promises as fs2 } from "fs";
210
+ import { promises as fs3 } from "fs";
55
211
  import { homedir } from "os";
56
212
  import { join } from "path";
57
213
  var credentialsDir = () => join(homedir(), ".verbumia");
@@ -59,7 +215,7 @@ var credentialsPath = () => join(credentialsDir(), "credentials");
59
215
  var EMPTY = { hosts: {} };
60
216
  async function readCredentials() {
61
217
  try {
62
- const raw = await fs2.readFile(credentialsPath(), "utf8");
218
+ const raw = await fs3.readFile(credentialsPath(), "utf8");
63
219
  const parsed = JSON.parse(raw);
64
220
  if (!parsed || typeof parsed !== "object" || !parsed.hosts) {
65
221
  return EMPTY;
@@ -73,12 +229,12 @@ async function readCredentials() {
73
229
  async function writeCredentials(creds) {
74
230
  const dir = credentialsDir();
75
231
  const path = credentialsPath();
76
- await fs2.mkdir(dir, { recursive: true, mode: 448 });
232
+ await fs3.mkdir(dir, { recursive: true, mode: 448 });
77
233
  const tmp = `${path}.tmp`;
78
- await fs2.writeFile(tmp, JSON.stringify(creds, null, 2) + "\n", { mode: 384 });
79
- await fs2.rename(tmp, path);
80
- await fs2.chmod(path, 384);
81
- await fs2.chmod(dir, 448).catch(() => {
234
+ await fs3.writeFile(tmp, JSON.stringify(creds, null, 2) + "\n", { mode: 384 });
235
+ await fs3.rename(tmp, path);
236
+ await fs3.chmod(path, 384);
237
+ await fs3.chmod(dir, 448).catch(() => {
82
238
  });
83
239
  }
84
240
  async function setHostEntry(host, entry, options = {}) {
@@ -273,7 +429,7 @@ async function collect(ctx, languages, namespace) {
273
429
  }
274
430
  return out;
275
431
  }
276
- var exportCommand = new Command("export").description(
432
+ var exportCommand = new Command2("export").description(
277
433
  "Export Verbumia translations as i18next JSON. Flat dot-notation by default (--nested for nested trees). Writes <out>/<lang>/<namespace>.json with --out, otherwise prints { locale: { namespace: tree } } to stdout."
278
434
  ).option("--language <code>", "Restrict to a single language code").option("--namespace <slug>", "Restrict to a single namespace slug").option("--nested", "Emit nested JSON instead of flat dot-notation", false).option("--out <dir>", "Write files instead of printing to stdout").option("--host <url>", "Override host (otherwise from config/credentials)").action(
279
435
  async (opts) => {
@@ -286,9 +442,9 @@ var exportCommand = new Command("export").description(
286
442
  for (const [lang, nss] of Object.entries(collected)) {
287
443
  for (const [ns, flat] of Object.entries(nss)) {
288
444
  const dir = join2(opts.out, lang);
289
- await fs3.mkdir(dir, { recursive: true });
445
+ await fs4.mkdir(dir, { recursive: true });
290
446
  const p = join2(dir, `${ns}.json`);
291
- await fs3.writeFile(p, JSON.stringify(shape(flat), null, 2) + "\n", "utf8");
447
+ await fs4.writeFile(p, JSON.stringify(shape(flat), null, 2) + "\n", "utf8");
292
448
  console.log(` ${p} ${Object.keys(flat).length} keys`);
293
449
  files++;
294
450
  }
@@ -306,9 +462,9 @@ var exportCommand = new Command("export").description(
306
462
  );
307
463
 
308
464
  // src/commands/import.ts
309
- import { promises as fs4 } from "fs";
465
+ import { promises as fs5 } from "fs";
310
466
  import { basename, dirname as dirname3 } from "path";
311
- import { Command as Command2 } from "commander";
467
+ import { Command as Command3 } from "commander";
312
468
  function resolveLangNs(filePath, optLang, optNs) {
313
469
  const stem = basename(filePath).replace(/\.json$/i, "");
314
470
  const parent = basename(dirname3(filePath));
@@ -324,7 +480,7 @@ function resolveLangNs(filePath, optLang, optNs) {
324
480
  return { lang, ns };
325
481
  }
326
482
  async function readTree(filePath) {
327
- const parsed = JSON.parse(await fs4.readFile(filePath, "utf8"));
483
+ const parsed = JSON.parse(await fs5.readFile(filePath, "utf8"));
328
484
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
329
485
  throw new Error(`${filePath} is not a JSON object`);
330
486
  }
@@ -338,7 +494,7 @@ function countLeaves(tree) {
338
494
  }
339
495
  return n;
340
496
  }
341
- var importCommand = new Command2("import").description(
497
+ var importCommand = new Command3("import").description(
342
498
  "Import i18next JSON file(s) into Verbumia in ONE call \u2014 creates missing keys and upserts translations (idempotent). Accepts nested or flat JSON. Language/namespace are inferred from the path (<lang>/<namespace>.json) or forced with --language / --namespace."
343
499
  ).argument("<files...>", "i18next JSON file(s), e.g. locales/fr/common.json or fr.json").option("--language <code>", "Force the language code for every file").option("--namespace <slug>", "Force the namespace slug for every file").option("--status <status>", "draft | translated (default: translated)").option("--version <slug>", "Target version slug (default: production version)").option("--dry-run", "Print what would be imported without sending", false).option("--host <url>", "Override host (otherwise from config/credentials)").action(
344
500
  async (files, opts) => {
@@ -381,11 +537,11 @@ var importCommand = new Command2("import").description(
381
537
 
382
538
  // src/commands/init.ts
383
539
  import { existsSync } from "fs";
384
- import { resolve as resolve2 } from "path";
385
- import { Command as Command3 } from "commander";
386
- var initCommand = new Command3("init").description("Scaffold a sonenta.config.json in the current directory.").option("--host <url>", "API base URL", "https://api.sonenta.com").option("--project <uuid>", "Project UUID").option("--version <slug>", "Version slug (default: main)", "main").option("--force", "Overwrite an existing sonenta.config.json", false).action(
540
+ import { resolve as resolve3 } from "path";
541
+ import { Command as Command4 } from "commander";
542
+ var initCommand = new Command4("init").description("Scaffold a sonenta.config.json in the current directory.").option("--host <url>", "API base URL", "https://api.sonenta.com").option("--project <uuid>", "Project UUID").option("--version <slug>", "Version slug (default: main)", "main").option("--force", "Overwrite an existing sonenta.config.json", false).action(
387
543
  async (opts) => {
388
- const path = resolve2(process.cwd(), CONFIG_FILENAME);
544
+ const path = resolve3(process.cwd(), CONFIG_FILENAME);
389
545
  if (existsSync(path) && !opts.force) {
390
546
  console.error(
391
547
  `${CONFIG_FILENAME} already exists at ${path}. Pass --force to overwrite.`
@@ -407,9 +563,9 @@ var initCommand = new Command3("init").description("Scaffold a sonenta.config.js
407
563
  );
408
564
 
409
565
  // src/commands/keys.ts
410
- import { Command as Command4 } from "commander";
411
- var keysCommand = new Command4("keys").description("Inspect translation keys for the current project.").addCommand(
412
- new Command4("list").description("List keys for the configured project (addressed by namespace slug + key name).").option("--namespace <slug>", "Filter by namespace slug").option("--host <url>", "Override host (otherwise from config/credentials)").action(async (opts) => {
566
+ import { Command as Command5 } from "commander";
567
+ var keysCommand = new Command5("keys").description("Inspect translation keys for the current project.").addCommand(
568
+ new Command5("list").description("List keys for the configured project (addressed by namespace slug + key name).").option("--namespace <slug>", "Filter by namespace slug").option("--host <url>", "Override host (otherwise from config/credentials)").action(async (opts) => {
413
569
  const ctx = await resolveContext({ hostOverride: opts.host });
414
570
  const items = await listKeys(ctx, { namespace: opts.namespace });
415
571
  console.log(`total: ${items.length}`);
@@ -418,7 +574,7 @@ var keysCommand = new Command4("keys").description("Inspect translation keys for
418
574
  );
419
575
 
420
576
  // src/commands/login.ts
421
- import { Command as Command5 } from "commander";
577
+ import { Command as Command6 } from "commander";
422
578
 
423
579
  // src/prompt.ts
424
580
  import { createInterface } from "readline";
@@ -426,8 +582,8 @@ async function promptLine(message) {
426
582
  if (!process.stdin.isTTY) return "";
427
583
  const rl = createInterface({ input: process.stdin, output: process.stdout });
428
584
  try {
429
- return await new Promise((resolve4) => {
430
- rl.question(message, (answer) => resolve4(answer.trim()));
585
+ return await new Promise((resolve5) => {
586
+ rl.question(message, (answer) => resolve5(answer.trim()));
431
587
  });
432
588
  } finally {
433
589
  rl.close();
@@ -443,7 +599,7 @@ async function promptSecret(message) {
443
599
  process.stdout.write(message);
444
600
  process.stdin.setRawMode(true);
445
601
  process.stdin.resume();
446
- return await new Promise((resolve4) => {
602
+ return await new Promise((resolve5) => {
447
603
  let buffer = "";
448
604
  const onData = (chunk) => {
449
605
  for (const byte of chunk) {
@@ -452,7 +608,7 @@ async function promptSecret(message) {
452
608
  process.stdin.removeListener("data", onData);
453
609
  process.stdin.setRawMode(false);
454
610
  process.stdin.pause();
455
- resolve4(buffer);
611
+ resolve5(buffer);
456
612
  return;
457
613
  }
458
614
  if (byte === CTRL_C) {
@@ -479,7 +635,7 @@ async function promptSecret(message) {
479
635
 
480
636
  // src/commands/login.ts
481
637
  var TOKEN_REGEX = /^vrb_[a-z]+_[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
482
- var loginCommand = new Command5("login").description(
638
+ var loginCommand = new Command6("login").description(
483
639
  "Store an API key for a host. Token resolution order: --token, SONENTA_TOKEN env, then interactive prompt (TTY only)."
484
640
  ).option("--host <url>", "API base URL", "https://api.sonenta.com").option("--token <vrb_live_\u2026>", "API key token (prefix.secret form)").option("--email <email>", "User email associated with the token (optional)").option("--default", "Set this host as the default for future commands", false).action(async (opts) => {
485
641
  let host = opts.host;
@@ -511,8 +667,8 @@ var loginCommand = new Command5("login").description(
511
667
  });
512
668
 
513
669
  // src/commands/logout.ts
514
- import { Command as Command6 } from "commander";
515
- var logoutCommand = new Command6("logout").description("Remove stored credentials for a host (default: the current default host).").option("--host <url>", "Host to forget. Omit to forget the current default.").action(async (opts) => {
670
+ import { Command as Command7 } from "commander";
671
+ var logoutCommand = new Command7("logout").description("Remove stored credentials for a host (default: the current default host).").option("--host <url>", "Host to forget. Omit to forget the current default.").action(async (opts) => {
516
672
  const creds = await readCredentials();
517
673
  const target = opts.host ?? creds.default;
518
674
  if (!target) {
@@ -528,8 +684,8 @@ var logoutCommand = new Command6("logout").description("Remove stored credential
528
684
  });
529
685
 
530
686
  // src/commands/missing.ts
531
- import { Command as Command7 } from "commander";
532
- var missingCommand = new Command7("missing").description(
687
+ import { Command as Command8 } from "commander";
688
+ var missingCommand = new Command8("missing").description(
533
689
  "List runtime-detected missing keys for the configured project. Requires the API key to carry the `mcp:*` scope."
534
690
  ).option("--namespace <slug>", "Filter by namespace slug").option("--language <code>", "Filter by language code").option("--status <state>", "Filter by status (open|resolved|...)").option("--limit <n>", "Page size (1-200, default 50)", "50").option("--host <url>", "Override host (otherwise from config/credentials)").action(
535
691
  async (opts) => {
@@ -563,9 +719,9 @@ var missingCommand = new Command7("missing").description(
563
719
  );
564
720
 
565
721
  // src/commands/projects.ts
566
- import { Command as Command8 } from "commander";
567
- var projectsCommand = new Command8("projects").description("Inspect Verbumia projects accessible to your API key.").addCommand(
568
- new Command8("list").description("List the projects this API key can reach.").option("--host <url>", "Override host (otherwise from config/credentials)").action(async (opts) => {
722
+ import { Command as Command9 } from "commander";
723
+ var projectsCommand = new Command9("projects").description("Inspect Verbumia projects accessible to your API key.").addCommand(
724
+ new Command9("list").description("List the projects this API key can reach.").option("--host <url>", "Override host (otherwise from config/credentials)").action(async (opts) => {
569
725
  const ctx = await resolveContext({ hostOverride: opts.host });
570
726
  const items = await listProjects(ctx);
571
727
  if (items.length === 0) {
@@ -581,17 +737,17 @@ var projectsCommand = new Command8("projects").description("Inspect Verbumia pro
581
737
  );
582
738
 
583
739
  // src/commands/pull.ts
584
- import { Command as Command9 } from "commander";
740
+ import { Command as Command10 } from "commander";
585
741
 
586
742
  // src/locales.ts
587
- import { promises as fs5 } from "fs";
588
- import { join as join3, resolve as resolve3 } from "path";
743
+ import { promises as fs6 } from "fs";
744
+ import { join as join3, resolve as resolve4 } from "path";
589
745
  var DEFAULT_LOCALES_DIR = "locales";
590
746
  async function listLocaleFiles(rootDir) {
591
- const root = resolve3(rootDir);
747
+ const root = resolve4(rootDir);
592
748
  let langDirs;
593
749
  try {
594
- langDirs = await fs5.readdir(root);
750
+ langDirs = await fs6.readdir(root);
595
751
  } catch {
596
752
  return [];
597
753
  }
@@ -600,12 +756,12 @@ async function listLocaleFiles(rootDir) {
600
756
  const langPath = join3(root, lang);
601
757
  let stat;
602
758
  try {
603
- stat = await fs5.stat(langPath);
759
+ stat = await fs6.stat(langPath);
604
760
  } catch {
605
761
  continue;
606
762
  }
607
763
  if (!stat.isDirectory()) continue;
608
- const files = await fs5.readdir(langPath);
764
+ const files = await fs6.readdir(langPath);
609
765
  for (const f of files) {
610
766
  if (!f.endsWith(".json")) continue;
611
767
  out.push({ lang, namespace: f.replace(/\.json$/, ""), path: join3(langPath, f) });
@@ -614,7 +770,7 @@ async function listLocaleFiles(rootDir) {
614
770
  return out;
615
771
  }
616
772
  async function readLocaleFile(path) {
617
- const raw = await fs5.readFile(path, "utf8");
773
+ const raw = await fs6.readFile(path, "utf8");
618
774
  const parsed = JSON.parse(raw);
619
775
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
620
776
  throw new Error(`${path} is not a flat object`);
@@ -630,12 +786,12 @@ async function readLocaleFile(path) {
630
786
  }
631
787
  async function writeLocaleFile(rootDir, lang, namespace, values) {
632
788
  const dir = join3(rootDir, lang);
633
- await fs5.mkdir(dir, { recursive: true });
789
+ await fs6.mkdir(dir, { recursive: true });
634
790
  const path = join3(dir, `${namespace}.json`);
635
791
  const sorted = Object.fromEntries(
636
792
  Object.entries(values).sort(([a], [b]) => a.localeCompare(b))
637
793
  );
638
- await fs5.writeFile(path, JSON.stringify(sorted, null, 2) + "\n", "utf8");
794
+ await fs6.writeFile(path, JSON.stringify(sorted, null, 2) + "\n", "utf8");
639
795
  return path;
640
796
  }
641
797
  function diffFlat(local, remote) {
@@ -659,7 +815,7 @@ function diffFlat(local, remote) {
659
815
  }
660
816
 
661
817
  // src/commands/pull.ts
662
- var pullCommand = new Command9("pull").description(
818
+ var pullCommand = new Command10("pull").description(
663
819
  "Pull translations from Verbumia into locales/<lang>/<namespace>.json (flat dot-notation). Overwrites local files \u2014 pair with `git status`."
664
820
  ).option("--language <code>", "Restrict to a single language code").option("--namespace <slug>", "Restrict to a single namespace slug").option("--dest <dir>", "Output directory", DEFAULT_LOCALES_DIR).option("--host <url>", "Override host (otherwise from config/credentials)").action(
665
821
  async (opts) => {
@@ -698,8 +854,8 @@ var pullCommand = new Command9("pull").description(
698
854
  );
699
855
 
700
856
  // src/commands/push.ts
701
- import { Command as Command10 } from "commander";
702
- var pushCommand = new Command10("push").description(
857
+ import { Command as Command11 } from "commander";
858
+ var pushCommand = new Command11("push").description(
703
859
  "Push the whole local locales/ tree to Verbumia in ONE import call \u2014 creates missing keys and upserts translations (idempotent). Reads locales/<lang>/<namespace>.json (flat dot-notation)."
704
860
  ).option("--language <code>", "Restrict to a single language code").option("--namespace <slug>", "Restrict to a single namespace slug").option("--src <dir>", "Locales directory", DEFAULT_LOCALES_DIR).option("--status <status>", "draft | translated (default: translated)").option("--version <slug>", "Target version slug (default: production version)").option("--dry-run", "Print what would be pushed without sending", false).option("--host <url>", "Override host (otherwise from config/credentials)").action(
705
861
  async (opts) => {
@@ -746,9 +902,9 @@ var pushCommand = new Command10("push").description(
746
902
  );
747
903
 
748
904
  // src/commands/releases.ts
749
- import { Command as Command11 } from "commander";
750
- var releasesCommand = new Command11("releases").description("Manage CDN releases for the project.").addCommand(
751
- new Command11("publish").description(
905
+ import { Command as Command12 } from "commander";
906
+ var releasesCommand = new Command12("releases").description("Manage CDN releases for the project.").addCommand(
907
+ new Command12("publish").description(
752
908
  "Trigger a CDN release: build bundles for every (language, namespace) and push them to the public CDN. Idempotent \u2014 unchanged bundles are reused. Subscribed SDKs receive a live `translations_published` event."
753
909
  ).option("--language <code>", "Restrict to one language (default: all)").option("--namespace <slug>", "Restrict to one namespace (default: all)").option("--version <slug>", "Version slug (default: production version)").option("--dry-run", "Print the planned release without publishing", false).option("--host <url>", "Override host (otherwise from config/credentials)").action(
754
910
  async (opts) => {
@@ -774,8 +930,8 @@ var releasesCommand = new Command11("releases").description("Manage CDN releases
774
930
  );
775
931
 
776
932
  // src/commands/snapshot.ts
777
- import { promises as fs6 } from "fs";
778
- import { Command as Command12 } from "commander";
933
+ import { promises as fs7 } from "fs";
934
+ import { Command as Command13 } from "commander";
779
935
  function bundleUrl(cdnBase, project, version, lang, ns) {
780
936
  return `${cdnBase.replace(/\/+$/, "")}/p/${project}/${version}/latest/${lang}/${ns}.json`;
781
937
  }
@@ -799,7 +955,7 @@ async function fetchBundle(url) {
799
955
  if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
800
956
  return await res.json();
801
957
  }
802
- var snapshotCommand = new Command12("snapshot").description(
958
+ var snapshotCommand = new Command13("snapshot").description(
803
959
  "Generate a build-time translations snapshot for @sonenta/react-i18next `initialBundles` (offline-first fallback). Fetches the PUBLIC CDN bundles (exactly what the SDK loads at runtime) and assembles Record<locale, Record<namespace, tree>>. Emits a .ts module (default) or .json."
804
960
  ).option("--language <code>", "Restrict to a single language code").option("--namespace <slug>", "Restrict to a single namespace slug").option("--version <slug>", "Version slug (default: the configured version_slug)").option("--format <fmt>", "ts | json (default: ts)", "ts").option("--cdn <base>", "CDN base URL", "https://cdn.sonenta.com").option("--out <file>", "Write to a file instead of stdout").option("--host <url>", "Override host (used to discover languages/namespaces)").action(
805
961
  async (opts) => {
@@ -841,7 +997,7 @@ var snapshotCommand = new Command12("snapshot").description(
841
997
  opts.format === "json" ? "json" : "ts"
842
998
  );
843
999
  if (opts.out) {
844
- await fs6.writeFile(opts.out, output, "utf8");
1000
+ await fs7.writeFile(opts.out, output, "utf8");
845
1001
  console.log(
846
1002
  `wrote ${opts.out}: ${fetched} bundle(s)` + (missing ? `, ${missing} not published (404)` : "")
847
1003
  );
@@ -853,8 +1009,8 @@ var snapshotCommand = new Command12("snapshot").description(
853
1009
  );
854
1010
 
855
1011
  // src/commands/status.ts
856
- import { Command as Command13 } from "commander";
857
- var statusCommand = new Command13("status").description("Diff local locales/ against the remote project state.").option("--language <code>", "Restrict to one language code").option("--namespace <slug>", "Restrict to one namespace slug").option("--src <dir>", "Locales directory", DEFAULT_LOCALES_DIR).option("--host <url>", "Override host (otherwise from config/credentials)").action(
1012
+ import { Command as Command14 } from "commander";
1013
+ var statusCommand = new Command14("status").description("Diff local locales/ against the remote project state.").option("--language <code>", "Restrict to one language code").option("--namespace <slug>", "Restrict to one namespace slug").option("--src <dir>", "Locales directory", DEFAULT_LOCALES_DIR).option("--host <url>", "Override host (otherwise from config/credentials)").action(
858
1014
  async (opts) => {
859
1015
  const ctx = await resolveContext({ hostOverride: opts.host });
860
1016
  const knownLangs = new Set((await getProjectInfo(ctx)).languages);
@@ -912,8 +1068,8 @@ var statusCommand = new Command13("status").description("Diff local locales/ aga
912
1068
  );
913
1069
 
914
1070
  // src/commands/whoami.ts
915
- import { Command as Command14 } from "commander";
916
- var whoamiCommand = new Command14("whoami").description("Show the configured default host + which API key is in use.").option("--host <url>", "Inspect a specific host instead of the default").action(async (opts) => {
1071
+ import { Command as Command15 } from "commander";
1072
+ var whoamiCommand = new Command15("whoami").description("Show the configured default host + which API key is in use.").option("--host <url>", "Inspect a specific host instead of the default").action(async (opts) => {
917
1073
  const creds = await readCredentials();
918
1074
  if (!creds.default && Object.keys(creds.hosts).length === 0) {
919
1075
  console.log("Not logged in. Run `sonenta login --host <url> --token <\u2026>`.");
@@ -941,8 +1097,8 @@ var whoamiCommand = new Command14("whoami").description("Show the configured def
941
1097
  });
942
1098
 
943
1099
  // src/index.ts
944
- var program = new Command15();
945
- program.name("sonenta").description("CLI for Sonenta translation management.").version("0.3.0");
1100
+ var program = new Command16();
1101
+ program.name("sonenta").description("CLI for Sonenta translation management.").version("0.4.0");
946
1102
  program.addCommand(loginCommand);
947
1103
  program.addCommand(logoutCommand);
948
1104
  program.addCommand(whoamiCommand);
@@ -957,6 +1113,7 @@ program.addCommand(statusCommand);
957
1113
  program.addCommand(releasesCommand);
958
1114
  program.addCommand(snapshotCommand);
959
1115
  program.addCommand(missingCommand);
1116
+ program.addCommand(agentsCommand);
960
1117
  program.parseAsync(process.argv).catch((err) => {
961
1118
  console.error(err instanceof Error ? err.message : err);
962
1119
  process.exit(1);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/export.ts","../src/config.ts","../src/credentials.ts","../src/api.ts","../src/i18next_tree.ts","../src/mcp.ts","../src/commands/import.ts","../src/commands/init.ts","../src/commands/keys.ts","../src/commands/login.ts","../src/prompt.ts","../src/commands/logout.ts","../src/commands/missing.ts","../src/commands/projects.ts","../src/commands/pull.ts","../src/locales.ts","../src/commands/push.ts","../src/commands/releases.ts","../src/commands/snapshot.ts","../src/commands/status.ts","../src/commands/whoami.ts"],"sourcesContent":["import { Command } from \"commander\";\n\nimport { exportCommand } from \"./commands/export.js\";\nimport { importCommand } from \"./commands/import.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { keysCommand } from \"./commands/keys.js\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { logoutCommand } from \"./commands/logout.js\";\nimport { missingCommand } from \"./commands/missing.js\";\nimport { projectsCommand } from \"./commands/projects.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { releasesCommand } from \"./commands/releases.js\";\nimport { snapshotCommand } from \"./commands/snapshot.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { whoamiCommand } from \"./commands/whoami.js\";\n\nconst program = new Command();\nprogram\n .name(\"sonenta\")\n .description(\"CLI for Sonenta translation management.\")\n .version(\"0.3.0\");\n\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(whoamiCommand);\nprogram.addCommand(initCommand);\nprogram.addCommand(projectsCommand);\nprogram.addCommand(keysCommand);\nprogram.addCommand(importCommand);\nprogram.addCommand(pushCommand);\nprogram.addCommand(pullCommand);\nprogram.addCommand(exportCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(releasesCommand);\nprogram.addCommand(snapshotCommand);\nprogram.addCommand(missingCommand);\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n console.error(err instanceof Error ? err.message : err);\n process.exit(1);\n});\n","import { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { Command } from \"commander\";\n\nimport { type ApiContext, resolveContext } from \"../api.js\";\nimport { type FlatMap, sortDeep, unflatten } from \"../i18next_tree.js\";\nimport { getProjectInfo, listKeys } from \"../mcp.js\";\n\n/** language_code -> namespace_slug -> flat map of key -> value. */\ntype Collected = Record<string, Record<string, FlatMap>>;\n\nasync function collect(\n ctx: ApiContext,\n languages: string[],\n namespace?: string,\n): Promise<Collected> {\n const out: Collected = {};\n for (const lang of languages) {\n const items = await listKeys(ctx, { languageCode: lang, namespace });\n for (const it of items) {\n const tr = it.translations?.find((t) => t.language_code === lang);\n if (!tr || tr.value === \"\") continue;\n (out[lang] ??= {})[it.namespace_slug] ??= {};\n out[lang]![it.namespace_slug]![it.key_name] = tr.value;\n }\n }\n return out;\n}\n\nexport const exportCommand = new Command(\"export\")\n .description(\n \"Export Verbumia translations as i18next JSON. Flat dot-notation by \" +\n \"default (--nested for nested trees). Writes <out>/<lang>/<namespace>.json \" +\n \"with --out, otherwise prints { locale: { namespace: tree } } to stdout.\",\n )\n .option(\"--language <code>\", \"Restrict to a single language code\")\n .option(\"--namespace <slug>\", \"Restrict to a single namespace slug\")\n .option(\"--nested\", \"Emit nested JSON instead of flat dot-notation\", false)\n .option(\"--out <dir>\", \"Write files instead of printing to stdout\")\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: {\n language?: string;\n namespace?: string;\n nested: boolean;\n out?: string;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const languages = opts.language ? [opts.language] : (await getProjectInfo(ctx)).languages;\n const collected = await collect(ctx, languages, opts.namespace);\n const shape = (flat: FlatMap): unknown => (opts.nested ? sortDeep(unflatten(flat)) : sortDeep(flat));\n\n if (opts.out) {\n let files = 0;\n for (const [lang, nss] of Object.entries(collected)) {\n for (const [ns, flat] of Object.entries(nss)) {\n const dir = join(opts.out, lang);\n await fs.mkdir(dir, { recursive: true });\n const p = join(dir, `${ns}.json`);\n await fs.writeFile(p, JSON.stringify(shape(flat), null, 2) + \"\\n\", \"utf8\");\n console.log(` ${p} ${Object.keys(flat).length} keys`);\n files++;\n }\n }\n console.log(`exported ${files} file(s)`);\n return;\n }\n\n const tree: Record<string, Record<string, unknown>> = {};\n for (const [lang, nss] of Object.entries(collected)) {\n tree[lang] = {};\n for (const [ns, flat] of Object.entries(nss)) tree[lang]![ns] = shape(flat);\n }\n process.stdout.write(JSON.stringify(tree, null, 2) + \"\\n\");\n },\n );\n","import { promises as fs } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\n\n/**\n * Project-local config — `sonenta.config.json` at the repo root.\n *\n * Layout (V1, intentionally minimal):\n *\n * {\n * \"host\": \"https://api.sonenta.com\",\n * \"project_uuid\": \"069fc15d-…\",\n * \"version_slug\": \"main\"\n * }\n *\n * `host` may be omitted — commands then fall back to the user-level\n * credentials default. `version_slug` defaults to \"main\" when omitted.\n *\n * Back-compat: the legacy filename `verbumia.config.json` is still READ\n * (with a one-time deprecation warning) when no `sonenta.config.json` is\n * found, so existing projects keep working. New configs are written as\n * `sonenta.config.json`.\n */\n\nexport interface ProjectConfig {\n host?: string;\n project_uuid?: string;\n version_slug?: string;\n}\n\nexport const CONFIG_FILENAME = \"sonenta.config.json\";\nexport const LEGACY_CONFIG_FILENAME = \"verbumia.config.json\";\n\nlet warnedLegacy = false;\n\nexport async function findConfigPath(startDir: string): Promise<string | null> {\n let dir = resolve(startDir);\n // Walk up until we hit a config or the filesystem root. At each level the\n // canonical name wins over the deprecated one.\n while (true) {\n for (const name of [CONFIG_FILENAME, LEGACY_CONFIG_FILENAME]) {\n const candidate = resolve(dir, name);\n try {\n await fs.access(candidate);\n if (name === LEGACY_CONFIG_FILENAME && !warnedLegacy) {\n warnedLegacy = true;\n process.emitWarning(\n `${LEGACY_CONFIG_FILENAME} is deprecated — rename it to ${CONFIG_FILENAME}. ` +\n `The legacy name still works for now.`,\n { type: \"DeprecationWarning\" },\n );\n }\n return candidate;\n } catch {\n // Continue to the next name / parent dir.\n }\n }\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nexport async function readConfig(startDir: string = process.cwd()): Promise<{\n path: string | null;\n config: ProjectConfig;\n}> {\n const path = await findConfigPath(startDir);\n if (!path) return { path: null, config: {} };\n const raw = await fs.readFile(path, \"utf8\");\n const parsed = JSON.parse(raw) as ProjectConfig;\n return { path, config: parsed };\n}\n\nexport async function writeConfig(\n config: ProjectConfig,\n targetDir: string = process.cwd(),\n): Promise<string> {\n // Always write the canonical filename.\n const path = resolve(targetDir, CONFIG_FILENAME);\n await fs.writeFile(path, JSON.stringify(config, null, 2) + \"\\n\", \"utf8\");\n return path;\n}\n","import { promises as fs } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Per-user credentials store.\n *\n * Lives at `~/.verbumia/credentials` with file mode 0600. The shape is a\n * keyed map of host → entry, so a single user can have multiple accounts\n * (cloud + self-hosted dev + self-hosted prod) without rewriting the file\n * each time they switch hosts.\n *\n * {\n * \"default\": \"https://api.sonenta.com\",\n * \"hosts\": {\n * \"https://api.sonenta.com\": { \"api_key\": \"vrb_live_…\", \"user_email\": \"...\" },\n * \"https://api.dev.verbumia.ca\":{ \"api_key\": \"vrb_live_…\" }\n * }\n * }\n *\n * The `default` host is what `sonenta` commands target when neither the\n * project's `sonenta.config.json` nor a `--host` flag override it.\n */\n\nexport interface CredentialsEntry {\n api_key: string;\n user_email?: string;\n}\n\nexport interface CredentialsFile {\n default?: string;\n hosts: Record<string, CredentialsEntry>;\n}\n\nexport const credentialsDir = (): string => join(homedir(), \".verbumia\");\nexport const credentialsPath = (): string => join(credentialsDir(), \"credentials\");\n\nconst EMPTY: CredentialsFile = { hosts: {} };\n\nexport async function readCredentials(): Promise<CredentialsFile> {\n try {\n const raw = await fs.readFile(credentialsPath(), \"utf8\");\n const parsed = JSON.parse(raw);\n if (!parsed || typeof parsed !== \"object\" || !parsed.hosts) {\n return EMPTY;\n }\n return parsed as CredentialsFile;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") return { hosts: {} };\n throw err;\n }\n}\n\nexport async function writeCredentials(creds: CredentialsFile): Promise<void> {\n const dir = credentialsDir();\n const path = credentialsPath();\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n // Write to a temp file first to keep the original intact if the process\n // crashes mid-write, then rename atomically.\n const tmp = `${path}.tmp`;\n await fs.writeFile(tmp, JSON.stringify(creds, null, 2) + \"\\n\", { mode: 0o600 });\n await fs.rename(tmp, path);\n // chmod again in case the umask widened it on creation\n await fs.chmod(path, 0o600);\n await fs.chmod(dir, 0o700).catch(() => {});\n}\n\nexport async function setHostEntry(\n host: string,\n entry: CredentialsEntry,\n options: { makeDefault?: boolean } = {},\n): Promise<void> {\n const creds = await readCredentials();\n creds.hosts[host] = entry;\n if (options.makeDefault || !creds.default) creds.default = host;\n await writeCredentials(creds);\n}\n\nexport async function removeHost(host: string): Promise<boolean> {\n const creds = await readCredentials();\n if (!(host in creds.hosts)) return false;\n delete creds.hosts[host];\n if (creds.default === host) {\n const remaining = Object.keys(creds.hosts);\n creds.default = remaining[0];\n }\n await writeCredentials(creds);\n return true;\n}\n\nexport function resolveHostEntry(\n creds: CredentialsFile,\n hostOverride?: string,\n): { host: string; entry: CredentialsEntry } | null {\n const host = hostOverride ?? creds.default;\n if (!host) return null;\n const entry = creds.hosts[host];\n if (!entry) return null;\n return { host, entry };\n}\n","import { readConfig } from \"./config.js\";\nimport {\n type CredentialsEntry,\n readCredentials,\n resolveHostEntry,\n} from \"./credentials.js\";\n\n/**\n * Resolved request context for a CLI command:\n * - host: which Verbumia API to talk to\n * - apiKey: the bearer ApiKey token (resolved from credentials)\n * - projectUuid: optional, from project config\n * - versionSlug: defaults to \"main\"\n */\nexport interface ApiContext {\n host: string;\n apiKey: string;\n projectUuid?: string;\n versionSlug: string;\n}\n\nexport interface ResolveOptions {\n hostOverride?: string;\n cwd?: string;\n}\n\nexport async function resolveContext(opts: ResolveOptions = {}): Promise<ApiContext> {\n const { config } = await readConfig(opts.cwd ?? process.cwd());\n const creds = await readCredentials();\n const host = opts.hostOverride ?? config.host ?? creds.default;\n if (!host) {\n throw new Error(\n \"No host configured. Run `sonenta login --host <url>` or add `host` to sonenta.config.json.\",\n );\n }\n\n // Auth resolution order (first wins):\n // 1. SONENTA_TOKEN (or legacy VERBUMIA_TOKEN) env var — highest priority for CI / scripts\n // 2. ~/.verbumia/credentials entry for this host\n // The env-var path lets users run a one-off command in CI without\n // touching the credentials file or typing the token interactively.\n const envToken = process.env.SONENTA_TOKEN ?? process.env.VERBUMIA_TOKEN;\n let apiKey: string | undefined = envToken && envToken.trim() ? envToken.trim() : undefined;\n if (!apiKey) {\n const resolved = resolveHostEntry(creds, host);\n if (!resolved) {\n throw new Error(\n `No credentials for host ${host}. Set SONENTA_TOKEN or run \\`sonenta login --host ${host}\\`.`,\n );\n }\n apiKey = resolved.entry.api_key;\n }\n\n return {\n host,\n apiKey,\n projectUuid: config.project_uuid,\n versionSlug: config.version_slug ?? \"main\",\n };\n}\n\nexport async function apiRequest<T = unknown>(\n ctx: ApiContext,\n path: string,\n init: RequestInit = {},\n): Promise<T> {\n const url = `${ctx.host.replace(/\\/+$/, \"\")}${path}`;\n const headers = new Headers(init.headers);\n headers.set(\"Authorization\", `ApiKey ${ctx.apiKey}`);\n if (init.body && !headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n const res = await fetch(url, { ...init, headers });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`HTTP ${res.status} ${res.statusText} on ${path}: ${text.slice(0, 300)}`);\n }\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n}\n","/**\n * i18next tree <-> flat-map helpers.\n *\n * Verbumia stores keys as flat dot-notation (`hero.title`). i18next files on\n * disk are commonly nested (`{hero:{title:..}}`). The one-shot import endpoint\n * accepts BOTH, so `import` passes trees through untouched — these helpers are\n * for the OUTPUT side (export/snapshot) where we choose flat or nested.\n */\n\nexport type FlatMap = Record<string, string>;\nexport type Tree = Record<string, unknown>;\n\n/** Expand dot-notation flat keys into a nested tree. */\nexport function unflatten(flat: FlatMap, sep = \".\"): Tree {\n const root: Tree = {};\n for (const [k, v] of Object.entries(flat)) {\n const parts = k.split(sep);\n let node: Tree = root;\n for (let i = 0; i < parts.length - 1; i++) {\n const p = parts[i]!;\n const existing = node[p];\n if (typeof existing !== \"object\" || existing === null) node[p] = {};\n node = node[p] as Tree;\n }\n node[parts[parts.length - 1]!] = v;\n }\n return root;\n}\n\n/** Flatten a nested tree to dot-notation (string leaves only). */\nexport function flatten(tree: Tree, sep = \".\"): FlatMap {\n const out: FlatMap = {};\n const walk = (node: unknown, prefix: string): void => {\n if (!node || typeof node !== \"object\" || Array.isArray(node)) return;\n for (const [k, v] of Object.entries(node as Tree)) {\n const key = prefix ? `${prefix}${sep}${k}` : k;\n if (v && typeof v === \"object\" && !Array.isArray(v)) walk(v, key);\n else if (typeof v === \"string\") out[key] = v;\n }\n };\n walk(tree, \"\");\n return out;\n}\n\n/** Recursively sort object keys for deterministic, diff-friendly output. */\nexport function sortDeep<T>(value: T): T {\n if (Array.isArray(value)) return value.map((v) => sortDeep(v)) as unknown as T;\n if (value && typeof value === \"object\") {\n const src = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const k of Object.keys(src).sort((a, b) => a.localeCompare(b))) out[k] = sortDeep(src[k]);\n return out as T;\n }\n return value;\n}\n","/**\n * MCP-surface client helpers.\n *\n * The whole CLI talks to the metered, key-addressable MCP surface\n * `/v1/mcp/projects/{project_id}/...` (auth = an API key carrying the `mcp:*`\n * scope). That surface addresses keys/namespaces/languages by NAME (no uuids),\n * exposes bulk + one-shot endpoints, and is the contract-blessed automation\n * entrypoint. These helpers wrap the endpoints the commands consume.\n */\n\nimport { type ApiContext, apiRequest } from \"./api.js\";\n\nexport function requireProject(ctx: ApiContext): string {\n if (!ctx.projectUuid) {\n throw new Error(\n \"no project configured — run `sonenta init --project <uuid>` \" +\n \"(or set project_uuid in sonenta.config.json).\",\n );\n }\n return ctx.projectUuid;\n}\n\nfunction projectBase(ctx: ApiContext): string {\n return `/v1/mcp/projects/${requireProject(ctx)}`;\n}\n\n// ---- projects ------------------------------------------------------------\n\nexport interface ProjectSummary {\n uuid: string;\n name?: string;\n slug?: string;\n source_language?: string;\n}\n\nexport async function listProjects(ctx: ApiContext, limit = 200): Promise<ProjectSummary[]> {\n const data = await apiRequest<{ items: ProjectSummary[] }>(\n ctx,\n `/v1/mcp/projects?limit=${limit}`,\n );\n return data.items ?? [];\n}\n\nexport interface ProjectInfo {\n source_language: string;\n languages: string[];\n namespaces: string[];\n}\n\n/** Normalises get_project_info into plain code/slug lists (shape-tolerant). */\nexport async function getProjectInfo(ctx: ApiContext): Promise<ProjectInfo> {\n const d = await apiRequest<Record<string, unknown>>(ctx, projectBase(ctx));\n const src =\n typeof d.source_language === \"string\"\n ? d.source_language\n : (d.source_language as { code?: string })?.code ?? \"\";\n const langs = Array.isArray(d.languages)\n ? (d.languages as unknown[]).map((l) =>\n typeof l === \"string\" ? l : ((l as { code?: string }).code ?? \"\"),\n )\n : [];\n const ns = Array.isArray(d.namespaces)\n ? (d.namespaces as unknown[]).map((n) =>\n typeof n === \"string\" ? n : ((n as { slug?: string }).slug ?? \"\"),\n )\n : [];\n return {\n source_language: src,\n languages: langs.filter(Boolean),\n namespaces: ns.filter(Boolean),\n };\n}\n\n// ---- keys (paginated list) ----------------------------------------------\n\nexport interface KeyTranslation {\n language_code: string;\n value: string;\n status: string;\n}\n\nexport interface KeyItem {\n key_name: string;\n namespace_slug: string;\n source_value: string | null;\n translations?: KeyTranslation[];\n}\n\nexport async function listKeys(\n ctx: ApiContext,\n opts: { languageCode?: string; namespace?: string } = {},\n): Promise<KeyItem[]> {\n const out: KeyItem[] = [];\n let cursor: string | null = null;\n do {\n const params = new URLSearchParams({ limit: \"200\" });\n if (opts.languageCode) params.set(\"language_code\", opts.languageCode);\n if (opts.namespace) params.set(\"namespace\", opts.namespace);\n if (cursor) params.set(\"cursor\", cursor);\n const page = await apiRequest<{ items: KeyItem[]; cursor_next: string | null }>(\n ctx,\n `${projectBase(ctx)}/keys?${params.toString()}`,\n );\n out.push(...(page.items ?? []));\n cursor = page.cursor_next ?? null;\n } while (cursor);\n return out;\n}\n\n// ---- i18next one-shot import --------------------------------------------\n\nexport type I18nextTree = Record<string, unknown>;\n\nexport interface ImportNamespaceUnit {\n namespace: string;\n translations: Record<string, I18nextTree>; // language_code -> i18next tree (nested or flat)\n}\n\nexport interface ImportBody {\n namespaces: ImportNamespaceUnit[];\n version?: string;\n status?: \"draft\" | \"translated\";\n}\n\nexport interface ImportBundleReport {\n project_uuid: string;\n version_slug: string;\n keys_created: number;\n keys_reused: number;\n translations_created: number;\n translations_updated: number;\n translations_unchanged: number;\n plural_keys_marked: number;\n units?: unknown[];\n errors?: { namespace: string; language_code: string; code: string }[];\n glossary_violation_count?: number;\n glossary_violations?: {\n namespace: string;\n language_code: string;\n key: string;\n rule_type: string;\n term: string;\n matched_text?: string;\n message?: string;\n }[];\n}\n\nexport async function importBundle(ctx: ApiContext, body: ImportBody): Promise<ImportBundleReport> {\n return apiRequest<ImportBundleReport>(ctx, `${projectBase(ctx)}/i18next/import`, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n}\n\n// ---- CDN releases (publish) ---------------------------------------------\n\nexport interface PublishCdnBody {\n language_code?: string;\n namespace?: string;\n version_slug?: string;\n}\n\nexport async function publishCdn(ctx: ApiContext, body: PublishCdnBody): Promise<unknown> {\n return apiRequest(ctx, `${projectBase(ctx)}/cdn/releases`, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n}\n\n// ---- formatting ----------------------------------------------------------\n\n/** Human-readable lines for an ImportBundleReport (created/updated/unchanged). */\nexport function summariseImport(r: ImportBundleReport): string[] {\n const lines = [\n `keys: ${r.keys_created} created, ${r.keys_reused} reused`,\n `translations: ${r.translations_created} created, ${r.translations_updated} updated, ${r.translations_unchanged} unchanged`,\n ];\n if (r.plural_keys_marked) lines.push(`plural keys: ${r.plural_keys_marked} marked`);\n if (r.errors?.length) {\n lines.push(`errors: ${r.errors.length}`);\n for (const e of r.errors) lines.push(` ! ${e.namespace}/${e.language_code}: ${e.code}`);\n }\n if (r.glossary_violations?.length) {\n lines.push(`glossary: ${r.glossary_violations.length} violation(s)`);\n for (const g of r.glossary_violations) {\n lines.push(` ⚠ ${g.namespace}/${g.language_code} ${g.key}: ${g.rule_type} \"${g.term}\"`);\n }\n }\n return lines;\n}\n","import { promises as fs } from \"node:fs\";\nimport { basename, dirname } from \"node:path\";\n\nimport { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { type I18nextTree, type ImportBody, importBundle, summariseImport } from \"../mcp.js\";\n\n/**\n * Resolve (language, namespace) for a file.\n * - explicit --language / --namespace always win;\n * - otherwise infer from the i18next layout `<lang>/<namespace>.json`\n * (parent dir = language, filename stem = namespace);\n * - a bare `<lang>.json` (no language dir) uses the stem as the language and\n * REQUIRES --namespace.\n */\nexport function resolveLangNs(\n filePath: string,\n optLang?: string,\n optNs?: string,\n): { lang: string; ns: string } {\n const stem = basename(filePath).replace(/\\.json$/i, \"\");\n const parent = basename(dirname(filePath));\n const hasLangDir = parent !== \"\" && parent !== \".\" && parent !== \"locales\";\n const lang = optLang ?? (hasLangDir ? parent : stem);\n const ns = optNs ?? (hasLangDir ? stem : undefined);\n if (!lang) throw new Error(`could not infer a language for ${filePath} — pass --language`);\n if (!ns) {\n throw new Error(\n `could not infer a namespace for ${filePath} — pass --namespace, ` +\n \"or lay files out as <lang>/<namespace>.json\",\n );\n }\n return { lang, ns };\n}\n\nasync function readTree(filePath: string): Promise<I18nextTree> {\n const parsed = JSON.parse(await fs.readFile(filePath, \"utf8\"));\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`${filePath} is not a JSON object`);\n }\n return parsed as I18nextTree;\n}\n\nfunction countLeaves(tree: I18nextTree): number {\n let n = 0;\n for (const v of Object.values(tree)) {\n if (v && typeof v === \"object\" && !Array.isArray(v)) n += countLeaves(v as I18nextTree);\n else n += 1;\n }\n return n;\n}\n\nexport const importCommand = new Command(\"import\")\n .description(\n \"Import i18next JSON file(s) into Verbumia in ONE call — creates missing \" +\n \"keys and upserts translations (idempotent). Accepts nested or flat JSON. \" +\n \"Language/namespace are inferred from the path (<lang>/<namespace>.json) \" +\n \"or forced with --language / --namespace.\",\n )\n .argument(\"<files...>\", \"i18next JSON file(s), e.g. locales/fr/common.json or fr.json\")\n .option(\"--language <code>\", \"Force the language code for every file\")\n .option(\"--namespace <slug>\", \"Force the namespace slug for every file\")\n .option(\"--status <status>\", \"draft | translated (default: translated)\")\n .option(\"--version <slug>\", \"Target version slug (default: production version)\")\n .option(\"--dry-run\", \"Print what would be imported without sending\", false)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (\n files: string[],\n opts: {\n language?: string;\n namespace?: string;\n status?: string;\n version?: string;\n dryRun: boolean;\n host?: string;\n },\n ) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n\n // namespace -> language_code -> tree\n const byNs = new Map<string, Record<string, I18nextTree>>();\n let totalLeaves = 0;\n for (const f of files) {\n const { lang, ns } = resolveLangNs(f, opts.language, opts.namespace);\n const tree = await readTree(f);\n totalLeaves += countLeaves(tree);\n const langs = byNs.get(ns) ?? {};\n if (langs[lang]) {\n throw new Error(`two input files map to namespace=${ns} language=${lang} — merge them first`);\n }\n langs[lang] = tree;\n byNs.set(ns, langs);\n console.log(` ${f} -> ${ns} / ${lang}`);\n }\n\n const body: ImportBody = {\n namespaces: [...byNs.entries()].map(([namespace, translations]) => ({\n namespace,\n translations,\n })),\n };\n if (opts.status === \"draft\" || opts.status === \"translated\") body.status = opts.status;\n if (opts.version) body.version = opts.version;\n\n if (opts.dryRun) {\n console.log(\n `\\n(dry-run) would import ${totalLeaves} value(s) across ${body.namespaces.length} ` +\n \"namespace(s); nothing sent.\",\n );\n return;\n }\n\n const report = await importBundle(ctx, body);\n console.log(\"\");\n for (const line of summariseImport(report)) console.log(line);\n if (report.errors?.length || report.glossary_violations?.length) process.exitCode = 1;\n },\n );\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nimport { Command } from \"commander\";\n\nimport { CONFIG_FILENAME, writeConfig } from \"../config.js\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Scaffold a sonenta.config.json in the current directory.\")\n .option(\"--host <url>\", \"API base URL\", \"https://api.sonenta.com\")\n .option(\"--project <uuid>\", \"Project UUID\")\n .option(\"--version <slug>\", \"Version slug (default: main)\", \"main\")\n .option(\"--force\", \"Overwrite an existing sonenta.config.json\", false)\n .action(\n async (opts: { host: string; project?: string; version: string; force: boolean }) => {\n const path = resolve(process.cwd(), CONFIG_FILENAME);\n if (existsSync(path) && !opts.force) {\n console.error(\n `${CONFIG_FILENAME} already exists at ${path}. Pass --force to overwrite.`,\n );\n process.exit(1);\n }\n const written = await writeConfig({\n host: opts.host,\n project_uuid: opts.project,\n version_slug: opts.version,\n });\n console.log(`Wrote ${written}`);\n if (!opts.project) {\n console.log(\n \"Tip: pass --project <uuid> to bind this directory to a specific project \" +\n \"(or edit project_uuid in the file later).\",\n );\n }\n },\n );\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { listKeys } from \"../mcp.js\";\n\nexport const keysCommand = new Command(\"keys\")\n .description(\"Inspect translation keys for the current project.\")\n .addCommand(\n new Command(\"list\")\n .description(\"List keys for the configured project (addressed by namespace slug + key name).\")\n .option(\"--namespace <slug>\", \"Filter by namespace slug\")\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(async (opts: { namespace?: string; host?: string }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const items = await listKeys(ctx, { namespace: opts.namespace });\n console.log(`total: ${items.length}`);\n for (const k of items) console.log(` ${k.namespace_slug}/${k.key_name}`);\n }),\n );\n","import { Command } from \"commander\";\n\nimport { setHostEntry } from \"../credentials.js\";\nimport { promptLine, promptSecret } from \"../prompt.js\";\n\nconst TOKEN_REGEX = /^vrb_[a-z]+_[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\n\nexport const loginCommand = new Command(\"login\")\n .description(\n \"Store an API key for a host. Token resolution order: --token, \" +\n \"SONENTA_TOKEN env, then interactive prompt (TTY only).\",\n )\n .option(\"--host <url>\", \"API base URL\", \"https://api.sonenta.com\")\n .option(\"--token <vrb_live_…>\", \"API key token (prefix.secret form)\")\n .option(\"--email <email>\", \"User email associated with the token (optional)\")\n .option(\"--default\", \"Set this host as the default for future commands\", false)\n .action(async (opts: { host: string; token?: string; email?: string; default?: boolean }) => {\n let host = opts.host;\n if (!host && process.stdin.isTTY) {\n host = (await promptLine(\"Host (default https://api.sonenta.com): \")) || \"https://api.sonenta.com\";\n }\n let token = opts.token ?? (process.env.SONENTA_TOKEN ?? process.env.VERBUMIA_TOKEN) ?? \"\";\n if (!token && process.stdin.isTTY) {\n token = await promptSecret(`API token for ${host}: `);\n }\n if (!token) {\n console.error(\n \"sonenta login: token required. Pass --token, set SONENTA_TOKEN, or run from a TTY for the interactive prompt.\",\n );\n process.exit(1);\n }\n if (!TOKEN_REGEX.test(token)) {\n console.error(\n \"sonenta login: token shape doesn't match `vrb_<env>_<prefix>.<secret>`. \" +\n \"Generate one in the dashboard at Org Settings → API Keys.\",\n );\n process.exit(1);\n }\n await setHostEntry(\n host,\n { api_key: token, user_email: opts.email },\n { makeDefault: opts.default },\n );\n console.log(`Stored credentials for ${host}.`);\n });\n","import { createInterface } from \"node:readline\";\n\n/**\n * Read a line from stdin. Used for interactive prompts when --token / --host\n * aren't passed on the CLI. Returns \"\" if stdin isn't a TTY (so tests + piped\n * usage don't hang waiting for input that will never arrive).\n */\nexport async function promptLine(message: string): Promise<string> {\n if (!process.stdin.isTTY) return \"\";\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n return await new Promise<string>((resolve) => {\n rl.question(message, (answer) => resolve(answer.trim()));\n });\n } finally {\n rl.close();\n }\n}\n\nconst CTRL_C = 0x03;\nconst BACKSPACE = 0x08;\nconst DEL = 0x7f;\nconst CR = 0x0d;\nconst LF = 0x0a;\n\n/**\n * Same as promptLine but masks each echoed character with `*`. If stdin\n * isn't a TTY we return \"\" rather than echoing the secret.\n */\nexport async function promptSecret(message: string): Promise<string> {\n if (!process.stdin.isTTY) return \"\";\n process.stdout.write(message);\n process.stdin.setRawMode(true);\n process.stdin.resume();\n return await new Promise<string>((resolve) => {\n let buffer = \"\";\n const onData = (chunk: Buffer): void => {\n for (const byte of chunk) {\n if (byte === CR || byte === LF) {\n process.stdout.write(\"\\n\");\n process.stdin.removeListener(\"data\", onData);\n process.stdin.setRawMode(false);\n process.stdin.pause();\n resolve(buffer);\n return;\n }\n if (byte === CTRL_C) {\n process.stdout.write(\"\\n\");\n process.stdin.removeListener(\"data\", onData);\n process.stdin.setRawMode(false);\n process.stdin.pause();\n process.exit(130);\n }\n if (byte === BACKSPACE || byte === DEL) {\n if (buffer.length > 0) {\n buffer = buffer.slice(0, -1);\n process.stdout.write(\"\\b \\b\");\n }\n continue;\n }\n buffer += String.fromCharCode(byte);\n process.stdout.write(\"*\");\n }\n };\n process.stdin.on(\"data\", onData);\n });\n}\n","import { Command } from \"commander\";\n\nimport { readCredentials, removeHost } from \"../credentials.js\";\n\nexport const logoutCommand = new Command(\"logout\")\n .description(\"Remove stored credentials for a host (default: the current default host).\")\n .option(\"--host <url>\", \"Host to forget. Omit to forget the current default.\")\n .action(async (opts: { host?: string }) => {\n const creds = await readCredentials();\n const target = opts.host ?? creds.default;\n if (!target) {\n console.log(\"Nothing to forget — no credentials stored.\");\n return;\n }\n const removed = await removeHost(target);\n if (!removed) {\n console.log(`No credentials stored for ${target}.`);\n return;\n }\n console.log(`Forgot credentials for ${target}.`);\n });\n","import { Command } from \"commander\";\n\nimport { apiRequest, resolveContext } from \"../api.js\";\n\ninterface MissingKey {\n uuid: string;\n namespace_slug: string;\n language_code: string;\n key: string;\n source_value: string | null;\n count: number;\n status: string;\n first_seen: string;\n last_seen: string;\n}\n\ninterface MissingKeysPage {\n items: MissingKey[];\n cursor_next: string | null;\n total: number;\n}\n\nexport const missingCommand = new Command(\"missing\")\n .description(\n \"List runtime-detected missing keys for the configured project. Requires \" +\n \"the API key to carry the `mcp:*` scope.\",\n )\n .option(\"--namespace <slug>\", \"Filter by namespace slug\")\n .option(\"--language <code>\", \"Filter by language code\")\n .option(\"--status <state>\", \"Filter by status (open|resolved|...)\")\n .option(\"--limit <n>\", \"Page size (1-200, default 50)\", \"50\")\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: {\n namespace?: string;\n language?: string;\n status?: string;\n limit: string;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n if (!ctx.projectUuid) {\n console.error(\n \"sonenta missing: no project_uuid configured. Run `sonenta init --project <uuid>`.\",\n );\n process.exit(1);\n }\n const params = new URLSearchParams();\n params.set(\"limit\", opts.limit);\n if (opts.namespace) params.set(\"namespace\", opts.namespace);\n if (opts.language) params.set(\"language_code\", opts.language);\n if (opts.status) params.set(\"status\", opts.status);\n const page = await apiRequest<MissingKeysPage>(\n ctx,\n `/v1/mcp/projects/${ctx.projectUuid}/missing-keys?${params.toString()}`,\n );\n console.log(`total: ${page.total}`);\n for (const m of page.items) {\n const sample = m.source_value ? ` ⟶ \"${m.source_value}\"` : \"\";\n console.log(\n ` ${m.namespace_slug}/${m.key} (${m.language_code}, count=${m.count}, ${m.status})${sample}`,\n );\n }\n if (page.cursor_next) {\n console.log(`(more — re-run with --cursor ${page.cursor_next})`);\n }\n },\n );\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { listProjects } from \"../mcp.js\";\n\nexport const projectsCommand = new Command(\"projects\")\n .description(\"Inspect Verbumia projects accessible to your API key.\")\n .addCommand(\n new Command(\"list\")\n .description(\"List the projects this API key can reach.\")\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(async (opts: { host?: string }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const items = await listProjects(ctx);\n if (items.length === 0) {\n console.log(\"No projects visible to this key (scoped or revoked?).\");\n return;\n }\n for (const p of items) {\n const slug = p.slug ? ` ${p.slug}` : \"\";\n const src = p.source_language ? ` src=${p.source_language}` : \"\";\n console.log(` ${p.uuid}${slug} ${p.name ?? \"\"}${src}`);\n }\n }),\n );\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { DEFAULT_LOCALES_DIR, type FlatTranslations, writeLocaleFile } from \"../locales.js\";\nimport { getProjectInfo, listKeys } from \"../mcp.js\";\n\nexport const pullCommand = new Command(\"pull\")\n .description(\n \"Pull translations from Verbumia into locales/<lang>/<namespace>.json \" +\n \"(flat dot-notation). Overwrites local files — pair with `git status`.\",\n )\n .option(\"--language <code>\", \"Restrict to a single language code\")\n .option(\"--namespace <slug>\", \"Restrict to a single namespace slug\")\n .option(\"--dest <dir>\", \"Output directory\", DEFAULT_LOCALES_DIR)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: { language?: string; namespace?: string; dest: string; host?: string }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n let languages: string[];\n if (opts.language) {\n languages = [opts.language];\n } else {\n languages = (await getProjectInfo(ctx)).languages;\n if (languages.length === 0) {\n console.error(\"sonenta pull: the project reports no languages.\");\n process.exit(1);\n }\n }\n\n let totalKeys = 0;\n let totalFiles = 0;\n for (const lang of languages) {\n const items = await listKeys(ctx, { languageCode: lang, namespace: opts.namespace });\n const byNs = new Map<string, FlatTranslations>();\n for (const it of items) {\n const tr = it.translations?.find((t) => t.language_code === lang);\n if (!tr || tr.value === \"\") continue;\n const map = byNs.get(it.namespace_slug) ?? {};\n map[it.key_name] = tr.value;\n byNs.set(it.namespace_slug, map);\n }\n for (const [ns, map] of byNs) {\n const path = await writeLocaleFile(opts.dest, lang, ns, map);\n totalKeys += Object.keys(map).length;\n totalFiles++;\n console.log(` ${path} ${Object.keys(map).length} keys`);\n }\n }\n console.log(`pulled ${totalKeys} translation(s) across ${totalFiles} file(s)`);\n },\n );\n","import { promises as fs } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\n\n/**\n * On-disk locales layout. V1 is flat: one JSON per (lang, namespace) under\n * the configured locales directory:\n *\n * locales/\n * en/\n * common.json // { \"hello.title\": \"Hello\", ... }\n * errors.json\n * fr/\n * common.json\n *\n * Keys inside each file are flat dot-notation, NOT nested objects. This\n * matches the editor surface 1:1 and keeps push/pull idempotent.\n */\n\nexport const DEFAULT_LOCALES_DIR = \"locales\";\n\nexport type FlatTranslations = Record<string, string>;\n\nexport async function listLocaleFiles(\n rootDir: string,\n): Promise<{ lang: string; namespace: string; path: string }[]> {\n const root = resolve(rootDir);\n let langDirs: string[];\n try {\n langDirs = await fs.readdir(root);\n } catch {\n return [];\n }\n const out: { lang: string; namespace: string; path: string }[] = [];\n for (const lang of langDirs) {\n const langPath = join(root, lang);\n let stat;\n try {\n stat = await fs.stat(langPath);\n } catch {\n continue;\n }\n if (!stat.isDirectory()) continue;\n const files = await fs.readdir(langPath);\n for (const f of files) {\n if (!f.endsWith(\".json\")) continue;\n out.push({ lang, namespace: f.replace(/\\.json$/, \"\"), path: join(langPath, f) });\n }\n }\n return out;\n}\n\nexport async function readLocaleFile(path: string): Promise<FlatTranslations> {\n const raw = await fs.readFile(path, \"utf8\");\n const parsed = JSON.parse(raw);\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`${path} is not a flat object`);\n }\n const out: FlatTranslations = {};\n for (const [k, v] of Object.entries(parsed)) {\n if (typeof v !== \"string\") {\n throw new Error(`${path}: key \"${k}\" is not a string (V1 flat layout only)`);\n }\n out[k] = v;\n }\n return out;\n}\n\nexport async function writeLocaleFile(\n rootDir: string,\n lang: string,\n namespace: string,\n values: FlatTranslations,\n): Promise<string> {\n const dir = join(rootDir, lang);\n await fs.mkdir(dir, { recursive: true });\n const path = join(dir, `${namespace}.json`);\n // Sort keys for deterministic diffs.\n const sorted = Object.fromEntries(\n Object.entries(values).sort(([a], [b]) => a.localeCompare(b)),\n );\n await fs.writeFile(path, JSON.stringify(sorted, null, 2) + \"\\n\", \"utf8\");\n return path;\n}\n\nexport interface DiffResult {\n added: string[]; // local-only — push will create\n removed: string[]; // remote-only — push leaves alone (V1 doesn't delete)\n changed: { key: string; local: string; remote: string }[];\n unchanged: number;\n}\n\nexport function diffFlat(local: FlatTranslations, remote: FlatTranslations): DiffResult {\n const added: string[] = [];\n const removed: string[] = [];\n const changed: { key: string; local: string; remote: string }[] = [];\n let unchanged = 0;\n\n for (const [k, v] of Object.entries(local)) {\n if (!(k in remote)) {\n added.push(k);\n } else if (remote[k] !== v) {\n changed.push({ key: k, local: v, remote: remote[k]! });\n } else {\n unchanged++;\n }\n }\n for (const k of Object.keys(remote)) {\n if (!(k in local)) removed.push(k);\n }\n return { added, removed, changed, unchanged };\n}\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { DEFAULT_LOCALES_DIR, listLocaleFiles, readLocaleFile } from \"../locales.js\";\nimport { type ImportBody, importBundle, summariseImport } from \"../mcp.js\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\n \"Push the whole local locales/ tree to Verbumia in ONE import call — \" +\n \"creates missing keys and upserts translations (idempotent). Reads \" +\n \"locales/<lang>/<namespace>.json (flat dot-notation).\",\n )\n .option(\"--language <code>\", \"Restrict to a single language code\")\n .option(\"--namespace <slug>\", \"Restrict to a single namespace slug\")\n .option(\"--src <dir>\", \"Locales directory\", DEFAULT_LOCALES_DIR)\n .option(\"--status <status>\", \"draft | translated (default: translated)\")\n .option(\"--version <slug>\", \"Target version slug (default: production version)\")\n .option(\"--dry-run\", \"Print what would be pushed without sending\", false)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: {\n language?: string;\n namespace?: string;\n src: string;\n status?: string;\n version?: string;\n dryRun: boolean;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const files = (await listLocaleFiles(opts.src)).filter((f) => {\n if (opts.language && f.lang !== opts.language) return false;\n if (opts.namespace && f.namespace !== opts.namespace) return false;\n return true;\n });\n if (files.length === 0) {\n console.log(`No locale files found under ${opts.src}/`);\n return;\n }\n\n // namespace -> language_code -> flat map\n const byNs = new Map<string, Record<string, Record<string, string>>>();\n let totalKeys = 0;\n for (const f of files) {\n const flat = await readLocaleFile(f.path);\n totalKeys += Object.keys(flat).length;\n const langs = byNs.get(f.namespace) ?? {};\n langs[f.lang] = flat;\n byNs.set(f.namespace, langs);\n console.log(` ${f.lang}/${f.namespace}.json ${Object.keys(flat).length} keys`);\n }\n\n const body: ImportBody = {\n namespaces: [...byNs.entries()].map(([namespace, translations]) => ({\n namespace,\n translations,\n })),\n };\n if (opts.status === \"draft\" || opts.status === \"translated\") body.status = opts.status;\n if (opts.version) body.version = opts.version;\n\n if (opts.dryRun) {\n console.log(\n `\\n(dry-run) would push ${totalKeys} key(s) across ${body.namespaces.length} ` +\n \"namespace(s); nothing sent.\",\n );\n return;\n }\n\n const report = await importBundle(ctx, body);\n console.log(\"\");\n for (const line of summariseImport(report)) console.log(line);\n if (report.errors?.length || report.glossary_violations?.length) process.exitCode = 1;\n },\n );\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { type PublishCdnBody, publishCdn } from \"../mcp.js\";\n\nexport const releasesCommand = new Command(\"releases\")\n .description(\"Manage CDN releases for the project.\")\n .addCommand(\n new Command(\"publish\")\n .description(\n \"Trigger a CDN release: build bundles for every (language, namespace) \" +\n \"and push them to the public CDN. Idempotent — unchanged bundles are \" +\n \"reused. Subscribed SDKs receive a live `translations_published` event.\",\n )\n .option(\"--language <code>\", \"Restrict to one language (default: all)\")\n .option(\"--namespace <slug>\", \"Restrict to one namespace (default: all)\")\n .option(\"--version <slug>\", \"Version slug (default: production version)\")\n .option(\"--dry-run\", \"Print the planned release without publishing\", false)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: {\n language?: string;\n namespace?: string;\n version?: string;\n dryRun: boolean;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const scope =\n [\n opts.language && `language=${opts.language}`,\n opts.namespace && `namespace=${opts.namespace}`,\n opts.version && `version=${opts.version}`,\n ]\n .filter(Boolean)\n .join(\", \") || \"ALL languages + namespaces\";\n\n if (opts.dryRun) {\n console.log(`(dry-run) would publish a CDN release for ${scope}; nothing sent.`);\n return;\n }\n\n const body: PublishCdnBody = {};\n if (opts.language) body.language_code = opts.language;\n if (opts.namespace) body.namespace = opts.namespace;\n if (opts.version) body.version_slug = opts.version;\n const res = await publishCdn(ctx, body);\n console.log(`published CDN release for ${scope}`);\n console.log(JSON.stringify(res, null, 2));\n },\n ),\n );\n","import { promises as fs } from \"node:fs\";\n\nimport { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { getProjectInfo, requireProject } from \"../mcp.js\";\n\ntype Tree = Record<string, unknown>;\ntype Bundles = Record<string, Record<string, Tree>>;\n\n/** Public CDN bundle URL — the exact path the SDK fetches at runtime. */\nexport function bundleUrl(\n cdnBase: string,\n project: string,\n version: string,\n lang: string,\n ns: string,\n): string {\n return `${cdnBase.replace(/\\/+$/, \"\")}/p/${project}/${version}/latest/${lang}/${ns}.json`;\n}\n\n/**\n * Render the snapshot module. The SDK (#757) reads `initialBundles` as the PURE\n * `Record<locale, Record<namespace, tree>>` map, so provenance lives in a\n * SEPARATE `meta` export — never mixed into `bundles` (otherwise the SDK would\n * treat `version`/`project` as locales).\n */\nexport function renderSnapshot(\n bundles: Bundles,\n meta: { project: string; version: string; cdn: string },\n format: \"ts\" | \"json\",\n): string {\n const json = JSON.stringify(bundles, null, 2);\n if (format === \"json\") return json + \"\\n\";\n return (\n \"// Generated by `sonenta snapshot` — do not edit by hand.\\n\" +\n \"// Build-time fallback for @sonenta/react-i18next:\\n\" +\n '// import { bundles } from \"./<this-file>\";\\n' +\n \"// <VerbumiaProvider initialBundles={bundles} />\\n\" +\n `export const bundles = ${json} as const;\\n\\n` +\n `export const meta = ${JSON.stringify(meta, null, 2)} as const;\\n\\n` +\n \"export default bundles;\\n\"\n );\n}\n\nasync function fetchBundle(url: string): Promise<Tree | null> {\n const res = await fetch(url, { headers: { Accept: \"application/json\" } });\n if (res.status === 404) return null; // bundle not published for this (lang, ns)\n if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);\n return (await res.json()) as Tree;\n}\n\nexport const snapshotCommand = new Command(\"snapshot\")\n .description(\n \"Generate a build-time translations snapshot for @sonenta/react-i18next \" +\n \"`initialBundles` (offline-first fallback). Fetches the PUBLIC CDN bundles \" +\n \"(exactly what the SDK loads at runtime) and assembles \" +\n \"Record<locale, Record<namespace, tree>>. Emits a .ts module (default) or .json.\",\n )\n .option(\"--language <code>\", \"Restrict to a single language code\")\n .option(\"--namespace <slug>\", \"Restrict to a single namespace slug\")\n .option(\"--version <slug>\", \"Version slug (default: the configured version_slug)\")\n .option(\"--format <fmt>\", \"ts | json (default: ts)\", \"ts\")\n .option(\"--cdn <base>\", \"CDN base URL\", \"https://cdn.sonenta.com\")\n .option(\"--out <file>\", \"Write to a file instead of stdout\")\n .option(\"--host <url>\", \"Override host (used to discover languages/namespaces)\")\n .action(\n async (opts: {\n language?: string;\n namespace?: string;\n version?: string;\n format: string;\n cdn: string;\n out?: string;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const project = requireProject(ctx);\n const version = opts.version ?? ctx.versionSlug;\n\n // Enumerate (language, namespace). Both pinned => no project-info call.\n let languages: string[];\n let namespaces: string[];\n if (opts.language && opts.namespace) {\n languages = [opts.language];\n namespaces = [opts.namespace];\n } else {\n const info = await getProjectInfo(ctx);\n languages = opts.language ? [opts.language] : info.languages;\n namespaces = opts.namespace ? [opts.namespace] : info.namespaces;\n if (languages.length === 0 || namespaces.length === 0) {\n throw new Error(\n \"could not enumerate languages/namespaces from project-info — \" +\n \"pass --language and --namespace explicitly.\",\n );\n }\n }\n\n const bundles: Bundles = {};\n let fetched = 0;\n let missing = 0;\n for (const lang of languages) {\n for (const ns of namespaces) {\n const tree = await fetchBundle(bundleUrl(opts.cdn, project, version, lang, ns));\n if (!tree) {\n missing++;\n continue;\n }\n (bundles[lang] ??= {})[ns] = tree;\n fetched++;\n }\n }\n\n const output = renderSnapshot(\n bundles,\n { project, version, cdn: opts.cdn.replace(/\\/+$/, \"\") },\n opts.format === \"json\" ? \"json\" : \"ts\",\n );\n\n if (opts.out) {\n await fs.writeFile(opts.out, output, \"utf8\");\n console.log(\n `wrote ${opts.out}: ${fetched} bundle(s)` +\n (missing ? `, ${missing} not published (404)` : \"\"),\n );\n } else {\n process.stdout.write(output);\n if (missing) console.error(`(${missing} bundle(s) not published)`);\n }\n },\n );\n","import { Command } from \"commander\";\n\nimport { type ApiContext, resolveContext } from \"../api.js\";\nimport {\n DEFAULT_LOCALES_DIR,\n diffFlat,\n type FlatTranslations,\n listLocaleFiles,\n readLocaleFile,\n} from \"../locales.js\";\nimport { getProjectInfo, listKeys } from \"../mcp.js\";\n\nexport const statusCommand = new Command(\"status\")\n .description(\"Diff local locales/ against the remote project state.\")\n .option(\"--language <code>\", \"Restrict to one language code\")\n .option(\"--namespace <slug>\", \"Restrict to one namespace slug\")\n .option(\"--src <dir>\", \"Locales directory\", DEFAULT_LOCALES_DIR)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: { language?: string; namespace?: string; src: string; host?: string }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const knownLangs = new Set((await getProjectInfo(ctx)).languages);\n\n const files = (await listLocaleFiles(opts.src)).filter((f) => {\n if (opts.language && f.lang !== opts.language) return false;\n if (opts.namespace && f.namespace !== opts.namespace) return false;\n return true;\n });\n\n // Fetch remote once per language, grouped by namespace slug.\n const remoteByLang = new Map<string, Map<string, FlatTranslations>>();\n async function remoteFor(ctx2: ApiContext, lang: string): Promise<Map<string, FlatTranslations>> {\n const cached = remoteByLang.get(lang);\n if (cached) return cached;\n const m = new Map<string, FlatTranslations>();\n for (const it of await listKeys(ctx2, { languageCode: lang })) {\n const tr = it.translations?.find((t) => t.language_code === lang);\n if (!tr || tr.value === \"\") continue;\n const map = m.get(it.namespace_slug) ?? {};\n map[it.key_name] = tr.value;\n m.set(it.namespace_slug, map);\n }\n remoteByLang.set(lang, m);\n return m;\n }\n\n let totalAdded = 0;\n let totalRemoved = 0;\n let totalChanged = 0;\n let totalUnchanged = 0;\n\n for (const f of files) {\n if (knownLangs.size > 0 && !knownLangs.has(f.lang)) {\n console.log(`! ${f.lang}/${f.namespace}.json — language not in project, skipped`);\n continue;\n }\n const local = await readLocaleFile(f.path);\n const remote = (await remoteFor(ctx, f.lang)).get(f.namespace) ?? {};\n const d = diffFlat(local, remote);\n totalAdded += d.added.length;\n totalRemoved += d.removed.length;\n totalChanged += d.changed.length;\n totalUnchanged += d.unchanged;\n\n if (d.added.length === 0 && d.removed.length === 0 && d.changed.length === 0) {\n console.log(`= ${f.lang}/${f.namespace}.json (in sync, ${d.unchanged} keys)`);\n continue;\n }\n console.log(\n `~ ${f.lang}/${f.namespace}.json +${d.added.length} -${d.removed.length} ~${d.changed.length}`,\n );\n for (const k of d.added) console.log(` + ${k}`);\n for (const k of d.removed) console.log(` - ${k} (remote-only — push leaves untouched)`);\n for (const c of d.changed) console.log(` ~ ${c.key} \"${c.local}\" <- \"${c.remote}\"`);\n }\n console.log(\n `summary: +${totalAdded} -${totalRemoved} ~${totalChanged} unchanged=${totalUnchanged}`,\n );\n },\n );\n","import { Command } from \"commander\";\n\nimport { readCredentials } from \"../credentials.js\";\n\nexport const whoamiCommand = new Command(\"whoami\")\n .description(\"Show the configured default host + which API key is in use.\")\n .option(\"--host <url>\", \"Inspect a specific host instead of the default\")\n .action(async (opts: { host?: string }) => {\n const creds = await readCredentials();\n if (!creds.default && Object.keys(creds.hosts).length === 0) {\n console.log(\"Not logged in. Run `sonenta login --host <url> --token <…>`.\");\n process.exit(1);\n }\n const target = opts.host ?? creds.default;\n if (!target) {\n console.log(\"No default host set.\");\n process.exit(1);\n }\n const entry = creds.hosts[target];\n if (!entry) {\n console.log(`No credentials stored for ${target}.`);\n process.exit(1);\n }\n const masked = entry.api_key.replace(/\\.[\\s\\S]+$/, \".•••\");\n const envOverride = (process.env.SONENTA_TOKEN ?? process.env.VERBUMIA_TOKEN)?.trim();\n console.log(`host: ${target}${target === creds.default ? \" (default)\" : \"\"}`);\n console.log(`api_key: ${masked}${envOverride ? \" (overridden by SONENTA_TOKEN env)\" : \"\"}`);\n if (entry.user_email) console.log(`email: ${entry.user_email}`);\n const others = Object.keys(creds.hosts).filter((h) => h !== target);\n if (others.length) {\n console.log(`other: ${others.join(\", \")}`);\n }\n });\n"],"mappings":"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","names":["Command","fs","join","fs","join","fs","fs","dirname","Command","dirname","fs","Command","resolve","Command","Command","resolve","Command","Command","Command","resolve","Command","Command","Command","Command","Command","Command","Command","Command","fs","join","resolve","Command","Command","Command","Command","Command","fs","Command","Command","fs","Command","Command","Command","Command","Command"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/agents.ts","../src/agents.ts","../src/commands/export.ts","../src/config.ts","../src/credentials.ts","../src/api.ts","../src/i18next_tree.ts","../src/mcp.ts","../src/commands/import.ts","../src/commands/init.ts","../src/commands/keys.ts","../src/commands/login.ts","../src/prompt.ts","../src/commands/logout.ts","../src/commands/missing.ts","../src/commands/projects.ts","../src/commands/pull.ts","../src/locales.ts","../src/commands/push.ts","../src/commands/releases.ts","../src/commands/snapshot.ts","../src/commands/status.ts","../src/commands/whoami.ts"],"sourcesContent":["import { Command } from \"commander\";\n\nimport { agentsCommand } from \"./commands/agents.js\";\nimport { exportCommand } from \"./commands/export.js\";\nimport { importCommand } from \"./commands/import.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { keysCommand } from \"./commands/keys.js\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { logoutCommand } from \"./commands/logout.js\";\nimport { missingCommand } from \"./commands/missing.js\";\nimport { projectsCommand } from \"./commands/projects.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { releasesCommand } from \"./commands/releases.js\";\nimport { snapshotCommand } from \"./commands/snapshot.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { whoamiCommand } from \"./commands/whoami.js\";\n\nconst program = new Command();\nprogram\n .name(\"sonenta\")\n .description(\"CLI for Sonenta translation management.\")\n .version(\"0.4.0\");\n\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(whoamiCommand);\nprogram.addCommand(initCommand);\nprogram.addCommand(projectsCommand);\nprogram.addCommand(keysCommand);\nprogram.addCommand(importCommand);\nprogram.addCommand(pushCommand);\nprogram.addCommand(pullCommand);\nprogram.addCommand(exportCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(releasesCommand);\nprogram.addCommand(snapshotCommand);\nprogram.addCommand(missingCommand);\nprogram.addCommand(agentsCommand);\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n console.error(err instanceof Error ? err.message : err);\n process.exit(1);\n});\n","import { Command } from \"commander\";\n\nimport { AGENTS_DIR, isInstalled, listAgents, writeAgent } from \"../agents.js\";\n\nexport const agentsCommand = new Command(\"agents\")\n .description(\"Install bundled Claude agents (e.g. sonenta-a11y) into .claude/agents/.\")\n .addCommand(\n new Command(\"list\")\n .description(\"List the bundled agents available to install.\")\n .option(\"--dir <path>\", \"Project directory (default: current directory)\")\n .action(async (opts: { dir?: string }) => {\n const baseDir = opts.dir;\n const agents = listAgents();\n console.log(`Available agents (${agents.length}):`);\n for (const a of agents) {\n const installed = await isInstalled(a.name, baseDir);\n const mark = installed ? \"✓ installed\" : \" not installed\";\n console.log(` ${a.name.padEnd(16)} ${mark}`);\n console.log(` ${a.summary}`);\n }\n console.log(`\\nInstall with: sonenta agents add <name>`);\n }),\n )\n .addCommand(\n new Command(\"add\")\n .description(\"Write a bundled agent definition into <dir>/.claude/agents/<name>.md.\")\n .argument(\"<name>\", \"Agent name (e.g. sonenta-a11y)\")\n .option(\"--dir <path>\", \"Project directory (default: current directory)\")\n .option(\"--force\", \"Overwrite an existing agent definition\", false)\n .action(async (name: string, opts: { dir?: string; force: boolean }) => {\n const path = await writeAgent(name, { baseDir: opts.dir, force: opts.force });\n console.log(`Wrote ${path}`);\n console.log(\n `\\nThe ${name} agent drives the Sonenta a11y MCP tools. Make sure the ` +\n `Sonenta MCP server is configured (npx -y @sonenta/mcp) with an ` +\n `mcp:* SONENTA_API_KEY, then use the agent in Claude Code or CI.`,\n );\n console.log(`Agent dir: ${AGENTS_DIR}/`);\n }),\n );\n","import { promises as fs } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\n/**\n * Installable Claude agents shipped with the CLI.\n *\n * `sonenta agents add <name>` writes a bundled agent definition into the\n * project's `.claude/agents/<name>.md` — the directory Claude Code reads\n * project-scoped subagents from. The agents drive the Sonenta a11y MCP tools\n * (from `@sonenta/mcp`) to audit and fix accessibility gaps; they work the same\n * interactively in Claude Code or headless in CI.\n *\n * The registry is intentionally simple (a name -> definition map) so more\n * agents can be added later without touching the command wiring.\n */\n\nexport const AGENTS_DIR = \".claude/agents\";\n\nexport interface AgentDef {\n /** File/agent name (no extension); the file is written as `<name>.md`. */\n name: string;\n /** One-line summary shown by `agents list`. */\n summary: string;\n /** Full agent definition (YAML frontmatter + system prompt). */\n content: string;\n}\n\nconst SONENTA_A11Y = `---\nname: sonenta-a11y\ndescription: Accessibility (a11y) auditor and fixer for Sonenta-managed i18n projects. Scans translation keys for WCAG gaps (missing aria-labels, images without alt text, hard-to-read copy, missing or untranslated a11y variants) and proposes or applies fixes through the Sonenta a11y MCP tools. Use interactively in Claude Code or headless in CI.\n---\n\nYou are **sonenta-a11y**, an accessibility specialist for internationalized\nprojects managed with Sonenta. You turn an accessibility audit into concrete,\nreviewable fixes, operating entirely through the Sonenta MCP server's a11y tools.\n\n## Requirements\n- The Sonenta MCP server (\\`@sonenta/mcp\\`) must be configured with an \\`mcp:*\\`\n API key. Every a11y operation goes through its tools — never call the HTTP API\n directly. If the a11y tools are missing, tell the user to add the server\n (\\`npx -y @sonenta/mcp\\`) and set \\`SONENTA_API_KEY\\`.\n- a11y MCP tools and what they do:\n - \\`a11y_report\\` — full WCAG gap report for the project (rollups + per-item gaps).\n - \\`list_a11y_gaps\\` — the actionable gap list, filterable by gap / surface / locale.\n - \\`a11y_estimate\\` — preview the CREDIT cost of a generate/translate run.\n - \\`generate_a11y_variant\\` — AI-generate source-language a11y variants.\n - \\`translate_a11y_variants\\` — translate existing a11y variants into locales.\n - \\`set_a11y_variant\\` / \\`delete_a11y_variant\\` — set/clear one variant by hand.\n\n## The four a11y surfaces\n- \\`aria_label\\` — accessible name for an interactive element (button, icon, link).\n- \\`alt_text\\` — alternative text for an image.\n- \\`screen_reader\\` — verbose screen-reader-only text.\n- \\`plain_language\\` — simplified / clear-language (FALC) rendering.\n\na11y values are SEMANTIC (the accessible name / alt / simplified wording for\nassistive tech) — not the visible UI string. Keep them concise and meaningful.\n\n## Gap types and how you resolve each\n- \\`a11y_untranslated\\` — a source a11y variant exists but a target locale lacks it\n → \\`translate_a11y_variants\\` for that \\`language_code\\`.\n- \\`alt_missing\\` — an image key has no source \\`alt_text\\` → \\`generate_a11y_variant\\`\n (or \\`set_a11y_variant\\`) for surface \\`alt_text\\`.\n- \\`reading_level_high\\` — source copy is hard to read with no \\`plain_language\\`\n → generate a \\`plain_language\\` variant.\n- \\`a11y_variant_absent\\` — a required surface is missing in the source\n → generate or set that surface.\n\n## Workflow\n1. **Scan.** Call \\`a11y_report\\`, passing \\`require_surface\\` for the surfaces this\n project needs (typically \\`aria_label\\` and \\`alt_text\\`). Summarize\n \\`total_gaps\\`, \\`by_gap\\`, \\`by_severity\\`, \\`by_surface\\`. Use \\`list_a11y_gaps\\`\n to drill into a specific gap type, surface, or locale.\n2. **Triage.** Group gaps by type and severity — warnings first\n (\\`a11y_untranslated\\`, \\`alt_missing\\`), then info (\\`reading_level_high\\`,\n \\`a11y_variant_absent\\`).\n3. **Estimate before spending.** generate and translate BILL CREDITS. ALWAYS call\n \\`a11y_estimate\\` first (mode \\`generate\\` or \\`translate\\`), report\n \\`credits_required\\` vs \\`balance\\`, and STOP if \\`sufficient\\` is false.\n4. **Fix source gaps.** Prefer \\`generate_a11y_variant\\`, scoped with\n \\`key_uuids\\` / \\`namespace_uuid\\` / \\`surfaces\\`. For a precise known value use\n \\`set_a11y_variant\\`, addressing by \\`key_uuid\\` + locale \\`code\\` + \\`surface\\` —\n exactly the fields each gap already carries (no UUID resolution).\n5. **Propagate to locales.** For \\`a11y_untranslated\\`, run\n \\`translate_a11y_variants\\` with the target \\`language_codes\\`.\n6. **Report.** Generated/translated values are **drafts** (bot-authored) awaiting\n human review — never present them as final. Summarize what changed (counts,\n \\`credits_charged\\` / \\`credits_refunded\\`) and what still needs review.\n\n## Modes\n- **Interactive (Claude Code):** present the fix plan and the credit estimate,\n and confirm before running any credit-billed operation unless the user already\n said to proceed.\n- **CI / headless:** run \\`a11y_report\\` and exit non-zero when \\`total_gaps\\` (or a\n chosen severity) exceeds the project's threshold. Only auto-generate or\n translate when the run is explicitly authorized to spend credits.\n\n## Guardrails\n- Never spend credits without an estimate first.\n- generate only FILLS gaps — never overwrite a human-reviewed variant blindly.\n- Stay within the configured project; confirm it before any bulk operation.\n`;\n\nexport const AGENTS: Record<string, AgentDef> = {\n \"sonenta-a11y\": {\n name: \"sonenta-a11y\",\n summary:\n \"Accessibility (a11y) auditor + fixer: scans WCAG gaps and fixes them via the Sonenta a11y MCP tools.\",\n content: SONENTA_A11Y,\n },\n};\n\nexport function listAgents(): AgentDef[] {\n return Object.values(AGENTS);\n}\n\nexport function getAgent(name: string): AgentDef | undefined {\n return AGENTS[name];\n}\n\n/** Absolute path the agent definition is (or would be) written to. */\nexport function agentInstallPath(name: string, baseDir: string = process.cwd()): string {\n return resolve(baseDir, AGENTS_DIR, `${name}.md`);\n}\n\nexport async function isInstalled(name: string, baseDir: string = process.cwd()): Promise<boolean> {\n try {\n await fs.access(agentInstallPath(name, baseDir));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Write a bundled agent into `<baseDir>/.claude/agents/<name>.md`.\n * Throws on an unknown agent or when the file exists and `force` is false.\n * Returns the absolute path written.\n */\nexport async function writeAgent(\n name: string,\n opts: { baseDir?: string; force?: boolean } = {},\n): Promise<string> {\n const agent = getAgent(name);\n if (!agent) {\n const known = Object.keys(AGENTS).join(\", \");\n throw new Error(`Unknown agent \"${name}\". Available: ${known}`);\n }\n const baseDir = opts.baseDir ?? process.cwd();\n const path = agentInstallPath(name, baseDir);\n if (!opts.force) {\n try {\n await fs.access(path);\n throw new Error(\n `${path} already exists. Pass --force to overwrite.`,\n );\n } catch (err) {\n // Re-throw the \"already exists\" error; an access() rejection means the\n // file is absent, which is the happy path.\n if (err instanceof Error && err.message.includes(\"already exists\")) throw err;\n }\n }\n await fs.mkdir(resolve(baseDir, AGENTS_DIR), { recursive: true });\n await fs.writeFile(path, agent.content, \"utf8\");\n return path;\n}\n","import { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { Command } from \"commander\";\n\nimport { type ApiContext, resolveContext } from \"../api.js\";\nimport { type FlatMap, sortDeep, unflatten } from \"../i18next_tree.js\";\nimport { getProjectInfo, listKeys } from \"../mcp.js\";\n\n/** language_code -> namespace_slug -> flat map of key -> value. */\ntype Collected = Record<string, Record<string, FlatMap>>;\n\nasync function collect(\n ctx: ApiContext,\n languages: string[],\n namespace?: string,\n): Promise<Collected> {\n const out: Collected = {};\n for (const lang of languages) {\n const items = await listKeys(ctx, { languageCode: lang, namespace });\n for (const it of items) {\n const tr = it.translations?.find((t) => t.language_code === lang);\n if (!tr || tr.value === \"\") continue;\n (out[lang] ??= {})[it.namespace_slug] ??= {};\n out[lang]![it.namespace_slug]![it.key_name] = tr.value;\n }\n }\n return out;\n}\n\nexport const exportCommand = new Command(\"export\")\n .description(\n \"Export Verbumia translations as i18next JSON. Flat dot-notation by \" +\n \"default (--nested for nested trees). Writes <out>/<lang>/<namespace>.json \" +\n \"with --out, otherwise prints { locale: { namespace: tree } } to stdout.\",\n )\n .option(\"--language <code>\", \"Restrict to a single language code\")\n .option(\"--namespace <slug>\", \"Restrict to a single namespace slug\")\n .option(\"--nested\", \"Emit nested JSON instead of flat dot-notation\", false)\n .option(\"--out <dir>\", \"Write files instead of printing to stdout\")\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: {\n language?: string;\n namespace?: string;\n nested: boolean;\n out?: string;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const languages = opts.language ? [opts.language] : (await getProjectInfo(ctx)).languages;\n const collected = await collect(ctx, languages, opts.namespace);\n const shape = (flat: FlatMap): unknown => (opts.nested ? sortDeep(unflatten(flat)) : sortDeep(flat));\n\n if (opts.out) {\n let files = 0;\n for (const [lang, nss] of Object.entries(collected)) {\n for (const [ns, flat] of Object.entries(nss)) {\n const dir = join(opts.out, lang);\n await fs.mkdir(dir, { recursive: true });\n const p = join(dir, `${ns}.json`);\n await fs.writeFile(p, JSON.stringify(shape(flat), null, 2) + \"\\n\", \"utf8\");\n console.log(` ${p} ${Object.keys(flat).length} keys`);\n files++;\n }\n }\n console.log(`exported ${files} file(s)`);\n return;\n }\n\n const tree: Record<string, Record<string, unknown>> = {};\n for (const [lang, nss] of Object.entries(collected)) {\n tree[lang] = {};\n for (const [ns, flat] of Object.entries(nss)) tree[lang]![ns] = shape(flat);\n }\n process.stdout.write(JSON.stringify(tree, null, 2) + \"\\n\");\n },\n );\n","import { promises as fs } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\n\n/**\n * Project-local config — `sonenta.config.json` at the repo root.\n *\n * Layout (V1, intentionally minimal):\n *\n * {\n * \"host\": \"https://api.sonenta.com\",\n * \"project_uuid\": \"069fc15d-…\",\n * \"version_slug\": \"main\"\n * }\n *\n * `host` may be omitted — commands then fall back to the user-level\n * credentials default. `version_slug` defaults to \"main\" when omitted.\n *\n * Back-compat: the legacy filename `verbumia.config.json` is still READ\n * (with a one-time deprecation warning) when no `sonenta.config.json` is\n * found, so existing projects keep working. New configs are written as\n * `sonenta.config.json`.\n */\n\nexport interface ProjectConfig {\n host?: string;\n project_uuid?: string;\n version_slug?: string;\n}\n\nexport const CONFIG_FILENAME = \"sonenta.config.json\";\nexport const LEGACY_CONFIG_FILENAME = \"verbumia.config.json\";\n\nlet warnedLegacy = false;\n\nexport async function findConfigPath(startDir: string): Promise<string | null> {\n let dir = resolve(startDir);\n // Walk up until we hit a config or the filesystem root. At each level the\n // canonical name wins over the deprecated one.\n while (true) {\n for (const name of [CONFIG_FILENAME, LEGACY_CONFIG_FILENAME]) {\n const candidate = resolve(dir, name);\n try {\n await fs.access(candidate);\n if (name === LEGACY_CONFIG_FILENAME && !warnedLegacy) {\n warnedLegacy = true;\n process.emitWarning(\n `${LEGACY_CONFIG_FILENAME} is deprecated — rename it to ${CONFIG_FILENAME}. ` +\n `The legacy name still works for now.`,\n { type: \"DeprecationWarning\" },\n );\n }\n return candidate;\n } catch {\n // Continue to the next name / parent dir.\n }\n }\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nexport async function readConfig(startDir: string = process.cwd()): Promise<{\n path: string | null;\n config: ProjectConfig;\n}> {\n const path = await findConfigPath(startDir);\n if (!path) return { path: null, config: {} };\n const raw = await fs.readFile(path, \"utf8\");\n const parsed = JSON.parse(raw) as ProjectConfig;\n return { path, config: parsed };\n}\n\nexport async function writeConfig(\n config: ProjectConfig,\n targetDir: string = process.cwd(),\n): Promise<string> {\n // Always write the canonical filename.\n const path = resolve(targetDir, CONFIG_FILENAME);\n await fs.writeFile(path, JSON.stringify(config, null, 2) + \"\\n\", \"utf8\");\n return path;\n}\n","import { promises as fs } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Per-user credentials store.\n *\n * Lives at `~/.verbumia/credentials` with file mode 0600. The shape is a\n * keyed map of host → entry, so a single user can have multiple accounts\n * (cloud + self-hosted dev + self-hosted prod) without rewriting the file\n * each time they switch hosts.\n *\n * {\n * \"default\": \"https://api.sonenta.com\",\n * \"hosts\": {\n * \"https://api.sonenta.com\": { \"api_key\": \"vrb_live_…\", \"user_email\": \"...\" },\n * \"https://api.dev.verbumia.ca\":{ \"api_key\": \"vrb_live_…\" }\n * }\n * }\n *\n * The `default` host is what `sonenta` commands target when neither the\n * project's `sonenta.config.json` nor a `--host` flag override it.\n */\n\nexport interface CredentialsEntry {\n api_key: string;\n user_email?: string;\n}\n\nexport interface CredentialsFile {\n default?: string;\n hosts: Record<string, CredentialsEntry>;\n}\n\nexport const credentialsDir = (): string => join(homedir(), \".verbumia\");\nexport const credentialsPath = (): string => join(credentialsDir(), \"credentials\");\n\nconst EMPTY: CredentialsFile = { hosts: {} };\n\nexport async function readCredentials(): Promise<CredentialsFile> {\n try {\n const raw = await fs.readFile(credentialsPath(), \"utf8\");\n const parsed = JSON.parse(raw);\n if (!parsed || typeof parsed !== \"object\" || !parsed.hosts) {\n return EMPTY;\n }\n return parsed as CredentialsFile;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") return { hosts: {} };\n throw err;\n }\n}\n\nexport async function writeCredentials(creds: CredentialsFile): Promise<void> {\n const dir = credentialsDir();\n const path = credentialsPath();\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n // Write to a temp file first to keep the original intact if the process\n // crashes mid-write, then rename atomically.\n const tmp = `${path}.tmp`;\n await fs.writeFile(tmp, JSON.stringify(creds, null, 2) + \"\\n\", { mode: 0o600 });\n await fs.rename(tmp, path);\n // chmod again in case the umask widened it on creation\n await fs.chmod(path, 0o600);\n await fs.chmod(dir, 0o700).catch(() => {});\n}\n\nexport async function setHostEntry(\n host: string,\n entry: CredentialsEntry,\n options: { makeDefault?: boolean } = {},\n): Promise<void> {\n const creds = await readCredentials();\n creds.hosts[host] = entry;\n if (options.makeDefault || !creds.default) creds.default = host;\n await writeCredentials(creds);\n}\n\nexport async function removeHost(host: string): Promise<boolean> {\n const creds = await readCredentials();\n if (!(host in creds.hosts)) return false;\n delete creds.hosts[host];\n if (creds.default === host) {\n const remaining = Object.keys(creds.hosts);\n creds.default = remaining[0];\n }\n await writeCredentials(creds);\n return true;\n}\n\nexport function resolveHostEntry(\n creds: CredentialsFile,\n hostOverride?: string,\n): { host: string; entry: CredentialsEntry } | null {\n const host = hostOverride ?? creds.default;\n if (!host) return null;\n const entry = creds.hosts[host];\n if (!entry) return null;\n return { host, entry };\n}\n","import { readConfig } from \"./config.js\";\nimport {\n type CredentialsEntry,\n readCredentials,\n resolveHostEntry,\n} from \"./credentials.js\";\n\n/**\n * Resolved request context for a CLI command:\n * - host: which Verbumia API to talk to\n * - apiKey: the bearer ApiKey token (resolved from credentials)\n * - projectUuid: optional, from project config\n * - versionSlug: defaults to \"main\"\n */\nexport interface ApiContext {\n host: string;\n apiKey: string;\n projectUuid?: string;\n versionSlug: string;\n}\n\nexport interface ResolveOptions {\n hostOverride?: string;\n cwd?: string;\n}\n\nexport async function resolveContext(opts: ResolveOptions = {}): Promise<ApiContext> {\n const { config } = await readConfig(opts.cwd ?? process.cwd());\n const creds = await readCredentials();\n const host = opts.hostOverride ?? config.host ?? creds.default;\n if (!host) {\n throw new Error(\n \"No host configured. Run `sonenta login --host <url>` or add `host` to sonenta.config.json.\",\n );\n }\n\n // Auth resolution order (first wins):\n // 1. SONENTA_TOKEN (or legacy VERBUMIA_TOKEN) env var — highest priority for CI / scripts\n // 2. ~/.verbumia/credentials entry for this host\n // The env-var path lets users run a one-off command in CI without\n // touching the credentials file or typing the token interactively.\n const envToken = process.env.SONENTA_TOKEN ?? process.env.VERBUMIA_TOKEN;\n let apiKey: string | undefined = envToken && envToken.trim() ? envToken.trim() : undefined;\n if (!apiKey) {\n const resolved = resolveHostEntry(creds, host);\n if (!resolved) {\n throw new Error(\n `No credentials for host ${host}. Set SONENTA_TOKEN or run \\`sonenta login --host ${host}\\`.`,\n );\n }\n apiKey = resolved.entry.api_key;\n }\n\n return {\n host,\n apiKey,\n projectUuid: config.project_uuid,\n versionSlug: config.version_slug ?? \"main\",\n };\n}\n\nexport async function apiRequest<T = unknown>(\n ctx: ApiContext,\n path: string,\n init: RequestInit = {},\n): Promise<T> {\n const url = `${ctx.host.replace(/\\/+$/, \"\")}${path}`;\n const headers = new Headers(init.headers);\n headers.set(\"Authorization\", `ApiKey ${ctx.apiKey}`);\n if (init.body && !headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n const res = await fetch(url, { ...init, headers });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`HTTP ${res.status} ${res.statusText} on ${path}: ${text.slice(0, 300)}`);\n }\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n}\n","/**\n * i18next tree <-> flat-map helpers.\n *\n * Verbumia stores keys as flat dot-notation (`hero.title`). i18next files on\n * disk are commonly nested (`{hero:{title:..}}`). The one-shot import endpoint\n * accepts BOTH, so `import` passes trees through untouched — these helpers are\n * for the OUTPUT side (export/snapshot) where we choose flat or nested.\n */\n\nexport type FlatMap = Record<string, string>;\nexport type Tree = Record<string, unknown>;\n\n/** Expand dot-notation flat keys into a nested tree. */\nexport function unflatten(flat: FlatMap, sep = \".\"): Tree {\n const root: Tree = {};\n for (const [k, v] of Object.entries(flat)) {\n const parts = k.split(sep);\n let node: Tree = root;\n for (let i = 0; i < parts.length - 1; i++) {\n const p = parts[i]!;\n const existing = node[p];\n if (typeof existing !== \"object\" || existing === null) node[p] = {};\n node = node[p] as Tree;\n }\n node[parts[parts.length - 1]!] = v;\n }\n return root;\n}\n\n/** Flatten a nested tree to dot-notation (string leaves only). */\nexport function flatten(tree: Tree, sep = \".\"): FlatMap {\n const out: FlatMap = {};\n const walk = (node: unknown, prefix: string): void => {\n if (!node || typeof node !== \"object\" || Array.isArray(node)) return;\n for (const [k, v] of Object.entries(node as Tree)) {\n const key = prefix ? `${prefix}${sep}${k}` : k;\n if (v && typeof v === \"object\" && !Array.isArray(v)) walk(v, key);\n else if (typeof v === \"string\") out[key] = v;\n }\n };\n walk(tree, \"\");\n return out;\n}\n\n/** Recursively sort object keys for deterministic, diff-friendly output. */\nexport function sortDeep<T>(value: T): T {\n if (Array.isArray(value)) return value.map((v) => sortDeep(v)) as unknown as T;\n if (value && typeof value === \"object\") {\n const src = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const k of Object.keys(src).sort((a, b) => a.localeCompare(b))) out[k] = sortDeep(src[k]);\n return out as T;\n }\n return value;\n}\n","/**\n * MCP-surface client helpers.\n *\n * The whole CLI talks to the metered, key-addressable MCP surface\n * `/v1/mcp/projects/{project_id}/...` (auth = an API key carrying the `mcp:*`\n * scope). That surface addresses keys/namespaces/languages by NAME (no uuids),\n * exposes bulk + one-shot endpoints, and is the contract-blessed automation\n * entrypoint. These helpers wrap the endpoints the commands consume.\n */\n\nimport { type ApiContext, apiRequest } from \"./api.js\";\n\nexport function requireProject(ctx: ApiContext): string {\n if (!ctx.projectUuid) {\n throw new Error(\n \"no project configured — run `sonenta init --project <uuid>` \" +\n \"(or set project_uuid in sonenta.config.json).\",\n );\n }\n return ctx.projectUuid;\n}\n\nfunction projectBase(ctx: ApiContext): string {\n return `/v1/mcp/projects/${requireProject(ctx)}`;\n}\n\n// ---- projects ------------------------------------------------------------\n\nexport interface ProjectSummary {\n uuid: string;\n name?: string;\n slug?: string;\n source_language?: string;\n}\n\nexport async function listProjects(ctx: ApiContext, limit = 200): Promise<ProjectSummary[]> {\n const data = await apiRequest<{ items: ProjectSummary[] }>(\n ctx,\n `/v1/mcp/projects?limit=${limit}`,\n );\n return data.items ?? [];\n}\n\nexport interface ProjectInfo {\n source_language: string;\n languages: string[];\n namespaces: string[];\n}\n\n/** Normalises get_project_info into plain code/slug lists (shape-tolerant). */\nexport async function getProjectInfo(ctx: ApiContext): Promise<ProjectInfo> {\n const d = await apiRequest<Record<string, unknown>>(ctx, projectBase(ctx));\n const src =\n typeof d.source_language === \"string\"\n ? d.source_language\n : (d.source_language as { code?: string })?.code ?? \"\";\n const langs = Array.isArray(d.languages)\n ? (d.languages as unknown[]).map((l) =>\n typeof l === \"string\" ? l : ((l as { code?: string }).code ?? \"\"),\n )\n : [];\n const ns = Array.isArray(d.namespaces)\n ? (d.namespaces as unknown[]).map((n) =>\n typeof n === \"string\" ? n : ((n as { slug?: string }).slug ?? \"\"),\n )\n : [];\n return {\n source_language: src,\n languages: langs.filter(Boolean),\n namespaces: ns.filter(Boolean),\n };\n}\n\n// ---- keys (paginated list) ----------------------------------------------\n\nexport interface KeyTranslation {\n language_code: string;\n value: string;\n status: string;\n}\n\nexport interface KeyItem {\n key_name: string;\n namespace_slug: string;\n source_value: string | null;\n translations?: KeyTranslation[];\n}\n\nexport async function listKeys(\n ctx: ApiContext,\n opts: { languageCode?: string; namespace?: string } = {},\n): Promise<KeyItem[]> {\n const out: KeyItem[] = [];\n let cursor: string | null = null;\n do {\n const params = new URLSearchParams({ limit: \"200\" });\n if (opts.languageCode) params.set(\"language_code\", opts.languageCode);\n if (opts.namespace) params.set(\"namespace\", opts.namespace);\n if (cursor) params.set(\"cursor\", cursor);\n const page = await apiRequest<{ items: KeyItem[]; cursor_next: string | null }>(\n ctx,\n `${projectBase(ctx)}/keys?${params.toString()}`,\n );\n out.push(...(page.items ?? []));\n cursor = page.cursor_next ?? null;\n } while (cursor);\n return out;\n}\n\n// ---- i18next one-shot import --------------------------------------------\n\nexport type I18nextTree = Record<string, unknown>;\n\nexport interface ImportNamespaceUnit {\n namespace: string;\n translations: Record<string, I18nextTree>; // language_code -> i18next tree (nested or flat)\n}\n\nexport interface ImportBody {\n namespaces: ImportNamespaceUnit[];\n version?: string;\n status?: \"draft\" | \"translated\";\n}\n\nexport interface ImportBundleReport {\n project_uuid: string;\n version_slug: string;\n keys_created: number;\n keys_reused: number;\n translations_created: number;\n translations_updated: number;\n translations_unchanged: number;\n plural_keys_marked: number;\n units?: unknown[];\n errors?: { namespace: string; language_code: string; code: string }[];\n glossary_violation_count?: number;\n glossary_violations?: {\n namespace: string;\n language_code: string;\n key: string;\n rule_type: string;\n term: string;\n matched_text?: string;\n message?: string;\n }[];\n}\n\nexport async function importBundle(ctx: ApiContext, body: ImportBody): Promise<ImportBundleReport> {\n return apiRequest<ImportBundleReport>(ctx, `${projectBase(ctx)}/i18next/import`, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n}\n\n// ---- CDN releases (publish) ---------------------------------------------\n\nexport interface PublishCdnBody {\n language_code?: string;\n namespace?: string;\n version_slug?: string;\n}\n\nexport async function publishCdn(ctx: ApiContext, body: PublishCdnBody): Promise<unknown> {\n return apiRequest(ctx, `${projectBase(ctx)}/cdn/releases`, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n}\n\n// ---- formatting ----------------------------------------------------------\n\n/** Human-readable lines for an ImportBundleReport (created/updated/unchanged). */\nexport function summariseImport(r: ImportBundleReport): string[] {\n const lines = [\n `keys: ${r.keys_created} created, ${r.keys_reused} reused`,\n `translations: ${r.translations_created} created, ${r.translations_updated} updated, ${r.translations_unchanged} unchanged`,\n ];\n if (r.plural_keys_marked) lines.push(`plural keys: ${r.plural_keys_marked} marked`);\n if (r.errors?.length) {\n lines.push(`errors: ${r.errors.length}`);\n for (const e of r.errors) lines.push(` ! ${e.namespace}/${e.language_code}: ${e.code}`);\n }\n if (r.glossary_violations?.length) {\n lines.push(`glossary: ${r.glossary_violations.length} violation(s)`);\n for (const g of r.glossary_violations) {\n lines.push(` ⚠ ${g.namespace}/${g.language_code} ${g.key}: ${g.rule_type} \"${g.term}\"`);\n }\n }\n return lines;\n}\n","import { promises as fs } from \"node:fs\";\nimport { basename, dirname } from \"node:path\";\n\nimport { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { type I18nextTree, type ImportBody, importBundle, summariseImport } from \"../mcp.js\";\n\n/**\n * Resolve (language, namespace) for a file.\n * - explicit --language / --namespace always win;\n * - otherwise infer from the i18next layout `<lang>/<namespace>.json`\n * (parent dir = language, filename stem = namespace);\n * - a bare `<lang>.json` (no language dir) uses the stem as the language and\n * REQUIRES --namespace.\n */\nexport function resolveLangNs(\n filePath: string,\n optLang?: string,\n optNs?: string,\n): { lang: string; ns: string } {\n const stem = basename(filePath).replace(/\\.json$/i, \"\");\n const parent = basename(dirname(filePath));\n const hasLangDir = parent !== \"\" && parent !== \".\" && parent !== \"locales\";\n const lang = optLang ?? (hasLangDir ? parent : stem);\n const ns = optNs ?? (hasLangDir ? stem : undefined);\n if (!lang) throw new Error(`could not infer a language for ${filePath} — pass --language`);\n if (!ns) {\n throw new Error(\n `could not infer a namespace for ${filePath} — pass --namespace, ` +\n \"or lay files out as <lang>/<namespace>.json\",\n );\n }\n return { lang, ns };\n}\n\nasync function readTree(filePath: string): Promise<I18nextTree> {\n const parsed = JSON.parse(await fs.readFile(filePath, \"utf8\"));\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`${filePath} is not a JSON object`);\n }\n return parsed as I18nextTree;\n}\n\nfunction countLeaves(tree: I18nextTree): number {\n let n = 0;\n for (const v of Object.values(tree)) {\n if (v && typeof v === \"object\" && !Array.isArray(v)) n += countLeaves(v as I18nextTree);\n else n += 1;\n }\n return n;\n}\n\nexport const importCommand = new Command(\"import\")\n .description(\n \"Import i18next JSON file(s) into Verbumia in ONE call — creates missing \" +\n \"keys and upserts translations (idempotent). Accepts nested or flat JSON. \" +\n \"Language/namespace are inferred from the path (<lang>/<namespace>.json) \" +\n \"or forced with --language / --namespace.\",\n )\n .argument(\"<files...>\", \"i18next JSON file(s), e.g. locales/fr/common.json or fr.json\")\n .option(\"--language <code>\", \"Force the language code for every file\")\n .option(\"--namespace <slug>\", \"Force the namespace slug for every file\")\n .option(\"--status <status>\", \"draft | translated (default: translated)\")\n .option(\"--version <slug>\", \"Target version slug (default: production version)\")\n .option(\"--dry-run\", \"Print what would be imported without sending\", false)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (\n files: string[],\n opts: {\n language?: string;\n namespace?: string;\n status?: string;\n version?: string;\n dryRun: boolean;\n host?: string;\n },\n ) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n\n // namespace -> language_code -> tree\n const byNs = new Map<string, Record<string, I18nextTree>>();\n let totalLeaves = 0;\n for (const f of files) {\n const { lang, ns } = resolveLangNs(f, opts.language, opts.namespace);\n const tree = await readTree(f);\n totalLeaves += countLeaves(tree);\n const langs = byNs.get(ns) ?? {};\n if (langs[lang]) {\n throw new Error(`two input files map to namespace=${ns} language=${lang} — merge them first`);\n }\n langs[lang] = tree;\n byNs.set(ns, langs);\n console.log(` ${f} -> ${ns} / ${lang}`);\n }\n\n const body: ImportBody = {\n namespaces: [...byNs.entries()].map(([namespace, translations]) => ({\n namespace,\n translations,\n })),\n };\n if (opts.status === \"draft\" || opts.status === \"translated\") body.status = opts.status;\n if (opts.version) body.version = opts.version;\n\n if (opts.dryRun) {\n console.log(\n `\\n(dry-run) would import ${totalLeaves} value(s) across ${body.namespaces.length} ` +\n \"namespace(s); nothing sent.\",\n );\n return;\n }\n\n const report = await importBundle(ctx, body);\n console.log(\"\");\n for (const line of summariseImport(report)) console.log(line);\n if (report.errors?.length || report.glossary_violations?.length) process.exitCode = 1;\n },\n );\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nimport { Command } from \"commander\";\n\nimport { CONFIG_FILENAME, writeConfig } from \"../config.js\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Scaffold a sonenta.config.json in the current directory.\")\n .option(\"--host <url>\", \"API base URL\", \"https://api.sonenta.com\")\n .option(\"--project <uuid>\", \"Project UUID\")\n .option(\"--version <slug>\", \"Version slug (default: main)\", \"main\")\n .option(\"--force\", \"Overwrite an existing sonenta.config.json\", false)\n .action(\n async (opts: { host: string; project?: string; version: string; force: boolean }) => {\n const path = resolve(process.cwd(), CONFIG_FILENAME);\n if (existsSync(path) && !opts.force) {\n console.error(\n `${CONFIG_FILENAME} already exists at ${path}. Pass --force to overwrite.`,\n );\n process.exit(1);\n }\n const written = await writeConfig({\n host: opts.host,\n project_uuid: opts.project,\n version_slug: opts.version,\n });\n console.log(`Wrote ${written}`);\n if (!opts.project) {\n console.log(\n \"Tip: pass --project <uuid> to bind this directory to a specific project \" +\n \"(or edit project_uuid in the file later).\",\n );\n }\n },\n );\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { listKeys } from \"../mcp.js\";\n\nexport const keysCommand = new Command(\"keys\")\n .description(\"Inspect translation keys for the current project.\")\n .addCommand(\n new Command(\"list\")\n .description(\"List keys for the configured project (addressed by namespace slug + key name).\")\n .option(\"--namespace <slug>\", \"Filter by namespace slug\")\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(async (opts: { namespace?: string; host?: string }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const items = await listKeys(ctx, { namespace: opts.namespace });\n console.log(`total: ${items.length}`);\n for (const k of items) console.log(` ${k.namespace_slug}/${k.key_name}`);\n }),\n );\n","import { Command } from \"commander\";\n\nimport { setHostEntry } from \"../credentials.js\";\nimport { promptLine, promptSecret } from \"../prompt.js\";\n\nconst TOKEN_REGEX = /^vrb_[a-z]+_[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\n\nexport const loginCommand = new Command(\"login\")\n .description(\n \"Store an API key for a host. Token resolution order: --token, \" +\n \"SONENTA_TOKEN env, then interactive prompt (TTY only).\",\n )\n .option(\"--host <url>\", \"API base URL\", \"https://api.sonenta.com\")\n .option(\"--token <vrb_live_…>\", \"API key token (prefix.secret form)\")\n .option(\"--email <email>\", \"User email associated with the token (optional)\")\n .option(\"--default\", \"Set this host as the default for future commands\", false)\n .action(async (opts: { host: string; token?: string; email?: string; default?: boolean }) => {\n let host = opts.host;\n if (!host && process.stdin.isTTY) {\n host = (await promptLine(\"Host (default https://api.sonenta.com): \")) || \"https://api.sonenta.com\";\n }\n let token = opts.token ?? (process.env.SONENTA_TOKEN ?? process.env.VERBUMIA_TOKEN) ?? \"\";\n if (!token && process.stdin.isTTY) {\n token = await promptSecret(`API token for ${host}: `);\n }\n if (!token) {\n console.error(\n \"sonenta login: token required. Pass --token, set SONENTA_TOKEN, or run from a TTY for the interactive prompt.\",\n );\n process.exit(1);\n }\n if (!TOKEN_REGEX.test(token)) {\n console.error(\n \"sonenta login: token shape doesn't match `vrb_<env>_<prefix>.<secret>`. \" +\n \"Generate one in the dashboard at Org Settings → API Keys.\",\n );\n process.exit(1);\n }\n await setHostEntry(\n host,\n { api_key: token, user_email: opts.email },\n { makeDefault: opts.default },\n );\n console.log(`Stored credentials for ${host}.`);\n });\n","import { createInterface } from \"node:readline\";\n\n/**\n * Read a line from stdin. Used for interactive prompts when --token / --host\n * aren't passed on the CLI. Returns \"\" if stdin isn't a TTY (so tests + piped\n * usage don't hang waiting for input that will never arrive).\n */\nexport async function promptLine(message: string): Promise<string> {\n if (!process.stdin.isTTY) return \"\";\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n return await new Promise<string>((resolve) => {\n rl.question(message, (answer) => resolve(answer.trim()));\n });\n } finally {\n rl.close();\n }\n}\n\nconst CTRL_C = 0x03;\nconst BACKSPACE = 0x08;\nconst DEL = 0x7f;\nconst CR = 0x0d;\nconst LF = 0x0a;\n\n/**\n * Same as promptLine but masks each echoed character with `*`. If stdin\n * isn't a TTY we return \"\" rather than echoing the secret.\n */\nexport async function promptSecret(message: string): Promise<string> {\n if (!process.stdin.isTTY) return \"\";\n process.stdout.write(message);\n process.stdin.setRawMode(true);\n process.stdin.resume();\n return await new Promise<string>((resolve) => {\n let buffer = \"\";\n const onData = (chunk: Buffer): void => {\n for (const byte of chunk) {\n if (byte === CR || byte === LF) {\n process.stdout.write(\"\\n\");\n process.stdin.removeListener(\"data\", onData);\n process.stdin.setRawMode(false);\n process.stdin.pause();\n resolve(buffer);\n return;\n }\n if (byte === CTRL_C) {\n process.stdout.write(\"\\n\");\n process.stdin.removeListener(\"data\", onData);\n process.stdin.setRawMode(false);\n process.stdin.pause();\n process.exit(130);\n }\n if (byte === BACKSPACE || byte === DEL) {\n if (buffer.length > 0) {\n buffer = buffer.slice(0, -1);\n process.stdout.write(\"\\b \\b\");\n }\n continue;\n }\n buffer += String.fromCharCode(byte);\n process.stdout.write(\"*\");\n }\n };\n process.stdin.on(\"data\", onData);\n });\n}\n","import { Command } from \"commander\";\n\nimport { readCredentials, removeHost } from \"../credentials.js\";\n\nexport const logoutCommand = new Command(\"logout\")\n .description(\"Remove stored credentials for a host (default: the current default host).\")\n .option(\"--host <url>\", \"Host to forget. Omit to forget the current default.\")\n .action(async (opts: { host?: string }) => {\n const creds = await readCredentials();\n const target = opts.host ?? creds.default;\n if (!target) {\n console.log(\"Nothing to forget — no credentials stored.\");\n return;\n }\n const removed = await removeHost(target);\n if (!removed) {\n console.log(`No credentials stored for ${target}.`);\n return;\n }\n console.log(`Forgot credentials for ${target}.`);\n });\n","import { Command } from \"commander\";\n\nimport { apiRequest, resolveContext } from \"../api.js\";\n\ninterface MissingKey {\n uuid: string;\n namespace_slug: string;\n language_code: string;\n key: string;\n source_value: string | null;\n count: number;\n status: string;\n first_seen: string;\n last_seen: string;\n}\n\ninterface MissingKeysPage {\n items: MissingKey[];\n cursor_next: string | null;\n total: number;\n}\n\nexport const missingCommand = new Command(\"missing\")\n .description(\n \"List runtime-detected missing keys for the configured project. Requires \" +\n \"the API key to carry the `mcp:*` scope.\",\n )\n .option(\"--namespace <slug>\", \"Filter by namespace slug\")\n .option(\"--language <code>\", \"Filter by language code\")\n .option(\"--status <state>\", \"Filter by status (open|resolved|...)\")\n .option(\"--limit <n>\", \"Page size (1-200, default 50)\", \"50\")\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: {\n namespace?: string;\n language?: string;\n status?: string;\n limit: string;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n if (!ctx.projectUuid) {\n console.error(\n \"sonenta missing: no project_uuid configured. Run `sonenta init --project <uuid>`.\",\n );\n process.exit(1);\n }\n const params = new URLSearchParams();\n params.set(\"limit\", opts.limit);\n if (opts.namespace) params.set(\"namespace\", opts.namespace);\n if (opts.language) params.set(\"language_code\", opts.language);\n if (opts.status) params.set(\"status\", opts.status);\n const page = await apiRequest<MissingKeysPage>(\n ctx,\n `/v1/mcp/projects/${ctx.projectUuid}/missing-keys?${params.toString()}`,\n );\n console.log(`total: ${page.total}`);\n for (const m of page.items) {\n const sample = m.source_value ? ` ⟶ \"${m.source_value}\"` : \"\";\n console.log(\n ` ${m.namespace_slug}/${m.key} (${m.language_code}, count=${m.count}, ${m.status})${sample}`,\n );\n }\n if (page.cursor_next) {\n console.log(`(more — re-run with --cursor ${page.cursor_next})`);\n }\n },\n );\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { listProjects } from \"../mcp.js\";\n\nexport const projectsCommand = new Command(\"projects\")\n .description(\"Inspect Verbumia projects accessible to your API key.\")\n .addCommand(\n new Command(\"list\")\n .description(\"List the projects this API key can reach.\")\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(async (opts: { host?: string }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const items = await listProjects(ctx);\n if (items.length === 0) {\n console.log(\"No projects visible to this key (scoped or revoked?).\");\n return;\n }\n for (const p of items) {\n const slug = p.slug ? ` ${p.slug}` : \"\";\n const src = p.source_language ? ` src=${p.source_language}` : \"\";\n console.log(` ${p.uuid}${slug} ${p.name ?? \"\"}${src}`);\n }\n }),\n );\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { DEFAULT_LOCALES_DIR, type FlatTranslations, writeLocaleFile } from \"../locales.js\";\nimport { getProjectInfo, listKeys } from \"../mcp.js\";\n\nexport const pullCommand = new Command(\"pull\")\n .description(\n \"Pull translations from Verbumia into locales/<lang>/<namespace>.json \" +\n \"(flat dot-notation). Overwrites local files — pair with `git status`.\",\n )\n .option(\"--language <code>\", \"Restrict to a single language code\")\n .option(\"--namespace <slug>\", \"Restrict to a single namespace slug\")\n .option(\"--dest <dir>\", \"Output directory\", DEFAULT_LOCALES_DIR)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: { language?: string; namespace?: string; dest: string; host?: string }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n let languages: string[];\n if (opts.language) {\n languages = [opts.language];\n } else {\n languages = (await getProjectInfo(ctx)).languages;\n if (languages.length === 0) {\n console.error(\"sonenta pull: the project reports no languages.\");\n process.exit(1);\n }\n }\n\n let totalKeys = 0;\n let totalFiles = 0;\n for (const lang of languages) {\n const items = await listKeys(ctx, { languageCode: lang, namespace: opts.namespace });\n const byNs = new Map<string, FlatTranslations>();\n for (const it of items) {\n const tr = it.translations?.find((t) => t.language_code === lang);\n if (!tr || tr.value === \"\") continue;\n const map = byNs.get(it.namespace_slug) ?? {};\n map[it.key_name] = tr.value;\n byNs.set(it.namespace_slug, map);\n }\n for (const [ns, map] of byNs) {\n const path = await writeLocaleFile(opts.dest, lang, ns, map);\n totalKeys += Object.keys(map).length;\n totalFiles++;\n console.log(` ${path} ${Object.keys(map).length} keys`);\n }\n }\n console.log(`pulled ${totalKeys} translation(s) across ${totalFiles} file(s)`);\n },\n );\n","import { promises as fs } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\n\n/**\n * On-disk locales layout. V1 is flat: one JSON per (lang, namespace) under\n * the configured locales directory:\n *\n * locales/\n * en/\n * common.json // { \"hello.title\": \"Hello\", ... }\n * errors.json\n * fr/\n * common.json\n *\n * Keys inside each file are flat dot-notation, NOT nested objects. This\n * matches the editor surface 1:1 and keeps push/pull idempotent.\n */\n\nexport const DEFAULT_LOCALES_DIR = \"locales\";\n\nexport type FlatTranslations = Record<string, string>;\n\nexport async function listLocaleFiles(\n rootDir: string,\n): Promise<{ lang: string; namespace: string; path: string }[]> {\n const root = resolve(rootDir);\n let langDirs: string[];\n try {\n langDirs = await fs.readdir(root);\n } catch {\n return [];\n }\n const out: { lang: string; namespace: string; path: string }[] = [];\n for (const lang of langDirs) {\n const langPath = join(root, lang);\n let stat;\n try {\n stat = await fs.stat(langPath);\n } catch {\n continue;\n }\n if (!stat.isDirectory()) continue;\n const files = await fs.readdir(langPath);\n for (const f of files) {\n if (!f.endsWith(\".json\")) continue;\n out.push({ lang, namespace: f.replace(/\\.json$/, \"\"), path: join(langPath, f) });\n }\n }\n return out;\n}\n\nexport async function readLocaleFile(path: string): Promise<FlatTranslations> {\n const raw = await fs.readFile(path, \"utf8\");\n const parsed = JSON.parse(raw);\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`${path} is not a flat object`);\n }\n const out: FlatTranslations = {};\n for (const [k, v] of Object.entries(parsed)) {\n if (typeof v !== \"string\") {\n throw new Error(`${path}: key \"${k}\" is not a string (V1 flat layout only)`);\n }\n out[k] = v;\n }\n return out;\n}\n\nexport async function writeLocaleFile(\n rootDir: string,\n lang: string,\n namespace: string,\n values: FlatTranslations,\n): Promise<string> {\n const dir = join(rootDir, lang);\n await fs.mkdir(dir, { recursive: true });\n const path = join(dir, `${namespace}.json`);\n // Sort keys for deterministic diffs.\n const sorted = Object.fromEntries(\n Object.entries(values).sort(([a], [b]) => a.localeCompare(b)),\n );\n await fs.writeFile(path, JSON.stringify(sorted, null, 2) + \"\\n\", \"utf8\");\n return path;\n}\n\nexport interface DiffResult {\n added: string[]; // local-only — push will create\n removed: string[]; // remote-only — push leaves alone (V1 doesn't delete)\n changed: { key: string; local: string; remote: string }[];\n unchanged: number;\n}\n\nexport function diffFlat(local: FlatTranslations, remote: FlatTranslations): DiffResult {\n const added: string[] = [];\n const removed: string[] = [];\n const changed: { key: string; local: string; remote: string }[] = [];\n let unchanged = 0;\n\n for (const [k, v] of Object.entries(local)) {\n if (!(k in remote)) {\n added.push(k);\n } else if (remote[k] !== v) {\n changed.push({ key: k, local: v, remote: remote[k]! });\n } else {\n unchanged++;\n }\n }\n for (const k of Object.keys(remote)) {\n if (!(k in local)) removed.push(k);\n }\n return { added, removed, changed, unchanged };\n}\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { DEFAULT_LOCALES_DIR, listLocaleFiles, readLocaleFile } from \"../locales.js\";\nimport { type ImportBody, importBundle, summariseImport } from \"../mcp.js\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\n \"Push the whole local locales/ tree to Verbumia in ONE import call — \" +\n \"creates missing keys and upserts translations (idempotent). Reads \" +\n \"locales/<lang>/<namespace>.json (flat dot-notation).\",\n )\n .option(\"--language <code>\", \"Restrict to a single language code\")\n .option(\"--namespace <slug>\", \"Restrict to a single namespace slug\")\n .option(\"--src <dir>\", \"Locales directory\", DEFAULT_LOCALES_DIR)\n .option(\"--status <status>\", \"draft | translated (default: translated)\")\n .option(\"--version <slug>\", \"Target version slug (default: production version)\")\n .option(\"--dry-run\", \"Print what would be pushed without sending\", false)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: {\n language?: string;\n namespace?: string;\n src: string;\n status?: string;\n version?: string;\n dryRun: boolean;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const files = (await listLocaleFiles(opts.src)).filter((f) => {\n if (opts.language && f.lang !== opts.language) return false;\n if (opts.namespace && f.namespace !== opts.namespace) return false;\n return true;\n });\n if (files.length === 0) {\n console.log(`No locale files found under ${opts.src}/`);\n return;\n }\n\n // namespace -> language_code -> flat map\n const byNs = new Map<string, Record<string, Record<string, string>>>();\n let totalKeys = 0;\n for (const f of files) {\n const flat = await readLocaleFile(f.path);\n totalKeys += Object.keys(flat).length;\n const langs = byNs.get(f.namespace) ?? {};\n langs[f.lang] = flat;\n byNs.set(f.namespace, langs);\n console.log(` ${f.lang}/${f.namespace}.json ${Object.keys(flat).length} keys`);\n }\n\n const body: ImportBody = {\n namespaces: [...byNs.entries()].map(([namespace, translations]) => ({\n namespace,\n translations,\n })),\n };\n if (opts.status === \"draft\" || opts.status === \"translated\") body.status = opts.status;\n if (opts.version) body.version = opts.version;\n\n if (opts.dryRun) {\n console.log(\n `\\n(dry-run) would push ${totalKeys} key(s) across ${body.namespaces.length} ` +\n \"namespace(s); nothing sent.\",\n );\n return;\n }\n\n const report = await importBundle(ctx, body);\n console.log(\"\");\n for (const line of summariseImport(report)) console.log(line);\n if (report.errors?.length || report.glossary_violations?.length) process.exitCode = 1;\n },\n );\n","import { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { type PublishCdnBody, publishCdn } from \"../mcp.js\";\n\nexport const releasesCommand = new Command(\"releases\")\n .description(\"Manage CDN releases for the project.\")\n .addCommand(\n new Command(\"publish\")\n .description(\n \"Trigger a CDN release: build bundles for every (language, namespace) \" +\n \"and push them to the public CDN. Idempotent — unchanged bundles are \" +\n \"reused. Subscribed SDKs receive a live `translations_published` event.\",\n )\n .option(\"--language <code>\", \"Restrict to one language (default: all)\")\n .option(\"--namespace <slug>\", \"Restrict to one namespace (default: all)\")\n .option(\"--version <slug>\", \"Version slug (default: production version)\")\n .option(\"--dry-run\", \"Print the planned release without publishing\", false)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: {\n language?: string;\n namespace?: string;\n version?: string;\n dryRun: boolean;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const scope =\n [\n opts.language && `language=${opts.language}`,\n opts.namespace && `namespace=${opts.namespace}`,\n opts.version && `version=${opts.version}`,\n ]\n .filter(Boolean)\n .join(\", \") || \"ALL languages + namespaces\";\n\n if (opts.dryRun) {\n console.log(`(dry-run) would publish a CDN release for ${scope}; nothing sent.`);\n return;\n }\n\n const body: PublishCdnBody = {};\n if (opts.language) body.language_code = opts.language;\n if (opts.namespace) body.namespace = opts.namespace;\n if (opts.version) body.version_slug = opts.version;\n const res = await publishCdn(ctx, body);\n console.log(`published CDN release for ${scope}`);\n console.log(JSON.stringify(res, null, 2));\n },\n ),\n );\n","import { promises as fs } from \"node:fs\";\n\nimport { Command } from \"commander\";\n\nimport { resolveContext } from \"../api.js\";\nimport { getProjectInfo, requireProject } from \"../mcp.js\";\n\ntype Tree = Record<string, unknown>;\ntype Bundles = Record<string, Record<string, Tree>>;\n\n/** Public CDN bundle URL — the exact path the SDK fetches at runtime. */\nexport function bundleUrl(\n cdnBase: string,\n project: string,\n version: string,\n lang: string,\n ns: string,\n): string {\n return `${cdnBase.replace(/\\/+$/, \"\")}/p/${project}/${version}/latest/${lang}/${ns}.json`;\n}\n\n/**\n * Render the snapshot module. The SDK (#757) reads `initialBundles` as the PURE\n * `Record<locale, Record<namespace, tree>>` map, so provenance lives in a\n * SEPARATE `meta` export — never mixed into `bundles` (otherwise the SDK would\n * treat `version`/`project` as locales).\n */\nexport function renderSnapshot(\n bundles: Bundles,\n meta: { project: string; version: string; cdn: string },\n format: \"ts\" | \"json\",\n): string {\n const json = JSON.stringify(bundles, null, 2);\n if (format === \"json\") return json + \"\\n\";\n return (\n \"// Generated by `sonenta snapshot` — do not edit by hand.\\n\" +\n \"// Build-time fallback for @sonenta/react-i18next:\\n\" +\n '// import { bundles } from \"./<this-file>\";\\n' +\n \"// <VerbumiaProvider initialBundles={bundles} />\\n\" +\n `export const bundles = ${json} as const;\\n\\n` +\n `export const meta = ${JSON.stringify(meta, null, 2)} as const;\\n\\n` +\n \"export default bundles;\\n\"\n );\n}\n\nasync function fetchBundle(url: string): Promise<Tree | null> {\n const res = await fetch(url, { headers: { Accept: \"application/json\" } });\n if (res.status === 404) return null; // bundle not published for this (lang, ns)\n if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);\n return (await res.json()) as Tree;\n}\n\nexport const snapshotCommand = new Command(\"snapshot\")\n .description(\n \"Generate a build-time translations snapshot for @sonenta/react-i18next \" +\n \"`initialBundles` (offline-first fallback). Fetches the PUBLIC CDN bundles \" +\n \"(exactly what the SDK loads at runtime) and assembles \" +\n \"Record<locale, Record<namespace, tree>>. Emits a .ts module (default) or .json.\",\n )\n .option(\"--language <code>\", \"Restrict to a single language code\")\n .option(\"--namespace <slug>\", \"Restrict to a single namespace slug\")\n .option(\"--version <slug>\", \"Version slug (default: the configured version_slug)\")\n .option(\"--format <fmt>\", \"ts | json (default: ts)\", \"ts\")\n .option(\"--cdn <base>\", \"CDN base URL\", \"https://cdn.sonenta.com\")\n .option(\"--out <file>\", \"Write to a file instead of stdout\")\n .option(\"--host <url>\", \"Override host (used to discover languages/namespaces)\")\n .action(\n async (opts: {\n language?: string;\n namespace?: string;\n version?: string;\n format: string;\n cdn: string;\n out?: string;\n host?: string;\n }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const project = requireProject(ctx);\n const version = opts.version ?? ctx.versionSlug;\n\n // Enumerate (language, namespace). Both pinned => no project-info call.\n let languages: string[];\n let namespaces: string[];\n if (opts.language && opts.namespace) {\n languages = [opts.language];\n namespaces = [opts.namespace];\n } else {\n const info = await getProjectInfo(ctx);\n languages = opts.language ? [opts.language] : info.languages;\n namespaces = opts.namespace ? [opts.namespace] : info.namespaces;\n if (languages.length === 0 || namespaces.length === 0) {\n throw new Error(\n \"could not enumerate languages/namespaces from project-info — \" +\n \"pass --language and --namespace explicitly.\",\n );\n }\n }\n\n const bundles: Bundles = {};\n let fetched = 0;\n let missing = 0;\n for (const lang of languages) {\n for (const ns of namespaces) {\n const tree = await fetchBundle(bundleUrl(opts.cdn, project, version, lang, ns));\n if (!tree) {\n missing++;\n continue;\n }\n (bundles[lang] ??= {})[ns] = tree;\n fetched++;\n }\n }\n\n const output = renderSnapshot(\n bundles,\n { project, version, cdn: opts.cdn.replace(/\\/+$/, \"\") },\n opts.format === \"json\" ? \"json\" : \"ts\",\n );\n\n if (opts.out) {\n await fs.writeFile(opts.out, output, \"utf8\");\n console.log(\n `wrote ${opts.out}: ${fetched} bundle(s)` +\n (missing ? `, ${missing} not published (404)` : \"\"),\n );\n } else {\n process.stdout.write(output);\n if (missing) console.error(`(${missing} bundle(s) not published)`);\n }\n },\n );\n","import { Command } from \"commander\";\n\nimport { type ApiContext, resolveContext } from \"../api.js\";\nimport {\n DEFAULT_LOCALES_DIR,\n diffFlat,\n type FlatTranslations,\n listLocaleFiles,\n readLocaleFile,\n} from \"../locales.js\";\nimport { getProjectInfo, listKeys } from \"../mcp.js\";\n\nexport const statusCommand = new Command(\"status\")\n .description(\"Diff local locales/ against the remote project state.\")\n .option(\"--language <code>\", \"Restrict to one language code\")\n .option(\"--namespace <slug>\", \"Restrict to one namespace slug\")\n .option(\"--src <dir>\", \"Locales directory\", DEFAULT_LOCALES_DIR)\n .option(\"--host <url>\", \"Override host (otherwise from config/credentials)\")\n .action(\n async (opts: { language?: string; namespace?: string; src: string; host?: string }) => {\n const ctx = await resolveContext({ hostOverride: opts.host });\n const knownLangs = new Set((await getProjectInfo(ctx)).languages);\n\n const files = (await listLocaleFiles(opts.src)).filter((f) => {\n if (opts.language && f.lang !== opts.language) return false;\n if (opts.namespace && f.namespace !== opts.namespace) return false;\n return true;\n });\n\n // Fetch remote once per language, grouped by namespace slug.\n const remoteByLang = new Map<string, Map<string, FlatTranslations>>();\n async function remoteFor(ctx2: ApiContext, lang: string): Promise<Map<string, FlatTranslations>> {\n const cached = remoteByLang.get(lang);\n if (cached) return cached;\n const m = new Map<string, FlatTranslations>();\n for (const it of await listKeys(ctx2, { languageCode: lang })) {\n const tr = it.translations?.find((t) => t.language_code === lang);\n if (!tr || tr.value === \"\") continue;\n const map = m.get(it.namespace_slug) ?? {};\n map[it.key_name] = tr.value;\n m.set(it.namespace_slug, map);\n }\n remoteByLang.set(lang, m);\n return m;\n }\n\n let totalAdded = 0;\n let totalRemoved = 0;\n let totalChanged = 0;\n let totalUnchanged = 0;\n\n for (const f of files) {\n if (knownLangs.size > 0 && !knownLangs.has(f.lang)) {\n console.log(`! ${f.lang}/${f.namespace}.json — language not in project, skipped`);\n continue;\n }\n const local = await readLocaleFile(f.path);\n const remote = (await remoteFor(ctx, f.lang)).get(f.namespace) ?? {};\n const d = diffFlat(local, remote);\n totalAdded += d.added.length;\n totalRemoved += d.removed.length;\n totalChanged += d.changed.length;\n totalUnchanged += d.unchanged;\n\n if (d.added.length === 0 && d.removed.length === 0 && d.changed.length === 0) {\n console.log(`= ${f.lang}/${f.namespace}.json (in sync, ${d.unchanged} keys)`);\n continue;\n }\n console.log(\n `~ ${f.lang}/${f.namespace}.json +${d.added.length} -${d.removed.length} ~${d.changed.length}`,\n );\n for (const k of d.added) console.log(` + ${k}`);\n for (const k of d.removed) console.log(` - ${k} (remote-only — push leaves untouched)`);\n for (const c of d.changed) console.log(` ~ ${c.key} \"${c.local}\" <- \"${c.remote}\"`);\n }\n console.log(\n `summary: +${totalAdded} -${totalRemoved} ~${totalChanged} unchanged=${totalUnchanged}`,\n );\n },\n );\n","import { Command } from \"commander\";\n\nimport { readCredentials } from \"../credentials.js\";\n\nexport const whoamiCommand = new Command(\"whoami\")\n .description(\"Show the configured default host + which API key is in use.\")\n .option(\"--host <url>\", \"Inspect a specific host instead of the default\")\n .action(async (opts: { host?: string }) => {\n const creds = await readCredentials();\n if (!creds.default && Object.keys(creds.hosts).length === 0) {\n console.log(\"Not logged in. Run `sonenta login --host <url> --token <…>`.\");\n process.exit(1);\n }\n const target = opts.host ?? creds.default;\n if (!target) {\n console.log(\"No default host set.\");\n process.exit(1);\n }\n const entry = creds.hosts[target];\n if (!entry) {\n console.log(`No credentials stored for ${target}.`);\n process.exit(1);\n }\n const masked = entry.api_key.replace(/\\.[\\s\\S]+$/, \".•••\");\n const envOverride = (process.env.SONENTA_TOKEN ?? process.env.VERBUMIA_TOKEN)?.trim();\n console.log(`host: ${target}${target === creds.default ? \" (default)\" : \"\"}`);\n console.log(`api_key: ${masked}${envOverride ? \" (overridden by SONENTA_TOKEN env)\" : \"\"}`);\n if (entry.user_email) console.log(`email: ${entry.user_email}`);\n const others = Object.keys(creds.hosts).filter((h) => h !== target);\n if (others.length) {\n console.log(`other: ${others.join(\", \")}`);\n }\n });\n"],"mappings":"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","names":["Command","fs","join","Command","fs","resolve","fs","Command","join","fs","fs","dirname","Command","dirname","fs","Command","resolve","Command","Command","resolve","Command","Command","Command","resolve","Command","Command","Command","Command","Command","Command","Command","Command","fs","join","resolve","Command","Command","Command","Command","Command","fs","Command","Command","fs","Command","Command","Command","Command","Command"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonenta/cli",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Command-line interface for Sonenta translation management.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://sonenta.com",