transduck 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/backend.js CHANGED
@@ -4,11 +4,31 @@
4
4
  import { getProvider } from './providers/index.js';
5
5
  // Re-export prompts for backward compat (tests import buildMessages from backend)
6
6
  export { buildMessages, buildPluralMessages } from './providers/prompts.js';
7
+ function checkApiKey(config) {
8
+ if (config.provider === 'claude_code') {
9
+ const token = process.env[config.tokenEnv];
10
+ if (!token) {
11
+ throw new Error(`Missing API token: ${config.tokenEnv} environment variable is not set.\n` +
12
+ `Run: export ${config.tokenEnv}=your-token-here`);
13
+ }
14
+ return;
15
+ }
16
+ const apiKey = process.env[config.apiKeyEnv];
17
+ if (!apiKey) {
18
+ throw new Error(`Missing API key: ${config.apiKeyEnv} environment variable is not set.\n` +
19
+ `Run: export ${config.apiKeyEnv}=your-key-here\n` +
20
+ `Or add it to your .env file.`);
21
+ }
22
+ }
7
23
  export async function translate(sourceText, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride) {
24
+ if (!_clientOverride)
25
+ checkApiKey(config);
8
26
  const provider = await getProvider(config);
9
27
  return provider.translate(sourceText, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride);
10
28
  }
11
29
  export async function translatePlural(one, other, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride) {
30
+ if (!_clientOverride)
31
+ checkApiKey(config);
12
32
  const provider = await getProvider(config);
13
33
  return provider.translatePlural(one, other, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride);
14
34
  }
package/dist/cli.d.ts CHANGED
@@ -43,6 +43,13 @@ export declare function runScan(opts: ScanOptions): Promise<string>;
43
43
  export interface StatsOptions {
44
44
  configPath?: string;
45
45
  }
46
+ export interface ClearOptions {
47
+ lang?: string;
48
+ failedOnly?: boolean;
49
+ yes?: boolean;
50
+ configPath?: string;
51
+ }
52
+ export declare function runClear(opts: ClearOptions): Promise<string>;
46
53
  export declare function runStats(opts: StatsOptions): Promise<string>;
47
54
  declare const program: Command;
48
55
  export { program };
package/dist/cli.js CHANGED
@@ -434,6 +434,45 @@ export async function runScan(opts) {
434
434
  }
435
435
  return lines.join('\n');
436
436
  }
437
+ export async function runClear(opts) {
438
+ const cfg = loadConfig(opts.configPath);
439
+ const store = new TranslationStore(cfg.storagePath);
440
+ await store.initialize();
441
+ const targetLang = opts.lang?.toUpperCase();
442
+ const entryCount = store.count(targetLang, opts.failedOnly);
443
+ let desc;
444
+ if (targetLang && opts.failedOnly) {
445
+ desc = `${entryCount} failed translation(s) for ${targetLang}`;
446
+ }
447
+ else if (targetLang) {
448
+ desc = `${entryCount} translation(s) for ${targetLang}`;
449
+ }
450
+ else if (opts.failedOnly) {
451
+ desc = `${entryCount} failed translation(s) across all languages`;
452
+ }
453
+ else {
454
+ desc = `${entryCount} translation(s) across all languages`;
455
+ }
456
+ if (entryCount === 0) {
457
+ store.close();
458
+ return 'Nothing to clear.';
459
+ }
460
+ if (!opts.yes) {
461
+ const readline = await import('readline');
462
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
463
+ const answer = await new Promise(resolve => {
464
+ rl.question(`This will delete ${desc}. Type "Yes" to confirm: `, resolve);
465
+ });
466
+ rl.close();
467
+ if (answer !== 'Yes') {
468
+ store.close();
469
+ return 'Cancelled.';
470
+ }
471
+ }
472
+ await store.clear(targetLang, opts.failedOnly);
473
+ store.close();
474
+ return `Deleted ${desc}.`;
475
+ }
437
476
  export async function runStats(opts) {
438
477
  const cfg = loadConfig(opts.configPath);
439
478
  const store = new TranslationStore(cfg.storagePath);
@@ -454,7 +493,7 @@ export async function runStats(opts) {
454
493
  }
455
494
  // CLI entry point
456
495
  const program = new Command();
457
- program.name('transduck').description('AI-native translation tool').version('0.5.0');
496
+ program.name('transduck').description('AI-native translation tool').version('0.5.1');
458
497
  program.command('init')
459
498
  .description('Initialize a new transduck project')
460
499
  .action(async () => {
@@ -539,6 +578,21 @@ program.command('warm')
539
578
  });
540
579
  console.log(output);
541
580
  });
581
+ program.command('clear')
582
+ .description('Clear cached translations from the database')
583
+ .option('--lang <code>', 'Only clear translations for this language')
584
+ .option('--failed-only', 'Only clear failed translations')
585
+ .option('-y, --yes', 'Skip confirmation prompt')
586
+ .option('--config <path>', 'Path to transduck.yaml')
587
+ .action(async (opts) => {
588
+ const output = await runClear({
589
+ lang: opts.lang,
590
+ failedOnly: opts.failedOnly,
591
+ yes: opts.yes,
592
+ configPath: opts.config ?? '',
593
+ });
594
+ console.log(output);
595
+ });
542
596
  program.command('stats')
543
597
  .description('Show translation database statistics')
544
598
  .option('--config <path>', 'Path to transduck.yaml')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "transduck",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "AI-native translation tool using source text as keys",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/backend.ts CHANGED
@@ -8,6 +8,27 @@ import { getProvider } from './providers/index.js';
8
8
  // Re-export prompts for backward compat (tests import buildMessages from backend)
9
9
  export { buildMessages, buildPluralMessages } from './providers/prompts.js';
10
10
 
11
+ function checkApiKey(config: TransduckConfig): void {
12
+ if (config.provider === 'claude_code') {
13
+ const token = process.env[config.tokenEnv];
14
+ if (!token) {
15
+ throw new Error(
16
+ `Missing API token: ${config.tokenEnv} environment variable is not set.\n` +
17
+ `Run: export ${config.tokenEnv}=your-token-here`
18
+ );
19
+ }
20
+ return;
21
+ }
22
+ const apiKey = process.env[config.apiKeyEnv];
23
+ if (!apiKey) {
24
+ throw new Error(
25
+ `Missing API key: ${config.apiKeyEnv} environment variable is not set.\n` +
26
+ `Run: export ${config.apiKeyEnv}=your-key-here\n` +
27
+ `Or add it to your .env file.`
28
+ );
29
+ }
30
+ }
31
+
11
32
  export async function translate(
12
33
  sourceText: string,
13
34
  sourceLang: string,
@@ -17,6 +38,7 @@ export async function translate(
17
38
  config: TransduckConfig,
18
39
  _clientOverride?: any,
19
40
  ): Promise<string> {
41
+ if (!_clientOverride) checkApiKey(config);
20
42
  const provider = await getProvider(config);
21
43
  return provider.translate(sourceText, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride);
22
44
  }
@@ -31,6 +53,7 @@ export async function translatePlural(
31
53
  config: TransduckConfig,
32
54
  _clientOverride?: any,
33
55
  ): Promise<Record<string, string>> {
56
+ if (!_clientOverride) checkApiKey(config);
34
57
  const provider = await getProvider(config);
35
58
  return provider.translatePlural(one, other, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride);
36
59
  }
package/src/cli.ts CHANGED
@@ -539,6 +539,55 @@ export interface StatsOptions {
539
539
  configPath?: string;
540
540
  }
541
541
 
542
+ export interface ClearOptions {
543
+ lang?: string;
544
+ failedOnly?: boolean;
545
+ yes?: boolean;
546
+ configPath?: string;
547
+ }
548
+
549
+ export async function runClear(opts: ClearOptions): Promise<string> {
550
+ const cfg = loadConfig(opts.configPath);
551
+ const store = new TranslationStore(cfg.storagePath);
552
+ await store.initialize();
553
+
554
+ const targetLang = opts.lang?.toUpperCase();
555
+ const entryCount = store.count(targetLang, opts.failedOnly);
556
+
557
+ let desc: string;
558
+ if (targetLang && opts.failedOnly) {
559
+ desc = `${entryCount} failed translation(s) for ${targetLang}`;
560
+ } else if (targetLang) {
561
+ desc = `${entryCount} translation(s) for ${targetLang}`;
562
+ } else if (opts.failedOnly) {
563
+ desc = `${entryCount} failed translation(s) across all languages`;
564
+ } else {
565
+ desc = `${entryCount} translation(s) across all languages`;
566
+ }
567
+
568
+ if (entryCount === 0) {
569
+ store.close();
570
+ return 'Nothing to clear.';
571
+ }
572
+
573
+ if (!opts.yes) {
574
+ const readline = await import('readline');
575
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
576
+ const answer = await new Promise<string>(resolve => {
577
+ rl.question(`This will delete ${desc}. Type "Yes" to confirm: `, resolve);
578
+ });
579
+ rl.close();
580
+ if (answer !== 'Yes') {
581
+ store.close();
582
+ return 'Cancelled.';
583
+ }
584
+ }
585
+
586
+ await store.clear(targetLang, opts.failedOnly);
587
+ store.close();
588
+ return `Deleted ${desc}.`;
589
+ }
590
+
542
591
  export async function runStats(opts: StatsOptions): Promise<string> {
543
592
  const cfg = loadConfig(opts.configPath);
544
593
  const store = new TranslationStore(cfg.storagePath);
@@ -562,7 +611,7 @@ export async function runStats(opts: StatsOptions): Promise<string> {
562
611
  // CLI entry point
563
612
  const program = new Command();
564
613
 
565
- program.name('transduck').description('AI-native translation tool').version('0.5.0');
614
+ program.name('transduck').description('AI-native translation tool').version('0.5.1');
566
615
 
567
616
  program.command('init')
568
617
  .description('Initialize a new transduck project')
@@ -659,6 +708,22 @@ program.command('warm')
659
708
  console.log(output);
660
709
  });
661
710
 
711
+ program.command('clear')
712
+ .description('Clear cached translations from the database')
713
+ .option('--lang <code>', 'Only clear translations for this language')
714
+ .option('--failed-only', 'Only clear failed translations')
715
+ .option('-y, --yes', 'Skip confirmation prompt')
716
+ .option('--config <path>', 'Path to transduck.yaml')
717
+ .action(async (opts: { lang?: string; failedOnly?: boolean; yes?: boolean; config?: string }) => {
718
+ const output = await runClear({
719
+ lang: opts.lang,
720
+ failedOnly: opts.failedOnly,
721
+ yes: opts.yes,
722
+ configPath: opts.config ?? '',
723
+ });
724
+ console.log(output);
725
+ });
726
+
662
727
  program.command('stats')
663
728
  .description('Show translation database statistics')
664
729
  .option('--config <path>', 'Path to transduck.yaml')