vibecodingmachine-cli 2025.12.6-1702 → 2025.12.24-2348

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.
@@ -9,7 +9,7 @@ const repo = require('../commands/repo');
9
9
  const auto = require('../commands/auto');
10
10
  const status = require('../commands/status');
11
11
  const requirements = require('../commands/requirements');
12
- const { getRepoPath, readConfig, writeConfig } = require('./config');
12
+ const { getRepoPath, readConfig, writeConfig, getAutoConfig } = require('./config');
13
13
  const { getProviderPreferences, saveProviderPreferences, getProviderDefinitions } = require('../utils/provider-registry');
14
14
  const { checkAutoModeStatus } = require('./auto-mode');
15
15
  const {
@@ -17,9 +17,36 @@ const {
17
17
  getHostname,
18
18
  getRequirementsFilename,
19
19
  requirementsExists,
20
- isComputerNameEnabled
20
+ isComputerNameEnabled,
21
+ t,
22
+ detectLocale,
23
+ setLocale
21
24
  } = require('vibecodingmachine-core');
22
25
 
26
+ // Initialize locale detection for interactive mode
27
+ const detectedLocale = detectLocale();
28
+ setLocale(detectedLocale);
29
+ const pkg = require('../../package.json');
30
+
31
+ /**
32
+ * Translate workflow stage names
33
+ */
34
+ function translateStage(stage) {
35
+ const stageMap = {
36
+ 'PREPARE': 'workflow.stage.prepare',
37
+ 'REPRODUCE': 'workflow.stage.reproduce',
38
+ 'CREATE UNIT TEST': 'workflow.stage.create.unit.test',
39
+ 'ACT': 'workflow.stage.act',
40
+ 'CLEAN UP': 'workflow.stage.clean.up',
41
+ 'VERIFY': 'workflow.stage.verify',
42
+ 'RUN UNIT TESTS': 'workflow.stage.run.unit.tests',
43
+ 'DONE': 'workflow.stage.done'
44
+ };
45
+
46
+ const key = stageMap[stage];
47
+ return key ? t(key) : stage;
48
+ }
49
+
23
50
  /**
24
51
  * Format IDE name for display
25
52
  * @param {string} ide - Internal IDE identifier
@@ -213,95 +240,9 @@ function formatPath(fullPath) {
213
240
 
214
241
  async function countRequirements() {
215
242
  try {
216
- const { getRequirementsPath, getVibeCodingMachineDir } = require('vibecodingmachine-core');
243
+ const { getProjectRequirementStats } = require('vibecodingmachine-core');
217
244
  const repoPath = await getRepoPath();
218
- const reqPath = await getRequirementsPath(repoPath);
219
-
220
- if (!reqPath || !await fs.pathExists(reqPath)) {
221
- return null;
222
- }
223
-
224
- const content = await fs.readFile(reqPath, 'utf8');
225
-
226
- // Count requirements in each section
227
- let todoCount = 0;
228
- let toVerifyCount = 0;
229
- let verifiedCount = 0;
230
-
231
- // Split by sections
232
- const lines = content.split('\n');
233
- let currentSection = '';
234
-
235
- for (const line of lines) {
236
- const trimmed = line.trim();
237
-
238
- // Check for requirement headers first (###), then section headers (##)
239
- // This prevents ### from being treated as section headers
240
- if (trimmed.startsWith('###')) {
241
- // Count requirements (### headers in new format)
242
- // IMPORTANT: Only count if we're in a recognized section
243
- if (currentSection) {
244
- const requirementText = trimmed.replace(/^###\s*/, '').trim();
245
- if (requirementText) { // Only count if requirement text is not empty
246
- if (currentSection === 'todo') {
247
- todoCount++;
248
- } else if (currentSection === 'toverify') {
249
- toVerifyCount++;
250
- }
251
- }
252
- }
253
- } else if (trimmed.startsWith('##') && !trimmed.startsWith('###')) {
254
- // Detect section headers (must start with ## but not ###)
255
- if (trimmed.includes('⏳ Requirements not yet completed') ||
256
- trimmed.includes('Requirements not yet completed')) {
257
- currentSection = 'todo';
258
- } else if (trimmed.includes('🔍 TO VERIFY BY HUMAN') ||
259
- trimmed.includes('TO VERIFY BY HUMAN') ||
260
- trimmed.includes('🔍 TO VERIFY') ||
261
- trimmed.includes('TO VERIFY') ||
262
- trimmed.includes('✅ Verified by AI') ||
263
- trimmed.includes('Verified by AI')) {
264
- currentSection = 'toverify';
265
- } else {
266
- // Any other section header clears the current section
267
- currentSection = '';
268
- }
269
- }
270
- }
271
-
272
- // Count verified requirements from CHANGELOG.md (at repository root)
273
- const allnightDir = await getVibeCodingMachineDir();
274
- if (allnightDir) {
275
- // CHANGELOG.md is at the repository root
276
- // If .vibecodingmachine is inside repo: go up one level
277
- // If .vibecodingmachine is sibling (../.vibecodingmachine-reponame): go up one level then into repo
278
- let changelogPath;
279
- const allnightStatus = await require('vibecodingmachine-core').checkVibeCodingMachineExists();
280
-
281
- if (allnightStatus.insideExists) {
282
- // .vibecodingmachine is inside repo, so go up one level
283
- changelogPath = path.join(path.dirname(allnightDir), 'CHANGELOG.md');
284
- } else if (allnightStatus.siblingExists) {
285
- // .vibecodingmachine is sibling, use current working directory
286
- changelogPath = path.join(process.cwd(), 'CHANGELOG.md');
287
- }
288
-
289
- if (changelogPath && await fs.pathExists(changelogPath)) {
290
- const changelogContent = await fs.readFile(changelogPath, 'utf8');
291
- // Count entries that look like completed requirements
292
- // Each entry typically starts with "- " followed by date/description
293
- const changelogLines = changelogContent.split('\n');
294
- for (const line of changelogLines) {
295
- const trimmed = line.trim();
296
- // Count lines that start with "- " and have substantial content (not just empty bullets)
297
- if (trimmed.startsWith('- ') && trimmed.length > 10) {
298
- verifiedCount++;
299
- }
300
- }
301
- }
302
- }
303
-
304
- return { todoCount, toVerifyCount, verifiedCount };
245
+ return await getProjectRequirementStats(repoPath);
305
246
  } catch (error) {
306
247
  return null;
307
248
  }
@@ -357,25 +298,14 @@ async function showWelcomeScreen() {
357
298
 
358
299
  const repoPath = process.cwd(); // Always use current working directory
359
300
  const autoStatus = await checkAutoModeStatus();
360
- const allnightStatus = await checkVibeCodingMachineExists();
361
301
  const hostname = getHostname();
362
- const requirementsFilename = await getRequirementsFilename();
363
- const useHostname = await isComputerNameEnabled();
364
302
 
365
303
  // Get current IDE from config
366
304
  const { getAutoConfig } = require('./config');
367
305
  const autoConfig = await getAutoConfig();
368
- const currentIDE = autoConfig.ide || autoStatus.ide || 'claude-code';
369
306
 
370
307
  // Check for requirements file
371
308
  const hasRequirements = await requirementsExists();
372
- let requirementsLocation = '';
373
-
374
- if (allnightStatus.insideExists) {
375
- requirementsLocation = '.vibecodingmachine';
376
- } else if (allnightStatus.siblingExists) {
377
- requirementsLocation = path.basename(allnightStatus.siblingDir);
378
- }
379
309
 
380
310
  // Count requirements if file exists
381
311
  const counts = hasRequirements ? await countRequirements() : null;
@@ -385,14 +315,13 @@ async function showWelcomeScreen() {
385
315
  console.clear();
386
316
 
387
317
  // Get version from package.json
388
- const pkg = require('../../package.json');
389
318
  const version = `v${pkg.version}`;
390
319
 
391
320
  // Display welcome banner with version
392
321
  console.log('\n' + boxen(
393
322
  chalk.bold.cyan('Vibe Coding Machine') + '\n' +
394
323
  chalk.gray(version) + '\n' +
395
- chalk.gray('Big Dreams + AI + VibeCodingMachine.com = Your money making apps'),
324
+ chalk.gray(t('banner.tagline')),
396
325
  {
397
326
  padding: 1,
398
327
  margin: 0,
@@ -403,8 +332,8 @@ async function showWelcomeScreen() {
403
332
 
404
333
  // Display repository and system info
405
334
  console.log();
406
- console.log(chalk.gray('Repo: '), formatPath(repoPath));
407
- console.log(chalk.gray('Computer Name: '), chalk.cyan(hostname));
335
+ console.log(chalk.gray(t('system.repo').padEnd(25)), formatPath(repoPath));
336
+ console.log(chalk.gray(t('system.computer.name').padEnd(25)), chalk.cyan(hostname));
408
337
 
409
338
  // Display auto mode progress if running
410
339
  if (autoStatus.running) {
@@ -475,8 +404,8 @@ function indexToLetter(index) {
475
404
 
476
405
  // Tree-style requirements navigator
477
406
  async function showRequirementsTree() {
478
- console.log(chalk.bold.cyan('\n📋 Requirements Navigator\n'));
479
- console.log(chalk.gray('Use ↑/↓ to navigate, →/Enter/Space to expand, ←/ESC to collapse/go back\n'));
407
+ console.log(chalk.bold.cyan('\n📋 ' + t('requirements.navigator.title') + '\n'));
408
+ console.log(chalk.gray(t('requirements.navigator.basic.instructions') + '\n'));
480
409
 
481
410
  const tree = {
482
411
  expanded: { root: true },
@@ -485,33 +414,45 @@ async function showRequirementsTree() {
485
414
  };
486
415
 
487
416
  // Build tree structure
488
- const buildTree = () => {
417
+ const buildTree = async () => {
489
418
  tree.items = [];
490
419
 
491
420
  // Root: Requirements
492
- tree.items.push({ level: 0, type: 'root', label: '📋 Requirements', key: 'root' });
421
+ tree.items.push({ level: 0, type: 'root', label: `📋 ${t('requirements.menu.label')}`, key: 'root' });
493
422
 
494
423
  if (tree.expanded.root) {
495
- tree.items.push({ level: 1, type: 'add', label: '➕ Add new requirement', key: 'add-one' });
496
- tree.items.push({ level: 1, type: 'add', label: '➕ Add multiple requirements', key: 'add-many' });
424
+ tree.items.push({ level: 1, type: 'add', label: '➕ ' + t('requirements.add.new'), key: 'add-one' });
425
+ tree.items.push({ level: 1, type: 'add', label: '➕ ' + t('requirements.add.multiple'), key: 'add-many' });
426
+
427
+ // Use pre-calculated stats and labels from shared logic
428
+ const stats = await countRequirements();
429
+ const { todoCount, toVerifyCount, verifiedCount, total, todoLabel, toVerifyLabel, verifiedLabel } = stats || {
430
+ todoCount: 0, toVerifyCount: 0, verifiedCount: 0, total: 0,
431
+ todoLabel: '⏳ TODO (0 - 0%)', toVerifyLabel: '✅ TO VERIFY (0 - 0%)', verifiedLabel: '🎉 VERIFIED (0 - 0%)'
432
+ };
433
+
434
+ // Create localized labels
435
+ const localizedTodoLabel = todoCount > 0 ?
436
+ `⏳ ${t('requirements.section.todo')} (${todoCount} - ${Math.round((todoCount / total) * 100)}%)` :
437
+ `⏳ ${t('requirements.section.todo')} (0 - 0%)`;
438
+
439
+ const localizedToVerifyLabel = toVerifyCount > 0 ?
440
+ `✅ ${t('requirements.section.to.verify')} (${toVerifyCount} - ${Math.round((toVerifyCount / total) * 100)}%)` :
441
+ `✅ ${t('requirements.section.to.verify')} (0 - 0%)`;
442
+
443
+ const localizedVerifiedLabel = verifiedCount > 0 ?
444
+ `🎉 ${t('requirements.section.verified')} (${verifiedCount} - ${Math.round((verifiedCount / total) * 100)}%)` :
445
+ `🎉 ${t('requirements.section.verified')} (0 - 0%)`;
497
446
 
498
- // Calculate counts and percentages
499
447
  const verifiedReqs = tree.verifiedReqs || [];
500
448
  const verifyReqs = tree.verifyReqs || [];
501
449
  const clarificationReqs = tree.clarificationReqs || [];
502
450
  const todoReqs = tree.todoReqs || [];
503
451
  const recycledReqs = tree.recycledReqs || [];
504
- const total = verifiedReqs.length + verifyReqs.length + clarificationReqs.length + todoReqs.length + recycledReqs.length;
505
-
506
- const verifiedPercent = total > 0 ? Math.round((verifiedReqs.length / total) * 100) : 0;
507
- const verifyPercent = total > 0 ? Math.round((verifyReqs.length / total) * 100) : 0;
508
- const clarificationPercent = total > 0 ? Math.round((clarificationReqs.length / total) * 100) : 0;
509
- const todoPercent = total > 0 ? Math.round((todoReqs.length / total) * 100) : 0;
510
- const recycledPercent = total > 0 ? Math.round((recycledReqs.length / total) * 100) : 0;
511
452
 
512
453
  // VERIFIED section (first) - only show if has requirements
513
- if (verifiedReqs.length > 0) {
514
- tree.items.push({ level: 1, type: 'section', label: `🎉 VERIFIED (${verifiedReqs.length} - ${verifiedPercent}%)`, key: 'verified' });
454
+ if (verifiedReqs.length > 0 || verifiedCount > 0) {
455
+ tree.items.push({ level: 1, type: 'section', label: localizedVerifiedLabel, key: 'verified' });
515
456
 
516
457
  if (tree.expanded.verified) {
517
458
  verifiedReqs.forEach((req, idx) => {
@@ -521,8 +462,8 @@ async function showRequirementsTree() {
521
462
  }
522
463
 
523
464
  // TO VERIFY section (second) - only show if has requirements
524
- if (verifyReqs.length > 0) {
525
- tree.items.push({ level: 1, type: 'section', label: `✅ TO VERIFY (${verifyReqs.length} - ${verifyPercent}%)`, key: 'verify', section: '✅ Verified by AI screenshot' });
465
+ if (verifyReqs.length > 0 || toVerifyCount > 0) {
466
+ tree.items.push({ level: 1, type: 'section', label: localizedToVerifyLabel, key: 'verify', section: '✅ Verified by AI screenshot' });
526
467
 
527
468
  if (tree.expanded.verify) {
528
469
  verifyReqs.forEach((req, idx) => {
@@ -543,8 +484,8 @@ async function showRequirementsTree() {
543
484
  }
544
485
 
545
486
  // TODO section (fourth) - only show if has requirements
546
- if (todoReqs.length > 0) {
547
- tree.items.push({ level: 1, type: 'section', label: `⏳ TODO (${todoReqs.length} - ${todoPercent}%)`, key: 'todo', section: '⏳ Requirements not yet completed' });
487
+ if (todoReqs.length > 0 || todoCount > 0) {
488
+ tree.items.push({ level: 1, type: 'section', label: localizedTodoLabel, key: 'todo', section: '⏳ Requirements not yet completed' });
548
489
 
549
490
  if (tree.expanded.todo) {
550
491
  todoReqs.forEach((req, idx) => {
@@ -600,17 +541,6 @@ async function showRequirementsTree() {
600
541
  // Reset inSection if we hit a section header that's not our target section
601
542
  if (sectionKey === 'verify' && inSection) {
602
543
  // Check if this is still a TO VERIFY section header
603
- const isStillToVerify = trimmed === '## 🔍 TO VERIFY BY HUMAN' ||
604
- trimmed.startsWith('## 🔍 TO VERIFY BY HUMAN') ||
605
- trimmed === '## 🔍 TO VERIFY' ||
606
- trimmed.startsWith('## 🔍 TO VERIFY') ||
607
- trimmed === '## TO VERIFY' ||
608
- trimmed.startsWith('## TO VERIFY') ||
609
- trimmed === '## ✅ TO VERIFY' ||
610
- trimmed.startsWith('## ✅ TO VERIFY') ||
611
- trimmed === '## ✅ Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG' ||
612
- (trimmed.startsWith('## ✅ Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG') &&
613
- trimmed.includes('Needs Human to Verify'));
614
544
  if (!isStillToVerify) {
615
545
  // This will be handled by the "leaving section" check below, but ensure we don't process it as entering
616
546
  }
@@ -658,11 +588,6 @@ async function showRequirementsTree() {
658
588
  if (sectionKey === 'verify') {
659
589
  // For TO VERIFY, only break if this is clearly a different section
660
590
  // Check for specific section headers that indicate we've left TO VERIFY
661
- const isVerifiedSection = trimmed === '## 📝 VERIFIED' || trimmed.startsWith('## 📝 VERIFIED');
662
- const isTodoSection = trimmed.includes('⏳ Requirements not yet completed') || trimmed.includes('Requirements not yet completed');
663
- const isRecycledSection = trimmed === '## ♻️ RECYCLED' || trimmed.startsWith('## ♻️ RECYCLED') ||
664
- trimmed === '## 📦 RECYCLED' || trimmed.startsWith('## 📦 RECYCLED');
665
- const isClarificationSection = trimmed.includes('## ❓ Requirements needing') || trimmed.includes('❓ Requirements needing');
666
591
 
667
592
  if (isVerifiedSection || isTodoSection || isRecycledSection || isClarificationSection) {
668
593
  break; // Different section, we've left TO VERIFY
@@ -688,7 +613,7 @@ async function showRequirementsTree() {
688
613
  }
689
614
 
690
615
  const details = [];
691
- let package = null;
616
+ let pkg = null;
692
617
 
693
618
  // Read package and description
694
619
  for (let j = i + 1; j < lines.length; j++) {
@@ -699,14 +624,14 @@ async function showRequirementsTree() {
699
624
  }
700
625
  // Check for PACKAGE line
701
626
  if (nextLine.startsWith('PACKAGE:')) {
702
- package = nextLine.replace(/^PACKAGE:\s*/, '').trim();
627
+ pkg = nextLine.replace(/^PACKAGE:\s*/, '').trim();
703
628
  } else if (nextLine && !nextLine.startsWith('PACKAGE:')) {
704
629
  // Description line
705
630
  details.push(nextLine);
706
631
  }
707
632
  }
708
633
 
709
- requirements.push({ title, details, package, lineIndex: i });
634
+ requirements.push({ title, details, pkg, lineIndex: i });
710
635
  }
711
636
  }
712
637
 
@@ -837,17 +762,17 @@ async function showRequirementsTree() {
837
762
  tree.recycledReqs = await loadSection('recycled', '♻️ Recycled');
838
763
 
839
764
  let inTree = true;
840
- buildTree();
765
+ await buildTree();
841
766
 
842
767
  while (inTree) {
843
768
  console.clear();
844
- console.log(chalk.bold.cyan('\n📋 Requirements Navigator\n'));
845
- console.log(chalk.gray('Use ↑/↓ to navigate, ') + chalk.white('→/Enter') + chalk.gray(' to expand/view questions, ← to go back, ') + chalk.white('R') + chalk.gray(' to Remove, ') + chalk.white('J') + chalk.gray(' to move down, ') + chalk.white('K') + chalk.gray(' to move up, ') + chalk.white('U') + chalk.gray(' Promote (👍), ') + chalk.white('D') + chalk.gray(' Demote/Move to TODO (👎), ') + chalk.white('X/ESC') + chalk.gray(' to Exit.\n'));
769
+ console.log(chalk.bold.cyan('\n📋 ' + t('requirements.navigator.title') + '\n'));
770
+ console.log(chalk.gray(t('requirements.navigator.instructions') + '\n'));
846
771
 
847
772
  // Safety check: ensure tree.selected is within bounds
848
773
  if (tree.items.length === 0) {
849
774
  console.log(chalk.yellow('No items to display.'));
850
- console.log(chalk.gray('\nPress any key to return to main menu...'));
775
+ console.log(chalk.gray(`\n${t('interactive.press.any.key.return')}`));
851
776
  await new Promise((resolve) => {
852
777
  process.stdin.once('keypress', () => resolve());
853
778
  });
@@ -944,7 +869,7 @@ async function showRequirementsTree() {
944
869
  if (tree.expanded[current.key]) {
945
870
  // Collapse expanded section
946
871
  tree.expanded[current.key] = false;
947
- buildTree();
872
+ await buildTree();
948
873
  } else if (current.level > 0) {
949
874
  // Go to parent
950
875
  for (let i = tree.selected - 1; i >= 0; i--) {
@@ -957,10 +882,12 @@ async function showRequirementsTree() {
957
882
  // At root level, go back to main menu
958
883
  inTree = false;
959
884
  }
960
- } else if (key.name === 'up') {
885
+ } else if (key.name === 'k' || key.name === 'up') {
961
886
  tree.selected = Math.max(0, tree.selected - 1);
962
- } else if (key.name === 'down') {
887
+ await buildTree();
888
+ } else if (key.name === 'j' || key.name === 'down') {
963
889
  tree.selected = Math.min(tree.items.length - 1, tree.selected + 1);
890
+ await buildTree();
964
891
  } else if (key.name === 'right' || key.name === 'return' || key.name === 'space') {
965
892
  const current = tree.items[tree.selected];
966
893
  if (!current) continue; // Safety check
@@ -977,25 +904,25 @@ async function showRequirementsTree() {
977
904
  } else if (current.key === 'recycled') {
978
905
  tree.recycledReqs = await loadSection(current.key, current.section);
979
906
  }
980
- buildTree();
907
+ await buildTree();
981
908
  } else {
982
909
  tree.expanded[current.key] = false;
983
- buildTree();
910
+ await buildTree();
984
911
  }
985
912
  } else if (current.type === 'requirement') {
986
913
  // Show requirement actions
987
914
  await showRequirementActions(current.req, current.sectionKey, tree);
988
- buildTree();
915
+ await buildTree();
989
916
  } else if (current.type === 'clarification') {
990
917
  // Show clarification requirement with questions
991
918
  await showClarificationActions(current.req, tree, loadClarification);
992
- buildTree();
919
+ await buildTree();
993
920
  } else if (current.type === 'verified') {
994
921
  // Show verified item details (read-only)
995
922
  console.clear();
996
923
  console.log(chalk.bold.green(`\n${current.label}\n`));
997
924
  console.log(chalk.gray('(From CHANGELOG.md - read only)'));
998
- console.log(chalk.gray('\nPress any key to go back...'));
925
+ console.log(chalk.gray(`\n${t('interactive.press.any.key.back')}`));
999
926
  await new Promise((resolve) => {
1000
927
  readline.emitKeypressEvents(process.stdin);
1001
928
  if (process.stdin.isTTY) {
@@ -1016,7 +943,7 @@ async function showRequirementsTree() {
1016
943
  await handleAddRequirement(current.key);
1017
944
  // Reload TODO section
1018
945
  tree.todoReqs = await loadSection('todo', '⏳ Requirements not yet completed');
1019
- buildTree();
946
+ await buildTree();
1020
947
  }
1021
948
  } else if (key.name === 'r') {
1022
949
  const current = tree.items[tree.selected];
@@ -1030,15 +957,15 @@ async function showRequirementsTree() {
1030
957
  } else if (current.sectionKey === 'verify') {
1031
958
  tree.verifyReqs = await loadSection('verify', '✅ Verified by AI screenshot');
1032
959
  }
1033
- buildTree();
960
+ await buildTree();
1034
961
  } else if (current.type === 'clarification') {
1035
962
  await deleteClarification(current.req, tree);
1036
963
  tree.clarificationReqs = await loadClarification();
1037
- buildTree();
964
+ await buildTree();
1038
965
  } else if (current.type === 'recycled') {
1039
966
  await permanentlyDeleteRequirement(current.req, current.sectionKey, tree);
1040
967
  tree.recycledReqs = await loadSection('recycled', '♻️ Recycled');
1041
- buildTree();
968
+ await buildTree();
1042
969
  }
1043
970
  } else if (key.name === 'j') {
1044
971
  const current = tree.items[tree.selected];
@@ -1046,7 +973,7 @@ async function showRequirementsTree() {
1046
973
 
1047
974
  if (current.type === 'requirement') {
1048
975
  await moveRequirementDown(current.req, current.sectionKey, tree);
1049
- buildTree();
976
+ await buildTree();
1050
977
  // Move selection down to follow the item
1051
978
  if (tree.selected < tree.items.length - 1) {
1052
979
  tree.selected++;
@@ -1058,7 +985,7 @@ async function showRequirementsTree() {
1058
985
 
1059
986
  if (current.type === 'requirement') {
1060
987
  await moveRequirementUp(current.req, current.sectionKey, tree);
1061
- buildTree();
988
+ await buildTree();
1062
989
  // Move selection up to follow the item
1063
990
  if (tree.selected > 0) {
1064
991
  tree.selected--;
@@ -1070,7 +997,7 @@ async function showRequirementsTree() {
1070
997
 
1071
998
  if (current.type === 'requirement') {
1072
999
  await promoteRequirement(current.req, current.sectionKey, tree, loadSection, loadVerified);
1073
- buildTree();
1000
+ await buildTree();
1074
1001
  }
1075
1002
  } else if (key.name === 'd') {
1076
1003
  const current = tree.items[tree.selected];
@@ -1081,12 +1008,12 @@ async function showRequirementsTree() {
1081
1008
  await moveClarificationToTodo(current.req, tree);
1082
1009
  tree.clarificationReqs = await loadClarification();
1083
1010
  tree.todoReqs = await loadSection('todo', '⏳ Requirements not yet completed');
1084
- buildTree();
1011
+ await buildTree();
1085
1012
  } else if (current.type === 'requirement' || current.type === 'verified') {
1086
1013
  const sectionKey = current.type === 'verified' ? 'verified' : current.sectionKey;
1087
1014
  const reqTitle = current.type === 'verified' ? current.label : current.req.title;
1088
1015
  await demoteRequirement(reqTitle, sectionKey, tree, loadSection, loadVerified);
1089
- buildTree();
1016
+ await buildTree();
1090
1017
  }
1091
1018
  }
1092
1019
  }
@@ -1098,7 +1025,7 @@ async function showRequirementsTree() {
1098
1025
  function showGoodbyeMessage() {
1099
1026
  const hour = new Date().getHours();
1100
1027
  const message = hour < 21
1101
- ? '\n👋 Goodbye! Be dreaming about what requirements to add!\n'
1028
+ ? '\n👋 ' + t('interactive.goodbye') + '\n'
1102
1029
  : '\n👋 Goodbye! Go get some sleep!\n';
1103
1030
  console.log(chalk.cyan(message));
1104
1031
  }
@@ -1168,7 +1095,7 @@ async function confirmAction(message) {
1168
1095
  async function confirmAndExit() {
1169
1096
  console.log(chalk.gray('\n[DEBUG] confirmAndExit called'));
1170
1097
  console.log();
1171
- process.stdout.write(chalk.yellow(`Are you sure you want to exit? (${chalk.white('x')}/${chalk.white('y')}/${chalk.white('N')}) `));
1098
+ process.stdout.write(chalk.yellow(`${t('interactive.confirm.exit')} `));
1172
1099
 
1173
1100
  const confirmed = await new Promise((resolve) => {
1174
1101
  readline.emitKeypressEvents(process.stdin);
@@ -1853,7 +1780,7 @@ async function permanentlyDeleteRequirement(req, sectionKey, tree) {
1853
1780
  const line = lines[i].trim();
1854
1781
  if (line.startsWith('###')) {
1855
1782
  const title = line.replace(/^###\s*/, '').trim();
1856
- if (title && title.includes(req.title)) {
1783
+ if (title && title === req.title) {
1857
1784
  requirementStartIndex = i;
1858
1785
  // Find the end of this requirement (next ### or ## header)
1859
1786
  for (let j = i + 1; j < lines.length; j++) {
@@ -1898,7 +1825,7 @@ async function moveRequirementToRecycled(reqPath, requirementTitle, fromSection)
1898
1825
  const line = lines[i].trim();
1899
1826
  if (line.startsWith('###')) {
1900
1827
  const title = line.replace(/^###\s*/, '').trim();
1901
- if (title && title.includes(requirementTitle)) {
1828
+ if (title && title === requirementTitle) {
1902
1829
  requirementStartIndex = i;
1903
1830
  // Find the end of this requirement (next ### or ## header)
1904
1831
  for (let j = i + 1; j < lines.length; j++) {
@@ -2219,7 +2146,7 @@ async function handleAddRequirement(type) {
2219
2146
  while (!done) {
2220
2147
  try {
2221
2148
  // Ask for package
2222
- const { package } = await inquirer.prompt([{
2149
+ const { package: pkg } = await inquirer.prompt([{
2223
2150
  type: 'list',
2224
2151
  name: 'package',
2225
2152
  message: `Package for requirement ${requirements.length + 1}:`,
@@ -2268,7 +2195,7 @@ async function handleAddRequirement(type) {
2268
2195
  }
2269
2196
 
2270
2197
  const description = descriptionLines.join('\n');
2271
- requirements.push({ name, package, description });
2198
+ requirements.push({ name, package: pkg, description });
2272
2199
  }
2273
2200
  } catch (err) {
2274
2201
  done = true;
@@ -2565,7 +2492,6 @@ async function showQuickMenu(items, initialSelectedIndex = 0) {
2565
2492
  if (selectedIndex >= items.length) selectedIndex = 0;
2566
2493
 
2567
2494
  let isFirstRender = true;
2568
- let lastLinesPrinted = 0;
2569
2495
 
2570
2496
  // Helper to calculate visual lines occupied by text
2571
2497
  const getVisualLineCount = (text) => {
@@ -2663,7 +2589,7 @@ async function showQuickMenu(items, initialSelectedIndex = 0) {
2663
2589
  const lastLetter = letterCount > 0 ? indexToLetter(letterCount - 1) : 'a';
2664
2590
  const rangeText = letterCount > 1 ? `a-${lastLetter}` : (letterCount === 1 ? 'a' : '');
2665
2591
  const helpText = rangeText ? `${rangeText}, x` : 'x';
2666
- const helpString = `\n ↑/↓ to navigate, →/Enter to select, ←/X to exit, or press a letter (${helpText})`;
2592
+ const helpString = `\n ${t('interactive.navigation.help', { letters: helpText })}`;
2667
2593
  console.log(chalk.gray(helpString));
2668
2594
  linesPrinted += getVisualLineCount(helpString);
2669
2595
 
@@ -2751,22 +2677,27 @@ async function showQuickMenu(items, initialSelectedIndex = 0) {
2751
2677
 
2752
2678
  // Arrow keys for navigation
2753
2679
  if (key.name === 'up') {
2754
- // Skip blank and info lines when navigating
2755
- let newIndex = selectedIndex > 0 ? selectedIndex - 1 : items.length - 1;
2756
- while ((items[newIndex].type === 'blank' || items[newIndex].type === 'info') && newIndex !== selectedIndex) {
2757
- newIndex = newIndex > 0 ? newIndex - 1 : items.length - 1;
2680
+ // Search backwards for the previous selectable item
2681
+ let testIndex = selectedIndex - 1;
2682
+ while (testIndex >= 0) {
2683
+ if (items[testIndex].type !== 'blank' && items[testIndex].type !== 'info') {
2684
+ selectedIndex = testIndex;
2685
+ await displayMenu();
2686
+ break;
2687
+ }
2688
+ testIndex--;
2758
2689
  }
2759
- selectedIndex = newIndex;
2760
- await displayMenu();
2761
2690
  } else if (key.name === 'down') {
2762
- // Skip blank and info lines when navigating
2763
- let newIndex = selectedIndex < items.length - 1 ? selectedIndex + 1 : 0;
2764
- while ((items[newIndex].type === 'blank' || items[newIndex].type === 'info') && newIndex !== selectedIndex) {
2765
- newIndex = newIndex < items.length - 1 ? newIndex + 1 : 0;
2691
+ // Search forwards for the next selectable item
2692
+ let testIndex = selectedIndex + 1;
2693
+ while (testIndex < items.length) {
2694
+ if (items[testIndex].type !== 'blank' && items[testIndex].type !== 'info') {
2695
+ selectedIndex = testIndex;
2696
+ await displayMenu();
2697
+ break;
2698
+ }
2699
+ testIndex++;
2766
2700
  }
2767
- selectedIndex = newIndex;
2768
- await displayMenu();
2769
-
2770
2701
  } else if (key.name === 'return' || key.name === 'right') {
2771
2702
  // Don't allow selecting blank or info lines
2772
2703
  if (items[selectedIndex].type !== 'blank' && items[selectedIndex].type !== 'info') {
@@ -2789,24 +2720,59 @@ async function showProviderManagerMenu() {
2789
2720
  let selectedIndex = 0;
2790
2721
  let dirty = false;
2791
2722
 
2792
- // Initialize ProviderManager to check rate limits
2793
- const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
2794
- const providerManager = new ProviderManager();
2723
+ const { fetchQuotaForAgent } = require('vibecodingmachine-core/src/quota-management');
2724
+
2725
+ const debugQuota = process.env.VCM_DEBUG_QUOTA === '1' || process.env.VCM_DEBUG_QUOTA === 'true';
2726
+
2727
+ const formatDuration = (ms) => {
2728
+ if (!ms || ms <= 0) return 'now';
2729
+ const totalSeconds = Math.ceil(ms / 1000);
2730
+ const hours = Math.floor(totalSeconds / 3600);
2731
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
2732
+ const seconds = totalSeconds % 60;
2733
+
2734
+ if (hours > 0) return `${hours}h ${minutes}m`;
2735
+ if (minutes > 0) return `${minutes}m ${seconds}s`;
2736
+ return `${seconds}s`;
2737
+ };
2795
2738
 
2796
- const render = () => {
2739
+ const render = async () => {
2797
2740
  process.stdout.write('\x1Bc');
2798
- console.log(chalk.bold.cyan('⚙ Provider Order & Availability\n'));
2799
- console.log(chalk.gray(' ↑/↓ move selection j/k reorder e enable d disable Space toggle Enter save/select Esc cancel\n'));
2741
+ console.log(chalk.bold.cyan('⚙ ' + t('provider.title') + '\n'));
2742
+
2743
+ // Fetch quota info
2744
+ const sharedAuth = require('vibecodingmachine-core/src/auth/shared-auth-storage');
2745
+ const autoConfig = await getAutoConfig();
2746
+ const quotaInfo = await sharedAuth.canRunAutoMode();
2747
+ const remaining = Math.max(0, (quotaInfo.maxIterations || 10) - (quotaInfo.todayUsage || 0));
2748
+
2749
+ // Calculate time until reset (midnight)
2750
+ const now = new Date();
2751
+ const tonight = new Date(now);
2752
+ tonight.setHours(24, 0, 0, 0);
2753
+ const msUntilReset = tonight.getTime() - now.getTime();
2754
+ const hoursUntilReset = Math.floor(msUntilReset / (1000 * 60 * 60));
2755
+ const minsUntilReset = Math.floor((msUntilReset % (1000 * 60 * 60)) / (1000 * 60));
2756
+
2757
+ // Display quota as time-based instead of numeric (0/1 or 1/1 format)
2758
+ let quotaDisplay;
2759
+ if (remaining === 0) {
2760
+ // Rate limit active - show when it resets (in red)
2761
+ quotaDisplay = chalk.gray(' ' + t('provider.overall.quota') + ': ') + chalk.red(`⏰ ${t('provider.rate.limit.resets')} ${hoursUntilReset}h ${minsUntilReset}m`);
2762
+ } else {
2763
+ // Quota available - show when it resets (in green)
2764
+ quotaDisplay = chalk.gray(' ' + t('provider.overall.quota') + ': ') + chalk.green(`✓ ${t('provider.available')} (${remaining}/${quotaInfo.maxIterations})`) + chalk.gray(' • ' + t('provider.resets.in') + ' ') + chalk.cyan(`${hoursUntilReset}h ${minsUntilReset}m`);
2765
+ }
2766
+ console.log(quotaDisplay);
2767
+ console.log(chalk.gray(' ' + t('provider.instructions') + '\n'));
2800
2768
 
2801
- order.forEach((id, idx) => {
2769
+ for (let idx = 0; idx < order.length; idx++) {
2770
+ const id = order[idx];
2802
2771
  const def = defMap.get(id);
2803
- if (!def) return;
2772
+ if (!def) continue;
2804
2773
  const isSelected = idx === selectedIndex;
2805
2774
  const isEnabled = enabled[id] !== false;
2806
2775
 
2807
- // Check for rate limits
2808
- const timeUntilReset = providerManager.getTimeUntilReset(id, def.model || id);
2809
-
2810
2776
  // Check for Kiro installation
2811
2777
  let isInstalled = true;
2812
2778
  if (id === 'kiro') {
@@ -2830,60 +2796,68 @@ async function showProviderManagerMenu() {
2830
2796
  const prefix = isSelected ? chalk.cyan('❯') : ' ';
2831
2797
  let line = `${prefix} ${statusEmoji} ${idx + 1}. ${def.name} ${chalk.gray(`(${def.id})`)} ${typeLabel}`;
2832
2798
 
2833
- // Add rate limit time if applicable
2834
- if (timeUntilReset && isEnabled) {
2835
- // Calculate reset time
2836
- const now = new Date();
2837
- const resetTime = new Date(now.getTime() + timeUntilReset);
2799
+ // Fetch and display specific quota for this agent
2800
+ try {
2801
+ // Find the active model for this provider if possible
2802
+ let model = def.defaultModel || id;
2803
+ if (id === 'groq') model = autoConfig.groqModel || model;
2804
+ else if (id === 'anthropic') model = autoConfig.anthropicModel || model;
2805
+ else if (id === 'ollama') {
2806
+ const preferredModel = autoConfig.llmModel && autoConfig.llmModel.includes('ollama/')
2807
+ ? autoConfig.llmModel.split('/')[1]
2808
+ : autoConfig.llmModel || autoConfig.aiderModel;
2809
+ model = (preferredModel && preferredModel !== id) ? preferredModel : model;
2810
+ }
2838
2811
 
2839
- // Format reset time in human-readable format
2840
- const timeFormatter = new Intl.DateTimeFormat('en-US', {
2841
- hour: 'numeric',
2842
- minute: '2-digit',
2843
- timeZoneName: 'short'
2844
- });
2845
- const timeStr = timeFormatter.format(resetTime);
2846
-
2847
- // Determine if it's today, tomorrow, or a specific date
2848
- const today = new Date();
2849
- today.setHours(0, 0, 0, 0);
2850
- const resetDate = new Date(resetTime);
2851
- resetDate.setHours(0, 0, 0, 0);
2852
- const tomorrow = new Date(today);
2853
- tomorrow.setDate(tomorrow.getDate() + 1);
2854
-
2855
- let dateStr = '';
2856
- if (resetDate.getTime() === today.getTime()) {
2857
- // Today - just show time
2858
- dateStr = timeStr;
2859
- } else if (resetDate.getTime() === tomorrow.getTime()) {
2860
- // Tomorrow
2861
- dateStr = `tomorrow, ${timeStr}`;
2862
- } else {
2863
- // Specific date - show date and time
2864
- const dateFormatter = new Intl.DateTimeFormat('en-US', {
2865
- month: 'short',
2866
- day: 'numeric',
2867
- year: 'numeric'
2868
- });
2869
- dateStr = `${timeStr} ${dateFormatter.format(resetTime)}`;
2812
+ const agentId = `${id}:${model}`;
2813
+ const quota = await fetchQuotaForAgent(agentId);
2814
+
2815
+ if (debugQuota) {
2816
+ const resetMs = quota?.resetsAt ? (new Date(quota.resetsAt).getTime() - Date.now()) : null;
2817
+ console.error(`[VCM_DEBUG_QUOTA] provider=${id} model=${model} type=${quota?.type} remaining=${quota?.remaining} limit=${quota?.limit} resetsAt=${quota?.resetsAt ? new Date(quota.resetsAt).toISOString() : 'null'} resetIn=${resetMs !== null ? formatDuration(resetMs) : 'null'}`);
2870
2818
  }
2871
2819
 
2872
- line += ` ${chalk.red('rate limited until ' + dateStr)}`;
2820
+ if (quota.type === 'infinite') {
2821
+ line += ` ${chalk.gray('[' + t('provider.status.quota.infinite') + ']')}`;
2822
+ } else if (quota.type === 'rate-limit') {
2823
+ if (quota.isExceeded()) {
2824
+ if (quota.resetsAt) {
2825
+ const msUntilReset = new Date(quota.resetsAt).getTime() - Date.now();
2826
+ line += ` ${chalk.red(`[⏳ resets in ${formatDuration(msUntilReset)}]`)}`;
2827
+ } else {
2828
+ line += ` ${chalk.red('[Rate limited]')}`;
2829
+ }
2830
+ } else {
2831
+ // Show time until rate limit starts (when it resets)
2832
+ if (quota.resetsAt) {
2833
+ const msUntilReset = new Date(quota.resetsAt).getTime() - Date.now();
2834
+ line += ` ${chalk.green(`[✓ ${t('provider.status.available.resets')} ${formatDuration(msUntilReset)}]`)}`;
2835
+ } else {
2836
+ line += ` ${chalk.green('[' + t('provider.status.available') + ']')}`;
2837
+ }
2838
+ }
2839
+ }
2840
+ } catch (e) {
2841
+ // Silently skip if quota fetch fails
2873
2842
  }
2874
2843
 
2875
2844
  console.log(line);
2876
- });
2845
+ }
2877
2846
 
2878
2847
  console.log();
2879
2848
  if (dirty) {
2880
- console.log(chalk.yellow('Pending changes will be saved on Enter...'));
2849
+ console.log(chalk.yellow(t('provider.pending.changes')));
2881
2850
  } else {
2882
- console.log(chalk.gray('No pending changes.'));
2851
+ console.log(chalk.gray(t('provider.no.pending.changes')));
2883
2852
  }
2884
2853
  };
2885
2854
 
2886
- return new Promise((resolve) => {
2855
+ if (process.env.VCM_RENDER_ONCE === '1' || process.env.VCM_RENDER_ONCE === 'true') {
2856
+ await render();
2857
+ return;
2858
+ }
2859
+
2860
+ return new Promise(async (resolve) => {
2887
2861
  const cleanup = () => {
2888
2862
  if (process.stdin.isTTY && process.stdin.setRawMode) {
2889
2863
  process.stdin.setRawMode(false);
@@ -2926,15 +2900,15 @@ async function showProviderManagerMenu() {
2926
2900
  resolve(null);
2927
2901
  };
2928
2902
 
2929
- const moveSelection = (delta) => {
2903
+ const moveSelection = async (delta) => {
2930
2904
  const next = selectedIndex + delta;
2931
2905
  if (next >= 0 && next < order.length) {
2932
2906
  selectedIndex = next;
2933
- render();
2907
+ await render();
2934
2908
  }
2935
2909
  };
2936
2910
 
2937
- const reorder = (delta) => {
2911
+ const reorder = async (delta) => {
2938
2912
  const target = selectedIndex + delta;
2939
2913
  if (target < 0 || target >= order.length) return;
2940
2914
  const temp = order[selectedIndex];
@@ -2942,17 +2916,17 @@ async function showProviderManagerMenu() {
2942
2916
  order[target] = temp;
2943
2917
  selectedIndex = target;
2944
2918
  dirty = true;
2945
- render();
2919
+ await render();
2946
2920
  };
2947
2921
 
2948
- const toggle = (value) => {
2922
+ const toggle = async (value) => {
2949
2923
  const id = order[selectedIndex];
2950
2924
  enabled[id] = value;
2951
2925
  dirty = true;
2952
- render();
2926
+ await render();
2953
2927
  };
2954
2928
 
2955
- const onKeypress = (str, key = {}) => {
2929
+ const onKeypress = async (str, key = {}) => {
2956
2930
  if (key.ctrl && key.name === 'c') {
2957
2931
  cancel();
2958
2932
  return;
@@ -2960,25 +2934,25 @@ async function showProviderManagerMenu() {
2960
2934
 
2961
2935
  switch (key.name) {
2962
2936
  case 'up':
2963
- moveSelection(-1);
2937
+ await moveSelection(-1);
2964
2938
  break;
2965
2939
  case 'down':
2966
- moveSelection(1);
2940
+ await moveSelection(1);
2967
2941
  break;
2968
2942
  case 'j':
2969
- reorder(1);
2943
+ await reorder(1);
2970
2944
  break;
2971
2945
  case 'k':
2972
- reorder(-1);
2946
+ await reorder(-1);
2973
2947
  break;
2974
2948
  case 'e':
2975
- toggle(true);
2949
+ await toggle(true);
2976
2950
  break;
2977
2951
  case 'd':
2978
- toggle(false);
2952
+ await toggle(false);
2979
2953
  break;
2980
2954
  case 'space':
2981
- toggle(!(enabled[order[selectedIndex]] !== false));
2955
+ await toggle(!(enabled[order[selectedIndex]] !== false));
2982
2956
  break;
2983
2957
  case 'return':
2984
2958
  saveAndExit(order[selectedIndex]);
@@ -3000,11 +2974,11 @@ async function showProviderManagerMenu() {
3000
2974
  process.stdin.on('keypress', onKeypress);
3001
2975
  process.stdin.resume();
3002
2976
 
3003
- render();
2977
+ await render();
3004
2978
  });
3005
2979
  }
3006
2980
 
3007
- async function showSettings() {
2981
+ /* async function showSettings() {
3008
2982
  console.log(chalk.bold.cyan('\n⚙️ Settings\n'));
3009
2983
 
3010
2984
  const { setConfigValue } = require('vibecodingmachine-core');
@@ -3095,8 +3069,8 @@ async function showSettings() {
3095
3069
  */
3096
3070
  async function showCloudSyncMenu() {
3097
3071
  console.clear();
3098
- console.log(chalk.bold.cyan('\n☁️ Cloud Sync Management\n'));
3099
-
3072
+ console.log(chalk.bold.cyan(`\n☁️ ${t('cloud.sync.title')}\n`));
3073
+
3100
3074
  // Check if cloud sync is configured
3101
3075
  try {
3102
3076
  const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
@@ -3104,14 +3078,14 @@ async function showCloudSyncMenu() {
3104
3078
  await testEngine.initialize();
3105
3079
  testEngine.stop();
3106
3080
  } catch (error) {
3107
- console.log(chalk.yellow('⚠️ Cloud sync is not configured.\n'));
3108
- console.log(chalk.white('To set up cloud sync:\n'));
3109
- console.log(chalk.gray('1. Run: ') + chalk.cyan('./scripts/setup-cloud-sync.sh'));
3110
- console.log(chalk.gray('2. Add AWS configuration to your .env file'));
3111
- console.log(chalk.gray('3. Restart vcm\n'));
3112
- console.log(chalk.gray('For more info, see: ') + chalk.cyan('docs/CLOUD_SYNC.md\n'));
3113
-
3114
- console.log(chalk.gray('Press Enter to return to main menu...'));
3081
+ console.log(chalk.yellow(`⚠️ ${t('cloud.sync.not.configured')}\n`));
3082
+ console.log(chalk.white(`${t('cloud.sync.setup.title')}\n`));
3083
+ console.log(chalk.gray(`1. ${t('cloud.sync.setup.step1')} `) + chalk.cyan('./scripts/setup-cloud-sync.sh'));
3084
+ console.log(chalk.gray(`2. ${t('cloud.sync.setup.step2')}`));
3085
+ console.log(chalk.gray(`3. ${t('cloud.sync.setup.step3')}\n`));
3086
+ console.log(chalk.gray(`${t('cloud.sync.setup.info')} `) + chalk.cyan('docs/CLOUD_SYNC.md\n'));
3087
+
3088
+ console.log(chalk.gray(t('interactive.press.enter.return')));
3115
3089
  await new Promise(resolve => {
3116
3090
  const rl = readline.createInterface({
3117
3091
  input: process.stdin,
@@ -3124,31 +3098,31 @@ async function showCloudSyncMenu() {
3124
3098
  });
3125
3099
  return;
3126
3100
  }
3127
-
3101
+
3128
3102
  const computerCommands = require('../commands/computers');
3129
3103
  const syncCommands = require('../commands/sync');
3130
-
3104
+
3131
3105
  const choices = [
3132
- { name: '📊 View All Computers', value: 'computers' },
3133
- { name: '🖥️ Manage Another Computer\'s Requirements', value: 'manage-remote' },
3134
- { name: '🔄 Sync Now', value: 'sync-now' },
3135
- { name: '📈 Sync Status', value: 'sync-status' },
3136
- { name: '📜 Sync History', value: 'sync-history' },
3137
- { name: '📋 View Offline Queue', value: 'sync-queue' },
3138
- { name: '🖥️ Register This Computer', value: 'register' },
3139
- { name: '🎯 Update Focus Area', value: 'update-focus' },
3140
- { name: chalk.gray('← Back to Main Menu'), value: 'back' }
3106
+ { name: t('cloud.sync.menu.view.computers'), value: 'computers' },
3107
+ { name: t('cloud.sync.menu.manage.remote'), value: 'manage-remote' },
3108
+ { name: t('cloud.sync.menu.sync.now'), value: 'sync-now' },
3109
+ { name: t('cloud.sync.menu.sync.status'), value: 'sync-status' },
3110
+ { name: t('cloud.sync.menu.sync.history'), value: 'sync-history' },
3111
+ { name: t('cloud.sync.menu.offline.queue'), value: 'sync-queue' },
3112
+ { name: t('cloud.sync.menu.register'), value: 'register' },
3113
+ { name: t('cloud.sync.menu.update.focus'), value: 'update-focus' },
3114
+ { name: chalk.gray(t('interactive.back.to.menu')), value: 'back' }
3141
3115
  ];
3142
-
3116
+
3143
3117
  const { action } = await inquirer.prompt([
3144
3118
  {
3145
3119
  type: 'list',
3146
3120
  name: 'action',
3147
- message: 'Select an option:',
3121
+ message: t('interactive.select.option'),
3148
3122
  choices: choices
3149
3123
  }
3150
3124
  ]);
3151
-
3125
+
3152
3126
  switch (action) {
3153
3127
  case 'computers':
3154
3128
  try {
@@ -3157,7 +3131,7 @@ async function showCloudSyncMenu() {
3157
3131
  console.log(chalk.red('\n✗ Error: ') + error.message);
3158
3132
  console.log(chalk.gray('\nTip: Make sure AWS credentials are configured and DynamoDB tables exist.'));
3159
3133
  }
3160
- console.log(chalk.gray('\nPress Enter to continue...'));
3134
+ console.log(chalk.gray(`\n${t('interactive.press.enter.continue')}`));
3161
3135
  await new Promise(resolve => {
3162
3136
  const rl = readline.createInterface({
3163
3137
  input: process.stdin,
@@ -3170,42 +3144,42 @@ async function showCloudSyncMenu() {
3170
3144
  });
3171
3145
  await showCloudSyncMenu();
3172
3146
  break;
3173
-
3147
+
3174
3148
  case 'manage-remote':
3175
3149
  try {
3176
3150
  // First, get list of computers
3177
3151
  const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
3178
3152
  const { ScanCommand } = require('@aws-sdk/lib-dynamodb');
3179
-
3153
+
3180
3154
  const syncEngine = new SyncEngine();
3181
3155
  await syncEngine.initialize();
3182
-
3156
+
3183
3157
  const tableName = 'vibecodingmachine-computers';
3184
3158
  const command = new ScanCommand({ TableName: tableName });
3185
3159
  const response = await syncEngine.dynamoClient.send(command);
3186
3160
  const computers = response.Items || [];
3187
-
3161
+
3188
3162
  syncEngine.stop();
3189
-
3163
+
3190
3164
  if (computers.length === 0) {
3191
- console.log(chalk.yellow('\n⚠ No computers registered yet.\n'));
3165
+ console.log(chalk.yellow(`\n⚠ ${t('computers.no.computers')}\n`));
3192
3166
  } else {
3193
3167
  // Let user select a computer
3194
3168
  const computerChoices = computers.map(c => ({
3195
- name: `${c.hostname || c.computerId} - ${c.focusArea || 'No focus'}`,
3169
+ name: `${c.hostname || c.computerId} - ${c.focusArea || t('computers.no.focus')}`,
3196
3170
  value: c.computerId
3197
3171
  }));
3198
- computerChoices.push({ name: chalk.gray('← Cancel'), value: null });
3199
-
3172
+ computerChoices.push({ name: chalk.gray(t('computers.cancel')), value: null });
3173
+
3200
3174
  const { selectedComputer } = await inquirer.prompt([
3201
3175
  {
3202
3176
  type: 'list',
3203
3177
  name: 'selectedComputer',
3204
- message: 'Select computer to manage:',
3178
+ message: t('computers.select.to.manage'),
3205
3179
  choices: computerChoices
3206
3180
  }
3207
3181
  ]);
3208
-
3182
+
3209
3183
  if (selectedComputer) {
3210
3184
  const remoteReqCommands = require('../commands/requirements-remote');
3211
3185
  await remoteReqCommands.manageRemoteRequirements(selectedComputer);
@@ -3216,14 +3190,14 @@ async function showCloudSyncMenu() {
3216
3190
  }
3217
3191
  await showCloudSyncMenu();
3218
3192
  break;
3219
-
3193
+
3220
3194
  case 'sync-now':
3221
3195
  try {
3222
3196
  await syncCommands.syncNow();
3223
3197
  } catch (error) {
3224
3198
  console.log(chalk.red('\n✗ Error: ') + error.message);
3225
3199
  }
3226
- console.log(chalk.gray('\nPress Enter to continue...'));
3200
+ console.log(chalk.gray(`\n${t('interactive.press.enter.continue')}`));
3227
3201
  await new Promise(resolve => {
3228
3202
  const rl = readline.createInterface({
3229
3203
  input: process.stdin,
@@ -3236,14 +3210,14 @@ async function showCloudSyncMenu() {
3236
3210
  });
3237
3211
  await showCloudSyncMenu();
3238
3212
  break;
3239
-
3213
+
3240
3214
  case 'sync-status':
3241
3215
  try {
3242
3216
  await syncCommands.syncStatus();
3243
3217
  } catch (error) {
3244
3218
  console.log(chalk.red('\n✗ Error: ') + error.message);
3245
3219
  }
3246
- console.log(chalk.gray('\nPress Enter to continue...'));
3220
+ console.log(chalk.gray(`\n${t('interactive.press.enter.continue')}`));
3247
3221
  await new Promise(resolve => {
3248
3222
  const rl = readline.createInterface({
3249
3223
  input: process.stdin,
@@ -3256,14 +3230,14 @@ async function showCloudSyncMenu() {
3256
3230
  });
3257
3231
  await showCloudSyncMenu();
3258
3232
  break;
3259
-
3233
+
3260
3234
  case 'sync-history':
3261
3235
  try {
3262
3236
  await syncCommands.viewHistory({ limit: 50 });
3263
3237
  } catch (error) {
3264
3238
  console.log(chalk.red('\n✗ Error: ') + error.message);
3265
3239
  }
3266
- console.log(chalk.gray('\nPress Enter to continue...'));
3240
+ console.log(chalk.gray(`\n${t('interactive.press.enter.continue')}`));
3267
3241
  await new Promise(resolve => {
3268
3242
  const rl = readline.createInterface({
3269
3243
  input: process.stdin,
@@ -3276,14 +3250,14 @@ async function showCloudSyncMenu() {
3276
3250
  });
3277
3251
  await showCloudSyncMenu();
3278
3252
  break;
3279
-
3253
+
3280
3254
  case 'sync-queue':
3281
3255
  try {
3282
3256
  await syncCommands.viewQueue();
3283
3257
  } catch (error) {
3284
3258
  console.log(chalk.red('\n✗ Error: ') + error.message);
3285
3259
  }
3286
- console.log(chalk.gray('\nPress Enter to continue...'));
3260
+ console.log(chalk.gray(`\n${t('interactive.press.enter.continue')}`));
3287
3261
  await new Promise(resolve => {
3288
3262
  const rl = readline.createInterface({
3289
3263
  input: process.stdin,
@@ -3296,22 +3270,22 @@ async function showCloudSyncMenu() {
3296
3270
  });
3297
3271
  await showCloudSyncMenu();
3298
3272
  break;
3299
-
3273
+
3300
3274
  case 'register':
3301
3275
  try {
3302
3276
  const { focusArea } = await inquirer.prompt([
3303
3277
  {
3304
3278
  type: 'input',
3305
3279
  name: 'focusArea',
3306
- message: 'Enter focus area for this computer:',
3307
- default: 'General Development'
3280
+ message: t('computers.register.focus.prompt'),
3281
+ default: t('computers.register.focus.default')
3308
3282
  }
3309
3283
  ]);
3310
3284
  await computerCommands.registerComputer(focusArea);
3311
3285
  } catch (error) {
3312
3286
  console.log(chalk.red('\n✗ Error: ') + error.message);
3313
3287
  }
3314
- console.log(chalk.gray('\nPress Enter to continue...'));
3288
+ console.log(chalk.gray(`\n${t('interactive.press.enter.continue')}`));
3315
3289
  await new Promise(resolve => {
3316
3290
  const rl = readline.createInterface({
3317
3291
  input: process.stdin,
@@ -3324,7 +3298,7 @@ async function showCloudSyncMenu() {
3324
3298
  });
3325
3299
  await showCloudSyncMenu();
3326
3300
  break;
3327
-
3301
+
3328
3302
  case 'update-focus':
3329
3303
  try {
3330
3304
  const { newFocus } = await inquirer.prompt([
@@ -3340,7 +3314,7 @@ async function showCloudSyncMenu() {
3340
3314
  } catch (error) {
3341
3315
  console.log(chalk.red('\n✗ Error: ') + error.message);
3342
3316
  }
3343
- console.log(chalk.gray('\nPress Enter to continue...'));
3317
+ console.log(chalk.gray(`\n${t('interactive.press.enter.continue')}`));
3344
3318
  await new Promise(resolve => {
3345
3319
  const rl = readline.createInterface({
3346
3320
  input: process.stdin,
@@ -3353,7 +3327,7 @@ async function showCloudSyncMenu() {
3353
3327
  });
3354
3328
  await showCloudSyncMenu();
3355
3329
  break;
3356
-
3330
+
3357
3331
  case 'back':
3358
3332
  // Return to main menu
3359
3333
  break;
@@ -3400,6 +3374,11 @@ async function startInteractive() {
3400
3374
 
3401
3375
  await showWelcomeScreen();
3402
3376
 
3377
+ if (process.env.VCM_OPEN_PROVIDER_MENU === '1' || process.env.VCM_OPEN_PROVIDER_MENU === 'true') {
3378
+ await showProviderManagerMenu();
3379
+ return;
3380
+ }
3381
+
3403
3382
  let exit = false;
3404
3383
  let lastSelectedIndex = 0; // Track last selected menu item
3405
3384
  while (!exit) {
@@ -3431,7 +3410,7 @@ async function startInteractive() {
3431
3410
  }
3432
3411
  // Fallback to current agent if no enabled agents found
3433
3412
  const displayAgent = firstEnabledAgent || autoConfig.agent || autoConfig.ide || 'ollama';
3434
- let agentDisplay = `First Agent: ${chalk.cyan(getAgentDisplayName(displayAgent))}`;
3413
+ let agentDisplay = `${t('interactive.first.agent')}: ${chalk.cyan(getAgentDisplayName(displayAgent))}`;
3435
3414
 
3436
3415
  // Check for rate limits (for LLM-based agents and Claude Code)
3437
3416
  if (displayAgent === 'ollama' || displayAgent === 'groq' || displayAgent === 'anthropic' || displayAgent === 'bedrock' || displayAgent === 'claude-code') {
@@ -3503,9 +3482,9 @@ async function startInteractive() {
3503
3482
 
3504
3483
 
3505
3484
  // Get stop condition from already loaded autoConfig (line 1704)
3506
- const stopCondition = autoConfig.neverStop ? 'Never Stop' :
3507
- autoConfig.maxChats ? `Stop after ${autoConfig.maxChats}` :
3508
- 'Never Stop';
3485
+ const stopCondition = autoConfig.neverStop ? t('interactive.never.stop') :
3486
+ autoConfig.maxChats ? t('interactive.stop.after', { count: autoConfig.maxChats }) :
3487
+ t('interactive.never.stop');
3509
3488
 
3510
3489
  if (autoStatus.running) {
3511
3490
  items.push({
@@ -3516,7 +3495,7 @@ async function startInteractive() {
3516
3495
  } else {
3517
3496
  items.push({
3518
3497
  type: 'setting',
3519
- name: `Auto Mode: ${chalk.yellow('Stopped ○')}`,
3498
+ name: `${t('interactive.auto.mode')}: ${chalk.yellow(t('interactive.auto.stopped') + ' ○')}`,
3520
3499
  value: 'setting:auto-start'
3521
3500
  });
3522
3501
  }
@@ -3524,7 +3503,7 @@ async function startInteractive() {
3524
3503
  // Add separate stop condition setting
3525
3504
  items.push({
3526
3505
  type: 'setting',
3527
- name: ` └─ Stop Condition: ${chalk.cyan(stopCondition)}`,
3506
+ name: ` └─ ${t('interactive.stop.condition')}: ${chalk.cyan(stopCondition)}`,
3528
3507
  value: 'setting:auto-stop-condition'
3529
3508
  });
3530
3509
 
@@ -3538,17 +3517,15 @@ async function startInteractive() {
3538
3517
  // Add Requirements as a selectable setting with counts
3539
3518
  const hasRequirements = await requirementsExists();
3540
3519
  const counts = hasRequirements ? await countRequirements() : null;
3541
- let requirementsText = 'Requirements: ';
3520
+ let requirementsText = t('interactive.requirements') + ': ';
3542
3521
  if (counts) {
3543
- // Calculate actual iterations: lesser of stop after number and TODO requirements
3544
- const actualIterations = autoConfig.neverStop ? counts.todoCount :
3545
- Math.min(autoConfig.maxChats || counts.todoCount, counts.todoCount);
3522
+ // Use actual counts for display (not capped by maxChats)
3546
3523
  const total = counts.todoCount + counts.toVerifyCount + counts.verifiedCount;
3547
3524
  if (total > 0) {
3548
- const todoPercent = Math.round((actualIterations / total) * 100);
3525
+ const todoPercent = Math.round((counts.todoCount / total) * 100);
3549
3526
  const toVerifyPercent = Math.round((counts.toVerifyCount / total) * 100);
3550
3527
  const verifiedPercent = Math.round((counts.verifiedCount / total) * 100);
3551
- requirementsText += `${chalk.yellow(actualIterations + ' (' + todoPercent + '%) TODO')}, ${chalk.cyan(counts.toVerifyCount + ' (' + toVerifyPercent + '%) TO VERIFY')}, ${chalk.green(counts.verifiedCount + ' (' + verifiedPercent + '%) VERIFIED')}`;
3528
+ requirementsText += `${chalk.yellow(counts.todoCount + ' (' + todoPercent + '%) ' + t('interactive.todo'))}, ${chalk.cyan(counts.toVerifyCount + ' (' + toVerifyPercent + '%) ' + t('interactive.to.verify'))}, ${chalk.green(counts.verifiedCount + ' (' + verifiedPercent + '%) ' + t('interactive.verified'))}`;
3552
3529
  } else {
3553
3530
  requirementsText = '';
3554
3531
  }
@@ -3568,28 +3545,38 @@ async function startInteractive() {
3568
3545
 
3569
3546
  items.push({
3570
3547
  type: 'setting',
3571
- name: ` └─ Use Hostname in Req File: ${useHostname ? chalk.green('✓') : chalk.red('🛑')} ${useHostname ? '✓ ENABLED' : '🛑 DISABLED'}`,
3548
+ name: ` └─ ${t('interactive.hostname.enabled')}: ${useHostname ? chalk.green('✓') : chalk.red('🛑')} ${useHostname ? '✓ ENABLED' : '🛑 ' + t('interactive.hostname.disabled')}`,
3572
3549
  value: 'setting:hostname'
3573
3550
  });
3574
3551
 
3552
+ // Add Stages configuration
3553
+ const { getStages } = require('./config');
3554
+ const configuredStages = await getStages();
3555
+ const stagesCount = configuredStages.length;
3556
+ items.push({
3557
+ type: 'setting',
3558
+ name: ` └─ ${t('interactive.configure.stages')}: ${chalk.cyan(stagesCount + ' ' + t('interactive.stages'))}`,
3559
+ value: 'setting:stages'
3560
+ });
3561
+
3575
3562
  // Cloud Sync Status
3576
3563
  try {
3577
3564
  const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
3578
3565
  const syncEngine = new SyncEngine();
3579
-
3566
+
3580
3567
  // Try to initialize, but don't fail if AWS not configured
3581
3568
  try {
3582
3569
  await syncEngine.initialize();
3583
3570
  const syncStatus = syncEngine.getStatus();
3584
3571
  syncEngine.stop();
3585
-
3572
+
3586
3573
  const onlineIcon = syncStatus.isOnline ? chalk.green('●') : chalk.red('●');
3587
- const onlineText = syncStatus.isOnline ? 'Online' : 'Offline';
3574
+ const onlineText = syncStatus.isOnline ? t('interactive.online') : t('interactive.offline');
3588
3575
  const queueText = syncStatus.queuedChanges > 0 ? chalk.yellow(` (${syncStatus.queuedChanges} queued)`) : '';
3589
-
3576
+
3590
3577
  items.push({
3591
3578
  type: 'setting',
3592
- name: `Cloud Sync: ${onlineIcon} ${onlineText}${queueText}`,
3579
+ name: `${t('interactive.cloud.sync')}: ${onlineIcon} ${onlineText}${queueText}`,
3593
3580
  value: 'setting:cloud-sync'
3594
3581
  });
3595
3582
  } catch (initError) {
@@ -3658,14 +3645,18 @@ async function startInteractive() {
3658
3645
 
3659
3646
  description += nextLine + '\n';
3660
3647
  }
3661
- nextReqText = title + '\n' + description;
3648
+ // Translate requirement labels
3649
+ let displayText = title + '\n' + description;
3650
+ displayText = displayText.replace(/PACKAGE:/g, t('requirement.label.package'));
3651
+ displayText = displayText.replace(/STATUS:/g, t('requirement.label.status'));
3652
+ nextReqText = displayText;
3662
3653
  break;
3663
3654
  }
3664
3655
  }
3665
3656
  }
3666
3657
  items.push({
3667
3658
  type: 'info',
3668
- name: ` └─ Next TODO Requirement: ${nextReqText}`,
3659
+ name: ` └─ ${t('interactive.next.todo')}: ${nextReqText}`,
3669
3660
  value: 'info:next-requirement'
3670
3661
  });
3671
3662
  }
@@ -3690,13 +3681,13 @@ async function startInteractive() {
3690
3681
  // Action items (with letters)
3691
3682
  // Only show Initialize option if neither directory exists
3692
3683
  if (!allnightStatus.exists) {
3693
- items.push({ type: 'action', name: 'Initialize repository (.vibecodingmachine)', value: 'repo:init' });
3684
+ items.push({ type: 'action', name: t('interactive.initialize'), value: 'repo:init' });
3694
3685
  }
3695
3686
 
3696
- items.push({ type: 'action', name: 'View All Computers', value: 'computers:list' });
3697
- items.push({ type: 'action', name: 'Sync Now', value: 'sync:now' });
3698
- items.push({ type: 'action', name: 'Logout', value: 'logout' });
3699
- items.push({ type: 'action', name: 'Exit', value: 'exit' });
3687
+ items.push({ type: 'action', name: t('interactive.view.computers'), value: 'computers:list' });
3688
+ items.push({ type: 'action', name: t('interactive.sync.now'), value: 'sync:now' });
3689
+ items.push({ type: 'action', name: t('interactive.logout'), value: 'logout' });
3690
+ items.push({ type: 'action', name: t('interactive.exit'), value: 'exit' });
3700
3691
 
3701
3692
 
3702
3693
  // Use custom quick menu with last selected index
@@ -3739,7 +3730,6 @@ async function startInteractive() {
3739
3730
  const path = require('path');
3740
3731
  const os = require('os');
3741
3732
  const yaml = require('js-yaml');
3742
- const { spawn } = require('child_process');
3743
3733
  const configPath = path.join(os.homedir(), '.continue', 'config.yaml');
3744
3734
 
3745
3735
  if (fs.existsSync(configPath)) {
@@ -3864,7 +3854,7 @@ async function startInteractive() {
3864
3854
  fs.writeFileSync(configPath, yaml.dump(config), 'utf8');
3865
3855
 
3866
3856
  console.log(chalk.green(`✓ Set ${model.name || modelName} as default model\n`));
3867
- console.log(chalk.gray('Press Enter to continue...'));
3857
+ console.log(chalk.gray(t('interactive.press.enter.continue')));
3868
3858
  await inquirer.prompt([
3869
3859
  {
3870
3860
  type: 'input',
@@ -3882,7 +3872,7 @@ async function startInteractive() {
3882
3872
  {
3883
3873
  type: 'input',
3884
3874
  name: 'continue',
3885
- message: 'Press Enter to return to menu...',
3875
+ message: t('interactive.press.enter.return'),
3886
3876
  }
3887
3877
  ]);
3888
3878
  }
@@ -3895,7 +3885,7 @@ async function startInteractive() {
3895
3885
  {
3896
3886
  type: 'input',
3897
3887
  name: 'continue',
3898
- message: 'Press Enter to return to menu...',
3888
+ message: t('interactive.press.enter.return'),
3899
3889
  }
3900
3890
  ]);
3901
3891
  }
@@ -3907,7 +3897,7 @@ async function startInteractive() {
3907
3897
  {
3908
3898
  type: 'input',
3909
3899
  name: 'continue',
3910
- message: 'Press Enter to return to menu...',
3900
+ message: t('interactive.press.enter.return'),
3911
3901
  }
3912
3902
  ]);
3913
3903
  }
@@ -3930,9 +3920,76 @@ async function startInteractive() {
3930
3920
  await showWelcomeScreen();
3931
3921
  break;
3932
3922
  }
3923
+ case 'setting:stages': {
3924
+ // Configure stages
3925
+ const { getStages, setStages, DEFAULT_STAGES } = require('./config');
3926
+ const inquirer = require('inquirer');
3927
+
3928
+ // Override inquirer's checkbox help text for current locale
3929
+ const CheckboxPrompt = require('inquirer/lib/prompts/checkbox');
3930
+ const originalGetHelpText = CheckboxPrompt.prototype.getHelp || function() {
3931
+ return '(Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)';
3932
+ };
3933
+
3934
+ CheckboxPrompt.prototype.getHelp = function() {
3935
+ const locale = detectLocale();
3936
+ if (locale === 'es') {
3937
+ return '(Presiona <espacio> para seleccionar, <a> para alternar todo, <i> para invertir selección, y <enter> para proceder)';
3938
+ }
3939
+ return originalGetHelpText.call(this);
3940
+ };
3941
+
3942
+ const currentStages = await getStages();
3943
+
3944
+ console.log(chalk.cyan(`\n🔨 ${t('workflow.config.title')}\n`));
3945
+ console.log(chalk.gray(t('workflow.config.description')));
3946
+ console.log(chalk.gray(t('workflow.config.order.note')));
3947
+ console.log(chalk.gray(`${t('workflow.config.instructions')}\n`));
3948
+
3949
+ const { selectedStages } = await inquirer.prompt([
3950
+ {
3951
+ type: 'checkbox',
3952
+ name: 'selectedStages',
3953
+ message: t('workflow.config.select.prompt'),
3954
+ choices: DEFAULT_STAGES.map(stage => ({
3955
+ name: translateStage(stage),
3956
+ value: stage, // Keep original English value for internal use
3957
+ checked: currentStages.includes(stage)
3958
+ })),
3959
+ validate: (answer) => {
3960
+ if (answer.length < 1) {
3961
+ return t('workflow.config.validation.error');
3962
+ }
3963
+ return true;
3964
+ },
3965
+ loop: false,
3966
+ pageSize: 15
3967
+ }
3968
+ ]);
3969
+
3970
+ // Restore original getHelp method
3971
+ CheckboxPrompt.prototype.getHelp = originalGetHelpText;
3972
+
3973
+ // Preserve order from DEFAULT_STAGES for selected items
3974
+ // This ensures stages always run in the correct logical order
3975
+ const newStages = DEFAULT_STAGES.filter(stage => selectedStages.includes(stage));
3976
+
3977
+ await setStages(newStages);
3978
+ const translatedStages = newStages.map(stage => translateStage(stage));
3979
+ console.log(chalk.green('\n✓'), `${t('workflow.config.updated')} ${translatedStages.join(' → ')}\n`);
3980
+
3981
+ const { continue: _ } = await inquirer.prompt([{
3982
+ type: 'input',
3983
+ name: 'continue',
3984
+ message: t('interactive.press.enter.return')
3985
+ }]);
3986
+
3987
+ await showWelcomeScreen();
3988
+ break;
3989
+ }
3933
3990
  case 'setting:auto-start': {
3934
3991
  try {
3935
- console.log(chalk.bold.cyan('\n🚀 Starting Auto Mode...\n'));
3992
+ console.log(chalk.bold.cyan('\n' + t('auto.starting') + '\n'));
3936
3993
  // Check if there are requirements to work on
3937
3994
  const hasRequirements = await requirementsExists();
3938
3995
  const counts = hasRequirements ? await countRequirements() : null;
@@ -3944,7 +4001,7 @@ async function startInteractive() {
3944
4001
  await inquirer.prompt([{
3945
4002
  type: 'input',
3946
4003
  name: 'continue',
3947
- message: 'Press Enter to return to menu...',
4004
+ message: t('interactive.press.enter.return'),
3948
4005
  }]);
3949
4006
  await showWelcomeScreen();
3950
4007
  break;
@@ -3988,7 +4045,7 @@ async function startInteractive() {
3988
4045
  await inquirer.prompt([{
3989
4046
  type: 'input',
3990
4047
  name: 'continue',
3991
- message: 'Press Enter to return to menu...',
4048
+ message: t('interactive.press.enter.return'),
3992
4049
  }]);
3993
4050
 
3994
4051
  await showWelcomeScreen();
@@ -4021,7 +4078,7 @@ async function startInteractive() {
4021
4078
  await inquirer.prompt([{
4022
4079
  type: 'input',
4023
4080
  name: 'continue',
4024
- message: 'Press Enter to return to menu...',
4081
+ message: t('interactive.press.enter.return'),
4025
4082
  }]);
4026
4083
  await showWelcomeScreen();
4027
4084
  }
@@ -4035,7 +4092,7 @@ async function startInteractive() {
4035
4092
  await inquirer.prompt([{
4036
4093
  type: 'input',
4037
4094
  name: 'continue',
4038
- message: 'Press Enter to return to menu...',
4095
+ message: t('interactive.press.enter.return'),
4039
4096
  }]);
4040
4097
  await showWelcomeScreen();
4041
4098
  }
@@ -4052,7 +4109,7 @@ async function startInteractive() {
4052
4109
  await inquirer.prompt([{
4053
4110
  type: 'input',
4054
4111
  name: 'continue',
4055
- message: 'Press Enter to return to menu...',
4112
+ message: t('interactive.press.enter.return'),
4056
4113
  }]);
4057
4114
  await showWelcomeScreen();
4058
4115
  break;
@@ -4079,12 +4136,12 @@ async function startInteractive() {
4079
4136
  defaultMaxChats = String(currentConfig.maxChats);
4080
4137
  }
4081
4138
 
4082
- console.log(chalk.bold.cyan('\n⚙️ Configure Stop Condition\n'));
4139
+ console.log(chalk.bold.cyan('\n' + t('config.stop.condition.title') + '\n'));
4083
4140
 
4084
4141
  const { maxChats } = await inquirer.prompt([{
4085
4142
  type: 'input',
4086
4143
  name: 'maxChats',
4087
- message: 'Max chats (0 for never stop):',
4144
+ message: t('config.max.chats.prompt'),
4088
4145
  default: defaultMaxChats
4089
4146
  }]);
4090
4147
 
@@ -4093,11 +4150,11 @@ async function startInteractive() {
4093
4150
  if (maxChats && maxChats.trim() !== '' && maxChats.trim() !== '0') {
4094
4151
  newConfig.maxChats = parseInt(maxChats);
4095
4152
  newConfig.neverStop = false;
4096
- console.log(chalk.green('\n✓'), `Stop condition updated: ${chalk.cyan(`Stop after ${newConfig.maxChats}`)}\n`);
4153
+ console.log(chalk.green('\n✓'), `${t('config.stop.condition.updated')} ${chalk.cyan(t('config.stop.after', { count: newConfig.maxChats }))}\n`);
4097
4154
  } else {
4098
4155
  delete newConfig.maxChats;
4099
4156
  newConfig.neverStop = true;
4100
- console.log(chalk.green('\n✓'), `Stop condition updated: ${chalk.cyan('Never Stop')}\n`);
4157
+ console.log(chalk.green('\n✓'), `${t('config.stop.condition.updated')} ${chalk.cyan(t('config.never.stop'))}\n`);
4101
4158
  }
4102
4159
 
4103
4160
  await setAutoConfig(newConfig);
@@ -4116,11 +4173,11 @@ async function startInteractive() {
4116
4173
  case 'repo:init':
4117
4174
  await repo.initRepo();
4118
4175
  break;
4119
-
4176
+
4120
4177
  case 'computers:list': {
4121
4178
  const computerCommands = require('../commands/computers');
4122
4179
  await computerCommands.listComputers();
4123
- console.log(chalk.gray('\nPress Enter to continue...'));
4180
+ console.log(chalk.gray(`\n${t('interactive.press.enter.continue')}`));
4124
4181
  await new Promise(resolve => {
4125
4182
  const rl = readline.createInterface({
4126
4183
  input: process.stdin,
@@ -4134,11 +4191,11 @@ async function startInteractive() {
4134
4191
  await showWelcomeScreen();
4135
4192
  break;
4136
4193
  }
4137
-
4194
+
4138
4195
  case 'sync:now': {
4139
4196
  const syncCommands = require('../commands/sync');
4140
4197
  await syncCommands.syncNow();
4141
- console.log(chalk.gray('\nPress Enter to continue...'));
4198
+ console.log(chalk.gray(`\n${t('interactive.press.enter.continue')}`));
4142
4199
  await new Promise(resolve => {
4143
4200
  const rl = readline.createInterface({
4144
4201
  input: process.stdin,
@@ -4152,13 +4209,13 @@ async function startInteractive() {
4152
4209
  await showWelcomeScreen();
4153
4210
  break;
4154
4211
  }
4155
-
4212
+
4156
4213
  case 'setting:cloud-sync': {
4157
4214
  await showCloudSyncMenu();
4158
4215
  await showWelcomeScreen();
4159
4216
  break;
4160
4217
  }
4161
-
4218
+
4162
4219
  case 'setting:cloud-sync-setup': {
4163
4220
  console.clear();
4164
4221
  console.log(chalk.bold.cyan('\n☁️ Cloud Sync Setup\n'));
@@ -4168,8 +4225,8 @@ async function startInteractive() {
4168
4225
  console.log(chalk.gray('2. Add AWS configuration to your .env file'));
4169
4226
  console.log(chalk.gray('3. Register this computer with: ') + chalk.cyan('vcm computer:register "<focus>"'));
4170
4227
  console.log(chalk.gray('\nFor more info, see: ') + chalk.cyan('docs/CLOUD_SYNC.md\n'));
4171
-
4172
- console.log(chalk.gray('Press Enter to continue...'));
4228
+
4229
+ console.log(chalk.gray(t('interactive.press.enter.continue')));
4173
4230
  await new Promise(resolve => {
4174
4231
  const rl = readline.createInterface({
4175
4232
  input: process.stdin,
@@ -4203,7 +4260,7 @@ async function startInteractive() {
4203
4260
  {
4204
4261
  type: 'input',
4205
4262
  name: 'maxChats',
4206
- message: 'Max chats (0 for never stop):',
4263
+ message: t('config.max.chats.prompt'),
4207
4264
  default: ''
4208
4265
  }
4209
4266
  ]);
@@ -4237,8 +4294,8 @@ async function startInteractive() {
4237
4294
  │ │
4238
4295
  ╰───────────────────────────────────────────────────────╯
4239
4296
 
4240
- Repo: ${repoPath || 'Not set'}
4241
- Computer Name: ${hostname}
4297
+ ${t('system.repo').padEnd(25)} ${repoPath || 'Not set'}
4298
+ ${t('system.computer.name').padEnd(25)} ${hostname}
4242
4299
  Current IDE: ${formatIDEName(ide)}
4243
4300
  AI Provider: ${getCurrentAIProvider(ide) || 'N/A'}
4244
4301
  Max Chats: ${maxChats || 'Never stop'}`;
@@ -4358,12 +4415,12 @@ Max Chats: ${maxChats || 'Never stop'}`;
4358
4415
  const auth = require('./auth');
4359
4416
  try {
4360
4417
  await auth.logout();
4361
- console.log(chalk.green('\n✓ Logged out successfully!\n'));
4362
- console.log(chalk.gray('Run "vcm auth:login" or "vcm" to login again.\n'));
4418
+ console.log(chalk.green(`\n✓ ${t('interactive.logout.success')}\n`));
4419
+ console.log(chalk.gray(`${t('interactive.logout.login.again')}\n`));
4363
4420
  process.exit(0);
4364
4421
  } catch (error) {
4365
- console.error(chalk.red('\n✗ Logout failed:'), error.message);
4366
- console.log(chalk.yellow('\nPress any key to continue...'));
4422
+ console.error(chalk.red(`\n✗ ${t('interactive.logout.failed')}`), error.message);
4423
+ console.log(chalk.yellow(`\n${t('interactive.press.any.key')}...`));
4367
4424
  await new Promise((resolve) => {
4368
4425
  process.stdin.once('keypress', () => resolve());
4369
4426
  });
@@ -4387,7 +4444,7 @@ Max Chats: ${maxChats || 'Never stop'}`;
4387
4444
  await inquirer.prompt([{
4388
4445
  type: 'input',
4389
4446
  name: 'continue',
4390
- message: 'Press Enter to return to menu...',
4447
+ message: t('interactive.press.enter.return'),
4391
4448
  }]);
4392
4449
  await showWelcomeScreen();
4393
4450
  break;
@@ -4402,7 +4459,7 @@ Max Chats: ${maxChats || 'Never stop'}`;
4402
4459
  await inquirer.prompt([{
4403
4460
  type: 'input',
4404
4461
  name: 'continue',
4405
- message: 'Press Enter to return to menu...',
4462
+ message: t('interactive.press.enter.return'),
4406
4463
  }]);
4407
4464
  await showWelcomeScreen();
4408
4465
  }