locai 2.2.0 → 2.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/dist/cli.js CHANGED
@@ -1,147 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import dotenv from "dotenv";
3
- import { promises as fs } from "fs";
4
- import path from "path";
5
- import os from "os";
6
2
  import { createRequire } from "module";
7
3
  import { program } from "commander";
8
- import { translateFile, findLocaleFiles, validateAndFixExistingTranslations, } from "./commands/translator.js";
9
- import ProviderFactory from "./core/provider-factory.js";
10
- import { FileManager } from "./utils/file-manager.js";
11
- import rateLimiter from "./utils/rate-limiter.js";
12
- import Orchestrator from "./core/orchestrator.js";
13
- import InputValidator from "./utils/input-validator.js";
14
4
  import ErrorHelper from "./utils/error-helper.js";
15
- import { getLogger } from "./utils/logger.js";
16
- import { FrameworkDetector } from "./core/detection/framework-detector.js";
17
- import { StructureDetector, LocaleStructure } from "./core/detection/structure-detector.js";
18
- import { loadConfig } from "./config/index.js";
5
+ import { loadConfig, validateEnvironment } from "./config/index.js";
6
+ import { loadEnvironmentVariables, configureComponents } from "./config/setup.js";
7
+ import { autoDetectConfig, runCommand } from "./cli/helpers.js";
8
+ import InputValidator from "./utils/input-validator.js";
19
9
  // Use createRequire to load package.json in ESM context
20
10
  const require = createRequire(import.meta.url);
21
11
  const { version } = require("../package.json");
22
- /**
23
- * Load environment variables from .env files.
24
- * Prioritizes .env.local over .env.
25
- */
26
- const loadEnvironmentVariables = async () => {
27
- // Load environment files in priority order
28
- // First load .env (base defaults), then .env.local (local overrides)
29
- // This ensures .env.local values take precedence over .env values
30
- const envFiles = [
31
- { file: ".env", override: false },
32
- { file: ".env.local", override: true },
33
- ];
34
- let loadedCount = 0;
35
- for (const { file: envFile, override } of envFiles) {
36
- const envPath = path.resolve(process.cwd(), envFile);
37
- try {
38
- await fs.access(envPath);
39
- const result = dotenv.config({ path: envPath, override });
40
- if (!result.error) {
41
- loadedCount++;
42
- // Only log in verbose/debug mode to reduce noise
43
- if (process.env.VERBOSE || process.env.DEBUG) {
44
- console.log(`Loaded environment variables from ${envFile}`);
45
- }
46
- }
47
- }
48
- catch (error) {
49
- // ENOENT is expected when files don't exist - silently skip
50
- if (error.code !== "ENOENT") {
51
- console.warn(`Warning: Could not load ${envFile}: ${error.message}`);
52
- }
53
- }
54
- }
55
- // Informative message only if verbose/debug and at least one file was loaded
56
- if ((process.env.VERBOSE || process.env.DEBUG) && loadedCount > 0) {
57
- console.log(`Environment: ${loadedCount} file(s) loaded`);
58
- }
59
- };
60
- // Local config loader replaced by src/config/index.ts
61
- /**
62
- * Configure global components (logger, rate limiter, etc.).
63
- * @param {any} config - Configuration object.
64
- */
65
- const configureComponents = (config) => {
66
- // Configure FileManager with config settings
67
- if (config.fileOperations) {
68
- FileManager.configure(config.fileOperations);
69
- }
70
- // Initialize logger with config
71
- if (config.logging) {
72
- const logger = getLogger(config.logging);
73
- if (config.logging.verbose || config.debug) {
74
- logger.info("Logger initialized", {
75
- saveErrorLogs: config.logging.saveErrorLogs,
76
- logDirectory: config.logging.logDirectory,
77
- rotationEnabled: config.logging.logRotation?.enabled,
78
- });
79
- }
80
- }
81
- if (config.rateLimiter) {
82
- rateLimiter.updateConfig({
83
- queueStrategy: config.rateLimiter.queueStrategy || "priority",
84
- queueTimeout: config.rateLimiter.queueTimeout || 30000,
85
- adaptiveThrottling: config.rateLimiter.adaptiveThrottling !== false,
86
- providerLimits: config.rateLimiter.providerLimits,
87
- });
88
- }
89
- if (config.advanced?.debug) {
90
- process.env.DEBUG = "true";
91
- }
92
- if (config.logging?.verbose) {
93
- process.env.VERBOSE = "true";
94
- }
95
- };
96
- /**
97
- * Configure and run the CLI program.
98
- * @param {any} defaultConfig - Default configuration.
99
- * @returns {Promise<any>} - Final configuration.
100
- */
101
- const configureCLI = async (defaultConfig) => {
102
- // Auto-detect framework and configuration if not explicitly set
103
- // This makes the tool framework-agnostic
104
- const detected = await FrameworkDetector.detect(process.cwd());
105
- if (detected) {
106
- // Set locales directory if not configured
107
- if (!defaultConfig.localesDir && detected.localesDir) {
108
- defaultConfig.localesDir = detected.localesDir;
109
- if (process.env.VERBOSE) {
110
- console.log(`Auto-detected locales directory: ${detected.localesDir}`);
111
- }
112
- }
113
- // Set file format if not configured
114
- if (!defaultConfig.fileOperations)
115
- defaultConfig.fileOperations = {};
116
- if ((!defaultConfig.fileOperations.format ||
117
- defaultConfig.fileOperations.format === "auto") &&
118
- detected.fileFormat) {
119
- defaultConfig.fileOperations.format = detected.fileFormat;
120
- if (process.env.VERBOSE) {
121
- console.log(`Auto-detected file format: ${detected.fileFormat}`);
122
- }
123
- }
124
- }
125
- // Auto-detect file structure (flat vs nested)
126
- if (!defaultConfig.fileOperations)
127
- defaultConfig.fileOperations = {};
128
- if (!defaultConfig.fileOperations.fileStructure ||
129
- defaultConfig.fileOperations.fileStructure === "auto") {
130
- const targetDir = defaultConfig.localesDir || "./locales";
131
- const structure = await StructureDetector.detect(targetDir);
132
- if (structure !== LocaleStructure.UNKNOWN) {
133
- defaultConfig.fileOperations.fileStructure = structure;
134
- if (process.env.VERBOSE) {
135
- console.log(`Auto-detected file structure: ${structure}`);
136
- }
137
- }
138
- else {
139
- // Default to flat if unknown
140
- defaultConfig.fileOperations.fileStructure = LocaleStructure.FLAT;
141
- }
142
- }
12
+ const main = async () => {
13
+ await loadEnvironmentVariables();
14
+ // Load configuration
15
+ const loaded = await loadConfig();
16
+ let defaultConfig = loaded.config || {};
17
+ defaultConfig = await autoDetectConfig(defaultConfig);
143
18
  configureComponents(defaultConfig);
144
- // Use config version if available, otherwise use package.json version
19
+ validateEnvironment(defaultConfig);
145
20
  const toolVersion = defaultConfig.version || version;
146
21
  program
147
22
  .name("locai")
@@ -161,278 +36,6 @@ const configureCLI = async (defaultConfig) => {
161
36
  process.env.VERBOSE = "true";
162
37
  console.log("Verbose mode: ENABLED (detailed diagnostics)");
163
38
  });
164
- const runCommand = async (options, commandOptions, commandName) => {
165
- try {
166
- const validCommands = ["translate", "fix", "analyze", "advanced"];
167
- if (!validCommands.includes(commandName)) {
168
- throw new Error(`Invalid command: ${commandName}`);
169
- }
170
- const globalOpts = program.opts();
171
- if (globalOpts.debug) {
172
- const safeGlobalOpts = { ...globalOpts };
173
- const safeCommandOptions = { ...commandOptions };
174
- if (safeGlobalOpts.targets) {
175
- safeGlobalOpts.targets = `[${safeGlobalOpts.targets.length} languages]`;
176
- }
177
- if (safeGlobalOpts.localesDir) {
178
- safeGlobalOpts.localesDir = "[directory_path]";
179
- }
180
- if (safeCommandOptions.provider) {
181
- safeCommandOptions.provider = "[provider_name]";
182
- }
183
- console.log("CLI Command:", commandName);
184
- console.log("Global Options:", JSON.stringify(safeGlobalOpts, null, 2));
185
- console.log("Command Options:", JSON.stringify(safeCommandOptions, null, 2));
186
- }
187
- const sanitizedGlobalOpts = { ...globalOpts };
188
- const sanitizedCommandOptions = { ...commandOptions };
189
- if (sanitizedGlobalOpts.source) {
190
- sanitizedGlobalOpts.source = InputValidator.validateLanguageCode(sanitizedGlobalOpts.source, "source language");
191
- }
192
- if (sanitizedGlobalOpts.targets && Array.isArray(sanitizedGlobalOpts.targets)) {
193
- sanitizedGlobalOpts.targets = InputValidator.validateLanguageCodes(sanitizedGlobalOpts.targets, "target languages");
194
- }
195
- if (sanitizedGlobalOpts.localesDir) {
196
- sanitizedGlobalOpts.localesDir = InputValidator.validateDirectoryPath(sanitizedGlobalOpts.localesDir, "locales directory");
197
- }
198
- if (sanitizedCommandOptions.provider) {
199
- sanitizedCommandOptions.provider = InputValidator.validateProvider(sanitizedCommandOptions.provider, "API provider");
200
- }
201
- if (sanitizedCommandOptions.concurrency !== undefined) {
202
- const concurrency = parseInt(sanitizedCommandOptions.concurrency);
203
- if (isNaN(concurrency) || concurrency < 1 || concurrency > 20) {
204
- throw new Error("Concurrency must be a number between 1 and 20");
205
- }
206
- sanitizedCommandOptions.concurrency = concurrency;
207
- }
208
- if (sanitizedCommandOptions.contextThreshold !== undefined) {
209
- const threshold = parseInt(sanitizedCommandOptions.contextThreshold);
210
- if (isNaN(threshold) || threshold < 1 || threshold > 10) {
211
- throw new Error("Context threshold must be a number between 1 and 10");
212
- }
213
- sanitizedCommandOptions.contextThreshold = threshold;
214
- }
215
- if (sanitizedCommandOptions.contextConfidence !== undefined) {
216
- const confidence = parseFloat(sanitizedCommandOptions.contextConfidence);
217
- if (isNaN(confidence) || confidence < 0 || confidence > 1) {
218
- throw new Error("Context confidence must be a number between 0 and 1");
219
- }
220
- sanitizedCommandOptions.contextConfidence = confidence;
221
- }
222
- if (sanitizedCommandOptions.length) {
223
- const validLengthModes = ["strict", "flexible", "exact", "relaxed", "smart"];
224
- if (!validLengthModes.includes(sanitizedCommandOptions.length)) {
225
- throw new Error(`Invalid length mode: ${sanitizedCommandOptions.length}. Valid modes: ${validLengthModes.join(", ")}`);
226
- }
227
- }
228
- const mergedOpts = {
229
- source: defaultConfig.source,
230
- targets: defaultConfig.targets,
231
- localesDir: defaultConfig.localesDir,
232
- apiProvider: defaultConfig.apiProvider,
233
- ...(sanitizedGlobalOpts.source && { source: sanitizedGlobalOpts.source }),
234
- ...(sanitizedGlobalOpts.targets && { targets: sanitizedGlobalOpts.targets }),
235
- ...(sanitizedGlobalOpts.localesDir && {
236
- localesDir: sanitizedGlobalOpts.localesDir,
237
- }),
238
- ...sanitizedCommandOptions,
239
- };
240
- let concurrencyLimit = parseInt(mergedOpts.concurrency) || defaultConfig.concurrencyLimit || 5;
241
- if (mergedOpts.autoOptimize) {
242
- const cpuCount = os.cpus().length;
243
- const memoryGB = Math.floor(os.totalmem() / (1024 * 1024 * 1024));
244
- if (memoryGB < 4) {
245
- concurrencyLimit = Math.min(3, cpuCount);
246
- }
247
- else if (memoryGB < 8) {
248
- concurrencyLimit = Math.min(5, Math.ceil(cpuCount * 0.5));
249
- }
250
- else {
251
- concurrencyLimit = Math.min(10, Math.ceil(cpuCount * 0.75));
252
- }
253
- console.log(`Auto-optimized settings for your system (${cpuCount} CPUs, ${memoryGB}GB RAM):`);
254
- console.log(` - Concurrency: ${concurrencyLimit}`);
255
- }
256
- if (concurrencyLimit < 1 || concurrencyLimit > 20) {
257
- throw new Error("Invalid concurrency limit after optimization");
258
- }
259
- const finalConfig = {
260
- ...defaultConfig,
261
- command: commandName,
262
- source: mergedOpts.source,
263
- targets: mergedOpts.targets,
264
- localesDir: mergedOpts.localesDir,
265
- apiProvider: mergedOpts.provider || defaultConfig.apiProvider,
266
- concurrencyLimit: concurrencyLimit,
267
- cacheEnabled: mergedOpts.noCache === undefined
268
- ? defaultConfig.cacheEnabled
269
- : !mergedOpts.noCache,
270
- debug: mergedOpts.debug,
271
- verbose: mergedOpts.verbose || defaultConfig.logging?.verbose || false,
272
- forceUpdate: mergedOpts.force || false,
273
- showDetailedStats: mergedOpts.stats || false,
274
- autoOptimize: mergedOpts.autoOptimize || defaultConfig.advanced?.autoOptimize || false,
275
- fixLength: commandName === "fix",
276
- apiConfig: defaultConfig.apiConfig || {},
277
- styleGuide: defaultConfig.styleGuide,
278
- qualityChecks: defaultConfig.qualityChecks,
279
- lengthControl: {
280
- ...defaultConfig.lengthControl,
281
- mode: mergedOpts.length || defaultConfig.lengthControl?.mode || "smart",
282
- },
283
- retryOptions: {
284
- ...defaultConfig.retryOptions,
285
- maxRetries: mergedOpts.maxRetries || defaultConfig.retryOptions?.maxRetries || 2,
286
- initialDelay: mergedOpts.initialDelay || defaultConfig.retryOptions?.initialDelay || 1000,
287
- maxDelay: mergedOpts.maxDelay || defaultConfig.retryOptions?.maxDelay || 10000,
288
- jitter: defaultConfig.retryOptions?.jitter !== false,
289
- },
290
- context: {
291
- ...defaultConfig.context,
292
- enabled: true,
293
- debug: mergedOpts.contextDebug || defaultConfig.context.debug || false,
294
- useAI: mergedOpts.useAi ||
295
- mergedOpts.contextProvider !== undefined ||
296
- defaultConfig.context.useAI ||
297
- false,
298
- aiProvider: mergedOpts.contextProvider || defaultConfig.context.aiProvider,
299
- minTextLength: mergedOpts.minTextLength || defaultConfig.context.minTextLength,
300
- allowNewCategories: mergedOpts.allowNewCategories !== undefined
301
- ? mergedOpts.allowNewCategories
302
- : defaultConfig.context.allowNewCategories,
303
- detection: {
304
- threshold: mergedOpts.contextThreshold ||
305
- defaultConfig.context.detection?.threshold ||
306
- 2,
307
- minConfidence: mergedOpts.contextConfidence ||
308
- defaultConfig.context.detection?.minConfidence ||
309
- 0.6,
310
- },
311
- },
312
- advanced: {
313
- ...defaultConfig.advanced,
314
- timeoutMs: mergedOpts.timeout || defaultConfig.advanced?.timeoutMs || 60000,
315
- maxKeyLength: defaultConfig.advanced?.maxKeyLength || 10000,
316
- maxBatchSize: defaultConfig.advanced?.maxBatchSize || 50,
317
- autoOptimize: mergedOpts.autoOptimize || defaultConfig.advanced?.autoOptimize || false,
318
- debug: mergedOpts.debug || defaultConfig.advanced?.debug || false,
319
- },
320
- rateLimiter: {
321
- ...defaultConfig.rateLimiter,
322
- enabled: defaultConfig.rateLimiter?.enabled !== false,
323
- },
324
- fileOperations: defaultConfig.fileOperations || {},
325
- logging: {
326
- ...defaultConfig.logging,
327
- verbose: mergedOpts.verbose || defaultConfig.logging?.verbose || false,
328
- },
329
- };
330
- try {
331
- InputValidator.validateConfig(finalConfig);
332
- if (finalConfig.debug || finalConfig.verbose) {
333
- console.log("Configuration validated successfully");
334
- }
335
- }
336
- catch (configError) {
337
- const error = ErrorHelper.configValidationError(configError.message.includes("validation failed")
338
- ? configError.message.split("\n").slice(1)
339
- : [configError.message]);
340
- console.error(ErrorHelper.formatError(error, { showDebug: globalOpts.debug }));
341
- process.exit(1);
342
- }
343
- configureComponents(finalConfig);
344
- validateEnvironment();
345
- if (finalConfig.debug) {
346
- console.log("\nConfiguration details:");
347
- const safeConfig = {
348
- ...finalConfig,
349
- apiConfig: Object.keys(finalConfig.apiConfig || {}).reduce((acc, provider) => {
350
- acc[provider] = {
351
- model: finalConfig.apiConfig[provider]?.model || "configured",
352
- temperature: finalConfig.apiConfig[provider]?.temperature,
353
- maxTokens: finalConfig.apiConfig[provider]?.maxTokens,
354
- };
355
- return acc;
356
- }, {}),
357
- advanced: {
358
- ...finalConfig.advanced,
359
- timeoutMs: finalConfig.advanced?.timeoutMs,
360
- maxKeyLength: finalConfig.advanced?.maxKeyLength,
361
- maxBatchSize: finalConfig.advanced?.maxBatchSize,
362
- autoOptimize: finalConfig.advanced?.autoOptimize,
363
- debug: finalConfig.advanced?.debug,
364
- },
365
- };
366
- delete safeConfig.apiProvider; // Could leak preferred provider info
367
- delete safeConfig.localesDir; // Could leak file system structure
368
- console.log(JSON.stringify(safeConfig, null, 2));
369
- }
370
- await displayPerformanceTips(finalConfig);
371
- const localesDir = path.resolve(finalConfig.localesDir);
372
- console.log(`\nLooking for source files in: ${localesDir}`);
373
- const files = await findLocaleFiles(localesDir, finalConfig.source);
374
- if (!files || !files.length) {
375
- const error = ErrorHelper.fileNotFoundError(`${localesDir}/${finalConfig.source}.json`, `${finalConfig.source}.json`);
376
- console.error(ErrorHelper.formatError(error, {
377
- showDebug: finalConfig.debug,
378
- showContext: true,
379
- }));
380
- process.exit(1);
381
- }
382
- switch (commandName) {
383
- case "fix":
384
- console.log("\nRunning in FIX mode");
385
- await Promise.all(files.map((file) => validateAndFixExistingTranslations(file, finalConfig)));
386
- break;
387
- case "analyze":
388
- console.log("\nRunning in ANALYZE mode");
389
- console.log("Context analysis mode is not fully implemented yet.");
390
- break;
391
- case "advanced":
392
- console.log("\nRunning ADVANCED configuration");
393
- // Fall through to translate
394
- case "translate":
395
- default:
396
- console.log("\nRunning in TRANSLATION mode");
397
- if (finalConfig.context.useAI) {
398
- console.log(`AI Context Analysis: ENABLED (Provider: ${finalConfig.context.aiProvider || finalConfig.apiProvider})`);
399
- if (finalConfig.context.allowNewCategories) {
400
- console.log("New category suggestions: ENABLED");
401
- }
402
- }
403
- console.log(`Performance settings:`);
404
- console.log(` - Concurrency: ${finalConfig.concurrencyLimit} parallel operations`);
405
- console.log(` - Caching: ${finalConfig.cacheEnabled ? "Enabled" : "Disabled"}`);
406
- console.log(` - Retries: ${finalConfig.retryOptions.maxRetries} max retries`);
407
- if (finalConfig.rateLimiter?.adaptiveThrottling) {
408
- console.log(` - Adaptive Rate Limiting: Enabled`);
409
- }
410
- if (finalConfig.forceUpdate) {
411
- console.log(`Warning: Force update mode ENABLED (will update existing translations)`);
412
- }
413
- for (const file of files) {
414
- await translateFile(file, finalConfig);
415
- }
416
- // Exit after successful translation in CLI mode
417
- // This allows translateFile to be used as library without forcing exit
418
- process.exit(0);
419
- break;
420
- }
421
- // Other commands (fix, analyze) handle their own completion
422
- }
423
- catch (validationError) {
424
- if (validationError.message.includes("Configuration validation")) {
425
- // Already handled above with better formatting
426
- return;
427
- }
428
- console.error(`\nInput validation error: ${validationError.message}`);
429
- if (validationError.stack && process.env.DEBUG) {
430
- console.error("\nStack trace:");
431
- console.error(validationError.stack);
432
- }
433
- process.exit(1);
434
- }
435
- };
436
39
  program
437
40
  .command("translate")
438
41
  .description("Translate missing strings (default command)")
@@ -447,10 +50,9 @@ const configureCLI = async (defaultConfig) => {
447
50
  .option("--save-review-queue", "Save low-confidence items for manual review", false)
448
51
  .action(async (options) => {
449
52
  try {
450
- await runCommand(program.opts(), options, "translate");
53
+ await runCommand(defaultConfig, options, "translate");
451
54
  }
452
55
  catch (error) {
453
- // Handle errors with better formatting
454
56
  if (error.code && error.code.startsWith("ERR_")) {
455
57
  console.error(ErrorHelper.formatError(error, {
456
58
  showDebug: process.env.DEBUG === "true",
@@ -473,7 +75,7 @@ const configureCLI = async (defaultConfig) => {
473
75
  .option("--length", "Fix length issues in existing translations", true)
474
76
  .action(async (options) => {
475
77
  try {
476
- await runCommand(program.opts(), options, "fix");
78
+ await runCommand(defaultConfig, options, "fix");
477
79
  }
478
80
  catch (error) {
479
81
  if (error.code && error.code.startsWith("ERR_")) {
@@ -491,12 +93,12 @@ const configureCLI = async (defaultConfig) => {
491
93
  program
492
94
  .command("analyze")
493
95
  .description("Analyze context patterns")
494
- .option("--use-ai", "Enable AI-based context analysis", defaultConfig.context.useAI)
495
- .option("--context-provider <provider>", "AI provider for analysis", defaultConfig.context.aiProvider)
496
- .option("--context-threshold <number>", "Minimum match count", Number, defaultConfig.context.detection.threshold)
96
+ .option("--use-ai", "Enable AI-based context analysis", defaultConfig.context?.useAI)
97
+ .option("--context-provider <provider>", "AI provider for analysis", defaultConfig.context?.aiProvider)
98
+ .option("--context-threshold <number>", "Minimum match count", Number, defaultConfig.context?.detection?.threshold)
497
99
  .action(async (options) => {
498
100
  try {
499
- await runCommand(program.opts(), options, "analyze");
101
+ await runCommand(defaultConfig, options, "analyze");
500
102
  }
501
103
  catch (error) {
502
104
  if (error.code && error.code.startsWith("ERR_")) {
@@ -514,14 +116,14 @@ const configureCLI = async (defaultConfig) => {
514
116
  program
515
117
  .command("advanced")
516
118
  .description("Access advanced configuration options")
517
- .option("--context-confidence <number>", "Minimum confidence score", Number, defaultConfig.context.detection.minConfidence)
518
- .option("--context-debug", "Show context details", defaultConfig.context.debug)
519
- .option("--min-text-length <number>", "Minimum text length for AI analysis", Number, defaultConfig.context.minTextLength)
520
- .option("--allow-new-categories", "Allow AI to suggest new categories", defaultConfig.context.allowNewCategories)
119
+ .option("--context-confidence <number>", "Minimum confidence score", Number, defaultConfig.context?.detection?.minConfidence)
120
+ .option("--context-debug", "Show context details", defaultConfig.context?.debug)
121
+ .option("--min-text-length <number>", "Minimum text length for AI analysis", Number, defaultConfig.context?.minTextLength)
122
+ .option("--allow-new-categories", "Allow AI to suggest new categories", defaultConfig.context?.allowNewCategories)
521
123
  .option("--max-retries <number>", "Maximum number of retries for API calls", Number, defaultConfig.retryOptions?.maxRetries || 2)
522
124
  .action(async (options) => {
523
125
  try {
524
- await runCommand(program.opts(), options, "advanced");
126
+ await runCommand(defaultConfig, options, "advanced");
525
127
  }
526
128
  catch (error) {
527
129
  if (error.code && error.code.startsWith("ERR_")) {
@@ -559,7 +161,7 @@ const configureCLI = async (defaultConfig) => {
559
161
  console.log(` Version: ${defaultConfig.version}`);
560
162
  }
561
163
  console.log(` Source: ${defaultConfig.source}`);
562
- console.log(` Targets: ${defaultConfig.targets.length} languages (${defaultConfig.targets.slice(0, 5).join(", ")}${defaultConfig.targets.length > 5 ? "..." : ""})`);
164
+ console.log(` Targets: ${(defaultConfig.targets || []).length} languages (${(defaultConfig.targets || []).slice(0, 5).join(", ")}${(defaultConfig.targets || []).length > 5 ? "..." : ""})`);
563
165
  console.log(` API Provider: ${defaultConfig.apiProvider || "auto-detect"}`);
564
166
  console.log(` Concurrency: ${defaultConfig.concurrencyLimit || 5} parallel operations`);
565
167
  console.log(` Cache: ${defaultConfig.cacheEnabled !== false ? "Enabled" : "Disabled"}`);
@@ -593,8 +195,16 @@ const configureCLI = async (defaultConfig) => {
593
195
  .option("--export <format>", "Export review queue (json|csv)")
594
196
  .action(async (options) => {
595
197
  try {
596
- const ReviewCommand = (await import("../src/commands/review.js")).default;
597
- const review = new ReviewCommand(defaultConfig);
198
+ const ReviewCommand = (await import("./commands/review.js")).default;
199
+ // Ensure config matches expectations of ReviewCommand
200
+ // ReviewCommand expects ConfigLayer/LocalizeConfig, need to ensure mandatory fields or cast
201
+ // ReviewCommandConfig requires localesDir and source to be strings (not undefined)
202
+ const reviewConfig = {
203
+ ...defaultConfig,
204
+ localesDir: defaultConfig.localesDir || "./locales",
205
+ source: defaultConfig.source || "en",
206
+ };
207
+ const review = new ReviewCommand(reviewConfig);
598
208
  if (options.export) {
599
209
  review.exportReviewQueue(options.export);
600
210
  }
@@ -612,107 +222,16 @@ const configureCLI = async (defaultConfig) => {
612
222
  });
613
223
  program.action(async () => {
614
224
  try {
615
- await runCommand(program.opts(), {}, "translate");
225
+ await runCommand(defaultConfig, {}, "translate");
616
226
  }
617
227
  catch (error) {
618
228
  console.error(`\nError: ${error.message}`);
619
- if (error.stack && process.env.DEBUG) {
620
- console.error(error.stack);
621
- }
622
229
  process.exit(1);
623
230
  }
624
231
  });
625
- program.parse(process.argv);
626
- return defaultConfig;
627
- };
628
- /**
629
- * Validate that necessary environment variables are set.
630
- * @returns {Array<string>} - List of available providers.
631
- */
632
- const validateEnvironment = () => {
633
- try {
634
- // Check that ProviderFactory can validate providers
635
- const availableProviders = ProviderFactory.validateProviders();
636
- console.log(`\nAvailable API providers: ${availableProviders.join(", ")}`);
637
- return availableProviders;
638
- }
639
- catch (error) {
640
- console.error("\nError: " + error.message);
641
- console.error("Please set at least one of the following environment variables:");
642
- const possibleProviders = [
643
- "DASHSCOPE_API_KEY",
644
- "OPENAI_API_KEY",
645
- "DEEPSEEK_API_KEY",
646
- "GEMINI_API_KEY",
647
- "XAI_API_KEY",
648
- ];
649
- possibleProviders.forEach((key) => console.error(` - ${key}`));
650
- process.exit(1);
651
- }
232
+ await program.parseAsync(process.argv);
652
233
  };
653
- /**
654
- * Display performance optimizations and tips.
655
- * @param {any} options - Configuration options.
656
- */
657
- const displayPerformanceTips = async (options) => {
658
- if (!options.debug)
659
- return;
660
- try {
661
- const cpuCount = os.cpus().length;
662
- const cpuModel = os.cpus()[0]?.model || "Unknown CPU";
663
- const memoryGB = Math.floor(os.totalmem() / (1024 * 1024 * 1024));
664
- const freememGB = Math.floor(os.freemem() / (1024 * 1024 * 1024));
665
- const orchestrator = new Orchestrator(options);
666
- const cacheStats = orchestrator.getCacheStats();
667
- console.log("\nPerformance Information:");
668
- console.log(` - CPU: ${cpuModel} (${cpuCount} cores)`);
669
- console.log(` - Memory: ${freememGB}GB free of ${memoryGB}GB total`);
670
- console.log(` - Concurrency: ${options.concurrencyLimit} parallel operations`);
671
- console.log(` - Cache: ${options.cacheEnabled ? "Enabled" : "Disabled"} (${cacheStats.size} items cached)`);
672
- // Provide tips
673
- console.log("\nPerformance Tips:");
674
- if (cpuCount > options.concurrencyLimit * 2) {
675
- console.log(` - Increase concurrency with --concurrency ${Math.min(cpuCount, 10)} to better utilize your CPU`);
676
- }
677
- if (!options.cacheEnabled) {
678
- console.log(` - Enable caching to improve repeated runs speed`);
679
- }
680
- if (memoryGB < 4) {
681
- console.log(` - Your system has limited memory (${memoryGB}GB). Consider reducing concurrency if you experience issues.`);
682
- }
683
- }
684
- catch (error) {
685
- console.log("Warning: Could not calculate performance tips", error);
686
- }
687
- };
688
- try {
689
- await loadEnvironmentVariables();
690
- let defaultConfig;
691
- try {
692
- const { config, configFile } = await loadConfig();
693
- defaultConfig = config;
694
- if (configFile) {
695
- console.log(`Loaded config from: ${path.relative(process.cwd(), configFile)}`);
696
- }
697
- else {
698
- console.log("No config file found, using defaults");
699
- }
700
- }
701
- catch (err) {
702
- console.warn(`Warning: Could not load config: ${err.message}`);
703
- // Fallback defaults if logic fails totally (shouldn't with c12 defaults)
704
- defaultConfig = {
705
- source: "en",
706
- targets: [],
707
- concurrencyLimit: 5,
708
- };
709
- }
710
- await configureCLI(defaultConfig);
711
- }
712
- catch (error) {
713
- console.error(`\nError: ${error.message}`);
714
- if (error.stack && process.env.DEBUG) {
715
- console.error(error.stack);
716
- }
234
+ main().catch((err) => {
235
+ console.error(err);
717
236
  process.exit(1);
718
- }
237
+ });
@@ -0,0 +1,9 @@
1
+ export declare class InteractiveReviewer {
2
+ private queue;
3
+ private decisions;
4
+ constructor();
5
+ loadQueue(): boolean;
6
+ start(): Promise<void>;
7
+ private handleAction;
8
+ private saveDecisions;
9
+ }