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.
- package/bin/vibecodingmachine.js +260 -52
- package/logs/audit/2025-12-24.jsonl +2 -0
- package/package.json +2 -2
- package/reproduce_issue.js +160 -0
- package/src/commands/auth.js +19 -19
- package/src/commands/auto-direct.js +133 -93
- package/src/commands/auto.js +165 -67
- package/src/commands/computers.js +62 -59
- package/src/commands/locale.js +73 -0
- package/src/commands/repo.js +0 -1
- package/src/commands/requirements-remote.js +14 -9
- package/src/commands/requirements.js +29 -3
- package/src/commands/setup.js +14 -13
- package/src/commands/status.js +17 -18
- package/src/commands/sync.js +72 -71
- package/src/utils/agent-selector.js +50 -0
- package/src/utils/antigravity-installer.js +212 -0
- package/src/utils/antigravity-js-handler.js +60 -0
- package/src/utils/asset-cleanup.js +0 -1
- package/src/utils/auth.js +149 -2
- package/src/utils/auto-mode-ansi-ui.js +0 -1
- package/src/utils/auto-mode-simple-ui.js +1 -1
- package/src/utils/compliance-check.js +166 -0
- package/src/utils/config.js +27 -1
- package/src/utils/copy-with-progress.js +167 -0
- package/src/utils/download-with-progress.js +84 -0
- package/src/utils/first-run.js +189 -71
- package/src/utils/interactive.js +419 -362
- package/src/utils/kiro-installer.js +56 -24
- package/src/utils/persistent-header.js +1 -3
- package/src/utils/provider-registry.js +43 -5
- package/src/utils/user-tracking.js +300 -0
- package/tests/requirements-navigator-buildtree-await.test.js +28 -0
package/src/utils/interactive.js
CHANGED
|
@@ -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 {
|
|
243
|
+
const { getProjectRequirementStats } = require('vibecodingmachine-core');
|
|
217
244
|
const repoPath = await getRepoPath();
|
|
218
|
-
|
|
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('
|
|
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('
|
|
407
|
-
console.log(chalk.gray('
|
|
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📋
|
|
479
|
-
console.log(chalk.gray('
|
|
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:
|
|
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: '➕
|
|
496
|
-
tree.items.push({ level: 1, type: 'add', label: '➕
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
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,
|
|
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📋
|
|
845
|
-
console.log(chalk.gray(
|
|
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('
|
|
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
|
-
|
|
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('
|
|
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👋
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
2755
|
-
let
|
|
2756
|
-
while (
|
|
2757
|
-
|
|
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
|
-
//
|
|
2763
|
-
let
|
|
2764
|
-
while (
|
|
2765
|
-
|
|
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
|
-
|
|
2793
|
-
|
|
2794
|
-
const
|
|
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('⚙
|
|
2799
|
-
|
|
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.
|
|
2769
|
+
for (let idx = 0; idx < order.length; idx++) {
|
|
2770
|
+
const id = order[idx];
|
|
2802
2771
|
const def = defMap.get(id);
|
|
2803
|
-
if (!def)
|
|
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
|
-
//
|
|
2834
|
-
|
|
2835
|
-
//
|
|
2836
|
-
|
|
2837
|
-
|
|
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
|
-
|
|
2840
|
-
const
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
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
|
-
|
|
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('
|
|
2849
|
+
console.log(chalk.yellow(t('provider.pending.changes')));
|
|
2881
2850
|
} else {
|
|
2882
|
-
console.log(chalk.gray('
|
|
2851
|
+
console.log(chalk.gray(t('provider.no.pending.changes')));
|
|
2883
2852
|
}
|
|
2884
2853
|
};
|
|
2885
2854
|
|
|
2886
|
-
|
|
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(
|
|
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('
|
|
3108
|
-
console.log(chalk.white('
|
|
3109
|
-
console.log(chalk.gray(
|
|
3110
|
-
console.log(chalk.gray(
|
|
3111
|
-
console.log(chalk.gray(
|
|
3112
|
-
console.log(chalk.gray('
|
|
3113
|
-
|
|
3114
|
-
console.log(chalk.gray('
|
|
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: '
|
|
3133
|
-
{ name: '
|
|
3134
|
-
{ name: '
|
|
3135
|
-
{ name: '
|
|
3136
|
-
{ name: '
|
|
3137
|
-
{ name: '
|
|
3138
|
-
{ name: '
|
|
3139
|
-
{ name: '
|
|
3140
|
-
{ name: chalk.gray('
|
|
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: '
|
|
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('
|
|
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(
|
|
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 || '
|
|
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('
|
|
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: '
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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: '
|
|
3307
|
-
default: '
|
|
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('
|
|
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('
|
|
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 =
|
|
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 ? '
|
|
3507
|
-
autoConfig.maxChats ?
|
|
3508
|
-
'
|
|
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:
|
|
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: ` └─
|
|
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 = '
|
|
3520
|
+
let requirementsText = t('interactive.requirements') + ': ';
|
|
3542
3521
|
if (counts) {
|
|
3543
|
-
//
|
|
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((
|
|
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(
|
|
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: ` └─
|
|
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 ? '
|
|
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:
|
|
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
|
-
|
|
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: ` └─
|
|
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: '
|
|
3684
|
+
items.push({ type: 'action', name: t('interactive.initialize'), value: 'repo:init' });
|
|
3694
3685
|
}
|
|
3695
3686
|
|
|
3696
|
-
items.push({ type: 'action', name: '
|
|
3697
|
-
items.push({ type: 'action', name: '
|
|
3698
|
-
items.push({ type: 'action', name: '
|
|
3699
|
-
items.push({ type: 'action', name: '
|
|
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('
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
|
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: '
|
|
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✓'),
|
|
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✓'),
|
|
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('
|
|
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('
|
|
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('
|
|
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: '
|
|
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
|
-
|
|
4241
|
-
|
|
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(
|
|
4362
|
-
console.log(chalk.gray('
|
|
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(
|
|
4366
|
-
console.log(chalk.yellow('
|
|
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: '
|
|
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: '
|
|
4462
|
+
message: t('interactive.press.enter.return'),
|
|
4406
4463
|
}]);
|
|
4407
4464
|
await showWelcomeScreen();
|
|
4408
4465
|
}
|